mirror of
https://github.com/sfall-team/sfall.git
synced 2026-07-27 16:52:34 -07:00
Refactoring combat hooks (#174)
* Fixed and improved the functionality of HOOK_AMMOCOST when CheckWeaponAmmoCost is enabled. * Fixed the instant death critical fix not working if there's no script using HOOK_COMBATDAMAGE, due to the new hooks injection implementation.
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
+66
-36
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+52
-68
@@ -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);
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user