mirror of
https://github.com/sfall-team/sfall.git
synced 2026-07-27 16:52:34 -07:00
Backported three more hooks from 4.1:
* USESKILLON, ONEXPLOSION, SETLIGHTING. Backported PartyMemberSkillFix option from 4.1. Updated documents.
This commit is contained in:
@@ -762,6 +762,13 @@ AIBestWeaponFix=1
|
||||
;chem_primary_desire also works as a priority list of drug items the NPC will try to pick up in combat when they are on the ground
|
||||
AIDrugUsePerfFix=0
|
||||
|
||||
;Set to 1 to fix the bug of using First Aid/Doctor skills when using them on the player
|
||||
;This will cause the party member to perform First Aid/Doctor skills when you use them on the player, but only if
|
||||
;the player is standing next to the party member
|
||||
;Note that because the related engine function is not fully implemented, enabling this option without a global script
|
||||
;that overrides First Aid/Doctor functions has very limited usefulness
|
||||
PartyMemberSkillFix=0
|
||||
|
||||
;Overrides the global variable number used to show the special death message of the Modoc toilet explosion
|
||||
;Set to -1 to disable the special death message when the global variable is set
|
||||
SpecialDeathGVAR=491
|
||||
|
||||
@@ -60,6 +60,10 @@
|
||||
#define HOOK_USEANIMOBJ (32)
|
||||
#define HOOK_EXPLOSIVETIMER (33)
|
||||
#define HOOK_DESCRIPTIONOBJ (34)
|
||||
#define HOOK_USESKILLON (35)
|
||||
#define HOOK_ONEXPLOSION (36)
|
||||
//#define HOOK_SUBCOMBATDAMAGE (37) // unimplemented
|
||||
#define HOOK_SETLIGHTING (38)
|
||||
#define HOOK_SNEAK (39)
|
||||
#define HOOK_STDPROCEDURE (40)
|
||||
#define HOOK_STDPROCEDURE_END (41)
|
||||
|
||||
@@ -679,6 +679,57 @@ int ret0 - a pointer to the new text received by using the get_string_pointe
|
||||
|
||||
-------------------------------------------
|
||||
|
||||
#### `HOOK_USESKILLON (hs_useskillon.int)`
|
||||
|
||||
Runs before using any skill on any object. Lets you override the critter that uses the skill.
|
||||
|
||||
__NOTE:__ The user critter can't be overridden when using Steal skill.
|
||||
|
||||
```
|
||||
Critter arg0 - the user critter (usually dude_obj)
|
||||
Obj arg1 - the target object/critter
|
||||
int arg2 - skill being used
|
||||
|
||||
int ret0 - a new critter to override the user critter. Pass -1 to cancel the skill use, pass 0 to skip this return value
|
||||
int ret1 - pass 1 to allow the skill to be used in combat (only for dude_obj or critter being controlled by the player)
|
||||
```
|
||||
|
||||
-------------------------------------------
|
||||
|
||||
#### `HOOK_ONEXPLOSION (hs_onexplosion.int)`
|
||||
|
||||
Runs when Fallout is checking all the tiles within the explosion radius for targets before an explosion occurs.\
|
||||
The tile checking will be interrupted when 6 additional targets (critters) are received.
|
||||
|
||||
```
|
||||
int arg0 - event type: 1 - when checking objects within the explosion radius without causing damage (e.g. the player drops an active explosive), 0 - otherwise
|
||||
Critter arg1 - the attacker
|
||||
int arg2 - the tile on which the explosion occurs
|
||||
int arg3 - checked tile within the explosion radius
|
||||
Obj arg4 - first found object on the checked tile as an additional target
|
||||
Critter arg5 - the target critter, may be 0 or equal to the attacker
|
||||
int arg6 - 1 when using throwing weapons (e.g. grenades), 0 otherwise
|
||||
|
||||
int ret0 - overrides the found object on the checked tile, pass 0 to skip the object
|
||||
```
|
||||
|
||||
-------------------------------------------
|
||||
|
||||
#### `HOOK_SETLIGHTING (hs_setlighting.int)`
|
||||
|
||||
Runs before setting the light level for an object or a map. You can override the result.
|
||||
|
||||
```
|
||||
Obj arg0 - the object being set, or -1 when setting the light level for a map
|
||||
int arg1 - the light intensity
|
||||
int arg2 - the light radius, or -1 when setting the light level for a map
|
||||
|
||||
int ret0 - overrides the light intensity. Intensity range is from 0 to 65536
|
||||
int ret1 - overrides the light radius. Radius range is from 0 to 8 (works only for the object)
|
||||
```
|
||||
|
||||
-------------------------------------------
|
||||
|
||||
#### `HOOK_SNEAK (hs_sneak.int)`
|
||||
|
||||
Runs when the Sneak skill is activated, or when the game rolls another Sneak check after the duration for the current one is over.\
|
||||
|
||||
@@ -42,6 +42,7 @@ FUNC(_word_wrap_, 0x4BC6F0)
|
||||
FUNC(action_get_an_object_, 0x412134)
|
||||
FUNC(action_loot_container_, 0x4123E8)
|
||||
FUNC(action_use_an_item_on_object_, 0x411F2C)
|
||||
FUNC(action_use_skill_on_, 0x41255C)
|
||||
FUNC(add_bar_box_, 0x4616F0)
|
||||
FUNC(adjust_ac_, 0x4715F8)
|
||||
FUNC(adjust_fid_, 0x4716E8)
|
||||
|
||||
@@ -97,10 +97,10 @@ static HooksInjectInfo injectHooks[] = {
|
||||
{HOOK_USEANIMOBJ, Inject_UseAnimateObjHook, false},
|
||||
{HOOK_EXPLOSIVETIMER, Inject_ExplosiveTimerHook, false},
|
||||
{HOOK_DESCRIPTIONOBJ, Inject_DescriptionObjHook, false},
|
||||
{HOOK_USESKILLON, Inject_UseSkillOnHook, false},
|
||||
{HOOK_ONEXPLOSION, Inject_OnExplosionHook, false},
|
||||
{-1, nullptr, false}, // dummy
|
||||
{-1, nullptr, false}, // dummy
|
||||
{-1, nullptr, false}, // dummy
|
||||
{-1, nullptr, false}, // dummy
|
||||
{HOOK_SETLIGHTING, Inject_SetLightingHook, false},
|
||||
{HOOK_SNEAK, Inject_SneakCheckHook, false},
|
||||
{HOOK_STDPROCEDURE, Inject_ScriptProcedureHook, false},
|
||||
{HOOK_STDPROCEDURE_END, Inject_ScriptProcedureHook2, false},
|
||||
|
||||
@@ -52,7 +52,7 @@ enum HookType
|
||||
HOOK_INVENTORYMOVE = 24,
|
||||
HOOK_INVENWIELD = 25,
|
||||
HOOK_ADJUSTFID = 26,
|
||||
HOOK_COMBATTURN = 27, // unimplemented
|
||||
// HOOK_COMBATTURN = 27, // unimplemented
|
||||
HOOK_CARTRAVEL = 28,
|
||||
HOOK_SETGLOBALVAR = 29,
|
||||
HOOK_RESTTIMER = 30,
|
||||
@@ -60,6 +60,10 @@ enum HookType
|
||||
HOOK_USEANIMOBJ = 32,
|
||||
HOOK_EXPLOSIVETIMER = 33,
|
||||
HOOK_DESCRIPTIONOBJ = 34,
|
||||
HOOK_USESKILLON = 35,
|
||||
HOOK_ONEXPLOSION = 36,
|
||||
// HOOK_SUBCOMBATDAMAGE = 37, // unimplemented
|
||||
HOOK_SETLIGHTING = 38,
|
||||
HOOK_SNEAK = 39,
|
||||
HOOK_STDPROCEDURE = 40,
|
||||
HOOK_STDPROCEDURE_END = 41,
|
||||
|
||||
@@ -317,6 +317,55 @@ skip:
|
||||
}
|
||||
}
|
||||
|
||||
fo::GameObject* __fastcall ComputeExplosionOnExtrasHook_Script(fo::GameObject* object, DWORD isCheck, DWORD checkTile, fo::ComputeAttackResult* ctdSource, DWORD isThrowing, fo::GameObject* who) {
|
||||
fo::GameObject* result = object;
|
||||
|
||||
BeginHook();
|
||||
argCount = 7;
|
||||
|
||||
args[0] = isCheck;
|
||||
args[1] = (DWORD)ctdSource->attacker;
|
||||
args[2] = ctdSource->targetTile;
|
||||
args[3] = checkTile;
|
||||
args[4] = (DWORD)object;
|
||||
args[5] = (DWORD)who;
|
||||
args[6] = isThrowing;
|
||||
|
||||
RunHookScript(HOOK_ONEXPLOSION);
|
||||
|
||||
if (cRet > 0) result = (fo::GameObject*)rets[0]; // override object
|
||||
|
||||
EndHook();
|
||||
return result;
|
||||
}
|
||||
|
||||
static void __declspec(naked) ComputeExplosionOnExtrasHook() {
|
||||
__asm {
|
||||
cmp dword ptr [esp + 0x34 + 4], 0x429533; // skip hook when AI assesses the situation in choosing the best weapon
|
||||
jz end;
|
||||
cmp dword ptr [esp + 0x34 + 4], 0x4296BC;
|
||||
jnz hook;
|
||||
end:
|
||||
jmp fo::funcoffs::obj_blocking_at_;
|
||||
hook:
|
||||
push ecx;
|
||||
push eax; // who (target/source)
|
||||
call fo::funcoffs::obj_blocking_at_;
|
||||
push [esp + 0x34 - 0x24 + 12]; // isThrowing
|
||||
xor edx, edx;
|
||||
cmp dword ptr [esp + 0x34 + 16], 0x412F11; // check called from action_explode_
|
||||
jz skip;
|
||||
mov edx, [esp + 0x34 - 0x34 + 16]; // isCheck (bypass damage)
|
||||
skip:
|
||||
push esi; // source ctd
|
||||
push edi; // check tile
|
||||
mov ecx, eax; // object
|
||||
call ComputeExplosionOnExtrasHook_Script; // edx - isCheck
|
||||
pop ecx;
|
||||
retn;
|
||||
}
|
||||
}
|
||||
|
||||
static DWORD targetRet = 0;
|
||||
static bool targetObjHookHasRet = false;
|
||||
|
||||
@@ -441,6 +490,10 @@ void Inject_AmmoCostHook() {
|
||||
HookCall(0x423A7C, AmmoCostHook); // compute_attack_
|
||||
}
|
||||
|
||||
void Inject_OnExplosionHook() {
|
||||
HookCall(0x423D70, ComputeExplosionOnExtrasHook);
|
||||
}
|
||||
|
||||
void Inject_TargetObjectHook() {
|
||||
MakeCall(0x44BB16, gmouse_bk_process_hook, 1);
|
||||
SafeWrite8(0x44BB00, 0x15);
|
||||
@@ -457,6 +510,7 @@ void InitCombatHookScripts() {
|
||||
HookScripts::LoadHookScript("hs_findtarget", HOOK_FINDTARGET);
|
||||
HookScripts::LoadHookScript("hs_itemdamage", HOOK_ITEMDAMAGE);
|
||||
HookScripts::LoadHookScript("hs_ammocost", HOOK_AMMOCOST);
|
||||
HookScripts::LoadHookScript("hs_onexplosion", HOOK_ONEXPLOSION);
|
||||
HookScripts::LoadHookScript("hs_targetobject", HOOK_TARGETOBJECT);
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ void Inject_CombatDamageHook();
|
||||
void Inject_FindTargetHook();
|
||||
void Inject_ItemDamageHook();
|
||||
void Inject_AmmoCostHook();
|
||||
void Inject_OnExplosionHook();
|
||||
void Inject_TargetObjectHook();
|
||||
|
||||
int __fastcall AmmoCostHook_Script(DWORD hookType, fo::GameObject* weapon, DWORD &rounds);
|
||||
|
||||
@@ -67,7 +67,7 @@ static bool CheckRecursiveHooks(DWORD hook) {
|
||||
if (hook == currentRunHook) {
|
||||
switch (hook) {
|
||||
case HOOK_SETGLOBALVAR:
|
||||
// case HOOK_SETLIGHTING:
|
||||
case HOOK_SETLIGHTING:
|
||||
return true;
|
||||
default:
|
||||
if (isDebug) fo::func::debug_printf("\nWARNING: A recursive hook with ID %d was running.", hook);
|
||||
|
||||
@@ -94,6 +94,86 @@ static void __declspec(naked) OverrideCost_BarterPriceHook() {
|
||||
}
|
||||
}
|
||||
|
||||
static fo::GameObject* sourceSkillOn = nullptr;
|
||||
void SourceUseSkillOnInit() { sourceSkillOn = *fo::ptr::obj_dude; }
|
||||
|
||||
static char resultSkillOn; // -1 - cancel handler, 1 - replace user
|
||||
static long bakupCombatState;
|
||||
|
||||
static void __fastcall UseSkillOnHook_Script(DWORD source, DWORD target, register DWORD skillId) {
|
||||
BeginHook();
|
||||
argCount = 3;
|
||||
|
||||
args[0] = source; // user
|
||||
args[1] = target; // target
|
||||
args[2] = skillId; // skill id
|
||||
|
||||
sourceSkillOn = *fo::ptr::obj_dude;
|
||||
resultSkillOn = 0;
|
||||
bakupCombatState = -1;
|
||||
|
||||
RunHookScript(HOOK_USESKILLON);
|
||||
|
||||
if (skillId != fo::Skill::SKILL_STEAL && cRet > 0) { // not work for steal skill
|
||||
if (rets[0] != 0) {
|
||||
resultSkillOn = (rets[0] == -1) ? -1 : 1;
|
||||
if (resultSkillOn == 1) {
|
||||
sourceSkillOn = (fo::GameObject*)rets[0];
|
||||
}
|
||||
}
|
||||
if (resultSkillOn != -1 && cRet > 1 && rets[1] == 1) {
|
||||
bakupCombatState = *fo::ptr::combat_state;
|
||||
*fo::ptr::combat_state = 0;
|
||||
}
|
||||
}
|
||||
EndHook();
|
||||
}
|
||||
|
||||
static void __declspec(naked) UseSkillOnHook() {
|
||||
__asm {
|
||||
push eax;
|
||||
push ecx;
|
||||
push edx;
|
||||
push ebx; // skill id
|
||||
mov ecx, eax; // user
|
||||
call UseSkillOnHook_Script; // edx - target
|
||||
pop edx;
|
||||
pop ecx;
|
||||
pop eax;
|
||||
cmp resultSkillOn, -1; // skip handler
|
||||
jnz handler;
|
||||
retn;
|
||||
handler:
|
||||
jmp fo::funcoffs::action_use_skill_on_;
|
||||
}
|
||||
}
|
||||
|
||||
static void __declspec(naked) UseSkillOnHack() {
|
||||
__asm {
|
||||
cmp bakupCombatState, -1;
|
||||
jz skip;
|
||||
mov ebp, bakupCombatState;
|
||||
mov dword ptr ds:[FO_VAR_combat_state], ebp;
|
||||
skip:
|
||||
cmp resultSkillOn, 0;
|
||||
jz default;
|
||||
mov edi, sourceSkillOn; // skill user (override from hook)
|
||||
retn; // flag ZF = 0
|
||||
default:
|
||||
// engine code
|
||||
cmp eax, dword ptr ds:[FO_VAR_obj_dude];
|
||||
retn;
|
||||
}
|
||||
}
|
||||
|
||||
static void __declspec(naked) skill_use_hack() {
|
||||
__asm {
|
||||
cmp ebp, dword ptr ds:[FO_VAR_obj_dude];
|
||||
setnz al;
|
||||
retn;
|
||||
}
|
||||
}
|
||||
|
||||
static void __declspec(naked) UseSkillHook() {
|
||||
__asm {
|
||||
HookBegin;
|
||||
@@ -610,6 +690,21 @@ void Inject_ExplosiveTimerHook() {
|
||||
HookCall(0x49BDC4, ExplosiveTimerHook);
|
||||
}
|
||||
|
||||
void Inject_UseSkillOnHook() {
|
||||
const DWORD useSkillOnHkAddr[] = {0x44C3CA, 0x44C81C};
|
||||
HookCalls(UseSkillOnHook, useSkillOnHkAddr);
|
||||
MakeCall(0x4127BA, UseSkillOnHack, 1);
|
||||
const DWORD skillUseTgtAddr[] = {0x4AB05D, 0x4AB558, 0x4ABA60};
|
||||
MakeCalls(skill_use_hack, skillUseTgtAddr); // fix checking obj_dude's target
|
||||
|
||||
// replace _obj_dude with source skill user (skill_use_ function)
|
||||
const DWORD skillUseSrcAddr[] = {
|
||||
0x4AAF47, 0x4AB051, 0x4AB3FB, 0x4AB550, 0x4AB8FA, 0x4ABA54,
|
||||
0x4AB0EF, 0x4AB5C0, 0x4ABAF2 // fix for time increment
|
||||
};
|
||||
SafeWriteBatch<DWORD>((DWORD)&sourceSkillOn, skillUseSrcAddr);
|
||||
}
|
||||
|
||||
void Inject_EncounterHook() {
|
||||
HookCall(0x4C02AF, wmWorldMap_hook);
|
||||
HookCall(0x4C095C, wmRndEncounterOccurred_hook);
|
||||
@@ -617,6 +712,7 @@ void Inject_EncounterHook() {
|
||||
|
||||
void InitMiscHookScripts() {
|
||||
HookScripts::LoadHookScript("hs_barterprice", HOOK_BARTERPRICE);
|
||||
HookScripts::LoadHookScript("hs_useskillon", HOOK_USESKILLON);
|
||||
HookScripts::LoadHookScript("hs_useskill", HOOK_USESKILL);
|
||||
HookScripts::LoadHookScript("hs_steal", HOOK_STEAL);
|
||||
HookScripts::LoadHookScript("hs_sneak", HOOK_SNEAK);
|
||||
|
||||
@@ -4,8 +4,10 @@ namespace sfall
|
||||
{
|
||||
|
||||
void InitMiscHookScripts();
|
||||
void SourceUseSkillOnInit();
|
||||
|
||||
void Inject_BarterPriceHook();
|
||||
void Inject_UseSkillOnHook();
|
||||
void Inject_UseSkillHook();
|
||||
void Inject_StealCheckHook();
|
||||
void Inject_SneakCheckHook();
|
||||
|
||||
@@ -185,6 +185,72 @@ skip:
|
||||
}
|
||||
}
|
||||
|
||||
static bool __fastcall SetLightingHook_Script(DWORD &intensity, DWORD &radius, DWORD object) {
|
||||
BeginHook();
|
||||
argCount = 3;
|
||||
|
||||
args[0] = object;
|
||||
args[1] = intensity;
|
||||
args[2] = radius;
|
||||
RunHookScript(HOOK_SETLIGHTING);
|
||||
|
||||
bool result = false;
|
||||
if (cRet > 0) {
|
||||
int light = rets[0];
|
||||
if (light < 0) light = 0;
|
||||
intensity = light;
|
||||
if (cRet > 1 && object != -1) {
|
||||
int dist = rets[1];
|
||||
if (dist < 0) dist = 0;
|
||||
radius = dist;
|
||||
}
|
||||
result = true;
|
||||
}
|
||||
EndHook();
|
||||
return result;
|
||||
}
|
||||
|
||||
static void __declspec(naked) SetObjectLightHook() {
|
||||
__asm {
|
||||
pushadc;
|
||||
push ebp;
|
||||
mov edx, esp; // &radius
|
||||
push ebx;
|
||||
mov ecx, esp; // &intensity
|
||||
push esi; // object
|
||||
call SetLightingHook_Script;
|
||||
test al, al;
|
||||
jz skip;
|
||||
pop ebx; // return intensity value
|
||||
pop ebp; // return radius value
|
||||
jmp end;
|
||||
skip:
|
||||
add esp, 8;
|
||||
end:
|
||||
popadc;
|
||||
jmp fo::funcoffs::obj_turn_off_light_;
|
||||
}
|
||||
}
|
||||
|
||||
static void __declspec(naked) SetMapLightHook() {
|
||||
__asm {
|
||||
push ecx;
|
||||
push ebx;
|
||||
mov ecx, esp; // &intensity
|
||||
push -1; // no object (it's a map)
|
||||
mov edx, esp; // no radius
|
||||
call SetLightingHook_Script;
|
||||
add esp, 4;
|
||||
test al, al;
|
||||
jz skip;
|
||||
mov ebx, [esp - 4]; // return intensity value
|
||||
skip:
|
||||
pop ecx;
|
||||
cmp ebx, dword ptr ds:[0x47A93D]; // get miminal ambient light intensity (16384)
|
||||
retn;
|
||||
}
|
||||
}
|
||||
|
||||
static DWORD __fastcall StdProcedureHook_Script(long numHandler, fo::ScriptInstance* script, DWORD procTable) {
|
||||
BeginHook();
|
||||
argCount = 4;
|
||||
@@ -282,6 +348,11 @@ void Inject_DescriptionObjHook() {
|
||||
HookCall(0x49AE28, DescriptionObjHook);
|
||||
}
|
||||
|
||||
void Inject_SetLightingHook() {
|
||||
HookCall(0x48ACA0, SetObjectLightHook);
|
||||
MakeCall(0x47A934, SetMapLightHook, 1);
|
||||
}
|
||||
|
||||
void Inject_ScriptProcedureHook() {
|
||||
MakeCall(0x4A491F, ScriptStdProcedureHook);
|
||||
}
|
||||
@@ -293,7 +364,9 @@ void Inject_ScriptProcedureHook2() {
|
||||
void InitObjectHookScripts() {
|
||||
HookScripts::LoadHookScript("hs_useobjon", HOOK_USEOBJON);
|
||||
HookScripts::LoadHookScript("hs_useobj", HOOK_USEOBJ);
|
||||
HookScripts::LoadHookScript("hs_useanimobj", HOOK_USEANIMOBJ);
|
||||
HookScripts::LoadHookScript("hs_descriptionobj", HOOK_DESCRIPTIONOBJ);
|
||||
HookScripts::LoadHookScript("hs_setlighting", HOOK_SETLIGHTING);
|
||||
HookScripts::LoadHookScript("hs_stdprocedure", HOOK_STDPROCEDURE); // combo hook
|
||||
HookScripts::LoadHookScript("hs_stdprocedure", HOOK_STDPROCEDURE_END);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ void Inject_UseObjOnHook();
|
||||
void Inject_UseObjHook();
|
||||
void Inject_UseAnimateObjHook();
|
||||
void Inject_DescriptionObjHook();
|
||||
void Inject_SetLightingHook();
|
||||
void Inject_ScriptProcedureHook();
|
||||
void Inject_ScriptProcedureHook2();
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#include "Graphics.h"
|
||||
#include "HeroAppearance.h"
|
||||
#include "HookScripts\Common.h"
|
||||
#include "HookScripts\MiscHs.h"
|
||||
#include "Interface.h"
|
||||
#include "Inventory.h"
|
||||
#include "LoadOrder.h"
|
||||
@@ -141,6 +142,7 @@ static void __stdcall RunOnBeforeGameStart() {
|
||||
static void __stdcall RunOnAfterGameStarted() {
|
||||
if (femaleMsgs) CheckPlayerGender();
|
||||
if (disableHorrigan) *fo::ptr::Meet_Frank_Horrigan = true;
|
||||
SourceUseSkillOnInit();
|
||||
InitGlobalScripts(); // running sfall scripts
|
||||
}
|
||||
|
||||
|
||||
@@ -261,8 +261,8 @@ static void __fastcall SwapHandSlots(fo::GameObject* item, fo::GameObject* &toSl
|
||||
} else { // swap slots
|
||||
fo::ItemButtonItem hands[2];
|
||||
std::memcpy(hands, fo::ptr::itemButtonItems, sizeof(fo::ItemButtonItem) * 2);
|
||||
hands[fo::HandSlot::Left].primaryAttack = fo::AttackType::ATKTYPE_RWEAPON_PRIMARY;
|
||||
hands[fo::HandSlot::Left].secondaryAttack = fo::AttackType::ATKTYPE_RWEAPON_SECONDARY;
|
||||
hands[fo::HandSlot::Left].primaryAttack = fo::AttackType::ATKTYPE_RWEAPON_PRIMARY;
|
||||
hands[fo::HandSlot::Left].secondaryAttack = fo::AttackType::ATKTYPE_RWEAPON_SECONDARY;
|
||||
hands[fo::HandSlot::Right].primaryAttack = fo::AttackType::ATKTYPE_LWEAPON_PRIMARY;
|
||||
hands[fo::HandSlot::Right].secondaryAttack = fo::AttackType::ATKTYPE_LWEAPON_SECONDARY;
|
||||
|
||||
@@ -332,6 +332,24 @@ static void __declspec(naked) intface_update_items_hack_end() {
|
||||
}
|
||||
}
|
||||
|
||||
static void __declspec(naked) action_use_skill_on_hook() {
|
||||
__asm { // eax = dude_obj, edx = target, ebp = party_member
|
||||
cmp eax, edx;
|
||||
jnz end; // jump if target != dude_obj
|
||||
mov edx, ebp;
|
||||
call fo::funcoffs::obj_dist_; // check distance between dude_obj and party_member
|
||||
cmp eax, 1; // if the distance is greater than 1, then reset the register
|
||||
jg skip;
|
||||
inc eax;
|
||||
retn;
|
||||
skip:
|
||||
xor eax, eax;
|
||||
retn;
|
||||
end:
|
||||
jmp fo::funcoffs::obj_dist_;
|
||||
}
|
||||
}
|
||||
|
||||
static void __declspec(naked) endgame_movie_hook() {
|
||||
__asm {
|
||||
cmp [esp + 16], 0x45C563; // call from op_endgame_movie_
|
||||
@@ -714,6 +732,21 @@ static void KeepSelectModePatch() {
|
||||
//}
|
||||
}
|
||||
|
||||
static void PartyMemberSkillPatch() {
|
||||
// Fix getting distance from source to target when using skills
|
||||
// Note: this will cause the party member to perform First Aid/Doctor skills when you use them on the player, but only if
|
||||
// the player is standing next to the party member. Because the related engine function is not fully implemented, enabling
|
||||
// this option without a global script that overrides First Aid/Doctor functions has very limited usefulness
|
||||
if (IniReader::GetConfigInt("Misc", "PartyMemberSkillFix", 0)) {
|
||||
dlog("Applying First Aid/Doctor skill use patch for party members.", DL_INIT);
|
||||
HookCall(0x412836, action_use_skill_on_hook);
|
||||
dlogr(" Done", DL_INIT);
|
||||
}
|
||||
// Small code patch for HOOK_USESKILLON (change obj_dude to source)
|
||||
SafeWrite32(0x4128F3, 0x90909090);
|
||||
SafeWrite16(0x4128F7, 0xFE39); // cmp esi, _obj_dude -> cmp esi, edi
|
||||
}
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct CodeData {
|
||||
DWORD dd;
|
||||
@@ -1085,6 +1118,8 @@ void MiscPatches::init() {
|
||||
DisplaySecondWeaponRangePatch();
|
||||
KeepSelectModePatch();
|
||||
|
||||
PartyMemberSkillPatch();
|
||||
|
||||
SkipLoadingGameSettingsPatch();
|
||||
|
||||
UseWalkDistancePatch();
|
||||
|
||||
Reference in New Issue
Block a user