From 736c2cb6feb65d72fa46075f5002aa3bde6c73de Mon Sep 17 00:00:00 2001 From: NovaRain Date: Fri, 10 Mar 2023 21:08:05 +0800 Subject: [PATCH] Backported "item_make_explosive" script function Updated documents for the function. Removed deprecated "register" keyword. --- artifacts/scripting/headers/sfall.h | 3 +- artifacts/scripting/sfall function notes.md | 13 +- sfall/Modules/Explosions.cpp | 171 ++++++++++++++++++ sfall/Modules/Explosions.h | 2 + sfall/Modules/HookScripts/MiscHs.cpp | 6 +- sfall/Modules/Interface.cpp | 2 +- sfall/Modules/MainLoopHook.cpp | 2 +- sfall/Modules/Scripting/Handlers/Metarule.cpp | 1 + sfall/Modules/Scripting/Handlers/Objects.cpp | 19 ++ sfall/Modules/Scripting/Handlers/Objects.h | 2 + 10 files changed, 214 insertions(+), 7 deletions(-) diff --git a/artifacts/scripting/headers/sfall.h b/artifacts/scripting/headers/sfall.h index e6633df4..75fd0d32 100644 --- a/artifacts/scripting/headers/sfall.h +++ b/artifacts/scripting/headers/sfall.h @@ -52,7 +52,7 @@ #define HOOK_INVENTORYMOVE (24) #define HOOK_INVENWIELD (25) #define HOOK_ADJUSTFID (26) -//#define HOOK_COMBATTURN (27) // unimplemented +//#define HOOK_COMBATTURN (27) // unimplemented #define HOOK_CARTRAVEL (28) #define HOOK_SETGLOBALVAR (29) #define HOOK_RESTTIMER (30) @@ -348,6 +348,7 @@ #define intface_redraw sfall_func0("intface_redraw") #define intface_show sfall_func0("intface_show") #define inventory_redraw(invSide) sfall_func1("inventory_redraw", invSide) +#define item_make_explosive(pid, activePid, min, max) sfall_func4("item_make_explosive", pid, activePid, min, max) #define item_weight(obj) sfall_func1("item_weight", obj) #define lock_is_jammed(obj) sfall_func1("lock_is_jammed", obj) #define loot_obj sfall_func0("loot_obj") diff --git a/artifacts/scripting/sfall function notes.md b/artifacts/scripting/sfall function notes.md index e7b981ab..3c54010b 100644 --- a/artifacts/scripting/sfall function notes.md +++ b/artifacts/scripting/sfall function notes.md @@ -286,7 +286,7 @@ FUNCTION REFERENCE ----- ##### `array get_explosion_damage(itemPid)` -- Returns an array of the minimum and maximum damage of Dynamite or Plastic Explosives. +- Returns an array of the minimum and maximum damage of the explosive item. ----- ##### `void set_dynamite_damage(minDmg, maxDmg)` @@ -682,6 +682,17 @@ sfall_funcX metarule functions - Redraws inventory list in the inventory/use inventory item on/loot/barter screens - `invSide` specifies which side needs to be redrawn: 0 - the player, 1 - target (container/NPC in loot/barter screens), -1 - both sides (same as without argument) +---- +#### item_make_explosive +`void sfall_func3("item_make_explosive", int pid, int activePid, int damage)`\ +`void sfall_func4("item_make_explosive", int pid, int activePid, int min, int max)` +- Makes the specified item (pid) an explosive item like Dynamite or Plastic Explosives +- `activePid` is for an item with an active timer, can be the same as the `pid` argument +- The item proto must be the **Misc Item** type and have the **Use** action flag +- `min` and `max` are the minimum and maximum explosion damage +- Using the function on an item that is already set as an explosive will override its previous settings +- __NOTE:__ this function does not work for pid's of Dynamite and Plastic Explosives + ---- #### get_string_pointer `int sfall_func1("get_string_pointer", string text)` diff --git a/sfall/Modules/Explosions.cpp b/sfall/Modules/Explosions.cpp index 1477d171..c797b2f4 100644 --- a/sfall/Modules/Explosions.cpp +++ b/sfall/Modules/Explosions.cpp @@ -16,6 +16,8 @@ * along with this program. If not, see . */ +#include + #include "..\main.h" #include "..\FalloutEngine\Fallout2.h" @@ -31,6 +33,15 @@ namespace sfall using namespace fo::Fields; +struct explosiveInfo { + DWORD pid; + DWORD pidActive; + DWORD minDamage; + DWORD maxDamage; +}; + +std::vector explosives; + static bool lightingEnabled = false; static bool explosionsMetaruleReset = false; static bool explosionsDamageReset = false; @@ -152,6 +163,162 @@ static void __declspec(naked) fire_dance_lighting_fix1() { } } +//////////////////////////////////////////////////////////////////////////////// + +static DWORD __fastcall CheckExplosives(DWORD pid) { + for (std::vector::const_iterator it = explosives.begin(); it != explosives.end(); ++it) { + if (it->pid == pid) return it->pidActive; + } + return 0; +} + +static DWORD __fastcall CheckActiveExplosives(DWORD pid) { + for (std::vector::const_iterator it = explosives.begin(); it != explosives.end(); ++it) { + if (it->pidActive == pid) return 0; + } + return 1; +} + +static DWORD __fastcall GetDamage(DWORD pid, DWORD &min, DWORD &max) { + DWORD result = 0; + for (std::vector::const_iterator it = explosives.begin(); it != explosives.end(); ++it) { + if (it->pidActive == pid) { + min = it->minDamage; + max = it->maxDamage; + result = 1; + break; + } + } + return result; +} + +static DWORD __fastcall SetQueueExplosionDamage(DWORD pid) { + DWORD min, max; + DWORD result = GetDamage(pid, min, max); + + if (result) { + __asm mov edx, min; + __asm mov ecx, max; + } + return result; +} + +static void __declspec(naked) obj_use_explosive_hack() { + using namespace fo; + __asm { + cmp edx, PID_PLASTIC_EXPLOSIVES; + jz end; + // end engine code + push edx; + mov ecx, edx; + call CheckExplosives; + pop edx; + test eax, eax; + jnz end; + retn; +end: + mov dword ptr [esp], 0x49BCE0; + retn; + } +} + +static void __declspec(naked) obj_use_explosive_active_hack() { + using namespace fo; + __asm { + cmp eax, PID_PLASTIC_EXPLOSIVES; + jz end; + // end engine code + mov ecx, eax; + call CheckExplosives; + test eax, eax; + jz skipSet; + mov dword ptr [esi + protoId], eax; // change item pid to active; +skipSet: + mov dword ptr [esp], 0x49BD62; +end: + retn; + } +} + +static void __declspec(naked) queue_do_explosion_hack() { + using namespace fo; + __asm { + cmp edx, PID_ACTIVE_DYNAMITE; + jz dynamite; + cmp edx, PID_ACTIVE_PLASTIC_EXPLOSIVE; + jz end; + push edx; + mov ecx, edx; + call SetQueueExplosionDamage; + test eax, eax; + mov ebx, edx; + pop edx; + jz end; + mov dword ptr [esp], 0x4A2888; +end: + retn; +dynamite: + mov dword ptr [esp], 0x4A2872; + retn; + } +} + +static void __declspec(naked) inven_action_cursor_drop_hack() { + using namespace fo; + __asm { + cmp ebx, PID_ACTIVE_PLASTIC_EXPLOSIVE; + jz end; + // end engine code + push eax; + mov ecx, ebx; + call CheckActiveExplosives; + test eax, eax; // check in engine + pop eax; +end: + retn; + } +} + +static void __declspec(naked) protinstTestDroppedExplosive_hack() { + __asm { + jz end; + mov ecx, edx; + call CheckActiveExplosives; + test eax, eax; + jz end; + mov dword ptr [esp], 0x49C112; // exit, no active explosive item +end: + retn; + } +} + +static void apply_expl_hack() { + MakeCall(0x49BCC7, obj_use_explosive_hack); // check explosives + MakeCall(0x49BD56, obj_use_explosive_active_hack); // set active explosive + MakeCall(0x4A2865, queue_do_explosion_hack); // set damage explosive + MakeCall(0x4737F2, inven_action_cursor_drop_hack, 1); // check drop explosives + MakeCall(0x49C005, protinstTestDroppedExplosive_hack, 1); // check drop explosives +} + +void Explosions::AddToExplosives(DWORD pid, DWORD activePid, DWORD minDmg, DWORD maxDmg) { + static bool onlyOnce = false; + for (unsigned int i = 0; i < explosives.size(); i++) { + if (explosives[i].pid == pid) { + explosives.erase(explosives.begin() + i); + break; + } + } + explosiveInfo expl = { pid, activePid, minDmg, maxDmg }; + explosives.push_back(expl); + + if (!onlyOnce) { + apply_expl_hack(); + onlyOnce = true; + } +} + +//////////////////////////////////////////////////////////////////////////////// + static const DWORD explosion_dmg_check_adr[] = {0x411709, 0x4119FC, 0x411C08, 0x4517C1, 0x423BC8, 0x42381A}; static const DWORD explosion_art_adr[] = {0x411A19, 0x411A29, 0x411A35, 0x411A3C}; static const DWORD explosion_art_defaults[] = {10, 2, 31, 29}; @@ -203,6 +370,8 @@ static int GetExplosionDamage(int pid) { min = *(DWORD*)plastic_min_dmg_addr; max = *(DWORD*)plastic_max_dmg_addr; break; + default: + GetDamage(pid, min, max); } DWORD arrayId = script::CreateTempArray(2, 0); @@ -298,6 +467,8 @@ void ResetExplosionRadius() { } static void ResetExplosionDamage() { + explosives.clear(); + if (!explosionsDamageReset) return; SafeWrite32(dynamite_min_dmg_addr, dynamite_minDmg); SafeWrite32(dynamite_max_dmg_addr, dynamite_maxDmg); diff --git a/sfall/Modules/Explosions.h b/sfall/Modules/Explosions.h index fe34840f..c9f62296 100644 --- a/sfall/Modules/Explosions.h +++ b/sfall/Modules/Explosions.h @@ -27,6 +27,8 @@ public: static void init(); static void OnGameLoad(); + + static void AddToExplosives(DWORD pid, DWORD activePid, DWORD minDmg, DWORD maxDmg); }; void ResetExplosionSettings(); diff --git a/sfall/Modules/HookScripts/MiscHs.cpp b/sfall/Modules/HookScripts/MiscHs.cpp index 4cd478c6..e2591dee 100644 --- a/sfall/Modules/HookScripts/MiscHs.cpp +++ b/sfall/Modules/HookScripts/MiscHs.cpp @@ -11,7 +11,7 @@ namespace sfall { // The hook is executed twice when entering the barter screen and after transaction: the first time is for the player; the second time is for NPC -static DWORD __fastcall BarterPriceHook_Script(register fo::GameObject* source, register fo::GameObject* target, DWORD callAddr) { +static DWORD __fastcall BarterPriceHook_Script(fo::GameObject* source, fo::GameObject* target, DWORD callAddr) { bool barterIsParty = (*fo::ptr::dialog_target_is_party != 0); long computeCost = fo::func::barter_compute_value(source, target); @@ -100,7 +100,7 @@ void SourceUseSkillOnInit() { sourceSkillOn = *fo::ptr::obj_dude; } static char resultSkillOn; // -1 - cancel handler, 1 - replace user static long bakupCombatState; -static void __fastcall UseSkillOnHook_Script(DWORD source, DWORD target, register DWORD skillId) { +static void __fastcall UseSkillOnHook_Script(DWORD source, DWORD target, DWORD skillId) { BeginHook(); argCount = 3; @@ -389,7 +389,7 @@ static void __declspec(naked) CarTravelHack() { } } -static long __fastcall GlobalVarHook_Script(register DWORD number, register int value) { +static long __fastcall GlobalVarHook_Script(DWORD number, int value) { int old = (*fo::ptr::game_global_vars)[number]; if (IsGameLoaded() && HookScripts::HookHasScript(HOOK_SETGLOBALVAR)) { // IsGameLoaded - don't execute hook until loading sfall scripts diff --git a/sfall/Modules/Interface.cpp b/sfall/Modules/Interface.cpp index 687563be..f3f17376 100644 --- a/sfall/Modules/Interface.cpp +++ b/sfall/Modules/Interface.cpp @@ -683,7 +683,7 @@ static void InterfaceWindowPatch() { // Increase the max text width of the information card on the character screen const DWORD drawCardAddr[] = {0x43ACD5, 0x43DD37}; // 136, 133 (DrawCard_, DrawCard2_) - SafeWriteBatch(145, drawCardAddr); + SafeWriteBatch(146, drawCardAddr); // Increase the width of the mouse drop area from 64px to 80px for the PC's and NPC's inventory on the barter screen // barter_move_from_table_inventory_ diff --git a/sfall/Modules/MainLoopHook.cpp b/sfall/Modules/MainLoopHook.cpp index 64df1c89..e2020b41 100644 --- a/sfall/Modules/MainLoopHook.cpp +++ b/sfall/Modules/MainLoopHook.cpp @@ -42,7 +42,7 @@ static void __stdcall CombatLoop() { // OnCombatLoop } static void __stdcall AfterCombatAttack() { // OnAfterCombatAttack - ResetExplosionSettings(); + ResetExplosionSettings(); // after each combat attack, reset metarule_explosions settings } static void __declspec(naked) MainGameLoopHook() { diff --git a/sfall/Modules/Scripting/Handlers/Metarule.cpp b/sfall/Modules/Scripting/Handlers/Metarule.cpp index 4aa12b6d..41730a90 100644 --- a/sfall/Modules/Scripting/Handlers/Metarule.cpp +++ b/sfall/Modules/Scripting/Handlers/Metarule.cpp @@ -109,6 +109,7 @@ static const SfallMetarule metarules[] = { {"intface_redraw", mf_intface_redraw, 0, 1}, {"intface_show", mf_intface_show, 0, 0}, {"inventory_redraw", mf_inventory_redraw, 0, 1, -1, {ARG_INT}}, + {"item_make_explosive", mf_item_make_explosive, 3, 4, -1, {ARG_INT, ARG_INT, ARG_INT, ARG_INT}}, {"item_weight", mf_item_weight, 1, 1, 0, {ARG_OBJECT}}, {"lock_is_jammed", mf_lock_is_jammed, 1, 1, 0, {ARG_OBJECT}}, {"loot_obj", mf_get_loot_object, 0, 0}, diff --git a/sfall/Modules/Scripting/Handlers/Objects.cpp b/sfall/Modules/Scripting/Handlers/Objects.cpp index 90983c18..b3439894 100644 --- a/sfall/Modules/Scripting/Handlers/Objects.cpp +++ b/sfall/Modules/Scripting/Handlers/Objects.cpp @@ -398,6 +398,25 @@ void mf_set_unjam_locks_time(OpcodeContext& ctx) { } } +void mf_item_make_explosive(OpcodeContext& ctx) { + DWORD pid = ctx.arg(0).rawValue(); + DWORD pidActive = ctx.arg(1).rawValue(); + DWORD min = ctx.arg(2).rawValue(); + DWORD max = (ctx.numArgs() == 4) ? ctx.arg(3).rawValue() : min; + + if (min > max) { + max = min; + ctx.printOpcodeError("%s() - Warning: value of max argument is less than the min argument.", ctx.getMetaruleName()); + } + + if (pid > 0 && pidActive > 0) { + Explosions::AddToExplosives(pid, pidActive, min, max); + } else { + ctx.printOpcodeError("%s() - invalid PID number, must be greater than 0.", ctx.getMetaruleName()); + ctx.setReturn(-1); + } +} + void mf_get_dialog_object(OpcodeContext& ctx) { ctx.setReturn(InDialog() ? *fo::ptr::dialog_target : 0); } diff --git a/sfall/Modules/Scripting/Handlers/Objects.h b/sfall/Modules/Scripting/Handlers/Objects.h index 438683fe..e09ceeb1 100644 --- a/sfall/Modules/Scripting/Handlers/Objects.h +++ b/sfall/Modules/Scripting/Handlers/Objects.h @@ -79,6 +79,8 @@ void mf_unjam_lock(OpcodeContext&); void mf_set_unjam_locks_time(OpcodeContext&); +void mf_item_make_explosive(OpcodeContext&); + void mf_get_dialog_object(OpcodeContext&); void mf_obj_under_cursor(OpcodeContext&);