diff --git a/sfall/FalloutEngine/EngineUtils.cpp b/sfall/FalloutEngine/EngineUtils.cpp index 70ba63a1..b546f7cb 100644 --- a/sfall/FalloutEngine/EngineUtils.cpp +++ b/sfall/FalloutEngine/EngineUtils.cpp @@ -20,6 +20,7 @@ #include "Functions.h" #include "Structs.h" +#include "Variables.h" #include "VariableOffsets.h" #include "EngineUtils.h" @@ -79,21 +80,29 @@ int _fastcall GetItemType(GameObject* item) { } _declspec(noinline) GameObject* GetItemPtrSlot(GameObject* critter, InvenType slot) { - GameObject* itemPtr = 0; + GameObject* itemPtr = nullptr; switch (slot) { - case fo::INVEN_TYPE_LEFT_HAND : + case fo::INVEN_TYPE_LEFT_HAND: itemPtr = fo::func::inven_left_hand(critter); break; - case fo::INVEN_TYPE_RIGHT_HAND : + case fo::INVEN_TYPE_RIGHT_HAND: itemPtr = fo::func::inven_right_hand(critter); break; - case fo::INVEN_TYPE_WORN : + case fo::INVEN_TYPE_WORN: itemPtr = fo::func::inven_worn(critter); break; } return itemPtr; } +long& GetActiveItemMode() { + return fo::var::itemButtonItems[fo::var::itemCurrentItem].mode; +} + +GameObject* GetActiveItem() { + return fo::var::itemButtonItems[fo::var::itemCurrentItem].item; +} + //--------------------------------------------------------- //print text to surface void PrintText(char *DisplayText, BYTE ColourIndex, DWORD Xpos, DWORD Ypos, DWORD TxtWidth, DWORD ToWidth, BYTE *ToSurface) { diff --git a/sfall/FalloutEngine/EngineUtils.h b/sfall/FalloutEngine/EngineUtils.h index a1b0ad5f..a5d33d8b 100644 --- a/sfall/FalloutEngine/EngineUtils.h +++ b/sfall/FalloutEngine/EngineUtils.h @@ -54,6 +54,10 @@ int _fastcall GetItemType(GameObject* item); _declspec(noinline) GameObject* GetItemPtrSlot(GameObject* critter, InvenType slot); +long& GetActiveItemMode(); + +GameObject* GetActiveItem(); + // Print text to surface void PrintText(char *displayText, BYTE colorIndex, DWORD x, DWORD y, DWORD textWidth, DWORD destWidth, BYTE *surface); // gets the height of the currently selected font diff --git a/sfall/FalloutEngine/Enums.h b/sfall/FalloutEngine/Enums.h index a17a4772..75ca5128 100644 --- a/sfall/FalloutEngine/Enums.h +++ b/sfall/FalloutEngine/Enums.h @@ -632,4 +632,52 @@ enum RollResult ROLL_CRITICAL_SUCCESS = 0x3, }; +namespace Fields { + enum CommonObj : long + { + id = 0x00, + tile = 0x04, + x = 0x08, + y = 0x0C, + sx = 0x10, + sy = 0x14, + frm = 0x18, + rotation = 0x1C, + artFid = 0x20, + flags = 0x24, + elevation = 0x28, + inventory = 0x2C, + protoId = 0x64, + cid = 0x68, + lightDistance = 0x6C, + lightIntensity = 0x70, + outline = 0x74, + scriptId = 0x78, + owner = 0x7C, + scriptIndex = 0x80, + }; + + enum CritterObj : long + { + reaction = 0x38, + combatState = 0x3C, + movePoints = 0x40, + damageFlags = 0x44, + damageLastTurn = 0x48, + aiPacket = 0x4C, + teamNum = 0x50, + whoHitMe = 0x54, + health = 0x58, + rads = 0x5C, + poison = 0x60, + }; + + enum ItemObj : long + { + updatedFlags = 0x38, + charges = 0x3C, + ammoPid = 0x40, + }; +} + } diff --git a/sfall/FalloutEngine/Functions_def.h b/sfall/FalloutEngine/Functions_def.h index 0f96d09a..89f5a0c8 100644 --- a/sfall/FalloutEngine/Functions_def.h +++ b/sfall/FalloutEngine/Functions_def.h @@ -43,8 +43,12 @@ WRAP_WATCOM_FUNC0(long, intface_is_item_right_hand) WRAP_WATCOM_FUNC0(void, intface_toggle_item_state) WRAP_WATCOM_FUNC0(void, intface_use_item) WRAP_WATCOM_FUNC0(long, is_pc_sneak_working) -WRAP_WATCOM_FUNC1(long, item_w_max_ammo, GameObject*, item) +WRAP_WATCOM_FUNC1(long, item_w_anim_code, GameObject*, item) +WRAP_WATCOM_FUNC2(long, item_w_anim_weap, GameObject*, item, DWORD, hitMode) +WRAP_WATCOM_FUNC2(long, item_w_compute_ammo_cost, GameObject*, item, DWORD*, rounds) WRAP_WATCOM_FUNC1(long, item_w_curr_ammo, GameObject*, item) +WRAP_WATCOM_FUNC1(long, item_w_rounds, GameObject*, item) +WRAP_WATCOM_FUNC1(long, item_w_max_ammo, GameObject*, item) WRAP_WATCOM_FUNC1(long, item_weight, GameObject*, item) // returns light level at given tile WRAP_WATCOM_FUNC2(long, light_get_tile, long, elevation, long, tileNum) diff --git a/sfall/FalloutEngine/Structs.h b/sfall/FalloutEngine/Structs.h index fe8c644c..b1a5dfaa 100644 --- a/sfall/FalloutEngine/Structs.h +++ b/sfall/FalloutEngine/Structs.h @@ -100,7 +100,7 @@ struct GameObject { union { struct { - char gap_38[4]; + char updatedFlags[4]; // for weapons - ammo in magazine, for ammo - amount of ammo in last ammo pack long charges; // current type of ammo loaded in magazine @@ -151,8 +151,8 @@ struct ComputeAttackResult { GameObject* target; long targetTile; long bodyPart; - long damage; - long flags; + long targetDamage; + long targetFlags; long knockbackValue; GameObject* mainTarget; long numExtras; diff --git a/sfall/Modules/BugFixes.cpp b/sfall/Modules/BugFixes.cpp index 65e1584c..83b951be 100644 --- a/sfall/Modules/BugFixes.cpp +++ b/sfall/Modules/BugFixes.cpp @@ -7,6 +7,7 @@ namespace sfall { using namespace fo; +using namespace Fields; DWORD WeightOnBody = 0; @@ -75,7 +76,7 @@ skip: static void __declspec(naked) protinst_default_use_item_hack() { __asm { - mov eax, dword ptr [edx+0x64] // eax = target pid + mov eax, dword ptr [edx + protoId] // eax = target pid cmp eax, PID_DRIVABLE_CAR je isCar cmp eax, PID_CAR_TRUNK @@ -300,7 +301,7 @@ static void __declspec(naked) invenWieldFunc_item_get_type_hook() { call fo::funcoffs::item_remove_mult_ xchg ebx, eax mov eax, esi - test cl, 0x2 // Right hand? + test cl, INVEN_TYPE_LEFT_HAND // Right hand? jz leftHand // No call fo::funcoffs::inven_right_hand_ jmp removeFlag @@ -543,7 +544,7 @@ static void __declspec(naked) drop_ammo_into_weapon_hook() { mov edx, [esp+0x24+4] // from_slot cmp edx, 1006 // Hands? jge skip // Yes - lea edx, [eax+0x2C] // Inventory + lea edx, [eax + inventory] // Inventory mov ecx, [edx] // itemsCount jcxz skip // inventory is empty (another excess check, but leave it) mov edx, [edx+8] // FirstItem @@ -617,13 +618,13 @@ end: static void __declspec(naked) action_melee_hack() { __asm { mov edx, 0x4113DC - mov ebx, [eax+0x20] // objStruct->FID + mov ebx, [eax + artFid] // objStruct->FID and ebx, 0x0F000000 sar ebx, 0x18 cmp ebx, OBJ_TYPE_CRITTER // check if object FID type flag is set to critter jne end // if object not a critter leave jump condition flags // set to skip dodge animation - test byte ptr [eax+0x44], 0x3 // (original code) DAM_KNOCKED_OUT or DAM_KNOCKED_DOWN + test byte ptr [eax + damageFlags], DAM_KNOCKED_OUT or DAM_KNOCKED_DOWN // (original code) jnz end mov edx, 0x4113FE end: @@ -634,13 +635,13 @@ end: static void __declspec(naked) action_ranged_hack() { __asm { mov edx, 0x411B6D - mov ebx, [eax+0x20] // objStruct->FID + mov ebx, [eax + artFid] // objStruct->FID and ebx, 0x0F000000 sar ebx, 0x18 cmp ebx, OBJ_TYPE_CRITTER // check if object FID type flag is set to critter jne end // if object not a critter leave jump condition flags // set to skip dodge animation - test byte ptr [eax+0x44], 0x3 // (original code) DAM_KNOCKED_OUT or DAM_KNOCKED_DOWN + test byte ptr [eax + damageFlags], DAM_KNOCKED_OUT or DAM_KNOCKED_DOWN // (original code) jnz end mov edx, 0x411BD2 end: @@ -650,12 +651,12 @@ end: static void __declspec(naked) set_new_results_hack() { __asm { - test ah, 0x1 // DAM_KNOCKED_OUT? + test ah, DAM_KNOCKED_OUT // DAM_KNOCKED_OUT? jz end // No mov eax, esi xor edx, edx inc edx // type = knockout - jmp fo::funcoffs::queue_remove_this_ // Remove knockout from queue (if there is one) + jmp fo::funcoffs::queue_remove_this_ // Remove knockout from queue (if there is one) end: pop eax // Destroy the return address push 0x424FC6 @@ -666,12 +667,12 @@ end: static void __declspec(naked) critter_wake_clear_hack() { __asm { jne end // This is not a critter - mov dl, [esi+0x44] - test dl, 0x80 // DAM_DEAD? + mov dl, [esi + damageFlags] + test dl, DAM_DEAD // DAM_DEAD? jnz end // This is a corpse - and dl, 0xFE // Unset DAM_KNOCKED_OUT - or dl, 0x2 // Set DAM_KNOCKED_DOWN - mov [esi+0x44], dl + and dl, ~DAM_KNOCKED_OUT // 0xFE Unset DAM_KNOCKED_OUT + or dl, DAM_KNOCKED_DOWN // Set DAM_KNOCKED_DOWN + mov [esi + damageFlags], dl end: xor eax, eax inc eax @@ -686,11 +687,11 @@ static void __declspec(naked) obj_load_func_hack() { __asm { test byte ptr [eax+0x25], 0x4 // Temp_ jnz end - mov edi, [eax+0x64] + mov edi, [eax + protoId] shr edi, 0x18 cmp edi, OBJ_TYPE_CRITTER jne skip - test byte ptr [eax+0x44], 0x2 // DAM_KNOCKED_DOWN? + test byte ptr [eax + damageFlags], DAM_KNOCKED_DOWN jz clear // No pushad xor ecx, ecx @@ -701,7 +702,7 @@ static void __declspec(naked) obj_load_func_hack() { call fo::funcoffs::queue_add_ popad clear: - and word ptr [eax+0x44], 0x7FFD // not (DAM_LOSE_TURN or DAM_KNOCKED_DOWN) + and word ptr [eax + damageFlags], ~(DAM_LOSE_TURN or DAM_KNOCKED_DOWN) // 0x7FFD skip: push 0x488F14 retn @@ -713,7 +714,7 @@ end: static void __declspec(naked) partyMemberPrepLoadInstance_hook() { __asm { - and word ptr [eax+0x44], 0x7FFD // not (DAM_LOSE_TURN or DAM_KNOCKED_DOWN) + and word ptr [eax + damageFlags], ~(DAM_LOSE_TURN or DAM_KNOCKED_DOWN) // 0x7FFD jmp fo::funcoffs::dude_stand_ } } @@ -721,10 +722,10 @@ static void __declspec(naked) partyMemberPrepLoadInstance_hook() { static void __declspec(naked) combat_ctd_init_hack() { __asm { mov [esi+0x24], eax // ctd.targetTile - mov eax, [ebx+0x54] // pobj.who_hit_me + mov eax, [ebx + whoHitMe] // pobj.who_hit_me inc eax jnz end - mov [ebx+0x54], eax // pobj.who_hit_me + mov [ebx + whoHitMe], eax // pobj.who_hit_me end: push 0x422F11 retn @@ -737,8 +738,8 @@ static void __declspec(naked) obj_save_hack() { jz end dec eax mov edx, [esp+0x1C] // combat_data - mov eax, [eax+0x68] // pobj.who_hit_me.cid - test byte ptr ds:[FO_VAR_combat_state], 1 // in combat? + mov eax, [eax + cid] // pobj.who_hit_me.cid + test byte ptr ds:[FO_VAR_combat_state], 1 // in combat? jz clear // No cmp dword ptr [edx], 0 // in combat? jne skip // Yes @@ -757,7 +758,7 @@ static void __declspec(naked) action_explode_hack() { using fo::ScriptProc::destroy_p_proc; __asm { mov edx, destroy_p_proc - mov eax, [esi+0x78] // pobj.sid + mov eax, [esi + scriptId] // pobj.sid call fo::funcoffs::exec_script_proc_ xor edx, edx dec edx @@ -777,9 +778,9 @@ static void __declspec(naked) action_explode_hack1() { static void __declspec(naked) barter_attempt_transaction_hack() { __asm { - cmp dword ptr [eax+0x64], PID_ACTIVE_GEIGER_COUNTER + cmp dword ptr [eax + protoId], PID_ACTIVE_GEIGER_COUNTER je found - cmp dword ptr [eax+0x64], PID_ACTIVE_STEALTH_BOY + cmp dword ptr [eax + protoId], PID_ACTIVE_STEALTH_BOY je found mov eax, 0x474D34 jmp eax @@ -812,10 +813,10 @@ static void __declspec(naked) combat_hack() { skip: pop edx xchg edx, eax // eax = source, edx = Max action points - mov [eax+0x40], edx // pobj.curr_mp - test byte ptr ds:[FO_VAR_combat_state], 1 // in combat? + mov [eax + movePoints], edx // pobj.curr_mp + test byte ptr ds:[FO_VAR_combat_state], 1 // in combat? jz end // No - mov edx, [eax+0x68] // pobj.cid + mov edx, [eax + cid] // pobj.cid cmp edx, -1 je end push eax @@ -924,19 +925,19 @@ static void __declspec(naked) switch_hand_hack() { mov eax, ds:[FO_VAR_inven_dude] push eax mov [edi], ebp - inc ecx + inc ecx // if ecx == -1 jz skip xor ebx, ebx inc ebx mov edx, ebp call fo::funcoffs::item_remove_mult_ skip: - pop edx + pop edx // _inven_dude mov eax, ebp call fo::funcoffs::item_get_type_ cmp eax, item_type_container jne end - mov [ebp+0x7C], edx // iobj.owner = _inven_dude + mov [ebp + owner], edx // iobj.owner = _inven_dude end: pop ebp pop edi @@ -949,7 +950,7 @@ static void __declspec(naked) inven_item_wearing() { __asm { mov esi, ds:[FO_VAR_inven_dude] xchg ebx, eax // ebx = source - mov eax, [esi+0x20] + mov eax, [esi + artFid] and eax, 0xF000000 sar eax, 0x18 test eax, eax // check if object FID type flag is set to item @@ -962,7 +963,7 @@ static void __declspec(naked) inven_item_wearing() { call fo::funcoffs::obj_top_environment_ test eax, eax // has an owner? jz skip // No - mov ecx, [eax+0x20] + mov ecx, [eax + artFid] and ecx, 0xF000000 sar ecx, 0x18 cmp ecx, OBJ_TYPE_CRITTER // check if object FID type flag is set to critter @@ -1047,7 +1048,7 @@ static void __declspec(naked) combat_display_hack() { __asm { mov ebx, 0x42536B; je end; // This is a critter - cmp dword ptr [ecx+0x78], -1; // Does the target have a script? + cmp dword ptr [ecx + scriptId], -1; // Does the target have a script? jne end; // Yes mov ebx, 0x425413; end: @@ -1112,18 +1113,41 @@ map_leave: static void __declspec(naked) ai_move_steps_closer_hook() { __asm { call fo::funcoffs::combat_turn_run_; - movzx dx, word ptr [esi + 0x44]; // combat_data.results + movzx dx, word ptr [esi + damageFlags]; // combat_data.results test dx, DAM_DEAD or DAM_KNOCKED_OUT or DAM_LOSE_TURN; jz end; - mov [esi + 0x40], 0; // pobj.curr_mp (source reset ap) + mov [esi + movePoints], 0; // pobj.curr_mp (source reset ap) end: retn; } } +//zero damage insta death criticals fix (moved from compute_damage hook) +static void __fastcall InstantDeathFix(fo::ComputeAttackResult &ctd) { + if (ctd.targetDamage == 0 && (ctd.targetFlags & fo::DamageFlag::DAM_DEAD)) { + ctd.targetDamage++; // set 1 hp damage + } +} + +static const DWORD ComputeDamageRet = 0x424BA7; +static void __declspec(naked) compute_damage_hack() { + __asm { + mov ecx, esi; // ctd + call InstantDeathFix; + // overwritted engine code + add esp, 0x34; + pop ebp; + pop edi; + jmp ComputeDamageRet; + } +} void BugFixes::init() { + #ifndef NDEBUG + if (isDebug && (GetConfigInt("Debugging", "BugFixes", 1) == 0)) return; + #endif + //if (GetConfigInt("Misc", "SharpshooterFix", 1)) { dlog("Applying Sharpshooter patch.", DL_INIT); // http://www.nma-fallout.com/threads/fo2-engine-tweaks-sfall.178390/page-119#post-4050162 @@ -1436,6 +1460,12 @@ void BugFixes::init() // Fix for critters killed in combat by scripting still being able to move in their combat turn if the distance parameter // in their AI packages is set to stay_close/charge, or NPCsTryToSpendExtraAP is enabled HookCall(0x42A1A8, ai_move_steps_closer_hook); // 0x42B24D + + // Fix instant death critical + dlog("Applying instant death fix.", DL_INIT); + MakeJump(0x424BA2, compute_damage_hack); + dlogr(" Done", DL_INIT); + } } diff --git a/sfall/Modules/HookScripts.h b/sfall/Modules/HookScripts.h index bce35599..c2eafd8a 100644 --- a/sfall/Modules/HookScripts.h +++ b/sfall/Modules/HookScripts.h @@ -85,7 +85,7 @@ void HookScriptClear(); void LoadHookScripts(); extern DWORD initingHookScripts; -extern void __declspec() AmmoCostHookWrapper(); +extern int __fastcall AmmoCostHook_Script(DWORD hookType, fo::GameObject* weapon, DWORD* rounds); void _stdcall RunHookScriptsAtProc(DWORD procId); } diff --git a/sfall/Modules/HookScripts/CombatHs.cpp b/sfall/Modules/HookScripts/CombatHs.cpp index c8a49a3d..ee30d745 100644 --- a/sfall/Modules/HookScripts/CombatHs.cpp +++ b/sfall/Modules/HookScripts/CombatHs.cpp @@ -10,86 +10,92 @@ namespace sfall static void __declspec(naked) ToHitHook() { __asm { - hookbegin(7); - mov args[4], eax; // attacker - mov args[8], ebx; // target - mov args[12], ecx; // body part - mov args[16], edx; // source tile - mov eax, [esp + 4]; // attack type - mov args[20], eax; - mov eax, [esp + 8]; // is ranged - mov args[24], eax; - mov eax, args[4]; - push[esp + 8]; - push[esp + 8]; + HookBegin; + mov args[4], eax; // attacker + mov args[8], ebx; // target + mov args[12], ecx; // body part + mov args[16], edx; // source tile + mov eax, [esp + 8]; + mov args[24], eax; // is ranged + push eax; + mov eax, [esp + 8]; + mov args[20], eax; // attack type + push eax; + mov eax, args[4]; // restore call fo::funcoffs::determine_to_hit_func_; - mov args[0], eax; + mov args[0], eax; pushad; - push HOOK_TOHIT; - call RunHookScript; + } + + argCount = 7; + RunHookScript(HOOK_TOHIT); + EndHook(); + + __asm { popad; - cmp cRet, 1; - jl end; - mov eax, rets[0]; -end: - hookend; + cmp cRet, 1; + cmovnb eax, rets[0]; retn 8; } } +static void __fastcall AfterHitRollHook_Script(fo::ComputeAttackResult &ctd, DWORD hitChance, DWORD hit) { + + BeginHook(); + argCount = 5; + + args[0] = hit; + args[1] = (DWORD)ctd.attacker; // Attacker + args[2] = (DWORD)ctd.target; // Target + args[3] = ctd.bodyPart; // bodypart + args[4] = hitChance; + + RunHookScript(HOOK_AFTERHITROLL); + if (cRet > 1) { + ctd.bodyPart = rets[1]; + if (cRet > 2) ctd.target = (fo::GameObject*)rets[2]; + } + EndHook(); +} + static const DWORD AfterHitRollAddr = 0x423898; static void __declspec(naked) AfterHitRollHook() { + using namespace fo; __asm { - hookbegin(5); - mov args[0], eax; //was it a hit? - mov ebx, [esi]; - mov args[4], ebx; //Attacker - mov ebx, [esi + 0x20]; - mov args[8], ebx; //Target - mov ebx, [esi + 0x28]; - mov args[12], ebx; //bodypart - mov ebx, [esp + 0x18]; - mov args[16], ebx; //hit chance pushad; - push HOOK_AFTERHITROLL; - call RunHookScript; + mov ecx, esi; // ctd + mov edx, [esp + 0x18 + 32]; // hit chance + push eax; // was it a hit? + call AfterHitRollHook_Script; popad; - cmp cRet, 1; - jl end; - mov eax, rets[0]; - cmp cRet, 2; - jl end; - mov ebx, rets[4]; - mov[esi + 0x28], ebx; - cmp cRet, 3; - jl end; - mov ebx, rets[8]; - mov[esi + 0x20], ebx; -end: - mov ebx, eax; - hookend; - cmp ebx, 1; - jmp AfterHitRollAddr; + cmp cRet, 1; + cmovnb eax, rets[0]; + // engine code + mov ebx, eax; + cmp ebx, ROLL_FAILURE; + jmp AfterHitRollAddr; } } static void __declspec(naked) CalcApCostHook() { __asm { - hookbegin(4); - mov args[0], eax; - mov args[4], edx; - mov args[8], ebx; + HookBegin; + mov args[0], eax; + mov args[4], edx; + mov args[8], ebx; call fo::funcoffs::item_w_mp_cost_; - mov args[12], eax; + mov args[12], eax; pushad; - push HOOK_CALCAPCOST; - call RunHookScript; + } + + argCount = 4; + RunHookScript(HOOK_CALCAPCOST); + EndHook(); + + __asm { popad; cmp cRet, 1; - jl end; - mov eax, rets[0]; -end: - hookend; + cmovnb eax, rets[0]; retn; } } @@ -97,210 +103,188 @@ end: // this is for using non-weapon items, always 2 AP in vanilla static void __declspec(naked) CalcApCostHook2() { __asm { - hookbegin(4); + HookBegin; mov args[0], ecx; // critter mov args[4], edx; // attack type (to determine hand) mov args[8], ebx; - mov eax, 2; // vanilla value + mov eax, 2; // vanilla value mov args[12], eax; pushad; - push HOOK_CALCAPCOST; - call RunHookScript; + } + + argCount = 4; + RunHookScript(HOOK_CALCAPCOST); + EndHook(); + + __asm { popad; cmp cRet, 1; - jl end; - mov eax, rets[0]; -end: - hookend; + cmovnb eax, rets[0]; retn; } } +static void __fastcall ComputeDamageHook_Script(fo::ComputeAttackResult &ctd, DWORD rounds, DWORD multiply) { + + BeginHook(); + argCount = 12; + + args[0] = (DWORD)ctd.target; // Target + args[1] = (DWORD)ctd.attacker; // Attacker + args[2] = ctd.targetDamage; // amountTarget + args[3] = ctd.attackerDamage; // amountSource + args[4] = ctd.targetFlags; // flagsTarget + args[5] = ctd.attackerFlags; // flagsSource + args[6] = (DWORD)ctd.weapon; + args[7] = ctd.bodyPart; + args[8] = multiply; // multiply damage + args[9] = rounds; // number rounds + args[10] = ctd.knockbackValue; + args[11] = ctd.hitMode; // attack type + + RunHookScript(HOOK_COMBATDAMAGE); + + if (cRet > 0) { + ctd.targetDamage = rets[0]; + if (cRet > 1) { + ctd.attackerDamage = rets[1]; + if (cRet > 2) { + ctd.targetFlags = rets[2]; // flagsTarget + if (cRet > 3) { + ctd.attackerFlags = rets[3]; // flagsSource + if (cRet > 4) ctd.knockbackValue = rets[4]; + } + } + } + } + EndHook(); +} + static void __declspec(naked) ComputeDamageHook() { __asm { - push edx; - push ebx; - push eax; + push ecx; + push ebx; // store dmg multiply args[8] + push edx; // store num rounds args[9] + push eax; // store ctd call fo::funcoffs::compute_damage_; - pop edx; - - //zero damage insta death criticals fix - mov ebx, [edx + 0x2c]; - test ebx, ebx; - jnz hookscript; - mov ebx, [edx + 0x30]; - test bl, 0x80; - jz hookscript; - inc dword ptr ds : [edx + 0x2c]; -hookscript: - hookbegin(12); - mov ebx, [edx + 0x20]; - mov args[0x00], ebx; - mov ebx, [edx + 0x00]; - mov args[0x04], ebx; - mov ebx, [edx + 0x2c]; - mov args[0x08], ebx; - mov ebx, [edx + 0x10]; - mov args[0x0c], ebx; - mov ebx, [edx + 0x30]; - mov args[0x10], ebx; - mov ebx, [edx + 0x14]; - mov args[0x14], ebx; - mov ebx, [edx + 0x08]; - mov args[0x18], ebx; - mov ebx, [edx + 0x28]; - mov args[0x1c], ebx; - pop ebx; // roll result - mov args[0x20], ebx; - pop ebx; // num rounds - mov args[0x24], ebx; - mov ebx, [edx + 0x34]; // knockback value - mov args[0x28], ebx; - mov ebx, [edx + 0x04]; // attack type - mov args[0x2c], ebx; - - pushad; - push HOOK_COMBATDAMAGE; - call RunHookScript; - popad; - - cmp cRet, 1; - jl end; - mov ebx, rets[0x00]; - mov[edx + 0x2c], ebx; - cmp cRet, 2; - jl end; - mov ebx, rets[0x04]; - mov[edx + 0x10], ebx; - cmp cRet, 3; - jl end; - mov ebx, rets[0x08]; - mov[edx + 0x30], ebx; - cmp cRet, 4; - jl end; - mov ebx, rets[0x0c]; - mov[edx + 0x14], ebx; - cmp cRet, 5; - jl end; - mov ebx, rets[0x10]; - mov[edx + 0x34], ebx; // knockback -end: - hookend; + pop ecx; // restore ctd (eax) + pop edx; // restore num rounds + call ComputeDamageHook_Script; + pop ecx; retn; } } +static void __fastcall FindTargetHook_Script(DWORD* target, DWORD attacker) { + + BeginHook(); + argCount = 5; + + args[0] = attacker; + args[1] = target[0]; + args[2] = target[1]; + args[3] = target[2]; + args[4] = target[3]; + + RunHookScript(HOOK_FINDTARGET); + + if (cRet >= 4) { + target[0] = args[1]; + target[1] = args[2]; + target[2] = args[3]; + target[3] = args[4]; + } + EndHook(); +} + static void __declspec(naked) FindTargetHook() { __asm { - hookbegin(5); - mov args[0], esi; //attacker - mov edi, [eax + 0]; - mov args[4], edi; - mov edi, [eax + 4]; - mov args[8], edi; - mov edi, [eax + 8]; - mov args[12], edi; - mov edi, [eax + 12]; - mov args[16], edi; pushad; - push HOOK_FINDTARGET; - call RunHookScript; + mov ecx, eax; // targets (base) + mov edx, esi; // attacker + call FindTargetHook_Script; popad; - cmp cRet, 4; - jge cont; + cmp cRet, 4; + jge skip; call fo::funcoffs::qsort_; - jmp end; -cont: - mov edi, rets[0]; - mov[eax + 0], edi; - mov edi, rets[4]; - mov[eax + 4], edi; - mov edi, rets[8]; - mov[eax + 8], edi; - mov edi, rets[12]; - mov[eax + 12], edi; -end: - hookend; +skip: retn; } } static void __declspec(naked) ItemDamageHook() { __asm { - hookbegin(6); - mov args[0], eax; //min - mov args[4], edx; //max - mov args[8], edi; //weapon - mov args[12], ecx; //critter - mov args[16], esi; //type - mov args[20], ebp; //non-zero for weapon melee attack - test edi, edi; - jnz skip; - add args[16], 8; -skip: + HookBegin; + mov args[0], eax; // min + mov args[4], edx; // max + mov args[8], edi; // weapon + mov args[12], ecx; // critter + mov args[16], esi; // type + mov args[20], ebp; // non-zero for weapon melee attack (add to min/max melee damage) pushad; - push HOOK_ITEMDAMAGE; - call RunHookScript; - popad; - cmp cRet, 0; - je runrandom; - mov eax, rets[0]; - cmp cRet, 1; - je end; - mov edx, rets[4]; -runrandom: - call fo::funcoffs::roll_random_; -end: - hookend; - retn; } + + if (args[2] == 0) { // weapon arg + args[4] += 8; // type arg + } + + argCount = 6; + RunHookScript(HOOK_ITEMDAMAGE); + EndHook(); + + _asm popad; + if (cRet > 0) { + _asm mov eax, rets[0]; + if (cRet > 1) { + _asm mov edx, rets[4]; + } else { + _asm retn; + } + } + _asm call fo::funcoffs::roll_random_; + _asm retn; } -static void __declspec(naked) AmmoCostHook_internal() { - __asm { - pushad; - mov args[0], eax; //weapon - mov ebx, [edx]; - mov args[4], ebx; //rounds in attack - call fo::funcoffs::item_w_compute_ammo_cost_; - cmp eax, -1; - je fail; - mov ebx, [edx]; - mov args[8], ebx; //rounds as computed by game +int __fastcall AmmoCostHook_Script(DWORD hookType, fo::GameObject* weapon, DWORD* rounds) { + int result = 0; - push HOOK_AMMOCOST; - call RunHookScript; - popad; - cmp cRet, 0; - je end; - mov eax, rets[0]; - mov[edx], eax; // override result - mov eax, 0; - jmp end; -fail: - popad; -end: - hookend; - retn; + BeginHook(); + argCount = 4; + + args[0] = (DWORD)weapon; + args[1] = *rounds; // rounds in attack + args[3] = hookType; + + if (hookType == 2) { // burst hook + *rounds = 1; // set default multiply for check burst attack + } else { + result = fo::func::item_w_compute_ammo_cost(weapon, rounds); + if (result == -1) goto failed; // failed computed } + args[2] = *rounds; // rounds as computed by game (cost) + + RunHookScript(HOOK_AMMOCOST); + + if (cRet > 0) *rounds = rets[0]; // override rounds + +failed: + EndHook(); + return result; } static void __declspec(naked) AmmoCostHook() { + using namespace fo; __asm { - hookbegin(4); - mov args[12], 0; // type of hook - jmp AmmoCostHook_internal; - } -} - -void __declspec(naked) AmmoCostHookWrapper() { - __asm { - hookbegin(4); - push eax; - mov eax, [esp + 8]; // hook type - mov args[12], eax; - pop eax; - call AmmoCostHook_internal; + xor ecx, ecx; // type of hook (0) + cmp dword ptr [esp + 0x1C + 4], ANIM_fire_burst; + jl skip; + cmp dword ptr [esp + 0x1C + 4], ANIM_fire_continuous; + jg skip; + mov ecx, 3; // hook type burst +skip: + xchg eax, edx; + push eax; // rounds in attack + call AmmoCostHook_Script; // edx - weapon retn; } } @@ -308,47 +292,55 @@ void __declspec(naked) AmmoCostHookWrapper() { // hooks combat_turn function static void _declspec(naked) CombatTurnHook() { __asm { - hookbegin(3); - mov args[0], 1; // turn begin + HookBegin; + mov args[0], 1; // turn begin mov args[4], eax; // critter - mov args[8], edx; // unknown + mov args[8], edx; // unknown (1 = dude turn) pushad; - push HOOK_COMBATTURN; - call RunHookScript; // Start of turn + } + + argCount = 3; + RunHookScript(HOOK_COMBATTURN); // Start of turn + + if (cRet > 0) { + EndHook(); + _asm popad; + _asm mov eax, rets[0]; + _asm retn; // exit hook + } + +// set_sfall_return not used, proceed normally + __asm { 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 + mov args[0], eax; pushad; - push HOOK_COMBATTURN; - call RunHookScript; // End of turn + } + + cRet = 0; // reset number of return values + RunHookScript(HOOK_COMBATTURN); // End of turn + EndHook(); + + __asm { popad; cmp cRet, 1; - jl end; - mov eax, rets[0]; // override result of turn -end: - hookend; + cmovnb eax, rets[0]; // override result of turn 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() { +static void _declspec(naked) CombatAddNoncoms_CombatTurnHack() { __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; + cmp eax, -1; + jne normalTurn; + mov ecx, FO_VAR_list_com; + mov dword ptr [ecx], 0; + mov ecx, [esp]; +normalTurn: + jmp CombatHack_add_noncoms_back; } } @@ -399,19 +391,19 @@ void Inject_CombatDamageHook() { } void Inject_FindTargetHook() { - HookCalls(FindTargetHook, { 0x429143 }); + HookCall(0x429143, FindTargetHook); } void Inject_ItemDamageHook() { - HookCalls(ItemDamageHook, { 0x478560 }); + HookCall(0x478560, ItemDamageHook); } void Inject_AmmoCostHook() { - HookCalls(AmmoCostHook, { 0x423A7C }); + HookCall(0x423A7C, AmmoCostHook); } void Inject_CombatTurnHook() { - MakeJump(0x422354, CombatAddNoncoms_CombatTurn_Hack); + MakeJump(0x422354, CombatAddNoncoms_CombatTurnHack); HookCalls(CombatTurnHook, { 0x422D87, 0x422E20 }); } diff --git a/sfall/Modules/Inventory.cpp b/sfall/Modules/Inventory.cpp index 2dd1c407..01fe67c8 100644 --- a/sfall/Modules/Inventory.cpp +++ b/sfall/Modules/Inventory.cpp @@ -1,4 +1,4 @@ -/* +/* * sfall * Copyright (C) 2011 Timeslip * @@ -37,23 +37,15 @@ static DWORD maxItemSize; static DWORD reloadWeaponKey = 0; static DWORD itemFastMoveKey = 0; -long& GetActiveItemMode() { - return fo::var::itemButtonItems[fo::var::itemCurrentItem].mode; -} - -fo::GameObject* GetActiveItem() { - return fo::var::itemButtonItems[fo::var::itemCurrentItem].item; -} - void InventoryKeyPressedHook(DWORD dxKey, bool pressed, DWORD vKey) { // TODO: move this out into a script if (pressed && reloadWeaponKey && dxKey == reloadWeaponKey && IsMapLoaded() && (GetLoopFlags() & ~(COMBAT | PCOMBAT)) == 0) { DWORD maxAmmo, curAmmo; - fo::GameObject* item = GetActiveItem(); + fo::GameObject* item = fo::GetActiveItem(); maxAmmo = fo::func::item_w_max_ammo(item); curAmmo = fo::func::item_w_curr_ammo(item); if (maxAmmo != curAmmo) { - long ¤tMode = GetActiveItemMode(); + long ¤tMode = fo::GetActiveItemMode(); long previusMode = currentMode; currentMode = 5; // reload mode fo::func::intface_use_item(); @@ -450,66 +442,57 @@ static void __declspec(naked) inven_ap_cost_hook() { } } -static const DWORD add_check_for_item_ammo_cost_back = 0x4266EE; -// adds check for weapons which require more than 1 ammo for single shot (super cattle prod & mega power fist) -static void __declspec(naked) add_check_for_item_ammo_cost() { +static DWORD __fastcall add_check_for_item_ammo_cost(register fo::GameObject* weapon, DWORD hitMode) { + DWORD rounds = 1; + DWORD anim = fo::func::item_w_anim_weap(weapon, hitMode); + if (anim == fo::Animation::ANIM_fire_burst || anim == fo::Animation::ANIM_fire_continuous) { + rounds = fo::func::item_w_rounds(weapon); + } + + AmmoCostHook_Script(1, weapon, &rounds); + + DWORD currAmmo = fo::func::item_w_curr_ammo(weapon); + DWORD cost = (DWORD)ceilf((float)rounds / currAmmo); // get rounds cost from hook + return (cost > currAmmo) ? 0 : 1; // 0 - this will force "Out of ammo", 1 - this will force success (enough ammo) +} + +// adds check for weapons which require more than 1 ammo for single shot (super cattle prod & mega power fist) and burst rounds +static void __declspec(naked) combat_check_bad_shot_hook() { __asm { - push edx - push ebx - sub esp, 4 - call fo::funcoffs::item_w_curr_ammo_ - mov ebx, eax - mov eax, ecx // weapon - mov edx, esp - mov dword ptr [esp], 1 - pushad - push 1 // hook type - call AmmoCostHookWrapper - add esp, 4 - popad - mov eax, [esp] - cmp eax, ebx - jle enoughammo - xor eax, eax // this will force "Out of ammo" - jmp end -enoughammo: - mov eax, 1 // this will force success -end: - add esp, 4 - pop ebx - pop edx - jmp add_check_for_item_ammo_cost_back; // jump back + push edx; + push ebx; + push ecx; // weapon + mov edx, edi; // hitMode + call add_check_for_item_ammo_cost; + pop ecx; + pop ebx; + pop edx; + retn; } } -static const DWORD divide_burst_rounds_by_ammo_cost_back = 0x4234B9; -static void __declspec(naked) divide_burst_rounds_by_ammo_cost() { +static DWORD __fastcall divide_burst_rounds_by_ammo_cost(fo::GameObject* weapon, register DWORD currAmmo, DWORD burstRounds) { + DWORD rounds = burstRounds; // rounds in burst (количество патронов израсходуемое в очереди) + + AmmoCostHook_Script(2, weapon, &rounds); + + DWORD cost = burstRounds * rounds; // so much ammo is required for this burst (количество патронов в очереди умноженное на 1 или на значения возвращаемое из скрипта) + if (cost > currAmmo) cost = currAmmo; // if cost ammo more than current ammo, set it to current + + return (cost / rounds); // divide back to get proper number of rounds for damage calculations +} + +static void __declspec(naked) compute_spray_hack() { __asm { - // ecx - current ammo, eax - burst rounds; need to set ebp - push edx - sub esp, 4 - mov ebp, eax - mov eax, edx // weapon - mov dword ptr [esp], 1 - mov edx, esp // *rounds - pushad - push 2 - call AmmoCostHookWrapper - add esp, 4 - popad - mov edx, 0 - mov eax, ebp // rounds in burst - imul dword ptr [esp] // so much ammo is required for this burst - cmp eax, ecx - jle skip - mov eax, ecx // if more than current ammo, set it to current -skip: - idiv dword ptr [esp] // divide back to get proper number of rounds for damage calculations - mov ebp, eax - add esp, 4 - pop edx - // end overwriten code - jmp divide_burst_rounds_by_ammo_cost_back; // jump back + push edx; // weapon + push ecx; // current ammo in weapon + xchg ecx, edx; + push eax; // eax - rounds in burst attack, need to set ebp + call divide_burst_rounds_by_ammo_cost; + mov ebp, eax; // overwriten code (в ebp устанавливаем правильное значение) + pop ecx; + pop edx; + retn; } } @@ -868,8 +851,9 @@ void Inventory::init() { } if (GetConfigInt("Misc", "CheckWeaponAmmoCost", 0)) { - MakeJump(0x4266E9, add_check_for_item_ammo_cost); - MakeJump(0x4234B3, divide_burst_rounds_by_ammo_cost); + HookCall(0x4266E9, combat_check_bad_shot_hook); + MakeCall(0x4234B3, compute_spray_hack); + SafeWrite8(0x4234B8, 0x90); } reloadWeaponKey = GetConfigInt("Input", "ReloadWeaponKey", 0); diff --git a/sfall/Modules/MiscPatches.cpp b/sfall/Modules/MiscPatches.cpp index 525f182a..26f1778e 100644 --- a/sfall/Modules/MiscPatches.cpp +++ b/sfall/Modules/MiscPatches.cpp @@ -367,9 +367,9 @@ static void __declspec(naked) objCanSeeObj_ShootThru_Fix() {//(EAX *objStruct, E static DWORD __fastcall GetWeaponSlotMode(DWORD itemPtr, DWORD mode) { int slot = (mode > 0) ? 1 : 0; - auto itemButton = fo::var::itemButtonItems; - if ((DWORD)itemButton[slot].item == itemPtr) { - int slotMode = itemButton[slot].mode; + fo::ItemButtonItem* itemButton = &fo::var::itemButtonItems[slot]; + if ((DWORD)itemButton->item == itemPtr) { + int slotMode = itemButton->mode; if (slotMode == 3 || slotMode == 4) { mode++; } @@ -407,7 +407,7 @@ static void __fastcall SwapHandSlots(fo::GameObject* item, DWORD* toSlot) { memcpy(item, rightSlot, 0x14); item[0].primaryAttack = fo::AttackType::ATKTYPE_LWEAPON_PRIMARY; item[0].secondaryAttack = fo::AttackType::ATKTYPE_LWEAPON_SECONDARY; - slot = leftSlot;// Rslot > Lslot; + slot = leftSlot; // Rslot > Lslot } else { memcpy(item, leftSlot, 0x14); item[0].primaryAttack = fo::AttackType::ATKTYPE_RWEAPON_PRIMARY; @@ -416,19 +416,14 @@ static void __fastcall SwapHandSlots(fo::GameObject* item, DWORD* toSlot) { } memcpy(slot, item, 0x14); } else { // swap slot - fo::ItemButtonItem swapLBuf[1]; - fo::ItemButtonItem swapRBuf[1]; + auto swapBuf = fo::var::itemButtonItems; + swapBuf[0].primaryAttack = fo::AttackType::ATKTYPE_RWEAPON_PRIMARY; + swapBuf[0].secondaryAttack = fo::AttackType::ATKTYPE_RWEAPON_SECONDARY; + swapBuf[1].primaryAttack = fo::AttackType::ATKTYPE_LWEAPON_PRIMARY; + swapBuf[1].secondaryAttack = fo::AttackType::ATKTYPE_LWEAPON_SECONDARY; - memcpy(swapLBuf, leftSlot, 0x14); - memcpy(swapRBuf, rightSlot, 0x14); - - swapLBuf[0].primaryAttack = fo::AttackType::ATKTYPE_RWEAPON_PRIMARY; - swapLBuf[0].secondaryAttack = fo::AttackType::ATKTYPE_RWEAPON_SECONDARY; - swapRBuf[0].primaryAttack = fo::AttackType::ATKTYPE_LWEAPON_PRIMARY; - swapRBuf[0].secondaryAttack = fo::AttackType::ATKTYPE_LWEAPON_SECONDARY; - - memcpy(leftSlot, swapRBuf, 0x14); // buf Rslot > Lslot; - memcpy(rightSlot, swapLBuf, 0x14); // buf Lslot > Rslot; + memcpy(leftSlot, &swapBuf[1], 0x14); // buf Rslot > Lslot + memcpy(rightSlot, &swapBuf[0], 0x14); // buf Lslot > Rslot } }