From aa5bd95dfe4acf945b66ac2603ccd8a13934b7f4 Mon Sep 17 00:00:00 2001 From: NovaRain Date: Tue, 28 May 2019 11:09: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. --- artifacts/scripting/sfall function notes.txt | 2 +- sfall/FalloutEngine/EngineUtils.cpp | 2 +- sfall/Modules/AI.cpp | 14 +++ sfall/Modules/BugFixes.cpp | 14 +-- sfall/Modules/Console.cpp | 4 +- sfall/Modules/Inventory.cpp | 109 +++++++++---------- sfall/Modules/Objects.cpp | 2 +- sfall/Modules/Scripting/Handlers/Objects.cpp | 7 +- 8 files changed, 85 insertions(+), 69 deletions(-) diff --git a/artifacts/scripting/sfall function notes.txt b/artifacts/scripting/sfall function notes.txt index 7bc48402..52f3eb35 100644 --- a/artifacts/scripting/sfall function notes.txt +++ b/artifacts/scripting/sfall function notes.txt @@ -604,7 +604,7 @@ optional arguments: > void sfall_func5("set_selectable_perk_npc", object npc, string namePerk, int active, int image, string desc) > int sfall_func2("has_fake_perk_npc", object npc, string namePerk) > int sfall_func2("has_fake_trait_npc", object npc, string nameTrait) -- functions are similar to has_fake_*/set_fake_*/set_selectable_perk opcodes, but work on the specified party member NPC (including dude_obj) +- these functions are similar to has_fake_*/set_fake_*/set_selectable_perk functions, but apply to the specified party member NPC (including dude_obj) ------------------------ ------ MORE INFO ------- diff --git a/sfall/FalloutEngine/EngineUtils.cpp b/sfall/FalloutEngine/EngineUtils.cpp index 59adc0ce..72d56ebd 100644 --- a/sfall/FalloutEngine/EngineUtils.cpp +++ b/sfall/FalloutEngine/EngineUtils.cpp @@ -150,7 +150,7 @@ long IsPartyMemberByPid(long pid) { } bool IsPartyMember(fo::GameObject* critter) { - if (critter->id < 18000) return false; + if (critter->id < PLAYER_ID) return false; return (IsPartyMemberByPid(critter->protoId) > 0); } diff --git a/sfall/Modules/AI.cpp b/sfall/Modules/AI.cpp index d0d83e83..e479c0b9 100644 --- a/sfall/Modules/AI.cpp +++ b/sfall/Modules/AI.cpp @@ -76,6 +76,17 @@ tryHeal: } } +static void __declspec(naked) ai_check_drugs_hook() { + __asm { + call fo::funcoffs::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; @@ -194,6 +205,9 @@ void AI::init() { // 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); HookCalls(ai_try_attack_hook_FleeFix, {0x42ABA8, 0x42ACE5}); diff --git a/sfall/Modules/BugFixes.cpp b/sfall/Modules/BugFixes.cpp index d2ee6b0f..8f96731c 100644 --- a/sfall/Modules/BugFixes.cpp +++ b/sfall/Modules/BugFixes.cpp @@ -801,19 +801,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 fo::funcoffs::display_print_ + test eax, eax; + jz end; + jmp fo::funcoffs::display_print_; end: - retn + retn; } } static void __declspec(naked) op_wield_obj_critter_adjust_ac_hook() { __asm { - call fo::funcoffs::adjust_ac_ - xor eax, eax // not animated - jmp fo::funcoffs::intface_update_ac_ + call fo::funcoffs::adjust_ac_; + xor eax, eax; // not animated + jmp fo::funcoffs::intface_update_ac_; } } diff --git a/sfall/Modules/Console.cpp b/sfall/Modules/Console.cpp index 980a8eac..f6246b7e 100644 --- a/sfall/Modules/Console.cpp +++ b/sfall/Modules/Console.cpp @@ -36,10 +36,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/Modules/Inventory.cpp b/sfall/Modules/Inventory.cpp index ea819e1b..85b9b553 100644 --- a/sfall/Modules/Inventory.cpp +++ b/sfall/Modules/Inventory.cpp @@ -63,6 +63,7 @@ void InventoryKeyPressedHook(DWORD dxKey, bool pressed) { } ///////////////////////////////////////////////////////////////// + DWORD __stdcall sf_item_total_size(fo::GameObject* critter) { int totalSize = fo::func::item_c_curr_size(critter); @@ -85,19 +86,6 @@ DWORD __stdcall sf_item_total_size(fo::GameObject* critter) { return totalSize; } -/*static const DWORD ObjPickupFail=0x49B70D; -static const DWORD ObjPickupEnd=0x49B6F8; -static __declspec(naked) void ObjPickupHook() { - __asm { - cmp edi, ds:[FO_VAR_obj_dude]; - jnz end; -end: - lea edx, [esp+0x10]; - mov eax, ecx; - jmp ObjPickupEnd; - } -}*/ - static int __stdcall CritterGetMaxSize(fo::GameObject* critter) { if (critter == fo::var::obj_dude) return invSizeMaxLimit; @@ -198,8 +186,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; @@ -232,19 +220,19 @@ fail: static __declspec(naked) void loot_container_hook_btn() { __asm { push ecx; - push edx; // source current weight - mov edx, eax; // target - mov ecx, [esp + 0x150 - 0x1C + 12]; // source + push edx; // source current weight + mov edx, eax; // target + mov ecx, [esp + 0x150 - 0x1C + 12]; // source call BarterAttemptTransaction; pop edx; pop ecx; test eax, eax; jz fail; - mov eax, ebp; // target + mov eax, ebp; // target jmp fo::funcoffs::item_total_weight_; fail: mov eax, edx; - inc eax; // weight + 1 + inc eax; // weight + 1 retn; } } @@ -340,51 +328,32 @@ static void __declspec(naked) gdControlUpdateInfo_hack() { ///////////////////////////////////////////////////////////////// static std::string superStimMsg; -static int __fastcall SuperStimFix2(fo::GameObject* item, fo::GameObject* target) { +static int __fastcall SuperStimFix(fo::GameObject* item, fo::GameObject* target) { if (item->protoId != fo::PID_SUPER_STIMPAK || !target || target->Type() != fo::OBJ_TYPE_CRITTER) { return 0; } - long curr_hp, max_hp; - curr_hp = fo::func::stat_level(target, fo::STAT_current_hp); - max_hp = fo::func::stat_level(target, fo::STAT_max_hit_points); - if (curr_hp < max_hp) return 0; + long max_hp = fo::func::stat_level(target, fo::STAT_max_hit_points); + if (target->critter.health < max_hp) return 0; fo::func::display_print(superStimMsg.c_str()); 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 } } @@ -570,7 +539,7 @@ DWORD __stdcall Inventory::adjust_fid_replacement() { using namespace fo; DWORD fid; - if (var::inven_dude->TypeFid() == fo::OBJ_TYPE_CRITTER) { + if (var::inven_dude->TypeFid() == ObjType::OBJ_TYPE_CRITTER) { DWORD frameNum; DWORD weaponAnimCode = 0; if (PartyControl::IsNpcControlled()) { @@ -652,6 +621,34 @@ 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 = fo::func::perk_level(fo::var::obj_dude, fo::PERK_quick_pockets); + return invenApCost - (invenApQPReduction * plevel); +}*/ + void InventoryReset() { invenApCost = invenApCostDef; } @@ -712,17 +709,19 @@ void Inventory::init() { } } - invenApCost = invenApCostDef = GetConfigInt("Misc", "InventoryApCost", 4); - invenApQPReduction = GetConfigInt("Misc", "QuickPocketsApCostReduction", 2); - MakeJump(0x46E80B, inven_ap_cost_hack); - if (GetConfigInt("Misc", "SuperStimExploitFix", 0)) { superStimMsg = Translate("sfall", "SuperStimExploitMsg", "You cannot use a super stim on someone who is not injured!"); - MakeCall(0x49C3D9, SuperStimFix); + MakeCall(0x49C3D9, protinst_use_item_on_hack); } reloadWeaponKey = GetConfigInt("Input", "ReloadWeaponKey", 0); + invenApCost = invenApCostDef = GetConfigInt("Misc", "InventoryApCost", 4); + invenApQPReduction = GetConfigInt("Misc", "QuickPocketsApCostReduction", 2); + if (invenApCostDef != 4 || invenApQPReduction != 2) { + ApplyInvenApCostPatch(); + } + if (GetConfigInt("Misc", "StackEmptyWeapons", 0)) { MakeCall(0x4736C6, inven_action_cursor_hack); HookCall(0x4772AA, item_add_mult_hook); diff --git a/sfall/Modules/Objects.cpp b/sfall/Modules/Objects.cpp index d296f6de..7f73f6d5 100644 --- a/sfall/Modules/Objects.cpp +++ b/sfall/Modules/Objects.cpp @@ -35,7 +35,7 @@ long Objects::uniqueID = UniqueID::Start; // current counter id, saving to sfall // player ID = 18000, all party members have ID = 18000 + its pid (file number of prototype) long Objects::SetObjectUniqueID(fo::GameObject* obj) { long id = obj->id; - if (id > UniqueID::Start || obj == fo::var::obj_dude || (id >= 18000 && id < 83536)) return id; // 65535 maximum possible number of prototypes + if (id > UniqueID::Start || obj == fo::var::obj_dude || (id >= PLAYER_ID && id < 83536)) return id; // 65535 maximum possible number of prototypes if ((DWORD)uniqueID >= UniqueID::End) uniqueID = UniqueID::Start; obj->id = ++uniqueID; diff --git a/sfall/Modules/Scripting/Handlers/Objects.cpp b/sfall/Modules/Scripting/Handlers/Objects.cpp index 82814d33..2994180a 100644 --- a/sfall/Modules/Scripting/Handlers/Objects.cpp +++ b/sfall/Modules/Scripting/Handlers/Objects.cpp @@ -365,10 +365,13 @@ void sf_item_weight(OpcodeContext& ctx) { void sf_set_dude_obj(OpcodeContext& ctx) { auto obj = ctx.arg(0).asObject(); if (obj->Type() == fo::OBJ_TYPE_CRITTER) { - PartyControl::SwitchToCritter(obj); + //if (!InCombat && obj != PartyControl::RealDudeObject()) { + // ctx.printOpcodeError("%s() - controlling of the critter is only allowed in combat mode.", ctx.getMetaruleName()); + //} else { + PartyControl::SwitchToCritter(obj); + //} } else { ctx.printOpcodeError("%s() - the object is not a critter.", ctx.getMetaruleName()); - ctx.setReturn(-1); } }