diff --git a/GameSettings.cpp b/GameSettings.cpp index 9a92439c..e6ca0f33 100644 --- a/GameSettings.cpp +++ b/GameSettings.cpp @@ -274,9 +274,7 @@ BOOLEAN LoadGameSettings() if (!IsHiSpeedClockMode()) gGameSettings.fOptions[TOPTION_AUTO_FAST_FORWARD_MODE] = FALSE; -#ifdef ENABLE_ZOMBIES gGameSettings.fOptions[TOPTION_ZOMBIES] = iniReader.ReadBoolean("JA2 Game Settings","TOPTION_ZOMBIES" , FALSE ); -#endif gGameSettings.fOptions[TOPTION_ENABLE_INVENTORY_POPUPS] = iniReader.ReadBoolean("JA2 Game Settings","TOPTION_ENABLE_INVENTORY_POPUPS" , TRUE ); // the_bob : enable popups for picking items from sector inv @@ -446,9 +444,7 @@ BOOLEAN SaveGameSettings() settings << "TOPTION_SHOW_LBE_CONTENT = " << (gGameSettings.fOptions[TOPTION_SHOW_LBE_CONTENT] ? "TRUE" : "FALSE" ) << endl; settings << "TOPTION_INVERT_WHEEL = " << (gGameSettings.fOptions[TOPTION_INVERT_WHEEL] ? "TRUE" : "FALSE" ) << endl; -#ifdef ENABLE_ZOMBIES settings << "TOPTION_ZOMBIES = " << (gGameSettings.fOptions[TOPTION_ZOMBIES] ? "TRUE" : "FALSE" ) << endl; -#endif settings << "TOPTION_ENABLE_INVENTORY_POPUPS = " << (gGameSettings.fOptions[TOPTION_ENABLE_INVENTORY_POPUPS] ? "TRUE" : "FALSE" ) << endl; // the_bob : enable popups for picking items from sector inv settings << "TOPTION_MERCENARY_FORMATIONS = " << (gGameSettings.fOptions[TOPTION_MERCENARY_FORMATIONS] ? "TRUE" : "FALSE" ) << endl; @@ -581,9 +577,7 @@ void InitGameSettings() gGameSettings.fOptions[ TOPTION_AUTO_FAST_FORWARD_MODE ] = FALSE; -#ifdef ENABLE_ZOMBIES - gGameSettings.fOptions[ TOPTION_ZOMBIES ] = FALSE; // Flugente Zombies 1.0 -#endif + gGameSettings.fOptions[ TOPTION_ZOMBIES ] = FALSE; // Flugente Zombies gGameSettings.fOptions[ TOPTION_ENABLE_INVENTORY_POPUPS ] = TRUE; // the_bob : enable popups for picking items from sector inv diff --git a/GameSettings.h b/GameSettings.h index 0b6e197e..f906c190 100644 --- a/GameSettings.h +++ b/GameSettings.h @@ -89,9 +89,7 @@ enum TOPTION_AUTO_FAST_FORWARD_MODE, // automatically fast forward through AI turns -#ifdef ENABLE_ZOMBIES - TOPTION_ZOMBIES, // Flugente Zombies 1.0: allow zombies -#endif + TOPTION_ZOMBIES, // Flugente Zombies: allow zombies TOPTION_ENABLE_INVENTORY_POPUPS, // the_bob : enable popups for picking items from sector inv diff --git a/Laptop/personnel.cpp b/Laptop/personnel.cpp index db1866df..421e9bfe 100644 --- a/Laptop/personnel.cpp +++ b/Laptop/personnel.cpp @@ -5075,11 +5075,8 @@ void DisplayDepartedCharStats(INT32 iId, INT32 iSlot, INT32 iState) // kills mprintf((INT16)(pPersonnelScreenPoints[20].x+(iSlot*TEXT_BOX_WIDTH)),(pPersonnelScreenPoints[20].y - 12),pPersonnelScreenStrings[PRSNL_TXT_KILLS]); -#ifdef ENABLE_ZOMBIES swprintf(sString, L"%d",(gMercProfiles[iId].records.usKillsElites + gMercProfiles[iId].records.usKillsRegulars + gMercProfiles[iId].records.usKillsAdmins + gMercProfiles[iId].records.usKillsHostiles + gMercProfiles[iId].records.usKillsCreatures + gMercProfiles[iId].records.usKillsZombies + gMercProfiles[iId].records.usKillsTanks + gMercProfiles[iId].records.usKillsOthers)); -#else - swprintf(sString, L"%d",(gMercProfiles[iId].records.usKillsElites + gMercProfiles[iId].records.usKillsRegulars + gMercProfiles[iId].records.usKillsAdmins + gMercProfiles[iId].records.usKillsHostiles + gMercProfiles[iId].records.usKillsCreatures + gMercProfiles[iId].records.usKillsTanks + gMercProfiles[iId].records.usKillsOthers)); -#endif + FindFontRightCoordinates((INT16)(pPersonnelScreenPoints[20].x+(iSlot*TEXT_BOX_WIDTH)),0,TEXT_BOX_WIDTH-20,0,sString, PERS_FONT, &sX, &sY); mprintf(sX,(pPersonnelScreenPoints[20].y - 12),sString); @@ -7053,11 +7050,8 @@ DEF:3/19/99: // kills mprintf((INT16)(pPersonnelScreenPoints[20].x+(iSlot*TEXT_BOX_WIDTH)),(pPersonnelScreenPoints[20].y - 12),pPersonnelScreenStrings[PRSNL_TXT_KILLS]); -#ifdef ENABLE_ZOMBIES swprintf(sString, L"%d",(gMercProfiles[Menptr[iId].ubProfile].records.usKillsElites + gMercProfiles[Menptr[iId].ubProfile].records.usKillsRegulars + gMercProfiles[Menptr[iId].ubProfile].records.usKillsAdmins + gMercProfiles[Menptr[iId].ubProfile].records.usKillsHostiles + gMercProfiles[Menptr[iId].ubProfile].records.usKillsCreatures + gMercProfiles[Menptr[iId].ubProfile].records.usKillsZombies + gMercProfiles[Menptr[iId].ubProfile].records.usKillsTanks + gMercProfiles[Menptr[iId].ubProfile].records.usKillsOthers)); -#else - swprintf(sString, L"%d",(gMercProfiles[Menptr[iId].ubProfile].records.usKillsElites + gMercProfiles[Menptr[iId].ubProfile].records.usKillsRegulars + gMercProfiles[Menptr[iId].ubProfile].records.usKillsAdmins + gMercProfiles[Menptr[iId].ubProfile].records.usKillsHostiles + gMercProfiles[Menptr[iId].ubProfile].records.usKillsCreatures + gMercProfiles[Menptr[iId].ubProfile].records.usKillsTanks + gMercProfiles[Menptr[iId].ubProfile].records.usKillsOthers)); -#endif + FindFontRightCoordinates((INT16)(pPersonnelScreenPoints[20].x+(iSlot*TEXT_BOX_WIDTH)),0,TEXT_BOX_WIDTH-20,0,sString, PERS_FONT, &sX, &sY); mprintf(sX,(pPersonnelScreenPoints[20].y - 12),sString); @@ -8612,7 +8606,6 @@ void AssignPersonnelKillsHelpText( INT32 ubProfile ) wcscat( apStr, atStr ); } -#ifdef ENABLE_ZOMBIES if (gGameSettings.fOptions[TOPTION_ZOMBIES] ) { if (gMercProfiles[ubProfile].records.usKillsZombies > 0 || fShowRecordsIfZero) @@ -8621,13 +8614,10 @@ void AssignPersonnelKillsHelpText( INT32 ubProfile ) wcscat( apStr, atStr ); } } -#endif // Set region help text SetRegionFastHelpText( &(gSkillTraitHelpTextRegion[7]), apStr ); SetRegionHelpEndCallback( &gSkillTraitHelpTextRegion[7], MSYS_NO_CALLBACK ); - - return; } void AssignPersonnelAssistsHelpText( INT32 ubProfile ) diff --git a/ModularizedTacticalAI/src/LegacyAIPlanFactory.cpp b/ModularizedTacticalAI/src/LegacyAIPlanFactory.cpp index 3ac06ee4..487aead1 100644 --- a/ModularizedTacticalAI/src/LegacyAIPlanFactory.cpp +++ b/ModularizedTacticalAI/src/LegacyAIPlanFactory.cpp @@ -34,10 +34,10 @@ namespace AI find_supper->add_subplan(peck); return find_supper; } -#ifdef ENABLE_ZOMBIES + if(npc->IsZombie()) return new LegacyZombiePlan(npc); -#endif + return new LegacyAIPlan(npc); // no special plan for other cases yet, return default legacy AI wrapper } diff --git a/Options Screen.cpp b/Options Screen.cpp index 5229765f..d494ba37 100644 --- a/Options Screen.cpp +++ b/Options Screen.cpp @@ -1398,9 +1398,7 @@ void HandleOptionToggle( UINT8 Button_UserData_0, UINT8 Button_UserData_1, BOOLE case TOPTION_ALLOW_SOLDIER_TOOLTIPS: case TOPTION_USE_AUTO_SAVE: case TOPTION_TOGGLE_TURN_MODE: -#ifdef ENABLE_ZOMBIES case TOPTION_ZOMBIES: -#endif gGameSettings.fOptions[ Button_UserData_1 ] = FALSE; break; } @@ -1465,9 +1463,7 @@ void HandleOptionToggle( UINT8 Button_UserData_0, UINT8 Button_UserData_1, BOOLE case TOPTION_ALLOW_SOLDIER_TOOLTIPS: case TOPTION_USE_AUTO_SAVE: case TOPTION_TOGGLE_TURN_MODE: -#ifdef ENABLE_ZOMBIES case TOPTION_ZOMBIES: -#endif gGameSettings.fOptions[ Button_UserData_1 ] = FALSE; break; } diff --git a/Strategic/Auto Resolve.cpp b/Strategic/Auto Resolve.cpp index 38263e34..cd2cdf73 100644 --- a/Strategic/Auto Resolve.cpp +++ b/Strategic/Auto Resolve.cpp @@ -4640,11 +4640,9 @@ void AttackTarget( SOLDIERCELL *pAttacker, SOLDIERCELL *pTarget ) case SOLDIER_CLASS_CREATURE : gMercProfiles[ pAttacker->pSoldier->ubProfile ].records.usKillsCreatures++; break; -#ifdef ENABLE_ZOMBIES case SOLDIER_CLASS_ZOMBIE : gMercProfiles[ pAttacker->pSoldier->ubProfile ].records.usKillsZombies++; break; -#endif default : if ( CREATURE_OR_BLOODCAT( pTarget->pSoldier ) ) gMercProfiles[ pAttacker->pSoldier->ubProfile ].records.usKillsCreatures++; @@ -4856,11 +4854,9 @@ void TargetHitCallback( SOLDIERCELL *pTarget, INT32 index ) case SOLDIER_CLASS_CREATURE : gMercProfiles[ pKiller->pSoldier->ubProfile ].records.usKillsCreatures++; break; -#ifdef ENABLE_ZOMBIES case SOLDIER_CLASS_ZOMBIE : gMercProfiles[ pKiller->pSoldier->ubProfile ].records.usKillsZombies++; break; -#endif default : if ( CREATURE_OR_BLOODCAT( pTarget->pSoldier ) ) gMercProfiles[ pKiller->pSoldier->ubProfile ].records.usKillsCreatures++; diff --git a/Strategic/Meanwhile.cpp b/Strategic/Meanwhile.cpp index 5f5871f1..932334f3 100644 --- a/Strategic/Meanwhile.cpp +++ b/Strategic/Meanwhile.cpp @@ -844,9 +844,7 @@ void EndMeanwhile( ) { // We leave this sector open for our POWs to escape! // Set music mode to enemy present! -#ifdef ENABLE_ZOMBIES UseCreatureMusic(HostileZombiesPresent()); -#endif SetMusicMode( MUSIC_TACTICAL_ENEMYPRESENT ); diff --git a/Strategic/PreBattle Interface.cpp b/Strategic/PreBattle Interface.cpp index 2de07916..f59f1839 100644 --- a/Strategic/PreBattle Interface.cpp +++ b/Strategic/PreBattle Interface.cpp @@ -921,16 +921,14 @@ void InitPreBattleInterface( GROUP *pBattleGroup, BOOLEAN fPersistantPBI ) //Disable the options button when the auto resolve screen comes up EnableDisAbleMapScreenOptionsButton( FALSE ); -#ifdef ENABLE_ZOMBIES UseCreatureMusic(HostileZombiesPresent()); -#endif - #ifdef NEWMUSIC +#ifdef NEWMUSIC GlobalSoundID = MusicSoundValues[ SECTOR( gubPBSectorX, gubPBSectorY ) ].SoundTacticalTensor[gubPBSectorZ]; if ( MusicSoundValues[ SECTOR( gubPBSectorX, gubPBSectorY ) ].SoundTacticalTensor[gubPBSectorZ] != -1 ) SetMusicModeID( MUSIC_TACTICAL_ENEMYPRESENT, MusicSoundValues[ SECTOR( gubPBSectorX, gubPBSectorY ) ].SoundTacticalTensor[gubPBSectorZ] ); else - #endif +#endif SetMusicMode( MUSIC_TACTICAL_ENEMYPRESENT ); #ifdef JA2UB diff --git a/Strategic/Strategic Movement.cpp b/Strategic/Strategic Movement.cpp index 233bdd20..a1e1c20b 100644 --- a/Strategic/Strategic Movement.cpp +++ b/Strategic/Strategic Movement.cpp @@ -1059,15 +1059,14 @@ void PrepareForPreBattleInterface( GROUP *pPlayerDialogGroup, GROUP *pInitiating } //Set music -#ifdef ENABLE_ZOMBIES UseCreatureMusic(HostileZombiesPresent()); -#endif - #ifdef NEWMUSIC + +#ifdef NEWMUSIC GlobalSoundID = MusicSoundValues[ SECTOR( gWorldSectorX, gWorldSectorY ) ].SoundTacticalTensor[gbWorldSectorZ]; if ( MusicSoundValues[ SECTOR( gWorldSectorX, gWorldSectorY ) ].SoundTacticalTensor[gbWorldSectorZ] != -1 ) SetMusicModeID( MUSIC_TACTICAL_ENEMYPRESENT, MusicSoundValues[ SECTOR( gWorldSectorX, gWorldSectorY ) ].SoundTacticalTensor[gbWorldSectorZ] ); else - #endif +#endif SetMusicMode( MUSIC_TACTICAL_ENEMYPRESENT ); if( gfTacticalTraversal && pInitiatingBattleGroup == gpTacticalTraversalGroup || diff --git a/Tactical/Inventory Choosing.cpp b/Tactical/Inventory Choosing.cpp index d289723b..d9939969 100644 --- a/Tactical/Inventory Choosing.cpp +++ b/Tactical/Inventory Choosing.cpp @@ -862,11 +862,9 @@ void GenerateRandomEquipment( SOLDIERCREATE_STRUCT *pp, INT8 bSoldierClass, INT8 } break; -#ifdef ENABLE_ZOMBIES case SOLDIER_CLASS_ZOMBIE: // zombies get no items at all... break; -#endif } UINT32 invsize = pp->Inv.size(); @@ -1086,11 +1084,8 @@ void ChooseWeaponForSoldierCreateStruct( SOLDIERCREATE_STRUCT *pp, INT8 bWeaponC bStatus += (INT8)(HighestPlayerProgressPercentage() / 10); bStatus = (INT8)min( 100, bStatus ); break; - -#ifdef ENABLE_ZOMBIES case SOLDIER_CLASS_ZOMBIE: - bStatus = (INT8)(1); // if a zombie gets anything, it'll be broken -#endif + bStatus = 1; // if a zombie gets anything, it'll be broken default: bStatus = (INT8)(50 + Random( 51 ) ); break; @@ -2144,9 +2139,7 @@ void ChooseFaceGearForSoldierCreateStruct( SOLDIERCREATE_STRUCT *pp ) break; case SOLDIER_CLASS_ADMINISTRATOR: case SOLDIER_CLASS_GREEN_MILITIA: -#ifdef ENABLE_ZOMBIES case SOLDIER_CLASS_ZOMBIE: -#endif break; } diff --git a/Tactical/Merc Entering.cpp b/Tactical/Merc Entering.cpp index 92df6001..2342e674 100644 --- a/Tactical/Merc Entering.cpp +++ b/Tactical/Merc Entering.cpp @@ -993,15 +993,14 @@ void HandleFirstHeliDropOfGame( ) SayQuoteFromAnyBodyInSector( QUOTE_ENEMY_PRESENCE ); // Start music -#ifdef ENABLE_ZOMBIES UseCreatureMusic(HostileZombiesPresent()); -#endif - #ifdef NEWMUSIC + +#ifdef NEWMUSIC GlobalSoundID = MusicSoundValues[ SECTOR( gWorldSectorX, gWorldSectorY ) ].SoundTacticalTensor[gbWorldSectorZ]; if ( MusicSoundValues[ SECTOR( gWorldSectorX, gWorldSectorY ) ].SoundTacticalTensor[gbWorldSectorZ] != -1 ) SetMusicModeID( MUSIC_TACTICAL_ENEMYPRESENT, MusicSoundValues[ SECTOR( gWorldSectorX, gWorldSectorY ) ].SoundTacticalTensor[gbWorldSectorZ] ); else - #endif +#endif SetMusicMode( MUSIC_TACTICAL_ENEMYPRESENT ); } diff --git a/Tactical/Overhead.cpp b/Tactical/Overhead.cpp index 86fd4344..6a55cbc3 100644 --- a/Tactical/Overhead.cpp +++ b/Tactical/Overhead.cpp @@ -6242,16 +6242,14 @@ void ExitCombatMode( ) // unused //gfForceMusicToTense = TRUE; -#ifdef ENABLE_ZOMBIES UseCreatureMusic(HostileZombiesPresent()); -#endif - #ifdef NEWMUSIC +#ifdef NEWMUSIC GlobalSoundID = MusicSoundValues[ SECTOR( gWorldSectorX, gWorldSectorY ) ].SoundTacticalTensor[gbWorldSectorZ]; if ( MusicSoundValues[ SECTOR( gWorldSectorX, gWorldSectorY ) ].SoundTacticalTensor[gbWorldSectorZ] != -1 ) SetMusicModeID( MUSIC_TACTICAL_ENEMYPRESENT, MusicSoundValues[ SECTOR( gWorldSectorX, gWorldSectorY ) ].SoundTacticalTensor[gbWorldSectorZ] ); else - #endif +#endif SetMusicMode( MUSIC_TACTICAL_ENEMYPRESENT ); BetweenTurnsVisibilityAdjustments(); @@ -6311,15 +6309,14 @@ void SetEnemyPresence( ) #endif { -#ifdef ENABLE_ZOMBIES UseCreatureMusic(HostileZombiesPresent()); -#endif - #ifdef NEWMUSIC + +#ifdef NEWMUSIC GlobalSoundID = MusicSoundValues[ SECTOR( gWorldSectorX, gWorldSectorY ) ].SoundTacticalTensor[gbWorldSectorZ]; if ( MusicSoundValues[ SECTOR( gWorldSectorX, gWorldSectorY ) ].SoundTacticalTensor[gbWorldSectorZ] != -1 ) SetMusicModeID( MUSIC_TACTICAL_ENEMYPRESENT, MusicSoundValues[ SECTOR( gWorldSectorX, gWorldSectorY ) ].SoundTacticalTensor[gbWorldSectorZ] ); else - #endif +#endif SetMusicMode( MUSIC_TACTICAL_ENEMYPRESENT ); DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("SetEnemyPresence: warnings = false")); @@ -6658,15 +6655,14 @@ BOOLEAN CheckForEndOfCombatMode( BOOLEAN fIncrementTurnsNotSeen ) // Begin tense music.... // unused //gfForceMusicToTense = TRUE; -#ifdef ENABLE_ZOMBIES UseCreatureMusic(HostileZombiesPresent()); -#endif - #ifdef NEWMUSIC + +#ifdef NEWMUSIC GlobalSoundID = MusicSoundValues[ SECTOR( gWorldSectorX, gWorldSectorY ) ].SoundTacticalTensor[gbWorldSectorZ]; if ( MusicSoundValues[ SECTOR( gWorldSectorX, gWorldSectorY ) ].SoundTacticalTensor[gbWorldSectorZ] != -1 ) SetMusicModeID( MUSIC_TACTICAL_ENEMYPRESENT, MusicSoundValues[ SECTOR( gWorldSectorX, gWorldSectorY ) ].SoundTacticalTensor[gbWorldSectorZ] ); else - #endif +#endif SetMusicMode( MUSIC_TACTICAL_ENEMYPRESENT ); return( TRUE ); @@ -7082,10 +7078,8 @@ BOOLEAN CheckForEndOfBattle( BOOLEAN fAnEnemyRetreated ) return( FALSE ); } -#ifdef ENABLE_ZOMBIES if ( HostileZombiesPresent() ) //Madd: got tired of the victory music playing right after the zombies arose return FALSE; -#endif // OK, this is to releave infinate looping...becasue we can kill guys in this function if ( gfKillingGuysForLosingBattle ) @@ -7802,7 +7796,6 @@ UINT8 NumEnemyInSector( ) } -#ifdef ENABLE_ZOMBIES UINT8 NumZombiesInSector( ) { SOLDIERTYPE *pTeamSoldier; @@ -7822,7 +7815,6 @@ UINT8 NumZombiesInSector( ) return( ubNumZombies ); } -#endif UINT8 NumEnemyInSectorExceptCreatures() { @@ -10056,7 +10048,6 @@ BOOLEAN HostileBloodcatsPresent( ) return( FALSE ); } -#ifdef ENABLE_ZOMBIES BOOLEAN HostileZombiesPresent( ) { INT32 iLoop; @@ -10079,7 +10070,6 @@ BOOLEAN HostileZombiesPresent( ) return( FALSE ); } -#endif void HandleCreatureTenseQuote( ) { diff --git a/Tactical/Overhead.h b/Tactical/Overhead.h index d7fda351..28ee58b5 100644 --- a/Tactical/Overhead.h +++ b/Tactical/Overhead.h @@ -318,10 +318,7 @@ void DoPOWPathChecks( ); BOOLEAN HostileCiviliansWithGunsPresent( ); BOOLEAN HostileCiviliansPresent( ); BOOLEAN HostileBloodcatsPresent( ); - -#ifdef ENABLE_ZOMBIES BOOLEAN HostileZombiesPresent( ); -#endif UINT8 NumPCsInSector( ); void SetSoldierNonNeutral( SOLDIERTYPE * pSoldier ); @@ -353,10 +350,8 @@ BOOLEAN WillAirRaidBeStopped( INT16 sSectorX, INT16 sSectorY ); // HEADROCK HAM 3.5: Externalized for First Arrival enemy check extern UINT8 NumEnemyInSector(); -#ifdef ENABLE_ZOMBIES // Flugente extern UINT8 NumZombiesInSector(); -#endif // Flugente: offer the enemy the chance to surrender void HandleSurrenderOffer( SOLDIERTYPE* pSoldier ); diff --git a/Tactical/Rotting Corpses.cpp b/Tactical/Rotting Corpses.cpp index 6dfedfd5..521c9039 100644 --- a/Tactical/Rotting Corpses.cpp +++ b/Tactical/Rotting Corpses.cpp @@ -590,11 +590,9 @@ INT32 AddRottingCorpse( ROTTING_CORPSE_DEFINITION *pCorpseDef ) pCorpse->pAniTile->uiUserData = iIndex; pCorpse->iID = iIndex; -#ifdef ENABLE_ZOMBIES // copy name of corpse definition... memcpy( &(pCorpse->name), &(pCorpseDef->name), sizeof(CHAR16) * 10 ); pCorpse->name[9] = '\0'; -#endif pCorpse->fActivated = TRUE; @@ -865,7 +863,6 @@ BOOLEAN TurnSoldierIntoCorpse( SOLDIERTYPE *pSoldier, BOOLEAN fRemoveMerc, BOOLE Corpse.usFlags |= ROTTING_CORPSE_USE_SNOW_CAMO_PALETTE; } -#ifdef ENABLE_ZOMBIES // Flugente Zombies: Determine if a zombie can rise from this corpse switch ( gGameExternalOptions.sZombieRiseBehaviour ) { @@ -900,7 +897,6 @@ BOOLEAN TurnSoldierIntoCorpse( SOLDIERTYPE *pSoldier, BOOLEAN fRemoveMerc, BOOLE // Flugente: copy name of soldier... memcpy( &(Corpse.name), &(pSoldier->name), sizeof(CHAR16) * 10 ); Corpse.name[9] = '\0'; -#endif // if this soldier's uniform was damaged (gunfire, blade attacks, explosions) then don't allow to take the uniform. We can't stay hidden if we're covered in blood :-) if ( pSoldier->usSoldierFlagMask & SOLDIER_DAMAGED_VEST ) @@ -2090,10 +2086,8 @@ BOOLEAN TakeCorpse( SOLDIERTYPE *pSoldier, INT32 sGridNo, INT8 bLevel ) if ( pCorpse->def.usFlags & ROTTING_CORPSE_NO_PANTS ) gTempObject[0]->data.sObjectFlag |= CORPSE_NO_PANTS; -#ifdef ENABLE_ZOMBIES if ( pCorpse->def.usFlags & ROTTING_CORPSE_NEVER_RISE_AGAIN ) gTempObject[0]->data.sObjectFlag |= CORPSE_NO_ZOMBIE_RISE; -#endif // now we have to get the correct flags for the object from the corpse, so that upon recreating the corpse, it looks the same UINT8 headpal = 0, skinpal = 0, vestpal = 0, pantspal = 0; @@ -2341,11 +2335,9 @@ BOOLEAN AddCorpseFromObject(OBJECTTYPE* pObj, INT32 sGridNo, INT8 bLevel ) Corpse.fHeadTaken = FALSE; Corpse.ubAIWarningValue = 20; -#ifdef ENABLE_ZOMBIES // Flugente: use zombie name (it's the only way this will ever be relevant again anyway) swprintf( Corpse.name, TacticalStr[ ZOMBIE_TEAM_MERC_NAME ] ); Corpse.name[9] = '\0'; -#endif INT32 iCorpseID = AddRottingCorpse( &Corpse ); @@ -2361,11 +2353,9 @@ BOOLEAN AddCorpseFromObject(OBJECTTYPE* pObj, INT32 sGridNo, INT8 bLevel ) if ( (*pObj)[0]->data.sObjectFlag & CORPSE_NO_PANTS ) gRottingCorpse[ iCorpseID ].def.usFlags |= ROTTING_CORPSE_NO_PANTS; - -#ifdef ENABLE_ZOMBIES + if ( (*pObj)[0]->data.sObjectFlag & CORPSE_NO_ZOMBIE_RISE ) gRottingCorpse[ iCorpseID ].def.usFlags |= ROTTING_CORPSE_NEVER_RISE_AGAIN; -#endif return TRUE; } @@ -2585,7 +2575,6 @@ UINT8 GetNearestRottingCorpseAIWarning( INT32 sGridNo ) return( ubHighestWarning ); } -#ifdef ENABLE_ZOMBIES // Flugente Zombies: resurrect zombies void RaiseZombies( void ) { @@ -3011,7 +3000,6 @@ BOOLEAN CorpseOkToSpawnZombie( ROTTING_CORPSE * pCorpse, UINT16* pAnimState ) return( canbezombie ); } -#endif // Flugente: can we take the clothes of this corpse? // calling this with NULL for soldier will give a general answer for any bodytype diff --git a/Tactical/Rotting Corpses.h b/Tactical/Rotting Corpses.h index 30048c64..73b60d3b 100644 --- a/Tactical/Rotting Corpses.h +++ b/Tactical/Rotting Corpses.h @@ -86,9 +86,7 @@ enum RottingCorpseDefines #define ROTTING_CORPSE_USE_DESERT_CAMO_PALETTE 0x00000200 //We use desert palette here.... #define ROTTING_CORPSE_USE_SNOW_CAMO_PALETTE 0x00000400 //We use snow palette here.... -#ifdef ENABLE_ZOMBIES - #define ROTTING_CORPSE_NEVER_RISE_AGAIN 0x00000800 //a zombie cannot be created from this corpse (if not set, it'll eventually rise again ) -#endif +#define ROTTING_CORPSE_NEVER_RISE_AGAIN 0x00000800 //a zombie cannot be created from this corpse (if not set, it'll eventually rise again ) // Flugente: corpses can now be gutted after they have been decapitated. Atm there is no corpse that can be both gutted and decapitated (to be done later) #define ROTTING_CORPSE_HEAD_TAKEN 0x00001000 // head has been taken off @@ -125,10 +123,8 @@ typedef struct UINT8 ubFiller[ 10 ]; // Flugente: 12 -> 10, because usFlags was cahnged from UINT16 to UINT32 -#ifdef ENABLE_ZOMBIES // Flugente: added name so we can display individual name if corpse gets resurrected... CHAR16 name[ 10 ]; -#endif } ROTTING_CORPSE_DEFINITION; @@ -151,10 +147,8 @@ typedef struct BOOLEAN fAttractCrowsOnlyWhenOnScreen; INT32 iID; -#ifdef ENABLE_ZOMBIES // Flugente: added name so we can display individual name if corpse gets resurrected... CHAR16 name[ 10 ]; -#endif } ROTTING_CORPSE; @@ -226,16 +220,14 @@ INT32 GetGridNoOfCorpseGivenProfileID( UINT8 ubProfileID ); void DecayRottingCorpseAIWarnings( void ); UINT8 GetNearestRottingCorpseAIWarning( INT32 sGridNo ); -#ifdef ENABLE_ZOMBIES - // Flugente: Raise zombies - void RaiseZombies( void ); +// Flugente: Raise zombies +void RaiseZombies( void ); - // Flugente: create a zombie from a corpse - void CreateZombiefromCorpse( ROTTING_CORPSE * pCorpse, UINT16 usAnimState ); +// Flugente: create a zombie from a corpse +void CreateZombiefromCorpse( ROTTING_CORPSE * pCorpse, UINT16 usAnimState ); - // Flugente: returns true if a zombie can be raised from this corpse, and returns the correct pAnimState for the new zombie - BOOLEAN CorpseOkToSpawnZombie( ROTTING_CORPSE * pCorpse, UINT16* pAnimState ); -#endif +// Flugente: returns true if a zombie can be raised from this corpse, and returns the correct pAnimState for the new zombie +BOOLEAN CorpseOkToSpawnZombie( ROTTING_CORPSE * pCorpse, UINT16* pAnimState ); // Flugente: can we take the clothes of this corpse? BOOLEAN CorpseOkToDress( ROTTING_CORPSE* pCorpse ); diff --git a/Tactical/Soldier Ani.cpp b/Tactical/Soldier Ani.cpp index 94ee76ef..a72fa41c 100644 --- a/Tactical/Soldier Ani.cpp +++ b/Tactical/Soldier Ani.cpp @@ -3943,11 +3943,9 @@ BOOLEAN HandleSoldierDeath( SOLDIERTYPE *pSoldier , BOOLEAN *pfMadeCorpse ) case SOLDIER_CLASS_CREATURE : gMercProfiles[ MercPtrs[ ubAttacker ]->ubProfile ].records.usKillsCreatures++; break; -#ifdef ENABLE_ZOMBIES case SOLDIER_CLASS_ZOMBIE : gMercProfiles[ MercPtrs[ ubAttacker ]->ubProfile ].records.usKillsZombies++; break; -#endif default : if ( CREATURE_OR_BLOODCAT( pSoldier ) ) gMercProfiles[ MercPtrs[ ubAttacker ]->ubProfile ].records.usKillsCreatures++; diff --git a/Tactical/Soldier Control.cpp b/Tactical/Soldier Control.cpp index 759cf7df..5479d16a 100644 --- a/Tactical/Soldier Control.cpp +++ b/Tactical/Soldier Control.cpp @@ -150,10 +150,8 @@ extern BOOLEAN gfShiftBombPlant; #define MIN_SUBSEQUENT_SNDS_DELAY 2000 #include "connect.h" -#ifdef ENABLE_ZOMBIES extern void TeleportSelectedSoldier( void ); extern BOOLEAN AddSoldierToSectorNoCalculateDirectionUseAnimation( UINT8 ubID, UINT16 usAnimState, UINT16 usAnimCode ); -#endif // Flugente: external sector data extern SECTOR_EXT_DATA SectorExternalData[256][4]; @@ -1369,11 +1367,7 @@ MERCPROFILESTRUCT& MERCPROFILESTRUCT::operator=(const OLD_MERCPROFILESTRUCT_101& this->records.usKillsAdmins = (src.usKills / 4); this->records.usKillsHostiles = 0; this->records.usKillsCreatures = 0; - -#ifdef ENABLE_ZOMBIES this->records.usKillsZombies = 0; -#endif - this->records.usKillsTanks = 0; this->records.usKillsOthers = 0; this->records.usAssistsMercs = (src.usAssists * 3 / 4); @@ -9363,7 +9357,6 @@ void SOLDIERTYPE::BeginSoldierClimbUpRoof( void ) this->ubPendingDirection = bNewDirection; //this->usPendingAnimation = CLIMBUPROOF; -#ifdef ENABLE_ZOMBIES // Flugente: In case an animation is missing (zombies with bodytype of civilians), we TELEPORT instead if ( IsAnimationValidForBodyType( this, CLIMBUPROOF ) == FALSE ) { @@ -9373,9 +9366,6 @@ void SOLDIERTYPE::BeginSoldierClimbUpRoof( void ) } else this->EVENT_InitNewSoldierAnim( CLIMBUPROOF, 0, FALSE ); -#else - this->EVENT_InitNewSoldierAnim( CLIMBUPROOF, 0, FALSE ); -#endif this->InternalReceivingSoldierCancelServices( FALSE ); this->InternalGivingSoldierCancelServices( FALSE ); @@ -10059,15 +10049,12 @@ UINT8 SOLDIERTYPE::SoldierTakeDamage( INT8 bHeight, INT16 sLifeDeduct, INT16 sBr } } } - -#ifdef ENABLE_ZOMBIES else if ( this->IsZombie( ) && this->stats.bLife > 0 && this->stats.bLife < OKLIFE ) { // a zombie doesn't automatically die, so he would normally stand up again after being hit. // We don't want that, because he is dying, so we manually skip that animation this->usPendingAnimation = NO_PENDING_ANIMATION; } -#endif if ( fShowDamage ) { @@ -10776,7 +10763,6 @@ BOOLEAN SOLDIERTYPE::InternalDoMercBattleSound( UINT8 ubBattleSoundID, INT8 bSpe } } } -#ifdef ENABLE_ZOMBIES else if ( pSoldier->IsZombie( ) ) // Madd: add zombie sounds { if ( ubSoundID == BATTLE_SOUND_DIE1 ) @@ -10796,7 +10782,6 @@ BOOLEAN SOLDIERTYPE::InternalDoMercBattleSound( UINT8 ubBattleSoundID, INT8 bSpe } } } -#endif else { if ( ubSoundID == BATTLE_SOUND_DIE1 ) @@ -11054,7 +11039,6 @@ void SOLDIERTYPE::BeginSoldierClimbDownRoof( void ) this->ubPendingDirection = bNewDirection; -#ifdef ENABLE_ZOMBIES // Flugente: In case an animation is missing (zombies with bodytype of civilians), we TELEPORT instead if ( IsAnimationValidForBodyType( this, JUMPDOWNWALL ) == FALSE ) { @@ -11064,9 +11048,6 @@ void SOLDIERTYPE::BeginSoldierClimbDownRoof( void ) } else this->EVENT_InitNewSoldierAnim( JUMPDOWNWALL, 0, FALSE ); -#else - this->EVENT_InitNewSoldierAnim( JUMPDOWNWALL, 0, FALSE ); -#endif this->InternalReceivingSoldierCancelServices( FALSE ); this->InternalGivingSoldierCancelServices( FALSE ); @@ -12330,7 +12311,6 @@ void SOLDIERTYPE::EVENT_SoldierBeginPunchAttack( INT32 sGridNo, UINT8 ubDirectio } else { -#ifdef ENABLE_ZOMBIES // Flugente: civilians can be zombies too, but they do not have a 'punch' animation. Simple fix: They 'attack' without animation... // CHECK IF WE CAN DO THIS ANIMATION! if ( this->IsZombie( ) && IsAnimationValidForBodyType( this, PUNCH ) == FALSE ) @@ -12397,13 +12377,10 @@ void SOLDIERTYPE::EVENT_SoldierBeginPunchAttack( INT32 sGridNo, UINT8 ubDirectio } else { -#endif // Flugente: zombies do not kick BOOLEAN nokick = FALSE; -#ifdef ENABLE_ZOMBIES if ( this->IsZombie( ) ) nokick = TRUE; -#endif // Look at stance of target switch ( gAnimControl[pTSoldier->usAnimState].ubEndHeight ) @@ -12547,9 +12524,7 @@ void SOLDIERTYPE::EVENT_SoldierBeginPunchAttack( INT32 sGridNo, UINT8 ubDirectio } break; -#ifdef ENABLE_ZOMBIES } -#endif } } @@ -14543,8 +14518,6 @@ INT32 SOLDIERTYPE::GetDamageResistance( BOOLEAN fAutoResolve, BOOLEAN fCalcBreat resistance += gGameExternalOptions.sEnemyRegularDamageResistance; else if ( this->ubSoldierClass == SOLDIER_CLASS_ELITE && gGameExternalOptions.sEnemyEliteDamageResistance != 0 ) resistance += gGameExternalOptions.sEnemyEliteDamageResistance; - -#ifdef ENABLE_ZOMBIES else if ( IsZombie( ) ) { if ( fCalcBreathLoss ) @@ -14552,7 +14525,6 @@ INT32 SOLDIERTYPE::GetDamageResistance( BOOLEAN fAutoResolve, BOOLEAN fCalcBreat else resistance += gGameExternalOptions.sEnemyZombieDamageResistance; } -#endif } ////////////////////////////////////////////////////////////////////////////////////// @@ -14652,11 +14624,7 @@ INT16 SOLDIERTYPE::GetSoldierCriticalDamageBonus( void ) BOOLEAN SOLDIERTYPE::IsZombie( void ) { -#ifdef ENABLE_ZOMBIES return(ubSoldierClass == SOLDIER_CLASS_ZOMBIE); -#else - return FALSE; -#endif } // reset the extra stat variables diff --git a/Tactical/Soldier Create.cpp b/Tactical/Soldier Create.cpp index fe3a613f..45b65599 100644 --- a/Tactical/Soldier Create.cpp +++ b/Tactical/Soldier Create.cpp @@ -2399,9 +2399,7 @@ void CreateDetailedPlacementGivenBasicPlacementInfo( SOLDIERCREATE_STRUCT *pp, B switch( ubSoldierClass ) { case SOLDIER_CLASS_ADMINISTRATOR: -#ifdef ENABLE_ZOMBIES case SOLDIER_CLASS_ZOMBIE: -#endif pp->bExpLevel = (INT8) 2 + bExpLevelModifier; break; case SOLDIER_CLASS_ARMY: @@ -2553,21 +2551,18 @@ void CreateDetailedPlacementGivenBasicPlacementInfo( SOLDIERCREATE_STRUCT *pp, B //If it is a detailed placement, don't do this yet, as detailed placements may have their //own equipment. DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("CreateDetailedPlacementGivenBasicPlacementInfo: generate random equipment")); -#ifdef ENABLE_ZOMBIES + if( !bp->fDetailedPlacement && ubSoldierClass != SOLDIER_CLASS_NONE && ubSoldierClass != SOLDIER_CLASS_CREATURE && ubSoldierClass != SOLDIER_CLASS_MINER && ubSoldierClass != SOLDIER_CLASS_ZOMBIE ) -#else - if( !bp->fDetailedPlacement && ubSoldierClass != SOLDIER_CLASS_NONE && ubSoldierClass != SOLDIER_CLASS_CREATURE && ubSoldierClass != SOLDIER_CLASS_MINER ) -#endif { - GenerateRandomEquipment( pp, ubSoldierClass, bp->bRelativeEquipmentLevel); + GenerateRandomEquipment( pp, ubSoldierClass, bp->bRelativeEquipmentLevel); - // Flugente testing: militia get equipment in a different way - if ( pp->bTeam == MILITIA_TEAM && sX > 0 && sX < 17 && sY > 0 && sY < 17 ) - { - INT8 sZ = gbWorldSectorZ > -1 ? gbWorldSectorZ : 0; + // Flugente testing: militia get equipment in a different way + if ( pp->bTeam == MILITIA_TEAM && sX > 0 && sX < 17 && sY > 0 && sY < 17 ) + { + INT8 sZ = gbWorldSectorZ > -1 ? gbWorldSectorZ : 0; - TakeMilitiaEquipmentfromSector(sX, sY, sZ, pp, ubSoldierClass); - } + TakeMilitiaEquipmentfromSector(sX, sY, sZ, pp, ubSoldierClass); + } } DecideToAssignSniperOrders(pp); @@ -2739,11 +2734,7 @@ void CreateDetailedPlacementGivenStaticDetailedPlacementAndBasicPlacementInfo( // ReplaceExtendedGuns( pp, bp->ubSoldierClass ); //} -#ifdef ENABLE_ZOMBIES if( bp->ubSoldierClass != SOLDIER_CLASS_NONE && bp->ubSoldierClass != SOLDIER_CLASS_CREATURE && bp->ubSoldierClass != SOLDIER_CLASS_MINER && bp->ubSoldierClass != SOLDIER_CLASS_ZOMBIE ) -#else - if( bp->ubSoldierClass != SOLDIER_CLASS_NONE && bp->ubSoldierClass != SOLDIER_CLASS_CREATURE && bp->ubSoldierClass != SOLDIER_CLASS_MINER ) -#endif { GenerateRandomEquipment( pp, bp->ubSoldierClass, bp->bRelativeEquipmentLevel); @@ -2946,12 +2937,9 @@ SOLDIERTYPE* ReserveTacticalSoldierForAutoresolve( UINT8 ubSoldierClass ) //returns the pointer to that soldier. This is used when copying the exact status of //all remaining enemy troops (or creatures) to finish the battle in autoresolve. To //signify that the troop has already been reserved, we simply set their gridno to NOWHERE. -#ifdef ENABLE_ZOMBIES if( ubSoldierClass != SOLDIER_CLASS_CREATURE && ubSoldierClass != SOLDIER_CLASS_ZOMBIE ) -#else - if( ubSoldierClass != SOLDIER_CLASS_CREATURE ) -#endif - { //use the enemy team + { + //use the enemy team iStart = gTacticalStatus.Team[ ENEMY_TEAM ].bFirstID; iEnd = gTacticalStatus.Team[ ENEMY_TEAM ].bLastID; } @@ -3171,41 +3159,39 @@ SOLDIERTYPE* TacticalCreateJeep() return( pSoldier ); } -#ifdef ENABLE_ZOMBIES - //USED BY STRATEGIC AI and AUTORESOLVE - SOLDIERTYPE* TacticalCreateZombie() +//USED BY STRATEGIC AI and AUTORESOLVE +SOLDIERTYPE* TacticalCreateZombie() +{ + BASIC_SOLDIERCREATE_STRUCT bp; + SOLDIERCREATE_STRUCT pp; + UINT8 ubID; + SOLDIERTYPE * pSoldier; + + if( guiCurrentScreen == AUTORESOLVE_SCREEN && !gfPersistantPBI ) { - BASIC_SOLDIERCREATE_STRUCT bp; - SOLDIERCREATE_STRUCT pp; - UINT8 ubID; - SOLDIERTYPE * pSoldier; - - if( guiCurrentScreen == AUTORESOLVE_SCREEN && !gfPersistantPBI ) - { - pSoldier = ReserveTacticalSoldierForAutoresolve( SOLDIER_CLASS_ZOMBIE ); - if( pSoldier ) return pSoldier; - } - - memset( &bp, 0, sizeof( BASIC_SOLDIERCREATE_STRUCT ) ); - RandomizeRelativeLevel( &( bp.bRelativeAttributeLevel ), SOLDIER_CLASS_ZOMBIE ); - RandomizeRelativeLevel( &( bp.bRelativeEquipmentLevel ), SOLDIER_CLASS_ZOMBIE ); - bp.bTeam = CREATURE_TEAM;//ZOMBIE_TEAM; - bp.bOrders = SEEKENEMY; - bp.bAttitude = AGGRESSIVE; - bp.ubBodyType = -1; - bp.ubSoldierClass = SOLDIER_CLASS_ZOMBIE; - CreateDetailedPlacementGivenBasicPlacementInfo( &pp, &bp ); - pSoldier = TacticalCreateSoldier( &pp, &ubID ); - if ( pSoldier ) - { - // send soldier to centre of map, roughly - pSoldier->aiData.sNoiseGridno = (CENTRAL_GRIDNO + ( Random( CENTRAL_RADIUS * 2 + 1 ) - CENTRAL_RADIUS ) + ( Random( CENTRAL_RADIUS * 2 + 1 ) - CENTRAL_RADIUS ) * WORLD_COLS); - pSoldier->aiData.ubNoiseVolume = MAX_MISC_NOISE_DURATION; - } - - return( pSoldier ); + pSoldier = ReserveTacticalSoldierForAutoresolve( SOLDIER_CLASS_ZOMBIE ); + if( pSoldier ) return pSoldier; } -#endif + + memset( &bp, 0, sizeof( BASIC_SOLDIERCREATE_STRUCT ) ); + RandomizeRelativeLevel( &( bp.bRelativeAttributeLevel ), SOLDIER_CLASS_ZOMBIE ); + RandomizeRelativeLevel( &( bp.bRelativeEquipmentLevel ), SOLDIER_CLASS_ZOMBIE ); + bp.bTeam = CREATURE_TEAM;//ZOMBIE_TEAM; + bp.bOrders = SEEKENEMY; + bp.bAttitude = AGGRESSIVE; + bp.ubBodyType = -1; + bp.ubSoldierClass = SOLDIER_CLASS_ZOMBIE; + CreateDetailedPlacementGivenBasicPlacementInfo( &pp, &bp ); + pSoldier = TacticalCreateSoldier( &pp, &ubID ); + if ( pSoldier ) + { + // send soldier to centre of map, roughly + pSoldier->aiData.sNoiseGridno = (CENTRAL_GRIDNO + ( Random( CENTRAL_RADIUS * 2 + 1 ) - CENTRAL_RADIUS ) + ( Random( CENTRAL_RADIUS * 2 + 1 ) - CENTRAL_RADIUS ) * WORLD_COLS); + pSoldier->aiData.ubNoiseVolume = MAX_MISC_NOISE_DURATION; + } + + return( pSoldier ); +} SOLDIERTYPE* ReserveTacticalMilitiaSoldierForAutoresolve( UINT8 ubSoldierClass ) { @@ -5085,10 +5071,8 @@ BOOLEAN AssignTraitsToSoldier( SOLDIERTYPE *pSoldier, SOLDIERCREATE_STRUCT *pCre iChance = 10 + ubProgress / 4; // 10-35% chance else if ( ubSolClass == SOLDIER_CLASS_ADMINISTRATOR || ubSolClass == SOLDIER_CLASS_GREEN_MILITIA ) iChance = ubProgress / 4; // 0-25% chance -#ifdef ENABLE_ZOMBIES else if ( ubSolClass == SOLDIER_CLASS_ZOMBIE ) iChance = 100; // 100% chance -#endif if ( foundHtH ) // if found brass knuckless, increase the chance (doesn't happen often) iChance += 35; @@ -5132,10 +5116,8 @@ BOOLEAN AssignTraitsToSoldier( SOLDIERTYPE *pSoldier, SOLDIERCREATE_STRUCT *pCre iChance = 25 + ubProgress * 2 / 5; // 25-65% chance else if ( ubSolClass == SOLDIER_CLASS_ADMINISTRATOR || ubSolClass == SOLDIER_CLASS_GREEN_MILITIA ) iChance = 10 + ubProgress * 2 / 5; // 10-50% chance -#ifdef ENABLE_ZOMBIES else if ( ubSolClass == SOLDIER_CLASS_ZOMBIE ) iChance = 100; // 100% chance -#endif // modify the chance by preset ini setting if ( gGameExternalOptions.bAssignedTraitsRarity != 0 ) diff --git a/Tactical/Soldier Create.h b/Tactical/Soldier Create.h index 999142d1..7ee08541 100644 --- a/Tactical/Soldier Create.h +++ b/Tactical/Soldier Create.h @@ -439,9 +439,7 @@ SOLDIERTYPE* TacticalCreateSoldier( SOLDIERCREATE_STRUCT *pCreateStruct, UINT8 * SOLDIERTYPE* TacticalCreateAdministrator(); SOLDIERTYPE* TacticalCreateEliteEnemy(); SOLDIERTYPE* TacticalCreateArmyTroop(); -#ifdef ENABLE_ZOMBIES - SOLDIERTYPE* TacticalCreateZombie(); // Flugente Zombies -#endif +SOLDIERTYPE* TacticalCreateZombie(); // Flugente Zombies SOLDIERTYPE* TacticalCreateMilitia( UINT8 ubMilitiaClass, INT16 sX, INT16 sY ); // Flugente: added sector coordinates SOLDIERTYPE* TacticalCreateCreature( INT8 bCreatureBodyType ); SOLDIERTYPE* TacticalCreateEnemyTank(); diff --git a/Tactical/Tactical Turns.cpp b/Tactical/Tactical Turns.cpp index c3aa97cc..a037ec5f 100644 --- a/Tactical/Tactical Turns.cpp +++ b/Tactical/Tactical Turns.cpp @@ -277,13 +277,11 @@ void HandleTacticalEndTurn( ) // Flugente: Cool down/decay all items not in a soldier's inventory CoolDownWorldItems(); -#ifdef ENABLE_ZOMBIES // Flugente: raise zombies if in gamescreen and option set if ( guiCurrentScreen == GAME_SCREEN ) { RaiseZombies(); } -#endif } diff --git a/Tactical/TeamTurns.cpp b/Tactical/TeamTurns.cpp index d800c47c..0432d458 100644 --- a/Tactical/TeamTurns.cpp +++ b/Tactical/TeamTurns.cpp @@ -477,13 +477,11 @@ void EndTurnEvents( void ) // Flugente: Cool down/decay all items not in a soldier's inventory CoolDownWorldItems( ); -#ifdef ENABLE_ZOMBIES // Flugente: raise zombies if in gamescreen and option set if ( guiCurrentScreen == GAME_SCREEN ) { RaiseZombies(); } -#endif // decay AI warning values from corpses DecayRottingCorpseAIWarnings(); diff --git a/Tactical/opplist.cpp b/Tactical/opplist.cpp index 0ce4276e..2846ed38 100644 --- a/Tactical/opplist.cpp +++ b/Tactical/opplist.cpp @@ -4859,10 +4859,7 @@ void DebugSoldierPage4( ) case SOLDIER_CLASS_REG_MILITIA: gprintf( 320, LINE_HEIGHT * ubLine, L"(Reg Militia)" ); break; case SOLDIER_CLASS_ELITE_MILITIA: gprintf( 320, LINE_HEIGHT * ubLine, L"(Elite Militia)" ); break; case SOLDIER_CLASS_MINER: gprintf( 320, LINE_HEIGHT * ubLine, L"(Miner)" ); break; - -#ifdef ENABLE_ZOMBIES case SOLDIER_CLASS_ZOMBIE: gprintf( 320, LINE_HEIGHT * ubLine, L"(Zombie)" ); break; -#endif default: break; //don't care (don't write anything) } diff --git a/TacticalAI/AIInternals.h b/TacticalAI/AIInternals.h index 38ebd7f7..67672e12 100644 --- a/TacticalAI/AIInternals.h +++ b/TacticalAI/AIInternals.h @@ -213,11 +213,9 @@ void CreatureCall( SOLDIERTYPE * pCaller ); INT8 CreatureDecideAction( SOLDIERTYPE * pCreature ); void CreatureDecideAlertStatus( SOLDIERTYPE *pCreature ); -#ifdef ENABLE_ZOMBIES - // Flugente: Zombie AI - INT8 ZombieDecideAction( SOLDIERTYPE * pZombie ); - void ZombieDecideAlertStatus( SOLDIERTYPE *pZombie ); -#endif +// Flugente: Zombie AI +INT8 ZombieDecideAction( SOLDIERTYPE * pZombie ); +void ZombieDecideAlertStatus( SOLDIERTYPE *pZombie ); // a variant of ClosestSeenOpponent(...), that allows to find enemies on a roof INT32 ClosestSeenOpponentWithRoof(SOLDIERTYPE *pSoldier, INT32 * psGridNo, INT8 * pbLevel); diff --git a/TacticalAI/AIMain.cpp b/TacticalAI/AIMain.cpp index 51490959..57e36fd5 100644 --- a/TacticalAI/AIMain.cpp +++ b/TacticalAI/AIMain.cpp @@ -1284,8 +1284,6 @@ INT16 ActionInProgress(SOLDIERTYPE *pSoldier) void TurnBasedHandleNPCAI(SOLDIERTYPE *pSoldier) { -#ifdef ENABLE_ZOMBIES - // added by Flugente: static pointers, used to break out of an endless circles (currently only used for zombie AI) static SOLDIERTYPE* pLastDecisionSoldier = NULL; static INT16 lastdecisioncount = 0; @@ -1312,7 +1310,7 @@ void TurnBasedHandleNPCAI(SOLDIERTYPE *pSoldier) pLastDecisionSoldier = pSoldier; lastdecisioncount = 0; } -#endif + // yikes, this shouldn't occur! we should be trying to finish our move! // pSoldier->flags.fNoAPToFinishMove = FALSE; @@ -1475,9 +1473,7 @@ void TurnBasedHandleNPCAI(SOLDIERTYPE *pSoldier) { // turn based... abort this guy's turn EndAIGuysTurn( pSoldier ); -#ifdef ENABLE_ZOMBIES lastdecisioncount = 0; -#endif } } else diff --git a/TacticalAI/AIUtils.cpp b/TacticalAI/AIUtils.cpp index 8cfe6c7b..10e0c042 100644 --- a/TacticalAI/AIUtils.cpp +++ b/TacticalAI/AIUtils.cpp @@ -2716,11 +2716,9 @@ UINT8 SoldierDifficultyLevel( SOLDIERTYPE * pSoldier ) bDifficulty = 4; break; -#ifdef ENABLE_ZOMBIES case SOLDIER_CLASS_ZOMBIE: bDifficulty = bDifficultyBase; break; -#endif default: if (pSoldier->bTeam == CREATURE_TEAM) diff --git a/TacticalAI/DecideAction.cpp b/TacticalAI/DecideAction.cpp index fa50f74a..8bdc3366 100644 --- a/TacticalAI/DecideAction.cpp +++ b/TacticalAI/DecideAction.cpp @@ -58,12 +58,10 @@ UINT32 guiRedSeekCounter = 0, guiRedHelpCounter = 0; guiRedHideCounter = 0; #define CENTER_OF_RING 11237//dnl!!! -#ifdef ENABLE_ZOMBIES - INT8 ZombieDecideActionGreen(SOLDIERTYPE *pSoldier); - INT8 ZombieDecideActionYellow(SOLDIERTYPE *pSoldier); - INT8 ZombieDecideActionRed(SOLDIERTYPE *pSoldier, UINT8 ubUnconsciousOK); - INT8 ZombieDecideActionBlack(SOLDIERTYPE *pSoldier); -#endif +INT8 ZombieDecideActionGreen(SOLDIERTYPE *pSoldier); +INT8 ZombieDecideActionYellow(SOLDIERTYPE *pSoldier); +INT8 ZombieDecideActionRed(SOLDIERTYPE *pSoldier, UINT8 ubUnconsciousOK); +INT8 ZombieDecideActionBlack(SOLDIERTYPE *pSoldier); void DoneScheduleAction( SOLDIERTYPE * pSoldier ) { @@ -6700,561 +6698,713 @@ void DecideAlertStatus( SOLDIERTYPE *pSoldier ) } +// ------------------------------ ZOMBIE AI -------------------------- +INT8 ZombieDecideActionGreen(SOLDIERTYPE *pSoldier) +{ + DOUBLE iChance, iSneaky = 10; + INT8 bInWater; +#ifdef DEBUGDECISIONS + STR16 tempstr; +#endif + DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("DecideActionGreen, orders = %d",pSoldier->aiData.bOrders)); -#ifdef ENABLE_ZOMBIES - // ------------------------------ ZOMBIE AI -------------------------- - INT8 ZombieDecideActionGreen(SOLDIERTYPE *pSoldier) + gubNPCPathCount = 0; + + if( !gGameExternalOptions.fEnemyTanksCanMoveInTactical && TANK( pSoldier ) ) { - DOUBLE iChance, iSneaky = 10; - INT8 bInWater; - #ifdef DEBUGDECISIONS - STR16 tempstr; - #endif + return( AI_ACTION_NONE ); + } - DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("DecideActionGreen, orders = %d",pSoldier->aiData.bOrders)); + bInWater = Water( pSoldier->sGridNo ); - gubNPCPathCount = 0; - - if( !gGameExternalOptions.fEnemyTanksCanMoveInTactical && TANK( pSoldier ) ) - { - return( AI_ACTION_NONE ); - } - - bInWater = Water( pSoldier->sGridNo ); - - if ( bInWater && PreRandom( 2 ) ) - { - // don't do nuttin! - return( AI_ACTION_NONE ); - } + if ( bInWater && PreRandom( 2 ) ) + { + // don't do nuttin! + return( AI_ACTION_NONE ); + } - // if real-time, and not in the way, do nothing 90% of the time (for GUARDS!) - // unless in water (could've started there), then we better swim to shore! + // if real-time, and not in the way, do nothing 90% of the time (for GUARDS!) + // unless in water (could've started there), then we better swim to shore! - //////////////////////////////////////////////////////////////////////////// - // POINT PATROL: move towards next point unless getting a bit winded - //////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////// + // POINT PATROL: move towards next point unless getting a bit winded + //////////////////////////////////////////////////////////////////////////// - // this takes priority over water/gas checks, so that point patrol WILL work - // from island to island, and through gas covered areas, too - if ((pSoldier->aiData.bOrders == POINTPATROL) && (pSoldier->bBreath >= 25)) + // this takes priority over water/gas checks, so that point patrol WILL work + // from island to island, and through gas covered areas, too + if ((pSoldier->aiData.bOrders == POINTPATROL) && (pSoldier->bBreath >= 25)) + { + if (PointPatrolAI(pSoldier)) { - if (PointPatrolAI(pSoldier)) + if (!gfTurnBasedAI) { - if (!gfTurnBasedAI) - { - // wait after this... - pSoldier->aiData.bNextAction = AI_ACTION_WAIT; - pSoldier->aiData.usNextActionData = RealtimeDelay( pSoldier ); - } - return(AI_ACTION_POINT_PATROL); + // wait after this... + pSoldier->aiData.bNextAction = AI_ACTION_WAIT; + pSoldier->aiData.usNextActionData = RealtimeDelay( pSoldier ); + } + return(AI_ACTION_POINT_PATROL); + } + else + { + // Reset path count to avoid dedlok + gubNPCPathCount = 0; + } + } + + if ((pSoldier->aiData.bOrders == RNDPTPATROL) && (pSoldier->bBreath >=25)) + { + if (RandomPointPatrolAI(pSoldier)) + { + if (!gfTurnBasedAI) + { + // wait after this... + pSoldier->aiData.bNextAction = AI_ACTION_WAIT; + pSoldier->aiData.usNextActionData = RealtimeDelay( pSoldier ); + } + return(AI_ACTION_POINT_PATROL); + } + else + { + // Reset path count to avoid dedlok + gubNPCPathCount = 0; + } + } + + //////////////////////////////////////////////////////////////////////////// + // WHEN LEFT IN WATER OR GAS, GO TO NEAREST REACHABLE SPOT OF UNGASSED LAND + //////////////////////////////////////////////////////////////////////////// + + DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("ZombieDecideActionGreen: get out of water and gas")); + + if ( bInWater ) + { + pSoldier->aiData.usActionData = FindNearestUngassedLand(pSoldier); + + if (!TileIsOutOfBounds(pSoldier->aiData.usActionData)) + { +#ifdef DEBUGDECISIONS + sprintf(tempstr,"%s - SEEKING NEAREST UNGASSED LAND at grid %d",pSoldier->name,pSoldier->aiData.usActionData); + AIPopMessage(tempstr); +#endif + + return(AI_ACTION_LEAVE_WATER_GAS); + } + } + + //////////////////////////////////////////////////////////////////////// + // REST IF RUNNING OUT OF BREATH + //////////////////////////////////////////////////////////////////////// + + DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("ZombieDecideActionGreen: rest if running out of breath")); + // if our breath is running a bit low, and we're not in the way or in water + if ((pSoldier->bBreath < 25) && !bInWater) + { + // take a breather for gods sake! + // for realtime, AI will use a standard wait set outside of here + pSoldier->aiData.usActionData = NOWHERE; + return(AI_ACTION_NONE); + } + + + //////////////////////////////////////////////////////////////////////////// + // CLIMB A BUILDING + //////////////////////////////////////////////////////////////////////////// + + if ( gGameExternalOptions.fZombieCanClimb && pSoldier->aiData.bLastAction != AI_ACTION_CLIMB_ROOF && !is_networked ) + { + iChance = 10 + pSoldier->aiData.bBypassToGreen; + + // set base chance and maximum seeking distance according to orders + switch (pSoldier->aiData.bOrders) + { + case STATIONARY: iChance *= 0; break; + case ONGUARD: iChance += 10; break; + case ONCALL: break; + case CLOSEPATROL: iChance += -20; break; + case RNDPTPATROL: + case POINTPATROL: iChance = -30; break; + case FARPATROL: iChance += -40; break; + case SEEKENEMY: iChance += -30; break; + case SNIPER: iChance += 70; break; + } + + // modify for attitude + switch (pSoldier->aiData.bAttitude) + { + case DEFENSIVE: iChance *= 1.5; break; + case BRAVESOLO: iChance /= 2; break; + case BRAVEAID: iChance /= 2; break; + case CUNNINGSOLO: iChance *= 1; break; + case CUNNINGAID: iChance /= 1; break; + case AGGRESSIVE: iChance /= 3; break; + case ATTACKSLAYONLY: break; + } + + // This is the chance that we want to be on the roof. If already there, invert the chance to see if we want back + // down + if (pSoldier->pathing.bLevel > 0) + { + iChance = 100 - iChance; + } + + if ((INT16) PreRandom(100) < iChance) + { + BOOLEAN fUp = FALSE; + if ( pSoldier->pathing.bLevel == 0 ) + { + fUp = TRUE; + } + else if (pSoldier->pathing.bLevel > 0 ) + { + fUp = FALSE; + } + + if ( CanClimbFromHere ( pSoldier, fUp ) ) + { + DebugMsg ( TOPIC_JA2AI , DBG_LEVEL_3 , String("Zombie %d is climbing roof",pSoldier->ubID) ); + return( AI_ACTION_CLIMB_ROOF ); } else { - // Reset path count to avoid dedlok - gubNPCPathCount = 0; - } - } - - if ((pSoldier->aiData.bOrders == RNDPTPATROL) && (pSoldier->bBreath >=25)) - { - if (RandomPointPatrolAI(pSoldier)) - { - if (!gfTurnBasedAI) + pSoldier->aiData.usActionData = FindClosestClimbPoint(pSoldier, fUp ); + // Added the check here because sniper militia who are locked inside of a building without keys + // will still have a >100% chance to want to climb, which means an infinite loop. In fact, any + // time a move is desired, there probably also will be a need to check for a path. + if ( !TileIsOutOfBounds(pSoldier->aiData.usActionData) && + LegalNPCDestination(pSoldier,pSoldier->aiData.usActionData,ENSURE_PATH,WATEROK, 0 )) { - // wait after this... - pSoldier->aiData.bNextAction = AI_ACTION_WAIT; - pSoldier->aiData.usNextActionData = RealtimeDelay( pSoldier ); + return( AI_ACTION_MOVE_TO_CLIMB ); } - return(AI_ACTION_POINT_PATROL); - } - else - { - // Reset path count to avoid dedlok - gubNPCPathCount = 0; } } + } - //////////////////////////////////////////////////////////////////////////// - // WHEN LEFT IN WATER OR GAS, GO TO NEAREST REACHABLE SPOT OF UNGASSED LAND - //////////////////////////////////////////////////////////////////////////// - DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("ZombieDecideActionGreen: get out of water and gas")); + //////////////////////////////////////////////////////////////////////////// + // RANDOM PATROL: determine % chance to start a new patrol route + //////////////////////////////////////////////////////////////////////////// + if (!gubNPCPathCount) // try to limit pathing in Green AI + { + iChance = 25 + pSoldier->aiData.bBypassToGreen; - if ( bInWater ) + // set base chance according to orders + switch (pSoldier->aiData.bOrders) { - pSoldier->aiData.usActionData = FindNearestUngassedLand(pSoldier); + case STATIONARY: iChance += -20; break; + case ONGUARD: iChance += -15; break; + case ONCALL: break; + case CLOSEPATROL: iChance += +15; break; + case RNDPTPATROL: + case POINTPATROL: iChance = 0; break; + case FARPATROL: iChance += +25; break; + case SEEKENEMY: iChance += -10; break; + case SNIPER: iChance += -10; break; + } + + // modify chance of patrol (and whether it's a sneaky one) by attitude + switch (pSoldier->aiData.bAttitude) + { + case DEFENSIVE: iChance += -10; break; + case BRAVESOLO: iChance += 5; break; + case BRAVEAID: break; + case CUNNINGSOLO: iChance += 5; iSneaky += 10; break; + case CUNNINGAID: iSneaky += 5; break; + case AGGRESSIVE: iChance += 10; iSneaky += -5; break; + case ATTACKSLAYONLY: iChance += 10; iSneaky += -5; break; + } + + // if we're in water with land miles (> 25 tiles) away, + // OR if we roll under the chance calculated + if (bInWater || ((INT16) PreRandom(100) < iChance)) + { + pSoldier->aiData.usActionData = RandDestWithinRange(pSoldier); + if (!TileIsOutOfBounds(pSoldier->aiData.usActionData)) { - #ifdef DEBUGDECISIONS - sprintf(tempstr,"%s - SEEKING NEAREST UNGASSED LAND at grid %d",pSoldier->name,pSoldier->aiData.usActionData); + pSoldier->aiData.usActionData = GoAsFarAsPossibleTowards( pSoldier, pSoldier->aiData.usActionData, AI_ACTION_RANDOM_PATROL ); + } + + if (!TileIsOutOfBounds(pSoldier->aiData.usActionData)) + { +#ifdef DEBUGDECISIONS + sprintf(tempstr,"%s - RANDOM PATROL to grid %d",pSoldier->name,pSoldier->aiData.usActionData); AIPopMessage(tempstr); - #endif +#endif - return(AI_ACTION_LEAVE_WATER_GAS); + if (!gfTurnBasedAI) + { + // wait after this... + pSoldier->aiData.bNextAction = AI_ACTION_WAIT; + pSoldier->aiData.usNextActionData = RealtimeDelay( pSoldier ); + } + return(AI_ACTION_RANDOM_PATROL); } } + } - //////////////////////////////////////////////////////////////////////// - // REST IF RUNNING OUT OF BREATH - //////////////////////////////////////////////////////////////////////// + if (!gubNPCPathCount) // try to limit pathing in Green AI + { + //////////////////////////////////////////////////////////////////////////// + // SEEK FRIEND: determine %chance for man to pay a friendly visit + //////////////////////////////////////////////////////////////////////////// - DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("ZombieDecideActionGreen: rest if running out of breath")); - // if our breath is running a bit low, and we're not in the way or in water - if ((pSoldier->bBreath < 25) && !bInWater) + iChance = 25 + pSoldier->aiData.bBypassToGreen; + + // set base chance and maximum seeking distance according to orders + switch (pSoldier->aiData.bOrders) { - // take a breather for gods sake! - // for realtime, AI will use a standard wait set outside of here - pSoldier->aiData.usActionData = NOWHERE; - return(AI_ACTION_NONE); + case STATIONARY: iChance += -20; break; + case ONGUARD: iChance += -15; break; + case ONCALL: break; + case CLOSEPATROL: iChance += +10; break; + case RNDPTPATROL: + case POINTPATROL: iChance = -10; break; + case FARPATROL: iChance += +20; break; + case SEEKENEMY: iChance += -10; break; + case SNIPER: iChance += -10; break; } - - //////////////////////////////////////////////////////////////////////////// - // CLIMB A BUILDING - //////////////////////////////////////////////////////////////////////////// - - if ( gGameExternalOptions.fZombieCanClimb && pSoldier->aiData.bLastAction != AI_ACTION_CLIMB_ROOF && !is_networked ) + // modify for attitude + switch (pSoldier->aiData.bAttitude) { - iChance = 10 + pSoldier->aiData.bBypassToGreen; - - // set base chance and maximum seeking distance according to orders - switch (pSoldier->aiData.bOrders) + case DEFENSIVE: break; + case BRAVESOLO: iChance /= 2; break; // loners + case BRAVEAID: iChance += 10; break; // friendly + case CUNNINGSOLO: iChance /= 2; break; // loners + case CUNNINGAID: iChance += 10; break; // friendly + case AGGRESSIVE: break; + case ATTACKSLAYONLY: break; + } + + if ((INT16) PreRandom(100) < iChance) + { + if (RandomFriendWithin(pSoldier)) { - case STATIONARY: iChance *= 0; break; - case ONGUARD: iChance += 10; break; - case ONCALL: break; - case CLOSEPATROL: iChance += -20; break; - case RNDPTPATROL: - case POINTPATROL: iChance = -30; break; - case FARPATROL: iChance += -40; break; - case SEEKENEMY: iChance += -30; break; - case SNIPER: iChance += 70; break; - } + if ( pSoldier->aiData.usActionData == GoAsFarAsPossibleTowards( pSoldier, pSoldier->aiData.usActionData, AI_ACTION_SEEK_FRIEND ) ) + { - // modify for attitude - switch (pSoldier->aiData.bAttitude) - { - case DEFENSIVE: iChance *= 1.5; break; - case BRAVESOLO: iChance /= 2; break; - case BRAVEAID: iChance /= 2; break; - case CUNNINGSOLO: iChance *= 1; break; - case CUNNINGAID: iChance /= 1; break; - case AGGRESSIVE: iChance /= 3; break; - case ATTACKSLAYONLY: break; - } - - // This is the chance that we want to be on the roof. If already there, invert the chance to see if we want back - // down - if (pSoldier->pathing.bLevel > 0) - { - iChance = 100 - iChance; - } +#ifdef DEBUGDECISIONS + sprintf(tempstr,"%s - SEEK FRIEND at grid %d",pSoldier->name,pSoldier->aiData.usActionData); + AIPopMessage(tempstr); +#endif - if ((INT16) PreRandom(100) < iChance) - { - BOOLEAN fUp = FALSE; - if ( pSoldier->pathing.bLevel == 0 ) - { - fUp = TRUE; - } - else if (pSoldier->pathing.bLevel > 0 ) - { - fUp = FALSE; - } - - if ( CanClimbFromHere ( pSoldier, fUp ) ) - { - DebugMsg ( TOPIC_JA2AI , DBG_LEVEL_3 , String("Zombie %d is climbing roof",pSoldier->ubID) ); - return( AI_ACTION_CLIMB_ROOF ); - } - else - { - pSoldier->aiData.usActionData = FindClosestClimbPoint(pSoldier, fUp ); - // Added the check here because sniper militia who are locked inside of a building without keys - // will still have a >100% chance to want to climb, which means an infinite loop. In fact, any - // time a move is desired, there probably also will be a need to check for a path. - if ( !TileIsOutOfBounds(pSoldier->aiData.usActionData) && - LegalNPCDestination(pSoldier,pSoldier->aiData.usActionData,ENSURE_PATH,WATEROK, 0 )) + if ( !gfTurnBasedAI) { - return( AI_ACTION_MOVE_TO_CLIMB ); + // pause at the end of the walk! + pSoldier->aiData.bNextAction = AI_ACTION_WAIT; + pSoldier->aiData.usNextActionData = (UINT16) REALTIME_CIV_AI_DELAY; } + + return(AI_ACTION_SEEK_FRIEND); } } } + } + + //////////////////////////////////////////////////////////////////////////// + // LOOK AROUND: determine %chance for man to turn in place + //////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////// - // RANDOM PATROL: determine % chance to start a new patrol route - //////////////////////////////////////////////////////////////////////////// - if (!gubNPCPathCount) // try to limit pathing in Green AI + DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("ZombieDecideActionGreen: Soldier deciding to turn")); + if (!gfTurnBasedAI || GetAPsToLook( pSoldier ) <= pSoldier->bActionPoints) + { + // avoid 2 consecutive random turns in a row + if (pSoldier->aiData.bLastAction != AI_ACTION_CHANGE_FACING) { iChance = 25 + pSoldier->aiData.bBypassToGreen; // set base chance according to orders - switch (pSoldier->aiData.bOrders) - { - case STATIONARY: iChance += -20; break; - case ONGUARD: iChance += -15; break; - case ONCALL: break; - case CLOSEPATROL: iChance += +15; break; - case RNDPTPATROL: - case POINTPATROL: iChance = 0; break; - case FARPATROL: iChance += +25; break; - case SEEKENEMY: iChance += -10; break; - case SNIPER: iChance += -10; break; - } + if (pSoldier->aiData.bOrders == STATIONARY || pSoldier->aiData.bOrders == SNIPER) + iChance += 25; - // modify chance of patrol (and whether it's a sneaky one) by attitude - switch (pSoldier->aiData.bAttitude) - { - case DEFENSIVE: iChance += -10; break; - case BRAVESOLO: iChance += 5; break; - case BRAVEAID: break; - case CUNNINGSOLO: iChance += 5; iSneaky += 10; break; - case CUNNINGAID: iSneaky += 5; break; - case AGGRESSIVE: iChance += 10; iSneaky += -5; break; - case ATTACKSLAYONLY: iChance += 10; iSneaky += -5; break; - } - + if (pSoldier->aiData.bOrders == ONGUARD) + iChance += 20; - // if we're in water with land miles (> 25 tiles) away, - // OR if we roll under the chance calculated - if (bInWater || ((INT16) PreRandom(100) < iChance)) + if (pSoldier->aiData.bAttitude == DEFENSIVE) + iChance += 25; + + if ( pSoldier->aiData.bOrders == SNIPER && pSoldier->pathing.bLevel == 1) + iChance += 35; + + if ((INT16)PreRandom(100) < iChance) { - pSoldier->aiData.usActionData = RandDestWithinRange(pSoldier); - - if (!TileIsOutOfBounds(pSoldier->aiData.usActionData)) + // roll random directions (stored in actionData) until different from current + do { - pSoldier->aiData.usActionData = GoAsFarAsPossibleTowards( pSoldier, pSoldier->aiData.usActionData, AI_ACTION_RANDOM_PATROL ); - } - - if (!TileIsOutOfBounds(pSoldier->aiData.usActionData)) - { - #ifdef DEBUGDECISIONS - sprintf(tempstr,"%s - RANDOM PATROL to grid %d",pSoldier->name,pSoldier->aiData.usActionData); - AIPopMessage(tempstr); - #endif + // if man has a LEGAL dominant facing, and isn't facing it, he will turn + // back towards that facing 50% of the time here (normally just enemies) + if ((pSoldier->aiData.bDominantDir >= 0) && (pSoldier->aiData.bDominantDir <= 8) && + (pSoldier->ubDirection != pSoldier->aiData.bDominantDir) && PreRandom(2) && pSoldier->aiData.bOrders != SNIPER ) + { + pSoldier->aiData.usActionData = pSoldier->aiData.bDominantDir; + } + else + { + INT32 iNoiseValue; + BOOLEAN fClimb; + BOOLEAN fReachable; + INT32 sNoiseGridNo = MostImportantNoiseHeard(pSoldier,&iNoiseValue, &fClimb, &fReachable); + UINT8 ubNoiseDir; + + if (TileIsOutOfBounds(sNoiseGridNo) || + (ubNoiseDir = atan8(CenterX(pSoldier->sGridNo),CenterY(pSoldier->sGridNo),CenterX(sNoiseGridNo),CenterY(sNoiseGridNo)) + ) == pSoldier->ubDirection ) + + { + pSoldier->aiData.usActionData = PreRandom(8); + } + else + { + pSoldier->aiData.usActionData = ubNoiseDir; + } + } + } while (pSoldier->aiData.usActionData == pSoldier->ubDirection); - if (!gfTurnBasedAI) + +#ifdef DEBUGDECISIONS + sprintf(tempstr,"%s - TURNS to face direction %d",pSoldier->name,pSoldier->aiData.usActionData); + AIPopMessage(tempstr); +#endif + + DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("DecideActionGreen: Trying to turn - checking stance validity, sniper = %d",pSoldier->sniper)); + if ( pSoldier->InternalIsValidStance( (INT8) pSoldier->aiData.usActionData, gAnimControl[ pSoldier->usAnimState ].ubEndHeight ) ) + { + + if ( !gfTurnBasedAI ) { // wait after this... pSoldier->aiData.bNextAction = AI_ACTION_WAIT; pSoldier->aiData.usNextActionData = RealtimeDelay( pSoldier ); } - return(AI_ACTION_RANDOM_PATROL); - } - } - } - if (!gubNPCPathCount) // try to limit pathing in Green AI - { - //////////////////////////////////////////////////////////////////////////// - // SEEK FRIEND: determine %chance for man to pay a friendly visit - //////////////////////////////////////////////////////////////////////////// - - iChance = 25 + pSoldier->aiData.bBypassToGreen; - - // set base chance and maximum seeking distance according to orders - switch (pSoldier->aiData.bOrders) - { - case STATIONARY: iChance += -20; break; - case ONGUARD: iChance += -15; break; - case ONCALL: break; - case CLOSEPATROL: iChance += +10; break; - case RNDPTPATROL: - case POINTPATROL: iChance = -10; break; - case FARPATROL: iChance += +20; break; - case SEEKENEMY: iChance += -10; break; - case SNIPER: iChance += -10; break; - } - - // modify for attitude - switch (pSoldier->aiData.bAttitude) - { - case DEFENSIVE: break; - case BRAVESOLO: iChance /= 2; break; // loners - case BRAVEAID: iChance += 10; break; // friendly - case CUNNINGSOLO: iChance /= 2; break; // loners - case CUNNINGAID: iChance += 10; break; // friendly - case AGGRESSIVE: break; - case ATTACKSLAYONLY: break; - } - - if ((INT16) PreRandom(100) < iChance) - { - if (RandomFriendWithin(pSoldier)) - { - if ( pSoldier->aiData.usActionData == GoAsFarAsPossibleTowards( pSoldier, pSoldier->aiData.usActionData, AI_ACTION_SEEK_FRIEND ) ) - { - - #ifdef DEBUGDECISIONS - sprintf(tempstr,"%s - SEEK FRIEND at grid %d",pSoldier->name,pSoldier->aiData.usActionData); - AIPopMessage(tempstr); - #endif - - if ( !gfTurnBasedAI) - { - // pause at the end of the walk! - pSoldier->aiData.bNextAction = AI_ACTION_WAIT; - pSoldier->aiData.usNextActionData = (UINT16) REALTIME_CIV_AI_DELAY; - } - - return(AI_ACTION_SEEK_FRIEND); - } - } - } - } - - - //////////////////////////////////////////////////////////////////////////// - // LOOK AROUND: determine %chance for man to turn in place - //////////////////////////////////////////////////////////////////////////// - - DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("ZombieDecideActionGreen: Soldier deciding to turn")); - if (!gfTurnBasedAI || GetAPsToLook( pSoldier ) <= pSoldier->bActionPoints) - { - // avoid 2 consecutive random turns in a row - if (pSoldier->aiData.bLastAction != AI_ACTION_CHANGE_FACING) - { - iChance = 25 + pSoldier->aiData.bBypassToGreen; - - // set base chance according to orders - if (pSoldier->aiData.bOrders == STATIONARY || pSoldier->aiData.bOrders == SNIPER) - iChance += 25; - - if (pSoldier->aiData.bOrders == ONGUARD) - iChance += 20; - - if (pSoldier->aiData.bAttitude == DEFENSIVE) - iChance += 25; - - if ( pSoldier->aiData.bOrders == SNIPER && pSoldier->pathing.bLevel == 1) - iChance += 35; - - if ((INT16)PreRandom(100) < iChance) - { - // roll random directions (stored in actionData) until different from current - do - { - // if man has a LEGAL dominant facing, and isn't facing it, he will turn - // back towards that facing 50% of the time here (normally just enemies) - if ((pSoldier->aiData.bDominantDir >= 0) && (pSoldier->aiData.bDominantDir <= 8) && - (pSoldier->ubDirection != pSoldier->aiData.bDominantDir) && PreRandom(2) && pSoldier->aiData.bOrders != SNIPER ) - { - pSoldier->aiData.usActionData = pSoldier->aiData.bDominantDir; - } - else - { - INT32 iNoiseValue; - BOOLEAN fClimb; - BOOLEAN fReachable; - INT32 sNoiseGridNo = MostImportantNoiseHeard(pSoldier,&iNoiseValue, &fClimb, &fReachable); - UINT8 ubNoiseDir; - - if (TileIsOutOfBounds(sNoiseGridNo) || - (ubNoiseDir = atan8(CenterX(pSoldier->sGridNo),CenterY(pSoldier->sGridNo),CenterX(sNoiseGridNo),CenterY(sNoiseGridNo)) - ) == pSoldier->ubDirection ) - - { - pSoldier->aiData.usActionData = PreRandom(8); - } - else - { - pSoldier->aiData.usActionData = ubNoiseDir; - } - } - } while (pSoldier->aiData.usActionData == pSoldier->ubDirection); - - - #ifdef DEBUGDECISIONS - sprintf(tempstr,"%s - TURNS to face direction %d",pSoldier->name,pSoldier->aiData.usActionData); - AIPopMessage(tempstr); - #endif - - DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("DecideActionGreen: Trying to turn - checking stance validity, sniper = %d",pSoldier->sniper)); - if ( pSoldier->InternalIsValidStance( (INT8) pSoldier->aiData.usActionData, gAnimControl[ pSoldier->usAnimState ].ubEndHeight ) ) - { - - if ( !gfTurnBasedAI ) - { - // wait after this... - pSoldier->aiData.bNextAction = AI_ACTION_WAIT; - pSoldier->aiData.usNextActionData = RealtimeDelay( pSoldier ); - } - - DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("DecideActionGreen: Soldier is turning")); - return(AI_ACTION_CHANGE_FACING); - } - } - } - } - - - //////////////////////////////////////////////////////////////////////////// - // NONE: - //////////////////////////////////////////////////////////////////////////// - - // by default, if everything else fails, just stands in place without turning - // for realtime, regular AI guys will use a standard wait set outside of here - pSoldier->aiData.usActionData = NOWHERE; - return(AI_ACTION_NONE); - } - - INT8 ZombieDecideActionYellow(SOLDIERTYPE *pSoldier) - { - UINT8 ubNoiseDir; - INT32 sNoiseGridNo; - INT32 iNoiseValue; - INT32 iChance; - BOOLEAN fClimb; - BOOLEAN fReachable; - #ifdef DEBUGDECISIONS - STR16 tempstr; - #endif - - // determine the most important noise heard, and its relative value - sNoiseGridNo = MostImportantNoiseHeard(pSoldier,&iNoiseValue, &fClimb, &fReachable); - - if (TileIsOutOfBounds(sNoiseGridNo)) - { - // then we have no business being under YELLOW status any more! - #ifdef RECORDNET - fprintf(NetDebugFile,"\nDecideActionYellow: ERROR - No important noise known by guynum %d\n\n",pSoldier->ubID); - #endif - - #ifdef BETAVERSION - NumMessage("DecideActionYellow: ERROR - No important noise known by guynum ",pSoldier->ubID); - #endif - - return(AI_ACTION_NONE); - } - - //////////////////////////////////////////////////////////////////////////// - // LOOK AROUND TOWARD NOISE: determine %chance for man to turn towards noise - //////////////////////////////////////////////////////////////////////////// - - // determine direction from this soldier in which the noise lies - ubNoiseDir = atan8(CenterX(pSoldier->sGridNo),CenterY(pSoldier->sGridNo),CenterX(sNoiseGridNo),CenterY(sNoiseGridNo)); - - // if soldier is not already facing in that direction, - // and the noise source is close enough that it could possibly be seen - if ( !gfTurnBasedAI || GetAPsToLook( pSoldier ) <= pSoldier->bActionPoints ) - { - if ((pSoldier->ubDirection != ubNoiseDir) && PythSpacesAway(pSoldier->sGridNo,sNoiseGridNo) <= pSoldier->GetMaxDistanceVisible(sNoiseGridNo) ) - { - // set base chance according to orders - if ((pSoldier->aiData.bOrders == STATIONARY) || (pSoldier->aiData.bOrders == ONGUARD) ) - iChance = 50; - else // all other orders - iChance = 25; - - if (pSoldier->aiData.bAttitude == DEFENSIVE) - iChance += 15; - - - if ((INT16)PreRandom(100) < iChance && pSoldier->InternalIsValidStance( ubNoiseDir, gAnimControl[ pSoldier->usAnimState ].ubEndHeight ) ) - { - pSoldier->aiData.usActionData = ubNoiseDir; - #ifdef DEBUGDECISIONS - sprintf(tempstr,"%s - TURNS TOWARDS NOISE to face direction %d",pSoldier->name,pSoldier->aiData.usActionData); - AIPopMessage(tempstr); - #endif + DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("DecideActionGreen: Soldier is turning")); return(AI_ACTION_CHANGE_FACING); } } } + } - //////////////////////////////////////////////////////////////////////////// - // RADIO YELLOW ALERT: determine %chance to call others and report noise - //////////////////////////////////////////////////////////////////////////// - // if we have the action points remaining to RADIO - // (we never want NPCs to choose to radio if they would have to wait a turn) - if ( (pSoldier->bActionPoints >= APBPConstants[AP_RADIO]) && (gTacticalStatus.Team[pSoldier->bTeam].bMenInSector > 1) ) + //////////////////////////////////////////////////////////////////////////// + // NONE: + //////////////////////////////////////////////////////////////////////////// + + // by default, if everything else fails, just stands in place without turning + // for realtime, regular AI guys will use a standard wait set outside of here + pSoldier->aiData.usActionData = NOWHERE; + return(AI_ACTION_NONE); +} + +INT8 ZombieDecideActionYellow(SOLDIERTYPE *pSoldier) +{ + UINT8 ubNoiseDir; + INT32 sNoiseGridNo; + INT32 iNoiseValue; + INT32 iChance; + BOOLEAN fClimb; + BOOLEAN fReachable; +#ifdef DEBUGDECISIONS + STR16 tempstr; +#endif + + // determine the most important noise heard, and its relative value + sNoiseGridNo = MostImportantNoiseHeard(pSoldier,&iNoiseValue, &fClimb, &fReachable); + + if (TileIsOutOfBounds(sNoiseGridNo)) + { + // then we have no business being under YELLOW status any more! +#ifdef RECORDNET + fprintf(NetDebugFile,"\nDecideActionYellow: ERROR - No important noise known by guynum %d\n\n",pSoldier->ubID); +#endif + +#ifdef BETAVERSION + NumMessage("DecideActionYellow: ERROR - No important noise known by guynum ",pSoldier->ubID); +#endif + + return(AI_ACTION_NONE); + } + + //////////////////////////////////////////////////////////////////////////// + // LOOK AROUND TOWARD NOISE: determine %chance for man to turn towards noise + //////////////////////////////////////////////////////////////////////////// + + // determine direction from this soldier in which the noise lies + ubNoiseDir = atan8(CenterX(pSoldier->sGridNo),CenterY(pSoldier->sGridNo),CenterX(sNoiseGridNo),CenterY(sNoiseGridNo)); + + // if soldier is not already facing in that direction, + // and the noise source is close enough that it could possibly be seen + if ( !gfTurnBasedAI || GetAPsToLook( pSoldier ) <= pSoldier->bActionPoints ) + { + if ((pSoldier->ubDirection != ubNoiseDir) && PythSpacesAway(pSoldier->sGridNo,sNoiseGridNo) <= pSoldier->GetMaxDistanceVisible(sNoiseGridNo) ) { - // base chance depends on how much new info we have to radio to the others - iChance = 5 * WhatIKnowThatPublicDont(pSoldier,FALSE); // use 5 * for YELLOW alert + // set base chance according to orders + if ((pSoldier->aiData.bOrders == STATIONARY) || (pSoldier->aiData.bOrders == ONGUARD) ) + iChance = 50; + else // all other orders + iChance = 25; - // if I actually know something they don't and I ain't swimming (deep water) - if (iChance && !DeepWater( pSoldier->sGridNo )) + if (pSoldier->aiData.bAttitude == DEFENSIVE) + iChance += 15; + + + if ((INT16)PreRandom(100) < iChance && pSoldier->InternalIsValidStance( ubNoiseDir, gAnimControl[ pSoldier->usAnimState ].ubEndHeight ) ) { - // CJC: this addition allows for varying difficulty levels for soldier types - iChance += gbDiff[ DIFF_RADIO_RED_ALERT ][ SoldierDifficultyLevel( pSoldier ) ] / 2; - - // Alex: this addition replaces the sectorValue/2 in original JA - //iChance += gsDiff[DIFF_RADIO_RED_ALERT][GameOption[ENEMYDIFFICULTY]] / 2; - - // modify base chance according to orders - switch (pSoldier->aiData.bOrders) - { - case STATIONARY: iChance += 20; break; - case ONGUARD: iChance += 15; break; - case ONCALL: iChance += 10; break; - case CLOSEPATROL: break; - case RNDPTPATROL: - case POINTPATROL: break; - case FARPATROL: iChance += -10; break; - case SEEKENEMY: iChance += -20; break; - case SNIPER: iChance += -10; break; //Madd: sniper contacts are supposed to be automatically reported - } - - // modify base chance according to attitude - switch (pSoldier->aiData.bAttitude) - { - case DEFENSIVE: iChance += 20; break; - case BRAVESOLO: iChance += -10; break; - case BRAVEAID: break; - case CUNNINGSOLO:iChance += -5; break; - case CUNNINGAID: break; - case AGGRESSIVE: iChance += -20; break; - case ATTACKSLAYONLY: iChance = 0; break; - } - - #ifdef DEBUGDECISIONS - AINumMessage("Chance to radio yellow alert = ",iChance); - #endif - - if ((INT16)PreRandom(100) < iChance) - { - #ifdef DEBUGDECISIONS - AINameMessage(pSoldier,"decides to radio a YELLOW alert!",1000); - #endif - - return(AI_ACTION_YELLOW_ALERT); - } + pSoldier->aiData.usActionData = ubNoiseDir; +#ifdef DEBUGDECISIONS + sprintf(tempstr,"%s - TURNS TOWARDS NOISE to face direction %d",pSoldier->name,pSoldier->aiData.usActionData); + AIPopMessage(tempstr); +#endif + return(AI_ACTION_CHANGE_FACING); } } + } - if( !gGameExternalOptions.fEnemyTanksCanMoveInTactical && TANK( pSoldier ) ) + //////////////////////////////////////////////////////////////////////////// + // RADIO YELLOW ALERT: determine %chance to call others and report noise + //////////////////////////////////////////////////////////////////////////// + + // if we have the action points remaining to RADIO + // (we never want NPCs to choose to radio if they would have to wait a turn) + if ( (pSoldier->bActionPoints >= APBPConstants[AP_RADIO]) && (gTacticalStatus.Team[pSoldier->bTeam].bMenInSector > 1) ) + { + // base chance depends on how much new info we have to radio to the others + iChance = 5 * WhatIKnowThatPublicDont(pSoldier,FALSE); // use 5 * for YELLOW alert + + // if I actually know something they don't and I ain't swimming (deep water) + if (iChance && !DeepWater( pSoldier->sGridNo )) { - return( AI_ACTION_NONE ); + // CJC: this addition allows for varying difficulty levels for soldier types + iChance += gbDiff[ DIFF_RADIO_RED_ALERT ][ SoldierDifficultyLevel( pSoldier ) ] / 2; + + // Alex: this addition replaces the sectorValue/2 in original JA + //iChance += gsDiff[DIFF_RADIO_RED_ALERT][GameOption[ENEMYDIFFICULTY]] / 2; + + // modify base chance according to orders + switch (pSoldier->aiData.bOrders) + { + case STATIONARY: iChance += 20; break; + case ONGUARD: iChance += 15; break; + case ONCALL: iChance += 10; break; + case CLOSEPATROL: break; + case RNDPTPATROL: + case POINTPATROL: break; + case FARPATROL: iChance += -10; break; + case SEEKENEMY: iChance += -20; break; + case SNIPER: iChance += -10; break; //Madd: sniper contacts are supposed to be automatically reported + } + + // modify base chance according to attitude + switch (pSoldier->aiData.bAttitude) + { + case DEFENSIVE: iChance += 20; break; + case BRAVESOLO: iChance += -10; break; + case BRAVEAID: break; + case CUNNINGSOLO:iChance += -5; break; + case CUNNINGAID: break; + case AGGRESSIVE: iChance += -20; break; + case ATTACKSLAYONLY: iChance = 0; break; + } + +#ifdef DEBUGDECISIONS + AINumMessage("Chance to radio yellow alert = ",iChance); +#endif + + if ((INT16)PreRandom(100) < iChance) + { +#ifdef DEBUGDECISIONS + AINameMessage(pSoldier,"decides to radio a YELLOW alert!",1000); +#endif + + return(AI_ACTION_YELLOW_ALERT); + } } + } - //////////////////////////////////////////////////////////////////////// - // REST IF RUNNING OUT OF BREATH - //////////////////////////////////////////////////////////////////////// + if( !gGameExternalOptions.fEnemyTanksCanMoveInTactical && TANK( pSoldier ) ) + { + return( AI_ACTION_NONE ); + } - // if our breath is running a bit low, and we're not in water - if ((pSoldier->bBreath < 25) && !pSoldier->MercInWater()) - { - // take a breather for gods sake! - pSoldier->aiData.usActionData = NOWHERE; - return(AI_ACTION_NONE); - } + //////////////////////////////////////////////////////////////////////// + // REST IF RUNNING OUT OF BREATH + //////////////////////////////////////////////////////////////////////// - //continue flanking - INT32 tempGridNo; + // if our breath is running a bit low, and we're not in water + if ((pSoldier->bBreath < 25) && !pSoldier->MercInWater()) + { + // take a breather for gods sake! + pSoldier->aiData.usActionData = NOWHERE; + return(AI_ACTION_NONE); + } + + //continue flanking + INT32 tempGridNo; - if (TileIsOutOfBounds(sNoiseGridNo)) + if (TileIsOutOfBounds(sNoiseGridNo)) + tempGridNo = pSoldier->lastFlankSpot; + else + tempGridNo = sNoiseGridNo; + + if ( pSoldier->numFlanks > 0 && pSoldier->numFlanks < MAX_FLANKS_YELLOW ) + { + INT16 currDir = GetDirectionFromGridNo ( tempGridNo, pSoldier ); + INT16 origDir = pSoldier->origDir; + pSoldier->numFlanks += 1; + if ( pSoldier->flags.lastFlankLeft ) + { + if ( origDir > currDir ) + origDir -= NUM_WORLD_DIRECTIONS; + + if ( (currDir - origDir) >= 4 ) + { + pSoldier->numFlanks = MAX_FLANKS_YELLOW; + } + else + { + pSoldier->aiData.usActionData = FindFlankingSpot (pSoldier, tempGridNo , AI_ACTION_FLANK_LEFT); + if (!TileIsOutOfBounds(pSoldier->aiData.usActionData) ) //&& (currDir - origDir) < 2 ) + return AI_ACTION_FLANK_LEFT ; + else + pSoldier->numFlanks = MAX_FLANKS_YELLOW; + } + } + else + { + if ( origDir < currDir ) + origDir += NUM_WORLD_DIRECTIONS; + + if ( (origDir - currDir) >= 4 ) + { + pSoldier->numFlanks = MAX_FLANKS_YELLOW; + } + else + { + pSoldier->aiData.usActionData = FindFlankingSpot (pSoldier, tempGridNo , AI_ACTION_FLANK_RIGHT); + if (!TileIsOutOfBounds(pSoldier->aiData.usActionData))//&& (origDir - currDir) < 2 ) + return AI_ACTION_FLANK_RIGHT ; + else + pSoldier->numFlanks = MAX_FLANKS_YELLOW; + } + } + } + + if ( pSoldier->numFlanks == MAX_FLANKS_YELLOW ) + { + pSoldier->numFlanks += 1; + pSoldier->aiData.usActionData = GoAsFarAsPossibleTowards(pSoldier,tempGridNo,AI_ACTION_SEEK_NOISE); + return AI_ACTION_SEEK_NOISE ; + } + + //////////////////////////////////////////////////////////////////////////// + // SWITCH TO GREEN: determine if soldier acts as if nothing at all was wrong + //////////////////////////////////////////////////////////////////////////// + if ((INT16)PreRandom(100) < 50 ) + { +#ifdef RECORDNET + fprintf(NetDebugFile,"\tDecideActionYellow: guynum %d ignores noise, switching to GREEN AI...\n",pSoldier->ubID); +#endif + +#ifdef DEBUGDECISIONS + AINameMessage(pSoldier,"ignores noise completely and BYPASSES to GREEN!",1000); +#endif + + // Skip YELLOW until new situation, 15% extra chance to do GREEN actions + pSoldier->aiData.bBypassToGreen = 15; + return(ZombieDecideActionGreen(pSoldier)); + } + + //////////////////////////////////////////////////////////////////////////// + // DO NOTHING: Not enough points left to move, so save them for next turn + //////////////////////////////////////////////////////////////////////////// + +#ifdef DEBUGDECISIONS + AINameMessage(pSoldier,"- DOES NOTHING (YELLOW)",1000); +#endif + + // by default, if everything else fails, just stands in place without turning + pSoldier->aiData.usActionData = NOWHERE; + return(AI_ACTION_NONE); +} + + +INT8 ZombieDecideActionRed(SOLDIERTYPE *pSoldier, UINT8 ubUnconsciousOK) +{ + INT32 iDummy; + INT32 iChance,sClosestOpponent,sClosestFriend; + INT32 sClosestDisturbance, sDistVisible; + UINT8 ubCanMove,ubOpponentDir; + INT8 bInWater, bInDeepWater; + INT8 bSeekPts = 0, bHelpPts = 0, bWatchPts = 0; + INT8 bHighestWatchLoc; +#ifdef AI_TIMING_TEST + UINT32 uiStartTime, uiEndTime; +#endif +#ifdef DEBUGDECISIONS + STR16 tempstr; +#endif + BOOLEAN fClimb; + + DebugMsg (TOPIC_JA2,DBG_LEVEL_3,String("ZombieDecideActionRed: soldier orders = %d",pSoldier->aiData.bOrders)); + + // if we have absolutely no action points, we can't do a thing under RED! + if (!pSoldier->bActionPoints) + { + pSoldier->aiData.usActionData = NOWHERE; + return(AI_ACTION_NONE); + } + + // can this guy move to any of the neighbouring squares ? (sets TRUE/FALSE) + ubCanMove = (pSoldier->bActionPoints >= MinPtsToMove(pSoldier)); + + + // determine if we happen to be in water (in which case we're in BIG trouble!) + bInWater = Water( pSoldier->sGridNo ); + bInDeepWater = Water( pSoldier->sGridNo ); + + DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionred: crouch and rest if running out of breath"); + //////////////////////////////////////////////////////////////////////// + // CROUCH & REST IF RUNNING OUT OF BREATH + //////////////////////////////////////////////////////////////////////// + + // if our breath is running a bit low, and we're not in water or under fire + if ((pSoldier->bBreath < 25) && !bInWater && !pSoldier->aiData.bUnderFire) + { + pSoldier->aiData.usActionData = NOWHERE; + return(AI_ACTION_NONE); + } + + + DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionred: radio red alert?"); + //////////////////////////////////////////////////////////////////////////// + // RADIO RED ALERT: determine %chance to call others and report contact + //////////////////////////////////////////////////////////////////////////// + + if( gGameExternalOptions.fEnemyTanksCanMoveInTactical || !TANK( pSoldier ) ) + { + DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionred: main red ai"); + + //////////////////////////////////////////////////////////////////////////// + // MAIN RED AI: Decide soldier's preference between SEEKING,HELPING & HIDING + //////////////////////////////////////////////////////////////////////////// + + // get the location of the closest reachable opponent + sClosestDisturbance = ClosestReachableDisturbance(pSoldier,ubUnconsciousOK, &fClimb); + + DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionred: check to continue flanking"); + // continue flanking + INT32 tempGridNo; + + if (TileIsOutOfBounds(sClosestDisturbance)) tempGridNo = pSoldier->lastFlankSpot; else - tempGridNo = sNoiseGridNo; + tempGridNo = sClosestDisturbance; - if ( pSoldier->numFlanks > 0 && pSoldier->numFlanks < MAX_FLANKS_YELLOW ) + if ( pSoldier->numFlanks > 0 && pSoldier->numFlanks < MAX_FLANKS_RED && gAnimControl[ pSoldier->usAnimState ].ubHeight != ANIM_PRONE ) { + DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionred: continue flanking"); INT16 currDir = GetDirectionFromGridNo ( tempGridNo, pSoldier ); INT16 origDir = pSoldier->origDir; pSoldier->numFlanks += 1; @@ -7265,15 +7415,16 @@ void DecideAlertStatus( SOLDIERTYPE *pSoldier ) if ( (currDir - origDir) >= 4 ) { - pSoldier->numFlanks = MAX_FLANKS_YELLOW; + pSoldier->numFlanks = MAX_FLANKS_RED; } else { - pSoldier->aiData.usActionData = FindFlankingSpot (pSoldier, tempGridNo , AI_ACTION_FLANK_LEFT); + pSoldier->aiData.usActionData = FindFlankingSpot (pSoldier, tempGridNo , AI_ACTION_FLANK_LEFT); + if (!TileIsOutOfBounds(pSoldier->aiData.usActionData) ) //&& (currDir - origDir) < 2 ) return AI_ACTION_FLANK_LEFT ; else - pSoldier->numFlanks = MAX_FLANKS_YELLOW; + pSoldier->numFlanks = MAX_FLANKS_RED; } } else @@ -7283,425 +7434,231 @@ void DecideAlertStatus( SOLDIERTYPE *pSoldier ) if ( (origDir - currDir) >= 4 ) { - pSoldier->numFlanks = MAX_FLANKS_YELLOW; + pSoldier->numFlanks = MAX_FLANKS_RED; } else { - pSoldier->aiData.usActionData = FindFlankingSpot (pSoldier, tempGridNo , AI_ACTION_FLANK_RIGHT); - if (!TileIsOutOfBounds(pSoldier->aiData.usActionData))//&& (origDir - currDir) < 2 ) + pSoldier->aiData.usActionData = FindFlankingSpot (pSoldier, tempGridNo , AI_ACTION_FLANK_RIGHT); + + if (!TileIsOutOfBounds(pSoldier->aiData.usActionData) )//&& (origDir - currDir) < 2 ) return AI_ACTION_FLANK_RIGHT ; else - pSoldier->numFlanks = MAX_FLANKS_YELLOW; + pSoldier->numFlanks = MAX_FLANKS_RED; } } } - - if ( pSoldier->numFlanks == MAX_FLANKS_YELLOW ) + if ( pSoldier->numFlanks == MAX_FLANKS_RED ) { pSoldier->numFlanks += 1; - pSoldier->aiData.usActionData = GoAsFarAsPossibleTowards(pSoldier,tempGridNo,AI_ACTION_SEEK_NOISE); - return AI_ACTION_SEEK_NOISE ; - } - - //////////////////////////////////////////////////////////////////////////// - // SWITCH TO GREEN: determine if soldier acts as if nothing at all was wrong - //////////////////////////////////////////////////////////////////////////// - if ((INT16)PreRandom(100) < 50 ) - { - #ifdef RECORDNET - fprintf(NetDebugFile,"\tDecideActionYellow: guynum %d ignores noise, switching to GREEN AI...\n",pSoldier->ubID); - #endif - - #ifdef DEBUGDECISIONS - AINameMessage(pSoldier,"ignores noise completely and BYPASSES to GREEN!",1000); - #endif - - // Skip YELLOW until new situation, 15% extra chance to do GREEN actions - pSoldier->aiData.bBypassToGreen = 15; - return(ZombieDecideActionGreen(pSoldier)); - } - - //////////////////////////////////////////////////////////////////////////// - // DO NOTHING: Not enough points left to move, so save them for next turn - //////////////////////////////////////////////////////////////////////////// - - #ifdef DEBUGDECISIONS - AINameMessage(pSoldier,"- DOES NOTHING (YELLOW)",1000); - #endif - - // by default, if everything else fails, just stands in place without turning - pSoldier->aiData.usActionData = NOWHERE; - return(AI_ACTION_NONE); - } - - - INT8 ZombieDecideActionRed(SOLDIERTYPE *pSoldier, UINT8 ubUnconsciousOK) - { - INT32 iDummy; - INT32 iChance,sClosestOpponent,sClosestFriend; - INT32 sClosestDisturbance, sDistVisible; - UINT8 ubCanMove,ubOpponentDir; - INT8 bInWater, bInDeepWater; - INT8 bSeekPts = 0, bHelpPts = 0, bWatchPts = 0; - INT8 bHighestWatchLoc; - #ifdef AI_TIMING_TEST - UINT32 uiStartTime, uiEndTime; - #endif - #ifdef DEBUGDECISIONS - STR16 tempstr; - #endif - BOOLEAN fClimb; - - DebugMsg (TOPIC_JA2,DBG_LEVEL_3,String("ZombieDecideActionRed: soldier orders = %d",pSoldier->aiData.bOrders)); - - // if we have absolutely no action points, we can't do a thing under RED! - if (!pSoldier->bActionPoints) - { - pSoldier->aiData.usActionData = NOWHERE; - return(AI_ACTION_NONE); - } - - // can this guy move to any of the neighbouring squares ? (sets TRUE/FALSE) - ubCanMove = (pSoldier->bActionPoints >= MinPtsToMove(pSoldier)); - - - // determine if we happen to be in water (in which case we're in BIG trouble!) - bInWater = Water( pSoldier->sGridNo ); - bInDeepWater = Water( pSoldier->sGridNo ); - - DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionred: crouch and rest if running out of breath"); - //////////////////////////////////////////////////////////////////////// - // CROUCH & REST IF RUNNING OUT OF BREATH - //////////////////////////////////////////////////////////////////////// - - // if our breath is running a bit low, and we're not in water or under fire - if ((pSoldier->bBreath < 25) && !bInWater && !pSoldier->aiData.bUnderFire) - { - pSoldier->aiData.usActionData = NOWHERE; - return(AI_ACTION_NONE); - } - - - DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionred: radio red alert?"); - //////////////////////////////////////////////////////////////////////////// - // RADIO RED ALERT: determine %chance to call others and report contact - //////////////////////////////////////////////////////////////////////////// - - if( gGameExternalOptions.fEnemyTanksCanMoveInTactical || !TANK( pSoldier ) ) - { - DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionred: main red ai"); - - //////////////////////////////////////////////////////////////////////////// - // MAIN RED AI: Decide soldier's preference between SEEKING,HELPING & HIDING - //////////////////////////////////////////////////////////////////////////// - - // get the location of the closest reachable opponent - sClosestDisturbance = ClosestReachableDisturbance(pSoldier,ubUnconsciousOK, &fClimb); - - DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionred: check to continue flanking"); - // continue flanking - INT32 tempGridNo; - - if (TileIsOutOfBounds(sClosestDisturbance)) - tempGridNo = pSoldier->lastFlankSpot; - else - tempGridNo = sClosestDisturbance; - - if ( pSoldier->numFlanks > 0 && pSoldier->numFlanks < MAX_FLANKS_RED && gAnimControl[ pSoldier->usAnimState ].ubHeight != ANIM_PRONE ) + DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionred: stop flanking"); + if (PythSpacesAway ( pSoldier->sGridNo, tempGridNo ) > MIN_FLANK_DIST_RED * 2 ) { - DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionred: continue flanking"); - INT16 currDir = GetDirectionFromGridNo ( tempGridNo, pSoldier ); - INT16 origDir = pSoldier->origDir; - pSoldier->numFlanks += 1; - if ( pSoldier->flags.lastFlankLeft ) - { - if ( origDir > currDir ) - origDir -= NUM_WORLD_DIRECTIONS; + pSoldier->aiData.usActionData = InternalGoAsFarAsPossibleTowards(pSoldier,tempGridNo,GetAPsProne( pSoldier, TRUE),AI_ACTION_SEEK_OPPONENT,0); - if ( (currDir - origDir) >= 4 ) - { - pSoldier->numFlanks = MAX_FLANKS_RED; - } - else - { - pSoldier->aiData.usActionData = FindFlankingSpot (pSoldier, tempGridNo , AI_ACTION_FLANK_LEFT); - - if (!TileIsOutOfBounds(pSoldier->aiData.usActionData) ) //&& (currDir - origDir) < 2 ) - return AI_ACTION_FLANK_LEFT ; - else - pSoldier->numFlanks = MAX_FLANKS_RED; - } - } - else + if ( LocationToLocationLineOfSightTest( pSoldier->aiData.usActionData, pSoldier->pathing.bLevel, tempGridNo, pSoldier->pathing.bLevel, TRUE) ) { - if ( origDir < currDir ) - origDir += NUM_WORLD_DIRECTIONS; - - if ( (origDir - currDir) >= 4 ) - { - pSoldier->numFlanks = MAX_FLANKS_RED; - } - else - { - pSoldier->aiData.usActionData = FindFlankingSpot (pSoldier, tempGridNo , AI_ACTION_FLANK_RIGHT); + // reserve APs for a possible crouch plus a shot + pSoldier->aiData.usActionData = InternalGoAsFarAsPossibleTowards(pSoldier, tempGridNo, (INT8) (MinAPsToAttack( pSoldier, tempGridNo, ADDTURNCOST, 0) + GetAPsCrouch( pSoldier, TRUE)), AI_ACTION_SEEK_OPPONENT, FLAG_CAUTIOUS); - if (!TileIsOutOfBounds(pSoldier->aiData.usActionData) )//&& (origDir - currDir) < 2 ) - return AI_ACTION_FLANK_RIGHT ; - else - pSoldier->numFlanks = MAX_FLANKS_RED; - } - } - } - if ( pSoldier->numFlanks == MAX_FLANKS_RED ) - { - pSoldier->numFlanks += 1; - DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionred: stop flanking"); - if (PythSpacesAway ( pSoldier->sGridNo, tempGridNo ) > MIN_FLANK_DIST_RED * 2 ) - { - pSoldier->aiData.usActionData = InternalGoAsFarAsPossibleTowards(pSoldier,tempGridNo,GetAPsProne( pSoldier, TRUE),AI_ACTION_SEEK_OPPONENT,0); - - if ( LocationToLocationLineOfSightTest( pSoldier->aiData.usActionData, pSoldier->pathing.bLevel, tempGridNo, pSoldier->pathing.bLevel, TRUE) ) - { - // reserve APs for a possible crouch plus a shot - pSoldier->aiData.usActionData = InternalGoAsFarAsPossibleTowards(pSoldier, tempGridNo, (INT8) (MinAPsToAttack( pSoldier, tempGridNo, ADDTURNCOST, 0) + GetAPsCrouch( pSoldier, TRUE)), AI_ACTION_SEEK_OPPONENT, FLAG_CAUTIOUS); - - if (!TileIsOutOfBounds(pSoldier->aiData.usActionData)) - { - pSoldier->aiData.fAIFlags |= AI_CAUTIOUS; - pSoldier->aiData.bNextAction = AI_ACTION_END_TURN; - return(AI_ACTION_SEEK_OPPONENT); - } - } - else + if (!TileIsOutOfBounds(pSoldier->aiData.usActionData)) { + pSoldier->aiData.fAIFlags |= AI_CAUTIOUS; + pSoldier->aiData.bNextAction = AI_ACTION_END_TURN; return(AI_ACTION_SEEK_OPPONENT); } } else { - pSoldier->aiData.usActionData = FindBestNearbyCover(pSoldier,pSoldier->aiData.bAIMorale,&iDummy); - return AI_ACTION_TAKE_COVER ; + return(AI_ACTION_SEEK_OPPONENT); } } + else + { + pSoldier->aiData.usActionData = FindBestNearbyCover(pSoldier,pSoldier->aiData.bAIMorale,&iDummy); + return AI_ACTION_TAKE_COVER ; + } + } - // if we can move at least 1 square's worth - // and have more APs than we want to reserve - DebugMsg (TOPIC_JA2,DBG_LEVEL_3,String("decideactionred: can we move? = %d, APs = %d",ubCanMove,pSoldier->bActionPoints)); + // if we can move at least 1 square's worth + // and have more APs than we want to reserve + DebugMsg (TOPIC_JA2,DBG_LEVEL_3,String("decideactionred: can we move? = %d, APs = %d",ubCanMove,pSoldier->bActionPoints)); - if (ubCanMove && pSoldier->bActionPoints > APBPConstants[MAX_AP_CARRIED]) - { - // zombies don't hide, they seek prey... - bSeekPts = 99; - bHelpPts = 40; - bWatchPts -= 10; + if (ubCanMove && pSoldier->bActionPoints > APBPConstants[MAX_AP_CARRIED]) + { + // zombies don't hide, they seek prey... + bSeekPts = 99; + bHelpPts = 40; + bWatchPts -= 10; - DebugMsg (TOPIC_JA2,DBG_LEVEL_3,String("decideactionred: checking seek/help/watch points... orders = %d, attitude = %d",pSoldier->aiData.bOrders,pSoldier->aiData.bAttitude)); - // calculate initial points for watch based on highest watch loc + DebugMsg (TOPIC_JA2,DBG_LEVEL_3,String("decideactionred: checking seek/help/watch points... orders = %d, attitude = %d",pSoldier->aiData.bOrders,pSoldier->aiData.bAttitude)); + // calculate initial points for watch based on highest watch loc - bWatchPts = GetHighestWatchedLocPoints( pSoldier->ubID ); - if ( bWatchPts <= 0 ) - { - // no watching - bWatchPts = -99; - } + bWatchPts = GetHighestWatchedLocPoints( pSoldier->ubID ); + if ( bWatchPts <= 0 ) + { + // no watching + bWatchPts = -99; + } - // modify RED movement tendencies according to morale - switch (pSoldier->aiData.bAIMorale) - { - case MORALE_HOPELESS: bSeekPts = -99; bHelpPts = -99; bWatchPts = -99; break; - case MORALE_WORRIED: bSeekPts += -2; bHelpPts += 0; bWatchPts += 1; break; - case MORALE_NORMAL: bSeekPts += 0; bHelpPts += 0; bWatchPts += 0; break; - case MORALE_CONFIDENT: bSeekPts += +1; bHelpPts += 0; bWatchPts += 0; break; - case MORALE_FEARLESS: bSeekPts += +1; bHelpPts += 0; bWatchPts += 0; break; - } + // modify RED movement tendencies according to morale + switch (pSoldier->aiData.bAIMorale) + { + case MORALE_HOPELESS: bSeekPts = -99; bHelpPts = -99; bWatchPts = -99; break; + case MORALE_WORRIED: bSeekPts += -2; bHelpPts += 0; bWatchPts += 1; break; + case MORALE_NORMAL: bSeekPts += 0; bHelpPts += 0; bWatchPts += 0; break; + case MORALE_CONFIDENT: bSeekPts += +1; bHelpPts += 0; bWatchPts += 0; break; + case MORALE_FEARLESS: bSeekPts += +1; bHelpPts += 0; bWatchPts += 0; break; + } - // modify tendencies according to orders - switch (pSoldier->aiData.bOrders) - { - case STATIONARY: bSeekPts += -1; bHelpPts += -1; bWatchPts += +1; break; - case ONGUARD: bSeekPts += -1; bHelpPts += 0; bWatchPts += +1; break; - case CLOSEPATROL: bSeekPts += 0; bHelpPts += 0; bWatchPts += 0; break; - case RNDPTPATROL: bSeekPts += 0; bHelpPts += 0; bWatchPts += 0; break; - case POINTPATROL: bSeekPts += 0; bHelpPts += 0; bWatchPts += 0; break; - case FARPATROL: bSeekPts += 0; bHelpPts += 0; bWatchPts += 0; break; - case ONCALL: bSeekPts += 0; bHelpPts += +1; bWatchPts += 0; break; - case SEEKENEMY: bSeekPts += +1; bHelpPts += 0; bWatchPts += -1; break; - case SNIPER: bSeekPts += -1; bHelpPts += 0; bWatchPts += +1; break; - } + // modify tendencies according to orders + switch (pSoldier->aiData.bOrders) + { + case STATIONARY: bSeekPts += -1; bHelpPts += -1; bWatchPts += +1; break; + case ONGUARD: bSeekPts += -1; bHelpPts += 0; bWatchPts += +1; break; + case CLOSEPATROL: bSeekPts += 0; bHelpPts += 0; bWatchPts += 0; break; + case RNDPTPATROL: bSeekPts += 0; bHelpPts += 0; bWatchPts += 0; break; + case POINTPATROL: bSeekPts += 0; bHelpPts += 0; bWatchPts += 0; break; + case FARPATROL: bSeekPts += 0; bHelpPts += 0; bWatchPts += 0; break; + case ONCALL: bSeekPts += 0; bHelpPts += +1; bWatchPts += 0; break; + case SEEKENEMY: bSeekPts += +1; bHelpPts += 0; bWatchPts += -1; break; + case SNIPER: bSeekPts += -1; bHelpPts += 0; bWatchPts += +1; break; + } - // modify tendencies according to attitude - switch (pSoldier->aiData.bAttitude) - { - case DEFENSIVE: bSeekPts += -1; bHelpPts += 0; bWatchPts += +1; break; - case BRAVESOLO: bSeekPts += +1; bHelpPts += -1; bWatchPts += -1; break; - case BRAVEAID: bSeekPts += +1; bHelpPts += +1; bWatchPts += -1; break; - case CUNNINGSOLO: bSeekPts += 1; bHelpPts += -1; bWatchPts += 0; break; - case CUNNINGAID: bSeekPts += 1; bHelpPts += +1; bWatchPts += 0; break; - case AGGRESSIVE: bSeekPts += +1; bHelpPts += 0; bWatchPts += 0; break; - case ATTACKSLAYONLY:bSeekPts += +1; bHelpPts += 0; bWatchPts += 0; break; - } + // modify tendencies according to attitude + switch (pSoldier->aiData.bAttitude) + { + case DEFENSIVE: bSeekPts += -1; bHelpPts += 0; bWatchPts += +1; break; + case BRAVESOLO: bSeekPts += +1; bHelpPts += -1; bWatchPts += -1; break; + case BRAVEAID: bSeekPts += +1; bHelpPts += +1; bWatchPts += -1; break; + case CUNNINGSOLO: bSeekPts += 1; bHelpPts += -1; bWatchPts += 0; break; + case CUNNINGAID: bSeekPts += 1; bHelpPts += +1; bWatchPts += 0; break; + case AGGRESSIVE: bSeekPts += +1; bHelpPts += 0; bWatchPts += 0; break; + case ATTACKSLAYONLY:bSeekPts += +1; bHelpPts += 0; bWatchPts += 0; break; + } - DebugMsg (TOPIC_JA2,DBG_LEVEL_3,String("decideactionred: hide = %d, seek = %d, watch = %d, help = %d",0,bSeekPts,bWatchPts,bHelpPts)); - // while one of the three main RED REACTIONS remains viable - while ((bSeekPts > -90) || (bHelpPts > -90) ) + DebugMsg (TOPIC_JA2,DBG_LEVEL_3,String("decideactionred: hide = %d, seek = %d, watch = %d, help = %d",0,bSeekPts,bWatchPts,bHelpPts)); + // while one of the three main RED REACTIONS remains viable + while ((bSeekPts > -90) || (bHelpPts > -90) ) + { + DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionred: checking to seek"); + // if SEEKING is possible and at least as desirable as helping or hiding + if ( ((bSeekPts > -90) && (bSeekPts >= bHelpPts) && (bSeekPts >= bWatchPts )) ) { - DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionred: checking to seek"); - // if SEEKING is possible and at least as desirable as helping or hiding - if ( ((bSeekPts > -90) && (bSeekPts >= bHelpPts) && (bSeekPts >= bWatchPts )) ) +#ifdef AI_TIMING_TESTS + uiStartTime = GetJA2Clock(); +#endif + +#ifdef AI_TIMING_TESTS + uiEndTime = GetJA2Clock(); + guiRedSeekTimeTotal += (uiEndTime - uiStartTime); + guiRedSeekCounter++; +#endif + // if there is an opponent reachable + if (!TileIsOutOfBounds(sClosestDisturbance) ) { - #ifdef AI_TIMING_TESTS - uiStartTime = GetJA2Clock(); - #endif + DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionred: seek opponent"); + ////////////////////////////////////////////////////////////////////// + // SEEK CLOSEST DISTURBANCE: GO DIRECTLY TOWARDS CLOSEST KNOWN OPPONENT + ////////////////////////////////////////////////////////////////////// - #ifdef AI_TIMING_TESTS - uiEndTime = GetJA2Clock(); - guiRedSeekTimeTotal += (uiEndTime - uiStartTime); - guiRedSeekCounter++; - #endif - // if there is an opponent reachable - if (!TileIsOutOfBounds(sClosestDisturbance) ) - { - DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionred: seek opponent"); - ////////////////////////////////////////////////////////////////////// - // SEEK CLOSEST DISTURBANCE: GO DIRECTLY TOWARDS CLOSEST KNOWN OPPONENT - ////////////////////////////////////////////////////////////////////// - - // try to move towards him - pSoldier->aiData.usActionData = InternalGoAsFarAsPossibleTowards(pSoldier,sClosestDisturbance,GetAPsCrouch( pSoldier, TRUE),AI_ACTION_SEEK_OPPONENT,0); + // try to move towards him + pSoldier->aiData.usActionData = InternalGoAsFarAsPossibleTowards(pSoldier,sClosestDisturbance,GetAPsCrouch( pSoldier, TRUE),AI_ACTION_SEEK_OPPONENT,0); - // if it's possible - if (!TileIsOutOfBounds(pSoldier->aiData.usActionData)) + // if it's possible + if (!TileIsOutOfBounds(pSoldier->aiData.usActionData)) + { +#ifdef DEBUGDECISIONS + // do it! + sprintf(tempstr,"%s - SEEKING OPPONENT at grid %d, MOVING to %d", + pSoldier->name,sClosestDisturbance,pSoldier->aiData.usActionData); + AIPopMessage(tempstr); +#endif + + if ( fClimb && gGameExternalOptions.fZombieCanClimb )//&& pSoldier->aiData.usActionData == sClosestDisturbance) { - #ifdef DEBUGDECISIONS - // do it! - sprintf(tempstr,"%s - SEEKING OPPONENT at grid %d, MOVING to %d", - pSoldier->name,sClosestDisturbance,pSoldier->aiData.usActionData); - AIPopMessage(tempstr); - #endif + // need to climb AND have enough APs to get there this turn + BOOLEAN fUp = TRUE; + if (pSoldier->pathing.bLevel > 0 ) + fUp = FALSE; - if ( fClimb && gGameExternalOptions.fZombieCanClimb )//&& pSoldier->aiData.usActionData == sClosestDisturbance) + if (!fUp) + DebugMsg ( TOPIC_JA2AI , DBG_LEVEL_3 , String("Soldier %d is climbing down",pSoldier->ubID) ); + + // As mentioned in the next part, the sClosestDisturbance IS the climb point desired. So the + // check here should be "Am I aready there?" If so, THEN possibly climb. This previous check + // would have a soldier climbing any building, even if it was not the desired building. So + // WRONG WRONG WRONG + //if ( CanClimbFromHere ( pSoldier, fUp ) ) + if (pSoldier->sGridNo == sClosestDisturbance) { - // need to climb AND have enough APs to get there this turn - BOOLEAN fUp = TRUE; - if (pSoldier->pathing.bLevel > 0 ) - fUp = FALSE; - - if (!fUp) - DebugMsg ( TOPIC_JA2AI , DBG_LEVEL_3 , String("Soldier %d is climbing down",pSoldier->ubID) ); - - // As mentioned in the next part, the sClosestDisturbance IS the climb point desired. So the - // check here should be "Am I aready there?" If so, THEN possibly climb. This previous check - // would have a soldier climbing any building, even if it was not the desired building. So - // WRONG WRONG WRONG - //if ( CanClimbFromHere ( pSoldier, fUp ) ) - if (pSoldier->sGridNo == sClosestDisturbance) + if (IsActionAffordable(pSoldier) && pSoldier->bActionPoints >= ( APBPConstants[AP_CLIMBROOF] + MinAPsToAttack( pSoldier, sClosestDisturbance, ADDTURNCOST, 0))) { - if (IsActionAffordable(pSoldier) && pSoldier->bActionPoints >= ( APBPConstants[AP_CLIMBROOF] + MinAPsToAttack( pSoldier, sClosestDisturbance, ADDTURNCOST, 0))) - { - return( AI_ACTION_CLIMB_ROOF ); - } - } - else - { - // Do not overwrite the usActionData here. If there's no nearby climb point, the action data - // would become NOWHERE, and then the SEEK_ENEMY fallback would also fail. - // In fact, sClosestDisturbance has ALREADY calculated the closest climb point when climbing is - // necessary. The returned grid # in sClosestDisturbance is that climb point. So if climb is - // set, then use sClosestDisturbance as is. - //INT16 usClimbPoint = FindClosestClimbPoint(pSoldier, pSoldier->sGridNo , sClosestDisturbance , fUp ); - INT32 usClimbPoint = sClosestDisturbance; - if (!TileIsOutOfBounds(usClimbPoint)) - { - pSoldier->aiData.usActionData = usClimbPoint; - return( AI_ACTION_MOVE_TO_CLIMB ); - } - } - } - //if ( fClimb && pSoldier->aiData.usActionData == sClosestDisturbance) - //{ - // return( AI_ACTION_CLIMB_ROOF ); - //} - - - if ( ( pSoldier->aiData.bAttitude == CUNNINGAID || pSoldier->aiData.bAttitude == CUNNINGSOLO || pSoldier->aiData.bAttitude == BRAVESOLO ) && gAnimControl[ pSoldier->usAnimState ].ubHeight != ANIM_PRONE ) - { - INT8 action = AI_ACTION_SEEK_OPPONENT; - INT16 dist = PythSpacesAway ( pSoldier->sGridNo, sClosestDisturbance ); - if ( dist > MIN_FLANK_DIST_RED && dist < MAX_FLANK_DIST_RED ) - { - INT16 rdm = Random(6); - - switch (rdm) - { - case 1: - case 2: - case 3: - if ( pSoldier->aiData.bLastAction != AI_ACTION_FLANK_LEFT && pSoldier->aiData.bLastAction != AI_ACTION_FLANK_RIGHT ) - action = AI_ACTION_FLANK_LEFT ; - break; - default: - if ( pSoldier->aiData.bLastAction != AI_ACTION_FLANK_LEFT && pSoldier->aiData.bLastAction != AI_ACTION_FLANK_RIGHT ) - action = AI_ACTION_FLANK_RIGHT ; - break; - } - - if (action == AI_ACTION_SEEK_OPPONENT) { - return action; - } - } - else - return AI_ACTION_SEEK_OPPONENT ; - - pSoldier->aiData.usActionData = FindFlankingSpot (pSoldier, sClosestDisturbance, action ); - - if (TileIsOutOfBounds(pSoldier->aiData.usActionData) || pSoldier->numFlanks >= MAX_FLANKS_RED ) - { - pSoldier->aiData.usActionData = InternalGoAsFarAsPossibleTowards(pSoldier,sClosestDisturbance,GetAPsCrouch( pSoldier, TRUE), AI_ACTION_SEEK_OPPONENT,0); - //pSoldier->numFlanks = 0; - if ( PythSpacesAway( pSoldier->aiData.usActionData, sClosestDisturbance ) < 5 || LocationToLocationLineOfSightTest( pSoldier->aiData.usActionData, pSoldier->pathing.bLevel, sClosestDisturbance, pSoldier->pathing.bLevel, TRUE ) ) - { - // reserve APs for a possible crouch plus a shot - pSoldier->aiData.usActionData = InternalGoAsFarAsPossibleTowards(pSoldier, sClosestDisturbance, (INT8) (MinAPsToAttack( pSoldier, sClosestDisturbance, ADDTURNCOST, 0) + GetAPsCrouch( pSoldier, TRUE)), AI_ACTION_SEEK_OPPONENT, FLAG_CAUTIOUS ); - - if (!TileIsOutOfBounds(pSoldier->aiData.usActionData)) - { - pSoldier->aiData.fAIFlags |= AI_CAUTIOUS; - pSoldier->aiData.bNextAction = AI_ACTION_END_TURN; - return(AI_ACTION_SEEK_OPPONENT); - } - } - - else - { - return(AI_ACTION_SEEK_OPPONENT); - } - } - else - { - if ( action == AI_ACTION_FLANK_LEFT ) - pSoldier->flags.lastFlankLeft = TRUE; - else - pSoldier->flags.lastFlankLeft = FALSE; - - if ( pSoldier->lastFlankSpot != sClosestDisturbance) - pSoldier->numFlanks=0; - - - pSoldier->origDir = GetDirectionFromGridNo ( sClosestDisturbance, pSoldier); - pSoldier->lastFlankSpot = sClosestDisturbance; - pSoldier->numFlanks++; - return(action); + return( AI_ACTION_CLIMB_ROOF ); } } else { - // let's be a bit cautious about going right up to a location without enough APs to shoot + // Do not overwrite the usActionData here. If there's no nearby climb point, the action data + // would become NOWHERE, and then the SEEK_ENEMY fallback would also fail. + // In fact, sClosestDisturbance has ALREADY calculated the closest climb point when climbing is + // necessary. The returned grid # in sClosestDisturbance is that climb point. So if climb is + // set, then use sClosestDisturbance as is. + //INT16 usClimbPoint = FindClosestClimbPoint(pSoldier, pSoldier->sGridNo , sClosestDisturbance , fUp ); + INT32 usClimbPoint = sClosestDisturbance; + if (!TileIsOutOfBounds(usClimbPoint)) + { + pSoldier->aiData.usActionData = usClimbPoint; + return( AI_ACTION_MOVE_TO_CLIMB ); + } + } + } + //if ( fClimb && pSoldier->aiData.usActionData == sClosestDisturbance) + //{ + // return( AI_ACTION_CLIMB_ROOF ); + //} + + + if ( ( pSoldier->aiData.bAttitude == CUNNINGAID || pSoldier->aiData.bAttitude == CUNNINGSOLO || pSoldier->aiData.bAttitude == BRAVESOLO ) && gAnimControl[ pSoldier->usAnimState ].ubHeight != ANIM_PRONE ) + { + INT8 action = AI_ACTION_SEEK_OPPONENT; + INT16 dist = PythSpacesAway ( pSoldier->sGridNo, sClosestDisturbance ); + if ( dist > MIN_FLANK_DIST_RED && dist < MAX_FLANK_DIST_RED ) + { + INT16 rdm = Random(6); + + switch (rdm) + { + case 1: + case 2: + case 3: + if ( pSoldier->aiData.bLastAction != AI_ACTION_FLANK_LEFT && pSoldier->aiData.bLastAction != AI_ACTION_FLANK_RIGHT ) + action = AI_ACTION_FLANK_LEFT ; + break; + default: + if ( pSoldier->aiData.bLastAction != AI_ACTION_FLANK_LEFT && pSoldier->aiData.bLastAction != AI_ACTION_FLANK_RIGHT ) + action = AI_ACTION_FLANK_RIGHT ; + break; + } + + if (action == AI_ACTION_SEEK_OPPONENT) { + return action; + } + } + else + return AI_ACTION_SEEK_OPPONENT ; + + pSoldier->aiData.usActionData = FindFlankingSpot (pSoldier, sClosestDisturbance, action ); + + if (TileIsOutOfBounds(pSoldier->aiData.usActionData) || pSoldier->numFlanks >= MAX_FLANKS_RED ) + { + pSoldier->aiData.usActionData = InternalGoAsFarAsPossibleTowards(pSoldier,sClosestDisturbance,GetAPsCrouch( pSoldier, TRUE), AI_ACTION_SEEK_OPPONENT,0); + //pSoldier->numFlanks = 0; if ( PythSpacesAway( pSoldier->aiData.usActionData, sClosestDisturbance ) < 5 || LocationToLocationLineOfSightTest( pSoldier->aiData.usActionData, pSoldier->pathing.bLevel, sClosestDisturbance, pSoldier->pathing.bLevel, TRUE ) ) { // reserve APs for a possible crouch plus a shot pSoldier->aiData.usActionData = InternalGoAsFarAsPossibleTowards(pSoldier, sClosestDisturbance, (INT8) (MinAPsToAttack( pSoldier, sClosestDisturbance, ADDTURNCOST, 0) + GetAPsCrouch( pSoldier, TRUE)), AI_ACTION_SEEK_OPPONENT, FLAG_CAUTIOUS ); - + if (!TileIsOutOfBounds(pSoldier->aiData.usActionData)) { pSoldier->aiData.fAIFlags |= AI_CAUTIOUS; @@ -7709,1195 +7666,1232 @@ void DecideAlertStatus( SOLDIERTYPE *pSoldier ) return(AI_ACTION_SEEK_OPPONENT); } } + else { return(AI_ACTION_SEEK_OPPONENT); } - break; } - } - } + else + { + if ( action == AI_ACTION_FLANK_LEFT ) + pSoldier->flags.lastFlankLeft = TRUE; + else + pSoldier->flags.lastFlankLeft = FALSE; - // mark SEEKING as impossible for next time through while loop - #ifdef DEBUGDECISIONS - AINameMessage(pSoldier,"couldn't SEEK...",1000); - #endif - bSeekPts = -99; - DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionred: couldn't seek"); - } + if ( pSoldier->lastFlankSpot != sClosestDisturbance) + pSoldier->numFlanks=0; - DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionred: checking to watch"); - // if WATCHING is possible and at least as desirable as anything else - if ((bWatchPts > -90) && (bWatchPts >= bSeekPts) && (bWatchPts >= bHelpPts) ) - { - // take a look at our highest watch point... if it's still visible, turn to face it and then wait - bHighestWatchLoc = GetHighestVisibleWatchedLoc( pSoldier->ubID ); - //sDistVisible = DistanceVisible( pSoldier, DIRECTION_IRRELEVANT, DIRECTION_IRRELEVANT, gsWatchedLoc[ pSoldier->ubID ][ bHighestWatchLoc ] ); - if ( bHighestWatchLoc != -1 ) - { - // see if we need turn to face that location - ubOpponentDir = atan8( CenterX(pSoldier->sGridNo),CenterY(pSoldier->sGridNo),CenterX( gsWatchedLoc[ pSoldier->ubID ][ bHighestWatchLoc ] ),CenterY( gsWatchedLoc[ pSoldier->ubID ][ bHighestWatchLoc ] ) ); - // if soldier is not already facing in that direction, - // and the opponent is close enough that he could possibly be seen - if ( pSoldier->ubDirection != ubOpponentDir && pSoldier->InternalIsValidStance( ubOpponentDir, gAnimControl[ pSoldier->usAnimState ].ubEndHeight ) ) - { - // turn - pSoldier->aiData.usActionData = ubOpponentDir; - pSoldier->aiData.bNextAction = AI_ACTION_END_TURN; - return(AI_ACTION_CHANGE_FACING); + pSoldier->origDir = GetDirectionFromGridNo ( sClosestDisturbance, pSoldier); + pSoldier->lastFlankSpot = sClosestDisturbance; + pSoldier->numFlanks++; + return(action); + } } else { - // consider at least crouching - if ( gAnimControl[ pSoldier->usAnimState ].ubHeight != ANIM_PRONE ) - { - // end turn - return( AI_ACTION_END_TURN ); - } - } - - } - - bWatchPts = -99; - DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionred: couldn't watch"); - - } - - - DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionred: checking to help"); - // if HELPING is possible and at least as desirable as seeking or hiding - if ((bHelpPts > -90) && (bHelpPts >= bSeekPts) && (bHelpPts >= bWatchPts )) - { - #ifdef AI_TIMING_TESTS - uiStartTime = GetJA2Clock(); - #endif - sClosestFriend = ClosestReachableFriendInTrouble(pSoldier, &fClimb ); - #ifdef AI_TIMING_TESTS - uiEndTime = GetJA2Clock(); - - guiRedHelpTimeTotal += (uiEndTime - uiStartTime); - guiRedHelpCounter++; - #endif - - if (!TileIsOutOfBounds(sClosestFriend)) - { - ////////////////////////////////////////////////////////////////////// - // GO DIRECTLY TOWARDS CLOSEST FRIEND UNDER FIRE OR WHO LAST RADIOED - ////////////////////////////////////////////////////////////////////// - pSoldier->aiData.usActionData = InternalGoAsFarAsPossibleTowards(pSoldier,sClosestFriend,GetAPsCrouch( pSoldier, TRUE), AI_ACTION_SEEK_OPPONENT,0); - - if (!TileIsOutOfBounds(pSoldier->aiData.usActionData)) - { - #ifdef DEBUGDECISIONS - sprintf(tempstr,"%s - SEEKING FRIEND at %d, MOVING to %d", - pSoldier->name,sClosestFriend,pSoldier->aiData.usActionData); - AIPopMessage(tempstr); - #endif - - if ( fClimb && gGameExternalOptions.fZombieCanClimb )//&& pSoldier->aiData.usActionData == sClosestFriend) + // let's be a bit cautious about going right up to a location without enough APs to shoot + if ( PythSpacesAway( pSoldier->aiData.usActionData, sClosestDisturbance ) < 5 || LocationToLocationLineOfSightTest( pSoldier->aiData.usActionData, pSoldier->pathing.bLevel, sClosestDisturbance, pSoldier->pathing.bLevel, TRUE ) ) { - // need to climb AND have enough APs to get there this turn - BOOLEAN fUp = TRUE; - if (pSoldier->pathing.bLevel > 0 ) - fUp = FALSE; - - if (!fUp) - DebugMsg ( TOPIC_JA2AI , DBG_LEVEL_3 , String("Soldier %d is climbing down",pSoldier->ubID) ); - - // 0verhaul: Yet another chance to climb the wrong building and otherwise waste CPU power. - // We already know the climb point we want, which may not be here even if climbing is possible. - //if ( CanClimbFromHere ( pSoldier, fUp ) ) - if (pSoldier->sGridNo == sClosestFriend) + // reserve APs for a possible crouch plus a shot + pSoldier->aiData.usActionData = InternalGoAsFarAsPossibleTowards(pSoldier, sClosestDisturbance, (INT8) (MinAPsToAttack( pSoldier, sClosestDisturbance, ADDTURNCOST, 0) + GetAPsCrouch( pSoldier, TRUE)), AI_ACTION_SEEK_OPPONENT, FLAG_CAUTIOUS ); + + if (!TileIsOutOfBounds(pSoldier->aiData.usActionData)) { - if (IsActionAffordable(pSoldier) && pSoldier->bActionPoints >= ( APBPConstants[AP_CLIMBROOF] + MinAPsToAttack( pSoldier, sClosestFriend, ADDTURNCOST, 0))) - { - return( AI_ACTION_CLIMB_ROOF ); - } - } - else - { - pSoldier->aiData.usActionData = sClosestFriend; - //INT32 sClimbPoint = FindClosestClimbPoint(pSoldier, pSoldier->sGridNo , sClosestFriend , fUp ); - //if (!TileIsOutOfBounds(sClimbPoint)) - { - //pSoldier->aiData.usActionData = sClimbPoint; - return( AI_ACTION_MOVE_TO_CLIMB ); - } + pSoldier->aiData.fAIFlags |= AI_CAUTIOUS; + pSoldier->aiData.bNextAction = AI_ACTION_END_TURN; + return(AI_ACTION_SEEK_OPPONENT); } } - //if (fClimb && pSoldier->aiData.usActionData == sClosestFriend) - //{ - // return( AI_ACTION_CLIMB_ROOF ); - //} - return(AI_ACTION_SEEK_FRIEND); + else + { + return(AI_ACTION_SEEK_OPPONENT); + } + break; + } + } + } + + // mark SEEKING as impossible for next time through while loop +#ifdef DEBUGDECISIONS + AINameMessage(pSoldier,"couldn't SEEK...",1000); +#endif + bSeekPts = -99; + DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionred: couldn't seek"); + } + + DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionred: checking to watch"); + // if WATCHING is possible and at least as desirable as anything else + if ((bWatchPts > -90) && (bWatchPts >= bSeekPts) && (bWatchPts >= bHelpPts) ) + { + // take a look at our highest watch point... if it's still visible, turn to face it and then wait + bHighestWatchLoc = GetHighestVisibleWatchedLoc( pSoldier->ubID ); + //sDistVisible = DistanceVisible( pSoldier, DIRECTION_IRRELEVANT, DIRECTION_IRRELEVANT, gsWatchedLoc[ pSoldier->ubID ][ bHighestWatchLoc ] ); + if ( bHighestWatchLoc != -1 ) + { + // see if we need turn to face that location + ubOpponentDir = atan8( CenterX(pSoldier->sGridNo),CenterY(pSoldier->sGridNo),CenterX( gsWatchedLoc[ pSoldier->ubID ][ bHighestWatchLoc ] ),CenterY( gsWatchedLoc[ pSoldier->ubID ][ bHighestWatchLoc ] ) ); + + // if soldier is not already facing in that direction, + // and the opponent is close enough that he could possibly be seen + if ( pSoldier->ubDirection != ubOpponentDir && pSoldier->InternalIsValidStance( ubOpponentDir, gAnimControl[ pSoldier->usAnimState ].ubEndHeight ) ) + { + // turn + pSoldier->aiData.usActionData = ubOpponentDir; + pSoldier->aiData.bNextAction = AI_ACTION_END_TURN; + return(AI_ACTION_CHANGE_FACING); + } + else + { + // consider at least crouching + if ( gAnimControl[ pSoldier->usAnimState ].ubHeight != ANIM_PRONE ) + { + // end turn + return( AI_ACTION_END_TURN ); } } - // mark SEEKING as impossible for next time through while loop - #ifdef DEBUGDECISIONS - AINameMessage(pSoldier,"couldn't HELP...",1000); - #endif - - DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionred: couldn't help"); - bHelpPts = -99; } + + bWatchPts = -99; + DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionred: couldn't watch"); + + } + + + DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionred: checking to help"); + // if HELPING is possible and at least as desirable as seeking or hiding + if ((bHelpPts > -90) && (bHelpPts >= bSeekPts) && (bHelpPts >= bWatchPts )) + { +#ifdef AI_TIMING_TESTS + uiStartTime = GetJA2Clock(); +#endif + sClosestFriend = ClosestReachableFriendInTrouble(pSoldier, &fClimb ); +#ifdef AI_TIMING_TESTS + uiEndTime = GetJA2Clock(); + + guiRedHelpTimeTotal += (uiEndTime - uiStartTime); + guiRedHelpCounter++; +#endif + + if (!TileIsOutOfBounds(sClosestFriend)) + { + ////////////////////////////////////////////////////////////////////// + // GO DIRECTLY TOWARDS CLOSEST FRIEND UNDER FIRE OR WHO LAST RADIOED + ////////////////////////////////////////////////////////////////////// + pSoldier->aiData.usActionData = InternalGoAsFarAsPossibleTowards(pSoldier,sClosestFriend,GetAPsCrouch( pSoldier, TRUE), AI_ACTION_SEEK_OPPONENT,0); + + if (!TileIsOutOfBounds(pSoldier->aiData.usActionData)) + { +#ifdef DEBUGDECISIONS + sprintf(tempstr,"%s - SEEKING FRIEND at %d, MOVING to %d", + pSoldier->name,sClosestFriend,pSoldier->aiData.usActionData); + AIPopMessage(tempstr); +#endif + + if ( fClimb && gGameExternalOptions.fZombieCanClimb )//&& pSoldier->aiData.usActionData == sClosestFriend) + { + // need to climb AND have enough APs to get there this turn + BOOLEAN fUp = TRUE; + if (pSoldier->pathing.bLevel > 0 ) + fUp = FALSE; + + if (!fUp) + DebugMsg ( TOPIC_JA2AI , DBG_LEVEL_3 , String("Soldier %d is climbing down",pSoldier->ubID) ); + + // 0verhaul: Yet another chance to climb the wrong building and otherwise waste CPU power. + // We already know the climb point we want, which may not be here even if climbing is possible. + //if ( CanClimbFromHere ( pSoldier, fUp ) ) + if (pSoldier->sGridNo == sClosestFriend) + { + if (IsActionAffordable(pSoldier) && pSoldier->bActionPoints >= ( APBPConstants[AP_CLIMBROOF] + MinAPsToAttack( pSoldier, sClosestFriend, ADDTURNCOST, 0))) + { + return( AI_ACTION_CLIMB_ROOF ); + } + } + else + { + pSoldier->aiData.usActionData = sClosestFriend; + //INT32 sClimbPoint = FindClosestClimbPoint(pSoldier, pSoldier->sGridNo , sClosestFriend , fUp ); + //if (!TileIsOutOfBounds(sClimbPoint)) + { + //pSoldier->aiData.usActionData = sClimbPoint; + return( AI_ACTION_MOVE_TO_CLIMB ); + } + } + } + //if (fClimb && pSoldier->aiData.usActionData == sClosestFriend) + //{ + // return( AI_ACTION_CLIMB_ROOF ); + //} + return(AI_ACTION_SEEK_FRIEND); + } + } + + // mark SEEKING as impossible for next time through while loop +#ifdef DEBUGDECISIONS + AINameMessage(pSoldier,"couldn't HELP...",1000); +#endif + + DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionred: couldn't help"); + bHelpPts = -99; } } - DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionred: nothing to do!"); - //////////////////////////////////////////////////////////////////////////// - // NOTHING USEFUL POSSIBLE! IF NPC IS CURRENTLY UNDER FIRE, TRY TO RUN AWAY - //////////////////////////////////////////////////////////////////////////// } - - - DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionred: look around towards opponent"); - //////////////////////////////////////////////////////////////////////////// - // LOOK AROUND TOWARD CLOSEST KNOWN OPPONENT, IF KNOWN + DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionred: nothing to do!"); //////////////////////////////////////////////////////////////////////////// + // NOTHING USEFUL POSSIBLE! IF NPC IS CURRENTLY UNDER FIRE, TRY TO RUN AWAY + //////////////////////////////////////////////////////////////////////////// + } + + DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionred: look around towards opponent"); + //////////////////////////////////////////////////////////////////////////// + // LOOK AROUND TOWARD CLOSEST KNOWN OPPONENT, IF KNOWN + //////////////////////////////////////////////////////////////////////////// + + if (!gfTurnBasedAI || GetAPsToLook( pSoldier ) <= pSoldier->bActionPoints) + { + // determine the location of the known closest opponent + // (don't care if he's conscious, don't care if he's reachable at all) + sClosestOpponent = ClosestKnownOpponent(pSoldier, NULL, NULL); + + if (!TileIsOutOfBounds(sClosestOpponent)) + { + // determine direction from this soldier to the closest opponent + ubOpponentDir = atan8(CenterX(pSoldier->sGridNo),CenterY(pSoldier->sGridNo),CenterX(sClosestOpponent),CenterY(sClosestOpponent)); + + // if soldier is not already facing in that direction, + // and the opponent is close enough that he could possibly be seen + // note, have to change this to use the level returned from ClosestKnownOpponent + sDistVisible = pSoldier->GetMaxDistanceVisible(sClosestOpponent, 0, CALC_FROM_ALL_DIRS ); + + if ((pSoldier->ubDirection != ubOpponentDir) && (PythSpacesAway(pSoldier->sGridNo,sClosestOpponent) <= sDistVisible)) + { + // set base chance according to orders + if ((pSoldier->aiData.bOrders == STATIONARY) || (pSoldier->aiData.bOrders == ONGUARD)) + iChance = 50; + else // all other orders + iChance = 25; + + if (pSoldier->aiData.bAttitude == DEFENSIVE) + iChance += 25; + + if ( TANK( pSoldier ) ) + { + iChance += 50; + } + + if ((INT16)PreRandom(100) < iChance && pSoldier->InternalIsValidStance( ubOpponentDir, gAnimControl[ pSoldier->usAnimState ].ubEndHeight ) ) + { + pSoldier->aiData.usActionData = ubOpponentDir; + +#ifdef DEBUGDECISIONS + sprintf(tempstr,"%s - TURNS TOWARDS CLOSEST ENEMY to face direction %d",pSoldier->name,pSoldier->aiData.usActionData); + AIPopMessage(tempstr); +#endif + return(AI_ACTION_CHANGE_FACING); + } + } + } + } + + if ( TANK( pSoldier ) ) + { + // try turning in a random direction as we still can't see anyone. if (!gfTurnBasedAI || GetAPsToLook( pSoldier ) <= pSoldier->bActionPoints) - { - // determine the location of the known closest opponent - // (don't care if he's conscious, don't care if he's reachable at all) - sClosestOpponent = ClosestKnownOpponent(pSoldier, NULL, NULL); - - if (!TileIsOutOfBounds(sClosestOpponent)) - { - // determine direction from this soldier to the closest opponent - ubOpponentDir = atan8(CenterX(pSoldier->sGridNo),CenterY(pSoldier->sGridNo),CenterX(sClosestOpponent),CenterY(sClosestOpponent)); - - // if soldier is not already facing in that direction, - // and the opponent is close enough that he could possibly be seen - // note, have to change this to use the level returned from ClosestKnownOpponent - sDistVisible = pSoldier->GetMaxDistanceVisible(sClosestOpponent, 0, CALC_FROM_ALL_DIRS ); - - if ((pSoldier->ubDirection != ubOpponentDir) && (PythSpacesAway(pSoldier->sGridNo,sClosestOpponent) <= sDistVisible)) - { - // set base chance according to orders - if ((pSoldier->aiData.bOrders == STATIONARY) || (pSoldier->aiData.bOrders == ONGUARD)) - iChance = 50; - else // all other orders - iChance = 25; - - if (pSoldier->aiData.bAttitude == DEFENSIVE) - iChance += 25; - - if ( TANK( pSoldier ) ) - { - iChance += 50; - } - - if ((INT16)PreRandom(100) < iChance && pSoldier->InternalIsValidStance( ubOpponentDir, gAnimControl[ pSoldier->usAnimState ].ubEndHeight ) ) - { - pSoldier->aiData.usActionData = ubOpponentDir; - - #ifdef DEBUGDECISIONS - sprintf(tempstr,"%s - TURNS TOWARDS CLOSEST ENEMY to face direction %d",pSoldier->name,pSoldier->aiData.usActionData); - AIPopMessage(tempstr); - #endif - return(AI_ACTION_CHANGE_FACING); - } - } - } - } - - if ( TANK( pSoldier ) ) - { - // try turning in a random direction as we still can't see anyone. - if (!gfTurnBasedAI || GetAPsToLook( pSoldier ) <= pSoldier->bActionPoints) - { - sClosestDisturbance = MostImportantNoiseHeard( pSoldier, NULL, NULL, NULL ); - - if (!TileIsOutOfBounds(sClosestDisturbance)) - { - ubOpponentDir = atan8( CenterX( pSoldier->sGridNo ), CenterY( pSoldier->sGridNo ), CenterX( sClosestDisturbance ), CenterY( sClosestDisturbance ) ); - if ( pSoldier->ubDirection == ubOpponentDir ) - { - ubOpponentDir = (UINT8) PreRandom( NUM_WORLD_DIRECTIONS ); - } - } - else - { - ubOpponentDir = (UINT8) PreRandom( NUM_WORLD_DIRECTIONS ); - } - - if ( (pSoldier->ubDirection != ubOpponentDir) ) - { - if ( (pSoldier->bActionPoints == pSoldier->bInitialActionPoints || (INT16)PreRandom(100) < 60) && pSoldier->InternalIsValidStance( ubOpponentDir, gAnimControl[ pSoldier->usAnimState ].ubEndHeight ) ) - { - pSoldier->aiData.usActionData = ubOpponentDir; - - #ifdef DEBUGDECISIONS - sprintf(tempstr,"%s - TURNS TOWARDS CLOSEST ENEMY to face direction %d",pSoldier->name,pSoldier->aiData.usActionData); - AIPopMessage(tempstr); - #endif - - // limit turning a bit... if the last thing we did was also a turn, add a 60% chance of this being our last turn - if ( pSoldier->aiData.bLastAction == AI_ACTION_CHANGE_FACING && PreRandom( 100 ) < 60 ) - { - if ( gfTurnBasedAI ) - { - pSoldier->aiData.bNextAction = AI_ACTION_END_TURN; - } - else - { - pSoldier->aiData.bNextAction = AI_ACTION_WAIT; - pSoldier->aiData.usNextActionData = (UINT16) REALTIME_AI_DELAY; - } - } - - return(AI_ACTION_CHANGE_FACING); - } - } - } - - // that's it for tanks - return( AI_ACTION_NONE ); - } - - - //////////////////////////////////////////////////////////////////////////// - // SWITCH TO GREEN: soldier does ordinary regular patrol, seeks friends - //////////////////////////////////////////////////////////////////////////// - - // if not in combat or under fire, and we COULD have moved, just chose not to - if ( (pSoldier->aiData.bAlertStatus != STATUS_BLACK) && !pSoldier->aiData.bUnderFire && ubCanMove && (!gfTurnBasedAI || pSoldier->bActionPoints >= pSoldier->bInitialActionPoints) && ( TileIsOutOfBounds(ClosestReachableDisturbance(pSoldier, TRUE, &fClimb))) ) - { - // addition: if soldier is bleeding then reduce bleeding and do nothing - /*if ( pSoldier->bBleeding > MIN_BLEEDING_THRESHOLD ) - { - // no! zombies don't bandage themselves! - // reduce bleeding by 1 point per AP (in RT, APs will get recalculated so it's okay) - // pSoldier->bBleeding = __max( 0, pSoldier->bBleeding - (pSoldier->bActionPoints/2) ); - return( AI_ACTION_NONE ); // will end-turn/wait depending on whether we're in TB or realtime - }*/ - #ifdef RECORDNET - fprintf(NetDebugFile,"\tZombieDecideActionRed: guynum %d switching to GREEN AI...\n",pSoldier->ubID); - #endif - - #ifdef DEBUGDECISIONS - AINameMessage(pSoldier,"- chose to SKIP all RED actions, BYPASSES to GREEN!",1000); - #endif - - // Skip RED until new situation/next turn, 30% extra chance to do GREEN actions - pSoldier->aiData.bBypassToGreen = 30; - return(ZombieDecideActionGreen(pSoldier)); - } - - - //////////////////////////////////////////////////////////////////////////// - // IF UNDER FIRE, FACE THE MOST IMPORTANT NOISE WE KNOW - //////////////////////////////////////////////////////////////////////////// - - if ( pSoldier->aiData.bUnderFire && pSoldier->bActionPoints >= (pSoldier->bInitialActionPoints - GetAPsToLook( pSoldier ) ) ) { sClosestDisturbance = MostImportantNoiseHeard( pSoldier, NULL, NULL, NULL ); - + if (!TileIsOutOfBounds(sClosestDisturbance)) { ubOpponentDir = atan8( CenterX( pSoldier->sGridNo ), CenterY( pSoldier->sGridNo ), CenterX( sClosestDisturbance ), CenterY( sClosestDisturbance ) ); - if ( pSoldier->ubDirection != ubOpponentDir ) + if ( pSoldier->ubDirection == ubOpponentDir ) { - if ( !gfTurnBasedAI || GetAPsToLook( pSoldier ) <= pSoldier->bActionPoints ) + ubOpponentDir = (UINT8) PreRandom( NUM_WORLD_DIRECTIONS ); + } + } + else + { + ubOpponentDir = (UINT8) PreRandom( NUM_WORLD_DIRECTIONS ); + } + + if ( (pSoldier->ubDirection != ubOpponentDir) ) + { + if ( (pSoldier->bActionPoints == pSoldier->bInitialActionPoints || (INT16)PreRandom(100) < 60) && pSoldier->InternalIsValidStance( ubOpponentDir, gAnimControl[ pSoldier->usAnimState ].ubEndHeight ) ) + { + pSoldier->aiData.usActionData = ubOpponentDir; + +#ifdef DEBUGDECISIONS + sprintf(tempstr,"%s - TURNS TOWARDS CLOSEST ENEMY to face direction %d",pSoldier->name,pSoldier->aiData.usActionData); + AIPopMessage(tempstr); +#endif + + // limit turning a bit... if the last thing we did was also a turn, add a 60% chance of this being our last turn + if ( pSoldier->aiData.bLastAction == AI_ACTION_CHANGE_FACING && PreRandom( 100 ) < 60 ) { - pSoldier->aiData.usActionData = ubOpponentDir; - return( AI_ACTION_CHANGE_FACING ); + if ( gfTurnBasedAI ) + { + pSoldier->aiData.bNextAction = AI_ACTION_END_TURN; + } + else + { + pSoldier->aiData.bNextAction = AI_ACTION_WAIT; + pSoldier->aiData.usNextActionData = (UINT16) REALTIME_AI_DELAY; + } } + + return(AI_ACTION_CHANGE_FACING); } } } - - //////////////////////////////////////////////////////////////////////////// - // DO NOTHING: Not enough points left to move, so save them for next turn - //////////////////////////////////////////////////////////////////////////// - DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("DecideActionRed: do nothing at all...")); - #ifdef DEBUGDECISIONS - AINameMessage(pSoldier,"- DOES NOTHING (RED)",1000); - #endif + // that's it for tanks + return( AI_ACTION_NONE ); + } + + + //////////////////////////////////////////////////////////////////////////// + // SWITCH TO GREEN: soldier does ordinary regular patrol, seeks friends + //////////////////////////////////////////////////////////////////////////// + + // if not in combat or under fire, and we COULD have moved, just chose not to + if ( (pSoldier->aiData.bAlertStatus != STATUS_BLACK) && !pSoldier->aiData.bUnderFire && ubCanMove && (!gfTurnBasedAI || pSoldier->bActionPoints >= pSoldier->bInitialActionPoints) && ( TileIsOutOfBounds(ClosestReachableDisturbance(pSoldier, TRUE, &fClimb))) ) + { + // addition: if soldier is bleeding then reduce bleeding and do nothing + /*if ( pSoldier->bBleeding > MIN_BLEEDING_THRESHOLD ) + { + // no! zombies don't bandage themselves! + // reduce bleeding by 1 point per AP (in RT, APs will get recalculated so it's okay) + // pSoldier->bBleeding = __max( 0, pSoldier->bBleeding - (pSoldier->bActionPoints/2) ); + return( AI_ACTION_NONE ); // will end-turn/wait depending on whether we're in TB or realtime + }*/ +#ifdef RECORDNET + fprintf(NetDebugFile,"\tZombieDecideActionRed: guynum %d switching to GREEN AI...\n",pSoldier->ubID); +#endif + +#ifdef DEBUGDECISIONS + AINameMessage(pSoldier,"- chose to SKIP all RED actions, BYPASSES to GREEN!",1000); +#endif + + // Skip RED until new situation/next turn, 30% extra chance to do GREEN actions + pSoldier->aiData.bBypassToGreen = 30; + return(ZombieDecideActionGreen(pSoldier)); + } + + + //////////////////////////////////////////////////////////////////////////// + // IF UNDER FIRE, FACE THE MOST IMPORTANT NOISE WE KNOW + //////////////////////////////////////////////////////////////////////////// + + if ( pSoldier->aiData.bUnderFire && pSoldier->bActionPoints >= (pSoldier->bInitialActionPoints - GetAPsToLook( pSoldier ) ) ) + { + sClosestDisturbance = MostImportantNoiseHeard( pSoldier, NULL, NULL, NULL ); + + if (!TileIsOutOfBounds(sClosestDisturbance)) + { + ubOpponentDir = atan8( CenterX( pSoldier->sGridNo ), CenterY( pSoldier->sGridNo ), CenterX( sClosestDisturbance ), CenterY( sClosestDisturbance ) ); + if ( pSoldier->ubDirection != ubOpponentDir ) + { + if ( !gfTurnBasedAI || GetAPsToLook( pSoldier ) <= pSoldier->bActionPoints ) + { + pSoldier->aiData.usActionData = ubOpponentDir; + return( AI_ACTION_CHANGE_FACING ); + } + } + } + } + + //////////////////////////////////////////////////////////////////////////// + // DO NOTHING: Not enough points left to move, so save them for next turn + //////////////////////////////////////////////////////////////////////////// + DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("DecideActionRed: do nothing at all...")); +#ifdef DEBUGDECISIONS + AINameMessage(pSoldier,"- DOES NOTHING (RED)",1000); +#endif + + pSoldier->aiData.usActionData = NOWHERE; + return(AI_ACTION_NONE); +} + +INT8 ZombieDecideActionBlack(SOLDIERTYPE *pSoldier) +{ + INT32 iCoverPercentBetter = 0; + INT32 iOffense, iDefense, iChance; + INT32 sClosestOpponent,sBestCover = NOWHERE; + INT16 ubMinAPCost; + UINT8 ubCanMove; + INT8 bInWater,bInDeepWater; + INT8 bDirection; + UINT8 ubBestAttackAction = AI_ACTION_NONE; + INT8 bCanAttack; + INT8 bWeaponIn; + BOOLEAN fTryPunching = FALSE; +#ifdef DEBUGDECISIONS + STR16 tempstr; +#endif + DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("DecideActionBlack: soldier = %d, orders = %d, attitude = %d",pSoldier->ubID,pSoldier->aiData.bOrders,pSoldier->aiData.bAttitude)); + + ATTACKTYPE BestStab, BestAttack;//dnl ch69 150913 + UINT8 ubBestStance = 1; + BOOLEAN fChangeStanceFirst; // before firing + INT16 ubBurstAPs; + + BOOLEAN fAllowCoverCheck = FALSE; + + DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"DecideActionBlack"); + + // if we have absolutely no action points, we can't do a thing under BLACK! + if (!pSoldier->bActionPoints) + { pSoldier->aiData.usActionData = NOWHERE; return(AI_ACTION_NONE); } - INT8 ZombieDecideActionBlack(SOLDIERTYPE *pSoldier) + // can this guy move to any of the neighbouring squares ? (sets TRUE/FALSE) + ubCanMove = (pSoldier->bActionPoints >= MinPtsToMove(pSoldier)); + + // determine if we happen to be in water (in which case we're in BIG trouble!) + bInWater = Water( pSoldier->sGridNo ); + bInDeepWater = WaterTooDeepForAttacks( pSoldier->sGridNo ); + + // calculate our morale + pSoldier->aiData.bAIMorale = CalcMorale(pSoldier); + + //////////////////////////////////////////////////////////////////////////// + // STUCK IN WATER OR GAS, NO COVER, GO TO NEAREST SPOT OF UNGASSED LAND + //////////////////////////////////////////////////////////////////////////// + + // if soldier in water/gas has enough APs left to move at least 1 square + if ( bInDeepWater && ubCanMove) { - INT32 iCoverPercentBetter = 0; - INT32 iOffense, iDefense, iChance; - INT32 sClosestOpponent,sBestCover = NOWHERE; - INT16 ubMinAPCost; - UINT8 ubCanMove; - INT8 bInWater,bInDeepWater; - INT8 bDirection; - UINT8 ubBestAttackAction = AI_ACTION_NONE; - INT8 bCanAttack; - INT8 bWeaponIn; - BOOLEAN fTryPunching = FALSE; - #ifdef DEBUGDECISIONS - STR16 tempstr; - #endif - DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("DecideActionBlack: soldier = %d, orders = %d, attitude = %d",pSoldier->ubID,pSoldier->aiData.bOrders,pSoldier->aiData.bAttitude)); - - ATTACKTYPE BestStab, BestAttack;//dnl ch69 150913 - UINT8 ubBestStance = 1; - BOOLEAN fChangeStanceFirst; // before firing - INT16 ubBurstAPs; - - BOOLEAN fAllowCoverCheck = FALSE; - - DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"DecideActionBlack"); - - // if we have absolutely no action points, we can't do a thing under BLACK! - if (!pSoldier->bActionPoints) + pSoldier->aiData.usActionData = FindNearestUngassedLand(pSoldier); + + if (!TileIsOutOfBounds(pSoldier->aiData.usActionData)) { - pSoldier->aiData.usActionData = NOWHERE; - return(AI_ACTION_NONE); +#ifdef DEBUGDECISIONS + sprintf(tempstr,"%s - SEEKING NEAREST UNGASSED LAND at grid %d",pSoldier->name,pSoldier->aiData.usActionData); + AIPopMessage(tempstr); +#endif + + return(AI_ACTION_LEAVE_WATER_GAS); } - // can this guy move to any of the neighbouring squares ? (sets TRUE/FALSE) - ubCanMove = (pSoldier->bActionPoints >= MinPtsToMove(pSoldier)); - - // determine if we happen to be in water (in which case we're in BIG trouble!) - bInWater = Water( pSoldier->sGridNo ); - bInDeepWater = WaterTooDeepForAttacks( pSoldier->sGridNo ); + // couldn't find ANY land within 25 tiles(!), this should never happen... + + // look for best place to RUN AWAY to (farthest from the closest threat) + pSoldier->aiData.usActionData = FindSpotMaxDistFromOpponents(pSoldier); - // calculate our morale - pSoldier->aiData.bAIMorale = CalcMorale(pSoldier); - - //////////////////////////////////////////////////////////////////////////// - // STUCK IN WATER OR GAS, NO COVER, GO TO NEAREST SPOT OF UNGASSED LAND - //////////////////////////////////////////////////////////////////////////// - - // if soldier in water/gas has enough APs left to move at least 1 square - if ( bInDeepWater && ubCanMove) + if (!TileIsOutOfBounds(pSoldier->aiData.usActionData)) { - pSoldier->aiData.usActionData = FindNearestUngassedLand(pSoldier); - - if (!TileIsOutOfBounds(pSoldier->aiData.usActionData)) +#ifdef DEBUGDECISIONS + sprintf(tempstr,"%s - NO LAND NEAR, RUNNING AWAY to grid %d",pSoldier->name,pSoldier->aiData.usActionData); + AIPopMessage(tempstr); +#endif + + return(AI_ACTION_RUN_AWAY); + } + + if ( gGameOptions.ubDifficultyLevel == DIF_LEVEL_INSANE ) + pSoldier->aiData.bAIMorale = MORALE_FEARLESS; // go nuts... + } + + + //////////////////////////////////////////////////////////////////////////// + // SOLDIER CAN ATTACK IF NOT IN WATER/GAS AND NOT DOING SOMETHING TOO FUNKY + //////////////////////////////////////////////////////////////////////////// + + + // NPCs in water/tear gas without masks are not permitted to shoot/stab/throw + if ((pSoldier->bActionPoints < 2) || bInDeepWater || pSoldier->aiData.bRTPCombat == RTP_COMBAT_REFRAIN) + { + bCanAttack = FALSE; + } + else if (pSoldier->flags.uiStatusFlags & SOLDIER_BOXER) + { + bCanAttack = TRUE; + fTryPunching = TRUE; + } + else + { + do + { + + bCanAttack = CanNPCAttack(pSoldier); + if (bCanAttack != TRUE) { - #ifdef DEBUGDECISIONS - sprintf(tempstr,"%s - SEEKING NEAREST UNGASSED LAND at grid %d",pSoldier->name,pSoldier->aiData.usActionData); - AIPopMessage(tempstr); - #endif + if (bCanAttack == NOSHOOT_NOAMMO && ubCanMove && !pSoldier->aiData.bNeutral) + { + //CHRISL: We need to know which weapon has no ammo in case the soldier is holding a weapoin in SECONDHANDPOS + if(pSoldier->inv[SECONDHANDPOS].exists() == true ) + { + // the current weapon appears is useless right now! + pSoldier->inv[HANDPOS].fFlags |= OBJECT_AI_UNUSABLE; - return(AI_ACTION_LEAVE_WATER_GAS); + // drop item + return AI_ACTION_DROP_ITEM; + } + else + { + return( pSoldier->aiData.bAction ); + } + } + else + { + bCanAttack = FALSE; + } } + } while( bCanAttack != TRUE && bCanAttack != FALSE ); - // couldn't find ANY land within 25 tiles(!), this should never happen... +#ifdef RETREAT_TESTING + bCanAttack = FALSE; +#endif - // look for best place to RUN AWAY to (farthest from the closest threat) - pSoldier->aiData.usActionData = FindSpotMaxDistFromOpponents(pSoldier); - - if (!TileIsOutOfBounds(pSoldier->aiData.usActionData)) - { - #ifdef DEBUGDECISIONS - sprintf(tempstr,"%s - NO LAND NEAR, RUNNING AWAY to grid %d",pSoldier->name,pSoldier->aiData.usActionData); - AIPopMessage(tempstr); - #endif - - return(AI_ACTION_RUN_AWAY); - } - - if ( gGameOptions.ubDifficultyLevel == DIF_LEVEL_INSANE ) - pSoldier->aiData.bAIMorale = MORALE_FEARLESS; // go nuts... - } - - - //////////////////////////////////////////////////////////////////////////// - // SOLDIER CAN ATTACK IF NOT IN WATER/GAS AND NOT DOING SOMETHING TOO FUNKY - //////////////////////////////////////////////////////////////////////////// - - - // NPCs in water/tear gas without masks are not permitted to shoot/stab/throw - if ((pSoldier->bActionPoints < 2) || bInDeepWater || pSoldier->aiData.bRTPCombat == RTP_COMBAT_REFRAIN) - { - bCanAttack = FALSE; - } - else if (pSoldier->flags.uiStatusFlags & SOLDIER_BOXER) + if (!bCanAttack) { bCanAttack = TRUE; fTryPunching = TRUE; } - else - { - do - { + } - bCanAttack = CanNPCAttack(pSoldier); - if (bCanAttack != TRUE) - { - if (bCanAttack == NOSHOOT_NOAMMO && ubCanMove && !pSoldier->aiData.bNeutral) - { - //CHRISL: We need to know which weapon has no ammo in case the soldier is holding a weapoin in SECONDHANDPOS - if(pSoldier->inv[SECONDHANDPOS].exists() == true ) - { - // the current weapon appears is useless right now! - pSoldier->inv[HANDPOS].fFlags |= OBJECT_AI_UNUSABLE; + BestStab.ubPossible = FALSE; // by default, assume Stabbing isn't possible - // drop item - return AI_ACTION_DROP_ITEM; - } - else - { - return( pSoldier->aiData.bAction ); - } - } - else - { - bCanAttack = FALSE; - } - } - } while( bCanAttack != TRUE && bCanAttack != FALSE ); - - #ifdef RETREAT_TESTING - bCanAttack = FALSE; - #endif - - if (!bCanAttack) - { - bCanAttack = TRUE; - fTryPunching = TRUE; - } - } - - BestStab.ubPossible = FALSE; // by default, assume Stabbing isn't possible - - BestAttack.ubChanceToReallyHit = 0; + BestAttack.ubChanceToReallyHit = 0; - // if we are able attack - if (bCanAttack) - { - pSoldier->bAimShotLocation = AIM_SHOT_RANDOM; + // if we are able attack + if (bCanAttack) + { + pSoldier->bAimShotLocation = AIM_SHOT_RANDOM; - ////////////////////////////////////////////////////////////////////////// - // GO STAB AN OPPONENT WITH A KNIFE - ////////////////////////////////////////////////////////////////////////// - - DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"GO STAB AN OPPONENT WITH A KNIFE"); - // if soldier has a knife in his hand - bWeaponIn = FindAIUsableObjClass( pSoldier, (IC_BLADE | IC_THROWING_KNIFE) ); - - ///////////////////////////////////////////////////////////////////////////////////////////////////// - // SANDRO - even if we don't have any blade, calculate how much damage we could do unarmed - if ( !TANK( pSoldier) ) - { - bWeaponIn = FindAIUsableObjClass( pSoldier, IC_PUNCH ); - if (bWeaponIn == NO_SLOT) // if no punch-type weapon found, just calculate it with empty hands - { - bWeaponIn = FindEmptySlotWithin( pSoldier, HANDPOS, NUM_INV_SLOTS ); - } - if (bWeaponIn != NO_SLOT) - { - DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"try to punch"); - BestStab.bWeaponIn = bWeaponIn; - // if it's in his holster, swap it into his hand temporarily - if (bWeaponIn != HANDPOS) - { - DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionblack: about to rearrange pocket before punch check"); - RearrangePocket(pSoldier,HANDPOS,bWeaponIn,TEMPORARILY); - } - - // get the minimum cost to attack with punch - ubMinAPCost = MinAPsToAttack(pSoldier,pSoldier->sLastTarget,DONTADDTURNCOST,0); - // if we can afford the minimum AP cost to punch - if (pSoldier->bActionPoints >= ubMinAPCost) - { - // then look around for a worthy target (which sets BestStab.ubPossible) - CalcBestStab(pSoldier,&BestStab, FALSE); - - if (BestStab.ubPossible) - { - // if we have not enough APs to deal at least two or three punches, - // reduce the attack value as one punch ain't much - if( gGameOptions.fNewTraitSystem ) - { - // if we are not specialized, reduce the attack attractiveness generaly - if ( !HAS_SKILL_TRAIT( pSoldier, MARTIAL_ARTS_NT ) ) - { - // if too far and not having APs for at least 3 hits no way to attack - if (((CalcTotalAPsToAttack( pSoldier,BestStab.sTarget,ADDTURNCOST, 0 ) + (2 * (ApsToPunch( pSoldier )))) > pSoldier->bActionPoints) && !(PythSpacesAway(pSoldier->sGridNo, BestStab.sTarget) <= 1) ) - { - BestStab.ubPossible = 0; - BestStab.iAttackValue = 0; - } - } - else - { - if (PythSpacesAway(pSoldier->sGridNo, BestStab.sTarget) <= 1) - { - BestStab.iAttackValue = (BestStab.iAttackValue * 2); - } - // if too far and not having APs for at least 2 hits - else if (((CalcTotalAPsToAttack( pSoldier,BestStab.sTarget,ADDTURNCOST, 0 ) + ApsToPunch( pSoldier )) > pSoldier->bActionPoints) && !(PythSpacesAway(pSoldier->sGridNo, BestStab.sTarget) <= 1)) - { - BestStab.iAttackValue /= 3; - } - } - } - else - { - if ( !HAS_SKILL_TRAIT( pSoldier, MARTIALARTS_OT ) && !HAS_SKILL_TRAIT( pSoldier, HANDTOHAND_OT ) ) - { - // if we are not specialized, reduce the attack attractiveness generaly - BestStab.iAttackValue /= 4; - // if too far and not having APs for at least 3 hits - if (((CalcTotalAPsToAttack( pSoldier,BestStab.sTarget,ADDTURNCOST, 0 ) + (2 * (ApsToPunch( pSoldier )))) > pSoldier->bActionPoints) && !(PythSpacesAway(pSoldier->sGridNo, BestStab.sTarget <= 1)) ) - { - BestStab.ubPossible = 0; - BestStab.iAttackValue = 0; - } - } - else - { - BestStab.iAttackValue = ((BestStab.iAttackValue * 3)/2); - - if (PythSpacesAway(pSoldier->sGridNo, BestStab.sTarget) <= 1) - { - BestStab.iAttackValue = ((BestStab.iAttackValue * 3)/2); - } - // if too far and not having APs for at least 2 hits - else if (((CalcTotalAPsToAttack( pSoldier,BestStab.sTarget,ADDTURNCOST, 0 ) + ApsToPunch( pSoldier )) > pSoldier->bActionPoints) && !(PythSpacesAway(pSoldier->sGridNo, BestStab.sTarget <= 1)) ) - { - BestStab.iAttackValue /= 3; - } - } - } - // now we KNOW FOR SURE that we will do something (stab, at least) - NPCDoesAct(pSoldier); - DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"NPC decided to punch"); - } - } - // if it was in his holster, swap it back into his holster for now - if (bWeaponIn != HANDPOS) - { - DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"about to rearrange pocket after punch check"); - RearrangePocket(pSoldier,HANDPOS,bWeaponIn,TEMPORARILY); - } - } - } - ///////////////////////////////////////////////////////////////////////////////////////////////////// - - ////////////////////////////////////////////////////////////////////////// - // CHOOSE THE BEST TYPE OF ATTACK OUT OF THOSE FOUND TO BE POSSIBLE - ////////////////////////////////////////////////////////////////////////// - DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"CHOOSE THE BEST TYPE OF ATTACK OUT OF THOSE FOUND TO BE POSSIBLE"); - - BestAttack.iAttackValue = 0; - - if (BestStab.ubPossible && ((BestStab.iAttackValue > BestAttack.iAttackValue) || (ubBestAttackAction == AI_ACTION_NONE))) - { - // zombies are always punching... - BestAttack.iAttackValue = BestStab.iAttackValue; - ubBestAttackAction = AI_ACTION_KNIFE_MOVE; - DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"best action = move to punch"); - //////////////////////////////////////////////////////////////////////////////////// - } - - if ( ( ubBestAttackAction == AI_ACTION_NONE ) && fTryPunching ) - { - // nothing (else) to attack with so let's try hand-to-hand - bWeaponIn = FindObj( pSoldier, NOTHING, HANDPOS, NUM_INV_SLOTS ); - - if (bWeaponIn != NO_SLOT) - { - BestStab.bWeaponIn = bWeaponIn; - // if it's in his holster, swap it into his hand temporarily - if (bWeaponIn != HANDPOS) - { - DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionblack: swap knife into hand"); - RearrangePocket(pSoldier,HANDPOS,bWeaponIn,TEMPORARILY); - } - - // get the minimum cost to attack by HTH - ubMinAPCost = MinAPsToAttack(pSoldier,pSoldier->sLastTarget,DONTADDTURNCOST,0,0); - - // if we can afford the minimum AP cost to use HTH combat - if (pSoldier->bActionPoints >= ubMinAPCost) - { - // then look around for a worthy target (which sets BestStab.ubPossible) - CalcBestStab(pSoldier,&BestStab, FALSE); - - if (BestStab.ubPossible && pSoldier->ubSoldierClass == SOLDIER_CLASS_ZOMBIE) - { - // now we KNOW FOR SURE that we will do something (stab, at least) - NPCDoesAct(pSoldier); - ubBestAttackAction = AI_ACTION_KNIFE_MOVE; - DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"NPC decided to move to stab"); - } - } - - // if it was in his holster, swap it back into his holster for now - if (bWeaponIn != HANDPOS) - { - DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionblack: about to put knife away"); - RearrangePocket(pSoldier,HANDPOS,bWeaponIn,TEMPORARILY); - } - } - } - - - // copy the information on the best action selected into BestAttack struct - DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"copy the information on the best action selected into BestAttack struct"); - switch (ubBestAttackAction) - { - case AI_ACTION_KNIFE_MOVE: - DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"DecideActionBlack: best attack = stab with a knife"); - memcpy(&BestAttack,&BestStab,sizeof(BestAttack)); - break; - - default: - // set to empty - DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"DecideActionBlack: best attack = no good attack"); - memset( &BestAttack, 0, sizeof( BestAttack ) ); - break; - } - } - - // NB a desire of 4 or more is only achievable by brave/aggressive guys with high morale - //UINT16 usRange = GetModifiedGunRange(pSoldier->inv[ BestAttack.bWeaponIn ].usItem);//dnl ch69 150913 not in use - - DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"DecideActionBlack: DECIDE BETWEEN ATTACKING AND DEFENDING (TAKING COVER)"); ////////////////////////////////////////////////////////////////////////// - // IF NECESSARY, DECIDE BETWEEN ATTACKING AND DEFENDING (TAKING COVER) + // GO STAB AN OPPONENT WITH A KNIFE ////////////////////////////////////////////////////////////////////////// - // if both are possible - if ((ubBestAttackAction != AI_ACTION_NONE) && ( !TileIsOutOfBounds(sBestCover))) + DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"GO STAB AN OPPONENT WITH A KNIFE"); + // if soldier has a knife in his hand + bWeaponIn = FindAIUsableObjClass( pSoldier, (IC_BLADE | IC_THROWING_KNIFE) ); + + ///////////////////////////////////////////////////////////////////////////////////////////////////// + // SANDRO - even if we don't have any blade, calculate how much damage we could do unarmed + if ( !TANK( pSoldier) ) { - // gotta compare their merits and select the more desirable option - iOffense = BestAttack.ubChanceToReallyHit; - iDefense = iCoverPercentBetter; - - // based on how we feel about the situation, decide whether to attack first - switch (pSoldier->aiData.bAIMorale) + bWeaponIn = FindAIUsableObjClass( pSoldier, IC_PUNCH ); + if (bWeaponIn == NO_SLOT) // if no punch-type weapon found, just calculate it with empty hands { - case MORALE_FEARLESS: - iOffense += iOffense / 2; // increase 50% - break; - - case MORALE_CONFIDENT: - iOffense += iOffense / 4; // increase 25% - break; - - case MORALE_NORMAL: - break; - - case MORALE_WORRIED: - iDefense += iDefense / 4; // increase 25% - break; - - case MORALE_HOPELESS: - iDefense += iDefense / 2; // increase 50% - break; + bWeaponIn = FindEmptySlotWithin( pSoldier, HANDPOS, NUM_INV_SLOTS ); } - - - // smart guys more likely to try to stay alive, dolts more likely to shoot! - if (pSoldier->stats.bWisdom >= 50) //Madd: reduced the wisdom required to want to live... - iDefense += 10; - else if (pSoldier->stats.bWisdom < 30) - iDefense -= 10; - - // some orders are more offensive, others more defensive - if (pSoldier->aiData.bOrders == SEEKENEMY) - iOffense += 10; - else if ((pSoldier->aiData.bOrders == STATIONARY) || (pSoldier->aiData.bOrders == ONGUARD) || pSoldier->aiData.bOrders == SNIPER ) - iDefense += 10; - - switch (pSoldier->aiData.bAttitude) + if (bWeaponIn != NO_SLOT) { - case DEFENSIVE: iDefense += 30; break; - case BRAVESOLO: iDefense -= 0; break; - case BRAVEAID: iDefense -= 0; break; - case CUNNINGSOLO: iDefense += 20; break; - case CUNNINGAID: iDefense += 20; break; - case AGGRESSIVE: iOffense += 10; break; - case ATTACKSLAYONLY:iOffense += 30; break; - } - - #ifdef DEBUGDECISIONS - STR tempstr=""; - sprintf( tempstr, "%s - CHOICE: iOffense = %d, iDefense = %d\n", - pSoldier->name,iOffense,iDefense); - DebugAI( tempstr ); - #endif - - DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"DecideActionBlack: if his defensive instincts win out, forget all about the attack"); - // if his defensive instincts win out, forget all about the attack - if (iDefense > iOffense) - ubBestAttackAction = AI_ACTION_NONE; - } - - - DebugMsg (TOPIC_JA2,DBG_LEVEL_3,String("DecideActionBlack: is attack still desirable? ubBestAttackAction = %d",ubBestAttackAction)); - - // if attack is still desirable (meaning it's also preferred to taking cover) - if (ubBestAttackAction != AI_ACTION_NONE) - { - DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"DecideActionBlack: attack is still desirable (meaning it's also preferred to taking cover)"); - // if we wanted to be REALLY mean, we could look at chance to hit and decide whether - // to shoot at the head... - - fChangeStanceFirst = FALSE; - - // default settings - //POSSIBLE STRUCTURE CHANGE PROBLEM, NOT CURRENTLY CHANGED. GOTTHARD 7/14/08 - pSoldier->aiData.bAimTime = BestAttack.ubAimTime; - pSoldier->bDoBurst = 0; - - // HEADROCK HAM 3.6: bAimTime represents how MANY aiming levels are used, not how much APs they cost necessarily. - INT16 sActualAimTime = CalcAPCostForAiming( pSoldier, BestAttack.sTarget, (INT8)pSoldier->aiData.bAimTime ); - - if (ubBestAttackAction == AI_ACTION_KNIFE_MOVE && gGameOptions.fNewTraitSystem) - { - pSoldier->aiData.bAimTime = 0; - - if (Item[pSoldier->inv[BestAttack.bWeaponIn].usItem].usItemClass == IC_PUNCH) + DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"try to punch"); + BestStab.bWeaponIn = bWeaponIn; + // if it's in his holster, swap it into his hand temporarily + if (bWeaponIn != HANDPOS) { - if (HAS_SKILL_TRAIT( pSoldier, MARTIAL_ARTS_NT)) - { - if( PreRandom( (gGameExternalOptions.fEnhancedCloseCombatSystem ? 2 : 0) + 2 * NUM_SKILL_TRAITS( pSoldier, MARTIAL_ARTS_NT) ) > 2 ) - { - pSoldier->aiData.bAimTime = (gGameExternalOptions.fEnhancedCloseCombatSystem ? gSkillTraitValues.ubModifierForAPsAddedOnAimedPunches : 6); - } - } - } - else - { - if (HAS_SKILL_TRAIT( pSoldier, MELEE_NT)) - { - if( PreRandom( gGameExternalOptions.fEnhancedCloseCombatSystem ? 3 : 1 ) > 0 ) - { - pSoldier->aiData.bAimTime = (gGameExternalOptions.fEnhancedCloseCombatSystem ? gSkillTraitValues.ubModifierForAPsAddedOnAimedBladedAttackes : 6); - } - } - } - } - - ////////////////////////////////////////////////////////////////////////// - // OTHERWISE, JUST GO AHEAD & ATTACK! - ////////////////////////////////////////////////////////////////////////// - DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"OTHERWISE, JUST GO AHEAD & ATTACK!"); - - // swap weapon to hand if necessary - if (BestAttack.bWeaponIn != HANDPOS) - { - DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionblack: swap weapon into hand"); - RearrangePocket(pSoldier,HANDPOS,BestAttack.bWeaponIn,FOREVER); - } - - if (fChangeStanceFirst) - { // currently only for guns... - pSoldier->aiData.bNextAction = AI_ACTION_FIRE_GUN; - pSoldier->aiData.usNextActionData = BestAttack.sTarget; - pSoldier->aiData.bNextTargetLevel = BestAttack.bTargetLevel; - pSoldier->aiData.usActionData = ubBestStance; - return( AI_ACTION_CHANGE_STANCE ); - } - else - { - pSoldier->aiData.usActionData = BestAttack.sTarget; - pSoldier->bTargetLevel = BestAttack.bTargetLevel; - - #ifdef DEBUGDECISIONS - STR tempstr=""; - sprintf( tempstr, - "%d(%s) %s %d(%s) at gridno %d (%d APs aim)\n", - pSoldier->ubID,pSoldier->name, - (ubBestAttackAction == AI_ACTION_FIRE_GUN)?"SHOOTS":((ubBestAttackAction == AI_ACTION_TOSS_PROJECTILE)?"TOSSES AT":"STABS"), - BestAttack.ubOpponent,pSoldier->name , - BestAttack.sTarget,BestAttack.ubAimTime ); - DebugAI( tempstr); - #endif - - //DebugMsg(TOPIC_JA2, DBG_LEVEL_3, String("DecideActionBlack: Check for GL Bursts, is launcher capable? = %d, rtpcombat? = %d, bestattackaction = %d",IsGunBurstCapable( pSoldier, BestAttack.bWeaponIn, FALSE ),pSoldier->aiData.bRTPCombat,ubBestAttackAction )); - //should be a bug - DebugMsg(TOPIC_JA2, DBG_LEVEL_3, String("DecideActionBlack: Check for GL Bursts, is launcher capable? = %d, rtpcombat? = %d, bestattackaction = %d",IsGunBurstCapable( &pSoldier->inv[BestAttack.bWeaponIn], FALSE, pSoldier ),pSoldier->aiData.bRTPCombat,ubBestAttackAction )); - if (ubBestAttackAction == AI_ACTION_TOSS_PROJECTILE && (Item[pSoldier->inv[BestAttack.bWeaponIn].usItem].usItemClass == IC_LAUNCHER && IsGunBurstCapable( &pSoldier->inv[BestAttack.bWeaponIn], FALSE, pSoldier )) && - (pSoldier->bTeam != gbPlayerNum || pSoldier->aiData.bRTPCombat == RTP_COMBAT_AGGRESSIVE) ) - { - - DebugMsg(TOPIC_JA2, DBG_LEVEL_3, "DecideActionBlack: Doing burst calc"); - ubBurstAPs = CalcAPsToBurst( pSoldier->CalcActionPoints(), &(pSoldier->inv[BestAttack.bWeaponIn]), pSoldier ); - - if ( (pSoldier->bActionPoints - BestAttack.ubAPCost) >= ubBurstAPs ) - { - // Base chance of bursting is 25% if best shot was +0 aim, down to 8% at +4 - if ( TANK( pSoldier ) ) - { - iChance = 100; - } - else - { - iChance = 50; - switch (pSoldier->aiData.bAttitude) - { - case DEFENSIVE: iChance += -5; break; - case BRAVESOLO: iChance += 5; break; - case BRAVEAID: iChance += 5; break; - case CUNNINGSOLO: iChance += 0; break; - case CUNNINGAID: iChance += 0; break; - case AGGRESSIVE: iChance += 10; break; - case ATTACKSLAYONLY:iChance += 30; break; - } - // increase chance based on proximity and difficulty of enemy - DebugMsg(TOPIC_JA2AI,DBG_LEVEL_3,String("DecideActionBlack: check chance to gl burst")); - iChance += ( 15 - PythSpacesAway( pSoldier->sGridNo, BestAttack.sTarget ) ) * ( 1 + SoldierDifficultyLevel( pSoldier ) ); - } - - if ( (INT32) PreRandom( 100 ) < iChance) - { - DebugMsg(TOPIC_JA2, DBG_LEVEL_3, "DecideActionBlack: Doing GL burst"); - BestAttack.ubAPCost = BestAttack.ubAPCost + CalcAPsToBurst( pSoldier->CalcActionPoints(), &(pSoldier->inv[HANDPOS]), pSoldier ); - // check for spread burst possibilities - if (pSoldier->aiData.bAttitude != ATTACKSLAYONLY) - { - CalcSpreadBurst( pSoldier, BestAttack.sTarget, BestAttack.bTargetLevel ); - } - //dnl ch58 140913 After HAM 4 BURSTING is not in use any more, for burst bDoAutofire must be set to 0 - pSoldier->bDoBurst = 1; - pSoldier->bDoAutofire = 0; - } - } + DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionblack: about to rearrange pocket before punch check"); + RearrangePocket(pSoldier,HANDPOS,bWeaponIn,TEMPORARILY); } - - return(ubBestAttackAction); - } - - } - - // end of tank AI - if( gGameExternalOptions.fEnemyTanksCanMoveInTactical || TANK( pSoldier ) ) - { - return( AI_ACTION_NONE ); - } - - // get the location of the closest reachable opponent - /* Flugente 22.02.2012 - A few clarifications: I changed ClosestSeenOpponent so that for zombies, this function also returns an opponent if he is on the - * roof of a building, we are not, but our GridNo belongs to that same building. - * If that is the case, it is clear that we have to get on that roof. However, we cannot do that in BlackState. If, by pure chance, we can still see our - * enemy, we cannot climb (there is no climbing option in BlackState sofar). - * So, I changed the code so that now we will climb the roof. - */ - INT32 targetGridNo = -1; - INT8 targetbLevel = 0; - sClosestOpponent = ClosestSeenOpponentWithRoof(pSoldier, &targetGridNo, &targetbLevel); - if ( pSoldier->ubSoldierClass == SOLDIER_CLASS_ZOMBIE ) - { - if (!TileIsOutOfBounds(sClosestOpponent) && ! ( (targetbLevel != pSoldier->pathing.bLevel) && SameBuilding( pSoldier->sGridNo, targetGridNo ) ) ) - { - ////////////////////////////////////////////////////////////////////// - // GO DIRECTLY TOWARDS CLOSEST KNOWN OPPONENT - ////////////////////////////////////////////////////////////////////// - - // try to move towards him - pSoldier->aiData.usActionData = GoAsFarAsPossibleTowards(pSoldier,sClosestOpponent,AI_ACTION_GET_CLOSER); - - // if it's possible - if (!TileIsOutOfBounds(sClosestOpponent)) + // get the minimum cost to attack with punch + ubMinAPCost = MinAPsToAttack(pSoldier,pSoldier->sLastTarget,DONTADDTURNCOST,0); + // if we can afford the minimum AP cost to punch + if (pSoldier->bActionPoints >= ubMinAPCost) { - if (targetbLevel != pSoldier->pathing.bLevel && gGameExternalOptions.fZombieCanClimb ) - return(AI_ACTION_MOVE_TO_CLIMB); - else + // then look around for a worthy target (which sets BestStab.ubPossible) + CalcBestStab(pSoldier,&BestStab, FALSE); + + if (BestStab.ubPossible) { - // Flugente: if on the same level and there is a jumpable window here, jump through it - if ( gGameExternalOptions.fZombieCanJumpWindows && targetbLevel == pSoldier->pathing.bLevel && targetbLevel == 0 ) + // if we have not enough APs to deal at least two or three punches, + // reduce the attack value as one punch ain't much + if( gGameOptions.fNewTraitSystem ) { - // determine if there is a jumpable window in the direction to our target - // if yes, and we are not facing it, face it now - // if yes, and we are facing it, jump - // if no, go on, nothing to see here - // determine direction of our target - INT8 targetdirection = (INT8)GetDirectionToGridNoFromGridNo(pSoldier->sGridNo, sClosestOpponent); - - // determine if there is a jumpable window here, in the direction of our target - // store old direction for this check - INT tmpdirection = pSoldier->ubDirection; - pSoldier->ubDirection = targetdirection; - - INT8 windowdirection = DIRECTION_IRRELEVANT; - if ( FindWindowJumpDirection(pSoldier, pSoldier->sGridNo, pSoldier->ubDirection, &windowdirection) && targetdirection == windowdirection ) - { - pSoldier->ubDirection = tmpdirection; - - // are we already looking in that direction? - if ( pSoldier->ubDirection == targetdirection ) + // if we are not specialized, reduce the attack attractiveness generaly + if ( !HAS_SKILL_TRAIT( pSoldier, MARTIAL_ARTS_NT ) ) + { + // if too far and not having APs for at least 3 hits no way to attack + if (((CalcTotalAPsToAttack( pSoldier,BestStab.sTarget,ADDTURNCOST, 0 ) + (2 * (ApsToPunch( pSoldier )))) > pSoldier->bActionPoints) && !(PythSpacesAway(pSoldier->sGridNo, BestStab.sTarget) <= 1) ) { - // jump through the window - return(AI_ACTION_JUMP_WINDOW); + BestStab.ubPossible = 0; + BestStab.iAttackValue = 0; } - else - { - // look into that direction - if ( pSoldier->InternalIsValidStance( targetdirection, gAnimControl[ pSoldier->usAnimState ].ubEndHeight ) ) - { - pSoldier->aiData.usActionData = targetdirection; - return(AI_ACTION_CHANGE_FACING); - } - - } - } - - pSoldier->ubDirection = tmpdirection; - } - - return(AI_ACTION_SEEK_OPPONENT); - } - } - } - // The situation mentioned above happens... - else if ( (targetbLevel != pSoldier->pathing.bLevel) && SameBuilding( pSoldier->sGridNo, targetGridNo ) ) - { - if ( gGameExternalOptions.fZombieCanClimb ) - { - if (!TileIsOutOfBounds(targetGridNo) ) - { - // need to climb AND have enough APs to get there this turn - BOOLEAN fUp = TRUE; - if (pSoldier->pathing.bLevel > 0 ) - fUp = FALSE; - - if ( pSoldier->bActionPoints > GetAPsToClimbRoof ( pSoldier, fUp ) ) - { - pSoldier->aiData.usActionData = targetGridNo;//FindClosestClimbPoint(pSoldier, fUp ); - - // Necessary test: can we climb up at this position? It might happen that our target is directly above us, then we'll have to move - INT8 newdirection; - if ( ( fUp && FindHeigherLevel( pSoldier, pSoldier->sGridNo, pSoldier->ubDirection, &newdirection ) ) || ( !fUp && FindLowerLevel( pSoldier, pSoldier->sGridNo, pSoldier->ubDirection, &newdirection ) ) ) - { - return( AI_ACTION_CLIMB_ROOF ); } else { - return(AI_ACTION_SEEK_OPPONENT); + if (PythSpacesAway(pSoldier->sGridNo, BestStab.sTarget) <= 1) + { + BestStab.iAttackValue = (BestStab.iAttackValue * 2); + } + // if too far and not having APs for at least 2 hits + else if (((CalcTotalAPsToAttack( pSoldier,BestStab.sTarget,ADDTURNCOST, 0 ) + ApsToPunch( pSoldier )) > pSoldier->bActionPoints) && !(PythSpacesAway(pSoldier->sGridNo, BestStab.sTarget) <= 1)) + { + BestStab.iAttackValue /= 3; + } } } - } + else + { + if ( !HAS_SKILL_TRAIT( pSoldier, MARTIALARTS_OT ) && !HAS_SKILL_TRAIT( pSoldier, HANDTOHAND_OT ) ) + { + // if we are not specialized, reduce the attack attractiveness generaly + BestStab.iAttackValue /= 4; + // if too far and not having APs for at least 3 hits + if (((CalcTotalAPsToAttack( pSoldier,BestStab.sTarget,ADDTURNCOST, 0 ) + (2 * (ApsToPunch( pSoldier )))) > pSoldier->bActionPoints) && !(PythSpacesAway(pSoldier->sGridNo, BestStab.sTarget <= 1)) ) + { + BestStab.ubPossible = 0; + BestStab.iAttackValue = 0; + } + } + else + { + BestStab.iAttackValue = ((BestStab.iAttackValue * 3)/2); + + if (PythSpacesAway(pSoldier->sGridNo, BestStab.sTarget) <= 1) + { + BestStab.iAttackValue = ((BestStab.iAttackValue * 3)/2); + } + // if too far and not having APs for at least 2 hits + else if (((CalcTotalAPsToAttack( pSoldier,BestStab.sTarget,ADDTURNCOST, 0 ) + ApsToPunch( pSoldier )) > pSoldier->bActionPoints) && !(PythSpacesAway(pSoldier->sGridNo, BestStab.sTarget <= 1)) ) + { + BestStab.iAttackValue /= 3; + } + } + } + // now we KNOW FOR SURE that we will do something (stab, at least) + NPCDoesAct(pSoldier); + DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"NPC decided to punch"); + } + } + // if it was in his holster, swap it back into his holster for now + if (bWeaponIn != HANDPOS) + { + DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"about to rearrange pocket after punch check"); + RearrangePocket(pSoldier,HANDPOS,bWeaponIn,TEMPORARILY); } } } + ///////////////////////////////////////////////////////////////////////////////////////////////////// + + ////////////////////////////////////////////////////////////////////////// + // CHOOSE THE BEST TYPE OF ATTACK OUT OF THOSE FOUND TO BE POSSIBLE + ////////////////////////////////////////////////////////////////////////// + DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"CHOOSE THE BEST TYPE OF ATTACK OUT OF THOSE FOUND TO BE POSSIBLE"); - //////////////////////////////////////////////////////////////////////////// - // TURN TO FACE CLOSEST KNOWN OPPONENT (IF NOT FACING THERE ALREADY) - //////////////////////////////////////////////////////////////////////////// + BestAttack.iAttackValue = 0; - if (!gfTurnBasedAI || GetAPsToLook( pSoldier ) <= pSoldier->bActionPoints) + if (BestStab.ubPossible && ((BestStab.iAttackValue > BestAttack.iAttackValue) || (ubBestAttackAction == AI_ACTION_NONE))) { - // hopeless guys shouldn't waste their time this way, UNLESS they CAN move - // but chose not to to get this far (which probably means they're cornered) - // ALSO, don't bother turning if we're already aiming a gun - if ( !gfHiddenInterrupt && ((pSoldier->aiData.bAIMorale > MORALE_HOPELESS) || ubCanMove) && !AimingGun(pSoldier)) + // zombies are always punching... + BestAttack.iAttackValue = BestStab.iAttackValue; + ubBestAttackAction = AI_ACTION_KNIFE_MOVE; + DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"best action = move to punch"); + //////////////////////////////////////////////////////////////////////////////////// + } + + if ( ( ubBestAttackAction == AI_ACTION_NONE ) && fTryPunching ) + { + // nothing (else) to attack with so let's try hand-to-hand + bWeaponIn = FindObj( pSoldier, NOTHING, HANDPOS, NUM_INV_SLOTS ); + + if (bWeaponIn != NO_SLOT) { - // determine the location of the known closest opponent - // (don't care if he's conscious, don't care if he's reachable at all) - - - sClosestOpponent = ClosestSeenOpponent(pSoldier, NULL, NULL); - // if we have a closest reachable opponent - if (!TileIsOutOfBounds(sClosestOpponent)) + BestStab.bWeaponIn = bWeaponIn; + // if it's in his holster, swap it into his hand temporarily + if (bWeaponIn != HANDPOS) { - bDirection = atan8(CenterX(pSoldier->sGridNo),CenterY(pSoldier->sGridNo),CenterX(sClosestOpponent),CenterY(sClosestOpponent)); + DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionblack: swap knife into hand"); + RearrangePocket(pSoldier,HANDPOS,bWeaponIn,TEMPORARILY); + } - // if we're not facing towards him - if ( pSoldier->ubDirection != bDirection && pSoldier->InternalIsValidStance( bDirection, gAnimControl[ pSoldier->usAnimState ].ubEndHeight ) ) + // get the minimum cost to attack by HTH + ubMinAPCost = MinAPsToAttack(pSoldier,pSoldier->sLastTarget,DONTADDTURNCOST,0,0); + + // if we can afford the minimum AP cost to use HTH combat + if (pSoldier->bActionPoints >= ubMinAPCost) + { + // then look around for a worthy target (which sets BestStab.ubPossible) + CalcBestStab(pSoldier,&BestStab, FALSE); + + if (BestStab.ubPossible && pSoldier->ubSoldierClass == SOLDIER_CLASS_ZOMBIE) { - pSoldier->aiData.usActionData = bDirection; + // now we KNOW FOR SURE that we will do something (stab, at least) + NPCDoesAct(pSoldier); + ubBestAttackAction = AI_ACTION_KNIFE_MOVE; + DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"NPC decided to move to stab"); + } + } - #ifdef DEBUGDECISIONS - sprintf(tempstr,"%s - TURNS to face CLOSEST OPPONENT in direction %d",pSoldier->name,pSoldier->aiData.usActionData); - AIPopMessage(tempstr); - #endif + // if it was in his holster, swap it back into his holster for now + if (bWeaponIn != HANDPOS) + { + DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionblack: about to put knife away"); + RearrangePocket(pSoldier,HANDPOS,bWeaponIn,TEMPORARILY); + } + } + } - return(AI_ACTION_CHANGE_FACING); + + // copy the information on the best action selected into BestAttack struct + DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"copy the information on the best action selected into BestAttack struct"); + switch (ubBestAttackAction) + { + case AI_ACTION_KNIFE_MOVE: + DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"DecideActionBlack: best attack = stab with a knife"); + memcpy(&BestAttack,&BestStab,sizeof(BestAttack)); + break; + + default: + // set to empty + DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"DecideActionBlack: best attack = no good attack"); + memset( &BestAttack, 0, sizeof( BestAttack ) ); + break; + } + } + + // NB a desire of 4 or more is only achievable by brave/aggressive guys with high morale + //UINT16 usRange = GetModifiedGunRange(pSoldier->inv[ BestAttack.bWeaponIn ].usItem);//dnl ch69 150913 not in use + + DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"DecideActionBlack: DECIDE BETWEEN ATTACKING AND DEFENDING (TAKING COVER)"); + ////////////////////////////////////////////////////////////////////////// + // IF NECESSARY, DECIDE BETWEEN ATTACKING AND DEFENDING (TAKING COVER) + ////////////////////////////////////////////////////////////////////////// + + // if both are possible + if ((ubBestAttackAction != AI_ACTION_NONE) && ( !TileIsOutOfBounds(sBestCover))) + { + // gotta compare their merits and select the more desirable option + iOffense = BestAttack.ubChanceToReallyHit; + iDefense = iCoverPercentBetter; + + // based on how we feel about the situation, decide whether to attack first + switch (pSoldier->aiData.bAIMorale) + { + case MORALE_FEARLESS: + iOffense += iOffense / 2; // increase 50% + break; + + case MORALE_CONFIDENT: + iOffense += iOffense / 4; // increase 25% + break; + + case MORALE_NORMAL: + break; + + case MORALE_WORRIED: + iDefense += iDefense / 4; // increase 25% + break; + + case MORALE_HOPELESS: + iDefense += iDefense / 2; // increase 50% + break; + } + + + // smart guys more likely to try to stay alive, dolts more likely to shoot! + if (pSoldier->stats.bWisdom >= 50) //Madd: reduced the wisdom required to want to live... + iDefense += 10; + else if (pSoldier->stats.bWisdom < 30) + iDefense -= 10; + + // some orders are more offensive, others more defensive + if (pSoldier->aiData.bOrders == SEEKENEMY) + iOffense += 10; + else if ((pSoldier->aiData.bOrders == STATIONARY) || (pSoldier->aiData.bOrders == ONGUARD) || pSoldier->aiData.bOrders == SNIPER ) + iDefense += 10; + + switch (pSoldier->aiData.bAttitude) + { + case DEFENSIVE: iDefense += 30; break; + case BRAVESOLO: iDefense -= 0; break; + case BRAVEAID: iDefense -= 0; break; + case CUNNINGSOLO: iDefense += 20; break; + case CUNNINGAID: iDefense += 20; break; + case AGGRESSIVE: iOffense += 10; break; + case ATTACKSLAYONLY:iOffense += 30; break; + } + +#ifdef DEBUGDECISIONS + STR tempstr=""; + sprintf( tempstr, "%s - CHOICE: iOffense = %d, iDefense = %d\n", + pSoldier->name,iOffense,iDefense); + DebugAI( tempstr ); +#endif + + DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"DecideActionBlack: if his defensive instincts win out, forget all about the attack"); + // if his defensive instincts win out, forget all about the attack + if (iDefense > iOffense) + ubBestAttackAction = AI_ACTION_NONE; + } + + + DebugMsg (TOPIC_JA2,DBG_LEVEL_3,String("DecideActionBlack: is attack still desirable? ubBestAttackAction = %d",ubBestAttackAction)); + + // if attack is still desirable (meaning it's also preferred to taking cover) + if (ubBestAttackAction != AI_ACTION_NONE) + { + DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"DecideActionBlack: attack is still desirable (meaning it's also preferred to taking cover)"); + // if we wanted to be REALLY mean, we could look at chance to hit and decide whether + // to shoot at the head... + + fChangeStanceFirst = FALSE; + + // default settings + //POSSIBLE STRUCTURE CHANGE PROBLEM, NOT CURRENTLY CHANGED. GOTTHARD 7/14/08 + pSoldier->aiData.bAimTime = BestAttack.ubAimTime; + pSoldier->bDoBurst = 0; + + // HEADROCK HAM 3.6: bAimTime represents how MANY aiming levels are used, not how much APs they cost necessarily. + INT16 sActualAimTime = CalcAPCostForAiming( pSoldier, BestAttack.sTarget, (INT8)pSoldier->aiData.bAimTime ); + + if (ubBestAttackAction == AI_ACTION_KNIFE_MOVE && gGameOptions.fNewTraitSystem) + { + pSoldier->aiData.bAimTime = 0; + + if (Item[pSoldier->inv[BestAttack.bWeaponIn].usItem].usItemClass == IC_PUNCH) + { + if (HAS_SKILL_TRAIT( pSoldier, MARTIAL_ARTS_NT)) + { + if( PreRandom( (gGameExternalOptions.fEnhancedCloseCombatSystem ? 2 : 0) + 2 * NUM_SKILL_TRAITS( pSoldier, MARTIAL_ARTS_NT) ) > 2 ) + { + pSoldier->aiData.bAimTime = (gGameExternalOptions.fEnhancedCloseCombatSystem ? gSkillTraitValues.ubModifierForAPsAddedOnAimedPunches : 6); + } + } + } + else + { + if (HAS_SKILL_TRAIT( pSoldier, MELEE_NT)) + { + if( PreRandom( gGameExternalOptions.fEnhancedCloseCombatSystem ? 3 : 1 ) > 0 ) + { + pSoldier->aiData.bAimTime = (gGameExternalOptions.fEnhancedCloseCombatSystem ? gSkillTraitValues.ubModifierForAPsAddedOnAimedBladedAttackes : 6); } } } } - - //////////////////////////////////////////////////////////////////////////// - // DO NOTHING: Not enough points left to move, so save them for next turn - //////////////////////////////////////////////////////////////////////////// - #ifdef DEBUGDECISIONS - AINameMessage(pSoldier,"- DOES NOTHING (BLACK)",1000); - #endif + ////////////////////////////////////////////////////////////////////////// + // OTHERWISE, JUST GO AHEAD & ATTACK! + ////////////////////////////////////////////////////////////////////////// + DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"OTHERWISE, JUST GO AHEAD & ATTACK!"); + + // swap weapon to hand if necessary + if (BestAttack.bWeaponIn != HANDPOS) + { + DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionblack: swap weapon into hand"); + RearrangePocket(pSoldier,HANDPOS,BestAttack.bWeaponIn,FOREVER); + } - // by default, if everything else fails, just stand in place and wait - pSoldier->aiData.usActionData = NOWHERE; - return(AI_ACTION_NONE); + if (fChangeStanceFirst) + { // currently only for guns... + pSoldier->aiData.bNextAction = AI_ACTION_FIRE_GUN; + pSoldier->aiData.usNextActionData = BestAttack.sTarget; + pSoldier->aiData.bNextTargetLevel = BestAttack.bTargetLevel; + pSoldier->aiData.usActionData = ubBestStance; + return( AI_ACTION_CHANGE_STANCE ); + } + else + { + pSoldier->aiData.usActionData = BestAttack.sTarget; + pSoldier->bTargetLevel = BestAttack.bTargetLevel; + +#ifdef DEBUGDECISIONS + STR tempstr=""; + sprintf( tempstr, + "%d(%s) %s %d(%s) at gridno %d (%d APs aim)\n", + pSoldier->ubID,pSoldier->name, + (ubBestAttackAction == AI_ACTION_FIRE_GUN)?"SHOOTS":((ubBestAttackAction == AI_ACTION_TOSS_PROJECTILE)?"TOSSES AT":"STABS"), + BestAttack.ubOpponent,pSoldier->name , + BestAttack.sTarget,BestAttack.ubAimTime ); + DebugAI( tempstr); +#endif + + //DebugMsg(TOPIC_JA2, DBG_LEVEL_3, String("DecideActionBlack: Check for GL Bursts, is launcher capable? = %d, rtpcombat? = %d, bestattackaction = %d",IsGunBurstCapable( pSoldier, BestAttack.bWeaponIn, FALSE ),pSoldier->aiData.bRTPCombat,ubBestAttackAction )); + //should be a bug + DebugMsg(TOPIC_JA2, DBG_LEVEL_3, String("DecideActionBlack: Check for GL Bursts, is launcher capable? = %d, rtpcombat? = %d, bestattackaction = %d",IsGunBurstCapable( &pSoldier->inv[BestAttack.bWeaponIn], FALSE, pSoldier ),pSoldier->aiData.bRTPCombat,ubBestAttackAction )); + if (ubBestAttackAction == AI_ACTION_TOSS_PROJECTILE && (Item[pSoldier->inv[BestAttack.bWeaponIn].usItem].usItemClass == IC_LAUNCHER && IsGunBurstCapable( &pSoldier->inv[BestAttack.bWeaponIn], FALSE, pSoldier )) && + (pSoldier->bTeam != gbPlayerNum || pSoldier->aiData.bRTPCombat == RTP_COMBAT_AGGRESSIVE) ) + { + + DebugMsg(TOPIC_JA2, DBG_LEVEL_3, "DecideActionBlack: Doing burst calc"); + ubBurstAPs = CalcAPsToBurst( pSoldier->CalcActionPoints(), &(pSoldier->inv[BestAttack.bWeaponIn]), pSoldier ); + + if ( (pSoldier->bActionPoints - BestAttack.ubAPCost) >= ubBurstAPs ) + { + // Base chance of bursting is 25% if best shot was +0 aim, down to 8% at +4 + if ( TANK( pSoldier ) ) + { + iChance = 100; + } + else + { + iChance = 50; + switch (pSoldier->aiData.bAttitude) + { + case DEFENSIVE: iChance += -5; break; + case BRAVESOLO: iChance += 5; break; + case BRAVEAID: iChance += 5; break; + case CUNNINGSOLO: iChance += 0; break; + case CUNNINGAID: iChance += 0; break; + case AGGRESSIVE: iChance += 10; break; + case ATTACKSLAYONLY:iChance += 30; break; + } + // increase chance based on proximity and difficulty of enemy + DebugMsg(TOPIC_JA2AI,DBG_LEVEL_3,String("DecideActionBlack: check chance to gl burst")); + iChance += ( 15 - PythSpacesAway( pSoldier->sGridNo, BestAttack.sTarget ) ) * ( 1 + SoldierDifficultyLevel( pSoldier ) ); + } + + if ( (INT32) PreRandom( 100 ) < iChance) + { + DebugMsg(TOPIC_JA2, DBG_LEVEL_3, "DecideActionBlack: Doing GL burst"); + BestAttack.ubAPCost = BestAttack.ubAPCost + CalcAPsToBurst( pSoldier->CalcActionPoints(), &(pSoldier->inv[HANDPOS]), pSoldier ); + // check for spread burst possibilities + if (pSoldier->aiData.bAttitude != ATTACKSLAYONLY) + { + CalcSpreadBurst( pSoldier, BestAttack.sTarget, BestAttack.bTargetLevel ); + } + //dnl ch58 140913 After HAM 4 BURSTING is not in use any more, for burst bDoAutofire must be set to 0 + pSoldier->bDoBurst = 1; + pSoldier->bDoAutofire = 0; + } + } + } + + + return(ubBestAttackAction); + } } - INT8 ZombieDecideAction( SOLDIERTYPE *pSoldier ) + // end of tank AI + if( gGameExternalOptions.fEnemyTanksCanMoveInTactical || TANK( pSoldier ) ) { - INT8 bAction = AI_ACTION_NONE; - - switch (pSoldier->aiData.bAlertStatus) + return( AI_ACTION_NONE ); + } + + // get the location of the closest reachable opponent + /* Flugente 22.02.2012 - A few clarifications: I changed ClosestSeenOpponent so that for zombies, this function also returns an opponent if he is on the + * roof of a building, we are not, but our GridNo belongs to that same building. + * If that is the case, it is clear that we have to get on that roof. However, we cannot do that in BlackState. If, by pure chance, we can still see our + * enemy, we cannot climb (there is no climbing option in BlackState sofar). + * So, I changed the code so that now we will climb the roof. + */ + INT32 targetGridNo = -1; + INT8 targetbLevel = 0; + sClosestOpponent = ClosestSeenOpponentWithRoof(pSoldier, &targetGridNo, &targetbLevel); + if ( pSoldier->ubSoldierClass == SOLDIER_CLASS_ZOMBIE ) + { + if (!TileIsOutOfBounds(sClosestOpponent) && ! ( (targetbLevel != pSoldier->pathing.bLevel) && SameBuilding( pSoldier->sGridNo, targetGridNo ) ) ) { - case STATUS_GREEN: - #ifdef DEBUGDECISIONS - AIPopMessage("AlertStatus = GREEN"); - #endif - bAction = ZombieDecideActionGreen(pSoldier); + ////////////////////////////////////////////////////////////////////// + // GO DIRECTLY TOWARDS CLOSEST KNOWN OPPONENT + ////////////////////////////////////////////////////////////////////// + + // try to move towards him + pSoldier->aiData.usActionData = GoAsFarAsPossibleTowards(pSoldier,sClosestOpponent,AI_ACTION_GET_CLOSER); + + // if it's possible + if (!TileIsOutOfBounds(sClosestOpponent)) + { + if (targetbLevel != pSoldier->pathing.bLevel && gGameExternalOptions.fZombieCanClimb ) + return(AI_ACTION_MOVE_TO_CLIMB); + else + { + // Flugente: if on the same level and there is a jumpable window here, jump through it + if ( gGameExternalOptions.fZombieCanJumpWindows && targetbLevel == pSoldier->pathing.bLevel && targetbLevel == 0 ) + { + // determine if there is a jumpable window in the direction to our target + // if yes, and we are not facing it, face it now + // if yes, and we are facing it, jump + // if no, go on, nothing to see here + // determine direction of our target + INT8 targetdirection = (INT8)GetDirectionToGridNoFromGridNo(pSoldier->sGridNo, sClosestOpponent); + + // determine if there is a jumpable window here, in the direction of our target + // store old direction for this check + INT tmpdirection = pSoldier->ubDirection; + pSoldier->ubDirection = targetdirection; + + INT8 windowdirection = DIRECTION_IRRELEVANT; + if ( FindWindowJumpDirection(pSoldier, pSoldier->sGridNo, pSoldier->ubDirection, &windowdirection) && targetdirection == windowdirection ) + { + pSoldier->ubDirection = tmpdirection; + + // are we already looking in that direction? + if ( pSoldier->ubDirection == targetdirection ) + { + // jump through the window + return(AI_ACTION_JUMP_WINDOW); + } + else + { + // look into that direction + if ( pSoldier->InternalIsValidStance( targetdirection, gAnimControl[ pSoldier->usAnimState ].ubEndHeight ) ) + { + pSoldier->aiData.usActionData = targetdirection; + return(AI_ACTION_CHANGE_FACING); + } + + } + } + + pSoldier->ubDirection = tmpdirection; + } + + return(AI_ACTION_SEEK_OPPONENT); + } + } + } + // The situation mentioned above happens... + else if ( (targetbLevel != pSoldier->pathing.bLevel) && SameBuilding( pSoldier->sGridNo, targetGridNo ) ) + { + if ( gGameExternalOptions.fZombieCanClimb ) + { + if (!TileIsOutOfBounds(targetGridNo) ) + { + // need to climb AND have enough APs to get there this turn + BOOLEAN fUp = TRUE; + if (pSoldier->pathing.bLevel > 0 ) + fUp = FALSE; + + if ( pSoldier->bActionPoints > GetAPsToClimbRoof ( pSoldier, fUp ) ) + { + pSoldier->aiData.usActionData = targetGridNo;//FindClosestClimbPoint(pSoldier, fUp ); + + // Necessary test: can we climb up at this position? It might happen that our target is directly above us, then we'll have to move + INT8 newdirection; + if ( ( fUp && FindHeigherLevel( pSoldier, pSoldier->sGridNo, pSoldier->ubDirection, &newdirection ) ) || ( !fUp && FindLowerLevel( pSoldier, pSoldier->sGridNo, pSoldier->ubDirection, &newdirection ) ) ) + { + return( AI_ACTION_CLIMB_ROOF ); + } + else + { + return(AI_ACTION_SEEK_OPPONENT); + } + } + } + } + } + } + + //////////////////////////////////////////////////////////////////////////// + // TURN TO FACE CLOSEST KNOWN OPPONENT (IF NOT FACING THERE ALREADY) + //////////////////////////////////////////////////////////////////////////// + + if (!gfTurnBasedAI || GetAPsToLook( pSoldier ) <= pSoldier->bActionPoints) + { + // hopeless guys shouldn't waste their time this way, UNLESS they CAN move + // but chose not to to get this far (which probably means they're cornered) + // ALSO, don't bother turning if we're already aiming a gun + if ( !gfHiddenInterrupt && ((pSoldier->aiData.bAIMorale > MORALE_HOPELESS) || ubCanMove) && !AimingGun(pSoldier)) + { + // determine the location of the known closest opponent + // (don't care if he's conscious, don't care if he's reachable at all) + + + sClosestOpponent = ClosestSeenOpponent(pSoldier, NULL, NULL); + // if we have a closest reachable opponent + if (!TileIsOutOfBounds(sClosestOpponent)) + { + bDirection = atan8(CenterX(pSoldier->sGridNo),CenterY(pSoldier->sGridNo),CenterX(sClosestOpponent),CenterY(sClosestOpponent)); + + // if we're not facing towards him + if ( pSoldier->ubDirection != bDirection && pSoldier->InternalIsValidStance( bDirection, gAnimControl[ pSoldier->usAnimState ].ubEndHeight ) ) + { + pSoldier->aiData.usActionData = bDirection; + +#ifdef DEBUGDECISIONS + sprintf(tempstr,"%s - TURNS to face CLOSEST OPPONENT in direction %d",pSoldier->name,pSoldier->aiData.usActionData); + AIPopMessage(tempstr); +#endif + + return(AI_ACTION_CHANGE_FACING); + } + } + } + } + + //////////////////////////////////////////////////////////////////////////// + // DO NOTHING: Not enough points left to move, so save them for next turn + //////////////////////////////////////////////////////////////////////////// + +#ifdef DEBUGDECISIONS + AINameMessage(pSoldier,"- DOES NOTHING (BLACK)",1000); +#endif + + // by default, if everything else fails, just stand in place and wait + pSoldier->aiData.usActionData = NOWHERE; + return(AI_ACTION_NONE); + +} + +INT8 ZombieDecideAction( SOLDIERTYPE *pSoldier ) +{ + INT8 bAction = AI_ACTION_NONE; + + switch (pSoldier->aiData.bAlertStatus) + { + case STATUS_GREEN: + #ifdef DEBUGDECISIONS + AIPopMessage("AlertStatus = GREEN"); + #endif + bAction = ZombieDecideActionGreen(pSoldier); + break; + + case STATUS_YELLOW: + #ifdef DEBUGDECISIONS + AIPopMessage("AlertStatus = YELLOW"); + #endif + bAction = ZombieDecideActionYellow(pSoldier); + break; + + case STATUS_RED: + #ifdef DEBUGDECISIONS + AIPopMessage("AlertStatus = RED"); + #endif + bAction = ZombieDecideActionRed(pSoldier, TRUE); + break; + + case STATUS_BLACK: + #ifdef DEBUGDECISIONS + AIPopMessage("AlertStatus = BLACK"); + #endif + bAction = ZombieDecideActionBlack(pSoldier); + break; + } + +#ifdef DEBUGDECISIONS + STR tempstr; + sprintf( tempstr, "ZombieDecideAction: selected action %d, actionData %d\n\n",bAction,pSoldier->aiData.usActionData ); + DebugAI(tempstr); +#endif + + return(bAction); +} + +void ZombieDecideAlertStatus( SOLDIERTYPE *pSoldier ) +{ + INT8 bOldStatus; + INT32 iDummy; + BOOLEAN fClimbDummy, fReachableDummy; + + // THE FOUR (4) POSSIBLE ALERT STATUSES ARE: + // GREEN - No one sensed, no suspicious noise heard, go about doing regular stuff + // YELLOW - Suspicious noise was heard personally + // RED - Either saw OPPONENTS in person, or definite contact had been called + // BLACK - Currently has one or more OPPONENTS in sight + + // save the man's previous status + bOldStatus = pSoldier->aiData.bAlertStatus; + + // determine the current alert status for this category of man + if (pSoldier->aiData.bOppCnt > 0) // opponent(s) in sight + { + pSoldier->aiData.bAlertStatus = STATUS_BLACK; + } + else // no opponents are in sight + { + switch (bOldStatus) + { + case STATUS_BLACK: + // then drop back to RED status + pSoldier->aiData.bAlertStatus = STATUS_RED; + break; + + case STATUS_RED: + // RED can never go back down below RED, only up to BLACK + // TODO: perhaps zombies might forget their target? They are pretty dumb after all... break; case STATUS_YELLOW: - #ifdef DEBUGDECISIONS - AIPopMessage("AlertStatus = YELLOW"); - #endif - bAction = ZombieDecideActionYellow(pSoldier); - break; - - case STATUS_RED: - #ifdef DEBUGDECISIONS - AIPopMessage("AlertStatus = RED"); - #endif - bAction = ZombieDecideActionRed(pSoldier, TRUE); - break; - - case STATUS_BLACK: - #ifdef DEBUGDECISIONS - AIPopMessage("AlertStatus = BLACK"); - #endif - bAction = ZombieDecideActionBlack(pSoldier); - break; - } - - #ifdef DEBUGDECISIONS - STR tempstr; - sprintf( tempstr, "ZombieDecideAction: selected action %d, actionData %d\n\n",bAction,pSoldier->aiData.usActionData ); - DebugAI(tempstr); - #endif - - return(bAction); - } - - void ZombieDecideAlertStatus( SOLDIERTYPE *pSoldier ) - { - INT8 bOldStatus; - INT32 iDummy; - BOOLEAN fClimbDummy, fReachableDummy; - - // THE FOUR (4) POSSIBLE ALERT STATUSES ARE: - // GREEN - No one sensed, no suspicious noise heard, go about doing regular stuff - // YELLOW - Suspicious noise was heard personally - // RED - Either saw OPPONENTS in person, or definite contact had been called - // BLACK - Currently has one or more OPPONENTS in sight - - // save the man's previous status - bOldStatus = pSoldier->aiData.bAlertStatus; - - // determine the current alert status for this category of man - if (pSoldier->aiData.bOppCnt > 0) // opponent(s) in sight - { - pSoldier->aiData.bAlertStatus = STATUS_BLACK; - } - else // no opponents are in sight - { - switch (bOldStatus) - { - case STATUS_BLACK: - // then drop back to RED status - pSoldier->aiData.bAlertStatus = STATUS_RED; - break; - - case STATUS_RED: - // RED can never go back down below RED, only up to BLACK - // TODO: perhaps zombies might forget their target? They are pretty dumb after all... - break; - - case STATUS_YELLOW: - // if all enemies have been RED alerted, or we're under fire - if (gTacticalStatus.Team[pSoldier->bTeam].bAwareOfOpposition || pSoldier->aiData.bUnderFire) - { - pSoldier->aiData.bAlertStatus = STATUS_RED; - } - else - { - // if we are NOT aware of any uninvestigated noises right now - // and we are not currently in the middle of an action - // (could still be on his way heading to investigate a noise!) - if (( TileIsOutOfBounds(MostImportantNoiseHeard(pSoldier,&iDummy,&fClimbDummy,&fReachableDummy))) && !pSoldier->aiData.bActionInProgress) - { - // then drop back to GREEN status - pSoldier->aiData.bAlertStatus = STATUS_GREEN; - } - } - break; - - case STATUS_GREEN: - // if all enemies have been RED alerted, or we're under fire - if (gTacticalStatus.Team[pSoldier->bTeam].bAwareOfOpposition || pSoldier->aiData.bUnderFire) - { - pSoldier->aiData.bAlertStatus = STATUS_RED; - } - else - { - // if we ARE aware of any uninvestigated noises right now - if ( !TileIsOutOfBounds(MostImportantNoiseHeard(pSoldier,&iDummy,&fClimbDummy,&fReachableDummy))) - { - // then move up to YELLOW status - pSoldier->aiData.bAlertStatus = STATUS_YELLOW; - } - } - break; - } - // otherwise, RED stays RED, YELLOW stays YELLOW, GREEN stays GREEN - } - - // if the creatures alert status has changed in any way - if (pSoldier->aiData.bAlertStatus != bOldStatus) - { - // HERE ARE TRYING TO AVOID NPCs SHUFFLING BACK & FORTH BETWEEN RED & BLACK - // if either status is < RED (ie. anything but RED->BLACK && BLACK->RED) - if ((bOldStatus < STATUS_RED) || (pSoldier->aiData.bAlertStatus < STATUS_RED)) - { - // force a NEW action decision on next pass through HandleManAI() - SetNewSituation( pSoldier ); - } - - // if this guy JUST discovered that there were opponents here for sure... - if ((bOldStatus < STATUS_RED) && (pSoldier->aiData.bAlertStatus >= STATUS_RED)) - { - // might want to make custom to let them go anywhere - CheckForChangingOrders(pSoldier); - } - - #ifdef DEBUGDECISIONS - // don't report status changes for human-controlled mercs - // if (!pSoldier->human) - // { - STR16 tempstr; - sprintf(tempstr,"%s's Alert Status changed from %d to %d", - pSoldier->name ,bOldStatus,pSoldier->aiData.bAlertStatus); - AIPopMessage(tempstr); - // } - #endif - - } - else // status didn't change - { - // if a guy on status GREEN or YELLOW is running low on breath - if (((pSoldier->aiData.bAlertStatus == STATUS_GREEN) && (pSoldier->bBreath < 75)) || - ((pSoldier->aiData.bAlertStatus == STATUS_YELLOW) && (pSoldier->bBreath < 50))) - { - // as long as he's not in water (standing on a bridge is OK) - if (!pSoldier->MercInWater()) + // if all enemies have been RED alerted, or we're under fire + if (gTacticalStatus.Team[pSoldier->bTeam].bAwareOfOpposition || pSoldier->aiData.bUnderFire) { - // force a NEW decision so that he can get some rest - SetNewSituation( pSoldier ); - - // current action will be canceled. if noise is no longer important - if ((pSoldier->aiData.bAlertStatus == STATUS_YELLOW) && - (TileIsOutOfBounds(MostImportantNoiseHeard(pSoldier,&iDummy,&fClimbDummy,&fReachableDummy)))) + pSoldier->aiData.bAlertStatus = STATUS_RED; + } + else + { + // if we are NOT aware of any uninvestigated noises right now + // and we are not currently in the middle of an action + // (could still be on his way heading to investigate a noise!) + if (( TileIsOutOfBounds(MostImportantNoiseHeard(pSoldier,&iDummy,&fClimbDummy,&fReachableDummy))) && !pSoldier->aiData.bActionInProgress) { // then drop back to GREEN status pSoldier->aiData.bAlertStatus = STATUS_GREEN; - CheckForChangingOrders(pSoldier); } } + break; + + case STATUS_GREEN: + // if all enemies have been RED alerted, or we're under fire + if (gTacticalStatus.Team[pSoldier->bTeam].bAwareOfOpposition || pSoldier->aiData.bUnderFire) + { + pSoldier->aiData.bAlertStatus = STATUS_RED; + } + else + { + // if we ARE aware of any uninvestigated noises right now + if ( !TileIsOutOfBounds(MostImportantNoiseHeard(pSoldier,&iDummy,&fClimbDummy,&fReachableDummy))) + { + // then move up to YELLOW status + pSoldier->aiData.bAlertStatus = STATUS_YELLOW; + } + } + break; } + // otherwise, RED stays RED, YELLOW stays YELLOW, GREEN stays GREEN + } + + // if the creatures alert status has changed in any way + if (pSoldier->aiData.bAlertStatus != bOldStatus) + { + // HERE ARE TRYING TO AVOID NPCs SHUFFLING BACK & FORTH BETWEEN RED & BLACK + // if either status is < RED (ie. anything but RED->BLACK && BLACK->RED) + if ((bOldStatus < STATUS_RED) || (pSoldier->aiData.bAlertStatus < STATUS_RED)) + { + // force a NEW action decision on next pass through HandleManAI() + SetNewSituation( pSoldier ); + } + + // if this guy JUST discovered that there were opponents here for sure... + if ((bOldStatus < STATUS_RED) && (pSoldier->aiData.bAlertStatus >= STATUS_RED)) + { + // might want to make custom to let them go anywhere + CheckForChangingOrders(pSoldier); + } + +#ifdef DEBUGDECISIONS + // don't report status changes for human-controlled mercs +// if (!pSoldier->human) +// { + STR16 tempstr; + sprintf(tempstr,"%s's Alert Status changed from %d to %d", + pSoldier->name ,bOldStatus,pSoldier->aiData.bAlertStatus); + AIPopMessage(tempstr); +// } +#endif + + } + else // status didn't change + { + // if a guy on status GREEN or YELLOW is running low on breath + if (((pSoldier->aiData.bAlertStatus == STATUS_GREEN) && (pSoldier->bBreath < 75)) || + ((pSoldier->aiData.bAlertStatus == STATUS_YELLOW) && (pSoldier->bBreath < 50))) + { + // as long as he's not in water (standing on a bridge is OK) + if (!pSoldier->MercInWater()) + { + // force a NEW decision so that he can get some rest + SetNewSituation( pSoldier ); + + // current action will be canceled. if noise is no longer important + if ((pSoldier->aiData.bAlertStatus == STATUS_YELLOW) && + (TileIsOutOfBounds(MostImportantNoiseHeard(pSoldier,&iDummy,&fClimbDummy,&fReachableDummy)))) + { + // then drop back to GREEN status + pSoldier->aiData.bAlertStatus = STATUS_GREEN; + CheckForChangingOrders(pSoldier); + } + } } } -#endif +} diff --git a/TileEngine/Explosion Control.cpp b/TileEngine/Explosion Control.cpp index eb23ee52..d558aca9 100644 --- a/TileEngine/Explosion Control.cpp +++ b/TileEngine/Explosion Control.cpp @@ -1846,40 +1846,7 @@ BOOLEAN DamageSoldierFromBlast( UINT8 ubPerson, UINT8 ubOwner, INT32 sBombGridNo sNewWoundAmt = max(1, sNewWoundAmt); } ////////////////////////////////////////////////////////////////////////////////////// - -#ifdef ENABLE_ZOMBIES - // Nothing to do here -#else - //////////////////////////////////////////////////////////////////////////////////// - // SANDRO - option to make special NPCs stronger - damage resistance - if (gGameExternalOptions.usSpecialNPCStronger > 0) - { - switch( pSoldier->ubProfile ) - { - case CARMEN: - case QUEEN: - case JOE: - case ANNIE: - case CHRIS: - case KINGPIN: - case TIFFANY: - case T_REX: - case DRUGGIST: - case GENERAL: - case JIM: - case JACK: - case OLAF: - case RAY: - case OLGA: - case TYRONE: - case MIKE: - sNewWoundAmt -= (sNewWoundAmt * gGameExternalOptions.usSpecialNPCStronger / 200); - sNewWoundAmt = max( 1, sNewWoundAmt); - break; - } - } -#endif - + // SANDRO - new merc records - times wounded (blasted by explosion) if ( ( sNewWoundAmt > 1 || sBreathAmt > 1000 ) && pSoldier->ubProfile != NO_PROFILE ) gMercProfiles[ pSoldier->ubProfile ].records.usTimesWoundedBlasted++; @@ -2008,12 +1975,8 @@ BOOLEAN DishOutGasDamage( SOLDIERTYPE * pSoldier, EXPLOSIVETYPE * pExplosive, IN } else if ( pExplosive->ubType == EXPLOSV_MUSTGAS ) { -#ifdef ENABLE_ZOMBIES // zombies and robots are unaffected by mustard gas if ( AM_A_ROBOT( pSoldier ) || pSoldier->IsZombie() ) -#else - if ( AM_A_ROBOT( pSoldier ) ) -#endif { return( fRecompileMovementCosts ); } diff --git a/Utils/Animated ProgressBar.cpp b/Utils/Animated ProgressBar.cpp index 8668f062..f0085ecb 100644 --- a/Utils/Animated ProgressBar.cpp +++ b/Utils/Animated ProgressBar.cpp @@ -289,10 +289,8 @@ void SetNewLoadScreenHint() fShow = TRUE; else if ( zLoadScreenHint[cnt].usFlags & LOADSCREEN_FOOD && gGameOptions.fFoodSystem ) fShow = TRUE; -#ifdef ENABLE_ZOMBIES else if ( zLoadScreenHint[cnt].usFlags & LOADSCREEN_ZOMBIE && gGameSettings.fOptions[TOPTION_ZOMBIES] ) fShow = TRUE; -#endif else if ( zLoadScreenHint[cnt].usFlags & LOADSCREEN_OVERHEAT_DIRT && (gGameExternalOptions.fWeaponOverheating || gGameExternalOptions.fDirtSystem) ) fShow = TRUE; else if ( zLoadScreenHint[cnt].usFlags & LOADSCREEN_NCTH && UsingNewCTHSystem() ) diff --git a/Utils/_ChineseText.cpp b/Utils/_ChineseText.cpp index c95ea3c7..8925ed7c 100644 --- a/Utils/_ChineseText.cpp +++ b/Utils/_ChineseText.cpp @@ -6256,9 +6256,7 @@ STR16 zOptionsToggleText[] = L"佣兵修理时保持沉默", // Madd: mercs don't say quotes while repairing L"佣兵医疗时保持沉默", // Madd: mercs don't say quotes while doctoring L"自动加速敌军回合", // Automatic fast forward through AI turns -#ifdef ENABLE_ZOMBIES L"僵尸模式", -#endif L"区域物品栏弹窗匹配拾取", // the_bob : enable popups for picking items from sector inv L"标记剩余敌人", L"显示LBE(携行具)物品", @@ -6373,9 +6371,7 @@ STR16 zOptionsScreenHelpText[] = L"打开时,佣兵医疗时不会随时汇报进程。", L"打开时,敌军回合将被大幅加速。", -#ifdef ENABLE_ZOMBIES L"打开时,被击毙的敌人将有可能变成僵尸!杀出个黎明!", -#endif L"打开时,在区域物品栏界面,点击佣兵身上空白的携行具位置会弹窗匹配拾取物品。", L"打开时,会直接显示该区域最后一个敌人的大致位置。", L"打开时,在区域物品栏界面,右键点击装有物品的携行具时可直接显示包含的物品。", diff --git a/Utils/_DutchText.cpp b/Utils/_DutchText.cpp index d9de9662..c25a17eb 100644 --- a/Utils/_DutchText.cpp +++ b/Utils/_DutchText.cpp @@ -6257,9 +6257,7 @@ STR16 zOptionsToggleText[] = L"Quiet Repairing", // Madd: mercs don't say quotes while repairing // TODO.Translate L"Quiet Doctoring", // Madd: mercs don't say quotes while doctoring // TODO.Translate L"Auto Fast Forward AI Turns", // Automatic fast forward through AI turns // TODO.Translate -#ifdef ENABLE_ZOMBIES L"Allow Zombies", // TODO.Translate -#endif L"Enable inventory popups", // the_bob : enable popups for picking items from sector inv // TODO.Translate L"Mark Remaining Hostiles", // TODO.Translate L"Show LBE Content", // TODO.Translate @@ -6374,9 +6372,7 @@ STR16 zOptionsScreenHelpText[] = L"When ON, mercs will not report progress during doctoring.", // TODO.Translate L"When ON, AI turns will be much faster.", // TODO.Translate -#ifdef ENABLE_ZOMBIES L"When ON, zombies will spawn. Beware!", // allow zombies // TODO.Translate -#endif L"When ON, enables popup boxes that appear when you left click on empty merc inventory slots while viewing sector inventory in mapscreen.", // TODO.Translate L"When ON, approximate locations of the last enemies in the sector are highlighted.", // TODO.Translate L"When ON, show the contents of an LBE item, otherwise show the regular NAS interface.", // TODO.Translate diff --git a/Utils/_EnglishText.cpp b/Utils/_EnglishText.cpp index 646817e9..fdd72d08 100644 --- a/Utils/_EnglishText.cpp +++ b/Utils/_EnglishText.cpp @@ -6253,9 +6253,7 @@ STR16 zOptionsToggleText[] = L"Quiet Repairing", // Madd: mercs don't say quotes while repairing L"Quiet Doctoring", // Madd: mercs don't say quotes while doctoring L"Auto Fast Forward AI Turns", // Automatic fast forward through AI turns -#ifdef ENABLE_ZOMBIES - L"Allow Zombies", // Flugente Zombies 1.0 -#endif + L"Allow Zombies", // Flugente Zombies L"Enable Inventory Popups", // the_bob : enable popups for picking items from sector inv L"Mark Remaining Hostiles", L"Show LBE Content", @@ -6370,9 +6368,7 @@ STR16 zOptionsScreenHelpText[] = L"When ON, mercs will not report progress during doctoring.", L"When ON, AI turns will be much faster.", -#ifdef ENABLE_ZOMBIES L"When ON, zombies will spawn. Beware!", // allow zombies -#endif L"When ON, enables popup boxes that appear when left-click on empty merc inventory slots in mapscreen sector inventory.", L"When ON, approximate locations of the last enemies in the sector will be highlighted.", L"When ON, will show the contents of an LBE item; otherwise, regular NAS interface will be shown.", diff --git a/Utils/_FrenchText.cpp b/Utils/_FrenchText.cpp index 9ee99816..b0bf60d6 100644 --- a/Utils/_FrenchText.cpp +++ b/Utils/_FrenchText.cpp @@ -6265,9 +6265,7 @@ STR16 zOptionsToggleText[] = L"Réparer en silence", // Madd: mercs don't say quotes while repairing L"Soigner en silence", // Madd: mercs don't say quotes while doctoring L"Accélérer les tours ennemis", // Automatic fast forward through AI turns -#ifdef ENABLE_ZOMBIES - L"Avec zombis", // Flugente Zombies 1.0 -#endif + L"Avec zombis", // Flugente Zombies L"Propose objet/inventaire", // the_bob : enable popups for picking items from sector inv L"Situer ennemi restant", L"Afficher contenu LBE/DESC.", @@ -6381,9 +6379,7 @@ STR16 zOptionsScreenHelpText[] = L"Si activé, les mercenaires ne feront pas de compte rendu des progrès sur les réparations.", L"Si activé, les mercenaires ne feront pas de compte rendu des progrès sur les soins.", L"Si activé, les tours de l'IA seront plus rapides.", -#ifdef ENABLE_ZOMBIES L"Si activé, les zombis seront présents. Soyez au courant !", // allow zombies -#endif L"Si activé, ouvrir inventaire d'un mercenaire et celui du secteur.\nClic gauche sur un emplacement vide inv/obj/arme et une fenêtre \nproposera des choix.", L"Si activé, la zone où se trouve le reste des ennemis dans le secteur, est mis en évidence.", L"Si activé, montre le contenu d'un élément LBE quand la fenêtre de description est ouverte.", diff --git a/Utils/_GermanText.cpp b/Utils/_GermanText.cpp index c9371cb6..5aa1bf74 100644 --- a/Utils/_GermanText.cpp +++ b/Utils/_GermanText.cpp @@ -6105,9 +6105,7 @@ STR16 zOptionsToggleText[] = L"Stummes Reparieren", // Madd: mercs don't say quotes while repairing L"Stumme Behandlung", // Madd: mercs don't say quotes while doctoring L"Autom. schnelle Gegner-Züge", // Automatic fast forward through AI turns -#ifdef ENABLE_ZOMBIES - L"Zombies erlauben", // Flugente Zombies 1.0 -#endif + L"Zombies erlauben", // Flugente Zombies L"Inventar Popup-Menüs", // the_bob : enable popups for picking items from sector inv L"Übrige Feinde markieren", L"Tascheninhalt anzeigen", @@ -6222,9 +6220,7 @@ STR16 zOptionsScreenHelpText[] = L"Wenn diese Funktion aktiviert ist, werden die Söldner über den ärztlichen Fortschritt nicht mehr berichten.", L"Wenn diese Funktion aktiviert ist, werden gegnerische Züge schneller durchgeführt.", -#ifdef ENABLE_ZOMBIES L"Wenn diese Funktion aktiviert ist, können Tote als Zombies wieder auferstehen. Seien Sie auf der Hut!", -#endif L"Wenn diese Funktion aktiviert ist, und Sie mit der linken Maustaste auf einen freien Söldner-Inventarplatz klicken (während das Sektor-Inventar angezeigt wird), wird ein hilfreiches Popup-Menü eingeblendet.", L"Wenn diese Funktion aktiviert ist, wird die ungefähre Postion der verbleibenden Feinde auf der Übersichtskarte schraffiert", L"Wenn diese Funktion aktiviert ist, wird in der erweiterten Beschreibung von Tashen statt den Anbauteilen deren Inhalt angezeigt.", diff --git a/Utils/_ItalianText.cpp b/Utils/_ItalianText.cpp index dbf34937..4525870c 100644 --- a/Utils/_ItalianText.cpp +++ b/Utils/_ItalianText.cpp @@ -6243,9 +6243,7 @@ STR16 zOptionsToggleText[] = L"Quiet Repairing", // Madd: mercs don't say quotes while repairing // TODO.Translate L"Quiet Doctoring", // Madd: mercs don't say quotes while doctoring // TODO.Translate L"Auto Fast Forward AI Turns", // Automatic fast forward through AI turns // TODO.Translate -#ifdef ENABLE_ZOMBIES - L"Allow Zombies", // Flugente Zombies 1.0 -#endif + L"Allow Zombies", // Flugente Zombies L"Enable inventory popups", // the_bob : enable popups for picking items from sector inv // TODO.Translate L"Mark Remaining Hostiles", // TODO.Translate L"Show LBE Content", // TODO.Translate @@ -6360,9 +6358,7 @@ STR16 zOptionsScreenHelpText[] = L"When ON, mercs will not report progress during doctoring.", // TODO.Translate L"When ON, AI turns will be much faster.", // TODO.Translate -#ifdef ENABLE_ZOMBIES L"When ON, zombies will spawn. Beware!", // allow zombies // TODO.Translate -#endif L"When ON, enables popup boxes that appear when you left click on empty merc inventory slots while viewing sector inventory in mapscreen.", // TODO.Translate L"When ON, approximate locations of the last enemies in the sector are highlighted.", // TODO.Translate L"When ON, show the contents of an LBE item, otherwise show the regular NAS interface.", // TODO.Translate diff --git a/Utils/_PolishText.cpp b/Utils/_PolishText.cpp index f95acaae..210aa7cd 100644 --- a/Utils/_PolishText.cpp +++ b/Utils/_PolishText.cpp @@ -6262,9 +6262,7 @@ STR16 zOptionsToggleText[] = L"Quiet Doctoring", // Madd: mercs don't say quotes while doctoring // TODO.Translate L"Auto Fast Forward AI Turns", // Automatic fast forward through AI turns // TODO.Translate -#ifdef ENABLE_ZOMBIES - L"Allow Zombies", // Flugente Zombies 1.0 -#endif + L"Allow Zombies", // Flugente Zombies L"Enable inventory popups", // the_bob : enable popups for picking items from sector inv // TODO.Translate L"Mark Remaining Hostiles", // TODO.Translate L"Show LBE Content", // TODO.Translate @@ -6379,9 +6377,7 @@ STR16 zOptionsScreenHelpText[] = L"When ON, mercs will not report progress during doctoring.", // TODO.Translate L"When ON, AI turns will be much faster.", // TODO.Translate -#ifdef ENABLE_ZOMBIES L"When ON, zombies will spawn. Beware!", // allow zombies // TODO.Translate -#endif L"When ON, enables popup boxes that appear when you left click on empty merc inventory slots while viewing sector inventory in mapscreen.", // TODO.Translate L"When ON, approximate locations of the last enemies in the sector are highlighted.", // TODO.Translate L"When ON, show the contents of an LBE item, otherwise show the regular NAS interface.", // TODO.Translate diff --git a/Utils/_RussianText.cpp b/Utils/_RussianText.cpp index 88df19db..f85834c3 100644 --- a/Utils/_RussianText.cpp +++ b/Utils/_RussianText.cpp @@ -6253,9 +6253,7 @@ STR16 zOptionsToggleText[] = L"Тихий ремонт", // Madd: mercs don't say quotes while repairing L"Тихое лечение", // Madd: mercs don't say quotes while doctoring L"Быстрый ход компьютера", // Automatic fast forward through AI turns -#ifdef ENABLE_ZOMBIES - L"Зомби в игре!", // Flugente Zombies 1.0 -#endif + L"Зомби в игре!", // Flugente Zombies L"Меню в инвентаре бойца", // the_bob : enable popups for picking items from sector inv L"Отметить оставшихся врагов", L"Показать содержимое разгрузки", @@ -6370,9 +6368,7 @@ STR16 zOptionsScreenHelpText[] = L"Если включено, бойцы не будут\nсообщать о статусе лечения.", L"Если включено, ожидание,\nпока сделают ход противник,\nгражданские и другие существа,\nбудет значительно меньше.", -#ifdef ENABLE_ZOMBIES L"Если включено, убитые враги\nбудут превращаться в зомби. Веселитесь!", -#endif L"Если включено, при просмотре\nпредметов сектора в инвентаре бойца\nбудет доступно меню по нажатии\nлевой кнопки на пустой карман.", L"Если включено, указывается примерное\nположение последних врагов в секторе.", L"Если включено, показывает содержимое разгрузки,\nиначе - обычный интерфейс новой системы навески.", diff --git a/builddefines.h b/builddefines.h index e19ba6ab..5e48f506 100644 --- a/builddefines.h +++ b/builddefines.h @@ -32,14 +32,6 @@ // ***************************** // ----------------------------- - -// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -// ----------------------------- -// WANNE: Enable this define, to build an executable where you can enable/disable zombies in tactical (ingame option screen). -#define ENABLE_ZOMBIES -// ----------------------------- -// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - // Map Editor version - you should use the MapEditor configuration instead of messing with these defines (ChrisL) //#define JA2BETAVERSION //#define JA2EDITOR