Backported three game hooks from 4.1:

* USEANIMOBJ, EXPLOSIVETIMER, DESCRIPTIONOBJ

Updated documents and example mods
This commit is contained in:
NovaRain
2022-11-27 09:27:46 +08:00
parent 4f34217625
commit 7ecdfe362f
14 changed files with 249 additions and 0 deletions
@@ -0,0 +1,44 @@
/*
Example implementation of the algorithm of how the game engine sets the explosive timer.
*/
#include "..\..\scripting_docs\headers\sfall.h"
#include "..\..\scripting_docs\headers\define_lite.h"
// from CONDTION.H
#define ROLL_CRITICAL_FAILURE (0)
#define ROLL_FAILURE (1)
#define ROLL_SUCCESS (2)
#define ROLL_CRITICAL_SUCCESS (3)
procedure start;
procedure start begin
if game_loaded then begin
register_hook(HOOK_EXPLOSIVETIMER);
end else begin
variable
time := get_sfall_arg,
result := ROLL_CRITICAL_FAILURE;
if has_trait(TRAIT_PERK, dude_obj, PERK_demolition_expert_perk) then
result := ROLL_SUCCESS;
else begin
result := roll_vs_skill(dude_obj, SKILL_TRAPS, 0);
end
if (result) then begin
if (result == ROLL_FAILURE) then
time /= 2;
else begin // success or critical success
result := ROLL_SUCCESS;
end
end else begin // critical failure
time := 0;
result := ROLL_FAILURE;
end
set_sfall_return(time);
set_sfall_return(result); // failure/success
end
end
+3
View File
@@ -57,6 +57,9 @@
#define HOOK_SETGLOBALVAR (29)
#define HOOK_RESTTIMER (30)
#define HOOK_GAMEMODECHANGE (31)
#define HOOK_USEANIMOBJ (32)
#define HOOK_EXPLOSIVETIMER (33)
#define HOOK_DESCRIPTIONOBJ (34)
// Valid arguments to list_begin
#define LIST_CRITTERS (0)
+44
View File
@@ -632,3 +632,47 @@ Runs once every time when the game mode was changed, like opening/closing the in
int arg0 - event type: 1 - when the player exits the game, 0 - otherwise
int arg1 - the previous game mode
```
-------------------------------------------
#### `HOOK_USEANIMOBJ (hs_useanimobj.int)`
Runs before playing the "use" (usually "magic hands") animation when a critter uses a scenery/container object on the map, or before walking/running animation if the player is at a distance from the object.
```
Critter arg0 - the critter that uses an object (usually dude_obj)
Obj arg1 - the object being used
int arg2 - the animation code being used (see ANIM_* in Animcomd.h)
int ret0 - overrides the animation code (pass -1 if you want to skip the animation)
```
-------------------------------------------
#### `HOOK_EXPLOSIVETIMER (hs_explosivetimer.int)`
Runs after setting the explosive timer. You can override the result.
```
int arg0 - the time in ticks set in the timer
Obj arg1 - the explosive object
int arg2 - the result of engine calculation of whether the timer was set successfully: 1 - failure, 2 - success (similar to ROLL_* in Condtion.h)
int ret0 - overrides the time of the timer (maximum 18000 ticks)
int ret1 - overrides the result of engine calculation: 0/1 - failure, 2/3 - success (similar to ROLL_*), any other value - use engine handler
```
-------------------------------------------
#### `HOOK_DESCRIPTIONOBJ (hs_descriptionobj.int)`
Runs when using the examine action icon to display the description of an object. You can override the description text.
An example usage would be to add an additional description to the item based on player's stats/skills.
Does not run if the script of the object overrides the description.
```
Obj arg0 - the object
int ret0 - a pointer to the new text received by using the get_string_pointer function
```
@@ -682,6 +682,11 @@ sfall_funcX metarule functions
- Redraws inventory list in the inventory/use inventory item on/loot/barter screens
- `invSide` specifies which side needs to be redrawn: 0 - the player, 1 - target (container/NPC in loot/barter screens), -1 - both sides (same as without argument)
----
#### get_string_pointer
`int sfall_func1("get_string_pointer", string text)`
- Returns a pointer to a string variable or to a text
----
#### dialog_message
`void sfall_func1("dialog_message", string text)`
@@ -543,6 +543,7 @@ FUNC(obj_turn_on_, 0x48ADF0)
FUNC(obj_unjam_lock_, 0x49D480)
FUNC(obj_use_book_, 0x49B9F0)
FUNC(obj_use_power_on_car_, 0x49BDE8)
FUNC(object_description_, 0x48C914)
FUNC(object_under_mouse_, 0x44CEC4)
FUNC(palette_fade_to_, 0x493AD4)
FUNC(palette_init_, 0x493A00)
+3
View File
@@ -94,6 +94,9 @@ static HooksInjectInfo injectHooks[] = {
{HOOK_SETGLOBALVAR, Inject_SetGlobalVarHook, false},
{HOOK_RESTTIMER, Inject_RestTimerHook, false},
{HOOK_GAMEMODECHANGE, nullptr, true}, // always embedded to the engine
{HOOK_USEANIMOBJ, Inject_UseAnimateObjHook, false},
{HOOK_EXPLOSIVETIMER, Inject_ExplosiveTimerHook, false},
{HOOK_DESCRIPTIONOBJ, Inject_DescriptionObjHook, false},
};
void HookScripts::InjectingHook(int hookId) {
+3
View File
@@ -57,6 +57,9 @@ enum HookType
HOOK_SETGLOBALVAR = 29,
HOOK_RESTTIMER = 30,
HOOK_GAMEMODECHANGE = 31,
HOOK_USEANIMOBJ = 32,
HOOK_EXPLOSIVETIMER = 33,
HOOK_DESCRIPTIONOBJ = 34,
HOOK_COUNT
};
+51
View File
@@ -387,6 +387,52 @@ skip:
}
}
static int __fastcall ExplosiveTimerHook_Script(DWORD &type, DWORD item, DWORD time) {
BeginHook();
argCount = 3;
args[0] = time;
args[1] = item;
args[2] = (type == 11) ? fo::ROLL_FAILURE : fo::ROLL_SUCCESS;
RunHookScript(HOOK_EXPLOSIVETIMER);
time = -1;
if (cRet > 0 && (long)rets[0] >= 0) {
time = min(rets[0], 18000); // max 30 minutes
if (cRet > 1) {
int typeRet = rets[1];
if (typeRet >= fo::ROLL_CRITICAL_FAILURE && typeRet <= fo::ROLL_CRITICAL_SUCCESS) {
type = (typeRet > fo::ROLL_FAILURE) ? 8 : 11; // returned new type (8 = SUCCESS, 11 = FAILURE)
}
}
}
EndHook();
return time;
}
static void __declspec(naked) ExplosiveTimerHook() {
__asm {
push eax; // time in ticks for queue_add_
push edx;
//-------
push ecx;
mov ecx, esp; // ptr to type
push edi; // time in ticks (w/o failure penalty)
call ExplosiveTimerHook_Script; // ecx - type, edx - item
pop ecx;
pop edx;
cmp eax, -1; // return new time in ticks
jne skip;
pop eax;
jmp end;
skip:
add esp, 4;
end:
jmp fo::funcoffs::queue_add_;
}
}
void Inject_BarterPriceHook() {
const DWORD barterPriceHkAddr[] = {
0x474D4C, // barter_attempt_transaction_ (offers button)
@@ -438,6 +484,10 @@ void Inject_RestTimerHook() {
MakeCalls(RestTimerEscapeHook, restTimerEscHkAddr);
}
void Inject_ExplosiveTimerHook() {
HookCall(0x49BDC4, ExplosiveTimerHook);
}
void InitMiscHookScripts() {
HookScripts::LoadHookScript("hs_barterprice", HOOK_BARTERPRICE);
HookScripts::LoadHookScript("hs_useskill", HOOK_USESKILL);
@@ -446,6 +496,7 @@ void InitMiscHookScripts() {
HookScripts::LoadHookScript("hs_cartravel", HOOK_CARTRAVEL);
HookScripts::LoadHookScript("hs_setglobalvar", HOOK_SETGLOBALVAR);
HookScripts::LoadHookScript("hs_resttimer", HOOK_RESTTIMER);
HookScripts::LoadHookScript("hs_explosivetimer", HOOK_EXPLOSIVETIMER);
}
}
+1
View File
@@ -12,6 +12,7 @@ void Inject_WithinPerceptionHook();
void Inject_CarTravelHook();
void Inject_SetGlobalVarHook();
void Inject_RestTimerHook();
void Inject_ExplosiveTimerHook();
long PerceptionRangeHook_Invoke(fo::GameObject* watcher, fo::GameObject* target, long type, long result);
+85
View File
@@ -110,6 +110,81 @@ defaultHandler:
}
}
static DWORD __fastcall UseAnimateObjHook_Script(DWORD critter, DWORD animCode, DWORD object) {
BeginHook();
argCount = 3;
args[0] = critter;
args[1] = object;
args[2] = animCode;
RunHookScript(HOOK_USEANIMOBJ);
if (cRet > 0 && static_cast<long>(rets[0]) <= 64) {
animCode = rets[0]; // new anim code
}
EndHook();
return animCode;
}
// Before animation of using map object
static void __declspec(naked) UseAnimateObjHook() {
__asm {
cmp dword ptr [esp], 0x412292 + 5;
push eax;
push ecx;
jne contr;
push ebp; // map object
jmp next;
contr:
push edi; // map object
next:
mov ecx, eax; // source critter
call UseAnimateObjHook_Script; // edx - anim code
pop ecx;
cmp eax, -1; // return anim code
jle end; // goto no animate
mov edx, eax; // restore vanilla or hook anim code
pop eax;
jmp fo::funcoffs::register_object_animate_;
end:
pop eax;
retn;
}
}
static DWORD __fastcall DescriptionObjHook_Script(DWORD object) {
BeginHook();
argCount = 1;
args[0] = object;
RunHookScript(HOOK_DESCRIPTIONOBJ);
DWORD textPrt = (cRet > 0) ? rets[0] : 0;
EndHook();
return textPrt;
}
static void __declspec(naked) DescriptionObjHook() {
__asm {
push ecx;
push edx;
mov ecx, eax; // object
call DescriptionObjHook_Script;
pop edx;
pop ecx;
test eax, eax; // pointer to text
jnz skip;
mov eax, ebp;
jmp fo::funcoffs::object_description_;
skip:
retn;
}
}
void Inject_UseObjOnHook() {
const DWORD useObjOnHkAddr[] = {0x49C606, 0x473619};
HookCalls(UseObjOnHook, useObjOnHkAddr);
@@ -128,9 +203,19 @@ void Inject_UseObjHook() {
HookCalls(UseObjHook, useObjHkAddr);
}
void Inject_UseAnimateObjHook() {
const DWORD useAnimateObjHkAddr[] = {0x4120C1, 0x412292};
HookCalls(UseAnimateObjHook, useAnimateObjHkAddr);
}
void Inject_DescriptionObjHook() {
HookCall(0x49AE28, DescriptionObjHook);
}
void InitObjectHookScripts() {
HookScripts::LoadHookScript("hs_useobjon", HOOK_USEOBJON);
HookScripts::LoadHookScript("hs_useobj", HOOK_USEOBJ);
HookScripts::LoadHookScript("hs_descriptionobj", HOOK_DESCRIPTIONOBJ);
}
}
+2
View File
@@ -7,6 +7,8 @@ void InitObjectHookScripts();
void Inject_UseObjOnHook();
void Inject_UseObjHook();
void Inject_UseAnimateObjHook();
void Inject_DescriptionObjHook();
long UseObjOnHook_Invoke(fo::GameObject* source, fo::GameObject* item, fo::GameObject* target);
@@ -96,6 +96,7 @@ static const SfallMetarule metarules[] = {
{"get_sfall_arg_at", mf_get_sfall_arg_at, 1, 1, 0, {ARG_INT}},
{"get_stat_max", mf_get_stat_max, 1, 2, 0, {ARG_INT, ARG_INT}},
{"get_stat_min", mf_get_stat_min, 1, 2, 0, {ARG_INT, ARG_INT}},
{"get_string_pointer", mf_get_string_pointer, 1, 1, 0, {ARG_STRING}},
{"get_terrain_name", mf_get_terrain_name, 0, 2, -1, {ARG_INT, ARG_INT}},
{"get_text_width", mf_get_text_width, 1, 1, 0, {ARG_STRING}},
{"get_window_attribute", mf_get_window_attribute, 1, 2, -1, {ARG_INT, ARG_INT}},
@@ -378,6 +378,10 @@ void mf_add_extra_msg_file(OpcodeContext& ctx) {
ctx.setReturn(result);
}
void mf_get_string_pointer(OpcodeContext& ctx) {
ctx.setReturn(reinterpret_cast<long>(ctx.arg(0).strValue()), DATATYPE_INT);
}
void mf_get_text_width(OpcodeContext& ctx) {
ctx.setReturn(fo::util::GetTextWidth(ctx.arg(0).strValue()));
}
+2
View File
@@ -47,6 +47,8 @@ void op_message_str_game(OpcodeContext&);
void mf_add_extra_msg_file(OpcodeContext&);
void mf_get_string_pointer(OpcodeContext&);
void mf_get_text_width(OpcodeContext&);
void mf_string_to_case(OpcodeContext&);