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 });