//Queen Command.c #ifdef PRECOMPILEDHEADERS #include "Strategic All.h" #include "GameSettings.h" #else #include "Queen Command.h" #include "Strategic Event Handler.h" #include "Overhead Types.h" #include "strategicmap.h" #include "Soldier Init List.h" #include "debug.h" #include "Random.h" #include "Strategic Movement.h" #include "Overhead.h" #include "Strategic Pathing.h" #include "strategic.h" #include "message.h" #include "Map Edgepoints.h" #include "PreBattle Interface.h" #include "strategic status.h" #include "squads.h" #include "assignments.h" #include "items.h" #include "tactical save.h" #include "Soldier Ani.h" #include "Strategic AI.h" #include "GameSettings.h" #include "MessageBoxScreen.h" #include "screenids.h" #include "Strategic Town Loyalty.h" #include "Soldier Profile.h" #include "Quests.h" #include "jascreens.h" #include "Auto Resolve.h" #include "Game Event Hook.h" #include "Animation Data.h" #include "Game Clock.h" #include "renderworld.h" #include "Town Militia.h" #include "Dialogue Control.h" #include "Campaign Init.h" #include "meanwhile.h" #include "Soldier macros.h" #include "Morale.h" #include "CampaignStats.h" // added by Flugente #include "ASD.h" // added by Flugente #endif #ifdef JA2BETAVERSION extern BOOLEAN gfClearCreatureQuest; #endif #include "connect.h" #include "Reinforcement.h" #include "MilitiaSquads.h" #ifdef JA2UB #include "Explosion Control.h" #include "Ja25_Tactical.h" #include "Ja25 Strategic Ai.h" #include "MapScreen Quotes.h" #include "email.h" #include "interface Dialogue.h" #include "Arms Dealer Init.h" #endif #include "GameInitOptionsScreen.h" #include //The sector information required for the strategic AI. Contains the number of enemy troops, //as well as intentions, etc. //SECTORINFO SectorInfo[256]; std::vector SectorInfo (256); UNDERGROUND_SECTORINFO *gpUndergroundSectorInfoHead = NULL; extern UNDERGROUND_SECTORINFO* gpUndergroundSectorInfoTail; BOOLEAN gfPendingNonPlayerTeam[PLAYER_PLAN] = {FALSE, FALSE, FALSE, FALSE, FALSE}; UINT32 guiTurnCnt = 0, guiReinforceTurn = 0, guiArrived = 0;//dnl ch68 080913 extern void BuildUndergroundSectorInfoList(); extern void EndCreatureQuest(); extern GARRISON_GROUP *gGarrisonGroup; extern INT32 giGarrisonArraySize; #ifdef JA2TESTVERSION extern BOOLEAN gfOverrideSector; #endif //Buggler: Externalized to gModSettings.iMeanwhileInterrogatePOWGridNo //INT32 gsInterrogationGridNo[3] = { 7756, 7757, 7758 }; #ifdef JA2UB INT32 gsGridNoForMapEdgePointInfo=-1; #endif #ifdef JA2UB void HandleBloodCatDeaths( SECTORINFO *pSector ); #endif extern void Ensure_RepairedGarrisonGroup( GARRISON_GROUP **ppGarrison, INT32 *pGarraySize ); void ValidateEnemiesHaveWeapons() { #ifdef JA2BETAVERSION SGPRect CenteringRect= {0 + xResOffset, 0, SCREEN_WIDTH - xResOffset, SCREEN_HEIGHT }; INT32 i, iErrorDialog; SOLDIERTYPE *pSoldier; INT32 iNumInvalid = 0; for( i = gTacticalStatus.Team[ ENEMY_TEAM ].bFirstID; i <= gTacticalStatus.Team[ ENEMY_TEAM ].bLastID; ++i ) { pSoldier = MercPtrs[ i ]; if( !pSoldier->bActive || !pSoldier->bInSector ) { continue; } // Flugente: zombies are fine with having no weapons... if ( pSoldier->IsZombie() ) continue; if( !pSoldier->inv[ HANDPOS ].usItem ) { ++iNumInvalid; } // WDS DEBUG // Uncommenting the following two lines will cause all the PLACED soldiers to instantly drop dead, // which is useful for debugging some things //pSoldier->bBleeding = 10; //pSoldier->stats.bLife = 1; } // do message box and return if( iNumInvalid ) { CHAR16 str[ 100 ]; swprintf( str, L"%d enemies have been added without any weapons! KM:0. Please note sector.", iNumInvalid ); iErrorDialog = DoMessageBox( MSG_BOX_BASIC_STYLE, str, GAME_SCREEN, MSG_BOX_FLAG_OK, NULL, &CenteringRect ); } #endif } //Counts enemies and crepitus, but not bloodcats. UINT8 NumHostilesInSector( INT16 sSectorX, INT16 sSectorY, INT16 sSectorZ ) { UINT8 ubNumHostiles = 0; AssertGE( sSectorX, MINIMUM_VALID_X_COORDINATE); AssertLE( sSectorX, MAXIMUM_VALID_X_COORDINATE ); AssertGE( sSectorY, MINIMUM_VALID_Y_COORDINATE); AssertLE( sSectorY, MAXIMUM_VALID_Y_COORDINATE ); AssertGE( sSectorZ, MINIMUM_VALID_Z_COORDINATE ); AssertLE( sSectorZ, MAXIMUM_VALID_Z_COORDINATE ); if( sSectorZ ) { UNDERGROUND_SECTORINFO *pSector; pSector = FindUnderGroundSector( sSectorX, sSectorY, (UINT8)sSectorZ ); if( pSector ) { ubNumHostiles = (UINT8)(pSector->ubNumAdmins + pSector->ubNumTroops + pSector->ubNumElites + pSector->ubNumCreatures + pSector->ubNumJeeps + pSector->ubNumTanks); } } else { SECTORINFO *pSector; GROUP *pGroup; //Count stationary hostiles pSector = &SectorInfo[ SECTOR( sSectorX, sSectorY ) ]; ubNumHostiles = (UINT8)(pSector->ubNumAdmins + pSector->ubNumTroops + pSector->ubNumElites + pSector->ubNumCreatures + pSector->ubNumJeeps + pSector->ubNumTanks); //Count mobile enemies pGroup = gpGroupList; while( pGroup ) { if ( pGroup->usGroupTeam != OUR_TEAM && pGroup->usGroupTeam != MILITIA_TEAM && !pGroup->fVehicle && pGroup->ubSectorX == sSectorX && pGroup->ubSectorY == sSectorY ) { ubNumHostiles += pGroup->ubGroupSize; } pGroup = pGroup->next; } } return ubNumHostiles; } UINT8 NumEnemiesInAnySector( INT16 sSectorX, INT16 sSectorY, INT16 sSectorZ ) { UINT8 ubNumEnemies = 0; AssertGE( sSectorX, MINIMUM_VALID_X_COORDINATE); AssertLE( sSectorX, MAXIMUM_VALID_X_COORDINATE ); AssertGE( sSectorY, MINIMUM_VALID_Y_COORDINATE); AssertLE( sSectorY, MAXIMUM_VALID_Y_COORDINATE ); AssertGE( sSectorZ, MINIMUM_VALID_Z_COORDINATE ); AssertLE( sSectorZ, MAXIMUM_VALID_Z_COORDINATE ); if( sSectorZ ) { UNDERGROUND_SECTORINFO *pSector; pSector = FindUnderGroundSector( sSectorX, sSectorY, (UINT8)sSectorZ ); if( pSector ) { ubNumEnemies = (UINT8)(pSector->ubNumAdmins + pSector->ubNumTroops + pSector->ubNumElites + pSector->ubNumJeeps + pSector->ubNumTanks); } } else { SECTORINFO *pSector; GROUP *pGroup; //Count stationary enemies pSector = &SectorInfo[ SECTOR( sSectorX, sSectorY ) ]; ubNumEnemies = (UINT8)(pSector->ubNumAdmins + pSector->ubNumTroops + pSector->ubNumElites + pSector->ubNumJeeps + pSector->ubNumTanks); //Count mobile enemies pGroup = gpGroupList; while( pGroup ) { if ( pGroup->usGroupTeam != OUR_TEAM && pGroup->usGroupTeam != MILITIA_TEAM && !pGroup->fVehicle && pGroup->ubSectorX == sSectorX && pGroup->ubSectorY == sSectorY ) { ubNumEnemies += pGroup->ubGroupSize; } pGroup = pGroup->next; } } return ubNumEnemies; } // returns how many members of a team are in a sector - not intended for OUR_TEAM! UINT8 NumNonPlayerTeamMembersInSector( INT16 sSectorX, INT16 sSectorY, UINT8 ubTeam ) { SECTORINFO *pSector; GROUP *pGroup; UINT8 ubNumTroops; // HEADROCK: This is a TEMPORARY fix to avoid the assertion error. Not sure this is the best solution, // probably isn't. But I need this bit to work. if (sSectorX < MINIMUM_VALID_X_COORDINATE || sSectorX > MAXIMUM_VALID_X_COORDINATE || sSectorY < MINIMUM_VALID_Y_COORDINATE || sSectorY > MAXIMUM_VALID_Y_COORDINATE) { return (0); } AssertGE( sSectorX, MINIMUM_VALID_X_COORDINATE); AssertLE( sSectorX, MAXIMUM_VALID_X_COORDINATE ); AssertGE( sSectorY, MINIMUM_VALID_Y_COORDINATE); AssertLE( sSectorY, MAXIMUM_VALID_Y_COORDINATE ); pSector = &SectorInfo[ SECTOR( sSectorX, sSectorY ) ]; if ( ubTeam == ENEMY_TEAM ) { ubNumTroops = (UINT8)(pSector->ubNumAdmins + pSector->ubNumTroops + pSector->ubNumElites + pSector->ubNumJeeps + pSector->ubNumTanks); if ( is_networked ) ubNumTroops += numenemyLAN((UINT8)sSectorX,(UINT8)sSectorY ); //hayden } else if ( ubTeam == MILITIA_TEAM ) { ubNumTroops = (UINT8)(pSector->ubNumberOfCivsAtLevel[0] + pSector->ubNumberOfCivsAtLevel[1] + pSector->ubNumberOfCivsAtLevel[2]); } pGroup = gpGroupList; while( pGroup ) { if ( pGroup->usGroupTeam == ubTeam && !pGroup->fVehicle && pGroup->ubSectorX == sSectorX && pGroup->ubSectorY == sSectorY ) { ubNumTroops += pGroup->ubGroupSize; } pGroup = pGroup->next; } return ubNumTroops; } UINT16 NumEnemyArmedVehiclesInSector( INT16 sSectorX, INT16 sSectorY, UINT8 usTeam ) { SECTORINFO *pSector; GROUP *pGroup; UINT16 ubNum; // HEADROCK: This is a TEMPORARY fix to avoid the assertion error. Not sure this is the best solution, // probably isn't. But I need this bit to work. if ( sSectorX < MINIMUM_VALID_X_COORDINATE || sSectorX > MAXIMUM_VALID_X_COORDINATE || sSectorY < MINIMUM_VALID_Y_COORDINATE || sSectorY > MAXIMUM_VALID_Y_COORDINATE ) { return (0); } AssertGE( sSectorX, MINIMUM_VALID_X_COORDINATE ); AssertLE( sSectorX, MAXIMUM_VALID_X_COORDINATE ); AssertGE( sSectorY, MINIMUM_VALID_Y_COORDINATE ); AssertLE( sSectorY, MAXIMUM_VALID_Y_COORDINATE ); pSector = &SectorInfo[SECTOR( sSectorX, sSectorY )]; if ( usTeam == ENEMY_TEAM ) ubNum = (UINT16)(pSector->ubNumTanks + pSector->ubNumJeeps); pGroup = gpGroupList; while ( pGroup ) { if ( pGroup->usGroupTeam == usTeam && !pGroup->fVehicle && pGroup->ubSectorX == sSectorX && pGroup->ubSectorY == sSectorY ) { ubNum += pGroup->pEnemyGroup->ubNumTanks + pGroup->pEnemyGroup->ubNumJeeps; } pGroup = pGroup->next; } return ubNum; } UINT8 NumStationaryEnemiesInSector( INT16 sSectorX, INT16 sSectorY ) { SECTORINFO *pSector; AssertGE( sSectorX, MINIMUM_VALID_X_COORDINATE); AssertLE( sSectorX, MAXIMUM_VALID_X_COORDINATE ); AssertGE( sSectorY, MINIMUM_VALID_Y_COORDINATE); AssertLE( sSectorY, MAXIMUM_VALID_Y_COORDINATE ); pSector = &SectorInfo[ SECTOR( sSectorX, sSectorY ) ]; if( pSector->ubGarrisonID == NO_GARRISON ) { //If no garrison, no stationary. return( 0 ); } Ensure_RepairedGarrisonGroup( &gGarrisonGroup, &giGarrisonArraySize ); /* added NULL fix, 2007-03-03, Sgt. Kolja */ // don't count roadblocks as stationary garrison, we want to see how many enemies are in them, not question marks if ( gGarrisonGroup[ pSector->ubGarrisonID ].ubComposition == ROADBLOCK ) { // pretend they're not stationary return( 0 ); } return (UINT8)(pSector->ubNumAdmins + pSector->ubNumTroops + pSector->ubNumElites + pSector->ubNumTanks); } UINT8 NumMobileEnemiesInSector( INT16 sSectorX, INT16 sSectorY ) { GROUP *pGroup; SECTORINFO *pSector; UINT8 ubNumTroops; AssertGE( sSectorX, MINIMUM_VALID_X_COORDINATE); AssertLE( sSectorX, MAXIMUM_VALID_X_COORDINATE ); AssertGE( sSectorY, MINIMUM_VALID_Y_COORDINATE); AssertLE( sSectorY, MAXIMUM_VALID_Y_COORDINATE ); ubNumTroops = 0; pGroup = gpGroupList; while( pGroup ) { if ( pGroup->usGroupTeam != OUR_TEAM && pGroup->usGroupTeam != MILITIA_TEAM && !pGroup->fVehicle && pGroup->ubSectorX == sSectorX && pGroup->ubSectorY == sSectorY ) { ubNumTroops += pGroup->ubGroupSize; } pGroup = pGroup->next; } pSector = &SectorInfo[ SECTOR( sSectorX, sSectorY ) ]; if( pSector->ubGarrisonID == ROADBLOCK ) { //consider these troops as mobile troops even though they are in a garrison ubNumTroops += (UINT8)(pSector->ubNumAdmins + pSector->ubNumTroops + pSector->ubNumElites + pSector->ubNumJeeps + pSector->ubNumTanks); } return ubNumTroops; } void GetNumberOfMobileEnemiesInSector( INT16 sSectorX, INT16 sSectorY, UINT8 *pubNumAdmins, UINT8 *pubNumTroops, UINT8 *pubNumElites, UINT8 *pubNumTanks, UINT8 *pubNumJeeps ) { GROUP *pGroup; SECTORINFO *pSector; AssertGE( sSectorX, MINIMUM_VALID_X_COORDINATE); AssertLE( sSectorX, MAXIMUM_VALID_X_COORDINATE ); AssertGE( sSectorY, MINIMUM_VALID_Y_COORDINATE); AssertLE( sSectorY, MAXIMUM_VALID_Y_COORDINATE ); //Now count the number of mobile groups in the sector. *pubNumTroops = *pubNumElites = *pubNumAdmins = *pubNumTanks = *pubNumJeeps =0; pGroup = gpGroupList; while( pGroup ) { if ( pGroup->usGroupTeam != OUR_TEAM && pGroup->usGroupTeam != MILITIA_TEAM && !pGroup->fVehicle && pGroup->ubSectorX == sSectorX && pGroup->ubSectorY == sSectorY ) { *pubNumTroops += pGroup->pEnemyGroup->ubNumTroops; *pubNumElites += pGroup->pEnemyGroup->ubNumElites; *pubNumAdmins += pGroup->pEnemyGroup->ubNumAdmins; *pubNumTanks += pGroup->pEnemyGroup->ubNumTanks; *pubNumJeeps += pGroup->pEnemyGroup->ubNumJeeps; } pGroup = pGroup->next; } pSector = &SectorInfo[ SECTOR( sSectorX, sSectorY ) ]; if( pSector->ubGarrisonID == ROADBLOCK ) { //consider these troops as mobile troops even though they are in a garrison *pubNumAdmins += pSector->ubNumAdmins; *pubNumTroops += pSector->ubNumTroops; *pubNumElites += pSector->ubNumElites; *pubNumTanks += pSector->ubNumTanks; *pubNumJeeps += pSector->ubNumJeeps; } } void GetNumberOfMobileEnemiesInSectorWithoutRoadBlock( INT16 sSectorX, INT16 sSectorY, UINT8 usTeam, UINT8 *pubNumAdmins, UINT8 *pubNumTroops, UINT8 *pubNumElites, UINT8 *pubNumTanks, UINT8 *pubNumJeeps ) { GROUP *pGroup; AssertGE( sSectorX, MINIMUM_VALID_X_COORDINATE); AssertLE( sSectorX, MAXIMUM_VALID_X_COORDINATE ); AssertGE( sSectorY, MINIMUM_VALID_Y_COORDINATE); AssertLE( sSectorY, MAXIMUM_VALID_Y_COORDINATE ); //Now count the number of mobile groups in the sector. *pubNumTroops = *pubNumElites = *pubNumAdmins = *pubNumTanks = *pubNumJeeps = 0; pGroup = gpGroupList; while( pGroup ) { if ( pGroup->usGroupTeam == usTeam && !pGroup->fVehicle && pGroup->ubSectorX == sSectorX && pGroup->ubSectorY == sSectorY ) { *pubNumTroops += pGroup->pEnemyGroup->ubNumTroops; *pubNumElites += pGroup->pEnemyGroup->ubNumElites; *pubNumAdmins += pGroup->pEnemyGroup->ubNumAdmins; *pubNumTanks += pGroup->pEnemyGroup->ubNumTanks; *pubNumJeeps += pGroup->pEnemyGroup->ubNumJeeps; } pGroup = pGroup->next; } } void GetNumberOfStationaryEnemiesInSector( INT16 sSectorX, INT16 sSectorY, UINT8 *pubNumAdmins, UINT8 *pubNumTroops, UINT8 *pubNumElites, UINT8 *pubNumTanks, UINT8 *pubNumJeeps ) { SECTORINFO *pSector; AssertGE( sSectorX, MINIMUM_VALID_X_COORDINATE); AssertLE( sSectorX, MAXIMUM_VALID_X_COORDINATE ); AssertGE( sSectorY, MINIMUM_VALID_Y_COORDINATE); AssertLE( sSectorY, MAXIMUM_VALID_Y_COORDINATE ); pSector = &SectorInfo[ SECTOR( sSectorX, sSectorY ) ]; //grab the number of each type in the stationary sector *pubNumAdmins = pSector->ubNumAdmins; *pubNumTroops = pSector->ubNumTroops; *pubNumElites = pSector->ubNumElites; *pubNumTanks = pSector->ubNumTanks; *pubNumJeeps = pSector->ubNumJeeps; } void GetNumberOfEnemiesInSector( INT16 sSectorX, INT16 sSectorY, UINT8 *pubNumAdmins, UINT8 *pubNumTroops, UINT8 *pubNumElites, UINT8 *pubNumTanks, UINT8 *pubNumJeeps ) { UINT8 ubNumAdmins, ubNumTroops, ubNumElites, ubNumTanks, ubNumJeeps; GetNumberOfStationaryEnemiesInSector( sSectorX, sSectorY, pubNumAdmins, pubNumTroops, pubNumElites, pubNumTanks, pubNumJeeps ); GetNumberOfMobileEnemiesInSector( sSectorX, sSectorY, &ubNumAdmins, &ubNumTroops, &ubNumElites, &ubNumTanks, &ubNumJeeps ); *pubNumAdmins += ubNumAdmins; *pubNumTroops += ubNumTroops; *pubNumElites += ubNumElites; *pubNumTanks += ubNumTanks; *pubNumJeeps += ubNumJeeps; } void EndTacticalBattleForEnemy() { GROUP *pGroup; INT32 i; //Clear enemies in battle for all stationary groups in the sector. if( gbWorldSectorZ > 0 ) { UNDERGROUND_SECTORINFO *pSector; pSector = FindUnderGroundSector( gWorldSectorX, gWorldSectorY, gbWorldSectorZ ); pSector->ubAdminsInBattle = 0; pSector->ubTroopsInBattle = 0; pSector->ubElitesInBattle = 0; pSector->ubTanksInBattle = 0; pSector->ubJeepsInBattle = 0; } else if( !gbWorldSectorZ ) { SECTORINFO *pSector; pSector = &SectorInfo[ SECTOR( gWorldSectorX, gWorldSectorY ) ]; //grab the number of each type in the stationary sector pSector->ubAdminsInBattle = 0; pSector->ubTroopsInBattle = 0; pSector->ubElitesInBattle = 0; pSector->ubTanksInBattle = 0; pSector->ubJeepsInBattle = 0; pSector->ubNumCreatures = 0; pSector->ubCreaturesInBattle = 0; } else // negative return; //Clear this value so that profiled enemies can be added into battles in the future. gfProfiledEnemyAdded = FALSE; //Clear enemies in battle for all mobile groups in the sector. pGroup = gpGroupList; while( pGroup ) { if ( pGroup->usGroupTeam == ENEMY_TEAM && !pGroup->fVehicle && pGroup->ubSectorX == gWorldSectorX && pGroup->ubSectorY == gWorldSectorY ) { pGroup->pEnemyGroup->ubTroopsInBattle = 0; pGroup->pEnemyGroup->ubElitesInBattle = 0; pGroup->pEnemyGroup->ubAdminsInBattle = 0; pGroup->pEnemyGroup->ubTanksInBattle = 0; pGroup->pEnemyGroup->ubJeepsInBattle = 0; } pGroup = pGroup->next; } //Check to see if any of our mercs have abandoned the militia during a battle. This is cause for a rather //severe loyalty blow. for( i = gTacticalStatus.Team[ MILITIA_TEAM ].bFirstID; i <= gTacticalStatus.Team[ MILITIA_TEAM ].bLastID; ++i ) { if( MercPtrs[ i ]->bActive && MercPtrs[ i ]->bInSector && MercPtrs[ i ]->stats.bLife >= OKLIFE ) { //found one live militia, so look for any enemies/creatures. // NOTE: this is relying on ENEMY_TEAM being immediately followed by CREATURE_TEAM for( i = gTacticalStatus.Team[ ENEMY_TEAM ].bFirstID; i <= gTacticalStatus.Team[ CREATURE_TEAM ].bLastID; ++i ) { if( MercPtrs[ i ]->bActive && MercPtrs[ i ]->bInSector && MercPtrs[ i ]->stats.bLife >= OKLIFE ) { //confirmed at least one enemy here, so do the loyalty penalty. HandleGlobalLoyaltyEvent( GLOBAL_LOYALTY_ABANDON_MILITIA, gWorldSectorX, gWorldSectorY, 0 ); break; } } break; } } } UINT8 NumFreeSlots( UINT8 ubTeam ) { UINT8 ubNumFreeSlots = 0; //Count the number of free enemy slots. It is possible to have multiple groups exceed the maximum. for ( INT32 i = gTacticalStatus.Team[ubTeam].bFirstID; i <= gTacticalStatus.Team[ubTeam].bLastID; ++i ) { if ( !Menptr[i].bActive ) ++ubNumFreeSlots; } // the militia team size can be restricted by the ini if ( ubTeam == MILITIA_TEAM ) { ubNumFreeSlots = (UINT8)max( 0, (INT8)ubNumFreeSlots - (INT8)(gGameExternalOptions.ubGameMaximumNumberOfRebels - gGameExternalOptions.iMaxMilitiaPerSector) ); } return ubNumFreeSlots; } //Called when entering a sector so the campaign AI can automatically insert the //correct number of troops of each type based on the current number in the sector //in global focus (gWorldSectorX/Y) BOOLEAN PrepareEnemyForSectorBattle() { SECTORINFO *pSector; GROUP *pGroup; SOLDIERTYPE *pSoldier; unsigned ubNumAdmins, ubNumTroops, ubNumElites, ubNumTanks, ubNumJeeps; unsigned ubTotalAdmins, ubTotalElites, ubTotalTroops, ubTotalTanks, ubTotalJeeps = 0; unsigned totalCountOfStationaryEnemies = 0; unsigned totalCountOfMobileEnemies = 0; int sNumSlots; gfPendingNonPlayerTeam[ENEMY_TEAM] = FALSE; if( gbWorldSectorZ > 0 ) return PrepareEnemyForUndergroundBattle(); // Add the invading group if ( gpBattleGroup && gpBattleGroup->usGroupTeam == ENEMY_TEAM ) { //The enemy has instigated the battle which means they are the ones entering the conflict. //The player was actually in the sector first, and the enemy doesn't use reinforced placements HandleArrivalOfReinforcements( gpBattleGroup ); // Reinforcement groups? Bring it on! // only if not Omerta on a non-insane difficulty level if( gGameExternalOptions.gfAllowReinforcements && !( ( GetTownIdForSector( gWorldSectorX, gWorldSectorY ) == OMERTA) && (gGameOptions.ubDifficultyLevel != DIF_LEVEL_INSANE) ) ) { UINT16 pusMoveDir[4][3]; UINT8 ubDirNumber = 0; GROUP *pGroup; SECTORINFO *pThisSector; // They arrived in multiple groups, so here they come pThisSector = &SectorInfo[ SECTOR( gWorldSectorX, gWorldSectorY ) ]; GenerateDirectionInfos( gWorldSectorX, gWorldSectorY, &ubDirNumber, pusMoveDir, FALSE, TRUE ); for( unsigned ubIndex = 0; ubIndex < ubDirNumber; ++ubIndex ) { while ( NumMobileEnemiesInSector( SECTORX( pusMoveDir[ubIndex][0] ), SECTORY( pusMoveDir[ubIndex][0] ) ) && GetNonPlayerGroupInSector( SECTORX( pusMoveDir[ubIndex][0] ), SECTORY( pusMoveDir[ubIndex][0] ), ENEMY_TEAM ) ) { pGroup = GetNonPlayerGroupInSector( SECTORX( pusMoveDir[ubIndex][0] ), SECTORY( pusMoveDir[ubIndex][0] ), ENEMY_TEAM ); pGroup->ubPrevX = pGroup->ubSectorX; pGroup->ubPrevY = pGroup->ubSectorY; pGroup->ubSectorX = pGroup->ubNextX = (UINT8)gWorldSectorX; pGroup->ubSectorY = pGroup->ubNextY = (UINT8)gWorldSectorY; } } //It is now possible that other enemy groups have also arrived. Add them in the same manner. pGroup = gpGroupList; while( pGroup ) { if ( pGroup != gpBattleGroup && pGroup->usGroupTeam != OUR_TEAM && !pGroup->fVehicle && pGroup->ubSectorX == gpBattleGroup->ubSectorX && pGroup->ubSectorY == gpBattleGroup->ubSectorY && !pGroup->pEnemyGroup->ubAdminsInBattle && !pGroup->pEnemyGroup->ubTroopsInBattle && !pGroup->pEnemyGroup->ubElitesInBattle && !pGroup->pEnemyGroup->ubTanksInBattle && !pGroup->pEnemyGroup->ubJeepsInBattle ) { HandleArrivalOfReinforcements( pGroup ); } pGroup = pGroup->next; } } ValidateEnemiesHaveWeapons(); UnPauseGame(); return ( ( BOOLEAN) ( gpBattleGroup->ubGroupSize > 0 ) ); } // WDS Count the number of placements. This will limit the maximum number of enemies we can place on the map SOLDIERINITNODE *curr = gSoldierInitHead; unsigned mapMaximumNumberOfEnemies = 0; while( curr ) { if( curr->pBasicPlacement->bTeam == ENEMY_TEAM ) { ++mapMaximumNumberOfEnemies; } curr = curr->next; } AssertGE (mapMaximumNumberOfEnemies, 0); if (mapMaximumNumberOfEnemies > gGameExternalOptions.ubGameMaximumNumberOfEnemies) mapMaximumNumberOfEnemies = gGameExternalOptions.ubGameMaximumNumberOfEnemies; //is there more enemies in the sector then we can actually place on map? //are there more enemies in the sector then we can actually place on map? gfPendingNonPlayerTeam[ENEMY_TEAM] = (NumNonPlayerTeamMembersInSector( gWorldSectorX, gWorldSectorY, ENEMY_TEAM ) > mapMaximumNumberOfEnemies); pSector = &SectorInfo[ SECTOR( gWorldSectorX, gWorldSectorY ) ]; // OJW - 20090403 - override max ai in co-op if (is_networked && is_server && gMaxEnemiesEnabled == 1) { float totalEnemies = (float)(pSector->ubNumAdmins + pSector->ubNumTroops + pSector->ubNumElites + pSector->ubNumTanks + pSector->ubNumJeeps); ubTotalAdmins = (unsigned int)(((float)pSector->ubNumAdmins / totalEnemies) * mapMaximumNumberOfEnemies); ubTotalTroops = (unsigned int)(((float)pSector->ubNumTroops / totalEnemies) * mapMaximumNumberOfEnemies); ubTotalElites = (unsigned int)(((float)pSector->ubNumElites / totalEnemies) * mapMaximumNumberOfEnemies); ubTotalTanks = (unsigned int)(((float)pSector->ubNumTanks / totalEnemies) * mapMaximumNumberOfEnemies); ubTotalJeeps = (unsigned int)(((float)pSector->ubNumJeeps / totalEnemies) * mapMaximumNumberOfEnemies); totalEnemies = (float)(ubTotalAdmins + ubTotalTroops + ubTotalElites + ubTotalTanks + ubTotalJeeps); // take care of any rounding losses while (totalEnemies < mapMaximumNumberOfEnemies) { // just fill out with standard troops ++ubTotalTroops; ++totalEnemies; } pSector->ubNumAdmins = ubTotalAdmins; pSector->ubNumTroops = ubTotalTroops; pSector->ubNumElites = ubTotalElites; pSector->ubNumTanks = ubTotalTanks; pSector->ubNumJeeps = ubTotalJeeps; pSector->ubAdminsInBattle = 0; pSector->ubTroopsInBattle = 0; pSector->ubElitesInBattle = 0; pSector->ubTanksInBattle = 0; pSector->ubJeepsInBattle = 0; } else { if( pSector->uiFlags & SF_USE_MAP_SETTINGS ) { //count the number of enemy placements in a map and use those SOLDIERINITNODE *curr = gSoldierInitHead; ubTotalAdmins = ubTotalTroops = ubTotalElites = ubTotalTanks = 0; while( curr ) { if( curr->pBasicPlacement->bTeam == ENEMY_TEAM ) { switch( curr->pBasicPlacement->ubSoldierClass ) { case SOLDIER_CLASS_ADMINISTRATOR: ubTotalAdmins++; break; case SOLDIER_CLASS_ARMY: ubTotalTroops++; break; case SOLDIER_CLASS_ELITE: ubTotalElites++; break; case SOLDIER_CLASS_TANK: ubTotalTanks++; break; case SOLDIER_CLASS_JEEP: ubTotalJeeps++; break; } } curr = curr->next; } pSector->ubNumAdmins = ubTotalAdmins; pSector->ubNumTroops = ubTotalTroops; pSector->ubNumElites = ubTotalElites; pSector->ubNumTanks = ubTotalTanks; pSector->ubNumJeeps = ubTotalJeeps; pSector->ubAdminsInBattle = 0; pSector->ubTroopsInBattle = 0; pSector->ubElitesInBattle = 0; pSector->ubTanksInBattle = 0; pSector->ubJeepsInBattle = 0; } else { ubTotalAdmins = pSector->ubNumAdmins - pSector->ubAdminsInBattle; ubTotalTroops = pSector->ubNumTroops - pSector->ubTroopsInBattle; ubTotalElites = pSector->ubNumElites - pSector->ubElitesInBattle; ubTotalTanks = pSector->ubNumTanks - pSector->ubTanksInBattle; ubTotalJeeps = pSector->ubNumJeeps - pSector->ubJeepsInBattle; } } totalCountOfStationaryEnemies = ubTotalAdmins + ubTotalTroops + ubTotalElites + ubTotalTanks + ubTotalJeeps; if( totalCountOfStationaryEnemies > mapMaximumNumberOfEnemies ) { /* #ifdef JA2BETAVERSION ScreenMsg( FONT_RED, MSG_ERROR, L"The total stationary enemy forces in sector %c%d is %d. (max %d)", gWorldSectorY + 'A' - 1, gWorldSectorX, ubTotalAdmins + ubTotalTroops + ubTotalElites, mapMaximumNumberOfEnemies ); #endif */ ubTotalAdmins = min( mapMaximumNumberOfEnemies, ubTotalAdmins ); ubTotalTroops = min( mapMaximumNumberOfEnemies-ubTotalAdmins, ubTotalTroops ); ubTotalElites = min( mapMaximumNumberOfEnemies-ubTotalAdmins-ubTotalTroops, ubTotalElites ); ubTotalTanks = min( mapMaximumNumberOfEnemies-ubTotalAdmins-ubTotalTroops-ubTotalElites, ubTotalTanks ); ubTotalJeeps = min( mapMaximumNumberOfEnemies - ubTotalAdmins - ubTotalTroops - ubTotalElites - ubTotalTanks, ubTotalJeeps ); } pSector->ubAdminsInBattle += ubTotalAdmins; pSector->ubTroopsInBattle += ubTotalTroops; pSector->ubElitesInBattle += ubTotalElites; pSector->ubTanksInBattle += ubTotalTanks; pSector->ubJeepsInBattle += ubTotalJeeps; #ifdef JA2TESTVERSION if( gfOverrideSector ) { //if there are no troops in the current groups, then we're done. if ( !ubTotalAdmins && !ubTotalTroops && !ubTotalElites && !ubTotalTanks && !ubTotalJeeps ) return FALSE; AddSoldierInitListEnemyDefenceSoldiers( ubTotalAdmins, ubTotalTroops, ubTotalElites, ubTotalTanks, ubTotalJeeps ); ValidateEnemiesHaveWeapons(); UnPauseGame(); return TRUE; } #endif //Search for movement groups that happen to be in the sector. sNumSlots = NumFreeSlots( ENEMY_TEAM ); if (sNumSlots > (int)mapMaximumNumberOfEnemies) sNumSlots = mapMaximumNumberOfEnemies; /* * WDS - this test no longer valid with the above change //Test: All slots should be free at this point! if( sNumSlots != gTacticalStatus.Team[ENEMY_TEAM].bLastID - gTacticalStatus.Team[ENEMY_TEAM].bFirstID + 1 ) { #ifdef JA2BETAVERSION ScreenMsg( FONT_RED, MSG_ERROR, L"All enemy slots should be free at this point. Only %d of %d are available.\nTrying to add %d admins, %d troops, and %d elites.", sNumSlots, gTacticalStatus.Team[ENEMY_TEAM].bLastID - gTacticalStatus.Team[ENEMY_TEAM].bFirstID + 1 , ubTotalAdmins, ubTotalTroops, ubTotalElites ); #endif } */ //Subtract the total number of stationary enemies from the available slots, as stationary forces take //precendence in combat. The mobile forces that could also be in the same sector are considered later if //all the slots fill up. sNumSlots -= ubTotalAdmins + ubTotalTroops + ubTotalElites + ubTotalTanks + ubTotalJeeps; //Now, process all of the groups and search for both enemy and player groups in the sector. //For enemy groups, we fill up the slots until we have none left or all of the groups have been //processed. for( pGroup = gpGroupList; pGroup; pGroup = pGroup->next) { if ( pGroup->usGroupTeam == ENEMY_TEAM && !pGroup->fVehicle && pGroup->ubSectorX == gWorldSectorX && pGroup->ubSectorY == gWorldSectorY && !gbWorldSectorZ ) { //Process enemy group in sector. if( sNumSlots > 0 ) { AssertGE(pGroup->pEnemyGroup->ubNumAdmins, pGroup->pEnemyGroup->ubAdminsInBattle); ubNumAdmins = pGroup->pEnemyGroup->ubNumAdmins - pGroup->pEnemyGroup->ubAdminsInBattle; sNumSlots -= ubNumAdmins; if( sNumSlots < 0 ) { //adjust the value to zero ubNumAdmins += sNumSlots; sNumSlots = 0; gfPendingNonPlayerTeam[ENEMY_TEAM] = TRUE; } pGroup->pEnemyGroup->ubAdminsInBattle += ubNumAdmins; ubTotalAdmins += ubNumAdmins; } if( sNumSlots > 0 ) { //Add regular army forces. AssertGE(pGroup->pEnemyGroup->ubNumTroops, pGroup->pEnemyGroup->ubTroopsInBattle); ubNumTroops = pGroup->pEnemyGroup->ubNumTroops - pGroup->pEnemyGroup->ubTroopsInBattle; sNumSlots -= ubNumTroops; if( sNumSlots < 0 ) { //adjust the value to zero ubNumTroops += sNumSlots; sNumSlots = 0; gfPendingNonPlayerTeam[ENEMY_TEAM] = TRUE; } pGroup->pEnemyGroup->ubTroopsInBattle += ubNumTroops; ubTotalTroops += ubNumTroops; } if( sNumSlots > 0 ) { //Add elite troops AssertGE(pGroup->pEnemyGroup->ubNumElites, pGroup->pEnemyGroup->ubElitesInBattle); ubNumElites = pGroup->pEnemyGroup->ubNumElites - pGroup->pEnemyGroup->ubElitesInBattle; sNumSlots -= ubNumElites; if( sNumSlots < 0 ) { //adjust the value to zero ubNumElites += sNumSlots; sNumSlots = 0; gfPendingNonPlayerTeam[ENEMY_TEAM] = TRUE; } pGroup->pEnemyGroup->ubElitesInBattle += ubNumElites; ubTotalElites += ubNumElites; } if( sNumSlots > 0 ) { //Add tanks AssertGE(pGroup->pEnemyGroup->ubNumTanks, pGroup->pEnemyGroup->ubTanksInBattle); ubNumTanks = pGroup->pEnemyGroup->ubNumTanks - pGroup->pEnemyGroup->ubTanksInBattle; sNumSlots -= ubNumTanks; if( sNumSlots < 0 ) { //adjust the value to zero ubNumTanks += sNumSlots; sNumSlots = 0; gfPendingNonPlayerTeam[ENEMY_TEAM] = TRUE; } pGroup->pEnemyGroup->ubTanksInBattle += ubNumTanks; ubTotalTanks += ubNumTanks; } if ( sNumSlots > 0 ) { //Add jeeps AssertGE( pGroup->pEnemyGroup->ubNumJeeps, pGroup->pEnemyGroup->ubJeepsInBattle ); ubNumJeeps = pGroup->pEnemyGroup->ubNumJeeps - pGroup->pEnemyGroup->ubJeepsInBattle; sNumSlots -= ubNumJeeps; if ( sNumSlots < 0 ) { //adjust the value to zero ubNumJeeps += sNumSlots; sNumSlots = 0; gfPendingNonPlayerTeam[ENEMY_TEAM] = TRUE; } pGroup->pEnemyGroup->ubJeepsInBattle += ubNumJeeps; ubTotalJeeps += ubNumJeeps; } //NOTE: //no provisions for profile troop leader or retreat groups yet. } if ( pGroup->usGroupTeam == OUR_TEAM && !pGroup->fVehicle && !pGroup->fBetweenSectors && pGroup->ubSectorX == gWorldSectorX && pGroup->ubSectorY == gWorldSectorY && !gbWorldSectorZ ) { //TEMP: The player path needs to get destroyed, otherwise, it'll be impossible to move the // group after the battle is resolved. // no one in the group any more continue loop if( pGroup->pPlayerList == NULL ) { continue; } // clear the movt for this grunt and his buddies RemoveGroupWaypoints( pGroup->ubGroupID ); } } //if there are no troops in the current groups, then we're done. if ( !ubTotalAdmins && !ubTotalTroops && !ubTotalElites && !ubTotalTanks && !ubTotalJeeps ) { return FALSE; } AddSoldierInitListEnemyDefenceSoldiers( ubTotalAdmins, ubTotalTroops, ubTotalElites, ubTotalTanks, ubTotalJeeps ); //Now, we have to go through all of the enemies in the new map, and assign their respective groups if //in a mobile group, but only for the ones that were assigned from the //sNumSlots = mapMaximumNumberOfEnemies - totalCountOfStationaryEnemies; // Flugente: at this point, soldiers have already been placed, thus enough slots for them exist sNumSlots = min( mapMaximumNumberOfEnemies, NumNonPlayerTeamMembersInSector( gWorldSectorX, gWorldSectorY, ENEMY_TEAM ) ); pGroup = gpGroupList; while( pGroup && sNumSlots > 0 ) { if ( pGroup->usGroupTeam != OUR_TEAM && !pGroup->fVehicle && pGroup->ubSectorX == gWorldSectorX && pGroup->ubSectorY == gWorldSectorY && !gbWorldSectorZ ) { ubNumAdmins = pGroup->pEnemyGroup->ubAdminsInBattle; ubNumTroops = pGroup->pEnemyGroup->ubTroopsInBattle; ubNumElites = pGroup->pEnemyGroup->ubElitesInBattle; ubNumTanks = pGroup->pEnemyGroup->ubTanksInBattle; ubNumJeeps = pGroup->pEnemyGroup->ubJeepsInBattle; unsigned num = ubNumAdmins + ubNumTroops + ubNumElites + ubNumTanks + ubNumJeeps; unsigned firstSlot = gTacticalStatus.Team[ ENEMY_TEAM ].bFirstID; unsigned lastSlot = gTacticalStatus.Team[ ENEMY_TEAM ].bLastID; unsigned slotsAvailable = lastSlot-firstSlot+1; AssertGE((int)slotsAvailable, sNumSlots); for (unsigned slot = firstSlot; (slot <= lastSlot) && num && sNumSlots; ++slot) { pSoldier = &Menptr[ slot ]; // Skip inactive and already grouped soldiers if (!pSoldier->bActive || pSoldier->ubGroupID) { // if this guy already has an ID, reduce the number of people who still need one --num; --sNumSlots; continue; } // At this point we should not have added more soldiers than are in slots AssertGT( sNumSlots, 0 ); switch( pSoldier->ubSoldierClass ) { case SOLDIER_CLASS_ADMINISTRATOR: if( ubNumAdmins ) { num--; sNumSlots--; ubNumAdmins--; pSoldier->ubGroupID = pGroup->ubGroupID; } break; case SOLDIER_CLASS_ARMY: if( ubNumTroops ) { num--; sNumSlots--; ubNumTroops--; pSoldier->ubGroupID = pGroup->ubGroupID; } break; case SOLDIER_CLASS_ELITE: if( ubNumElites ) { num--; sNumSlots--; ubNumElites--; pSoldier->ubGroupID = pGroup->ubGroupID; } break; // silversurfer: bugfix for Jaggzilla bug #623 // Mike or Iggy can be part of the enemy team and they are created from an Elite but they don't have SOLDIER_CLASS_ELITE. // Therefore once this for loop was done ubNumElites was still 1 which caused an assertion error. case SOLDIER_CLASS_NONE: if( ubNumElites ) { if ( pSoldier->ubProfile == MIKE || pSoldier->ubProfile == IGGY ) { num--; sNumSlots--; ubNumElites--; pSoldier->ubGroupID = pGroup->ubGroupID; } } break; case SOLDIER_CLASS_TANK: if( ubNumTanks ) { num--; sNumSlots--; ubNumTanks--; pSoldier->ubGroupID = pGroup->ubGroupID; } break; case SOLDIER_CLASS_JEEP: if ( ubNumJeeps ) { num--; sNumSlots--; ubNumJeeps--; pSoldier->ubGroupID = pGroup->ubGroupID; } break; } } // Flugente: instead of just crashing the game without any explanation to the user, ignore this issue if it still exists. // The worst that should happen is a warning that a soldier has no group id. /*AssertEQ( ubNumElites , 0); AssertEQ( ubNumTroops , 0); AssertEQ( ubNumAdmins , 0); AssertEQ( ubNumTanks , 0); AssertEQ( ubNumJeeps , 0); AssertEQ( num , 0);*/ } pGroup = pGroup->next; } ValidateEnemiesHaveWeapons(); UnPauseGame(); return TRUE; } BOOLEAN PrepareEnemyForUndergroundBattle() { UNDERGROUND_SECTORINFO *pUnderground; unsigned ubTotalAdmins, ubTotalTroops, ubTotalElites, ubTotalTanks, ubTotalJeeps; pUnderground = gpUndergroundSectorInfoHead; while( pUnderground ) { if( pUnderground->ubSectorX == gWorldSectorX && pUnderground->ubSectorY == gWorldSectorY && pUnderground->ubSectorZ == gbWorldSectorZ ) { //This is the sector we are going to be fighting in. if( pUnderground->ubNumAdmins || pUnderground->ubNumTroops || pUnderground->ubNumElites ) { ubTotalAdmins = (UINT8)(pUnderground->ubNumAdmins - pUnderground->ubAdminsInBattle); ubTotalTroops = (UINT8)(pUnderground->ubNumTroops - pUnderground->ubTroopsInBattle); ubTotalElites = (UINT8)(pUnderground->ubNumElites - pUnderground->ubElitesInBattle); ubTotalTanks = (UINT8)(pUnderground->ubNumTanks - pUnderground->ubTanksInBattle); ubTotalJeeps = (UINT8)(pUnderground->ubNumJeeps - pUnderground->ubJeepsInBattle); pUnderground->ubAdminsInBattle += ubTotalAdmins; pUnderground->ubTroopsInBattle += ubTotalTroops; pUnderground->ubElitesInBattle += ubTotalElites; pUnderground->ubTanksInBattle += ubTotalTanks; pUnderground->ubJeepsInBattle += ubTotalJeeps; AddSoldierInitListEnemyDefenceSoldiers( pUnderground->ubNumAdmins, pUnderground->ubNumTroops, pUnderground->ubNumElites, pUnderground->ubNumTanks, pUnderground->ubNumJeeps ); ValidateEnemiesHaveWeapons(); UnPauseGame(); } return ((BOOLEAN)(pUnderground->ubNumAdmins + pUnderground->ubNumTroops + pUnderground->ubNumElites + pUnderground->ubNumTanks + pUnderground->ubNumJeeps > 0)); } pUnderground = pUnderground->next; } // underground sector not found in list Assert( FALSE ); return FALSE; } //The queen AI layer must process the event by subtracting forces, etc. void ProcessQueenCmdImplicationsOfDeath( SOLDIERTYPE *pSoldier ) { INT32 iNumEnemiesInSector; SECTORINFO *pSector; INT32 iMaxEnemyGroupSize = gGameExternalOptions.iMaxEnemyGroupSize; DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"QueenCommand"); AssertNotNIL (pSoldier); EvaluateDeathEffectsToSoldierInitList( pSoldier ); switch( pSoldier->ubProfile ) { #ifdef JA2UB case 75://MORRIS: if( !pSoldier->bSectorZ ) { pSector = &SectorInfo[ SECTOR( pSoldier->sSectorX, pSoldier->sSectorY ) ]; if( pSector->ubNumElites ) { pSector->ubNumElites--; } if( pSector->ubElitesInBattle ) { pSector->ubElitesInBattle--; } } else { UNDERGROUND_SECTORINFO *pUnderground; pUnderground = FindUnderGroundSector( (UINT8)pSoldier->sSectorX, (UINT8)pSoldier->sSectorY, (UINT8)pSoldier->bSectorZ ); Assert( pUnderground ); if( pUnderground->ubNumElites ) { pUnderground->ubNumElites--; } if( pUnderground->ubElitesInBattle ) { pUnderground->ubElitesInBattle--; } } break; #endif case MIKE: case IGGY: if( pSoldier->ubProfile == IGGY && !gubFact[ FACT_IGGY_AVAILABLE_TO_ARMY ] ) { //Iggy is on our team! break; } if( !pSoldier->bSectorZ ) { pSector = &SectorInfo[ SECTOR( pSoldier->sSectorX, pSoldier->sSectorY ) ]; if( pSector->ubNumElites ) { pSector->ubNumElites--; } if( pSector->ubElitesInBattle ) { pSector->ubElitesInBattle--; } } else { UNDERGROUND_SECTORINFO *pUnderground; pUnderground = FindUnderGroundSector( (UINT8)pSoldier->sSectorX, (UINT8)pSoldier->sSectorY, (UINT8)pSoldier->bSectorZ ); Assert( pUnderground ); if( pUnderground->ubNumElites ) { pUnderground->ubNumElites--; } if( pUnderground->ubElitesInBattle ) { pUnderground->ubElitesInBattle--; } } break; case TANK_CAR: if( !pSoldier->bSectorZ ) { pSector = &SectorInfo[ SECTOR( pSoldier->sSectorX, pSoldier->sSectorY ) ]; if( pSector->ubNumTanks ) { pSector->ubNumTanks--; } if( pSector->ubTanksInBattle ) { pSector->ubTanksInBattle--; } } else { UNDERGROUND_SECTORINFO *pUnderground; pUnderground = FindUnderGroundSector( (UINT8)pSoldier->sSectorX, (UINT8)pSoldier->sSectorY, (UINT8)pSoldier->bSectorZ ); Assert( pUnderground ); if( pUnderground->ubNumTanks) { pUnderground->ubNumTanks--; } if( pUnderground->ubTanksInBattle ) { pUnderground->ubTanksInBattle--; } } break; case COMBAT_JEEP_CAR: if ( !pSoldier->bSectorZ ) { pSector = &SectorInfo[SECTOR( pSoldier->sSectorX, pSoldier->sSectorY )]; if ( pSector->ubNumJeeps ) { pSector->ubNumJeeps--; } if ( pSector->ubJeepsInBattle ) { pSector->ubJeepsInBattle--; } } else { UNDERGROUND_SECTORINFO *pUnderground; pUnderground = FindUnderGroundSector( (UINT8)pSoldier->sSectorX, (UINT8)pSoldier->sSectorY, (UINT8)pSoldier->bSectorZ ); Assert( pUnderground ); if ( pUnderground->ubNumJeeps ) { pUnderground->ubNumJeeps--; } if ( pUnderground->ubJeepsInBattle ) { pUnderground->ubJeepsInBattle--; } } break; } if( pSoldier->aiData.bNeutral || pSoldier->bTeam != ENEMY_TEAM && pSoldier->bTeam != CREATURE_TEAM ) return; //we are recording an enemy death if( pSoldier->ubGroupID ) { //The enemy was in a mobile group GROUP *pGroup; pGroup = GetGroup( pSoldier->ubGroupID ); if( !pGroup ) { #ifdef JA2BETAVERSION CHAR16 str[256]; swprintf( str, L"Enemy soldier killed with ubGroupID of %d, and the group doesn't exist!", pSoldier->ubGroupID ); DoScreenIndependantMessageBox( str, MSG_BOX_FLAG_OK, NULL ); #endif return; } if ( pGroup->usGroupTeam == OUR_TEAM ) { #ifdef JA2BETAVERSION CHAR16 str[256]; swprintf( str, L"Attempting to process player group thinking it's an enemy group in ProcessQueenCmdImplicationsOfDeath()", pSoldier->ubGroupID ); DoScreenIndependantMessageBox( str, MSG_BOX_FLAG_OK, NULL ); #endif return; } else if ( pGroup->usGroupTeam == MILITIA_TEAM ) { #ifdef JA2BETAVERSION CHAR16 str[256]; swprintf( str, L"Attempting to process militia group thinking it's an enemy group in ProcessQueenCmdImplicationsOfDeath()", pSoldier->ubGroupID ); DoScreenIndependantMessageBox( str, MSG_BOX_FLAG_OK, NULL ); #endif return; } switch( pSoldier->ubSoldierClass ) { case SOLDIER_CLASS_ELITE: #ifdef JA2BETAVERSION if( !pGroup->pEnemyGroup->ubNumElites ) { //swprintf( str, L"Enemy elite killed with ubGroupID of %d, but the group doesn't contain elites!", pGroup->ubGroupID ); //DoScreenIndependantMessageBox( str, MSG_BOX_FLAG_OK, NULL ); break; } if( guiCurrentScreen == GAME_SCREEN ) { if ( pGroup->ubGroupSize <= iMaxEnemyGroupSize && pGroup->pEnemyGroup->ubNumElites != pGroup->pEnemyGroup->ubElitesInBattle && !gfPendingNonPlayerTeam[ENEMY_TEAM] || pGroup->ubGroupSize > iMaxEnemyGroupSize /* || pGroup->pEnemyGroup->ubNumElites > 50 || pGroup->pEnemyGroup->ubElitesInBattle > 50*/ ) { DoScreenIndependantMessageBox( L"Group elite counters are bad. What were the last 2-3 things to die, and how? Save game and send to KM with info!!!", MSG_BOX_FLAG_OK, NULL ); } } #endif if( pGroup->pEnemyGroup->ubNumElites ) { pGroup->pEnemyGroup->ubNumElites--; } if( pGroup->pEnemyGroup->ubElitesInBattle ) { pGroup->pEnemyGroup->ubElitesInBattle--; } break; case SOLDIER_CLASS_ARMY: #ifdef JA2BETAVERSION if( !pGroup->pEnemyGroup->ubNumTroops ) { //swprintf( str, L"Enemy troop killed with ubGroupID of %d, but the group doesn't contain elites!", pGroup->ubGroupID ); //DoScreenIndependantMessageBox( str, MSG_BOX_FLAG_OK, NULL ); break; } if( guiCurrentScreen == GAME_SCREEN ) { if ( pGroup->ubGroupSize <= iMaxEnemyGroupSize && pGroup->pEnemyGroup->ubNumTroops != pGroup->pEnemyGroup->ubTroopsInBattle && !gfPendingNonPlayerTeam[ENEMY_TEAM] || pGroup->ubGroupSize > iMaxEnemyGroupSize /*|| pGroup->pEnemyGroup->ubNumTroops > 50 || pGroup->pEnemyGroup->ubTroopsInBattle > 50*/ ) { // haydent if (!is_client) { DoScreenIndependantMessageBox( L"Group troop counters are bad. What were the last 2-3 things to die, and how? Save game and send to KM with info!!!", MSG_BOX_FLAG_OK, NULL ); } } } #endif if( pGroup->pEnemyGroup->ubNumTroops ) { pGroup->pEnemyGroup->ubNumTroops--; } if( pGroup->pEnemyGroup->ubTroopsInBattle ) { pGroup->pEnemyGroup->ubTroopsInBattle--; } break; case SOLDIER_CLASS_ADMINISTRATOR: #ifdef JA2BETAVERSION if( !pGroup->pEnemyGroup->ubNumAdmins ) { //swprintf( str, L"Enemy administrator killed with ubGroupID of %d, but the group doesn't contain elites!", pGroup->ubGroupID ); //DoScreenIndependantMessageBox( str, MSG_BOX_FLAG_OK, NULL ); break; } if( guiCurrentScreen == GAME_SCREEN ) { if ( pGroup->ubGroupSize <= iMaxEnemyGroupSize && pGroup->pEnemyGroup->ubNumAdmins != pGroup->pEnemyGroup->ubAdminsInBattle && !gfPendingNonPlayerTeam[ENEMY_TEAM] || pGroup->ubGroupSize > iMaxEnemyGroupSize /*|| pGroup->pEnemyGroup->ubNumAdmins > 50 || pGroup->pEnemyGroup->ubAdminsInBattle > 50*/ ) { DoScreenIndependantMessageBox( L"Group admin counters are bad. What were the last 2-3 things to die, and how? Save game and send to KM with info!!!", MSG_BOX_FLAG_OK, NULL ); } } #endif if( pGroup->pEnemyGroup->ubNumAdmins ) { pGroup->pEnemyGroup->ubNumAdmins--; } if( pGroup->pEnemyGroup->ubAdminsInBattle ) { pGroup->pEnemyGroup->ubAdminsInBattle--; } break; } if( pGroup->ubGroupSize ) pGroup->ubGroupSize--; RecalculateGroupWeight( pGroup ); if( !pGroup->ubGroupSize ) { RemovePGroup( pGroup ); } } else { //The enemy was in a stationary defence group if( !gbWorldSectorZ || IsAutoResolveActive() ) { //ground level (SECTORINFO) SECTORINFO *pSector; #ifdef JA2BETAVERSION UINT32 ubTotalEnemies; #endif // if( !IsAutoResolveActive() ) // { pSector = &SectorInfo[ SECTOR( pSoldier->sSectorX, pSoldier->sSectorY ) ]; // } // else // { // pSector = &SectorInfo[ GetAutoResolveSectorID() ]; // } #ifdef JA2BETAVERSION ubTotalEnemies = pSector->ubNumAdmins + pSector->ubNumTroops + pSector->ubNumElites + pSector->ubNumTanks + pSector->ubNumJeeps; #endif switch( pSoldier->ubSoldierClass ) { case SOLDIER_CLASS_ADMINISTRATOR: #ifdef JA2BETAVERSION if( guiCurrentScreen == GAME_SCREEN ) { if( ubTotalEnemies <= gGameExternalOptions.ubGameMaximumNumberOfEnemies && pSector->ubNumAdmins != pSector->ubAdminsInBattle || !pSector->ubNumAdmins || !pSector->ubAdminsInBattle || pSector->ubNumAdmins > gGameExternalOptions.ubGameMaximumNumberOfEnemies || pSector->ubAdminsInBattle > gGameExternalOptions.ubGameMaximumNumberOfEnemies ) { DoScreenIndependantMessageBox( L"Sector admin counters are bad. What were the last 2-3 things to die, and how? Save game and send to KM with info!!!", MSG_BOX_FLAG_OK, NULL ); } } #endif if( pSector->ubNumAdmins ) { pSector->ubNumAdmins--; } if( pSector->ubAdminsInBattle ) { pSector->ubAdminsInBattle--; } break; case SOLDIER_CLASS_ARMY: #ifdef JA2BETAVERSION if( guiCurrentScreen == GAME_SCREEN ) { if( ubTotalEnemies <= gGameExternalOptions.ubGameMaximumNumberOfEnemies && pSector->ubNumTroops != pSector->ubTroopsInBattle || !pSector->ubNumTroops || !pSector->ubTroopsInBattle || pSector->ubNumTroops > gGameExternalOptions.ubGameMaximumNumberOfEnemies || pSector->ubTroopsInBattle > gGameExternalOptions.ubGameMaximumNumberOfEnemies ) { if(!is_client)DoScreenIndependantMessageBox( L"Sector troop counters are bad. What were the last 2-3 things to die, and how? Save game and send to KM with info!!!", MSG_BOX_FLAG_OK, NULL ); //disabled: hayden. } } #endif if( pSector->ubNumTroops ) { pSector->ubNumTroops--; } if( pSector->ubTroopsInBattle ) { pSector->ubTroopsInBattle--; } break; case SOLDIER_CLASS_ELITE: #ifdef JA2BETAVERSION if( guiCurrentScreen == GAME_SCREEN ) { if( ubTotalEnemies <= gGameExternalOptions.ubGameMaximumNumberOfEnemies && pSector->ubNumElites != pSector->ubElitesInBattle || !pSector->ubNumElites || !pSector->ubElitesInBattle || pSector->ubNumElites > gGameExternalOptions.ubGameMaximumNumberOfEnemies || pSector->ubElitesInBattle > gGameExternalOptions.ubGameMaximumNumberOfEnemies ) { DoScreenIndependantMessageBox( L"Sector elite counters are bad. What were the last 2-3 things to die, and how? Save game and send to KM with info!!!", MSG_BOX_FLAG_OK, NULL ); } } #endif if( pSector->ubNumElites ) { pSector->ubNumElites--; } if( pSector->ubElitesInBattle ) { pSector->ubElitesInBattle--; } break; case SOLDIER_CLASS_CREATURE: if( pSoldier->ubBodyType != BLOODCAT ) { #ifdef JA2BETAVERSION if( guiCurrentScreen == GAME_SCREEN ) { if( ubTotalEnemies <= (UINT32)iMaxEnemyGroupSize && pSector->ubNumCreatures != pSector->ubCreaturesInBattle || !pSector->ubNumCreatures || !pSector->ubCreaturesInBattle || pSector->ubNumCreatures > 50 || pSector->ubCreaturesInBattle > 50 ) { DoScreenIndependantMessageBox( L"Sector creature counters are bad. What were the last 2-3 things to die, and how? Save game and send to KM with info!!!", MSG_BOX_FLAG_OK, NULL ); } } #endif if( pSector->ubNumCreatures ) { pSector->ubNumCreatures--; } if( pSector->ubCreaturesInBattle ) { pSector->ubCreaturesInBattle--; } } else { if( pSector->bBloodCats ) { pSector->bBloodCats--; } #ifdef JA2UB //JA25 UB //handle anything important when bloodcats die HandleBloodCatDeaths( pSector ); #endif } break; } RecalculateSectorWeight( (UINT8)SECTOR( pSoldier->sSectorX, pSoldier->sSectorY ) ); } else { //basement level (UNDERGROUND_SECTORINFO) UNDERGROUND_SECTORINFO *pSector = FindUnderGroundSector( gWorldSectorX, gWorldSectorY, gbWorldSectorZ ); #ifdef JA2BETAVERSION UINT32 ubTotalEnemies = pSector->ubNumAdmins + pSector->ubNumTroops + pSector->ubNumElites + pSector->ubNumTanks + pSector->ubNumJeeps; #endif if( pSector ) { switch( pSoldier->ubSoldierClass ) { case SOLDIER_CLASS_ADMINISTRATOR: #ifdef JA2BETAVERSION if( ubTotalEnemies <= (UINT32)iMaxEnemyGroupSize && pSector->ubNumAdmins != pSector->ubAdminsInBattle || !pSector->ubNumAdmins || !pSector->ubAdminsInBattle || pSector->ubNumAdmins > 100 || pSector->ubAdminsInBattle > iMaxEnemyGroupSize ) { DoScreenIndependantMessageBox( L"Underground sector admin counters are bad. What were the last 2-3 things to die, and how? Save game and send to KM with info!!!", MSG_BOX_FLAG_OK, NULL ); } #endif if( pSector->ubNumAdmins ) { pSector->ubNumAdmins--; } if( pSector->ubAdminsInBattle ) { pSector->ubAdminsInBattle--; } break; case SOLDIER_CLASS_ARMY: #ifdef JA2BETAVERSION if( ubTotalEnemies <= (UINT32)iMaxEnemyGroupSize && pSector->ubNumTroops != pSector->ubTroopsInBattle || !pSector->ubNumTroops || !pSector->ubTroopsInBattle || pSector->ubNumTroops > 100 || pSector->ubTroopsInBattle > iMaxEnemyGroupSize ) { DoScreenIndependantMessageBox( L"Underground sector troop counters are bad. What were the last 2-3 things to die, and how? Save game and send to KM with info!!!", MSG_BOX_FLAG_OK, NULL ); } #endif if( pSector->ubNumTroops ) { pSector->ubNumTroops--; } if( pSector->ubTroopsInBattle ) { pSector->ubTroopsInBattle--; } break; case SOLDIER_CLASS_ELITE: #ifdef JA2BETAVERSION if( ubTotalEnemies <= (UINT32)iMaxEnemyGroupSize && pSector->ubNumElites != pSector->ubElitesInBattle || !pSector->ubNumElites || !pSector->ubElitesInBattle || pSector->ubNumElites > 100 || pSector->ubElitesInBattle > iMaxEnemyGroupSize ) { DoScreenIndependantMessageBox( L"Underground sector elite counters are bad. What were the last 2-3 things to die, and how? Save game and send to KM with info!!!", MSG_BOX_FLAG_OK, NULL ); } #endif if( pSector->ubNumElites ) { pSector->ubNumElites--; } if( pSector->ubElitesInBattle ) { pSector->ubElitesInBattle--; } break; case SOLDIER_CLASS_CREATURE: if (pSoldier->ubBodyType == BLOODCAT) { if( pSector->ubNumBloodcats > 0 ) pSector->ubNumBloodcats--; } else { #ifdef JA2BETAVERSION if( ubTotalEnemies <= (UINT32)iMaxEnemyGroupSize && pSector->ubNumCreatures != pSector->ubCreaturesInBattle || !pSector->ubNumCreatures || !pSector->ubCreaturesInBattle || pSector->ubNumCreatures > gGameExternalOptions.ubGameMaximumNumberOfCreatures || pSector->ubCreaturesInBattle > gGameExternalOptions.ubGameMaximumNumberOfCreatures ) { DoScreenIndependantMessageBox( L"Underground sector creature counters are bad. What were the last 2-3 things to die, and how? Save game and send to KM with info!!!", MSG_BOX_FLAG_OK, NULL ); } #endif if( pSector->ubNumCreatures ) { pSector->ubNumCreatures--; } if( pSector->ubCreaturesInBattle ) { pSector->ubCreaturesInBattle--; } if( !pSector->ubNumCreatures && gWorldSectorX != 9 && gWorldSectorY != 10 ) { //If the player has successfully killed all creatures in ANY underground sector except J9 //then cancel any pending creature town attack. DeleteAllStrategicEventsOfType( EVENT_CREATURE_ATTACK ); } // a monster has died. Post an event to immediately check whether a mine has been cleared. AddStrategicEventUsingSeconds( EVENT_CHECK_IF_MINE_CLEARED, GetWorldTotalSeconds() + 15, 0); if( pSoldier->ubBodyType == QUEENMONSTER ) { //Need to call this, as the queen is really big, and killing her leaves a bunch //of bad tiles in behind her. Calling this function cleans it up. InvalidateWorldRedundency(); //Now that the queen is dead, turn off the creature quest. EndCreatureQuest(); EndQuest( QUEST_CREATURES, gWorldSectorX, gWorldSectorY ); } } break; } } } } if( !pSoldier->bSectorZ ) { pSector = &SectorInfo[ SECTOR( pSoldier->sSectorX, pSoldier->sSectorY ) ]; iNumEnemiesInSector = NumNonPlayerTeamMembersInSector( pSoldier->sSectorX, pSoldier->sSectorY, ENEMY_TEAM ); if( iNumEnemiesInSector ) { if( pSector->bLastKnownEnemies >= 0 ) { pSector->bLastKnownEnemies = (INT8)iNumEnemiesInSector; } } else { pSector->bLastKnownEnemies = 0; } } } //Well, not so rarely with mobile reinforcements! //Rarely, there will be more enemies than supported by the engine. In this case, these //soldiers are waiting for a slot to be free so that they can enter the battle. This //essentially allows for an infinite number of troops, though only gGameExternalOptions.ubGameMaximumNumberOfEnemies at a time can fight. //This is also called whenever an enemy group's reinforcements arrive because the code is //identical, though it is highly likely that they will all be successfully added on the first call. void AddPossiblePendingEnemiesToBattle() { // Check if no world is loaded, and is not underground level if(!(gWorldSectorX > 0 && gWorldSectorY > 0 && gbWorldSectorZ == 0))//dnl ch57 161009 return; #ifdef JA2UB BOOLEAN fMagicallyAppeared=FALSE; #endif UINT8 ubSlots, ubNumAvailable; UINT8 ubNumElites, ubNumTroops, ubNumAdmins, ubNumTanks, ubNumJeeps; UINT8 ubNumGroupsInSector; UINT8 ubGroupIndex; GROUP *pGroup; SECTORINFO *pSector = &SectorInfo[ SECTOR( gWorldSectorX, gWorldSectorY ) ]; GROUP **pGroupInSectorList; static UINT8 ubPredefinedInsertionCode = 255; // haydent if (is_networked) { if ((is_client && !is_server) || gEnemyEnabled == 0) { return; } } if ( (!PlayerMercsInSector( (UINT8)gWorldSectorX, (UINT8)gWorldSectorY, 0 ) && !NumNonPlayerTeamMembersInSector( gWorldSectorX, gWorldSectorY, MILITIA_TEAM )) || !NumNonPlayerTeamMembersInSector( gWorldSectorX, gWorldSectorY, ENEMY_TEAM ) ) return; // Flugente: if there is an enemy helicopter here, it can add reinforcements if ( gTacticalStatus.Team[ENEMY_TEAM].bAwareOfOpposition ) { EnemyHeliTroopDrop( SECTOR( gWorldSectorX, gWorldSectorY ) ); } ubSlots = NumFreeSlots( ENEMY_TEAM ); // silversurfer: Only if there are enemy troops in the sector already we can talk about "reinforcements". If no troops are there we will spawn the incoming troops without delay. if( gGameExternalOptions.sMinDelayEnemyReinforcements && gTacticalStatus.Team[ENEMY_TEAM].bTeamActive )//dnl ch68 080913 { if(gTacticalStatus.Team[ENEMY_TEAM].bAwareOfOpposition == TRUE) { if(guiReinforceTurn == 0) { guiReinforceTurn = guiTurnCnt + gGameExternalOptions.sMinDelayEnemyReinforcements + Random(gGameExternalOptions.sRndDelayEnemyReinforcements+1);// first possible reinforcement ubSlots = 0; } else if(guiTurnCnt >= guiReinforceTurn) { guiReinforceTurn = guiTurnCnt + gGameExternalOptions.sMinDelayEnemyReinforcements/3 + Random(gGameExternalOptions.sRndDelayEnemyReinforcements+1);// any other reinforcement should appear more frequently ubNumAvailable = gGameExternalOptions.sMinEnterEnemyReinforcements + Random(gGameExternalOptions.sRndEnterEnemyReinforcements+1);// total allowed units to enter per reinforce turn //SendFmtMsg("Enemy reinforcements: ubSlots=%d ubNumAvailable=%d", ubSlots, ubNumAvailable); if(ubSlots > ubNumAvailable) ubSlots = ubNumAvailable; guiArrived += (ubNumAvailable - ubSlots); } else { if(guiArrived > 0) { if(ubSlots > guiArrived) ubSlots = guiArrived; guiArrived -= ubSlots; } else ubSlots = 0; } } else guiReinforceTurn = guiArrived = 0; //SendFmtMsg("Enemy reinforcements: guiTurnCnt=%d guiReinforceTurn=%d guiArrived=%d", guiTurnCnt, guiReinforceTurn, guiArrived); } if( !ubSlots ) { //no available slots to add enemies to. Try again later... return; } if ( !gfPendingNonPlayerTeam[ENEMY_TEAM] ) { //Optimization. No point in checking for group reinforcements if we know that there aren't any more enemies that can //be added to this battle. This changes whenever a new enemy group arrives at the scene. while (ubSlots) { UINT8 ubInsertionCode = 255; if( gTacticalStatus.Team[ ENEMY_TEAM ].bAwareOfOpposition == TRUE ) ubInsertionCode = DoReinforcementAsPendingNonPlayer( gWorldSectorX, gWorldSectorY, ENEMY_TEAM ); if (ubInsertionCode == 255) { break; } // Assume we added one since there are supposedly more available and room for them ubSlots--; } // It's probable that the "pending enemies" flag isn't working right. So we'll go ahead and continue into // the group insertion code from here. //return; } if ( pSector->ubNumElites + pSector->ubNumTroops + pSector->ubNumAdmins + pSector->ubNumTanks + pSector->ubNumJeeps ) { ubNumElites = ubNumTroops = ubNumAdmins = ubNumTanks = ubNumJeeps = 0; ubNumAvailable = pSector->ubNumElites + pSector->ubNumTroops + pSector->ubNumAdmins + pSector->ubNumTanks + pSector->ubNumJeeps - pSector->ubElitesInBattle - pSector->ubTroopsInBattle - pSector->ubAdminsInBattle - pSector->ubTanksInBattle - pSector->ubJeepsInBattle; while( ubNumAvailable && ubSlots ) { // So they just magically appear out of nowhere from the edge? //This group has enemies waiting for a chance to enter the battle. if( pSector->ubElitesInBattle < pSector->ubNumElites ) { //Add an elite troop pSector->ubElitesInBattle++; ubNumAvailable--; ubSlots--; ubNumElites++; } else if( pSector->ubTroopsInBattle < pSector->ubNumTroops ) { //Add a regular troop. pSector->ubTroopsInBattle++; ubNumAvailable--; ubSlots--; ubNumTroops++; } else if( pSector->ubAdminsInBattle < pSector->ubNumAdmins ) { //Add an elite troop pSector->ubAdminsInBattle++; ubNumAvailable--; ubSlots--; ubNumAdmins++; } else if( pSector->ubTanksInBattle < pSector->ubNumTanks ) { //Add a tank pSector->ubTanksInBattle++; ubNumAvailable--; ubSlots--; ubNumTanks++; } else if ( pSector->ubJeepsInBattle < pSector->ubNumJeeps ) { //Add a jeep pSector->ubJeepsInBattle++; ubNumAvailable--; ubSlots--; ubNumJeeps++; } else { AssertMsg( 0, "AddPossiblePendingEnemiesToBattle(): Logic Error -- by Kris" ); } } if ( ubNumAdmins || ubNumTroops || ubNumElites || ubNumTanks || ubNumJeeps ) { //This group has contributed forces, then add them now, because different //groups appear on different sides of the map. UINT8 ubStrategicInsertionCode = ubPredefinedInsertionCode; //First, determine which entrypoint to use, based on the travel direction of the group. if( ubStrategicInsertionCode == 255 ) { if ( NumNonPlayerTeamMembersInSector( gWorldSectorX + 1, gWorldSectorY, ENEMY_TEAM ) ) ubStrategicInsertionCode = INSERTION_CODE_EAST; else if ( NumNonPlayerTeamMembersInSector( gWorldSectorX - 1, gWorldSectorY, ENEMY_TEAM ) ) ubStrategicInsertionCode = INSERTION_CODE_WEST; else if ( NumNonPlayerTeamMembersInSector( gWorldSectorX, gWorldSectorY + 1, ENEMY_TEAM ) ) ubStrategicInsertionCode = INSERTION_CODE_SOUTH; else if ( NumNonPlayerTeamMembersInSector( gWorldSectorX, gWorldSectorY - 1, ENEMY_TEAM ) ) ubStrategicInsertionCode = INSERTION_CODE_NORTH; #ifdef JA2UB else if( gsGridNoForMapEdgePointInfo != -1 ) { //Ja25: it doesnt matter the entry point at this point, it will become GRIDNO at a later point ubStrategicInsertionCode = INSERTION_CODE_NORTH; fMagicallyAppeared = FALSE; } #endif } if( ubStrategicInsertionCode == 255 ) { if ( !NumNonPlayerTeamMembersInSector( gWorldSectorX + 1, gWorldSectorY, MILITIA_TEAM ) ) ubStrategicInsertionCode = INSERTION_CODE_EAST; else if ( !NumNonPlayerTeamMembersInSector( gWorldSectorX - 1, gWorldSectorY, MILITIA_TEAM ) ) ubStrategicInsertionCode = INSERTION_CODE_WEST; else if ( !NumNonPlayerTeamMembersInSector( gWorldSectorX, gWorldSectorY + 1, MILITIA_TEAM ) ) ubStrategicInsertionCode = INSERTION_CODE_SOUTH; else if ( !NumNonPlayerTeamMembersInSector( gWorldSectorX, gWorldSectorY - 1, MILITIA_TEAM ) ) ubStrategicInsertionCode = INSERTION_CODE_NORTH; } if( ubStrategicInsertionCode < INSERTION_CODE_NORTH ||ubStrategicInsertionCode > INSERTION_CODE_WEST ) ubStrategicInsertionCode = INSERTION_CODE_NORTH + Random( 4 ); AddEnemiesToBattle( 0, ubStrategicInsertionCode, ubNumAdmins, ubNumTroops, ubNumElites, ubNumTanks, ubNumJeeps, FALSE ); gfPendingNonPlayerTeam[ENEMY_TEAM] = TRUE; } } ubPredefinedInsertionCode = 255; // Figure out which groups are in the sector, so we can have reinforcements arrive at random for (ubNumGroupsInSector = 0, pGroup = gpGroupList; pGroup; pGroup = pGroup->next) { if ( pGroup->usGroupTeam == ENEMY_TEAM && !pGroup->fVehicle && pGroup->ubSectorX == gWorldSectorX && pGroup->ubSectorY == gWorldSectorY && !gbWorldSectorZ ) ++ubNumGroupsInSector; } pGroupInSectorList = (GROUP**) MemAlloc( ubNumGroupsInSector * sizeof( GROUP*)); for (ubNumGroupsInSector = 0, pGroup = gpGroupList; pGroup; pGroup = pGroup->next) { if ( pGroup->usGroupTeam == ENEMY_TEAM && !pGroup->fVehicle && pGroup->ubSectorX == gWorldSectorX && pGroup->ubSectorY == gWorldSectorY && !gbWorldSectorZ ) { pGroupInSectorList[ ubNumGroupsInSector++] = pGroup; } } while( ubSlots && ubNumGroupsInSector ) { UINT8 ubInsertionCode = 255; ubGroupIndex = Random( ubNumGroupsInSector); pGroup = pGroupInSectorList[ ubGroupIndex]; // Flugente fix: check for underflow... UINT8 currentgroupsize = pGroup->pEnemyGroup->ubElitesInBattle + pGroup->pEnemyGroup->ubTroopsInBattle + pGroup->pEnemyGroup->ubAdminsInBattle + pGroup->pEnemyGroup->ubTanksInBattle + pGroup->pEnemyGroup->ubJeepsInBattle; if ( currentgroupsize > pGroup->ubGroupSize ) ubNumAvailable = 0; else ubNumAvailable = pGroup->ubGroupSize - currentgroupsize; if (!ubNumAvailable) { // Looks like we picked an empty group. Make a note of it pGroupInSectorList[ ubGroupIndex] = pGroupInSectorList[ --ubNumGroupsInSector ]; continue; } if( pGroup->ubPrevX && pGroup->ubPrevY ) { if( pGroup->ubSectorX < pGroup->ubPrevX ) ubInsertionCode = INSERTION_CODE_EAST; else if( pGroup->ubSectorX > pGroup->ubPrevX ) ubInsertionCode = INSERTION_CODE_WEST; else if( pGroup->ubSectorY < pGroup->ubPrevY ) ubInsertionCode = INSERTION_CODE_SOUTH; else if( pGroup->ubSectorY > pGroup->ubPrevY ) ubInsertionCode = INSERTION_CODE_NORTH; else { // WANNE: Hack: If no valid insertion is found, get random insertion instead of Assert() error UINT32 rndInsertionCode = GetRndNum(3); ubInsertionCode = rndInsertionCode; //Assert(0); } } else if( pGroup->ubNextX && pGroup->ubNextY ) { if( pGroup->ubSectorX < pGroup->ubNextX ) ubInsertionCode = INSERTION_CODE_EAST; else if( pGroup->ubSectorX > pGroup->ubNextX ) ubInsertionCode = INSERTION_CODE_WEST; else if( pGroup->ubSectorY < pGroup->ubNextY ) ubInsertionCode = INSERTION_CODE_SOUTH; else if( pGroup->ubSectorY > pGroup->ubNextY ) ubInsertionCode = INSERTION_CODE_NORTH; else { // WANNE: Hack: If no valid insertion is found, get random insertion instead of Assert() error UINT32 rndInsertionCode = GetRndNum(3); ubInsertionCode = rndInsertionCode; //Assert(0); } } else { // The group has no movement orders. Where did it come from? Assert(0); } if ( pGroup->pEnemyGroup->ubElitesInBattle < pGroup->pEnemyGroup->ubNumElites ) { //Add an elite troop pGroup->pEnemyGroup->ubElitesInBattle++; ubSlots--; AddEnemiesToBattle( pGroup, ubInsertionCode, 0, 0, 1, 0, 0, FALSE ); } else if( pGroup->pEnemyGroup->ubTroopsInBattle < pGroup->pEnemyGroup->ubNumTroops ) { //Add a regular troop. pGroup->pEnemyGroup->ubTroopsInBattle++; ubSlots--; AddEnemiesToBattle( pGroup, ubInsertionCode, 0, 1, 0, 0, 0, FALSE ); } else if( pGroup->pEnemyGroup->ubAdminsInBattle < pGroup->pEnemyGroup->ubNumAdmins ) { //Add an admin troop pGroup->pEnemyGroup->ubAdminsInBattle++; ubSlots--; AddEnemiesToBattle( pGroup, ubInsertionCode, 1, 0, 0, 0, 0, FALSE ); } else if( pGroup->pEnemyGroup->ubTanksInBattle < pGroup->pEnemyGroup->ubNumTanks ) { //Add a tank pGroup->pEnemyGroup->ubTanksInBattle++; ubSlots--; AddEnemiesToBattle( pGroup, ubInsertionCode, 0, 0, 0, 1, 0, FALSE ); } else if ( pGroup->pEnemyGroup->ubJeepsInBattle < pGroup->pEnemyGroup->ubNumJeeps ) { //Add a jeep pGroup->pEnemyGroup->ubJeepsInBattle++; ubSlots--; AddEnemiesToBattle( pGroup, ubInsertionCode, 0, 0, 0, 0, 1, FALSE ); } else { // WANNE: Instead of asserting, just add a regular troop //AssertMsg( 0, "AddPossiblePendingEnemiesToBattle(): Logic Error -- by Kris" ); pGroup->pEnemyGroup->ubTroopsInBattle++; ubSlots--; AddEnemiesToBattle( pGroup, ubInsertionCode, 0, 1, 0, 0, 0, FALSE ); } } MemFree( pGroupInSectorList); if( ubSlots ) { //After going through the process, we have finished with some free slots and no more enemies to add. //So, we can turn off the flag, as this check is no longer needed. gfPendingNonPlayerTeam[ENEMY_TEAM] = FALSE; } } void NotifyPlayersOfNewEnemies() { INT32 iSoldiers, iChosenSoldier, i; SOLDIERTYPE *pSoldier; BOOLEAN fIgnoreBreath = FALSE; iSoldiers = 0; for( i = gTacticalStatus.Team[ OUR_TEAM ].bFirstID; i <= gTacticalStatus.Team[ OUR_TEAM ].bLastID; i++ ) { //find a merc that is aware. pSoldier = MercPtrs[ i ]; if( pSoldier->bInSector && pSoldier->bActive && pSoldier->stats.bLife >= OKLIFE && pSoldier->bBreath >= OKBREATH ) { iSoldiers++; } } if( !iSoldiers ) { // look for an out of breath merc. fIgnoreBreath = TRUE; for( i = gTacticalStatus.Team[ OUR_TEAM ].bFirstID; i <= gTacticalStatus.Team[ OUR_TEAM ].bLastID; i++ ) { //find a merc that is aware. pSoldier = MercPtrs[ i ]; if( pSoldier->bInSector && pSoldier->bActive && pSoldier->stats.bLife >= OKLIFE ) { iSoldiers++; } } } if( iSoldiers ) { iChosenSoldier = Random( iSoldiers ); for( i = gTacticalStatus.Team[ OUR_TEAM ].bFirstID; i <= gTacticalStatus.Team[ OUR_TEAM ].bLastID; i++ ) { //find a merc that is aware. pSoldier = MercPtrs[ i ]; if( pSoldier->bInSector && pSoldier->bActive && pSoldier->stats.bLife >= OKLIFE && ( ( pSoldier->bBreath >= OKBREATH ) || fIgnoreBreath ) ) { if( !iChosenSoldier ) { // ATE: This is to allow special handling of initial heli drop if ( !DidGameJustStart() ) { TacticalCharacterDialogueWithSpecialEvent( pSoldier, QUOTE_ENEMY_PRESENCE, 0, 0, 0 ); } return; } iChosenSoldier--; } } } else { //There is either nobody here or our mercs can't talk } } void AddEnemiesToBattle( GROUP *pGroup, UINT8 ubStrategicInsertionCode, UINT8 ubNumAdmins, UINT8 ubNumTroops, UINT8 ubNumElites, UINT8 ubNumTanks, UINT8 ubNumJeeps, BOOLEAN fMagicallyAppeared ) { SOLDIERTYPE *pSoldier; MAPEDGEPOINTINFO MapEdgepointInfo; UINT8 ubCurrSlot; UINT8 ubTotalSoldiers; UINT8 bDesiredDirection=0; 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 AddEnemiesToBattle()" ); break; } #ifdef JA2TESTVERSION ScreenMsg( FONT_RED, MSG_INTERFACE, L"Enemy reinforcements have arrived! (%d admins, %d troops, %d elite)", ubNumAdmins, ubNumTroops, ubNumElites ); #endif if( fMagicallyAppeared ) { //update the strategic counters if( !gbWorldSectorZ ) { SECTORINFO *pSector = &SectorInfo[ SECTOR( gWorldSectorX, gWorldSectorY ) ]; pSector->ubNumAdmins += ubNumAdmins; pSector->ubAdminsInBattle += ubNumAdmins; pSector->ubNumTroops += ubNumTroops; pSector->ubTroopsInBattle += ubNumTroops; pSector->ubNumElites += ubNumElites; pSector->ubElitesInBattle += ubNumElites; pSector->ubNumTanks += ubNumTanks; pSector->ubTanksInBattle += ubNumTanks; pSector->ubNumJeeps += ubNumJeeps; pSector->ubJeepsInBattle += ubNumJeeps; } else { UNDERGROUND_SECTORINFO *pSector = FindUnderGroundSector( gWorldSectorX, gWorldSectorY, gbWorldSectorZ ); if( pSector ) { pSector->ubNumAdmins += ubNumAdmins; pSector->ubAdminsInBattle += ubNumAdmins; pSector->ubNumTroops += ubNumTroops; pSector->ubTroopsInBattle += ubNumTroops; pSector->ubNumElites += ubNumElites; pSector->ubElitesInBattle += ubNumElites; pSector->ubNumTanks += ubNumTanks; pSector->ubTanksInBattle += ubNumTanks; pSector->ubNumJeeps += ubNumJeeps; pSector->ubJeepsInBattle += ubNumJeeps; } } //Because the enemies magically appeared, have one of our soldiers say something... NotifyPlayersOfNewEnemies(); } ubTotalSoldiers = ubNumAdmins + ubNumTroops + ubNumElites + ubNumTanks + ubNumJeeps; #ifdef JA2UB if( gsGridNoForMapEdgePointInfo != -1 ) { ubStrategicInsertionCode = INSERTION_CODE_GRIDNO; } if( ubStrategicInsertionCode == INSERTION_CODE_GRIDNO ) { if( gsGridNoForMapEdgePointInfo == -1 ) { Assert( 0 ); gsGridNoForMapEdgePointInfo=0; } for( UINT8 ubCnt=0; ubCnt<32; ++ubCnt) { MapEdgepointInfo.sGridNo[ ubCnt ] = gsGridNoForMapEdgePointInfo; } MapEdgepointInfo.ubNumPoints = 32; MapEdgepointInfo.ubStrategicInsertionCode = INSERTION_CODE_GRIDNO; } else { ChooseMapEdgepoints( &MapEdgepointInfo, ubStrategicInsertionCode, ubTotalSoldiers ); } #else ChooseMapEdgepoints( &MapEdgepointInfo, ubStrategicInsertionCode, ubTotalSoldiers ); #endif ubCurrSlot = 0; while( ubTotalSoldiers ) { if( ubNumElites && Random( ubTotalSoldiers ) < ubNumElites ) { ubNumElites--; ubTotalSoldiers--; Assert(pSoldier = TacticalCreateEliteEnemy()); if( pGroup ) { pSoldier->ubGroupID = pGroup->ubGroupID; } pSoldier->ubInsertionDirection = bDesiredDirection; //Setup the position if( ubCurrSlot < MapEdgepointInfo.ubNumPoints ) { //using an edgepoint pSoldier->ubStrategicInsertionCode = INSERTION_CODE_GRIDNO; pSoldier->usStrategicInsertionData = MapEdgepointInfo.sGridNo[ ubCurrSlot++ ]; } else { //no edgepoints left, so put him at the entrypoint. pSoldier->ubStrategicInsertionCode = ubStrategicInsertionCode; } UpdateMercInSector( pSoldier, gWorldSectorX, gWorldSectorY, 0 ); } else if( ubNumTroops && (UINT8)Random( ubTotalSoldiers ) < (UINT8)(ubNumElites + ubNumTroops) ) { ubNumTroops--; ubTotalSoldiers--; Assert(pSoldier = TacticalCreateArmyTroop()); if( pGroup ) { pSoldier->ubGroupID = pGroup->ubGroupID; } pSoldier->ubInsertionDirection = bDesiredDirection; //Setup the position if( ubCurrSlot < MapEdgepointInfo.ubNumPoints ) { //using an edgepoint pSoldier->ubStrategicInsertionCode = INSERTION_CODE_GRIDNO; pSoldier->usStrategicInsertionData = MapEdgepointInfo.sGridNo[ ubCurrSlot++ ]; } else { //no edgepoints left, so put him at the entrypoint. pSoldier->ubStrategicInsertionCode = ubStrategicInsertionCode; } UpdateMercInSector( pSoldier, gWorldSectorX, gWorldSectorY, 0 ); } else if( ubNumAdmins && (UINT8)Random( ubTotalSoldiers ) < (UINT8)(ubNumElites + ubNumTroops + ubNumAdmins) ) { ubNumAdmins--; ubTotalSoldiers--; Assert(pSoldier = TacticalCreateAdministrator()); if( pGroup ) { pSoldier->ubGroupID = pGroup->ubGroupID; } pSoldier->ubInsertionDirection = bDesiredDirection; //Setup the position if( ubCurrSlot < MapEdgepointInfo.ubNumPoints ) { //using an edgepoint pSoldier->ubStrategicInsertionCode = INSERTION_CODE_GRIDNO; pSoldier->usStrategicInsertionData = MapEdgepointInfo.sGridNo[ ubCurrSlot++ ]; } else { //no edgepoints left, so put him at the entrypoint. pSoldier->ubStrategicInsertionCode = ubStrategicInsertionCode; } UpdateMercInSector( pSoldier, gWorldSectorX, gWorldSectorY, 0 ); } else if( ubNumTanks && (UINT8)Random( ubTotalSoldiers ) < (UINT8)(ubNumElites + ubNumTroops + ubNumAdmins + ubNumTanks) && ubNumberOfVehicles < 9 ) { ubNumTanks--; ubTotalSoldiers--; Assert(pSoldier = TacticalCreateEnemyTank()); if( pGroup ) { pSoldier->ubGroupID = pGroup->ubGroupID; } pSoldier->ubInsertionDirection = bDesiredDirection; //Setup the position if( ubCurrSlot < MapEdgepointInfo.ubNumPoints ) { //using an edgepoint pSoldier->ubStrategicInsertionCode = INSERTION_CODE_GRIDNO; pSoldier->usStrategicInsertionData = MapEdgepointInfo.sGridNo[ ubCurrSlot++ ]; } else { //no edgepoints left, so put him at the entrypoint. pSoldier->ubStrategicInsertionCode = ubStrategicInsertionCode; } UpdateMercInSector( pSoldier, gWorldSectorX, gWorldSectorY, 0 ); } else if ( ubNumJeeps && (UINT8)Random( ubTotalSoldiers ) < (UINT8)(ubNumElites + ubNumTroops + ubNumAdmins + ubNumTanks + ubNumJeeps) && ubNumberOfVehicles < 9 ) { ubNumJeeps--; ubTotalSoldiers--; Assert( pSoldier = TacticalCreateEnemyJeep( ) ); if ( pGroup ) { pSoldier->ubGroupID = pGroup->ubGroupID; } pSoldier->ubInsertionDirection = bDesiredDirection; //Setup the position if ( ubCurrSlot < MapEdgepointInfo.ubNumPoints ) { //using an edgepoint pSoldier->ubStrategicInsertionCode = INSERTION_CODE_GRIDNO; pSoldier->usStrategicInsertionData = MapEdgepointInfo.sGridNo[ubCurrSlot++]; } else { //no edgepoints left, so put him at the entrypoint. pSoldier->ubStrategicInsertionCode = ubStrategicInsertionCode; } UpdateMercInSector( pSoldier, gWorldSectorX, gWorldSectorY, 0 ); } // HEADROCK HAM 3.2: enemy reinforcements arrive with 0 APs. if (gGameExternalOptions.ubReinforcementsFirstTurnFreeze == 1 || gGameExternalOptions.ubReinforcementsFirstTurnFreeze == 2) { pSoldier->bActionPoints = 0; // Flugente: due to a fix, also note here that the reinforcements get no APs. pSoldier->usSoldierFlagMask |= SOLDIER_NO_AP; // Flugente: campaign stats if ( IsOurSoldier(pSoldier) ) gCurrentIncident.usIncidentFlags |= INCIDENT_REINFORCEMENTS_PLAYERSIDE; else gCurrentIncident.usIncidentFlags |= INCIDENT_REINFORCEMENTS_ENEMY; } } #ifdef JA2UB gsGridNoForMapEdgePointInfo = -1; #endif } void AddMilitiaToBattle( GROUP *pGroup, UINT8 ubStrategicInsertionCode, UINT8 ubNumGreens, UINT8 ubNumRegulars, UINT8 ubNumElites, BOOLEAN fMagicallyAppeared ) { SOLDIERTYPE *pSoldier; MAPEDGEPOINTINFO MapEdgepointInfo; UINT8 ubCurrSlot; UINT8 ubTotalSoldiers; UINT8 bDesiredDirection = 0; 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 AddEnemiesToBattle()" ); break; } #ifdef JA2TESTVERSION ScreenMsg( FONT_RED, MSG_INTERFACE, L"Militia reinforcements have arrived! (%d greens, %d regulars, %d elite)", ubNumGreens, ubNumRegulars, ubNumElites ); #endif if ( fMagicallyAppeared ) { //update the strategic counters if ( !gbWorldSectorZ ) { SECTORINFO *pSector = &SectorInfo[SECTOR( gWorldSectorX, gWorldSectorY )]; pSector->ubNumberOfCivsAtLevel[GREEN_MILITIA] += ubNumGreens; pSector->ubNumberOfCivsAtLevel[REGULAR_MILITIA] += ubNumRegulars; pSector->ubNumberOfCivsAtLevel[ELITE_MILITIA] += ubNumElites; } else { // there are no underground militia... error if this ever happens AssertMsg( 0, "Tried to add militia to an underground sector" ); } } ubTotalSoldiers = ubNumGreens + ubNumRegulars + ubNumElites; #ifdef JA2UB if ( gsGridNoForMapEdgePointInfo != -1 ) { ubStrategicInsertionCode = INSERTION_CODE_GRIDNO; } if ( ubStrategicInsertionCode == INSERTION_CODE_GRIDNO ) { if ( gsGridNoForMapEdgePointInfo == -1 ) { Assert( 0 ); gsGridNoForMapEdgePointInfo = 0; } for ( UINT8 ubCnt = 0; ubCnt<32; ++ubCnt ) { MapEdgepointInfo.sGridNo[ubCnt] = gsGridNoForMapEdgePointInfo; } MapEdgepointInfo.ubNumPoints = 32; MapEdgepointInfo.ubStrategicInsertionCode = INSERTION_CODE_GRIDNO; } else { ChooseMapEdgepoints( &MapEdgepointInfo, ubStrategicInsertionCode, ubTotalSoldiers ); } #else ChooseMapEdgepoints( &MapEdgepointInfo, ubStrategicInsertionCode, (ubTotalSoldiers) ); #endif ubCurrSlot = 0; while ( ubTotalSoldiers ) { if ( ubNumElites && Random( ubTotalSoldiers ) < ubNumElites ) { ubNumElites--; ubTotalSoldiers--; Assert( pSoldier = TacticalCreateMilitia( SOLDIER_CLASS_ELITE_MILITIA, gWorldSectorX, gWorldSectorY ) ); if ( pGroup ) { pSoldier->ubGroupID = pGroup->ubGroupID; } pSoldier->ubInsertionDirection = bDesiredDirection; //Setup the position if ( ubCurrSlot < MapEdgepointInfo.ubNumPoints ) { //using an edgepoint pSoldier->ubStrategicInsertionCode = INSERTION_CODE_GRIDNO; pSoldier->usStrategicInsertionData = MapEdgepointInfo.sGridNo[ubCurrSlot++]; } else { //no edgepoints left, so put him at the entrypoint. pSoldier->ubStrategicInsertionCode = ubStrategicInsertionCode; } UpdateMercInSector( pSoldier, gWorldSectorX, gWorldSectorY, 0 ); } else if ( ubNumRegulars && (UINT8)Random( ubTotalSoldiers ) < (UINT8)(ubNumElites + ubNumRegulars) ) { ubNumRegulars--; ubTotalSoldiers--; Assert( pSoldier = TacticalCreateMilitia( SOLDIER_CLASS_REG_MILITIA, gWorldSectorX, gWorldSectorY ) ); if ( pGroup ) { pSoldier->ubGroupID = pGroup->ubGroupID; } pSoldier->ubInsertionDirection = bDesiredDirection; //Setup the position if ( ubCurrSlot < MapEdgepointInfo.ubNumPoints ) { //using an edgepoint pSoldier->ubStrategicInsertionCode = INSERTION_CODE_GRIDNO; pSoldier->usStrategicInsertionData = MapEdgepointInfo.sGridNo[ubCurrSlot++]; } else { //no edgepoints left, so put him at the entrypoint. pSoldier->ubStrategicInsertionCode = ubStrategicInsertionCode; } UpdateMercInSector( pSoldier, gWorldSectorX, gWorldSectorY, 0 ); } else if ( ubNumGreens && (UINT8)Random( ubTotalSoldiers ) < (UINT8)(ubNumElites + ubNumRegulars + ubNumGreens) ) { ubNumGreens--; ubTotalSoldiers--; Assert( pSoldier = TacticalCreateMilitia( SOLDIER_CLASS_GREEN_MILITIA, gWorldSectorX, gWorldSectorY ) ); if ( pGroup ) { pSoldier->ubGroupID = pGroup->ubGroupID; } pSoldier->ubInsertionDirection = bDesiredDirection; //Setup the position if ( ubCurrSlot < MapEdgepointInfo.ubNumPoints ) { //using an edgepoint pSoldier->ubStrategicInsertionCode = INSERTION_CODE_GRIDNO; pSoldier->usStrategicInsertionData = MapEdgepointInfo.sGridNo[ubCurrSlot++]; } else { //no edgepoints left, so put him at the entrypoint. pSoldier->ubStrategicInsertionCode = ubStrategicInsertionCode; } UpdateMercInSector( pSoldier, gWorldSectorX, gWorldSectorY, 0 ); } // HEADROCK HAM 3.2: enemy reinforcements arrive with 0 APs. if ( gGameExternalOptions.ubReinforcementsFirstTurnFreeze == 1 || gGameExternalOptions.ubReinforcementsFirstTurnFreeze == 2 ) { pSoldier->bActionPoints = 0; // Flugente: due to a fix, also note here that the reinforcements get no APs. pSoldier->usSoldierFlagMask |= SOLDIER_NO_AP; // Flugente: campaign stats if ( IsOurSoldier( pSoldier ) ) gCurrentIncident.usIncidentFlags |= INCIDENT_REINFORCEMENTS_PLAYERSIDE; else gCurrentIncident.usIncidentFlags |= INCIDENT_REINFORCEMENTS_ENEMY; } } #ifdef JA2UB gsGridNoForMapEdgePointInfo = -1; #endif } BOOLEAN SaveUnderGroundSectorInfoToSaveGame( HWFILE hFile ) { UINT32 uiNumBytesWritten; UINT32 uiNumOfRecords=0; UNDERGROUND_SECTORINFO *TempNode = gpUndergroundSectorInfoHead; //Loop through all the nodes to count how many there are while( TempNode ) { uiNumOfRecords++; TempNode = TempNode->next; } //Write how many nodes there are FileWrite( hFile, &uiNumOfRecords, sizeof( UINT32 ), &uiNumBytesWritten ); if( uiNumBytesWritten != sizeof( UINT32 ) ) { return(FALSE); } TempNode = gpUndergroundSectorInfoHead; //Go through each node and save it. while( TempNode ) { FileWrite( hFile, TempNode, sizeof( UNDERGROUND_SECTORINFO ), &uiNumBytesWritten ); if( uiNumBytesWritten != sizeof( UNDERGROUND_SECTORINFO ) ) { return(FALSE); } TempNode = TempNode->next; } return( TRUE ); } BOOLEAN LoadUnderGroundSectorInfoFromSavedGame( HWFILE hFile ) { UINT32 uiNumBytesRead; UINT32 uiNumOfRecords=0; UINT32 cnt=0; UNDERGROUND_SECTORINFO *TempNode = NULL; UNDERGROUND_SECTORINFO *TempSpot = NULL; //Clear the current LL TrashUndergroundSectorInfo(); //Read in the number of nodes stored FileRead( hFile, &uiNumOfRecords, sizeof( UINT32 ), &uiNumBytesRead ); if( uiNumBytesRead != sizeof( UINT32 ) ) { return(FALSE); } for( cnt = 0; cnt< uiNumOfRecords; cnt ++) { //Malloc space for the new node TempNode = (UNDERGROUND_SECTORINFO *) MemAlloc( sizeof( UNDERGROUND_SECTORINFO ) ); if( TempNode == NULL ) return( FALSE ); //read in the new node FileRead( hFile, TempNode, sizeof( UNDERGROUND_SECTORINFO ), &uiNumBytesRead ); if( uiNumBytesRead != sizeof( UNDERGROUND_SECTORINFO ) ) { return(FALSE); } //If its the first time in, assign the node to the list if( cnt == 0 ) { gpUndergroundSectorInfoHead = TempNode; TempSpot = gpUndergroundSectorInfoHead; TempSpot->next = NULL; } else { //assign the new node to the LL TempSpot->next = TempNode; //advance to the next node TempSpot = TempSpot->next; TempSpot->next = NULL; gpUndergroundSectorInfoTail = TempSpot; } } #ifdef JA2BETAVERSION if( !uiNumOfRecords ) { BuildUndergroundSectorInfoList(); gfClearCreatureQuest = TRUE; } #endif return( TRUE ); } UNDERGROUND_SECTORINFO* FindUnderGroundSector( INT16 sMapX, INT16 sMapY, UINT8 bMapZ ) { UNDERGROUND_SECTORINFO *pUnderground; pUnderground = gpUndergroundSectorInfoHead; //Loop through all the underground sectors looking for specified sector while( pUnderground ) { //If the sector is the right one if( pUnderground->ubSectorX == sMapX && pUnderground->ubSectorY == sMapY && pUnderground->ubSectorZ == bMapZ ) { return( pUnderground ); } pUnderground = pUnderground->next; } return( NULL ); } void BeginCaptureSquence( ) { if( !( gStrategicStatus.uiFlags & STRATEGIC_PLAYER_CAPTURED_FOR_RESCUE ) || !( gStrategicStatus.uiFlags & STRATEGIC_PLAYER_CAPTURED_FOR_ESCAPE ) ) { gStrategicStatus.ubNumCapturedForRescue = 0; } } void EndCaptureSequence( ) { #ifdef JA2UB // no UB #else // Set flag... if( !( gStrategicStatus.uiFlags & STRATEGIC_PLAYER_CAPTURED_FOR_RESCUE ) || !(gStrategicStatus.uiFlags & STRATEGIC_PLAYER_CAPTURED_FOR_ESCAPE) ) { // CJC Dec 1 2002: fixing multiple captures: //gStrategicStatus.uiFlags |= STRATEGIC_PLAYER_CAPTURED_FOR_RESCUE; if ( gubQuest[ QUEST_HELD_IN_ALMA ] == QUESTNOTSTARTED ) { // CJC Dec 1 2002: fixing multiple captures: gStrategicStatus.uiFlags |= STRATEGIC_PLAYER_CAPTURED_FOR_RESCUE; StartQuest( QUEST_HELD_IN_ALMA, gWorldSectorX, gWorldSectorY ); } // CJC Dec 1 2002: fixing multiple captures: //else if ( gubQuest[ QUEST_HELD_IN_ALMA ] == QUESTDONE ) else if ( gubQuest[ QUEST_HELD_IN_ALMA ] == QUESTDONE && gubQuest[ QUEST_INTERROGATION ] == QUESTNOTSTARTED ) { StartQuest( QUEST_INTERROGATION, gWorldSectorX, gWorldSectorY ); // CJC Dec 1 2002: fixing multiple captures: gStrategicStatus.uiFlags |= STRATEGIC_PLAYER_CAPTURED_FOR_ESCAPE; // OK! - Schedule Meanwhile now! HandleInterrogationMeanwhileScene(); } // CJC Dec 1 2002: fixing multiple captures else { // !?!? set both flags gStrategicStatus.uiFlags |= STRATEGIC_PLAYER_CAPTURED_FOR_RESCUE; gStrategicStatus.uiFlags |= STRATEGIC_PLAYER_CAPTURED_FOR_ESCAPE; } } #endif } void EnemyCapturesPlayerSoldier( SOLDIERTYPE *pSoldier ) { UINT32 i; BOOLEAN fMadeCorpse; INT32 iNumEnemiesInSector; // Buggler: Externalized Hardcoded grid number //static INT32 sAlmaCaptureGridNos[] = { 9208, 9688, 9215 }; //dnl!!! //static INT32 sAlmaCaptureItemsGridNo[] = { 12246, 12406, 12086 }; //dnl!!! //static INT32 sInterrogationItemGridNo[] = { 12089, 12089, 12089 }; //dnl!!! AssertNotNIL(pSoldier); // ATE: Check first if ! in player captured sequence already // CJC Dec 1 2002: fixing multiple captures if ( ( gStrategicStatus.uiFlags & STRATEGIC_PLAYER_CAPTURED_FOR_RESCUE ) && (gStrategicStatus.uiFlags & STRATEGIC_PLAYER_CAPTURED_FOR_ESCAPE) ) { return; } // ATE: If maximum prisoners captured, return! if ( gStrategicStatus.ubNumCapturedForRescue > 3 ) { return; } // If this is an EPC , just kill them... if ( AM_AN_EPC( pSoldier ) ) { pSoldier->stats.bLife = 0; pSoldier->iHealableInjury = 0; // added by SANDRO HandleSoldierDeath( pSoldier, &fMadeCorpse ); return; } if ( pSoldier->flags.uiStatusFlags & SOLDIER_VEHICLE ) { return; } // ATE: Patch fix If in a vehicle, remove from vehicle... TakeSoldierOutOfVehicle( pSoldier ); // Are there enemies in ALMA? ( I13 ) iNumEnemiesInSector = NumNonPlayerTeamMembersInSector( gModSettings.ubInitialPOWSectorX, gModSettings.ubInitialPOWSectorY, ENEMY_TEAM ); //(13, 9) // IF there are no enemies, and we need to do alma, skip! if ( gubQuest[ QUEST_HELD_IN_ALMA ] == QUESTNOTSTARTED && iNumEnemiesInSector == 0 ) { InternalStartQuest( QUEST_HELD_IN_ALMA, gWorldSectorX, gWorldSectorY, FALSE ); InternalEndQuest( QUEST_HELD_IN_ALMA, gWorldSectorX, gWorldSectorY, FALSE ); } HandleMoraleEvent( pSoldier, MORALE_MERC_CAPTURED, pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ ); // Change to POW.... //-add him to a POW assignment/group if( ( pSoldier->bAssignment != ASSIGNMENT_POW ) ) { SetTimeOfAssignmentChangeForMerc( pSoldier ); } ChangeSoldiersAssignment( pSoldier, ASSIGNMENT_POW ); // ATE: Make them neutral! if ( gubQuest[ QUEST_HELD_IN_ALMA ] == QUESTNOTSTARTED ) { pSoldier->aiData.bNeutral = TRUE; } RemoveCharacterFromSquads( pSoldier ); WORLDITEM WorldItem; std::vector pWorldItem(1);//dnl ch75 271013 // Is this the first one..? if ( gubQuest[ QUEST_HELD_IN_ALMA ] == QUESTNOTSTARTED ) { //-teleport him to NE Alma sector (not Tixa as originally planned) pSoldier->sSectorX = gModSettings.ubInitialPOWSectorX; //13 pSoldier->sSectorY = gModSettings.ubInitialPOWSectorY; //9 pSoldier->bSectorZ = 0; // put him on the floor!! pSoldier->pathing.bLevel = 0; // OK, drop all items! UINT32 invsize = pSoldier->inv.size(); for ( i = 0; i < invsize; ++i ) { if( pSoldier->inv[ i ].exists() == true ) { WorldItem.fExists = TRUE; WorldItem.sGridNo = gModSettings.iInitialPOWItemGridNo[ gStrategicStatus.ubNumCapturedForRescue ]; WorldItem.ubLevel = 0; WorldItem.usFlags = 0; WorldItem.bVisible = FALSE; WorldItem.bRenderZHeightAboveLevel = 0; pSoldier->inv[ i ].MoveThisObjectTo(WorldItem.object); //dnl ch75 271013 pWorldItem[0] = WorldItem; AddWorldItemsToUnLoadedSector( gModSettings.ubInitialPOWSectorX, gModSettings.ubInitialPOWSectorY, 0, gModSettings.iInitialPOWItemGridNo[ gStrategicStatus.ubNumCapturedForRescue ], 1, pWorldItem, FALSE ); } } pSoldier->ubStrategicInsertionCode = INSERTION_CODE_GRIDNO; pSoldier->usStrategicInsertionData = gModSettings.iInitialPOWGridNo[ gStrategicStatus.ubNumCapturedForRescue ]; gStrategicStatus.ubNumCapturedForRescue++; } else if ( gubQuest[ QUEST_HELD_IN_ALMA ] == QUESTDONE ) { //-teleport him to N7 pSoldier->sSectorX = gModSettings.ubMeanwhileInterrogatePOWSectorX; //7 pSoldier->sSectorY = gModSettings.ubMeanwhileInterrogatePOWSectorY; //14 pSoldier->bSectorZ = 0; // put him on the floor!! pSoldier->pathing.bLevel = 0; // OK, drop all items! UINT32 invsize = pSoldier->inv.size(); for ( i = 0; i < invsize; ++i ) { if( pSoldier->inv[ i ].exists() == true ) { WorldItem.fExists = TRUE; WorldItem.sGridNo = gModSettings.iMeanwhileInterrogatePOWItemGridNo[ gStrategicStatus.ubNumCapturedForRescue ]; WorldItem.ubLevel = 0; WorldItem.usFlags = 0; WorldItem.bVisible = FALSE; WorldItem.bRenderZHeightAboveLevel = 0; pSoldier->inv[ i ].MoveThisObjectTo(WorldItem.object); //dnl ch75 271013 pWorldItem[0] = WorldItem; AddWorldItemsToUnLoadedSector( gModSettings.ubMeanwhileInterrogatePOWSectorX, gModSettings.ubMeanwhileInterrogatePOWSectorY, 0, gModSettings.iMeanwhileInterrogatePOWItemGridNo[ gStrategicStatus.ubNumCapturedForRescue ], 1, pWorldItem, FALSE ); } } pSoldier->ubStrategicInsertionCode = INSERTION_CODE_GRIDNO; pSoldier->usStrategicInsertionData = gModSettings.iMeanwhileInterrogatePOWGridNo[ gStrategicStatus.ubNumCapturedForRescue ]; gStrategicStatus.ubNumCapturedForRescue++; } //Bandaging him would prevent him from dying (due to low HP) pSoldier->bBleeding = 0; // wake him up if ( pSoldier->flags.fMercAsleep ) { PutMercInAwakeState( pSoldier ); pSoldier->flags.fForcedToStayAwake = FALSE; } //Set his life to 50% + or - 10 HP. INT8 oldlife = pSoldier->stats.bLife; pSoldier->stats.bLife = pSoldier->stats.bLifeMax / 2; if ( pSoldier->stats.bLife <= 35 ) { pSoldier->stats.bLife = 35; } else if ( pSoldier->stats.bLife >= 45 ) { pSoldier->stats.bLife += (INT8)(10 - Random( 21 ) ); } // SANDRO - make the lost life insta-healable pSoldier->iHealableInjury = ((pSoldier->stats.bLifeMax - pSoldier->stats.bLife) * 100); // make him quite exhausted when found pSoldier->bBreath = pSoldier->bBreathMax = 50; pSoldier->sBreathRed = 0; pSoldier->flags.fMercCollapsedFlag = FALSE; } void HandleEnemyStatusInCurrentMapBeforeLoadingNewMap() { INT32 i; BOOLEAN fMadeCorpse; INT8 bKilledEnemies = 0, bKilledCreatures = 0, bKilledRebels = 0, bKilledCivilians = 0; return; //If any of the soldiers are dying, kill them now. for( i = gTacticalStatus.Team[ ENEMY_TEAM ].bFirstID; i <= gTacticalStatus.Team[ ENEMY_TEAM ].bLastID; i++ ) { if( MercPtrs[ i ]->bActive && MercPtrs[ i ]->stats.bLife < OKLIFE && MercPtrs[ i ]->stats.bLife ) { MercPtrs[ i ]->stats.bLife = 0; MercPtrs[ i ]->iHealableInjury = 0; // added just for sure - SANDRO HandleSoldierDeath( MercPtrs[ i ], &fMadeCorpse ); bKilledEnemies++; } } //Do the same for the creatures. for( i = gTacticalStatus.Team[ CREATURE_TEAM ].bFirstID; i <= gTacticalStatus.Team[ CREATURE_TEAM ].bLastID; i++ ) { if( MercPtrs[ i ]->bActive && MercPtrs[ i ]->stats.bLife < OKLIFE && MercPtrs[ i ]->stats.bLife ) { MercPtrs[ i ]->stats.bLife = 0; HandleSoldierDeath( MercPtrs[ i ], &fMadeCorpse ); bKilledCreatures++; } } //Militia for( i = gTacticalStatus.Team[ MILITIA_TEAM ].bFirstID; i <= gTacticalStatus.Team[ MILITIA_TEAM ].bLastID; i++ ) { if( MercPtrs[ i ]->bActive && MercPtrs[ i ]->stats.bLife < OKLIFE && MercPtrs[ i ]->stats.bLife ) { MercPtrs[ i ]->stats.bLife = 0; MercPtrs[ i ]->iHealableInjury = 0; // added just for sure - SANDRO HandleSoldierDeath( MercPtrs[ i ], &fMadeCorpse ); bKilledRebels++; } } //Civilians for( i = gTacticalStatus.Team[ CIV_TEAM ].bFirstID; i <= gTacticalStatus.Team[ CIV_TEAM ].bLastID; i++ ) { if( MercPtrs[ i ]->bActive && MercPtrs[ i ]->stats.bLife < OKLIFE && MercPtrs[ i ]->stats.bLife ) { MercPtrs[ i ]->stats.bLife = 0; MercPtrs[ i ]->iHealableInjury = 0; // added just for sure - SANDRO HandleSoldierDeath( MercPtrs[ i ], &fMadeCorpse ); bKilledCivilians++; } } // TEST MESSAGES ONLY! if( bKilledCivilians ) ScreenMsg( FONT_BLUE, MSG_TESTVERSION, L"%d civilians died after you left the sector.", bKilledCivilians ); if( bKilledRebels ) ScreenMsg( FONT_BLUE, MSG_TESTVERSION, L"%d militia died after you left the sector.", bKilledRebels ); if( bKilledEnemies ) ScreenMsg( FONT_BLUE, MSG_TESTVERSION, L"%d enemies died after you left the sector.", bKilledEnemies ); if( bKilledCreatures ) ScreenMsg( FONT_BLUE, MSG_TESTVERSION, L"%d creatures died after you left the sector.", bKilledCreatures ); if( !gbWorldSectorZ ) { SECTORINFO *pSector; pSector = &SectorInfo[ SECTOR(gWorldSectorX, gWorldSectorY) ]; pSector->ubAdminsInBattle = 0; pSector->ubTroopsInBattle = 0; pSector->ubElitesInBattle = 0; pSector->ubCreaturesInBattle = 0; //RecalculateSectorWeight( } else if( gbWorldSectorZ > 0 ) { UNDERGROUND_SECTORINFO *pSector; pSector = FindUnderGroundSector( gWorldSectorX, gWorldSectorY, gbWorldSectorZ ); if( !pSector ) return; pSector->ubAdminsInBattle = 0; pSector->ubTroopsInBattle = 0; pSector->ubElitesInBattle = 0; pSector->ubCreaturesInBattle = 0; } } BOOLEAN PlayerSectorDefended( UINT8 ubSectorID ) { if ( NumNonPlayerTeamMembersInSector( SECTORX( ubSectorID ), SECTORY( ubSectorID ), MILITIA_TEAM ) ) { //militia in sector return TRUE; } if ( FindMovementGroupInSector( SECTORX( ubSectorID ), SECTORY( ubSectorID ), OUR_TEAM ) ) { // player/militia in sector return TRUE; } return FALSE; } //Assumes gTacticalStatus.fEnemyInSector BOOLEAN OnlyHostileCivsInSector() { SOLDIERTYPE *pSoldier; INT32 i; BOOLEAN fHostileCivs = FALSE; //Look for any hostile civs. for( i = gTacticalStatus.Team[ CIV_TEAM ].bFirstID; i <= gTacticalStatus.Team[ CIV_TEAM ].bLastID; i++ ) { pSoldier = MercPtrs[ i ]; if( pSoldier->bActive && pSoldier->bInSector && pSoldier->stats.bLife ) { if( !pSoldier->aiData.bNeutral ) { fHostileCivs = TRUE; break; } } } if( !fHostileCivs ) { //No hostile civs, so return FALSE return FALSE; } //Look for anybody else hostile. If found, return FALSE immediately. for( i = gTacticalStatus.Team[ ENEMY_TEAM ].bFirstID; i <= gTacticalStatus.Team[ ENEMY_TEAM ].bLastID; i++ ) { pSoldier = MercPtrs[ i ]; if( pSoldier->bActive && pSoldier->bInSector && pSoldier->stats.bLife ) { if( !pSoldier->aiData.bNeutral ) { return FALSE; } } } for( i = gTacticalStatus.Team[ CREATURE_TEAM ].bFirstID; i <= gTacticalStatus.Team[ CREATURE_TEAM ].bLastID; i++ ) { pSoldier = MercPtrs[ i ]; if( pSoldier->bActive && pSoldier->bInSector && pSoldier->stats.bLife ) { if( !pSoldier->aiData.bNeutral ) { return FALSE; } } } for( i = gTacticalStatus.Team[ MILITIA_TEAM ].bFirstID; i <= gTacticalStatus.Team[ MILITIA_TEAM ].bLastID; i++ ) { pSoldier = MercPtrs[ i ]; if( pSoldier->bActive && pSoldier->bInSector && pSoldier->stats.bLife ) { if( !pSoldier->aiData.bNeutral ) { return FALSE; } } } //We only have hostile civilians, don't allow time compression. return TRUE; } // Flugente check whether a team has nearby members that can be added to the current battle // Note that this requires separate counts of teammembers in the sector and those that are actually fighting // This currently does not exist for MILITIA_TEAM, making such a check relatively useless BOOLEAN CheckPendingNonPlayerTeam( UINT8 usTeam ) { if( gbWorldSectorZ ) return FALSE; SECTORINFO *pSector = &SectorInfo[SECTOR( gWorldSectorX, gWorldSectorY )]; if ( usTeam == ENEMY_TEAM ) { if ( (pSector->ubNumElites + pSector->ubNumTroops + pSector->ubNumAdmins + pSector->ubNumTanks + pSector->ubNumJeeps) > (pSector->ubElitesInBattle + pSector->ubTroopsInBattle + pSector->ubAdminsInBattle + pSector->ubTanksInBattle + pSector->ubJeepsInBattle) ) return TRUE; } for (GROUP *pGroup = gpGroupList; pGroup; pGroup = pGroup->next) { if ( pGroup->usGroupTeam == usTeam && !pGroup->fVehicle && pGroup->ubSectorX == gWorldSectorX && pGroup->ubSectorY == gWorldSectorY ) { if ( usTeam == ENEMY_TEAM ) { if ( pGroup->ubGroupSize > pGroup->pEnemyGroup->ubElitesInBattle + pGroup->pEnemyGroup->ubTroopsInBattle + pGroup->pEnemyGroup->ubAdminsInBattle + pGroup->pEnemyGroup->ubTanksInBattle + pGroup->pEnemyGroup->ubJeepsInBattle ) return TRUE; } else if ( usTeam == MILITIA_TEAM ) { if ( pGroup->ubGroupSize > pSector->ubNumberOfCivsAtLevel[GREEN_MILITIA] + pSector->ubNumberOfCivsAtLevel[REGULAR_MILITIA] + pSector->ubNumberOfCivsAtLevel[ELITE_MILITIA] ) return TRUE; } } } return FALSE; } #ifdef JA2UB void HandleBloodCatDeaths( SECTORINFO *pSector ) { //if the current sector is the first part of the town if( gWorldSectorX == 10 && gWorldSectorY == 9 && gbWorldSectorZ == 0 ) { //if ALL the bloodcats are killed if( pSector->bBloodCats == 0 ) { UINT8 bId1=-1; UINT8 bId2=-1; UINT8 bNum=0; SetFactTrue( FACT_PLAYER_KILLED_ALL_BETTYS_BLOODCATS ); //Instantly have betties missing items show up //DailyCheckOnItemQuantities( TRUE ); // Now have a merc say the killed bloodcat quote bNum = Get3RandomQualifiedMercs( &bId1, &bId2, NULL ); //if there are some qualified mercs if( bNum != 0 ) { //must make sure TEX doesnt say the quote if( bId1 != NOBODY && Menptr[ bId1 ].ubProfile != TEX_UB ) { TacticalCharacterDialogue( &Menptr[ bId1 ], QUOTE_RENEW_REFUSAL_DUE_TO_LACK_OF_FUNDS ); } else if( bId2 != NOBODY && Menptr[ bId2 ].ubProfile != TEX_UB ) { TacticalCharacterDialogue( &Menptr[ bId2 ], QUOTE_RENEW_REFUSAL_DUE_TO_LACK_OF_FUNDS ); } } } } } #endif