//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" #endif #ifdef JA2BETAVERSION extern BOOLEAN gfClearCreatureQuest; #endif #include "Reinforcement.h" //The sector information required for the strategic AI. Contains the number of enemy troops, //as well as intentions, etc. SECTORINFO SectorInfo[256]; UNDERGROUND_SECTORINFO *gpUndergroundSectorInfoHead = NULL; extern UNDERGROUND_SECTORINFO* gpUndergroundSectorInfoTail; BOOLEAN gfPendingEnemies = FALSE; extern void BuildUndergroundSectorInfoList(); extern void EndCreatureQuest(); extern GARRISON_GROUP *gGarrisonGroup; extern INT32 giGarrisonArraySize; #ifdef JA2TESTVERSION extern BOOLEAN gfOverrideSector; #endif INT16 gsInterrogationGridNo[3] = { 7756, 7757, 7758 }; void ValidateEnemiesHaveWeapons() { #ifdef JA2BETAVERSION SGPRect CenteringRect= {0, 0, 639, 479 }; 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; } if( !pSoldier->inv[ HANDPOS ].usItem ) { iNumInvalid++; } } // do message box and return if( iNumInvalid ) { UINT16 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; Assert( sSectorX >= 1 && sSectorX <= 16 ); Assert( sSectorY >= 1 && sSectorY <= 16 ); Assert( sSectorZ >= 0 && sSectorZ <= 3 ); if( sSectorZ ) { UNDERGROUND_SECTORINFO *pSector; pSector = FindUnderGroundSector( sSectorX, sSectorY, (UINT8)sSectorZ ); if( pSector ) { ubNumHostiles = (UINT8)(pSector->ubNumAdmins + pSector->ubNumTroops + pSector->ubNumElites + pSector->ubNumCreatures); } } else { SECTORINFO *pSector; GROUP *pGroup; //Count stationary hostiles pSector = &SectorInfo[ SECTOR( sSectorX, sSectorY ) ]; ubNumHostiles = (UINT8)(pSector->ubNumAdmins + pSector->ubNumTroops + pSector->ubNumElites + pSector->ubNumCreatures); //Count mobile enemies pGroup = gpGroupList; while( pGroup ) { if( !pGroup->fPlayer && !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; Assert( sSectorX >= 1 && sSectorX <= 16 ); Assert( sSectorY >= 1 && sSectorY <= 16 ); Assert( sSectorZ >= 0 && sSectorZ <= 3 ); if( sSectorZ ) { UNDERGROUND_SECTORINFO *pSector; pSector = FindUnderGroundSector( sSectorX, sSectorY, (UINT8)sSectorZ ); if( pSector ) { ubNumEnemies = (UINT8)(pSector->ubNumAdmins + pSector->ubNumTroops + pSector->ubNumElites); } } else { SECTORINFO *pSector; GROUP *pGroup; //Count stationary enemies pSector = &SectorInfo[ SECTOR( sSectorX, sSectorY ) ]; ubNumEnemies = (UINT8)(pSector->ubNumAdmins + pSector->ubNumTroops + pSector->ubNumElites); //Count mobile enemies pGroup = gpGroupList; while( pGroup ) { if( !pGroup->fPlayer && !pGroup->fVehicle && pGroup->ubSectorX == sSectorX && pGroup->ubSectorY == sSectorY ) { ubNumEnemies += pGroup->ubGroupSize; } pGroup = pGroup->next; } } return ubNumEnemies; } UINT8 NumEnemiesInSector( INT16 sSectorX, INT16 sSectorY ) { SECTORINFO *pSector; GROUP *pGroup; UINT8 ubNumTroops; Assert( sSectorX >= 1 && sSectorX <= 16 ); Assert( sSectorY >= 1 && sSectorY <= 16 ); pSector = &SectorInfo[ SECTOR( sSectorX, sSectorY ) ]; ubNumTroops = (UINT8)(pSector->ubNumAdmins + pSector->ubNumTroops + pSector->ubNumElites); pGroup = gpGroupList; while( pGroup ) { if( !pGroup->fPlayer && !pGroup->fVehicle && pGroup->ubSectorX == sSectorX && pGroup->ubSectorY == sSectorY ) { ubNumTroops += pGroup->ubGroupSize; } pGroup = pGroup->next; } return ubNumTroops; } UINT8 NumStationaryEnemiesInSector( INT16 sSectorX, INT16 sSectorY ) { SECTORINFO *pSector; Assert( sSectorX >= 1 && sSectorX <= 16 ); Assert( sSectorY >= 1 && sSectorY <= 16 ); pSector = &SectorInfo[ SECTOR( sSectorX, sSectorY ) ]; if( pSector->ubGarrisonID == NO_GARRISON ) { //If no garrison, no stationary. return( 0 ); } // 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); } UINT8 NumMobileEnemiesInSector( INT16 sSectorX, INT16 sSectorY ) { GROUP *pGroup; SECTORINFO *pSector; UINT8 ubNumTroops; Assert( sSectorX >= 1 && sSectorX <= 16 ); Assert( sSectorY >= 1 && sSectorY <= 16 ); ubNumTroops = 0; pGroup = gpGroupList; while( pGroup ) { if( !pGroup->fPlayer && !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); } return ubNumTroops; } void GetNumberOfMobileEnemiesInSector( INT16 sSectorX, INT16 sSectorY, UINT8 *pubNumAdmins, UINT8 *pubNumTroops, UINT8 *pubNumElites ) { GROUP *pGroup; SECTORINFO *pSector; Assert( sSectorX >= 1 && sSectorX <= 16 ); Assert( sSectorY >= 1 && sSectorY <= 16 ); //Now count the number of mobile groups in the sector. *pubNumTroops = *pubNumElites = *pubNumAdmins = 0; pGroup = gpGroupList; while( pGroup ) { if( !pGroup->fPlayer && !pGroup->fVehicle && pGroup->ubSectorX == sSectorX && pGroup->ubSectorY == sSectorY ) { *pubNumTroops += pGroup->pEnemyGroup->ubNumTroops; *pubNumElites += pGroup->pEnemyGroup->ubNumElites; *pubNumAdmins += pGroup->pEnemyGroup->ubNumAdmins; } 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; } } void GetNumberOfMobileEnemiesInSectorWithoutRoadBlock( INT16 sSectorX, INT16 sSectorY, UINT8 *pubNumAdmins, UINT8 *pubNumTroops, UINT8 *pubNumElites ) { GROUP *pGroup; SECTORINFO *pSector; Assert( sSectorX >= 1 && sSectorX <= 16 ); Assert( sSectorY >= 1 && sSectorY <= 16 ); //Now count the number of mobile groups in the sector. *pubNumTroops = *pubNumElites = *pubNumAdmins = 0; pGroup = gpGroupList; while( pGroup ) { if( !pGroup->fPlayer && !pGroup->fVehicle && pGroup->ubSectorX == sSectorX && pGroup->ubSectorY == sSectorY ) { *pubNumTroops += pGroup->pEnemyGroup->ubNumTroops; *pubNumElites += pGroup->pEnemyGroup->ubNumElites; *pubNumAdmins += pGroup->pEnemyGroup->ubNumAdmins; } pGroup = pGroup->next; } } void GetNumberOfStationaryEnemiesInSector( INT16 sSectorX, INT16 sSectorY, UINT8 *pubNumAdmins, UINT8 *pubNumTroops, UINT8 *pubNumElites ) { SECTORINFO *pSector; Assert( sSectorX >= 1 && sSectorX <= 16 ); Assert( sSectorY >= 1 && sSectorY <= 16 ); pSector = &SectorInfo[ SECTOR( sSectorX, sSectorY ) ]; //grab the number of each type in the stationary sector *pubNumAdmins = pSector->ubNumAdmins; *pubNumTroops = pSector->ubNumTroops; *pubNumElites = pSector->ubNumElites; } void GetNumberOfEnemiesInSector( INT16 sSectorX, INT16 sSectorY, UINT8 *pubNumAdmins, UINT8 *pubNumTroops, UINT8 *pubNumElites ) { UINT8 ubNumAdmins, ubNumTroops, ubNumElites; GetNumberOfStationaryEnemiesInSector( sSectorX, sSectorY, pubNumAdmins, pubNumTroops, pubNumElites ); GetNumberOfMobileEnemiesInSector( sSectorX, sSectorY, &ubNumAdmins, &ubNumTroops, &ubNumElites ); *pubNumAdmins += ubNumAdmins; *pubNumTroops += ubNumTroops; *pubNumElites += ubNumElites; } void EndTacticalBattleForEnemy() { GROUP *pGroup; INT32 i, iNumMilitia = 0, iNumEnemies = 0; //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; } 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->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->fPlayer && !pGroup->fVehicle && pGroup->ubSectorX == gWorldSectorX && pGroup->ubSectorY == gWorldSectorY ) { pGroup->pEnemyGroup->ubTroopsInBattle = 0; pGroup->pEnemyGroup->ubElitesInBattle = 0; pGroup->pEnemyGroup->ubAdminsInBattle = 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 ]->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 ]->bLife >= OKLIFE ) { //confirmed at least one enemy here, so do the loyalty penalty. HandleGlobalLoyaltyEvent( GLOBAL_LOYALTY_ABANDON_MILITIA, gWorldSectorX, gWorldSectorY, 0 ); break; } } break; } } } UINT8 NumFreeEnemySlots() { UINT8 ubNumFreeSlots = 0; INT32 i; SOLDIERTYPE *pSoldier; //Count the number of free enemy slots. It is possible to have multiple groups exceed the maximum. for( i = gTacticalStatus.Team[ ENEMY_TEAM ].bFirstID; i <= gTacticalStatus.Team[ ENEMY_TEAM].bLastID; i++ ) { pSoldier = &Menptr[ i ]; if( !pSoldier->bActive ) ubNumFreeSlots++; } 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; UINT8 ubNumAdmins, ubNumTroops, ubNumElites; UINT8 ubTotalAdmins, ubTotalElites, ubTotalTroops; UINT8 ubStationaryEnemies; INT32 i, num; INT16 sNumSlots; gfPendingEnemies = FALSE; if( gbWorldSectorZ > 0 ) return PrepareEnemyForUndergroundBattle(); if( gpBattleGroup && !gpBattleGroup->fPlayer ) { //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 ); //It is possible that other enemy groups have also arrived. Add them in the same manner. pGroup = gpGroupList; while( pGroup ) { if( pGroup != gpBattleGroup && !pGroup->fPlayer && !pGroup->fVehicle && pGroup->ubSectorX == gpBattleGroup->ubSectorX && pGroup->ubSectorY == gpBattleGroup->ubSectorY && !pGroup->pEnemyGroup->ubAdminsInBattle && !pGroup->pEnemyGroup->ubTroopsInBattle && !pGroup->pEnemyGroup->ubElitesInBattle ) { HandleArrivalOfReinforcements( pGroup ); } pGroup = pGroup->next; } ValidateEnemiesHaveWeapons(); return ( ( BOOLEAN) ( gpBattleGroup->ubGroupSize > 0 ) ); } if( !gbWorldSectorZ ) { if( NumEnemiesInSector( gWorldSectorX, gWorldSectorY ) > 32 ) { gfPendingEnemies = TRUE; } } pSector = &SectorInfo[ SECTOR( gWorldSectorX, gWorldSectorY ) ]; if( pSector->uiFlags & SF_USE_MAP_SETTINGS ) { //count the number of enemy placements in a map and use those SOLDIERINITNODE *curr; curr = gSoldierInitHead; ubTotalAdmins = ubTotalTroops = ubTotalElites = 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; } } curr = curr->next; } pSector->ubNumAdmins = ubTotalAdmins; pSector->ubNumTroops = ubTotalTroops; pSector->ubNumElites = ubTotalElites; pSector->ubAdminsInBattle = 0; pSector->ubTroopsInBattle = 0; pSector->ubElitesInBattle = 0; } else { ubTotalAdmins = (UINT8)(pSector->ubNumAdmins - pSector->ubAdminsInBattle); ubTotalTroops = (UINT8)(pSector->ubNumTroops - pSector->ubTroopsInBattle); ubTotalElites = (UINT8)(pSector->ubNumElites - pSector->ubElitesInBattle); } ubStationaryEnemies = (UINT8)(ubTotalAdmins + ubTotalTroops + ubTotalElites); if( ubTotalAdmins + ubTotalTroops + ubTotalElites > 32 ) { #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, 32 ); #endif ubTotalAdmins = min( 32, ubTotalAdmins ); ubTotalTroops = min( 32-ubTotalAdmins, ubTotalTroops ); ubTotalElites = min( 32-ubTotalAdmins+ubTotalTroops, ubTotalElites ); } pSector->ubAdminsInBattle += ubTotalAdmins; pSector->ubTroopsInBattle += ubTotalTroops; pSector->ubElitesInBattle += ubTotalElites; #ifdef JA2TESTVERSION if( gfOverrideSector ) { //if there are no troops in the current groups, then we're done. if( !ubTotalAdmins && !ubTotalTroops && !ubTotalElites ) return FALSE; AddSoldierInitListEnemyDefenceSoldiers( ubTotalAdmins, ubTotalTroops, ubTotalElites ); ValidateEnemiesHaveWeapons(); return TRUE; } #endif //Search for movement groups that happen to be in the sector. sNumSlots = NumFreeEnemySlots(); //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; //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. pGroup = gpGroupList; while( pGroup && sNumSlots ) { if( !pGroup->fPlayer && !pGroup->fVehicle && pGroup->ubSectorX == gWorldSectorX && pGroup->ubSectorY == gWorldSectorY && !gbWorldSectorZ ) { //Process enemy group in sector. if( sNumSlots > 0 ) { ubNumAdmins = (UINT8)(pGroup->pEnemyGroup->ubNumAdmins - pGroup->pEnemyGroup->ubAdminsInBattle); sNumSlots -= ubNumAdmins; if( sNumSlots < 0 ) { //adjust the value to zero ubNumAdmins += sNumSlots; sNumSlots = 0; gfPendingEnemies = TRUE; } pGroup->pEnemyGroup->ubAdminsInBattle += ubNumAdmins; ubTotalAdmins += ubNumAdmins; } if( sNumSlots > 0 ) { //Add regular army forces. ubNumTroops = (UINT8)(pGroup->pEnemyGroup->ubNumTroops - pGroup->pEnemyGroup->ubTroopsInBattle); sNumSlots -= ubNumTroops; if( sNumSlots < 0 ) { //adjust the value to zero ubNumTroops += sNumSlots; sNumSlots = 0; gfPendingEnemies = TRUE; } pGroup->pEnemyGroup->ubTroopsInBattle += ubNumTroops; ubTotalTroops += ubNumTroops; } if( sNumSlots > 0 ) { //Add elite troops ubNumElites = (UINT8)(pGroup->pEnemyGroup->ubNumElites - pGroup->pEnemyGroup->ubElitesInBattle); sNumSlots -= ubNumElites; if( sNumSlots < 0 ) { //adjust the value to zero ubNumElites += sNumSlots; sNumSlots = 0; gfPendingEnemies = TRUE; } pGroup->pEnemyGroup->ubElitesInBattle += ubNumElites; ubTotalElites += ubNumElites; } //NOTE: //no provisions for profile troop leader or retreat groups yet. } if( pGroup->fPlayer && !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 ) { pGroup = pGroup->next; continue; } // clear the movt for this grunt and his buddies RemoveGroupWaypoints( pGroup->ubGroupID ); } pGroup = pGroup->next; } //if there are no troops in the current groups, then we're done. if( !ubTotalAdmins && !ubTotalTroops && !ubTotalElites ) { return FALSE; } AddSoldierInitListEnemyDefenceSoldiers( ubTotalAdmins, ubTotalTroops, ubTotalElites ); //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 = 32 - ubStationaryEnemies; pGroup = gpGroupList; while( pGroup && sNumSlots ) { i = gTacticalStatus.Team[ ENEMY_TEAM ].bFirstID; pSoldier = &Menptr[ i ]; if( !pGroup->fPlayer && !pGroup->fVehicle && pGroup->ubSectorX == gWorldSectorX && pGroup->ubSectorY == gWorldSectorY && !gbWorldSectorZ ) { num = pGroup->ubGroupSize; ubNumAdmins = pGroup->pEnemyGroup->ubAdminsInBattle; ubNumTroops = pGroup->pEnemyGroup->ubTroopsInBattle; ubNumElites = pGroup->pEnemyGroup->ubElitesInBattle; while( num && sNumSlots && i <= gTacticalStatus.Team[ ENEMY_TEAM ].bLastID ) { while( !pSoldier->bActive || pSoldier->ubGroupID ) { pSoldier = &Menptr[ ++i ]; if( i > gTacticalStatus.Team[ ENEMY_TEAM ].bLastID ) { AssertMsg( 0, "Failed to assign battle counters for enemies properly. Please send save. KM: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; } pSoldier = &Menptr[ ++i ]; } } pGroup = pGroup->next; } ValidateEnemiesHaveWeapons(); return TRUE; } BOOLEAN PrepareEnemyForUndergroundBattle() { UNDERGROUND_SECTORINFO *pUnderground; UINT8 ubTotalAdmins, ubTotalTroops, ubTotalElites; 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); pUnderground->ubAdminsInBattle += ubTotalAdmins; pUnderground->ubTroopsInBattle += ubTotalTroops; pUnderground->ubElitesInBattle += ubTotalElites; AddSoldierInitListEnemyDefenceSoldiers( pUnderground->ubNumAdmins, pUnderground->ubNumTroops, pUnderground->ubNumElites ); ValidateEnemiesHaveWeapons(); } return ( ( BOOLEAN) ( pUnderground->ubNumAdmins + pUnderground->ubNumTroops + pUnderground->ubNumElites > 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; // UINT16 str[128]; INT32 iMaxEnemyGroupSize = gGameExternalOptions.iMaxEnemyGroupSize; DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"QueenCommand"); EvaluateDeathEffectsToSoldierInitList( pSoldier ); switch( pSoldier->ubProfile ) { 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; } if( pSoldier->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 UINT16 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->fPlayer ) { #ifdef JA2BETAVERSION UINT16 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; } 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 && !gfPendingEnemies || 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 && !gfPendingEnemies || pGroup->ubGroupSize > iMaxEnemyGroupSize || pGroup->pEnemyGroup->ubNumTroops > 50 || pGroup->pEnemyGroup->ubTroopsInBattle > 50 ) { 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 && !gfPendingEnemies || 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; #endif switch( pSoldier->ubSoldierClass ) { case SOLDIER_CLASS_ADMINISTRATOR: #ifdef JA2BETAVERSION if( guiCurrentScreen == GAME_SCREEN ) { if( ubTotalEnemies <= 32 && pSector->ubNumAdmins != pSector->ubAdminsInBattle || !pSector->ubNumAdmins || !pSector->ubAdminsInBattle || pSector->ubNumAdmins > 100 || pSector->ubAdminsInBattle > 32 ) { 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 <= 32 && pSector->ubNumTroops != pSector->ubTroopsInBattle || !pSector->ubNumTroops || !pSector->ubTroopsInBattle || pSector->ubNumTroops > 100 || pSector->ubTroopsInBattle > 32 ) { 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 ); } } #endif if( pSector->ubNumTroops ) { pSector->ubNumTroops--; } if( pSector->ubTroopsInBattle ) { pSector->ubTroopsInBattle--; } break; case SOLDIER_CLASS_ELITE: #ifdef JA2BETAVERSION if( guiCurrentScreen == GAME_SCREEN ) { if( ubTotalEnemies <= 32 && pSector->ubNumElites != pSector->ubElitesInBattle || !pSector->ubNumElites || !pSector->ubElitesInBattle || pSector->ubNumElites > 100 || pSector->ubElitesInBattle > 32 ) { 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 <= 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--; } } 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; #endif if( pSector ) { switch( pSoldier->ubSoldierClass ) { case SOLDIER_CLASS_ADMINISTRATOR: #ifdef JA2BETAVERSION if( ubTotalEnemies <= 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 <= 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 <= 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: #ifdef JA2BETAVERSION if( ubTotalEnemies <= iMaxEnemyGroupSize && pSector->ubNumCreatures != pSector->ubCreaturesInBattle || !pSector->ubNumCreatures || !pSector->ubCreaturesInBattle || pSector->ubNumCreatures > 50 || pSector->ubCreaturesInBattle > 50 ) { 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 = NumEnemiesInSector( pSoldier->sSectorX, pSoldier->sSectorY ); if( iNumEnemiesInSector ) { if( pSector->bLastKnownEnemies >= 0 ) { pSector->bLastKnownEnemies = (INT8)iNumEnemiesInSector; } } else { pSector->bLastKnownEnemies = 0; } } } //Rarely, there will be more enemies than supported by the engine. In this case, these //soldier's 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 32 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() { UINT8 ubSlots, ubNumAvailable; UINT8 ubNumElites, ubNumTroops, ubNumAdmins; GROUP *pGroup; SECTORINFO *pSector = &SectorInfo[ SECTOR( gWorldSectorX, gWorldSectorY ) ]; static UINT8 ubPredefinedInsertionCode = 255; if( ( !PlayerMercsInSector( gWorldSectorX, gWorldSectorY, 0 ) && !CountAllMilitiaInSector( gWorldSectorX, gWorldSectorY ) ) || !NumEnemiesInSector( gWorldSectorX, gWorldSectorY ) ) return; /* if( !gfPendingEnemies ) { //Optimization. No point in checking 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. return; }*/ ubSlots = NumFreeEnemySlots(); if( !ubSlots ) { //no available slots to add enemies to. Try again later... return; } if( pSector->ubNumElites + pSector->ubNumTroops + pSector->ubNumAdmins ) { ubNumElites = ubNumTroops = ubNumAdmins = 0; ubNumAvailable = pSector->ubNumElites + pSector->ubNumTroops + pSector->ubNumAdmins - pSector->ubElitesInBattle - pSector->ubTroopsInBattle - pSector->ubAdminsInBattle; while( ubNumAvailable && ubSlots ) { //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 { AssertMsg( 0, "AddPossiblePendingEnemiesToBattle(): Logic Error -- by Kris" ); } } if( ubNumAdmins || ubNumTroops || ubNumElites ) { //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( NumEnemiesInSector( gWorldSectorX + 1, gWorldSectorY ) ) ubStrategicInsertionCode = INSERTION_CODE_EAST; else if( NumEnemiesInSector( gWorldSectorX - 1, gWorldSectorY ) ) ubStrategicInsertionCode = INSERTION_CODE_WEST; else if( NumEnemiesInSector( gWorldSectorX, gWorldSectorY + 1 ) ) ubStrategicInsertionCode = INSERTION_CODE_SOUTH; else if( NumEnemiesInSector( gWorldSectorX, gWorldSectorY - 1 ) ) ubStrategicInsertionCode = INSERTION_CODE_NORTH; } if( ubStrategicInsertionCode == 255 ) { if( !CountAllMilitiaInSector( gWorldSectorX + 1, gWorldSectorY ) ) ubStrategicInsertionCode = INSERTION_CODE_EAST; else if( !CountAllMilitiaInSector( gWorldSectorX - 1, gWorldSectorY ) ) ubStrategicInsertionCode = INSERTION_CODE_WEST; else if( !CountAllMilitiaInSector( gWorldSectorX, gWorldSectorY + 1 ) ) ubStrategicInsertionCode = INSERTION_CODE_SOUTH; else if( !CountAllMilitiaInSector( gWorldSectorX, gWorldSectorY - 1 ) ) 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, FALSE ); gfPendingEnemies = TRUE; } } ubPredefinedInsertionCode = 255; pGroup = gpGroupList; while( pGroup && ubSlots ) { if( !pGroup->fPlayer && !pGroup->fVehicle && pGroup->ubSectorX == gWorldSectorX && pGroup->ubSectorY == gWorldSectorY && !gbWorldSectorZ ) { //This enemy group is currently in the sector. ubNumElites = ubNumTroops = ubNumAdmins = 0; ubNumAvailable = pGroup->ubGroupSize - pGroup->pEnemyGroup->ubElitesInBattle - pGroup->pEnemyGroup->ubTroopsInBattle - pGroup->pEnemyGroup->ubAdminsInBattle; while( ubNumAvailable && ubSlots ) { //This group has enemies waiting for a chance to enter the battle. if( pGroup->pEnemyGroup->ubElitesInBattle < pGroup->pEnemyGroup->ubNumElites ) { //Add an elite troop pGroup->pEnemyGroup->ubElitesInBattle++; ubNumAvailable--; ubSlots--; ubNumElites++; } else if( pGroup->pEnemyGroup->ubTroopsInBattle < pGroup->pEnemyGroup->ubNumTroops ) { //Add a regular troop. pGroup->pEnemyGroup->ubTroopsInBattle++; ubNumAvailable--; ubSlots--; ubNumTroops++; } else if( pGroup->pEnemyGroup->ubAdminsInBattle < pGroup->pEnemyGroup->ubNumAdmins ) { //Add an admin troop pGroup->pEnemyGroup->ubAdminsInBattle++; ubNumAvailable--; ubSlots--; ubNumAdmins++; } else { AssertMsg( 0, "AddPossiblePendingEnemiesToBattle(): Logic Error -- by Kris" ); } } if( ubNumAdmins || ubNumTroops || ubNumElites ) { //This group has contributed forces, then add them now, because different //groups appear on different sides of the map. UINT8 ubStrategicInsertionCode=0; //First, determine which entrypoint to use, based on the travel direction of the group. if( pGroup->ubPrevX && pGroup->ubPrevY ) { if( pGroup->ubSectorX < pGroup->ubPrevX ) ubStrategicInsertionCode = INSERTION_CODE_EAST; else if( pGroup->ubSectorX > pGroup->ubPrevX ) ubStrategicInsertionCode = INSERTION_CODE_WEST; else if( pGroup->ubSectorY < pGroup->ubPrevY ) ubStrategicInsertionCode = INSERTION_CODE_SOUTH; else if( pGroup->ubSectorY > pGroup->ubPrevY ) ubStrategicInsertionCode = INSERTION_CODE_NORTH; } else if( pGroup->ubNextX && pGroup->ubNextY ) { if( pGroup->ubSectorX < pGroup->ubNextX ) ubStrategicInsertionCode = INSERTION_CODE_EAST; else if( pGroup->ubSectorX > pGroup->ubNextX ) ubStrategicInsertionCode = INSERTION_CODE_WEST; else if( pGroup->ubSectorY < pGroup->ubNextY ) ubStrategicInsertionCode = INSERTION_CODE_SOUTH; else if( pGroup->ubSectorY > pGroup->ubNextY ) ubStrategicInsertionCode = INSERTION_CODE_NORTH; } //Add the number of each type of troop and place them in the appropriate positions AddEnemiesToBattle( pGroup, ubStrategicInsertionCode, ubNumAdmins, ubNumTroops, ubNumElites, FALSE ); gfPendingEnemies = TRUE; } } pGroup = pGroup->next; } 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. gfPendingEnemies = FALSE; if( gTacticalStatus.Team[ ENEMY_TEAM ].bAwareOfOpposition == TRUE ) ubPredefinedInsertionCode = DoReinforcementAsPendingEnemy( gWorldSectorX, gWorldSectorY ); else ubPredefinedInsertionCode = 255; if( ubPredefinedInsertionCode != 255 ) AddPossiblePendingEnemiesToBattle(); } } 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->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->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->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, 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; } 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; } } //Because the enemies magically appeared, have one of our soldiers say something... NotifyPlayersOfNewEnemies(); } ubTotalSoldiers = ubNumAdmins + ubNumTroops + ubNumElites; ChooseMapEdgepoints( &MapEdgepointInfo, ubStrategicInsertionCode, (UINT8)(ubNumAdmins+ubNumElites+ubNumTroops) ); ubCurrSlot = 0; while( ubTotalSoldiers ) { if( ubNumElites && Random( ubTotalSoldiers ) < ubNumElites ) { ubNumElites--; ubTotalSoldiers--; 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--; 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--; 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 ); } } } 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( ) { // 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! { MEANWHILE_DEFINITION MeanwhileDef; MeanwhileDef.sSectorX = 7; MeanwhileDef.sSectorY = 14; MeanwhileDef.ubNPCNumber = QUEEN; MeanwhileDef.usTriggerEvent = 0; MeanwhileDef.ubMeanwhileID = INTERROGATION; ScheduleMeanwhileEvent( &MeanwhileDef, 10 ); } } // 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; } } } void EnemyCapturesPlayerSoldier( SOLDIERTYPE *pSoldier ) { INT32 i; WORLDITEM WorldItem; BOOLEAN fMadeCorpse; INT32 iNumEnemiesInSector; static INT16 sAlmaCaptureGridNos[] = { 9208, 9688, 9215 }; static INT16 sAlmaCaptureItemsGridNo[] = { 12246, 12406, 13046 }; static INT16 sInterrogationItemGridNo[] = { 12089, 12089, 12089 }; // 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->bLife = 0; HandleSoldierDeath( pSoldier, &fMadeCorpse ); return; } if ( pSoldier->uiStatusFlags & SOLDIER_VEHICLE ) { return; } // ATE: Patch fix If in a vehicle, remove from vehicle... TakeSoldierOutOfVehicle( pSoldier ); // Are there anemies in ALMA? ( I13 ) iNumEnemiesInSector = NumEnemiesInSector( 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->bNeutral = TRUE; } RemoveCharacterFromSquads( pSoldier ); // 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 = 13; pSoldier->sSectorY = 9; pSoldier->bSectorZ = 0; // put him on the floor!! pSoldier->bLevel = 0; // OK, drop all items! for ( i = 0; i < NUM_INV_SLOTS; i++ ) { if( pSoldier->inv[ i ].usItem != 0 ) { WorldItem.fExists = TRUE; WorldItem.sGridNo = sAlmaCaptureItemsGridNo[ gStrategicStatus.ubNumCapturedForRescue ]; WorldItem.ubLevel = 0; WorldItem.usFlags = 0; WorldItem.bVisible = FALSE; WorldItem.bRenderZHeightAboveLevel = 0; memcpy( &(WorldItem.o), &pSoldier->inv[i], sizeof( OBJECTTYPE ) ); AddWorldItemsToUnLoadedSector( 13, 9, 0, sAlmaCaptureItemsGridNo[ gStrategicStatus.ubNumCapturedForRescue ], 1, &WorldItem, FALSE ); DeleteObj( &( pSoldier->inv[ i ] ) ); } } pSoldier->ubStrategicInsertionCode = INSERTION_CODE_GRIDNO; pSoldier->usStrategicInsertionData = sAlmaCaptureGridNos[ gStrategicStatus.ubNumCapturedForRescue ]; gStrategicStatus.ubNumCapturedForRescue++; } else if ( gubQuest[ QUEST_HELD_IN_ALMA ] == QUESTDONE ) { //-teleport him to N7 pSoldier->sSectorX = 7; pSoldier->sSectorY = 14; pSoldier->bSectorZ = 0; // put him on the floor!! pSoldier->bLevel = 0; // OK, drop all items! for ( i = 0; i < NUM_INV_SLOTS; i++ ) { if( pSoldier->inv[ i ].usItem != 0 ) { WorldItem.fExists = TRUE; WorldItem.sGridNo = sInterrogationItemGridNo[ gStrategicStatus.ubNumCapturedForRescue ]; WorldItem.ubLevel = 0; WorldItem.usFlags = 0; WorldItem.bVisible = FALSE; WorldItem.bRenderZHeightAboveLevel = 0; memcpy( &(WorldItem.o), &pSoldier->inv[i], sizeof( OBJECTTYPE ) ); AddWorldItemsToUnLoadedSector( 7, 14, 0, sInterrogationItemGridNo[ gStrategicStatus.ubNumCapturedForRescue ], 1, &WorldItem, FALSE ); DeleteObj( &( pSoldier->inv[ i ] ) ); } } pSoldier->ubStrategicInsertionCode = INSERTION_CODE_GRIDNO; pSoldier->usStrategicInsertionData = gsInterrogationGridNo[ gStrategicStatus.ubNumCapturedForRescue ]; gStrategicStatus.ubNumCapturedForRescue++; } //Bandaging him would prevent him from dying (due to low HP) pSoldier->bBleeding = 0; // wake him up if ( pSoldier->fMercAsleep ) { PutMercInAwakeState( pSoldier ); pSoldier->fForcedToStayAwake = FALSE; } //Set his life to 50% + or - 10 HP. pSoldier->bLife = pSoldier->bLifeMax / 2; if ( pSoldier->bLife <= 35 ) { pSoldier->bLife = 35; } else if ( pSoldier->bLife >= 45 ) { pSoldier->bLife += (INT8)(10 - Random( 21 ) ); } // make him quite exhausted when found pSoldier->bBreath = pSoldier->bBreathMax = 50; pSoldier->sBreathRed = 0; pSoldier->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 ]->bLife < OKLIFE && MercPtrs[ i ]->bLife ) { MercPtrs[ i ]->bLife = 0; 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 ]->bLife < OKLIFE && MercPtrs[ i ]->bLife ) { MercPtrs[ i ]->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 ]->bLife < OKLIFE && MercPtrs[ i ]->bLife ) { MercPtrs[ i ]->bLife = 0; 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 ]->bLife < OKLIFE && MercPtrs[ i ]->bLife ) { MercPtrs[ i ]->bLife = 0; 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 ) { SECTORINFO *pSector; pSector = &SectorInfo[ ubSectorID ]; if( pSector->ubNumberOfCivsAtLevel[ GREEN_MILITIA ] + pSector->ubNumberOfCivsAtLevel[ REGULAR_MILITIA ] + pSector->ubNumberOfCivsAtLevel[ ELITE_MILITIA ] ) { //militia in sector return TRUE; } if( FindMovementGroupInSector( (UINT8)SECTORX( ubSectorID ), (UINT8)SECTORY( ubSectorID ), TRUE ) ) { //player 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->bLife ) { if( !pSoldier->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->bLife ) { if( !pSoldier->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->bLife ) { if( !pSoldier->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->bLife ) { if( !pSoldier->bNeutral ) { return FALSE; } } } //We only have hostile civilians, don't allow time compression. return TRUE; }