From a9aeac1f6ff137e413bec0dfad249b6470d9b3ef Mon Sep 17 00:00:00 2001 From: NovaRain Date: Tue, 28 May 2019 11:18:11 +0800 Subject: [PATCH] Fixed AI not checking min HP properly for using stims. Edited the code of InventoryApCost in Inventory.cpp. Minor edits to some other code. --- sfall/AI.cpp | 14 +++++ sfall/BugFixes.cpp | 14 ++--- sfall/Console.cpp | 4 +- sfall/Define.h | 2 + sfall/Inventory.cpp | 126 +++++++++++++++++++-------------------- sfall/ScriptExtender.cpp | 2 +- 6 files changed, 88 insertions(+), 74 deletions(-) diff --git a/sfall/AI.cpp b/sfall/AI.cpp index 4e61f604..8243e909 100644 --- a/sfall/AI.cpp +++ b/sfall/AI.cpp @@ -72,6 +72,17 @@ tryHeal: } } +static void __declspec(naked) ai_check_drugs_hook() { + __asm { + call stat_level_; // current hp + mov edx, dword ptr [esp + 0x34 - 0x1C + 4]; // ai cap + mov edx, [edx + 0x10]; // min_hp + cmp eax, edx; // curr_hp < cap.min_hp + cmovl edi, edx; + retn; + } +} + //////////////////////////////////////////////////////////////////////////////// static DWORD RetryCombatLastAP; @@ -190,6 +201,9 @@ void AIInit() { // Fix to allow fleeing NPC to use drugs MakeCall(0x42B1DC, combat_ai_hack); + // Fix for AI not checking minimum hp properly for using stimpaks (prevents premature fleeing) + HookCall(0x428579, ai_check_drugs_hook); + // Fix for NPC stuck in fleeing mode when the hit chance of a target was too low HookCall(0x42B1E3, combat_ai_hook_FleeFix); HookCall(0x42ABA8, ai_try_attack_hook_FleeFix); diff --git a/sfall/BugFixes.cpp b/sfall/BugFixes.cpp index 93a3a7ae..956069b5 100644 --- a/sfall/BugFixes.cpp +++ b/sfall/BugFixes.cpp @@ -791,19 +791,19 @@ static void __declspec(naked) PipStatus_AddHotLines_hook() { static void __declspec(naked) perform_withdrawal_start_display_print_hook() { __asm { - test eax, eax - jz end - jmp display_print_ + test eax, eax; + jz end; + jmp display_print_; end: - retn + retn; } } static void __declspec(naked) op_wield_obj_critter_adjust_ac_hook() { __asm { - call adjust_ac_ - xor eax, eax // not animated - jmp intface_update_ac_ + call adjust_ac_; + xor eax, eax; // not animated + jmp intface_update_ac_; } } diff --git a/sfall/Console.cpp b/sfall/Console.cpp index ab7db53f..b4ddf8b8 100644 --- a/sfall/Console.cpp +++ b/sfall/Console.cpp @@ -33,10 +33,10 @@ static void _stdcall ConsoleFilePrint(const char* msg) { static const DWORD ConsoleHookRet = 0x431871; static void __declspec(naked) ConsoleHook() { __asm { - pushad; + pushadc; push eax; call ConsoleFilePrint; - popad; + popadc; push ebx; push ecx; push edx; diff --git a/sfall/Define.h b/sfall/Define.h index 2344a621..6a7f0c70 100644 --- a/sfall/Define.h +++ b/sfall/Define.h @@ -337,6 +337,8 @@ enum AttackType : long ATKTYPE_PIERCINGKICK = 19 }; +#define PLAYER_ID (18000) + #define OBJFLAG_CAN_WEAR_ITEMS (0xF000000) #define OBJFLAG_HELD_IN_RIGHT (0x10000) diff --git a/sfall/Inventory.cpp b/sfall/Inventory.cpp index 106d5ba9..4bf0ed5d 100644 --- a/sfall/Inventory.cpp +++ b/sfall/Inventory.cpp @@ -29,9 +29,9 @@ static DWORD sizeLimitMode; static DWORD invSizeMaxLimit; -static DWORD ReloadWeaponKey = 0; -static DWORD ItemFastMoveKey = 0; -static DWORD SkipFromContainer = 0; +static DWORD reloadWeaponKey = 0; +static DWORD itemFastMoveKey = 0; +static DWORD skipFromContainer = 0; struct sMessage { DWORD number; @@ -59,7 +59,7 @@ TGameObj* GetActiveItem() { } void InventoryKeyPressedHook(DWORD dxKey, bool pressed, DWORD vKey) { - if (pressed && ReloadWeaponKey && dxKey == ReloadWeaponKey && IsMapLoaded() && (GetCurrentLoops() & ~(COMBAT | PCOMBAT)) == 0) { + if (pressed && reloadWeaponKey && dxKey == reloadWeaponKey && IsMapLoaded() && (GetCurrentLoops() & ~(COMBAT | PCOMBAT)) == 0) { DWORD maxAmmo, curAmmo; TGameObj* item = GetActiveItem(); __asm { @@ -91,6 +91,7 @@ void InventoryKeyPressedHook(DWORD dxKey, bool pressed, DWORD vKey) { } ///////////////////////////////////////////////////////////////// + DWORD __stdcall sf_item_total_size(TGameObj* critter) { int totalSize; __asm { @@ -118,20 +119,6 @@ DWORD __stdcall sf_item_total_size(TGameObj* critter) { return totalSize; } -/*static const DWORD ObjPickupFail=0x49B70D; -static const DWORD ObjPickupEnd=0x49B6F8; -static const DWORD size_limit; -static __declspec(naked) void ObjPickupHook() { - __asm { - cmp edi, ds:[_obj_dude]; - jnz end; -end: - lea edx, [esp+0x10]; - mov eax, ecx; - jmp ObjPickupEnd; - } -}*/ - static int __stdcall CritterGetMaxSize(TGameObj* critter) { if (critter == *ptr_obj_dude) return invSizeMaxLimit; @@ -232,8 +219,8 @@ static __declspec(naked) void barter_attempt_transaction_hack_pc() { /* cmp eax, edx */ jg fail; // if there's no available weight //------ - mov ecx, edi; // source (pc) - mov edx, ebp; // npc table + mov ecx, edi; // source (pc) + mov edx, ebp; // npc table call BarterAttemptTransaction; test eax, eax; jz fail; @@ -372,51 +359,33 @@ static void __declspec(naked) gdControlUpdateInfo_hack() { ///////////////////////////////////////////////////////////////// static char SuperStimMsg[128]; -static int __fastcall SuperStimFix2(TGameObj* item, TGameObj* target) { +static int __fastcall SuperStimFix(TGameObj* item, TGameObj* target) { if (item->pid != PID_SUPER_STIMPAK || !target || (target->pid & 0xFF000000) != (OBJ_TYPE_CRITTER << 24)) { // 0x01000000 return 0; } - long curr_hp, max_hp; - curr_hp = StatLevel(target, STAT_current_hp); - max_hp = StatLevel(target, STAT_max_hit_points); + long curr_hp = StatLevel(target, STAT_current_hp); + long max_hp = StatLevel(target, STAT_max_hit_points); if (curr_hp < max_hp) return 0; DisplayConsoleMessage(SuperStimMsg); return -1; } -static const DWORD UseItemHookRet = 0x49C5F4; -static void __declspec(naked) SuperStimFix() { +static const DWORD protinst_use_item_on_Ret = 0x49C5F4; +static void __declspec(naked) protinst_use_item_on_hack() { __asm { push ecx; - mov ecx, ebx; // ecx - item - call SuperStimFix2; // edx - target + mov ecx, ebx; // ecx - item + call SuperStimFix; // edx - target pop ecx; test eax, eax; jnz end; - mov ebp, -1; // overwritten engine code + mov ebp, -1; // overwritten engine code retn; end: - add esp, 4; // destroy ret - jmp UseItemHookRet; // exit - } -} - -static int invenApCost, invenApCostDef; -static char invenApQPReduction; -void _stdcall SetInvenApCost(int cost) { - invenApCost = cost; -} -static const DWORD inven_ap_cost_hack_ret = 0x46E816; -static void __declspec(naked) inven_ap_cost_hack() { - _asm { - movzx ebx, byte ptr invenApQPReduction; - mul bl; - mov edx, invenApCost; - sub edx, eax; - mov eax, edx; - jmp inven_ap_cost_hack_ret; + add esp, 4; // destroy ret + jmp protinst_use_item_on_Ret; // exit } } @@ -690,10 +659,10 @@ static void __declspec(naked) do_move_timer_hook() { pushadc; } - KeyDown(ItemFastMoveKey); // check pressed + KeyDown(itemFastMoveKey); // check pressed __asm { - cmp SkipFromContainer, 0; + cmp skipFromContainer, 0; jz noSkip; cmp dword ptr [esp + 0x14 + 16], 0x474A43; jnz noSkip; @@ -710,6 +679,37 @@ end: } } +static int invenApCost, invenApCostDef; +static char invenApQPReduction; +static const DWORD inven_ap_cost_Ret = 0x46E812; +static void __declspec(naked) inven_ap_cost_hack() { + _asm { + mul byte ptr invenApQPReduction; + mov edx, invenApCost; + jmp inven_ap_cost_Ret; + } +} + +static bool onlyOnceAP = false; +inline static void ApplyInvenApCostPatch() { + MakeJump(0x46E80B, inven_ap_cost_hack); + onlyOnceAP = true; +} + +void _stdcall SetInvenApCost(int cost) { + invenApCost = cost; + if (!onlyOnceAP) ApplyInvenApCostPatch(); +} + +// TODO: Make GetInvenApCost() function +/*long GetInvenApCost() { + long plevel = PerkLevel(*ptr_obj_dude, PERK_quick_pockets); + return invenApCost - (invenApQPReduction * plevel); +}*/ + +void InventoryReset() { + invenApCost = invenApCostDef; +} void InventoryInit() { sizeLimitMode = GetPrivateProfileInt("Misc", "CritterInvSizeLimitMode", 0, ini); @@ -762,13 +762,17 @@ void InventoryInit() { } } - invenApCost = invenApCostDef = GetPrivateProfileInt("Misc", "InventoryApCost", 4, ini); - invenApQPReduction = GetPrivateProfileInt("Misc", "QuickPocketsApCostReduction", 2, ini); - MakeJump(0x46E80B, inven_ap_cost_hack); - if(GetPrivateProfileInt("Misc", "SuperStimExploitFix", 0, ini)) { GetPrivateProfileString("sfall", "SuperStimExploitMsg", "You cannot use a super stim on someone who is not injured!", SuperStimMsg, 128, translationIni); - MakeCall(0x49C3D9, SuperStimFix); + MakeCall(0x49C3D9, protinst_use_item_on_hack); + } + + reloadWeaponKey = GetPrivateProfileInt("Input", "ReloadWeaponKey", 0, ini); + + invenApCost = invenApCostDef = GetPrivateProfileInt("Misc", "InventoryApCost", 4, ini); + invenApQPReduction = GetPrivateProfileInt("Misc", "QuickPocketsApCostReduction", 2, ini); + if (invenApCostDef != 4 || invenApQPReduction != 2) { + ApplyInvenApCostPatch(); } if(GetPrivateProfileInt("Misc", "CheckWeaponAmmoCost", 0, ini)) { @@ -778,8 +782,6 @@ void InventoryInit() { MakeCall(0x4234B3, compute_spray_hack, 1); } - ReloadWeaponKey = GetPrivateProfileInt("Input", "ReloadWeaponKey", 0, ini); - if (GetPrivateProfileIntA("Misc", "StackEmptyWeapons", 0, ini)) { MakeCall(0x4736C6, inven_action_cursor_hack); HookCall(0x4772AA, item_add_mult_hook); @@ -794,11 +796,11 @@ void InventoryInit() { SafeWrite8(0x476569, 0x91); // xchg ecx, eax }; - ItemFastMoveKey = GetPrivateProfileIntA("Input", "ItemFastMoveKey", DIK_LCONTROL, ini); - if (ItemFastMoveKey > 0) { + itemFastMoveKey = GetPrivateProfileIntA("Input", "ItemFastMoveKey", DIK_LCONTROL, ini); + if (itemFastMoveKey > 0) { HookCall(0x476897, do_move_timer_hook); // Do not call the 'Move Items' window when taking items from containers or corpses - SkipFromContainer = GetPrivateProfileIntA("Input", "FastMoveFromContainer", 0, ini); + skipFromContainer = GetPrivateProfileIntA("Input", "FastMoveFromContainer", 0, ini); } if (GetPrivateProfileIntA("Misc", "ItemCounterDefaultMax", 0, ini)) { @@ -818,7 +820,3 @@ void InventoryInit() { *((DWORD*)_max) = 100; }; } - -void InventoryReset() { - invenApCost = invenApCostDef; -} diff --git a/sfall/ScriptExtender.cpp b/sfall/ScriptExtender.cpp index 7dcdd6b4..11d32e0a 100644 --- a/sfall/ScriptExtender.cpp +++ b/sfall/ScriptExtender.cpp @@ -1224,7 +1224,7 @@ long objUniqueID = UID_START; // saving to sfallgv.sav // player ID = 18000, all party members have ID = 18000 + its pid (file number of prototype) long SetObjectUniqueID(TGameObj* obj) { long id = obj->ID; - if (id > UID_START || obj == *ptr_obj_dude || (id >= 18000 && id < 83536)) return id; // 65535 maximum possible number of prototypes + if (id > UID_START || obj == *ptr_obj_dude || (id >= PLAYER_ID && id < 83536)) return id; // 65535 maximum possible number of prototypes if ((DWORD)objUniqueID >= UID_END) objUniqueID = UID_START; obj->ID = ++objUniqueID;