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:
Mr.Stalin
2018-07-03 09:34:47 +08:00
committed by NovaRain
parent 2f22a1e982
commit 44603174dd
10 changed files with 449 additions and 383 deletions
+10 -1
View File
@@ -20,6 +20,7 @@
#include "Functions.h" #include "Functions.h"
#include "Structs.h" #include "Structs.h"
#include "Variables.h"
#include "VariableOffsets.h" #include "VariableOffsets.h"
#include "EngineUtils.h" #include "EngineUtils.h"
@@ -79,7 +80,7 @@ int _fastcall GetItemType(GameObject* item) {
} }
_declspec(noinline) GameObject* GetItemPtrSlot(GameObject* critter, InvenType slot) { _declspec(noinline) GameObject* GetItemPtrSlot(GameObject* critter, InvenType slot) {
GameObject* itemPtr = 0; GameObject* itemPtr = nullptr;
switch (slot) { switch (slot) {
case fo::INVEN_TYPE_LEFT_HAND: case fo::INVEN_TYPE_LEFT_HAND:
itemPtr = fo::func::inven_left_hand(critter); itemPtr = fo::func::inven_left_hand(critter);
@@ -94,6 +95,14 @@ _declspec(noinline) GameObject* GetItemPtrSlot(GameObject* critter, InvenType sl
return itemPtr; 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 //print text to surface
void PrintText(char *DisplayText, BYTE ColourIndex, DWORD Xpos, DWORD Ypos, DWORD TxtWidth, DWORD ToWidth, BYTE *ToSurface) { void PrintText(char *DisplayText, BYTE ColourIndex, DWORD Xpos, DWORD Ypos, DWORD TxtWidth, DWORD ToWidth, BYTE *ToSurface) {
+4
View File
@@ -54,6 +54,10 @@ int _fastcall GetItemType(GameObject* item);
_declspec(noinline) GameObject* GetItemPtrSlot(GameObject* critter, InvenType slot); _declspec(noinline) GameObject* GetItemPtrSlot(GameObject* critter, InvenType slot);
long& GetActiveItemMode();
GameObject* GetActiveItem();
// Print text to surface // Print text to surface
void PrintText(char *displayText, BYTE colorIndex, DWORD x, DWORD y, DWORD textWidth, DWORD destWidth, BYTE *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 // gets the height of the currently selected font
+48
View File
@@ -632,4 +632,52 @@ enum RollResult
ROLL_CRITICAL_SUCCESS = 0x3, 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,
};
}
} }
+5 -1
View File
@@ -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_toggle_item_state)
WRAP_WATCOM_FUNC0(void, intface_use_item) WRAP_WATCOM_FUNC0(void, intface_use_item)
WRAP_WATCOM_FUNC0(long, is_pc_sneak_working) 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_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) WRAP_WATCOM_FUNC1(long, item_weight, GameObject*, item)
// returns light level at given tile // returns light level at given tile
WRAP_WATCOM_FUNC2(long, light_get_tile, long, elevation, long, tileNum) WRAP_WATCOM_FUNC2(long, light_get_tile, long, elevation, long, tileNum)
+3 -3
View File
@@ -100,7 +100,7 @@ struct GameObject {
union { union {
struct { struct {
char gap_38[4]; char updatedFlags[4];
// for weapons - ammo in magazine, for ammo - amount of ammo in last ammo pack // for weapons - ammo in magazine, for ammo - amount of ammo in last ammo pack
long charges; long charges;
// current type of ammo loaded in magazine // current type of ammo loaded in magazine
@@ -151,8 +151,8 @@ struct ComputeAttackResult {
GameObject* target; GameObject* target;
long targetTile; long targetTile;
long bodyPart; long bodyPart;
long damage; long targetDamage;
long flags; long targetFlags;
long knockbackValue; long knockbackValue;
GameObject* mainTarget; GameObject* mainTarget;
long numExtras; long numExtras;
+63 -33
View File
@@ -7,6 +7,7 @@
namespace sfall namespace sfall
{ {
using namespace fo; using namespace fo;
using namespace Fields;
DWORD WeightOnBody = 0; DWORD WeightOnBody = 0;
@@ -75,7 +76,7 @@ skip:
static void __declspec(naked) protinst_default_use_item_hack() { static void __declspec(naked) protinst_default_use_item_hack() {
__asm { __asm {
mov eax, dword ptr [edx+0x64] // eax = target pid mov eax, dword ptr [edx + protoId] // eax = target pid
cmp eax, PID_DRIVABLE_CAR cmp eax, PID_DRIVABLE_CAR
je isCar je isCar
cmp eax, PID_CAR_TRUNK cmp eax, PID_CAR_TRUNK
@@ -300,7 +301,7 @@ static void __declspec(naked) invenWieldFunc_item_get_type_hook() {
call fo::funcoffs::item_remove_mult_ call fo::funcoffs::item_remove_mult_
xchg ebx, eax xchg ebx, eax
mov eax, esi mov eax, esi
test cl, 0x2 // Right hand? test cl, INVEN_TYPE_LEFT_HAND // Right hand?
jz leftHand // No jz leftHand // No
call fo::funcoffs::inven_right_hand_ call fo::funcoffs::inven_right_hand_
jmp removeFlag jmp removeFlag
@@ -543,7 +544,7 @@ static void __declspec(naked) drop_ammo_into_weapon_hook() {
mov edx, [esp+0x24+4] // from_slot mov edx, [esp+0x24+4] // from_slot
cmp edx, 1006 // Hands? cmp edx, 1006 // Hands?
jge skip // Yes jge skip // Yes
lea edx, [eax+0x2C] // Inventory lea edx, [eax + inventory] // Inventory
mov ecx, [edx] // itemsCount mov ecx, [edx] // itemsCount
jcxz skip // inventory is empty (another excess check, but leave it) jcxz skip // inventory is empty (another excess check, but leave it)
mov edx, [edx+8] // FirstItem mov edx, [edx+8] // FirstItem
@@ -617,13 +618,13 @@ end:
static void __declspec(naked) action_melee_hack() { static void __declspec(naked) action_melee_hack() {
__asm { __asm {
mov edx, 0x4113DC mov edx, 0x4113DC
mov ebx, [eax+0x20] // objStruct->FID mov ebx, [eax + artFid] // objStruct->FID
and ebx, 0x0F000000 and ebx, 0x0F000000
sar ebx, 0x18 sar ebx, 0x18
cmp ebx, OBJ_TYPE_CRITTER // check if object FID type flag is set to critter 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 jne end // if object not a critter leave jump condition flags
// set to skip dodge animation // 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 jnz end
mov edx, 0x4113FE mov edx, 0x4113FE
end: end:
@@ -634,13 +635,13 @@ end:
static void __declspec(naked) action_ranged_hack() { static void __declspec(naked) action_ranged_hack() {
__asm { __asm {
mov edx, 0x411B6D mov edx, 0x411B6D
mov ebx, [eax+0x20] // objStruct->FID mov ebx, [eax + artFid] // objStruct->FID
and ebx, 0x0F000000 and ebx, 0x0F000000
sar ebx, 0x18 sar ebx, 0x18
cmp ebx, OBJ_TYPE_CRITTER // check if object FID type flag is set to critter 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 jne end // if object not a critter leave jump condition flags
// set to skip dodge animation // 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 jnz end
mov edx, 0x411BD2 mov edx, 0x411BD2
end: end:
@@ -650,7 +651,7 @@ end:
static void __declspec(naked) set_new_results_hack() { static void __declspec(naked) set_new_results_hack() {
__asm { __asm {
test ah, 0x1 // DAM_KNOCKED_OUT? test ah, DAM_KNOCKED_OUT // DAM_KNOCKED_OUT?
jz end // No jz end // No
mov eax, esi mov eax, esi
xor edx, edx xor edx, edx
@@ -666,12 +667,12 @@ end:
static void __declspec(naked) critter_wake_clear_hack() { static void __declspec(naked) critter_wake_clear_hack() {
__asm { __asm {
jne end // This is not a critter jne end // This is not a critter
mov dl, [esi+0x44] mov dl, [esi + damageFlags]
test dl, 0x80 // DAM_DEAD? test dl, DAM_DEAD // DAM_DEAD?
jnz end // This is a corpse jnz end // This is a corpse
and dl, 0xFE // Unset DAM_KNOCKED_OUT and dl, ~DAM_KNOCKED_OUT // 0xFE Unset DAM_KNOCKED_OUT
or dl, 0x2 // Set DAM_KNOCKED_DOWN or dl, DAM_KNOCKED_DOWN // Set DAM_KNOCKED_DOWN
mov [esi+0x44], dl mov [esi + damageFlags], dl
end: end:
xor eax, eax xor eax, eax
inc eax inc eax
@@ -686,11 +687,11 @@ static void __declspec(naked) obj_load_func_hack() {
__asm { __asm {
test byte ptr [eax+0x25], 0x4 // Temp_ test byte ptr [eax+0x25], 0x4 // Temp_
jnz end jnz end
mov edi, [eax+0x64] mov edi, [eax + protoId]
shr edi, 0x18 shr edi, 0x18
cmp edi, OBJ_TYPE_CRITTER cmp edi, OBJ_TYPE_CRITTER
jne skip jne skip
test byte ptr [eax+0x44], 0x2 // DAM_KNOCKED_DOWN? test byte ptr [eax + damageFlags], DAM_KNOCKED_DOWN
jz clear // No jz clear // No
pushad pushad
xor ecx, ecx xor ecx, ecx
@@ -701,7 +702,7 @@ static void __declspec(naked) obj_load_func_hack() {
call fo::funcoffs::queue_add_ call fo::funcoffs::queue_add_
popad popad
clear: 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: skip:
push 0x488F14 push 0x488F14
retn retn
@@ -713,7 +714,7 @@ end:
static void __declspec(naked) partyMemberPrepLoadInstance_hook() { static void __declspec(naked) partyMemberPrepLoadInstance_hook() {
__asm { __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_ jmp fo::funcoffs::dude_stand_
} }
} }
@@ -721,10 +722,10 @@ static void __declspec(naked) partyMemberPrepLoadInstance_hook() {
static void __declspec(naked) combat_ctd_init_hack() { static void __declspec(naked) combat_ctd_init_hack() {
__asm { __asm {
mov [esi+0x24], eax // ctd.targetTile mov [esi+0x24], eax // ctd.targetTile
mov eax, [ebx+0x54] // pobj.who_hit_me mov eax, [ebx + whoHitMe] // pobj.who_hit_me
inc eax inc eax
jnz end jnz end
mov [ebx+0x54], eax // pobj.who_hit_me mov [ebx + whoHitMe], eax // pobj.who_hit_me
end: end:
push 0x422F11 push 0x422F11
retn retn
@@ -737,7 +738,7 @@ static void __declspec(naked) obj_save_hack() {
jz end jz end
dec eax dec eax
mov edx, [esp+0x1C] // combat_data mov edx, [esp+0x1C] // combat_data
mov eax, [eax+0x68] // pobj.who_hit_me.cid mov eax, [eax + cid] // pobj.who_hit_me.cid
test byte ptr ds:[FO_VAR_combat_state], 1 // in combat? test byte ptr ds:[FO_VAR_combat_state], 1 // in combat?
jz clear // No jz clear // No
cmp dword ptr [edx], 0 // in combat? cmp dword ptr [edx], 0 // in combat?
@@ -757,7 +758,7 @@ static void __declspec(naked) action_explode_hack() {
using fo::ScriptProc::destroy_p_proc; using fo::ScriptProc::destroy_p_proc;
__asm { __asm {
mov edx, destroy_p_proc mov edx, destroy_p_proc
mov eax, [esi+0x78] // pobj.sid mov eax, [esi + scriptId] // pobj.sid
call fo::funcoffs::exec_script_proc_ call fo::funcoffs::exec_script_proc_
xor edx, edx xor edx, edx
dec edx dec edx
@@ -777,9 +778,9 @@ static void __declspec(naked) action_explode_hack1() {
static void __declspec(naked) barter_attempt_transaction_hack() { static void __declspec(naked) barter_attempt_transaction_hack() {
__asm { __asm {
cmp dword ptr [eax+0x64], PID_ACTIVE_GEIGER_COUNTER cmp dword ptr [eax + protoId], PID_ACTIVE_GEIGER_COUNTER
je found je found
cmp dword ptr [eax+0x64], PID_ACTIVE_STEALTH_BOY cmp dword ptr [eax + protoId], PID_ACTIVE_STEALTH_BOY
je found je found
mov eax, 0x474D34 mov eax, 0x474D34
jmp eax jmp eax
@@ -812,10 +813,10 @@ static void __declspec(naked) combat_hack() {
skip: skip:
pop edx pop edx
xchg edx, eax // eax = source, edx = Max action points xchg edx, eax // eax = source, edx = Max action points
mov [eax+0x40], edx // pobj.curr_mp mov [eax + movePoints], edx // pobj.curr_mp
test byte ptr ds:[FO_VAR_combat_state], 1 // in combat? test byte ptr ds:[FO_VAR_combat_state], 1 // in combat?
jz end // No jz end // No
mov edx, [eax+0x68] // pobj.cid mov edx, [eax + cid] // pobj.cid
cmp edx, -1 cmp edx, -1
je end je end
push eax push eax
@@ -924,19 +925,19 @@ static void __declspec(naked) switch_hand_hack() {
mov eax, ds:[FO_VAR_inven_dude] mov eax, ds:[FO_VAR_inven_dude]
push eax push eax
mov [edi], ebp mov [edi], ebp
inc ecx inc ecx // if ecx == -1
jz skip jz skip
xor ebx, ebx xor ebx, ebx
inc ebx inc ebx
mov edx, ebp mov edx, ebp
call fo::funcoffs::item_remove_mult_ call fo::funcoffs::item_remove_mult_
skip: skip:
pop edx pop edx // _inven_dude
mov eax, ebp mov eax, ebp
call fo::funcoffs::item_get_type_ call fo::funcoffs::item_get_type_
cmp eax, item_type_container cmp eax, item_type_container
jne end jne end
mov [ebp+0x7C], edx // iobj.owner = _inven_dude mov [ebp + owner], edx // iobj.owner = _inven_dude
end: end:
pop ebp pop ebp
pop edi pop edi
@@ -949,7 +950,7 @@ static void __declspec(naked) inven_item_wearing() {
__asm { __asm {
mov esi, ds:[FO_VAR_inven_dude] mov esi, ds:[FO_VAR_inven_dude]
xchg ebx, eax // ebx = source xchg ebx, eax // ebx = source
mov eax, [esi+0x20] mov eax, [esi + artFid]
and eax, 0xF000000 and eax, 0xF000000
sar eax, 0x18 sar eax, 0x18
test eax, eax // check if object FID type flag is set to item 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_ call fo::funcoffs::obj_top_environment_
test eax, eax // has an owner? test eax, eax // has an owner?
jz skip // No jz skip // No
mov ecx, [eax+0x20] mov ecx, [eax + artFid]
and ecx, 0xF000000 and ecx, 0xF000000
sar ecx, 0x18 sar ecx, 0x18
cmp ecx, OBJ_TYPE_CRITTER // check if object FID type flag is set to critter 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 { __asm {
mov ebx, 0x42536B; mov ebx, 0x42536B;
je end; // This is a critter 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 jne end; // Yes
mov ebx, 0x425413; mov ebx, 0x425413;
end: end:
@@ -1112,18 +1113,41 @@ map_leave:
static void __declspec(naked) ai_move_steps_closer_hook() { static void __declspec(naked) ai_move_steps_closer_hook() {
__asm { __asm {
call fo::funcoffs::combat_turn_run_; 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; test dx, DAM_DEAD or DAM_KNOCKED_OUT or DAM_LOSE_TURN;
jz end; jz end;
mov [esi + 0x40], 0; // pobj.curr_mp (source reset ap) mov [esi + movePoints], 0; // pobj.curr_mp (source reset ap)
end: end:
retn; 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() void BugFixes::init()
{ {
#ifndef NDEBUG
if (isDebug && (GetConfigInt("Debugging", "BugFixes", 1) == 0)) return;
#endif
//if (GetConfigInt("Misc", "SharpshooterFix", 1)) { //if (GetConfigInt("Misc", "SharpshooterFix", 1)) {
dlog("Applying Sharpshooter patch.", DL_INIT); dlog("Applying Sharpshooter patch.", DL_INIT);
// http://www.nma-fallout.com/threads/fo2-engine-tweaks-sfall.178390/page-119#post-4050162 // 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 // 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 // in their AI packages is set to stay_close/charge, or NPCsTryToSpendExtraAP is enabled
HookCall(0x42A1A8, ai_move_steps_closer_hook); // 0x42B24D 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);
} }
} }
+1 -1
View File
@@ -85,7 +85,7 @@ void HookScriptClear();
void LoadHookScripts(); void LoadHookScripts();
extern DWORD initingHookScripts; extern DWORD initingHookScripts;
extern void __declspec() AmmoCostHookWrapper(); extern int __fastcall AmmoCostHook_Script(DWORD hookType, fo::GameObject* weapon, DWORD* rounds);
void _stdcall RunHookScriptsAtProc(DWORD procId); void _stdcall RunHookScriptsAtProc(DWORD procId);
} }
File diff suppressed because it is too large Load Diff
+52 -68
View File
@@ -1,4 +1,4 @@
/* /*
* sfall * sfall
* Copyright (C) 2011 Timeslip * Copyright (C) 2011 Timeslip
* *
@@ -37,23 +37,15 @@ static DWORD maxItemSize;
static DWORD reloadWeaponKey = 0; static DWORD reloadWeaponKey = 0;
static DWORD itemFastMoveKey = 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) { void InventoryKeyPressedHook(DWORD dxKey, bool pressed, DWORD vKey) {
// TODO: move this out into a script // TODO: move this out into a script
if (pressed && reloadWeaponKey && dxKey == reloadWeaponKey && IsMapLoaded() && (GetLoopFlags() & ~(COMBAT | PCOMBAT)) == 0) { if (pressed && reloadWeaponKey && dxKey == reloadWeaponKey && IsMapLoaded() && (GetLoopFlags() & ~(COMBAT | PCOMBAT)) == 0) {
DWORD maxAmmo, curAmmo; DWORD maxAmmo, curAmmo;
fo::GameObject* item = GetActiveItem(); fo::GameObject* item = fo::GetActiveItem();
maxAmmo = fo::func::item_w_max_ammo(item); maxAmmo = fo::func::item_w_max_ammo(item);
curAmmo = fo::func::item_w_curr_ammo(item); curAmmo = fo::func::item_w_curr_ammo(item);
if (maxAmmo != curAmmo) { if (maxAmmo != curAmmo) {
long &currentMode = GetActiveItemMode(); long &currentMode = fo::GetActiveItemMode();
long previusMode = currentMode; long previusMode = currentMode;
currentMode = 5; // reload mode currentMode = 5; // reload mode
fo::func::intface_use_item(); 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; static DWORD __fastcall add_check_for_item_ammo_cost(register fo::GameObject* weapon, DWORD hitMode) {
// adds check for weapons which require more than 1 ammo for single shot (super cattle prod & mega power fist) DWORD rounds = 1;
static void __declspec(naked) add_check_for_item_ammo_cost() { 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 { __asm {
push edx push edx;
push ebx push ebx;
sub esp, 4 push ecx; // weapon
call fo::funcoffs::item_w_curr_ammo_ mov edx, edi; // hitMode
mov ebx, eax call add_check_for_item_ammo_cost;
mov eax, ecx // weapon pop ecx;
mov edx, esp pop ebx;
mov dword ptr [esp], 1 pop edx;
pushad retn;
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
} }
} }
static const DWORD divide_burst_rounds_by_ammo_cost_back = 0x4234B9; static DWORD __fastcall divide_burst_rounds_by_ammo_cost(fo::GameObject* weapon, register DWORD currAmmo, DWORD burstRounds) {
static void __declspec(naked) divide_burst_rounds_by_ammo_cost() { 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 { __asm {
// ecx - current ammo, eax - burst rounds; need to set ebp push edx; // weapon
push edx push ecx; // current ammo in weapon
sub esp, 4 xchg ecx, edx;
mov ebp, eax push eax; // eax - rounds in burst attack, need to set ebp
mov eax, edx // weapon call divide_burst_rounds_by_ammo_cost;
mov dword ptr [esp], 1 mov ebp, eax; // overwriten code (в ebp устанавливаем правильное значение)
mov edx, esp // *rounds pop ecx;
pushad pop edx;
push 2 retn;
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
} }
} }
@@ -868,8 +851,9 @@ void Inventory::init() {
} }
if (GetConfigInt("Misc", "CheckWeaponAmmoCost", 0)) { if (GetConfigInt("Misc", "CheckWeaponAmmoCost", 0)) {
MakeJump(0x4266E9, add_check_for_item_ammo_cost); HookCall(0x4266E9, combat_check_bad_shot_hook);
MakeJump(0x4234B3, divide_burst_rounds_by_ammo_cost); MakeCall(0x4234B3, compute_spray_hack);
SafeWrite8(0x4234B8, 0x90);
} }
reloadWeaponKey = GetConfigInt("Input", "ReloadWeaponKey", 0); reloadWeaponKey = GetConfigInt("Input", "ReloadWeaponKey", 0);
+11 -16
View File
@@ -367,9 +367,9 @@ static void __declspec(naked) objCanSeeObj_ShootThru_Fix() {//(EAX *objStruct, E
static DWORD __fastcall GetWeaponSlotMode(DWORD itemPtr, DWORD mode) { static DWORD __fastcall GetWeaponSlotMode(DWORD itemPtr, DWORD mode) {
int slot = (mode > 0) ? 1 : 0; int slot = (mode > 0) ? 1 : 0;
auto itemButton = fo::var::itemButtonItems; fo::ItemButtonItem* itemButton = &fo::var::itemButtonItems[slot];
if ((DWORD)itemButton[slot].item == itemPtr) { if ((DWORD)itemButton->item == itemPtr) {
int slotMode = itemButton[slot].mode; int slotMode = itemButton->mode;
if (slotMode == 3 || slotMode == 4) { if (slotMode == 3 || slotMode == 4) {
mode++; mode++;
} }
@@ -407,7 +407,7 @@ static void __fastcall SwapHandSlots(fo::GameObject* item, DWORD* toSlot) {
memcpy(item, rightSlot, 0x14); memcpy(item, rightSlot, 0x14);
item[0].primaryAttack = fo::AttackType::ATKTYPE_LWEAPON_PRIMARY; item[0].primaryAttack = fo::AttackType::ATKTYPE_LWEAPON_PRIMARY;
item[0].secondaryAttack = fo::AttackType::ATKTYPE_LWEAPON_SECONDARY; item[0].secondaryAttack = fo::AttackType::ATKTYPE_LWEAPON_SECONDARY;
slot = leftSlot;// Rslot > Lslot; slot = leftSlot; // Rslot > Lslot
} else { } else {
memcpy(item, leftSlot, 0x14); memcpy(item, leftSlot, 0x14);
item[0].primaryAttack = fo::AttackType::ATKTYPE_RWEAPON_PRIMARY; 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); memcpy(slot, item, 0x14);
} else { // swap slot } else { // swap slot
fo::ItemButtonItem swapLBuf[1]; auto swapBuf = fo::var::itemButtonItems;
fo::ItemButtonItem swapRBuf[1]; 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(leftSlot, &swapBuf[1], 0x14); // buf Rslot > Lslot
memcpy(swapRBuf, rightSlot, 0x14); memcpy(rightSlot, &swapBuf[0], 0x14); // buf Lslot > Rslot
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;
} }
} }