mirror of
https://github.com/sfall-team/sfall.git
synced 2026-07-27 16:52:34 -07:00
Backported three game hooks from 4.x:
* ROLLCHECK, BESTWEAPON, CANUSEWEAPON Updated documents.
This commit is contained in:
+1
-1
@@ -131,7 +131,7 @@ OverrideMusicDir=1
|
||||
;This will slightly increase the startup time of the game on older computers
|
||||
AutoSearchSFX=1
|
||||
|
||||
;Uncomment these lines to override the names of sound files for game modes
|
||||
;Uncomment these lines to override the names of sound files used by the engine
|
||||
;Filenames are limited to 8 characters (without extension)
|
||||
;MainMenuMusic=07desert
|
||||
;WorldMapMusic=23world
|
||||
|
||||
@@ -70,6 +70,11 @@
|
||||
#define HOOK_STDPROCEDURE_END (41)
|
||||
#define HOOK_TARGETOBJECT (42)
|
||||
#define HOOK_ENCOUNTER (43)
|
||||
//#define HOOK_ADJUSTPOISON (44) // unimplemented
|
||||
//#define HOOK_ADJUSTRADS (45) // unimplemented
|
||||
#define HOOK_ROLLCHECK (46)
|
||||
#define HOOK_BESTWEAPON (47)
|
||||
#define HOOK_CANUSEWEAPON (48)
|
||||
|
||||
// Valid arguments to list_begin
|
||||
#define LIST_CRITTERS (0)
|
||||
@@ -261,6 +266,7 @@
|
||||
#define check_pid(pid) (get_proto_data(pid, 0) != -1)
|
||||
|
||||
// sets the status of an unusable weapon that cannot be used in combat
|
||||
// use the HOOK_CANUSEWEAPON hook with weapon_is_unusable macro to override the engine value
|
||||
#define set_weapon_unusable(item) set_object_data(item, OBJ_DATA_MISC_FLAGS, get_object_data(item, OBJ_DATA_MISC_FLAGS) bwor 0x00000010)
|
||||
#define set_weapon_usable(item) set_object_data(item, OBJ_DATA_MISC_FLAGS, get_object_data(item, OBJ_DATA_MISC_FLAGS) bwand 0xFFFFFFEF)
|
||||
#define weapon_is_unusable(item) (get_object_data(item, OBJ_DATA_MISC_FLAGS) bwand 0x00000010)
|
||||
|
||||
@@ -795,3 +795,62 @@ int arg2 - 1 when the encounter occurs is a special encounter, 0 otherwise
|
||||
int ret0 - overrides the map ID, or pass -1 for event type 0 to cancel the encounter and continue traveling
|
||||
int ret1 - pass 1 to cancel the encounter and load the specified map from the ret0 (only for event type 0)
|
||||
```
|
||||
|
||||
-------------------------------------------
|
||||
|
||||
#### `HOOK_ROLLCHECK (hs_rollcheck.int)`
|
||||
|
||||
Runs when a game event performs a random roll to check the chance of success or failure.
|
||||
|
||||
```
|
||||
int arg0 - event type:
|
||||
1 - checks the chance of an attack hitting the target
|
||||
2 - checks the chance of a bullet from a burst hitting the target (for burst attacks)
|
||||
3 - checks the chance when using skills (not listed below)
|
||||
4 - check the chance of using Repair skill
|
||||
5 - check the chance of using Doctor skill
|
||||
6 - check the chance of using Steal skill for the thief (usually the player)
|
||||
7 - the second Steal skill chance check for the target to catch the thief, in which the target's failure is the thief's success result
|
||||
int arg1 - the value of roll result (see ROLL_* constants), which is calculated as:
|
||||
for ROLL_CRITICAL_SUCCESS: random(1, 100) <= (random_chance / 10) + bonus
|
||||
for ROLL_CRITICAL_FAILURE: random(1, 100) <= -random_chance / 10
|
||||
int arg2 - the chance value
|
||||
int arg3 - the bonus value, used when checking critical success
|
||||
int arg4 - random chance, calculated as: (chance - random(1, 100)), where a negative value is a failure check (ROLL_FAILURE)
|
||||
|
||||
int ret0 - overrides the roll result
|
||||
```
|
||||
|
||||
-------------------------------------------
|
||||
|
||||
#### `HOOK_BESTWEAPON (hs_bestweapon.int)`
|
||||
|
||||
Runs when the AI decides which weapon is the best while searching the inventory for a weapon to equip in combat.\
|
||||
This also runs when the player presses the "Use Best Weapon" button on the party member control panel.
|
||||
|
||||
```
|
||||
Critter arg0 - the critter searching for a weapon
|
||||
Item arg1 - the best weapon chosen from two items
|
||||
Item arg2 - the first choice of weapon
|
||||
Item arg3 - the second choice of weapon
|
||||
Critter arg4 - the target of the critter (can be 0)
|
||||
|
||||
Item ret0 - overrides the chosen best weapon
|
||||
```
|
||||
|
||||
-------------------------------------------
|
||||
|
||||
#### `HOOK_CANUSEWEAPON (hs_canuseweapon.int)`
|
||||
|
||||
Run when the AI checks whether it can use a weapon, or when the game checks whether the player can use an item (weapon) in hand slot.\
|
||||
For AI, this mostly happens when NPCs try to find weapons in their inventory or on the map.\
|
||||
For the player, this happens when the game updates the item data for active item slots on the interface bar.
|
||||
|
||||
```
|
||||
Critter arg0 - the critter doing the check
|
||||
Item arg1 - the item being checked
|
||||
int arg2 - attack type (see ATKTYPE_* constants), or -1 for dude_obj
|
||||
int arg3 - original result of engine function: 1 - can use, 0 - cannot use
|
||||
|
||||
int ret0 - overrides the result of engine function. Any non-zero value allows using the weapon
|
||||
```
|
||||
|
||||
@@ -47,7 +47,8 @@ FUNC(action_use_skill_on_, 0x41255C)
|
||||
FUNC(add_bar_box_, 0x4616F0)
|
||||
FUNC(adjust_ac_, 0x4715F8)
|
||||
FUNC(adjust_fid_, 0x4716E8)
|
||||
FUNC(ai_can_use_weapon_, 0x4298EC) // (GameObject *aCritter<eax>, int aWeapon<edx>, int a2Or3<ebx>) returns 1 or 0
|
||||
FUNC(ai_best_weapon_, 0x4293BC)
|
||||
FUNC(ai_can_use_weapon_, 0x4298EC) // (fo::GameObject *aCritter<eax>, int aWeapon<edx>, int a2Or3<ebx>) returns 1 or 0
|
||||
FUNC(ai_cap_, 0x4280B4)
|
||||
FUNC(ai_check_drugs_, 0x428480)
|
||||
FUNC(ai_danger_source_, 0x428F4C)
|
||||
@@ -630,6 +631,8 @@ FUNC(register_object_turn_towards_, 0x414C50) // int aObj<eax>, int aTile<edx>
|
||||
FUNC(remove_bk_process_, 0x4C8DC4)
|
||||
FUNC(report_explosion_, 0x413144)
|
||||
FUNC(reset_box_bar_win_, 0x4614A0)
|
||||
FUNC(roll_check_, 0x4A3000)
|
||||
FUNC(roll_check_critical_, 0x4A3030)
|
||||
FUNC(roll_random_, 0x4A30C0)
|
||||
FUNC(runProgram_, 0x46E154) // eax - programPtr, called once for each program after first loaded - hooks program to game and UI events
|
||||
FUNC(scr_build_lookup_table_, 0x4A49D0)
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
changed to stdcall wrappers instead for stability
|
||||
*/
|
||||
WRAP_WATCOM_FUNC4(long, _word_wrap, const char*, text, int, maxWidth, DWORD*, buf, BYTE*, count)
|
||||
WRAP_WATCOM_FUNC3(bool, ai_can_use_weapon, fo::GameObject*, critter, fo::GameObject*, item, DWORD, hitMode)
|
||||
WRAP_WATCOM_FUNC3(long, ai_have_ammo, fo::GameObject*, critter, fo::GameObject*, item, fo::GameObject**, outAmmo)
|
||||
WRAP_WATCOM_FUNC3(long, ai_pick_hit_mode, fo::GameObject*, source, fo::GameObject*, item, fo::GameObject*, target)
|
||||
WRAP_WATCOM_FUNC3(long, ai_magic_hands, fo::GameObject*, source, fo::GameObject*, object, long, msgNumber)
|
||||
|
||||
@@ -19,7 +19,13 @@ namespace game
|
||||
namespace sf = sfall;
|
||||
|
||||
static const long aiUseItemAPCost = 2;
|
||||
|
||||
/*
|
||||
// Implementation of ai_can_use_weapon_ engine function with the HOOK_CANUSEWEAPON hook
|
||||
bool CombatAI::ai_can_use_weapon(fo::GameObject* source, fo::GameObject* weapon, long hitMode) {
|
||||
bool result = fo::func::ai_can_use_weapon(source, weapon, hitMode);
|
||||
return sf::CanUseWeaponHook_Invoke(result, source, weapon, hitMode);
|
||||
}
|
||||
*/
|
||||
static long drugUsePerfFixMode;
|
||||
|
||||
void __stdcall CombatAI::ai_check_drugs(fo::GameObject* source) {
|
||||
|
||||
@@ -15,6 +15,8 @@ class CombatAI {
|
||||
public:
|
||||
static void init();
|
||||
|
||||
//static bool ai_can_use_weapon(fo::GameObject* source, fo::GameObject* weapon, long hitMode);
|
||||
|
||||
static void __stdcall ai_check_drugs(fo::GameObject* source);
|
||||
};
|
||||
|
||||
|
||||
@@ -106,6 +106,11 @@ static HooksInjectInfo injectHooks[] = {
|
||||
{HOOK_STDPROCEDURE_END, Inject_ScriptProcedureHook2, false},
|
||||
{HOOK_TARGETOBJECT, Inject_TargetObjectHook, false},
|
||||
{HOOK_ENCOUNTER, Inject_EncounterHook, false},
|
||||
{-1, nullptr, false}, // dummy
|
||||
{-1, nullptr, false}, // dummy
|
||||
{HOOK_ROLLCHECK, Inject_RollCheckHook, false},
|
||||
{HOOK_BESTWEAPON, Inject_BestWeaponHook, false},
|
||||
{HOOK_CANUSEWEAPON, Inject_CanUseWeaponHook, false},
|
||||
};
|
||||
|
||||
void HookScripts::InjectingHook(int hookId) {
|
||||
|
||||
@@ -69,6 +69,11 @@ enum HookType
|
||||
HOOK_STDPROCEDURE_END = 41,
|
||||
HOOK_TARGETOBJECT = 42,
|
||||
HOOK_ENCOUNTER = 43,
|
||||
// HOOK_ADJUSTPOISON = 44, // unimplemented
|
||||
// HOOK_ADJUSTRADS = 45, // unimplemented
|
||||
HOOK_ROLLCHECK = 46,
|
||||
HOOK_BESTWEAPON = 47,
|
||||
HOOK_CANUSEWEAPON = 48,
|
||||
HOOK_COUNT
|
||||
};
|
||||
|
||||
|
||||
@@ -228,7 +228,11 @@ static void __fastcall FindTargetHook_Script(DWORD* target, fo::GameObject* atta
|
||||
}
|
||||
EndHook();
|
||||
}
|
||||
|
||||
/*
|
||||
void FindTargetHook_Invoke(fo::GameObject* targets[], fo::GameObject* attacker) {
|
||||
if (HookScripts::HookHasScript(HOOK_FINDTARGET)) FindTargetHook_Script((DWORD*)targets, attacker);
|
||||
}
|
||||
*/
|
||||
static void __declspec(naked) FindTargetHook() {
|
||||
__asm {
|
||||
push eax;
|
||||
@@ -426,6 +430,99 @@ default:
|
||||
}
|
||||
}
|
||||
|
||||
static fo::GameObject* __stdcall BestWeaponHook_Script(fo::GameObject* bestWeapon, fo::GameObject* source, fo::GameObject* weapon1, fo::GameObject* weapon2, fo::GameObject* target) {
|
||||
BeginHook();
|
||||
argCount = 5;
|
||||
|
||||
args[0] = (DWORD)source;
|
||||
args[1] = (DWORD)bestWeapon;
|
||||
args[2] = (DWORD)weapon1;
|
||||
args[3] = (DWORD)weapon2;
|
||||
args[4] = (DWORD)target;
|
||||
|
||||
RunHookScript(HOOK_BESTWEAPON);
|
||||
|
||||
if (cRet > 0) bestWeapon = (fo::GameObject*)rets[0];
|
||||
|
||||
EndHook();
|
||||
return bestWeapon;
|
||||
}
|
||||
|
||||
static void __declspec(naked) ai_search_inven_weap_hook() {
|
||||
__asm {
|
||||
push ecx; // target
|
||||
push ebx; // weapon2 (secondary)
|
||||
push edx; // weapon1 (primary)
|
||||
push eax; // source
|
||||
call fo::funcoffs::ai_best_weapon_;
|
||||
push eax; // bestWeapon
|
||||
call BestWeaponHook_Script;
|
||||
retn;
|
||||
}
|
||||
}
|
||||
/*
|
||||
fo::GameObject* BestWeaponHook_Invoke(fo::GameObject* bestWeapon, fo::GameObject* source, fo::GameObject* weapon1, fo::GameObject* weapon2, fo::GameObject* target) {
|
||||
return (HookScripts::HookHasScript(HOOK_BESTWEAPON))
|
||||
? BestWeaponHook_Script(bestWeapon, source, weapon1, weapon2, target)
|
||||
: bestWeapon;
|
||||
}
|
||||
*/
|
||||
static bool __stdcall CanUseWeaponHook_Script(bool result, fo::GameObject* source, fo::GameObject* weapon, long hitMode) {
|
||||
BeginHook();
|
||||
argCount = 4;
|
||||
|
||||
args[0] = (DWORD)source;
|
||||
args[1] = (DWORD)weapon;
|
||||
args[2] = hitMode;
|
||||
args[3] = result;
|
||||
|
||||
RunHookScript(HOOK_CANUSEWEAPON);
|
||||
|
||||
if (cRet > 0) result = (rets[0]) ? true : false;
|
||||
|
||||
EndHook();
|
||||
return result; // only 0 and 1
|
||||
}
|
||||
|
||||
static void __declspec(naked) AICanUseWeaponHook() {
|
||||
__asm {
|
||||
push ecx;
|
||||
push ebx; // hitMode
|
||||
push edx; // weapon
|
||||
push eax; // source
|
||||
call fo::funcoffs::ai_can_use_weapon_;
|
||||
push eax; // result
|
||||
call CanUseWeaponHook_Script;
|
||||
//and eax, 1;
|
||||
pop ecx;
|
||||
retn;
|
||||
}
|
||||
}
|
||||
|
||||
static void __declspec(naked) CanUseWeaponHook() {
|
||||
__asm {
|
||||
push ecx;
|
||||
push edx;
|
||||
push -1; // hitMode (indefinite)
|
||||
push eax; // weapon
|
||||
push ds:[FO_VAR_obj_dude]; // source
|
||||
call fo::funcoffs::can_use_weapon_; // 1 - can't use
|
||||
xor al, 1;
|
||||
push eax; // result
|
||||
call CanUseWeaponHook_Script;
|
||||
xor al, 1; // invert
|
||||
pop edx;
|
||||
pop ecx;
|
||||
retn;
|
||||
}
|
||||
}
|
||||
/*
|
||||
bool CanUseWeaponHook_Invoke(bool result, fo::GameObject* source, fo::GameObject* weapon, long hitMode) {
|
||||
return (HookScripts::HookHasScript(HOOK_CANUSEWEAPON))
|
||||
? CanUseWeaponHook_Script(result, source, weapon, hitMode)
|
||||
: result;
|
||||
}
|
||||
*/
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void Inject_ToHitHook() {
|
||||
@@ -502,6 +599,21 @@ void Inject_TargetObjectHook() {
|
||||
SafeWrite8(0x44C26E, 0x17);
|
||||
}
|
||||
|
||||
void Inject_BestWeaponHook() {
|
||||
HookCall(0x429A59, ai_search_inven_weap_hook);
|
||||
}
|
||||
|
||||
void Inject_CanUseWeaponHook() {
|
||||
const DWORD aiCanUseWpnHkAddr[] = {
|
||||
0x429A1B, // ai_search_inven_weap_
|
||||
0x429CF2, // ai_search_environ_
|
||||
0x429E1C // ai_pick_hit_mode_
|
||||
};
|
||||
HookCalls(AICanUseWeaponHook, aiCanUseWpnHkAddr);
|
||||
const DWORD canUseWpnHkAddr[] = {0x45F05E, 0x45F1C1, 0x45F203, 0x45F36A}; // intface_update_items_
|
||||
HookCalls(CanUseWeaponHook, canUseWpnHkAddr);
|
||||
}
|
||||
|
||||
void InitCombatHookScripts() {
|
||||
HookScripts::LoadHookScript("hs_tohit", HOOK_TOHIT);
|
||||
HookScripts::LoadHookScript("hs_afterhitroll", HOOK_AFTERHITROLL);
|
||||
@@ -512,6 +624,8 @@ void InitCombatHookScripts() {
|
||||
HookScripts::LoadHookScript("hs_ammocost", HOOK_AMMOCOST);
|
||||
HookScripts::LoadHookScript("hs_onexplosion", HOOK_ONEXPLOSION);
|
||||
HookScripts::LoadHookScript("hs_targetobject", HOOK_TARGETOBJECT);
|
||||
HookScripts::LoadHookScript("hs_bestweapon", HOOK_BESTWEAPON);
|
||||
HookScripts::LoadHookScript("hs_canuseweapon", HOOK_CANUSEWEAPON);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,9 +16,14 @@ void Inject_ItemDamageHook();
|
||||
void Inject_AmmoCostHook();
|
||||
void Inject_OnExplosionHook();
|
||||
void Inject_TargetObjectHook();
|
||||
void Inject_BestWeaponHook();
|
||||
void Inject_CanUseWeaponHook();
|
||||
|
||||
int __fastcall AmmoCostHook_Script(DWORD hookType, fo::GameObject* weapon, DWORD &rounds);
|
||||
|
||||
long CalcApCostHook_Invoke(fo::GameObject* source, long hitMode, long isCalled, long cost, fo::GameObject* weapon);
|
||||
//void FindTargetHook_Invoke(fo::GameObject* targets[], fo::GameObject* attacker);
|
||||
//fo::GameObject* BestWeaponHook_Invoke(fo::GameObject* bestWeapon, fo::GameObject* source, fo::GameObject* weapon1, fo::GameObject* weapon2, fo::GameObject* target);
|
||||
//bool CanUseWeaponHook_Invoke(bool result, fo::GameObject* source, fo::GameObject* weapon, long hitMode);
|
||||
|
||||
}
|
||||
|
||||
@@ -631,6 +631,66 @@ cancelEnc:
|
||||
}
|
||||
}
|
||||
|
||||
static long __stdcall RollCheckHook_Script(long roll, long chance, long bonus, long randomChance, long calledFrom) {
|
||||
long hookType;
|
||||
switch (calledFrom - 5) {
|
||||
case 0x42388E: // compute_attack_
|
||||
case 0x4234D1: // compute_spray_
|
||||
hookType = 1; // single and burst attack hit event
|
||||
break;
|
||||
case 0x42356C: // compute_spray_
|
||||
hookType = 2; // burst attack bullet hit event
|
||||
break;
|
||||
case 0x4AAB29: // skill_result_
|
||||
hookType = 3; // common skill check event
|
||||
break;
|
||||
case 0x4AB3B6: // skill_use_
|
||||
hookType = 4; // SKILL_REPAIR
|
||||
break;
|
||||
case 0x4AB8B5: // skill_use_
|
||||
hookType = 5; // SKILL_DOCTOR
|
||||
break;
|
||||
case 0x4ABC9F: // skill_check_stealing_
|
||||
hookType = 6; // SKILL_STEAL - source stealing check event
|
||||
break;
|
||||
case 0x4ABCE6: // skill_check_stealing_
|
||||
hookType = 7; // SKILL_STEAL - target stealing check event (fail for success stealing)
|
||||
break;
|
||||
default:
|
||||
return roll; // unsupported hook
|
||||
}
|
||||
|
||||
BeginHook();
|
||||
argCount = 5;
|
||||
|
||||
args[0] = hookType;
|
||||
args[1] = roll;
|
||||
args[2] = chance;
|
||||
args[3] = bonus;
|
||||
args[4] = randomChance;
|
||||
|
||||
RunHookScript(HOOK_ROLLCHECK);
|
||||
if (cRet) roll = rets[0];
|
||||
|
||||
EndHook();
|
||||
return roll;
|
||||
}
|
||||
|
||||
static void __declspec(naked) roll_check_hook() {
|
||||
__asm {
|
||||
push ecx;
|
||||
push [esp + 0xC + 8]; // calledFrom
|
||||
push ebx; // random chance value
|
||||
push esi; // bonus
|
||||
push edi; // chance value
|
||||
call fo::funcoffs::roll_check_critical_;
|
||||
push eax; // roll result
|
||||
call RollCheckHook_Script;
|
||||
pop ecx;
|
||||
retn;
|
||||
}
|
||||
}
|
||||
|
||||
void Inject_BarterPriceHook() {
|
||||
const DWORD barterPriceHkAddr[] = {
|
||||
0x474D4C, // barter_attempt_transaction_ (offers button)
|
||||
@@ -710,6 +770,10 @@ void Inject_EncounterHook() {
|
||||
HookCall(0x4C095C, wmRndEncounterOccurred_hook);
|
||||
}
|
||||
|
||||
void Inject_RollCheckHook() {
|
||||
HookCall(0x4A3020, roll_check_hook);
|
||||
}
|
||||
|
||||
void InitMiscHookScripts() {
|
||||
HookScripts::LoadHookScript("hs_barterprice", HOOK_BARTERPRICE);
|
||||
HookScripts::LoadHookScript("hs_useskillon", HOOK_USESKILLON);
|
||||
@@ -722,6 +786,7 @@ void InitMiscHookScripts() {
|
||||
HookScripts::LoadHookScript("hs_resttimer", HOOK_RESTTIMER);
|
||||
HookScripts::LoadHookScript("hs_explosivetimer", HOOK_EXPLOSIVETIMER);
|
||||
HookScripts::LoadHookScript("hs_encounter", HOOK_ENCOUNTER);
|
||||
HookScripts::LoadHookScript("hs_rollcheck", HOOK_ROLLCHECK);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ void Inject_SetGlobalVarHook();
|
||||
void Inject_RestTimerHook();
|
||||
void Inject_ExplosiveTimerHook();
|
||||
void Inject_EncounterHook();
|
||||
void Inject_RollCheckHook();
|
||||
|
||||
long PerceptionRangeHook_Invoke(fo::GameObject* watcher, fo::GameObject* target, long type, long result);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user