mirror of
https://github.com/sfall-team/sfall.git
synced 2026-07-27 16:52:34 -07:00
Backported three game hooks from 4.1:
* USEANIMOBJ, EXPLOSIVETIMER, DESCRIPTIONOBJ Updated documents and example mods
This commit is contained in:
@@ -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
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
|
||||
@@ -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&);
|
||||
|
||||
Reference in New Issue
Block a user