mirror of
https://github.com/1dot13/source.git
synced 2026-07-22 13:40:22 +02:00
Compare commits
86
Commits
latest
...
decideaction
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7b8c2d1ff9 | ||
|
|
caf925b216 | ||
|
|
bd9cbd9944 | ||
|
|
41945842d5 | ||
|
|
91a1059f87 | ||
|
|
9c0fb0b6da | ||
|
|
3d7d7cc88a | ||
|
|
737cd848ce | ||
|
|
1bead3a0fe | ||
|
|
b50ed93f39 | ||
|
|
75355f6cac | ||
|
|
2448ccb191 | ||
|
|
42215e7a51 | ||
|
|
1419cc6925 | ||
|
|
76af6b7641 | ||
|
|
2b1838dbb0 | ||
|
|
4ad34f80a9 | ||
|
|
ff96d653e2 | ||
|
|
58ea3be2fd | ||
|
|
4678cab16f | ||
|
|
1b9cb1f75e | ||
|
|
df8d4bd8a2 | ||
|
|
52a4959dfb | ||
|
|
e806fd37e7 | ||
|
|
cd054b549f | ||
|
|
c3f4ee754a | ||
|
|
a46d2dbb96 | ||
|
|
16fc2d8177 | ||
|
|
e634edd862 | ||
|
|
f5b705cbe4 | ||
|
|
4e19816b57 | ||
|
|
20dadec54c | ||
|
|
2e3e4b38d9 | ||
|
|
f075963bca | ||
|
|
de91c7a9fd | ||
|
|
ab2c5fc10d | ||
|
|
5fe730b075 | ||
|
|
57716e1c07 | ||
|
|
dfc17c68c6 | ||
|
|
67a26e0936 | ||
|
|
1a98420160 | ||
|
|
877ec385ee | ||
|
|
b480a67d36 | ||
|
|
adf86380ca | ||
|
|
93acd19c11 | ||
|
|
f6052f0eff | ||
|
|
eaf76957c0 | ||
|
|
3c2b048367 | ||
|
|
1aec54f59c | ||
|
|
0240729c11 | ||
|
|
5c86cf0b07 | ||
|
|
79372d5d23 | ||
|
|
4faccb2957 | ||
|
|
0ed68b6214 | ||
|
|
f646bc2bfa | ||
|
|
01327265a3 | ||
|
|
cd193c7f5e | ||
|
|
f249406ba0 | ||
|
|
7ee8165e02 | ||
|
|
caf5828153 | ||
|
|
ffb95a2418 | ||
|
|
22f2c9ff9a | ||
|
|
a90e11619d | ||
|
|
d6c9cb20b2 | ||
|
|
1586e8fd2b | ||
|
|
88af94eaaf | ||
|
|
6131ebc689 | ||
|
|
ae709f2d19 | ||
|
|
a45f3ae319 | ||
|
|
318e4a40cf | ||
|
|
1170f82159 | ||
|
|
d28ed97dff | ||
|
|
96a7fa0538 | ||
|
|
c5d1615035 | ||
|
|
138ad0656f | ||
|
|
a0cbe42a01 | ||
|
|
06478266d0 | ||
|
|
613de1b869 | ||
|
|
1c4929c3e1 | ||
|
|
664142c36c | ||
|
|
1c8d008a66 | ||
|
|
0e529070c4 | ||
|
|
bacad9b4c0 | ||
|
|
e502727828 | ||
|
|
d99b12cb5f | ||
|
|
070867d965 |
@@ -332,6 +332,7 @@ BOOLEAN LoadGameSettings()
|
||||
|
||||
gGameSettings.fOptions[TOPTION_ALT_START_AIM] = iniReader.ReadBoolean("JA2 Game Settings", "TOPTION_ALT_START_AIM" , TRUE); // Start at max aiming level instead of default no aiming
|
||||
gGameSettings.fOptions[TOPTION_ALT_PATHFINDING] = iniReader.ReadBoolean("JA2 Game Settings", "TOPTION_ALT_PATHFINDING" , FALSE); // A* pathfinding
|
||||
gGameSettings.fOptions[TOPTION_USE_LEGACY_TACTICALAI] = iniReader.ReadBoolean("JA2 Game Settings", "TOPTION_USE_LEGACY_TACTICALAI", FALSE); // A* pathfinding
|
||||
gGameSettings.fOptions[TOPTION_MERCENARY_FORMATIONS] = iniReader.ReadBoolean("JA2 Game Settings","TOPTION_MERCENARY_FORMATIONS" , FALSE ); // Flugente: mercenary formations
|
||||
gGameSettings.fOptions[TOPTION_SHOW_ENEMY_LOCATION] = iniReader.ReadBoolean("JA2 Game Settings","TOPTION_SHOW_ENEMY_LOCATION" , FALSE); // sevenfm: show locations of known enemies
|
||||
gGameSettings.fOptions[TOPTION_REPORT_MISS_MARGIN] = iniReader.ReadBoolean("JA2 Game Settings","TOPTION_REPORT_MISS_MARGIN" , FALSE ); // HEADROCK HAM 4: Shot offset report
|
||||
@@ -616,6 +617,7 @@ BOOLEAN SaveGameSettings()
|
||||
settings << "TOPTION_SHOW_ENEMY_LOCATION = " << (gGameSettings.fOptions[TOPTION_SHOW_ENEMY_LOCATION] ? "TRUE" : "FALSE" ) << endl;
|
||||
settings << "TOPTION_ALT_START_AIM = " << (gGameSettings.fOptions[TOPTION_ALT_START_AIM] ? "TRUE" : "FALSE") << endl;
|
||||
settings << "TOPTION_ALT_PATHFINDING = " << (gGameSettings.fOptions[TOPTION_ALT_PATHFINDING] ? "TRUE" : "FALSE") << endl;
|
||||
settings << "TOPTION_USE_LEGACY_TACTICALAI = " << (gGameSettings.fOptions[TOPTION_USE_LEGACY_TACTICALAI] ? "TRUE" : "FALSE") << endl;
|
||||
|
||||
settings << "TOPTION_CHEAT_MODE_OPTIONS_HEADER = " << (gGameSettings.fOptions[TOPTION_CHEAT_MODE_OPTIONS_HEADER] ? "TRUE" : "FALSE" ) << endl;
|
||||
settings << "TOPTION_FORCE_BOBBY_RAY_SHIPMENTS = " << (gGameSettings.fOptions[TOPTION_FORCE_BOBBY_RAY_SHIPMENTS] ? "TRUE" : "FALSE" ) << endl;
|
||||
@@ -846,6 +848,7 @@ void InitGameSettings()
|
||||
gGameSettings.fOptions[TOPTION_SHOW_ENEMY_LOCATION] = FALSE; // sevenfm: show locations of known enemies
|
||||
gGameSettings.fOptions[TOPTION_ALT_START_AIM] = TRUE;
|
||||
gGameSettings.fOptions[TOPTION_ALT_PATHFINDING] = FALSE;
|
||||
gGameSettings.fOptions[TOPTION_USE_LEGACY_TACTICALAI] = FALSE;
|
||||
|
||||
// arynn: Cheat/Debug Menu
|
||||
gGameSettings.fOptions[ TOPTION_CHEAT_MODE_OPTIONS_HEADER ] = FALSE;
|
||||
|
||||
@@ -105,6 +105,7 @@ enum
|
||||
TOPTION_SHOW_ENEMY_LOCATION,
|
||||
TOPTION_ALT_START_AIM,
|
||||
TOPTION_ALT_PATHFINDING,
|
||||
TOPTION_USE_LEGACY_TACTICALAI,
|
||||
|
||||
// arynn: Debug/Cheat
|
||||
TOPTION_CHEAT_MODE_OPTIONS_HEADER,
|
||||
|
||||
@@ -6,6 +6,10 @@ set(ModularizedTacticalAISrc
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/src/LegacyAIPlanFactory.cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/src/LegacyCreaturePlan.cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/src/LegacyZombiePlan.cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/src/BoxerPlan.cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/src/CivilianPlan.cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/src/RobotPlan.cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/src/SoldierPlan.cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/src/NullPlan.cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/src/NullPlanFactory.cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/src/Plan.cpp"
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include "Plan.h"
|
||||
|
||||
namespace AI
|
||||
{
|
||||
namespace tactical
|
||||
{
|
||||
/**@class LegacyAIPlan
|
||||
* @brief Component/Concrete Product. Wrapper/Re-Write of DecideAction()
|
||||
*
|
||||
* Wrapper around boxer related AI uplifted from original DecideAction() routines
|
||||
*/
|
||||
class LegacyAIBoxerPlan: public Plan
|
||||
{
|
||||
private:
|
||||
public:
|
||||
LegacyAIBoxerPlan(SOLDIERTYPE* npc);
|
||||
virtual void execute(PlanInputData& environment);
|
||||
virtual bool done() const {return false;}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include "Plan.h"
|
||||
|
||||
namespace AI
|
||||
{
|
||||
namespace tactical
|
||||
{
|
||||
/**@class LegacyAIPlan
|
||||
* @brief Component/Concrete Product. Wrapper/Re-Write of DecideAction()
|
||||
*
|
||||
* Wrapper around civilian/noncombatant related AI uplifted from original DecideAction() routines
|
||||
*/
|
||||
class CivilianPlan : public Plan
|
||||
{
|
||||
private:
|
||||
public:
|
||||
CivilianPlan(SOLDIERTYPE* npc);
|
||||
virtual void execute(PlanInputData& environment);
|
||||
virtual bool done() const { return false; }
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include "Plan.h"
|
||||
|
||||
namespace AI
|
||||
{
|
||||
namespace tactical
|
||||
{
|
||||
/**@class LegacyAIPlan
|
||||
* @brief Component/Concrete Product. Wrapper/Re-Write of DecideAction()
|
||||
*
|
||||
* Wrapper around robot related AI uplifted from original DecideAction() routines
|
||||
*/
|
||||
class RobotPlan : public Plan
|
||||
{
|
||||
private:
|
||||
public:
|
||||
RobotPlan(SOLDIERTYPE* npc);
|
||||
virtual void execute(PlanInputData& environment);
|
||||
virtual bool done() const { return false; }
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include "Plan.h"
|
||||
|
||||
namespace AI
|
||||
{
|
||||
namespace tactical
|
||||
{
|
||||
/**@class LegacyAIPlan
|
||||
* @brief Component/Concrete Product. Wrapper/Re-Write of DecideAction()
|
||||
*
|
||||
* Wrapper around soldier related AI uplifted from original DecideAction() routines
|
||||
*/
|
||||
class SoldierPlan : public Plan
|
||||
{
|
||||
private:
|
||||
public:
|
||||
SoldierPlan(SOLDIERTYPE* npc);
|
||||
virtual void execute(PlanInputData& environment);
|
||||
virtual bool done() const { return false; }
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
#include "../include/BoxerPlan.h"
|
||||
#include "../../TacticalAI/ai.h"
|
||||
|
||||
namespace AI
|
||||
{
|
||||
namespace tactical
|
||||
{
|
||||
LegacyAIBoxerPlan::LegacyAIBoxerPlan(SOLDIERTYPE* npc)
|
||||
: Plan(npc)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void LegacyAIBoxerPlan::execute(PlanInputData& environment)
|
||||
{
|
||||
switch (get_npc()->aiData.bAlertStatus)
|
||||
{
|
||||
case STATUS_GREEN:
|
||||
get_npc()->aiData.bAction = DecideActionGreenBoxer(get_npc());
|
||||
break;
|
||||
case STATUS_YELLOW:
|
||||
get_npc()->aiData.bAction = DecideActionGreenBoxer(get_npc());
|
||||
break;
|
||||
case STATUS_RED:
|
||||
get_npc()->aiData.bAction = DecideActionBlackBoxer(get_npc());
|
||||
break;
|
||||
case STATUS_BLACK:
|
||||
get_npc()->aiData.bAction = DecideActionBlackBoxer(get_npc());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace tactical
|
||||
} // namespace AI
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
#include "../include/CivilianPlan.h"
|
||||
#include "../../TacticalAI/ai.h"
|
||||
#include "NPC.h"
|
||||
#include "Soldier Profile.h"
|
||||
|
||||
namespace AI
|
||||
{
|
||||
namespace tactical
|
||||
{
|
||||
CivilianPlan::CivilianPlan(SOLDIERTYPE* npc)
|
||||
: Plan(npc)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void CivilianPlan::execute(PlanInputData& environment)
|
||||
{
|
||||
if ( !environment.turn_based() )
|
||||
{
|
||||
if ( (get_npc()->ubProfile != NO_PROFILE) && (gMercProfiles[get_npc()->ubProfile].ubMiscFlags3 & PROFILE_MISC_FLAG3_HANDLE_DONE_TRAVERSAL) )
|
||||
{
|
||||
TriggerNPCWithGivenApproach(get_npc()->ubProfile, APPROACH_DONE_TRAVERSAL, FALSE);
|
||||
gMercProfiles[get_npc()->ubProfile].ubMiscFlags3 &= (~PROFILE_MISC_FLAG3_HANDLE_DONE_TRAVERSAL);
|
||||
get_npc()->ubQuoteActionID = 0;
|
||||
// wait a tiny bit
|
||||
get_npc()->aiData.usActionData = 100;
|
||||
get_npc()->aiData.bAction = AI_ACTION_WAIT;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
switch (get_npc()->aiData.bAlertStatus)
|
||||
{
|
||||
case STATUS_GREEN:
|
||||
get_npc()->aiData.bAction = DecideActionGreenCivilian(get_npc());
|
||||
break;
|
||||
case STATUS_YELLOW:
|
||||
get_npc()->aiData.bAction = DecideActionYellowCivilian(get_npc());
|
||||
break;
|
||||
case STATUS_RED:
|
||||
get_npc()->aiData.bAction = DecideActionRedCivilian(get_npc());
|
||||
break;
|
||||
case STATUS_BLACK:
|
||||
get_npc()->aiData.bAction = DecideActionBlackCivilian(get_npc());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace tactical
|
||||
} // namespace AI
|
||||
|
||||
@@ -7,6 +7,10 @@
|
||||
#include "../include/LegacyCreaturePlan.h"
|
||||
#include "../include/LegacyZombiePlan.h"
|
||||
#include "../include/LegacyArmedVehiclePlan.h"
|
||||
#include "../include/BoxerPlan.h"
|
||||
#include "../include/CivilianPlan.h"
|
||||
#include "../include/RobotPlan.h"
|
||||
#include "../include/SoldierPlan.h"
|
||||
#include "../include/CrowPlan.h"
|
||||
#include "../include/PlanList.h"
|
||||
|
||||
@@ -14,8 +18,7 @@
|
||||
#include "../../Tactical/Soldier Control.h" // For SOLDIERTYPE definition
|
||||
#include "../../Tactical/Animation Data.h" // For the definition of, wait for it... BLOODCAT!
|
||||
#include "Soldier macros.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include "ai.h"
|
||||
|
||||
|
||||
namespace AI
|
||||
@@ -43,6 +46,11 @@ namespace AI
|
||||
if(npc->IsZombie())
|
||||
return new LegacyZombiePlan(npc);
|
||||
|
||||
if (BOXER(npc)) { return new LegacyAIBoxerPlan(npc); }
|
||||
if (IS_CIV_BODY_TYPE(npc)) { return new CivilianPlan(npc); }
|
||||
if (ENEMYROBOT(npc)) { return new RobotPlan(npc); }
|
||||
if (SoldierAI(npc)) { return new SoldierPlan(npc); }
|
||||
|
||||
return new LegacyAIPlan(npc); // no special plan for other cases yet, return default legacy AI wrapper
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
#include "../include/RobotPlan.h"
|
||||
#include "../../TacticalAI/ai.h"
|
||||
|
||||
namespace AI
|
||||
{
|
||||
namespace tactical
|
||||
{
|
||||
RobotPlan::RobotPlan(SOLDIERTYPE* npc)
|
||||
: Plan(npc)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void RobotPlan::execute(PlanInputData& environment)
|
||||
{
|
||||
switch (get_npc()->aiData.bAlertStatus)
|
||||
{
|
||||
case STATUS_GREEN:
|
||||
get_npc()->aiData.bAction = DecideActionGreenRobot(get_npc());
|
||||
break;
|
||||
case STATUS_YELLOW:
|
||||
get_npc()->aiData.bAction = DecideActionYellowRobot(get_npc());
|
||||
break;
|
||||
case STATUS_RED:
|
||||
get_npc()->aiData.bAction = DecideActionRedRobot(get_npc());
|
||||
break;
|
||||
case STATUS_BLACK:
|
||||
get_npc()->aiData.bAction = DecideActionBlackRobot(get_npc());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace tactical
|
||||
} // namespace AI
|
||||
@@ -0,0 +1,104 @@
|
||||
#include "../include/SoldierPlan.h"
|
||||
#include "../../TacticalAI/ai.h"
|
||||
#include "../../TacticalAI/AIInternals.h" // ACTING_ON_SCHEDULE
|
||||
#include "../../TacticalAI/NPC.h" // NPCReachedDestination
|
||||
#include "../../Tactical/Dialogue Control.h" // DialogueQueueIsEmpty
|
||||
#include "../../Utils/Font Control.h" // ScreenMsg about deadlock
|
||||
#include "../../i18n/include/Text.h" // Sniper warning
|
||||
#include "../../Utils/message.h" // ditto
|
||||
|
||||
namespace AI
|
||||
{
|
||||
namespace tactical
|
||||
{
|
||||
SoldierPlan::SoldierPlan(SOLDIERTYPE* npc)
|
||||
: Plan(npc)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void SoldierPlan::execute(PlanInputData& environment)
|
||||
{
|
||||
if (!environment.turn_based())
|
||||
{
|
||||
if ((get_npc()->ubProfile != NO_PROFILE) && (gMercProfiles[get_npc()->ubProfile].ubMiscFlags3 & PROFILE_MISC_FLAG3_HANDLE_DONE_TRAVERSAL))
|
||||
{
|
||||
TriggerNPCWithGivenApproach(get_npc()->ubProfile, APPROACH_DONE_TRAVERSAL, FALSE);
|
||||
gMercProfiles[get_npc()->ubProfile].ubMiscFlags3 &= (~PROFILE_MISC_FLAG3_HANDLE_DONE_TRAVERSAL);
|
||||
get_npc()->ubQuoteActionID = 0;
|
||||
// wait a tiny bit
|
||||
get_npc()->aiData.usActionData = 100;
|
||||
get_npc()->aiData.bAction = AI_ACTION_WAIT;
|
||||
return;
|
||||
}
|
||||
if (get_npc()->bTeam == gbPlayerNum)
|
||||
{
|
||||
if (environment.get_tactical_status().fAutoBandageMode)
|
||||
{
|
||||
get_npc()->aiData.bAction = DecideAutoBandage(get_npc());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (get_npc()->bTeam != MILITIA_TEAM)
|
||||
{
|
||||
if (!sniperwarning && get_npc()->aiData.bOrders == SNIPER)
|
||||
{
|
||||
ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, New113Message[MSG113_WATHCHOUTFORSNIPERS]);
|
||||
sniperwarning = TRUE;
|
||||
|
||||
// Flugente: additional dialogue
|
||||
AdditionalTacticalCharacterDialogue_AllInCurrentSector(NO_PROFILE, ADE_SNIPERWARNING);
|
||||
}
|
||||
|
||||
if (!biggunwarning && FindRocketLauncherOrCannon(get_npc()) != NO_SLOT)
|
||||
{
|
||||
biggunwarning = TRUE;
|
||||
//TODO: don't say this again after reloading a savegame
|
||||
SayQuoteFromAnyBodyInSector(QUOTE_WEARY_SLASH_SUSPUCIOUS);
|
||||
}
|
||||
}
|
||||
get_npc()->aiData.fAIFlags &= (~AI_CAUTIOUS); // turn off cautious flag
|
||||
// if status override is set, bypass RED/YELLOW and go directly to GREEN!
|
||||
if ((get_npc()->aiData.bBypassToGreen) && (get_npc()->aiData.bAlertStatus < STATUS_BLACK))
|
||||
{
|
||||
get_npc()->aiData.bAction = DecideActionGreenSoldier(get_npc());
|
||||
if (!gfTurnBasedAI)
|
||||
{
|
||||
// reset bypass now
|
||||
get_npc()->aiData.bBypassToGreen = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (get_npc()->aiData.bAlertStatus)
|
||||
{
|
||||
case STATUS_GREEN:
|
||||
get_npc()->aiData.bAction = DecideActionGreenSoldier(get_npc());
|
||||
break;
|
||||
case STATUS_YELLOW:
|
||||
get_npc()->aiData.bAction = DecideActionYellowSoldier(get_npc());
|
||||
break;
|
||||
case STATUS_RED:
|
||||
get_npc()->aiData.bAction = DecideActionRedSoldier(get_npc());
|
||||
break;
|
||||
case STATUS_BLACK:
|
||||
//if ( gGameSettings.fOptions[TOPTION_USE_LEGACY_TACTICALAI] ) // Commented out for now since new AI is WIP
|
||||
{
|
||||
get_npc()->aiData.bAction = DecideActionBlackSoldier(get_npc());
|
||||
}
|
||||
//else
|
||||
//{
|
||||
// get_npc()->aiData.bAction = DecideActionBlackSoldierUtilityAI(get_npc());
|
||||
//}
|
||||
break;
|
||||
}
|
||||
}
|
||||
DEBUGAIMSG("Deciding for guynum " << (int)get_npc()->ubID << " at gridno " << get_npc()->sGridNo << ", APs " << get_npc()->bActionPoints <<
|
||||
", decided action: " << (int)get_npc()->aiData.bAction << ", data " << (int)get_npc()->aiData.usActionData);
|
||||
}
|
||||
|
||||
} // namespace tactical
|
||||
} // namespace AI
|
||||
|
||||
@@ -6024,7 +6024,10 @@ void HandleItemCooldownFunctions( OBJECTTYPE* itemStack, INT32 deltaSeconds, BOO
|
||||
FLOAT newguntemperature = max(0.0f, guntemperature - tickspassed * cooldownfactor ); // ... calculate new temperature ...
|
||||
|
||||
#if JA2TESTVERSION
|
||||
ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"World: Item temperature lowered from %4.2f to %4.2f", guntemperature, newguntemperature);
|
||||
if (guntemperature != newguntemperature)
|
||||
{
|
||||
ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"World: Item temperature lowered from %4.2f to %4.2f", guntemperature, newguntemperature);
|
||||
}
|
||||
#endif
|
||||
|
||||
(*itemStack)[i]->data.bTemperature = newguntemperature; // ... set new temperature
|
||||
@@ -6044,7 +6047,10 @@ void HandleItemCooldownFunctions( OBJECTTYPE* itemStack, INT32 deltaSeconds, BOO
|
||||
(*iter)[i]->data.bTemperature = newtemperature; // ... set new temperature
|
||||
|
||||
#if JA2TESTVERSION
|
||||
ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"World: Item temperature lowered from %4.2f to %4.2f", temperature, newtemperature);
|
||||
if (temperature != newtemperature)
|
||||
{
|
||||
ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"World: Item temperature lowered from %4.2f to %4.2f", temperature, newtemperature);
|
||||
}
|
||||
#endif
|
||||
|
||||
// we assume that there can exist only 1 underbarrel weapon per gun
|
||||
|
||||
+6
-5
@@ -21,6 +21,7 @@
|
||||
#include "GameSettings.h" // added by SANDRO
|
||||
#include <Font Control.h>
|
||||
#include <message.h>
|
||||
#include "Soldier macros.h"
|
||||
|
||||
INT32 gsBoxerGridNo[ NUM_BOXERS ] = { 11393, 11233, 11073 };
|
||||
SoldierID gubBoxerID[ NUM_BOXERS ] = { NOBODY, NOBODY, NOBODY };
|
||||
@@ -54,7 +55,7 @@ void ExitBoxing( void )
|
||||
|
||||
if ( pSoldier != NULL )
|
||||
{
|
||||
if ( ( pSoldier->flags.uiStatusFlags & SOLDIER_BOXER ) && InARoom( pSoldier->sGridNo, &usRoom ) && usRoom == BOXING_RING )
|
||||
if ( BOXER(pSoldier) && InARoom( pSoldier->sGridNo, &usRoom ) && usRoom == BOXING_RING )
|
||||
{
|
||||
if ( pSoldier->flags.uiStatusFlags & SOLDIER_PC )
|
||||
{
|
||||
@@ -238,7 +239,7 @@ static void CountPeopleInBoxingRingAndDoActions( void )
|
||||
{
|
||||
++ubPlayersInRing;
|
||||
|
||||
if ( !pNonBoxingPlayer && !(pSoldier->flags.uiStatusFlags & SOLDIER_BOXER) )
|
||||
if ( !pNonBoxingPlayer && !BOXER(pSoldier) )
|
||||
{
|
||||
pNonBoxingPlayer = pSoldier;
|
||||
}
|
||||
@@ -290,7 +291,7 @@ static void CountPeopleInBoxingRingAndDoActions( void )
|
||||
// ladieees and gennleman, we have a fight!
|
||||
for (uiLoop = 0; uiLoop < 2; ++uiLoop)
|
||||
{
|
||||
if (!(pInRing[uiLoop]->flags.uiStatusFlags & SOLDIER_BOXER))
|
||||
if (!BOXER(pInRing[uiLoop]))
|
||||
{
|
||||
// set as boxer!
|
||||
pInRing[uiLoop]->flags.uiStatusFlags |= SOLDIER_BOXER;
|
||||
@@ -511,7 +512,7 @@ void BoxingMovementCheck( SOLDIERTYPE * pSoldier )
|
||||
// someone moving in/into the ring
|
||||
CountPeopleInBoxingRingAndDoActions();
|
||||
}
|
||||
else if ( ( gTacticalStatus.bBoxingState == BOXING ) && ( pSoldier->flags.uiStatusFlags & SOLDIER_BOXER ) )
|
||||
else if ( ( gTacticalStatus.bBoxingState == BOXING ) && BOXER(pSoldier) )
|
||||
{
|
||||
// boxer stepped out of the ring!
|
||||
BoxingPlayerDisqualified( pSoldier, BOXER_OUT_OF_RING );
|
||||
@@ -561,7 +562,7 @@ void ClearAllBoxerFlags( void )
|
||||
{
|
||||
for (UINT32 uiSlot = 0; uiSlot < guiNumMercSlots; ++uiSlot)
|
||||
{
|
||||
if ( MercSlots[ uiSlot ] && MercSlots[ uiSlot ]->flags.uiStatusFlags & SOLDIER_BOXER )
|
||||
if ( MercSlots[ uiSlot ] && BOXER(MercSlots[ uiSlot ]) )
|
||||
{
|
||||
// Flugente: nuke the entire opponent count, remove boxing flag, reevaluate opponent list
|
||||
DecayIndividualOpplist(MercSlots[uiSlot]);
|
||||
|
||||
@@ -758,6 +758,29 @@ void EatFromInventory( SOLDIERTYPE *pSoldier, BOOLEAN fcanteensonly )
|
||||
}
|
||||
}
|
||||
|
||||
void DrinkFromInventory(SOLDIERTYPE* pSoldier)
|
||||
{
|
||||
if ( !pSoldier )
|
||||
return;
|
||||
|
||||
INT8 invsize = pSoldier->inv.size();
|
||||
|
||||
for ( INT8 bLoop = 0; bLoop < invsize; ++bLoop )
|
||||
{
|
||||
if ( pSoldier->inv[bLoop].exists() && ItemIsCanteen(pSoldier->inv[bLoop].usItem) )
|
||||
{
|
||||
OBJECTTYPE* pObj = &(pSoldier->inv[bLoop]);
|
||||
|
||||
if ( pObj && TotalPoints(pObj) > 1 )
|
||||
{
|
||||
//ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, Message[STR_DRINKS], pSoldier->GetName());
|
||||
ApplyConsumable(pSoldier, pObj, false, true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HourlyFoodUpdate( void )
|
||||
{
|
||||
SoldierID bMercID, bLastTeamID;
|
||||
|
||||
@@ -108,5 +108,6 @@ void SoldierAutoFillCanteens(SOLDIERTYPE *pSoldier);
|
||||
BOOLEAN HasFoodInInventory( SOLDIERTYPE *pSoldier, BOOLEAN fCheckFood, BOOLEAN fCheckDrink );
|
||||
|
||||
void DrinkFromWaterTap( SOLDIERTYPE *pSoldier );
|
||||
void DrinkFromInventory(SOLDIERTYPE* pSoldier);
|
||||
|
||||
#endif
|
||||
|
||||
+51
-2
@@ -73,7 +73,7 @@
|
||||
#include "TeamTurns.h"
|
||||
#include "Map Screen Interface.h" // added by Flugente for SquadNames
|
||||
#include "Keys.h" // added by silversurfer for door handling from the side
|
||||
|
||||
#include "Cheats.h"
|
||||
#include "AIInternals.h"
|
||||
extern BOOLEAN gubWorldTileInLight[MAX_ALLOWED_WORLD_MAX];
|
||||
extern BOOLEAN gubIsCorpseThere[MAX_ALLOWED_WORLD_MAX];
|
||||
@@ -372,7 +372,7 @@ BOOLEAN gfDisplayTimerCursor = FALSE;
|
||||
UINT32 guiTimerCursorID = 0;
|
||||
UINT32 guiTimerLastUpdate = 0;
|
||||
UINT32 guiTimerCursorDelay = 0;
|
||||
|
||||
UINT8 gRenderDebugInfoMode = DEBUG_OFF;
|
||||
|
||||
CHAR16 gzLocation[ 20 ];
|
||||
BOOLEAN gfLocation = FALSE;
|
||||
@@ -500,6 +500,54 @@ void GetMercOknoDirection( SoldierID ubSoldierID, BOOLEAN *pfGoDown, BOOLEAN *pf
|
||||
}
|
||||
//----------------------------------------------------------------------------------
|
||||
|
||||
void HandleRenderDebugInfoModes()
|
||||
{
|
||||
if (DEBUG_CHEAT_LEVEL())
|
||||
{
|
||||
switch (gRenderDebugInfoMode)
|
||||
{
|
||||
case DEBUG_PATHFINDING:
|
||||
// Nothing to do here, pathfinding info is filled in the pathing functions.
|
||||
break;
|
||||
case DEBUG_THREATVALUE:
|
||||
break;
|
||||
case DEBUG_COVERVALUE:
|
||||
// Calculate cover values for pSoldier under cursor, or for currently selected merc, if nobody is under the cursor.
|
||||
if (gTacticalStatus.Team[OUR_TEAM].bTeamActive)
|
||||
{
|
||||
static SOLDIERTYPE* previousSoldier = nullptr;
|
||||
static INT32 previousLocation = NOWHERE;
|
||||
static UINT8 previousStance = 0;
|
||||
|
||||
SoldierID usSoldierIndex = NOBODY;
|
||||
UINT32 uiMercFlags;
|
||||
FindSoldierFromMouse(&usSoldierIndex, &uiMercFlags);
|
||||
if (usSoldierIndex == NOBODY)
|
||||
{
|
||||
usSoldierIndex = gusSelectedSoldier;
|
||||
}
|
||||
|
||||
if (usSoldierIndex != NOBODY)
|
||||
{
|
||||
// Get Soldier
|
||||
INT32 iPercentBetter;
|
||||
SOLDIERTYPE* pSoldier;
|
||||
GetSoldier(&pSoldier, usSoldierIndex);
|
||||
if (previousSoldier != pSoldier || previousLocation != pSoldier->sGridNo || previousStance != gAnimControl[pSoldier->usAnimState].ubEndHeight)
|
||||
{
|
||||
FindBestNearbyCover(pSoldier, pSoldier->aiData.bAIMorale, &iPercentBetter, NOWHERE, false);
|
||||
previousSoldier = pSoldier;
|
||||
previousLocation = pSoldier->sGridNo;
|
||||
previousStance = gAnimControl[pSoldier->usAnimState].ubEndHeight;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default: // off
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PreventFromTheFreezingBug(SOLDIERTYPE* pSoldier)
|
||||
{
|
||||
@@ -673,6 +721,7 @@ UINT32 HandleTacticalUI( void )
|
||||
}
|
||||
}
|
||||
|
||||
HandleRenderDebugInfoModes();
|
||||
// Check if current event has changed and clear event if so, to prepare it for execution
|
||||
// Clearing it does things like set first time flag, param variables, etc
|
||||
if ( uiNewEvent != guiOldEvent )
|
||||
|
||||
@@ -184,6 +184,7 @@ extern UINT32 guiCurrentEvent;
|
||||
extern INT16 gsSelectedLevel;
|
||||
extern BOOLEAN gfPlotNewMovement;
|
||||
extern UINT32 guiPendingOverrideEvent;
|
||||
extern UINT8 gRenderDebugInfoMode;
|
||||
|
||||
|
||||
// GLOBALS
|
||||
|
||||
@@ -6265,7 +6265,7 @@ BOOLEAN ShowSoldierRoleSymbol(SOLDIERTYPE* pSoldier)
|
||||
if ( pSoldier->usSkillCounter[SOLDIER_COUNTER_ROLE_OBSERVED] >= gGameExternalOptions.usTurnsToUncover )
|
||||
{
|
||||
// are we a VIP? show that only when the player knows a VIP is in this sector. otherwise, don't even show our officer property
|
||||
if ( pSoldier->usSoldierFlagMask & SOLDIER_VIP && !pSoldier->bSectorZ )
|
||||
if (ISVIP(pSoldier) && !pSoldier->bSectorZ )
|
||||
{
|
||||
if ( PlayerKnowsAboutVIP( pSoldier->sSectorX, pSoldier->sSectorY ) )
|
||||
{
|
||||
|
||||
@@ -12426,6 +12426,51 @@ INT8 FindCamoKit( SOLDIERTYPE * pSoldier )
|
||||
return( NO_SLOT );
|
||||
}
|
||||
|
||||
INT8 FindCanteen(SOLDIERTYPE* pSoldier)
|
||||
{
|
||||
const INT8 invsize = pSoldier->inv.size();
|
||||
for ( INT8 bLoop = 0; bLoop < invsize; ++bLoop )
|
||||
{
|
||||
if ( pSoldier->inv[bLoop].exists() && ItemIsCanteen(pSoldier->inv[bLoop].usItem) )
|
||||
{
|
||||
OBJECTTYPE* pObj = &(pSoldier->inv[bLoop]);
|
||||
if ( pObj && TotalPoints(pObj) > 1 )
|
||||
{
|
||||
return(bLoop);
|
||||
}
|
||||
}
|
||||
}
|
||||
return(NO_SLOT);
|
||||
}
|
||||
|
||||
INT8 FindWirecutters(SOLDIERTYPE* pSoldier)
|
||||
{
|
||||
const INT8 invsize = pSoldier->inv.size();
|
||||
for ( INT8 bLoop = 0; bLoop < invsize; ++bLoop )
|
||||
{
|
||||
if ( pSoldier->inv[bLoop].exists() && ItemIsWirecutters(pSoldier->inv[bLoop].usItem) )
|
||||
{
|
||||
return(bLoop);
|
||||
}
|
||||
}
|
||||
|
||||
return NO_SLOT;
|
||||
}
|
||||
|
||||
INT8 FindTNT(SOLDIERTYPE* pSoldier)
|
||||
{
|
||||
const INT8 invsize = pSoldier->inv.size();
|
||||
for ( INT8 bLoop = 0; bLoop < invsize; ++bLoop )
|
||||
{
|
||||
if ( pSoldier->inv[bLoop].exists() && ItemIsTNT(pSoldier->inv[bLoop].usItem) )
|
||||
{
|
||||
return(bLoop);
|
||||
}
|
||||
}
|
||||
|
||||
return NO_SLOT;
|
||||
}
|
||||
|
||||
//JMich_SkillModifiers: Adding a function to see if we have an item with disarm bonus
|
||||
INT8 FindDisarmKit( SOLDIERTYPE * pSoldier )
|
||||
{
|
||||
@@ -16045,6 +16090,20 @@ BOOLEAN FindAttachmentRange(UINT16 usAttachment, UINT32* pStartIndex, UINT32* pE
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
BOOLEAN ItemIsTNT(UINT16 usItem)
|
||||
{
|
||||
if ( Item[usItem].usItemClass == IC_BOMB &&
|
||||
!ItemIsMine(usItem) &&
|
||||
!ItemIsTripwire(usItem) &&
|
||||
Explosive[Item[usItem].ubClassIndex].ubType == EXPLOSV_NORMAL &&
|
||||
Explosive[Item[usItem].ubClassIndex].ubDamage > 40 &&
|
||||
GetLauncherFromLaunchable(usItem) == NOTHING )
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
////////////////////////////////////
|
||||
// Item flagmask utility functions
|
||||
// Just to improve readability
|
||||
|
||||
@@ -249,6 +249,7 @@ BOOLEAN ItemIsOnlyInDisease(UINT16 usItem);
|
||||
BOOLEAN ItemProvidesRobotCamo(UINT16 usItem);
|
||||
BOOLEAN ItemProvidesRobotNightvision(UINT16 usItem);
|
||||
BOOLEAN ItemProvidesRobotLaserBonus(UINT16 usItem);
|
||||
BOOLEAN ItemIsTNT(UINT16 usItem);
|
||||
|
||||
//Existing functions without header def's, added them here, just incase I'll need to call
|
||||
//them from the editor.
|
||||
@@ -479,9 +480,13 @@ INT8 FindFirstAidKit( SOLDIERTYPE * pSoldier );
|
||||
INT8 FindDisarmKit( SOLDIERTYPE * pSoldier ); //JMich_SkillsModifiers: Added function to check for disarm bonus
|
||||
INT8 FindLocksmithKit( SOLDIERTYPE * pSoldier );
|
||||
INT8 FindCamoKit( SOLDIERTYPE * pSoldier );
|
||||
INT8 FindCanteen(SOLDIERTYPE* pSoldier);
|
||||
INT8 FindWalkman( SOLDIERTYPE * pSoldier );
|
||||
INT8 FindTrigger( SOLDIERTYPE * pSoldier );
|
||||
INT8 FindRemoteControl( SOLDIERTYPE * pSoldier );
|
||||
INT8 FindWirecutters(SOLDIERTYPE* pSoldier);
|
||||
INT8 FindTNT(SOLDIERTYPE* pSoldier);
|
||||
|
||||
INT16 GetWornCamo( SOLDIERTYPE * pSoldier );
|
||||
INT16 GetCamoBonus( OBJECTTYPE * pObj );
|
||||
INT16 GetWornStealth( SOLDIERTYPE * pSoldier );
|
||||
|
||||
+42
-14
@@ -3026,7 +3026,7 @@ BOOLEAN HandleAtNewGridNo( SOLDIERTYPE *pSoldier, BOOLEAN *pfKeepMoving )
|
||||
|
||||
// sevenfm: check all nearby enemy boxers for opportunity attack
|
||||
if (IS_MERC_BODY_TYPE(pSoldier) &&
|
||||
(pSoldier->flags.uiStatusFlags & SOLDIER_BOXER) &&
|
||||
BOXER(pSoldier) &&
|
||||
gTacticalStatus.bBoxingState == BOXING &&
|
||||
pSoldier->aiData.bAlertStatus >= STATUS_RED)
|
||||
{
|
||||
@@ -3047,7 +3047,7 @@ BOOLEAN HandleAtNewGridNo( SOLDIERTYPE *pSoldier, BOOLEAN *pfKeepMoving )
|
||||
pOpponent->bCollapsed ||
|
||||
pOpponent->bBreathCollapsed ||
|
||||
!IS_MERC_BODY_TYPE(pOpponent) ||
|
||||
!(pOpponent->flags.uiStatusFlags & SOLDIER_BOXER) ||
|
||||
!BOXER(pOpponent) ||
|
||||
gAnimControl[pOpponent->usAnimState].ubEndHeight < ANIM_STAND ||
|
||||
pOpponent->pathing.bLevel != pSoldier->pathing.bLevel ||
|
||||
!SoldierToSoldierLineOfSightTest(pOpponent, pSoldier, TRUE, CALC_FROM_WANTED_DIR) ||
|
||||
@@ -5031,11 +5031,11 @@ BOOLEAN NewOKDestination( SOLDIERTYPE * pCurrSoldier, INT32 sGridNo, BOOLEAN fPe
|
||||
BOOLEAN fOKCheckStruct;
|
||||
|
||||
// Allow civilians and NPCs with profile to go off screen, and also enemies if tactical retreat is enabled
|
||||
auto destinationOffscreen = !(GridNoOnVisibleWorldTile(sGridNo));
|
||||
auto hasProfile = pCurrSoldier->ubProfile != NO_PROFILE;
|
||||
auto isCivilian = pCurrSoldier->bTeam == CIV_TEAM;
|
||||
auto isEnemy = pCurrSoldier->bTeam == ENEMY_TEAM;
|
||||
auto retreatAllowed = gGameExternalOptions.fAITacticalRetreat == true;
|
||||
const auto destinationOffscreen = !(GridNoOnVisibleWorldTile(sGridNo));
|
||||
const auto hasProfile = pCurrSoldier->ubProfile != NO_PROFILE;
|
||||
const auto isCivilian = pCurrSoldier->bTeam == CIV_TEAM;
|
||||
const auto isEnemy = pCurrSoldier->bTeam == ENEMY_TEAM;
|
||||
const auto retreatAllowed = gGameExternalOptions.fAITacticalRetreat == TRUE;
|
||||
|
||||
if (destinationOffscreen && !(isCivilian || hasProfile || (isEnemy && retreatAllowed)))
|
||||
{
|
||||
@@ -5400,6 +5400,34 @@ BOOLEAN TeamMemberNear( INT8 bTeam, INT32 sGridNo, INT32 iRange )
|
||||
return(FALSE);
|
||||
}
|
||||
|
||||
BOOLEAN NotDeafTeamMemberNear(INT8 bTeam, INT32 sGridNo, INT32 iRange)
|
||||
{
|
||||
for ( SoldierID pSoldier = gTacticalStatus.Team[bTeam].bFirstID; pSoldier <= gTacticalStatus.Team[bTeam].bLastID; ++pSoldier )
|
||||
{
|
||||
if ( pSoldier->bActive &&
|
||||
pSoldier->bInSector &&
|
||||
pSoldier->stats.bLife >= OKLIFE &&
|
||||
pSoldier->bDeafenedCounter == 0 )
|
||||
{
|
||||
if ( PythSpacesAway(pSoldier->sGridNo, sGridNo) <= iRange )
|
||||
{
|
||||
return(TRUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return(FALSE);
|
||||
}
|
||||
|
||||
BOOLEAN PlayerCanHearNoise(SOLDIERTYPE* pSoldier)
|
||||
{
|
||||
if ( pSoldier &&
|
||||
(pSoldier->bVisible == TRUE || NotDeafTeamMemberNear(gbPlayerNum, pSoldier->sGridNo, TACTICAL_RANGE / 2) || NightLight() && !(gTacticalStatus.uiFlags & TURNBASED && gTacticalStatus.uiFlags & INCOMBAT) && NotDeafTeamMemberNear(gbPlayerNum, pSoldier->sGridNo, TACTICAL_RANGE)) )
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
INT32 FindAdjacentGridEx( SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT8 *pubDirection, INT32 *psAdjustedGridNo, BOOLEAN fForceToPerson, BOOLEAN fDoor, bool allow_diagonal )
|
||||
{
|
||||
// psAdjustedGridNo gets the original gridno or the new one if updated
|
||||
@@ -7175,7 +7203,7 @@ static void RemoveCapturedEnemiesFromSectorInfo( INT16 sMapX, INT16 sMapY, INT8
|
||||
//if ( pTeamSoldier->stats.bLife >= OKLIFE && pTeamSoldier->stats.bLife != 0 )
|
||||
{
|
||||
// officers and generals are 'special' prisoners...
|
||||
if ( pTeamSoldier->usSoldierFlagMask & SOLDIER_VIP )
|
||||
if (ISVIP(pTeamSoldier))
|
||||
++sNumPrisoner[PRISONER_GENERAL];
|
||||
// downed pilots count as officers too, even though they are civilians. This makes capturing them more rewarding
|
||||
else if ( (pTeamSoldier->usSoldierFlagMask & SOLDIER_ENEMY_OFFICER) || pTeamSoldier->ubCivilianGroup == DOWNEDPILOT_CIV_GROUP )
|
||||
@@ -7204,7 +7232,7 @@ static void RemoveCapturedEnemiesFromSectorInfo( INT16 sMapX, INT16 sMapY, INT8
|
||||
}
|
||||
|
||||
// Flugente: VIPs
|
||||
if ( pTeamSoldier->usSoldierFlagMask & SOLDIER_VIP )
|
||||
if (ISVIP(pTeamSoldier))
|
||||
DeleteVIP( pTeamSoldier->sSectorX, pTeamSoldier->sSectorY );
|
||||
|
||||
// Flugente: turncoats
|
||||
@@ -9251,10 +9279,10 @@ BOOLEAN ProcessImplicationsOfPCAttack( SOLDIERTYPE * pSoldier, SOLDIERTYPE ** pp
|
||||
if ( gTacticalStatus.bBoxingState == BOXING )
|
||||
{
|
||||
// should have a check for "in boxing ring", no?
|
||||
if ( ( pSoldier->usAttackingWeapon != NOTHING && !ItemIsBrassKnuckles(pSoldier->usAttackingWeapon)) || !( pSoldier->flags.uiStatusFlags & SOLDIER_BOXER ) || pSoldier->IsRiotShieldEquipped() )
|
||||
if ( ( pSoldier->usAttackingWeapon != NOTHING && !ItemIsBrassKnuckles(pSoldier->usAttackingWeapon)) || !BOXER(pSoldier) || pSoldier->IsRiotShieldEquipped() )
|
||||
{
|
||||
// someone's cheating!
|
||||
if ( (Item[ pSoldier->usAttackingWeapon ].usItemClass == IC_BLADE || Item[ pSoldier->usAttackingWeapon ].usItemClass == IC_PUNCH) && (pTarget->flags.uiStatusFlags & SOLDIER_BOXER) )
|
||||
if ( (Item[ pSoldier->usAttackingWeapon ].usItemClass == IC_BLADE || Item[ pSoldier->usAttackingWeapon ].usItemClass == IC_PUNCH) && BOXER(pTarget) )
|
||||
{
|
||||
// knife or brass knuckles disqualify the player!
|
||||
BoxingPlayerDisqualified( pSoldier, BAD_ATTACK );
|
||||
@@ -9265,7 +9293,7 @@ BOOLEAN ProcessImplicationsOfPCAttack( SOLDIERTYPE * pSoldier, SOLDIERTYPE ** pp
|
||||
//gTacticalStatus.bBoxingState = NOT_BOXING;
|
||||
SetBoxingState( NOT_BOXING );
|
||||
// if we are attacking a boxer we should set them to neutral (temporarily) so that the rest of the civgroup code works...
|
||||
if ( (pTarget->bTeam == CIV_TEAM) && (pTarget->flags.uiStatusFlags & SOLDIER_BOXER) )
|
||||
if ( (pTarget->bTeam == CIV_TEAM) && BOXER(pTarget) )
|
||||
{
|
||||
SetSoldierNeutral( pTarget );
|
||||
}
|
||||
@@ -9412,7 +9440,7 @@ BOOLEAN ProcessImplicationsOfPCAttack( SOLDIERTYPE * pSoldier, SOLDIERTYPE ** pp
|
||||
//TriggerNPCWithIHateYouQuote( pTarget->ubProfile );
|
||||
}
|
||||
}
|
||||
else if ( pTarget->ubCivilianGroup != NON_CIV_GROUP && !( pTarget->flags.uiStatusFlags & SOLDIER_BOXER ) )
|
||||
else if ( pTarget->ubCivilianGroup != NON_CIV_GROUP && !BOXER(pTarget) )
|
||||
{
|
||||
// Firing at a civ in a civ group who isn't hostile... if anyone in that civ group can see this
|
||||
// going on they should become hostile.
|
||||
@@ -10848,7 +10876,7 @@ static void TurnCoatAttemptMessageBoxCallBack( UINT8 ubExitValue )
|
||||
UINT8 approachchance = gusSelectedSoldier->GetTurncoatConvinctionChance( prisonerdialoguetargetID, approachselected );
|
||||
|
||||
// you can never turn a VIP (though we don't tell the player if someone is a VIP, lest they have an exploit to find out)
|
||||
if ( pSoldier->usSoldierFlagMask & SOLDIER_VIP )
|
||||
if (ISVIP(pSoldier))
|
||||
approachchance = 0;
|
||||
|
||||
// as using random numbers to pass the check would result in players savescumming, use a number based on the soldier's stats
|
||||
|
||||
+3
-1
@@ -261,6 +261,9 @@ void SelectNextAvailSoldier( SOLDIERTYPE *pSoldier );
|
||||
BOOLEAN TeamMemberNear(INT8 bTeam, INT32 sGridNo, INT32 iRange);
|
||||
BOOLEAN IsValidTargetMerc( SoldierID ubSoldierID );
|
||||
|
||||
BOOLEAN NotDeafTeamMemberNear(INT8 bTeam, INT32 sGridNo, INT32 iRange);
|
||||
BOOLEAN PlayerCanHearNoise(SOLDIERTYPE* pSoldier);
|
||||
|
||||
// FUNCTIONS FOR MANIPULATING MERC SLOTS - A LIST OF ALL ACTIVE MERCS
|
||||
INT32 GetFreeMercSlot( );
|
||||
INT32 AddMercSlot( SOLDIERTYPE *pSoldier );
|
||||
@@ -433,4 +436,3 @@ BOOLEAN IsFreeSlotAvailable( int aTeam );
|
||||
void AttemptToCapturePlayerSoldiers();
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
+35
-99
@@ -36,6 +36,9 @@
|
||||
#include "BinaryHeap.hpp"
|
||||
#include "opplist.h"
|
||||
#include "Weapons.h"
|
||||
#include "renderworld.h"
|
||||
#include "Cheats.h"
|
||||
#include "Handle UI.h"
|
||||
|
||||
//forward declarations of common classes to eliminate includes
|
||||
class OBJECTTYPE;
|
||||
@@ -56,19 +59,8 @@ extern BOOLEAN InGasSpot(SOLDIERTYPE *pSoldier, INT32 sGridNo, INT8 bLevel);
|
||||
// skiplist has extra level of pointers every 4 elements, so a level 5is optimized for
|
||||
// 4 to the power of 5 elements, or 2 to the power of 10, 1024
|
||||
|
||||
//#define PATHAI_VISIBLE_DEBUG
|
||||
|
||||
//#define PATHAI_SKIPLIST_DEBUG
|
||||
|
||||
#ifdef PATHAI_VISIBLE_DEBUG
|
||||
#include "video.h"
|
||||
|
||||
//extern INT16 gsCoverValue[WORLD_MAX];
|
||||
extern INT16 * gsCoverValue;
|
||||
BOOLEAN gfDisplayCoverValues = TRUE;
|
||||
BOOLEAN gfDrawPathPoints = TRUE;
|
||||
#endif
|
||||
|
||||
BOOLEAN gfPlotPathToExitGrid = FALSE;
|
||||
BOOLEAN gfRecalculatingExistingPathCost = FALSE;
|
||||
UINT8 gubGlobalPathFlags = 0;
|
||||
@@ -613,15 +605,12 @@ int AStarPathfinder::GetPath(SOLDIERTYPE *s ,
|
||||
}
|
||||
|
||||
|
||||
|
||||
#if defined( PATHAI_VISIBLE_DEBUG )
|
||||
if (gfDisplayCoverValues && gfDrawPathPoints)
|
||||
if (gRenderDebugInfoMode == DEBUG_PATHFINDING && DEBUG_CHEAT_LEVEL())
|
||||
{
|
||||
memset( gsCoverValue, 0x7F, sizeof( INT16 ) * WORLD_MAX );
|
||||
ResetDebugInfoValues();
|
||||
gRenderDebugInfoValues[ StartNode ] = 0;
|
||||
PATHAI_VISIBLE_DEBUG_Counter = 1;
|
||||
}
|
||||
gsCoverValue[ StartNode ] = 0;
|
||||
PATHAI_VISIBLE_DEBUG_Counter = 1;
|
||||
#endif
|
||||
|
||||
//init other private data, mostly flags
|
||||
endDir = lastDir = direction = startDir = 0;
|
||||
@@ -786,12 +775,10 @@ int AStarPathfinder::GetPath(SOLDIERTYPE *s ,
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined( PATHAI_VISIBLE_DEBUG )
|
||||
if (gfDisplayCoverValues && gfDrawPathPoints)
|
||||
if (gRenderDebugInfoMode == DEBUG_PATHFINDING && DEBUG_CHEAT_LEVEL())
|
||||
{
|
||||
SetRenderFlags( RENDER_FLAG_FULL );
|
||||
}
|
||||
#endif
|
||||
|
||||
// Count the number of steps, but keep it less than the max path length.
|
||||
// Adjust the parent until it begins at the tail end of the max path length (or the dest if reachable)
|
||||
@@ -874,12 +861,10 @@ int AStarPathfinder::GetPath(SOLDIERTYPE *s ,
|
||||
sizePath = giPathDataSize;
|
||||
}
|
||||
|
||||
#if defined( PATHAI_VISIBLE_DEBUG )
|
||||
if (gfDisplayCoverValues && gfDrawPathPoints)
|
||||
if (gRenderDebugInfoMode == DEBUG_PATHFINDING && DEBUG_CHEAT_LEVEL())
|
||||
{
|
||||
SetRenderFlags( RENDER_FLAG_FULL );
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef COUNT_PATHS
|
||||
guiSuccessfulPathChecks++;
|
||||
@@ -991,15 +976,13 @@ void AStarPathfinder::ExecuteAStarLogic()
|
||||
SetAStarStatus(ParentNode, AStar_Closed);
|
||||
//ClosedList.push_back(ParentNode);
|
||||
|
||||
#if defined( PATHAI_VISIBLE_DEBUG )
|
||||
if (gfDisplayCoverValues && gfDrawPathPoints)
|
||||
if (gRenderDebugInfoMode == DEBUG_PATHFINDING && DEBUG_CHEAT_LEVEL())
|
||||
{
|
||||
if (gsCoverValue[ ParentNode ] > 0)
|
||||
if (gRenderDebugInfoValues[ParentNode] > 0)
|
||||
{
|
||||
gsCoverValue[ ParentNode ] *= -1;
|
||||
gRenderDebugInfoValues[ParentNode] *= -1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Shouldn't G and AP be the same thing?
|
||||
INT16 baseGCost = GetAStarG(ParentNode);
|
||||
@@ -1082,7 +1065,7 @@ void AStarPathfinder::ExecuteAStarLogic()
|
||||
gpWorldLevelData[ CurrentNode ].ubExtFlags[0] |= MAPELEMENT_EXT_CLIMBPOINT;
|
||||
gpWorldLevelData[ ParentNode ].ubExtFlags[1] |= MAPELEMENT_EXT_CLIMBPOINT;
|
||||
#ifdef ROOF_DEBUG
|
||||
gsCoverValue[CurrentNode] = 1;
|
||||
gRenderDebugInfoValues[CurrentNode] = 1;
|
||||
#endif
|
||||
|
||||
}
|
||||
@@ -1180,26 +1163,13 @@ void AStarPathfinder::ExecuteAStarLogic()
|
||||
int AStarH = CalcH();
|
||||
int AStarF = (AStarG + extraGCoverCost) + AStarH;
|
||||
|
||||
#if defined( PATHAI_VISIBLE_DEBUG )
|
||||
if (gfDisplayCoverValues && gfDrawPathPoints)
|
||||
if (gRenderDebugInfoMode == DEBUG_PATHFINDING && DEBUG_CHEAT_LEVEL())
|
||||
{
|
||||
if (gsCoverValue[CurrentNode] == 0x7F7F)
|
||||
if (gRenderDebugInfoValues[CurrentNode] == 0x7FFFFFFF)
|
||||
{
|
||||
//gsCoverValue[CurrentNode] = PATHAI_VISIBLE_DEBUG_Counter++;
|
||||
gsCoverValue[CurrentNode] = (INT16) AStarF;
|
||||
gRenderDebugInfoValues[CurrentNode] = (INT16) AStarF;
|
||||
}
|
||||
/*
|
||||
else if (gsCoverValue[CurrentNodeIndex] >= 0)
|
||||
{
|
||||
gsCoverValue[CurrentNodeIndex]++;
|
||||
}
|
||||
else
|
||||
{
|
||||
gsCoverValue[CurrentNodeIndex]--;
|
||||
}
|
||||
*/
|
||||
}
|
||||
#endif
|
||||
|
||||
//insert this node onto the heap
|
||||
if (GetAStarStatus(CurrentNode) == AStar_Init)
|
||||
@@ -2250,9 +2220,7 @@ INT32 FindBestPath(SOLDIERTYPE *s , INT32 sDestination, INT8 bLevel, INT16 usMov
|
||||
CHAR8 zTempString[1000], zTS[50];
|
||||
#endif
|
||||
|
||||
#ifdef PATHAI_VISIBLE_DEBUG
|
||||
UINT16 usCounter = 0;
|
||||
#endif
|
||||
|
||||
fVehicle = FALSE;
|
||||
iOriginationX = iOriginationY = 0;
|
||||
@@ -2502,12 +2470,10 @@ if(!GridNoOnVisibleWorldTile(iDestination))
|
||||
memset( pathQ, 0, iMaxPathQ * sizeof( path_t ) );
|
||||
memset( trailTree, 0, iMaxTrailTree * sizeof( trail_t ) );
|
||||
|
||||
#if defined( PATHAI_VISIBLE_DEBUG )
|
||||
if (gfDisplayCoverValues && gfDrawPathPoints)
|
||||
if (gRenderDebugInfoMode == DEBUG_PATHFINDING && DEBUG_CHEAT_LEVEL())
|
||||
{
|
||||
memset( gsCoverValue, 0x7F, sizeof( INT16 ) * WORLD_MAX );
|
||||
ResetDebugInfoValues();
|
||||
}
|
||||
#endif
|
||||
|
||||
bSkipListLevel = 1;
|
||||
iSkipListSize = 0;
|
||||
@@ -2619,15 +2585,13 @@ if(!GridNoOnVisibleWorldTile(iDestination))
|
||||
// remember the cost used to get here...
|
||||
prevCost = gubWorldMovementCosts[ trailTree[ sCurPathNdx ].sGridNo ][ trailTree[ sCurPathNdx ].stepDir ][ bLevel ];
|
||||
|
||||
#if defined( PATHAI_VISIBLE_DEBUG )
|
||||
if (gfDisplayCoverValues && gfDrawPathPoints)
|
||||
if (gRenderDebugInfoMode == DEBUG_PATHFINDING && DEBUG_CHEAT_LEVEL())
|
||||
{
|
||||
if (gsCoverValue[ curLoc ] > 0)
|
||||
if (gRenderDebugInfoValues[ curLoc ] > 0)
|
||||
{
|
||||
gsCoverValue[ curLoc ] *= -1;
|
||||
gRenderDebugInfoValues[ curLoc ] *= -1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
if (fTurnSlow)
|
||||
@@ -3553,27 +3517,13 @@ if(!GridNoOnVisibleWorldTile(iDestination))
|
||||
// costs less than the best so far to the same location?
|
||||
if (trailCostUsed[newLoc] != gubGlobalPathCount || newTotCost < trailCost[newLoc])
|
||||
{
|
||||
|
||||
#if defined( PATHAI_VISIBLE_DEBUG )
|
||||
|
||||
if (gfDisplayCoverValues && gfDrawPathPoints)
|
||||
{
|
||||
if (gsCoverValue[newLoc] == 0x7F7F)
|
||||
if (gRenderDebugInfoMode == DEBUG_PATHFINDING && DEBUG_CHEAT_LEVEL())
|
||||
{
|
||||
if (gRenderDebugInfoValues[newLoc] == 0x7FFFFFFF)
|
||||
{
|
||||
gsCoverValue[newLoc] = usCounter++;
|
||||
gRenderDebugInfoValues[newLoc] = usCounter++;
|
||||
}
|
||||
/*
|
||||
else if (gsCoverValue[newLoc] >= 0)
|
||||
{
|
||||
gsCoverValue[newLoc]++;
|
||||
}
|
||||
else
|
||||
{
|
||||
gsCoverValue[newLoc]--;
|
||||
}
|
||||
*/
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
//NEWQUENODE;
|
||||
{
|
||||
@@ -3816,20 +3766,13 @@ ENDOFLOOP:
|
||||
while (pathQNotEmpty && pathNotYetFound);
|
||||
|
||||
|
||||
#if defined( PATHAI_VISIBLE_DEBUG )
|
||||
if (gfDisplayCoverValues && gfDrawPathPoints)
|
||||
if (gRenderDebugInfoMode == DEBUG_PATHFINDING && DEBUG_CHEAT_LEVEL())
|
||||
{
|
||||
if ( guiCurrentScreen == GAME_SCREEN )
|
||||
{
|
||||
SetRenderFlags( RENDER_FLAG_FULL );
|
||||
if ( guiCurrentScreen == GAME_SCREEN )
|
||||
{
|
||||
RenderWorld();
|
||||
RenderCoverDebug( );
|
||||
InvalidateScreen( );
|
||||
EndFrameBufferRender();
|
||||
RefreshScreen( NULL );
|
||||
}
|
||||
InvalidateRegion(gsVIEWPORT_START_X, gsVIEWPORT_START_Y, gsVIEWPORT_END_X, gsVIEWPORT_WINDOW_END_Y);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// work finished. Did we find a path?
|
||||
@@ -3884,17 +3827,10 @@ ENDOFLOOP:
|
||||
|
||||
}
|
||||
|
||||
#if defined( PATHAI_VISIBLE_DEBUG )
|
||||
if (gfDisplayCoverValues && gfDrawPathPoints)
|
||||
{
|
||||
SetRenderFlags( RENDER_FLAG_FULL );
|
||||
RenderWorld();
|
||||
RenderCoverDebug( );
|
||||
InvalidateScreen( );
|
||||
EndFrameBufferRender();
|
||||
RefreshScreen( NULL );
|
||||
}
|
||||
#endif
|
||||
if (gRenderDebugInfoMode == DEBUG_PATHFINDING && DEBUG_CHEAT_LEVEL())
|
||||
{
|
||||
InvalidateRegion(gsVIEWPORT_START_X, gsVIEWPORT_START_Y, gsVIEWPORT_END_X, gsVIEWPORT_WINDOW_END_Y);
|
||||
}
|
||||
|
||||
|
||||
// return path length : serves as a "successful" flag and a path length counter
|
||||
|
||||
@@ -4223,7 +4223,7 @@ BOOLEAN HandleSoldierDeath( SOLDIERTYPE *pSoldier , BOOLEAN *pfMadeCorpse )
|
||||
}
|
||||
|
||||
// Flugente: VIPs
|
||||
if ( pSoldier->usSoldierFlagMask & SOLDIER_VIP )
|
||||
if (ISVIP(pSoldier))
|
||||
{
|
||||
DeleteVIP( pSoldier->sSectorX, pSoldier->sSectorY );
|
||||
}
|
||||
|
||||
@@ -9700,6 +9700,85 @@ void SOLDIERTYPE::BeginSoldierClimbWallUp( void )
|
||||
}
|
||||
//------------------------------------------------------------------------------------------
|
||||
|
||||
void SOLDIERTYPE::BeginSoldierJumpWindowAI(void)
|
||||
{
|
||||
DebugAI(AI_MSG_INFO, this, String("check if we can jump through window"));
|
||||
|
||||
//UINT8 ubDirection = this->aiData.usActionData;
|
||||
UINT8 ubDirection = this->ubDirection;
|
||||
|
||||
INT32 sWindowGridNo = this->sGridNo;
|
||||
if ( ubDirection == NORTH || ubDirection == WEST )
|
||||
sWindowGridNo = NewGridNo(this->sGridNo, (UINT16)DirectionInc((UINT8)ubDirection));
|
||||
|
||||
DebugAI(AI_MSG_INFO, this, String("sWindowGridNo %d direction %d", sWindowGridNo, ubDirection));
|
||||
|
||||
if (//CheckWindow(this->sGridNo, ubDirection, gGameExternalOptions.fCanJumpThroughClosedWindows) &&
|
||||
IsJumpableWindowPresentAtGridNo(sWindowGridNo, ubDirection, gGameExternalOptions.fCanJumpThroughClosedWindows) &&
|
||||
//FindWindowJumpDirection(this, this->sGridNo, bDirection, &bDirection) &&
|
||||
this->pathing.bLevel == 0 &&
|
||||
(ubDirection == NORTH || ubDirection == EAST || ubDirection == SOUTH || ubDirection == WEST) )
|
||||
{
|
||||
// Flugente: if we are jumping through an intact window, smash it during our animation
|
||||
if ( gGameExternalOptions.fCanJumpThroughClosedWindows )
|
||||
{
|
||||
// is there really an intact window that we jump through?
|
||||
if ( IsJumpableWindowPresentAtGridNo(sWindowGridNo, ubDirection, TRUE) && !IsJumpableWindowPresentAtGridNo(sWindowGridNo, ubDirection, FALSE) )
|
||||
{
|
||||
STRUCTURE* pStructure = FindStructure(sWindowGridNo, STRUCTURE_WALLNWINDOW);
|
||||
if ( pStructure && !(pStructure->fFlags & STRUCTURE_OPEN) )
|
||||
{
|
||||
DebugAI(AI_MSG_INFO, this, String("jumping through closed window, damage soldier"));
|
||||
// intact window found. Smash it!
|
||||
WindowHit(sWindowGridNo, pStructure->usStructureID, (ubDirection == SOUTH || ubDirection == EAST), TRUE, PlayerCanHearNoise(this));
|
||||
|
||||
// we get a bit of damage for jumping through a window
|
||||
this->SoldierTakeDamage(0, 2 + 2 * Random(4), 0, 1000, TAKE_DAMAGE_ELECTRICITY, NOBODY, sWindowGridNo, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this->sTempNewGridNo = NewGridNo(this->sGridNo, (UINT16)DirectionInc(ubDirection));
|
||||
this->flags.fDontChargeTurningAPs = TRUE;
|
||||
EVENT_InternalSetSoldierDesiredDirection(this, ubDirection, FALSE, this->usAnimState);
|
||||
this->flags.fTurningUntilDone = TRUE;
|
||||
// ATE: Reset flag to go back to prone...
|
||||
|
||||
// Flugente: In case an animation is missing (civilian bodytypes), we TELEPORT instead
|
||||
if ( IsAnimationValidForBodyType(this, JUMPWINDOWS) == FALSE )
|
||||
{
|
||||
DebugAI(AI_MSG_INFO, this, String("teleport soldier to %d", this->sTempNewGridNo));
|
||||
|
||||
// sevenfm: deduct APs for jumping
|
||||
if ( UsingNewInventorySystem() && FindBackpackOnSoldier(this) != ITEM_NOT_FOUND )
|
||||
DeductPoints(this, GetAPsToJumpThroughWindows(this, TRUE), GetBPsToJumpThroughWindows(this, TRUE), SP_MOVEMENT_INTERRUPT);
|
||||
else
|
||||
DeductPoints(this, GetAPsToJumpThroughWindows(this, FALSE), GetBPsToJumpThroughWindows(this, FALSE), SP_MOVEMENT_INTERRUPT);
|
||||
|
||||
TeleportSoldier(this, this->sTempNewGridNo, TRUE);
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugAI(AI_MSG_INFO, this, String("start jumping"));
|
||||
this->flags.bTurningFromPronePosition = TURNING_FROM_PRONE_OFF;
|
||||
this->EVENT_InitNewSoldierAnim(JUMPWINDOWS, 0, FALSE);
|
||||
//this->usPendingAnimation = JUMPWINDOWS;
|
||||
//EndAIGuysTurn(this);
|
||||
}
|
||||
|
||||
// Flugente: should be fixed now, re-enable if not
|
||||
// Flugente: if an AI guy, end turn (weird endless clock syndrome)
|
||||
//if ( this->bTeam != OUR_TEAM )
|
||||
//EndAIGuysTurn( this);
|
||||
}
|
||||
else
|
||||
{
|
||||
ScreenMsg(FONT_LTRED, MSG_INTERFACE, L"[%d] %s cannot jump", this->ubID, this->GetName());
|
||||
DebugAI(AI_MSG_INFO, this, String("CancelAIAction: cannot jump"));
|
||||
CancelAIAction(this, TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
UINT32 SleepDartSuccumbChance( SOLDIERTYPE * pSoldier )
|
||||
{
|
||||
UINT32 uiChance;
|
||||
@@ -9853,7 +9932,7 @@ void SOLDIERTYPE::BeginSoldierGetup( void )
|
||||
else
|
||||
{
|
||||
this->bTurnsCollapsed++;
|
||||
if ( (gTacticalStatus.bBoxingState == BOXING) && (this->flags.uiStatusFlags & SOLDIER_BOXER) )
|
||||
if ( (gTacticalStatus.bBoxingState == BOXING) && (BOXER(this)) )
|
||||
{
|
||||
if ( this->bTurnsCollapsed > 1 )
|
||||
{
|
||||
@@ -19046,7 +19125,7 @@ BOOLEAN SOLDIERTYPE::IsJamming( )
|
||||
{
|
||||
if ( CanUseRadio( FALSE ) )
|
||||
return TRUE;
|
||||
// if we cannot use the radio, remove that flag hile we're at it
|
||||
// if we cannot use the radio, remove that flag while we're at it
|
||||
else
|
||||
usSoldierFlagMask &= ~SOLDIER_RADIO_OPERATOR_JAMMING;
|
||||
}
|
||||
@@ -26297,3 +26376,13 @@ void SOLDIERTYPE::InitializeExtraData(void)
|
||||
|
||||
this->delayedDamageFunction = nullptr;
|
||||
}
|
||||
|
||||
UINT8 SOLDIERTYPE::AnimHeight(void) const
|
||||
{
|
||||
return gAnimControl[this->usAnimState].ubHeight;
|
||||
}
|
||||
|
||||
UINT8 SOLDIERTYPE::AnimEndHeight(void) const
|
||||
{
|
||||
return gAnimControl[this->usAnimState].ubEndHeight;
|
||||
}
|
||||
|
||||
@@ -1788,10 +1788,13 @@ public:
|
||||
void ChangeToFallbackAnimation( UINT8 fallBackDirection );
|
||||
|
||||
// sevenfm
|
||||
void BeginSoldierJumpWindowAI(void);
|
||||
void BreakWindow(void);
|
||||
BOOLEAN CanBreakWindow(void);
|
||||
BOOLEAN CanStartDrag(void);
|
||||
void StartDrag(void);
|
||||
UINT8 AnimHeight(void) const;
|
||||
UINT8 AnimEndHeight(void) const;
|
||||
|
||||
void UpdateRobotControllerGivenController( void );
|
||||
void UpdateRobotControllerGivenRobot( void );
|
||||
|
||||
@@ -37,8 +37,10 @@
|
||||
#define TANK( p ) (p->ubBodyType == TANK_NE || p->ubBodyType == TANK_NW )
|
||||
#define ENEMYROBOT( p ) (p->ubBodyType == ROBOTNOWEAPON && p->bTeam == ENEMY_TEAM)
|
||||
#define ARMED_VEHICLE( p ) ( TANK( p ) || COMBAT_JEEP(p) )
|
||||
#define BOXER( p ) ( p->flags.uiStatusFlags & SOLDIER_BOXER )
|
||||
#define ISVIP( p ) ( p->usSoldierFlagMask & SOLDIER_VIP )
|
||||
|
||||
//#define OK_ENTERABLE_VEHICLE( p ) ( ( p->flags.uiStatusFlags & SOLDIER_VEHICLE ) && !TANK( p ) && p->stats.bLife >= OKLIFE )
|
||||
#define OK_ENTERABLE_VEHICLE( p ) ( ( p->flags.uiStatusFlags & SOLDIER_VEHICLE ) && (!ARMED_VEHICLE( p ) || !(p->flags.uiStatusFlags & SOLDIER_ENEMY) ) && p->stats.bLife >= OKLIFE )
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -1218,11 +1218,10 @@ void EndInterrupt( BOOLEAN fMarkInterruptOccurred )
|
||||
else
|
||||
{
|
||||
ubInterruptedSoldier = LATEST_INTERRUPT_GUY;
|
||||
|
||||
DebugMsg( TOPIC_JA2INTERRUPT, DBG_LEVEL_3, String("INTERRUPT: interrupt over, %d's team regains control", ubInterruptedSoldier ) );
|
||||
|
||||
pSoldier = ubInterruptedSoldier;
|
||||
|
||||
DebugMsg( TOPIC_JA2INTERRUPT, DBG_LEVEL_3, String("INTERRUPT: interrupt over, soldier %d's team %d regains control", ubInterruptedSoldier, pSoldier->bTeam ) );
|
||||
|
||||
for ( SoldierID id = 0; id < MAX_NUM_SOLDIERS; ++id)
|
||||
{
|
||||
pTempSoldier = id;
|
||||
|
||||
@@ -1684,6 +1684,24 @@ void ItemCreationCallBack( UINT8 ubResult )
|
||||
memset(gszMsgBoxInputString,0,sizeof(gszMsgBoxInputString));
|
||||
}
|
||||
|
||||
static void CycleThroughTileDebugInfo()
|
||||
{
|
||||
const STR16 modeStrings[] =
|
||||
{
|
||||
L"Debug draw mode: Pathfinding",
|
||||
L"Debug drawmode: Threat values",
|
||||
L"Debug drawmode: Cover values",
|
||||
L"Debug drawmode: Off",
|
||||
};
|
||||
|
||||
gRenderDebugInfoMode += 1;
|
||||
if (gRenderDebugInfoMode > DEBUG_OFF)
|
||||
{
|
||||
gRenderDebugInfoMode = 0;
|
||||
}
|
||||
ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, modeStrings[gRenderDebugInfoMode]);
|
||||
}
|
||||
|
||||
extern BOOLEAN gfDisableRegionActive;
|
||||
extern BOOLEAN gfUserTurnRegionActive;
|
||||
|
||||
@@ -4752,6 +4770,13 @@ void GetKeyboardInput( UINT32 *puiNewEvent )
|
||||
break;
|
||||
|
||||
case 'Z':
|
||||
if (fCtrl)
|
||||
{
|
||||
if (DEBUG_CHEAT_LEVEL())
|
||||
{
|
||||
CycleThroughTileDebugInfo();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
+13
-11
@@ -4257,10 +4257,10 @@ BOOLEAN UseHandToHand( SOLDIERTYPE *pSoldier, INT32 sTargetGridNo, BOOLEAN fStea
|
||||
|
||||
// sevenfm: bonus for boxers for attack from the back
|
||||
if (iHitChance < 100 &&
|
||||
(pSoldier->flags.uiStatusFlags & SOLDIER_BOXER) &&
|
||||
BOXER(pSoldier) &&
|
||||
!pSoldier->bBlindedCounter &&
|
||||
gAnimControl[pTargetSoldier->usAnimState].ubEndHeight > ANIM_PRONE &&
|
||||
(pTargetSoldier->flags.uiStatusFlags & SOLDIER_BOXER) &&
|
||||
BOXER(pTargetSoldier) &&
|
||||
pTargetSoldier->usSoldierFlagMask2 & SOLDIER_BACK_ATTACK)
|
||||
{
|
||||
iHitChance += (100 - iHitChance) / 2;
|
||||
@@ -4876,7 +4876,7 @@ BOOLEAN UseHandToHand( SOLDIERTYPE *pSoldier, INT32 sTargetGridNo, BOOLEAN fStea
|
||||
if (pTargetSoldier->bActionPoints > 0 &&
|
||||
gGameOptions.fNewTraitSystem &&
|
||||
gTacticalStatus.bBoxingState == BOXING &&
|
||||
(pTargetSoldier->flags.uiStatusFlags & SOLDIER_BOXER) &&
|
||||
BOXER(pTargetSoldier) &&
|
||||
Chance(ubCounterattackChance) &&
|
||||
IS_MERC_BODY_TYPE(pSoldier) &&
|
||||
IS_MERC_BODY_TYPE(pTargetSoldier) &&
|
||||
@@ -6089,7 +6089,7 @@ void StructureHit( INT32 iBullet, UINT16 usWeaponIndex, INT16 bWeaponStatus, Sol
|
||||
}
|
||||
}
|
||||
|
||||
void WindowHit( INT32 sGridNo, UINT16 usStructureID, BOOLEAN fBlowWindowSouth, BOOLEAN fLargeForce )
|
||||
void WindowHit( INT32 sGridNo, UINT16 usStructureID, BOOLEAN fBlowWindowSouth, BOOLEAN fLargeForce, BOOLEAN fSound)
|
||||
{
|
||||
STRUCTURE * pWallAndWindow;
|
||||
DB_STRUCTURE * pWallAndWindowInDB;
|
||||
@@ -6210,10 +6210,12 @@ void WindowHit( INT32 sGridNo, UINT16 usStructureID, BOOLEAN fBlowWindowSouth, B
|
||||
|
||||
pNode = CreateAnimationTile( &AniParams );
|
||||
//ddd window{
|
||||
CompileWorldMovementCosts();
|
||||
//ddd window}
|
||||
PlayJA2Sample( GLASS_SHATTER1 + Random(2), RATE_11025, MIDVOLUME, 1, SoundDir( sGridNo ) );
|
||||
|
||||
CompileWorldMovementCosts();
|
||||
//ddd window}
|
||||
if ( fSound )
|
||||
{
|
||||
PlayJA2Sample(GLASS_SHATTER1 + Random(2), RATE_11025, MIDVOLUME, 1, SoundDir(sGridNo));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9723,7 +9725,7 @@ UINT32 CalcChanceHTH( SOLDIERTYPE * pAttacker,SOLDIERTYPE *pDefender, INT16 ubAi
|
||||
{
|
||||
// Changed from DG by CJC to give higher chances of hitting with a stab or punch
|
||||
// sevenfm: lowered chance for boxers
|
||||
if (pAttacker->flags.uiStatusFlags & SOLDIER_BOXER)
|
||||
if (BOXER(pAttacker))
|
||||
iChance = 50 + (iAttRating - iDefRating) / 3;
|
||||
else
|
||||
iChance = 67 + (iAttRating - iDefRating) / 3;
|
||||
@@ -9778,8 +9780,8 @@ UINT32 CalcChanceHTH( SOLDIERTYPE * pAttacker,SOLDIERTYPE *pDefender, INT16 ubAi
|
||||
|
||||
// sevenfm: bonus for boxers for attacking from the back
|
||||
if (ubMode == HTH_MODE_PUNCH &&
|
||||
(pAttacker->flags.uiStatusFlags & SOLDIER_BOXER) &&
|
||||
(pDefender->flags.uiStatusFlags & SOLDIER_BOXER) &&
|
||||
BOXER(pAttacker) &&
|
||||
BOXER(pDefender) &&
|
||||
iChance < 100 &&
|
||||
!pAttacker->bBlindedCounter &&
|
||||
gAnimControl[pDefender->usAnimState].ubEndHeight > ANIM_PRONE &&
|
||||
|
||||
+1
-1
@@ -456,7 +456,7 @@ INT16 ArmourVersusFirePercent( SOLDIERTYPE * pSoldier );
|
||||
extern BOOLEAN FireWeapon( SOLDIERTYPE *pSoldier , INT32 sTargetGridNo );
|
||||
extern void WeaponHit( SoldierID usSoldierID, UINT16 usWeaponIndex, INT16 sDamage, INT16 sBreathLoss, UINT16 usDirection, INT16 sXPos, INT16 sYPos, INT16 sZPos, INT16 sRange, SoldierID ubAttackerID, BOOLEAN fHit, UINT8 ubSpecial, UINT8 ubHitLocation );
|
||||
extern void StructureHit( INT32 iBullet, UINT16 usWeaponIndex, INT16 bWeaponStatus, SoldierID ubAttackerID, UINT16 sXPos, INT16 sYPos, INT16 sZPos, UINT16 usStructureID, INT32 iImpact, BOOLEAN fStopped );
|
||||
extern void WindowHit( INT32 sGridNo, UINT16 usStructureID, BOOLEAN fBlowWindowSouth, BOOLEAN fLargeForce );
|
||||
void WindowHit(INT32 sGridNo, UINT16 usStructureID, BOOLEAN fBlowWindowSouth, BOOLEAN fLargeForce, BOOLEAN fSound = TRUE);
|
||||
// HEADROCK HAM 5.1: Moved to Bullets.h
|
||||
extern BOOLEAN InRange( SOLDIERTYPE *pSoldier, INT32 sGridNo );
|
||||
extern void ShotMiss( SoldierID ubAttackerID, INT32 iBullet );
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include "Overhead.h"
|
||||
#include "random.h"
|
||||
#include "Points.h"
|
||||
|
||||
#include "ai.h"
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
|
||||
@@ -190,7 +190,6 @@ typedef enum
|
||||
|
||||
INT16 AdvanceToFiringRange( SOLDIERTYPE * pSoldier, INT16 sClosestOpponent );
|
||||
|
||||
BOOLEAN AimingGun(SOLDIERTYPE *pSoldier);
|
||||
void CalcBestShot(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestShot);
|
||||
void CalcBestStab(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestStab, BOOLEAN fBladeAttack);
|
||||
void CalcBestThrow(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestThrow);
|
||||
@@ -223,7 +222,6 @@ INT8 ArmedVehicleDecideAction( SOLDIERTYPE* pSoldier );
|
||||
// a variant of ClosestSeenOpponent(...), that allows to find enemies on a roof
|
||||
INT32 ClosestSeenOpponentWithRoof(SOLDIERTYPE *pSoldier, INT32 * psGridNo, INT8 * pbLevel);
|
||||
|
||||
INT8 CrowDecideAction( SOLDIERTYPE * pSoldier );
|
||||
void DecideAlertStatus( SOLDIERTYPE *pSoldier );
|
||||
INT8 DecideAutoBandage( SOLDIERTYPE * pSoldier );
|
||||
UINT16 DetermineMovementMode( SOLDIERTYPE * pSoldier, INT8 bAction );
|
||||
@@ -241,9 +239,12 @@ INT32 GetInterveningClimbingLocation( SOLDIERTYPE * pSoldier, INT32 sDestGridNo,
|
||||
UINT8 GetTraversalQuoteActionID( INT8 bDirection );
|
||||
INT32 GoAsFarAsPossibleTowards(SOLDIERTYPE *pSoldier, INT32 sDesGrid, INT8 bAction);
|
||||
|
||||
INT8 HeadForTheStairCase( SOLDIERTYPE * pSoldier );
|
||||
ActionType HeadForTheStairCase( SOLDIERTYPE * pSoldier );
|
||||
|
||||
BOOLEAN InSmoke(INT32 sGridNo, INT8 bLevel);
|
||||
BOOLEAN InSmoke(SOLDIERTYPE* pSoldier, INT32 sGridNo);
|
||||
BOOLEAN InTearGas(SOLDIERTYPE* pSoldier, INT32 sGridNo);
|
||||
BOOLEAN InMustardGas(SOLDIERTYPE* pSoldier, INT32 sGridNo);
|
||||
BOOLEAN InGas( SOLDIERTYPE *pSoldier, INT32 sGridNo );
|
||||
BOOLEAN InGasOrSmoke( SOLDIERTYPE *pSoldier, INT32 sGridNo );
|
||||
BOOLEAN InWaterGasOrSmoke( SOLDIERTYPE *pSoldier, INT32 sGridNo );
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "opplist.h"
|
||||
#include "Interface.h"
|
||||
#include "Tactical Save.h"
|
||||
#include <Soldier macros.h>
|
||||
|
||||
#define AI_LIST_SIZE TOTAL_SOLDIERS
|
||||
|
||||
@@ -180,7 +181,7 @@ BOOLEAN InsertIntoAIList( SoldierID ubID, INT8 bPriority )
|
||||
|
||||
BOOLEAN SatisfiesAIListConditions( SOLDIERTYPE * pSoldier, UINT16 * pubDoneCount, BOOLEAN fDoRandomChecks )
|
||||
{
|
||||
if ( (gTacticalStatus.bBoxingState == BOXING) && !(pSoldier->flags.uiStatusFlags & SOLDIER_BOXER) )
|
||||
if ( (gTacticalStatus.bBoxingState == BOXING) && !BOXER(pSoldier) )
|
||||
{
|
||||
return( FALSE );
|
||||
}
|
||||
|
||||
+328
-107
@@ -49,6 +49,7 @@
|
||||
#include "Soldier Functions.h" // added by SANDRO
|
||||
#include "Text.h" // sevenfm
|
||||
#include "english.h" // sevenfm: for ESC key
|
||||
#include "Food.h"
|
||||
|
||||
#include "connect.h"
|
||||
// needed to use the modularized tactical AI:
|
||||
@@ -206,18 +207,96 @@ STR szAction[] = {
|
||||
"AI_ACTION_STOP_MEDIC"
|
||||
};
|
||||
|
||||
STR16 wszAction[] = {
|
||||
L"AI_ACTION_NONE",
|
||||
|
||||
L"AI_ACTION_RANDOM_PATROL",
|
||||
L"AI_ACTION_SEEK_FRIEND",
|
||||
L"AI_ACTION_SEEK_OPPONENT",
|
||||
L"AI_ACTION_TAKE_COVER",
|
||||
L"AI_ACTION_GET_CLOSER",
|
||||
|
||||
L"AI_ACTION_POINT_PATROL",
|
||||
L"AI_ACTION_LEAVE_WATER_GAS",
|
||||
L"AI_ACTION_SEEK_NOISE",
|
||||
L"AI_ACTION_ESCORTED_MOVE",
|
||||
L"AI_ACTION_RUN_AWAY",
|
||||
|
||||
L"AI_ACTION_KNIFE_MOVE",
|
||||
L"AI_ACTION_APPROACH_MERC",
|
||||
L"AI_ACTION_TRACK",
|
||||
L"AI_ACTION_EAT",
|
||||
L"AI_ACTION_PICKUP_ITEM",
|
||||
|
||||
L"AI_ACTION_SCHEDULE_MOVE",
|
||||
L"AI_ACTION_WALK",
|
||||
L"AI_ACTION_RUN",
|
||||
L"AI_ACTION_WITHDRAW",
|
||||
L"AI_ACTION_FLANK_LEFT",
|
||||
L"AI_ACTION_FLANK_RIGHT",
|
||||
L"AI_ACTION_MOVE_TO_CLIMB",
|
||||
|
||||
L"AI_ACTION_CHANGE_FACING",
|
||||
|
||||
L"AI_ACTION_CHANGE_STANCE",
|
||||
|
||||
L"AI_ACTION_YELLOW_ALERT",
|
||||
L"AI_ACTION_RED_ALERT",
|
||||
L"AI_ACTION_CREATURE_CALL",
|
||||
L"AI_ACTION_PULL_TRIGGER",
|
||||
|
||||
L"AI_ACTION_USE_DETONATOR",
|
||||
L"AI_ACTION_FIRE_GUN",
|
||||
L"AI_ACTION_TOSS_PROJECTILE",
|
||||
L"AI_ACTION_KNIFE_STAB",
|
||||
L"AI_ACTION_THROW_KNIFE",
|
||||
|
||||
L"AI_ACTION_GIVE_AID",
|
||||
L"AI_ACTION_WAIT",
|
||||
L"AI_ACTION_PENDING_ACTION",
|
||||
L"AI_ACTION_DROP_ITEM",
|
||||
L"AI_ACTION_COWER",
|
||||
|
||||
L"AI_ACTION_STOP_COWERING",
|
||||
L"AI_ACTION_OPEN_OR_CLOSE_DOOR",
|
||||
L"AI_ACTION_UNLOCK_DOOR",
|
||||
L"AI_ACTION_LOCK_DOOR",
|
||||
L"AI_ACTION_LOWER_GUN",
|
||||
|
||||
L"AI_ACTION_ABSOLUTELY_NONE",
|
||||
L"AI_ACTION_CLIMB_ROOF",
|
||||
L"AI_ACTION_END_TURN",
|
||||
L"AI_ACTION_END_COWER_AND_MOVE",
|
||||
L"AI_ACTION_TRAVERSE_DOWN",
|
||||
L"AI_ACTION_OFFER_SURRENDER",
|
||||
L"AI_ACTION_RAISE_GUN",
|
||||
L"AI_ACTION_STEAL_MOVE",
|
||||
|
||||
L"AI_ACTION_RELOAD_GUN",
|
||||
|
||||
L"AI_ACTION_JUMP_WINDOW",
|
||||
L"AI_ACTION_FREE_PRISONER",
|
||||
L"AI_ACTION_USE_SKILL",
|
||||
L"AI_ACTION_DOCTOR",
|
||||
L"AI_ACTION_DOCTOR_SELF",
|
||||
L"AI_ACTION_SELFDETONATE",
|
||||
L"AI_ACTION_STOP_MEDIC"
|
||||
};
|
||||
|
||||
// sevenfm
|
||||
UINT32 guiAIStartCounter = 0, guiAILastCounter = 0;
|
||||
//UINT8 gubAISelectedSoldier = NOBODY;
|
||||
BOOLEAN gfLogsEnabled = TRUE;
|
||||
bool gLogDecideActionRed = true;
|
||||
bool gLogDecideActionBlack = true;
|
||||
|
||||
void DebugAI( INT8 bMsgType, SOLDIERTYPE *pSoldier, STR szOutput, INT8 bAction )
|
||||
void DebugAI( INT8 bMsgType, SOLDIERTYPE *pSoldier, STR szOutput, bool doLog, INT8 bAction)
|
||||
{
|
||||
FILE* DebugFile;
|
||||
CHAR8 msg[1024];
|
||||
CHAR8 buf[1024];
|
||||
|
||||
if (!gfLogsEnabled || pSoldier == nullptr)
|
||||
if (!gfTurnBasedAI || !gfLogsEnabled || !doLog || pSoldier == nullptr)
|
||||
return;
|
||||
|
||||
memset(buf, 0, 1024 * sizeof(char));
|
||||
@@ -258,7 +337,7 @@ void DebugAI( INT8 bMsgType, SOLDIERTYPE *pSoldier, STR szOutput, INT8 bAction )
|
||||
strcat(msg, buf);
|
||||
}
|
||||
|
||||
if (bAction >= AI_ACTION_NONE && bAction <= AI_ACTION_LAST)
|
||||
if (bAction >= AI_ACTION_NONE && bAction < AI_ACTION_INVALID)
|
||||
{
|
||||
strcat(msg, " ");
|
||||
strcat(msg, szAction[bAction]);
|
||||
@@ -301,16 +380,19 @@ void DebugAI( INT8 bMsgType, SOLDIERTYPE *pSoldier, STR szOutput, INT8 bAction )
|
||||
}
|
||||
|
||||
// also log to individual file for selected soldier
|
||||
sprintf(buf, "Logs\\AI_Decisions [%d].txt", pSoldier->ubID.i);
|
||||
if ((DebugFile = fopen(buf, "a+t")) != NULL)
|
||||
if (pSoldier)
|
||||
{
|
||||
if (bMsgType == AI_MSG_START)
|
||||
sprintf(buf, "Logs\\AI_Decisions [%d].txt", pSoldier->ubID.i);
|
||||
if ((DebugFile = fopen(buf, "a+t")) != NULL)
|
||||
{
|
||||
if (bMsgType == AI_MSG_START)
|
||||
{
|
||||
fputs("\n", DebugFile);
|
||||
}
|
||||
fputs(msg, DebugFile);
|
||||
fputs("\n", DebugFile);
|
||||
fclose(DebugFile);
|
||||
}
|
||||
fputs(msg, DebugFile);
|
||||
fputs("\n", DebugFile);
|
||||
fclose(DebugFile);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -354,6 +436,27 @@ void DebugQuestInfo(STR szOutput)
|
||||
}
|
||||
}
|
||||
|
||||
static INT16 ShouldActionStayInProgress(SOLDIERTYPE* pSoldier)
|
||||
{
|
||||
// this here should never happen, but it seems to (turns sometimes hang!)
|
||||
if ((pSoldier->aiData.bAction == AI_ACTION_CHANGE_FACING) && (pSoldier->pathing.bDesiredDirection != pSoldier->aiData.usActionData))
|
||||
{
|
||||
// don't try to pay any more APs for this, it was paid for once already!
|
||||
pSoldier->pathing.bDesiredDirection = (INT8)pSoldier->aiData.usActionData; // turn to face direction in actionData
|
||||
return(TRUE);
|
||||
}
|
||||
else if ((pSoldier->aiData.bAction == AI_ACTION_CHANGE_FACING) && (pSoldier->pathing.bDesiredDirection == pSoldier->aiData.usActionData))
|
||||
{
|
||||
return(FALSE);
|
||||
}
|
||||
else if (pSoldier->aiData.bAction == AI_ACTION_END_TURN || pSoldier->aiData.bAction == AI_ACTION_NONE)
|
||||
{
|
||||
return(FALSE);
|
||||
}
|
||||
// needs more time to complete action
|
||||
return(TRUE);
|
||||
}
|
||||
|
||||
|
||||
BOOLEAN InitAI( void )
|
||||
{
|
||||
@@ -361,13 +464,6 @@ BOOLEAN InitAI( void )
|
||||
FILE * DebugFile;
|
||||
#endif
|
||||
|
||||
#ifdef _DEBUG
|
||||
if (gfDisplayCoverValues)
|
||||
{
|
||||
//memset( gsCoverValue, 0x7F, sizeof( INT16 ) * WORLD_MAX );
|
||||
}
|
||||
#endif
|
||||
|
||||
//If we are not loading a saved game ( if we are, this has already been called )
|
||||
if( !( gTacticalStatus.uiFlags & LOADING_SAVED_GAME ) )
|
||||
{
|
||||
@@ -399,10 +495,6 @@ BOOLEAN InitAI( void )
|
||||
return( TRUE );
|
||||
}
|
||||
|
||||
BOOLEAN AimingGun(SOLDIERTYPE *pSoldier)
|
||||
{
|
||||
return(FALSE);
|
||||
}
|
||||
|
||||
void HandleSoldierAI( SOLDIERTYPE *pSoldier ) // FIXME - this function is named inappropriately
|
||||
{
|
||||
@@ -645,6 +737,9 @@ void HandleSoldierAI( SOLDIERTYPE *pSoldier ) // FIXME - this function is named
|
||||
{
|
||||
// traversing offmap, ignore new situations
|
||||
}
|
||||
// FIXME: Disabled temporarily to prevent AI actions constantly being cancelled during normal turn based combat.
|
||||
// Need to find out when this conditional is actually needed.
|
||||
#if 0
|
||||
else if ( pSoldier->ubQuoteRecord == 0 && !gTacticalStatus.fAutoBandageMode )
|
||||
{
|
||||
// don't force, don't want escorted mercs reacting to new opponents, etc.
|
||||
@@ -658,6 +753,7 @@ void HandleSoldierAI( SOLDIERTYPE *pSoldier ) // FIXME - this function is named
|
||||
}
|
||||
DecideAlertStatus( pSoldier );
|
||||
}
|
||||
#endif
|
||||
else
|
||||
{
|
||||
if ( pSoldier->ubQuoteRecord )
|
||||
@@ -665,6 +761,15 @@ void HandleSoldierAI( SOLDIERTYPE *pSoldier ) // FIXME - this function is named
|
||||
// make sure we're not using combat AI
|
||||
pSoldier->aiData.bAlertStatus = STATUS_GREEN;
|
||||
}
|
||||
|
||||
// Prevent AI deadlocking in case enemy is performing an action and player gets an interrupt.
|
||||
// Without this, if player doesn't move any mercs, the AI soldier will wait until the deadlock is broken.
|
||||
// By canceling the AI action, the AI can then reconsider actions.
|
||||
//if (pSoldier->aiData.bAction == AI_ACTION_FIRE_GUN || pSoldier->aiData.bAction == AI_ACTION_KNIFE_MOVE || pSoldier->aiData.bAction == AI_ACTION_STEAL_MOVE || pSoldier->aiData.bAction == AI_ACTION_KNIFE_STAB)
|
||||
{
|
||||
DebugAI(AI_MSG_INFO, pSoldier, String("New Situation"));
|
||||
CancelAIAction(pSoldier, FALSE);
|
||||
}
|
||||
pSoldier->aiData.bNewSituation = WAS_NEW_SITUATION;
|
||||
}
|
||||
}
|
||||
@@ -674,7 +779,6 @@ void HandleSoldierAI( SOLDIERTYPE *pSoldier ) // FIXME - this function is named
|
||||
// might have been in 'was' state; no longer so...
|
||||
pSoldier->aiData.bNewSituation = NOT_NEW_SITUATION;
|
||||
}
|
||||
|
||||
#ifdef TESTAI
|
||||
DebugMsg( TOPIC_JA2AI, DBG_LEVEL_3,String( ".... HANDLING AI FOR %d",pSoldier->ubID));
|
||||
#endif
|
||||
@@ -682,42 +786,79 @@ void HandleSoldierAI( SOLDIERTYPE *pSoldier ) // FIXME - this function is named
|
||||
/*********
|
||||
Start of new overall AI system
|
||||
********/
|
||||
|
||||
if (gfTurnBasedAI)
|
||||
{
|
||||
time_t tCurrentTime = time(0);
|
||||
UINT32 uiShortDelay = 10;
|
||||
UINT32 uiDelay = (UINT32)gGameExternalOptions.gubDeadLockDelay;
|
||||
UINT32 uiTime = (UINT32)(tCurrentTime - gtTimeSinceMercAIStart);
|
||||
BOOLEAN fKeyPressed = _KeyDown(ESC);
|
||||
|
||||
if ((uiTime > uiDelay || uiTime > uiShortDelay && fKeyPressed) && !gfUIInDeadlock)
|
||||
//if ( ( GetJA2Clock() - gTacticalStatus.uiTimeSinceMercAIStart ) > ( (UINT32)gGameExternalOptions.gubDeadLockDelay * 1000 ) && !gfUIInDeadlock )
|
||||
#if 0
|
||||
{
|
||||
// ATE: Display message that deadlock occured...
|
||||
LiveMessage( "Breaking Deadlock" );
|
||||
// added by Flugente: static pointers, used to break out of an endless circles
|
||||
static SOLDIERTYPE* pLastDecisionSoldier = NULL;
|
||||
static INT16 lastdecisioncount = 0;
|
||||
|
||||
// simple solution to prevent an endless clock: remember the last soldier that decided an action. If its the same one, increase the counter.
|
||||
// if counter is high enough, end this guy's turn
|
||||
if (pSoldier == pLastDecisionSoldier)
|
||||
{
|
||||
// we will only end our turn this way if this function was called over 100 times with same soldier without ending a turn.
|
||||
// so many actions in a single turn smell of an endless clock.
|
||||
// If we end a turn normally, the counter will be set back to 0, so this wont be a problem if you have a single soldier left for multiple turns
|
||||
if (lastdecisioncount >= 600)
|
||||
{
|
||||
ScreenMsg(FONT_MCOLOR_LTRED, MSG_INTERFACE, L"Aborting AI deadlock for [%d] %s data %d", pSoldier->ubID, wszAction[pSoldier->aiData.bAction], pSoldier->aiData.usActionData);
|
||||
DebugAI(AI_MSG_INFO, pSoldier, String("Aborting AI deadlock for [%d] %s data %d", pSoldier->ubID, szAction[pSoldier->aiData.bAction], pSoldier->aiData.usActionData));
|
||||
DebugAI(AI_MSG_INFO, pSoldier, String("Last action was %s ", szAction[pSoldier->aiData.bLastAction]));
|
||||
|
||||
EndAIDeadlock();
|
||||
//EndAIGuysTurn(pSoldier);
|
||||
lastdecisioncount = 0;
|
||||
return;
|
||||
}
|
||||
else
|
||||
++lastdecisioncount;
|
||||
}
|
||||
else
|
||||
{
|
||||
pLastDecisionSoldier = pSoldier;
|
||||
lastdecisioncount = 0;
|
||||
}
|
||||
}
|
||||
#else
|
||||
{
|
||||
time_t tCurrentTime = time(0);
|
||||
UINT32 uiShortDelay = 10;
|
||||
UINT32 uiDelay = (UINT32)gGameExternalOptions.gubDeadLockDelay;
|
||||
UINT32 uiTime = (UINT32)(tCurrentTime - gtTimeSinceMercAIStart);
|
||||
BOOLEAN fKeyPressed = _KeyDown(ESC);
|
||||
|
||||
if ((uiTime > uiDelay || uiTime > uiShortDelay && fKeyPressed) && !gfUIInDeadlock)
|
||||
//if ( ( GetJA2Clock() - gTacticalStatus.uiTimeSinceMercAIStart ) > ( (UINT32)gGameExternalOptions.gubDeadLockDelay * 1000 ) && !gfUIInDeadlock )
|
||||
{
|
||||
// ATE: Display message that deadlock occured...
|
||||
LiveMessage( "Breaking Deadlock" );
|
||||
|
||||
ScreenMsg(FONT_MCOLOR_LTRED, MSG_INTERFACE, L"Aborting AI deadlock for [%d] %s data %d", pSoldier->ubID, wszAction[pSoldier->aiData.bAction], pSoldier->aiData.usActionData);
|
||||
DebugAI(AI_MSG_INFO, pSoldier, String("Aborting AI deadlock for [%d] %s data %d", pSoldier->ubID, szAction[pSoldier->aiData.bAction], pSoldier->aiData.usActionData));
|
||||
DebugAI(AI_MSG_INFO, pSoldier, String("Last action was %s ", szAction[pSoldier->aiData.bLastAction]));
|
||||
|
||||
ScreenMsg(FONT_MCOLOR_LTRED, MSG_INTERFACE, L"Aborting AI deadlock for [%d] %s %s data %d", pSoldier->ubID.i, pSoldier->GetName(), utf8_to_wstring(std::string(szAction[pSoldier->aiData.bAction])), pSoldier->aiData.usActionData);
|
||||
DebugAI(String("Aborting AI deadlock for [%d] %s data %d", pSoldier->ubID, szAction[pSoldier->aiData.bAction], pSoldier->aiData.usActionData));
|
||||
|
||||
#ifdef JA2TESTVERSION
|
||||
// display deadlock message
|
||||
gfUIInDeadlock = TRUE;
|
||||
DebugAI( String("DEADLOCK soldier %d action %s ABC %d", pSoldier->ubID.i, gzActionStr[pSoldier->aiData.bAction], gTacticalStatus.ubAttackBusyCount ) );
|
||||
// display deadlock message
|
||||
gfUIInDeadlock = TRUE;
|
||||
DebugAI( String("DEADLOCK soldier %d action %s ABC %d", pSoldier->ubID.i, gzActionStr[pSoldier->aiData.bAction], gTacticalStatus.ubAttackBusyCount ) );
|
||||
#else
|
||||
|
||||
// If we are in beta version, also report message!
|
||||
// If we are in beta version, also report message!
|
||||
#ifdef JA2BETAVERSION
|
||||
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_ERROR, L"Aborting AI deadlock for %d. Please sent DEBUG.TXT file and SAVE.", pSoldier->ubID.i );
|
||||
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_ERROR, L"Aborting AI deadlock for %d. Please sent DEBUG.TXT file and SAVE.", pSoldier->ubID.i );
|
||||
#endif
|
||||
// just abort
|
||||
EndAIDeadlock();
|
||||
if ( !(pSoldier->flags.uiStatusFlags & SOLDIER_UNDERAICONTROL) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
// just abort
|
||||
EndAIDeadlock();
|
||||
if ( !(pSoldier->flags.uiStatusFlags & SOLDIER_UNDERAICONTROL) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// We STILL do not want to issue new orders while an attack busy situation is going on. This can happen, for example,
|
||||
@@ -727,7 +868,7 @@ void HandleSoldierAI( SOLDIERTYPE *pSoldier ) // FIXME - this function is named
|
||||
return;
|
||||
}
|
||||
|
||||
if (pSoldier->aiData.bAction == AI_ACTION_NONE)
|
||||
if (!pSoldier->aiData.bActionInProgress)
|
||||
{
|
||||
// being handled so turn off muzzle flash
|
||||
if ( pSoldier->flags.fMuzzleFlash )
|
||||
@@ -863,6 +1004,12 @@ void HandleSoldierAI( SOLDIERTYPE *pSoldier ) // FIXME - this function is named
|
||||
{
|
||||
ActionDone(pSoldier);
|
||||
}
|
||||
|
||||
if (!ShouldActionStayInProgress(pSoldier))
|
||||
{
|
||||
DebugAI(AI_MSG_INFO, pSoldier, String("Action %s was stuck as being in progress. Canceling action", szAction[pSoldier->aiData.bAction]));
|
||||
ActionDone(pSoldier);
|
||||
}
|
||||
}
|
||||
/*********
|
||||
End of new overall AI system
|
||||
@@ -1327,7 +1474,7 @@ void FreeUpNPCFromRoofClimb(SOLDIERTYPE *pSoldier )
|
||||
void ActionDone(SOLDIERTYPE *pSoldier)
|
||||
{
|
||||
// if an action is currently selected
|
||||
if (pSoldier->aiData.bAction != AI_ACTION_NONE)
|
||||
//if (pSoldier->aiData.bAction != AI_ACTION_NONE)
|
||||
{
|
||||
if (pSoldier->flags.uiStatusFlags & SOLDIER_MONSTER)
|
||||
{
|
||||
@@ -1547,33 +1694,6 @@ INT16 ActionInProgress(SOLDIERTYPE *pSoldier)
|
||||
|
||||
void TurnBasedHandleNPCAI(SOLDIERTYPE *pSoldier)
|
||||
{
|
||||
// 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;
|
||||
|
||||
// simple solution to prevent an endless clock: remember the last soldier that decided an action. If its the same one, increase the counter.
|
||||
// if counter is high enough, end this guy's turn
|
||||
if ( pSoldier == pLastDecisionSoldier )
|
||||
{
|
||||
// we will only end our turn this way if this function was called over 100 times with same soldier without ending a turn.
|
||||
// so many actions in a single turn smell of an endless clock.
|
||||
// If we end a turn normally, the counter will be set back to 0, so this wont be a problem if you have a single soldier left for multiple turns
|
||||
if ( lastdecisioncount >= 100 )
|
||||
{
|
||||
// zombie is done doing harm...
|
||||
EndAIGuysTurn( pSoldier);
|
||||
lastdecisioncount = 0;
|
||||
return ;
|
||||
}
|
||||
else
|
||||
++lastdecisioncount;
|
||||
}
|
||||
else
|
||||
{
|
||||
pLastDecisionSoldier = pSoldier;
|
||||
lastdecisioncount = 0;
|
||||
}
|
||||
|
||||
// yikes, this shouldn't occur! we should be trying to finish our move!
|
||||
// pSoldier->flags.fNoAPToFinishMove = FALSE;
|
||||
|
||||
@@ -1606,6 +1726,8 @@ void TurnBasedHandleNPCAI(SOLDIERTYPE *pSoldier)
|
||||
#ifdef DEBUGBUSY
|
||||
AINumMessage("Busy with action, skipping guy#",pSoldier->ubID);
|
||||
#endif
|
||||
ScreenMsg(FONT_MCOLOR_LTRED, MSG_INTERFACE, L"Busy with action %s, skipping guy [%d]", wszAction[pSoldier->aiData.bAction], pSoldier->ubID);
|
||||
DebugAI(AI_MSG_INFO, pSoldier, String("Busy with action %s, skipping guy [%d]", wszAction[pSoldier->aiData.bAction], pSoldier->ubID));
|
||||
|
||||
// let it continue
|
||||
return;
|
||||
@@ -1731,43 +1853,42 @@ void TurnBasedHandleNPCAI(SOLDIERTYPE *pSoldier)
|
||||
{
|
||||
pSoldier->aiData.bAction = AI_ACTION_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
// if he chose to continue doing nothing
|
||||
if (pSoldier->aiData.bAction == AI_ACTION_NONE)
|
||||
{
|
||||
// if he chose to continue doing nothing
|
||||
if (pSoldier->aiData.bAction == AI_ACTION_NONE)
|
||||
{
|
||||
#ifdef RECORDNET
|
||||
fprintf(NetDebugFile,"\tMOVED BECOMING TRUE: Chose to do nothing, guynum %d\n",pSoldier->ubID);
|
||||
fprintf(NetDebugFile,"\tMOVED BECOMING TRUE: Chose to do nothing, guynum %d\n",pSoldier->ubID);
|
||||
#endif
|
||||
|
||||
DebugMsg (TOPIC_JA2AI,DBG_LEVEL_3,"NPC has no action assigned");
|
||||
NPCDoesNothing(pSoldier); // sets pSoldier->moved to TRUE
|
||||
return;
|
||||
}
|
||||
// to get here, we MUST have an action selected, but not in progress...
|
||||
// see if we can afford to do this action
|
||||
if (IsActionAffordable(pSoldier))
|
||||
{
|
||||
NPCDoesAct(pSoldier);
|
||||
DebugMsg (TOPIC_JA2AI,DBG_LEVEL_3,"NPC has no action assigned");
|
||||
NPCDoesNothing(pSoldier); // sets pSoldier->moved to TRUE
|
||||
return;
|
||||
}
|
||||
// to get here, we MUST have an action selected, but not in progress...
|
||||
// see if we can afford to do this action
|
||||
if (IsActionAffordable(pSoldier))
|
||||
{
|
||||
NPCDoesAct(pSoldier);
|
||||
|
||||
// perform the chosen action
|
||||
pSoldier->aiData.bActionInProgress = ExecuteAction(pSoldier); // if started, mark us as busy
|
||||
// perform the chosen action
|
||||
pSoldier->aiData.bActionInProgress = ExecuteAction(pSoldier); // if started, mark us as busy
|
||||
|
||||
if ( !pSoldier->aiData.bActionInProgress && !TileIsOutOfBounds(pSoldier->sAbsoluteFinalDestination))
|
||||
{
|
||||
// turn based... abort this guy's turn
|
||||
EndAIGuysTurn( pSoldier );
|
||||
lastdecisioncount = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
if ( !pSoldier->aiData.bActionInProgress && !TileIsOutOfBounds(pSoldier->sAbsoluteFinalDestination))
|
||||
{
|
||||
#ifdef DEBUGDECISIONS
|
||||
AINumMessage("HandleManAI - Not enough APs, skipping guy#",pSoldier->ubID);
|
||||
#endif
|
||||
HaltMoveForSoldierOutOfPoints( pSoldier);
|
||||
return;
|
||||
// turn based... abort this guy's turn
|
||||
EndAIGuysTurn( pSoldier );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef DEBUGDECISIONS
|
||||
AINumMessage("HandleManAI - Not enough APs, skipping guy#",pSoldier->ubID);
|
||||
#endif
|
||||
HaltMoveForSoldierOutOfPoints( pSoldier);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1799,6 +1920,7 @@ void RefreshAI(SOLDIERTYPE *pSoldier)
|
||||
if ((pSoldier->aiData.bAlertStatus == STATUS_BLACK) || (pSoldier->aiData.bAlertStatus == STATUS_RED))
|
||||
{
|
||||
// always freshly rethink things at start of his turn
|
||||
//CancelAIAction(pSoldier, FALSE);
|
||||
pSoldier->aiData.bNewSituation = IS_NEW_SITUATION;
|
||||
}
|
||||
else
|
||||
@@ -2281,14 +2403,24 @@ INT8 ExecuteAction(SOLDIERTYPE *pSoldier)
|
||||
usHandItem = GetAttachedGrenadeLauncher(&pSoldier->inv[HANDPOS]);
|
||||
|
||||
iRetCode = HandleItem( pSoldier, pSoldier->aiData.usActionData, pSoldier->bTargetLevel, usHandItem, FALSE );
|
||||
// If AI cannot shoot because of lack of APs, attempt to try again with lower aim.
|
||||
// Usually happens when they have to turn before shooting. Without this, the game would cancel soldier's whole turn
|
||||
if (iRetCode == ITEM_HANDLE_NOAPS && pSoldier->aiData.bAimTime > 0)
|
||||
{
|
||||
do
|
||||
{
|
||||
pSoldier->aiData.bAimTime -= 1;
|
||||
iRetCode = HandleItem(pSoldier, pSoldier->aiData.usActionData, pSoldier->bTargetLevel, usHandItem, FALSE);
|
||||
} while (iRetCode == ITEM_HANDLE_NOAPS && pSoldier->aiData.bAimTime > 0);
|
||||
}
|
||||
|
||||
if ( iRetCode != ITEM_HANDLE_OK)
|
||||
{
|
||||
if ( iRetCode != ITEM_HANDLE_BROKEN ) // if the item broke, this is 'legal' and doesn't need reporting
|
||||
{
|
||||
DebugAI( String( "AI %d got error code %ld from HandleItem, doing action %d, has %d APs... aborting deadlock!", pSoldier->ubID, iRetCode, pSoldier->aiData.bAction, pSoldier->bActionPoints ) );
|
||||
DebugAI(AI_MSG_INFO, pSoldier, String( "AI %d got error code %ld from HandleItem, doing action %d, has %d APs... aborting deadlock!", pSoldier->ubID, iRetCode, pSoldier->aiData.bAction, pSoldier->bActionPoints ) );
|
||||
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_BETAVERSION, L"AI %d got error code %ld from HandleItem, doing action %d... aborting deadlock!", pSoldier->ubID, iRetCode, pSoldier->aiData.bAction );
|
||||
}
|
||||
DebugAI(AI_MSG_INFO, pSoldier, String("CancelAIAction: !ITEM_HANDLE_OK"));
|
||||
CancelAIAction( pSoldier, FORCE);
|
||||
#ifdef TESTAICONTROL
|
||||
if (gfTurnBasedAI)
|
||||
@@ -2612,7 +2744,30 @@ INT8 ExecuteAction(SOLDIERTYPE *pSoldier)
|
||||
|
||||
case AI_ACTION_JUMP_WINDOW:
|
||||
{
|
||||
pSoldier->BeginSoldierClimbWindow();
|
||||
pSoldier->BeginSoldierJumpWindowAI();
|
||||
if ( gfTurnBasedAI )
|
||||
{
|
||||
//if (pSoldier->bActionPoints >= GetAPsToJumpThroughWindows(pSoldier, FALSE) + GetAPsToLook(pSoldier))
|
||||
if ( pSoldier->bActionPoints >= GetAPsToLook(pSoldier) )
|
||||
{
|
||||
pSoldier->aiData.bNextAction = AI_ACTION_CHANGE_FACING;
|
||||
INT32 sClosestOpponent = ClosestKnownOpponent(pSoldier, NULL, NULL);
|
||||
if ( !TileIsOutOfBounds(sClosestOpponent) )
|
||||
pSoldier->aiData.usNextActionData = AIDirection(pSoldier->sGridNo, sClosestOpponent);
|
||||
else
|
||||
pSoldier->aiData.usNextActionData = PreRandom(8);
|
||||
}
|
||||
else
|
||||
{
|
||||
pSoldier->aiData.bNextAction = AI_ACTION_END_TURN;
|
||||
pSoldier->aiData.usNextActionData = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pSoldier->aiData.bNextAction = AI_ACTION_WAIT;
|
||||
pSoldier->aiData.usNextActionData = REALTIME_AI_DELAY / 10;
|
||||
}
|
||||
ActionDone( pSoldier );
|
||||
}
|
||||
break;
|
||||
@@ -2675,7 +2830,73 @@ INT8 ExecuteAction(SOLDIERTYPE *pSoldier)
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
case AI_ACTION_DRINK_CANTEEN:
|
||||
DrinkFromInventory(pSoldier);
|
||||
ActionDone(pSoldier);
|
||||
break;
|
||||
|
||||
case AI_ACTION_HANDLE_ITEM:
|
||||
iRetCode = HandleItem(pSoldier, pSoldier->aiData.usActionData, pSoldier->pathing.bLevel, pSoldier->inv[HANDPOS].usItem, FALSE);
|
||||
if ( iRetCode != ITEM_HANDLE_OK )
|
||||
{
|
||||
DebugAI(AI_MSG_INFO, pSoldier, String("CancelAIAction (AI_ACTION_HANDLE_ITEM): HandleItem error code %d", iRetCode));
|
||||
CancelAIAction(pSoldier, FORCE);
|
||||
EndAIGuysTurn(pSoldier);
|
||||
}
|
||||
break;
|
||||
|
||||
case AI_ACTION_PLANT_BOMB:
|
||||
if ( pSoldier->inv[HANDPOS].exists() &&
|
||||
(Item[pSoldier->inv[HANDPOS].usItem].usItemClass & IC_BOMB) )
|
||||
{
|
||||
OBJECTTYPE bombobj;
|
||||
INT32 sSpot = pSoldier->sGridNo;
|
||||
|
||||
if ( !TileIsOutOfBounds(sSpot) &&
|
||||
pSoldier->inv[HANDPOS].MoveThisObjectTo(bombobj, 1) == 0 )
|
||||
{
|
||||
bombobj.fFlags |= OBJECT_ARMED_BOMB;
|
||||
bombobj[0]->data.misc.bDetonatorType = BOMB_TIMED;
|
||||
bombobj[0]->data.misc.usBombItem = bombobj.usItem;
|
||||
//bombobj[0]->data.misc.ubBombOwner = pSoldier->ubID + 2;
|
||||
bombobj[0]->data.misc.ubBombOwner = 1;
|
||||
bombobj[0]->data.misc.bDelay = 1 + Random(2);
|
||||
//pSoldier->inv[HANDPOS][0]->data.bTrap = EffectiveExplosive(pSoldier) / 20 + EffectiveExpLevel(pSoldier, TRUE) / 2;
|
||||
pSoldier->inv[HANDPOS][0]->data.bTrap = 6 + SoldierDifficultyLevel(pSoldier);
|
||||
AddItemToPool(sSpot, &bombobj, INVISIBLE, pSoldier->pathing.bLevel, WORLD_ITEM_ARMED_BOMB, 0);
|
||||
NotifySoldiersToLookforItems();
|
||||
DeductPoints(pSoldier, APBPConstants[AP_INVENTORY_ARM] + APBPConstants[AP_DROP_BOMB], APBPConstants[BP_INVENTORY_ARM] + APBPConstants[BP_DROP_BOMB]);
|
||||
if ( gAnimControl[pSoldier->usAnimState].ubHeight == ANIM_STAND )
|
||||
{
|
||||
pSoldier->EVENT_InitNewSoldierAnim(DROP_ITEM, 0, FALSE);
|
||||
}
|
||||
else if ( gAnimControl[pSoldier->usAnimState].ubHeight == ANIM_CROUCH )
|
||||
{
|
||||
pSoldier->EVENT_InitNewSoldierAnim(CUTTING_FENCE, 0, FALSE);
|
||||
}
|
||||
if ( pSoldier->bVisible != -1 )
|
||||
{
|
||||
PlayJA2Sample(THROW_IMPACT_2, RATE_11025, SoundVolume(MIDVOLUME, pSoldier->sGridNo), 1, SoundDir(pSoldier->sGridNo));
|
||||
}
|
||||
ActionDone(pSoldier);
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugAI(AI_MSG_INFO, pSoldier, String("CancelAIAction (AI_ACTION_PLANT_BOMB): failed to move object"));
|
||||
CancelAIAction(pSoldier, FORCE);
|
||||
EndAIGuysTurn(pSoldier);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugAI(AI_MSG_INFO, pSoldier, String("CancelAIAction (AI_ACTION_PLANT_BOMB): failed to find bomb in hand"));
|
||||
CancelAIAction(pSoldier, FORCE);
|
||||
EndAIGuysTurn(pSoldier);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
#ifdef BETAVERSION
|
||||
NumMessage("ExecuteAction - Illegal action type = ",pSoldier->aiData.bAction);
|
||||
#endif
|
||||
|
||||
+353
-24
@@ -27,6 +27,7 @@
|
||||
#include "SmokeEffects.h" // sevenfm
|
||||
|
||||
#include "GameInitOptionsScreen.h"
|
||||
#include "Structure Wrap.h"
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// SANDRO - In this file, all APBPConstants[AP_CROUCH] and APBPConstants[AP_PRONE] were changed to GetAPsCrouch() and GetAPsProne()
|
||||
@@ -134,18 +135,17 @@ INT8 OKToAttack(SOLDIERTYPE * pSoldier, int target)
|
||||
|
||||
BOOLEAN ConsiderProne( SOLDIERTYPE * pSoldier )
|
||||
{
|
||||
INT32 sOpponentGridNo;
|
||||
INT32 sOpponentGridNo;
|
||||
INT8 bOpponentLevel;
|
||||
INT32 iRange;
|
||||
INT32 iRangeInCellCoords;
|
||||
|
||||
if (pSoldier->aiData.bAIMorale >= MORALE_NORMAL)
|
||||
{
|
||||
return( FALSE );
|
||||
}
|
||||
// We don't want to go prone if there is a nearby enemy
|
||||
ClosestKnownOpponent( pSoldier, &sOpponentGridNo, &bOpponentLevel );
|
||||
iRange = PythSpacesAway( pSoldier->sGridNo, sOpponentGridNo );
|
||||
if (iRange > 10)
|
||||
ClosestKnownOpponent( pSoldier, &sOpponentGridNo, &bOpponentLevel, nullptr, &iRangeInCellCoords);
|
||||
if ( iRangeInCellCoords > 10*CELL_X_SIZE)
|
||||
{
|
||||
return( TRUE );
|
||||
}
|
||||
@@ -397,7 +397,11 @@ UINT16 DetermineMovementMode( SOLDIERTYPE * pSoldier, INT8 bAction )
|
||||
// sevenfm: movement mode tweaks
|
||||
if (gGameExternalOptions.fAIMovementMode)
|
||||
{
|
||||
INT32 sClosestThreat = ClosestKnownOpponent(pSoldier, NULL, NULL);
|
||||
INT32 distanceToThreat;
|
||||
const INT32 sClosestThreat = ClosestKnownOpponent(pSoldier, NULL, NULL, NULL, &distanceToThreat);
|
||||
const auto mediumRange = TACTICAL_RANGE_CELL_COORDS / 2;
|
||||
const auto close = TACTICAL_RANGE_CELL_COORDS / 4;
|
||||
const auto reallyClose = TACTICAL_RANGE_CELL_COORDS / 8;
|
||||
|
||||
// use walking mode if no enemy known
|
||||
if (pSoldier->aiData.bAlertStatus < STATUS_RED &&
|
||||
@@ -418,11 +422,12 @@ UINT16 DetermineMovementMode( SOLDIERTYPE * pSoldier, INT8 bAction )
|
||||
if (IS_MERC_BODY_TYPE(pSoldier) &&
|
||||
pSoldier->aiData.bAlertStatus >= STATUS_YELLOW &&
|
||||
!InWaterGasOrSmoke(pSoldier, pSoldier->sGridNo) &&
|
||||
!(pSoldier->flags.uiStatusFlags & SOLDIER_BOXER) &&
|
||||
!BOXER(pSoldier) &&
|
||||
!TileIsOutOfBounds(sClosestThreat) &&
|
||||
(pSoldier->bTeam == ENEMY_TEAM || pSoldier->bTeam == MILITIA_TEAM))
|
||||
{
|
||||
INT16 sDistanceVisible = VISION_RANGE;
|
||||
const auto beyondVisionRange = (CELL_X_SIZE * 3 * sDistanceVisible) / 2;
|
||||
INT32 iRCD = RangeChangeDesire(pSoldier);
|
||||
|
||||
// use running when in light at night
|
||||
@@ -445,7 +450,7 @@ UINT16 DetermineMovementMode( SOLDIERTYPE * pSoldier, INT8 bAction )
|
||||
!GuySawEnemy(pSoldier) &&
|
||||
(NightTime() || gAnimControl[pSoldier->usAnimState].ubEndHeight <= ANIM_CROUCH) &&
|
||||
CountNearbyFriends(pSoldier, pSoldier->sGridNo, TACTICAL_RANGE / 4) < 3 &&
|
||||
PythSpacesAway(pSoldier->sGridNo, sClosestThreat) < 3 * sDistanceVisible / 2 &&
|
||||
distanceToThreat < beyondVisionRange &&
|
||||
CountFriendsBlack(pSoldier) == 0 &&
|
||||
bAction == AI_ACTION_SEEK_OPPONENT)
|
||||
{
|
||||
@@ -454,7 +459,7 @@ UINT16 DetermineMovementMode( SOLDIERTYPE * pSoldier, INT8 bAction )
|
||||
|
||||
// use swatting for taking cover
|
||||
if (pSoldier->aiData.bAlertStatus >= STATUS_RED &&
|
||||
PythSpacesAway(pSoldier->sGridNo, sClosestThreat) > (INT16)TACTICAL_RANGE / 8 &&
|
||||
distanceToThreat > reallyClose &&
|
||||
(pSoldier->aiData.bUnderFire && iRCD < 4 ||
|
||||
pSoldier->aiData.bShock > 2 * iRCD ||
|
||||
pSoldier->aiData.bShock > 0 && gAnimControl[pSoldier->usAnimState].ubEndHeight == ANIM_PRONE) &&
|
||||
@@ -466,9 +471,9 @@ UINT16 DetermineMovementMode( SOLDIERTYPE * pSoldier, INT8 bAction )
|
||||
|
||||
// use SWATTING when under fire
|
||||
if (pSoldier->aiData.bAlertStatus >= STATUS_RED &&
|
||||
(pSoldier->aiData.bShock > iRCD && PythSpacesAway(pSoldier->sGridNo, sClosestThreat) > (INT16)TACTICAL_RANGE / 2 ||
|
||||
pSoldier->aiData.bShock > 0 && gAnimControl[pSoldier->usAnimState].ubEndHeight == ANIM_PRONE && PythSpacesAway(pSoldier->sGridNo, sClosestThreat) > (INT16)TACTICAL_RANGE / 4) &&
|
||||
PythSpacesAway(pSoldier->sGridNo, sClosestThreat) < 3 * sDistanceVisible / 2 &&
|
||||
(pSoldier->aiData.bShock > iRCD && distanceToThreat > mediumRange ||
|
||||
pSoldier->aiData.bShock > 0 && gAnimControl[pSoldier->usAnimState].ubEndHeight == ANIM_PRONE && distanceToThreat > close) &&
|
||||
distanceToThreat < beyondVisionRange &&
|
||||
gAnimControl[pSoldier->usAnimState].ubEndHeight <= ANIM_CROUCH &&
|
||||
!pSoldier->aiData.bLastAttackHit &&
|
||||
(bAction == AI_ACTION_SEEK_OPPONENT ||
|
||||
@@ -485,7 +490,7 @@ UINT16 DetermineMovementMode( SOLDIERTYPE * pSoldier, INT8 bAction )
|
||||
(pSoldier->aiData.bOrders == SNIPER ||
|
||||
pSoldier->aiData.bOrders == STATIONARY ||
|
||||
(GuySawEnemy(pSoldier) || pSoldier->aiData.bShock > 0) && iRCD < 4) &&
|
||||
PythSpacesAway(pSoldier->sGridNo, sClosestThreat) > (INT16)TACTICAL_RANGE / 4 &&
|
||||
distanceToThreat > close &&
|
||||
(bAction == AI_ACTION_SEEK_OPPONENT ||
|
||||
bAction == AI_ACTION_GET_CLOSER ||
|
||||
bAction == AI_ACTION_SEEK_FRIEND ||
|
||||
@@ -501,7 +506,7 @@ UINT16 DetermineMovementMode( SOLDIERTYPE * pSoldier, INT8 bAction )
|
||||
(pSoldier->aiData.bOrders == SNIPER ||
|
||||
pSoldier->aiData.bOrders == STATIONARY ||
|
||||
pSoldier->aiData.bShock > 0 && iRCD < 4) &&
|
||||
PythSpacesAway(pSoldier->sGridNo, sClosestThreat) > (INT16)TACTICAL_RANGE / 4 &&
|
||||
distanceToThreat > close &&
|
||||
(bAction == AI_ACTION_SEEK_OPPONENT ||
|
||||
bAction == AI_ACTION_GET_CLOSER ||
|
||||
bAction == AI_ACTION_SEEK_FRIEND ||
|
||||
@@ -515,7 +520,7 @@ UINT16 DetermineMovementMode( SOLDIERTYPE * pSoldier, INT8 bAction )
|
||||
if (!pSoldier->aiData.bUnderFire &&
|
||||
bAction == AI_ACTION_TAKE_COVER &&
|
||||
pSoldier->bInitialActionPoints > APBPConstants[AP_MINIMUM] &&
|
||||
(!InARoom(pSoldier->sGridNo, NULL) || PythSpacesAway(pSoldier->sGridNo, sClosestThreat) > sDistanceVisible * 2) &&
|
||||
(!InARoom(pSoldier->sGridNo, NULL) || distanceToThreat > sDistanceVisible * 20) &&
|
||||
pSoldier->aiData.bAIMorale >= MORALE_NORMAL &&
|
||||
pSoldier->bBreath > 25 &&
|
||||
pSoldier->pathing.bLevel == 0 &&
|
||||
@@ -539,14 +544,14 @@ UINT16 DetermineMovementMode( SOLDIERTYPE * pSoldier, INT8 bAction )
|
||||
else if (gAnimControl[pSoldier->usAnimState].ubEndHeight == ANIM_CROUCH)
|
||||
{
|
||||
if (WeaponReady(pSoldier) && !pSoldier->aiData.bUnderFire && pSoldier->aiData.bAlertStatus == STATUS_BLACK ||
|
||||
pSoldier->aiData.bUnderFire && PythSpacesAway(pSoldier->sGridNo, sClosestThreat) > (INT16)TACTICAL_RANGE / 8)
|
||||
pSoldier->aiData.bUnderFire && distanceToThreat > reallyClose )
|
||||
return SWATTING;
|
||||
else
|
||||
return RUNNING;
|
||||
}
|
||||
else if (gAnimControl[pSoldier->usAnimState].ubEndHeight == ANIM_PRONE)
|
||||
{
|
||||
if (pSoldier->aiData.bUnderFire && !pSoldier->aiData.bLastAttackHit && PythSpacesAway(pSoldier->sGridNo, sClosestThreat) > (INT16)TACTICAL_RANGE / 8)
|
||||
if (pSoldier->aiData.bUnderFire && !pSoldier->aiData.bLastAttackHit && distanceToThreat > reallyClose )
|
||||
return SWATTING;
|
||||
else
|
||||
return RUNNING;
|
||||
@@ -808,6 +813,18 @@ BOOLEAN IsActionAffordable(SOLDIERTYPE *pSoldier, INT8 bAction)
|
||||
bMinPointsNeeded = 20; // TODO
|
||||
break;
|
||||
|
||||
case AI_ACTION_DRINK_CANTEEN:
|
||||
bMinPointsNeeded = APBPConstants[AP_DRINK];
|
||||
break;
|
||||
|
||||
case AI_ACTION_HANDLE_ITEM:
|
||||
bMinPointsNeeded = 0;
|
||||
break;
|
||||
|
||||
case AI_ACTION_PLANT_BOMB:
|
||||
bMinPointsNeeded = APBPConstants[AP_INVENTORY_ARM] + APBPConstants[AP_DROP_BOMB];
|
||||
break;
|
||||
|
||||
default:
|
||||
#ifdef BETAVERSION
|
||||
//NumMessage("AffordableAction - Illegal action type = ",pSoldier->aiData.bAction);
|
||||
@@ -1379,7 +1396,7 @@ INT32 ClosestReachableDisturbance(SOLDIERTYPE *pSoldier, BOOLEAN * pfChangeLevel
|
||||
}
|
||||
|
||||
|
||||
INT32 ClosestKnownOpponent(SOLDIERTYPE *pSoldier, INT32 * psGridNo, INT8 * pbLevel, SoldierID * pubOpponentID)
|
||||
INT32 ClosestKnownOpponent(SOLDIERTYPE *pSoldier, INT32 * psGridNo, INT8 * pbLevel, SoldierID * pubOpponentID, INT32 * distanceInCellCoords)
|
||||
{
|
||||
INT32 *psLastLoc,sGridNo, sClosestOpponent = NOWHERE;
|
||||
UINT32 uiLoop;
|
||||
@@ -1475,7 +1492,7 @@ INT32 ClosestKnownOpponent(SOLDIERTYPE *pSoldier, INT32 * psGridNo, INT8 * pbLev
|
||||
|
||||
if (sClosestOpponent == NOWHERE ||
|
||||
iRange < iClosestRange ||
|
||||
pClosestOpponent && !pClosestOpponent->IsZombie() && !(pSoldier->flags.uiStatusFlags & SOLDIER_BOXER) && pClosestOpponent->stats.bLife < OKLIFE && pOpponent->stats.bLife >= OKLIFE)
|
||||
pClosestOpponent && !pClosestOpponent->IsZombie() && !BOXER(pSoldier) && pClosestOpponent->stats.bLife < OKLIFE && pOpponent->stats.bLife >= OKLIFE)
|
||||
{
|
||||
iClosestRange = iRange;
|
||||
sClosestOpponent = sGridNo;
|
||||
@@ -1503,6 +1520,10 @@ INT32 ClosestKnownOpponent(SOLDIERTYPE *pSoldier, INT32 * psGridNo, INT8 * pbLev
|
||||
{
|
||||
*pubOpponentID = pClosestOpponent->ubID;
|
||||
}
|
||||
if ( distanceInCellCoords )
|
||||
{
|
||||
*distanceInCellCoords = iClosestRange;
|
||||
}
|
||||
return( sClosestOpponent );
|
||||
}
|
||||
|
||||
@@ -2232,6 +2253,31 @@ INT16 DistanceToClosestFriend( SOLDIERTYPE * pSoldier )
|
||||
return( sMinDist );
|
||||
}
|
||||
|
||||
BOOLEAN InSmoke(SOLDIERTYPE* pSoldier, INT32 sGridNo)
|
||||
{
|
||||
if ( gpWorldLevelData[sGridNo].ubExtFlags[pSoldier->pathing.bLevel] & (MAPELEMENT_EXT_SMOKE | MAPELEMENT_EXT_SIGNAL_SMOKE | MAPELEMENT_EXT_DEBRIS_SMOKE | MAPELEMENT_EXT_FIRERETARDANT_SMOKE) )
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOLEAN InTearGas(SOLDIERTYPE* pSoldier, INT32 sGridNo)
|
||||
{
|
||||
if ( (gpWorldLevelData[sGridNo].ubExtFlags[pSoldier->pathing.bLevel] & MAPELEMENT_EXT_TEARGAS))
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOLEAN InMustardGas(SOLDIERTYPE* pSoldier, INT32 sGridNo)
|
||||
{
|
||||
if ( gpWorldLevelData[sGridNo].ubExtFlags[pSoldier->pathing.bLevel] & (MAPELEMENT_EXT_BURNABLEGAS | MAPELEMENT_EXT_CREATUREGAS | MAPELEMENT_EXT_MUSTARDGAS) )
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
BOOLEAN InWaterGasOrSmoke( SOLDIERTYPE *pSoldier, INT32 sGridNo )
|
||||
{
|
||||
if (WaterTooDeepForAttacks( sGridNo, pSoldier->pathing.bLevel ))
|
||||
@@ -2250,6 +2296,12 @@ BOOLEAN InWaterGasOrSmoke( SOLDIERTYPE *pSoldier, INT32 sGridNo )
|
||||
|
||||
BOOLEAN InGasOrSmoke( SOLDIERTYPE *pSoldier, INT32 sGridNo )
|
||||
{
|
||||
// Armed vehicles and robots do not care about gas or smoke
|
||||
if (ARMED_VEHICLE(pSoldier) || ENEMYROBOT(pSoldier))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// smoke
|
||||
if ( gpWorldLevelData[sGridNo].ubExtFlags[pSoldier->pathing.bLevel] & (MAPELEMENT_EXT_SMOKE | MAPELEMENT_EXT_SIGNAL_SMOKE | MAPELEMENT_EXT_DEBRIS_SMOKE | MAPELEMENT_EXT_FIRERETARDANT_SMOKE ) )
|
||||
return TRUE;
|
||||
@@ -2297,6 +2349,12 @@ BOOLEAN InGas(SOLDIERTYPE *pSoldier, INT32 sGridNo)
|
||||
if (TileIsOutOfBounds(sGridNo))
|
||||
return FALSE;
|
||||
|
||||
// Armed vehicles and robots do not care about gas or smoke
|
||||
if (ARMED_VEHICLE(pSoldier) || ENEMYROBOT(pSoldier))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (InGasSpot(pSoldier, sGridNo, pSoldier->pathing.bLevel))
|
||||
{
|
||||
return TRUE;
|
||||
@@ -2655,7 +2713,7 @@ INT32 CalcManThreatValue( SOLDIERTYPE *pEnemy, INT32 sMyGrid, UINT8 ubReduceForC
|
||||
}
|
||||
|
||||
// in boxing mode, let only a boxer be considered a threat.
|
||||
if ( (gTacticalStatus.bBoxingState == BOXING) && !(pEnemy->flags.uiStatusFlags & SOLDIER_BOXER) )
|
||||
if ( (gTacticalStatus.bBoxingState == BOXING) && !BOXER(pEnemy) )
|
||||
{
|
||||
iThreatValue = -999;
|
||||
return( iThreatValue );
|
||||
@@ -3896,8 +3954,6 @@ INT8 CalcMoraleNew(SOLDIERTYPE *pSoldier)
|
||||
bMoraleCategory ++;
|
||||
}
|
||||
|
||||
INT32 sClosestOpponent = ClosestKnownOpponent(pSoldier, NULL, NULL);
|
||||
|
||||
// if last attack of this soldier hit enemy - increase morale
|
||||
if( pSoldier->aiData.bLastAttackHit )
|
||||
{
|
||||
@@ -4576,6 +4632,61 @@ UINT8 RedSmokeDanger(INT32 sGridNo, INT8 bLevel)
|
||||
return ubDangerPercent;
|
||||
}
|
||||
|
||||
BOOLEAN FindClosestVisibleSmoke(SOLDIERTYPE* pSoldier, INT32& sSpot, INT8& bLevel, BOOLEAN fOnlyGas)
|
||||
{
|
||||
CHECKF(pSoldier);
|
||||
|
||||
INT32 sDist;
|
||||
INT32 sClosestDist = INT32_MAX;
|
||||
INT32 sCheckSpot;
|
||||
INT8 bCheckLevel;
|
||||
|
||||
sSpot = NOWHERE;
|
||||
bLevel = 0;
|
||||
|
||||
//loop through all smoke effects and find closest visible
|
||||
for ( UINT32 uiCnt = 0; uiCnt < guiNumSmokeEffects; uiCnt++ )
|
||||
{
|
||||
if ( gSmokeEffectData[uiCnt].fAllocated &&
|
||||
!TileIsOutOfBounds(gSmokeEffectData[uiCnt].sGridNo) )
|
||||
{
|
||||
// ignore smoke if not dangerous
|
||||
if ( fOnlyGas &&
|
||||
gSmokeEffectData[uiCnt].bType != TEARGAS_SMOKE_EFFECT &&
|
||||
gSmokeEffectData[uiCnt].bType != MUSTARDGAS_SMOKE_EFFECT &&
|
||||
gSmokeEffectData[uiCnt].bType != CREATURE_SMOKE_EFFECT )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
sCheckSpot = gSmokeEffectData[uiCnt].sGridNo;
|
||||
|
||||
if ( gSmokeEffectData[uiCnt].bFlags & SMOKE_EFFECT_ON_ROOF )
|
||||
bCheckLevel = 1;
|
||||
else
|
||||
bCheckLevel = 0;
|
||||
|
||||
sDist = PythSpacesAway(sCheckSpot, pSoldier->sGridNo);
|
||||
|
||||
if ( sDist < DAY_VISION_RANGE &&
|
||||
SoldierToVirtualSoldierLineOfSightTest(pSoldier, sCheckSpot, bCheckLevel, ANIM_PRONE, 0, CALC_FROM_ALL_DIRS) &&
|
||||
(sSpot == NOWHERE || sDist < sClosestDist) )
|
||||
{
|
||||
sClosestDist = sDist;
|
||||
sSpot = sCheckSpot;
|
||||
bLevel = bCheckLevel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( !TileIsOutOfBounds(sSpot) )
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// check if artillery strike was ordered by any team
|
||||
BOOLEAN CheckArtilleryStrike(void)
|
||||
{
|
||||
@@ -4784,7 +4895,7 @@ BOOLEAN ValidOpponent(SOLDIERTYPE* pSoldier, SOLDIERTYPE* pOpponent)
|
||||
pSoldier->bSide == pOpponent->bSide ||
|
||||
pSoldier->aiData.bAttitude == ATTACKSLAYONLY && pOpponent->ubProfile != SLAY ||
|
||||
(pOpponent->ubWhatKindOfMercAmI == MERC_TYPE__VEHICLE && GetNumberInVehicle(pOpponent->bVehicleID) == 0) ||
|
||||
gTacticalStatus.bBoxingState == BOXING && (pSoldier->flags.uiStatusFlags & SOLDIER_BOXER) && !(pOpponent->flags.uiStatusFlags & SOLDIER_BOXER) ||
|
||||
gTacticalStatus.bBoxingState == BOXING && BOXER(pSoldier) && !BOXER(pOpponent) ||
|
||||
pOpponent->ubBodyType == CROW)
|
||||
{
|
||||
return FALSE;
|
||||
@@ -4893,7 +5004,7 @@ BOOLEAN SoldierAI(SOLDIERTYPE *pSoldier)
|
||||
if (!IS_MERC_BODY_TYPE(pSoldier) ||
|
||||
pSoldier->aiData.bNeutral ||
|
||||
fCivilian ||
|
||||
pSoldier->flags.uiStatusFlags & SOLDIER_BOXER ||
|
||||
BOXER(pSoldier) ||
|
||||
ARMED_VEHICLE(pSoldier) ||
|
||||
pSoldier->flags.uiStatusFlags & SOLDIER_VEHICLE ||
|
||||
AM_A_ROBOT(pSoldier) ||
|
||||
@@ -6229,3 +6340,221 @@ BOOLEAN CheckSuppressionDirection(SOLDIERTYPE *pSoldier, INT32 sTargetGridNo, IN
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
UINT8 CountKnownEnemies(SOLDIERTYPE* pSoldier, INT32 sSpot, INT16 sDistance, INT8 bLevel)
|
||||
{
|
||||
CHECKF(pSoldier);
|
||||
|
||||
SOLDIERTYPE* pOpponent;
|
||||
INT32 sThreatLoc;
|
||||
INT8 bThreatLevel;
|
||||
UINT8 ubNum = 0;
|
||||
|
||||
// loop through all the enemies
|
||||
for ( UINT32 uiLoop = 0; uiLoop < guiNumMercSlots; ++uiLoop )
|
||||
{
|
||||
pOpponent = MercSlots[uiLoop];
|
||||
|
||||
// if this merc is inactive, at base, on assignment, dead, unconscious
|
||||
if ( !pOpponent || pOpponent->stats.bLife < OKLIFE )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( !ValidOpponent(pSoldier, pOpponent) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// check knowledge
|
||||
if ( Knowledge(pSoldier, pOpponent->ubID) == NOT_HEARD_OR_SEEN )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
sThreatLoc = KnownLocation(pSoldier, pOpponent->ubID);
|
||||
bThreatLevel = KnownLevel(pSoldier, pOpponent->ubID);
|
||||
|
||||
if ( TileIsOutOfBounds(sThreatLoc) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( PythSpacesAway(sSpot, sThreatLoc) > sDistance )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( bLevel >= 0 && bThreatLevel != bLevel )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ubNum++;
|
||||
}
|
||||
|
||||
return ubNum;
|
||||
}
|
||||
|
||||
UINT8 CountKnownEnemiesInRoom(SOLDIERTYPE* pSoldier, UINT16 usRoom)
|
||||
{
|
||||
CHECKF(pSoldier);
|
||||
|
||||
UINT8 ubNum = 0;
|
||||
for ( UINT32 uiLoop = 0; uiLoop < guiNumMercSlots; ++uiLoop )
|
||||
{
|
||||
SOLDIERTYPE* pOpponent = MercSlots[uiLoop];
|
||||
|
||||
// if this merc is inactive, at base, on assignment, dead, unconscious
|
||||
if ( !pOpponent || pOpponent->stats.bLife < OKLIFE )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( !ValidOpponent(pSoldier, pOpponent) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// check public knowledge
|
||||
if ( Knowledge(pSoldier, pOpponent->ubID) == NOT_HEARD_OR_SEEN )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
INT32 sThreatLoc = KnownLocation(pSoldier, pOpponent->ubID);
|
||||
|
||||
if ( TileIsOutOfBounds(sThreatLoc) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// check room
|
||||
UINT16 usRoomNo;
|
||||
if ( !InARoom(sThreatLoc, &usRoomNo) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( usRoomNo != usRoom )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ubNum++;
|
||||
}
|
||||
|
||||
return ubNum;
|
||||
}
|
||||
|
||||
UINT8 CountFriendsInRoom(SOLDIERTYPE* pSoldier, UINT16 usRoom)
|
||||
{
|
||||
CHECKF(pSoldier);
|
||||
|
||||
SOLDIERTYPE* pFriend;
|
||||
UINT8 ubFriendCount = 0;
|
||||
UINT16 usRoomNo;
|
||||
|
||||
// Run through each friendly.
|
||||
for ( SoldierID iCounter = gTacticalStatus.Team[pSoldier->bTeam].bFirstID; iCounter <= gTacticalStatus.Team[pSoldier->bTeam].bLastID; ++iCounter )
|
||||
{
|
||||
pFriend = iCounter;
|
||||
|
||||
if ( pFriend &&
|
||||
pFriend != pSoldier &&
|
||||
pFriend->bActive &&
|
||||
pFriend->stats.bLife >= OKLIFE &&
|
||||
InARoom(pFriend->sGridNo, &usRoomNo) &&
|
||||
usRoomNo == usRoom )
|
||||
{
|
||||
ubFriendCount++;
|
||||
}
|
||||
}
|
||||
|
||||
return ubFriendCount;
|
||||
}
|
||||
|
||||
INT32 CountCorpsesInRoom(SOLDIERTYPE* pSoldier, UINT16 usRoomNo, INT8 bLevel)
|
||||
{
|
||||
CHECKF(pSoldier);
|
||||
|
||||
ROTTING_CORPSE* pCorpse;
|
||||
INT32 iCount = 0;
|
||||
|
||||
for ( INT32 cnt = 0; cnt < giNumRottingCorpse; ++cnt )
|
||||
{
|
||||
pCorpse = &(gRottingCorpse[cnt]);
|
||||
|
||||
if ( pCorpse &&
|
||||
pCorpse->fActivated &&
|
||||
pCorpse->def.ubType < ROTTING_STAGE2 &&
|
||||
pCorpse->def.ubBodyType <= REGFEMALE &&
|
||||
pCorpse->def.ubAIWarningValue > 0 &&
|
||||
pCorpse->def.bLevel == bLevel &&
|
||||
!TileIsOutOfBounds(pCorpse->def.sGridNo) &&
|
||||
RoomNo(pCorpse->def.sGridNo) == usRoomNo &&
|
||||
(pSoldier->bTeam == ENEMY_TEAM && CorpseEnemyTeam(pCorpse) || pSoldier->bTeam == MILITIA_TEAM && CorpseMilitiaTeam(pCorpse) || pSoldier->bTeam == CIV_TEAM && !pSoldier->aiData.bNeutral) )
|
||||
{
|
||||
iCount++;
|
||||
}
|
||||
}
|
||||
|
||||
return iCount;
|
||||
}
|
||||
|
||||
BOOLEAN FindFenceAroundSpot(INT32 sSpot)
|
||||
{
|
||||
if ( TileIsOutOfBounds(sSpot) )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
INT32 sTempSpot;
|
||||
|
||||
// check adjacent locations
|
||||
for ( UINT8 ubDirection = 0; ubDirection < NUM_WORLD_DIRECTIONS; ubDirection++ )
|
||||
{
|
||||
sTempSpot = NewGridNo(sSpot, DirectionInc(ubDirection));
|
||||
|
||||
if ( sTempSpot != sSpot && IsCuttableWireFenceAtGridNo(sTempSpot) )
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOLEAN SameRoom(INT32 sSpot1, INT32 sSpot2)
|
||||
{
|
||||
if ( RoomNo(sSpot1) == RoomNo(sSpot2) && sSpot1 != NO_ROOM )
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
UINT16 RoomNo(INT32 sSpot)
|
||||
{
|
||||
if ( TileIsOutOfBounds(sSpot) )
|
||||
return NO_ROOM;
|
||||
|
||||
return gusWorldRoomInfo[sSpot];
|
||||
}
|
||||
|
||||
BOOLEAN CheckWindow(INT32 sSpot, UINT8 ubDirection, BOOLEAN fAllowClosed)
|
||||
{
|
||||
CHECKF(!TileIsOutOfBounds(sSpot));
|
||||
|
||||
// find window spot
|
||||
INT32 sWindowSpot = sSpot;
|
||||
if ( ubDirection == NORTH || ubDirection == WEST )
|
||||
sWindowSpot = NewGridNo(sSpot, (UINT16)DirectionInc(ubDirection));
|
||||
|
||||
//if (IsJumpableWindowPresentAtGridNo(sWindowSpot, ubDirection, gGameExternalOptions.fCanJumpThroughClosedWindows))
|
||||
if ( IsJumpableWindowPresentAtGridNo(sWindowSpot, ubDirection, fAllowClosed) )
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@@ -4234,3 +4234,687 @@ void CheckTossGrenadeAt(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestThrow, INT32 sTa
|
||||
|
||||
pSoldier->bWeaponMode = WM_NORMAL;
|
||||
}
|
||||
|
||||
|
||||
static void CalculatePossibleShots(SOLDIERTYPE* pSoldier, INT8 gun, std::vector<ATTACKTYPE> &possibleShots)
|
||||
{
|
||||
UINT32 uiLoop;
|
||||
INT32 iAttackValue, iThreatValue, iHitRate, iBestHitRate, iPercentBetter, iEstDamage, iTrueLastTarget;
|
||||
UINT16 usTrueState, usTurningCost, usRaiseGunCost;
|
||||
INT16 sMinAPcost;
|
||||
INT16 sRawAPCost;
|
||||
INT16 sAimAPCost;
|
||||
INT16 sBestAPcost;
|
||||
INT16 sChanceToHit;
|
||||
INT16 sAimTime;
|
||||
INT16 sBestAimTime;
|
||||
INT16 sMaxPossibleAimTime;
|
||||
UINT8 ubChanceToGetThrough;
|
||||
UINT8 ubBestChanceToGetThrough;
|
||||
UINT8 ubFriendlyFireChance;
|
||||
UINT8 ubBestFriendlyFireChance;
|
||||
INT16 sBestChanceToHit;
|
||||
INT16 sStanceAPcost;
|
||||
BOOLEAN fAddingTurningCost, fAddingRaiseGunCost;
|
||||
UINT8 ubStance, ubBestStance, ubChanceToReallyHit;
|
||||
INT8 bScopeMode;
|
||||
SOLDIERTYPE* pOpponent;
|
||||
|
||||
// sevenfm:
|
||||
BOOLEAN fSuppression = FALSE;
|
||||
BOOLEAN fReturnFire = FALSE;
|
||||
INT32 sTarget = NOWHERE;
|
||||
INT8 bLevel;
|
||||
|
||||
INT8 bKnowledge;
|
||||
INT8 bPersonalKnowledge;
|
||||
INT8 bPublicKnowledge;
|
||||
|
||||
DebugMsg(TOPIC_JA2, DBG_LEVEL_3, "CalcBestShot");
|
||||
|
||||
|
||||
sBestChanceToHit = sBestAimTime = sChanceToHit = ubBestChanceToGetThrough = ubBestFriendlyFireChance = ubChanceToReallyHit = 0;
|
||||
|
||||
// sevenfm: set attacking hand and target
|
||||
pSoldier->ubAttackingHand = HANDPOS;
|
||||
pSoldier->ubTargetID = NOBODY;
|
||||
|
||||
pSoldier->usAttackingWeapon = pSoldier->inv[HANDPOS].usItem;
|
||||
pSoldier->bWeaponMode = WM_NORMAL;
|
||||
|
||||
std::map<INT8, OBJECTTYPE*> ObjList;
|
||||
GetScopeLists(pSoldier, &pSoldier->inv[HANDPOS], ObjList);
|
||||
pSoldier->bScopeMode = USE_BEST_SCOPE;
|
||||
pSoldier->bDoBurst = 0;
|
||||
pSoldier->bDoAutofire = 0;
|
||||
|
||||
// determine which attack against which target has the greatest attack value
|
||||
for (uiLoop = 0; uiLoop < guiNumMercSlots; uiLoop++)
|
||||
{
|
||||
ATTACKTYPE pBestShot;
|
||||
pBestShot.ubPossible = FALSE;
|
||||
pBestShot.ubChanceToReallyHit = 0;
|
||||
pBestShot.iAttackValue = 0;
|
||||
pBestShot.ubOpponent = NOBODY;
|
||||
pBestShot.ubFriendlyFireChance = 0;
|
||||
pBestShot.bWeaponIn = gun;
|
||||
|
||||
pOpponent = MercSlots[uiLoop];
|
||||
fSuppression = FALSE;
|
||||
fReturnFire = FALSE;
|
||||
|
||||
// if this merc is inactive, at base, on assignment, or dead
|
||||
if (!pOpponent || !pOpponent->stats.bLife)
|
||||
continue; // next merc
|
||||
|
||||
if (!ValidOpponent(pSoldier, pOpponent))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// determine return fire
|
||||
if (pSoldier->aiData.bUnderFire &&
|
||||
!pSoldier->bBlindedCounter &&
|
||||
pSoldier->ubPreviousAttackerID == pOpponent->ubID)
|
||||
{
|
||||
fReturnFire = TRUE;
|
||||
}
|
||||
|
||||
bKnowledge = Knowledge(pSoldier, pOpponent->ubID);
|
||||
bPersonalKnowledge = PersonalKnowledge(pSoldier, pOpponent->ubID);
|
||||
bPublicKnowledge = PublicKnowledge(pSoldier->bTeam, pOpponent->ubID);
|
||||
|
||||
// check knowledge
|
||||
if (bKnowledge != SEEN_CURRENTLY &&
|
||||
bKnowledge != SEEN_THIS_TURN &&
|
||||
bKnowledge != SEEN_LAST_TURN &&
|
||||
bKnowledge != HEARD_THIS_TURN &&
|
||||
bKnowledge != HEARD_LAST_TURN &&
|
||||
!((bKnowledge == SEEN_2_TURNS_AGO || bKnowledge == SEEN_3_TURNS_AGO || bKnowledge == HEARD_2_TURNS_AGO) && Weapon[pSoldier->usAttackingWeapon].ubWeaponType == GUN_LMG))
|
||||
{
|
||||
continue; // next opponent
|
||||
}
|
||||
|
||||
// sevenfm: blind soldier can only attack seen/heard personally
|
||||
if (pSoldier->bBlindedCounter > 0 &&
|
||||
bPersonalKnowledge != SEEN_THIS_TURN &&
|
||||
bPersonalKnowledge != HEARD_THIS_TURN)
|
||||
{
|
||||
continue; // next opponent
|
||||
}
|
||||
|
||||
// sevenfm: determine if we shoot on unseen target for suppression
|
||||
if (bPersonalKnowledge != SEEN_CURRENTLY &&
|
||||
bPublicKnowledge != SEEN_CURRENTLY &&
|
||||
//!SoldierToSoldierLineOfSightTest(pSoldier, pOpponent, TRUE, CALC_FROM_ALL_DIRS))
|
||||
!LOS_Raised(pSoldier, pOpponent, CALC_FROM_ALL_DIRS))
|
||||
{
|
||||
fSuppression = TRUE;
|
||||
}
|
||||
|
||||
// sevenfm: shooting at unseen opponents is optional
|
||||
if (fSuppression && !gGameExternalOptions.fAIShootUnseen)
|
||||
{
|
||||
continue; // next opponent
|
||||
}
|
||||
|
||||
// determine enemy location
|
||||
if (fSuppression)
|
||||
{
|
||||
// personal/public knowledge
|
||||
sTarget = KnownLocation(pSoldier, pOpponent->ubID);
|
||||
bLevel = KnownLevel(pSoldier, pOpponent->ubID);
|
||||
// try to randomize location
|
||||
sTarget = RandomizeLocation(sTarget, bLevel, 1, pSoldier);
|
||||
}
|
||||
else
|
||||
{
|
||||
// we know exact enemy location
|
||||
sTarget = pOpponent->sGridNo;
|
||||
bLevel = pOpponent->pathing.bLevel;
|
||||
}
|
||||
|
||||
// safety check
|
||||
if (TileIsOutOfBounds(sTarget))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// skip if we can see location and location is empty
|
||||
if (SoldierToVirtualSoldierLineOfSightTest(pSoldier, sTarget, bLevel, ANIM_PRONE, TRUE, CALC_FROM_ALL_DIRS) &&
|
||||
WhoIsThere2(sTarget, bLevel) == NOBODY)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// no fire on unseen opponents with throwing knives
|
||||
if ((Item[pSoldier->usAttackingWeapon].usItemClass & IC_THROWING_KNIFE) &&
|
||||
bPersonalKnowledge != SEEN_CURRENTLY &&
|
||||
//!SoldierToSoldierLineOfSightTest(pSoldier, pOpponent, TRUE, CALC_FROM_ALL_DIRS))
|
||||
!LOS_Raised(pSoldier, pOpponent, CALC_FROM_ALL_DIRS))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// sevenfm: only enemy team can use blind suppression fire
|
||||
if (fSuppression &&
|
||||
pSoldier->bTeam != ENEMY_TEAM)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// sevenfm: only try to suppress alive and conscious human targets
|
||||
if (fSuppression &&
|
||||
(pOpponent->stats.bLife < OKLIFE ||
|
||||
pOpponent->bCollapsed && pOpponent->bBreath == 0 ||
|
||||
pOpponent->IsCowering() ||
|
||||
pOpponent->IsZombie() ||
|
||||
!IS_MERC_BODY_TYPE(pOpponent)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// shoot through wall check
|
||||
if (bPersonalKnowledge != SEEN_CURRENTLY &&
|
||||
//!SoldierToSoldierLineOfSightTest(pSoldier, pOpponent, TRUE, CALC_FROM_ALL_DIRS) &&
|
||||
!LOS_Raised(pSoldier, pOpponent, CALC_FROM_ALL_DIRS) &&
|
||||
!SoldierToVirtualSoldierLineOfSightTest(pSoldier, sTarget, bLevel, ANIM_STAND, TRUE, NO_DISTANCE_LIMIT) &&
|
||||
!LocationToLocationLineOfSightTest(pSoldier->sGridNo, pSoldier->pathing.bLevel, sTarget, bLevel, TRUE, NO_DISTANCE_LIMIT) &&
|
||||
!fReturnFire &&
|
||||
!(InARoom(sTarget, NULL) && bLevel == 0 && Weapon[pSoldier->usAttackingWeapon].ubWeaponType == GUN_LMG) && // bPublicKnowledge == SEEN_CURRENTLY &&
|
||||
!(InARoom(sTarget, NULL) && bLevel == 0 && TeamPercentKilled(pSoldier->bTeam) > (100 - 20 * SoldierDifficultyLevel(pSoldier))))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
#ifdef DEBUGATTACKS
|
||||
DebugAI(String("%s sees %s at gridno %d\n", pSoldier->GetName(), ExtMen[pOpponent->ubID].GetName(), pOpponent->sGridNo));
|
||||
#endif
|
||||
sMinAPcost = MinAPsToAttack(pSoldier, sTarget, DONTADDTURNCOST, 0);
|
||||
// later will be decide if shoot is possible this here is just best guess so ignore turnover
|
||||
|
||||
// if we don't have enough APs left to shoot even a snap-shot at this guy
|
||||
if (sMinAPcost > pSoldier->bActionPoints)
|
||||
continue; // next opponent
|
||||
|
||||
// sevenfm: check CTGT and friendly fire for each stance instead since they can be different
|
||||
// calculate chance to get through the opponent's cover (if any)
|
||||
//dnl ch61 180813
|
||||
/*gUnderFire.Clear();
|
||||
gUnderFire.Enable();
|
||||
ubChanceToGetThrough = AISoldierToSoldierChanceToGetThrough( pSoldier, pOpponent );
|
||||
ubFriendlyFireChance = gUnderFire.Chance(pSoldier->bTeam, pSoldier->bSide, TRUE);
|
||||
gUnderFire.Disable();
|
||||
|
||||
// if we can't possibly get through all the cover
|
||||
if (ubChanceToGetThrough == 0)
|
||||
continue; // next opponent
|
||||
|
||||
// sevenfm: ignore opponent if we can hit friend
|
||||
if (ubFriendlyFireChance > MIN_CHANCE_TO_ACCIDENTALLY_HIT_SOMEONE)
|
||||
continue;*/
|
||||
|
||||
if ((pSoldier->flags.uiStatusFlags & SOLDIER_MONSTER) && (pSoldier->ubBodyType != QUEENMONSTER))
|
||||
{
|
||||
STRUCTURE_FILE_REF* pStructureFileRef;
|
||||
UINT16 usAnimSurface;
|
||||
|
||||
usAnimSurface = DetermineSoldierAnimationSurface(pSoldier, pSoldier->usUIMovementMode);
|
||||
pStructureFileRef = GetAnimationStructureRef(pSoldier->ubID, usAnimSurface, pSoldier->usUIMovementMode);
|
||||
|
||||
if (pStructureFileRef)
|
||||
{
|
||||
UINT16 usStructureID;
|
||||
INT8 bDir;
|
||||
|
||||
// must make sure that structure data can be added in the direction of the target
|
||||
bDir = (INT8)GetDirectionToGridNoFromGridNo(pSoldier->sGridNo, sTarget);
|
||||
|
||||
// ATE: Only if we have a levelnode...
|
||||
if (pSoldier->pLevelNode != NULL && pSoldier->pLevelNode->pStructureData != NULL)
|
||||
{
|
||||
usStructureID = pSoldier->pLevelNode->pStructureData->usStructureID;
|
||||
}
|
||||
else
|
||||
{
|
||||
usStructureID = INVALID_STRUCTURE_ID;
|
||||
}
|
||||
|
||||
if (!OkayToAddStructureToWorld(pSoldier->sGridNo, pSoldier->pathing.bLevel, &(pStructureFileRef->pDBStructureRef[gOneCDirection[bDir]]), usStructureID))
|
||||
{
|
||||
// can't turn in that dir.... next opponent
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
iBestHitRate = 0; // reset best hit rate to minimum
|
||||
|
||||
//dnl ch69 130913 Hoping to optimize
|
||||
// consider alternate holding mode and different scopes
|
||||
// sevenfm: alt weapon holding scope mode is used only when ubAllowAlternativeWeaponHolding == 3
|
||||
for (pSoldier->bScopeMode = (gGameExternalOptions.ubAllowAlternativeWeaponHolding == 3 ? USE_ALT_WEAPON_HOLD : USE_BEST_SCOPE);
|
||||
pSoldier->bScopeMode <= (gGameExternalOptions.fScopeModes ? NUM_SCOPE_MODES - 1 : USE_BEST_SCOPE);
|
||||
pSoldier->bScopeMode++)
|
||||
{
|
||||
if (pSoldier->bScopeMode == USE_ALT_WEAPON_HOLD)
|
||||
{
|
||||
//dnl ch71 180913 throwing knives cannot be used in fire from hip
|
||||
if (Item[pSoldier->usAttackingWeapon].usItemClass & IC_THROWING_KNIFE)
|
||||
continue;
|
||||
|
||||
// sevenfm: hip firing allowed only for human bodytypes
|
||||
if (!IS_MERC_BODY_TYPE(pSoldier))
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pSoldier->bScopeMode == USE_ALT_WEAPON_HOLD || (pSoldier->bScopeMode >= USE_BEST_SCOPE && ObjList[pSoldier->bScopeMode] != NULL))
|
||||
{
|
||||
usTrueState = pSoldier->usAnimState; // because is used in CalculateRaiseGunCost, CalcAimingLevelsAvailableWithAP, CalculateTurningCost
|
||||
iTrueLastTarget = pSoldier->sLastTarget; // because is used in MinAPsToShootOrStab
|
||||
|
||||
// --------- Standing ---------
|
||||
ubStance = ANIM_STAND;
|
||||
// sevenfm: take into account direction when checking stance
|
||||
// sevenfm: shoot heavy guns in standing stance only when using hip fire
|
||||
if (pSoldier->InternalIsValidStance(AIDirection(pSoldier->sGridNo, sTarget), ubStance) &&
|
||||
(pSoldier->bScopeMode == USE_ALT_WEAPON_HOLD || !Weapon[pSoldier->usAttackingWeapon].HeavyGun || !ItemIsTwoHanded(pSoldier->usAttackingWeapon) || !gGameExternalOptions.ubAllowAlternativeWeaponHolding))
|
||||
{
|
||||
sStanceAPcost = GetAPsToChangeStance(pSoldier, ubStance);
|
||||
if (sStanceAPcost)
|
||||
{
|
||||
// Going up so first is stance change then turnover, do animation change before APs calculation
|
||||
pSoldier->usAnimState = STANDING;
|
||||
pSoldier->sLastTarget = NOWHERE;
|
||||
}
|
||||
GetAPChargeForShootOrStabWRTGunRaises(pSoldier, sTarget, TRUE, &fAddingTurningCost, &fAddingRaiseGunCost, 0);
|
||||
usTurningCost = CalculateTurningCost(pSoldier, pSoldier->usAttackingWeapon, fAddingTurningCost);
|
||||
usRaiseGunCost = CalculateRaiseGunCost(pSoldier, fAddingRaiseGunCost, sTarget, 0);
|
||||
if (fAddingTurningCost && fAddingRaiseGunCost)//dnl ch71 180913
|
||||
{
|
||||
if (usRaiseGunCost > usTurningCost)
|
||||
usTurningCost = 0;
|
||||
else
|
||||
usRaiseGunCost = 0;
|
||||
}
|
||||
sRawAPCost = MinAPsToShootOrStab(pSoldier, sTarget, 0, FALSE, 2);
|
||||
sMinAPcost = sRawAPCost + usTurningCost + sStanceAPcost + usRaiseGunCost;
|
||||
|
||||
if (pSoldier->bActionPoints - sMinAPcost >= 0)
|
||||
{
|
||||
// calc next attack's minimum shooting cost (excludes readying & turning & raise gun)
|
||||
sMaxPossibleAimTime = CalcAimingLevelsAvailableWithAP(pSoldier, sTarget, pSoldier->bActionPoints - sMinAPcost);
|
||||
|
||||
// sevenfm: check CTGT and friendly fire chance for every stance
|
||||
gUnderFire.Clear();
|
||||
gUnderFire.Enable();
|
||||
if (fSuppression)
|
||||
ubChanceToGetThrough = SoldierToLocationChanceToGetThrough(pSoldier, sTarget, bLevel, 3, NOBODY);
|
||||
else
|
||||
ubChanceToGetThrough = SoldierToSoldierChanceToGetThrough(pSoldier, pOpponent);
|
||||
ubFriendlyFireChance = gUnderFire.Chance(pSoldier->bTeam, pSoldier->bSide, TRUE);
|
||||
gUnderFire.Disable();
|
||||
|
||||
// sevenfm: only use this stance if we can hit target and cannot hit friends
|
||||
if (ubChanceToGetThrough > 0 && ubFriendlyFireChance <= MIN_CHANCE_TO_ACCIDENTALLY_HIT_SOMEONE)
|
||||
{
|
||||
for (sAimTime = 0; sAimTime <= sMaxPossibleAimTime; sAimTime++)
|
||||
{
|
||||
sChanceToHit = AICalcChanceToHitGun(pSoldier, sTarget, sAimTime, AIM_SHOT_TORSO, bLevel, STANDING);
|
||||
sAimAPCost = CalcAPCostForAiming(pSoldier, sTarget, (INT8)sAimTime);
|
||||
iHitRate = sChanceToHit * (pSoldier->bActionPoints - (sMinAPcost - sRawAPCost)) / (sRawAPCost + sAimAPCost);
|
||||
// sevenfm: take into account CTGT for every stance
|
||||
if (iHitRate * ubChanceToGetThrough > iBestHitRate * ubBestChanceToGetThrough ||
|
||||
(Item[pSoldier->usAttackingWeapon].usItemClass & IC_THROWING_KNIFE) && sChanceToHit > sBestChanceToHit)// rather take best chance for throwing knives
|
||||
{
|
||||
iBestHitRate = iHitRate;
|
||||
sBestAimTime = sAimTime;
|
||||
sBestChanceToHit = sChanceToHit;
|
||||
ubBestChanceToGetThrough = ubChanceToGetThrough;
|
||||
ubBestFriendlyFireChance = ubFriendlyFireChance;
|
||||
bScopeMode = pSoldier->bScopeMode;
|
||||
sBestAPcost = sMinAPcost;
|
||||
ubBestStance = ubStance;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pSoldier->usAnimState = usTrueState;
|
||||
pSoldier->sLastTarget = iTrueLastTarget;
|
||||
}
|
||||
|
||||
// no crouched/prone if we are tank/using throwing knife/hip firing
|
||||
if (pSoldier->bScopeMode == USE_ALT_WEAPON_HOLD || ARMED_VEHICLE(pSoldier) || ENEMYROBOT(pSoldier) || (Item[pSoldier->usAttackingWeapon].usItemClass & IC_THROWING_KNIFE))
|
||||
continue;
|
||||
|
||||
// --------- Crouched ---------
|
||||
ubStance = ANIM_CROUCH;
|
||||
// sevenfm: take into account direction
|
||||
if (pSoldier->InternalIsValidStance(AIDirection(pSoldier->sGridNo, sTarget), ubStance))
|
||||
{
|
||||
// change stance then turn
|
||||
sStanceAPcost = GetAPsToChangeStance(pSoldier, ubStance);
|
||||
if (sStanceAPcost)
|
||||
{
|
||||
pSoldier->usAnimState = CROUCHING;
|
||||
pSoldier->sLastTarget = NOWHERE;
|
||||
}
|
||||
GetAPChargeForShootOrStabWRTGunRaises(pSoldier, sTarget, TRUE, &fAddingTurningCost, &fAddingRaiseGunCost, 0);
|
||||
usTurningCost = CalculateTurningCost(pSoldier, pSoldier->usAttackingWeapon, fAddingTurningCost);
|
||||
usRaiseGunCost = CalculateRaiseGunCost(pSoldier, fAddingRaiseGunCost, sTarget, 0);
|
||||
if (fAddingTurningCost && fAddingRaiseGunCost)//dnl ch71 180913
|
||||
{
|
||||
if (usRaiseGunCost > usTurningCost)
|
||||
usTurningCost = 0;
|
||||
else
|
||||
usRaiseGunCost = 0;
|
||||
}
|
||||
sRawAPCost = MinAPsToShootOrStab(pSoldier, sTarget, 0, FALSE, 2);
|
||||
sMinAPcost = sRawAPCost + usTurningCost + sStanceAPcost + usRaiseGunCost;
|
||||
|
||||
if (pSoldier->bActionPoints - sMinAPcost >= 0)
|
||||
{
|
||||
sMaxPossibleAimTime = CalcAimingLevelsAvailableWithAP(pSoldier, sTarget, pSoldier->bActionPoints - sMinAPcost);
|
||||
|
||||
// sevenfm: check CTGT and friendly fire chance for every stance
|
||||
gUnderFire.Clear();
|
||||
gUnderFire.Enable();
|
||||
if (fSuppression)
|
||||
ubChanceToGetThrough = SoldierToLocationChanceToGetThrough(pSoldier, sTarget, bLevel, 3, NOBODY);
|
||||
else
|
||||
ubChanceToGetThrough = SoldierToSoldierChanceToGetThrough(pSoldier, pOpponent);
|
||||
ubFriendlyFireChance = gUnderFire.Chance(pSoldier->bTeam, pSoldier->bSide, TRUE);
|
||||
gUnderFire.Disable();
|
||||
|
||||
// sevenfm: only use this stance if we can hit target and cannot hit friends
|
||||
if (ubChanceToGetThrough > 0 && ubFriendlyFireChance <= MIN_CHANCE_TO_ACCIDENTALLY_HIT_SOMEONE)
|
||||
{
|
||||
for (sAimTime = 0; sAimTime <= sMaxPossibleAimTime; sAimTime++)
|
||||
{
|
||||
sChanceToHit = AICalcChanceToHitGun(pSoldier, sTarget, sAimTime, AIM_SHOT_TORSO, bLevel, CROUCHING);
|
||||
sAimAPCost = CalcAPCostForAiming(pSoldier, sTarget, (INT8)sAimTime);
|
||||
iHitRate = sChanceToHit * (pSoldier->bActionPoints - (sMinAPcost - sRawAPCost)) / (sRawAPCost + sAimAPCost);
|
||||
// sevenfm: take into account CTGT for every stance
|
||||
if (iHitRate * ubChanceToGetThrough > iBestHitRate * ubBestChanceToGetThrough)
|
||||
{
|
||||
iBestHitRate = iHitRate;
|
||||
sBestAimTime = sAimTime;
|
||||
sBestChanceToHit = sChanceToHit;
|
||||
ubBestChanceToGetThrough = ubChanceToGetThrough;
|
||||
ubBestFriendlyFireChance = ubFriendlyFireChance;
|
||||
bScopeMode = pSoldier->bScopeMode;
|
||||
sBestAPcost = sMinAPcost;
|
||||
ubBestStance = ubStance;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pSoldier->usAnimState = usTrueState;
|
||||
pSoldier->sLastTarget = iTrueLastTarget;
|
||||
}
|
||||
|
||||
// no prone stance if we have to change direction and stance at the same time
|
||||
if (pSoldier->ubDirection != AIDirection(pSoldier->sGridNo, sTarget) &&
|
||||
gAnimControl[pSoldier->usAnimState].ubEndHeight > ANIM_PRONE)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// --------- Prone ---------
|
||||
ubStance = ANIM_PRONE;
|
||||
if (pSoldier->InternalIsValidStance(AIDirection(pSoldier->sGridNo, sTarget), ubStance))
|
||||
{
|
||||
sStanceAPcost = GetAPsToChangeStance(pSoldier, ubStance);
|
||||
if (sStanceAPcost)
|
||||
{
|
||||
pSoldier->usAnimState = PRONE;
|
||||
pSoldier->sLastTarget = NOWHERE;
|
||||
}
|
||||
GetAPChargeForShootOrStabWRTGunRaises(pSoldier, sTarget, TRUE, &fAddingTurningCost, &fAddingRaiseGunCost, 0);
|
||||
usTurningCost = CalculateTurningCost(pSoldier, pSoldier->usAttackingWeapon, fAddingTurningCost);
|
||||
usRaiseGunCost = CalculateRaiseGunCost(pSoldier, fAddingRaiseGunCost, sTarget, 0);
|
||||
sRawAPCost = MinAPsToShootOrStab(pSoldier, sTarget, 0, FALSE, 2);
|
||||
sMinAPcost = sRawAPCost + usTurningCost + sStanceAPcost + usRaiseGunCost;
|
||||
|
||||
if (pSoldier->bActionPoints - sMinAPcost >= 0)
|
||||
{
|
||||
sMaxPossibleAimTime = CalcAimingLevelsAvailableWithAP(pSoldier, sTarget, pSoldier->bActionPoints - sMinAPcost);
|
||||
|
||||
// sevenfm: check CTGT and friendly fire chance for every stance
|
||||
gUnderFire.Clear();
|
||||
gUnderFire.Enable();
|
||||
if (fSuppression)
|
||||
ubChanceToGetThrough = SoldierToLocationChanceToGetThrough(pSoldier, sTarget, bLevel, 3, NOBODY);
|
||||
else
|
||||
ubChanceToGetThrough = SoldierToSoldierChanceToGetThrough(pSoldier, pOpponent);
|
||||
ubFriendlyFireChance = gUnderFire.Chance(pSoldier->bTeam, pSoldier->bSide, TRUE);
|
||||
gUnderFire.Disable();
|
||||
|
||||
// sevenfm: only use this stance if we can hit target and cannot hit friends
|
||||
if (ubChanceToGetThrough > 0 && ubFriendlyFireChance <= MIN_CHANCE_TO_ACCIDENTALLY_HIT_SOMEONE)
|
||||
{
|
||||
for (sAimTime = 0; sAimTime <= sMaxPossibleAimTime; sAimTime++)
|
||||
{
|
||||
sChanceToHit = AICalcChanceToHitGun(pSoldier, sTarget, sAimTime, AIM_SHOT_TORSO, bLevel, PRONE);
|
||||
sAimAPCost = CalcAPCostForAiming(pSoldier, sTarget, (INT8)sAimTime);
|
||||
iHitRate = sChanceToHit * (pSoldier->bActionPoints - (sMinAPcost - sRawAPCost)) / (sRawAPCost + sAimAPCost);
|
||||
// sevenfm: take into account CTGT for every stance
|
||||
if (iHitRate * ubChanceToGetThrough > iBestHitRate * ubBestChanceToGetThrough)
|
||||
{
|
||||
iBestHitRate = iHitRate;
|
||||
sBestAimTime = sAimTime;
|
||||
sBestChanceToHit = sChanceToHit;
|
||||
ubBestChanceToGetThrough = ubChanceToGetThrough;
|
||||
ubBestFriendlyFireChance = ubFriendlyFireChance;
|
||||
bScopeMode = pSoldier->bScopeMode;
|
||||
sBestAPcost = sMinAPcost;
|
||||
ubBestStance = ubStance;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pSoldier->usAnimState = usTrueState;
|
||||
pSoldier->sLastTarget = iTrueLastTarget;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if we can't get any kind of hit rate at all
|
||||
if (iBestHitRate == 0)
|
||||
continue; // next opponent
|
||||
|
||||
// calculate chance to REALLY hit: shoot accurately AND get past cover
|
||||
ubChanceToReallyHit = (UINT8)ceil((sBestChanceToHit * ubBestChanceToGetThrough) / 100.0f);
|
||||
|
||||
// if we can't REALLY hit at all
|
||||
if (ubChanceToReallyHit == 0)
|
||||
continue; // next opponent
|
||||
|
||||
// really limit knife throwing so it doesn't look wrong
|
||||
if (Item[pSoldier->usAttackingWeapon].usItemClass == IC_THROWING_KNIFE &&
|
||||
(ubChanceToReallyHit < 25 || (PythSpacesAway(pSoldier->sGridNo, sTarget) > CalcMaxTossRange(pSoldier, pSoldier->usAttackingWeapon, FALSE))))// Madd / 2 ) ) ) //dnl ch69 160913 was ubChanceToReallyHit < 30
|
||||
continue; // don't bother... next opponent
|
||||
|
||||
// calculate this opponent's threat value (factor in my cover from him)
|
||||
iThreatValue = CalcManThreatValue(pOpponent, pSoldier->sGridNo, TRUE, pSoldier);
|
||||
|
||||
// estimate the damage this shot would do to this opponent
|
||||
iEstDamage = EstimateShotDamage(pSoldier, pOpponent, sBestChanceToHit);
|
||||
//NumMessage("SHOT EstDamage = ",iEstDamage);
|
||||
|
||||
// calculate the combined "attack value" for this opponent
|
||||
// highest possible value before division should be about 1.8 billion...
|
||||
// normal value before division should be about 5 million...
|
||||
iAttackValue = (iEstDamage * iBestHitRate * ubChanceToReallyHit * iThreatValue) / 1000;
|
||||
//NumMessage("SHOT AttackValue = ",iAttackValue / 1000);
|
||||
|
||||
// sevenfm: penalize suppression fire
|
||||
if (fSuppression)
|
||||
{
|
||||
// 25% penalty for shooting at invisible target
|
||||
iAttackValue = iAttackValue / 2;
|
||||
}
|
||||
|
||||
// special stuff for assassins to ignore militia more
|
||||
if (pSoldier->IsAssassin() && pOpponent->bTeam == MILITIA_TEAM)
|
||||
{
|
||||
iAttackValue /= 2;
|
||||
}
|
||||
|
||||
// sevenfm: empty vehicles have very low priority
|
||||
if (pOpponent->ubWhatKindOfMercAmI == MERC_TYPE__VEHICLE && GetNumberInVehicle(pOpponent->bVehicleID) == 0)
|
||||
{
|
||||
iAttackValue /= 4;
|
||||
}
|
||||
|
||||
// sevenfm: dying, cowering or unconscious soldiers have very low priority
|
||||
if (pOpponent->stats.bLife < OKLIFE || pOpponent->bCollapsed || pOpponent->bBreathCollapsed)
|
||||
{
|
||||
iAttackValue /= 4;
|
||||
}
|
||||
|
||||
#ifdef DEBUGATTACKS
|
||||
DebugAI(String("CalcBestShot: best AttackValue vs %d = %d\n", uiLoop, iAttackValue));
|
||||
#endif
|
||||
|
||||
// if we can hurt the guy, OR probably not, but at least it's our best
|
||||
// chance to actually hit him and maybe scare him, knock him down, etc.
|
||||
if ((iAttackValue > 0) || (ubChanceToReallyHit > pBestShot.ubChanceToReallyHit))
|
||||
{
|
||||
#if 0 // if there already was another viable target
|
||||
if (pBestShot.ubChanceToReallyHit > 0)
|
||||
{
|
||||
// OK, how does our chance to hit him compare to the previous best one?
|
||||
iPercentBetter = ((ubChanceToReallyHit * 100) / pBestShot.ubChanceToReallyHit) - 100;
|
||||
|
||||
//dnl ch62 180813 ignore firing into breathless targets if there are targets in better condition
|
||||
// sevenfm: check that best opponent exists
|
||||
if (pBestShot.ubOpponent != NOBODY &&
|
||||
(Menptr[pBestShot.ubOpponent].bCollapsed || Menptr[pBestShot.ubOpponent].bBreathCollapsed) &&
|
||||
Menptr[pBestShot.ubOpponent].bBreath < OKBREATH
|
||||
&& Menptr[pBestShot.ubOpponent].bBreath < pOpponent->bBreath)
|
||||
{
|
||||
iPercentBetter = PERCENT_TO_IGNORE_THREAT;
|
||||
}
|
||||
|
||||
// sevenfm: if best opponent is dying and new opponent is ok, use new opponent
|
||||
if (pBestShot.ubOpponent != NOBODY &&
|
||||
Menptr[pBestShot.ubOpponent].stats.bLife < OKLIFE &&
|
||||
pOpponent->stats.bLife >= OKLIFE)
|
||||
{
|
||||
iPercentBetter = PERCENT_TO_IGNORE_THREAT;
|
||||
}
|
||||
|
||||
// if this chance to really hit is more than 50% worse, and the other
|
||||
// guy is conscious at all
|
||||
if (iPercentBetter < -PERCENT_TO_IGNORE_THREAT &&
|
||||
pBestShot.ubOpponent != NOBODY &&
|
||||
Menptr[pBestShot.ubOpponent].stats.bLife >= OKLIFE)
|
||||
{
|
||||
// then stick with the older guy as the better target
|
||||
continue;
|
||||
}
|
||||
|
||||
// if this chance to really hit between 50% worse to 50% better
|
||||
if (iPercentBetter < PERCENT_TO_IGNORE_THREAT)
|
||||
{
|
||||
// then the one with the higher ATTACK VALUE is the better target
|
||||
if (iAttackValue < pBestShot.iAttackValue)
|
||||
// the previous guy is more important since he's more dangerous
|
||||
continue; // next opponent
|
||||
}
|
||||
}
|
||||
|
||||
// sevenfm: if new opponent is dying and best opponent is ok, ignore new opponent
|
||||
if (pBestShot.ubOpponent != NOBODY &&
|
||||
Menptr[pBestShot.ubOpponent].stats.bLife >= OKLIFE &&
|
||||
pOpponent->stats.bLife < OKLIFE)
|
||||
{
|
||||
//DebugShot(pSoldier, String("new opponent is dying, best opponent is ok - skip"));
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
// OOOF! That was a lot of work! But we've got a new viable target!
|
||||
pBestShot.ubPossible = TRUE;
|
||||
pBestShot.ubOpponent = pOpponent->ubID;
|
||||
pBestShot.ubAimTime = sBestAimTime;
|
||||
pBestShot.ubChanceToReallyHit = ubChanceToReallyHit;
|
||||
pBestShot.sTarget = sTarget;
|
||||
pBestShot.bTargetLevel = bLevel;
|
||||
pBestShot.iAttackValue = iAttackValue;
|
||||
pBestShot.ubAPCost = sBestAPcost;
|
||||
pBestShot.ubStance = ubBestStance;
|
||||
pBestShot.bScopeMode = bScopeMode;
|
||||
pBestShot.ubFriendlyFireChance = ubBestFriendlyFireChance;
|
||||
|
||||
possibleShots.push_back(pBestShot);
|
||||
}
|
||||
}
|
||||
|
||||
pSoldier->bScopeMode = USE_BEST_SCOPE; // better reset this back
|
||||
}
|
||||
|
||||
bool CompareAttacks(const ATTACKTYPE& a, const ATTACKTYPE& b)
|
||||
{
|
||||
if (a.iAttackValue > b.iAttackValue)
|
||||
return true;
|
||||
else if (a.iAttackValue < b.iAttackValue)
|
||||
return false;
|
||||
|
||||
if (a.ubChanceToReallyHit > b.ubChanceToReallyHit)
|
||||
return true;
|
||||
else if (a.ubChanceToReallyHit < b.ubChanceToReallyHit)
|
||||
return false;
|
||||
|
||||
if (a.ubFriendlyFireChance < b.ubFriendlyFireChance)
|
||||
return true;
|
||||
else if (a.ubFriendlyFireChance > b.ubFriendlyFireChance)
|
||||
return false;
|
||||
|
||||
if (a.ubAPCost < b.ubAPCost)
|
||||
return true;
|
||||
else if (a.ubAPCost > b.ubAPCost)
|
||||
return false;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void CheckIfShotsPossible(SOLDIERTYPE* pSoldier, std::vector<ATTACKTYPE>& possibleShots)
|
||||
{
|
||||
INT8 guns[] = { FindAIUsableObjClass(pSoldier, IC_GUN), FindAIUsableObjClass(pSoldier, IC_GUN, TRUE) };
|
||||
// Only merc bodytypes can use a sidearm, also don't bother to check for targets if found sidearm is the same as main gun
|
||||
if (!IS_MERC_BODY_TYPE(pSoldier) || guns[0] == guns[1])
|
||||
{
|
||||
guns[1] = NO_SLOT;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < 2; i++)
|
||||
{
|
||||
const auto gun = guns[i];
|
||||
// if the soldier does have a gun
|
||||
if (gun != NO_SLOT)
|
||||
{
|
||||
// if it's in his holster, swap it into his hand temporarily
|
||||
if (gun != HANDPOS)
|
||||
{
|
||||
RearrangePocket(pSoldier, HANDPOS, gun, TEMPORARILY);
|
||||
}
|
||||
|
||||
// get the minimum cost to attack with this item
|
||||
INT16 ubMinAPcost = MinAPsToAttack(pSoldier, pSoldier->sLastTarget, ADDTURNCOST, 0);
|
||||
|
||||
// if we can afford the minimum AP cost
|
||||
if (pSoldier->bActionPoints >= ubMinAPcost)
|
||||
{
|
||||
// then look around for worthy targets
|
||||
CalculatePossibleShots(pSoldier, gun, possibleShots);
|
||||
}
|
||||
|
||||
// if it was in his holster, swap it back into his holster for now
|
||||
if (gun != HANDPOS)
|
||||
{
|
||||
RearrangePocket(pSoldier, HANDPOS, gun, TEMPORARILY);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,4 +14,6 @@ set(TacticalAISrc
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/QuestDebug.cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/Realtime.cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/ZombieDecideAction.cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/UtilityAI.cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/UtilityAI_ResponseCurve.cpp"
|
||||
PARENT_SCOPE)
|
||||
|
||||
@@ -844,7 +844,8 @@ INT8 CreatureDecideActionRed(SOLDIERTYPE *pSoldier, UINT8 ubUnconsciousOK)
|
||||
{
|
||||
// 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);
|
||||
INT32 distanceToThreat;
|
||||
sClosestOpponent = ClosestKnownOpponent(pSoldier, NULL, NULL, NULL, &distanceToThreat);
|
||||
|
||||
if (!TileIsOutOfBounds(sClosestOpponent))
|
||||
{
|
||||
@@ -854,9 +855,9 @@ INT8 CreatureDecideActionRed(SOLDIERTYPE *pSoldier, UINT8 ubUnconsciousOK)
|
||||
// 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 );
|
||||
sDistVisible = pSoldier->GetMaxDistanceVisible(sClosestOpponent, 0 ) * CELL_X_SIZE;
|
||||
|
||||
if ((pSoldier->ubDirection != ubOpponentDir) && (PythSpacesAway(pSoldier->sGridNo,sClosestOpponent) <= sDistVisible))
|
||||
if ((pSoldier->ubDirection != ubOpponentDir) && (distanceToThreat <= sDistVisible))
|
||||
{
|
||||
// set base chance according to orders
|
||||
if ((pSoldier->aiData.bOrders == STATIONARY) || (pSoldier->aiData.bOrders == ONGUARD))
|
||||
@@ -1380,11 +1381,12 @@ INT8 CreatureDecideActionBlack( SOLDIERTYPE * 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 = ClosestKnownOpponent(pSoldier, NULL, NULL);
|
||||
INT32 distanceToThreat;
|
||||
sClosestOpponent = ClosestKnownOpponent(pSoldier, NULL, NULL, NULL, &distanceToThreat);
|
||||
// if we have a closest reachable opponent
|
||||
if (!TileIsOutOfBounds(sClosestOpponent))
|
||||
{
|
||||
if ( ubCanMove && PythSpacesAway( pSoldier->sGridNo, sClosestOpponent ) > 2 )
|
||||
if ( ubCanMove && distanceToThreat > 20 ) // 2 tiles -> 20 in fractional Cell Coordinates
|
||||
{
|
||||
if ( bSpitIn != NO_SLOT )
|
||||
{
|
||||
|
||||
+12611
-288
File diff suppressed because it is too large
Load Diff
+138
-287
@@ -15,10 +15,6 @@
|
||||
#include "Render Fun.h"
|
||||
#include "Boxing.h"
|
||||
#include "Text.h"
|
||||
#ifdef _DEBUG
|
||||
#include "renderworld.h"
|
||||
#include "video.h"
|
||||
#endif
|
||||
#include "worldman.h"
|
||||
#include "strategicmap.h"
|
||||
#include "environment.h"
|
||||
@@ -26,7 +22,8 @@
|
||||
#include "Buildings.h"
|
||||
#include "GameSettings.h"
|
||||
#include "Soldier Profile.h"
|
||||
#include "Rotting Corpses.h" // sevenfm
|
||||
#include "rotting corpses.h" // sevenfm
|
||||
#include <Cheats.h>
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -34,17 +31,7 @@
|
||||
// were changed to GetAPsCrouch() and GetAPsProne()
|
||||
// - also all "APBPConstants[AP_PICKUP_ITEM]" were replaced by GetBasicAPsToPickupItem()
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
INT16 * gsCoverValue = NULL;
|
||||
#ifdef _DEBUG
|
||||
|
||||
INT16 gsBestCover;
|
||||
#ifndef PATHAI_VISIBLE_DEBUG
|
||||
// NB Change this to true to get visible cover debug -- CJC
|
||||
BOOLEAN gfDisplayCoverValues = FALSE;
|
||||
#endif
|
||||
extern void RenderCoverDebug( void );
|
||||
#endif
|
||||
INT16 gsBestCover;
|
||||
|
||||
INT16 gubAIPathCosts[19][19];
|
||||
|
||||
@@ -613,42 +600,115 @@ UINT8 NumberOfTeamMatesAdjacent( SOLDIERTYPE * pSoldier, INT32 sGridNo )
|
||||
return( ubCount );
|
||||
}
|
||||
|
||||
INT32 FindBestNearbyCover(SOLDIERTYPE *pSoldier, INT32 morale, INT32 *piPercentBetter)
|
||||
static void CalculateCoverValue(SOLDIERTYPE* pSoldier, const INT32 sGridNo, const INT32 iPathCost, const INT32 iMyThreatValue, const UINT32 uiThreatCnt, const UINT8 ubDiff, const BOOLEAN fNight, const UINT8 ubBackgroundLightPercent, const INT32 morale, INT32 &iCoverValue, INT32& iCoverScale)
|
||||
{
|
||||
iCoverValue = 0;
|
||||
iCoverScale = 0;
|
||||
|
||||
// sevenfm: check sight cover
|
||||
BOOLEAN fProneCover = TRUE;
|
||||
|
||||
// for every opponent that threatens, consider this spot's cover vs. him
|
||||
for (UINT32 uiLoop = 0; uiLoop < uiThreatCnt; uiLoop++)
|
||||
{
|
||||
// calculate the range we would be at from this opponent
|
||||
INT32 iThreatRange = Threat[uiLoop].iOrigRange;
|
||||
if (sGridNo != pSoldier->sGridNo)
|
||||
{
|
||||
iThreatRange = GetRangeInCellCoordsFromGridNoDiff(sGridNo, Threat[uiLoop].sGridNo);
|
||||
}
|
||||
|
||||
// if this threat would be within 20 tiles, count it
|
||||
if (iThreatRange <= MAX_THREAT_RANGE)
|
||||
{
|
||||
iCoverValue += CalcCoverValue(
|
||||
pSoldier, sGridNo, iMyThreatValue,
|
||||
(pSoldier->bActionPoints - iPathCost),
|
||||
uiLoop, iThreatRange, morale, &iCoverScale
|
||||
);
|
||||
}
|
||||
|
||||
// sevenfm: sight test
|
||||
if (gGameExternalOptions.fAIBetterCover)
|
||||
{
|
||||
if (LocationToLocationLineOfSightTest(Threat[uiLoop].sGridNo, Threat[uiLoop].pOpponent->pathing.bLevel, sGridNo, pSoldier->pathing.bLevel, TRUE, MAX_VISION_RANGE, STANDING_LOS_POS, PRONE_LOS_POS))
|
||||
//if ( SoldierToVirtualSoldierLineOfSightTest( Threat[uiLoop].pOpponent, sGridNo, pSoldier->pathing.bLevel, ANIM_PRONE, TRUE, -1 ) != 0 )
|
||||
{
|
||||
fProneCover = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
//sprintf(tempstr,"iCoverValue after opponent %d is now %d",iLoop,iCoverValue);
|
||||
//PopMessage(tempstr);
|
||||
}
|
||||
|
||||
// reduce cover for each person adjacent to this gridno who is on our team,
|
||||
// by 10% (so locations next to several people will be very much frowned upon
|
||||
iCoverValue -= (abs(iCoverValue) / 5) * NumberOfTeamMatesAdjacent(pSoldier, sGridNo);
|
||||
|
||||
if (gGameExternalOptions.fAIBetterCover)
|
||||
{
|
||||
// sevenfm: when defending (range change <= 3), prefer locations with sight cover
|
||||
if (RangeChangeDesire(pSoldier) < 4)
|
||||
{
|
||||
if (fProneCover) { iCoverValue += abs(iCoverValue) / __max(2, 2 * RangeChangeDesire(pSoldier)); }
|
||||
}
|
||||
|
||||
// sevenfm: check for nearby friends, add bonus/penalty 10%
|
||||
UINT8 ubNearbyFriends = __max(0, CountNearbyFriends(pSoldier, sGridNo, TACTICAL_RANGE_CLOSE));
|
||||
iCoverValue -= ubNearbyFriends * abs(iCoverValue) / (6 - ubDiff);
|
||||
|
||||
// sevenfm: penalize locations with fresh corpses
|
||||
if (GetNearestRottingCorpseAIWarning(sGridNo) > 0)
|
||||
{
|
||||
iCoverValue -= abs(iCoverValue) / (6 - ubDiff);
|
||||
}
|
||||
|
||||
// sevenfm: penalize locations near red smoke
|
||||
iCoverValue -= abs(iCoverValue) * RedSmokeDanger(sGridNo, pSoldier->pathing.bLevel) / 100;
|
||||
}
|
||||
|
||||
if (fNight && !(InARoom(sGridNo, NULL))) // ignore in buildings in case placed there
|
||||
{
|
||||
// reduce cover at nighttime based on how bright the light is at that location
|
||||
// using the difference in sighting distance between the background and the
|
||||
// light for this tile
|
||||
//ubLightPercentDifference = (gbLightSighting[ 0 ][ LightTrueLevel( sGridNo, pSoldier->pathing.bLevel ) ] - ubBackgroundLightPercent );
|
||||
UINT8 ubLightPercentDifference = (gGameExternalOptions.ubBrightnessVisionMod[LightTrueLevel(sGridNo, pSoldier->pathing.bLevel)] - ubBackgroundLightPercent);
|
||||
iCoverValue -= (abs(iCoverValue) / 100) * ubLightPercentDifference;
|
||||
}
|
||||
}
|
||||
|
||||
INT32 FindBestNearbyCover(SOLDIERTYPE *pSoldier, INT32 morale, INT32 *piPercentBetter, INT32 targetGridNo, bool ignoreSearchRange)
|
||||
{
|
||||
if (gRenderDebugInfoMode == DEBUG_COVERVALUE && DEBUG_CHEAT_LEVEL())
|
||||
{
|
||||
ResetDebugInfoValues();
|
||||
}
|
||||
DebugMsg(TOPIC_JA2AI,DBG_LEVEL_3,String("FindBestNearbyCover"));
|
||||
|
||||
// all 32-bit integers for max. speed
|
||||
UINT32 uiLoop;
|
||||
INT32 iCurrentCoverValue, iCoverValue, iBestCoverValue;
|
||||
INT32 iCurrentScale = -1, iCoverScale = -1, iBestCoverScale = -1;
|
||||
INT32 iDistFromOrigin, iDistCoverFromOrigin;
|
||||
//INT32 iThreatCertainty;
|
||||
INT32 sGridNo, sBestCover = NOWHERE;
|
||||
INT32 iPathCost;
|
||||
INT32 iThreatRange, iClosestThreatRange = 1500;
|
||||
// INT16 sClosestThreatGridno = NOWHERE;
|
||||
INT32 iMyThreatValue;
|
||||
//INT32 sThreatLoc;
|
||||
//INT32 iMaxThreatRange;
|
||||
UINT32 uiThreatCnt = 0;
|
||||
INT32 iMaxMoveTilesLeft, iSearchRange, iRoamRange;
|
||||
INT16 sMaxLeft, sMaxRight, sMaxUp, sMaxDown, sXOffset, sYOffset;
|
||||
INT32 sOrigin; // has to be a short, need a pointer
|
||||
INT32 * pusLastLoc;
|
||||
INT8 * pbPersOL;
|
||||
INT8 * pbPublOL;
|
||||
//SOLDIERTYPE *pOpponent;
|
||||
UINT16 usMovementMode;
|
||||
|
||||
UINT8 ubBackgroundLightLevel;
|
||||
UINT8 ubBackgroundLightPercent = 0;
|
||||
UINT8 ubLightPercentDifference;
|
||||
BOOLEAN fNight;
|
||||
|
||||
// sevenfm
|
||||
UINT8 ubNearbyFriends;
|
||||
BOOLEAN fProneCover;
|
||||
UINT8 ubDiff = SoldierDifficultyLevel( pSoldier );
|
||||
const UINT8 ubDiff = SoldierDifficultyLevel( pSoldier );
|
||||
|
||||
if (targetGridNo == NOWHERE)
|
||||
{
|
||||
targetGridNo = pSoldier->sGridNo;
|
||||
}
|
||||
|
||||
// There's no cover when boxing!
|
||||
if (gTacticalStatus.bBoxingState == BOXING)
|
||||
@@ -676,25 +736,6 @@ INT32 FindBestNearbyCover(SOLDIERTYPE *pSoldier, INT32 morale, INT32 *piPercentB
|
||||
}
|
||||
}
|
||||
|
||||
iBestCoverValue = -1;
|
||||
|
||||
#if defined( _DEBUG ) && defined( COVER_DEBUG )
|
||||
if (gfDisplayCoverValues)
|
||||
{
|
||||
memset( gsCoverValue, 0x7F, sizeof( INT16 ) * WORLD_MAX );
|
||||
}
|
||||
#endif
|
||||
|
||||
//NameMessage(pSoldier,"looking for some cover...");
|
||||
|
||||
// BUILD A LIST OF THREATENING GRID #s FROM PERSONAL & PUBLIC opplists
|
||||
|
||||
pusLastLoc = &(gsLastKnownOppLoc[pSoldier->ubID][0]);
|
||||
|
||||
// hang a pointer into personal opplist
|
||||
pbPersOL = &(pSoldier->aiData.bOppList[0]);
|
||||
// hang a pointer into public opplist
|
||||
pbPublOL = &(gbPublicOpplist[pSoldier->bTeam][0]);
|
||||
|
||||
// decide how far we're gonna be looking
|
||||
iSearchRange = gbDiff[DIFF_MAX_COVER_RANGE][ SoldierDifficultyLevel( pSoldier ) ];
|
||||
@@ -711,9 +752,9 @@ INT32 FindBestNearbyCover(SOLDIERTYPE *pSoldier, INT32 morale, INT32 *piPercentB
|
||||
}*/
|
||||
|
||||
// maximum search range is 1 tile / 8 pts of wisdom
|
||||
if (iSearchRange > (pSoldier->stats.bWisdom / 8))
|
||||
if (iSearchRange > (pSoldier->stats.bWisdom / 4))
|
||||
{
|
||||
iSearchRange = (pSoldier->stats.bWisdom / 8);
|
||||
iSearchRange = (pSoldier->stats.bWisdom / 4);
|
||||
}
|
||||
|
||||
if (!gfTurnBasedAI)
|
||||
@@ -724,6 +765,7 @@ INT32 FindBestNearbyCover(SOLDIERTYPE *pSoldier, INT32 morale, INT32 *piPercentB
|
||||
|
||||
usMovementMode = DetermineMovementMode( pSoldier, AI_ACTION_TAKE_COVER );
|
||||
|
||||
#if 1
|
||||
if (pSoldier->aiData.bAlertStatus >= STATUS_RED) // if already in battle
|
||||
{
|
||||
// must be able to reach the cover, so it can't possibly be more than
|
||||
@@ -744,6 +786,12 @@ INT32 FindBestNearbyCover(SOLDIERTYPE *pSoldier, INT32 morale, INT32 *piPercentB
|
||||
iSearchRange = iMaxMoveTilesLeft;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (ignoreSearchRange)
|
||||
{
|
||||
// For debugging
|
||||
iSearchRange = 16;
|
||||
}
|
||||
|
||||
if (iSearchRange <= 0)
|
||||
{
|
||||
@@ -765,95 +813,30 @@ INT32 FindBestNearbyCover(SOLDIERTYPE *pSoldier, INT32 morale, INT32 *piPercentB
|
||||
|
||||
// calculate our current cover value in the place we are now, since the
|
||||
// cover we are searching for must be better than what we have now!
|
||||
iCurrentCoverValue = 0;
|
||||
iCurrentScale = 0;
|
||||
CalculateCoverValue(pSoldier, targetGridNo, 0, iMyThreatValue, uiThreatCnt, ubDiff, fNight, ubBackgroundLightPercent, morale, iCurrentCoverValue, iCurrentScale);
|
||||
// the initial cover value to beat is our current cover value
|
||||
iBestCoverValue = iCurrentCoverValue;
|
||||
|
||||
// sevenfm: sight cover
|
||||
fProneCover = TRUE;
|
||||
|
||||
// for every opponent that threatens, consider this spot's cover vs. him
|
||||
for (uiLoop = 0; uiLoop < uiThreatCnt; uiLoop++)
|
||||
if (gRenderDebugInfoMode == DEBUG_COVERVALUE && DEBUG_CHEAT_LEVEL())
|
||||
{
|
||||
// if this threat is CURRENTLY within 20 tiles
|
||||
if (Threat[uiLoop].iOrigRange <= MAX_THREAT_RANGE)
|
||||
{
|
||||
// add this opponent's cover value to our current total cover value
|
||||
iCurrentCoverValue += CalcCoverValue(pSoldier,pSoldier->sGridNo,iMyThreatValue,pSoldier->bActionPoints,uiLoop,Threat[uiLoop].iOrigRange,morale,&iCurrentScale);
|
||||
}
|
||||
// sevenfm: sight test
|
||||
if( gGameExternalOptions.fAIBetterCover )
|
||||
{
|
||||
if ( LocationToLocationLineOfSightTest( Threat[uiLoop].sGridNo, Threat[uiLoop].pOpponent->pathing.bLevel, pSoldier->sGridNo, pSoldier->pathing.bLevel, TRUE, MAX_VISION_RANGE, STANDING_LOS_POS, PRONE_LOS_POS ) )
|
||||
//if ( SoldierToVirtualSoldierLineOfSightTest( Threat[uiLoop].pOpponent, pSoldier->sGridNo, pSoldier->pathing.bLevel, ANIM_PRONE, TRUE, NO_DISTANCE_LIMIT ) )
|
||||
{
|
||||
fProneCover = FALSE;
|
||||
}
|
||||
}
|
||||
//sprintf(tempstr,"iCurrentCoverValue after opponent %d is now %d",iLoop,iCurrentCoverValue);
|
||||
//PopMessage(tempstr);
|
||||
gRenderDebugInfoValues[targetGridNo] = (INT32)(iCurrentCoverValue / 100);
|
||||
}
|
||||
|
||||
// reduce cover for each person adjacent to this gridno who is on our team,
|
||||
// by 10% (so locations next to several people will be very much frowned upon
|
||||
if ( iCurrentCoverValue >= 0 )
|
||||
{
|
||||
iCurrentCoverValue -= (iCurrentCoverValue / 10) * NumberOfTeamMatesAdjacent( pSoldier, pSoldier->sGridNo );
|
||||
}
|
||||
else
|
||||
{
|
||||
// when negative, must add a negative to decrease the total
|
||||
iCurrentCoverValue += (iCurrentCoverValue / 10) * NumberOfTeamMatesAdjacent( pSoldier, pSoldier->sGridNo );
|
||||
}
|
||||
|
||||
if( gGameExternalOptions.fAIBetterCover )
|
||||
{
|
||||
// sevenfm: when defending (range change <= 3), prefer locations with sight cover
|
||||
if( RangeChangeDesire(pSoldier) < 4 )
|
||||
{
|
||||
if( fProneCover )
|
||||
{
|
||||
iCurrentCoverValue += abs(iCurrentCoverValue) / __max(2, 2*RangeChangeDesire(pSoldier));
|
||||
}
|
||||
}
|
||||
|
||||
// sevenfm: check for nearby friends, add bonus/penalty
|
||||
ubNearbyFriends = __min(5, CountNearbyFriends( pSoldier, pSoldier->sGridNo, 5 ));
|
||||
iCurrentCoverValue -= ubNearbyFriends * abs(iCurrentCoverValue) / (10-ubDiff);
|
||||
|
||||
// sevenfm: penalize locations with fresh corpses
|
||||
if(GetNearestRottingCorpseAIWarning( pSoldier->sGridNo ) > 0)
|
||||
{
|
||||
iCurrentCoverValue -= abs(iCurrentCoverValue) / (8-ubDiff);
|
||||
}
|
||||
|
||||
// sevenfm: penalize locations near red smoke
|
||||
iCurrentCoverValue -= abs(iCurrentCoverValue) * RedSmokeDanger(pSoldier->sGridNo, pSoldier->pathing.bLevel) / 100;
|
||||
}
|
||||
|
||||
#ifdef DEBUGCOVER
|
||||
// AINumMessage("Search Range = ",iSearchRange);
|
||||
#endif
|
||||
|
||||
// determine maximum horizontal limits
|
||||
sMaxLeft = min(iSearchRange,(pSoldier->sGridNo % MAXCOL));
|
||||
//NumMessage("sMaxLeft = ",sMaxLeft);
|
||||
sMaxRight = min(iSearchRange,MAXCOL - ((pSoldier->sGridNo % MAXCOL) + 1));
|
||||
//NumMessage("sMaxRight = ",sMaxRight);
|
||||
|
||||
sMaxLeft = min(iSearchRange,(targetGridNo % MAXCOL));
|
||||
sMaxRight = min(iSearchRange,MAXCOL - ((targetGridNo % MAXCOL) + 1));
|
||||
// determine maximum vertical limits
|
||||
sMaxUp = min(iSearchRange,(pSoldier->sGridNo / MAXROW));
|
||||
//NumMessage("sMaxUp = ",sMaxUp);
|
||||
sMaxDown = min(iSearchRange,MAXROW - ((pSoldier->sGridNo / MAXROW) + 1));
|
||||
//NumMessage("sMaxDown = ",sMaxDown);
|
||||
sMaxUp = min(iSearchRange,(targetGridNo / MAXROW));
|
||||
sMaxDown = min(iSearchRange,MAXROW - ((targetGridNo / MAXROW) + 1));
|
||||
|
||||
iRoamRange = RoamingRange(pSoldier,&sOrigin);
|
||||
|
||||
// if status isn't black (life & death combat), and roaming range is limited
|
||||
if ((pSoldier->aiData.bAlertStatus != STATUS_BLACK) && (iRoamRange < MAX_ROAMING_RANGE) &&
|
||||
//if ((pSoldier->aiData.bAlertStatus != STATUS_BLACK) && (iRoamRange < MAX_ROAMING_RANGE) &&
|
||||
if ((pSoldier->aiData.bAlertStatus < STATUS_RED) && (iRoamRange < MAX_ROAMING_RANGE) &&
|
||||
(!TileIsOutOfBounds(sOrigin)))
|
||||
{
|
||||
// must try to stay within or return to the point of origin
|
||||
iDistFromOrigin = SpacesAway(sOrigin,pSoldier->sGridNo);
|
||||
iDistFromOrigin = SpacesAway(sOrigin,targetGridNo);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -862,19 +845,6 @@ INT32 FindBestNearbyCover(SOLDIERTYPE *pSoldier, INT32 morale, INT32 *piPercentB
|
||||
}
|
||||
|
||||
|
||||
#ifdef DEBUGCOVER
|
||||
DebugAI( String( "FBNC: iRoamRange %d, sMaxLeft %d, sMaxRight %d, sMaxUp %d, sMaxDown %d\n",iRoamRange,sMaxLeft,sMaxRight,sMaxUp,sMaxDown) );
|
||||
#endif
|
||||
|
||||
// the initial cover value to beat is our current cover value
|
||||
iBestCoverValue = iCurrentCoverValue;
|
||||
|
||||
#ifdef DEBUGDECISIONS
|
||||
STR tempstr="";
|
||||
sprintf( tempstr, "FBNC: CURRENT iCoverValue = %d\n",iCurrentCoverValue );
|
||||
DebugAI( tempstr );
|
||||
#endif
|
||||
|
||||
if (pSoldier->aiData.bAlertStatus >= STATUS_RED) // if already in battle
|
||||
{
|
||||
// to speed this up, tell PathAI to cancel any paths beyond our AP reach!
|
||||
@@ -900,7 +870,7 @@ INT32 FindBestNearbyCover(SOLDIERTYPE *pSoldier, INT32 morale, INT32 *piPercentB
|
||||
{
|
||||
for (sXOffset = -sMaxLeft; sXOffset <= sMaxRight; sXOffset++)
|
||||
{
|
||||
sGridNo = pSoldier->sGridNo + sXOffset + (MAXCOL * sYOffset);
|
||||
sGridNo = targetGridNo + sXOffset + (MAXCOL * sYOffset);
|
||||
if ( !(sGridNo >=0 && sGridNo < WORLD_MAX) )
|
||||
{
|
||||
continue;
|
||||
@@ -908,22 +878,20 @@ INT32 FindBestNearbyCover(SOLDIERTYPE *pSoldier, INT32 morale, INT32 *piPercentB
|
||||
gpWorldLevelData[sGridNo].uiFlags &= ~(MAPELEMENT_REACHABLE);
|
||||
}
|
||||
}
|
||||
|
||||
FindBestPath( pSoldier, GRIDSIZE, pSoldier->pathing.bLevel, DetermineMovementMode( pSoldier, AI_ACTION_TAKE_COVER ), COPYREACHABLE_AND_APS, 0 );//dnl ch50 071009
|
||||
|
||||
// Turn off the "reachable" flag for his current location
|
||||
// so we don't consider it
|
||||
gpWorldLevelData[pSoldier->sGridNo].uiFlags &= ~(MAPELEMENT_REACHABLE);
|
||||
gpWorldLevelData[targetGridNo].uiFlags &= ~(MAPELEMENT_REACHABLE);
|
||||
|
||||
// SET UP DOUBLE-LOOP TO STEP THROUGH POTENTIAL GRID #s
|
||||
for (sYOffset = -sMaxUp; sYOffset <= sMaxDown; sYOffset++)
|
||||
{
|
||||
for (sXOffset = -sMaxLeft; sXOffset <= sMaxRight; sXOffset++)
|
||||
{
|
||||
//HandleMyMouseCursor(KEYBOARDALSO);
|
||||
|
||||
// calculate the next potential gridno
|
||||
sGridNo = pSoldier->sGridNo + sXOffset + (MAXCOL * sYOffset);
|
||||
sGridNo = targetGridNo + sXOffset + (MAXCOL * sYOffset);
|
||||
if ( !(sGridNo >=0 && sGridNo < WORLD_MAX) )
|
||||
{
|
||||
continue;
|
||||
@@ -980,7 +948,7 @@ INT32 FindBestNearbyCover(SOLDIERTYPE *pSoldier, INT32 morale, INT32 *piPercentB
|
||||
|
||||
// sevenfm: avoid moving into light
|
||||
if (InLightAtNight(sGridNo, pSoldier->pathing.bLevel) &&
|
||||
!InLightAtNight(pSoldier->sGridNo, pSoldier->pathing.bLevel) &&
|
||||
!InLightAtNight(targetGridNo, pSoldier->pathing.bLevel) &&
|
||||
!pSoldier->aiData.bUnderFire)
|
||||
{
|
||||
continue;
|
||||
@@ -989,12 +957,19 @@ INT32 FindBestNearbyCover(SOLDIERTYPE *pSoldier, INT32 morale, INT32 *piPercentB
|
||||
// avoid moving into red smoke
|
||||
if (gGameExternalOptions.fAIBetterCover &&
|
||||
RedSmokeDanger(sGridNo, pSoldier->pathing.bLevel) &&
|
||||
!RedSmokeDanger(pSoldier->sGridNo, pSoldier->pathing.bLevel))
|
||||
!RedSmokeDanger(targetGridNo, pSoldier->pathing.bLevel))
|
||||
{
|
||||
//DebugCover(pSoldier, String("moving into red smoke, skip"));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Avoid overcrowding
|
||||
const auto nearbyFriends = CountNearbyFriends(pSoldier, sGridNo, TACTICAL_RANGE_VERYCLOSE);
|
||||
if ( nearbyFriends > 1 && !pSoldier->IsZombie() )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// zombies only want to hide
|
||||
if (pSoldier->IsZombie() && !SightCoverAtSpot(pSoldier, sGridNo, TRUE))
|
||||
{
|
||||
@@ -1027,110 +1002,15 @@ INT32 FindBestNearbyCover(SOLDIERTYPE *pSoldier, INT32 morale, INT32 *piPercentB
|
||||
|
||||
// OK, this place shows potential. How useful is it as cover?
|
||||
// EVALUATE EACH GRID #, remembering the BEST PROTECTED ONE
|
||||
iCoverValue = 0;
|
||||
iCoverScale = 0;
|
||||
CalculateCoverValue(pSoldier, sGridNo, iPathCost, iMyThreatValue, uiThreatCnt, ubDiff, fNight, ubBackgroundLightPercent, morale, iCoverValue, iCoverScale);
|
||||
|
||||
// sevenfm: check sight cover
|
||||
fProneCover = TRUE;
|
||||
|
||||
// for every opponent that threatens, consider this spot's cover vs. him
|
||||
for (uiLoop = 0; uiLoop < uiThreatCnt; uiLoop++)
|
||||
if (gRenderDebugInfoMode == DEBUG_COVERVALUE && DEBUG_CHEAT_LEVEL())
|
||||
{
|
||||
// calculate the range we would be at from this opponent
|
||||
iThreatRange = GetRangeInCellCoordsFromGridNoDiff( sGridNo, Threat[uiLoop].sGridNo );
|
||||
// if this threat would be within 20 tiles, count it
|
||||
if (iThreatRange <= MAX_THREAT_RANGE)
|
||||
{
|
||||
iCoverValue += CalcCoverValue(pSoldier,sGridNo,iMyThreatValue,
|
||||
(pSoldier->bActionPoints - iPathCost),
|
||||
uiLoop,iThreatRange,morale,&iCoverScale);
|
||||
}
|
||||
|
||||
// sevenfm: sight test
|
||||
if( gGameExternalOptions.fAIBetterCover )
|
||||
{
|
||||
if ( LocationToLocationLineOfSightTest( Threat[uiLoop].sGridNo, Threat[uiLoop].pOpponent->pathing.bLevel, sGridNo, pSoldier->pathing.bLevel, TRUE, MAX_VISION_RANGE, STANDING_LOS_POS, PRONE_LOS_POS ) )
|
||||
//if ( SoldierToVirtualSoldierLineOfSightTest( Threat[uiLoop].pOpponent, sGridNo, pSoldier->pathing.bLevel, ANIM_PRONE, TRUE, -1 ) != 0 )
|
||||
{
|
||||
fProneCover = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
//sprintf(tempstr,"iCoverValue after opponent %d is now %d",iLoop,iCoverValue);
|
||||
//PopMessage(tempstr);
|
||||
gRenderDebugInfoValues[sGridNo] = (INT32) (iCoverValue / 100);
|
||||
}
|
||||
|
||||
// reduce cover for each person adjacent to this gridno who is on our team,
|
||||
// by 10% (so locations next to several people will be very much frowned upon
|
||||
if ( iCoverValue >= 0 )
|
||||
{
|
||||
iCoverValue -= (iCoverValue / 10) * NumberOfTeamMatesAdjacent( pSoldier, sGridNo );
|
||||
}
|
||||
else
|
||||
{
|
||||
// when negative, must add a negative to decrease the total
|
||||
iCoverValue += (iCoverValue / 10) * NumberOfTeamMatesAdjacent( pSoldier, sGridNo );
|
||||
}
|
||||
|
||||
if( gGameExternalOptions.fAIBetterCover )
|
||||
{
|
||||
// sevenfm: when defending (range change <= 3), prefer locations with sight cover
|
||||
if( RangeChangeDesire(pSoldier) < 4 )
|
||||
{
|
||||
if( fProneCover )
|
||||
iCoverValue += abs(iCoverValue) / __max(2, 2*RangeChangeDesire(pSoldier));
|
||||
}
|
||||
|
||||
// sevenfm: check for nearby friends in 10 radius, add bonus/penalty 10%
|
||||
ubNearbyFriends = __min(5, CountNearbyFriends( pSoldier, sGridNo, 5 ));
|
||||
iCoverValue -= ubNearbyFriends * abs(iCoverValue) / (10-ubDiff);
|
||||
|
||||
// sevenfm: penalize locations with fresh corpses
|
||||
if(GetNearestRottingCorpseAIWarning( sGridNo ) > 0)
|
||||
{
|
||||
iCoverValue -= abs(iCoverValue) / (8-ubDiff);
|
||||
}
|
||||
|
||||
// sevenfm: penalize locations near red smoke
|
||||
iCoverValue -= abs(iCoverValue) * RedSmokeDanger(sGridNo, pSoldier->pathing.bLevel) / 100;
|
||||
}
|
||||
|
||||
if ( fNight && !( InARoom( sGridNo, NULL ) ) ) // ignore in buildings in case placed there
|
||||
{
|
||||
// reduce cover at nighttime based on how bright the light is at that location
|
||||
// using the difference in sighting distance between the background and the
|
||||
// light for this tile
|
||||
//ubLightPercentDifference = (gbLightSighting[ 0 ][ LightTrueLevel( sGridNo, pSoldier->pathing.bLevel ) ] - ubBackgroundLightPercent );
|
||||
ubLightPercentDifference = (gGameExternalOptions.ubBrightnessVisionMod[ LightTrueLevel( sGridNo, pSoldier->pathing.bLevel ) ] - ubBackgroundLightPercent );
|
||||
|
||||
if ( iCoverValue >= 0 )
|
||||
{
|
||||
iCoverValue -= (iCoverValue / 100) * ubLightPercentDifference;
|
||||
}
|
||||
else
|
||||
{
|
||||
iCoverValue += (iCoverValue / 100) * ubLightPercentDifference;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef DEBUGCOVER
|
||||
// if there ARE multiple opponents
|
||||
if (uiThreatCnt > 1)
|
||||
{
|
||||
DebugAI( String( "FBNC: Total iCoverValue at gridno %d is %d\n\n",sGridNo,iCoverValue ) );
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined( _DEBUG ) && defined( COVER_DEBUG )
|
||||
if (gfDisplayCoverValues)
|
||||
{
|
||||
gsCoverValue[sGridNo] = (INT16) (iCoverValue / 100);
|
||||
}
|
||||
#endif
|
||||
|
||||
// if this is better than the best place found so far
|
||||
|
||||
if (iCoverValue > iBestCoverValue)
|
||||
{
|
||||
// ONLY DO THIS CHECK HERE IF WE'RE WAITING FOR OPPCHANCETODECIDE,
|
||||
@@ -1148,11 +1028,6 @@ INT32 FindBestNearbyCover(SOLDIERTYPE *pSoldier, INT32 morale, INT32 *piPercentB
|
||||
}
|
||||
*/
|
||||
|
||||
#ifdef DEBUGDECISIONS
|
||||
STR tempstr;
|
||||
sprintf( tempstr,"FBNC: NEW BEST iCoverValue at gridno %d is %d\n",sGridNo,iCoverValue );
|
||||
DebugAI( tempstr );
|
||||
#endif
|
||||
// remember it instead
|
||||
sBestCover = sGridNo;
|
||||
iBestCoverValue = iCoverValue;
|
||||
@@ -1164,34 +1039,15 @@ INT32 FindBestNearbyCover(SOLDIERTYPE *pSoldier, INT32 morale, INT32 *piPercentB
|
||||
gubNPCAPBudget = 0;
|
||||
gubNPCDistLimit = 0;
|
||||
|
||||
#if defined( _DEBUG ) && !defined( PATHAI_VISIBLE_DEBUG )
|
||||
if (gfDisplayCoverValues)
|
||||
if (gRenderDebugInfoMode == DEBUG_COVERVALUE && DEBUG_CHEAT_LEVEL())
|
||||
{
|
||||
// do a locate?
|
||||
LocateSoldier( pSoldier->ubID, SETLOCATORFAST );
|
||||
gsBestCover = sBestCover;
|
||||
SetRenderFlags( RENDER_FLAG_FULL );
|
||||
RenderWorld();
|
||||
RenderCoverDebug( );
|
||||
InvalidateScreen( );
|
||||
EndFrameBufferRender();
|
||||
RefreshScreen( NULL );
|
||||
/*
|
||||
iLoop = GetJA2Clock();
|
||||
do
|
||||
{
|
||||
|
||||
} while( ( GetJA2Clock( ) - iLoop ) < 2000 );
|
||||
*/
|
||||
InvalidateRegion(gsVIEWPORT_START_X, gsVIEWPORT_START_Y, gsVIEWPORT_END_X, gsVIEWPORT_WINDOW_END_Y);
|
||||
}
|
||||
#endif
|
||||
|
||||
// if a better cover location was found
|
||||
if (!TileIsOutOfBounds(sBestCover))
|
||||
{
|
||||
#if defined( _DEBUG ) && !defined( PATHAI_VISIBLE_DEBUG )
|
||||
gsBestCover = sBestCover;
|
||||
#endif
|
||||
// cover values already take the AP cost of getting there into account in
|
||||
// a BIG way, so no need to worry about that here, even small improvements
|
||||
// are actually very significant once we get our APs back (if we live!)
|
||||
@@ -1200,16 +1056,6 @@ INT32 FindBestNearbyCover(SOLDIERTYPE *pSoldier, INT32 morale, INT32 *piPercentB
|
||||
// if best cover value found was at least 5% better than our current cover
|
||||
if (*piPercentBetter >= MIN_PERCENT_BETTER)
|
||||
{
|
||||
#ifdef DEBUGDECISIONS
|
||||
STR tempstr;
|
||||
sprintf( tempstr,"Found Cover: current %ld, best %ld, %%%%Better %ld\n", iCurrentCoverValue,iBestCoverValue,*piPercentBetter );
|
||||
DebugAI( tempstr );
|
||||
#endif
|
||||
|
||||
#ifdef BETAVERSION
|
||||
SnuggleDebug(pSoldier,"Found Cover");
|
||||
#endif
|
||||
|
||||
return(sBestCover); // return the gridno of that cover
|
||||
}
|
||||
}
|
||||
@@ -3128,7 +2974,11 @@ INT32 FindAdvanceSpot(SOLDIERTYPE *pSoldier, INT32 sTargetSpot, INT8 bAction, UI
|
||||
{
|
||||
if (usMovementMode == RUNNING || usMovementMode == WALKING)
|
||||
{
|
||||
ubReserveAP = APBPConstants[AP_CHANGE_FACING] + GetAPsCrouch(pSoldier, TRUE) + GetAPsProne(pSoldier, TRUE);
|
||||
ubReserveAP = APBPConstants[AP_CHANGE_FACING] + GetAPsCrouch(pSoldier, TRUE) +GetAPsProne(pSoldier, TRUE);
|
||||
if (pSoldier->bActionPoints <= ubReserveAP)
|
||||
{
|
||||
ubReserveAP = APBPConstants[AP_CHANGE_FACING] + GetAPsCrouch(pSoldier, TRUE);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -3279,7 +3129,8 @@ INT32 FindAdvanceSpot(SOLDIERTYPE *pSoldier, INT32 sTargetSpot, INT8 bAction, UI
|
||||
}
|
||||
|
||||
// avoid overcrowding
|
||||
if (!pSoldier->IsZombie() && NumberOfTeamMatesAdjacent(pSoldier, sGridNo) > 1)
|
||||
const auto nearbyFriends = CountNearbyFriends(pSoldier, sGridNo, TACTICAL_RANGE_VERYCLOSE);
|
||||
if ( nearbyFriends > 1 && !pSoldier->IsZombie() )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
+107
-111
@@ -432,7 +432,7 @@ INT8 RandomPointPatrolAI(SOLDIERTYPE *pSoldier)
|
||||
INT32 InternalGoAsFarAsPossibleTowards(SOLDIERTYPE *pSoldier, INT32 sDesGrid, INT16 bReserveAPs, INT8 bAction, INT8 fFlags )
|
||||
{
|
||||
#ifdef DEBUGDECISIONS
|
||||
STR16 tempstr;
|
||||
STR16 tempstr;
|
||||
#endif
|
||||
INT32 sLoop,sAPCost;
|
||||
INT32 sTempDest,sGoToGrid;
|
||||
@@ -588,156 +588,152 @@ INT32 InternalGoAsFarAsPossibleTowards(SOLDIERTYPE *pSoldier, INT32 sDesGrid, IN
|
||||
}
|
||||
}
|
||||
|
||||
// HAVE FOUND AN OK destination AND PLOTTED A VALID BEST PATH TO IT
|
||||
// HAVE FOUND AN OK destination AND PLOTTED A VALID BEST PATH TO IT
|
||||
|
||||
|
||||
#ifdef DEBUGDECISIONS
|
||||
AINumMessage("Chosen legal destination is gridno ",sDesGrid);
|
||||
AINumMessage("Tracing along path, pathRouteToGo = ",pSoldier->pathing.usPathIndex);
|
||||
AINumMessage("Chosen legal destination is gridno ",sDesGrid);
|
||||
AINumMessage("Tracing along path, pathRouteToGo = ",pSoldier->pathing.usPathIndex);
|
||||
#endif
|
||||
|
||||
sGoToGrid = pSoldier->sGridNo; // start back where soldier is standing now
|
||||
sAPCost = 0; // initialize path cost counter
|
||||
sGoToGrid = pSoldier->sGridNo; // start back where soldier is standing now
|
||||
sAPCost = 0; // initialize path cost counter
|
||||
|
||||
// 0verhaul: If the destination is within the patrol route, allow it. This is necessary to allow an errant soldier
|
||||
// to return to his patrol route if flanking or other actions have pulled him beyond his allowed range from origin.
|
||||
if (SpacesAway(sOrigin,sDesGrid) <= usMaxDist)
|
||||
{
|
||||
fAllowDest = TRUE;
|
||||
}
|
||||
// 0verhaul: If the destination is within the patrol route, allow it. This is necessary to allow an errant soldier
|
||||
// to return to his patrol route if flanking or other actions have pulled him beyond his allowed range from origin.
|
||||
if (SpacesAway(sOrigin,sDesGrid) <= usMaxDist)
|
||||
{
|
||||
fAllowDest = TRUE;
|
||||
}
|
||||
|
||||
// we'll only go as far along the plotted route as is within our
|
||||
// permitted roaming range, and we'll stop as soon as we're down to <= 5 APs
|
||||
// we'll only go as far along the plotted route as is within our
|
||||
// permitted roaming range, and we'll stop as soon as we're down to <= 5 APs
|
||||
|
||||
sTempDest = pSoldier->sGridNo;
|
||||
|
||||
for (sLoop = 0; sLoop < (pSoldier->pathing.usPathDataSize - pSoldier->pathing.usPathIndex); sLoop++)
|
||||
for (sLoop = 0; sLoop < (pSoldier->pathing.usPathDataSize - pSoldier->pathing.usPathIndex); sLoop++)
|
||||
{
|
||||
// what is the next gridno in the path?
|
||||
// what is the next gridno in the path?
|
||||
|
||||
//sTempDest = NewGridNo( sGoToGrid,DirectionInc( (INT16) (pSoldier->pathing.usPathingData[sLoop] + 1) ) );
|
||||
//sTempDest = NewGridNo( sGoToGrid,DirectionInc( (UINT8) (pSoldier->pathing.usPathingData[sLoop]) ) );
|
||||
sTempDest = NewGridNo( sTempDest,DirectionInc( (UINT8) (pSoldier->pathing.usPathingData[sLoop]) ) );
|
||||
//sTempDest = NewGridNo( sGoToGrid,DirectionInc( (INT16) (pSoldier->pathing.usPathingData[sLoop] + 1) ) );
|
||||
//sTempDest = NewGridNo( sGoToGrid,DirectionInc( (UINT8) (pSoldier->pathing.usPathingData[sLoop]) ) );
|
||||
sTempDest = NewGridNo( sTempDest,DirectionInc( (UINT8) (pSoldier->pathing.usPathingData[sLoop]) ) );
|
||||
|
||||
// this should NEVER be out of bounds
|
||||
if (sTempDest == sGoToGrid)
|
||||
{
|
||||
// this should NEVER be out of bounds
|
||||
if (sTempDest == sGoToGrid)
|
||||
{
|
||||
#ifdef BETAVERSION
|
||||
sprintf(tempstr,"GoAsFarAsPossibleTowards: ERROR - gridno along valid route is invalid! guynum %d, sTempDest = %d",pSoldier->ubID,sTempDest);
|
||||
sprintf(tempstr,"GoAsFarAsPossibleTowards: ERROR - gridno along valid route is invalid! guynum %d, sTempDest = %d",pSoldier->ubID,sTempDest);
|
||||
|
||||
#ifdef RECORDNET
|
||||
fprintf(NetDebugFile,"\n\t%s\n",tempstr);
|
||||
fprintf(NetDebugFile,"\n\t%s\n",tempstr);
|
||||
#endif
|
||||
|
||||
PopMessage(tempstr);
|
||||
SaveGame(ERROR_SAVE);
|
||||
PopMessage(tempstr);
|
||||
SaveGame(ERROR_SAVE);
|
||||
#endif
|
||||
|
||||
break; // quit here, sGoToGrid is where we are going
|
||||
}
|
||||
|
||||
// if this takes us beyond our permitted "roaming range"
|
||||
// but if it brings us closer, then allow it!
|
||||
if (SpacesAway(sOrigin,sTempDest) > usMaxDist && !fAllowDest)
|
||||
break; // quit here, sGoToGrid is where we are going
|
||||
|
||||
|
||||
if ( usRoomRequired )
|
||||
{
|
||||
if ( !( InARoom( sTempDest, &usTempRoom ) && usTempRoom == usRoomRequired ) )
|
||||
{
|
||||
// quit here, limited by room!
|
||||
break;
|
||||
break; // quit here, sGoToGrid is where we are going
|
||||
}
|
||||
}
|
||||
|
||||
if ( (fFlags & FLAG_STOPSHORT) && SpacesAway( sDesGrid, sTempDest ) <= STOPSHORTDIST )
|
||||
{
|
||||
break; // quit here, sGoToGrid is where we are going
|
||||
}
|
||||
// if this takes us beyond our permitted "roaming range"
|
||||
// but if it brings us closer, then allow it!
|
||||
if (SpacesAway(sOrigin,sTempDest) > usMaxDist && !fAllowDest)
|
||||
break; // quit here, sGoToGrid is where we are going
|
||||
|
||||
// CAN'T CALL PathCost() HERE! IT CALLS findBestPath() and overwrites
|
||||
// pathRouteToGo !!! Gotta calculate the cost ourselves - Ian
|
||||
//
|
||||
//ubAPsLeft = pSoldier->bActionPoints - PathCost(pSoldier,sTempDest,FALSE,FALSE,FALSE,FALSE,FALSE);
|
||||
|
||||
if (gfTurnBasedAI)
|
||||
{
|
||||
// if we're just starting the "costing" process (first gridno)
|
||||
if (sLoop == 0)
|
||||
{
|
||||
if (pSoldier->usUIMovementMode == RUNNING)
|
||||
{
|
||||
sAPCost += GetAPsStartRun( pSoldier ); // changed by SANDRO
|
||||
}
|
||||
}
|
||||
|
||||
// ATE: Direction here?
|
||||
sAPCost += EstimateActionPointCost( pSoldier, sTempDest, (INT8) pSoldier->pathing.usPathingData[sLoop], pSoldier->usUIMovementMode, (INT8) sLoop, (INT8) pSoldier->pathing.usPathDataSize );
|
||||
|
||||
bAPsLeft = pSoldier->bActionPoints - sAPCost;
|
||||
}
|
||||
|
||||
// if this gridno is NOT a legal NPC destination
|
||||
// DONT'T test path again - that would replace the traced path! - Ian
|
||||
// NOTE: It's OK to go *THROUGH* water to try and get to the destination!
|
||||
// sevenfm: jump over fence code - if current gridno is not legal destination, check next gridno
|
||||
if (!LegalNPCDestination(pSoldier,sTempDest,IGNORE_PATH,WATEROK,0))
|
||||
{
|
||||
// break;
|
||||
continue;
|
||||
}
|
||||
|
||||
// if after this, we have <= 5 APs remaining, that's far enough, break out
|
||||
// (the idea is to preserve APs so we can crouch or react if
|
||||
// necessary, and benefit from the carry-over next turn if not needed)
|
||||
// This routine is NOT used by any GREEN AI, so such caution is warranted!
|
||||
|
||||
if ( gfTurnBasedAI && (bAPsLeft < bReserveAPs) )
|
||||
break;
|
||||
else
|
||||
if ( usRoomRequired )
|
||||
{
|
||||
sGoToGrid = sTempDest; // we're OK up to here
|
||||
if ( !( InARoom( sTempDest, &usTempRoom ) && usTempRoom == usRoomRequired ) )
|
||||
{
|
||||
// quit here, limited by room!
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// if exactly 5 APs left, don't bother checking any further
|
||||
if ( gfTurnBasedAI && (bAPsLeft == bReserveAPs) )
|
||||
if ( (fFlags & FLAG_STOPSHORT) && SpacesAway( sDesGrid, sTempDest ) <= STOPSHORTDIST )
|
||||
{
|
||||
break; // quit here, sGoToGrid is where we are going
|
||||
}
|
||||
|
||||
// CAN'T CALL PathCost() HERE! IT CALLS findBestPath() and overwrites
|
||||
// pathRouteToGo !!! Gotta calculate the cost ourselves - Ian
|
||||
//
|
||||
//ubAPsLeft = pSoldier->bActionPoints - PathCost(pSoldier,sTempDest,FALSE,FALSE,FALSE,FALSE,FALSE);
|
||||
|
||||
if (gfTurnBasedAI)
|
||||
{
|
||||
// if we're just starting the "costing" process (first gridno)
|
||||
if (sLoop == 0)
|
||||
{
|
||||
if (pSoldier->usUIMovementMode == RUNNING)
|
||||
{
|
||||
sAPCost += GetAPsStartRun( pSoldier ); // changed by SANDRO
|
||||
}
|
||||
}
|
||||
|
||||
// ATE: Direction here?
|
||||
sAPCost += EstimateActionPointCost( pSoldier, sTempDest, (INT8) pSoldier->pathing.usPathingData[sLoop], pSoldier->usUIMovementMode, (INT8) sLoop, (INT8) pSoldier->pathing.usPathDataSize );
|
||||
bAPsLeft = pSoldier->bActionPoints - sAPCost;
|
||||
}
|
||||
|
||||
// if this gridno is NOT a legal NPC destination
|
||||
// DONT'T test path again - that would replace the traced path! - Ian
|
||||
// NOTE: It's OK to go *THROUGH* water to try and get to the destination!
|
||||
// sevenfm: jump over fence code - if current gridno is not legal destination, check next gridno
|
||||
if (!LegalNPCDestination(pSoldier,sTempDest,IGNORE_PATH,WATEROK,0))
|
||||
{
|
||||
// break;
|
||||
continue;
|
||||
}
|
||||
|
||||
// if after this, we have <= 5 APs remaining, that's far enough, break out
|
||||
// (the idea is to preserve APs so we can crouch or react if
|
||||
// necessary, and benefit from the carry-over next turn if not needed)
|
||||
// This routine is NOT used by any GREEN AI, so such caution is warranted!
|
||||
|
||||
if ( gfTurnBasedAI && (bAPsLeft < bReserveAPs) )
|
||||
break;
|
||||
else
|
||||
{
|
||||
sGoToGrid = sTempDest; // we're OK up to here
|
||||
|
||||
// if exactly 5 APs left, don't bother checking any further
|
||||
if ( gfTurnBasedAI && (bAPsLeft == bReserveAPs) )
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// if it turned out we couldn't go even 1 tile towards the desired gridno
|
||||
if (sGoToGrid == pSoldier->sGridNo)
|
||||
// if it turned out we couldn't go even 1 tile towards the desired gridno
|
||||
if (sGoToGrid == pSoldier->sGridNo)
|
||||
{
|
||||
#ifdef DEBUGDECISIONS
|
||||
sprintf(tempstr,"%s will go NOWHERE, path doesn't meet criteria",pSoldier->name);
|
||||
AIPopMessage(tempstr);
|
||||
sprintf(tempstr,"%s will go NOWHERE, path doesn't meet criteria",pSoldier->name);
|
||||
AIPopMessage(tempstr);
|
||||
#endif
|
||||
|
||||
pSoldier->pathing.usPathIndex = pSoldier->pathing.usPathDataSize = 0;
|
||||
return(NOWHERE); // then go nowhere
|
||||
pSoldier->pathing.usPathIndex = pSoldier->pathing.usPathDataSize = 0;
|
||||
return(NOWHERE); // then go nowhere
|
||||
}
|
||||
else
|
||||
{
|
||||
// possible optimization - stored path IS good if we're going all the way
|
||||
if (sGoToGrid == sDesGrid)
|
||||
else
|
||||
{
|
||||
pSoldier->pathing.bPathStored = TRUE;
|
||||
pSoldier->pathing.sFinalDestination = sGoToGrid;
|
||||
}
|
||||
else if ( pSoldier->pathing.usPathIndex == 0 )
|
||||
{
|
||||
// we can hack this surely! -- CJC
|
||||
pSoldier->pathing.bPathStored = TRUE;
|
||||
pSoldier->pathing.sFinalDestination = sGoToGrid;
|
||||
pSoldier->pathing.usPathDataSize = sLoop + 1;
|
||||
}
|
||||
// possible optimization - stored path IS good if we're going all the way
|
||||
if (sGoToGrid == sDesGrid)
|
||||
{
|
||||
pSoldier->pathing.bPathStored = TRUE;
|
||||
pSoldier->pathing.sFinalDestination = sGoToGrid;
|
||||
}
|
||||
else if ( pSoldier->pathing.usPathIndex == 0 )
|
||||
{
|
||||
// we can hack this surely! -- CJC
|
||||
pSoldier->pathing.bPathStored = TRUE;
|
||||
pSoldier->pathing.sFinalDestination = sGoToGrid;
|
||||
pSoldier->pathing.usPathDataSize = sLoop + 1;
|
||||
}
|
||||
|
||||
#ifdef DEBUGDECISIONS
|
||||
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_BETAVERSION, L"%d to %d with %d APs left", pSoldier->ubID, sGoToGrid, pSoldier->bActionPoints );
|
||||
#endif
|
||||
|
||||
|
||||
return( sGoToGrid );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -244,7 +244,7 @@ void PossiblyMakeThisEnemyChosenOne( SOLDIERTYPE * pSoldier )
|
||||
}
|
||||
|
||||
|
||||
INT8 PanicAI(SOLDIERTYPE *pSoldier, UINT8 ubCanMove)
|
||||
ActionType PanicAI(SOLDIERTYPE *pSoldier, UINT8 ubCanMove)
|
||||
{
|
||||
BOOLEAN fFoundRoute = FALSE;
|
||||
INT8 bSlot;
|
||||
@@ -307,7 +307,7 @@ INT8 PanicAI(SOLDIERTYPE *pSoldier, UINT8 ubCanMove)
|
||||
if (bPanicTrigger == -1)
|
||||
{
|
||||
// augh!
|
||||
return( -1 );
|
||||
return( AI_ACTION_INVALID );
|
||||
}
|
||||
|
||||
sPanicTriggerGridNo = gTacticalStatus.sPanicTriggerGridNo[ bPanicTrigger ];
|
||||
@@ -430,7 +430,7 @@ INT8 PanicAI(SOLDIERTYPE *pSoldier, UINT8 ubCanMove)
|
||||
}
|
||||
|
||||
// no action decided
|
||||
return(-1);
|
||||
return(AI_ACTION_INVALID);
|
||||
}
|
||||
|
||||
void InitPanicSystem( void )
|
||||
@@ -534,7 +534,7 @@ BOOLEAN NeedToRadioAboutPanicTrigger( void )
|
||||
#define STAIRCASE_GRIDNO 12067
|
||||
#define STAIRCASE_DIRECTION 0
|
||||
|
||||
INT8 HeadForTheStairCase( SOLDIERTYPE * pSoldier )
|
||||
ActionType HeadForTheStairCase( SOLDIERTYPE * pSoldier )
|
||||
{
|
||||
UNDERGROUND_SECTORINFO * pBasementInfo;
|
||||
|
||||
|
||||
@@ -0,0 +1,372 @@
|
||||
#include "UtilityAI.h"
|
||||
#include "UtilityAI_ResponseCurve.h"
|
||||
#include "Overhead Types.h"
|
||||
#include "Soldier Control.h"
|
||||
#include "Isometric Utils.h"
|
||||
#include "soldier profile type.h"
|
||||
#include "AIInternals.h"
|
||||
#include "Render Fun.h"
|
||||
|
||||
namespace UtilityAI
|
||||
{
|
||||
|
||||
typedef enum DecisionInput
|
||||
{
|
||||
MyHealth,
|
||||
MyStamina,
|
||||
MyStance,
|
||||
MyMorale,
|
||||
MyCurrentWeaponRange,
|
||||
DistanceFromMe,
|
||||
HealthOfTarget,
|
||||
AllyCount,
|
||||
EnemyCount,
|
||||
AllyEnemyRatio,
|
||||
EnemiesNearTarget,
|
||||
AlliesNearTarget,
|
||||
HaveGasmaskInInventory,
|
||||
HaveNVGInInventory,
|
||||
HasKnife,
|
||||
HasThrowingKnife,
|
||||
HasSmokeGrenade,
|
||||
IamInSmoke,
|
||||
IamInTearGas,
|
||||
IamInMustardGas, // Also fire & creature gas
|
||||
NearSmoke,
|
||||
NearTearGas,
|
||||
NearMustardGas,
|
||||
InShock,
|
||||
InsideRoom,
|
||||
NightTime,
|
||||
InLightAtNight,
|
||||
TakenLargeHit,
|
||||
HasTrait_AutoWeapons,
|
||||
HasTrait_HeavyWeapons,
|
||||
HasTrait_Sniper,
|
||||
HasTrait_Ranger,
|
||||
HasTrait_Gunslinger,
|
||||
HasTrait_MartialArts,
|
||||
HasTrait_SquadLeader,
|
||||
HasTrait_Technician,
|
||||
HasTrait_Doctor,
|
||||
HasTrait_Ambidextrous,
|
||||
HasTrait_Melee,
|
||||
HasTrait_Throwing,
|
||||
HasTrait_NightOps,
|
||||
HasTrait_Stealthy,
|
||||
HasTrait_Athletics,
|
||||
HasTrait_Bodybuilding,
|
||||
HasTrait_Demolitions,
|
||||
HasTrait_Teaching,
|
||||
HasTrait_Scouting,
|
||||
HasTrait_Covert,
|
||||
HasTrait_RadioOperator,
|
||||
HasTrait_Snitch,
|
||||
HasTrait_Survival,
|
||||
HasTraitOld_Lockpicking,
|
||||
HasTraitOld_HandToHand,
|
||||
HasTraitOld_Electronics,
|
||||
HasTraitOld_NightOps,
|
||||
HasTraitOld_Throwing,
|
||||
HasTraitOld_Teaching,
|
||||
HasTraitOld_HeavyWeapons,
|
||||
HasTraitOld_AutoWeapons,
|
||||
HasTraitOld_Stealthy,
|
||||
HasTraitOld_Ambidextrous,
|
||||
HasTraitOld_Thief,
|
||||
HasTraitOld_MartialArts,
|
||||
HasTraitOld_Knifing,
|
||||
HasTraitOld_Sniper,
|
||||
HasTraitOld_Camouflaged,
|
||||
|
||||
};
|
||||
|
||||
struct Consideration
|
||||
{
|
||||
DecisionInput input;
|
||||
ResponseCurve curve;
|
||||
SoldierID target;
|
||||
INT32 targetLocation;
|
||||
};
|
||||
|
||||
struct DecisionScoreEvaluator
|
||||
{
|
||||
ActionType Action;
|
||||
float priorityWeight;
|
||||
std::string description;
|
||||
std::vector<Consideration> considerations;
|
||||
};
|
||||
|
||||
struct DecisionScore
|
||||
{
|
||||
float score;
|
||||
size_t idx; // Index into DM::decisions
|
||||
|
||||
bool operator<(const DecisionScore& a) const
|
||||
{
|
||||
return score < a.score;
|
||||
}
|
||||
|
||||
bool operator>(const DecisionScore& a) const
|
||||
{
|
||||
return score > a.score;
|
||||
}
|
||||
|
||||
bool operator==(const DecisionScore& a) const
|
||||
{
|
||||
return score == a.score;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
struct DecisionMaker
|
||||
{
|
||||
std::vector<DecisionScoreEvaluator> decisions;
|
||||
std::vector<DecisionScore> scores;
|
||||
};
|
||||
|
||||
static float GetTrait(DecisionInput input, SOLDIERTYPE* soldier)
|
||||
{
|
||||
switch ( input )
|
||||
{
|
||||
case HasTrait_AutoWeapons:
|
||||
return static_cast<float>(HAS_SKILL_TRAIT(soldier, AUTO_WEAPONS_NT));
|
||||
case HasTrait_HeavyWeapons:
|
||||
return static_cast<float>(HAS_SKILL_TRAIT(soldier, HEAVY_WEAPONS_NT));
|
||||
case HasTrait_Sniper:
|
||||
return static_cast<float>(HAS_SKILL_TRAIT(soldier, SNIPER_NT));
|
||||
case HasTrait_Ranger:
|
||||
return static_cast<float>(HAS_SKILL_TRAIT(soldier, RANGER_NT));
|
||||
case HasTrait_Gunslinger:
|
||||
return static_cast<float>(HAS_SKILL_TRAIT(soldier, GUNSLINGER_NT));
|
||||
case HasTrait_MartialArts:
|
||||
return static_cast<float>(HAS_SKILL_TRAIT(soldier, MARTIAL_ARTS_NT));
|
||||
case HasTrait_SquadLeader:
|
||||
return static_cast<float>(HAS_SKILL_TRAIT(soldier, SQUADLEADER_NT));
|
||||
case HasTrait_Technician:
|
||||
return static_cast<float>(HAS_SKILL_TRAIT(soldier, TECHNICIAN_NT));
|
||||
case HasTrait_Doctor:
|
||||
return static_cast<float>(HAS_SKILL_TRAIT(soldier, DOCTOR_NT));
|
||||
case HasTrait_Ambidextrous:
|
||||
return static_cast<float>(HAS_SKILL_TRAIT(soldier, AMBIDEXTROUS_NT));
|
||||
case HasTrait_Melee:
|
||||
return static_cast<float>(HAS_SKILL_TRAIT(soldier, MELEE_NT));
|
||||
case HasTrait_Throwing:
|
||||
return static_cast<float>(HAS_SKILL_TRAIT(soldier, THROWING_NT));
|
||||
case HasTrait_NightOps:
|
||||
return static_cast<float>(HAS_SKILL_TRAIT(soldier, NIGHT_OPS_NT));
|
||||
case HasTrait_Stealthy:
|
||||
return static_cast<float>(HAS_SKILL_TRAIT(soldier, STEALTHY_NT));
|
||||
case HasTrait_Athletics:
|
||||
return static_cast<float>(HAS_SKILL_TRAIT(soldier, ATHLETICS_NT));
|
||||
case HasTrait_Bodybuilding:
|
||||
return static_cast<float>(HAS_SKILL_TRAIT(soldier, BODYBUILDING_NT));
|
||||
case HasTrait_Demolitions:
|
||||
return static_cast<float>(HAS_SKILL_TRAIT(soldier, DEMOLITIONS_NT));
|
||||
case HasTrait_Teaching:
|
||||
return static_cast<float>(HAS_SKILL_TRAIT(soldier, TEACHING_NT));
|
||||
case HasTrait_Scouting:
|
||||
return static_cast<float>(HAS_SKILL_TRAIT(soldier, SCOUTING_NT));
|
||||
case HasTrait_Covert:
|
||||
return static_cast<float>(HAS_SKILL_TRAIT(soldier, COVERT_NT));
|
||||
case HasTrait_RadioOperator:
|
||||
return static_cast<float>(HAS_SKILL_TRAIT(soldier, RADIO_OPERATOR_NT));
|
||||
case HasTrait_Snitch:
|
||||
return static_cast<float>(HAS_SKILL_TRAIT(soldier, SNITCH_NT));
|
||||
case HasTrait_Survival:
|
||||
return static_cast<float>(HAS_SKILL_TRAIT(soldier, SURVIVAL_NT));
|
||||
case HasTraitOld_Lockpicking:
|
||||
return static_cast<float>(HAS_SKILL_TRAIT(soldier, LOCKPICKING_OT));
|
||||
case HasTraitOld_HandToHand:
|
||||
return static_cast<float>(HAS_SKILL_TRAIT(soldier, HANDTOHAND_OT));
|
||||
case HasTraitOld_Electronics:
|
||||
return static_cast<float>(HAS_SKILL_TRAIT(soldier, ELECTRONICS_OT));
|
||||
case HasTraitOld_NightOps:
|
||||
return static_cast<float>(HAS_SKILL_TRAIT(soldier, NIGHTOPS_OT));
|
||||
case HasTraitOld_Throwing:
|
||||
return static_cast<float>(HAS_SKILL_TRAIT(soldier, THROWING_OT));
|
||||
case HasTraitOld_Teaching:
|
||||
return static_cast<float>(HAS_SKILL_TRAIT(soldier, TEACHING_OT));
|
||||
case HasTraitOld_HeavyWeapons:
|
||||
return static_cast<float>(HAS_SKILL_TRAIT(soldier, HEAVY_WEAPS_OT));
|
||||
case HasTraitOld_AutoWeapons:
|
||||
return static_cast<float>(HAS_SKILL_TRAIT(soldier, AUTO_WEAPS_OT));
|
||||
case HasTraitOld_Stealthy:
|
||||
return static_cast<float>(HAS_SKILL_TRAIT(soldier, STEALTHY_OT));
|
||||
case HasTraitOld_Ambidextrous:
|
||||
return static_cast<float>(HAS_SKILL_TRAIT(soldier, AMBIDEXT_OT));
|
||||
case HasTraitOld_Thief:
|
||||
return static_cast<float>(HAS_SKILL_TRAIT(soldier, THIEF_OT));
|
||||
case HasTraitOld_MartialArts:
|
||||
return static_cast<float>(HAS_SKILL_TRAIT(soldier, MARTIALARTS_OT));
|
||||
case HasTraitOld_Knifing:
|
||||
return static_cast<float>(HAS_SKILL_TRAIT(soldier, KNIFING_OT));
|
||||
case HasTraitOld_Sniper:
|
||||
return static_cast<float>(HAS_SKILL_TRAIT(soldier, PROF_SNIPER_OT));
|
||||
case HasTraitOld_Camouflaged:
|
||||
return static_cast<float>(HAS_SKILL_TRAIT(soldier, CAMOUFLAGED_OT));
|
||||
}
|
||||
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
float GetInputValue(DecisionInput input, SOLDIERTYPE* me, SoldierID target, INT32 targetLocation)
|
||||
{
|
||||
switch ( input )
|
||||
{
|
||||
case MyHealth:
|
||||
{
|
||||
const auto health = static_cast<float>(me->stats.bLife);
|
||||
const auto min = 0.0f;
|
||||
const auto max = static_cast<float>(me->stats.bLifeMax);
|
||||
return NormalizeInput(health, min, max);
|
||||
}
|
||||
case MyMorale:
|
||||
{
|
||||
const auto morale = static_cast<float>(me->aiData.bAIMorale);
|
||||
const auto min = 0.0f;
|
||||
const auto max = static_cast<float>(MORALE_FEARLESS);
|
||||
return NormalizeInput(morale, min, max);
|
||||
}
|
||||
case HaveGasmaskInInventory:
|
||||
{
|
||||
const auto gasmaskInvSlot = FindGasMask(me);
|
||||
if ( gasmaskInvSlot != NO_SLOT ) { return 1.0f; }
|
||||
else { return 0.0f; }
|
||||
}
|
||||
case HasKnife:
|
||||
{
|
||||
const auto bWeaponIn = FindAIUsableObjClass(me, (IC_BLADE));
|
||||
if ( bWeaponIn != NO_SLOT ) { return 1.0f; }
|
||||
else { return 0.0f; }
|
||||
}
|
||||
case HasThrowingKnife:
|
||||
{
|
||||
const auto bWeaponIn = FindAIUsableObjClass(me, (IC_THROWING_KNIFE));
|
||||
if ( bWeaponIn != NO_SLOT ) { return 1.0f; }
|
||||
else { return 0.0f; }
|
||||
}
|
||||
case HasSmokeGrenade:
|
||||
{
|
||||
const auto bGrenadeIn = FindThrowableGrenade(me, EXPLOSV_SMOKE);
|
||||
if ( bGrenadeIn != NO_SLOT ) { return 1.0f; }
|
||||
else { return 0.0f; }
|
||||
}
|
||||
case DistanceFromMe:
|
||||
{
|
||||
const auto distance = PythSpacesAway(me->sGridNo, targetLocation);
|
||||
}
|
||||
case IamInSmoke:
|
||||
return static_cast<float>(InSmoke(me, me->sGridNo));
|
||||
case IamInTearGas:
|
||||
return static_cast<float>(InTearGas(me, me->sGridNo));
|
||||
case IamInMustardGas:
|
||||
return static_cast<float>(InMustardGas(me, me->sGridNo));
|
||||
case NearSmoke:
|
||||
|
||||
case NearTearGas:
|
||||
|
||||
case NearMustardGas:
|
||||
|
||||
case InShock:
|
||||
|
||||
case InsideRoom:
|
||||
return static_cast<float>(InARoom(me->sGridNo, NULL));
|
||||
case NightTime:
|
||||
|
||||
case InLightAtNight:
|
||||
|
||||
case TakenLargeHit:
|
||||
return static_cast<float>(me->TakenLargeHit());
|
||||
|
||||
case HasTrait_AutoWeapons:
|
||||
case HasTrait_HeavyWeapons:
|
||||
case HasTrait_Sniper:
|
||||
case HasTrait_Ranger:
|
||||
case HasTrait_Gunslinger:
|
||||
case HasTrait_MartialArts:
|
||||
case HasTrait_SquadLeader:
|
||||
case HasTrait_Technician:
|
||||
case HasTrait_Doctor:
|
||||
case HasTrait_Ambidextrous:
|
||||
case HasTrait_Melee:
|
||||
case HasTrait_Throwing:
|
||||
case HasTrait_NightOps:
|
||||
case HasTrait_Stealthy:
|
||||
case HasTrait_Athletics:
|
||||
case HasTrait_Bodybuilding:
|
||||
case HasTrait_Demolitions:
|
||||
case HasTrait_Teaching:
|
||||
case HasTrait_Scouting:
|
||||
case HasTrait_Covert:
|
||||
case HasTrait_RadioOperator:
|
||||
case HasTrait_Snitch:
|
||||
case HasTrait_Survival:
|
||||
case HasTraitOld_Lockpicking:
|
||||
case HasTraitOld_HandToHand:
|
||||
case HasTraitOld_Electronics:
|
||||
case HasTraitOld_NightOps:
|
||||
case HasTraitOld_Throwing:
|
||||
case HasTraitOld_Teaching:
|
||||
case HasTraitOld_HeavyWeapons:
|
||||
case HasTraitOld_AutoWeapons:
|
||||
case HasTraitOld_Stealthy:
|
||||
case HasTraitOld_Ambidextrous:
|
||||
case HasTraitOld_Thief:
|
||||
case HasTraitOld_MartialArts:
|
||||
case HasTraitOld_Knifing:
|
||||
case HasTraitOld_Sniper:
|
||||
case HasTraitOld_Camouflaged:
|
||||
return GetTrait(input, me);
|
||||
}
|
||||
}
|
||||
|
||||
float ScoreDSE(DecisionScoreEvaluator DSE, SOLDIERTYPE* me)
|
||||
{
|
||||
float finalScore = 1.0f;
|
||||
|
||||
for ( const auto& consideration : DSE.considerations )
|
||||
{
|
||||
if ( finalScore <= 0.0f ) { break; }
|
||||
|
||||
float curveInput = GetInputValue(consideration.input, me, consideration.target, consideration.targetLocation);
|
||||
float considerationScore = CalculateResponse(curveInput, consideration.curve);
|
||||
|
||||
finalScore *= considerationScore;
|
||||
}
|
||||
|
||||
// Add consideration compensation
|
||||
const auto modificationFactor = 1 - 1 / (DSE.considerations.size());
|
||||
const auto makeUpValue = (1 - finalScore) * modificationFactor;
|
||||
finalScore += finalScore * makeUpValue;
|
||||
|
||||
// Add DSE priority weight
|
||||
finalScore *= DSE.priorityWeight;
|
||||
|
||||
return finalScore;
|
||||
}
|
||||
|
||||
void ScoreAllDecisions(DecisionMaker DM, SOLDIERTYPE* me)
|
||||
{
|
||||
size_t idx = 0;
|
||||
for ( const auto& decision : DM.decisions )
|
||||
{
|
||||
DM.scores[idx].score = ScoreDSE(decision, me);
|
||||
DM.scores[idx].idx = idx;
|
||||
++idx;
|
||||
}
|
||||
|
||||
// Sort all scores from best to last
|
||||
std::sort(DM.scores.begin(), DM.scores.end(), std::greater<DecisionScore>());
|
||||
}
|
||||
|
||||
ActionType SelectScoredAction(DecisionMaker DM)
|
||||
{
|
||||
// Select highest for now. TODO weighted random choice
|
||||
const auto idx = DM.scores[0].idx;
|
||||
const auto action = DM.decisions[idx].Action;
|
||||
|
||||
return action;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
//void ScoreAllDecisions(DecisionMaker DM, SOLDIERTYPE* me);
|
||||
//ActionType SelectScoredAction(DecisionMaker DM);
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
#include "UtilityAI_ResponseCurve.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
|
||||
namespace UtilityAI
|
||||
{
|
||||
|
||||
float NormalizeInput(float value, float min, float max)
|
||||
{
|
||||
const float normalizedValue = (value - min) / (max - min);
|
||||
return normalizedValue;
|
||||
}
|
||||
|
||||
float CalculateResponse(float x, ResponseCurve curve)
|
||||
{
|
||||
const auto min = curve.min;
|
||||
const auto max = curve.max;
|
||||
const auto type = curve.type;
|
||||
const auto m = curve.m;
|
||||
const auto k = curve.k;
|
||||
const auto b = curve.b;
|
||||
const auto c = curve.c;
|
||||
|
||||
float input = std::clamp(x, min, max);
|
||||
input = NormalizeInput(input, min, max);
|
||||
|
||||
float y = 0.0f;
|
||||
// Polynomial
|
||||
float n;
|
||||
// Logistic
|
||||
const float e = 2.71828f; // Euler's number. I think? Lecture slides did not explain if this is what it's supposed to be.
|
||||
const float exponent = -input + c;
|
||||
const float denominator = 1.0f + 1000.0f * e * pow(m, exponent);
|
||||
|
||||
switch ( type )
|
||||
{
|
||||
case Step:
|
||||
if ( input != 0.0f ) { return 1.0f; }
|
||||
else { return 0.0f; }
|
||||
case Linear:
|
||||
case Polynomial:
|
||||
n = input - c;
|
||||
y = m * pow(n, k) + b;
|
||||
break;
|
||||
case Logistic:
|
||||
y = (k / denominator) + b;
|
||||
break;
|
||||
//case Logit: // Not implemented yet
|
||||
//break;
|
||||
}
|
||||
|
||||
y = std::clamp(y, 0.0f, 1.0f);
|
||||
return y;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
typedef enum
|
||||
{
|
||||
Step,
|
||||
Linear,
|
||||
Polynomial,
|
||||
Logistic,
|
||||
Logit
|
||||
} ResponseCurveType;
|
||||
|
||||
struct ResponseCurve
|
||||
{
|
||||
ResponseCurveType type;
|
||||
float m;
|
||||
float k;
|
||||
float b;
|
||||
float c;
|
||||
float min;
|
||||
float max;
|
||||
};
|
||||
|
||||
float NormalizeInput(float value, float min, float max);
|
||||
float CalculateResponse(float x, ResponseCurve curve);
|
||||
@@ -33,7 +33,7 @@
|
||||
#include "Text.h"
|
||||
|
||||
extern BOOLEAN gfHiddenInterrupt;
|
||||
extern void LogDecideInfo(SOLDIERTYPE *pSoldier);
|
||||
extern void LogDecideInfo(SOLDIERTYPE *pSoldier, bool doLog = true);
|
||||
|
||||
extern STR8 gStr8AlertStatus[];
|
||||
extern STR8 gStr8Attitude[];
|
||||
|
||||
+39
-12
@@ -12,10 +12,6 @@
|
||||
extern INT16 gubAIPathCosts[19][19];
|
||||
#define AI_PATHCOST_RADIUS 9
|
||||
|
||||
extern BOOLEAN gfDisplayCoverValues;
|
||||
//extern INT16 gsCoverValue[WORLD_MAX];
|
||||
extern INT16 * gsCoverValue;
|
||||
|
||||
// AI actions
|
||||
|
||||
enum CreatureCalls
|
||||
@@ -110,7 +106,10 @@ typedef enum
|
||||
AI_ACTION_DOCTOR_SELF, // added by Flugente: AI-ONLY! bandage/surgery on self. DO NOT USE THIS FOR MERCS!!!
|
||||
AI_ACTION_SELFDETONATE, // added by Flugente: blow up an explosive in own inventory
|
||||
AI_ACTION_STOP_MEDIC, // sevenfm: stop giving aid animation
|
||||
AI_ACTION_LAST = AI_ACTION_STOP_MEDIC
|
||||
AI_ACTION_DRINK_CANTEEN, // sevenfm: drink from canteen in inventory
|
||||
AI_ACTION_HANDLE_ITEM, // sevenfm: use item in hand
|
||||
AI_ACTION_PLANT_BOMB, // sevenfm: plant bomb using item in hand
|
||||
AI_ACTION_INVALID
|
||||
} ActionType;
|
||||
|
||||
|
||||
@@ -175,21 +174,36 @@ void CheckForChangingOrders(SOLDIERTYPE *pSoldier );
|
||||
|
||||
INT8 ClosestPanicTrigger( SOLDIERTYPE * pSoldier );
|
||||
|
||||
INT32 ClosestKnownOpponent(SOLDIERTYPE *pSoldier, INT32 * psGridNo, INT8 * pbLevel, SoldierID *pubOpponentID = NULL);
|
||||
INT32 ClosestKnownOpponent(SOLDIERTYPE *pSoldier, INT32 * psGridNo, INT8 * pbLevel, SoldierID *pubOpponentID = NULL, INT32 * distanceInCellCoords = NULL);
|
||||
INT32 ClosestPC( SOLDIERTYPE *pSoldier, INT32 * psDistance );
|
||||
INT32 ClosestUnDisguisedPC( SOLDIERTYPE *pSoldier, INT32 * psDistance ); // Flugente: like ClosestPC(...), but does not account for covert or not visible mercs
|
||||
BOOLEAN CanAutoBandage( BOOLEAN fDoFullCheck );
|
||||
|
||||
void DebugAI( STR szOutput );
|
||||
enum { AI_MSG_START, AI_MSG_DECIDE, AI_MSG_INFO, AI_MSG_TOPIC };
|
||||
void DebugAI(INT8 bMsgType, SOLDIERTYPE *pSoldier, STR szOutput, INT8 bAction = -1);
|
||||
void DebugAI(INT8 bMsgType, SOLDIERTYPE *pSoldier, STR szOutput, bool doLog = true, INT8 bAction = -1);
|
||||
void DebugQuestInfo(STR szOutput);
|
||||
INT8 DecideAction(SOLDIERTYPE *pSoldier);
|
||||
INT8 DecideActionBlack(SOLDIERTYPE *pSoldier);
|
||||
INT8 DecideActionEscort(SOLDIERTYPE *pSoldier);
|
||||
INT8 DecideActionGreen(SOLDIERTYPE *pSoldier);
|
||||
INT8 DecideActionRed(SOLDIERTYPE *pSoldier);
|
||||
INT8 DecideActionYellow(SOLDIERTYPE *pSoldier);
|
||||
INT8 DecideActionRed(SOLDIERTYPE *pSoldier);
|
||||
INT8 DecideActionBlack(SOLDIERTYPE *pSoldier);
|
||||
INT8 DecideActionGreenBoxer(SOLDIERTYPE* pSoldier);
|
||||
INT8 DecideActionBlackBoxer(SOLDIERTYPE* pSoldier);
|
||||
INT8 DecideActionGreenCivilian(SOLDIERTYPE* pSoldier);
|
||||
INT8 DecideActionYellowCivilian(SOLDIERTYPE* pSoldier);
|
||||
INT8 DecideActionRedCivilian(SOLDIERTYPE* pSoldier);
|
||||
INT8 DecideActionBlackCivilian(SOLDIERTYPE* pSoldier);
|
||||
INT8 DecideActionGreenSoldier(SOLDIERTYPE* pSoldier);
|
||||
INT8 DecideActionYellowSoldier(SOLDIERTYPE* pSoldier);
|
||||
INT8 DecideActionRedSoldier(SOLDIERTYPE* pSoldier);
|
||||
INT8 DecideActionBlackSoldier(SOLDIERTYPE* pSoldier);
|
||||
INT8 DecideActionGreenRobot(SOLDIERTYPE* pSoldier);
|
||||
INT8 DecideActionYellowRobot(SOLDIERTYPE* pSoldier);
|
||||
INT8 DecideActionRedRobot(SOLDIERTYPE* pSoldier);
|
||||
INT8 DecideActionBlackRobot(SOLDIERTYPE* pSoldier);
|
||||
|
||||
INT8 DecideActionBlackSoldierUtilityAI(SOLDIERTYPE* pSoldier);
|
||||
|
||||
INT16 DistanceToClosestFriend( SOLDIERTYPE * pSoldier );
|
||||
|
||||
@@ -199,11 +213,12 @@ void EndAIGuysTurn( SOLDIERTYPE *pSoldier );
|
||||
INT8 ExecuteAction(SOLDIERTYPE *pSoldier);
|
||||
|
||||
INT32 FindAdjacentSpotBeside(SOLDIERTYPE *pSoldier, INT32 sGridNo);
|
||||
INT32 FindBestNearbyCover(SOLDIERTYPE *pSoldier, INT32 morale, INT32 *pPercentBetter);
|
||||
INT32 FindBestNearbyCover(SOLDIERTYPE *pSoldier, INT32 morale, INT32 *pPercentBetter, INT32 targetGridNo = NOWHERE, bool ignoreSearchRange = false);
|
||||
INT32 FindClosestDoor( SOLDIERTYPE * pSoldier );
|
||||
INT32 FindNearbyPointOnEdgeOfMap( SOLDIERTYPE * pSoldier, INT8 * pbDirection );
|
||||
INT32 FindNearestEdgePoint( INT32 sGridNo );
|
||||
INT32 FindNearestPassableSpot( INT32 sGridNo, UINT8 usSearchRadius = 5 );
|
||||
BOOLEAN FindFenceAroundSpot(INT32 sSpot);
|
||||
|
||||
//Kris: Added these as I need specific searches on certain sides.
|
||||
enum
|
||||
@@ -244,7 +259,7 @@ void ManChecksOnFriends(SOLDIERTYPE *pSoldier);
|
||||
void NewDest(SOLDIERTYPE *pSoldier, INT32 sGridNo);
|
||||
INT32 NextPatrolPoint(SOLDIERTYPE *pSoldier);
|
||||
|
||||
INT8 PanicAI(SOLDIERTYPE *pSoldier, UINT8 ubCanMove);
|
||||
ActionType PanicAI(SOLDIERTYPE *pSoldier, UINT8 ubCanMove);
|
||||
void HaltMoveForSoldierOutOfPoints(SOLDIERTYPE *pSoldier);
|
||||
|
||||
INT32 RandDestWithinRange(SOLDIERTYPE *pSoldier);
|
||||
@@ -288,6 +303,7 @@ SoldierID GetClosestMedicSoldierID( SOLDIERTYPE * pSoldier, INT16 aRange, UINT8
|
||||
// sevenfm:
|
||||
BOOLEAN NightLight(void);
|
||||
BOOLEAN DuskLight(void);
|
||||
BOOLEAN FindClosestVisibleSmoke(SOLDIERTYPE* pSoldier, INT32& sSpot, INT8& bLevel, BOOLEAN fOnlyGas);
|
||||
BOOLEAN InSmokeNearby(INT32 sGridNo, INT8 bLevel);
|
||||
INT16 MaxNormalVisionDistance( void );
|
||||
UINT8 MinFlankDirections( SOLDIERTYPE *pSoldier );
|
||||
@@ -300,6 +316,13 @@ UINT8 CountFriendsBlack( SOLDIERTYPE *pSoldier, INT32 sClosestOpponent = NOWHERE
|
||||
UINT16 CountTeamUnderAttack(INT8 bTeam, INT32 sGridNo, INT16 sDistance);
|
||||
UINT16 CountPublicKnownEnemies(SOLDIERTYPE *pSoldier, INT32 sGridNo, INT16 sDistance);
|
||||
UINT16 CountPublicKnownEnemies(SOLDIERTYPE *pSoldier);
|
||||
UINT8 CountKnownEnemies(SOLDIERTYPE* pSoldier, INT32 sSpot, INT16 sDistance, INT8 bLevel = HEARD_THIS_TURN);
|
||||
UINT8 CountKnownEnemiesInRoom(SOLDIERTYPE* pSoldier, UINT16 usRoom);
|
||||
UINT8 CountFriendsInRoom(SOLDIERTYPE* pSoldier, UINT16 usRoom);
|
||||
INT32 CountCorpsesInRoom(SOLDIERTYPE* pSoldier, UINT16 usRoomNo, INT8 bLevel);
|
||||
UINT16 RoomNo(INT32 sSpot);
|
||||
BOOLEAN SameRoom(INT32 sSpot1, INT32 sSpot2);
|
||||
BOOLEAN CheckWindow(INT32 sSpot, UINT8 ubDirection, BOOLEAN fAllowClosed);
|
||||
|
||||
UINT8 SectorCurfew(BOOLEAN fNight);
|
||||
UINT8 TeamPercentKilled(INT8 bTeam);
|
||||
@@ -420,6 +443,10 @@ INT8 KnownPublicLevel(UINT8 bTeam, SoldierID ubOpponentID);
|
||||
|
||||
// sevenfm: distance for tactical AI checks, roughly equal to normal day vision range
|
||||
#define TACTICAL_RANGE (gGameExternalOptions.ubStraightSightRange * STRAIGHT_RATIO * 2)
|
||||
#define TACTICAL_RANGE_CELL_COORDS TACTICAL_RANGE*CELL_X_SIZE
|
||||
#define BOMB_DETECTION_RANGE (TACTICAL_RANGE / 4)
|
||||
#define TACTICAL_RANGE_HALF (TACTICAL_RANGE / 2)
|
||||
#define TACTICAL_RANGE_CLOSE (TACTICAL_RANGE / 4)
|
||||
#define TACTICAL_RANGE_VERYCLOSE (TACTICAL_RANGE / 6)
|
||||
|
||||
#endif
|
||||
|
||||
+166
-157
@@ -23,6 +23,7 @@
|
||||
#include "LogicalBodyTypes/BodyTypeDB.h"
|
||||
|
||||
#include "Utilities.h"
|
||||
#include <Cheats.h>
|
||||
|
||||
UINT32 guiShieldGraphic = 0;
|
||||
BOOLEAN fShieldGraphicInit = FALSE;
|
||||
@@ -552,22 +553,25 @@ void ResetRenderParameters( );
|
||||
|
||||
void RenderRoomInfo( INT16 sStartPointX_M, INT16 sStartPointY_M, INT16 sStartPointX_S, INT16 sStartPointY_S, INT16 sEndXS, INT16 sEndYS );
|
||||
|
||||
|
||||
#ifdef _DEBUG
|
||||
//extern UINT8 gubFOVDebugInfoInfo[ WORLD_MAX ];
|
||||
//extern UINT8 gubGridNoMarkers[ WORLD_MAX ];
|
||||
extern UINT8 * gubFOVDebugInfoInfo;
|
||||
extern UINT8 * gubGridNoMarkers;
|
||||
extern UINT8 gubGridNoValue;
|
||||
extern BOOLEAN gfDisplayCoverValues;
|
||||
extern BOOLEAN gfDisplayGridNoVisibleValues = 0;
|
||||
//extern INT16 gsCoverValue[ WORLD_MAX ];
|
||||
extern INT16 * gsCoverValue;
|
||||
extern INT16 gsBestCover;
|
||||
void RenderFOVDebugInfo( INT16 sStartPointX_M, INT16 sStartPointY_M, INT16 sStartPointX_S, INT16 sStartPointY_S, INT16 sEndXS, INT16 sEndYS );
|
||||
void RenderCoverDebugInfo( INT16 sStartPointX_M, INT16 sStartPointY_M, INT16 sStartPointX_S, INT16 sStartPointY_S, INT16 sEndXS, INT16 sEndYS );
|
||||
void RenderGridNoVisibleDebugInfo( INT16 sStartPointX_M, INT16 sStartPointY_M, INT16 sStartPointX_S, INT16 sStartPointY_S, INT16 sEndXS, INT16 sEndYS );
|
||||
#endif
|
||||
//extern UINT8 * gubFOVDebugInfoInfo;
|
||||
//extern UINT8 * gubGridNoMarkers;
|
||||
//extern UINT8 gubGridNoValue;
|
||||
//extern BOOLEAN gfDisplayGridNoVisibleValues = 0;
|
||||
//void RenderFOVDebugInfo( INT16 sStartPointX_M, INT16 sStartPointY_M, INT16 sStartPointX_S, INT16 sStartPointY_S, INT16 sEndXS, INT16 sEndYS );
|
||||
//void RenderGridNoVisibleDebugInfo( INT16 sStartPointX_M, INT16 sStartPointY_M, INT16 sStartPointX_S, INT16 sStartPointY_S, INT16 sEndXS, INT16 sEndYS );
|
||||
extern INT16 gsBestCover;
|
||||
INT32* gRenderDebugInfoValues = nullptr;
|
||||
void RenderDebugInfo(INT16 sStartPointX_M, INT16 sStartPointY_M, INT16 sStartPointX_S, INT16 sStartPointY_S, INT16 sEndXS, INT16 sEndYS);
|
||||
void ResetDebugInfoValues()
|
||||
{
|
||||
if (gRenderDebugInfoValues)
|
||||
{
|
||||
for (size_t i = 0; i < WORLD_MAX; i++)
|
||||
{
|
||||
gRenderDebugInfoValues[i] = 0x7FFFFFFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DeleteFromWorld( UINT16 usTileIndex, UINT32 uiRenderTiles, UINT16 usIndex );
|
||||
|
||||
@@ -3272,23 +3276,10 @@ UINT32 cnt = 0;
|
||||
RenderRoomInfo( gsStartPointX_M, gsStartPointY_M, gsStartPointX_S, gsStartPointY_S, gsEndXS, gsEndYS );
|
||||
}
|
||||
|
||||
#ifdef _DEBUG
|
||||
if( gRenderFlags&RENDER_FLAG_FOVDEBUG )
|
||||
if (DEBUG_CHEAT_LEVEL() && gTacticalStatus.Team[OUR_TEAM].bTeamActive)
|
||||
{
|
||||
RenderFOVDebugInfo( gsStartPointX_M, gsStartPointY_M, gsStartPointX_S, gsStartPointY_S, gsEndXS, gsEndYS );
|
||||
RenderDebugInfo(gsStartPointX_M, gsStartPointY_M, gsStartPointX_S, gsStartPointY_S, gsEndXS, gsEndYS);
|
||||
}
|
||||
else if (gfDisplayCoverValues)
|
||||
{
|
||||
RenderCoverDebugInfo( gsStartPointX_M, gsStartPointY_M, gsStartPointX_S, gsStartPointY_S, gsEndXS, gsEndYS );
|
||||
}
|
||||
else if (gfDisplayGridNoVisibleValues)
|
||||
{
|
||||
RenderGridNoVisibleDebugInfo( gsStartPointX_M, gsStartPointY_M, gsStartPointX_S, gsStartPointY_S, gsEndXS, gsEndYS );
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
//#endif
|
||||
|
||||
//RenderStaticWorldRect( gsVIEWPORT_START_X, gsVIEWPORT_START_Y, gsVIEWPORT_END_X, gsVIEWPORT_END_Y );
|
||||
//AddBaseDirtyRect(gsVIEWPORT_START_X, gsVIEWPORT_START_Y, gsVIEWPORT_END_X, gsVIEWPORT_END_Y );
|
||||
@@ -8014,6 +8005,10 @@ void RenderRoomInfo( INT16 sStartPointX_M, INT16 sStartPointY_M, INT16 sStartPoi
|
||||
|
||||
|
||||
#ifdef _DEBUG
|
||||
// From fov.cpp
|
||||
extern UINT8* gubGridNoMarkers;
|
||||
extern UINT8 gubGridNoValue;
|
||||
extern UINT8* gubFOVDebugInfoInfo;
|
||||
|
||||
void RenderFOVDebugInfo( INT16 sStartPointX_M, INT16 sStartPointY_M, INT16 sStartPointX_S, INT16 sStartPointY_S, INT16 sEndXS, INT16 sEndYS )
|
||||
{
|
||||
@@ -8124,114 +8119,7 @@ void RenderFOVDebugInfo( INT16 sStartPointX_M, INT16 sStartPointY_M, INT16 sStar
|
||||
|
||||
}
|
||||
|
||||
void RenderCoverDebugInfo( INT16 sStartPointX_M, INT16 sStartPointY_M, INT16 sStartPointX_S, INT16 sStartPointY_S, INT16 sEndXS, INT16 sEndYS )
|
||||
{
|
||||
INT8 bXOddFlag = 0;
|
||||
INT16 sAnchorPosX_M, sAnchorPosY_M;
|
||||
INT16 sAnchorPosX_S, sAnchorPosY_S;
|
||||
INT16 sTempPosX_M, sTempPosY_M;
|
||||
INT16 sTempPosX_S, sTempPosY_S;
|
||||
BOOLEAN fEndRenderRow = FALSE, fEndRenderCol = FALSE;
|
||||
UINT16 usTileIndex;
|
||||
INT16 sX, sY;
|
||||
UINT32 uiDestPitchBYTES;
|
||||
UINT8 *pDestBuf;
|
||||
|
||||
|
||||
// Begin Render Loop
|
||||
sAnchorPosX_M = sStartPointX_M;
|
||||
sAnchorPosY_M = sStartPointY_M;
|
||||
sAnchorPosX_S = sStartPointX_S;
|
||||
sAnchorPosY_S = sStartPointY_S;
|
||||
|
||||
pDestBuf = LockVideoSurface( FRAME_BUFFER, &uiDestPitchBYTES );
|
||||
|
||||
do
|
||||
{
|
||||
|
||||
fEndRenderRow = FALSE;
|
||||
sTempPosX_M = sAnchorPosX_M;
|
||||
sTempPosY_M = sAnchorPosY_M;
|
||||
sTempPosX_S = sAnchorPosX_S;
|
||||
sTempPosY_S = sAnchorPosY_S;
|
||||
|
||||
if(bXOddFlag > 0)
|
||||
sTempPosX_S += 20;
|
||||
|
||||
|
||||
do
|
||||
{
|
||||
|
||||
usTileIndex=FASTMAPROWCOLTOPOS( sTempPosY_M, sTempPosX_M );
|
||||
|
||||
if ( usTileIndex < GRIDSIZE )
|
||||
{
|
||||
sX = sTempPosX_S + ( WORLD_TILE_X / 2 ) - 5;
|
||||
sY = sTempPosY_S + ( WORLD_TILE_Y / 2 ) - 5;
|
||||
|
||||
// Adjust for interface level
|
||||
sY -= gpWorldLevelData[ usTileIndex ].sHeight;
|
||||
sY += gsRenderHeight;
|
||||
|
||||
if (gsCoverValue[ usTileIndex] != 0x7F7F)
|
||||
{
|
||||
SetFont( SMALLCOMPFONT );
|
||||
SetFontDestBuffer( FRAME_BUFFER , 0, 0, SCREEN_WIDTH, gsVIEWPORT_END_Y, FALSE );
|
||||
if (usTileIndex == gsBestCover)
|
||||
{
|
||||
SetFontForeground( FONT_MCOLOR_RED );
|
||||
}
|
||||
else if (gsCoverValue[ usTileIndex ] < 0)
|
||||
{
|
||||
SetFontForeground( FONT_MCOLOR_WHITE );
|
||||
}
|
||||
else
|
||||
{
|
||||
SetFontForeground( FONT_GRAY3 );
|
||||
}
|
||||
mprintf_buffer( pDestBuf, uiDestPitchBYTES, TINYFONT1, sX, sY , L"%d", gsCoverValue[ usTileIndex ] );
|
||||
SetFontDestBuffer( FRAME_BUFFER , 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, FALSE );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
sTempPosX_S += 40;
|
||||
sTempPosX_M ++;
|
||||
sTempPosY_M --;
|
||||
|
||||
if ( sTempPosX_S >= sEndXS )
|
||||
{
|
||||
fEndRenderRow = TRUE;
|
||||
}
|
||||
|
||||
} while( !fEndRenderRow );
|
||||
|
||||
if ( bXOddFlag > 0 )
|
||||
{
|
||||
sAnchorPosY_M ++;
|
||||
}
|
||||
else
|
||||
{
|
||||
sAnchorPosX_M ++;
|
||||
}
|
||||
|
||||
|
||||
bXOddFlag = !bXOddFlag;
|
||||
sAnchorPosY_S += 10;
|
||||
|
||||
if ( sAnchorPosY_S >= sEndYS )
|
||||
{
|
||||
fEndRenderCol = TRUE;
|
||||
}
|
||||
|
||||
}
|
||||
while( !fEndRenderCol );
|
||||
|
||||
UnLockVideoSurface( FRAME_BUFFER );
|
||||
|
||||
}
|
||||
|
||||
|
||||
// This one just renders tile gridnos. Not much use nowadays
|
||||
void RenderGridNoVisibleDebugInfo( INT16 sStartPointX_M, INT16 sStartPointY_M, INT16 sStartPointX_S, INT16 sStartPointY_S, INT16 sEndXS, INT16 sEndYS )
|
||||
{
|
||||
INT8 bXOddFlag = 0;
|
||||
@@ -8334,9 +8222,148 @@ void RenderGridNoVisibleDebugInfo( INT16 sStartPointX_M, INT16 sStartPointY_M, I
|
||||
}
|
||||
|
||||
|
||||
void RenderFOVDebug()
|
||||
{
|
||||
RenderFOVDebugInfo(gsStartPointX_M, gsStartPointY_M, gsStartPointX_S, gsStartPointY_S, gsEndXS, gsEndYS);
|
||||
}
|
||||
|
||||
void RenderGridNoVisibleDebug()
|
||||
{
|
||||
RenderGridNoVisibleDebugInfo(gsStartPointX_M, gsStartPointY_M, gsStartPointX_S, gsStartPointY_S, gsEndXS, gsEndYS);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void RenderDebugInfo(INT16 sStartPointX_M, INT16 sStartPointY_M, INT16 sStartPointX_S, INT16 sStartPointY_S, INT16 sEndXS, INT16 sEndYS)
|
||||
{
|
||||
INT8 bXOddFlag = 0;
|
||||
INT16 sTempPosX_M, sTempPosY_M;
|
||||
INT16 sTempPosX_S, sTempPosY_S;
|
||||
BOOLEAN fEndRenderRow = FALSE, fEndRenderCol = FALSE;
|
||||
|
||||
// Begin Render Loop
|
||||
INT16 sAnchorPosX_M = sStartPointX_M;
|
||||
INT16 sAnchorPosY_M = sStartPointY_M;
|
||||
INT16 sAnchorPosX_S = sStartPointX_S;
|
||||
INT16 sAnchorPosY_S = sStartPointY_S;
|
||||
|
||||
UINT32 uiDestPitchBYTES;
|
||||
UINT8* pDestBuf = LockVideoSurface(FRAME_BUFFER, &uiDestPitchBYTES);
|
||||
|
||||
const auto mode = gRenderDebugInfoMode;
|
||||
|
||||
do
|
||||
{
|
||||
fEndRenderRow = FALSE;
|
||||
sTempPosX_M = sAnchorPosX_M;
|
||||
sTempPosY_M = sAnchorPosY_M;
|
||||
sTempPosX_S = sAnchorPosX_S;
|
||||
sTempPosY_S = sAnchorPosY_S;
|
||||
|
||||
if (bXOddFlag > 0)
|
||||
sTempPosX_S += 20;
|
||||
|
||||
do
|
||||
{
|
||||
UINT16 usTileIndex = FASTMAPROWCOLTOPOS(sTempPosY_M, sTempPosX_M);
|
||||
if (usTileIndex < GRIDSIZE)
|
||||
{
|
||||
INT16 sX = sTempPosX_S + (WORLD_TILE_X / 2) - 5;
|
||||
INT16 sY = sTempPosY_S + (WORLD_TILE_Y / 2) - 5;
|
||||
|
||||
// Adjust for interface level
|
||||
sY -= gpWorldLevelData[usTileIndex].sHeight;
|
||||
sY += gsRenderHeight;
|
||||
|
||||
|
||||
if (gRenderDebugInfoValues[usTileIndex] != 0x7FFFFFFF)
|
||||
{
|
||||
SetFont(SMALLCOMPFONT);
|
||||
SetFontDestBuffer(FRAME_BUFFER, 0, 0, SCREEN_WIDTH, gsVIEWPORT_END_Y, FALSE);
|
||||
|
||||
|
||||
////////////////////////////
|
||||
// Debug mode specific setup
|
||||
switch (mode)
|
||||
{
|
||||
case DEBUG_PATHFINDING:
|
||||
if (gRenderDebugInfoValues[usTileIndex] < 0)
|
||||
{
|
||||
SetFontForeground(FONT_LTRED);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetFontForeground(FONT_LTGREEN);
|
||||
}
|
||||
break;
|
||||
case DEBUG_THREATVALUE:
|
||||
//TODO implement
|
||||
goto exit_loop;
|
||||
break;
|
||||
case DEBUG_COVERVALUE:
|
||||
if (usTileIndex == gsBestCover)
|
||||
{
|
||||
SetFontForeground(FONT_YELLOW);
|
||||
}
|
||||
else if (gRenderDebugInfoValues[usTileIndex] < 0)
|
||||
{
|
||||
SetFontForeground(FONT_LTRED);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetFontForeground(FONT_LTGREEN);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
goto exit_loop;
|
||||
break;
|
||||
}
|
||||
////////////////////////////
|
||||
|
||||
|
||||
mprintf_buffer(pDestBuf, uiDestPitchBYTES, TINYFONT1, sX, sY, L"%d", gRenderDebugInfoValues[usTileIndex]);
|
||||
SetFontDestBuffer(FRAME_BUFFER, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, FALSE);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
sTempPosX_S += 40;
|
||||
sTempPosX_M++;
|
||||
sTempPosY_M--;
|
||||
|
||||
if (sTempPosX_S >= sEndXS)
|
||||
{
|
||||
fEndRenderRow = TRUE;
|
||||
}
|
||||
|
||||
} while (!fEndRenderRow);
|
||||
|
||||
if (bXOddFlag > 0)
|
||||
{
|
||||
sAnchorPosY_M++;
|
||||
}
|
||||
else
|
||||
{
|
||||
sAnchorPosX_M++;
|
||||
}
|
||||
|
||||
|
||||
bXOddFlag = !bXOddFlag;
|
||||
sAnchorPosY_S += 10;
|
||||
|
||||
if (sAnchorPosY_S >= sEndYS)
|
||||
{
|
||||
fEndRenderCol = TRUE;
|
||||
}
|
||||
} while (!fEndRenderCol);
|
||||
|
||||
exit_loop:
|
||||
UnLockVideoSurface(FRAME_BUFFER);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ExamineZBufferRect( INT16 sLeft, INT16 sTop, INT16 sRight, INT16 sBottom)
|
||||
{
|
||||
CalcRenderParameters( sLeft, sTop, sRight, sBottom );
|
||||
@@ -8997,21 +9024,3 @@ void SetRenderCenter( INT16 sNewX, INT16 sNewY )
|
||||
gfScrollInertia = FALSE;
|
||||
|
||||
}
|
||||
|
||||
#ifdef _DEBUG
|
||||
void RenderFOVDebug( )
|
||||
{
|
||||
RenderFOVDebugInfo( gsStartPointX_M, gsStartPointY_M, gsStartPointX_S, gsStartPointY_S, gsEndXS, gsEndYS );
|
||||
}
|
||||
|
||||
void RenderCoverDebug( )
|
||||
{
|
||||
RenderCoverDebugInfo( gsStartPointX_M, gsStartPointY_M, gsStartPointX_S, gsStartPointY_S, gsEndXS, gsEndYS );
|
||||
}
|
||||
|
||||
static void RenderGridNoVisibleDebug( )
|
||||
{
|
||||
RenderGridNoVisibleDebugInfo( gsStartPointX_M, gsStartPointY_M, gsStartPointX_S, gsStartPointY_S, gsEndXS, gsEndYS );
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -216,6 +216,16 @@ void SetRenderCenter( INT16 sNewX, INT16 sNewY );
|
||||
#ifdef _DEBUG
|
||||
void RenderFOVDebug( );
|
||||
#endif
|
||||
enum RenderDebugInfoModes
|
||||
{
|
||||
DEBUG_PATHFINDING,
|
||||
DEBUG_THREATVALUE,
|
||||
DEBUG_COVERVALUE,
|
||||
DEBUG_OFF
|
||||
};
|
||||
void ResetDebugInfoValues();
|
||||
extern INT32* gRenderDebugInfoValues;
|
||||
|
||||
|
||||
BOOLEAN Zero8BPPDataTo16BPPBufferTransparent( UINT16 *pBuffer, UINT32 uiDestPitchBYTES, HVOBJECT hSrcVObject, INT32 iX, INT32 iY, UINT16 usIndex );
|
||||
BOOLEAN Blt8BPPDataTo16BPPBufferTransZIncClipZSameZBurnsThrough( UINT16 *pBuffer, UINT32 uiDestPitchBYTES, UINT16 *pZBuffer, UINT16 usZValue, HVOBJECT hSrcVObject, INT32 iX, INT32 iY, UINT16 usIndex, SGPRect *clipregion, INT16 sZStripIndex );
|
||||
@@ -227,4 +237,4 @@ BOOLEAN Blt8BPPDataTo16BPPBufferTransZIncObscureClip(UINT16 *pBuffer, UINT32 uiD
|
||||
BOOLEAN Blt8BPPDataTo16BPPBufferTransZTransShadowIncObscureClip(UINT16 *pBuffer, UINT32 uiDestPitchBYTES, UINT16 *pZBuffer, UINT16 usZValue, HVOBJECT hSrcVObject, INT32 iX, INT32 iY, UINT16 usIndex, SGPRect *clipregion, INT16 sZIndex, UINT16 *p16BPPPalette, BOOLEAN fIgnoreShadows = FALSE);
|
||||
BOOLEAN Blt8BPPDataTo16BPPBufferTransZTransShadowIncObscureClipAlpha(UINT16 *pBuffer, UINT32 uiDestPitchBYTES, UINT16 *pZBuffer, UINT16 usZValue, HVOBJECT hSrcVObject, HVOBJECT hAlphaVObject, INT32 iX, INT32 iY, UINT16 usIndex, SGPRect *clipregion, INT16 sZIndex, UINT16 *p16BPPPalette, BOOLEAN fIgnoreShadows = FALSE);
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
#include "Button Defines.h"
|
||||
#include "Animation Data.h"
|
||||
#endif
|
||||
#include <Cheats.h>
|
||||
|
||||
|
||||
#define SET_MOVEMENTCOST( a, b, c, d ) ( ( gubWorldMovementCosts[ a ][ b ][ c ] < d ) ? ( gubWorldMovementCosts[ a ][ b ][ c ] = d ) : 0 );
|
||||
@@ -84,7 +85,6 @@ extern UINT8 *gubFOVDebugInfoInfo;
|
||||
extern INT16 gsFullTileDirections[MAX_FULLTILE_DIRECTIONS];
|
||||
extern INT32 dirDelta[8];
|
||||
extern INT16 DirIncrementer[8];
|
||||
extern INT16 *gsCoverValue;
|
||||
extern INT32 gsTempActionGridNo;
|
||||
extern INT32 gsOverItemsGridNo;
|
||||
extern INT32 gsOutOfRangeGridNo;
|
||||
@@ -316,8 +316,6 @@ void DeinitializeWorld()
|
||||
TrashWorld();
|
||||
if(gubGridNoMarkers)
|
||||
MemFree(gubGridNoMarkers);
|
||||
if(gsCoverValue)
|
||||
MemFree(gsCoverValue);
|
||||
if(gubBuildingInfo)
|
||||
MemFree(gubBuildingInfo);
|
||||
if(gusWorldRoomInfo)
|
||||
@@ -4308,10 +4306,12 @@ void SetWorldSize(INT32 nWorldRows, INT32 nWorldCols)
|
||||
gubGridNoMarkers = (UINT8*)MemAlloc(WORLD_MAX);
|
||||
memset(gubGridNoMarkers, 0, sizeof(UINT8)*WORLD_MAX);
|
||||
|
||||
if(gsCoverValue)
|
||||
MemFree(gsCoverValue);
|
||||
gsCoverValue = (INT16*)MemAlloc(sizeof(INT16)*WORLD_MAX);
|
||||
memset(gsCoverValue, 0x7F, sizeof(INT16)*WORLD_MAX);
|
||||
if (DEBUG_CHEAT_LEVEL())
|
||||
{
|
||||
MemFree(gRenderDebugInfoValues);
|
||||
gRenderDebugInfoValues = (INT32*)MemAlloc(sizeof(INT32) * WORLD_MAX);
|
||||
ResetDebugInfoValues();
|
||||
}
|
||||
|
||||
// Init building structures and variables
|
||||
if(gubBuildingInfo)
|
||||
|
||||
@@ -6641,6 +6641,7 @@ STR16 zOptionsToggleText[] =
|
||||
L"显示已知敌人位置", //L"Show enemy location", show locator on last known enemy location
|
||||
L"准心开始时为最大", // L"Start at maximum aim",
|
||||
L"替换新的寻路方式", // L"Alternative pathfinding",
|
||||
L"Use old Tactical AI", // L"Use old Tactical AI",
|
||||
L"--作弊模式选项--", // TOPTION_CHEAT_MODE_OPTIONS_HEADER,
|
||||
L"强制 Bobby Ray 送货", // force all pending Bobby Ray shipments
|
||||
L"-----------------", // TOPTION_CHEAT_MODE_OPTIONS_END
|
||||
@@ -6762,6 +6763,7 @@ STR16 zOptionsScreenHelpText[] =
|
||||
L"打开时,会显示已知敌人最后移动的位置。", //L"When ON, shows last known enemy location.",
|
||||
L"打开时,默认瞄准值为最大,而不是无。", //L"When ON, aiming at enemy will start at maximum aiming instead of default no aim",
|
||||
L"打开时,使用A*寻路算法,而不是原始算法。", //L"When ON, Use A* pathfinding algorithm, instead of original",
|
||||
L"When ON, Uses the old tactical AI",
|
||||
L"(text not rendered)TOPTION_CHEAT_MODE_OPTIONS_HEADER",
|
||||
L"强制 Bobby Ray 出货",
|
||||
L"(text not rendered)TOPTION_CHEAT_MODE_OPTIONS_END",
|
||||
|
||||
@@ -6643,6 +6643,7 @@ STR16 zOptionsToggleText[] =
|
||||
L"Show enemy location", // show locator on last known enemy location
|
||||
L"Start at maximum aim",
|
||||
L"Alternative pathfinding",
|
||||
L"Use old Tactical AI",
|
||||
L"--Cheat Mode Options--", // TOPTION_CHEAT_MODE_OPTIONS_HEADER,
|
||||
L"Force Bobby Ray shipments", // force all pending Bobby Ray shipments
|
||||
L"-----------------", // TOPTION_CHEAT_MODE_OPTIONS_END
|
||||
@@ -6764,6 +6765,7 @@ STR16 zOptionsScreenHelpText[] =
|
||||
L"When ON, shows last known enemy location.", //TODO.Translate
|
||||
L"When ON, aiming at enemy will start at maximum aiming instead of default no aim",
|
||||
L"When ON, Use A* pathfinding algorithm, instead of original",
|
||||
L"When ON, Uses the old tactical AI",
|
||||
L"(text not rendered)TOPTION_CHEAT_MODE_OPTIONS_HEADER",
|
||||
L"Force all pending Bobby Ray shipments",
|
||||
L"(text not rendered)TOPTION_CHEAT_MODE_OPTIONS_END",
|
||||
|
||||
@@ -6641,6 +6641,7 @@ STR16 zOptionsToggleText[] =
|
||||
L"Show enemy location", // show locator on last known enemy location
|
||||
L"Start at maximum aim",
|
||||
L"Alternative pathfinding",
|
||||
L"Use old Tactical AI",
|
||||
L"--Cheat Mode Options--", // TOPTION_CHEAT_MODE_OPTIONS_HEADER,
|
||||
L"Force Bobby Ray Shipments", // force all pending Bobby Ray shipments
|
||||
L"-----------------", // TOPTION_CHEAT_MODE_OPTIONS_END
|
||||
@@ -6762,6 +6763,7 @@ STR16 zOptionsScreenHelpText[] =
|
||||
L"When ON, shows last known enemy location.",
|
||||
L"When ON, aiming at enemy will start at maximum aiming instead of default no aim",
|
||||
L"When ON, Use A* pathfinding algorithm, instead of original",
|
||||
L"When ON, Uses the old tactical AI",
|
||||
L"(text not rendered)TOPTION_CHEAT_MODE_OPTIONS_HEADER",
|
||||
L"Force all pending Bobby Ray shipments",
|
||||
L"(text not rendered)TOPTION_CHEAT_MODE_OPTIONS_END",
|
||||
|
||||
@@ -6648,6 +6648,7 @@ STR16 zOptionsToggleText[] =
|
||||
L"Show enemy location", // show locator on last known enemy location
|
||||
L"Start at maximum aim",
|
||||
L"Alternative pathfinding",
|
||||
L"Use old Tactical AI",
|
||||
L"--Options mode triche--", // TOPTION_CHEAT_MODE_OPTIONS_HEADER,
|
||||
L"Forcer envois Bobby Ray", // force all pending Bobby Ray shipments
|
||||
L"-----------------", // TOPTION_CHEAT_MODE_OPTIONS_END
|
||||
@@ -6768,6 +6769,7 @@ STR16 zOptionsScreenHelpText[] =
|
||||
L"When ON, shows last known enemy location.", //TODO.Translate
|
||||
L"When ON, aiming at enemy will start at maximum aiming instead of default no aim",
|
||||
L"When ON, Use A* pathfinding algorithm, instead of original",
|
||||
L"When ON, Uses the old tactical AI",
|
||||
L"(text not rendered)TOPTION_CHEAT_MODE_OPTIONS_HEADER",
|
||||
L"Forcer tous les envois en attente de Bobby Ray",
|
||||
L"(text not rendered)TOPTION_CHEAT_MODE_OPTIONS_END",
|
||||
|
||||
@@ -6515,6 +6515,7 @@ STR16 zOptionsToggleText[] =
|
||||
L"Show enemy location", // show locator on last known enemy location
|
||||
L"Start at maximum aim",
|
||||
L"Alternative pathfinding",
|
||||
L"Use old Tactical AI",
|
||||
L"--Cheat Mode Options--", // TOPTION_CHEAT_MODE_OPTIONS_HEADER,
|
||||
L"Erzwinge BR Lieferung", // force all pending Bobby Ray shipments
|
||||
L"-----------------", // TOPTION_CHEAT_MODE_OPTIONS_END
|
||||
@@ -6636,6 +6637,7 @@ STR16 zOptionsScreenHelpText[] =
|
||||
L"When ON, shows last known enemy location.", //TODO.Translate
|
||||
L"When ON, aiming at enemy will start at maximum aiming instead of default no aim",
|
||||
L"When ON, Use A* pathfinding algorithm, instead of original",
|
||||
L"When ON, Uses the old tactical AI",
|
||||
L"(text not rendered)TOPTION_CHEAT_MODE_OPTIONS_HEADER",
|
||||
L"Force all pending Bobby Ray shipments",
|
||||
L"(text not rendered)TOPTION_CHEAT_MODE_OPTIONS_END",
|
||||
|
||||
@@ -6626,6 +6626,7 @@ STR16 zOptionsToggleText[] =
|
||||
L"Show enemy location", // show locator on last known enemy location
|
||||
L"Start at maximum aim",
|
||||
L"Alternative pathfinding",
|
||||
L"Use old Tactical AI",
|
||||
L"--Cheat Mode Options--", // TOPTION_CHEAT_MODE_OPTIONS_HEADER,
|
||||
L"Force Bobby Ray shipments", // force all pending Bobby Ray shipments
|
||||
L"-----------------", // TOPTION_CHEAT_MODE_OPTIONS_END
|
||||
@@ -6747,6 +6748,7 @@ STR16 zOptionsScreenHelpText[] =
|
||||
L"When ON, shows last known enemy location.", //TODO.Translate
|
||||
L"When ON, aiming at enemy will start at maximum aiming instead of default no aim",
|
||||
L"When ON, Use A* pathfinding algorithm, instead of original",
|
||||
L"When ON, Uses the old tactical AI",
|
||||
L"(text not rendered)TOPTION_CHEAT_MODE_OPTIONS_HEADER",
|
||||
L"Force all pending Bobby Ray shipments",
|
||||
L"(text not rendered)TOPTION_CHEAT_MODE_OPTIONS_END",
|
||||
|
||||
@@ -6645,6 +6645,7 @@ STR16 zOptionsToggleText[] =
|
||||
L"Show enemy location", // show locator on last known enemy location
|
||||
L"Start at maximum aim",
|
||||
L"Alternative pathfinding",
|
||||
L"Use old Tactical AI",
|
||||
L"--Cheat Mode Options--", // TOPTION_CHEAT_MODE_OPTIONS_HEADER,
|
||||
L"Force Bobby Ray shipments", // force all pending Bobby Ray shipments
|
||||
L"-----------------", // TOPTION_CHEAT_MODE_OPTIONS_END
|
||||
@@ -6766,6 +6767,7 @@ STR16 zOptionsScreenHelpText[] =
|
||||
L"When ON, shows last known enemy location.", //TODO.Translate
|
||||
L"When ON, aiming at enemy will start at maximum aiming instead of default no aim",
|
||||
L"When ON, Use A* pathfinding algorithm, instead of original",
|
||||
L"When ON, Uses the old tactical AI",
|
||||
L"(text not rendered)TOPTION_CHEAT_MODE_OPTIONS_HEADER",
|
||||
L"Wymuś wszystkie oczekiwane dostawy od Bobby Ray's.",
|
||||
L"(text not rendered)TOPTION_CHEAT_MODE_OPTIONS_END",
|
||||
|
||||
@@ -6637,6 +6637,7 @@ STR16 zOptionsToggleText[] =
|
||||
L"Показывать расположение", // show locator on last known enemy location
|
||||
L"Start at maximum aim",
|
||||
L"Alternative pathfinding",
|
||||
L"Use old Tactical AI",
|
||||
L"--Читерские настройки--", // TOPTION_CHEAT_MODE_OPTIONS_HEADER,
|
||||
L"Ускорить доставку Бобби Рэя", // force all pending Bobby Ray shipments
|
||||
L"-----------------", // TOPTION_CHEAT_MODE_OPTIONS_END
|
||||
@@ -6758,6 +6759,7 @@ STR16 zOptionsScreenHelpText[] =
|
||||
L"Если включено, показывает известное расположение противника\n(нажмите |S|h|i|f|t, чтобы показать источник шума).",
|
||||
L"When ON, aiming at enemy will start at maximum aiming instead of default no aim",
|
||||
L"When ON, Use A* pathfinding algorithm, instead of original",
|
||||
L"When ON, Uses the old tactical AI",
|
||||
L"(text not rendered)TOPTION_CHEAT_MODE_OPTIONS_HEADER",
|
||||
L"Выберите этот пункт,\nчтобы груз Бобби Рэя прибыл немедленно.",
|
||||
L"(text not rendered)TOPTION_CHEAT_MODE_OPTIONS_END",
|
||||
|
||||
Reference in New Issue
Block a user