mirror of
https://github.com/sfall-team/sfall.git
synced 2026-07-27 16:52:34 -07:00
Fixed the check of the ammo cost for a shot in CheckWeaponAmmoCost (#360)
This commit is contained in:
@@ -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
@@ -342,14 +342,14 @@ static long tempReloadCost;
|
|||||||
|
|
||||||
static long __fastcall item_weapon_reload_cost_fix(TGameObj* source, TGameObj* weapon, TGameObj** outAmmo) {
|
static long __fastcall item_weapon_reload_cost_fix(TGameObj* source, TGameObj* weapon, TGameObj** outAmmo) {
|
||||||
long reloadCost = sfgame_item_weapon_mp_cost(source, weapon, ATKTYPE_RWEAPON_RELOAD, 0);
|
long reloadCost = sfgame_item_weapon_mp_cost(source, weapon, 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_ai_have_ammo(source, weapon, outAmmo); // 0 - no ammo
|
return fo_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
|
||||||
@@ -357,9 +357,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_
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+45
-36
@@ -79,6 +79,8 @@ static bool hookedAimedShot;
|
|||||||
static std::vector<DWORD> disabledAS;
|
static std::vector<DWORD> disabledAS;
|
||||||
static std::vector<DWORD> forcedAS;
|
static std::vector<DWORD> forcedAS;
|
||||||
|
|
||||||
|
static bool checkWeaponAmmoCost;
|
||||||
|
|
||||||
///////////////////////////////// COMBAT BLOCK /////////////////////////////////
|
///////////////////////////////// COMBAT BLOCK /////////////////////////////////
|
||||||
|
|
||||||
static bool combatDisabled;
|
static bool combatDisabled;
|
||||||
@@ -123,30 +125,33 @@ void __stdcall SetBlockCombat(long toggle) {
|
|||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
DWORD __fastcall Combat_check_item_ammo_cost(TGameObj* weapon, DWORD hitMode) {
|
// Compares the cost (required count of rounds) for one shot with the current amount of ammo to make an attack or other checks
|
||||||
DWORD rounds = 1;
|
long __fastcall Combat_check_item_ammo_cost(TGameObj* weapon, AttackType hitMode) {
|
||||||
|
long currAmmo = fo_item_w_curr_ammo(weapon);
|
||||||
|
if (!checkWeaponAmmoCost || currAmmo <= 0) return currAmmo;
|
||||||
|
|
||||||
long anim = fo_item_w_anim_weap(weapon, (AttackType)hitMode);
|
long rounds = 1; // default ammo for single shot
|
||||||
|
|
||||||
|
long anim = fo_item_w_anim_weap(weapon, hitMode);
|
||||||
if (anim == ANIM_fire_burst || anim == ANIM_fire_continuous) {
|
if (anim == ANIM_fire_burst || anim == ANIM_fire_continuous) {
|
||||||
rounds = fo_item_w_rounds(weapon); // ammo in burst
|
rounds = fo_item_w_rounds(weapon); // ammo in burst
|
||||||
}
|
}
|
||||||
AmmoCostHook_Script(1, weapon, rounds); // get rounds cost from hook
|
|
||||||
long currAmmo = fo_item_w_curr_ammo(weapon);
|
|
||||||
|
|
||||||
long cost = 1; // default cost
|
DWORD newRounds = rounds;
|
||||||
if (currAmmo > 0) {
|
|
||||||
cost = rounds / currAmmo;
|
AmmoCostHook_Script(1, weapon, newRounds); // newRounds returns the new "ammo" value multiplied by the cost
|
||||||
if (rounds % currAmmo) cost++; // round up
|
|
||||||
}
|
// calculate the cost
|
||||||
return (cost > currAmmo) ? 0 : 1; // 0 - this will force "Out of ammo", 1 - this will force success (enough ammo)
|
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
|
||||||
static void __declspec(naked) combat_check_bad_shot_hook() {
|
static void __declspec(naked) combat_check_bad_shot_hook() {
|
||||||
__asm {
|
__asm {
|
||||||
push edx;
|
push edx;
|
||||||
push ecx; // weapon
|
push ecx; // weapon
|
||||||
mov edx, edi; // hitMode
|
mov edx, edi; // hitMode
|
||||||
call Combat_check_item_ammo_cost;
|
call Combat_check_item_ammo_cost;
|
||||||
pop ecx;
|
pop ecx;
|
||||||
pop edx;
|
pop edx;
|
||||||
@@ -158,9 +163,9 @@ static void __declspec(naked) combat_check_bad_shot_hook() {
|
|||||||
static void __declspec(naked) ai_search_inven_weap_hook() {
|
static void __declspec(naked) ai_search_inven_weap_hook() {
|
||||||
__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;
|
||||||
}
|
}
|
||||||
@@ -188,32 +193,34 @@ tryAttack:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static DWORD __fastcall divide_burst_rounds_by_ammo_cost(TGameObj* 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, TGameObj* weapon, long burstRounds) {
|
||||||
|
DWORD roundsCost = 1; // default burst cost
|
||||||
|
|
||||||
rounds = burstRounds; // rounds in burst
|
roundsCost = burstRounds; // rounds in burst attack
|
||||||
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, TGameObj* object, double val) {
|
static double ApplyModifiers(std::vector<KnockbackModifier>* mods, TGameObj* 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];
|
||||||
@@ -297,10 +304,11 @@ bool __stdcall Combat_IsBurstDisabled(TGameObj* critter) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static long __fastcall CheckDisableBurst(TGameObj* critter, TGameObj* weapon) {
|
static long __fastcall CheckDisableBurst(TGameObj* critter, TGameObj* weapon) {
|
||||||
if (fo_item_w_anim_weap(weapon, ATKTYPE_RWEAPON_SECONDARY) == ANIM_fire_burst &&
|
if (Combat_IsBurstDisabled(critter)) {
|
||||||
Combat_IsBurstDisabled(critter))
|
long anim = fo_item_w_anim_weap(weapon, ATKTYPE_RWEAPON_SECONDARY);
|
||||||
{
|
if (anim == ANIM_fire_burst || anim == 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;
|
||||||
}
|
}
|
||||||
@@ -562,10 +570,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
@@ -43,7 +43,7 @@ void Combat_Init();
|
|||||||
void Combat_OnGameLoad();
|
void Combat_OnGameLoad();
|
||||||
void BodypartHitChances();
|
void BodypartHitChances();
|
||||||
|
|
||||||
DWORD __fastcall Combat_check_item_ammo_cost(TGameObj* weapon, DWORD hitMode);
|
long __fastcall Combat_check_item_ammo_cost(TGameObj* weapon, AttackType hitMode);
|
||||||
bool __stdcall Combat_IsBurstDisabled(TGameObj* critter);
|
bool __stdcall Combat_IsBurstDisabled(TGameObj* critter);
|
||||||
|
|
||||||
void __stdcall SetBlockCombat(long toggle);
|
void __stdcall SetBlockCombat(long toggle);
|
||||||
|
|||||||
@@ -899,7 +899,7 @@ int __fastcall AmmoCostHook_Script(DWORD hookType, TGameObj* weapon, DWORD &roun
|
|||||||
rounds = 1; // set default multiply for check burst attack
|
rounds = 1; // set default multiply for check burst attack
|
||||||
} else {
|
} else {
|
||||||
result = fo_item_w_compute_ammo_cost(weapon, &rounds);
|
result = fo_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)
|
||||||
|
|
||||||
@@ -922,7 +922,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;
|
||||||
}
|
}
|
||||||
@@ -1904,7 +1904,7 @@ void HookScripts_Init() {
|
|||||||
HookCall(0x478560, ItemDamageHook);
|
HookCall(0x478560, ItemDamageHook);
|
||||||
|
|
||||||
// HOOK_AMMOCOST
|
// HOOK_AMMOCOST
|
||||||
HookCall(0x423A7C, AmmoCostHook);
|
HookCall(0x423A7C, AmmoCostHook); // compute_attack_
|
||||||
|
|
||||||
// HOOK_USEOBJ
|
// HOOK_USEOBJ
|
||||||
const DWORD useObjHkAddr[] = {0x42AEBF, 0x473607, 0x49C12E};
|
const DWORD useObjHkAddr[] = {0x42AEBF, 0x473607, 0x49C12E};
|
||||||
|
|||||||
Reference in New Issue
Block a user