Fixed the check of the ammo cost for a shot in CheckWeaponAmmoCost (#360)

This commit is contained in:
NovaRain
2021-03-11 14:39:21 +08:00
parent d95e43956d
commit 4d04cae5a6
5 changed files with 58 additions and 47 deletions
+1
View File
@@ -349,6 +349,7 @@ int arg2 - Ammo cost calculated by original function (this is basically 2 fo
int arg3 - Type of hook (0 - when subtracting ammo after single shot attack, 1 - when checking for "out of ammo" before attack, 2 - when calculating number of burst rounds, 3 - when subtracting ammo after burst attack) int arg3 - Type of hook (0 - when subtracting ammo after single shot attack, 1 - when checking for "out of ammo" before attack, 2 - when calculating number of burst rounds, 3 - when subtracting ammo after burst attack)
int ret0 - new ammo cost value (set to 0 for unlimited ammo) int ret0 - new ammo cost value (set to 0 for unlimited ammo)
NOTE: for hook type 2, you should return the new cost value based on the original ammo cost instead of the number of bullets
------------------------------------------- -------------------------------------------
+5 -4
View File
@@ -346,14 +346,14 @@ static long tempReloadCost;
static long __fastcall item_weapon_reload_cost_fix(fo::GameObject* source, fo::GameObject* weapon, fo::GameObject** outAmmo) { static long __fastcall item_weapon_reload_cost_fix(fo::GameObject* source, fo::GameObject* weapon, fo::GameObject** outAmmo) {
long reloadCost = game::Items::item_weapon_mp_cost(source, weapon, fo::AttackType::ATKTYPE_RWEAPON_RELOAD, 0); long reloadCost = game::Items::item_weapon_mp_cost(source, weapon, fo::AttackType::ATKTYPE_RWEAPON_RELOAD, 0);
if (reloadCost > source->critter.movePoints) return -1; // no action points if (reloadCost > source->critter.movePoints) return -1; // not enough action points
tempReloadCost = reloadCost; tempReloadCost = reloadCost;
return fo::func::ai_have_ammo(source, weapon, outAmmo); // 0 - no ammo return fo::func::ai_have_ammo(source, weapon, outAmmo); // 0 - no ammo
} }
static void __declspec(naked) ai_try_attack_hook_cost_reload() { static void __declspec(naked) ai_try_attack_hook_cost_reload() {
static const DWORD ai_try_attack_hook_unwield_Ret = 0x42AACD; static const DWORD ai_try_attack_hook_goNext_Ret = 0x42A9F2;
__asm { __asm {
push ebx; // ammoObj ref push ebx; // ammoObj ref
mov ecx, eax; // source mov ecx, eax; // source
@@ -361,9 +361,10 @@ static void __declspec(naked) ai_try_attack_hook_cost_reload() {
cmp eax, -1; cmp eax, -1;
je noAPs; je noAPs;
retn; retn;
noAPs: noAPs: // not enough action points
add esp, 4; // destroy ret add esp, 4; // destroy ret
jmp ai_try_attack_hook_unwield_Ret; // unwield weapon (default) mov edi, 10;
jmp ai_try_attack_hook_goNext_Ret; // end ai_try_attack_
} }
} }
+42 -33
View File
@@ -89,26 +89,31 @@ static bool hookedAimedShot;
static std::vector<DWORD> disabledAS; static std::vector<DWORD> disabledAS;
static std::vector<DWORD> forcedAS; static std::vector<DWORD> forcedAS;
DWORD __fastcall Combat::check_item_ammo_cost(fo::GameObject* weapon, DWORD hitMode) { static bool checkWeaponAmmoCost;
DWORD rounds = 1;
long anim = fo::func::item_w_anim_weap(weapon, (fo::AttackType)hitMode); // Compares the cost (required count of rounds) for one shot with the current amount of ammo to make an attack or other checks
long __fastcall Combat::check_item_ammo_cost(fo::GameObject* weapon, fo::AttackType hitMode) {
long currAmmo = fo::func::item_w_curr_ammo(weapon);
if (!checkWeaponAmmoCost || currAmmo <= 0) return currAmmo;
long rounds = 1; // default ammo for single shot
long anim = fo::func::item_w_anim_weap(weapon, hitMode);
if (anim == fo::Animation::ANIM_fire_burst || anim == fo::Animation::ANIM_fire_continuous) { if (anim == fo::Animation::ANIM_fire_burst || anim == fo::Animation::ANIM_fire_continuous) {
rounds = fo::func::item_w_rounds(weapon); // ammo in burst rounds = fo::func::item_w_rounds(weapon); // ammo in burst
} }
if (HookScripts::IsInjectHook(HOOK_AMMOCOST)) {
AmmoCostHook_Script(1, weapon, rounds); // get rounds cost from hook
} else if (rounds == 1) {
fo::func::item_w_compute_ammo_cost(weapon, &rounds);
}
long currAmmo = fo::func::item_w_curr_ammo(weapon);
long cost = 1; // default cost DWORD newRounds = rounds;
if (currAmmo > 0) {
cost = rounds / currAmmo; if (HookScripts::IsInjectHook(HOOK_AMMOCOST)) {
if (rounds % currAmmo) cost++; // round up AmmoCostHook_Script(1, weapon, newRounds); // newRounds returns the new "ammo" value multiplied by the cost
} else if (rounds == 1) { // NOTE: it doesn't make sense to call item_w_compute_ammo_cost for rounds greater than one
fo::func::item_w_compute_ammo_cost(weapon, &newRounds);
} }
return (cost > currAmmo) ? 0 : 1; // 0 - this will force "Out of ammo", 1 - this will force success (enough ammo)
// calculate the cost
long cost = (newRounds != rounds) ? newRounds / rounds : 1; // 1 - default cost
return (cost > currAmmo) ? 0 : currAmmo; // 0 - this will force "Not Enough Ammo"
} }
// adds check for weapons which require more than 1 ammo for single shot (super cattle prod & mega power fist) and burst rounds // adds check for weapons which require more than 1 ammo for single shot (super cattle prod & mega power fist) and burst rounds
@@ -129,8 +134,8 @@ static void __declspec(naked) ai_search_inven_weap_hook() {
using namespace fo; using namespace fo;
__asm { __asm {
push ecx; push ecx;
mov ecx, eax; // weapon
mov edx, ATKTYPE_RWEAPON_PRIMARY; // hitMode mov edx, ATKTYPE_RWEAPON_PRIMARY; // hitMode
mov ecx, eax; // weapon
call Combat::check_item_ammo_cost; // enough ammo? call Combat::check_item_ammo_cost; // enough ammo?
pop ecx; pop ecx;
retn; retn;
@@ -160,34 +165,36 @@ tryAttack:
} }
} }
static DWORD __fastcall divide_burst_rounds_by_ammo_cost(fo::GameObject* weapon, register DWORD currAmmo, DWORD burstRounds) { /*** The return value should set the correct count of rounds for the burst calculated based on the cost ***/
DWORD rounds = 1; // default multiply static long __fastcall divide_burst_rounds_by_ammo_cost(long currAmmo, fo::GameObject* weapon, long burstRounds) {
DWORD roundsCost = 1; // default burst cost
if (HookScripts::IsInjectHook(HOOK_AMMOCOST)) { if (HookScripts::IsInjectHook(HOOK_AMMOCOST)) {
rounds = burstRounds; // rounds in burst roundsCost = burstRounds; // rounds in burst (the number of rounds fired in the burst)
AmmoCostHook_Script(2, weapon, rounds); AmmoCostHook_Script(2, weapon, roundsCost); // roundsCost returns the new cost
} }
DWORD cost = burstRounds * rounds; // so much ammo is required for this burst long cost = burstRounds * roundsCost; // amount of ammo required for this burst (multiplied by 1 or by the value returned from HOOK_AMMOCOST)
if (cost > currAmmo) cost = currAmmo; // if cost ammo more than current ammo, set it to current if (cost > currAmmo) cost = currAmmo; // if cost ammo more than current ammo, set it to current
return (cost / rounds); // divide back to get proper number of rounds for damage calculations return (cost / roundsCost); // divide back to get proper number of rounds for damage calculations
} }
static void __declspec(naked) compute_spray_hack() { static void __declspec(naked) compute_spray_hack() {
__asm { __asm { // ebp = current ammo
push edx; // weapon // push edx; // weapon
push ecx; // current ammo in weapon // push ecx; // current ammo in weapon
xchg ecx, edx; push eax; // rounds in burst attack, need to set ebp
push eax; // eax - rounds in burst attack, need to set ebp
call divide_burst_rounds_by_ammo_cost; call divide_burst_rounds_by_ammo_cost;
mov ebp, eax; // overwriten code mov ebp, eax; // overwriten code
pop ecx; // pop ecx;
pop edx; // pop edx;
retn; retn;
} }
} }
////////////////////////////////////////////////////////////////////////////////
static double ApplyModifiers(std::vector<KnockbackModifier>* mods, fo::GameObject* object, double val) { static double ApplyModifiers(std::vector<KnockbackModifier>* mods, fo::GameObject* object, double val) {
for (size_t i = 0; i < mods->size(); i++) { for (size_t i = 0; i < mods->size(); i++) {
KnockbackModifier* mod = &(*mods)[i]; KnockbackModifier* mod = &(*mods)[i];
@@ -271,11 +278,12 @@ bool Combat::IsBurstDisabled(fo::GameObject* critter) {
} }
static long __fastcall CheckDisableBurst(fo::GameObject* critter, fo::GameObject* weapon) { static long __fastcall CheckDisableBurst(fo::GameObject* critter, fo::GameObject* weapon) {
if (fo::func::item_w_anim_weap(weapon, fo::AttackType::ATKTYPE_RWEAPON_SECONDARY) == fo::Animation::ANIM_fire_burst && if (Combat::IsBurstDisabled(critter)) {
Combat::IsBurstDisabled(critter)) long anim = fo::func::item_w_anim_weap(weapon, fo::AttackType::ATKTYPE_RWEAPON_SECONDARY);
{ if (anim == fo::Animation::ANIM_fire_burst || anim == fo::Animation::ANIM_fire_continuous) {
return 10; // Disable Burst (area_attack_mode - non-existent value) return 10; // Disable Burst (area_attack_mode - non-existent value)
} }
}
return 0; return 0;
} }
@@ -520,10 +528,11 @@ void Combat::init() {
// Disables secondary burst attacks for the critter // Disables secondary burst attacks for the critter
MakeCall(0x429E44, ai_pick_hit_mode_hack_noBurst, 1); MakeCall(0x429E44, ai_pick_hit_mode_hack_noBurst, 1);
if (GetConfigInt("Misc", "CheckWeaponAmmoCost", 0)) { checkWeaponAmmoCost = (GetConfigInt("Misc", "CheckWeaponAmmoCost", 0) != 0);
if (checkWeaponAmmoCost) {
MakeCall(0x4234B3, compute_spray_hack, 1); MakeCall(0x4234B3, compute_spray_hack, 1);
HookCall(0x4266E9, combat_check_bad_shot_hook); HookCall(0x4266E9, combat_check_bad_shot_hook);
HookCall(0x429A37, ai_search_inven_weap_hook); HookCall(0x429A37, ai_search_inven_weap_hook); // check if there is enough ammo to shoot
HookCall(0x42A95D, ai_try_attack_hook); // jz func HookCall(0x42A95D, ai_try_attack_hook); // jz func
} }
+1 -1
View File
@@ -49,7 +49,7 @@ public:
static long determineHitChance; static long determineHitChance;
static DWORD __fastcall check_item_ammo_cost(fo::GameObject* weapon, DWORD hitMode); static long __fastcall check_item_ammo_cost(fo::GameObject* weapon, fo::AttackType hitMode);
static bool IsBurstDisabled(fo::GameObject* critter); static bool IsBurstDisabled(fo::GameObject* critter);
}; };
+3 -3
View File
@@ -343,7 +343,7 @@ int __fastcall AmmoCostHook_Script(DWORD hookType, fo::GameObject* weapon, DWORD
rounds = 1; // set default multiply for check burst attack rounds = 1; // set default multiply for check burst attack
} else { } else {
result = fo::func::item_w_compute_ammo_cost(weapon, &rounds); result = fo::func::item_w_compute_ammo_cost(weapon, &rounds);
if (result == -1) goto failed; // failed computed if (result == -1) goto failed; // computation failed
} }
args[2] = rounds; // rounds as computed by game (cost) args[2] = rounds; // rounds as computed by game (cost)
@@ -367,7 +367,7 @@ static void __declspec(naked) AmmoCostHook() {
mov ecx, 3; // hook type burst mov ecx, 3; // hook type burst
skip: skip:
xchg eax, edx; xchg eax, edx;
push eax; // rounds in attack push eax; // rounds in attack ref
call AmmoCostHook_Script; // edx - weapon call AmmoCostHook_Script; // edx - weapon
retn; retn;
} }
@@ -634,7 +634,7 @@ void Inject_ItemDamageHook() {
} }
void Inject_AmmoCostHook() { void Inject_AmmoCostHook() {
HookCall(0x423A7C, AmmoCostHook); HookCall(0x423A7C, AmmoCostHook); // compute_attack_
} }
void Inject_CombatTurnHook() { void Inject_CombatTurnHook() {