diff --git a/artifacts/scripting/headers/sfall.h b/artifacts/scripting/headers/sfall.h
index b681f789..e1c7376b 100644
--- a/artifacts/scripting/headers/sfall.h
+++ b/artifacts/scripting/headers/sfall.h
@@ -47,6 +47,7 @@
#define HOOK_INVENTORYMOVE (24)
#define HOOK_INVENWIELD (25)
#define HOOK_ADJUSTFID (26)
+#define HOOK_COMBATTURN (27)
//Valid arguments to list_begin
#define LIST_CRITTERS (0)
diff --git a/artifacts/scripting/hookscripts.txt b/artifacts/scripting/hookscripts.txt
index 826acf68..4ad04e44 100644
--- a/artifacts/scripting/hookscripts.txt
+++ b/artifacts/scripting/hookscripts.txt
@@ -450,3 +450,17 @@ NOTE: FID has following format: 0x0ABBCDDD, where A is object type, BB - animati
int arg1 - the vanilla fid calculated by the engine according to critter base FID and armor/weapon being used
int ret1 - overrides the calculated FID with provided value
+
+
+-------------------------------------------
+
+HOOK_COMBATTURN (hs_combatturn.int)
+
+Runs before and after each turn in combat (for both PC and NPC).
+
+int arg1 - event type: 1 - start of turn, 0 - normal end of turn, -1 - combat ended abruptly (by script or by pressing Enter during PC turn)
+int arg2 - critter doing the turn
+int arg3 - unknown boolean argument
+
+int ret1 - pass 1 at the start of turn to skip the turn, pass -1 at the end of turn to force end of combat
+
diff --git a/sfall/Modules/HookScripts.h b/sfall/Modules/HookScripts.h
index daa7d777..a440b4c9 100644
--- a/sfall/Modules/HookScripts.h
+++ b/sfall/Modules/HookScripts.h
@@ -53,6 +53,7 @@ enum HookType
HOOK_INVENTORYMOVE = 24,
HOOK_INVENWIELD = 25,
HOOK_ADJUSTFID = 26,
+ HOOK_COMBATTURN = 27,
HOOK_COUNT
};
diff --git a/sfall/Modules/HookScripts/CombatHs.cpp b/sfall/Modules/HookScripts/CombatHs.cpp
index 819f9810..eda7f07a 100644
--- a/sfall/Modules/HookScripts/CombatHs.cpp
+++ b/sfall/Modules/HookScripts/CombatHs.cpp
@@ -303,6 +303,53 @@ void __declspec(naked) AmmoCostHookWrapper() {
}
}
+// hooks combat_turn function
+static void _declspec(naked) CombatTurnHook() {
+ __asm {
+ hookbegin(3);
+ mov args[0], 1; // turn begin
+ mov args[4], eax; // critter
+ mov args[8], edx; // unknown
+ pushad;
+ push HOOK_COMBATTURN;
+ call RunHookScript; // Start of turn
+ popad;
+ cmp cRet, 1;
+ jl gonormal; // set_sfall_return not used, proceed normally
+ mov eax, rets[0];
+ jmp end;
+gonormal:
+ call fo::funcoffs::combat_turn_;
+ mov args[0], eax;
+ mov cRet, 0; // reset number of return values
+ pushad;
+ push HOOK_COMBATTURN;
+ call RunHookScript; // End of turn
+ popad;
+ cmp cRet, 1;
+ jl end;
+ mov eax, rets[0]; // override result of turn
+end:
+ hookend;
+ retn;
+ }
+}
+
+// hack to exit from combat_add_noncoms function without crashing when you load game during NPC turn
+static const DWORD CombatHack_add_noncoms_back = 0x422359;
+static void _declspec(naked) CombatAddNoncoms_CombatTurn_Hack() {
+ __asm {
+ call CombatTurnHook;
+ cmp eax, -1;
+ jne gonormal;
+ mov ecx, FO_VAR_list_com;
+ mov [ecx], 0;
+ mov ecx, [esp];
+gonormal:
+ jmp CombatHack_add_noncoms_back;
+ }
+}
+
void InitCombatHookScripts() {
LoadHookScript("hs_tohit", HOOK_TOHIT);
HookCalls(ToHitHook, {
@@ -354,6 +401,10 @@ void InitCombatHookScripts() {
LoadHookScript("hs_ammocost", HOOK_AMMOCOST);
HookCalls(AmmoCostHook, { 0x423A7C });
+
+ LoadHookScript("hs_combatturn", HOOK_COMBATTURN);
+ MakeJump(0x422354, CombatAddNoncoms_CombatTurn_Hack);
+ HookCalls(CombatTurnHook, { 0x422D87, 0x422E20 });
}
}
diff --git a/sfall/Modules/PartyControl.cpp b/sfall/Modules/PartyControl.cpp
index 72a6a791..37c8ed16 100644
--- a/sfall/Modules/PartyControl.cpp
+++ b/sfall/Modules/PartyControl.cpp
@@ -16,12 +16,6 @@
* along with this program. If not, see .
*/
-
-/*
- KNOWN ISSUES with party control
- - doesn't work with NPC's wearing armor mod, armor won't change when you change it from critter's inventory
-*/
-
#include
#include
@@ -171,7 +165,7 @@ static void RestoreRealDudeState() {
}
// return values: 0 - use vanilla handler, 1 - skip vanilla handler, return 0 (normal status), -1 - skip vanilla, return -1 (game ended)
-static int _stdcall CombatWrapperInner(fo::GameObject* obj) {
+/*static int _stdcall CombatWrapperInner(fo::GameObject* obj) {
if ((obj != fo::var::obj_dude) && (allowedCritterPids.size() == 0 || IsInPidList(obj)) && (controlMode == 1 || fo::func::isPartyMember(obj))) {
// save "real" dude state
SaveRealDudeState();
@@ -188,7 +182,7 @@ static int _stdcall CombatWrapperInner(fo::GameObject* obj) {
return (turnResult == -1) ? -1 : 1;
}
return 0;
-}
+}*/
static void __stdcall DisplayCantDoThat() {
fo::func::display_print(fo::GetMessageStr(&fo::var::proto_main_msg_file, 675)); // I Can't do that
@@ -196,16 +190,8 @@ static void __stdcall DisplayCantDoThat() {
// 1 skip handler, -1 don't skip
int __stdcall PartyControl::SwitchHandHook(fo::GameObject* item) {
+ // don't allow to use the weapon, if no art exist for it
if (fo::func::item_get_type(item) == fo::ItemType::item_type_weapon && isControllingNPC) {
- /* check below uses AI packets and skills to check if weapon is usable
- __asm {
- mov edx, item;
- mov eax, obj_dude_ptr;
- mov eax, [eax];
- mov ebx, 2;
- call fo::funcoffs::ai_can_use_weapon_;
- mov canUse, eax;
- }*/
int fId = fo::var::obj_dude->artFid;
long weaponCode = fo::AnimCodeByWeapon(item);
fId = (fId & 0xffff0fff) | (weaponCode << 12);
@@ -217,45 +203,6 @@ int __stdcall PartyControl::SwitchHandHook(fo::GameObject* item) {
return -1;
}
-
-static void _declspec(naked) CombatWrapper_v2() {
- __asm {
- sub esp, 4;
- pushad;
- push eax;
- call CombatWrapperInner;
- mov [esp+32], eax;
- popad;
- add esp, 4;
- cmp [esp-4], 0;
- je gonormal;
- cmp [esp-4], -1;
- je combatend;
- xor eax, eax;
- retn;
-combatend:
- mov eax, -1; // don't continue combat, as the game was loaded
- retn;
-gonormal:
- jmp fo::funcoffs::combat_turn_;
- }
-}
-
-// hack to exit from this function safely when you load game during NPC turn
-static const DWORD CombatHack_add_noncoms_back = 0x422359;
-static void _declspec(naked) CombatHack_add_noncoms_() {
- __asm {
- call CombatWrapper_v2;
- cmp eax, -1;
- jne gonormal;
- mov eax, FO_VAR_list_com;
- mov [eax], 0;
- mov ecx, [esp];
-gonormal:
- jmp CombatHack_add_noncoms_back;
- }
-}
-
static void __declspec(naked) stat_pc_add_experience_hook() {
__asm {
cmp isControllingNPC, 0
@@ -322,9 +269,6 @@ void PartyControl::init() {
}
dlog_f(" Mode %d, Chars read: %d.\n", DL_INIT, controlMode, allowedCritterPids.size());
- MakeJump(0x422354, CombatHack_add_noncoms_);
- HookCalls(CombatWrapper_v2, { 0x422D87, 0x422E20 });
-
HookCall(0x454218, stat_pc_add_experience_hook); // call inside op_give_exp_points_hook
HookCalls(pc_flag_toggle_hook, { 0x4124F1, 0x41279A });