diff --git a/artifacts/mods/gl_npcarmor.int b/artifacts/mods/gl_npcarmor.int index 1fb06619..fe51142f 100644 Binary files a/artifacts/mods/gl_npcarmor.int and b/artifacts/mods/gl_npcarmor.int differ diff --git a/artifacts/mods/gl_npcarmor.ssl b/artifacts/mods/gl_npcarmor.ssl index 422154cd..32fd3c54 100644 --- a/artifacts/mods/gl_npcarmor.ssl +++ b/artifacts/mods/gl_npcarmor.ssl @@ -9,7 +9,7 @@ NOTE: this script requires compiler from sfall modderspack with -s option (short circuit evaluation) - version 1.1 + version 1.2 */ @@ -21,6 +21,7 @@ #define FID_OBJ_CRITTER (0x01000000) procedure update_armor_apperance; +procedure canuseweapon_handler; variable modIni := "npcarmor.ini", @@ -203,6 +204,7 @@ procedure start begin register_hook_proc(HOOK_INVENWIELD, invenwield_handler); register_hook_proc(HOOK_ADJUSTFID, adjustfid_handler); register_hook_proc(HOOK_INVENTORYMOVE, inventorymove_handler); + register_hook_proc_spec(HOOK_CANUSEWEAPON, canuseweapon_handler); debug_msg("NPC armor appearance mod: Done."); end @@ -220,3 +222,14 @@ procedure update_armor_apperance begin end end end + +procedure canuseweapon_handler begin + variable critter, canWield; + critter := get_sfall_arg; + if (critter) then begin + canWield := check_weapon_change(critter, get_sfall_arg, true); + //if (canWield != 0) then canWield := 1; + set_sfall_arg(3, canWield); // override result + set_sfall_return(canWield); + end +end diff --git a/artifacts/scripting/headers/sfall.h b/artifacts/scripting/headers/sfall.h index 2de164b0..b596aa7b 100644 --- a/artifacts/scripting/headers/sfall.h +++ b/artifacts/scripting/headers/sfall.h @@ -73,6 +73,7 @@ #define HOOK_ADJUSTRADS (45) #define HOOK_ROLLCHECK (46) #define HOOK_BESTWEAPON (47) +#define HOOK_CANUSEWEAPON (48) //Valid arguments to list_begin #define LIST_CRITTERS (0) diff --git a/artifacts/scripting/hookscripts.md b/artifacts/scripting/hookscripts.md index 877c6868..4b81fec3 100644 --- a/artifacts/scripting/hookscripts.md +++ b/artifacts/scripting/hookscripts.md @@ -888,3 +888,19 @@ Critter arg4 - the target of the critter (can be 0) Item ret0 - overrides the chosen best weapon ``` + +------------------------------------------- + +#### `HOOK_CANUSEWEAPON (hs_canuseweapon.int)` + +Run when the AI checks whether it can use a weapon. +This mostly happens when NPCs try to find weapons in their inventory or on the map. + +``` +Critter arg0 - the critter doing the check +Item arg1 - the weapon being checked +int arg2 - attack type (see ATKTYPE_* constants) +int arg3 - original result of engine function: 1 - can use, 0 - can't use + +int ret0 - overrides the result of engine function. Any non-zero value allows using the weapon +``` diff --git a/sfall/FalloutEngine/Functions_def.h b/sfall/FalloutEngine/Functions_def.h index 01c1672c..86e5232e 100644 --- a/sfall/FalloutEngine/Functions_def.h +++ b/sfall/FalloutEngine/Functions_def.h @@ -19,6 +19,8 @@ because the compiler builds the better/optimized code when calling the engine functions */ WRAP_WATCOM_FFUNC4(long, _word_wrap, const char*, text, int, maxWidth, DWORD*, buf, BYTE*, count) +WRAP_WATCOM_FFUNC4(fo::GameObject*, ai_best_weapon, fo::GameObject*, source, fo::GameObject*, prevItem, fo::GameObject*, checkItem, fo::GameObject*, target) +WRAP_WATCOM_FFUNC3(bool, ai_can_use_weapon, fo::GameObject*, critter, fo::GameObject*, item, DWORD, hitMode) WRAP_WATCOM_FFUNC3(long, ai_have_ammo, fo::GameObject*, critter, fo::GameObject*, item, fo::GameObject**, outAmmo) WRAP_WATCOM_FFUNC3(long, ai_pick_hit_mode, fo::GameObject*, source, fo::GameObject*, item, fo::GameObject*, target) WRAP_WATCOM_FFUNC3(fo::GameObject*, ai_search_inven_weap, fo::GameObject*, source, long, apCheck, fo::GameObject*, target) diff --git a/sfall/Game/combatAI.cpp b/sfall/Game/combatAI.cpp new file mode 100644 index 00000000..79e1acec --- /dev/null +++ b/sfall/Game/combatAI.cpp @@ -0,0 +1,28 @@ +/* + * sfall + * Copyright (C) 2008-2021 The sfall team + * + */ + +//#include "..\main.h" +#include "..\FalloutEngine\Fallout2.h" + +#include "..\Modules\HookScripts\CombatHs.h" + +#include "combatAI.h" + +namespace game +{ + +namespace sf = sfall; + +// Implementation of ai_can_use_weapon_ engine function with the HOOK_CANUSEWEAPON hook +long CombatAI::ai_can_use_weapon(fo::GameObject* source, fo::GameObject* weapon, long hitMode) { + long result = fo::func::ai_can_use_weapon(source, weapon, hitMode); + return sf::CanUseWeaponHook_Invoke(result, source, weapon, hitMode); +} + +void CombatAI::init() { // TODO: add to main.cpp +} + +} diff --git a/sfall/Game/combatAI.h b/sfall/Game/combatAI.h new file mode 100644 index 00000000..0a19075d --- /dev/null +++ b/sfall/Game/combatAI.h @@ -0,0 +1,19 @@ +/* + * sfall + * Copyright (C) 2008-2021 The sfall team + * + */ + +#pragma once + +namespace game +{ + +class CombatAI { +public: + static void init(); + + static long ai_can_use_weapon(fo::GameObject* source, fo::GameObject* weapon, long hitMode); +}; + +} \ No newline at end of file diff --git a/sfall/Modules/HookScripts.cpp b/sfall/Modules/HookScripts.cpp index a1a0eed4..27a2e7e9 100644 --- a/sfall/Modules/HookScripts.cpp +++ b/sfall/Modules/HookScripts.cpp @@ -109,6 +109,7 @@ static HooksInjectInfo injectHooks[] = { {HOOK_ADJUSTRADS, Inject_AdjustRadsHook, 1}, // always embedded for party control fix {HOOK_ROLLCHECK, Inject_RollCheckHook, 0}, {HOOK_BESTWEAPON, Inject_BestWeaponHook, 0}, + {HOOK_CANUSEWEAPON, Inject_CanUseWeaponHook, 0}, }; void HookScripts::InjectingHook(int hookId) { diff --git a/sfall/Modules/HookScripts.h b/sfall/Modules/HookScripts.h index beb6391d..fa069ebd 100644 --- a/sfall/Modules/HookScripts.h +++ b/sfall/Modules/HookScripts.h @@ -74,6 +74,7 @@ enum HookType HOOK_ADJUSTRADS = 45, HOOK_ROLLCHECK = 46, HOOK_BESTWEAPON = 47, + HOOK_CANUSEWEAPON = 48, HOOK_COUNT }; diff --git a/sfall/Modules/HookScripts/CombatHs.cpp b/sfall/Modules/HookScripts/CombatHs.cpp index 0ff0c430..a6b7266c 100644 --- a/sfall/Modules/HookScripts/CombatHs.cpp +++ b/sfall/Modules/HookScripts/CombatHs.cpp @@ -600,12 +600,12 @@ static fo::GameObject* __stdcall BestWeaponHook_Script(fo::GameObject* bestWeapo static void __declspec(naked) ai_search_inven_weap_hook() { __asm { - push ecx; - push ebx; - push edx; - push eax; + push ecx; // target + push ebx; // weapon2 (secondary) + push edx; // weapon1 (primary) + push eax; // source call fo::funcoffs::ai_best_weapon_; - push eax; + push eax; // bestWeapon call BestWeaponHook_Script; retn; } @@ -619,6 +619,48 @@ fo::GameObject* BestWeaponHook_Invoke(fo::GameObject* bestWeapon, fo::GameObject } */ +static long __stdcall CanUseWeaponHook_Script(long result, fo::GameObject* source, fo::GameObject* weapon, long hitMode) { + BeginHook(); + argCount = 4; + + args[0] = (DWORD)source; + args[1] = (DWORD)weapon; + args[2] = hitMode; + args[3] = result; + + RunHookScript(HOOK_CANUSEWEAPON); + + if (cRet > 0) { + result = rets[0]; + if (result != 0) result = 1; + } + + EndHook(); + return result; +} + +static void __declspec(naked) CanUseWeaponHook() { + __asm { + push ecx; + push ebx; // hitMode + push edx; // weapon + push eax; // source + call fo::funcoffs::ai_can_use_weapon_; + push eax; // result + call CanUseWeaponHook_Script; + pop ecx; + retn; + } +} + +long CanUseWeaponHook_Invoke(long result, fo::GameObject* source, fo::GameObject* weapon, long hitMode) { + return (HookScripts::HookHasScript(HOOK_CANUSEWEAPON)) + ? CanUseWeaponHook_Script(result, source, weapon, hitMode) + : result; +} + +//////////////////////////////////////////////////////////////////////////////// + void Inject_ToHitHook() { HookCalls(ToHitHook, { 0x421686, // combat_safety_invalidate_weapon_func_ @@ -709,6 +751,14 @@ void Inject_BestWeaponHook() { HookCall(0x429A59, ai_search_inven_weap_hook); } +void Inject_CanUseWeaponHook() { + HookCalls(CanUseWeaponHook, { + 0x429A1B, // ai_search_inven_weap_ + 0x429CF2, // ai_search_environ_ + 0x429E1C // ai_pick_hit_mode_ + }); +} + void InitCombatHookScripts() { HookScripts::LoadHookScript("hs_tohit", HOOK_TOHIT); HookScripts::LoadHookScript("hs_afterhitroll", HOOK_AFTERHITROLL); @@ -722,6 +772,7 @@ void InitCombatHookScripts() { HookScripts::LoadHookScript("hs_subcombatdmg", HOOK_SUBCOMBATDAMAGE); HookScripts::LoadHookScript("hs_targetobject", HOOK_TARGETOBJECT); HookScripts::LoadHookScript("hs_bestweapon", HOOK_BESTWEAPON); + HookScripts::LoadHookScript("hs_canuseweapon", HOOK_CANUSEWEAPON); } } diff --git a/sfall/Modules/HookScripts/CombatHs.h b/sfall/Modules/HookScripts/CombatHs.h index 04ed91f4..5027e204 100644 --- a/sfall/Modules/HookScripts/CombatHs.h +++ b/sfall/Modules/HookScripts/CombatHs.h @@ -19,11 +19,13 @@ void Inject_OnExplosionHook(); void Inject_SubCombatDamageHook(); void Inject_TargetObjectHook(); void Inject_BestWeaponHook(); +void Inject_CanUseWeaponHook(); int __fastcall AmmoCostHook_Script(DWORD hookType, fo::GameObject* weapon, DWORD &rounds); long CalcApCostHook_Invoke(fo::GameObject* source, long hitMode, long isCalled, long cost, fo::GameObject* weapon); //void FindTargetHook_Invoke(fo::GameObject* targets[], fo::GameObject* attacker); //fo::GameObject* BestWeaponHook_Invoke(fo::GameObject* bestWeapon, fo::GameObject* source, fo::GameObject* weapon1, fo::GameObject* weapon2, fo::GameObject* target); +long CanUseWeaponHook_Invoke(long result, fo::GameObject* source, fo::GameObject* weapon, long hitMode); } diff --git a/sfall/ddraw.vcxproj b/sfall/ddraw.vcxproj index d3c79632..497fc3c1 100644 --- a/sfall/ddraw.vcxproj +++ b/sfall/ddraw.vcxproj @@ -291,6 +291,7 @@ + @@ -402,6 +403,7 @@ + diff --git a/sfall/ddraw.vcxproj.filters b/sfall/ddraw.vcxproj.filters index f6023c84..edb22c53 100644 --- a/sfall/ddraw.vcxproj.filters +++ b/sfall/ddraw.vcxproj.filters @@ -343,6 +343,9 @@ Game + + Game + @@ -636,6 +639,9 @@ Game + + Game +