diff --git a/Editor/EditorMercs.cpp b/Editor/EditorMercs.cpp index edd7702b..1ac165ae 100644 --- a/Editor/EditorMercs.cpp +++ b/Editor/EditorMercs.cpp @@ -3769,7 +3769,7 @@ void PasteMercPlacement( INT32 iMapIndex ) if( gTempBasicPlacement.fDetailedPlacement ) { - CreateDetailedPlacementGivenStaticDetailedPlacementAndBasicPlacementInfo( &tempDetailedPlacement, &gTempDetailedPlacement, &gTempBasicPlacement ); + CreateDetailedPlacementGivenStaticDetailedPlacementAndBasicPlacementInfo( &tempDetailedPlacement, &gTempDetailedPlacement, &gTempBasicPlacement, sSectorX, sSectorY ); } else { diff --git a/GameSettings.cpp b/GameSettings.cpp index db7d55da..e5322e4e 100644 --- a/GameSettings.cpp +++ b/GameSettings.cpp @@ -1833,7 +1833,20 @@ void LoadGameExternalOptions() gGameExternalOptions.bRegularMilitiaEquipmentQualityModifier = iniReader.ReadInteger("Militia Strength Settings", "REGULAR_MILITIA_EQUIPMENT_QUALITY_MODIFIER", 0, -5, 10); gGameExternalOptions.bVeteranMilitiaEquipmentQualityModifier = iniReader.ReadInteger("Militia Strength Settings", "VETERAN_MILITIA_EQUIPMENT_QUALITY_MODIFIER", 1, -5, 10); + //################# Militia Equipment Settings ################## + gGameExternalOptions.fMilitiaUseSectorInventory = iniReader.ReadBoolean("Militia Equipment Settings","MILITIA_USE_SECTOR_EQUIPMENT", FALSE); + gGameExternalOptions.fMilitiaUseSectorInventory_Armour = iniReader.ReadBoolean("Militia Equipment Settings","MILITIA_USE_SECTOR_EQUIPMENT_ARMOUR", TRUE); + gGameExternalOptions.fMilitiaUseSectorInventory_Face = iniReader.ReadBoolean("Militia Equipment Settings","MILITIA_USE_SECTOR_EQUIPMENT_FACE", TRUE); + gGameExternalOptions.fMilitiaUseSectorInventory_Melee = iniReader.ReadBoolean("Militia Equipment Settings","MILITIA_USE_SECTOR_EQUIPMENT_MELEE", TRUE); + gGameExternalOptions.fMilitiaUseSectorInventory_Gun = iniReader.ReadBoolean("Militia Equipment Settings","MILITIA_USE_SECTOR_EQUIPMENT_GUN", TRUE); + gGameExternalOptions.fMilitiaUseSectorInventory_Ammo = iniReader.ReadBoolean("Militia Equipment Settings","MILITIA_USE_SECTOR_EQUIPMENT_AMMO", TRUE); + gGameExternalOptions.fMilitiaUseSectorInventory_Grenade = iniReader.ReadBoolean("Militia Equipment Settings","MILITIA_USE_SECTOR_EQUIPMENT_GRENADE", TRUE); + gGameExternalOptions.fMilitiaUseSectorInventory_Launcher = iniReader.ReadBoolean("Militia Equipment Settings","MILITIA_USE_SECTOR_EQUIPMENT_LAUNCHER", TRUE); + gGameExternalOptions.usMilitiaAmmo_Min = iniReader.ReadInteger("Militia Equipment Settings","MILITIA_USE_SECTOR_EQUIPMENT_AMMO_MIN", 10, 1, 400); + gGameExternalOptions.usMilitiaAmmo_Max = iniReader.ReadInteger("Militia Equipment Settings","MILITIA_USE_SECTOR_EQUIPMENT_AMMO_MAX", 200, 30, 400); + gGameExternalOptions.usMilitiaAmmo_OptimalMagCount = iniReader.ReadInteger("Militia Equipment Settings","MILITIA_USE_SECTOR_EQUIPMENT_AMMO_OPTIMAL_MAG_COUNT", 2, 1, 10); + //################# Shopkeeper Inventory Settings ################## // WDS - Improve Tony's and Devin's inventory like BR's diff --git a/GameSettings.h b/GameSettings.h index dac79c42..6f70ca9f 100644 --- a/GameSettings.h +++ b/GameSettings.h @@ -473,6 +473,19 @@ typedef struct INT8 bRegularMilitiaEquipmentQualityModifier; INT8 bVeteranMilitiaEquipmentQualityModifier; + // Flugente - militia equipment + BOOLEAN fMilitiaUseSectorInventory; + BOOLEAN fMilitiaUseSectorInventory_Armour; + BOOLEAN fMilitiaUseSectorInventory_Face; + BOOLEAN fMilitiaUseSectorInventory_Melee; + BOOLEAN fMilitiaUseSectorInventory_Gun; + BOOLEAN fMilitiaUseSectorInventory_Ammo; + BOOLEAN fMilitiaUseSectorInventory_Grenade; + BOOLEAN fMilitiaUseSectorInventory_Launcher; + UINT16 usMilitiaAmmo_Min; + UINT16 usMilitiaAmmo_Max; + UINT16 usMilitiaAmmo_OptimalMagCount; + // WDS - Improve Tony's and Devin's inventory like BR's BOOLEAN tonyUsesBRSetting; BOOLEAN devinUsesBRSetting; diff --git a/GameVersion.h b/GameVersion.h index ff323764..58872c49 100644 --- a/GameVersion.h +++ b/GameVersion.h @@ -20,8 +20,8 @@ extern CHAR16 zTrackingNumber[16]; // // Keeps track of the saved game version. Increment the saved game version whenever // you will invalidate the saved game file -#define SAVE_GAME_VERSION 138 +#define MILITIA_EQUIPMENT 139 // Flugente: militia can equip from sector inventory #define MULTITURN_ACTIONS 138 // Flugente: multiturn actions #define EXTERNALISED_MERCHANTS 137 // Flugente: increased the number of merchants and externalised their data #define PRISON_SYSTEM 136 // Flugente: added var to the sector structure. Shouldn't break compatibility, but let's be safe @@ -58,6 +58,7 @@ extern CHAR16 zTrackingNumber[16]; #define AP100_SAVEGAME_DATATYPE_CHANGE 105 // Before this, we didn't have the 100AP structure changes #define NIV_SAVEGAME_DATATYPE_CHANGE 102 // Before this, we used the old structure system +#define SAVE_GAME_VERSION MILITIA_EQUIPMENT //#define RUSSIANGOLD #ifdef __cplusplus diff --git a/MessageBoxScreen.cpp b/MessageBoxScreen.cpp index 4e3778b6..680b9249 100644 --- a/MessageBoxScreen.cpp +++ b/MessageBoxScreen.cpp @@ -412,13 +412,12 @@ INT32 DoMessageBox( UINT8 ubStyle, const STR16 zString, UINT32 uiExitScreen, UIN sButtonY += MSGBOX_SMALL_BUTTON_WIDTH; // begin from the front - sButtonX = sBlankSpace / 2 - MSGBOX_SMALL_BUTTON_WIDTH - MSGBOX_SMALL_BUTTON_X_SEP; + sButtonX = sBlankSpace / 2; for ( INT8 j = 0; j < 4; ++j) { INT8 k = 4*i + j; - sButtonX += MSGBOX_SMALL_BUTTON_WIDTH + MSGBOX_SMALL_BUTTON_X_SEP; gMsgBox.uiButton[k] = CreateIconAndTextButton( gMsgBox.iButtonImages, gzUserDefinedButton[k], FONT12ARIAL, ubFontColor, ubFontShadowColor, ubFontColor, ubFontShadowColor, @@ -428,6 +427,8 @@ INT32 DoMessageBox( UINT8 ubStyle, const STR16 zString, UINT32 uiExitScreen, UIN MSYS_SetBtnUserData( gMsgBox.uiButton[k], 0, k+1); SetButtonCursor(gMsgBox.uiButton[k], usCursor); ForceButtonUnDirty( gMsgBox.uiButton[k] ); + + sButtonX += 75 + MSGBOX_SMALL_BUTTON_WIDTH + MSGBOX_SMALL_BUTTON_X_SEP; } } } diff --git a/Strategic/Auto Resolve.cpp b/Strategic/Auto Resolve.cpp index 945bd027..a252d496 100644 --- a/Strategic/Auto Resolve.cpp +++ b/Strategic/Auto Resolve.cpp @@ -2186,7 +2186,7 @@ static void ARCreateMilitia( UINT8 mclass, INT32 i, INT16 sX, INT16 sY) if( mclass == SOLDIER_CLASS_ELITE_MILITIA ) { - gpCivs[i].pSoldier = TacticalCreateMilitia( SOLDIER_CLASS_ELITE_MILITIA ); + gpCivs[i].pSoldier = TacticalCreateMilitia( SOLDIER_CLASS_ELITE_MILITIA, sX, sY ); if( gpCivs[i].pSoldier->ubBodyType == REGFEMALE ) { gpCivs[i].usIndex = MILITIA3F_FACE; @@ -2198,7 +2198,7 @@ static void ARCreateMilitia( UINT8 mclass, INT32 i, INT16 sX, INT16 sY) } else if( mclass == SOLDIER_CLASS_REG_MILITIA ) { - gpCivs[i].pSoldier = TacticalCreateMilitia( SOLDIER_CLASS_REG_MILITIA ); + gpCivs[i].pSoldier = TacticalCreateMilitia( SOLDIER_CLASS_REG_MILITIA, sX, sY ); if( gpCivs[i].pSoldier->ubBodyType == REGFEMALE ) { gpCivs[i].usIndex = MILITIA2F_FACE; @@ -2210,7 +2210,7 @@ static void ARCreateMilitia( UINT8 mclass, INT32 i, INT16 sX, INT16 sY) } else if( mclass == SOLDIER_CLASS_GREEN_MILITIA ) { - gpCivs[i].pSoldier = TacticalCreateMilitia( SOLDIER_CLASS_GREEN_MILITIA ); + gpCivs[i].pSoldier = TacticalCreateMilitia( SOLDIER_CLASS_GREEN_MILITIA, sX, sY ); if( gpCivs[i].pSoldier->ubBodyType == REGFEMALE ) { gpCivs[i].usIndex = MILITIA1F_FACE; @@ -2725,6 +2725,10 @@ DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"Autoresolve2"); #endif break; } + + // Flugente: drop sector equipment + gpCivs[ i ].pSoldier->DropSectorEquipment(); + if( fDeleteForGood && gpCivs[ i ].pSoldier->stats.bLife < OKLIFE/2 ) { AddDeadSoldierToUnLoadedSector( gpAR->ubSectorX, gpAR->ubSectorY, 0, gpCivs[ i ].pSoldier, RandomGridNo(), ADD_DEAD_SOLDIER_TO_SWEETSPOT ); diff --git a/Strategic/Map Screen Interface Map Inventory.cpp b/Strategic/Map Screen Interface Map Inventory.cpp index 5184fffa..6c254830 100644 --- a/Strategic/Map Screen Interface Map Inventory.cpp +++ b/Strategic/Map Screen Interface Map Inventory.cpp @@ -612,6 +612,15 @@ BOOLEAN RenderItemInPoolSlot( INT32 iCurrentSlot, INT32 iFirstSlotOnPage ) } } + if( gGameExternalOptions.fMilitiaUseSectorInventory && ( pInventoryPoolList[ iCurrentSlot + iFirstSlotOnPage ].usFlags & WORLD_ITEM_TABOO_FOR_MILITIA_EQ ) ) + { + //Shade the item, but only if it is an active item! + if ( pInventoryPoolList[ iCurrentSlot + iFirstSlotOnPage ].object.exists() == true) + { + UINT16 usMilitia_EQColor = Get16BPPColor( FROMRGB( 69, 197, 149 ) ); + DrawHatchOnInventory_MilitiaAccess( guiSAVEBUFFER, sX, sY, MAP_INVEN_SLOT_WIDTH, MAP_INVEN_SLOT_IMAGE_HEIGHT , usMilitia_EQColor); + } + } // the name @@ -1316,13 +1325,24 @@ void MapInvenPoolSlots(MOUSE_REGION * pRegion, INT32 iReason ) { if ( gpItemPointer == NULL ) { - swprintf( sString, pMapInventoryErrorString[ 2 ], Menptr[ gCharactersList[ bSelectedInfoChar ].usSolID ].name ); + if ( _KeyDown ( TAB ) && gGameExternalOptions.fMilitiaUseSectorInventory ) + { + if ( pInventoryPoolList[ ( iCurrentInventoryPoolPage * MAP_INVENTORY_POOL_SLOT_COUNT ) + iCounter ].usFlags & WORLD_ITEM_TABOO_FOR_MILITIA_EQ ) + pInventoryPoolList[ ( iCurrentInventoryPoolPage * MAP_INVENTORY_POOL_SLOT_COUNT ) + iCounter ].usFlags &= ~ WORLD_ITEM_TABOO_FOR_MILITIA_EQ; + else + pInventoryPoolList[ ( iCurrentInventoryPoolPage * MAP_INVENTORY_POOL_SLOT_COUNT ) + iCounter ].usFlags |= WORLD_ITEM_TABOO_FOR_MILITIA_EQ; + } + else + { + swprintf( sString, pMapInventoryErrorString[ 2 ], Menptr[ gCharactersList[ bSelectedInfoChar ].usSolID ].name ); + DoMapMessageBox( MSG_BOX_BASIC_STYLE, sString, MAP_SCREEN, MSG_BOX_FLAG_OK, NULL ); + } } else { swprintf( sString, pMapInventoryErrorString[ 5 ], Menptr[ gCharactersList[ bSelectedInfoChar ].usSolID ].name ); - } - DoMapMessageBox( MSG_BOX_BASIC_STYLE, sString, MAP_SCREEN, MSG_BOX_FLAG_OK, NULL ); + DoMapMessageBox( MSG_BOX_BASIC_STYLE, sString, MAP_SCREEN, MSG_BOX_FLAG_OK, NULL ); + } return; } } @@ -1366,7 +1386,15 @@ void MapInvenPoolSlots(MOUSE_REGION * pRegion, INT32 iReason ) */ } - BeginInventoryPoolPtr( &( pInventoryPoolList[ ( iCurrentInventoryPoolPage * MAP_INVENTORY_POOL_SLOT_COUNT ) + iCounter ].object ) ); + if ( _KeyDown ( TAB ) && gGameExternalOptions.fMilitiaUseSectorInventory ) + { + if ( pInventoryPoolList[ ( iCurrentInventoryPoolPage * MAP_INVENTORY_POOL_SLOT_COUNT ) + iCounter ].usFlags & WORLD_ITEM_TABOO_FOR_MILITIA_EQ ) + pInventoryPoolList[ ( iCurrentInventoryPoolPage * MAP_INVENTORY_POOL_SLOT_COUNT ) + iCounter ].usFlags &= ~ WORLD_ITEM_TABOO_FOR_MILITIA_EQ; + else + pInventoryPoolList[ ( iCurrentInventoryPoolPage * MAP_INVENTORY_POOL_SLOT_COUNT ) + iCounter ].usFlags |= WORLD_ITEM_TABOO_FOR_MILITIA_EQ; + } + else + BeginInventoryPoolPtr( &( pInventoryPoolList[ ( iCurrentInventoryPoolPage * MAP_INVENTORY_POOL_SLOT_COUNT ) + iCounter ].object ) ); } else { diff --git a/Strategic/MilitiaSquads.cpp b/Strategic/MilitiaSquads.cpp index 3e615e0e..452aa7b6 100644 --- a/Strategic/MilitiaSquads.cpp +++ b/Strategic/MilitiaSquads.cpp @@ -31,6 +31,7 @@ #include "connect.h" #include "MilitiaSquads.h" #include "Reinforcement.h" +#include "Inventory Choosing.h" // added by Flugente for MoveOneMilitiaEquipmentSet() and MoveMilitiaEquipment() // Debug defines @@ -321,6 +322,7 @@ void GenerateMilitiaSquad(INT16 sMapX, INT16 sMapY, INT16 sTMapX, INT16 sTMapY, ubTargetElite--; ubMilitiaToTrain--; ubActualyAdded++; + MoveOneMilitiaEquipmentSet(sMapX, sMapY, sTMapX, sTMapY, ELITE_MILITIA); } else if (ubTargetRegular > 0) { @@ -329,6 +331,7 @@ void GenerateMilitiaSquad(INT16 sMapX, INT16 sMapY, INT16 sTMapX, INT16 sTMapY, ubTargetRegular--; ubMilitiaToTrain--; ubActualyAdded++; + MoveOneMilitiaEquipmentSet(sMapX, sMapY, sTMapX, sTMapY, REGULAR_MILITIA); } else if (ubTargetGreen > 0) { @@ -337,6 +340,7 @@ void GenerateMilitiaSquad(INT16 sMapX, INT16 sMapY, INT16 sTMapX, INT16 sTMapY, ubTargetGreen--; ubMilitiaToTrain--; ubActualyAdded++; + MoveOneMilitiaEquipmentSet(sMapX, sMapY, sTMapX, sTMapY, GREEN_MILITIA); } else { @@ -366,7 +370,7 @@ void GenerateMilitiaSquad(INT16 sMapX, INT16 sMapY, INT16 sTMapX, INT16 sTMapY, GetWorldDay( ) < gGameExternalOptions.guiTrainVeteranMilitiaDelay )) // Or not YET allowed to train elites { // Add a regular. This will effectively replace one Green militia - StrategicAddMilitiaToSector( sTMapX, sTMapY, ELITE_MILITIA, 1); + StrategicAddMilitiaToSector( sTMapX, sTMapY, REGULAR_MILITIA, 1); ubTargetElite--; ubMilitiaToTrain--; ubActualyAdded++; @@ -599,6 +603,10 @@ void MoveMilitiaSquad(INT16 sMapX, INT16 sMapY, INT16 sTMapX, INT16 sTMapY, BOOL bRegularsDestTeam = bTotalRegulars - bRegularsSourceTeam; bElitesDestTeam = bTotalElites - bElitesSourceTeam; + // Flugente: mobiles take along their gear + // move only gear for those who come new into a sector + MoveMilitiaEquipment(sMapX, sMapY, sTMapX, sTMapY, bElitesDestTeam - pTSectorInfo->ubNumberOfCivsAtLevel[ ELITE_MILITIA ], bRegularsDestTeam - pTSectorInfo->ubNumberOfCivsAtLevel[ REGULAR_MILITIA ], bGreensDestTeam - pTSectorInfo->ubNumberOfCivsAtLevel[ GREEN_MILITIA ]); + // Erase ALL militia from both locations. StrategicRemoveMilitiaFromSector( sMapX, sMapY, GREEN_MILITIA, pSectorInfo->ubNumberOfCivsAtLevel[ GREEN_MILITIA ] ); StrategicRemoveMilitiaFromSector( sMapX, sMapY, REGULAR_MILITIA, pSectorInfo->ubNumberOfCivsAtLevel[ REGULAR_MILITIA ] ); @@ -627,6 +635,10 @@ void MoveMilitiaSquad(INT16 sMapX, INT16 sMapY, INT16 sTMapX, INT16 sTMapY, BOOL bElitesSourceTeam = pSectorInfo->ubNumberOfCivsAtLevel[ ELITE_MILITIA ]; bElitesDestTeam = bElitesSourceTeam / 2; + // Flugente: mobiles take along their gear + // move only gear for those who come new into a sector + MoveMilitiaEquipment(sMapX, sMapY, sTMapX, sTMapY, bElitesDestTeam - pTSectorInfo->ubNumberOfCivsAtLevel[ ELITE_MILITIA ], bRegularsDestTeam - pTSectorInfo->ubNumberOfCivsAtLevel[ REGULAR_MILITIA ], bGreensDestTeam - pTSectorInfo->ubNumberOfCivsAtLevel[ GREEN_MILITIA ]); + // Add half team to target sector StrategicAddMilitiaToSector( sTMapX, sTMapY, GREEN_MILITIA, bGreensDestTeam ); StrategicAddMilitiaToSector( sTMapX, sTMapY, REGULAR_MILITIA, bRegularsDestTeam ); @@ -703,6 +715,10 @@ void MoveMilitiaSquad(INT16 sMapX, INT16 sMapY, INT16 sTMapX, INT16 sTMapY, BOOL } } + // Flugente: mobiles take along their gear + // move only gear for those who come new into a sector + MoveMilitiaEquipment(sMapX, sMapY, sTMapX, sTMapY, bElitesDestTeam - pTSectorInfo->ubNumberOfCivsAtLevel[ ELITE_MILITIA ], bRegularsDestTeam - pTSectorInfo->ubNumberOfCivsAtLevel[ REGULAR_MILITIA ], bGreensDestTeam - pTSectorInfo->ubNumberOfCivsAtLevel[ GREEN_MILITIA ]); + // Erase ALL militia from both locations. StrategicRemoveMilitiaFromSector( sMapX, sMapY, GREEN_MILITIA, pSectorInfo->ubNumberOfCivsAtLevel[ GREEN_MILITIA ] ); StrategicRemoveMilitiaFromSector( sMapX, sMapY, REGULAR_MILITIA, pSectorInfo->ubNumberOfCivsAtLevel[ REGULAR_MILITIA ] ); @@ -721,6 +737,9 @@ void MoveMilitiaSquad(INT16 sMapX, INT16 sMapY, INT16 sTMapX, INT16 sTMapY, BOOL } else { + // Flugente: mobiles take along their gear + MoveMilitiaEquipment(sMapX, sMapY, sTMapX, sTMapY, pSectorInfo->ubNumberOfCivsAtLevel[ ELITE_MILITIA ], pSectorInfo->ubNumberOfCivsAtLevel[ REGULAR_MILITIA ], pSectorInfo->ubNumberOfCivsAtLevel[ GREEN_MILITIA ]); + // Entire group moves from Source to Target, leaving no one behind. StrategicAddMilitiaToSector( sTMapX, sTMapY, GREEN_MILITIA, pSectorInfo->ubNumberOfCivsAtLevel[ GREEN_MILITIA ] ); StrategicAddMilitiaToSector( sTMapX, sTMapY, REGULAR_MILITIA, pSectorInfo->ubNumberOfCivsAtLevel[ REGULAR_MILITIA ] ); diff --git a/Strategic/strategicmap.cpp b/Strategic/strategicmap.cpp index d647adff..0a6ee997 100644 --- a/Strategic/strategicmap.cpp +++ b/Strategic/strategicmap.cpp @@ -6120,6 +6120,9 @@ BOOLEAN HandleDefiniteUnloadingOfWorld( UINT8 ubUnloadCode ) //if we arent loading a saved game if( !(gTacticalStatus.uiFlags & LOADING_SAVED_GAME ) ) { + // Flugente: cause all militia whose equipment is from this sector to drop it + TeamDropAll( MILITIA_TEAM ); + // Save the current sectors Item list to a temporary file, if its not the first time in SaveCurrentSectorsInformationToTempItemFile(); diff --git a/Tactical/Handle Items.cpp b/Tactical/Handle Items.cpp index 98e89c7f..5224d2ba 100644 --- a/Tactical/Handle Items.cpp +++ b/Tactical/Handle Items.cpp @@ -4683,7 +4683,11 @@ void StartTacticalFunctionSelectionMessageBox( SOLDIERTYPE * pSoldier, INT32 sGr wcscpy( gzUserDefinedButton[1], TacticalStr[ CLEAN_ONE_GUN_STR ] ); wcscpy( gzUserDefinedButton[2], TacticalStr[ CLEAN_ALL_GUNS_STR ] ); wcscpy( gzUserDefinedButton[3], TacticalStr[ TAKE_OFF_CLOTHES_STR ] ); - DoMessageBox( MSG_BOX_BASIC_MEDIUM_BUTTONS, TacticalStr[ FUNCTION_SELECTION_STR ], GAME_SCREEN, MSG_BOX_FLAG_GENERIC_FOUR_BUTTONS, TacticalFunctionSelectionMessageBoxCallBack, NULL ); + wcscpy( gzUserDefinedButton[4], TacticalStr[ MILITIA_DROP_EQ_STR ] ); + wcscpy( gzUserDefinedButton[5], TacticalStr[ UNUSED_STR ] ); + wcscpy( gzUserDefinedButton[6], TacticalStr[ UNUSED_STR ] ); + wcscpy( gzUserDefinedButton[7], TacticalStr[ UNUSED_STR ] ); + DoMessageBox( MSG_BOX_BASIC_MEDIUM_BUTTONS, TacticalStr[ FUNCTION_SELECTION_STR ], GAME_SCREEN, MSG_BOX_FLAG_GENERIC_EIGHT_BUTTONS, TacticalFunctionSelectionMessageBoxCallBack, NULL ); } void CleanWeapons( BOOLEAN fCleanAll ) @@ -4854,6 +4858,11 @@ void TacticalFunctionSelectionMessageBoxCallBack( UINT8 ubExitValue ) // undisguise or take off custom clothes Strip(gpTempSoldier); break; + + case 5: + // militia drops all gear taken from sector inventory + TeamDropAll( MILITIA_TEAM ); + break; default: break; } diff --git a/Tactical/Inventory Choosing.cpp b/Tactical/Inventory Choosing.cpp index 6597edd7..5bf8da08 100644 --- a/Tactical/Inventory Choosing.cpp +++ b/Tactical/Inventory Choosing.cpp @@ -17,6 +17,8 @@ #include "EnemyItemDrops.h" #include "strategic.h" #include "Game Clock.h" + #include "message.h" + #include "Tactical Save.h" // added by Flugente #endif /* @@ -1262,7 +1264,7 @@ void ChooseWeaponForSoldierCreateStruct( SOLDIERCREATE_STRUCT *pp, INT8 bWeaponC if( bAmmoClips && usAmmoIndex ) { //Madd: ensure a minimum # of bullets to make sure enemies don't run out and run away - while ( ( bAmmoClips * Weapon[usGunIndex].ubMagSize - Weapon[usGunIndex].ubMagSize) < 20 ) // each soldier should have at least 20 bullets, ie: 2 9mm 15rd clips, 3 7rd shotgun shells, 4 6rd speedloaders, etc. + while ( ( (bAmmoClips - 1) * Weapon[usGunIndex].ubMagSize ) < 20 ) // each soldier should have at least 20 bullets, ie: 2 9mm 15rd clips, 3 7rd shotgun shells, 4 6rd speedloaders, etc. { bAmmoClips++; } @@ -3572,3 +3574,1055 @@ UINT16 PickARandomAttachment(UINT8 typeIndex, INT8 bSoldierClass, UINT16 usBaseI else return 0; } + +////////////////// Flugente: militia equipment feature /////////////////////////////////// +// determines how good an item is +UINT32 ItemFitness( OBJECTTYPE* pObj, UINT8 idx ) +{ + UINT32 value = 0; + + if ( Item[ pObj->usItem ].usItemClass & IC_ARMOUR ) + { + value = Armour[ Item[ pObj->usItem ].ubClassIndex ].ubProtection * Armour[ Item[ pObj->usItem ].ubClassIndex ].ubCoverage * (*pObj)[idx]->data.objectStatus; + } + else if ( Item[ pObj->usItem ].usItemClass & IC_FACE ) + { + if ( Item[ pObj->usItem ].gasmask ) + value = (*pObj)[idx]->data.objectStatus; + else if ( Item[ pObj->usItem ].hearingrangebonus ) + { + value = Item[ pObj->usItem ].hearingrangebonus; + } + else + { + value += (NightTime() ? Item[ pObj->usItem ].nightvisionrangebonus : Item[ pObj->usItem ].dayvisionrangebonus ); + } + } + else if ( Item[ pObj->usItem ].usItemClass & (IC_BLADE|IC_PUNCH) ) + { + value = (*pObj)[idx]->data.objectStatus * Weapon[ Item[ pObj->usItem ].ubClassIndex ].ubImpact; + } + else if ( Item[ pObj->usItem ].usItemClass & (IC_GUN|IC_LAUNCHER) ) + { + // value is damage * range * firing rate, modified by number of attachments and accuracy + value = GetDamage(pObj) * GunRange(pObj, NULL) * max(1, GetAutofireShotsPerFiveAPs(pObj)); + + value = value * (10 + (UINT32)GetGunAccuracy(pObj)) / 10; + + value = value * (10 + (*pObj)[0]->AttachmentListSize()) / 10; + + value *= (*pObj)[idx]->data.objectStatus; + } + // default + else + { + value = (*pObj)[idx]->data.objectStatus * Item[pObj->usItem].ubCoolness; + } + + return value; +} + +// how much ammo does this item need? +UINT32 GetNeededTotalAmmo( UINT16 usItem ) +{ + // determine what amount of ammo is deemed 'enough' for this gun + return max(gGameExternalOptions.usMilitiaAmmo_Min, min(gGameExternalOptions.usMilitiaAmmo_Max, gGameExternalOptions.usMilitiaAmmo_OptimalMagCount * Weapon[ usItem ].ubMagSize)); +} + +// special version of ItemFitness() that takes ammo count into consideration +UINT32 ItemFitness_WithAmmo( OBJECTTYPE* pObj, UINT8 idx, UINT32 uiBullets ) +{ + return (ItemFitness(pObj, idx) * uiBullets) / max(1, GetNeededTotalAmmo(pObj->usItem)); +} + +// retreives the next free slot in pWorldItem (we want to keep it short and not add too many items) +UINT32 GetNextFreeIndex( WORLDITEM* pWorldItem, UINT32 uiMaxCount, UINT32 uiCount ) +{ + for( ; uiCount < uiMaxCount; ++uiCount ) + { + if( !pWorldItem[ uiCount ].fExists ) + return uiCount; + } + + return -1; +} + +// These are the things we are searching for +enum { + SI_HELMET = 0, + SI_VEST, + SI_LEGS, + SI_FACE1, + SI_FACE2, + SI_GASMASK, + SI_MELEE, + SI_GRENADE, + SI_GUN, + SI_LAUNCHER, + + SI_MAX, +} searchItem; + +// this structis used to store the location of the best item of a category, we use it to later easily retrieve it from the sector inventory +struct ItemSearchStruct { + ItemSearchStruct::ItemSearchStruct() : found(FALSE), done(FALSE), pos(0), slot(0), soldierslot(0), val(0) {} + BOOLEAN found; // has any item been found for this? + BOOLEAN done; // are we done evaluating this item? + UINT32 pos; // position in pWorldItem of this object + UINT8 slot; // slot of the specific object on the position in pWorldItem + UINT16 soldierslot; // slot on which this item should be equipped + UINT32 val; // the fitness value of this object +}; + +// evaluate an object an remember it if it is the best so far +void EvaluateObjForItem( WORLDITEM* pWorldItem, OBJECTTYPE* pObj, UINT32 uiCount, ItemSearchStruct* pSi ) +{ + // safety first + if ( !pWorldItem || !pObj || !pSi ) + // THIS SHOULDN'T HAPPEN! + return; + + for(UINT8 i = 0; i < pObj->ubNumberOfObjects; ++i) + { + UINT32 fitness = ItemFitness(pObj, i); + if ( !pSi->found || fitness > pSi->val ) + { + pSi->found = TRUE; + pSi->pos = uiCount; + pSi->slot = i; + pSi->val = fitness; + } + } +} + +// special version of EvaluateObjForItem(): we take the existing ammo count into consideration +void EvaluateObjForItem_WithAmmo( WORLDITEM* pWorldItem, OBJECTTYPE* pObj, UINT32 uiCount, ItemSearchStruct* pSi, UINT32 uiBullets ) +{ + // safety first + if ( !pWorldItem || !pObj || !pSi ) + // THIS SHOULDN'T HAPPEN! + return; + + for(UINT8 i = 0; i < pObj->ubNumberOfObjects; ++i) + { + UINT32 fitness = ItemFitness_WithAmmo(pObj, i, uiBullets); + if ( !pSi->found || fitness > pSi->val ) + { + pSi->found = TRUE; + pSi->pos = uiCount; + pSi->slot = i; + pSi->val = fitness; + } + } +} + +// forward declaration for default parameter +// if pSi has an entry, move gun from pWorldItem into pp +void SearchItemRetrieval( WORLDITEM* pWorldItem, ItemSearchStruct* pSi, SOLDIERCREATE_STRUCT *pp, UINT8 usTake = 1 ); + +// if pSi has an entry, move gun from pWorldItem into pp +void SearchItemRetrieval( WORLDITEM* pWorldItem, ItemSearchStruct* pSi, SOLDIERCREATE_STRUCT *pp, UINT8 usTake ) +{ + if ( pSi->found && !pSi->done ) + { + UINT8 usRealTake = min(usTake, pWorldItem[ pSi->pos ].object.ubNumberOfObjects); + pWorldItem[ pSi->pos ].object.MoveThisObjectTo(gTempObject, usRealTake ); + + for ( UINT8 i = 0; i < usRealTake; ++i ) + gTempObject[i]->data.sObjectFlag |= TAKEN_BY_MILITIA; + + pp->Inv[ pSi->soldierslot ] = gTempObject; + + if ( pWorldItem[ pSi->pos ].object.ubNumberOfObjects < 1 ) + { + RemoveItemFromPool(pWorldItem[ pSi->pos ].sGridNo, (pSi->pos), pWorldItem[ pSi->pos ].ubLevel); + pWorldItem[ pSi->pos ].fExists = FALSE; + } + } + + pSi->done = TRUE; +} + +typedef std::map AmmoType_BulletCountMap; // used to count how many bullets of an ammotype we have +typedef std::map Calibre_BulletCountMap; // this map stores a map containing all the different ammotypes and how many bulltes we have for them + +struct LauncherHelpStruct { + LauncherHelpStruct::LauncherHelpStruct() : fNeedsAmmo(TRUE), ammocount(0) {} + BOOLEAN fNeedsAmmo; // the launcher isn't singleshot (LAW), it needs ammo + UINT16 ammocount; // so much ammo found for this +}; + +typedef std::map LauncherHelpMap; + +// how many bullets does this map have? (used for one caliber each) +UINT32 GetTotalCalibreAmmo( AmmoType_BulletCountMap* pAmmoBulletMap ) +{ + UINT32 bullets = 0; + AmmoType_BulletCountMap::iterator ammotype_itend = pAmmoBulletMap->end(); + for( AmmoType_BulletCountMap::iterator ammotype_it = pAmmoBulletMap->begin(); ammotype_it != ammotype_itend; ++ammotype_it ) + { + bullets += (*ammotype_it).second; + } + + return bullets; +} + +// are there enough bullets for the gun? +BOOLEAN EnoughBulletsForGun( UINT16 usItem, AmmoType_BulletCountMap* pAmmoBulletMap ) +{ + if ( !pAmmoBulletMap || !(Item[usItem].usItemClass & IC_GUN) ) + return FALSE; + + // for now, we are satisfied if we have enough bullets for at least 2 magazines/at least 10 bullets/max 200 bullets. This could be externalised... + if ( GetTotalCalibreAmmo(pAmmoBulletMap) >= GetNeededTotalAmmo(usItem) ) + return TRUE; + + return FALSE; +} + +// spawn fitting ammo for pObj from pAmmoBulletMap into pp +void SpawnFittingAmmo(SOLDIERCREATE_STRUCT *pp, OBJECTTYPE* pObj, AmmoType_BulletCountMap* pAmmoBulletMap, BOOLEAN fUseAll ) +{ + if ( !pObj || !pAmmoBulletMap || !(Item[pObj->usItem].usItemClass & IC_GUN) ) + return; + + BOOLEAN firstmag = TRUE; + OBJECTTYPE newMag; + UINT16 magsize = GetMagSize(pObj); + UINT32 neededbullets = GetNeededTotalAmmo(pObj->usItem); + if ( fUseAll ) + neededbullets = GetTotalCalibreAmmo(pAmmoBulletMap); + + // we simply use up the ammotypes one after another. Until people complain, and this gets more complicated ;-) + AmmoType_BulletCountMap::iterator ammotype_itend = pAmmoBulletMap->end(); + for( AmmoType_BulletCountMap::iterator ammotype_it = pAmmoBulletMap->begin(); ammotype_it != ammotype_itend; ++ammotype_it ) + { + if ( (*ammotype_it).second > 0 ) + { + UINT8 ammotype = (*ammotype_it).first; + UINT16 magitem = FindReplacementMagazine( Weapon[ pObj->usItem ].ubCalibre, magsize, ammotype ); + + while ( neededbullets > 0 ) + { + UINT16 deductedbullets = min(neededbullets, min(magsize, (*ammotype_it).second)); + + (*ammotype_it).second -= deductedbullets; + neededbullets -= deductedbullets; + + // the first mag goes right into the gun + if ( firstmag ) + { + firstmag = FALSE; + + (*pObj)[0]->data.gun.ubGunAmmoType = ammotype; + (*pObj)[0]->data.gun.ubGunShotsLeft = deductedbullets; + } + else + { + // create ammo in inventory + CreateAmmo(magitem, &gTempObject, deductedbullets); + gTempObject[0]->data.sObjectFlag |= TAKEN_BY_MILITIA; + PlaceObjectInSoldierCreateStruct( pp, &gTempObject ); + } + + // if we have as many bullets as we wanted, get out of here + if ( !neededbullets ) + return; + + // if we used all bullets of this ammotype, go to the next one + if ( !(*ammotype_it).second ) + break; + } + } + } +} + +void MoveOneMilitiaEquipmentSet(INT16 sSourceX, INT16 sSourceY, INT16 sTargetX, INT16 sTargetY, INT8 bSoldierClass) +{ + BOOLEAN fReturn = FALSE; + UINT32 uiTotalNumberOfRealItems = 0; + UINT32 uiNumOriginalItems = 0; + WORLDITEM* pWorldItem = NULL; + SOLDIERCREATE_STRUCT tmp; + UINT32 uiCount = 0; + INT32 dummygridno = NOWHERE; + + if ( !gGameExternalOptions.fMilitiaUseSectorInventory ) + return; + + TakeMilitiaEquipmentfromSector(sSourceX, sSourceY, 0, &tmp, bSoldierClass); + + // open target sector inv + if( ( gWorldSectorX == sTargetX )&&( gWorldSectorY == sTargetY ) && (gbWorldSectorZ == 0 ) ) + { + uiTotalNumberOfRealItems = guiNumWorldItems; + pWorldItem = gWorldItems; + } + else + { + // not loaded, load + // get total number, visable and invisible + fReturn = GetNumberOfWorldItemsFromTempItemFile( sTargetX, sTargetY, 0, &( uiTotalNumberOfRealItems ), TRUE ); + Assert( fReturn ); + + if( uiTotalNumberOfRealItems > 0 ) + { + // allocate space for the list + pWorldItem = new WORLDITEM[ uiTotalNumberOfRealItems ]; + + // now load into mem + LoadWorldItemsFromTempItemFile( sTargetX, sTargetY, 0, pWorldItem ); + } + } + + uiNumOriginalItems = uiTotalNumberOfRealItems; + + // we note the last item existing in the inventory (but not ammo, as we delete those). We use this to assess how much we really need to increase the inventory + UINT32 existingitemsfound = 0; + + for( uiCount = 0; uiCount < uiTotalNumberOfRealItems; ++uiCount ) // ... for all items in the world ... + { + if( pWorldItem[ uiCount ].fExists ) // ... if item exists ... + { + ++existingitemsfound; + dummygridno = pWorldItem[ uiCount ].sGridNo; + } + } + + if ( dummygridno == NOWHERE ) + { + dummygridno = RandomGridNo(); + + if ( dummygridno == NOWHERE ) + return; + } + + /////////////////////////////// ADD ITEMS FROM STRUCT TO SECTOR ///////////////////////////////////////////////////// + // We will now add the items from tmp to the target sector + // first, determine how many items we added, and add an increased copy of pWorldItem + UINT16 numnewitems = 0; + UINT8 invsize = tmp.Inv.size(); + for (UINT8 i = 0; i < invsize; ++i) + { + if ( tmp.Inv[ i ].exists() ) + ++numnewitems; + } + + // new total size needed is number of existing items found + number of needed ammo crate objects, but at least uiTotalNumberOfRealItems: + UINT32 uiNewInvSize = max(uiTotalNumberOfRealItems, existingitemsfound + numnewitems); + + // create a bigger inventory wit big enough size + WORLDITEM* pWorldItem_tmp = new WORLDITEM[ uiNewInvSize ]; + + // copy over old inventory + for( uiCount = 0; uiCount < uiTotalNumberOfRealItems; ++uiCount ) + { + pWorldItem_tmp[uiCount] = pWorldItem[uiCount]; + } + + uiCount = 0; + + // now add the items from the struct + for (UINT8 i = 0; i < invsize; ++i) + { + // only add existing items that were taken by militia (it can happen that we generate fitting ammo, do not take that) + if ( tmp.Inv[ i ].exists() && tmp.Inv[ i ][0]->data.sObjectFlag & TAKEN_BY_MILITIA ) + { + uiCount = GetNextFreeIndex( pWorldItem_tmp, uiNewInvSize, uiCount ); + + if ( uiCount < 0 ) + { + // Error! this shouldn't happen! + return; + } + + pWorldItem_tmp[uiCount].fExists = TRUE; + pWorldItem_tmp[uiCount].sGridNo = dummygridno; + pWorldItem_tmp[uiCount].ubLevel = 0; + pWorldItem_tmp[uiCount].usFlags = WORLD_ITEM_REACHABLE; + pWorldItem_tmp[uiCount].bVisible = 1; + pWorldItem_tmp[uiCount].bRenderZHeightAboveLevel = 0; + pWorldItem_tmp[uiCount].soldierID = -1; + pWorldItem_tmp[uiCount].object = tmp.Inv[ i ]; + } + } + + // use the new map + uiTotalNumberOfRealItems = uiNewInvSize; + delete[] pWorldItem; + pWorldItem = pWorldItem_tmp; + /////////////////////////////// ADD ITEMS FROM STRUCT TO SECTOR ///////////////////////////////////////////////////// + + // save the changed intentory + // open sector inv + if( ( gWorldSectorX == sTargetX )&&( gWorldSectorY == sTargetY ) && (gbWorldSectorZ == 0 ) ) + { + guiNumWorldItems = uiTotalNumberOfRealItems; + gWorldItems = pWorldItem; + } + else + { + //Save the Items to the the file + SaveWorldItemsToTempItemFile( sTargetX, sTargetY, 0, uiTotalNumberOfRealItems, pWorldItem ); + } +} + +void MoveMilitiaEquipment(INT16 sSourceX, INT16 sSourceY, INT16 sTargetX, INT16 sTargetY, UINT8 usElites, UINT8 usRegulars, UINT8 usGreens) +{ + // atm there is no class-specific selection, but that might change in the future + for (UINT8 i = 0; i < usElites; ++i) + { + MoveOneMilitiaEquipmentSet( sSourceX, sSourceY, sTargetX, sTargetY, SOLDIER_CLASS_ELITE_MILITIA); + } + + for (UINT8 i = 0; i < usRegulars; ++i) + { + MoveOneMilitiaEquipmentSet( sSourceX, sSourceY, sTargetX, sTargetY, SOLDIER_CLASS_REG_MILITIA); + } + + for (UINT8 i = 0; i < usGreens; ++i) + { + MoveOneMilitiaEquipmentSet( sSourceX, sSourceY, sTargetX, sTargetY, SOLDIER_CLASS_GREEN_MILITIA); + } +} + +void TakeMilitiaEquipmentfromSector( INT16 sMapX, INT16 sMapY, INT8 sMapZ, SOLDIERCREATE_STRUCT *pp, INT8 bSoldierClass ) +{ + BOOLEAN fReturn = FALSE; + UINT32 uiTotalNumberOfRealItems = 0; + WORLDITEM* pWorldItem = NULL; + UINT32 uiCount = 0; + INT32 dummygridno = NOWHERE; // this gridno will be the new position of items we create (ammo crates) + BOOLEAN fNightTime = NightTime(); + ItemSearchStruct si[SI_MAX]; // this struct stores the necessary data on the items we find during our search + Calibre_BulletCountMap calibrebulletountmap; // a struct that saves all ammo we will find + UINT32 existingitemsfound = 0; // a counter that stores the number of really existing items in the inventory + UINT16 numcrates = 0; + OBJECTTYPE newObj; + BOOLEAN fSearchForAmmo = TRUE; + UINT16 usLauncherItem = 0; + UINT8 usLauncherAmmoLeftToTake = 0; + + if ( !gGameExternalOptions.fMilitiaUseSectorInventory ) + return; + + // open sector inv + if( ( gWorldSectorX == sMapX )&&( gWorldSectorY == sMapY ) && (gbWorldSectorZ == sMapZ ) ) + { + uiTotalNumberOfRealItems = guiNumWorldItems; + pWorldItem = gWorldItems; + } + else + { + // not loaded, load + // get total number, visable and invisible + fReturn = GetNumberOfWorldItemsFromTempItemFile( sMapX, sMapY, ( INT8 )( sMapZ ), &( uiTotalNumberOfRealItems ), FALSE ); + Assert( fReturn ); + + if( uiTotalNumberOfRealItems > 0 ) + { + // allocate space for the list + pWorldItem = new WORLDITEM[ uiTotalNumberOfRealItems ]; + + if ( !uiTotalNumberOfRealItems ) + return; + + // now load into mem + LoadWorldItemsFromTempItemFile( sMapX, sMapY, ( INT8 ) ( sMapZ ), pWorldItem ); + } + } + + std::vector > blarg; + for( uiCount = 0; uiCount < uiTotalNumberOfRealItems; ++uiCount ) + { + blarg.push_back( std::pair(pWorldItem[ uiCount ].fExists, pWorldItem[ uiCount ].object.usItem) ); + } + + // initialize soldier slots + si[SI_HELMET].soldierslot = HELMETPOS; + si[SI_VEST].soldierslot = VESTPOS; + si[SI_LEGS].soldierslot = LEGPOS; + si[SI_FACE1].soldierslot = HEAD1POS; + si[SI_FACE2].soldierslot = HEAD2POS; + si[SI_GASMASK].soldierslot = MEDPOCK1POS; + si[SI_MELEE].soldierslot = BIGPOCK1POS; + si[SI_GRENADE].soldierslot = BIGPOCK2POS; + si[SI_GUN].soldierslot = HANDPOS; + si[SI_LAUNCHER].soldierslot = BIGPOCK2POS; + + // depending on gamesettings, we only have to search for some items + si[SI_HELMET].done = !gGameExternalOptions.fMilitiaUseSectorInventory_Armour; + si[SI_VEST].done = !gGameExternalOptions.fMilitiaUseSectorInventory_Armour; + si[SI_LEGS].done = !gGameExternalOptions.fMilitiaUseSectorInventory_Armour; + si[SI_FACE1].done = !gGameExternalOptions.fMilitiaUseSectorInventory_Face; + si[SI_FACE2].done = !gGameExternalOptions.fMilitiaUseSectorInventory_Face; + si[SI_GASMASK].done = !gGameExternalOptions.fMilitiaUseSectorInventory_Face; + si[SI_MELEE].done = !gGameExternalOptions.fMilitiaUseSectorInventory_Melee; + si[SI_GRENADE].done = !gGameExternalOptions.fMilitiaUseSectorInventory_Grenade; + si[SI_GUN].done = !gGameExternalOptions.fMilitiaUseSectorInventory_Gun; + si[SI_LAUNCHER].done = !gGameExternalOptions.fMilitiaUseSectorInventory_Launcher; + fSearchForAmmo = (gGameExternalOptions.fMilitiaUseSectorInventory_Gun && gGameExternalOptions.fMilitiaUseSectorInventory_Ammo); // we only search for ammo if we also search for guns + + LauncherHelpMap launcherhelpmap; + + // erase existing soldier inventory, if any + for ( UINT8 i = 0; i < pp->Inv.size(); ++i ) + { + //clear items, but only if they have write status. + if( !(pp->Inv[ i ].fFlags & OBJECT_NO_OVERWRITE) ) + { + if ( Item[pp->Inv[ i ].usItem].usItemClass & IC_ARMOUR && gGameExternalOptions.fMilitiaUseSectorInventory_Armour ) + { + if ( pp->Inv[ i ][0]->data.sObjectFlag & TAKEN_BY_MILITIA ) + si[SI_HELMET].done = si[SI_VEST].done = si[SI_LEGS].done = TRUE; + else + DeleteObj(&pp->Inv[ i ]); + } + else if ( Item[pp->Inv[ i ].usItem].usItemClass & IC_FACE && gGameExternalOptions.fMilitiaUseSectorInventory_Face ) + { + if ( pp->Inv[ i ][0]->data.sObjectFlag & TAKEN_BY_MILITIA ) + si[SI_FACE1].done = si[SI_FACE2].done = si[SI_GASMASK].done = TRUE; + else + DeleteObj(&pp->Inv[ i ]); + } + else if ( Item[pp->Inv[ i ].usItem].usItemClass & (IC_BLADE|IC_PUNCH) && gGameExternalOptions.fMilitiaUseSectorInventory_Melee ) + { + if ( pp->Inv[ i ][0]->data.sObjectFlag & TAKEN_BY_MILITIA ) + si[SI_MELEE].done = TRUE; + else + DeleteObj(&pp->Inv[ i ]); + } + else if ( Item[pp->Inv[ i ].usItem].usItemClass & IC_GRENADE && gGameExternalOptions.fMilitiaUseSectorInventory_Grenade ) + { + if ( pp->Inv[ i ][0]->data.sObjectFlag & TAKEN_BY_MILITIA ) + si[SI_GRENADE].done = TRUE; + else + DeleteObj(&pp->Inv[ i ]); + } + else if ( Item[pp->Inv[ i ].usItem].usItemClass & (IC_GUN|IC_AMMO) && gGameExternalOptions.fMilitiaUseSectorInventory_Gun ) + { + if ( pp->Inv[ i ][0]->data.sObjectFlag & TAKEN_BY_MILITIA ) + { + si[SI_GUN].done = TRUE; + fSearchForAmmo = FALSE; + } + else + DeleteObj(&pp->Inv[ i ]); + } + else if ( Item[pp->Inv[ i ].usItem].usItemClass & IC_LAUNCHER && gGameExternalOptions.fMilitiaUseSectorInventory_Launcher ) + { + if ( pp->Inv[ i ][0]->data.sObjectFlag & TAKEN_BY_MILITIA ) + si[SI_LAUNCHER].done = TRUE; + else + DeleteObj(&pp->Inv[ i ]); + } + } + } + + if ( uiTotalNumberOfRealItems == 0 ) + { + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Militia found no items to equip, uses harsh langugage instead!" ); + return; + } + + // Preparation: first, remove all ammo from all guns and merge it into ammo crates. add these crates at the beginning of pWorldItem, sorted by caliber. Remember the total bullet count + // While doing this loop, also check if there exist any guns/helmets/.. etc., If not, then we can simply skip the specific checks by setting the corresponding markers to true + // we can ease up work by already checking for reachability and militia accessabilitiy + // Then we will do a first loop over all items. In here, we will check for which guns and launchers there exists sufficient ammo. + // We will save these calibers in a map, where we also store the amount of ammo available in the crates. + // We will only allow the selection of a gun if + // - it is not jammed + // - its status is over 50% (ini parameter, TODO) + // - there is at least 2 times its magazine capacity available (but check for a minimum of 10 [parameter] bullets and a maximum of 300 [paramter] bullets) + // if possible, select such a gun and add ammo in fitting magazines + // if necessary, reload gun + // if not possible, lower these thresholds + + // Idea: only sort at the very first loop of calling this function (1st militia). After the last loop (last militiaman), resort again to shorten the list + + for( uiCount = 0; uiCount < uiTotalNumberOfRealItems; ++uiCount ) // ... for all items in the world ... + { + if( pWorldItem[ uiCount ].fExists ) // ... if item exists ... + { + dummygridno = pWorldItem[ uiCount ].sGridNo; + + OBJECTTYPE* pObj = &(pWorldItem[ uiCount ].object); // ... get pointer for this item ... + + if ( pObj != NULL && pObj->exists() ) // ... if pointer is not obviously useless ... + { + // this would be the place where we check wether the militia is allowed to pick up an item depending on its soldierclass + + // test wether item is reachable and useable by militia + if ( (pWorldItem[ uiCount ].usFlags & WORLD_ITEM_REACHABLE) && !(pWorldItem[ uiCount ].usFlags & WORLD_ITEM_TABOO_FOR_MILITIA_EQ) ) + { + // armour + if ( Item[pWorldItem[ uiCount ].object.usItem].usItemClass & IC_ARMOUR && (!si[SI_HELMET].done || !si[SI_VEST].done || !si[SI_LEGS].done ) ) + { + switch( Armour[ Item[ pWorldItem[ uiCount ].object.usItem ].ubClassIndex ].ubArmourClass ) + { + case( ARMOURCLASS_HELMET ): + EvaluateObjForItem( pWorldItem, pObj, uiCount, &si[SI_HELMET] ); + break; + case( ARMOURCLASS_VEST ): + EvaluateObjForItem( pWorldItem, pObj, uiCount, &si[SI_VEST] ); + break; + case( ARMOURCLASS_LEGGINGS ): + EvaluateObjForItem( pWorldItem, pObj, uiCount, &si[SI_LEGS] ); + break; + } + } + // face gear + else if ( Item[pWorldItem[ uiCount ].object.usItem].usItemClass & IC_FACE && (!si[SI_FACE1].done || !si[SI_FACE2].done) ) + { + // make sure we dont wear NVGs on the day or sunglasses at night (only posers wear sunglasses at night) + if ( ( !fNightTime && Item[ pWorldItem[ uiCount ].object.usItem ].nightvisionrangebonus > Item[ pWorldItem[ uiCount ].object.usItem ].dayvisionrangebonus ) + || ( fNightTime && Item[ pWorldItem[ uiCount ].object.usItem ].nightvisionrangebonus < Item[ pWorldItem[ uiCount ].object.usItem ].dayvisionrangebonus ) ) + continue; + + // gasmasks are reserved for a special slot and will only be worn if we do not have 2 face items. items that increase our vision (NVGs adn sungooggles) get to slot 1, everything else in 2 + if ( Item[ pWorldItem[ uiCount ].object.usItem ].gasmask ) + EvaluateObjForItem( pWorldItem, pObj, uiCount, &si[SI_GASMASK] ); + else if ( Item[ pWorldItem[ uiCount ].object.usItem ].nightvisionrangebonus || Item[ pWorldItem[ uiCount ].object.usItem ].dayvisionrangebonus ) + EvaluateObjForItem( pWorldItem, pObj, uiCount, &si[SI_FACE1] ); + else + EvaluateObjForItem( pWorldItem, pObj, uiCount, &si[SI_FACE2] ); + } + else if ( Item[pWorldItem[ uiCount ].object.usItem].usItemClass & (IC_BLADE|IC_PUNCH) && !si[SI_MELEE].done ) + { + EvaluateObjForItem( pWorldItem, pObj, uiCount, &si[SI_MELEE] ); + } + else if ( Item[pWorldItem[ uiCount ].object.usItem].usItemClass & IC_GRENADE && !si[SI_GRENADE].done ) + { + // if this is a valid launchable for any launcher we found, update their potential ammocount + LauncherHelpMap::iterator itend = launcherhelpmap.end(); + for (LauncherHelpMap::iterator it = launcherhelpmap.begin(); it != itend; ++it) + { + if ( (*it).second.fNeedsAmmo && ValidLaunchable( pWorldItem[ uiCount ].object.usItem, (*it).first ) ) + (*it).second.ammocount += pWorldItem[ uiCount ].object.ubNumberOfObjects; + } + + // if this item can be thrown, its a hand grenade, thus we will evaluate it + if ( Item[ pWorldItem[ uiCount ].object.usItem ].ubCursor == TOSSCURS ) + EvaluateObjForItem( pWorldItem, pObj, uiCount, &si[SI_GRENADE] ); + } + else if ( Item[pWorldItem[ uiCount ].object.usItem].usItemClass & IC_BOMB ) + { + // if this is a valid launchable for any launcher we found, update their potential ammocount + LauncherHelpMap::iterator itend = launcherhelpmap.end(); + for (LauncherHelpMap::iterator it = launcherhelpmap.begin(); it != itend; ++it) + { + if ( (*it).second.fNeedsAmmo && ValidLaunchable( pWorldItem[ uiCount ].object.usItem, (*it).first ) ) + (*it).second.ammocount += pWorldItem[ uiCount ].object.ubNumberOfObjects; + } + } + // ammo + else if ( Item[pWorldItem[ uiCount ].object.usItem].usItemClass & IC_AMMO && fSearchForAmmo ) + { + UINT16 usItem = pWorldItem[ uiCount ].object.usItem; + UINT16 usMagIndex = Item[usItem].ubClassIndex; + + // we count ammo and then remove it. We will later add it again in the form of ammo crates + UINT8 calibre = Magazine[usMagIndex].ubCalibre; + UINT8 ammotype = Magazine[usMagIndex].ubAmmoType; + UINT32 newammo = 0; + for(INT16 i = 0; i < pObj->ubNumberOfObjects; ++i) + { + newammo += (*pObj)[i]->data.ubShotsLeft; + } + + // add ammo to our map + if ( calibrebulletountmap[calibre][ammotype] ) + calibrebulletountmap[calibre][ammotype] += newammo; + else + calibrebulletountmap[calibre][ammotype] = newammo; + + // delete items + RemoveItemFromPool(pWorldItem[uiCount].sGridNo,(uiCount),pWorldItem[uiCount].ubLevel); + pWorldItem[ uiCount ].fExists = FALSE; + } + // gun + else if ( Item[pWorldItem[ uiCount ].object.usItem].usItemClass & IC_GUN && !si[SI_GUN].done ) + { + EvaluateObjForItem( pWorldItem, pObj, uiCount, &si[SI_GUN] ); + + // we count ammo and then remove it. We will later add it again in the form of ammo crates + UINT8 calibre = Weapon[ pWorldItem[ uiCount ].object.usItem ].ubCalibre; + // guns in a stack can have different ammo, thus he need to add it individually + for(INT16 i = 0; i < pObj->ubNumberOfObjects; ++i) + { + UINT8 ammotype = (*pObj)[i]->data.gun.ubGunAmmoType; + + if ( (*pObj)[i]->data.gun.ubGunShotsLeft ) + { + // add ammo to our map + if ( calibrebulletountmap[calibre][ammotype] ) + calibrebulletountmap[calibre][ammotype] += (*pObj)[i]->data.gun.ubGunShotsLeft; + else + calibrebulletountmap[calibre][ammotype] = (*pObj)[i]->data.gun.ubGunShotsLeft; + + // we empty the gun + (*pObj)[i]->data.gun.ubGunShotsLeft = 0; + } + } + } + // launcher + else if ( Item[pWorldItem[ uiCount ].object.usItem].usItemClass & IC_LAUNCHER && !si[SI_LAUNCHER].done ) + { + // add launcher type to our launcher map + LauncherHelpMap::iterator fnd = launcherhelpmap.find( pWorldItem[ uiCount ].object.usItem ); + if ( fnd == launcherhelpmap.end() ) + { + LauncherHelpStruct tmp; + tmp.fNeedsAmmo = !Item[pWorldItem[ uiCount ].object.usItem].singleshotrocketlauncher; + launcherhelpmap[ pWorldItem[ uiCount ].object.usItem ] = tmp; + } + } + } + } + } + } + + // if the dummy GridNo is still NOWHERE, we did not find a single reachable item. So we can just get out of here (no need to make ammo crates, as we did not find any ammo either) + if ( dummygridno == NOWHERE ) + { + dummygridno = RandomGridNo(); + + if ( dummygridno == NOWHERE ) + { + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Militia found no items to equip, uses harsh langugage instead!" ); + return; + } + } + + SearchItemRetrieval( pWorldItem, &si[SI_HELMET], pp ); + SearchItemRetrieval( pWorldItem, &si[SI_VEST], pp ); + SearchItemRetrieval( pWorldItem, &si[SI_LEGS], pp ); + SearchItemRetrieval( pWorldItem, &si[SI_FACE1], pp ); + SearchItemRetrieval( pWorldItem, &si[SI_FACE2], pp ); + + // special: of there is a free face slot and we have a gasmask, equip it there + if ( si[SI_GASMASK].found ) + { + if ( !si[SI_FACE1].found ) + si[SI_GASMASK].soldierslot = si[SI_FACE1].soldierslot; + else if ( !si[SI_FACE2].found ) + si[SI_GASMASK].soldierslot = si[SI_FACE2].soldierslot; + } + + SearchItemRetrieval( pWorldItem, &si[SI_GASMASK], pp ); + SearchItemRetrieval( pWorldItem, &si[SI_MELEE], pp ); + SearchItemRetrieval( pWorldItem, &si[SI_GRENADE], pp, 2 ); // if on a stack, we are willing to take 2 grenades at once + + // we investigate the launchers we found, and their ammo. + // It can happen that we miss launcher ammo if it coccured earlier than a launcher, but this cannot be helped, as I dont want to add an extra loop over all items for this + if ( gGameExternalOptions.fMilitiaUseSectorInventory_Launcher && !si[SI_LAUNCHER].done ) + { + LauncherHelpMap::iterator itend = launcherhelpmap.end(); + for (LauncherHelpMap::iterator it = launcherhelpmap.begin(); it != itend; ++it) + { + // we pick the first launcher that needs no ammo or has enough ammo + if ( !(*it).second.fNeedsAmmo || (*it).second.ammocount > 3 ) + { + usLauncherItem = (*it).first; + usLauncherAmmoLeftToTake = (*it).second.ammocount ? 3 : 0; + } + } + } + + // if found a gun, check wether we have enough ammo for it + if ( si[SI_GUN].found ) + { + UINT8 calibre = Weapon[ pWorldItem[ si[SI_GUN].pos ].object.usItem ].ubCalibre; + + Calibre_BulletCountMap::iterator calibremap_it = calibrebulletountmap.find( calibre ); + if ( !fSearchForAmmo || calibremap_it != calibrebulletountmap.end() ) + { + // do we have enough ammo form that calibre? + if ( !fSearchForAmmo || EnoughBulletsForGun( pWorldItem[ si[SI_GUN].pos ].object.usItem, &((*calibremap_it).second) ) ) + { + // awesome! then take that gun... + SearchItemRetrieval( pWorldItem, &si[SI_GUN], pp ); + + // ... and give us enough ammo + if ( fSearchForAmmo ) + SpawnFittingAmmo(pp, &(pp->Inv[ HANDPOS ]), &((*calibremap_it).second), FALSE ); + } + // too bad. We need to look for another weapon then + else + { + si[SI_GUN].found = FALSE; + } + } + } + // we didn't find any gun at all. Now what? + else + { + si[SI_GUN].done = TRUE; + } + + // if we found guns, but not enough ammo for our favourite, reset our results. In the next loop, we will also accept lower ammo counts + if ( !si[SI_GUN].done ) + { + si[SI_GUN].found = FALSE; + si[SI_GUN].val = 0; + } + + for( uiCount = 0; uiCount < uiTotalNumberOfRealItems; ++uiCount ) // ... for all items in the world ... + { + if( pWorldItem[ uiCount ].fExists ) // ... if item exists ... + { + ++existingitemsfound; + OBJECTTYPE* pObj = &(pWorldItem[ uiCount ].object); // ... get pointer for this item ... + + if ( pObj != NULL && pObj->exists() ) // ... if pointer is not obviously useless ... + { + // this would be the place where we check wether the militia is allowed to pick up an item depending on its soldierclass + + // test wether item is reachable + if ( (pWorldItem[ uiCount ].usFlags & WORLD_ITEM_REACHABLE) && !(pWorldItem[ uiCount ].usFlags & WORLD_ITEM_TABOO_FOR_MILITIA_EQ) ) + { + // only if we are still looking for a gun + if ( Item[pWorldItem[ uiCount ].object.usItem].usItemClass & IC_GUN && !si[SI_GUN].done ) + { + UINT8 calibre = Weapon[ pWorldItem[ uiCount ].object.usItem ].ubCalibre; + + Calibre_BulletCountMap::iterator calibremap_it = calibrebulletountmap.find( calibre ); + + if ( calibremap_it != calibrebulletountmap.end() ) + { + UINT32 bullets = GetTotalCalibreAmmo( &((*calibremap_it).second) ); + + EvaluateObjForItem_WithAmmo( pWorldItem, pObj, uiCount, &si[SI_GUN], bullets ); + } + // no ammo found... a gun without bullets is useless, ignore this thing + else + { + //EvaluateObjForItem_WithAmmo( pWorldItem, pObj, uiCount, &si[SI_GUN], 0 ); + } + } + else if ( Item[pWorldItem[ uiCount ].object.usItem].usItemClass & IC_AMMO && fSearchForAmmo ) + { + // THIS SHOULDN'T HAPPEN!!! ERROR IF IT DOES!!! + return; + } + else if ( Item[pWorldItem[ uiCount ].object.usItem].usItemClass & IC_LAUNCHER && !si[SI_LAUNCHER].done ) + { + // if launcher is of the type we want, evaluate it + if ( pWorldItem[ uiCount ].object.usItem == usLauncherItem ) + { + EvaluateObjForItem( pWorldItem, pObj, uiCount, &si[SI_LAUNCHER] ); + } + } + else if ( Item[pWorldItem[ uiCount ].object.usItem].usItemClass & (IC_GRENADE|IC_BOMB) && !si[SI_LAUNCHER].done && usLauncherAmmoLeftToTake ) + { + // if launcher is of the type we want, evaluate it + if ( ValidLaunchable(pWorldItem[ uiCount ].object.usItem, usLauncherItem) ) + { + // take item and reduce amount left to take + UINT8 totake = min(usLauncherAmmoLeftToTake, pWorldItem[ uiCount ].object.ubNumberOfObjects); + usLauncherAmmoLeftToTake = max(0, usLauncherAmmoLeftToTake - totake); + + pWorldItem[ uiCount ].object.MoveThisObjectTo(gTempObject, totake); + + gTempObject[0]->data.sObjectFlag |= TAKEN_BY_MILITIA; + + PlaceObjectInSoldierCreateStruct( pp, &gTempObject ); + + if ( pWorldItem[ uiCount ].object.ubNumberOfObjects < 1 ) + { + RemoveItemFromPool(pWorldItem[ uiCount ].sGridNo, uiCount, pWorldItem[ uiCount ].ubLevel); + pWorldItem[ uiCount ].fExists = FALSE; + } + } + } + } + } + } + } + + SearchItemRetrieval( pWorldItem, &si[SI_LAUNCHER], pp ); + + // now that we know which gun to take, do so + // if found a gun, check wether we have enough ammo for it + if ( !si[SI_GUN].done ) + { + if ( si[SI_GUN].found ) + { + UINT8 calibre = Weapon[ pWorldItem[ si[SI_GUN].pos ].object.usItem ].ubCalibre; + + Calibre_BulletCountMap::iterator calibremap_it = calibrebulletountmap.find( calibre ); + if ( !fSearchForAmmo || calibremap_it != calibrebulletountmap.end() ) + { + // do we have enough ammo form that calibre? + if ( !fSearchForAmmo || EnoughBulletsForGun( pWorldItem[ si[SI_GUN].pos ].object.usItem, &((*calibremap_it).second) ) ) + { + // take this gun + SearchItemRetrieval( pWorldItem, &si[SI_GUN], pp ); + + // enough ammo found. Take the correct amount + SpawnFittingAmmo(pp, &(pp->Inv[ HANDPOS ]), &((*calibremap_it).second), FALSE ); + } + else + { + // take this gun + SearchItemRetrieval( pWorldItem, &si[SI_GUN], pp ); + + // not enough ammo... use whatever is left + SpawnFittingAmmo(pp, &(pp->Inv[ HANDPOS ]), &((*calibremap_it).second), TRUE ); + } + } + // no ammo at all... this is bad + else + { + si[SI_GUN].done = TRUE; + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Militia found no gun to equip, uses harsh langugage instead!" ); + } + } + // we didn't find any gun at all. Now what? + else + { + si[SI_GUN].done = TRUE; + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Militia found no gun to equip, uses harsh langugage instead!" ); + } + } + + // if we selected a gun but do not select ammo, we have to spawn fitting ammo the traditional way + if ( gGameExternalOptions.fMilitiaUseSectorInventory_Gun && !gGameExternalOptions.fMilitiaUseSectorInventory_Ammo) + { + UINT16 usGunIndex = pp->Inv[ HANDPOS ].usItem; + if ( usGunIndex ) + { + // check default ammo first... + UINT16 usAmmoIndex = DefaultMagazine( usGunIndex ); + UINT8 ubChanceStandardAmmo = 100; + if ( AmmoTypes[Magazine[ Item[ usAmmoIndex ].ubClassIndex ].ubAmmoType].standardIssue ) + ubChanceStandardAmmo = 80; + else + ubChanceStandardAmmo = 100 - (Random(9) * 9); + + usAmmoIndex = RandomMagazine( &pp->Inv[HANDPOS], ubChanceStandardAmmo, max(Item[usGunIndex].ubCoolness, HighestPlayerProgressPercentage() / 10 + 3 ), pp->ubSoldierClass); + + if ( usAmmoIndex <= 0 ) + usAmmoIndex = DefaultMagazine(usGunIndex); + + //Madd: ensure a minimum # of bullets to make sure enemies don't run out and run away + UINT8 numberofmags = 3 + Random(2); + while ( ( (numberofmags - 1) * Weapon[usGunIndex].ubMagSize ) < 20 ) // each soldier should have at least 20 bullets, ie: 2 9mm 15rd clips, 3 7rd shotgun shells, 4 6rd speedloaders, etc + ++numberofmags; + + CreateItems( usAmmoIndex, 100, numberofmags, &gTempObject ); + gTempObject.fFlags |= OBJECT_UNDROPPABLE; + PlaceObjectInSoldierCreateStruct( pp, &gTempObject ); + } + } + + /////////////////////////////// ADD AMMO CRATES ///////////////////////////////////////////////////// + // add ammo in crates to the inventory + // if we found a gun, we've already deducted mmo for it. Now we add the ammo to the inventory again + // as we do not want to increase the sector inventory size needlessly, we analyzed in the last loop wether there are empty slots - we will fill these up + + // TODO: this does not take into account instances where we will need MULTIPLE stacks of ammo crates + // first determine how many ammocrates we will create + Calibre_BulletCountMap::iterator calibremap_itend = calibrebulletountmap.end(); + for ( Calibre_BulletCountMap::iterator calibremap_it = calibrebulletountmap.begin(); calibremap_it != calibremap_itend; ++calibremap_it ) + { + numcrates += (*calibremap_it).second.size(); + } + + // new total size needed is number of existing items found + number of needed ammo crate objects, but at least uiTotalNumberOfRealItems: + UINT32 uiNewInvSize = max(uiTotalNumberOfRealItems, existingitemsfound + numcrates); + + // create a bigger inventory wit big enough size + WORLDITEM* pWorldItem_tmp = new WORLDITEM[ uiNewInvSize ]; + + // copy over old inventory + for( uiCount = 0; uiCount < uiTotalNumberOfRealItems; ++uiCount ) + { + pWorldItem_tmp[uiCount] = pWorldItem[uiCount]; + } + + // set counter back to 0, as we will loop over the inventory in search for empty slots + uiCount = 0; + + // add ammo crates + for ( Calibre_BulletCountMap::iterator calibremap_it = calibrebulletountmap.begin(); calibremap_it != calibremap_itend; ++calibremap_it ) + { + UINT8 calibre = (*calibremap_it).first; + + AmmoType_BulletCountMap::iterator ammotype_itend = (*calibremap_it).second.end(); + for( AmmoType_BulletCountMap::iterator ammotype_it = (*calibremap_it).second.begin(); ammotype_it != ammotype_itend; ++ammotype_it ) + { + UINT8 ammotype = (*ammotype_it).first; + UINT32 bullets = (*ammotype_it).second; + + if ( bullets > 0 ) + { + // find a fitting crate + for(int iLoop = 0; iLoop < MAXITEMS; ++iLoop) + { + if (Item[iLoop].usItemClass == 0) + break; //no more valid items after this point + + //if ammo crate && calibers match && Ammo Types match + if(Item[iLoop].usItemClass == IC_AMMO && Magazine[Item[iLoop].ubClassIndex].ubMagType == AMMO_CRATE && Magazine[Item[iLoop].ubClassIndex].ubCalibre == calibre && Magazine[Item[iLoop].ubClassIndex].ubAmmoType == ammotype ) + { + // We determine how many ammo crates we will need. We create them as a stack, where the first entry might not be full + // Note: if we exceed 255 crates, we would get an overflow, thus the min()-check. + UINT8 neededcrates = min(255, bullets / Magazine[Item[iLoop].ubClassIndex].ubMagSize + 1); + + CreateItems( iLoop, 100, neededcrates, &newObj ); + + bullets -= (neededcrates - 1) * Magazine[Item[iLoop].ubClassIndex].ubMagSize; + newObj[0]->data.ubShotsLeft = bullets; + bullets -= bullets; + + uiCount = GetNextFreeIndex( pWorldItem_tmp, uiNewInvSize, uiCount ); + if ( uiCount < 0 ) + { + // Error! this shouldn't happen! + return; + } + + pWorldItem_tmp[uiCount].fExists = TRUE; + pWorldItem_tmp[uiCount].sGridNo = dummygridno; + pWorldItem_tmp[uiCount].ubLevel = 0; + pWorldItem_tmp[uiCount].usFlags = WORLD_ITEM_REACHABLE; + pWorldItem_tmp[uiCount].bVisible = 1; + pWorldItem_tmp[uiCount].bRenderZHeightAboveLevel = 0; + pWorldItem_tmp[uiCount].soldierID = -1; + pWorldItem_tmp[uiCount].object = newObj; + + break; + } + } + } + + // if there are still bullets, then we did not find a fitting ammo crate. Better tell that to the player + // This can also happen if we'd need over 255 ammo crates + if ( bullets > 0 ) + { + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Found no ammocrate for calibre %d ammotype %d, %d bullets will be lost.", calibre, ammotype, bullets ); + } + } + } + + // use the new map + uiTotalNumberOfRealItems = uiNewInvSize; + delete[] pWorldItem; + pWorldItem = pWorldItem_tmp; + /////////////////////////////// ADD AMMO CRATES ///////////////////////////////////////////////////// + + // save the changed intentory + // open sector inv + if( ( sMapX == gWorldSectorX )&&( gWorldSectorY == sMapY ) && (gbWorldSectorZ == sMapZ ) ) + { + guiNumWorldItems = uiTotalNumberOfRealItems; + gWorldItems = pWorldItem; + } + else + { + //Save the Items to the the file + SaveWorldItemsToTempItemFile( sMapX, sMapY, (INT8)sMapZ, uiTotalNumberOfRealItems, pWorldItem ); + } +} +////////////////// Flugente: militia equipment feature /////////////////////////////////// diff --git a/Tactical/Inventory Choosing.h b/Tactical/Inventory Choosing.h index f0e54684..32d2b27c 100644 --- a/Tactical/Inventory Choosing.h +++ b/Tactical/Inventory Choosing.h @@ -119,5 +119,14 @@ enum // Flugente: created separate gun choices for different soldier classes extern ARMY_GUN_CHOICE_TYPE gArmyItemChoices[SOLDIER_GUN_CHOICE_SELECTIONS][MAX_ITEM_TYPES]; +////////////////// Flugente: militia equipment feature /////////////////////////////////// +// move one set of militia equipment between sectors +void MoveOneMilitiaEquipmentSet(INT16 sSourceX, INT16 sSourceY, INT16 sTargetX, INT16 sTargetY, INT8 bSoldierClass); + +// move a number of militia equipment sets between sectors +void MoveMilitiaEquipment(INT16 sSourceX, INT16 sSourceY, INT16 sTargetX, INT16 sTargetY, UINT8 usElites, UINT8 usRegulars, UINT8 usGreens); + +// take militia items from sector +void TakeMilitiaEquipmentfromSector( INT16 sMapX, INT16 sMapY, INT8 sMapZ, SOLDIERCREATE_STRUCT *pp, INT8 bSoldierClass ); #endif \ No newline at end of file diff --git a/Tactical/Item Types.h b/Tactical/Item Types.h index eab72250..03eea876 100644 --- a/Tactical/Item Types.h +++ b/Tactical/Item Types.h @@ -485,7 +485,7 @@ public: INT16 sRepairThreshold; // repair only possible up to this value FLOAT bDirtLevel; // counter for how dirty a gun is - INT32 sObjectFlag; // used to notify of various states that apply to this object, but not the item in general + UINT64 sObjectFlag; // used to notify of various states that apply to this object, but not the item in general }; // Flugente: needed for reading WF maps #define SIZEOF_OBJECTDATA_POD (offsetof(ObjectData, endOfPOD)) @@ -808,7 +808,12 @@ extern OBJECTTYPE gTempObject; #define CORPSE_PANTS_BLUE 0x10000000 //268435456 #define CORPSE_PANTS_BEIGE 0x20000000 //536870912 #define CORPSE_NO_VEST 0x40000000 //1073741824 // corpse has no vest item (it has been either taken or been destroyed) -#define CORPSE_NO_PANTS_AND_TRIPWIRE_ACTIVATED 0x80000000 //2147483648 // corpse has no pants item/for tripwire activation (gets set and unset when activating tripwire) +#define CORPSE_NO_PANTS 0x80000000 //2147483648 // corpse has no pants item/for tripwire activation (gets set and unset when activating tripwire) + +#define TRIPWIRE_ACTIVATED 0x0000000100000000 // 4294967296 +#define TAKEN_BY_MILITIA 0x0000000200000000 // 8589934592 +//#define CORPSE_PANTS_TAN 0x0000000400000000 // 17179869184 +//#define CORPSE_PANTS_BLACK 0x0000000800000000 // 34359738368 // Flugente TODO 2012-09-17: next time we break savegame compatibility, extend the flagmasks from UINT32 to UINT64. I didn't do it this time (see double-used flag above), as we try to minimise those breaks. But it is needed. // ---------------------------------------------------------------- diff --git a/Tactical/Militia Control.cpp b/Tactical/Militia Control.cpp index a0635ac3..1d90cd98 100644 --- a/Tactical/Militia Control.cpp +++ b/Tactical/Militia Control.cpp @@ -153,6 +153,9 @@ void ResetMilitia() // a flag for autoresolve if different initialization or destruction is desired. guiCurrentScreen = GAME_SCREEN; + // Flugente: cause all militia whose equipment is from this sector to drop it + TeamDropAll( MILITIA_TEAM ); + RemoveMilitiaFromTactical(); ubNumGreen = MilitiaInSectorOfRank(gWorldSectorX, gWorldSectorY, GREEN_MILITIA); ubNumReg = MilitiaInSectorOfRank(gWorldSectorX, gWorldSectorY, REGULAR_MILITIA); diff --git a/Tactical/Overhead.cpp b/Tactical/Overhead.cpp index f6965874..676ca9d8 100644 --- a/Tactical/Overhead.cpp +++ b/Tactical/Overhead.cpp @@ -10219,3 +10219,34 @@ void HandleSurrenderOffer( SOLDIERTYPE* pSoldier ) wcscpy( gzUserDefinedButton[3], TacticalStr[ PRISONER_TALK_STR ] ); DoMessageBox( MSG_BOX_BASIC_MEDIUM_BUTTONS, TacticalStr[ PRISONER_OFFER_SURRENDER ], guiCurrentScreen, MSG_BOX_FLAG_GENERIC_FOUR_BUTTONS, PrisonerSurrenderMessageBoxCallBack, NULL ); } + +void TeamDropAll(UINT8 bTeam) +{ + if ( bTeam > PLAYER_PLAN ) + return; + + // not if there is a battle going on, or this team is hostile to us + if ( (gTacticalStatus.uiFlags & INCOMBAT ) ) + return; + + if ( gTacticalStatus.Team[ gbPlayerNum ].bSide != gTacticalStatus.Team[ bTeam ].bSide ) + return; + + SOLDIERTYPE *pSoldier; + +#ifdef JA2BETAVERSION + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_TESTVERSION, L"Team %d drops all items for inspection!", bTeam ); +#endif + + UINT32 uiCnt = 0; + UINT32 firstid = gTacticalStatus.Team[ bTeam ].bFirstID; + UINT32 lastid = gTacticalStatus.Team[ bTeam ].bLastID; + for ( uiCnt = firstid, pSoldier = MercPtrs[ uiCnt ]; uiCnt <= lastid; ++uiCnt, ++pSoldier) + { + // if soldier is in the current sector, drop all equipment (that has the TAKEN_BY_MILITIA-flag set) + if( pSoldier->bActive && ( pSoldier->sSectorX == gWorldSectorX ) && ( pSoldier->sSectorY == gWorldSectorY ) && ( pSoldier->bSectorZ == gbWorldSectorZ) ) + { + pSoldier->DropSectorEquipment(); + } + } +} diff --git a/Tactical/Overhead.h b/Tactical/Overhead.h index 7c184b37..7a9e8cd6 100644 --- a/Tactical/Overhead.h +++ b/Tactical/Overhead.h @@ -408,4 +408,6 @@ STR16 GetNewTraitStr(UINT8 aTrait); // Flugente: deal with the insane new trait void HandleSurrenderOffer( SOLDIERTYPE* pSoldier ); // Flugente: offer the enemy the chance to surrender +void TeamDropAll(UINT8 bTeam); + #endif \ No newline at end of file diff --git a/Tactical/Rotting Corpses.cpp b/Tactical/Rotting Corpses.cpp index e072b121..09fbc5c9 100644 --- a/Tactical/Rotting Corpses.cpp +++ b/Tactical/Rotting Corpses.cpp @@ -2055,7 +2055,7 @@ void TakeCorpse( SOLDIERTYPE *pSoldier, INT32 sGridNo, INT8 bLevel ) gTempObject[0]->data.sObjectFlag |= CORPSE_NO_VEST; if ( pCorpse->def.usFlags & ROTTING_CORPSE_NO_PANTS ) - gTempObject[0]->data.sObjectFlag |= CORPSE_NO_PANTS_AND_TRIPWIRE_ACTIVATED; + gTempObject[0]->data.sObjectFlag |= CORPSE_NO_PANTS; #ifdef ENABLE_ZOMBIES if ( pCorpse->def.usFlags & ROTTING_CORPSE_NEVER_RISE_AGAIN ) @@ -2316,7 +2316,7 @@ BOOLEAN AddCorpseFromObject(OBJECTTYPE* pObj, INT32 sGridNo, INT8 bLevel ) if ( (*pObj)[0]->data.sObjectFlag & CORPSE_NO_VEST ) gRottingCorpse[ iCorpseID ].def.usFlags |= ROTTING_CORPSE_NO_VEST; - if ( (*pObj)[0]->data.sObjectFlag & CORPSE_NO_PANTS_AND_TRIPWIRE_ACTIVATED ) + if ( (*pObj)[0]->data.sObjectFlag & CORPSE_NO_PANTS ) gRottingCorpse[ iCorpseID ].def.usFlags |= ROTTING_CORPSE_NO_PANTS; #ifdef ENABLE_ZOMBIES diff --git a/Tactical/ShopKeeper Interface.cpp b/Tactical/ShopKeeper Interface.cpp index cf82e8f7..bab9e7f8 100644 --- a/Tactical/ShopKeeper Interface.cpp +++ b/Tactical/ShopKeeper Interface.cpp @@ -3241,6 +3241,38 @@ void DrawHatchOnInventory( UINT32 uiSurface, UINT16 usPosX, UINT16 usPosY, UINT1 UnLockVideoSurface( uiSurface ); } +void DrawHatchOnInventory_MilitiaAccess( UINT32 uiSurface, UINT16 usPosX, UINT16 usPosY, UINT16 usWidth, UINT16 usHeight, UINT16 usColor ) +{ + UINT8 *pDestBuf; + UINT32 uiDestPitchBYTES; + SGPRect ClipRect; + static UINT8 Pattern[8][8] = + { + 1,0,0,0,0,0,0,0, + 0,0,0,0,0,1,0,0, + 0,0,1,0,0,0,0,0, + 0,0,0,0,0,0,0,1, + 0,0,0,0,1,0,0,0, + 0,1,0,0,0,0,0,0, + 0,0,0,0,0,0,1,0, + 0,0,0,1,0,0,0,0 + }; + // CHRISL: + ClipRect.iLeft = usPosX-1; + ClipRect.iRight = usPosX + usWidth-1; + ClipRect.iTop = usPosY; + ClipRect.iBottom = usPosY + usHeight; + + pDestBuf = LockVideoSurface( uiSurface, &uiDestPitchBYTES ); + if(usColor == 0){ + Blt16BPPBufferPixelateRect( (UINT16*)pDestBuf, uiDestPitchBYTES, &ClipRect, Pattern ); + } + else{ + Blt16BPPBufferPixelateRectWithColor( (UINT16*)pDestBuf, uiDestPitchBYTES, &ClipRect, Pattern, usColor ); + } + UnLockVideoSurface( uiSurface ); +} + UINT32 CalcShopKeeperItemPrice( BOOLEAN fDealerSelling, BOOLEAN fUnitPriceOnly, UINT16 usItemID, FLOAT dModifier, OBJECTTYPE *pItemObject ) { diff --git a/Tactical/ShopKeeper Interface.h b/Tactical/ShopKeeper Interface.h index f23e759a..c869709b 100644 --- a/Tactical/ShopKeeper Interface.h +++ b/Tactical/ShopKeeper Interface.h @@ -121,6 +121,7 @@ void EnterShopKeeperInterfaceScreen( UINT8 ubArmsDealer ); void EnableDisableShopkeeperButtons(UINT32 uiCurrentItemDescriptionScreen, int bpAction); void DrawHatchOnInventory( UINT32 uiSurface, UINT16 usPosX, UINT16 usPosY, UINT16 usWidth, UINT16 usHeight, UINT16 usColor = 0 ); +void DrawHatchOnInventory_MilitiaAccess( UINT32 uiSurface, UINT16 usPosX, UINT16 usPosY, UINT16 usWidth, UINT16 usHeight, UINT16 usColor = 0 ); BOOLEAN ShouldSoldierDisplayHatchOnItem( UINT8 ubProfileID, INT16 sSlotNum ); INT8 AddItemToPlayersOfferArea( UINT8 ubProfileID, INVENTORY_IN_SLOT* pInvSlot, INT16 bSlotIdInOtherLocation ); void ConfirmToDeductMoneyFromPlayersAccountMessageBoxCallBack( UINT8 bExitValue ); diff --git a/Tactical/Soldier Control.cpp b/Tactical/Soldier Control.cpp index 3273364a..fb9b2001 100644 --- a/Tactical/Soldier Control.cpp +++ b/Tactical/Soldier Control.cpp @@ -112,6 +112,7 @@ #include "IMP Skill Trait.h" // added by Flugente #include "Food.h" // added by Flugente +#include "Tactical Save.h" // added by Flugente for AddItemsToUnLoadedSector() //forward declarations of common classes to eliminate includes class OBJECTTYPE; @@ -14717,6 +14718,11 @@ INT8 SOLDIERTYPE::GetUniformType() // is our equipment too good for a soldier? BOOLEAN SOLDIERTYPE::EquipmentTooGood( BOOLEAN fCloselook ) { + // if militia is equipped from sector inventory(and thu by the player itself), then its item selection is no longer bound to any progress calculation + // we thus canno check for equipment - the only way to find out is to look at this guy sharply, and to eventually realise that this gear did not come from the player + if ( gGameExternalOptions.fMilitiaUseSectorInventory && this->IsAssassin() ) + return FALSE; + // check the guns in our hands and rifle sling // alert if we have more than 2, any of them has too much attachments or they are way too cool UINT8 numberofguns = 0; @@ -15710,6 +15716,65 @@ BOOLEAN SOLDIERTYPE::UpdateMultiTurnAction() return TRUE; } +void SOLDIERTYPE::DropSectorEquipment() +{ + OBJECTTYPE* pObj = NULL; + UINT8 size = this->inv.size(); + + if ( ( this->sSectorX == gWorldSectorX ) && ( this->sSectorY == gWorldSectorY ) && ( this->bSectorZ == gbWorldSectorZ) ) + { + for ( UINT8 cnt = 0; cnt < size; ++cnt ) + { + pObj = &( this->inv[ cnt ] ); + + if ( pObj->exists() ) + { + // Check if it's supposed to be dropped + if ( !( (*pObj).fFlags & OBJECT_UNDROPPABLE ) && !(Item[ pObj->usItem ].defaultundroppable ) && (*pObj)[0]->data.sObjectFlag & TAKEN_BY_MILITIA ) + { + (*pObj)[0]->data.sObjectFlag &= ~TAKEN_BY_MILITIA; + + // if we are not replacing ammo, unload gun prior to dropping it + if ( !gGameExternalOptions.fMilitiaUseSectorInventory_Ammo && Item[ pObj->usItem ].usItemClass & IC_GUN ) + (*pObj)[0]->data.gun.ubGunShotsLeft = 0; + + AddItemToPool( this->sGridNo, pObj, 1 , this->pathing.bLevel, (WOLRD_ITEM_FIND_SWEETSPOT_FROM_GRIDNO|WORLD_ITEM_REACHABLE), -1 ); + DeleteObj( &(this->inv[cnt]) ); + } + } + } + } + else + { + OBJECTTYPE pObject[NUM_INV_SLOTS]; + UINT8 counter = 0; + + for ( UINT8 cnt = 0; cnt < size; ++cnt ) + { + pObj = &( this->inv[ cnt ] ); + + if ( pObj->exists() ) + { + // Check if it's supposed to be dropped + if ( !( (*pObj).fFlags & OBJECT_UNDROPPABLE ) && !(Item[ pObj->usItem ].defaultundroppable ) && (*pObj)[0]->data.sObjectFlag & TAKEN_BY_MILITIA ) + { + (*pObj)[0]->data.sObjectFlag &= ~TAKEN_BY_MILITIA; + + // if we are not replacing ammo, unload gun prior to dropping it + if ( !gGameExternalOptions.fMilitiaUseSectorInventory_Ammo && Item[ pObj->usItem ].usItemClass & IC_GUN ) + (*pObj)[0]->data.gun.ubGunShotsLeft = 0; + + pObject[counter++] = *pObj; + + DeleteObj( &(this->inv[cnt]) ); + } + } + } + + AddItemsToUnLoadedSector( this->sSectorX, this->sSectorY, this->bSectorZ, RandomGridNo(), counter, pObject, 0, WORLD_ITEM_REACHABLE, 0, 1, FALSE ); + } +} + INT32 CheckBleeding( SOLDIERTYPE *pSoldier ) { INT8 bBandaged; //,savedOurTurn; diff --git a/Tactical/Soldier Control.h b/Tactical/Soldier Control.h index 18a08899..946e4de6 100644 --- a/Tactical/Soldier Control.h +++ b/Tactical/Soldier Control.h @@ -363,8 +363,8 @@ enum #define SOLDIER_POW 0x00000400 //1024 // we are a prisoner of war #define SOLDIER_ASSASSIN 0x00000800 //2048 // we are an enemy assassin, and thus we will behave very different from normal enemies -/*#define ENEMY_NET_1_LVL_4 0x00001000 //4096 -#define ENEMY_NET_2_LVL_4 0x00002000 //8192 +/*#define SOLDIER_EQ_TAKEN 0x00001000 //4096 // this militia's equipment was taken from a sector inventory +#define SOLDIER_EQ_EMPTY 0x00002000 //8192 // this militia has no equipment and is to be equipped from the sector inventory #define ENEMY_NET_3_LVL_4 0x00004000 //16384 #define ENEMY_NET_4_LVL_4 0x00008000 //32768 @@ -1533,6 +1533,8 @@ public: void StartMultiTurnAction(UINT8 usActionType); void CancelMultiTurnAction(BOOLEAN fFinished); BOOLEAN UpdateMultiTurnAction(); + + void DropSectorEquipment(); ////////////////////////////////////////////////////////////////////////////// }; // SOLDIERTYPE; diff --git a/Tactical/Soldier Create.cpp b/Tactical/Soldier Create.cpp index 7b5ac2d6..fcaed968 100644 --- a/Tactical/Soldier Create.cpp +++ b/Tactical/Soldier Create.cpp @@ -2093,7 +2093,7 @@ INT8 CalcDifficultyModifier( UINT8 ubSoldierClass ) //Used to generate a detailed placement from a basic placement. This assumes that the detailed placement //doesn't exist, meaning there are no static attributes. This is called when you wish to convert a basic //placement into a detailed placement just before creating a soldier. -void CreateDetailedPlacementGivenBasicPlacementInfo( SOLDIERCREATE_STRUCT *pp, BASIC_SOLDIERCREATE_STRUCT *bp ) +void CreateDetailedPlacementGivenBasicPlacementInfo( SOLDIERCREATE_STRUCT *pp, BASIC_SOLDIERCREATE_STRUCT *bp, INT16 sX, INT16 sY ) { INT8 bBaseAttribute; UINT8 ubSoldierClass; @@ -2375,7 +2375,17 @@ void CreateDetailedPlacementGivenBasicPlacementInfo( SOLDIERCREATE_STRUCT *pp, B #else if( !bp->fDetailedPlacement && ubSoldierClass != SOLDIER_CLASS_NONE && ubSoldierClass != SOLDIER_CLASS_CREATURE && ubSoldierClass != SOLDIER_CLASS_MINER ) #endif - GenerateRandomEquipment( pp, ubSoldierClass, bp->bRelativeEquipmentLevel); + { + GenerateRandomEquipment( pp, ubSoldierClass, bp->bRelativeEquipmentLevel); + + // Flugente testing: militia get equipment in a different way + if ( pp->bTeam == MILITIA_TEAM && sX > 0 && sX < 17 && sY > 0 && sY < 17 ) + { + INT8 sZ = gbWorldSectorZ > -1 ? gbWorldSectorZ : 0; + + TakeMilitiaEquipmentfromSector(sX, sY, sZ, pp, ubSoldierClass); + } + } DecideToAssignSniperOrders(pp); @@ -2462,7 +2472,7 @@ void CreateStaticDetailedPlacementGivenBasicPlacementInfo( SOLDIERCREATE_STRUCT //the proper detailed placement slot given the static detailed placement and it's accompanying basic placement. //For the purposes of merc editing, the static detailed placement is preserved. void CreateDetailedPlacementGivenStaticDetailedPlacementAndBasicPlacementInfo( - SOLDIERCREATE_STRUCT *pp, SOLDIERCREATE_STRUCT *spp, BASIC_SOLDIERCREATE_STRUCT *bp ) + SOLDIERCREATE_STRUCT *pp, SOLDIERCREATE_STRUCT *spp, BASIC_SOLDIERCREATE_STRUCT *bp, INT16 sX, INT16 sY ) { UINT32 i; @@ -2499,7 +2509,7 @@ void CreateDetailedPlacementGivenStaticDetailedPlacementAndBasicPlacementInfo( return; //done } - CreateDetailedPlacementGivenBasicPlacementInfo( pp, bp ); + CreateDetailedPlacementGivenBasicPlacementInfo( pp, bp, sX, sY ); pp->ubScheduleID = spp->ubScheduleID; //Replace any of the new placement's attributes with static attributes. if( spp->bExpLevel != -1 ) pp->bExpLevel = spp->bExpLevel; @@ -2550,6 +2560,15 @@ void CreateDetailedPlacementGivenStaticDetailedPlacementAndBasicPlacementInfo( #endif { GenerateRandomEquipment( pp, bp->ubSoldierClass, bp->bRelativeEquipmentLevel); + + // Flugente testing: militia get equipment in a different way + if ( pp->bTeam == MILITIA_TEAM && sX > 0 && sX < 17 && sY > 0 && sY < 17 ) + { + INT8 sZ = gbWorldSectorZ > -1 ? gbWorldSectorZ : 0; + + TakeMilitiaEquipmentfromSector(sX, sY, sZ, pp, bp->ubSoldierClass); + } + DecideToAssignSniperOrders(pp); } @@ -2958,7 +2977,7 @@ SOLDIERTYPE* ReserveTacticalMilitiaSoldierForAutoresolve( UINT8 ubSoldierClass ) } -SOLDIERTYPE* TacticalCreateMilitia( UINT8 ubMilitiaClass ) +SOLDIERTYPE* TacticalCreateMilitia( UINT8 ubMilitiaClass, INT16 sX, INT16 sY ) { BASIC_SOLDIERCREATE_STRUCT bp; SOLDIERCREATE_STRUCT pp; @@ -2984,7 +3003,7 @@ SOLDIERTYPE* TacticalCreateMilitia( UINT8 ubMilitiaClass ) bp.bAttitude = (INT8) Random( MAXATTITUDES ); //bp.bAttitude = AGGRESSIVE; bp.bBodyType = -1; - CreateDetailedPlacementGivenBasicPlacementInfo( &pp, &bp ); + CreateDetailedPlacementGivenBasicPlacementInfo( &pp, &bp, sX, sY ); pSoldier = TacticalCreateSoldier( &pp, &ubID ); return pSoldier; @@ -4293,7 +4312,10 @@ BOOLEAN AssignTraitsToSoldier( SOLDIERTYPE *pSoldier, SOLDIERCREATE_STRUCT *pCre // Chance to gain second weapon and ambidextrous trait else if ( Chance( iChance/2 ) && !Item[ pCreateStruct->Inv[ HANDPOS ].usItem ].twohanded && !BTraitAssigned ) // 1/2 of chance { - (pCreateStruct->Inv[SECONDHANDPOS]) = (pCreateStruct->Inv[HANDPOS]); + if ( pCreateStruct->bTeam == MILITIA_TEAM && gGameExternalOptions.fMilitiaUseSectorInventory && gGameExternalOptions.fMilitiaUseSectorInventory_Gun ) + ; + else + (pCreateStruct->Inv[SECONDHANDPOS]) = (pCreateStruct->Inv[HANDPOS]); pSoldier->stats.ubSkillTraits[1] = AMBIDEXTROUS_NT; diff --git a/Tactical/Soldier Create.h b/Tactical/Soldier Create.h index 13bbe5c7..96d8bcdc 100644 --- a/Tactical/Soldier Create.h +++ b/Tactical/Soldier Create.h @@ -423,7 +423,7 @@ SOLDIERTYPE* TacticalCreateArmyTroop(); #ifdef ENABLE_ZOMBIES SOLDIERTYPE* TacticalCreateZombie(); // Flugente Zombies #endif -SOLDIERTYPE* TacticalCreateMilitia( UINT8 ubMilitiaClass ); +SOLDIERTYPE* TacticalCreateMilitia( UINT8 ubMilitiaClass, INT16 sX, INT16 sY ); // Flugente: added sector coordinates SOLDIERTYPE* TacticalCreateCreature( INT8 bCreatureBodyType ); // Flugente: assassins are elite soldiers of the civ team that go hostile on a certain event, otherwise they just blend in @@ -461,7 +461,8 @@ UINT8 GetPythDistanceFromPalace( INT16 sSectorX, INT16 sSectorY ); //Used to generate a detailed placement from a basic placement. This assumes that the detailed placement //doesn't exist, meaning there are no static attributes. This is called when you wish to convert a basic //placement into a detailed placement just before creating a soldier. -void CreateDetailedPlacementGivenBasicPlacementInfo( SOLDIERCREATE_STRUCT *pp, BASIC_SOLDIERCREATE_STRUCT *bp ); +// Flugente: added sector coordinates, used for militia using sector equipment +void CreateDetailedPlacementGivenBasicPlacementInfo( SOLDIERCREATE_STRUCT *pp, BASIC_SOLDIERCREATE_STRUCT *bp, INT16 sX = -1, INT16 sY = -1 ); //Used exclusively by the editor when the user wishes to change a basic placement into a detailed placement. //Because the intention is to make some of the attributes static, all of the information that can be generated @@ -473,8 +474,9 @@ void CreateStaticDetailedPlacementGivenBasicPlacementInfo( SOLDIERCREATE_STRUCT //When you are ready to generate a soldier with a static detailed placement slot, this function will generate //the proper priority placement slot given the static detailed placement and it's accompanying basic placment. //For the purposes of merc editing, the static detailed placement is preserved. +// Flugente: added sector coordinates, used for militia using sector equipment void CreateDetailedPlacementGivenStaticDetailedPlacementAndBasicPlacementInfo( - SOLDIERCREATE_STRUCT *pp, SOLDIERCREATE_STRUCT *spp, BASIC_SOLDIERCREATE_STRUCT *bp ); + SOLDIERCREATE_STRUCT *pp, SOLDIERCREATE_STRUCT *spp, BASIC_SOLDIERCREATE_STRUCT *bp, INT16 sX = -1, INT16 sY = -1); //Used to update a existing soldier's attributes with the new static detailed placement info. This is used //by the editor upon exiting the editor into the game, to update the existing soldiers with new information. diff --git a/Tactical/Soldier Init List.cpp b/Tactical/Soldier Init List.cpp index 5cdbc65d..0d0c5c56 100644 --- a/Tactical/Soldier Init List.cpp +++ b/Tactical/Soldier Init List.cpp @@ -646,12 +646,12 @@ BOOLEAN AddPlacementToWorld( SOLDIERINITNODE *curr, GROUP *pGroup = NULL ) } } CreateDetailedPlacementGivenStaticDetailedPlacementAndBasicPlacementInfo( - &tempDetailedPlacement, curr->pDetailedPlacement, curr->pBasicPlacement ); + &tempDetailedPlacement, curr->pDetailedPlacement, curr->pBasicPlacement, gWorldSectorX, gWorldSectorY ); } else { DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("AddPlacementToWorld: create detailed placement")); - CreateDetailedPlacementGivenBasicPlacementInfo( &tempDetailedPlacement, curr->pBasicPlacement ); + CreateDetailedPlacementGivenBasicPlacementInfo( &tempDetailedPlacement, curr->pBasicPlacement, gWorldSectorX, gWorldSectorY ); } DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("AddPlacementToWorld: if not edit mode")); @@ -1482,10 +1482,16 @@ void AddSoldierInitListMilitia( UINT8 ubNumGreen, UINT8 ubNumRegs, UINT8 ubNumEl //we now have the numbers of available slots for each soldier class, so loop through three times //and randomly choose some (or all) of the matching slots to fill. This is done randomly. - for( ubCurrClass = SOLDIER_CLASS_ADMINISTRATOR; ubCurrClass <= SOLDIER_CLASS_ARMY; ubCurrClass++ ) + // Flugente: changed the order of the classes, as we want to ahve elites first (relevant if militia takes equipment from the sector) + UINT8 reorder[3]; + reorder[0] = SOLDIER_CLASS_ELITE; + reorder[1] = SOLDIER_CLASS_ARMY; + reorder[2] = SOLDIER_CLASS_ADMINISTRATOR; + + for( UINT8 i = 0; i < 3; ++i ) { //First, prepare the counters. - switch( ubCurrClass ) + switch( reorder[i] ) { case SOLDIER_CLASS_ADMINISTRATOR: pCurrSlots = &ubGreenSlots; @@ -1506,13 +1512,13 @@ void AddSoldierInitListMilitia( UINT8 ubNumGreen, UINT8 ubNumRegs, UINT8 ubNumEl { if( curr->pBasicPlacement->bTeam == ENEMY_TEAM || curr->pBasicPlacement->bTeam == MILITIA_TEAM ) { - if( curr->pBasicPlacement->ubSoldierClass == ubCurrClass ) + if( curr->pBasicPlacement->ubSoldierClass == reorder[i] ) { if( *pCurrSlots <= *pCurrTotal || Random( *pCurrSlots ) < *pCurrTotal ) { curr->pBasicPlacement->bTeam = MILITIA_TEAM; curr->pBasicPlacement->bOrders = STATIONARY; - switch( ubCurrClass ) + switch( reorder[i] ) { case SOLDIER_CLASS_ADMINISTRATOR: curr->pBasicPlacement->ubSoldierClass = SOLDIER_CLASS_GREEN_MILITIA; @@ -2537,18 +2543,38 @@ void AddSoldierInitListMilitiaOnEdge( UINT8 ubStrategicInsertionCode, UINT8 ubNu if (ubTotalSoldiers == 0) return; + // Flugente: if militia picks up equipemnt from sectors, it is necessary to know from where it comes + INT16 sX = gWorldSectorX; + INT16 sY = gWorldSectorY; + switch( ubStrategicInsertionCode ) { - case INSERTION_CODE_NORTH: bDesiredDirection = SOUTHEAST; break; - case INSERTION_CODE_EAST: bDesiredDirection = SOUTHWEST; break; - case INSERTION_CODE_SOUTH: bDesiredDirection = NORTHWEST; break; - case INSERTION_CODE_WEST: bDesiredDirection = NORTHEAST; break; - default: AssertMsg( 0, "Illegal direction passed to AddSoldierInitListMilitiaOnEdge()" ); break; + case INSERTION_CODE_NORTH: + bDesiredDirection = SOUTHEAST; + --sY; + break; + case INSERTION_CODE_EAST: + bDesiredDirection = SOUTHWEST; + --sX; + break; + case INSERTION_CODE_SOUTH: + bDesiredDirection = NORTHWEST; + ++sY; + break; + case INSERTION_CODE_WEST: + bDesiredDirection = NORTHEAST; + ++sX; + break; + default: + AssertMsg( 0, "Illegal direction passed to AddSoldierInitListMilitiaOnEdge()" ); + break; } #ifdef JA2TESTVERSION ScreenMsg( FONT_RED, MSG_INTERFACE, L"Militia reinforcements have arrived! (%d admins, %d troops, %d elite)", ubNumGreen, ubNumReg, ubNumElites ); #endif + // Flugente: if militia takes items from sector inventories, then militia coming from neighbouring sectors will have to take it from there + ChooseMapEdgepoints( &MapEdgepointInfo, ubStrategicInsertionCode, (UINT8)(ubNumGreen + ubNumReg + ubNumElites) ); ubCurrSlot = 0; while( ubTotalSoldiers ) @@ -2557,7 +2583,7 @@ void AddSoldierInitListMilitiaOnEdge( UINT8 ubStrategicInsertionCode, UINT8 ubNu { ubNumElites--; ubTotalSoldiers--; - pSoldier = TacticalCreateMilitia(SOLDIER_CLASS_ELITE_MILITIA); + pSoldier = TacticalCreateMilitia(SOLDIER_CLASS_ELITE_MILITIA, sX, sY); // Lesh: if pSoldier is NULL then no slot for a new men or other problems // it better to leave this function is such case @@ -2597,7 +2623,7 @@ void AddSoldierInitListMilitiaOnEdge( UINT8 ubStrategicInsertionCode, UINT8 ubNu { ubNumReg--; ubTotalSoldiers--; - pSoldier = TacticalCreateMilitia(SOLDIER_CLASS_REG_MILITIA); + pSoldier = TacticalCreateMilitia(SOLDIER_CLASS_REG_MILITIA, sX, sY); // Lesh: if pSoldier is NULL then no slot for a new men or other problems // it better to leave this function is such case @@ -2637,7 +2663,7 @@ void AddSoldierInitListMilitiaOnEdge( UINT8 ubStrategicInsertionCode, UINT8 ubNu { ubNumGreen--; ubTotalSoldiers--; - pSoldier = TacticalCreateMilitia(SOLDIER_CLASS_GREEN_MILITIA); + pSoldier = TacticalCreateMilitia(SOLDIER_CLASS_GREEN_MILITIA, sX, sY); // Lesh: if pSoldier is NULL then no slot for a new men or other problems // it better to leave this function is such case diff --git a/Tactical/Tactical Save.h b/Tactical/Tactical Save.h index 86ef1a25..4a3da993 100644 --- a/Tactical/Tactical Save.h +++ b/Tactical/Tactical Save.h @@ -49,6 +49,8 @@ BOOLEAN LoadCurrentSectorsInformationFromTempItemsFile(); // Loads a World Item array from that sectors temp item file BOOLEAN LoadWorldItemsFromTempItemFile( INT16 sMapX, INT16 sMapY, INT8 bMapZ, WORLDITEM *pData ); +BOOLEAN SaveWorldItemsToTempItemFile( INT16 sMapX, INT16 sMapY, INT8 bMapZ, UINT32 uiNumberOfItems, WORLDITEM *pData ); + //When the savegame version changes, load the temp files, then immediately save them again in the new format BOOLEAN UpdateWorldItemsTempFile( INT16 sMapX, INT16 sMapY, INT8 bMapZ ); diff --git a/Tactical/Turn Based Input.cpp b/Tactical/Turn Based Input.cpp index 7c570e84..cae87786 100644 --- a/Tactical/Turn Based Input.cpp +++ b/Tactical/Turn Based Input.cpp @@ -4512,6 +4512,12 @@ void GetKeyboardInput( UINT32 *puiNewEvent ) break; + // added by Flugente + case ':': + + TeamDropAll( MILITIA_TEAM ); + break; + case 'z': if( fCtrl && fAlt ) { diff --git a/Tactical/World Items.h b/Tactical/World Items.h index 9d5c7bd5..475e4e1e 100644 --- a/Tactical/World Items.h +++ b/Tactical/World Items.h @@ -15,6 +15,9 @@ //Kaiden: This constant is to flag items that an enemy drops when they die. #define WORLD_ITEM_DROPPED_FROM_ENEMY 0x0800 +// Flugente: when equipping militia from inventory, do not consider this item +#define WORLD_ITEM_TABOO_FOR_MILITIA_EQ 0x1000 + class WORLDITEM;//dnl ch33 120909 class _OLD_WORLDITEM;//dnl ch42 280909 diff --git a/TileEngine/Explosion Control.cpp b/TileEngine/Explosion Control.cpp index 2df5dce3..21e47483 100644 --- a/TileEngine/Explosion Control.cpp +++ b/TileEngine/Explosion Control.cpp @@ -3701,7 +3701,7 @@ BOOLEAN ActivateSurroundingTripwire( UINT8 ubID, INT32 sGridNo, INT8 bLevel, UIN if ( Item[usBombItem].tripwire == 1 ) { // this is important - we have to check wether the wire has already been activated - if ( ( (*pObj)[0]->data.sObjectFlag & CORPSE_NO_PANTS_AND_TRIPWIRE_ACTIVATED ) == 0 ) + if ( ( (*pObj)[0]->data.sObjectFlag & TRIPWIRE_ACTIVATED ) == 0 ) { // determine this tripwire's flag UINT32 ubWireNetworkFlag = (*pObj)[0]->data.ubWireNetworkFlag; @@ -3751,7 +3751,7 @@ BOOLEAN ActivateSurroundingTripwire( UINT8 ubID, INT32 sGridNo, INT8 bLevel, UIN // Flugente hack: the tripwire will get removed in HandleExplosionQueue (it is still needed in there until the bomb gets called). // Because of this, ActivateSurroundingTripwire would normally find this wire again, thus a loop would occur. // So we mark the wire with this flag - (*pObj)[0]->data.sObjectFlag |= CORPSE_NO_PANTS_AND_TRIPWIRE_ACTIVATED; + (*pObj)[0]->data.sObjectFlag |= TRIPWIRE_ACTIVATED; // activate surrounding tripwires, unless tripwire is too much damaged and we are unlucky.. if ( (*pObj)[0]->data.objectStatus > (INT16)Random(50) ) @@ -3918,7 +3918,7 @@ void HandleExplosionQueue( void ) } // delete the flag, otherwise wire will only work once - (newtripwireObject)[0]->data.sObjectFlag &= ~CORPSE_NO_PANTS_AND_TRIPWIRE_ACTIVATED; + (newtripwireObject)[0]->data.sObjectFlag &= ~TRIPWIRE_ACTIVATED; // now add a tripwire item to the floor, simulating that activating tripwire deactivates it AddItemToPool( sGridNo, &newtripwireObject, 1, ubLevel, 0, -1 ); @@ -3946,7 +3946,7 @@ void HandleExplosionQueue( void ) } // delete the flag, otherwise wire will only work once - (newtripwireObject)[0]->data.sObjectFlag &= ~CORPSE_NO_PANTS_AND_TRIPWIRE_ACTIVATED; + (newtripwireObject)[0]->data.sObjectFlag &= ~TRIPWIRE_ACTIVATED; // now add a tripwire item to the floor, simulating that activating tripwire deactivates it AddItemToPool( sGridNo, &newtripwireObject, 1, ubLevel, 0, -1 ); @@ -4434,7 +4434,7 @@ BOOLEAN SetOffBombsInGridNo( UINT8 ubID, INT32 sGridNo, BOOLEAN fAllBombs, INT8 // Flugente hack: the tripwire will get removed in HandleExplosionQueue (it is still needed in there until the bomb gets called). // Because of this, ActivateSurroundingTripwire would normally find this wire again, thus a loop would occur. // So we mark the wire with this flag - (*pObj)[0]->data.sObjectFlag |= CORPSE_NO_PANTS_AND_TRIPWIRE_ACTIVATED; + (*pObj)[0]->data.sObjectFlag |= TRIPWIRE_ACTIVATED; // activate surrounding tripwires and tripwire-activated mines, unless tripwire is too much damaged and we are unlucky.. if ( (*pObj)[0]->data.objectStatus > (INT16)Random(50) ) diff --git a/Utils/Text.h b/Utils/Text.h index 0821b516..5fb53e5d 100644 --- a/Utils/Text.h +++ b/Utils/Text.h @@ -946,6 +946,8 @@ enum PRISONER_DEMAND_SURRENDER_STR, PRISONER_OFFER_SURRENDER_STR, PRISONER_TALK_STR, + MILITIA_DROP_EQ_STR, + UNUSED_STR, TEXT_NUM_TACTICAL_STR }; diff --git a/Utils/_ChineseText.cpp b/Utils/_ChineseText.cpp index 2b183122..f1dbb7f0 100644 --- a/Utils/_ChineseText.cpp +++ b/Utils/_ChineseText.cpp @@ -2931,6 +2931,8 @@ CHAR16 TacticalStr[][ MED_STRING_LENGTH ] = L"劝说敌人投降", L"Offer surrender",//TODO.Translate L"交谈", + L"Militia inspection",//TODO.Translate + L"unused", }; //Varying helptext explains (for the "Go to Sector/Map" checkbox) what will happen given different circumstances in the "exiting sector" interface. diff --git a/Utils/_DutchText.cpp b/Utils/_DutchText.cpp index 2bfc2053..f778fab8 100644 --- a/Utils/_DutchText.cpp +++ b/Utils/_DutchText.cpp @@ -2928,6 +2928,8 @@ CHAR16 TacticalStr[][ MED_STRING_LENGTH ] = L"Demand surrender", L"Offer surrender", L"Talk", + L"Militia inspection",//TODO.Translate + L"unused", }; //Varying helptext explains (for the "Go to Sector/Map" checkbox) what will happen given different circumstances in the "exiting sector" interface. diff --git a/Utils/_EnglishText.cpp b/Utils/_EnglishText.cpp index 71f1fe05..10f831e1 100644 --- a/Utils/_EnglishText.cpp +++ b/Utils/_EnglishText.cpp @@ -2930,6 +2930,8 @@ CHAR16 TacticalStr[][ MED_STRING_LENGTH ] = L"Demand surrender", L"Offer surrender", L"Talk", + L"Militia inspection", + L"unused", }; //Varying helptext explains (for the "Go to Sector/Map" checkbox) what will happen given different circumstances in the "exiting sector" interface. diff --git a/Utils/_FrenchText.cpp b/Utils/_FrenchText.cpp index 75180eca..b5a6c100 100644 --- a/Utils/_FrenchText.cpp +++ b/Utils/_FrenchText.cpp @@ -2935,6 +2935,8 @@ CHAR16 TacticalStr[][ MED_STRING_LENGTH ] = L"Demand surrender", L"Offer surrender", L"Talk", + L"Militia inspection",//TODO.Translate + L"unused", }; //Varying helptext explains (for the "Go to Sector/Map" checkbox) what will happen given different circumstances in the "exiting sector" interface. diff --git a/Utils/_GermanText.cpp b/Utils/_GermanText.cpp index f23bedb0..3c28f247 100644 --- a/Utils/_GermanText.cpp +++ b/Utils/_GermanText.cpp @@ -2933,6 +2933,8 @@ CHAR16 TacticalStr[][ MED_STRING_LENGTH ] = L"Aufgabe einfordern", L"Aufgabe anbieten", L"Sprechen", + L"Militia inspection",//TODO.Translate + L"unused", }; //Varying helptext explains (for the "Go to Sector/Map" checkbox) what will happen given different circumstances in the "exiting sector" interface. diff --git a/Utils/_ItalianText.cpp b/Utils/_ItalianText.cpp index 338b98f0..0215893e 100644 --- a/Utils/_ItalianText.cpp +++ b/Utils/_ItalianText.cpp @@ -2923,6 +2923,8 @@ CHAR16 TacticalStr[][ MED_STRING_LENGTH ] = L"Demand surrender", L"Offer surrender", L"Talk", + L"Militia inspection",//TODO.Translate + L"unused", }; //Varying helptext explains (for the "Go to Sector/Map" checkbox) what will happen given different circumstances in the "exiting sector" interface. diff --git a/Utils/_PolishText.cpp b/Utils/_PolishText.cpp index 6cbee10b..6ca44fad 100644 --- a/Utils/_PolishText.cpp +++ b/Utils/_PolishText.cpp @@ -2938,6 +2938,8 @@ CHAR16 TacticalStr[][ MED_STRING_LENGTH ] = L"Demand surrender", L"Offer surrender", L"Talk", + L"Militia inspection",//TODO.Translate + L"unused", }; //Varying helptext explains (for the "Go to Sector/Map" checkbox) what will happen given different circumstances in the "exiting sector" interface. diff --git a/Utils/_RussianText.cpp b/Utils/_RussianText.cpp index f9e535f7..3c75e837 100644 --- a/Utils/_RussianText.cpp +++ b/Utils/_RussianText.cpp @@ -2930,6 +2930,8 @@ CHAR16 TacticalStr[][ MED_STRING_LENGTH ] = L"Требовать сдаться", L"Предлоить сдаться", L"Переговоры", + L"Militia inspection",//TODO.Translate + L"unused", }; //Varying helptext explains (for the "Go to Sector/Map" checkbox) what will happen given different circumstances in the "exiting sector" interface. diff --git a/Utils/_TaiwaneseText.cpp b/Utils/_TaiwaneseText.cpp index 6610b0d4..5be7f7be 100644 --- a/Utils/_TaiwaneseText.cpp +++ b/Utils/_TaiwaneseText.cpp @@ -2932,6 +2932,8 @@ CHAR16 TacticalStr[][ MED_STRING_LENGTH ] = L"Demand surrender", L"Offer surrender", L"Talk", + L"Militia inspection",//TODO.Translate + L"unused", }; //Varying helptext explains (for the "Go to Sector/Map" checkbox) what will happen given different circumstances in the "exiting sector" interface.