Fixed a bug in CheckWeaponAmmoCost (from Mr.Stalin)

It caused NPCs to not switch to other weapons when there is not enough
ammo to shoot.
Moved CheckWeaponAmmoCost code from Inventory.cpp to Combat.cpp.
Now the debug editor starts at the center of the screen.
Updated the previous fix in NPC combat control mod.
This commit is contained in:
NovaRain
2019-04-07 10:59:02 +08:00
parent d946850af4
commit 143f6895a4
6 changed files with 91 additions and 69 deletions
+1
View File
@@ -180,6 +180,7 @@
this.Controls.Add(this.dataGridView1);
this.MinimumSize = new System.Drawing.Size(450, 300);
this.Name = "DebugEditor";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "sfall Debug Editor";
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.DebugEditor_FormClosing);
((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).EndInit();
+1
View File
@@ -111,6 +111,7 @@
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.SizableToolWindow;
this.MinimumSize = new System.Drawing.Size(300, 200);
this.Name = "EditorWindow";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "Edit Values";
((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).EndInit();
this.ResumeLayout(false);
Binary file not shown.
+2 -2
View File
@@ -109,8 +109,8 @@ procedure invenwield_handler begin
event := get_sfall_arg;
// Fix weapon duplication when equipping in combat
if (event and AllowControl(obj_pid(critter))) then begin
set_flags(item, get_flags(item) bwand bwnot(FLAG_LEFT_HAND bwor FLAG_RIGHT_HAND));
if (event and slot == INVEN_TYPE_RIGHT_HAND and AllowControl(obj_pid(critter))) then begin
set_flags(item, get_flags(item) bwand bwnot(FLAG_LEFT_HAND));
end
end
+87
View File
@@ -21,6 +21,7 @@
#include "..\main.h"
#include "..\FalloutEngine\Fallout2.h"
#include "HookScripts.h"
#include "LoadGameHook.h"
#include "Objects.h"
@@ -48,6 +49,87 @@ static bool hookedAimedShot;
static std::vector<DWORD> disabledAS;
static std::vector<DWORD> forcedAS;
static DWORD __fastcall add_check_for_item_ammo_cost(register fo::GameObject* weapon, DWORD hitMode) {
DWORD rounds = 1;
DWORD anim = fo::func::item_w_anim_weap(weapon, hitMode);
if (anim == fo::Animation::ANIM_fire_burst || anim == fo::Animation::ANIM_fire_continuous) {
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);
}
DWORD currAmmo = fo::func::item_w_curr_ammo(weapon);
DWORD cost = 1; // default cost
if (currAmmo > 0) {
cost = rounds / currAmmo;
if (rounds % currAmmo) cost++; // round up
}
return (cost > currAmmo) ? 0 : 1; // 0 - this will force "Out of ammo", 1 - this will force success (enough ammo)
}
// 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() {
__asm {
push edx;
push ecx; // weapon
mov edx, edi; // hitMode
call add_check_for_item_ammo_cost;
pop ecx;
pop edx;
retn;
}
}
// check if there is enough ammo to shoot
static void __declspec(naked) ai_search_inven_weap_hook() {
using namespace fo;
__asm {
push ecx;
mov ecx, eax; // weapon
mov edx, ATKTYPE_RWEAPON_PRIMARY; // hitMode
call add_check_for_item_ammo_cost;
mov ebx, eax; // result
mov ecx, esi;
mov edx, ATKTYPE_RWEAPON_SECONDARY;
call add_check_for_item_ammo_cost;
and eax, ebx; // enough ammo?
pop ecx;
retn;
}
}
static DWORD __fastcall divide_burst_rounds_by_ammo_cost(fo::GameObject* weapon, register DWORD currAmmo, DWORD burstRounds) {
DWORD rounds = 1; // default multiply
if (HookScripts::IsInjectHook(HOOK_AMMOCOST)) {
rounds = burstRounds; // rounds in burst
AmmoCostHook_Script(2, weapon, &rounds);
}
DWORD cost = burstRounds * rounds; // so much ammo is required for this burst
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
}
static void __declspec(naked) compute_spray_hack() {
__asm {
push edx; // weapon
push ecx; // current ammo in weapon
xchg ecx, edx;
push eax; // eax - rounds in burst attack, need to set ebp
call divide_burst_rounds_by_ammo_cost;
mov ebp, eax; // overwriten code
pop ecx;
pop edx;
retn;
}
}
static double ApplyModifiers(std::vector<KnockbackModifier>* mods, fo::GameObject* object, double val) {
for (DWORD i = 0; i < mods->size(); i++) {
KnockbackModifier* mod = &(*mods)[i];
@@ -308,6 +390,11 @@ void Combat::init() {
// Actually disables all secondary attacks for the critter, regardless of whether the weapon has a burst attack
MakeCall(0x429E44, ai_pick_hit_mode_hack, 1); // NoBurst
if (GetConfigInt("Misc", "CheckWeaponAmmoCost", 0)) {
HookCall(0x4266E9, combat_check_bad_shot_hook);
MakeCall(0x4234B3, compute_spray_hack, 1);
HookCall(0x429A37, ai_search_inven_weap_hook);
}
LoadGameHook::OnGameReset() += Combat_OnGameLoad;
}
-67
View File
@@ -388,68 +388,6 @@ static void __declspec(naked) inven_ap_cost_hack() {
}
}
static DWORD __fastcall add_check_for_item_ammo_cost(register fo::GameObject* weapon, DWORD hitMode) {
DWORD rounds = 1;
DWORD anim = fo::func::item_w_anim_weap(weapon, hitMode);
if (anim == fo::Animation::ANIM_fire_burst || anim == fo::Animation::ANIM_fire_continuous) {
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
}
DWORD currAmmo = fo::func::item_w_curr_ammo(weapon);
DWORD cost = 1; // default cost
if (currAmmo > 0) cost = (DWORD)ceilf((float)rounds / currAmmo);
return (cost > currAmmo) ? 0 : 1; // 0 - this will force "Out of ammo", 1 - this will force success (enough ammo)
}
// 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() {
__asm {
push edx;
push ebx;
push ecx; // weapon
mov edx, edi; // hitMode
call add_check_for_item_ammo_cost;
pop ecx;
pop ebx;
pop edx;
retn;
}
}
static DWORD __fastcall divide_burst_rounds_by_ammo_cost(fo::GameObject* weapon, register DWORD currAmmo, DWORD burstRounds) {
DWORD rounds = 1; // default multiply
if (HookScripts::IsInjectHook(HOOK_AMMOCOST)) {
rounds = burstRounds; // rounds in burst
AmmoCostHook_Script(2, weapon, &rounds);
}
DWORD cost = burstRounds * rounds; // so much ammo is required for this burst
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
}
static void __declspec(naked) compute_spray_hack() {
__asm {
push edx; // weapon
push ecx; // current ammo in weapon
xchg ecx, edx;
push eax; // eax - rounds in burst attack, need to set ebp
call divide_burst_rounds_by_ammo_cost;
mov ebp, eax; // overwriten code
pop ecx;
pop edx;
retn;
}
}
static void __declspec(naked) SetDefaultAmmo() {
using namespace fo;
using namespace Fields;
@@ -783,11 +721,6 @@ void Inventory::init() {
MakeCall(0x49C3D9, SuperStimFix);
}
if (GetConfigInt("Misc", "CheckWeaponAmmoCost", 0)) {
HookCall(0x4266E9, combat_check_bad_shot_hook);
MakeCall(0x4234B3, compute_spray_hack, 1);
}
reloadWeaponKey = GetConfigInt("Input", "ReloadWeaponKey", 0);
if (GetConfigInt("Misc", "StackEmptyWeapons", 0)) {