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
+