mirror of
https://github.com/1dot13/source.git
synced 2026-07-22 13:40:22 +02:00
Compare commits
86
Commits
| 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 |
+2
-2
@@ -27,7 +27,7 @@ if(ADDRESS_SANITIZER)
|
||||
endif()
|
||||
|
||||
if(MSVC)
|
||||
add_compile_options("/wd4838") # silence implicit narrowing conversion warnings
|
||||
add_compile_options("/wd4838")
|
||||
endif()
|
||||
|
||||
# whether we are using MSBuild as a generator
|
||||
@@ -163,5 +163,5 @@ foreach(app IN LISTS ApplicationTargets)
|
||||
# for SGP only
|
||||
target_link_libraries(${app}_sgp PRIVATE "ddraw.lib" "${PROJECT_SOURCE_DIR}/fmodvc.lib")
|
||||
target_link_libraries(${app}_sgp PRIVATE libpng)
|
||||
target_compile_definitions(${app}_sgp PRIVATE ${compilationFlags} ${debugFlags} NO_ZLIB_COMPRESSION)
|
||||
target_compile_definitions(${app}_sgp PRIVATE NO_ZLIB_COMPRESSION)
|
||||
endforeach()
|
||||
|
||||
@@ -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;
|
||||
@@ -2749,7 +2752,6 @@ void LoadSkillTraitsExternalSettings()
|
||||
gSkillTraitValues.ubTERepairRobotPenaltyReduction = iniReader.ReadInteger("Technician","REPAIR_SPEED_ROBOT_PENALTY_REDUCTION", 30, 0, 100);
|
||||
if (gSkillTraitValues.ubTETraitsNumToRepairRobot == 2) {gSkillTraitValues.ubTERepairRobotPenaltyReduction /= 2; }
|
||||
gSkillTraitValues.fTETraitsCanRestoreItemThreshold = iniReader.ReadBoolean( "Technician", "MERCS_CAN_DO_ADVANCED_REPAIRS", FALSE );
|
||||
gSkillTraitValues.ubTechLevelNeededForAdvancedRepair = iniReader.ReadInteger("Technician", "TECH_LEVEL_NEEDED_FOR_ADVANCED_REPAIR", 2, 1, 2);
|
||||
|
||||
// DOCTOR
|
||||
gSkillTraitValues.ubDONumberTraitsNeededForSurgery = iniReader.ReadInteger("Doctor","NUMBER_OF_TRAITS_NEEDED_FOR_SURGERY", 1, 0, 2);
|
||||
|
||||
+1
-1
@@ -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,
|
||||
@@ -2145,7 +2146,6 @@ typedef struct
|
||||
UINT8 ubTERepairRobotPenaltyReduction;
|
||||
UINT8 ubTETraitsNumToRepairRobot;
|
||||
BOOLEAN fTETraitsCanRestoreItemThreshold;
|
||||
UINT8 ubTechLevelNeededForAdvancedRepair; // if we have a high enough technician level, we can repair items above the normal threshold (1 for technician, 2 for engineer)
|
||||
|
||||
// DOCTOR
|
||||
UINT8 ubDONumberTraitsNeededForSurgery;
|
||||
|
||||
@@ -1822,7 +1822,7 @@ void HandleHighLightedText( BOOLEAN fHighLight )
|
||||
bLastRegion = -1;
|
||||
}
|
||||
|
||||
if( bHighLight != -1 && toggle_box_array[bHighLight] != -1)
|
||||
if( bHighLight != -1 )
|
||||
{
|
||||
if( bHighLight < OPT_FIRST_COLUMN_TOGGLE_CUT_OFF )
|
||||
{
|
||||
|
||||
@@ -1560,8 +1560,6 @@ BOOLEAN DisplayMercsInventory(UINT8 ubMercID)
|
||||
CHAR16 gzItemName[ 5000 ];
|
||||
UINT8 ubItemCount=0;
|
||||
UINT8 ubColumnCount=0;
|
||||
const bool realisticGameStyle = gGameOptions.ubGameStyle != STYLE_SCIFI;
|
||||
const bool reducedGuns = gGameOptions.fGunNut == false;
|
||||
|
||||
//if the mercs inventory has already been purchased, dont display the inventory
|
||||
if ( (gMercProfiles[ ubMercID ].ubMiscFlags & PROFILE_MISC_FLAG_ALREADY_USED_ITEMS) && !gGameExternalOptions.fGearKitsAlwaysAvailable )
|
||||
@@ -1585,18 +1583,6 @@ BOOLEAN DisplayMercsInventory(UINT8 ubMercID)
|
||||
{
|
||||
usItem = gMercProfiles[ubMercID].inv[ i ];
|
||||
|
||||
if (realisticGameStyle && ItemIsOnlyInScifi(usItem))
|
||||
{
|
||||
gMercProfiles[ubMercID].inv[i] = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (reducedGuns && ItemIsOnlyInTonsOfGuns(usItem))
|
||||
{
|
||||
gMercProfiles[ubMercID].inv[i] = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
//if its a valid item AND we are only displaying less then 8 items
|
||||
if( usItem && ubItemCount < WEAPONBOX_TOTAL_ITEMS )
|
||||
{
|
||||
@@ -1676,18 +1662,6 @@ BOOLEAN DisplayMercsInventory(UINT8 ubMercID)
|
||||
{
|
||||
usItem = gMercProfiles[ubMercID].inv[ i ];
|
||||
|
||||
if (realisticGameStyle && ItemIsOnlyInScifi(usItem))
|
||||
{
|
||||
gMercProfiles[ubMercID].inv[i] = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (reducedGuns && ItemIsOnlyInTonsOfGuns(usItem))
|
||||
{
|
||||
gMercProfiles[ubMercID].inv[i] = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
//if its a valid item AND we are only displaying less then 8 items
|
||||
if( usItem && ubItemCount < WEAPONBOX_NUMBER )
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -4948,7 +4948,7 @@ static void CollectRepairableItems(SOLDIERTYPE* pRepairSoldier, SOLDIERTYPE* pSo
|
||||
// Check for attachments (are there stackable items that can take attachments though?)
|
||||
UINT8 attachmentIndex = 0;
|
||||
for (attachmentList::const_iterator iter = (*pObj)[stackIndex]->attachments.begin(); iter != (*pObj)[stackIndex]->attachments.end(); ++iter, ++attachmentIndex) {
|
||||
if ( iter->exists() && IsItemRepairable(pRepairSoldier, iter->usItem, (*iter)[attachmentIndex]->data.objectStatus, (*iter)[attachmentIndex]->data.sRepairThreshold )) {
|
||||
if (IsItemRepairable(pRepairSoldier, iter->usItem, (*iter)[attachmentIndex]->data.objectStatus, (*iter)[attachmentIndex]->data.sRepairThreshold )) {
|
||||
// Send the main item, not the attachment
|
||||
RepairItem item(pObj, pSoldier, (INVENTORY_SLOT) pocketIndex);
|
||||
itemsToFix.push(item);
|
||||
@@ -5081,7 +5081,7 @@ OBJECTTYPE* FindRepairableItemInSpecificPocket(SOLDIERTYPE * pSoldier, OBJECTTYP
|
||||
// have to check for attachments after...
|
||||
for (attachmentList::iterator iter = (*pObj)[subObject]->attachments.begin(); iter != (*pObj)[subObject]->attachments.end(); ++iter) {
|
||||
// if it's repairable and NEEDS repairing
|
||||
if ( iter->exists() && IsItemRepairable( pSoldier, iter->usItem, (*iter)[subObject]->data.objectStatus, (*iter)[subObject]->data.sRepairThreshold ) ) {
|
||||
if ( IsItemRepairable( pSoldier, iter->usItem, (*iter)[subObject]->data.objectStatus, (*iter)[subObject]->data.sRepairThreshold ) && iter->exists() ) {
|
||||
return( &(*iter) );
|
||||
}
|
||||
}
|
||||
@@ -5171,12 +5171,8 @@ BOOLEAN RepairObject( SOLDIERTYPE * pSoldier, SOLDIERTYPE * pOwner, OBJECTTYPE *
|
||||
INT16 threshold = 100;
|
||||
if ( gGameExternalOptions.fAdvRepairSystem && (Item[pObj->usItem].usItemClass & (IC_WEAPON|IC_ARMOUR)) )
|
||||
{
|
||||
if (gSkillTraitValues.fTETraitsCanRestoreItemThreshold && NUM_SKILL_TRAITS(pSoldier, TECHNICIAN_NT) >= gSkillTraitValues.ubTechLevelNeededForAdvancedRepair) // Greysa: added skill check for advanced repair. If we have a high enough technician level, we can repair items above the normal threshold (1 for technician, 2 for engineer)
|
||||
if ( !gSkillTraitValues.fTETraitsCanRestoreItemThreshold || !HAS_SKILL_TRAIT( pSoldier, TECHNICIAN_NT ) )
|
||||
{
|
||||
threshold = 100;
|
||||
}
|
||||
else
|
||||
{
|
||||
threshold = (*pObj)[ubLoop]->data.sRepairThreshold;
|
||||
}
|
||||
}
|
||||
@@ -11089,8 +11085,7 @@ void DetermineWhichAssignmentMenusCanBeShown( void )
|
||||
CreateDestroyMouseRegionForFacilityMenu();
|
||||
CreateDestroyMouseRegionsForFacilityAssignmentMenu();
|
||||
|
||||
const auto selectedCharacter = gCharactersList[bSelectedInfoChar].usSolID;
|
||||
if( selectedCharacter < NOBODY && ( (selectedCharacter->stats.bLife == 0 )||(selectedCharacter->bAssignment == ASSIGNMENT_POW ) ) && ( (guiTacticalInterfaceFlags & INTERFACE_MAPSCREEN ) ) )
|
||||
if( ( ( gCharactersList[ bSelectedInfoChar ].usSolID->stats.bLife == 0 )||( gCharactersList[bSelectedInfoChar].usSolID->bAssignment == ASSIGNMENT_POW ) ) && ( (guiTacticalInterfaceFlags & INTERFACE_MAPSCREEN ) ) )
|
||||
{
|
||||
// show basic assignment menu
|
||||
ShowBox( ghRemoveMercAssignBox );
|
||||
|
||||
@@ -2481,12 +2481,6 @@ DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"Autoresolve2");
|
||||
DeleteVideoObjectFromIndex( gpAR->iIndent );
|
||||
DeleteVideoSurfaceFromIndex( gpAR->iInterfaceBuffer );
|
||||
|
||||
// Load necessary mapinfo to place corpses
|
||||
CHAR8 bFilename[50];
|
||||
INT32 worldRows, worldCols;
|
||||
GetMapFileName(gpAR->ubSectorX, gpAR->ubSectorY, 0, bFilename, TRUE, TRUE);
|
||||
LoadWorldInfoForAutoResolve(bFilename, worldRows, worldCols);
|
||||
|
||||
if( fDeleteForGood )
|
||||
{ //Delete the soldier instances -- done when we are completely finished.
|
||||
|
||||
@@ -2525,7 +2519,7 @@ DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"Autoresolve2");
|
||||
RemoveCharacterFromSquads( gpMercs[ i ].pSoldier );
|
||||
ChangeSoldiersAssignment( gpMercs[ i ].pSoldier, ASSIGNMENT_DEAD );
|
||||
|
||||
AddDeadSoldierToUnLoadedSector( gpAR->ubSectorX, gpAR->ubSectorY, 0, gpMercs[ i ].pSoldier, RandomGridNoUnloadedSector(worldRows, worldCols), ADD_DEAD_SOLDIER_TO_SWEETSPOT );
|
||||
AddDeadSoldierToUnLoadedSector( gpAR->ubSectorX, gpAR->ubSectorY, 0, gpMercs[ i ].pSoldier, RandomGridNo(), ADD_DEAD_SOLDIER_TO_SWEETSPOT );
|
||||
}
|
||||
else if( gpAR->ubBattleStatus == BATTLE_SURRENDERED || gpAR->ubBattleStatus == BATTLE_CAPTURED )
|
||||
{
|
||||
@@ -2630,8 +2624,7 @@ DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"Autoresolve2");
|
||||
}
|
||||
|
||||
// Flugente: drop sector equipment
|
||||
const auto gridno = RandomGridNoUnloadedSector(worldRows, worldCols);
|
||||
gpCivs[ i ].pSoldier->DropSectorEquipment(gridno);
|
||||
gpCivs[ i ].pSoldier->DropSectorEquipment();
|
||||
|
||||
if( fDeleteForGood && gpCivs[ i ].pSoldier->stats.bLife < OKLIFE/2 )
|
||||
{
|
||||
@@ -2656,7 +2649,7 @@ DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"Autoresolve2");
|
||||
UpdateMilitia( militia );
|
||||
}
|
||||
|
||||
AddDeadSoldierToUnLoadedSector( gpAR->ubSectorX, gpAR->ubSectorY, 0, gpCivs[ i ].pSoldier, gridno, ADD_DEAD_SOLDIER_TO_SWEETSPOT );
|
||||
AddDeadSoldierToUnLoadedSector( gpAR->ubSectorX, gpAR->ubSectorY, 0, gpCivs[ i ].pSoldier, RandomGridNo(), ADD_DEAD_SOLDIER_TO_SWEETSPOT );
|
||||
StrategicRemoveMilitiaFromSector( gpCivs[ i ].pSoldier->sSectorX, gpCivs[ i ].pSoldier->sSectorY, ubCurrentRank, 1 );
|
||||
|
||||
if( ProcessLoyalty() )
|
||||
@@ -2688,8 +2681,7 @@ DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"Autoresolve2");
|
||||
TrackEnemiesKilled( ENEMY_KILLED_IN_AUTO_RESOLVE, gpEnemies[ i ].pSoldier->ubSoldierClass ); //add casualty to some statistic
|
||||
if( ProcessLoyalty() )HandleGlobalLoyaltyEvent( GLOBAL_LOYALTY_ENEMY_KILLED, gpAR->ubSectorX, gpAR->ubSectorY, 0 );
|
||||
ProcessQueenCmdImplicationsOfDeath( gpEnemies[ i ].pSoldier );
|
||||
const auto gridno = RandomGridNoUnloadedSector(worldRows, worldCols);
|
||||
AddDeadSoldierToUnLoadedSector( gpAR->ubSectorX, gpAR->ubSectorY, 0, gpEnemies[ i ].pSoldier, gridno, ADD_DEAD_SOLDIER_TO_SWEETSPOT);
|
||||
AddDeadSoldierToUnLoadedSector( gpAR->ubSectorX, gpAR->ubSectorY, 0, gpEnemies[ i ].pSoldier, RandomGridNo(), ADD_DEAD_SOLDIER_TO_SWEETSPOT );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6015,7 +6007,7 @@ void AutoResolveMilitiaDropAndPromote()
|
||||
}
|
||||
|
||||
// Flugente: drop sector equipment
|
||||
gpCivs[i].pSoldier->DropSectorEquipment( NOWHERE );
|
||||
gpCivs[i].pSoldier->DropSectorEquipment( );
|
||||
|
||||
if ( gpCivs[i].pSoldier->stats.bLife < OKLIFE / 2 )
|
||||
{
|
||||
|
||||
@@ -249,6 +249,10 @@ void AdvanceClock( UINT8 ubWarpCode )
|
||||
guiDay = ( guiGameClock / NUM_SEC_IN_DAY );
|
||||
guiHour = ( guiGameClock - ( guiDay * NUM_SEC_IN_DAY ) ) / NUM_SEC_IN_HOUR;
|
||||
guiMin = ( guiGameClock - ( ( guiDay * NUM_SEC_IN_DAY ) + ( guiHour * NUM_SEC_IN_HOUR ) ) ) / NUM_SEC_IN_MIN;
|
||||
|
||||
uiHourLua = guiHour;
|
||||
uiDayLua = guiDay;
|
||||
uiMinLua = guiMin;
|
||||
|
||||
swprintf( WORLDTIMESTR, L"%s %d, %02d:%02d", gpGameClockString[ STR_GAMECLOCK_DAY_NAME ], guiDay, guiHour, guiMin );
|
||||
|
||||
|
||||
@@ -158,6 +158,7 @@ CHAR16 zString[128];
|
||||
|
||||
HourlyDrugUpdate();
|
||||
|
||||
RebelCommand::HourlyUpdate();
|
||||
|
||||
// WANNE: This check should avoid the resaving of a loaded auto-save game, when entering tactical
|
||||
BOOLEAN doAutoSave = TRUE;
|
||||
@@ -168,10 +169,6 @@ CHAR16 zString[128];
|
||||
|
||||
if (doAutoSave)
|
||||
{
|
||||
// We're also using the doAutoSave check to prevent RebelCommand::DailyUpdate() from running a second time when loading an autosave at 00:00
|
||||
RebelCommand::HourlyUpdate();
|
||||
|
||||
|
||||
if ( AutoSaveToSlot[1] == FALSE && AutoSaveToSlot[2] == FALSE && AutoSaveToSlot[3] == FALSE && AutoSaveToSlot[4] == FALSE )
|
||||
AutoSaveToSlot[0] = TRUE;
|
||||
|
||||
|
||||
@@ -859,7 +859,6 @@ static int l_PlayerTeamFull(lua_State* L);
|
||||
|
||||
static int l_ProfilesStrategicInsertionData(lua_State* L);
|
||||
|
||||
static int l_HealBoxers(lua_State* L);
|
||||
static int l_ResetBoxers(lua_State* L);
|
||||
|
||||
static int l_AddVolunteers(lua_State* L);
|
||||
@@ -913,6 +912,9 @@ static int l_SetIntelAndQuestMapDataForSector(lua_State* L);
|
||||
using namespace std;
|
||||
|
||||
UINT16 idProfil;
|
||||
UINT32 uiHourLua;
|
||||
UINT32 uiDayLua;
|
||||
UINT32 uiMinLua;
|
||||
|
||||
UINT16 PROFILLUA_sSectorX;
|
||||
UINT16 PROFILLUA_sSectorY;
|
||||
@@ -1187,7 +1189,6 @@ static void IniFunction(lua_State* L, BOOLEAN bQuests)
|
||||
lua_register(L, "TacticalCharacterDialogueWithSpecialEvent", l_TacticalCharacterDialogueWithSpecialEvent);
|
||||
lua_register(L, "SetSalary", l_MercSalary);
|
||||
lua_register(L, "SetProfileStrategicInsertionData", l_ProfilesStrategicInsertionData);
|
||||
lua_register(L, "HealBoxers", l_HealBoxers);
|
||||
|
||||
//Get merc
|
||||
lua_register(L, "GetDirection", l_GetDirection); //new
|
||||
@@ -9314,27 +9315,6 @@ static int l_ResetBoxers(lua_State* L)
|
||||
return 0;
|
||||
}
|
||||
|
||||
// heal boxers back to max health in case the player stays in sector while the boxers are resting
|
||||
static int l_HealBoxers(lua_State* L)
|
||||
{
|
||||
for (UINT8 i = 0; i < NUM_BOXERS; ++i)
|
||||
{
|
||||
// Get breath back
|
||||
gubBoxerID[i]->bBreath = gubBoxerID[i]->bBreathMax;
|
||||
gubBoxerID[i]->sBreathRed = 0;
|
||||
// Get life back
|
||||
gubBoxerID[i]->stats.bLife = gubBoxerID[i]->stats.bLifeMax;
|
||||
gubBoxerID[i]->bBleeding = 0;
|
||||
// erase insta-healable injury
|
||||
gubBoxerID[i]->iHealableInjury = 0;
|
||||
|
||||
DebugQuestInfo(String("Lua: healed gubBoxerID[%d] %d back to full health", i, gubBoxerID[i]));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
//Set character to sector Y
|
||||
static int l_SetCharacterSectorY(lua_State* L)
|
||||
{
|
||||
|
||||
@@ -26,6 +26,9 @@ typedef struct
|
||||
|
||||
extern ACTION_ITEM_VALUES ActionItemsValues[500];
|
||||
|
||||
extern UINT32 uiHourLua;
|
||||
extern UINT32 uiDayLua;
|
||||
extern UINT32 uiMinLua;
|
||||
extern UINT16 PROFILLUA_sSectorX;
|
||||
extern UINT16 PROFILLUA_sSectorY;
|
||||
extern UINT8 PROFILLUA_bSectorZ;
|
||||
|
||||
+9
-10
@@ -510,6 +510,15 @@ void IniGlobalGameSetting(lua_State *L)
|
||||
lua_pushinteger(L, gubBoxingMatchesWon);
|
||||
lua_setglobal(L, "gubBoxingMatchesWon");
|
||||
|
||||
lua_pushinteger(L, uiHourLua);
|
||||
lua_setglobal(L, "cHour");
|
||||
|
||||
lua_pushinteger(L, uiDayLua);
|
||||
lua_setglobal(L, "cDay");
|
||||
|
||||
lua_pushinteger(L, uiMinLua);
|
||||
lua_setglobal(L, "cMin");
|
||||
|
||||
lua_pushinteger(L, gbPlayerNum);
|
||||
lua_setglobal(L, "gbPlayerNum");
|
||||
|
||||
@@ -581,16 +590,6 @@ void IniGlobalGameSetting(lua_State *L)
|
||||
lua_pushinteger(L, guiMin);
|
||||
lua_setglobal(L, "guiMin");
|
||||
|
||||
lua_pushinteger(L, guiDay);
|
||||
lua_setglobal(L, "cDay");
|
||||
|
||||
lua_pushinteger(L, guiHour);
|
||||
lua_setglobal(L, "cHour");
|
||||
|
||||
lua_pushinteger(L, guiMin);
|
||||
lua_setglobal(L, "cMin");
|
||||
|
||||
|
||||
lua_pushinteger(L, guiCurrentScreen);
|
||||
lua_setglobal(L, "guiCurrentScreen");
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -4881,14 +4881,14 @@ void ExecuteStrategicAIAction( UINT16 usActionCode, INT16 sSectorX, INT16 sSecto
|
||||
}
|
||||
|
||||
// depending on which cities the player currently holds, we send out attack on multiple cities. We try to make these attacks occur simultaneously, so the player will have to fend off
|
||||
// multiple gigantic attacks on different cities. Ideally, the attacks will be timed so well that the player cannot use a squad in both battles, even with use of the helicopter
|
||||
// multiple gigantic attacks on different cities. Ideally, the attacks will be timed so well that the player cannot use a sqaud in both battles, even with use of the helicopter
|
||||
// we first have to check which cities we have to attack. For that, we simply check wether there are troops in the target sector. If not, we will attack here
|
||||
BOOLEAN fAttack_Grumm = !(SectorInfo[ SEC_H3 ].ubNumTroops > 0);
|
||||
BOOLEAN fAttack_Cambria = !(SectorInfo[ SEC_H8 ].ubNumTroops > 0);
|
||||
BOOLEAN fCambriaSAMOccupied = !(SectorInfo[ SEC_I8 ].ubNumTroops > 0);
|
||||
BOOLEAN fAttack_Alma = !(SectorInfo[ SEC_I13 ].ubNumTroops > 0);
|
||||
BOOLEAN fAttack_Alma = !(SectorInfo[ SEC_I13 ].ubNumTroops > 0);
|
||||
BOOLEAN fAttack_Balime = !(SectorInfo[ SEC_L11 ].ubNumTroops > 0);
|
||||
BOOLEAN fAttack_Chitzena = !(SectorInfo[ SEC_B2 ].ubNumTroops > 0);
|
||||
BOOLEAN fAttack_Chitzena = !(SectorInfo[ SEC_L11 ].ubNumTroops > 0);
|
||||
|
||||
ubNumSoldiers = (UINT8)( gubMinEnemyGroupSize + difficultyMod * 3);
|
||||
|
||||
|
||||
@@ -8472,12 +8472,6 @@ void GetMapKeyboardInput( UINT32 *puiNewEvent )
|
||||
if ( HandleNailsVestFetish( pSoldier, i, NOTHING ) )
|
||||
continue;
|
||||
|
||||
// tais: never strip Madlab's robot of its installed components (ammo,
|
||||
// targeting, chassis/armour, utility) - the player can't re-install them
|
||||
// and the robot would be left defenceless. Only its cargo slot may be emptied.
|
||||
if ( AM_A_ROBOT( pSoldier ) && i != ROBOT_INVENTORY_SLOT )
|
||||
continue;
|
||||
|
||||
AutoPlaceObjectInInventoryStash(&pSoldier->inv[i], pSoldier->sGridNo, pSoldier->pathing.bLevel);
|
||||
DeleteObj(&pSoldier->inv[i]);
|
||||
}
|
||||
|
||||
@@ -1423,7 +1423,7 @@ void AddObjectToArmsDealerInventory( UINT8 ubArmsDealer, OBJECTTYPE *pObject )
|
||||
for (attachmentList::iterator iter = seperateObject[0]->attachments.begin(); iter != seperateObject[0]->attachments.end();) {
|
||||
// ARM: Note: this is only used for selling, not repairs, so attachmentes are seperated when sold to a dealer
|
||||
// If the attachment is detachable
|
||||
if (! (iter->exists() && Item[ iter->usItem ].inseparable))
|
||||
if (! (Item[ iter->usItem ].inseparable ) && iter->exists())
|
||||
{
|
||||
// add this particular attachment (they can't be imprinted, or themselves have attachments!)
|
||||
AddObjectToArmsDealerInventory( ubArmsDealer, &(*iter) );
|
||||
@@ -1931,7 +1931,7 @@ UINT32 CalculateObjectItemRepairTime( UINT8 ubArmsDealer, OBJECTTYPE *pItemObjec
|
||||
// add time to repair any attachments on it
|
||||
for (attachmentList::iterator iter = (*pItemObject)[x]->attachments.begin(); iter != (*pItemObject)[x]->attachments.end(); ++iter) {
|
||||
// if damaged and repairable
|
||||
if ( iter->exists() && ( (*iter)[x]->data.objectStatus < 100 ) && CanDealerRepairItem( ubArmsDealer, iter->usItem ) )
|
||||
if ( ( (*iter)[x]->data.objectStatus < 100 ) && CanDealerRepairItem( ubArmsDealer, iter->usItem ) && iter->exists())
|
||||
{
|
||||
//uiRepairTime += CalculateSimpleItemRepairTime( ubArmsDealer, iter->usItem, (*iter)[x]->data.objectStatus );
|
||||
uiRepairTime += CalculateObjectItemRepairTime( ubArmsDealer, &(*iter) );
|
||||
@@ -1988,7 +1988,7 @@ UINT32 CalculateObjectItemRepairCost( UINT8 ubArmsDealer, OBJECTTYPE *pItemObjec
|
||||
// add cost of repairing any attachments on it
|
||||
for (attachmentList::iterator iter = (*pItemObject)[x]->attachments.begin(); iter != (*pItemObject)[x]->attachments.end(); ++iter) {
|
||||
// if damaged and repairable
|
||||
if ( iter->exists() && ( (*iter)[x]->data.objectStatus < 100 ) && CanDealerRepairItem( ubArmsDealer, iter->usItem ) )
|
||||
if ( ( (*iter)[x]->data.objectStatus < 100 ) && CanDealerRepairItem( ubArmsDealer, iter->usItem ) && iter->exists())
|
||||
{
|
||||
uiRepairCost += CalculateObjectItemRepairCost( ubArmsDealer, &(*iter));
|
||||
}
|
||||
|
||||
+8
-10
@@ -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;
|
||||
@@ -453,14 +454,11 @@ BOOLEAN BoxerAvailable( void )
|
||||
return( FALSE );
|
||||
}
|
||||
|
||||
// NOTE THIS IS NOW BROKEN BECAUSE NPC.C ASSUMES THAT BOXERSAVAILABLE < 3 IS A
|
||||
// SEQUEL FIGHT. Maybe we could check Kingpin's location instead!
|
||||
UINT8 BoxersAvailable( void )
|
||||
{
|
||||
UINT8 ubCount = 0;
|
||||
// Should also run CheckOnBoxers to make sure boxer IDs are set for this function similarly to BoxerAvailable
|
||||
if (CheckOnBoxers() == FALSE)
|
||||
{
|
||||
return(ubCount);
|
||||
}
|
||||
|
||||
for (UINT8 ubLoop = 0; ubLoop < NUM_BOXERS; ++ubLoop)
|
||||
{
|
||||
@@ -514,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 );
|
||||
@@ -564,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]);
|
||||
|
||||
+557
-413
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
|
||||
@@ -10073,7 +10073,7 @@ void DrawArmorValues( OBJECTTYPE * gpItemDescObject )
|
||||
UINT16 iFinalProtectionValue = iProtectionValue;
|
||||
for (attachmentList::iterator iter = (*gpItemDescObject)[0]->attachments.begin(); iter != (*gpItemDescObject)[0]->attachments.end(); ++iter)
|
||||
{
|
||||
if ( iter->exists() && Item[ iter->usItem ].usItemClass == IC_ARMOUR)
|
||||
if (Item[ iter->usItem ].usItemClass == IC_ARMOUR)
|
||||
{
|
||||
iFinalProtectionValue += Armour[Item[iter->usItem].ubClassIndex].ubProtection;
|
||||
}
|
||||
@@ -10099,7 +10099,7 @@ void DrawArmorValues( OBJECTTYPE * gpItemDescObject )
|
||||
UINT16 iComparedFinalProtectionValue = iComparedProtectionValue;
|
||||
for (attachmentList::iterator iter = (*gpComparedItemDescObject)[0]->attachments.begin(); iter != (*gpComparedItemDescObject)[0]->attachments.end(); ++iter)
|
||||
{
|
||||
if ( iter->exists() && Item[ iter->usItem ].usItemClass == IC_ARMOUR)
|
||||
if (Item[ iter->usItem ].usItemClass == IC_ARMOUR)
|
||||
{
|
||||
iComparedFinalProtectionValue += Armour[Item[iter->usItem].ubClassIndex].ubProtection;
|
||||
}
|
||||
|
||||
@@ -643,29 +643,13 @@ void popupCallbackItem(INT16 itemId){
|
||||
i++;p++;
|
||||
}
|
||||
|
||||
static OBJECTTYPE pObjTmp;
|
||||
OBJECTTYPE pObjTmp;
|
||||
pObjTmp.initialize();
|
||||
|
||||
if( bestStack->RemoveObjectAtIndex(leastDamagedIndex, &pObjTmp) ){
|
||||
gpItemPointer = &pObjTmp; // pick up the object (or stack)
|
||||
DoAttachment((UINT8)gubPopupStatusIndex, guiPopupItemPos); // try to attach it
|
||||
|
||||
// If attaching failed, change mouse cursor to item as we're still holding it
|
||||
if ( gpItemPointer->exists() ) {
|
||||
// Set mouse
|
||||
guiExternVo = GetInterfaceGraphicForItem(&(Item[gpItemPointer->usItem]));
|
||||
gusExternVoSubIndex = Item[gpItemPointer->usItem].ubGraphicNum;
|
||||
|
||||
MSYS_ChangeRegionCursor(&gMPanelRegion, EXTERN_CURSOR);
|
||||
MSYS_SetCurrentCursor(EXTERN_CURSOR);
|
||||
fMapInventoryItem = TRUE;
|
||||
fTeamPanelDirty = TRUE;
|
||||
|
||||
//Dirty interface
|
||||
fInterfacePanelDirty = DIRTYLEVEL2;
|
||||
|
||||
UpdateItemHatches();
|
||||
}
|
||||
//gpItemPointer = NULL; // dont drop it!
|
||||
|
||||
gItemDescAttachmentPopups[giActiveAttachmentPopup]->hide();
|
||||
RenderItemDescriptionBox();
|
||||
@@ -6076,8 +6060,7 @@ void ItemDescAmmoCallback(GUI_BUTTON *btn,INT32 reason)
|
||||
gfItemAmmoDown = FALSE;
|
||||
|
||||
//CHRISL: We dont' want to be able to reload guns using the ammo crate from this function
|
||||
//Greysa: add check for ammo item
|
||||
if( gpItemPointer != NULL && ( (Item[gpItemPointer->usItem].usItemClass != IC_AMMO || Magazine[Item[gpItemPointer->usItem].ubClassIndex].ubMagType >= AMMO_BOX) ) || !EnoughPoints(gpItemDescSoldier, APBPConstants[AP_RELOAD_GUN], 0, TRUE) )//dnl ch65 040913
|
||||
if((gpItemPointer != NULL && Magazine[Item[gpItemPointer->usItem].ubClassIndex].ubMagType >= AMMO_BOX) || !EnoughPoints(gpItemDescSoldier, APBPConstants[AP_RELOAD_GUN], 0, TRUE))//dnl ch65 040913
|
||||
{
|
||||
fInterfacePanelDirty = DIRTYLEVEL2;
|
||||
btn->uiFlags &= (~BUTTON_CLICKED_ON );
|
||||
@@ -6100,8 +6083,7 @@ void ItemDescAmmoCallback(GUI_BUTTON *btn,INT32 reason)
|
||||
else
|
||||
{
|
||||
//holding an item
|
||||
//Greysa: add check for ammo item
|
||||
if( Item[gpItemPointer->usItem].usItemClass == IC_AMMO && Magazine[Item[gpItemPointer->usItem].ubClassIndex].ubCalibre == Weapon[Item[gpItemDescObject->usItem].ubClassIndex].ubCalibre )
|
||||
if(Magazine[Item[gpItemPointer->usItem].ubClassIndex].ubCalibre == Weapon[Item[gpItemDescObject->usItem].ubClassIndex].ubCalibre)
|
||||
{
|
||||
ReloadGun(gpItemDescSoldier, gpItemDescObject, gpItemPointer, ubStatusIndex);
|
||||
}
|
||||
|
||||
@@ -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 ) )
|
||||
{
|
||||
|
||||
+104
-137
@@ -1995,7 +1995,7 @@ OBJECTTYPE* FindAttachment( OBJECTTYPE * pObj, UINT16 usItem, UINT8 subObject )
|
||||
{
|
||||
if (pObj->exists() == true) {
|
||||
for (attachmentList::iterator iter = (*pObj)[subObject]->attachments.begin(); iter != (*pObj)[subObject]->attachments.end(); ++iter) {
|
||||
if ( iter->exists() && iter->usItem == usItem )
|
||||
if (iter->usItem == usItem && iter->exists())
|
||||
{
|
||||
return &(*iter);
|
||||
}
|
||||
@@ -2008,7 +2008,7 @@ OBJECTTYPE* FindAttachmentByClass( OBJECTTYPE * pObj, UINT32 uiItemClass, UINT8
|
||||
{
|
||||
if (pObj->exists() == true) {
|
||||
for (attachmentList::iterator iter = (*pObj)[subObject]->attachments.begin(); iter != (*pObj)[subObject]->attachments.end(); ++iter) {
|
||||
if ( iter->exists() && Item[iter->usItem].usItemClass == uiItemClass )
|
||||
if (Item[iter->usItem].usItemClass == uiItemClass && iter->exists())
|
||||
{
|
||||
return &(*iter);
|
||||
}
|
||||
@@ -2021,7 +2021,7 @@ OBJECTTYPE* FindAttachmentByAttachmentClass( OBJECTTYPE * pObj, UINT32 uiAttachm
|
||||
{
|
||||
if (pObj->exists() == true) {
|
||||
for (attachmentList::iterator iter = (*pObj)[subObject]->attachments.begin(); iter != (*pObj)[subObject]->attachments.end(); ++iter) {
|
||||
if ( iter->exists() && Item[iter->usItem].attachmentclass == uiAttachmentClass )
|
||||
if (Item[iter->usItem].attachmentclass == uiAttachmentClass && iter->exists())
|
||||
{
|
||||
return &(*iter);
|
||||
}
|
||||
@@ -2139,7 +2139,7 @@ OBJECTTYPE* FindNonSmokeLaunchableAttachment( OBJECTTYPE * pObj, UINT16 usWeapon
|
||||
|
||||
if (pObj->exists() == true) {
|
||||
for (attachmentList::iterator iter = (*pObj)[0]->attachments.begin(); iter != (*pObj)[0]->attachments.end(); ++iter) {
|
||||
if ( iter->exists() && ValidLaunchable( iter->usItem, usWeapon) && Explosive[Item[iter->usItem].ubClassIndex].ubType != EXPLOSV_SMOKE )
|
||||
if (ValidLaunchable( iter->usItem, usWeapon) && Explosive[Item[iter->usItem].ubClassIndex].ubType != EXPLOSV_SMOKE && iter->exists())
|
||||
{
|
||||
return &(*iter);
|
||||
}
|
||||
@@ -2162,7 +2162,7 @@ BOOLEAN ItemHasAttachments( OBJECTTYPE * pObj, SOLDIERTYPE * pSoldier, UINT8 ite
|
||||
{
|
||||
for(attachmentList::iterator att = (*pObj)[iter]->attachments.begin(); att != (*pObj)[iter]->attachments.end(); ++att)
|
||||
{
|
||||
if ( att->exists() && att->usItem != 0 && !ItemIsHiddenAttachment(att->usItem) )
|
||||
if ( att->usItem != 0 && !ItemIsHiddenAttachment(att->usItem) )
|
||||
{
|
||||
attachmentHidden = FALSE;
|
||||
break;
|
||||
@@ -2178,7 +2178,7 @@ BOOLEAN ItemHasAttachments( OBJECTTYPE * pObj, SOLDIERTYPE * pSoldier, UINT8 ite
|
||||
{
|
||||
for(attachmentList::iterator att = (*pObj)[iter]->attachments.begin(); att != (*pObj)[iter]->attachments.end(); ++att)
|
||||
{
|
||||
if ( att->exists() && att->usItem != 0 && !ItemIsHiddenAttachment(att->usItem) )
|
||||
if ( att->usItem != 0 && !ItemIsHiddenAttachment(att->usItem) )
|
||||
{
|
||||
attachmentHidden = FALSE;
|
||||
break;
|
||||
@@ -3872,111 +3872,13 @@ BOOLEAN AutoReload( SOLDIERTYPE * pSoldier, bool aReloadEvenIfNotEmpty )
|
||||
{
|
||||
OBJECTTYPE *pObj, *pObj2;
|
||||
INT8 bSlot;
|
||||
INT16 bAPCost;
|
||||
INT16 bAPCost;
|
||||
BOOLEAN fRet = FALSE;
|
||||
|
||||
CHECKF( pSoldier );
|
||||
|
||||
// Flugente: check for underbarrel weapons and use that object if necessary
|
||||
pObj = pSoldier->GetUsedWeapon( &(pSoldier->inv[HANDPOS]) );
|
||||
pObj2 = NULL;
|
||||
|
||||
if (pSoldier->IsValidSecondHandShotForReloadingPurposes()) //check for valid second hand weapon for reloading purposes (something that doesn't use ammo)
|
||||
{
|
||||
pObj2 = pSoldier->GetUsedWeapon( &(pSoldier->inv[SECONDHANDPOS]) );
|
||||
}
|
||||
// Greysa: Check if weapon is jammed and unjam it first
|
||||
if ((*pObj)[0]->data.gun.bGunAmmoStatus < 0 || ((pObj2 != NULL) && (*pObj2)[0]->data.gun.bGunAmmoStatus < 0))
|
||||
{
|
||||
if ((*pObj)[0]->data.gun.bGunAmmoStatus < 0)
|
||||
{
|
||||
//borrowed from Weapons.cpp
|
||||
if (EnoughPoints(pSoldier, APBPConstants[AP_UNJAM], APBPConstants[BP_UNJAM], FALSE))
|
||||
{
|
||||
DeductPoints(pSoldier, APBPConstants[AP_UNJAM], APBPConstants[BP_UNJAM]);
|
||||
|
||||
INT8 bChanceMod;
|
||||
|
||||
if (Weapon[pObj->usItem].EasyUnjam)
|
||||
bChanceMod = 100;
|
||||
else
|
||||
bChanceMod = (INT8)(GetReliability(pObj) * 4);
|
||||
|
||||
int iResult = SkillCheck(pSoldier, UNJAM_GUN_CHECK, bChanceMod);
|
||||
|
||||
if (iResult > 0)
|
||||
{
|
||||
// yay! unjammed the gun
|
||||
(*pObj)[0]->data.gun.bGunAmmoStatus *= -1;
|
||||
|
||||
// MECHANICAL/DEXTERITY GAIN: Unjammed a gun
|
||||
|
||||
if (bChanceMod < 100) // don't give exp for unjamming an easily unjammable gun
|
||||
{
|
||||
StatChange(pSoldier, MECHANAMT, 5, FALSE);
|
||||
StatChange(pSoldier, DEXTAMT, 5, FALSE);
|
||||
}
|
||||
|
||||
DirtyMercPanelInterface(pSoldier, DIRTYLEVEL2);
|
||||
PlayJA2Sample(Weapon[Item[pObj->usItem].ubClassIndex].ManualReloadSound, RATE_11025, SoundVolume(HIGHVOLUME, pSoldier->sGridNo), 1, SoundDir(pSoldier->sGridNo));
|
||||
ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, Message[STR_UNJAMMED], pSoldier->GetName(), ItemNames[pObj->usItem]);
|
||||
// merc voice feedback?
|
||||
}
|
||||
else
|
||||
{
|
||||
ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, Message[STR_FAILED_UNJAM], pSoldier->GetName(), ItemNames[pObj->usItem]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, Message[STR_NO_AP_NO_UNJAM], pSoldier->GetName(), ItemNames[pObj->usItem]);
|
||||
}
|
||||
}
|
||||
if ((pObj2 != NULL) && (*pObj2)[0]->data.gun.bGunAmmoStatus < 0)
|
||||
{
|
||||
if (EnoughPoints(pSoldier, APBPConstants[AP_UNJAM], APBPConstants[BP_UNJAM], FALSE))
|
||||
{
|
||||
DeductPoints(pSoldier, APBPConstants[AP_UNJAM], APBPConstants[BP_UNJAM]);
|
||||
|
||||
INT8 bChanceMod;
|
||||
|
||||
if (Weapon[pObj2->usItem].EasyUnjam)
|
||||
bChanceMod = 100;
|
||||
else
|
||||
bChanceMod = (INT8)(GetReliability(pObj2) * 4);
|
||||
|
||||
int iResult = SkillCheck(pSoldier, UNJAM_GUN_CHECK, bChanceMod);
|
||||
|
||||
if (iResult > 0)
|
||||
{
|
||||
// yay! unjammed the gun
|
||||
(*pObj2)[0]->data.gun.bGunAmmoStatus *= -1;
|
||||
|
||||
// MECHANICAL/DEXTERITY GAIN: Unjammed a gun
|
||||
|
||||
if (bChanceMod < 100) // don't give exp for unjamming an easily unjammable gun
|
||||
{
|
||||
StatChange(pSoldier, MECHANAMT, 5, FALSE);
|
||||
StatChange(pSoldier, DEXTAMT, 5, FALSE);
|
||||
}
|
||||
|
||||
DirtyMercPanelInterface(pSoldier, DIRTYLEVEL2);
|
||||
PlayJA2Sample(Weapon[Item[pObj2->usItem].ubClassIndex].ManualReloadSound, RATE_11025, SoundVolume(HIGHVOLUME, pSoldier->sGridNo), 1, SoundDir(pSoldier->sGridNo));
|
||||
ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, Message[STR_UNJAMMED], pSoldier->GetName(), ItemNames[pObj2->usItem]);
|
||||
// merc voice feedback?
|
||||
}
|
||||
else
|
||||
{
|
||||
ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, Message[STR_FAILED_UNJAM], pSoldier->GetName(), ItemNames[pObj2->usItem]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, Message[STR_NO_AP_NO_UNJAM], pSoldier->GetName(), ItemNames[pObj2->usItem]);
|
||||
}
|
||||
}
|
||||
return FALSE; // Greysa: We want to skip reloading if we attempted to unjam, regardless of outcome. Return value doesn't seem to matter as there doesn't seem to be any actual checks on the returned value. I picked FALSE as actual reload hasn't occurred
|
||||
}
|
||||
|
||||
//<SB> manual recharge
|
||||
if ((*pObj)[0]->data.gun.ubGunShotsLeft && !((*pObj)[0]->data.gun.ubGunState & GS_CARTRIDGE_IN_CHAMBER) )
|
||||
@@ -4013,8 +3915,10 @@ BOOLEAN AutoReload( SOLDIERTYPE * pSoldier, bool aReloadEvenIfNotEmpty )
|
||||
|
||||
PlayJA2Sample( Weapon[ Item[pObj->usItem].ubClassIndex ].ManualReloadSound, RATE_11025, SoundVolume( HIGHVOLUME, pSoldier->sGridNo ), 1, SoundDir( pSoldier->sGridNo ) );
|
||||
|
||||
if (pObj2 != NULL)
|
||||
if ( pSoldier->IsValidSecondHandShot( ) )
|
||||
{
|
||||
pObj2 = &(pSoldier->inv[SECONDHANDPOS]);
|
||||
|
||||
if ((*pObj2)[0]->data.gun.ubGunShotsLeft && !((*pObj2)[0]->data.gun.ubGunState & GS_CARTRIDGE_IN_CHAMBER) )
|
||||
{
|
||||
(*pObj2)[0]->data.gun.ubGunState |= GS_CARTRIDGE_IN_CHAMBER;
|
||||
@@ -4026,8 +3930,10 @@ BOOLEAN AutoReload( SOLDIERTYPE * pSoldier, bool aReloadEvenIfNotEmpty )
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pObj2 != NULL)
|
||||
if ( pSoldier->IsValidSecondHandShot( ) )
|
||||
{
|
||||
pObj2 = &(pSoldier->inv[SECONDHANDPOS]);
|
||||
|
||||
if ((*pObj2)[0]->data.gun.ubGunShotsLeft && !((*pObj2)[0]->data.gun.ubGunState & GS_CARTRIDGE_IN_CHAMBER) )
|
||||
{
|
||||
(*pObj2)[0]->data.gun.ubGunState |= GS_CARTRIDGE_IN_CHAMBER;
|
||||
@@ -4083,19 +3989,21 @@ BOOLEAN AutoReload( SOLDIERTYPE * pSoldier, bool aReloadEvenIfNotEmpty )
|
||||
// if we are valid for two-pistol shooting (reloading) and we have enough APs still
|
||||
// then do a reload of both guns!
|
||||
// Flugente: only reload if it's empty, or we really want to
|
||||
if ( pObj2 != NULL && ( aReloadEvenIfNotEmpty || !EnoughAmmo( pSoldier, FALSE, SECONDHANDPOS ) ) )
|
||||
if ( pSoldier->IsValidSecondHandShotForReloadingPurposes()
|
||||
&& ( aReloadEvenIfNotEmpty || !EnoughAmmo( pSoldier, FALSE, SECONDHANDPOS ) ) )
|
||||
{
|
||||
// Flugente: check for underbarrel weapons and use that object if necessary
|
||||
pObj = pSoldier->GetUsedWeapon( &( pSoldier->inv[SECONDHANDPOS] ) );
|
||||
|
||||
bSlot = FindAmmoToReload( pSoldier, SECONDHANDPOS, NO_SLOT );
|
||||
if ( bSlot != NO_SLOT )
|
||||
{
|
||||
// ce would reload using this ammo!
|
||||
bAPCost = GetAPsToReloadGunWithAmmo( pSoldier, pObj2, &( pSoldier->inv[bSlot] ) );
|
||||
bAPCost = GetAPsToReloadGunWithAmmo( pSoldier, pObj, &( pSoldier->inv[bSlot] ) );
|
||||
if ( EnoughPoints( pSoldier, (INT16)bAPCost, 0, FALSE ) )
|
||||
{
|
||||
// reload the 2nd gun too
|
||||
fRet = ReloadGun( pSoldier, pObj2, &( pSoldier->inv[bSlot] ) );
|
||||
fRet = ReloadGun( pSoldier, pObj, &( pSoldier->inv[bSlot] ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -5658,7 +5566,7 @@ BOOLEAN OBJECTTYPE::AttachObjectNAS( SOLDIERTYPE * pSoldier, OBJECTTYPE * pAttac
|
||||
|
||||
//Madd: if the attaching/merging item had any attachments on it, then try to move them to the first result
|
||||
for (attachmentList::iterator iter = (*pAttachment)[0]->attachments.begin(); iter != (*pAttachment)[0]->attachments.end();) {
|
||||
if( iter->exists() && ValidItemAttachmentSlot(this, iter->usItem, TRUE, FALSE, subObject )){
|
||||
if( ValidItemAttachmentSlot(this, iter->usItem, TRUE, FALSE, subObject )){
|
||||
//This seems to be rather valid. Can't be 100% sure though.
|
||||
OBJECTTYPE tempAttachment; // Madd: we must recreate the attachments because they may themselves have default inseparable attachments...
|
||||
CreateItem(iter->usItem, (*iter)[0]->data.objectStatus, &tempAttachment);
|
||||
@@ -5995,7 +5903,7 @@ void RemoveProhibitedAttachments(SOLDIERTYPE* pSoldier, OBJECTTYPE* pObj, UINT16
|
||||
|
||||
//Start by trying to re-attach inseperable items. They take precedence over items that can normally be removed
|
||||
for (attachmentList::iterator iter = tempAttachList.begin(); iter != tempAttachList.end();) {
|
||||
if( iter->exists() && Item[iter->usItem].inseparable && ValidItemAttachmentSlot(pObj, iter->usItem, TRUE, FALSE, 0, -1, 0, NULL, usAttachmentSlotIndexVector)){
|
||||
if(Item[iter->usItem].inseparable && ValidItemAttachmentSlot(pObj, iter->usItem, TRUE, FALSE, 0, -1, 0, NULL, usAttachmentSlotIndexVector)){
|
||||
//This seems to be rather valid. Can't be 100% sure though.
|
||||
if(pObj->AttachObject(NULL, &(*iter), FALSE, 0, -1, FALSE, usAttachmentSlotIndexVector)){
|
||||
//Ok now we can be sure, lets remove this object so we don't try to drop it later.
|
||||
@@ -6009,7 +5917,7 @@ void RemoveProhibitedAttachments(SOLDIERTYPE* pSoldier, OBJECTTYPE* pObj, UINT16
|
||||
}
|
||||
//Try to attach all the other attachments that didn't fit their current slot.
|
||||
for (attachmentList::iterator iter = tempAttachList.begin(); iter != tempAttachList.end();) {
|
||||
if( iter->exists() && ValidItemAttachmentSlot(pObj, iter->usItem, TRUE, FALSE, 0, -1, 0, NULL, usAttachmentSlotIndexVector)){
|
||||
if(ValidItemAttachmentSlot(pObj, iter->usItem, TRUE, FALSE, 0, -1, 0, NULL, usAttachmentSlotIndexVector)){
|
||||
//This seems to be rather valid. Can't be 100% sure though.
|
||||
if(pObj->AttachObject(NULL, &(*iter), FALSE, 0, -1, FALSE, usAttachmentSlotIndexVector)){
|
||||
//Ok now we can be sure, lets remove this object so we don't try to drop it later.
|
||||
@@ -6185,7 +6093,7 @@ attachmentList ReInitMergedItem(SOLDIERTYPE* pSoldier, OBJECTTYPE* pObj, UINT16
|
||||
|
||||
//First re-attach any slot-changing attachments.
|
||||
for (attachmentList::iterator iter = tempSlotChangingAttachList.begin(); iter != tempSlotChangingAttachList.end();) {
|
||||
if( iter->exists() && ValidItemAttachmentSlot(pObj, iter->usItem, TRUE, FALSE, ubStatusIndex )){
|
||||
if( ValidItemAttachmentSlot(pObj, iter->usItem, TRUE, FALSE, ubStatusIndex )){
|
||||
//This seems to be rather valid. Can't be 100% sure though.
|
||||
OBJECTTYPE tempAttachment; // Madd: we must recreate the attachments because they may themselves have default inseparable attachments...
|
||||
CreateItem(iter->usItem, (*iter)[0]->data.objectStatus, &tempAttachment);
|
||||
@@ -6202,7 +6110,7 @@ attachmentList ReInitMergedItem(SOLDIERTYPE* pSoldier, OBJECTTYPE* pObj, UINT16
|
||||
|
||||
//Time to re-attach the other attachments, if we can. I am the king of copy pasta.
|
||||
for (attachmentList::iterator iter = tempAttachList.begin(); iter != tempAttachList.end();) {
|
||||
if( iter->exists() && ValidItemAttachmentSlot(pObj, iter->usItem, TRUE, FALSE, ubStatusIndex)){
|
||||
if( ValidItemAttachmentSlot(pObj, iter->usItem, TRUE, FALSE, ubStatusIndex)){
|
||||
//This seems to be rather valid. Can't be 100% sure though.
|
||||
OBJECTTYPE tempAttachment; // Madd: we must recreate the attachments because they may themselves have default inseparable attachments...
|
||||
CreateItem(iter->usItem, (*iter)[0]->data.objectStatus, &tempAttachment);
|
||||
@@ -6219,7 +6127,7 @@ attachmentList ReInitMergedItem(SOLDIERTYPE* pSoldier, OBJECTTYPE* pObj, UINT16
|
||||
|
||||
//drop all items we couldn't re-attach.
|
||||
for (attachmentList::iterator iter = tempSlotChangingAttachList.begin(); iter != tempSlotChangingAttachList.end(); ++iter) {
|
||||
if ( iter->exists() && Item[iter->usItem].inseparable != 1)
|
||||
if ( Item[iter->usItem].inseparable != 1)
|
||||
{//WarmSteel - Couldn't re-attach this item, try to drop it.
|
||||
if (pSoldier) {
|
||||
OBJECTTYPE tempAttachment; // Madd: we must recreate the attachments because they may themselves have default inseparable attachments...
|
||||
@@ -6237,7 +6145,7 @@ attachmentList ReInitMergedItem(SOLDIERTYPE* pSoldier, OBJECTTYPE* pObj, UINT16
|
||||
}
|
||||
//and the rest too
|
||||
for (attachmentList::iterator iter = tempAttachList.begin(); iter != tempAttachList.end(); ++iter) {
|
||||
if ( iter->exists() && Item[iter->usItem].inseparable != 1)
|
||||
if ( Item[iter->usItem].inseparable != 1)
|
||||
{//WarmSteel - Couldn't re-attach this item, try to drop it.
|
||||
if (pSoldier) {
|
||||
OBJECTTYPE tempAttachment; // Madd: we must recreate the attachments because they may themselves have default inseparable attachments...
|
||||
@@ -10422,7 +10330,7 @@ BOOLEAN NCTHIsScoped( OBJECTTYPE * pObj )
|
||||
return TRUE;
|
||||
|
||||
for (attachmentList::iterator iter = (*pObj)[0]->attachments.begin(); iter != (*pObj)[0]->attachments.end(); ++iter) {
|
||||
if ( iter->exists() && Item[iter->usItem].scopemagfactor > 1.0 )
|
||||
if ( Item[iter->usItem].scopemagfactor > 1.0 && iter->exists() )
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
@@ -10441,7 +10349,7 @@ BOOLEAN IsScoped( OBJECTTYPE * pObj )
|
||||
return TRUE;
|
||||
|
||||
for (attachmentList::iterator iter = (*pObj)[0]->attachments.begin(); iter != (*pObj)[0]->attachments.end(); ++iter) {
|
||||
if ( iter->exists() && Item[iter->usItem].aimbonus > 0 )
|
||||
if ( Item[iter->usItem].aimbonus > 0 && iter->exists())
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
@@ -10528,7 +10436,7 @@ INT16 GetBaseScopeAimBonus( OBJECTTYPE * pObj, INT32 iRange )
|
||||
//Search for the most powerful scope we can use.
|
||||
for(attachmentList::iterator iter = (*pObj)[0]->attachments.begin(); iter != (*pObj)[0]->attachments.end(); iter++)
|
||||
{
|
||||
if( iter->exists() && Item[iter->usItem].aimbonus > bonus && iRange >= Item[iter->usItem].minrangeforaimbonus )
|
||||
if(Item[iter->usItem].aimbonus > bonus && iRange >= Item[iter->usItem].minrangeforaimbonus && iter->exists())
|
||||
{
|
||||
bonus = Item[iter->usItem].aimbonus;
|
||||
}
|
||||
@@ -10606,7 +10514,7 @@ UINT32 FindRangeBonusAttachment( OBJECTTYPE * pObj )
|
||||
{
|
||||
if (pObj->exists() == true) {
|
||||
for (attachmentList::iterator iter = (*pObj)[0]->attachments.begin(); iter != (*pObj)[0]->attachments.end(); ++iter) {
|
||||
if ( iter->exists() && Item[iter->usItem].rangebonus > 0 )
|
||||
if (Item[iter->usItem].rangebonus > 0 && iter->exists())
|
||||
{
|
||||
return( Item[iter->usItem].uiIndex );
|
||||
}
|
||||
@@ -10627,7 +10535,7 @@ INT16 GetRangeBonus( OBJECTTYPE * pObj )
|
||||
bonus += Item[(*pObj)[0]->data.gun.usGunAmmoItem].rangebonus;
|
||||
|
||||
for (attachmentList::iterator iter = (*pObj)[0]->attachments.begin(); iter != (*pObj)[0]->attachments.end(); ++iter) {
|
||||
if ( iter->exists() && !ItemIsDuckbill(iter->usItem) || (ItemIsDuckbill(iter->usItem) && (*pObj)[0]->data.gun.ubGunAmmoType == AMMO_BUCKSHOT ) )
|
||||
if ( !ItemIsDuckbill(iter->usItem) || (ItemIsDuckbill(iter->usItem) && (*pObj)[0]->data.gun.ubGunAmmoType == AMMO_BUCKSHOT ) && iter->exists())
|
||||
bonus += BonusReduce( Item[iter->usItem].rangebonus, (*iter)[0]->data.objectStatus );
|
||||
}
|
||||
}
|
||||
@@ -12053,7 +11961,7 @@ BOOLEAN HasThermalOptics( SOLDIERTYPE * pSoldier )
|
||||
OBJECTTYPE* pObj = &pSoldier->inv[HANDPOS];
|
||||
if (pObj->exists() == true) {
|
||||
for (attachmentList::iterator iter = (*pObj)[0]->attachments.begin(); iter != (*pObj)[0]->attachments.end(); ++iter) {
|
||||
if ( iter->exists() && ItemIsThermalOptics(iter->usItem) )
|
||||
if (ItemIsThermalOptics(iter->usItem) && iter->exists() )
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
@@ -12110,7 +12018,7 @@ BOOLEAN IsDuckbill( OBJECTTYPE * pObj )
|
||||
return TRUE;
|
||||
|
||||
for (attachmentList::iterator iter = (*pObj)[0]->attachments.begin(); iter != (*pObj)[0]->attachments.end(); ++iter) {
|
||||
if ( iter->exists() && ItemIsDuckbill(iter->usItem) )
|
||||
if (ItemIsDuckbill(iter->usItem) && iter->exists() )
|
||||
{
|
||||
return( TRUE );
|
||||
}
|
||||
@@ -12161,7 +12069,7 @@ BOOLEAN IsDetonatorAttached( OBJECTTYPE * pObj )
|
||||
// return TRUE;
|
||||
|
||||
for (attachmentList::iterator iter = (*pObj)[0]->attachments.begin(); iter != (*pObj)[0]->attachments.end(); ++iter) {
|
||||
if ( iter->exists() && IsAttachmentClass( iter->usItem, AC_DETONATOR ) )
|
||||
if ( IsAttachmentClass( iter->usItem, AC_DETONATOR ) && iter->exists() )
|
||||
{
|
||||
return( TRUE );
|
||||
}
|
||||
@@ -12177,7 +12085,7 @@ BOOLEAN IsRemoteDetonatorAttached( OBJECTTYPE * pObj )
|
||||
// return TRUE;
|
||||
|
||||
for (attachmentList::iterator iter = (*pObj)[0]->attachments.begin(); iter != (*pObj)[0]->attachments.end(); ++iter) {
|
||||
if ( iter->exists() && IsAttachmentClass( iter->usItem, AC_REMOTEDET ) )
|
||||
if ( IsAttachmentClass( iter->usItem, AC_REMOTEDET ) && iter->exists() )
|
||||
{
|
||||
return( TRUE );
|
||||
}
|
||||
@@ -12449,7 +12357,7 @@ OBJECTTYPE* FindAttachedBatteries( OBJECTTYPE * pObj )
|
||||
{
|
||||
if (pObj->exists() == true) {
|
||||
for (attachmentList::iterator iter = (*pObj)[0]->attachments.begin(); iter != (*pObj)[0]->attachments.end(); ++iter) {
|
||||
if ( iter->exists() && ItemIsBatteries(iter->usItem) )
|
||||
if (ItemIsBatteries(iter->usItem) && iter->exists())
|
||||
{
|
||||
return( &(*iter) );
|
||||
}
|
||||
@@ -12518,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 )
|
||||
{
|
||||
@@ -12681,7 +12634,7 @@ INT16 GetCamoBonus( OBJECTTYPE * pObj )
|
||||
bonus = (Item[pObj->usItem].camobonus);// * (WEAPON_STATUS_MOD((*pObj)[0]->data.objectStatus) / 100)) ;
|
||||
|
||||
for (attachmentList::iterator iter = (*pObj)[0]->attachments.begin(); iter != (*pObj)[0]->attachments.end(); ++iter) {
|
||||
if ( iter->exists() && !ItemIsCamoKit(iter->usItem) )
|
||||
if (!ItemIsCamoKit(iter->usItem) && iter->exists())
|
||||
bonus += (INT16) (Item[iter->usItem].camobonus);// * (WEAPON_STATUS_MOD((*iter)[0]->data.objectStatus) / 100));
|
||||
}
|
||||
}
|
||||
@@ -12694,7 +12647,7 @@ INT16 GetUrbanCamoBonus( OBJECTTYPE * pObj )
|
||||
bonus = (Item[pObj->usItem].urbanCamobonus);// * (WEAPON_STATUS_MOD((*pObj)[0]->data.objectStatus) / 100)) ;
|
||||
|
||||
for (attachmentList::iterator iter = (*pObj)[0]->attachments.begin(); iter != (*pObj)[0]->attachments.end(); ++iter) {
|
||||
if ( iter->exists() && !ItemIsCamoKit(iter->usItem) )
|
||||
if (!ItemIsCamoKit(iter->usItem) && iter->exists())
|
||||
bonus += (INT16) (Item[iter->usItem].urbanCamobonus);// * (WEAPON_STATUS_MOD((*iter)[0]->data.objectStatus) / 100));
|
||||
}
|
||||
}
|
||||
@@ -12707,7 +12660,7 @@ INT16 GetDesertCamoBonus( OBJECTTYPE * pObj )
|
||||
bonus = (Item[pObj->usItem].desertCamobonus);// * (WEAPON_STATUS_MOD((*pObj)[0]->data.objectStatus) / 100)) ;
|
||||
|
||||
for (attachmentList::iterator iter = (*pObj)[0]->attachments.begin(); iter != (*pObj)[0]->attachments.end(); ++iter) {
|
||||
if ( iter->exists() && !ItemIsCamoKit(iter->usItem) )
|
||||
if (!ItemIsCamoKit(iter->usItem) && iter->exists())
|
||||
bonus += (INT16) (Item[iter->usItem].desertCamobonus);// * (WEAPON_STATUS_MOD((*iter)[0]->data.objectStatus) / 100));
|
||||
}
|
||||
}
|
||||
@@ -12720,7 +12673,7 @@ INT16 GetSnowCamoBonus( OBJECTTYPE * pObj )
|
||||
bonus = (Item[pObj->usItem].snowCamobonus);// * (WEAPON_STATUS_MOD((*pObj)[0]->data.objectStatus) / 100)) ;
|
||||
|
||||
for (attachmentList::iterator iter = (*pObj)[0]->attachments.begin(); iter != (*pObj)[0]->attachments.end(); ++iter) {
|
||||
if ( iter->exists() && !ItemIsCamoKit(iter->usItem) )
|
||||
if (!ItemIsCamoKit(iter->usItem) && iter->exists())
|
||||
bonus += (INT16) (Item[iter->usItem].snowCamobonus);// * (WEAPON_STATUS_MOD((*iter)[0]->data.objectStatus) / 100));
|
||||
}
|
||||
}
|
||||
@@ -13165,7 +13118,7 @@ OBJECTTYPE* FindNightGogglesInInv( SOLDIERTYPE * pSoldier, INT8 * bSlot, BOOLEAN
|
||||
if (searchAllInventory) {
|
||||
for (UINT8 loop = 0; loop < pSoldier->inv[bLoop].ubNumberOfObjects; loop ++){
|
||||
for (attachmentList::iterator iter = pSoldier->inv[bLoop][loop]->attachments.begin(); iter != pSoldier->inv[bLoop][loop]->attachments.end(); ++iter) {
|
||||
if ( iter->exists() && Item[ iter->usItem ].cavevisionrangebonus > bonusToBeat && Item[ iter->usItem ].usItemClass == IC_FACE ) {
|
||||
if ( Item[ iter->usItem ].cavevisionrangebonus > bonusToBeat && Item[ iter->usItem ].usItemClass == IC_FACE && iter->exists() ) {
|
||||
pGoggles = &(*iter);
|
||||
*bSlot = bLoop;
|
||||
*isAttach = TRUE;
|
||||
@@ -13223,7 +13176,7 @@ INT16 GetMinRangeForAimBonus( SOLDIERTYPE* pSoldier, OBJECTTYPE * pObj )
|
||||
attachmentList::iterator iterend = (*pObj)[0]->attachments.end();
|
||||
for (attachmentList::iterator iter = (*pObj)[0]->attachments.begin(); iter != iterend; ++iter)
|
||||
{
|
||||
if ( !gGameExternalOptions.fScopeModes || (iter->exists() && !IsAttachmentClass(iter->usItem, AC_SCOPE|AC_SIGHT|AC_IRONSIGHT )) )
|
||||
if ( !gGameExternalOptions.fScopeModes || !IsAttachmentClass(iter->usItem, AC_SCOPE|AC_SIGHT|AC_IRONSIGHT ) )
|
||||
bonus += Item[iter->usItem].minrangeforaimbonus;
|
||||
}
|
||||
}
|
||||
@@ -14141,7 +14094,7 @@ INT16 GetAverageBestLaserRange( OBJECTTYPE * pObj )
|
||||
}
|
||||
for (attachmentList::iterator iter = (*pObj)[0]->attachments.begin(); iter != (*pObj)[0]->attachments.end(); ++iter)
|
||||
{
|
||||
if ( iter->exists() && Item[iter->usItem].bestlaserrange > 0 )
|
||||
if (Item[iter->usItem].bestlaserrange > 0 && iter->exists())
|
||||
{
|
||||
numModifiers++;
|
||||
bonus += (FLOAT) Item[iter->usItem].bestlaserrange;
|
||||
@@ -14167,7 +14120,7 @@ INT16 GetBestLaserRange( OBJECTTYPE * pObj )
|
||||
}
|
||||
for (attachmentList::iterator iter = (*pObj)[0]->attachments.begin(); iter != (*pObj)[0]->attachments.end(); ++iter)
|
||||
{
|
||||
if ( iter->exists() && Item[iter->usItem].bestlaserrange > range )
|
||||
if (Item[iter->usItem].bestlaserrange > range && iter->exists())
|
||||
{
|
||||
range = Item[iter->usItem].bestlaserrange;
|
||||
}
|
||||
@@ -15180,7 +15133,7 @@ BOOLEAN OBJECTTYPE::TransformObject( SOLDIERTYPE * pSoldier, UINT8 ubStatusIndex
|
||||
// I am the prince of copy pasta ;)
|
||||
for (attachmentList::iterator iter = unattachableList.begin(); iter != unattachableList.end();)
|
||||
{
|
||||
if( iter->exists() && ValidItemAttachmentSlot(&gTempObject, iter->usItem, TRUE, FALSE, ubStatusIndex ))
|
||||
if( ValidItemAttachmentSlot(&gTempObject, iter->usItem, TRUE, FALSE, ubStatusIndex ))
|
||||
{
|
||||
//This seems to be rather valid. Can't be 100% sure though.
|
||||
OBJECTTYPE tempAttachment; // Madd: we must recreate the attachments because they may themselves have default inseparable attachments...
|
||||
@@ -15868,7 +15821,7 @@ INT32 GetPercentRangeBonus( OBJECTTYPE * pObj )
|
||||
bonus = ( bonus * ( 100 + Item[(*pObj)[0]->data.gun.usGunAmmoItem].percentrangebonus ) ) / 100;
|
||||
|
||||
for (attachmentList::iterator iter = (*pObj)[0]->attachments.begin(); iter != (*pObj)[0]->attachments.end(); ++iter) {
|
||||
if ( iter->exists() && !ItemIsDuckbill(iter->usItem) || (ItemIsDuckbill(iter->usItem) && (*pObj)[0]->data.gun.ubGunAmmoType == AMMO_BUCKSHOT ))
|
||||
if ( !ItemIsDuckbill(iter->usItem) || (ItemIsDuckbill(iter->usItem) && (*pObj)[0]->data.gun.ubGunAmmoType == AMMO_BUCKSHOT ))
|
||||
bonus = ( bonus * ( 100 + BonusReduce( Item[iter->usItem].percentrangebonus, (*iter)[0]->data.objectStatus ) ) ) / 100;
|
||||
}
|
||||
}
|
||||
@@ -16137,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 );
|
||||
|
||||
+1
-1
@@ -171,7 +171,7 @@ INT32 GetSpreadPattern( OBJECTTYPE * pObj )
|
||||
//If there are attachments, check them. Stop on the first one with something defined.
|
||||
//Dear God, I hate C++ iterators. What a fugly mess. //WarmSteel - I made it even messier ;3
|
||||
for (attachmentList::iterator iter = pObj[0][0]->attachments.begin(); iter != pObj[0][0]->attachments.end(); ++iter){
|
||||
if( iter->exists() && (n=Item[ iter->usItem ].spreadPattern) ){
|
||||
if( (n=Item[ iter->usItem ].spreadPattern) && iter->exists()){
|
||||
//An attachment has it, and it trumps everything, so return it's value.
|
||||
return n;}
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ namespace LogicalBodyTypes {
|
||||
data->filter = FilterDB::Instance().FindFilter(aFilter);
|
||||
if (data->filter == NULL) throw XMLParseException("Unknown filter specified!", name, data->pParser);
|
||||
}
|
||||
if (aPalette != NULL && strcmp(aPalette, "") != 0 && strcmp(aPalette, "default") != 0) {
|
||||
if (aPalette != NULL && strcmp(aFilter, "") != 0 && strcmp(aFilter, "default") != 0) {
|
||||
data->paletteTable = PaletteDB::Instance().FindPaletteTable(aPalette);
|
||||
if (data->paletteTable == NULL) throw XMLParseException("Unknown palette specified!", name, data->pParser);
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@ FLOAT gdMajorMapVersion = MAJOR_MAP_VERSION;
|
||||
BOOLEAN gfWorldLoaded;
|
||||
|
||||
MAPCREATE_STRUCT gMapInformation;
|
||||
MAPCREATE_STRUCT gMapInformationAutoResolve;
|
||||
|
||||
//CHRISL: MINOR_MAP_VERSION information moved to worlddef.h by ADB. We're using these values elsewhere and need them
|
||||
// in the header file
|
||||
|
||||
@@ -57,7 +57,6 @@ public:
|
||||
};
|
||||
|
||||
extern MAPCREATE_STRUCT gMapInformation;
|
||||
extern MAPCREATE_STRUCT gMapInformationAutoResolve;
|
||||
|
||||
void LoadMapInformation(INT8** hBuffer, FLOAT dMajorMapVersion);
|
||||
void SaveMapInformation(HWFILE hFile, FLOAT dMajorMapVersion, UINT8 ubMinorMapVersion);
|
||||
|
||||
+43
-15
@@ -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
|
||||
@@ -11403,7 +11431,7 @@ void TeamDropAll(UINT8 bTeam, BOOLEAN fForce)
|
||||
// if soldier is in the current sector, drop all equipment (that has the TAKEN_BY_MILITIA-flag set)
|
||||
if( pSoldier->bActive && ( pSoldier->sSectorX == gWorldSectorX ) && ( pSoldier->sSectorY == gWorldSectorY ) && ( pSoldier->bSectorZ == gbWorldSectorZ) )
|
||||
{
|
||||
pSoldier->DropSectorEquipment( NOWHERE );
|
||||
pSoldier->DropSectorEquipment();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+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
|
||||
|
||||
|
||||
+36
-100
@@ -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)
|
||||
@@ -1380,7 +1350,7 @@ INT16 AStarPathfinder::CalcAP(int const terrainCost, UINT8 const direction)
|
||||
}
|
||||
|
||||
// Flugente: dragging someone
|
||||
if ( pSoldier->IsDragging() )
|
||||
if ( pSoldier->IsDragging( false ) )
|
||||
{
|
||||
movementAPCost *= gItemSettings.fDragAPCostModifier;
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -713,10 +713,6 @@ void RemoveCorpse( INT32 iCorpseID )
|
||||
DeleteAniTile( gRottingCorpse[ iCorpseID ].pAniTile );
|
||||
|
||||
FreeCorpsePalettes( &( gRottingCorpse[ iCorpseID ] ) );
|
||||
|
||||
const auto sGridNo = gRottingCorpse[iCorpseID].def.sGridNo;
|
||||
auto pStructure = FindLastStructure(sGridNo, STRUCTURE_CORPSE);
|
||||
DeleteStructureFromWorld(pStructure);
|
||||
}
|
||||
|
||||
BOOLEAN CreateCorpsePalette( ROTTING_CORPSE *pCorpse )
|
||||
@@ -1066,7 +1062,7 @@ BOOLEAN TurnSoldierIntoCorpse( SOLDIERTYPE *pSoldier, BOOLEAN fRemoveMerc, BOOLE
|
||||
DropKeysInKeyRing( pSoldier, pSoldier->sGridNo, pSoldier->pathing.bLevel, bVisible, FALSE, 0, FALSE );
|
||||
|
||||
// Flugente: even if we forbid militia from dropping their equipment, they will still drop what they took via sector inventory (this functions only drops what they took)
|
||||
pSoldier->DropSectorEquipment( NOWHERE );
|
||||
pSoldier->DropSectorEquipment();
|
||||
}
|
||||
|
||||
// Make team look for items
|
||||
@@ -1112,8 +1108,7 @@ BOOLEAN TurnSoldierIntoCorpse( SOLDIERTYPE *pSoldier, BOOLEAN fRemoveMerc, BOOLE
|
||||
iCorpseID = AddRottingCorpse( &Corpse );
|
||||
|
||||
// If this is our guy......make visible...
|
||||
//if ( pSoldier->bTeam == gbPlayerNum )
|
||||
if ( iCorpseID != -1 )
|
||||
if ( iCorpseID != -1 && pSoldier->bTeam == OUR_TEAM || gbPublicOpplist[OUR_TEAM][pSoldier->ubID] == SEEN_CURRENTLY )
|
||||
{
|
||||
MakeCorpseVisible( pSoldier, &( gRottingCorpse[ iCorpseID ] ) );
|
||||
}
|
||||
@@ -1775,10 +1770,10 @@ BOOLEAN IsValidDecapitationCorpse( ROTTING_CORPSE *pCorpse )
|
||||
|
||||
ROTTING_CORPSE *GetCorpseAtGridNo( INT32 sGridNo, INT8 bLevel )
|
||||
{
|
||||
STRUCTURE *pStructure, *pBaseStructure;
|
||||
STRUCTURE *pStructure, *pBaseStructure;
|
||||
INT32 sBaseGridNo;
|
||||
|
||||
pStructure = FindLastStructure( sGridNo, STRUCTURE_CORPSE );
|
||||
pStructure = FindStructure( sGridNo, STRUCTURE_CORPSE );
|
||||
|
||||
if ( pStructure != NULL )
|
||||
{
|
||||
@@ -2540,6 +2535,9 @@ void LookForAndMayCommentOnSeeingCorpse( SOLDIERTYPE *pSoldier, INT32 sGridNo, U
|
||||
return;
|
||||
}
|
||||
|
||||
if (pCorpse->fActivated && pCorpse->def.bVisible != 1)
|
||||
pCorpse->def.bVisible = 1;
|
||||
|
||||
if ( pCorpse->def.ubType != ROTTING_STAGE2 )
|
||||
{
|
||||
return;
|
||||
|
||||
@@ -3031,7 +3031,7 @@ BOOLEAN RepairIsDone(DEALER_SPECIAL_ITEM* pSpecial)
|
||||
|
||||
// max condition of all permanent attachments on it
|
||||
for (attachmentList::iterator iter = RepairItem.ItemObject[0]->attachments.begin(); iter != RepairItem.ItemObject[0]->attachments.end(); ++iter) {
|
||||
if ( iter->exists() && CanDealerRepairItem( gbSelectedArmsDealerID, iter->usItem ) )
|
||||
if ( CanDealerRepairItem( gbSelectedArmsDealerID, iter->usItem ) && iter->exists())
|
||||
{
|
||||
// fix it up
|
||||
(*iter)[0]->data.objectStatus = 100;
|
||||
|
||||
@@ -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 );
|
||||
}
|
||||
|
||||
@@ -9046,7 +9046,7 @@ void CalculateSoldierAniSpeed( SOLDIERTYPE *pSoldier, SOLDIERTYPE *pStatsSoldier
|
||||
}
|
||||
|
||||
// Flugente: drag people
|
||||
if ( pSoldier->IsDragging() )
|
||||
if ( pSoldier->IsDragging( false ) )
|
||||
{
|
||||
pSoldier->sAniDelay = gItemSettings.fDragAPCostModifier * pSoldier->sAniDelay;
|
||||
}
|
||||
@@ -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 )
|
||||
{
|
||||
@@ -11516,7 +11595,7 @@ void SOLDIERTYPE::MoveMerc( FLOAT dMovementChange, FLOAT dAngle, BOOLEAN fCheckR
|
||||
|
||||
// Flugente: as we move a tile, we would now be too far away to drag someone.
|
||||
// So remember whether we were dragging (we have to set our position now, otherwise the person we drag woul soon occupy our gridno).
|
||||
BOOLEAN currentlydragging = this->IsDragging(true);
|
||||
BOOLEAN currentlydragging = this->IsDragging();
|
||||
INT32 sOldGridNo = this->sGridNo;
|
||||
|
||||
// OK, set new position
|
||||
@@ -14668,7 +14747,7 @@ void SOLDIERTYPE::SoldierInventoryCoolDown( void )
|
||||
attachmentList::iterator iterend = (*pObj)[0]->attachments.end( );
|
||||
for ( attachmentList::iterator iter = (*pObj)[0]->attachments.begin( ); iter != iterend; ++iter )
|
||||
{
|
||||
if ( iter->exists() && Item[iter->usItem].usFlashLightRange )
|
||||
if ( Item[iter->usItem].usFlashLightRange )
|
||||
flashlightfound = TRUE;
|
||||
}
|
||||
}
|
||||
@@ -16818,7 +16897,7 @@ BOOLEAN SOLDIERTYPE::UpdateMultiTurnAction( )
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void SOLDIERTYPE::DropSectorEquipment( INT32 UnloadedSectorGridNo)
|
||||
void SOLDIERTYPE::DropSectorEquipment( )
|
||||
{
|
||||
// not if we already dropped the gear
|
||||
if ( this->usSoldierFlagMask & SOLDIER_EQUIPMENT_DROPPED )
|
||||
@@ -16887,7 +16966,7 @@ void SOLDIERTYPE::DropSectorEquipment( INT32 UnloadedSectorGridNo)
|
||||
}
|
||||
}
|
||||
|
||||
AddItemsToUnLoadedSector( this->sSectorX, this->sSectorY, this->bSectorZ, UnloadedSectorGridNo, counter, pObject, 0, WORLD_ITEM_REACHABLE, 0, 1, FALSE );
|
||||
AddItemsToUnLoadedSector( this->sSectorX, this->sSectorY, this->bSectorZ, RandomGridNo( ), counter, pObject, 0, WORLD_ITEM_REACHABLE, 0, 1, FALSE );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -24226,7 +24305,7 @@ BOOLEAN SOLDIERTYPE::CanBreakWindow(void)
|
||||
|
||||
BOOLEAN SOLDIERTYPE::CanStartDrag(void)
|
||||
{
|
||||
if (!this->IsDragging() && this->CanDragInPrinciple())
|
||||
if (!this->IsDragging(false) && this->CanDragInPrinciple())
|
||||
{
|
||||
INT32 sNewGridNo = NewGridNo(this->sGridNo, DirectionInc(this->ubDirection));
|
||||
|
||||
@@ -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 );
|
||||
@@ -1916,7 +1919,7 @@ public:
|
||||
void CancelMultiTurnAction(BOOLEAN fFinished);
|
||||
BOOLEAN UpdateMultiTurnAction();
|
||||
|
||||
void DropSectorEquipment( INT32 UnloadedSectorGridNo);
|
||||
void DropSectorEquipment();
|
||||
|
||||
// sevenfm: Take new bomb with id = usItem from iventory to HANDPOS
|
||||
void TakeNewBombFromInventory(UINT16 usItem);
|
||||
@@ -2056,7 +2059,7 @@ public:
|
||||
BOOLEAN CanDragPerson(SoldierID usID, BOOLEAN fCheckStance = FALSE);
|
||||
BOOLEAN CanDragCorpse(UINT16 usCorpseNum, BOOLEAN fCheckStance = FALSE);
|
||||
BOOLEAN CanDragStructure(INT32 sGridNo, BOOLEAN fCheckStance = FALSE);
|
||||
BOOLEAN IsDragging(bool aStopIfConditionNotSatisfied = false);
|
||||
BOOLEAN IsDragging(bool aStopIfConditionNotSatisfied = true);
|
||||
void SetDragOrderPerson( SoldierID usID );
|
||||
void SetDragOrderCorpse( UINT32 uiCorpseID );
|
||||
void SetDragOrderStructure( INT32 sGridNo );
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -147,7 +147,7 @@ void SoldierTooltip( SOLDIERTYPE* pSoldier )
|
||||
{
|
||||
OBJECTTYPE* pObject = &(gusSelectedSoldier->inv[HANDPOS]);
|
||||
for (attachmentList::iterator iter = (*pObject)[0]->attachments.begin(); iter != (*pObject)[0]->attachments.end(); ++iter) {
|
||||
if ( iter->exists() && Item[iter->usItem].visionrangebonus > 0 )
|
||||
if ( Item[iter->usItem].visionrangebonus > 0 && iter->exists())
|
||||
{
|
||||
fMercIsUsingScope = TRUE;
|
||||
break;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -2221,7 +2239,7 @@ void GetKeyboardInput( UINT32 *puiNewEvent )
|
||||
// sevenfm: also stop dragging
|
||||
if (gusSelectedSoldier != NOBODY &&
|
||||
gusSelectedSoldier &&
|
||||
gusSelectedSoldier->IsDragging())
|
||||
gusSelectedSoldier->IsDragging(false))
|
||||
{
|
||||
gusSelectedSoldier->CancelDrag();
|
||||
DirtyMercPanelInterface(gusSelectedSoldier, DIRTYLEVEL2);
|
||||
@@ -4303,25 +4321,21 @@ void GetKeyboardInput( UINT32 *puiNewEvent )
|
||||
|
||||
// Make auto reload with magazines from sector inventory
|
||||
case 'R':
|
||||
if (fAlt && gusSelectedSoldier != NOBODY ) // Greysa: Moved here from 'r' to retain all functionality during testing.
|
||||
{
|
||||
if (CHEATER_CHEAT_LEVEL())
|
||||
{
|
||||
ReloadWeapon(gusSelectedSoldier, gusSelectedSoldier->ubAttackingHand);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
HandleTBReloadAll();
|
||||
}
|
||||
HandleTBReloadAll();
|
||||
|
||||
break;
|
||||
case 'r':
|
||||
if( gusSelectedSoldier != NOBODY )
|
||||
{
|
||||
if( fAlt )
|
||||
if( fAlt ) //reload selected merc's weapon
|
||||
{
|
||||
HandleTBReload();
|
||||
}
|
||||
if ( CHEATER_CHEAT_LEVEL( ) )
|
||||
{
|
||||
ReloadWeapon( gusSelectedSoldier, gusSelectedSoldier->ubAttackingHand );
|
||||
}
|
||||
else
|
||||
HandleTBReload();
|
||||
}
|
||||
else if( fCtrl )
|
||||
{
|
||||
if ( INFORMATION_CHEAT_LEVEL( ) )
|
||||
@@ -4756,6 +4770,13 @@ void GetKeyboardInput( UINT32 *puiNewEvent )
|
||||
break;
|
||||
|
||||
case 'Z':
|
||||
if (fCtrl)
|
||||
{
|
||||
if (DEBUG_CHEAT_LEVEL())
|
||||
{
|
||||
CycleThroughTileDebugInfo();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
@@ -6770,7 +6791,7 @@ void SwapGogglesUniformly(SOLDIERTYPE *pTeamSoldier, BOOLEAN fToNightVision)
|
||||
pObj = &(pTeamSoldier->inv[gear]);
|
||||
for (attachmentList::iterator iter = (*pObj)[0]->attachments.begin(); iter != (*pObj)[0]->attachments.end(); ++iter)
|
||||
{
|
||||
if ( iter->exists() && Item[ iter->usItem ].nightvisionrangebonus > bestBonus && Item[ iter->usItem ].usItemClass == IC_FACE )
|
||||
if ( Item[ iter->usItem ].nightvisionrangebonus > bestBonus && Item[ iter->usItem ].usItemClass == IC_FACE && iter->exists())
|
||||
{
|
||||
pGoggles = &(*iter);
|
||||
bestBonus = Item[ iter->usItem ].nightvisionrangebonus;
|
||||
@@ -6818,7 +6839,7 @@ void SwapGogglesUniformly(SOLDIERTYPE *pTeamSoldier, BOOLEAN fToNightVision)
|
||||
pObj = &(pTeamSoldier->inv[gear]);
|
||||
for (attachmentList::iterator iter = (*pObj)[0]->attachments.begin(); iter != (*pObj)[0]->attachments.end(); ++iter)
|
||||
{
|
||||
if ( iter->exists() && Item[ iter->usItem ].brightlightvisionrangebonus > bestBonus && Item[ iter->usItem ].usItemClass == IC_FACE )
|
||||
if ( Item[ iter->usItem ].brightlightvisionrangebonus > bestBonus && Item[ iter->usItem ].usItemClass == IC_FACE && iter->exists())
|
||||
{
|
||||
pGoggles = &(*iter);
|
||||
bestBonus = Item[ iter->usItem ].brightlightvisionrangebonus;
|
||||
|
||||
@@ -2081,7 +2081,7 @@ UINT8 HandleNonActivatedTossCursor( SOLDIERTYPE *pSoldier, INT32 sGridNo, BOOLEA
|
||||
//sevenfm this should be checked only for guns because we can throw items with attached explosives
|
||||
if(Item[pObj->usItem].usItemClass & IC_WEAPON )
|
||||
for (attachmentList::iterator iter = (*pObj)[0]->attachments.begin(); iter != (*pObj)[0]->attachments.end(); ++iter) {
|
||||
if ( iter->exists() && Item[ iter->usItem ].usItemClass & IC_EXPLOSV )
|
||||
if ( Item[ iter->usItem ].usItemClass & IC_EXPLOSV && iter->exists())
|
||||
{
|
||||
pObject = &(*iter);
|
||||
break;
|
||||
|
||||
+24
-22
@@ -1036,7 +1036,7 @@ INT32 EffectiveArmour( OBJECTTYPE * pObj )
|
||||
iValue = iValue * (*pObj)[0]->data.objectStatus * Armour[ Item[pObj->usItem].ubClassIndex ].ubCoverage / 10000;
|
||||
|
||||
for (attachmentList::iterator iter = (*pObj)[0]->attachments.begin(); iter != (*pObj)[0]->attachments.end(); ++iter) {
|
||||
if ( iter->exists() && Item[iter->usItem].usItemClass == IC_ARMOUR && (*iter)[0]->data.objectStatus > 0 )
|
||||
if (Item[iter->usItem].usItemClass == IC_ARMOUR && (*iter)[0]->data.objectStatus > 0 && iter->exists())
|
||||
{
|
||||
INT32 iValue2;
|
||||
|
||||
@@ -1062,7 +1062,7 @@ INT32 EffectiveArmourLBE( OBJECTTYPE * pObj )
|
||||
iValue = 0;
|
||||
|
||||
for (attachmentList::iterator iter = (*pObj)[0]->attachments.begin(); iter != (*pObj)[0]->attachments.end(); ++iter) {
|
||||
if ( iter->exists() && Item[iter->usItem].usItemClass == IC_ARMOUR && (*iter)[0]->data.objectStatus > 0 )
|
||||
if (Item[iter->usItem].usItemClass == IC_ARMOUR && (*iter)[0]->data.objectStatus > 0 )
|
||||
{
|
||||
INT32 iValue2;
|
||||
|
||||
@@ -1088,7 +1088,7 @@ INT32 ExplosiveEffectiveArmourLBE( OBJECTTYPE * pObj )
|
||||
iValue = 0;
|
||||
|
||||
for (attachmentList::iterator iter = (*pObj)[0]->attachments.begin(); iter != (*pObj)[0]->attachments.end(); ++iter) {
|
||||
if ( iter->exists() && Item[iter->usItem].usItemClass == IC_ARMOUR && (*iter)[0]->data.objectStatus > 0 )
|
||||
if (Item[iter->usItem].usItemClass == IC_ARMOUR && (*iter)[0]->data.objectStatus > 0 )
|
||||
{
|
||||
INT32 iValue2;
|
||||
|
||||
@@ -1214,7 +1214,7 @@ INT32 ExplosiveEffectiveArmour( OBJECTTYPE * pObj )
|
||||
}
|
||||
|
||||
for (attachmentList::iterator iter = (*pObj)[0]->attachments.begin(); iter != (*pObj)[0]->attachments.end(); ++iter) {
|
||||
if ( iter->exists() && Item[iter->usItem].usItemClass == IC_ARMOUR && (*iter)[0]->data.objectStatus > 0 )
|
||||
if (Item[iter->usItem].usItemClass == IC_ARMOUR && (*iter)[0]->data.objectStatus > 0 && iter->exists())
|
||||
{
|
||||
INT32 iValue2;
|
||||
|
||||
@@ -1295,7 +1295,7 @@ INT16 FireEffectiveArmour( OBJECTTYPE * pObj )
|
||||
|
||||
for ( attachmentList::iterator iter = ( *pObj )[0]->attachments.begin(); iter != ( *pObj )[0]->attachments.end(); ++iter )
|
||||
{
|
||||
if ( iter->exists() && Item[iter->usItem].usItemClass == IC_ARMOUR && ( *iter )[0]->data.objectStatus > 0 )
|
||||
if ( Item[iter->usItem].usItemClass == IC_ARMOUR && ( *iter )[0]->data.objectStatus > 0 && iter->exists() )
|
||||
{
|
||||
iValue += Item[iter->usItem].sFireResistance * ( *iter )[0]->data.objectStatus / 100;
|
||||
}
|
||||
@@ -2560,7 +2560,7 @@ BOOLEAN UseGunNCTH( SOLDIERTYPE *pSoldier , INT32 sTargetGridNo )
|
||||
{
|
||||
for (attachmentList::iterator iter = (*pObjAttHand)[0]->attachments.begin(); iter != (*pObjAttHand)[0]->attachments.end(); ++iter)
|
||||
{
|
||||
if ( iter->exists() && ItemHasHiddenMuzzleFlash(iter->usItem))
|
||||
if (ItemHasHiddenMuzzleFlash(iter->usItem))
|
||||
{
|
||||
OBJECTTYPE* pA= &(*iter);
|
||||
if ( (*pA)[0]->data.objectStatus >=USABLE)
|
||||
@@ -3346,7 +3346,7 @@ BOOLEAN UseGun( SOLDIERTYPE *pSoldier , INT32 sTargetGridNo )
|
||||
{
|
||||
for (attachmentList::iterator iter = (*pObjUsed)[0]->attachments.begin(); iter != (*pObjUsed)[0]->attachments.end(); ++iter)
|
||||
{
|
||||
if ( iter->exists() && ItemHasHiddenMuzzleFlash(iter->usItem))
|
||||
if (ItemHasHiddenMuzzleFlash(iter->usItem))
|
||||
{
|
||||
OBJECTTYPE* pA= &(*iter);
|
||||
if ( (*pA)[0]->data.objectStatus >=USABLE)
|
||||
@@ -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) &&
|
||||
@@ -5111,7 +5111,7 @@ BOOLEAN UseLauncher( SOLDIERTYPE *pSoldier, INT32 sTargetGridNo )
|
||||
pObj = &(pSoldier->inv[HANDPOS]);
|
||||
attachmentList::iterator iter;
|
||||
for (iter = (*pObj)[0]->attachments.begin(); iter != (*pObj)[0]->attachments.end(); ++iter) {
|
||||
if ( iter->exists() && Item[ iter->usItem ].usItemClass & IC_EXPLOSV )
|
||||
if ( Item[ iter->usItem ].usItemClass & IC_EXPLOSV && iter->exists())
|
||||
{
|
||||
break;
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7993,7 +7995,7 @@ INT32 TotalArmourProtection( SOLDIERTYPE * pTarget, UINT8 ubHitLocation, INT32 i
|
||||
if (iSlot == VESTPOS && pVestPack->exists() == true)
|
||||
{
|
||||
for (attachmentList::iterator iter = (*pVestPack)[0]->attachments.begin(); iter != (*pVestPack)[0]->attachments.end(); ++iter) {
|
||||
if ( iter->exists() && Item[iter->usItem].usItemClass == IC_ARMOUR && (*iter)[0]->data.objectStatus > 0 )
|
||||
if (Item[iter->usItem].usItemClass == IC_ARMOUR && (*iter)[0]->data.objectStatus > 0 )
|
||||
{
|
||||
INT32 protection = ArmourProtection( pTarget, Item[iter->usItem].ubClassIndex, &((*iter)[0]->data.objectStatus), iImpact, ubAmmoType, &plateHit );
|
||||
|
||||
@@ -8025,7 +8027,7 @@ INT32 TotalArmourProtection( SOLDIERTYPE * pTarget, UINT8 ubHitLocation, INT32 i
|
||||
if (pArmour->exists() == true)
|
||||
{
|
||||
for (attachmentList::iterator iter = (*pArmour)[0]->attachments.begin(); iter != (*pArmour)[0]->attachments.end(); ++iter) {
|
||||
if ( iter->exists() && Item[iter->usItem].usItemClass == IC_ARMOUR && (*iter)[0]->data.objectStatus > 0 )
|
||||
if (Item[iter->usItem].usItemClass == IC_ARMOUR && (*iter)[0]->data.objectStatus > 0 && iter->exists())
|
||||
{
|
||||
// bullet got through jacket; apply ceramic plate armour
|
||||
INT32 protection = ArmourProtection( pTarget, Item[iter->usItem].ubClassIndex, &((*iter)[0]->data.objectStatus), iImpact, ubAmmoType, &plateHit );
|
||||
@@ -8071,7 +8073,7 @@ INT32 TotalArmourProtection( SOLDIERTYPE * pTarget, UINT8 ubHitLocation, INT32 i
|
||||
{
|
||||
//Madd: put any attachments that someone might have added to the armour in the merc's inventory
|
||||
for (attachmentList::iterator iter = (*pArmour)[0]->attachments.begin(); iter != (*pArmour)[0]->attachments.end(); ++iter) {
|
||||
if ( iter->exists() && !AutoPlaceObject( pTarget, &(*iter), FALSE ) )
|
||||
if ( !AutoPlaceObject( pTarget, &(*iter), FALSE ) && iter->exists())
|
||||
{ // put it on the ground
|
||||
AddItemToPool( pTarget->sGridNo, &(*iter), 1, pTarget->pathing.bLevel, 0 , -1 );
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
+685
-1
@@ -2207,7 +2207,7 @@ INT32 EstimateShotDamage(SOLDIERTYPE *pSoldier, SOLDIERTYPE *pOpponent, INT16 ub
|
||||
|
||||
// check for ceramic plates; these do affect monster spit
|
||||
for (attachmentList::iterator iter = pOpponent->inv[VESTPOS][0]->attachments.begin(); iter != pOpponent->inv[VESTPOS][0]->attachments.end(); ++iter) {
|
||||
if ( iter->exists() && Item[iter->usItem].usItemClass == IC_ARMOUR && (*iter)[0]->data.objectStatus > 0 )
|
||||
if (Item[iter->usItem].usItemClass == IC_ARMOUR && (*iter)[0]->data.objectStatus > 0 && iter->exists() )
|
||||
{
|
||||
iTorsoProt += (INT32) Armour[Item[iter->usItem].ubClassIndex].ubProtection *
|
||||
(INT32) (*iter)[0]->data.objectStatus / 100;
|
||||
@@ -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
|
||||
|
||||
@@ -1319,7 +1319,7 @@ BOOLEAN FindFenceJumpDirection( SOLDIERTYPE *pSoldier, INT32 sGridNo, INT8 bStar
|
||||
return( FALSE );
|
||||
}
|
||||
|
||||
//Simply chooses a random gridno within valid boundaries (for dropping things in loaded sectors)
|
||||
//Simply chooses a random gridno within valid boundaries (for dropping things in unloaded sectors)
|
||||
INT32 RandomGridNo()
|
||||
{
|
||||
INT32 iMapXPos, iMapYPos, iMapIndex;
|
||||
@@ -1332,69 +1332,6 @@ INT32 RandomGridNo()
|
||||
return iMapIndex;
|
||||
}
|
||||
|
||||
|
||||
static bool pointLeftOfEdge(INT32 xp, INT32 yp, INT32 x1, INT32 y1, INT32 x2, INT32 y2)
|
||||
{
|
||||
//Test whether the point(xp, yp) lies on the left-hand side of the edge(x1, y1) - (x2, y2)
|
||||
const auto D = (x2 - x1) * (yp - y1) - (xp - x1) * (y2 - y1);
|
||||
return (D > 0);
|
||||
}
|
||||
|
||||
INT32 RandomGridNoUnloadedSector(INT32 worldRows, INT32 worldCols)
|
||||
{
|
||||
INT32 iMapIndex;
|
||||
const auto centerHeight = gpWorldLevelDataAutoResolve[gMapInformationAutoResolve.sCenterGridNo].sHeight;
|
||||
|
||||
const auto CenterWorldX = (WORLD_ROWS) / 2 * CELL_X_SIZE;
|
||||
const auto CenterWorldY = (WORLD_COLS) / 2 * CELL_Y_SIZE;
|
||||
|
||||
// Sector corners are shifted inwards a bit to limit the area to be slightly smaller than actual sector.
|
||||
// It fixed a few annyoing edge cases where a tile would evaluate to being inside the map, but it was juuust out of reach in actual gameplay
|
||||
const auto TLX = 2*CELL_X_SIZE; // Shift X-coordinate by one tile inwards from actual edge.
|
||||
const auto TLY = (WORLD_ROWS / 2) * CELL_X_SIZE;
|
||||
|
||||
const auto TRX = (WORLD_COLS / 2) * CELL_Y_SIZE;
|
||||
const auto TRY = 2*CELL_X_SIZE; // Shift Y-coordinate by one tile inwards from actual edge
|
||||
|
||||
const auto BLX = ((WORLD_COLS / 2) * CELL_Y_SIZE);
|
||||
const auto BLY = ((WORLD_ROWS-1) * CELL_Y_SIZE); // Shift Y-coordinate by one row inwards from actual edge
|
||||
|
||||
const auto BRX = ((WORLD_COLS-1) * CELL_Y_SIZE); // Shift X-coordinate by one row inwards from actual edge
|
||||
const auto BRY = ((WORLD_ROWS / 2) * CELL_X_SIZE);
|
||||
|
||||
UINT8 randomGridHeight = 255;
|
||||
do
|
||||
{
|
||||
randomGridHeight = 255; // Reset height to be tested
|
||||
INT32 iMapXPos = Random(worldCols);
|
||||
INT32 iMapYPos = Random(worldRows);
|
||||
iMapIndex = iMapYPos * worldCols + iMapXPos;
|
||||
|
||||
// Discard gridno that is not within map bounds.
|
||||
// Check point against world area's edges. If any edge test fails, it means point is not inside.
|
||||
// Edge traverals goes counterclockwise, Top Right -> Top Left -> Bottom Left -> Bottom Right -> Top Right
|
||||
const auto pX = iMapXPos * CELL_X_SIZE;
|
||||
const auto pY = iMapYPos * CELL_Y_SIZE;
|
||||
|
||||
//TR->TL
|
||||
if ( !pointLeftOfEdge(pX, pY, TLX, TLY, TRX, TRY) )
|
||||
continue;
|
||||
//TL->BL
|
||||
if ( !pointLeftOfEdge(pX, pY, BLX, BLY, TLX, TLY) )
|
||||
continue;
|
||||
//BL->BR
|
||||
if ( !pointLeftOfEdge(pX, pY, BRX, BRY, BLX, BLY) )
|
||||
continue;
|
||||
//BR->TR
|
||||
if ( !pointLeftOfEdge(pX, pY, TRX, TRY, BRX, BRY) )
|
||||
continue;
|
||||
|
||||
// Compare randomGridHeight with height of the center grid of the map, as long as the center of the map is on walkable height it will work properly
|
||||
randomGridHeight = gpWorldLevelDataAutoResolve[iMapIndex].sHeight;
|
||||
} while ( !(randomGridHeight == centerHeight) );
|
||||
return iMapIndex;
|
||||
}
|
||||
|
||||
// Flugente: is this gridno near a player merc?
|
||||
BOOLEAN GridNoNearPlayerMercs( INT32 sGridNo, INT16 sRadius )
|
||||
{
|
||||
|
||||
@@ -113,7 +113,6 @@ BOOLEAN FindFenceJumpDirection( SOLDIERTYPE *pSoldier, INT32 sGridNo, INT8 bStar
|
||||
|
||||
//Simply chooses a random gridno within valid boundaries (for dropping things in unloaded sectors)
|
||||
INT32 RandomGridNo();
|
||||
INT32 RandomGridNoUnloadedSector(INT32 worldRows, INT32 worldCols);
|
||||
|
||||
extern UINT32 guiForceRefreshMousePositionCalculation;
|
||||
|
||||
|
||||
+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
|
||||
|
||||
@@ -1428,7 +1428,6 @@ STRUCTURE * SwapStructureForPartnerForcingGraphicalChange( INT32 sGridNo, STRUCT
|
||||
}
|
||||
#endif
|
||||
|
||||
// Finds the first structure with matching flag
|
||||
STRUCTURE * FindStructure( INT32 sGridNo, UINT32 fFlags )
|
||||
{
|
||||
// finds a structure that matches any of the given flags
|
||||
@@ -1452,33 +1451,6 @@ STRUCTURE * FindStructure( INT32 sGridNo, UINT32 fFlags )
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
// Finds the last structure with matching flag
|
||||
STRUCTURE* FindLastStructure(INT32 sGridNo, UINT32 fFlags)
|
||||
{
|
||||
// finds a structure that matches any of the given flags
|
||||
STRUCTURE* pCurrent;
|
||||
STRUCTURE* pLastMatch = nullptr;
|
||||
|
||||
//bug fix for win98 crash when traveling between sectors
|
||||
if ( TileIsOutOfBounds(sGridNo) )
|
||||
{
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
pCurrent = gpWorldLevelData[sGridNo].pStructureHead;
|
||||
while ( pCurrent != NULL )
|
||||
{
|
||||
if ( (pCurrent->fFlags & fFlags) != 0 )
|
||||
{
|
||||
pLastMatch = pCurrent;
|
||||
//return(pCurrent);
|
||||
}
|
||||
pCurrent = pCurrent->pNext;
|
||||
}
|
||||
|
||||
return(pLastMatch);
|
||||
}
|
||||
|
||||
STRUCTURE * FindNextStructure( STRUCTURE * pStructure, UINT32 fFlags )
|
||||
{
|
||||
STRUCTURE * pCurrent;
|
||||
|
||||
@@ -47,7 +47,6 @@ BOOLEAN DeleteStructureFromWorld( STRUCTURE * pStructure );
|
||||
// functions to find a structure in a location
|
||||
//
|
||||
STRUCTURE * FindStructure( INT32 sGridNo, UINT32 fFlags );
|
||||
STRUCTURE* FindLastStructure(INT32 sGridNo, UINT32 fFlags);
|
||||
STRUCTURE * FindNextStructure( STRUCTURE * pStructure, UINT32 fFlags );
|
||||
STRUCTURE * FindStructureByID( INT32 sGridNo, UINT16 usStructureID );
|
||||
STRUCTURE * FindBaseStructure( STRUCTURE * pStructure );
|
||||
|
||||
+8
-299
@@ -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;
|
||||
@@ -138,8 +138,7 @@ void SaveMapLights( HWFILE hfile );
|
||||
void LoadMapLights( INT8 **hBuffer );
|
||||
|
||||
// Global Variables
|
||||
MAP_ELEMENT* gpWorldLevelData;
|
||||
MAP_ELEMENT *gpWorldLevelDataAutoResolve;
|
||||
MAP_ELEMENT *gpWorldLevelData;
|
||||
INT32 *gpDirtyData;
|
||||
UINT32 gSurfaceMemUsage;
|
||||
//UINT8 gubWorldMovementCosts[ WORLD_MAX ][MAXDIR][2];
|
||||
@@ -317,8 +316,6 @@ void DeinitializeWorld()
|
||||
TrashWorld();
|
||||
if(gubGridNoMarkers)
|
||||
MemFree(gubGridNoMarkers);
|
||||
if(gsCoverValue)
|
||||
MemFree(gsCoverValue);
|
||||
if(gubBuildingInfo)
|
||||
MemFree(gubBuildingInfo);
|
||||
if(gusWorldRoomInfo)
|
||||
@@ -3313,296 +3310,6 @@ BOOLEAN LoadWorld(const STR8 puiFilename, FLOAT* pMajorMapVersion, UINT8* pMinor
|
||||
return(TRUE);
|
||||
}
|
||||
|
||||
// A stub LoadWorld function so we can read a sector's tile height values into gpWorldLevelDataAutoResolve[].sHeight and entrypoint gridnos into gMapInformationAutoResolve
|
||||
// They are needed when placing corpses and dropped items into a random gridno in an unloaded sector when we're finished with autoresolve battle.
|
||||
BOOLEAN LoadWorldInfoForAutoResolve(const STR8 puiFilename, INT32& iRowSize, INT32& iColSize)
|
||||
{
|
||||
HWFILE hfile;
|
||||
FLOAT dMajorMapVersion;
|
||||
UINT8 ubMinorMapVersion;
|
||||
UINT32 uiFlags;
|
||||
UINT32 uiBytesRead;
|
||||
UINT32 uiFileSize;
|
||||
INT32 i, cnt, cnt2;
|
||||
UINT8 ubType;
|
||||
CHAR8 aFilename[2 * FILENAME_BUFLEN];
|
||||
UINT8 ubCombine;
|
||||
UINT8(*bCounts)[8] = NULL;
|
||||
INT8* pBuffer;
|
||||
INT8* pBufferHead;
|
||||
|
||||
|
||||
// Append exension to filename!
|
||||
if ( gfForceLoad )
|
||||
sprintf(aFilename, "MAPS\\%s", gzForceLoadFile);
|
||||
else
|
||||
sprintf(aFilename, "MAPS\\%s", puiFilename);
|
||||
|
||||
// Open file
|
||||
hfile = FileOpen(aFilename, FILE_ACCESS_READ);
|
||||
|
||||
if ( !hfile )
|
||||
{
|
||||
#ifndef JA2EDITOR
|
||||
SET_ERROR("Could not load map file %S", aFilename);
|
||||
#endif
|
||||
return(FALSE);
|
||||
}
|
||||
|
||||
|
||||
// Get the file size and alloc one huge buffer for it. We will use this buffer to transfer all of the data from.
|
||||
uiFileSize = FileGetSize(hfile);
|
||||
pBuffer = (INT8*)MemAlloc(uiFileSize);
|
||||
pBufferHead = pBuffer;
|
||||
FileRead(hfile, pBuffer, uiFileSize, &uiBytesRead);
|
||||
FileClose(hfile);
|
||||
|
||||
|
||||
// Read JA2 Version ID
|
||||
LOADDATA(&dMajorMapVersion, pBuffer, sizeof(FLOAT));
|
||||
LOADDATA(&ubMinorMapVersion, pBuffer, sizeof(UINT8));
|
||||
|
||||
iRowSize = OLD_WORLD_ROWS;
|
||||
iColSize = OLD_WORLD_COLS;
|
||||
if ( dMajorMapVersion >= 7.00 )
|
||||
{
|
||||
LOADDATA(&iRowSize, pBuffer, sizeof(INT32));
|
||||
LOADDATA(&iColSize, pBuffer, sizeof(INT32));
|
||||
}
|
||||
|
||||
// Actual world size of the map we loaded!
|
||||
INT32 iWorldSize = iRowSize * iColSize;
|
||||
gMapTrn.ResizeTrnCfg(WORLD_ROWS, WORLD_COLS, iRowSize, iColSize);
|
||||
|
||||
|
||||
// Initialize world data
|
||||
if ( gpWorldLevelDataAutoResolve != NULL )
|
||||
MemFree(gpWorldLevelDataAutoResolve);
|
||||
gpWorldLevelDataAutoResolve = (MAP_ELEMENT*)MemAlloc(sizeof(MAP_ELEMENT) * WORLD_MAX);
|
||||
memset(gpWorldLevelDataAutoResolve, 0, sizeof(MAP_ELEMENT) * WORLD_MAX);
|
||||
|
||||
bCounts = (UINT8(*)[8])MemAlloc(WORLD_MAX * 8);
|
||||
memset(bCounts, 0, sizeof(UINT8) * WORLD_MAX * 8);
|
||||
|
||||
|
||||
// Read FLAGS FOR WORLD
|
||||
LOADDATA(&uiFlags, pBuffer, sizeof(INT32));
|
||||
SKIPDATA(pBuffer, pBuffer, sizeof(INT32)); // TilesetID
|
||||
|
||||
// Load soldier size
|
||||
SKIPDATA(pBuffer, pBuffer, sizeof(INT32));
|
||||
|
||||
// NOTE! We need this!
|
||||
// Read height values
|
||||
for ( i = 0; i < iWorldSize; i++ )
|
||||
{
|
||||
gMapTrn.GetTrnCnt(cnt = i);
|
||||
LOADDATA(&gpWorldLevelDataAutoResolve[cnt].sHeight, pBuffer, sizeof(INT16));
|
||||
}
|
||||
|
||||
// Read layer counts
|
||||
for ( i = 0; i < iWorldSize; i++ )
|
||||
{
|
||||
gMapTrn.GetTrnCnt(cnt = i);
|
||||
// Read combination of land/world flags
|
||||
LOADDATA(&ubCombine, pBuffer, sizeof(UINT8));
|
||||
// Split
|
||||
bCounts[cnt][0] = (UINT8)(ubCombine & 0x0F);
|
||||
gpWorldLevelDataAutoResolve[cnt].uiFlags |= (UINT8)((ubCombine & 0xF0) >> 4);
|
||||
// Read #objects, structs
|
||||
LOADDATA(&ubCombine, pBuffer, sizeof(UINT8));
|
||||
// Split
|
||||
bCounts[cnt][1] = (UINT8)(ubCombine & 0x0F);
|
||||
bCounts[cnt][2] = (UINT8)((ubCombine & 0xF0) >> 4);
|
||||
// Read shadows, roof
|
||||
LOADDATA(&ubCombine, pBuffer, sizeof(UINT8));
|
||||
// Split
|
||||
bCounts[cnt][3] = (UINT8)(ubCombine & 0x0F);
|
||||
bCounts[cnt][4] = (UINT8)((ubCombine & 0xF0) >> 4);
|
||||
// Read OnRoof, nothing
|
||||
LOADDATA(&ubCombine, pBuffer, sizeof(UINT8));
|
||||
// Split
|
||||
bCounts[cnt][5] = (UINT8)(ubCombine & 0x0F);
|
||||
}
|
||||
|
||||
for ( i = 0; i < iWorldSize; i++ )
|
||||
{
|
||||
gMapTrn.GetTrnCnt(cnt = i);
|
||||
for ( cnt2 = 0; cnt2 < bCounts[cnt][0]; cnt2++ )
|
||||
{
|
||||
SKIPDATA(pBuffer, pBuffer, sizeof(UINT8));
|
||||
SKIPDATA(pBuffer, pBuffer, sizeof(UINT8));
|
||||
}
|
||||
}
|
||||
|
||||
// New load require UINT16 for the type subindex due to the fact that ROADPIECES contain over 300 type subindices.
|
||||
for ( i = 0; i < iWorldSize; i++ )
|
||||
{
|
||||
gMapTrn.GetTrnCnt(cnt = i);
|
||||
// Set objects
|
||||
for ( cnt2 = 0; cnt2 < bCounts[cnt][1]; cnt2++ )
|
||||
{
|
||||
LOADDATA(&ubType, pBuffer, sizeof(UINT8));
|
||||
SKIPDATA(pBuffer, pBuffer, sizeof(UINT16));
|
||||
|
||||
if ( ubType >= FIRSTPOINTERS )
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
for ( i = 0; i < iWorldSize; i++ )
|
||||
{
|
||||
gMapTrn.GetTrnCnt(cnt = i);
|
||||
// Set structs
|
||||
for ( cnt2 = 0; cnt2 < bCounts[cnt][2]; cnt2++ )
|
||||
{
|
||||
SKIPDATA(pBuffer, pBuffer, sizeof(UINT8));
|
||||
SKIPDATA(pBuffer, pBuffer, sizeof(UINT8));
|
||||
}
|
||||
}
|
||||
|
||||
for ( i = 0; i < iWorldSize; i++ )
|
||||
{
|
||||
gMapTrn.GetTrnCnt(cnt = i);
|
||||
for ( cnt2 = 0; cnt2 < bCounts[cnt][3]; cnt2++ )
|
||||
{
|
||||
SKIPDATA(pBuffer, pBuffer, sizeof(UINT8));
|
||||
SKIPDATA(pBuffer, pBuffer, sizeof(UINT8));
|
||||
}
|
||||
}
|
||||
|
||||
for ( i = 0; i < iWorldSize; i++ )
|
||||
{
|
||||
gMapTrn.GetTrnCnt(cnt = i);
|
||||
for ( cnt2 = 0; cnt2 < bCounts[cnt][4]; cnt2++ )
|
||||
{
|
||||
SKIPDATA(pBuffer, pBuffer, sizeof(UINT8));
|
||||
SKIPDATA(pBuffer, pBuffer, sizeof(UINT8));
|
||||
}
|
||||
}
|
||||
|
||||
for ( i = 0; i < iWorldSize; i++ )
|
||||
{
|
||||
gMapTrn.GetTrnCnt(cnt = i);
|
||||
for ( cnt2 = 0; cnt2 < bCounts[cnt][5]; cnt2++ )
|
||||
{
|
||||
SKIPDATA(pBuffer, pBuffer, sizeof(UINT8));
|
||||
SKIPDATA(pBuffer, pBuffer, sizeof(UINT8));
|
||||
}
|
||||
}
|
||||
|
||||
// For old Russian Maps which have version 6.0
|
||||
if ( dMajorMapVersion == 6.00 && ubMinorMapVersion < 27 )
|
||||
{
|
||||
UINT32 uiNums[37];
|
||||
LOADDATA(uiNums, pBuffer, sizeof(INT32) * 37);
|
||||
dMajorMapVersion = 5.00;
|
||||
}
|
||||
// For new maps
|
||||
else if ( dMajorMapVersion == 6.00 && ubMinorMapVersion < 27 && MAJOR_MAP_VERSION == 6.00 )
|
||||
{
|
||||
UINT32 uiNums[37];
|
||||
LOADDATA(uiNums, pBuffer, sizeof(UINT32) * 37);
|
||||
}
|
||||
//now the data is discarded and when saved, as 6.27, you won't have this problem!
|
||||
|
||||
|
||||
for ( i = 0; i < iWorldSize; i++ ) {
|
||||
// Read room information, 2 byte for new files
|
||||
if ( ubMinorMapVersion < 29 ) {
|
||||
SKIPDATA(pBuffer, pBuffer, sizeof(INT8));
|
||||
}
|
||||
else {
|
||||
SKIPDATA(pBuffer, pBuffer, sizeof(UINT16));
|
||||
}
|
||||
}
|
||||
|
||||
// Load out item information
|
||||
if ( uiFlags & MAP_WORLDITEMS_SAVED )
|
||||
{
|
||||
WORLDITEM dummyItem;
|
||||
UINT32 uiNumWorldItems;
|
||||
|
||||
//Read the number of items that were saved in the map.
|
||||
LOADDATA(&uiNumWorldItems, pBuffer, 4);
|
||||
|
||||
if ( gTacticalStatus.uiFlags & LOADING_SAVED_GAME && !gfEditMode )
|
||||
{ //The sector has already been visited. The items are saved in a different format that will be
|
||||
//loaded later on. So, all we need to do is skip the data entirely.
|
||||
if ( dMajorMapVersion >= 6.0 && ubMinorMapVersion > 26 ) {
|
||||
for ( unsigned int x = 0; x < uiNumWorldItems; ++x )
|
||||
{
|
||||
//ADB WORLDITEM's size on disk is unknown
|
||||
dummyItem.Load(&pBuffer, dMajorMapVersion, ubMinorMapVersion);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pBuffer += sizeof(OLD_WORLDITEM_101) * uiNumWorldItems;
|
||||
}
|
||||
}
|
||||
else for ( i = 0; i < uiNumWorldItems; i++ )
|
||||
{
|
||||
dummyItem.Load(&pBuffer, dMajorMapVersion, ubMinorMapVersion);
|
||||
}
|
||||
}
|
||||
|
||||
if ( uiFlags & MAP_AMBIENTLIGHTLEVEL_SAVED )
|
||||
{
|
||||
//Ambient light levels are only saved in underground levels
|
||||
SKIPDATA(pBuffer, pBuffer, sizeof(BOOLEAN));
|
||||
SKIPDATA(pBuffer, pBuffer, sizeof(BOOLEAN));
|
||||
SKIPDATA(pBuffer, pBuffer, sizeof(UINT8));
|
||||
|
||||
}
|
||||
|
||||
if ( uiFlags & MAP_WORLDLIGHTS_SAVED )
|
||||
{
|
||||
SGPPaletteEntry LColors[3];
|
||||
UINT8 ubNumColors;
|
||||
UINT16 usNumLights;
|
||||
CHAR8 str[30];
|
||||
UINT8 ubStrLen;
|
||||
LIGHT_SPRITE TmpLight;
|
||||
|
||||
// read in the light colors!
|
||||
LOADDATA(&ubNumColors, pBuffer, 1);
|
||||
LOADDATA(LColors, pBuffer, sizeof(SGPPaletteEntry) * ubNumColors);
|
||||
|
||||
LOADDATA(&usNumLights, pBuffer, 2);
|
||||
|
||||
|
||||
for ( cnt = 0; cnt < usNumLights; cnt++ )
|
||||
{
|
||||
LOADDATA(&TmpLight, pBuffer, sizeof(LIGHT_SPRITE));
|
||||
LOADDATA(&ubStrLen, pBuffer, 1);
|
||||
|
||||
if ( ubStrLen )
|
||||
{
|
||||
LOADDATA(str, pBuffer, ubStrLen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gMapInformationAutoResolve.Load(&pBuffer, dMajorMapVersion);
|
||||
|
||||
|
||||
// NOTE! We need these!
|
||||
// Translation routine for map grid numbers
|
||||
gMapTrn.GetTrnCnt(gMapInformationAutoResolve.sCenterGridNo);
|
||||
gMapTrn.GetTrnCnt(gMapInformationAutoResolve.sIsolatedGridNo);
|
||||
gMapTrn.GetTrnCnt(gMapInformationAutoResolve.sNorthGridNo);
|
||||
gMapTrn.GetTrnCnt(gMapInformationAutoResolve.sEastGridNo);
|
||||
gMapTrn.GetTrnCnt(gMapInformationAutoResolve.sWestGridNo);
|
||||
gMapTrn.GetTrnCnt(gMapInformationAutoResolve.sSouthGridNo);
|
||||
|
||||
// Remove this rather large chunk of memory from the system now!
|
||||
MemFree(pBufferHead);
|
||||
MemFree(bCounts);
|
||||
return(TRUE);
|
||||
}
|
||||
|
||||
|
||||
//****************************************************************************************
|
||||
//
|
||||
// Deletes everything then re-creates the world with simple ground tiles
|
||||
@@ -4599,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)
|
||||
|
||||
@@ -281,7 +281,6 @@ typedef struct
|
||||
|
||||
// World Data
|
||||
extern MAP_ELEMENT *gpWorldLevelData;
|
||||
extern MAP_ELEMENT* gpWorldLevelDataAutoResolve;
|
||||
extern UINT8 (*gubWorldMovementCosts)[MAXDIR][2];//dnl ch43 260909
|
||||
|
||||
//dnl ch44 290909 Translation routine
|
||||
@@ -340,7 +339,6 @@ BOOLEAN NewWorld( INT32 nMapRows, INT32 nMapCols );
|
||||
|
||||
BOOLEAN SaveWorld(const STR8 puiFilename, FLOAT dMajorMapVersion=MAJOR_MAP_VERSION, UINT8 ubMinorMapVersion=MINOR_MAP_VERSION);//dnl ch33 150909
|
||||
BOOLEAN LoadWorld(const STR8 puiFilename, FLOAT* pMajorMapVersion=NULL, UINT8* pMinorMapVersion=NULL);//dnl ch44 290909
|
||||
BOOLEAN LoadWorldInfoForAutoResolve(const STR8 puiFilename, INT32& iRowSize, INT32& iColSize);
|
||||
|
||||
void CompileWorldMovementCosts(void);//dnl ch56 151009
|
||||
void RecompileLocalMovementCosts( INT32 sCentreGridNo );
|
||||
|
||||
+47
-58
@@ -199,10 +199,7 @@ WRAPPED_STRING *LineWrap(INT32 iFont, UINT16 usLineWidthPixels, UINT16 *pusLineW
|
||||
usCurrentWidthPixels = usCurrentWidthPixels + WFStringPixLength( OneChar, iFont);
|
||||
|
||||
//If we are at the end of the string
|
||||
|
||||
//Juqi: Add NEWLINE_CHAR detect
|
||||
// NEWLINE_CHAR "±" is only used to handle Chinese ITMES.XML file.
|
||||
if (TempString[ usCurIndex ] == 0 && !fNewLine)
|
||||
if(TempString[ usCurIndex ] == 0)
|
||||
{
|
||||
//get to next WrappedString structure
|
||||
pWrappedString = &FirstWrappedString;
|
||||
@@ -227,13 +224,10 @@ WRAPPED_STRING *LineWrap(INT32 iFont, UINT16 usLineWidthPixels, UINT16 *pusLineW
|
||||
|
||||
}
|
||||
|
||||
//Juqi: Allow manual line breaks to be added in XML,
|
||||
// rather than relying solely on width for automatic line breaks.
|
||||
if((usCurrentWidthPixels > usLineWidthPixels || fNewLine)
|
||||
&&
|
||||
((g_lang != i18n::Lang::zh)
|
||||
||
|
||||
(g_lang == i18n::Lang::zh
|
||||
|
||||
if( (g_lang != i18n::Lang::zh && usCurrentWidthPixels > usLineWidthPixels)
|
||||
||
|
||||
(g_lang == i18n::Lang::zh && usCurrentWidthPixels > usLineWidthPixels
|
||||
&& TempString[usCurIndex] != L','
|
||||
&& TempString[usCurIndex] != L'。'
|
||||
&& TempString[usCurIndex] != L';'
|
||||
@@ -245,9 +239,8 @@ WRAPPED_STRING *LineWrap(INT32 iFont, UINT16 usLineWidthPixels, UINT16 *pusLineW
|
||||
&& TempString[usCurIndex] != L'?'
|
||||
&& TempString[usCurIndex] != L')'
|
||||
&& TempString[usCurIndex] != L')'
|
||||
))
|
||||
)
|
||||
|
||||
)//||(DestString[ usDestIndex ]==NEWLINE_CHAR )||(fNewLine))
|
||||
)
|
||||
{
|
||||
|
||||
//if an error has occured, and the string is too long
|
||||
@@ -258,55 +251,51 @@ WRAPPED_STRING *LineWrap(INT32 iFont, UINT16 usLineWidthPixels, UINT16 *pusLineW
|
||||
// Fix really long unbroken strings, the above code dosent work for it
|
||||
usLastMaxWidthIndex = usDestIndex;
|
||||
|
||||
//Juqi: if NEWLINE_CHAR, don't go back
|
||||
if (!fNewLine)
|
||||
{
|
||||
//Go back to begining of word
|
||||
while(
|
||||
(g_lang != i18n::Lang::zh && DestString[ usDestIndex ] != L' ' && usCurIndex > 0 && usDestIndex > 0)
|
||||
||
|
||||
(g_lang == i18n::Lang::zh && DestString[ usDestIndex ] != L' ' && usCurIndex > 0 && usDestIndex > 0 && DestString[usDestIndex] < 255))
|
||||
{
|
||||
OneChar[0] = DestString[ usDestIndex ];
|
||||
//Go back to begining of word
|
||||
while(
|
||||
(g_lang != i18n::Lang::zh && DestString[ usDestIndex ] != L' ' && usCurIndex > 0 && usDestIndex > 0)
|
||||
||
|
||||
(g_lang == i18n::Lang::zh && DestString[ usDestIndex ] != L' ' && usCurIndex > 0 && usDestIndex > 0 && DestString[usDestIndex] < 255))
|
||||
{
|
||||
OneChar[0] = DestString[ usDestIndex ];
|
||||
|
||||
usCurrentWidthPixels = usCurrentWidthPixels - WFStringPixLength( OneChar, iFont);
|
||||
usCurrentWidthPixels = usCurrentWidthPixels - WFStringPixLength( OneChar, iFont);
|
||||
|
||||
usCurIndex--;
|
||||
usDestIndex--;
|
||||
}
|
||||
if (g_lang == i18n::Lang::zh && DestString[usDestIndex] > 255)
|
||||
{
|
||||
if (DestString[usDestIndex] == L','
|
||||
|| DestString[usDestIndex] == L','
|
||||
|| DestString[usDestIndex] == L'。'
|
||||
|| DestString[usDestIndex] == L';'
|
||||
|| DestString[usDestIndex] == L'”'
|
||||
|| DestString[usDestIndex] == L'’'
|
||||
|| DestString[usDestIndex] == L':'
|
||||
|| DestString[usDestIndex] == L'、'
|
||||
|| DestString[usDestIndex] == L'?'
|
||||
|| DestString[usDestIndex] == L'?'
|
||||
|| DestString[usDestIndex] == L')'
|
||||
|| DestString[usDestIndex] == L')')
|
||||
{usDestIndex++;}
|
||||
else
|
||||
{usCurIndex--;}
|
||||
}
|
||||
usCurIndex--;
|
||||
usDestIndex--;
|
||||
}
|
||||
if (g_lang == i18n::Lang::zh && DestString[usDestIndex] > 255)
|
||||
{
|
||||
if (DestString[usDestIndex] == L','
|
||||
|| DestString[usDestIndex] == L','
|
||||
|| DestString[usDestIndex] == L'。'
|
||||
|| DestString[usDestIndex] == L';'
|
||||
|| DestString[usDestIndex] == L'”'
|
||||
|| DestString[usDestIndex] == L'’'
|
||||
|| DestString[usDestIndex] == L':'
|
||||
|| DestString[usDestIndex] == L'、'
|
||||
|| DestString[usDestIndex] == L'?'
|
||||
|| DestString[usDestIndex] == L'?'
|
||||
|| DestString[usDestIndex] == L')'
|
||||
|| DestString[usDestIndex] == L')')
|
||||
{usDestIndex++;}
|
||||
else
|
||||
{usCurIndex--;}
|
||||
}
|
||||
|
||||
usEndIndex = usDestIndex;
|
||||
usEndIndex = usDestIndex;
|
||||
|
||||
// OJW - 20090427
|
||||
// Fix really long unbroken strings
|
||||
if( usEndIndex <= 0 )
|
||||
{
|
||||
usEndIndex = usLastMaxWidthIndex;
|
||||
usDestIndex = usLastMaxWidthIndex;
|
||||
usCurIndex += usLastMaxWidthIndex - 1;
|
||||
}
|
||||
// OJW - 20090427
|
||||
// Fix really long unbroken strings
|
||||
if( usEndIndex <= 0 )
|
||||
{
|
||||
usEndIndex = usLastMaxWidthIndex;
|
||||
usDestIndex = usLastMaxWidthIndex;
|
||||
usCurIndex += usLastMaxWidthIndex - 1;
|
||||
}
|
||||
|
||||
// put next line into temp buffer
|
||||
DestString[usEndIndex] = 0;
|
||||
} //Juqi: endif
|
||||
// put next line into temp buffer
|
||||
DestString[usEndIndex] = 0;
|
||||
|
||||
//get to next WrappedString structure
|
||||
pWrappedString = &FirstWrappedString;
|
||||
|
||||
+145
-148
@@ -2361,11 +2361,6 @@ CHAR16 Message[][STRING_LENGTH] =
|
||||
L"没有能够训练的民兵。", //L"No militia that can be drilled present.",
|
||||
|
||||
L"%s 已经完全的探索了 %s。", //L"%s has fully explored %s."
|
||||
|
||||
// The first %s is a merc name and the second %s is an item name
|
||||
L"%s 排除了 %s 的故障。", //L"%s unjammed %s.",
|
||||
L"%s 未能排除 %s 的故障。", //L"%s failed to unjam %s.",
|
||||
L"%s 的行动点数不足以排除 %s 的故障。", //L"%s does not have enough APs to unjam %s.",
|
||||
};
|
||||
|
||||
// the country and its noun in the game
|
||||
@@ -6646,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
|
||||
@@ -6767,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",
|
||||
@@ -7556,7 +7553,7 @@ STR16 gzLateLocalizedString[] =
|
||||
L"%s用完了医药箱里的药品!", //"%s ran out of medical kits!",
|
||||
L"%s没有所需技能来医疗他人!", //"%s lacks the necessary skill to doctor anyone!",
|
||||
L"%s用完工具箱里的工具!", //"%s ran out of tool kits!",
|
||||
L"%s没有所需技能或属性来修理物品!", //"%s lacks the necessary skill to repair anything!",
|
||||
L"%s没有所需技能来修理物品!", //"%s lacks the necessary skill to repair anything!",
|
||||
|
||||
//44-45
|
||||
L"修理时间", //L"Repair Time",
|
||||
@@ -9390,7 +9387,7 @@ STR16 szBackgroundText_Value[]=
|
||||
L" %s%d%% 对人员治疗疾病的效率 \n", //L" %s%d%% effectiveness at treating population against diseases\n",
|
||||
L" 能够发现%d格之内的脚印 \n", //L"Can spot tracks up to %d tiles away\n",
|
||||
L" %s%d%% 被伏击时的初始距离 \n", //L" %s%d%% initial distance to enemy in ambush\n",
|
||||
L" %s%d%% 闪避蛇类攻击 \n", //L" %s%d%% chance to evade snake attacks\n",
|
||||
L" %s%d%% 几率回避攻击 \n", //L" %s%d%% chance to evade snake attacks\n",
|
||||
|
||||
L" 对某些其他背景的厌恶 \n", //L" dislikes some other backgrounds\n",
|
||||
L" 吸烟者", //L"Smoker",
|
||||
@@ -9487,8 +9484,8 @@ STR16 szNationalityText[]=
|
||||
L"古巴人", // 5
|
||||
L"丹麦人",
|
||||
L"法国人",
|
||||
L"俄罗斯人",
|
||||
L"特拉科纳人", // (UB takes place in Tracona)
|
||||
L"俄国人",
|
||||
L"Tracona人", // (UB takes place in Tracona)
|
||||
L"瑞士人", // 10
|
||||
L"牙买加人",
|
||||
L"波兰人",
|
||||
@@ -9497,103 +9494,103 @@ STR16 szNationalityText[]=
|
||||
L"南非人", // 15
|
||||
L"匈牙利人",
|
||||
L"苏格兰人",
|
||||
L"阿鲁科人",
|
||||
L"Arulco人",
|
||||
L"德国人",
|
||||
L"非洲人", // 20
|
||||
L"意大利人",
|
||||
L"荷兰人",
|
||||
L"罗马尼亚人",
|
||||
L"梅塔维拉人",
|
||||
L"罗马利亚人",
|
||||
L"Metavira人",
|
||||
|
||||
// newly added from here on
|
||||
L"阿富汗人", // 25
|
||||
L"阿尔巴尼亚人",
|
||||
L"阿根廷人",
|
||||
L"亚美尼亚人",
|
||||
L"阿塞拜疆人",
|
||||
L"孟加拉国人", // 30
|
||||
L"Afghan", // 25
|
||||
L"Albanian",
|
||||
L"Argentinian",
|
||||
L"Armenian",
|
||||
L"Azerbaijani",
|
||||
L"Bangladeshi ", // 30
|
||||
L"白俄罗斯人",
|
||||
L"比利时人",
|
||||
L"贝宁人",
|
||||
L"玻利维亚人",
|
||||
L"波斯尼亚人", // 35
|
||||
L"Beninese",
|
||||
L"Bolivian",
|
||||
L"Bosnian", // 35
|
||||
L"巴西人",
|
||||
L"保加利亚人",
|
||||
L"柬埔寨人",
|
||||
L"乍得人",
|
||||
L"智利人", // 40
|
||||
L"哥伦比亚人",
|
||||
L"刚果人",
|
||||
L"克罗地亚人",
|
||||
L"厄瓜多尔人",
|
||||
L"埃及人", // 45
|
||||
L"英格兰人",
|
||||
L"厄立特里亚人",
|
||||
L"Cambodian",
|
||||
L"Chadian",
|
||||
L"Chilean", // 40
|
||||
L"Columbian",
|
||||
L"Congolese",
|
||||
L"Croatian",
|
||||
L"Ecuadorian",
|
||||
L"Egyptian", // 45
|
||||
L"English",
|
||||
L"Eritrean",
|
||||
L"爱沙尼亚人",
|
||||
L"埃塞俄比亚人",
|
||||
L"菲律宾人", // 50
|
||||
L"Ethiopian",
|
||||
L"Filipino", // 50
|
||||
L"芬兰人",
|
||||
L"格鲁吉亚人",
|
||||
L"Georgian",
|
||||
L"希腊人",
|
||||
L"危地马拉人",
|
||||
L"海地人", // 55
|
||||
L"洪都拉斯人",
|
||||
L"Guatemalan",
|
||||
L"Haitian", // 55
|
||||
L"Honduran",
|
||||
L"印度人",
|
||||
L"印度尼西亚人",
|
||||
L"Indonesian",
|
||||
L"伊朗人",
|
||||
L"伊拉克人", // 60
|
||||
L"冰岛人",
|
||||
L"Islandic",
|
||||
L"以色列人",
|
||||
L"日本人",
|
||||
L"约旦人",
|
||||
L"哈萨克斯坦人", // 65
|
||||
L"韩国人和朝鲜人",
|
||||
L"吉尔吉斯斯坦人",
|
||||
L"老挝人",
|
||||
L"拉脱维亚人",
|
||||
L"黎巴嫩人", // 70
|
||||
L"立陶宛人",
|
||||
L"利比亚人",
|
||||
L"马其顿人",
|
||||
L"马来西亚人",
|
||||
L"Jordanian",
|
||||
L"Kazakhstani", // 65
|
||||
L"Korean",
|
||||
L"Kyrgyzstani",
|
||||
L"Laotian",
|
||||
L"Latvian",
|
||||
L"Lebanese", // 70
|
||||
L"Lithuanian",
|
||||
L"Lybian",
|
||||
L"Macedonian",
|
||||
L"Malaysian",
|
||||
L"墨西哥人", // 75
|
||||
L"蒙古人",
|
||||
L"摩洛哥人",
|
||||
L"莫桑比克人",
|
||||
L"缅甸人",
|
||||
L"纳米比亚人", // 80
|
||||
L"尼加拉瓜人",
|
||||
L"Mongolian",
|
||||
L"Moroccan",
|
||||
L"Mozambican",
|
||||
L"Myanma",
|
||||
L"Namibian", // 80
|
||||
L"Nicaraguan",
|
||||
L"尼日利亚人",
|
||||
L"尼日尔人",
|
||||
L"Nigerien",
|
||||
L"挪威人",
|
||||
L"巴基斯坦人", // 85
|
||||
L"巴拿马人",
|
||||
L"Pakistani", // 85
|
||||
L"Panamanian",
|
||||
L"葡萄牙人",
|
||||
L"卢旺达人",
|
||||
L"萨尔瓦多人",
|
||||
L"沙特人", // 90
|
||||
L"Rwandanese",
|
||||
L"Salvadoran",
|
||||
L"Saudi", // 90
|
||||
L"塞尔维亚人",
|
||||
L"斯洛伐克人",
|
||||
L"斯洛文尼亚人",
|
||||
L"索马里人",
|
||||
L"Slovakian",
|
||||
L"Slovenian",
|
||||
L"Somali",
|
||||
L"西班牙人", // 95
|
||||
L"苏丹人",
|
||||
L"Sudanese",
|
||||
L"瑞典人",
|
||||
L"叙利亚人",
|
||||
L"泰国人",
|
||||
L"多哥人", // 100
|
||||
L"突尼斯人",
|
||||
L"Thai",
|
||||
L"Togolese", // 100
|
||||
L"Tunisian",
|
||||
L"土耳其人",
|
||||
L"乌干达人",
|
||||
L"乌克兰人",
|
||||
L"乌拉圭人", // 105
|
||||
L"乌兹别克斯坦人",
|
||||
L"Ugandan",
|
||||
L"Ukrainian",
|
||||
L"Uruguayan", // 105
|
||||
L"Uzbekistani",
|
||||
L"委内瑞拉人",
|
||||
L"越南人",
|
||||
L"威尔士人",
|
||||
L"也门人", // 110
|
||||
L"扎蒙达人", // Zamunda
|
||||
L"津巴布韦人",
|
||||
L"Vietnamese",
|
||||
L"Welsh",
|
||||
L"Yemeni", // 110
|
||||
L"Zamundan", // Zamunda
|
||||
L"Zimbabwean",
|
||||
};
|
||||
|
||||
STR16 szNationalityTextAdjective[] =
|
||||
@@ -9606,8 +9603,8 @@ STR16 szNationalityTextAdjective[] =
|
||||
L"古巴人", // 5
|
||||
L"丹麦人",
|
||||
L"法国人",
|
||||
L"俄罗斯人",
|
||||
L"特拉科纳人" // UB takes place in Tracona
|
||||
L"俄国人",
|
||||
L"traconia人" // UB takes place in Tracona
|
||||
L"瑞士人", // 10
|
||||
L"牙买加人",
|
||||
L"波兰人",
|
||||
@@ -9616,103 +9613,103 @@ STR16 szNationalityTextAdjective[] =
|
||||
L"南非人", // 15
|
||||
L"匈牙利人",
|
||||
L"苏格兰人",
|
||||
L"阿鲁科人",
|
||||
L"Arulco人",
|
||||
L"德国人",
|
||||
L"非洲人", // 20
|
||||
L"意大利人",
|
||||
L"荷兰人",
|
||||
L"罗马尼亚人",
|
||||
L"梅塔维拉人",
|
||||
L"罗马利亚人",
|
||||
L"Metavira人",
|
||||
|
||||
// newly added from here on
|
||||
L"阿富汗人", // 25
|
||||
L"阿尔巴尼亚人",
|
||||
L"阿根廷人",
|
||||
L"亚美尼亚人",
|
||||
L"阿塞拜疆人",
|
||||
L"孟加拉国人", // 30
|
||||
L"afghans", // 25
|
||||
L"albanians",
|
||||
L"argentinians",
|
||||
L"armenians",
|
||||
L"azerbaijani",
|
||||
L"bangladeshi", // 30
|
||||
L"白俄罗斯人",
|
||||
L"比利时人",
|
||||
L"贝宁人",
|
||||
L"玻利维亚人",
|
||||
L"波斯尼亚人", // 35
|
||||
L"beninese",
|
||||
L"bolivians",
|
||||
L"bosnians", // 35
|
||||
L"巴西人",
|
||||
L"保加利亚人",
|
||||
L"柬埔寨人",
|
||||
L"乍得人",
|
||||
L"智利人", // 40
|
||||
L"哥伦比亚人",
|
||||
L"刚果人",
|
||||
L"克罗地亚人",
|
||||
L"厄瓜多尔人",
|
||||
L"埃及人", // 45
|
||||
L"英格兰人",
|
||||
L"厄立特里亚人",
|
||||
L"cambodians",
|
||||
L"chadians",
|
||||
L"chileans", // 40
|
||||
L"columbians",
|
||||
L"congolese",
|
||||
L"croatians",
|
||||
L"ecuadorians",
|
||||
L"egyptians", // 45
|
||||
L"englishmen",
|
||||
L"eritreans",
|
||||
L"爱沙尼亚人",
|
||||
L"埃塞俄比亚人",
|
||||
L"菲律宾人", // 50
|
||||
L"ethiopians",
|
||||
L"filipinos", // 50
|
||||
L"芬兰人",
|
||||
L"格鲁吉亚人",
|
||||
L"georgians",
|
||||
L"希腊人",
|
||||
L"危地马拉人",
|
||||
L"海地人", // 55
|
||||
L"洪都拉斯人",
|
||||
L"guatemalans",
|
||||
L"haitians", // 55
|
||||
L"hondurans",
|
||||
L"印度人",
|
||||
L"印度尼西亚人",
|
||||
L"indonesians",
|
||||
L"伊朗人",
|
||||
L"伊拉克人", // 60
|
||||
L"冰岛人",
|
||||
L"islandics",
|
||||
L"以色列人",
|
||||
L"日本人",
|
||||
L"约旦人",
|
||||
L"哈萨克斯坦人", // 65
|
||||
L"韩国人和朝鲜人",
|
||||
L"吉尔吉斯斯坦人",
|
||||
L"老挝人",
|
||||
L"拉脱维亚人",
|
||||
L"黎巴嫩人", // 70
|
||||
L"立陶宛人",
|
||||
L"利比亚人",
|
||||
L"马其顿人",
|
||||
L"马来西亚人",
|
||||
L"jordanians",
|
||||
L"kazakhstani", // 65
|
||||
L"koreans",
|
||||
L"kyrgyzstani",
|
||||
L"laotians",
|
||||
L"latvians",
|
||||
L"lebanese", // 70
|
||||
L"lithuanians",
|
||||
L"lybians",
|
||||
L"macedonians",
|
||||
L"malaysians",
|
||||
L"墨西哥人", // 75
|
||||
L"蒙古人",
|
||||
L"摩洛哥人",
|
||||
L"莫桑比克人",
|
||||
L"缅甸人",
|
||||
L"纳米比亚人", // 80
|
||||
L"尼加拉瓜人",
|
||||
L"mongolians",
|
||||
L"moroccans",
|
||||
L"mozambicans",
|
||||
L"myanmarians",
|
||||
L"namibians", // 80
|
||||
L"nicaraguans",
|
||||
L"尼日利亚人",
|
||||
L"尼日尔人",
|
||||
L"nigeriens",
|
||||
L"挪威人",
|
||||
L"巴基斯坦人", // 85
|
||||
L"巴拿马人",
|
||||
L"panamanians",
|
||||
L"葡萄牙人",
|
||||
L"卢旺达人",
|
||||
L"萨尔瓦多人",
|
||||
L"沙特人", // 90
|
||||
L"rwandanese",
|
||||
L"salvadorans",
|
||||
L"saudis", // 90
|
||||
L"塞尔维亚人",
|
||||
L"斯洛伐克人",
|
||||
L"斯洛文尼亚人",
|
||||
L"索马里人",
|
||||
L"slovakians",
|
||||
L"slovenians",
|
||||
L"somali",
|
||||
L"西班牙人", // 95
|
||||
L"苏丹人",
|
||||
L"sudanese",
|
||||
L"瑞典人",
|
||||
L"叙利亚人",
|
||||
L"泰国人",
|
||||
L"多哥人", // 100
|
||||
L"突尼斯人",
|
||||
L"thais",
|
||||
L"togolese", // 100
|
||||
L"tunisians",
|
||||
L"土耳其人",
|
||||
L"乌干达人",
|
||||
L"乌克兰人",
|
||||
L"乌拉圭人", // 105
|
||||
L"乌兹别克斯坦人",
|
||||
L"ugandans",
|
||||
L"ukrainians",
|
||||
L"uruguayans", // 105
|
||||
L"uzbekistani",
|
||||
L"委内瑞拉人",
|
||||
L"越南人",
|
||||
L"威尔士人",
|
||||
L"也门人", // 110
|
||||
L"扎蒙达人", // Zamunda
|
||||
L"津巴布韦人",
|
||||
L"vietnamese",
|
||||
L"welshs",
|
||||
L"yemenites", // 110
|
||||
L"zamundans", // Zamunda
|
||||
L"zimbabweans",
|
||||
};
|
||||
|
||||
// special text used if we do not hate any nation (value of -1)
|
||||
|
||||
+2
-5
@@ -2360,11 +2360,6 @@ CHAR16 Message[][STRING_LENGTH] =
|
||||
L"No militia that can be drilled present.",
|
||||
|
||||
L"%s has fully explored %s.", // TODO.Translate
|
||||
|
||||
// The first %s is a merc name and the second %s is an item name
|
||||
L"%s unjammed %s.",
|
||||
L"%s failed to unjam %s.",
|
||||
L"%s does not have enough APs to unjam %s."
|
||||
};
|
||||
|
||||
// the country and its noun in the game
|
||||
@@ -6648,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
|
||||
@@ -6769,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",
|
||||
|
||||
@@ -2361,11 +2361,6 @@ CHAR16 Message[][STRING_LENGTH] =
|
||||
L"No militia that can be drilled present.",
|
||||
|
||||
L"%s has fully explored %s.",
|
||||
|
||||
// The first %s is a merc name and the second %s is an item name
|
||||
L"%s unjammed %s.",
|
||||
L"%s failed to unjam %s.",
|
||||
L"%s does not have enough APs to unjam %s."
|
||||
};
|
||||
|
||||
// the country and its noun in the game
|
||||
@@ -6646,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
|
||||
@@ -6767,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",
|
||||
|
||||
@@ -2369,11 +2369,6 @@ CHAR16 Message[][STRING_LENGTH] =
|
||||
L"No militia that can be drilled present.",
|
||||
|
||||
L"%s has fully explored %s.", // TODO.Translate
|
||||
|
||||
// The first %s is a merc name and the second %s is an item name
|
||||
L"%s unjammed %s.",
|
||||
L"%s failed to unjam %s.",
|
||||
L"%s does not have enough APs to unjam %s."
|
||||
};
|
||||
|
||||
// the country and its noun in the game // TODO.Translate
|
||||
@@ -6653,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
|
||||
@@ -6773,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",
|
||||
|
||||
@@ -2403,11 +2403,6 @@ CHAR16 Message[][STRING_LENGTH] =
|
||||
L"No militia that can be drilled present.",
|
||||
|
||||
L"%s hat %s vollständig erkundet.",
|
||||
|
||||
// The first %s is a merc name and the second %s is an item name
|
||||
L"%s unjammed %s.",
|
||||
L"%s failed to unjam %s.",
|
||||
L"%s does not have enough APs to unjam %s."
|
||||
};
|
||||
|
||||
// the country and its noun in the game
|
||||
@@ -6520,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
|
||||
@@ -6641,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",
|
||||
|
||||
@@ -2355,11 +2355,6 @@ CHAR16 Message[][STRING_LENGTH] =
|
||||
L"No militia that can be drilled present.",
|
||||
|
||||
L"%s has fully explored %s.", // TODO.Translate
|
||||
|
||||
// The first %s is a merc name and the second %s is an item name
|
||||
L"%s unjammed %s.",
|
||||
L"%s failed to unjam %s.",
|
||||
L"%s does not have enough APs to unjam %s."
|
||||
};
|
||||
|
||||
// the country and its noun in the game
|
||||
@@ -6631,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
|
||||
@@ -6752,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",
|
||||
|
||||
@@ -396,8 +396,8 @@ STR16 gzIMPMajorTraitsHelpTextsSurvival[] =
|
||||
|
||||
L"疾病抗性 %s%d%%\n",//L" %s%d%% disease resistance\n",
|
||||
L"食物消耗 %s%d%%\n",//L" %s%d%% food consumption\n",
|
||||
L"水消耗 %s%d%%\n",//L" %s%d%% water consumption\n",
|
||||
L"闪避蛇类 +%d%%\n", //L"+%d%% snake evasion\n",
|
||||
L"水 消 耗 %s%d%%\n",//L" %s%d%% water consumption\n",
|
||||
L"回避几率 +%d%%\n", //L"+%d%% snake evasion\n",
|
||||
L"迷彩涂装效果 +%d%%\n",// L"+%d%s camouflage effectiveness\n",
|
||||
};
|
||||
|
||||
|
||||
@@ -2367,11 +2367,6 @@ CHAR16 Message[][STRING_LENGTH] =
|
||||
L"No militia that can be drilled present.",
|
||||
|
||||
L"%s has fully explored %s.", // TODO.Translate
|
||||
|
||||
// The first %s is a merc name and the second %s is an item name
|
||||
L"%s unjammed %s.",
|
||||
L"%s failed to unjam %s.",
|
||||
L"%s does not have enough APs to unjam %s."
|
||||
};
|
||||
|
||||
// the country and its noun in the game
|
||||
@@ -6650,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
|
||||
@@ -6771,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",
|
||||
|
||||
@@ -2361,11 +2361,6 @@ CHAR16 Message[][STRING_LENGTH] =
|
||||
L"No militia that can be drilled present.",
|
||||
|
||||
L"%s has fully explored %s.", // TODO.Translate
|
||||
|
||||
// The first %s is a merc name and the second %s is an item name
|
||||
L"%s unjammed %s.",
|
||||
L"%s failed to unjam %s.",
|
||||
L"%s does not have enough APs to unjam %s."
|
||||
};
|
||||
|
||||
// the country and its noun in the game
|
||||
@@ -6642,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
|
||||
@@ -6763,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",
|
||||
|
||||
@@ -612,10 +612,6 @@ enum
|
||||
|
||||
STR_ASSIGNMENT_EXPLORATION_DONE,
|
||||
|
||||
STR_UNJAMMED,
|
||||
STR_FAILED_UNJAM,
|
||||
STR_NO_AP_NO_UNJAM,
|
||||
|
||||
TEXT_NUM_STR_MESSAGE,
|
||||
};
|
||||
|
||||
|
||||
+5
-19
@@ -836,11 +836,8 @@ void Load32bppPNGImage(HIMAGE hImage, png::png_bytepp rows, png::png_infop info)
|
||||
hImage->p32BPPData = (UINT32*)MemAlloc(SIZE);
|
||||
if(!hImage->p32BPPData)
|
||||
{
|
||||
CHAR8 errorText[512];
|
||||
sprintf(errorText, "Bad allocation loading image. Filename: %s", hImage->ImageFile);
|
||||
|
||||
MemFree(hImage->pETRLEObject);
|
||||
SGP_THROW(errorText);
|
||||
SGP_THROW(L"bad alloc");
|
||||
}
|
||||
memset(hImage->p32BPPData, 0, SIZE);
|
||||
|
||||
@@ -881,11 +878,8 @@ void Load24bppPNGImage(HIMAGE hImage, png::png_bytepp rows, png::png_infop info)
|
||||
hImage->p16BPPData = (UINT16*)MemAlloc(SIZE);
|
||||
if(!hImage->p16BPPData)
|
||||
{
|
||||
CHAR8 errorText[512];
|
||||
sprintf(errorText, "Bad allocation loading image. Filename: %s", hImage->ImageFile);
|
||||
|
||||
MemFree(hImage->pETRLEObject);
|
||||
SGP_THROW(errorText);
|
||||
SGP_THROW(L"bad alloc");
|
||||
}
|
||||
memset(hImage->p16BPPData, 0, SIZE);
|
||||
|
||||
@@ -905,15 +899,11 @@ void Load24bppPNGImage(HIMAGE hImage, png::png_bytepp rows, png::png_infop info)
|
||||
|
||||
void LoadPalettedPNGImage(HIMAGE hImage, png::png_bytepp rows, png::png_infop info)
|
||||
{
|
||||
CHAR8 errorText[512];
|
||||
UINT32 SIZE = info->height * info->width * sizeof(UINT8);
|
||||
UINT8 *data = (UINT8*)MemAlloc(SIZE);
|
||||
if(!data)
|
||||
{
|
||||
sprintf(errorText, "Bad allocation loading image. Filename: %s", hImage->ImageFile);
|
||||
|
||||
MemFree(hImage->pETRLEObject);
|
||||
SGP_THROW(errorText);
|
||||
SGP_THROW(L"bad alloc");
|
||||
}
|
||||
memset(data,0,SIZE);
|
||||
for(unsigned int i = 0; i < info->height; ++i)
|
||||
@@ -925,10 +915,8 @@ void LoadPalettedPNGImage(HIMAGE hImage, png::png_bytepp rows, png::png_infop in
|
||||
hImage->pETRLEObject = (ETRLEObject*)MemAlloc(hImage->usNumberOfObjects * sizeof(ETRLEObject));
|
||||
if(!hImage->pETRLEObject)
|
||||
{
|
||||
sprintf(errorText, "Bad allocation for ETRLEObject. Filename: %s", hImage->ImageFile);
|
||||
|
||||
MemFree(data);
|
||||
SGP_THROW(errorText);
|
||||
SGP_THROW(L"bad alloc");
|
||||
}
|
||||
memset(hImage->pETRLEObject,0, hImage->usNumberOfObjects * sizeof(ETRLEObject));
|
||||
|
||||
@@ -943,11 +931,9 @@ void LoadPalettedPNGImage(HIMAGE hImage, png::png_bytepp rows, png::png_infop in
|
||||
hImage->p8BPPData = (UINT8*)MemAlloc( RESIZE );
|
||||
if(!hImage->p8BPPData)
|
||||
{
|
||||
sprintf(errorText, "Bad allocation for image pixel data. Filename: %s", hImage->ImageFile);
|
||||
|
||||
MemFree(hImage->pETRLEObject);
|
||||
MemFree(data);
|
||||
SGP_THROW(errorText);
|
||||
SGP_THROW(L"bad alloc");
|
||||
}
|
||||
UINT8 *compressed = hImage->p8BPPData;
|
||||
memset(hImage->p8BPPData,0,RESIZE);
|
||||
|
||||
Reference in New Issue
Block a user