From 19f6d64e5bacf1dab60c680ca16ce32884057b7a Mon Sep 17 00:00:00 2001 From: NovaRain Date: Wed, 30 Dec 2020 22:11:02 +0800 Subject: [PATCH] Added a new hook: HOOK_ADJUSTPOISON Added a fix to the poison handling in the engine when an NPC is under the player's control. --- artifacts/scripting/headers/sfall.h | 1 + artifacts/scripting/hookscripts.txt | 16 +++- sfall/FalloutEngine/Functions_def.h | 1 + sfall/Modules/BugFixes.h | 2 +- sfall/Modules/CritterPoison.cpp | 122 +++++++++++++++++++++++++ sfall/Modules/CritterPoison.h | 39 ++++++++ sfall/Modules/HookScripts.cpp | 1 + sfall/Modules/HookScripts.h | 1 + sfall/Modules/HookScripts/ObjectHs.cpp | 38 +++++++- sfall/Modules/HookScripts/ObjectHs.h | 1 + sfall/Modules/Sound.cpp | 5 +- sfall/Modules/Stats.cpp | 22 ----- sfall/ddraw.vcxproj | 2 + sfall/ddraw.vcxproj.filters | 6 ++ sfall/main.cpp | 2 + sfall/version.h | 2 +- 16 files changed, 231 insertions(+), 30 deletions(-) create mode 100644 sfall/Modules/CritterPoison.cpp create mode 100644 sfall/Modules/CritterPoison.h diff --git a/artifacts/scripting/headers/sfall.h b/artifacts/scripting/headers/sfall.h index 1ee667d9..802cea30 100644 --- a/artifacts/scripting/headers/sfall.h +++ b/artifacts/scripting/headers/sfall.h @@ -69,6 +69,7 @@ #define HOOK_STDPROCEDURE_END (41) #define HOOK_TARGETOBJECT (42) #define HOOK_ENCOUNTER (43) +#define HOOK_ADJUSTPOISON (44) //Valid arguments to list_begin #define LIST_CRITTERS (0) diff --git a/artifacts/scripting/hookscripts.txt b/artifacts/scripting/hookscripts.txt index 712f7912..d41419da 100644 --- a/artifacts/scripting/hookscripts.txt +++ b/artifacts/scripting/hookscripts.txt @@ -368,7 +368,7 @@ int ret0 - overrides the pressed key (a new key DX scancode or 0 for no over HOOK_MOUSECLICK (hs_mouseclick.int) -Runs once every time when a mouse button was pressed or release. +Runs once every time when a mouse button was pressed or released. int arg0 - event type: 1 - pressed, 0 - released int arg1 - button number (0 - left, 1 - right, up to 7) @@ -693,3 +693,17 @@ int arg2 - 1 when the encounter occurs is a special encounter, 0 otherwise int ret0 - overrides the map ID, or pass -1 for event type 0 to cancel the encounter and continue traveling int ret1 - pass 1 to cancel the encounter and load the specified map from the ret1 (only for event type 0) + +------------------------------------------- + +HOOK_ADJUSTPOISON (hs_adjustpoison.int) + +Runs when a critter's poison level is changed, or when the player takes damage from the poison. + +Critter arg0 - the critter +int arg1 - the amount of poison being added/removed +int arg2 - the damage value at the time of applying the poison effect + (damage from the poison effect is implemented only for the player character; for other critters, this value will always be 0) + +int ret0 - the new amount of poison being added/removed +int ret1 - the new damage value, only negative values are allowed (will only be valid at the time of taking damage from the poison) diff --git a/sfall/FalloutEngine/Functions_def.h b/sfall/FalloutEngine/Functions_def.h index 2d0242ad..c7c82da7 100644 --- a/sfall/FalloutEngine/Functions_def.h +++ b/sfall/FalloutEngine/Functions_def.h @@ -205,6 +205,7 @@ WRAP_WATCOM_FUNC0(void, process_bk) WRAP_WATCOM_FUNC0(void, proto_dude_update_gender) // Places pointer to a prototype structure into ptrPtr and returns 0 on success or -1 on failure WRAP_WATCOM_FUNC2(long, proto_ptr, long, pid, Proto**, ptrPtr) +WRAP_WATCOM_FUNC2(void, queue_clear_type, long, qType, void*, func) WRAP_WATCOM_FUNC2(void*, queue_find_first, GameObject*, object, long, qType) WRAP_WATCOM_FUNC2(void*, queue_find_next, GameObject*, object, long, qType) WRAP_WATCOM_FUNC2(void, queue_remove_this, GameObject*, object, long, qType) diff --git a/sfall/Modules/BugFixes.h b/sfall/Modules/BugFixes.h index 859a5a52..88010834 100644 --- a/sfall/Modules/BugFixes.h +++ b/sfall/Modules/BugFixes.h @@ -16,6 +16,6 @@ public: extern int tagSkill4LevelBase; -extern void ResetBodyState(); +void ResetBodyState(); } diff --git a/sfall/Modules/CritterPoison.cpp b/sfall/Modules/CritterPoison.cpp new file mode 100644 index 00000000..b3a72a9d --- /dev/null +++ b/sfall/Modules/CritterPoison.cpp @@ -0,0 +1,122 @@ +/* + * sfall + * Copyright (C) 2008-2021 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "..\main.h" +#include "..\FalloutEngine\Fallout2.h" +#include "PartyControl.h" + +#include "CritterPoison.h" + +namespace sfall +{ + +//static long adjustPoison = -2; + +long CritterPoison::adjustPoisonHP_Default = -1; +long CritterPoison::adjustPoisonHP; // default or returned value from HOOK_ADJUSTPOISON + +void CritterPoison::SetDefaultAdjustPoisonHP(long value) { + adjustPoisonHP_Default = adjustPoisonHP = value; +} + +void __fastcall sf_critter_adjust_poison(fo::GameObject* critter, long amount) { + if (amount == 0) return; + if (amount > 0) { + amount -= fo::func::stat_level(critter, fo::STAT_poison_resist) * amount / 100; + } else if (critter->critter.poison == 0) { + return; + } + critter->critter.poison += amount; + if (critter->critter.poison < 0) { + critter->critter.poison = 0; // level can't be negative + } else { + // set uID for saving queue + //Objects::SetObjectUniqueID(critter); + //fo::func::queue_add(10 * (505 - 5 * critter->critter.poison), critter, nullptr, fo::QueueType::poison_event); + } +} + +static void __declspec(naked) critter_adjust_poison_hack() { + __asm { + mov edx, esi; + mov ecx, edi; + jmp sf_critter_adjust_poison; + } +} + +void __declspec(naked) critter_check_poison_hack() { + __asm { + mov eax, CritterPoison::adjustPoisonHP_Default; + mov edx, CritterPoison::adjustPoisonHP; + mov CritterPoison::adjustPoisonHP, eax; + retn; + } +} + +void __fastcall critter_check_poison_fix() { + if (PartyControl::IsNpcControlled()) { + // since another critter is being controlled, we can't apply the poison effect to it + // instead, we add the "poison" event to dude again, which will be triggered when dude returns to the player's control + fo::func::queue_clear_type(fo::QueueType::poison_event, nullptr); + fo::GameObject* dude = PartyControl::RealDudeObject(); + fo::func::queue_add(10, dude, nullptr, fo::QueueType::poison_event); + } +} + +static void __declspec(naked) critter_check_poison_hack_fix() { + using namespace fo; + using namespace Fields; + __asm { + mov ecx, [eax + protoId]; // critter.pid + cmp ecx, PID_Player; + jnz notDude; + retn; +notDude: + call critter_check_poison_fix; + or al, 1; // unset ZF (exit from func) + retn; + } +} + +void __declspec(naked) critter_adjust_poison_hack_fix() { // can also be called from HOOK_ADJUSTPOISON + using namespace fo; + using namespace Fields; + __asm { + mov edx, ds:[FO_VAR_obj_dude]; + mov ebx, [eax + protoId]; // critter.pid + mov ecx, PID_Player; + retn; + } +} + +void CritterPoison::init() { + // Allow changing the poison level for critters + MakeCall(0x42D226, critter_adjust_poison_hack); + SafeWrite8(0x42D22C, 0xDA); // jmp 0x42D30A + + // Adjust poison damage + SetDefaultAdjustPoisonHP(*(DWORD*)0x42D332); + MakeCall(0x42D331, critter_check_poison_hack); + + // Fix/tweak for party control + MakeCall(0x42D31F, critter_check_poison_hack_fix, 1); + MakeCall(0x42D21C, critter_adjust_poison_hack_fix, 1); + SafeWrite8(0x42D223, 0xCB); // cmp eax, edx > cmp ebx, ecx +} + +} diff --git a/sfall/Modules/CritterPoison.h b/sfall/Modules/CritterPoison.h new file mode 100644 index 00000000..c0899055 --- /dev/null +++ b/sfall/Modules/CritterPoison.h @@ -0,0 +1,39 @@ +/* + * sfall + * Copyright (C) 2008-2021 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "Module.h" + +namespace sfall +{ + +class CritterPoison : public Module { +public: + const char* name() { return "CritterPoison"; } + void init(); + + static long adjustPoisonHP_Default; + static long adjustPoisonHP; // temp value from HOOK_ADJUSTPOISON + + static void SetDefaultAdjustPoisonHP(long value); +}; + +void critter_adjust_poison_hack_fix(); + +} diff --git a/sfall/Modules/HookScripts.cpp b/sfall/Modules/HookScripts.cpp index 685eb7ea..de56cd4f 100644 --- a/sfall/Modules/HookScripts.cpp +++ b/sfall/Modules/HookScripts.cpp @@ -103,6 +103,7 @@ static HooksInjectInfo injectHooks[] = { {HOOK_STDPROCEDURE_END, Inject_ScriptProcedureHook2, false}, {HOOK_TARGETOBJECT, Inject_TargetObjectHook, false}, {HOOK_ENCOUNTER, Inject_EncounterHook, false}, + {HOOK_ADJUSTPOISON, Inject_AdjustPoisonHook, false}, }; void HookScripts::InjectingHook(int hookId) { diff --git a/sfall/Modules/HookScripts.h b/sfall/Modules/HookScripts.h index d411e083..30fbad0b 100644 --- a/sfall/Modules/HookScripts.h +++ b/sfall/Modules/HookScripts.h @@ -70,6 +70,7 @@ enum HookType HOOK_STDPROCEDURE_END = 41, HOOK_TARGETOBJECT = 42, HOOK_ENCOUNTER = 43, + HOOK_ADJUSTPOISON = 44, HOOK_COUNT }; diff --git a/sfall/Modules/HookScripts/ObjectHs.cpp b/sfall/Modules/HookScripts/ObjectHs.cpp index d599612c..27f7c681 100644 --- a/sfall/Modules/HookScripts/ObjectHs.cpp +++ b/sfall/Modules/HookScripts/ObjectHs.cpp @@ -1,6 +1,7 @@ #include "..\..\FalloutEngine\Fallout2.h" #include "..\..\SafeWrite.h" #include "..\HookScripts.h" +#include "..\CritterPoison.h" #include "Common.h" #include "ObjectHs.h" @@ -302,6 +303,36 @@ skip: } } +static DWORD __fastcall AdjustPoison_Script(DWORD critter, long amount, DWORD addr) { + BeginHook(); + argCount = 3; + + bool checkPoison = (addr == (0x42D32C + 5)); // from critter_check_poison_ + + args[0] = critter; + args[1] = amount; + args[2] = (checkPoison) ? CritterPoison::adjustPoisonHP_Default : 0; + + RunHookScript(HOOK_ADJUSTPOISON); + + if (cRet > 0) amount = rets[0]; + if (cRet > 1 && checkPoison && (long)rets[1] < 0) CritterPoison::adjustPoisonHP = rets[1]; + + EndHook(); + return amount; +} + +static void __declspec(naked) critter_adjust_poison_hack() { + __asm { + push [esp + 0x24 + 4]; // called from + mov ecx, eax; // critter + call AdjustPoison_Script; // edx - amount + mov esi, eax; // old/new amount + mov eax, edi; // restore eax value + jmp critter_adjust_poison_hack_fix; // in CritterPoison + } +} + void Inject_UseObjOnHook() { HookCalls(UseObjOnHook, { 0x49C606, 0x473619 }); @@ -310,7 +341,7 @@ void Inject_UseObjOnHook() { 0x4285DF, // ai_check_drugs 0x4286F8, // ai_check_drugs 0x4287F8, // ai_check_drugs - 0x473573 // inven_action_cursor + 0x473573 // inven_action_cursor }); } @@ -339,6 +370,10 @@ void Inject_ScriptProcedureHook2() { HookCall(0x4A49A7, After_ScriptStdProcedureHook); } +void Inject_AdjustPoisonHook() { + MakeCall(0x42D21C, critter_adjust_poison_hack, 1); +} + void InitObjectHookScripts() { HookScripts::LoadHookScript("hs_useobjon", HOOK_USEOBJON); HookScripts::LoadHookScript("hs_useobj", HOOK_USEOBJ); @@ -347,6 +382,7 @@ void InitObjectHookScripts() { HookScripts::LoadHookScript("hs_setlighting", HOOK_SETLIGHTING); HookScripts::LoadHookScript("hs_stdprocedure", HOOK_STDPROCEDURE); // combo hook HookScripts::LoadHookScript("hs_stdprocedure", HOOK_STDPROCEDURE_END); + HookScripts::LoadHookScript("hs_adjustpoison", HOOK_ADJUSTPOISON); } } diff --git a/sfall/Modules/HookScripts/ObjectHs.h b/sfall/Modules/HookScripts/ObjectHs.h index 832f00df..3a05f626 100644 --- a/sfall/Modules/HookScripts/ObjectHs.h +++ b/sfall/Modules/HookScripts/ObjectHs.h @@ -11,4 +11,5 @@ namespace sfall void Inject_SetLightingHook(); void Inject_ScriptProcedureHook(); void Inject_ScriptProcedureHook2(); + void Inject_AdjustPoisonHook(); } diff --git a/sfall/Modules/Sound.cpp b/sfall/Modules/Sound.cpp index 95a1a3fe..e9481f76 100644 --- a/sfall/Modules/Sound.cpp +++ b/sfall/Modules/Sound.cpp @@ -916,10 +916,7 @@ static void __fastcall SetPanPosition(fo::ACMSoundData* sound) { if (distance > 5) { long direction = fo::func::tile_dir(fo::var::obj_dude->tile, relativeObject->tile); bool isRightSide = (direction <= 2); - - long panValue = (distance - 5) * 100; - if (panValue > 10000) panValue = 10000; - + long panValue = (distance < 55) ? (distance - 5) * 200 : 10000; sound->soundBuffer->SetPan((isRightSide) ? panValue : -panValue); // left mute 10000 ... -10000 right mute } relativeObject = nullptr; // just in case diff --git a/sfall/Modules/Stats.cpp b/sfall/Modules/Stats.cpp index d135ab8f..f51179a5 100644 --- a/sfall/Modules/Stats.cpp +++ b/sfall/Modules/Stats.cpp @@ -26,17 +26,6 @@ namespace sfall { -void __fastcall sf_critter_adjust_poison(fo::GameObject* critter, long amount) { - if (amount == 0) return; - if (amount > 0) { - amount -= fo::func::stat_level(critter, fo::STAT_poison_resist) * amount / 100; - } else if (critter->critter.poison == 0) { - return; - } - critter->critter.poison += amount; - if (critter->critter.poison < 0) critter->critter.poison = 0; // level can't be negative -} - static DWORD statMaximumsPC[fo::STAT_max_stat]; static DWORD statMinimumsPC[fo::STAT_max_stat]; static DWORD statMaximumsNPC[fo::STAT_max_stat]; @@ -220,14 +209,6 @@ allow: } } -static void __declspec(naked) critter_adjust_poison_hack() { - __asm { - mov edx, esi; - mov ecx, edi; - jmp sf_critter_adjust_poison; - } -} - static void StatsReset() { for (size_t i = 0; i < fo::STAT_max_stat; i++) { statMaximumsPC[i] = statMaximumsNPC[i] = fo::var::stat_data[i].maxValue; @@ -262,9 +243,6 @@ void Stats::init() { // Allow set_critter_stat function to change STAT_unused and STAT_dmg_* stats for the player MakeCall(0x4AF54E, stat_set_base_hack_allow); MakeCall(0x455D65, op_set_critter_stat_hack); // STAT_unused for other critters - // Allow changing the poison level for critters - MakeCall(0x42D226, critter_adjust_poison_hack); - SafeWrite8(0x42D22C, 0xDA); // jmp 0x42D30A auto xpTableList = GetConfigList("Misc", "XPTable", "", 2048); size_t numLevels = xpTableList.size(); diff --git a/sfall/ddraw.vcxproj b/sfall/ddraw.vcxproj index 9a245402..3c301a23 100644 --- a/sfall/ddraw.vcxproj +++ b/sfall/ddraw.vcxproj @@ -291,6 +291,7 @@ + @@ -388,6 +389,7 @@ + diff --git a/sfall/ddraw.vcxproj.filters b/sfall/ddraw.vcxproj.filters index 7034ad7c..7f60f541 100644 --- a/sfall/ddraw.vcxproj.filters +++ b/sfall/ddraw.vcxproj.filters @@ -304,6 +304,9 @@ Modules\SubModules + + Modules + @@ -562,6 +565,9 @@ Modules\SubModules + + Modules + diff --git a/sfall/main.cpp b/sfall/main.cpp index 615559a4..250ae616 100644 --- a/sfall/main.cpp +++ b/sfall/main.cpp @@ -38,6 +38,7 @@ #include "Modules\Credits.h" #include "Modules\Criticals.h" #include "Modules\CritterStats.h" +#include "Modules\CritterPoison.h" #include "Modules\DamageMod.h" #include "Modules\DebugEditor.h" #include "Modules\Drugs.h" @@ -185,6 +186,7 @@ static void InitModules() { manager.add(); manager.add(); manager.add(); + manager.add(); manager.add(); manager.add(); manager.add(); diff --git a/sfall/version.h b/sfall/version.h index e936c480..6bce6f1f 100644 --- a/sfall/version.h +++ b/sfall/version.h @@ -20,7 +20,7 @@ #define TARGETVERSION "Fallout 2 v1.02 US" -#define LEGAL_COPYRIGHT "Copyright (C) 2006-2020, sfall team" +#define LEGAL_COPYRIGHT "Copyright (C) 2006-2021, sfall team" #define VERSION_MAJOR 4 #define VERSION_MINOR 2