#include "builddefines.h" #include "strategicmap.h" #include "strategic.h" #include "Strategic Mines.h" #include "types.h" #include "memory.h" #include #include #include #include #include "jascreens.h" #include "worlddef.h" #include "Soldier Control.h" #include "overhead.h" #include "isometric utils.h" #include "worlddef.h" #include "worlddat.h" #include "text.h" #include "Soldier add.h" #include "soldier macros.h" #include "Strategic Pathing.h" #include "soldier create.h" #include "handle UI.h" #include "faces.h" #include "renderworld.h" #include "Map Information.h" #include "Assignments.h" #include "message.h" #include "Game Events.h" #include "quests.h" #include "FileMan.h" #include "animated progressbar.h" #include "Strategic Movement.h" #include "Campaign Types.h" #include "Exit Grids.h" #include "Tactical Save.h" #include "animation control.h" #include "squads.h" #include "Overhead.h" #include "Strategic Town Loyalty.h" #include "Queen Command.h" #include "cursor control.h" #include "PreBattle Interface.h" #include "gameloop.h" #include "Random.h" #include "ai.h" #include "keys.h" #include "Tactical Placement GUI.h" #include "Map Screen Helicopter.h" #include "Player Command.h" #include "Event Pump.h" #include "air raid.h" #include "Strategic Mines.h" #include "SaveLoadMap.h" #include "Militia Control.h" #include "gamesettings.h" #include "Town Militia.h" #include "sysutil.h" #include "Debug Control.h" #include "Queen Command.h" #include "Strategic Event Handler.h" #include "MessageBoxScreen.h" #include "interface dialogue.h" #include "Map Screen Interface.h" #include "Bullets.h" #include "physics.h" #include "Auto Resolve.h" #include "cursors.h" #ifdef JA2UB #include "Soldier Init List.h" #include "Dialogue Control.h" #include "Game Clock.h" #include "opplist.h" #include "Ja25Update.h" #include "Ja25_Tactical.h" #include "Ja25 Strategic Ai.h" #include "MapScreen Quotes.h" #include "email.h" #include "SaveLoadGame.h" #include "Campaign.h" #include "Strategic Status.h" #include "ub_config.h" //** Defines ******************************************************************* #define JA25_SECTOR_H7_INITIAL_ATTACK ( 7 * 60 + 10 ) #define JA25_TIME_BETWEEN_SAI_UPDATES_EASY ( 60 * 5 ) #define JA25_TIME_BETWEEN_SAI_UPDATES_NORMAL ( 60 * 3 ) #define JA25_TIME_BETWEEN_SAI_UPDATES_HARD ( 60 * 2 ) #define JA25_SAI_RANDOM_TIME ( 60 * 2 ) #define JA25_MINIMUM_TIME_BETWEEN_ATTACKS ( 4 * 60 ) //4 hours #define JA25_SAI_DEBUG_FILE "JA25AiDebug.rtf" //** Global Variables ********************************************************* //extern UINT32 guiSaveGameVersion; INT32 giNumJA25Sectors = NUM_CAMPAIGN_JA25_SECTORS; JA25_SAVE_INFO gJa25SaveStruct; JA25_SECTOR_AI gJa25AiSectorStruct[CUSTOMSECTOR]; //*gJa25AiSectorStruct = NULL; JA25_SECTOR_AI_MANAGER gJa25StrategicAi; //BOOLEAN gfEnemyShouldImmediatelySeekThePlayer=FALSE; //** Prototypes **************************************************************** //extern UNDERGROUND_SECTORINFO* NewUndergroundNode( UINT8 ubSectorX, UINT8 ubSectorY, UINT8 ubSectorZ, BOOLEAN fCampaignSector ); BOOLEAN AddEnemiesToInitialSectorH7(); UINT32 GetNumberOfJA25EnemiesInSector( INT16 sSectorX, INT16 sSectorY, INT8 bSectorZ, UINT8 *pNumAdmins, UINT8 *pNumTroops, UINT8 *pNumElites, UINT8 *pubNumTanks, UINT8 *pubNumJeeps, UINT8 *pubNumRobots ); void SetNumberJa25EnemiesInSurfaceSector( INT32 iSectorID, UINT8 ubNumAdmins, UINT8 ubNumTroops, UINT8 ubNumElites, UINT8 ubNumTanks, UINT8 ubNumJeeps, UINT8 ubNumRobots ); void InitJa25InitialEnemiesInSector(); void InitJa25UnderGroundSectors(); void InitNumberOfEnemiesInAboveGroundSectors( ); void InitNumberOfEnemiesInUnderGroundSectors( ); void SetNumberOfJa25BloodCatsInSector( INT32 iSectorID, INT8 bNumBloodCats, INT8 bBloodCatPlacements ); void SetNumberJa25EnemiesInUnderGroundSector( INT16 sSectorX, INT16 sSectorY, INT8 bSectorZ, UINT8 ubNumAdmins, UINT8 ubNumTroops, UINT8 ubNumElites, UINT8 ubNumTanks, UINT8 ubNumJeeps, UINT8 ubNumRobots ); void ResetJa25SectorProbabilities(); BOOLEAN InitJa25StrategicSectorAI( BOOLEAN fReset ); BOOLEAN HandleAddingEnemiesToSector( INT16 sSaiSector, UINT8 ubNumEnemies, INT8 bAttackDirection, INT16 sGridNo ); BOOLEAN HandleAddEnemiesToSectorPlayerIsntIn( INT16 sSaiSector, UINT8 ubNumEnemies ); void Ja25SAI_DetermineWhichLevelToAttackFrom( INT16 sSaiSector, INT16 *psSector, INT8 *pbLevel ); BOOLEAN AreAllPlayerMercTraversingBetweenSectors(); UINT32 GetMinimumTimeBetweenAttacks(); void InitJohnKulbaInitialSector(); void FixEnemyCounterInSectorBug(); void AddEnemiesToFirstTunnelSector(); void AddEnemiesToSecondTunnelSector(); UINT8 NumEnemiesToAttackFirstTunnelSector( UINT8 *pAdmins, UINT8 *pTroops, UINT8 *pElites, UINT8 *pTanks, UINT8 *pJeeps, UINT8 *pRobots ); UINT8 NumEnemiesToAttackSecondTunnelSector( UINT8 *pAdmins, UINT8 *pTroops, UINT8 *pElites, UINT8 *pTanks, UINT8 *pJeeps, UINT8 *pRobots ); void RemoveAllEnemySoldierInitListLinks(); INT16 GetGridNoEnemyWillSeekWhenAttacking( INT8 bSaiSector ); void SetH11NumEnemiesInSector(); BOOLEAN HaveMercsEverBeenInComplex(); #ifdef JA2BETAVERSION BOOLEAN Ja25BetaDateToInvalidateExe(); #endif //ppp //void CopyJa25SectorAiData( JA25_SECTOR_AI *pDest, JA25_SECTOR_AI *pSource ); #ifdef JA2BETAVERSION BOOLEAN RecordJa25StrategicAiDecisions( INT16 sSectorAttacked, UINT8 ubNumEnemies ); BOOLEAN OutputJA25SaiString( HWFILE hFile, CHAR *pString ); #endif //** Functions **************************************************************** UINT32 GetNumberOfJA25EnemiesInSector( INT16 sSectorX, INT16 sSectorY, INT8 bSectorZ, UINT8 *pNumAdmins, UINT8 *pNumTroops, UINT8 *pNumElites, UINT8 *pNumTanks, UINT8 *pubNumJeeps, UINT8 *pubNumRobots ) { SECTORINFO *pSector; //if it is above ground if( bSectorZ == 0 ) { pSector = &SectorInfo[ SECTOR( sSectorX, sSectorY ) ]; *pNumAdmins = pSector->ubNumAdmins; *pNumTroops = pSector->ubNumTroops; *pNumElites = pSector->ubNumElites; *pNumTanks = pSector->ubNumTanks; *pubNumJeeps = pSector->ubNumJeeps; *pubNumRobots = pSector->ubNumRobots; } else { UNDERGROUND_SECTORINFO *pSector; pSector = FindUnderGroundSector( sSectorX, sSectorY, bSectorZ ); if( pSector ) { *pNumAdmins = pSector->ubNumAdmins; *pNumTroops = pSector->ubNumTroops; *pNumElites = pSector->ubNumElites; *pNumTanks = pSector->ubNumTanks; *pubNumJeeps = pSector->ubNumJeeps; *pubNumRobots = pSector->ubNumRobots; } } return( 0 ); } void InitJa25StrategicAi() { //Initialize the array of new gun quotes InitNewGunArray(); /* #ifdef ENABLE_CUSTOM_MAP_INTERFACE //if the player is using a CUSTOM MAP SCENARIO disable the S.A.I. if( gJa25SaveStruct.fInCustomMap ) { return; } #endif #ifdef JA2BETAVERSION InitJa25StrategicAiDecisions( FALSE ); #endif */ // Init and memset AI struct // giNumJA25Sectors = NUM_CAMPAIGN_JA25_SECTORS; // gJa25AiSectorStruct = MemAlloc( sizeof( JA25_SAVE_INFO ) * giNumJA25Sectors ); // memset( gJa25AiSectorStruct, 0, sizeof( JA25_SAVE_INFO ) * giNumJA25Sectors ); //add event at 7:03 if ( gGameUBOptions.EventAttackInitialSectorIfPlayerStillThere == TRUE ) { AddSameDayStrategicEvent( EVENT_ATTACK_INITIAL_SECTOR_IF_PLAYER_STILL_THERE, JA25_SECTOR_H7_INITIAL_ATTACK, 0 ); AddEveryDayStrategicEventUsingSeconds(EVENT_ATTACK_INITIAL_SECTOR_IF_PLAYER_STILL_THERE,JA25_SECTOR_H7_INITIAL_ATTACK * 60,0); } //Init the underground sectors ( add them to the list of under ground ) InitJa25UnderGroundSectors(); //Init the # of enemies in all the sectors InitJa25InitialEnemiesInSector(); //Init the Ja215 Stratigic AI InitJa25StrategicSectorAI( TRUE ); gStrategicStatus.ubHighestProgress = CurrentPlayerProgressPercentage(); //InitJohnKulbaInitialSector(); // wyłączone, skrypt lua } BOOLEAN ShouldEnemiesBeAddedToInitialSector() { //if there are still players in the first sector if( gfWorldLoaded && gWorldSectorX == JA2_5_START_SECTOR_X && gWorldSectorY == JA2_5_START_SECTOR_Y && AreAnyPlayerMercsStillInSector( JA2_5_START_SECTOR_X, JA2_5_START_SECTOR_Y, 0 ) ) { //add enemies to the initial sector if( !AddEnemiesToInitialSectorH7() ) { Assert( 0 ); return( FALSE ); } } return( TRUE ); } BOOLEAN AddEnemiesToInitialSectorH7() { GROUP *pGroup; UINT8 ubSector; UINT32 uiWorldMin; UINT8 ubNumAdmins; UINT8 ubNumTroops; UINT8 ubNumElites; UINT8 ubNumTanks; UINT8 ubNumJeeps; UINT8 ubNumRobots; UINT8 ubNumRemovedAdmins=0; UINT8 ubNumRemovedTroops=0; UINT8 ubNumRemovedElites=0; UINT8 ubNumRemovedTanks=0; UINT8 ubNumRemovedJeeps=0; UINT8 ubNumRemovedRobots=0; ubSector = SECTOR( JA2_5_START_SECTOR_X, JA2_5_START_SECTOR_Y ); //Get the number of enemies in the guard post sector GetNumberOfJA25EnemiesInSector( 8, MAP_ROW_H, 0, &ubNumAdmins, &ubNumTroops, &ubNumElites, &ubNumTanks, &ubNumJeeps, &ubNumRobots ); //determine the #of enemies to travel to the initial heli sector if( ubNumAdmins > 1 ) ubNumRemovedAdmins = ubNumAdmins / 3 + Random( 3 ); if( ubNumTroops > 1 ) ubNumRemovedTroops = ubNumTroops / 3 + Random( 3 ); if( ubNumElites > 1 ) ubNumRemovedElites = ubNumElites / 3 + + Random( 3 ); if( ubNumTanks > 1 ) ubNumRemovedTanks = ubNumTanks / 3 + + Random( 3 ); if( ubNumJeeps > 1 ) ubNumRemovedJeeps = ubNumJeeps / 3 + + Random( 3 ); if( ubNumRobots > 1 ) ubNumRemovedRobots = ubNumRobots / 3 + + Random( 3 ); //deduct the # that are moving from the # in the guard post sector // SetNumberJa25EnemiesInSurfaceSector( SEC_H8, (UINT8)(ubNumAdmins-ubNumRemovedAdmins), (UINT8)(ubNumTroops-ubNumRemovedTroops), (UINT8)(ubNumElites-ubNumRemovedElites) ); SetNumberJa25EnemiesInSector( 8, MAP_ROW_H, 0, (UINT8)(ubNumAdmins-ubNumRemovedAdmins), (UINT8)(ubNumTroops-ubNumRemovedTroops), (UINT8)(ubNumElites-ubNumRemovedElites), (UINT8)(ubNumTanks-ubNumRemovedTanks), (UINT8)(ubNumJeeps-ubNumRemovedJeeps), (UINT8)(ubNumRobots-ubNumRemovedRobots) ); uiWorldMin = GetWorldTotalMin(); pGroup = CreateNewEnemyGroupDepartingFromSector( ubSector+1, ubNumRemovedAdmins, ubNumRemovedTroops, ubNumRemovedElites, ubNumRemovedTanks, ubNumRemovedJeeps, ubNumRemovedRobots ); if( pGroup == NULL ) { Assert( 0 ); return( FALSE ); } //Set the sector to attack pGroup->ubNextX = (UINT8)JA2_5_START_SECTOR_X; pGroup->ubNextY = (UINT8)JA2_5_START_SECTOR_Y; pGroup->uiTraverseTime = 10; pGroup->pEnemyGroup->ubIntention = ASSAULT; SetGroupArrivalTime( pGroup, uiWorldMin+1 ); pGroup->ubMoveType = ONE_WAY; // AddStrategicEvent( EVENT_GROUP_ARRIVAL, pGroup->uiArrivalTime, pGroup->ubGroupID ); AddAdvancedStrategicEvent( ONETIME_EVENT, EVENT_GROUP_ARRIVAL, GetWorldTotalSeconds( ) + 1, pGroup->ubGroupID ); return( TRUE ); } BOOLEAN AreAnyPlayerMercsStillInSector( INT16 sSectorX, INT16 sSectorY, INT8 bSectorZ ) { INT32 iCnt; for( iCnt = gTacticalStatus.Team[ OUR_TEAM ].bFirstID; iCnt <= gTacticalStatus.Team[ OUR_TEAM ].bLastID; iCnt++ ) { if( MercPtrs[ iCnt ]->bActive ) { if( MercPtrs[ iCnt ]->sSectorX == sSectorX && MercPtrs[ iCnt ]->sSectorY == sSectorY && MercPtrs[ iCnt ]->bSectorZ == bSectorZ ) { return( TRUE ); } } } return( FALSE ); } void InitJa25InitialEnemiesInSector() { // // Set the number of enemies in each of the sectors // //InitNumberOfEnemiesInAboveGroundSectors( ); //wyłączono, skrypt lua //Below Ground //InitNumberOfEnemiesInUnderGroundSectors( ); //wyłączono, skrypt lua } void InitJa25UnderGroundSectors() { /* UNDERGROUND_SECTORINFO *curr; //Under the mine ( i13 curr = NewUndergroundNode( 13, 9, 1, TRUE ); Assert( curr ); //under the power generator j13 curr = NewUndergroundNode( 13, 10, 1, TRUE ); Assert( curr ); //the tunnels J14, k14 curr = NewUndergroundNode( 14, 10, 1, TRUE ); Assert( curr ); curr = NewUndergroundNode( 14, 11, 1, TRUE ); Assert( curr ); //under the complex, K15, level 1 & 2 curr = NewUndergroundNode( 15, 11, 1, TRUE ); Assert( curr ); curr = NewUndergroundNode( 15, 11, 2, TRUE ); Assert( curr ); //under the complex, 2nd part, L15, level 2 & 3 curr = NewUndergroundNode( 15, 12, 2, TRUE ); Assert( curr ); curr = NewUndergroundNode( 15, 12, 3, TRUE ); Assert( curr ); */ } void SetNumberJa25EnemiesInSector( INT16 sSectorX, INT16 sSectorY, INT8 bSectorZ, UINT8 ubNumAdmins, UINT8 ubNumTroops, UINT8 ubNumElites, UINT8 ubNumTanks, UINT8 ubNumJeeps, UINT8 ubNumRobots ) { //if its a ground level if( bSectorZ == 0 ) { SetNumberJa25EnemiesInSurfaceSector( SECTOR( sSectorX, sSectorY ), ubNumAdmins, ubNumTroops, ubNumElites, ubNumTanks, ubNumJeeps, ubNumRobots ); } //its an underground level else { SetNumberJa25EnemiesInUnderGroundSector( sSectorX, sSectorY, bSectorZ, ubNumAdmins, ubNumTroops, ubNumElites, ubNumTanks, ubNumJeeps, ubNumRobots ); } } void SetNumberJa25EnemiesInSurfaceSector( INT32 iSectorID, UINT8 ubNumAdmins, UINT8 ubNumTroops, UINT8 ubNumElites, UINT8 ubNumTanks, UINT8 ubNumJeeps, UINT8 ubNumRobots ) { SECTORINFO* pSector = &SectorInfo[iSectorID]; pSector->ubNumAdmins = ubNumAdmins; pSector->ubNumTroops = ubNumTroops; pSector->ubNumElites = ubNumElites; pSector->ubNumTanks = ubNumTanks; pSector->ubNumJeeps = ubNumJeeps; pSector->ubNumRobots = ubNumRobots; } void SetNumberOfJa25BloodCatsInSector( INT32 iSectorID, INT8 bNumBloodCats, INT8 bBloodCatPlacements ) { SECTORINFO *pSector; pSector = &SectorInfo[ iSectorID ]; pSector->bBloodCats = bNumBloodCats; pSector->bBloodCatPlacements = bBloodCatPlacements; } void SetNumberJa25EnemiesInUnderGroundSector( INT16 sSectorX, INT16 sSectorY, INT8 bSectorZ, UINT8 ubNumAdmins, UINT8 ubNumTroops, UINT8 ubNumElites, UINT8 ubNumTanks, UINT8 ubNumJeeps, UINT8 ubNumRobots ) { UNDERGROUND_SECTORINFO *pSector=NULL; pSector = FindUnderGroundSector( sSectorX, sSectorY, bSectorZ ); Assert( pSector ); //Set the values pSector->ubNumAdmins = ubNumAdmins; pSector->ubNumTroops = ubNumTroops; pSector->ubNumElites = ubNumElites; pSector->ubNumTanks = ubNumTanks; pSector->ubNumJeeps = ubNumJeeps; pSector->ubNumRobots = ubNumRobots; } void InitNumberOfEnemiesInAboveGroundSectors( ) { UINT8 ubNumAdmins=0; UINT8 ubNumTroops=0; UINT8 ubNumElites=0; UINT8 ubNumTanks=0; UINT8 ubNumJeeps=0; UINT8 ubNumRobots=0; //H7 { switch( gGameOptions.ubDifficultyLevel ) { case DIF_LEVEL_EASY: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 0 + Random( 0 ); ubNumElites = 0 + Random( 0 ); break; case DIF_LEVEL_MEDIUM: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 0 + Random( 0 ); ubNumElites = 0 + Random( 0 ); break; case DIF_LEVEL_HARD: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 0 + Random( 0 ); ubNumElites = 0 + Random( 0 ); break; case DIF_LEVEL_INSANE: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 0 + Random( 0 ); ubNumElites = 0 + Random( 0 ); break; } SetNumberJa25EnemiesInSurfaceSector( SEC_H7, ubNumAdmins, ubNumTroops, ubNumElites, ubNumTanks, ubNumJeeps, ubNumRobots ); } //SEC_H8: { switch( gGameOptions.ubDifficultyLevel ) { case DIF_LEVEL_EASY: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 6 + Random( 4 ); ubNumElites = 0 + Random( 0 ); break; case DIF_LEVEL_MEDIUM: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 13 + Random( 3 ); ubNumElites = 0 + Random( 0 ); break; case DIF_LEVEL_HARD: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 16 + Random( 6 ); ubNumElites = 2 + Random( 2 ); break; case DIF_LEVEL_INSANE: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 19 + Random( 9 ); ubNumElites = 2 + Random( 2 ); break; } SetNumberJa25EnemiesInSurfaceSector( SEC_H8, ubNumAdmins, ubNumTroops, ubNumElites, ubNumTanks, ubNumJeeps, ubNumRobots ); } //Guard Post //SEC_H9: { switch( gGameOptions.ubDifficultyLevel ) { case DIF_LEVEL_EASY: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 10 + Random( 5 ); ubNumElites = 1 + Random( 0 ); break; case DIF_LEVEL_MEDIUM: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 16 + Random( 4 ); ubNumElites = 1 + Random( 0 ); break; case DIF_LEVEL_HARD: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 19 + Random( 4 ); ubNumElites = 3 + Random( 2 ); break; case DIF_LEVEL_INSANE: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 19 + Random( 6 ); ubNumElites = 3 + Random( 2 ); break; } SetNumberJa25EnemiesInSurfaceSector( SEC_H9, ubNumAdmins, ubNumTroops, ubNumElites, ubNumTanks, ubNumJeeps, ubNumRobots ); } // SEC_H10: { switch( gGameOptions.ubDifficultyLevel ) { case DIF_LEVEL_EASY: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 6 + Random( 5 ); ubNumElites = 0 + Random( 0 ); break; case DIF_LEVEL_MEDIUM: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 9 + Random( 3 ); ubNumElites = 0 + Random( 0 ); break; case DIF_LEVEL_HARD: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 10 + Random( 4 ); ubNumElites = 2 + Random( 4 ); break; case DIF_LEVEL_INSANE: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 20 + Random( 4 ); ubNumElites = 2 + Random( 4 ); break; } SetNumberJa25EnemiesInSurfaceSector( SEC_H10, ubNumAdmins, ubNumTroops, ubNumElites, ubNumTanks, ubNumJeeps, ubNumRobots ); } //SEC_H11 SetH11NumEnemiesInSector(); // SEC_I9: { switch( gGameOptions.ubDifficultyLevel ) { case DIF_LEVEL_EASY: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 6 + Random( 5 ); ubNumElites = 0 + Random( 0 ); break; case DIF_LEVEL_MEDIUM: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 9 + Random( 4 ); ubNumElites = 0 + Random( 0 ); break; case DIF_LEVEL_HARD: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 10 + Random( 4 ); ubNumElites = 2 + Random( 4 ); break; case DIF_LEVEL_INSANE: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 20 + Random( 6 ); ubNumElites = 2 + Random( 4 ); break; } SetNumberJa25EnemiesInSurfaceSector( SEC_I9, ubNumAdmins, ubNumTroops, ubNumElites, ubNumTanks, ubNumJeeps, ubNumRobots ); } //First part of town // SEC_I10: { switch( gGameOptions.ubDifficultyLevel ) { case DIF_LEVEL_EASY: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 13 + Random( 6 ); ubNumElites = 0 + Random( 0 ); break; case DIF_LEVEL_MEDIUM: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 16 + Random( 4 ); ubNumElites = 1 + Random( 3 ); break; case DIF_LEVEL_HARD: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 14 + Random( 4 ); ubNumElites = 5 + Random( 3 ); break; case DIF_LEVEL_INSANE: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 20 + Random( 4 ); ubNumElites = 5 + Random( 3 ); break; } SetNumberJa25EnemiesInSurfaceSector( SEC_I10, ubNumAdmins, ubNumTroops, ubNumElites, ubNumTanks, ubNumJeeps, ubNumRobots ); } //Second part of town //SEC_I11: { switch( gGameOptions.ubDifficultyLevel ) { case DIF_LEVEL_EASY: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 14 + Random( 7 ); ubNumElites = 0 + Random( 0 ); break; case DIF_LEVEL_MEDIUM: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 16 + Random( 5 ); ubNumElites = 2 + Random( 2 ); break; case DIF_LEVEL_HARD: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 14 + Random( 4 ); ubNumElites = 5 + Random( 4 ); break; case DIF_LEVEL_INSANE: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 17 + Random( 4 ); ubNumElites = 5 + Random( 4 ); break; } SetNumberJa25EnemiesInSurfaceSector( SEC_I11, ubNumAdmins, ubNumTroops, ubNumElites, ubNumTanks, ubNumJeeps, ubNumRobots ); } //SEC_I12: { switch( gGameOptions.ubDifficultyLevel ) { case DIF_LEVEL_EASY: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 8 + Random( 6 ); ubNumElites = 0 + Random( 0 ); break; case DIF_LEVEL_MEDIUM: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 12 + Random( 5 ); ubNumElites = 2 + Random( 2 ); break; case DIF_LEVEL_HARD: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 12 + Random( 6 ); ubNumElites = 3 + Random( 2 ); break; case DIF_LEVEL_INSANE: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 20 + Random( 6 ); ubNumElites = 3 + Random( 2 ); break; } SetNumberJa25EnemiesInSurfaceSector( SEC_I12, ubNumAdmins, ubNumTroops, ubNumElites, ubNumTanks, ubNumJeeps, ubNumRobots ); } //Abandoned mine // SEC_I13: { switch( gGameOptions.ubDifficultyLevel ) { case DIF_LEVEL_EASY: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 0 + Random( 0 ); ubNumElites = 0 + Random( 0 ); break; case DIF_LEVEL_MEDIUM: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 0 + Random( 0 ); ubNumElites = 0 + Random( 0 ); break; case DIF_LEVEL_HARD: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 0 + Random( 0 ); ubNumElites = 0 + Random( 0 ); break; case DIF_LEVEL_INSANE: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 0 + Random( 0 ); ubNumElites = 0 + Random( 0 ); break; } SetNumberJa25EnemiesInSurfaceSector( SEC_I13, ubNumAdmins, ubNumTroops, ubNumElites, ubNumTanks, ubNumJeeps, ubNumRobots ); } // SEC_J11: { switch( gGameOptions.ubDifficultyLevel ) { case DIF_LEVEL_EASY: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 10 + Random( 6 ); ubNumElites = 0 + Random( 0 ); break; case DIF_LEVEL_MEDIUM: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 10 + Random( 3 ); ubNumElites = 3 + Random( 2 ); break; case DIF_LEVEL_HARD: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 11 + Random( 5 ); ubNumElites = 6 + Random( 3 ); break; case DIF_LEVEL_INSANE: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 20 + Random( 5 ); ubNumElites = 6 + Random( 3 ); break; } SetNumberJa25EnemiesInSurfaceSector( SEC_J11, ubNumAdmins, ubNumTroops, ubNumElites, ubNumTanks, ubNumJeeps, ubNumRobots ); } // SEC_J12: { switch( gGameOptions.ubDifficultyLevel ) { case DIF_LEVEL_EASY: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 12 + Random( 4 ); ubNumElites = 0 + Random( 0 ); break; case DIF_LEVEL_MEDIUM: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 10 + Random( 4 ); ubNumElites = 1 + Random( 2 ); break; case DIF_LEVEL_HARD: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 11 + Random( 3 ); ubNumElites = 3 + Random( 2 ); break; case DIF_LEVEL_INSANE: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 21 + Random( 3 ); ubNumElites = 3 + Random( 2 ); break; } SetNumberJa25EnemiesInSurfaceSector( SEC_J12, ubNumAdmins, ubNumTroops, ubNumElites, ubNumTanks, ubNumJeeps, ubNumRobots ); } //Power Generator, Ground Level // SEC_J13: { switch( gGameOptions.ubDifficultyLevel ) { case DIF_LEVEL_EASY: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 15 + Random( 6 ); ubNumElites = 1 + Random( 0 ); break; case DIF_LEVEL_MEDIUM: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 16 + Random( 5 ); ubNumElites = 5 + Random( 2 ); break; case DIF_LEVEL_HARD: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 18 + Random( 4 ); ubNumElites = 6 + Random( 3 ); break; case DIF_LEVEL_INSANE: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 18 + Random( 4 ); ubNumElites = 7 + Random( 3 ); break; } SetNumberJa25EnemiesInSurfaceSector( SEC_J13, ubNumAdmins, ubNumTroops, ubNumElites, ubNumTanks, ubNumJeeps, ubNumRobots ); } //Complex, Ground Level // SEC_K15: { switch( gGameOptions.ubDifficultyLevel ) { case DIF_LEVEL_EASY: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 14 + Random( 5 ); ubNumElites = 0 + Random( 0 ); break; case DIF_LEVEL_MEDIUM: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 18 + Random( 3 ); ubNumElites = 4 + Random( 2 ); break; case DIF_LEVEL_HARD: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 17 + Random( 3 ); ubNumElites = 8 + Random( 2 ); break; case DIF_LEVEL_INSANE: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 17 + Random( 3 ); ubNumElites = 8 + Random( 2 ); break; } SetNumberJa25EnemiesInSurfaceSector( SEC_K15, ubNumAdmins, ubNumTroops, ubNumElites, ubNumTanks, ubNumJeeps, ubNumRobots ); } } void InitNumberOfEnemiesInUnderGroundSectors( ) { UINT8 ubNumAdmins=0; UINT8 ubNumTroops=0; UINT8 ubNumElites=0; UINT8 ubNumTanks=0; UINT8 ubNumJeeps=0; UINT8 ubNumRobots=0; //Mine Sector Level 1 switch( gGameOptions.ubDifficultyLevel ) { case DIF_LEVEL_EASY: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 0 + Random( 0 ); ubNumElites = 0 + Random( 0 ); break; case DIF_LEVEL_MEDIUM: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 0 + Random( 0 ); ubNumElites = 0 + Random( 0 ); break; case DIF_LEVEL_HARD: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 0 + Random( 0 ); ubNumElites = 0 + Random( 0 ); break; } SetNumberJa25EnemiesInSector( 13, 9, 1, ubNumAdmins, ubNumTroops, ubNumElites, ubNumTanks, ubNumJeeps, ubNumRobots ); //Power Generator Level 1 switch( gGameOptions.ubDifficultyLevel ) { case DIF_LEVEL_EASY: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 0 + Random( 0 ); ubNumElites = 0 + Random( 0 ); break; case DIF_LEVEL_MEDIUM: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 0 + Random( 0 ); ubNumElites = 0 + Random( 0 ); break; case DIF_LEVEL_HARD: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 0 + Random( 0 ); ubNumElites = 0 + Random( 0 ); break; } SetNumberJa25EnemiesInSector( 13, 10, 1, ubNumAdmins, ubNumTroops, ubNumElites, ubNumTanks, ubNumJeeps, ubNumRobots ); //Tunnel First Part, Level 1 switch( gGameOptions.ubDifficultyLevel ) { case DIF_LEVEL_EASY: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 0 + Random( 0 ); ubNumElites = 0 + Random( 0 ); break; case DIF_LEVEL_MEDIUM: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 0 + Random( 0 ); ubNumElites = 0 + Random( 0 ); break; case DIF_LEVEL_HARD: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 0 + Random( 0 ); ubNumElites = 0 + Random( 0 ); break; } SetNumberJa25EnemiesInSector( 14, 10, 1, ubNumAdmins, ubNumTroops, ubNumElites, ubNumTanks, ubNumJeeps, ubNumRobots ); //Tunnel Second Part, Level 1 switch( gGameOptions.ubDifficultyLevel ) { case DIF_LEVEL_EASY: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 0 + Random( 0 ); ubNumElites = 0 + Random( 0 ); break; case DIF_LEVEL_MEDIUM: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 0 + Random( 0 ); ubNumElites = 0 + Random( 0 ); break; case DIF_LEVEL_HARD: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 0 + Random( 0 ); ubNumElites = 0 + Random( 0 ); break; } SetNumberJa25EnemiesInSector( 14, 11, 1, ubNumAdmins, ubNumTroops, ubNumElites, ubNumTanks, ubNumJeeps, ubNumRobots ); //Complex, Level 1, K15 switch( gGameOptions.ubDifficultyLevel ) { case DIF_LEVEL_EASY: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 14 + Random( 6 ); ubNumElites = 0 + Random( 0 ); break; case DIF_LEVEL_MEDIUM: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 14 + Random( 4 ); ubNumElites = 4 + Random( 3 ); break; case DIF_LEVEL_HARD: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 15 + Random( 4 ); ubNumElites = 6 + Random( 4 ); break; } SetNumberJa25EnemiesInSector( 15, 11, 1, ubNumAdmins, ubNumTroops, ubNumElites, ubNumTanks, ubNumJeeps, ubNumRobots ); //Complex, Level 2, K15 switch( gGameOptions.ubDifficultyLevel ) { case DIF_LEVEL_EASY: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 10 + Random( 3 ); ubNumElites = 0 + Random( 0 ); break; case DIF_LEVEL_MEDIUM: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 15 + Random( 2 ); ubNumElites = 5 + Random( 2 ); break; case DIF_LEVEL_HARD: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 12 + Random( 3 ); ubNumElites = 10 + Random( 2 ); break; } SetNumberJa25EnemiesInSector( 15, 11, 2, ubNumAdmins, ubNumTroops, ubNumElites, ubNumTanks, ubNumJeeps, ubNumRobots ); //Complex, Level 2, L15 switch( gGameOptions.ubDifficultyLevel ) { case DIF_LEVEL_EASY: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 12 + Random( 2 ); ubNumElites = 1 + Random( 2 ); break; case DIF_LEVEL_MEDIUM: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 17 + Random( 2 ); ubNumElites = 5 + Random( 2 ); break; case DIF_LEVEL_HARD: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 10 + Random( 3 ); ubNumElites = 14 + Random( 2 ); break; } SetNumberJa25EnemiesInSector( 15, 12, 2, ubNumAdmins, ubNumTroops, ubNumElites, ubNumTanks, ubNumJeeps, ubNumRobots ); //Complex, Level 3, L15 switch( gGameOptions.ubDifficultyLevel ) { case DIF_LEVEL_EASY: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 26 + Random( 0 ); ubNumElites = 2 + Random( 0 ); break; case DIF_LEVEL_MEDIUM: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 20 + Random( 0 ); ubNumElites = 8 + Random( 0 ); break; case DIF_LEVEL_HARD: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 13 + Random( 0 ); ubNumElites = 15 + Random( 0 ); break; } SetNumberJa25EnemiesInSector( 15, 12, 3, ubNumAdmins, ubNumTroops, ubNumElites, ubNumTanks, ubNumJeeps, ubNumRobots ); } void InitJa25SaveStruct() { memset( &gJa25SaveStruct, 0, sizeof( JA25_SAVE_INFO ) ); gJa25SaveStruct.iPowerGenFanPositionSndID = -1; } BOOLEAN SaveJa25SaveInfoToSaveGame( HWFILE hFile ) { UINT32 uiNumBytesWritten; UINT32 uiSize = sizeof( JA25_SAVE_INFO ); // CHAR8 zString[512]; //Make sure struct is 1000 bytes in size // sprintf( zString, "JA2 SaveInfo Struct != 1000, it is: %d", uiSize ); // AssertMsg( uiSize == 1000, zString ); //Make sure struct is 32 bytes in size // sprintf( zString, "AI manager != 32, it is: %d", sizeof( JA25_SECTOR_AI_MANAGER ) ); // AssertMsg( sizeof( JA25_SECTOR_AI_MANAGER ) == 32, zString ); //Make sure struct is 840 bytes in size // sprintf( zString, "AI sector Struct != 40, it is: %d", sizeof( JA25_SECTOR_AI ) ); // AssertMsg( sizeof( JA25_SECTOR_AI ) == 40, zString ); FileWrite( hFile, &gJa25SaveStruct, sizeof( JA25_SAVE_INFO ), &uiNumBytesWritten ); if( uiNumBytesWritten != sizeof( JA25_SAVE_INFO ) ) { return( FALSE ); } // // Ai Manager // FileWrite( hFile, &gJa25StrategicAi, sizeof( JA25_SECTOR_AI_MANAGER ), &uiNumBytesWritten ); if( uiNumBytesWritten != sizeof( JA25_SECTOR_AI_MANAGER ) ) { return( FALSE ); } // // Sector Info // FileWrite( hFile, &gJa25AiSectorStruct, sizeof( JA25_SECTOR_AI ) * CUSTOMSECTOR , &uiNumBytesWritten ); //NUM_CAMPAIGN_JA25_SECTORS if( uiNumBytesWritten != sizeof( JA25_SECTOR_AI ) * CUSTOMSECTOR ) //NUM_CAMPAIGN_JA25_SECTORS { return( FALSE ); } // OK, now write out additional sectors /* { INT32 iNumCustomSectors = giNumJA25Sectors - NUM_CAMPAIGN_JA25_SECTORS; // Write out # of additional sectors FileWrite( hFile, &iNumCustomSectors, sizeof( iNumCustomSectors ), &uiNumBytesWritten ); if( uiNumBytesWritten != sizeof( iNumCustomSectors ) ) { return( FALSE ); } */ /* if ( iNumCustomSectors > 0 ) { FileWrite( hFile, &(gJa25AiSectorStruct[ NUM_CAMPAIGN_JA25_SECTORS ] ), sizeof( JA25_SECTOR_AI ) * iNumCustomSectors, &uiNumBytesWritten ); if( uiNumBytesWritten != sizeof( JA25_SECTOR_AI ) * iNumCustomSectors ) { return( FALSE ); } } */ // } return( TRUE ); } BOOLEAN LoadJa25SaveInfoFromSavedGame( HWFILE hFile ) { UINT32 uiNumBytesRead; // Free old data // if ( gJa25AiSectorStruct ) // { // MemFree( gJa25AiSectorStruct ); // gJa25AiSectorStruct = NULL; // } FileRead( hFile, &gJa25SaveStruct, sizeof( JA25_SAVE_INFO ), &uiNumBytesRead ); if( uiNumBytesRead != sizeof( JA25_SAVE_INFO ) ) { return( FALSE ); } // // Ai Manager // FileRead( hFile, &gJa25StrategicAi, sizeof( JA25_SECTOR_AI_MANAGER ), &uiNumBytesRead ); if( uiNumBytesRead != sizeof( JA25_SECTOR_AI_MANAGER ) ) { return( FALSE ); } // // Sector Info // // Init array for campaign sectors // giNumJA25Sectors = NUM_CAMPAIGN_JA25_SECTORS; /// gJa25AiSectorStruct = MemAlloc( giNumJA25Sectors * sizeof( JA25_SECTOR_AI ) ); FileRead( hFile, &gJa25AiSectorStruct, sizeof( JA25_SECTOR_AI ) * CUSTOMSECTOR , &uiNumBytesRead ); //NUM_CAMPAIGN_JA25_SECTORS if( uiNumBytesRead != sizeof( JA25_SECTOR_AI ) * CUSTOMSECTOR ) //NUM_CAMPAIGN_JA25_SECTORS { return( FALSE ); } // FileRead( hFile, gJa25AiSectorStruct, sizeof( JA25_SECTOR_AI ) * NUM_CAMPAIGN_JA25_SECTORS, &uiNumBytesRead ); // if( uiNumBytesRead != sizeof( JA25_SECTOR_AI ) * NUM_CAMPAIGN_JA25_SECTORS ) // { // return( FALSE ); // } /* if( guiSaveGameVersion < 1008 ) { InitJa25StrategicSectorAI( FALSE ); gJa25StrategicAi.uiTimeOfLastBattleInMinutes = GetWorldTotalMin( ); gJa25StrategicAi.uiTimeOfLastUpdate = gJa25StrategicAi.uiTimeOfLastBattleInMinutes; SectorInfo[ SEC_I13 ].ubNumTroops = 0; FixEnemyCounterInSectorBug(); } */ /* if( guiSaveGameVersion < 1006 ) { InitJohnKulbaInitialSector(); } if( guiSaveGameVersion < 1010 ) { gJa25SaveStruct.bNewMercProfileIDForSayingMorrisNote = -1; } if( guiSaveGameVersion < 1011 ) { //Add the enemies to the new sector SetH11NumEnemiesInSector(); } */ //for some reason the J13 level wasnt set up right // if( guiSaveGameVersion < 1012 ) // { // // J13: Power Generator, First Level // // gJa25AiSectorStruct[ JA25_J13_1 ].iSectorID = SEC_J13; // gJa25AiSectorStruct[ JA25_J13_1 ].bSectorZ = 1; // // I13: First Level of Mine // // gJa25AiSectorStruct[ JA25_I13_1 ].iSectorID = SEC_I13; // gJa25AiSectorStruct[ JA25_I13_1 ].bSectorZ = 1; // } // ATE: At end, we added additional AI info.... // if ( guiSaveGameVersion > 1012 ) // { // Read in ja25 AI information that is > # of custom maps.... // UINT32 iNumCustomSectors; // FileRead( hFile, &iNumCustomSectors, sizeof( UINT32 ), &uiNumBytesRead ); // if( uiNumBytesRead != sizeof( UINT32 ) ) // { // return( FALSE ); // } // giNumJA25Sectors = giNumJA25Sectors + iNumCustomSectors; // if ( giNumJA25Sectors > 0 ) // { // if # > 0, realloc and read in // gJa25AiSectorStruct = realloc( gJa25AiSectorStruct, giNumJA25Sectors * sizeof( JA25_SECTOR_AI ) ); // FileRead( hFile, &(gJa25AiSectorStruct[ NUM_CAMPAIGN_JA25_SECTORS ] ), sizeof( JA25_SECTOR_AI ) * iNumCustomSectors, &uiNumBytesRead ); // if( uiNumBytesRead != sizeof( JA25_SECTOR_AI ) * iNumCustomSectors ) // { // return( FALSE ); // } // } // } gJa25SaveStruct.iPowerGenFanPositionSndID = -1; return( TRUE ); } void InitJa25StrategicAiBloodcats( ) { INT8 bNumBloodCats=0; switch( gGameOptions.ubDifficultyLevel ) { case DIF_LEVEL_EASY: bNumBloodCats = 3; break; case DIF_LEVEL_MEDIUM: bNumBloodCats = 4; break; case DIF_LEVEL_HARD: bNumBloodCats = 5; break; } SetNumberOfJa25BloodCatsInSector( SEC_I10, bNumBloodCats, 5 ); } void HandleAddingEnemiesToTunnelMaps() { BOOLEAN fInCombat = ( gTacticalStatus.uiFlags & INCOMBAT ); UINT8 ubNumEnemies=0; //if the player has been in the complex if( HaveMercsEverBeenInComplex() ) { //dont add any enemies to the tunnel maps return; } //if the player IS NOT in either of the tunnel sectors if( !AreAnyPlayerMercsStillInSector( 14, 10, 1 ) && !AreAnyPlayerMercsStillInSector( 14, 11, 1 ) ) { AddEnemiesToJa25TunnelMaps(); } //else if the player IS NOT in the 2nd sector but in the first sector else if( AreAnyPlayerMercsStillInSector( 14, 10, 1 ) && !AreAnyPlayerMercsStillInSector( 14, 11, 1 ) ) { ubNumEnemies = NumEnemiesToAttackFirstTunnelSector( NULL, NULL, NULL, NULL, NULL, NULL ); //Add enemies to the first sector HandleAddEnemiesToSectorPlayerIsntIn( JA25_J14_1, ubNumEnemies ); //Remember to move the enemies to be in the entrance to the tunnel SetJa25GeneralFlag( JA_GF__MOVE_ENEMIES_TO_EDGE_IN_TUNNEL_1 ); AddEnemiesToSecondTunnelSector(); } //else if the player is in the second sector else if( AreAnyPlayerMercsStillInSector( 14, 11, 1 ) ) { ubNumEnemies = NumEnemiesToAttackSecondTunnelSector( NULL, NULL, NULL, NULL, NULL, NULL ); //Add enemies to the first sector HandleAddEnemiesToSectorPlayerIsntIn( JA25_K14_1, ubNumEnemies ); //Remember to move the enemies to be in the entrance to the tunnel SetJa25GeneralFlag( JA_GF__MOVE_ENEMIES_TO_EDGE_IN_TUNNEL_2 ); } } void AddEnemiesToJa25TunnelMaps() { if( gJa25SaveStruct.ubHowPlayerGotThroughFan == PG__PLAYER_BLEW_UP_FAN_TO_GET_THROUGH ) { //if the player blew up the fan, add a ton of enemies to the sector AddEnemiesToFirstTunnelSector(); AddEnemiesToSecondTunnelSector(); } else if( gJa25SaveStruct.ubHowPlayerGotThroughFan == PG__PLAYER_STOPPED_FAN_TO_GET_THROUGH ) { } } void AddEnemiesToFirstTunnelSector() { UINT8 ubNumAdmins=0; UINT8 ubNumTroops=0; UINT8 ubNumElites=0; UINT8 ubNumTanks=0; UINT8 ubNumJeeps=0; UINT8 ubNumRobots=0; NumEnemiesToAttackFirstTunnelSector( &ubNumAdmins, &ubNumTroops, &ubNumElites, &ubNumTanks, &ubNumJeeps, &ubNumRobots ); SetNumberJa25EnemiesInSector( 14, 10, 1, ubNumAdmins, ubNumTroops, ubNumElites, ubNumTanks, ubNumJeeps, ubNumRobots ); } void AddEnemiesToSecondTunnelSector() { UINT8 ubNumAdmins=0; UINT8 ubNumTroops=0; UINT8 ubNumElites=0; UINT8 ubNumTanks=0; UINT8 ubNumJeeps=0; UINT8 ubNumRobots=0; NumEnemiesToAttackSecondTunnelSector( &ubNumAdmins, &ubNumTroops, &ubNumElites, &ubNumTanks, &ubNumJeeps, &ubNumRobots ); SetNumberJa25EnemiesInSector( 14, 11, 1, ubNumAdmins, ubNumTroops, ubNumElites, ubNumTanks, ubNumJeeps, ubNumRobots ); } UINT8 NumEnemiesToAttackFirstTunnelSector( UINT8 *pAdmins, UINT8 *pTroops, UINT8 *pElites, UINT8 *pTanks, UINT8 *pJeeps, UINT8 *pRobots ) { UINT8 ubNumAdmins=0; UINT8 ubNumTroops=0; UINT8 ubNumElites=0; UINT8 ubNumTanks=0; UINT8 ubNumJeeps=0; UINT8 ubNumRobots=0; //if the player blew up the fan, add a ton of enemies to the sector //1st Tunnel Sector switch( gGameOptions.ubDifficultyLevel ) { case DIF_LEVEL_EASY: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 6 + Random( 0 ); ubNumElites = 0 + Random( 0 ); break; case DIF_LEVEL_MEDIUM: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 6 + Random( 0 ); ubNumElites = 1 + Random( 0 ); break; case DIF_LEVEL_HARD: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 4 + Random( 0 ); ubNumElites = 4 + Random( 0 ); break; } if( pAdmins ) *pAdmins = ubNumAdmins; if( pTroops ) *pTroops = ubNumTroops; if( pElites ) *pElites = ubNumElites; if( pTanks ) *pTanks = ubNumTanks; if( pJeeps ) *pJeeps = ubNumJeeps; if( pRobots ) *pRobots = ubNumRobots; return( ubNumAdmins + ubNumTroops + ubNumElites + ubNumTanks + ubNumJeeps + ubNumRobots ); } UINT8 NumEnemiesToAttackSecondTunnelSector( UINT8 *pAdmins, UINT8 *pTroops, UINT8 *pElites, UINT8 *pTanks, UINT8 *pJeeps, UINT8 *pRobots ) { UINT8 ubNumAdmins=0; UINT8 ubNumTroops=0; UINT8 ubNumElites=0; UINT8 ubNumTanks=0; UINT8 ubNumJeeps=0; UINT8 ubNumRobots=0; //if the player blew up the fan, add a ton of enemies to the sector //1st Tunnel Sector //2nd Tunnel Sector switch( gGameOptions.ubDifficultyLevel ) { case DIF_LEVEL_EASY: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 7 + Random( 2 ); ubNumElites = 0 + Random( 0 ); break; case DIF_LEVEL_MEDIUM: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 6 + Random( 2 ); ubNumElites = 2 + Random( 2 ); break; case DIF_LEVEL_HARD: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 6 + Random( 0 ); ubNumElites = 4 + Random( 3 ); break; } if( pAdmins ) *pAdmins = ubNumAdmins; if( pTroops ) *pTroops = ubNumTroops; if( pElites ) *pElites = ubNumElites; if( pTanks ) *pTanks = ubNumTanks; if( pJeeps ) *pJeeps = ubNumJeeps; if( pRobots ) *pRobots = ubNumRobots; return( ubNumAdmins + ubNumTroops + ubNumElites + ubNumTanks + ubNumJeeps + ubNumRobots ); } void SetJa25GeneralFlag( UINT32 uiFlagToSet ) { gJa25SaveStruct.uiJa25GeneralFlags |= uiFlagToSet; } void ClearJa25GeneralFlag( UINT32 uiFlagToClear ) { gJa25SaveStruct.uiJa25GeneralFlags &= ~uiFlagToClear; } BOOLEAN IsJa25GeneralFlagSet( UINT32 uiFlagToCheck ) { return( ( gJa25SaveStruct.uiJa25GeneralFlags & uiFlagToCheck ) != 0 ); } BOOLEAN InitJa25StrategicSectorAI( BOOLEAN fReset ) { if( fReset ) { //Clear out the structures first memset( &gJa25StrategicAi, 0, sizeof( JA25_SECTOR_AI_MANAGER ) ); /* MemFree( gJa25AiSectorStruct ); giNumJA25Sectors = NUM_CAMPAIGN_JA25_SECTORS; gJa25AiSectorStruct = MemAlloc( sizeof( JA25_SAVE_INFO ) * giNumJA25Sectors ); memset( gJa25AiSectorStruct, 0, sizeof( JA25_SAVE_INFO ) * giNumJA25Sectors ); */ } //Init the sector values if( !InitJa25SectorAi() ) { return( FALSE ); } gJa25StrategicAi.uiTimeOfLastBattleInMinutes = /*STARTING_TIME*/ gGameExternalOptions.iGameStartingTime / 60 + gGameExternalOptions.iFirstArrivalDelay /*FIRST_ARRIVAL_DELAY*/ / 60; gJa25StrategicAi.uiTimeOfLastUpdate = gJa25StrategicAi.uiTimeOfLastBattleInMinutes; //The player owns the starting sector //gJa25AiSectorStruct[ (UINT8)SECTOR( gGameExternalOptions.ubDefaultArrivalSectorX, gGameExternalOptions.ubDefaultArrivalSectorY ) ].fPlayerControlled = TRUE; SectorInfo[ (UINT8)SECTOR( gGameExternalOptions.ubDefaultArrivalSectorX, gGameExternalOptions.ubDefaultArrivalSectorY ) ].fSurfaceWasEverPlayerControlled = TRUE; return( TRUE ); } BOOLEAN InitJa25SectorAi() { UINT32 ubCnt; // // H7: Initial sector // if ( gGameUBOptions.pJA2UB == TRUE ) { for( ubCnt=0; ubCnt uiCurrentTime ) { //get out return( FALSE ); } return( TRUE ); } BOOLEAN Ja25_AreEnemiesAbleToAttackSector( INT16 iSectorID, INT8 bSectorZ ) { //Can the strategic ai attack if( !CanJa25SaiAttack() ) { //get out return( FALSE ); } // // All Checks Failed, therefore, enemies can attack // return( TRUE ); } void Ja25_UpdateTimeOfEndOfLastBattle( INT16 sSectorX, INT16 sSectorY, INT8 bSectorZ ) { // // Reset timers // gsGridNoForMapEdgePointInfo = -1; gJa25StrategicAi.uiTimeOfLastBattleInMinutes = GetWorldTotalMin( ); gJa25StrategicAi.uiTimeOfLastUpdate = GetWorldTotalMin( ); ResetJa25SectorProbabilities(); gJa25SaveStruct.fEnemyShouldImmediatelySeekThePlayer = FALSE; gJa25SaveStruct.bSectorTheEnemyWillSeekEnemy = -1; } void ResetJa25SectorProbabilities() { /* INT8 bCnt; //loop through the array for( bCnt=0; bCnt< giNumJA25Sectors; bCnt++ ) { //if the SAI did the attack ( the varible only gets set when SAI is attacking if( gJa25SaveStruct.fEnemyShouldImmediatelySeekThePlayer ) { gJa25AiSectorStruct[ bCnt ].bCurrentProbability = gJa25AiSectorStruct[ bCnt ].bCurrentProbability / 2; } else { gJa25AiSectorStruct[ bCnt ].bCurrentProbability = 0; } } */ } //returns the element into the array of which sector the enemy will attack INT8 GetTheFurthestSectorPlayerOwns() { INT8 bCnt; BOOLEAN fFoundLatest=FALSE; INT8 bSector=-1; if ( gGameUBOptions.pJA2UB == TRUE ) { //Loop through from the end of the list for( bCnt=giNumJA25Sectors-1; bCnt>=0; bCnt-- ) { if( gJa25AiSectorStruct[ bCnt ].fPlayerControlled ) { fFoundLatest = TRUE; break; } } if( fFoundLatest ) { //do sector specific logic to determine the sector //switch( bCnt ) //{ //default: bSector = bCnt; //break; //} } } //There is no sector return( bSector ); } void SetJa25SectorOwnedStatus( INT16 sSectorX, INT16 sSectorY, INT8 bSectorZ, BOOLEAN fPlayerOwned ) { INT8 bSector; if ( gGameUBOptions.pJA2UB == TRUE ) { //get the sector id bSector = (INT8)GetJA25SectorID( sSectorX, sSectorY, bSectorZ ); //if it failed to find this array if( bSector == -1 ) { //if this is the new sector if( sSectorX == 11 && sSectorY == 8 && bSectorZ == 0 ) { //If its the end of combat mode, update sai timers Ja25_UpdateTimeOfEndOfLastBattle( sSectorX, sSectorY, bSectorZ ); } else { //If its the end of combat mode, update sai timers Ja25_UpdateTimeOfEndOfLastBattle( sSectorX, sSectorY, bSectorZ ); return; } return; } if( NumEnemiesInAnySector( sSectorX, sSectorY, bSectorZ ) != 0 ) { return; } gJa25AiSectorStruct[ bSector ].fPlayerControlled = fPlayerOwned; //if its the players that is doing the taking over if( fPlayerOwned ) { //Remeber that the player has Liberated this sector before gJa25AiSectorStruct[ bSector ].fPlayerHasLiberatedSectorBefore = TRUE; } //If its the end of combat mode, update sai timers Ja25_UpdateTimeOfEndOfLastBattle( sSectorX, sSectorY, bSectorZ ); //if this is sector H8 if( sSectorX == 8 && sSectorY == MAP_ROW_H && bSectorZ == 0 ) { AddStrategicEvent( EVENT_SECTOR_H8_DONT_WAIT_IN_SECTOR, GetWorldTotalMin() + ( 5 * 60 ), 0 ); } } } INT16 GetJA25SectorID( INT16 sSectorX, INT16 sSectorY, INT8 bSectorZ ) { INT8 bCnt=0; INT16 sSector = 0; if ( gGameUBOptions.pJA2UB == TRUE ) { if ( gJa25AiSectorStruct == NULL ) { return( -1 ); } if( sSectorX == -1 || sSectorY == -1 ) { return( -1 ); } sSector = SECTOR( sSectorX, sSectorY ); //loop through the array for( bCnt=0; bCnt< giNumJA25Sectors; bCnt++ ) { //if this is the right sector if( gJa25AiSectorStruct[ bCnt ].iSectorID == sSector && gJa25AiSectorStruct[ bCnt ].bSectorZ == bSectorZ ) { //return the array element return( bCnt ); } } } //failed to find the array return( -1 ); } BOOLEAN GetSectorJa25SaiWillAttack( INT8 *pbSectorToAttack, INT8 *pbFromDirection, INT16 *psGridNo ) { INT8 bSectorToAttackID=-1; INT8 bFurthestPlayerSector=-1; INT8 bAttackDirection=-1; INT16 sGridNo=-1; *pbSectorToAttack = -1; *pbFromDirection = -1; *psGridNo = -1; return( TRUE ); } void JA25_HandleUpdateOfStrategicAi() { UINT32 uiCurrentTime=0; INT8 bSectorToAttackID=-1; INT8 bAttackDirection=-1; INT8 bRandom=0; INT16 sGridNo=-1; if ( gGameUBOptions.pJA2UB == TRUE ) { if ( !gJa25AiSectorStruct ) { return; } uiCurrentTime = GetWorldTotalMin( ); //if the ai cannot currently attack if( !CanJa25SaiAttack() ) { return; } //if it isnt time to update if( ( uiCurrentTime - gJa25StrategicAi.uiTimeOfLastUpdate ) < GetMinimumTimeBetweenAttacks() ) { return; } //Remeber that we are updating gJa25StrategicAi.uiTimeOfLastUpdate = uiCurrentTime; // gJa25StrategicAi.uiTimeOfLastBattleInMinutes //Get the sector the enemies will attack from if( !GetSectorJa25SaiWillAttack( &bSectorToAttackID, &bAttackDirection, &sGridNo ) ) { return; } //Increment the probabilty of attacking gJa25AiSectorStruct[ bSectorToAttackID ].bCurrentProbability += gJa25AiSectorStruct[ bSectorToAttackID ].bProbabilityOfAttacking; //if we are above the maximum amount if( gJa25AiSectorStruct[ bSectorToAttackID ].bCurrentProbability > gJa25AiSectorStruct[ bSectorToAttackID ].bMaxProbabilityForAttackingSector ) { gJa25AiSectorStruct[ bSectorToAttackID ].bCurrentProbability = gJa25AiSectorStruct[ bSectorToAttackID ].bMaxProbabilityForAttackingSector; } //Debug Check to see if the date is valid #ifdef JA2BETAVERSION // if( !Ja25BetaDateToInvalidateExe() ) // { // Assert( 0 ); // } #endif //Check wether the minimum prbability is above the current probabilty if( gJa25AiSectorStruct[ bSectorToAttackID ].ubMinimumProbabiltyBeforeAttack >= gJa25AiSectorStruct[ bSectorToAttackID ].bCurrentProbability ) { //if so leave return; } //Get random # bRandom = Random( 120 ); //if the enemies should attack if( bRandom < gJa25AiSectorStruct[ bSectorToAttackID ].bCurrentProbability ) { INT16 sSectorToAttack; INT16 sSector; UINT8 ubNumEnemies=0; UINT8 ubSectorX = SECTORX( gJa25AiSectorStruct[ bSectorToAttackID ].iSectorID ); UINT8 ubSectorY = SECTORY( gJa25AiSectorStruct[ bSectorToAttackID ].iSectorID ); INT8 bSectorZ = gJa25AiSectorStruct[ bSectorToAttackID ].bSectorZ; UINT32 uiBaseNumEnemies=0; UINT32 uiRandomNumEnemies=0; // // The AI has decided to attack // // return if we have send guys already if ( gJa25AiSectorStruct[ bSectorToAttackID ].fEnemyEnrouteToAttack ) { return; } //reset the current probability gJa25AiSectorStruct[ bSectorToAttackID ].bCurrentProbability = 0; //if the AI is attacking the same sector if( gJa25StrategicAi.iLastBattleSectorID == bSectorToAttackID ) { gJa25StrategicAi.uiAttackedSameSectorCounter += 1; } else { //Other wise, reset the counter gJa25StrategicAi.uiAttackedSameSectorCounter = 0; } gJa25StrategicAi.iLastBattleSectorID = bSectorToAttackID; //if all the player mercs are between sector if( AreAllPlayerMercTraversingBetweenSectors() ) { //then the ai shouldnt attack return; } sSectorToAttack = gJa25AiSectorStruct[ bSectorToAttackID ].iSectorID; sSector = sSectorToAttack; uiBaseNumEnemies = gJa25AiSectorStruct[ bSectorToAttackID ].bBaseNumEnemies + gJa25StrategicAi.uiAttackedSameSectorCounter; uiRandomNumEnemies = gJa25AiSectorStruct[ bSectorToAttackID ].bRandomNumEnemies + ( gJa25StrategicAi.uiAttackedSameSectorCounter * 2 ); ubNumEnemies = uiBaseNumEnemies + Random( uiRandomNumEnemies ); //Guarentee no more then 20 enemies in a sector if( ubNumEnemies > gGameExternalOptions.iMaxEnemyGroupSize ) //MAX_STRATEGIC_TEAM_SIZE ) { ubNumEnemies = gGameExternalOptions.iMaxEnemyGroupSize; //MAX_STRATEGIC_TEAM_SIZE; } //if there are enemies to attack if( ubNumEnemies ) { //if the player is NOT in this sector if( PlayerMercsInSector( ubSectorX, ubSectorY, bSectorZ ) ) { //Setup an attack HandleAddingEnemiesToSector( bSectorToAttackID, ubNumEnemies, bAttackDirection, sGridNo ); } else { HandleAddEnemiesToSectorPlayerIsntIn( bSectorToAttackID, ubNumEnemies ); } #ifdef JA2BETAVERSION RecordJa25StrategicAiDecisions( bSectorToAttackID, ubNumEnemies ); #endif } } } } BOOLEAN HandleAddEnemiesToSectorPlayerIsntIn( INT16 sSaiSector, UINT8 ubNumEnemies ) { if ( gGameUBOptions.pJA2UB == TRUE ) { INT16 sSectorX = SECTORX( gJa25AiSectorStruct[sSaiSector].iSectorID ); INT16 sSectorY = SECTORY( gJa25AiSectorStruct[sSaiSector].iSectorID ); INT8 bSectorZ = gJa25AiSectorStruct[sSaiSector].bSectorZ; if( !( gTacticalStatus.uiFlags & INCOMBAT ) ) { SetEnemyEncounterCode( ENEMY_INVASION_CODE ); } SetNumberJa25EnemiesInSector( sSectorX, sSectorY, bSectorZ, 0, ubNumEnemies, 0, 0, 0, 0 ); SetThisSectorAsEnemyControlled( sSectorX, sSectorY, bSectorZ, FALSE ); //Set up flag so enemies will go and find the player in that sector gJa25SaveStruct.fEnemyShouldImmediatelySeekThePlayer = TRUE; gJa25SaveStruct.bSectorTheEnemyWillSeekEnemy = (INT8)sSaiSector; } return( TRUE ); } BOOLEAN HandleAddingEnemiesToSector( INT16 sSaiSector, UINT8 ubNumEnemies, INT8 bAttackDirection, INT16 sGridNo ) { GROUP *pGroup=NULL; INT16 sSectorToAttack; INT16 sSector; UINT32 uiWorldMin=0; INT8 bLevel=0; if ( gGameUBOptions.pJA2UB == TRUE ) { sSectorToAttack = gJa25AiSectorStruct[ sSaiSector ].iSectorID; sSector = sSectorToAttack; switch( bAttackDirection ) { case INSERTION_CODE_NORTH: sSector -= 16; break; case INSERTION_CODE_SOUTH: sSector += 16; break; case INSERTION_CODE_WEST: sSector -= 1; break; case INSERTION_CODE_EAST: sSector += 1; break; } uiWorldMin = GetWorldTotalMin(); if( gJa25AiSectorStruct[ sSaiSector ].bSectorZ == 0 ) { pGroup = CreateNewEnemyGroupDepartingFromSector( sSector, 0, ubNumEnemies, 0, 0, 0, 0 ); if( sGridNo != -1 ) { gsGridNoForMapEdgePointInfo = sGridNo; bAttackDirection = INSERTION_CODE_GRIDNO; gfCantRetreatInPBI = TRUE; } } else { //Set the gridno the enemies will land at if( sGridNo == -1 ) { Assert( 0 ); return( FALSE ); } //Remeber the gridno the enemies should arrive at gsGridNoForMapEdgePointInfo = sGridNo; //the enemies should arrive at a spcific gridno bAttackDirection = INSERTION_CODE_GRIDNO; //Determine which level the enemies should be created at Ja25SAI_DetermineWhichLevelToAttackFrom( sSaiSector, &sSector, &bLevel ); //underground, we have to specify pGroup = CreateNewEnemyGroupDepartingFromSectorUsingZLevel( sSector, bLevel, 0, ubNumEnemies, 0, 0 );//was sSector // pGroup->pEnemyGroup->ubTroopsInBattle = ubNumEnemies; } // pGroup->ubSectorX = (UINT8)SECTORX(sSectorToAttack); // pGroup->ubSectorY = (UINT8)SECTORY(sSectorToAttack); // pGroup->ubSectorZ = gJa25AiSectorStruct[ sSaiSector ].bSectorZ; pGroup->ubNextX = (UINT8)SECTORX(sSectorToAttack); pGroup->ubNextY = (UINT8)SECTORY(sSectorToAttack); pGroup->uiTraverseTime = 1; pGroup->pEnemyGroup->ubIntention = ASSAULT; SetGroupArrivalTime( pGroup, uiWorldMin + 1 ); pGroup->ubMoveType = ONE_WAY; SetEnemyEncounterCode( ENEMY_INVASION_CODE ); if( gJa25AiSectorStruct[ sSaiSector ].bSectorZ == 0 ) { AddStrategicEvent( EVENT_GROUP_ARRIVAL, pGroup->uiArrivalTime, pGroup->ubGroupID ); // Set vairable telling us to not send any more! gJa25AiSectorStruct[ sSaiSector ].fEnemyEnrouteToAttack = TRUE; } else { // AddEnemiesToBattle( pGroup, bAttackDirection, 0, ubNumEnemies, 0, TRUE ); pGroup->ubSectorX = (UINT8)SECTORX(sSectorToAttack); pGroup->ubSectorY = (UINT8)SECTORY(sSectorToAttack); pGroup->ubSectorZ = gJa25AiSectorStruct[ sSaiSector ].bSectorZ; pGroup->ubPrevX = pGroup->ubSectorX; pGroup->ubPrevY = pGroup->ubSectorY; pGroup->ubSectorX = pGroup->ubNextX; pGroup->ubSectorY = pGroup->ubNextY; pGroup->ubNextX = 0; pGroup->ubNextY = 0; InitPreBattleInterface( pGroup, TRUE ); } //Set up flag so enemies will go and find the player in that sector gJa25SaveStruct.fEnemyShouldImmediatelySeekThePlayer = TRUE; gJa25SaveStruct.bSectorTheEnemyWillSeekEnemy = (INT8)sSaiSector; } return( TRUE ); } void SetEnemiesToFindThePlayerMercs() { INT32 cnt; SOLDIERTYPE *pSoldier; INT16 sGridNoToGoto=0; if ( gGameUBOptions.pJA2UB == TRUE ) { sGridNoToGoto = GetGridNoEnemyWillSeekWhenAttacking( gJa25SaveStruct.bSectorTheEnemyWillSeekEnemy ); //Reset fact that we send enemy to find player gJa25SaveStruct.fEnemyShouldImmediatelySeekThePlayer = FALSE; gJa25SaveStruct.bSectorTheEnemyWillSeekEnemy = -1; //if its not a valid gridno if( sGridNoToGoto == 0 ) { Assert( 0 ); return; } cnt = gTacticalStatus.Team[ ENEMY_TEAM ].bFirstID; for ( pSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ ENEMY_TEAM ].bLastID; cnt++, pSoldier++) { if( pSoldier->stats.bLife >= OKLIFE && pSoldier->bActive && pSoldier->bInSector ) { // send soldier to centre of map, roughly pSoldier->aiData.sNoiseGridno = sGridNoToGoto; pSoldier->bNoiseLevel = 0; pSoldier->aiData.ubNoiseVolume = MAX_MISC_NOISE_DURATION / 2; pSoldier->aiData.bAlertStatus = STATUS_YELLOW; pSoldier->flags.bHasKeys = TRUE; pSoldier->aiData.bOrders = SEEKENEMY; } } } } INT16 GetGridNoEnemyWillSeekWhenAttacking( INT8 bSaiSector ) { INT16 sGridNo=0; if ( gGameUBOptions.pJA2UB == TRUE ) { switch( bSaiSector ) { case JA25_J14_1: sGridNo = 19749; break; case JA25_K14_1: sGridNo = 5978; break; default: //if for some reason the map doesnt have a center gridno if( gMapInformation.sCenterGridNo != -1 && gMapInformation.sCenterGridNo != 0 ) { sGridNo = gMapInformation.sCenterGridNo; } else { INT8 bID = RandomSoldierIdForAnyMercInSector(); if( bID != -1 ) { sGridNo = Menptr[ bID ].sGridNo; } } break; } } return( sGridNo ); } void Ja25SAI_DetermineWhichLevelToAttackFrom( INT16 sSaiSector, INT16 *psSector, INT8 *pbLevel ) { INT8 bLevel=0; INT16 sSector=0; if ( gGameUBOptions.pJA2UB == TRUE ) { switch( sSaiSector ) { case JA25_K15_1: sSector = SEC_K15; bLevel = 2; break; case JA25_K15_2: sSector = SEC_L15; bLevel = 2; break; case JA25_L15_2: sSector = SEC_L15; bLevel = 3; break; default: sSector = gJa25AiSectorStruct[ sSaiSector ].iSectorID; bLevel = gJa25AiSectorStruct[ sSaiSector ].bSectorZ; break; } *psSector = sSector; *pbLevel = bLevel; } } BOOLEAN AreAllPlayerMercTraversingBetweenSectors() { INT32 cnt; SOLDIERTYPE *pSoldier=NULL; GROUP *pGroup=0; cnt = gTacticalStatus.Team[ OUR_TEAM ].bFirstID; for ( pSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ OUR_TEAM ].bLastID; cnt++, pSoldier++) { if( pSoldier->bActive ) { pGroup = GetGroup( pSoldier->ubGroupID ); if( pGroup != NULL ) { //if the group is NOT between sector if( pGroup->fBetweenSectors == FALSE ) { return( FALSE ); } } } } return( TRUE ); } #ifdef JA2BETAVERSION BOOLEAN RecordJa25StrategicAiDecisions( INT16 sSectorAttacked, UINT8 ubNumEnemies ) { /* CHAR8 zOutputString[512]; HWFILE hFile; //open up the debug file hFile = FileOpen( JA25_SAI_DEBUG_FILE, FILE_ACCESS_WRITE | FILE_OPEN_ALWAYS, FALSE ); if( !hFile ) { FileClose( hFile ); return( FALSE ); } //move the end of the file FileSeek( hFile, 0, FILE_SEEK_FROM_END ); OutputJA25SaiString( hFile, "\n\n--- Strategic Ai Attack ---\n" ); //Sector sprintf( zOutputString, "Sector Attacked: %c%d: Level %d\n", 'A' + SECTORY( gJa25AiSectorStruct[ sSectorAttacked ].iSectorID ) - 1, SECTORX( gJa25AiSectorStruct[ sSectorAttacked ].iSectorID ), gJa25AiSectorStruct[ sSectorAttacked ].bSectorZ ); OutputJA25SaiString( hFile, zOutputString ); //Num enemies sprintf( zOutputString, "Num Enemies: %d\n", ubNumEnemies ); OutputJA25SaiString( hFile, zOutputString ); //Time since last attack sprintf( zOutputString, "Time Since Last Attack: %d hours\n", ( GetWorldTotalMin() - gJa25StrategicAi.uiTimeOfLastBattleInMinutes ) / 60 ); OutputJA25SaiString( hFile, zOutputString ); //Probability sprintf( zOutputString, "Probability of Attack: %d/%d\n", gJa25AiSectorStruct[ sSectorAttacked ].bProbabilityOfAttacking, gJa25AiSectorStruct[ sSectorAttacked ].bCurrentProbability ); OutputJA25SaiString( hFile, zOutputString ); FileClose( hFile ); */ return( TRUE ); } BOOLEAN InitJa25StrategicAiDecisions( BOOLEAN fLoadedGame ) { HWFILE hFile; //open up the debug file hFile = FileOpen( JA25_SAI_DEBUG_FILE, FILE_ACCESS_WRITE | FILE_OPEN_ALWAYS, FALSE ); if( !hFile ) { FileClose( hFile ); return( FALSE ); } if( fLoadedGame ) OutputJA25SaiString( hFile, "\n\n\n\n\n\n****** Loaded Game ******\n\n"); else OutputJA25SaiString( hFile, "\n\n\n\n\n\n****** New Game ******\n\n"); switch( gGameOptions.ubDifficultyLevel ) { case DIF_LEVEL_EASY: OutputJA25SaiString( hFile, "Easy Difficulty\n"); break; case DIF_LEVEL_MEDIUM: OutputJA25SaiString( hFile, "Normal Difficulty\n"); break; case DIF_LEVEL_HARD: OutputJA25SaiString( hFile, "Hard Difficulty\n"); break; } OutputJA25SaiString( hFile, "\n\n\n"); FileClose( hFile ); return( FALSE ); } BOOLEAN OutputJA25SaiString( HWFILE hFile, CHAR *pString ) { UINT32 uiNumBytesWritten; UINT32 uiStrLen; uiStrLen = strlen( pString ); FileWrite( hFile, pString, uiStrLen, &uiNumBytesWritten ); if( uiNumBytesWritten != uiStrLen ) { return( FALSE ); } return( TRUE ); } #endif UINT32 GetMinimumTimeBetweenAttacks() { UINT32 uiTime=0; switch( gGameOptions.ubDifficultyLevel ) { case DIF_LEVEL_EASY: uiTime = JA25_TIME_BETWEEN_SAI_UPDATES_EASY; break; case DIF_LEVEL_MEDIUM: uiTime = JA25_TIME_BETWEEN_SAI_UPDATES_NORMAL; break; case DIF_LEVEL_HARD: uiTime = JA25_TIME_BETWEEN_SAI_UPDATES_HARD; break; } //Add a random time to calculation uiTime += Random( JA25_SAI_RANDOM_TIME ); return( uiTime ); } void InitJohnKulbaInitialSector() { gJa25SaveStruct.ubJohnKulbaInitialSectorY = MAP_ROW_I; //Randomly choose John's starting sector if( Random( 100 ) < 50 ) { gJa25SaveStruct.ubJohnKulbaInitialSectorX = 10; } else { gJa25SaveStruct.ubJohnKulbaInitialSectorX = 11; } } void HandleSayingDontStayToLongWarningInSectorH8() { UINT8 ubMercArray[3]; UINT8 ubNumMercs=0; SOLDIERTYPE *pSoldier=NULL; INT32 cnt; //if the player has advance passed this sector, leave if( gJa25AiSectorStruct[ JA25_H9 ].fPlayerControlled && gGameUBOptions.pJA2UB == TRUE ) { return; } if ( SectorInfo[ (UINT8)SECTOR( gGameExternalOptions.ubDefaultArrivalSectorX, gGameExternalOptions.ubDefaultArrivalSectorY ) ].fSurfaceWasEverPlayerControlled == TRUE && gGameUBOptions.pJA2UB == FALSE ) { return; } //if there are no enemies in the sector, leave if ( NumNonPlayerTeamMembersInSector( 8, 8, ENEMY_TEAM ) > 0 ) { return; } //if all the mercs are traversing, leave if( AreAllPlayerMercTraversingBetweenSectors() ) { return; } //if there are no mercs still in H8, leave if( PlayerMercsInSector( 8, 8, 0 ) == 0 ) { return; } cnt = gTacticalStatus.Team[ OUR_TEAM ].bFirstID; //Check to see if Gaston, Stogie or the PGC is on the team for ( pSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ OUR_TEAM ].bLastID; cnt++,pSoldier++) { //if the merc is alive, in this sector, etc... if( pSoldier->bActive && pSoldier->sSectorX == 8 && pSoldier->sSectorY == 8 && pSoldier->bSectorZ == 0 && pSoldier->stats.bLife >= OKLIFE && !pSoldier->flags.fBetweenSectors ) { //if the merc is either PGC, gaston, or stogie if( pSoldier->ubProfile == GASTON_UB || pSoldier->ubProfile == STOGIE_UB || pSoldier->ubWhatKindOfMercAmI == MERC_TYPE__PLAYER_CHARACTER ) { ubMercArray[ ubNumMercs++ ] = pSoldier->ubID; } } } if( ubNumMercs == 0 ) { return; } TacticalCharacterDialogue( &Menptr[ ubMercArray[ Random( ubNumMercs ) ] ], QUOTE_LAME_REFUSAL ); } void FixEnemyCounterInSectorBug() { INT32 iSectorCnt; SECTORINFO *pSector; //loop through all the sector and see if there has been for( iSectorCnt=0; iSectorCnt<256 ; iSectorCnt++ ) { //if the sector can never be rreached pSector = &SectorInfo[ iSectorCnt ]; if( pSector->ubTraversability[ THROUGH_STRATEGIC_MOVE ] == EDGEOFWORLD ) { pSector->ubNumAdmins = 0; pSector->ubNumTroops = 0; pSector->ubNumElites = 0; } } pSector = &SectorInfo[ SEC_L15 ]; pSector->ubNumAdmins = 0; pSector->ubNumTroops = 0; pSector->ubNumElites = 0; } void HandleEnricosUnderstandingEmail() { //if the player just got the laptop working again, and we are to send an email when entering a new sector if( gJa25SaveStruct.fSendEmail_10_NextSector ) { AddEmail( EMAIL_UNDERSTANDING, EMAIL_UNDERSTANDING_LENGTH, MAIL_ENRICO, GetWorldTotalMin() , -1 , -1, TYPE_EMAIL_EMAIL_EDT); //Remeber we sent it gJa25SaveStruct.ubEmailFromSectorFlag |= SECTOR_EMAIL__ANOTHER_SECTOR; gJa25SaveStruct.fSendEmail_10_NextSector = FALSE; } } void HandleRemovingEnemySoldierInitLinksIfPlayerEverWonInSector() { INT16 sJa25SaiSectorValue; if ( gGameUBOptions.pJA2UB == TRUE ) { //Get the Ja25 SAI sector value for the sector that is being attacked sJa25SaiSectorValue = GetJA25SectorID( gWorldSectorX, gWorldSectorY, gbWorldSectorZ ); //if the player has WON in this sector before, we must destroy the ENEMIES soldier init list links if( sJa25SaiSectorValue != -1 && gJa25AiSectorStruct[ sJa25SaiSectorValue ].fPlayerHasLiberatedSectorBefore ) { //Toast the ENEMIES init links RemoveAllEnemySoldierInitListLinks(); } } } void RemoveAllEnemySoldierInitListLinks() { SOLDIERINITNODE *curr; curr = gSoldierInitHead; //Loop through the list while( curr ) { // if this soldier is using detailed placement information, AND he is an enemy if( curr->pDetailedPlacement && curr->pDetailedPlacement->bTeam == ENEMY_TEAM ) { MemFree( curr->pDetailedPlacement ); curr->pDetailedPlacement = NULL; curr->pBasicPlacement->fDetailedPlacement = FALSE; } curr = curr->next; } } void Ja25HandleStartingAnyBattlesInOtherSectors() { UINT8 ubNumEnemies=0; UINT8 ubNumPlayers=0; INT8 bCnt; INT8 bSectorX, bSectorY, bSectorZ; if ( gGameUBOptions.pJA2UB == TRUE ) { //if there is a world loaded if( gWorldSectorX != -1 && gWorldSectorY != -1 && gbWorldSectorZ != -1 ) { ubNumEnemies = NumEnemiesInAnySector( gWorldSectorX, gWorldSectorY, gbWorldSectorZ ); //if there is enemies in this sector if( ubNumEnemies != 0 ) { //get out return; } } //loop through all the sectors and see if there any that have both enemy and player team mercs for( bCnt=0; bCnt< giNumJA25Sectors; bCnt++ ) { bSectorX = SECTORX( gJa25AiSectorStruct[ bCnt ].iSectorID ); bSectorY = SECTORX( gJa25AiSectorStruct[ bCnt ].iSectorID ); bSectorZ = gJa25AiSectorStruct[ bCnt ].bSectorZ; ubNumEnemies = NumEnemiesInAnySector( bSectorX, bSectorY, bSectorZ ); ubNumPlayers = PlayerMercsInSector( bSectorX, bSectorY, bSectorZ ); //if there are both enemies and player mercs in the same sector if( ubNumEnemies != 0 && ubNumPlayers != 0 ) { // GROUP *pEnemyGroup; // GROUP *pPlayerGroup; //we must start a battle // PrepareForPreBattleInterface( GROUP *pPlayerDialogGroup, GROUP *pInitiatingBattleGroup ); return; } } } } // -1 if enemy isnt supposed to seek player, otherwise SAI sectgor ID get returned INT8 GetSectorEnemyIsToImmediatelySeekEnemyIn() { INT16 sJa25SaiSectorValue; //if the currently loaded sector isnt the sector which the enemies are to attack sJa25SaiSectorValue = GetJA25SectorID( gWorldSectorX, gWorldSectorY, gbWorldSectorZ ); if( sJa25SaiSectorValue == -1 || sJa25SaiSectorValue != gJa25SaveStruct.bSectorTheEnemyWillSeekEnemy ) { return( -1 ); } //if the enemy is to seek the player when entering the sector if( gJa25SaveStruct.fEnemyShouldImmediatelySeekThePlayer ) { return( gJa25SaveStruct.bSectorTheEnemyWillSeekEnemy ); } else { return( -1 ); } } void SetH11NumEnemiesInSector() { UINT8 ubNumAdmins=0; UINT8 ubNumTroops=0; UINT8 ubNumElites=0; UINT8 ubNumTanks=0; UINT8 ubNumJeeps = 0; UINT8 ubNumRobots = 0; // SEC_H11: { switch( gGameOptions.ubDifficultyLevel ) { case DIF_LEVEL_EASY: ubNumAdmins = 1 + Random( 2 ); ubNumTroops = 6 + Random( 3 ); ubNumElites = 0 + Random( 0 ); break; case DIF_LEVEL_MEDIUM: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 8 + Random( 3 ); ubNumElites = 2 + Random( 2 ); break; case DIF_LEVEL_HARD: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 10 + Random( 4 ); ubNumElites = 4 + Random( 3 ); break; case DIF_LEVEL_INSANE: ubNumAdmins = 0 + Random( 0 ); ubNumTroops = 20 + Random( 4 ); ubNumElites = 4 + Random( 3 ); break; } SetNumberJa25EnemiesInSurfaceSector( SEC_H11, ubNumAdmins, ubNumTroops, ubNumElites, ubNumTanks, ubNumJeeps, ubNumRobots ); } } BOOLEAN HaveMercsEverBeenInComplex() { if (gGameUBOptions.pJA2UB == TRUE ) { //if the player has liberated K15 level 1 if( gJa25AiSectorStruct[ JA25_K15_1 ].fPlayerHasLiberatedSectorBefore ) { return( TRUE ); } else if( AreAnyPlayerMercsStillInSector( 15, 11, 1 ) ) { return( TRUE ); } } else if (gGameUBOptions.pJA2UB == FALSE ) { //if the player has liberated K15 level 1 if( SectorInfo[ SEC_K15 ].uiTimeLastPlayerLiberated && gbWorldSectorZ == 1 ) { return( TRUE ); } else if( AreAnyPlayerMercsStillInSector( 15, 11, 1 ) ) { return( TRUE ); } } return( FALSE ); } #ifdef JA2BETAVERSION BOOLEAN Ja25BetaDateToInvalidateExe() { SYSTEMTIME sSystemTime; GetSystemTime( &sSystemTime ); //if according to the system clock, we are past May 12, 2000, quit the game if( sSystemTime.wYear >= 2000 && sSystemTime.wMonth >= 5 && sSystemTime.wDay >= 12 ) { return( FALSE ); } return( TRUE ); } #endif void AddJA25AIDataToSector( JA25_SECTOR_AI *pSectorAIInfo ) { // See if there exists AI information here already... /* INT8 bSector; //get the sector id bSector = (INT8)GetJA25SectorID( (INT16)SECTORX( pSectorAIInfo->iSectorID ), (INT16)SECTORY( pSectorAIInfo->iSectorID ), pSectorAIInfo->bSectorZ ); if ( bSector != -1 ) { memcpy( &( gJa25AiSectorStruct[ bSector ] ), pSectorAIInfo, sizeof( JA25_SECTOR_AI ) ); return; } // Increment array gJa25AiSectorStruct = realloc( gJa25AiSectorStruct, ( giNumJA25Sectors + 1 ) * sizeof( JA25_SECTOR_AI ) ); memcpy( &( gJa25AiSectorStruct[ giNumJA25Sectors ] ), pSectorAIInfo, sizeof( JA25_SECTOR_AI ) ); giNumJA25Sectors++; */ } #endif