diff --git a/artifacts/scripting/headers/sfall.h b/artifacts/scripting/headers/sfall.h index 74da1b5e..d6734487 100644 --- a/artifacts/scripting/headers/sfall.h +++ b/artifacts/scripting/headers/sfall.h @@ -255,6 +255,7 @@ #define item_make_explosive(pid, aPid, min, max) sfall_func4("item_make_explosive", pid, aPid, 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") #define obj_under_cursor(crSwitch, inclDude) sfall_func2("obj_under_cursor", crSwitch, inclDude) #define outlined_object sfall_func0("outlined_object") #define real_dude_obj sfall_func0("real_dude_obj") diff --git a/artifacts/scripting/hookscripts.txt b/artifacts/scripting/hookscripts.txt index 00b98778..09fa4993 100644 --- a/artifacts/scripting/hookscripts.txt +++ b/artifacts/scripting/hookscripts.txt @@ -189,7 +189,7 @@ Critter arg1 - The critter that just died HOOK_FINDTARGET (hs_findtarget.int) -Runs when the ai is trying to pick a target in combat. Fallout first chooses a list of 4 likely suspects, then normally sorts them in order of weakness/distance/etc depending on the ai caps of the attacker. This hook replaces that sorting function, allowing you to sort the targets in some arbitrary way. Use sfall_return to give the 4 targets, in order of preference. All 4 must be given if you want to override normal sorting; if you want to specify less than 4 targets fill in the extra spaces with 0's. If you do not give 4 targets, the NPCs normal sorting mechanism will be used. +Runs when the ai is trying to pick a target in combat. Fallout first chooses a list of 4 likely suspects, then normally sorts them in order of weakness/distance/etc depending on the ai caps of the attacker. This hook replaces that sorting function, allowing you to sort the targets in some arbitrary way. Use sfall_return to give the 4 targets, in order of preference. If you want to specify less than 4 targets, fill in the extra spaces with 0's. Pass -1 to skip the return value. The return values can include critters that weren't in the list of possible targets, but the additional targets may still be discarded later on in the combat turn if they are out of the attackers perception or the chance of a successful hit is too low. The list of possible targets often includes duplicated entries. diff --git a/artifacts/scripting/sfall function notes.txt b/artifacts/scripting/sfall function notes.txt index af2c136a..c14ce4e6 100644 --- a/artifacts/scripting/sfall function notes.txt +++ b/artifacts/scripting/sfall function notes.txt @@ -540,13 +540,16 @@ Some utility/math functions are available: - the can_rest_here values in maps.txt are ignored > object sfall_func0("dialog_obj") -- returns the object (critter) the player is having a conversation or bartering with +- returns a pointer to the object (critter) the player is having a conversation or bartering with > object sfall_func2("obj_under_cursor", bool crSwitch, bool inclDude) - returns the object under the cursor on the main game screen - crSwitch: True - only checks critters and ignores their cover (roof tiles, walls, scenery, etc.), False - checks all objects (can't check critters under objects) - passing False to the inclDude argument will ignore dude_obj +> object sfall_func0("loot_obj") +- returns a pointer to the target object (container or critter) of the loot screen + ------------------------ ------ MORE INFO ------- ------------------------ diff --git a/sfall/Modules/HookScripts/CombatHs.cpp b/sfall/Modules/HookScripts/CombatHs.cpp index 9269afe0..6cdf66ff 100644 --- a/sfall/Modules/HookScripts/CombatHs.cpp +++ b/sfall/Modules/HookScripts/CombatHs.cpp @@ -185,27 +185,22 @@ static void __fastcall FindTargetHook_Script(DWORD* target, DWORD attacker) { RunHookScript(HOOK_FINDTARGET); - if (cRet >= 4) { - target[0] = rets[0]; - target[1] = rets[1]; - target[2] = rets[2]; - target[3] = rets[3]; + if (cRet > 0) { + if (rets[0] != -1) target[0] = rets[0]; + if (cRet > 1 && rets[1] != -1) target[1] = rets[1]; + if (cRet > 2 && rets[2] != -1) target[2] = rets[2]; + if (cRet > 3 && rets[3] != -1) target[3] = rets[3]; } EndHook(); } static void __declspec(naked) FindTargetHook() { __asm { - pushad; - mov ecx, eax; // targets (base) - mov edx, esi; // attacker - call FindTargetHook_Script; - popad; - cmp cRet, 4; - jge skip; + push eax; call fo::funcoffs::qsort_; -skip: - retn; + pop ecx; // targets (base) + mov edx, esi; // attacker + jmp FindTargetHook_Script; } } diff --git a/sfall/Modules/LoadGameHook.cpp b/sfall/Modules/LoadGameHook.cpp index ceb93ef6..aa787e0f 100644 --- a/sfall/Modules/LoadGameHook.cpp +++ b/sfall/Modules/LoadGameHook.cpp @@ -60,6 +60,8 @@ static Delegate<> onAfterNewGame; static Delegate onGameModeChange; static Delegate<> onBeforeGameClose; +DWORD LoadGameHook::LootTarget = 0; + static DWORD inLoop = 0; static DWORD saveInCombatFix; static bool disableHorrigan = false; @@ -528,6 +530,7 @@ static void __declspec(naked) UseInventoryOnHook() { static void __declspec(naked) LootContainerHook() { __asm { + mov LoadGameHook::LootTarget, edx; _InLoop(1, INTFACELOOT); call fo::funcoffs::loot_container_; _InLoop(0, INTFACELOOT); diff --git a/sfall/Modules/LoadGameHook.h b/sfall/Modules/LoadGameHook.h index 9dba5a2d..354a25fd 100644 --- a/sfall/Modules/LoadGameHook.h +++ b/sfall/Modules/LoadGameHook.h @@ -52,6 +52,8 @@ public: // Invoked before the game exits to windows static Delegate<>& OnBeforeGameClose(); + + static DWORD LootTarget; }; // True if some map was loaded, false when on the main menu diff --git a/sfall/Modules/Scripting/Handlers/Metarule.cpp b/sfall/Modules/Scripting/Handlers/Metarule.cpp index 725fff3f..7b63c9a3 100644 --- a/sfall/Modules/Scripting/Handlers/Metarule.cpp +++ b/sfall/Modules/Scripting/Handlers/Metarule.cpp @@ -104,6 +104,7 @@ static const SfallMetarule metarules[] = { {"item_make_explosive", sf_item_make_explosive, 3, 4, {ARG_INT, ARG_INT, ARG_INT, ARG_INT}}, {"item_weight", sf_item_weight, 1, 1, {ARG_OBJECT}}, {"lock_is_jammed", sf_lock_is_jammed, 1, 1, {ARG_OBJECT}}, + {"loot_obj", sf_get_loot_object, 0, 0}, {"obj_under_cursor", sf_obj_under_cursor, 2, 2, {ARG_INT, ARG_INT}}, {"outlined_object", sf_outlined_object, 0, 0}, {"real_dude_obj", sf_real_dude_obj, 0, 0}, diff --git a/sfall/Modules/Scripting/Handlers/Objects.cpp b/sfall/Modules/Scripting/Handlers/Objects.cpp index 614913b3..25ddbfbe 100644 --- a/sfall/Modules/Scripting/Handlers/Objects.cpp +++ b/sfall/Modules/Scripting/Handlers/Objects.cpp @@ -456,5 +456,9 @@ void sf_get_dialog_object(OpcodeContext& ctx) { ctx.setReturn(InDialog() ? fo::var::dialog_target : 0); } +void sf_get_loot_object(OpcodeContext& ctx) { + ctx.setReturn((GetLoopFlags() & INTFACELOOT) ? LoadGameHook::LootTarget : 0); +} + } } diff --git a/sfall/Modules/Scripting/Handlers/Objects.h b/sfall/Modules/Scripting/Handlers/Objects.h index 9cac2466..3883b64b 100644 --- a/sfall/Modules/Scripting/Handlers/Objects.h +++ b/sfall/Modules/Scripting/Handlers/Objects.h @@ -97,5 +97,7 @@ void sf_get_current_inven_size(OpcodeContext&); void sf_get_dialog_object(OpcodeContext&); +void sf_get_loot_object(OpcodeContext&); + } } diff --git a/sfall/SafeWrite.cpp b/sfall/SafeWrite.cpp index 723f780b..56d78ccb 100644 --- a/sfall/SafeWrite.cpp +++ b/sfall/SafeWrite.cpp @@ -47,19 +47,19 @@ void _stdcall SafeWrite32(DWORD addr, DWORD data) { void _stdcall SafeWriteStr(DWORD addr, const char* data) { DWORD oldProtect; - VirtualProtect((void *)addr, strlen(data)+1, PAGE_EXECUTE_READWRITE, &oldProtect); + VirtualProtect((void *)addr, strlen(data) + 1, PAGE_EXECUTE_READWRITE, &oldProtect); strcpy((char *)addr, data); - VirtualProtect((void *)addr, strlen(data)+1, oldProtect, &oldProtect); + VirtualProtect((void *)addr, strlen(data) + 1, oldProtect, &oldProtect); } void HookCall(DWORD addr, void* func) { - SafeWrite32(addr+1, (DWORD)func - (addr+5)); + SafeWrite32(addr + 1, (DWORD)func - (addr + 5)); #ifndef NDEBUG bool exist = false; for (const auto &wa : writeAddress) { if (addr == wa) { exist = true; - char buf[512]; + char buf[256]; sprintf_s(buf, "Memory writing conflict at address 0x%x. The address has already been overwritten by other code.", addr); MessageBoxA(0, buf, "Conflict Detected", MB_TASKMODAL); }