From 227a062fa605c63cbe90d6c1ce3ee3fca7d749c7 Mon Sep 17 00:00:00 2001 From: NovaRain Date: Sun, 10 Nov 2019 18:09:12 +0800 Subject: [PATCH 01/36] Added a fix for scripted interface windows (#25) * prevents player's movement when clicking on the window. * the mouse cursor will not be toggled when the cursor hovers over a hidden window. Updated version number. --- artifacts/ddraw.ini | 2 +- artifacts/mods/gl_partycontrol.ssl | 8 ++--- sfall/FalloutEngine/Enums.h | 2 +- sfall/FalloutEngine/Functions_def.h | 1 + sfall/FalloutEngine/Structs.h | 14 ++++---- sfall/FalloutEngine/VariableOffsets.h | 6 ++++ sfall/FalloutEngine/Variables_def.h | 6 +++- sfall/Modules/Interface.cpp | 51 +++++++++++++++++++++++++-- sfall/Modules/Objects.cpp | 5 ++- sfall/Modules/Tiles.cpp | 4 +-- sfall/main.cpp | 6 ++-- sfall/main.h | 2 +- sfall/version.h | 4 +-- 13 files changed, 82 insertions(+), 29 deletions(-) diff --git a/artifacts/ddraw.ini b/artifacts/ddraw.ini index 0decda1d..d50af44c 100644 --- a/artifacts/ddraw.ini +++ b/artifacts/ddraw.ini @@ -1,5 +1,5 @@ ;sfall configuration settings -;v4.2.1 +;v4.2.2 [Main] ;Change to 1 if you want to use command line args to tell sfall to use another ini file. diff --git a/artifacts/mods/gl_partycontrol.ssl b/artifacts/mods/gl_partycontrol.ssl index 2e9d0f63..40d77521 100644 --- a/artifacts/mods/gl_partycontrol.ssl +++ b/artifacts/mods/gl_partycontrol.ssl @@ -54,9 +54,7 @@ procedure combatturn_handler begin // set perks (only work with 4.1.8+) foreach (perkID in perksList) begin level := has_trait(TRAIT_PERK, real_dude_obj, perkID); - if (level) then begin - critter_add_trait(critter, TRAIT_PERK, perkID, level); - end + if (level) then critter_add_trait(critter, TRAIT_PERK, perkID, level); end intface_redraw; lightDist := get_object_data(critter, OBJ_DATA_LIGHT_DISTANCE); @@ -83,9 +81,7 @@ procedure combatturn_handler begin if (dude_obj != real_dude_obj) then begin foreach (perkID in perksList) begin level := has_trait(TRAIT_PERK, real_dude_obj, perkID); - if (level) then begin - critter_rm_trait(critter, TRAIT_PERK, perkID, level); - end + if (level) then critter_rm_trait(critter, TRAIT_PERK, perkID, level); end end if isShowTag then begin diff --git a/sfall/FalloutEngine/Enums.h b/sfall/FalloutEngine/Enums.h index 5eb62138..3b6adea2 100644 --- a/sfall/FalloutEngine/Enums.h +++ b/sfall/FalloutEngine/Enums.h @@ -739,7 +739,7 @@ namespace WinFlags { Transparent = 0x20, UnknownFlag40 = 0x40, UnknownFlag80 = 0x80, - scriptWindow = 0x100, + ScriptWindow = 0x100, itsButton = 0x10000, }; } diff --git a/sfall/FalloutEngine/Functions_def.h b/sfall/FalloutEngine/Functions_def.h index 68c22576..439c1ce0 100644 --- a/sfall/FalloutEngine/Functions_def.h +++ b/sfall/FalloutEngine/Functions_def.h @@ -25,6 +25,7 @@ WRAP_WATCOM_FFUNC3(FrmFrameData*, frame_ptr, FrmHeaderData*, frm, long, frame, l WRAP_WATCOM_FFUNC3(void, intface_update_items, long, animate, long, modeLeft,long, modeRight) WRAP_WATCOM_FFUNC3(long, item_add_force, GameObject*, critter, GameObject*, item, long, count) WRAP_WATCOM_FFUNC7(void, make_straight_path_func, GameObject*, objFrom, DWORD, tileFrom, DWORD, tileTo, void*, rotationPtr, DWORD*, result, long, flags, void*, func) +WRAP_WATCOM_FFUNC4(long, mouse_click_in, long, x, long, y, long, x_end, long, y_end) WRAP_WATCOM_FFUNC3(GameObject*, obj_blocking_at, GameObject*, object, long, tile, long, elevation) WRAP_WATCOM_FFUNC3(long, object_under_mouse, long, crSwitch, long, inclDude, long, elevation) WRAP_WATCOM_FFUNC3(long, scr_get_local_var, long, sid, long, varId, long*, value) diff --git a/sfall/FalloutEngine/Structs.h b/sfall/FalloutEngine/Structs.h index 0fec931c..2ec0339f 100644 --- a/sfall/FalloutEngine/Structs.h +++ b/sfall/FalloutEngine/Structs.h @@ -697,20 +697,20 @@ struct ScriptListInfoItem { //for holding window info #pragma pack(1) struct Window { - long ref; + long wID; long flags; RECT wRect; long width; long height; long clearColour; - long unknown2; - long unknown3; + long rand1; + long rand2; BYTE *surface; // bytes frame data ref to palette - long buttonListP; - long unknown5;//buttonptr? + long *buttonsList; + long unknown5; // buttonptr? long unknown6; - long unknown7; - long drawFuncP; + long *menuBar; + long *drawFunc; }; #pragma pack(1) diff --git a/sfall/FalloutEngine/VariableOffsets.h b/sfall/FalloutEngine/VariableOffsets.h index d8b9237b..e71bbc8a 100644 --- a/sfall/FalloutEngine/VariableOffsets.h +++ b/sfall/FalloutEngine/VariableOffsets.h @@ -51,6 +51,7 @@ #define FO_VAR_dialogue_switch_mode 0x518718 #define FO_VAR_dialog_target 0x518848 #define FO_VAR_dialog_target_is_party 0x51884C +#define FO_VAR_display_win 0x631E4C #define FO_VAR_displayMapList 0x41B560 #define FO_VAR_dropped_explosive 0x5190E0 #define FO_VAR_drugInfoList 0x5191CC @@ -148,6 +149,7 @@ #define FO_VAR_name_sort_list 0x56FCB0 #define FO_VAR_num_game_global_vars 0x5186C4 #define FO_VAR_num_map_global_vars 0x519574 +#define FO_VAR_num_windows 0x6ADF24 #define FO_VAR_card_old_fid1 0x5709EC #define FO_VAR_obj_dude 0x6610B8 #define FO_VAR_obj_seen 0x662445 @@ -232,6 +234,7 @@ #define FO_VAR_trait_data 0x51DB84 #define FO_VAR_view_page 0x664520 #define FO_VAR_wd_obj 0x59E98C +#define FO_VAR_window 0x6ADE58 #define FO_VAR_wmAreaInfoList 0x51DDF8 #define FO_VAR_wmLastRndTime 0x51DEA0 #define FO_VAR_wmMaxMapNum 0x51DE10 @@ -248,10 +251,13 @@ // colors #define FO_VAR_BlueColor 0x6A38EF +#define FO_VAR_DARK_GREY_Color 0x6A59D8 #define FO_VAR_DarkGreenColor 0x6A3A90 +#define FO_VAR_DarkGreenGreyColor 0x6A3DF1 #define FO_VAR_DullPinkColor 0x6AB718 #define FO_VAR_GoodColor 0x6AB4EF #define FO_VAR_GreenColor 0x6A3CB0 +#define FO_VAR_LIGHT_GREY_Color 0x6A76BF #define FO_VAR_PeanutButter 0x6A82F3 #define FO_VAR_RedColor 0x6AB4D0 #define FO_VAR_WhiteColor 0x6AB8CF diff --git a/sfall/FalloutEngine/Variables_def.h b/sfall/FalloutEngine/Variables_def.h index 6de8226d..34d02728 100644 --- a/sfall/FalloutEngine/Variables_def.h +++ b/sfall/FalloutEngine/Variables_def.h @@ -34,7 +34,9 @@ VAR_(curr_font_num, DWORD) VARA(curr_pc_stat, long, PCSTAT_max_pc_stat) VAR_(curr_stack, DWORD) VAR_(cursor_line, DWORD) -VAR_(DarkGreenColor, BYTE) +VAR_(DARK_GREY_Color, BYTE) +VAR_(DarkGreenColor, BYTE) +VAR_(DarkGreenGreyColor, BYTE) VAR_(dialogue_state, DWORD) VAR_(dialogue_switch_mode, DWORD) VAR_(dialog_target, DWORD) @@ -95,6 +97,7 @@ VAR_(last_button_winID, DWORD) VAR_(last_level, DWORD) VAR_(Level_, DWORD) VAR_(Lifegiver, DWORD) +VAR_(LIGHT_GREY_Color, BYTE) VAR_(lipsFID, DWORD) VAR_(list_com, DWORD) VAR_(list_total, DWORD) @@ -200,6 +203,7 @@ VAR_(title_font, DWORD) VARA(trait_data, TraitInfo, TRAIT_count) VAR_(view_page, DWORD) VAR_(wd_obj, DWORD) +VARA(window, fo::Window*, 50) VAR_(WhiteColor, BYTE) VAR_(wmAreaInfoList, DWORD) VAR_(wmLastRndTime, DWORD) diff --git a/sfall/Modules/Interface.cpp b/sfall/Modules/Interface.cpp index 01fdc574..9f6a2db4 100644 --- a/sfall/Modules/Interface.cpp +++ b/sfall/Modules/Interface.cpp @@ -81,8 +81,8 @@ static void ActionPointsBarPatch() { dlog("Applying expanded action points bar patch.", DL_INIT); if (hrpIsEnabled) { // check valid data - if (hrpVersionValid && !_stricmp((const char*)HRPAddressOffset(0x39358), "HR_IFACE_%i.frm")) { - SafeWriteStr(HRPAddressOffset(0x39363), "E.frm"); // patching HRP + if (hrpVersionValid && !_stricmp((const char*)HRPAddress(0x10039358), "HR_IFACE_%i.frm")) { + SafeWriteStr(HRPAddress(0x10039363), "E.frm"); // patching HRP } else { dlogr(" Incorrect HRP version!", DL_INIT); return; @@ -561,6 +561,44 @@ static void SpeedInterfaceCounterAnimsPatch() { } } +static bool IFACE_BAR_MODE = false; +static long gmouse_handle_event_hook() { + long countWin = *(DWORD*)FO_VAR_num_windows; + long ifaceWin = fo::var::interfaceWindow; + fo::Window* win = nullptr; + + for (int n = 1; n < countWin; n++) { + win = fo::var::window[n]; + if ((win->wID == ifaceWin || (win->flags & fo::WinFlags::ScriptWindow && !(win->flags & fo::WinFlags::Transparent))) // also check the script windows + && !(win->flags & fo::WinFlags::Hidden)) { + RECT *rect = &win->wRect; + if (fo::func::mouse_click_in(rect->left, rect->top, rect->right, rect->bottom)) return 0; // 0 - block clicking in the window area + } + } + if (IFACE_BAR_MODE) return 1; + // if IFACE_BAR_MODE is not enabled, check the display_win window area + win = fo::func::GNW_find(*(DWORD*)FO_VAR_display_win); + RECT *rect = &win->wRect; + return fo::func::mouse_click_in(rect->left, rect->top, rect->right, rect->bottom); // 1 - click in the display_win area +} + +static void __declspec(naked) gmouse_bk_process_hook() { + using namespace fo::WinFlags; + __asm { + call fo::funcoffs::win_get_top_win_; + cmp eax, ds:[FO_VAR_display_win]; + jnz checkFlag; + retn; +checkFlag: + call fo::funcoffs::GNW_find_; + cmp [eax + 4], Hidden; // window flags + jnz skip; + mov eax, ds:[FO_VAR_display_win]; // window is hidden, so return the number of the display_win +skip: + retn; + } +} + void Interface::init() { if (GetConfigInt("Interface", "ActionPointsBar", 0)) { ActionPointsBarPatch(); @@ -568,6 +606,15 @@ void Interface::init() { DrawActionPointsNumber(); WorldMapInterfacePatch(); SpeedInterfaceCounterAnimsPatch(); + + // Fix for interface windows with 'Hidden' and 'ScriptWindow' flags + // Hidden - will not toggle the mouse cursor when the cursor hovers over a hidden window + // ScriptWindow - prevents player's movement when clicking on the window if the 'Transparent' flag is not set + HookCall(0x44B737, gmouse_bk_process_hook); + LoadGameHook::OnBeforeGameInit() += []() { + if (hrpVersionValid) IFACE_BAR_MODE = *(BYTE*)HRPAddress(0x1006EB0C) != 0; + HookCall(0x44C018, gmouse_handle_event_hook); // replaces hack function from HRP + }; } void Interface::exit() { diff --git a/sfall/Modules/Objects.cpp b/sfall/Modules/Objects.cpp index 7bfe3f68..27e09039 100644 --- a/sfall/Modules/Objects.cpp +++ b/sfall/Modules/Objects.cpp @@ -120,6 +120,7 @@ static void __declspec(naked) queue_add_hack() { __asm { mov [edx + 8], edi; // queue.object mov [edx], esi; // queue.time + // test edi, edi; jnz fix; retn; @@ -152,9 +153,7 @@ end: } void Objects::SetAutoUnjamLockTime(DWORD time) { - if (!unjamTimeState) { - BlockCall(0x4A364A); // disable auto unjam at midnight - } + if (!unjamTimeState) BlockCall(0x4A364A); // disable auto unjam at midnight if (time > 0) { SafeWrite8(0x4831D9, (BYTE)time); diff --git a/sfall/Modules/Tiles.cpp b/sfall/Modules/Tiles.cpp index 26707387..3fe647f9 100644 --- a/sfall/Modules/Tiles.cpp +++ b/sfall/Modules/Tiles.cpp @@ -274,8 +274,8 @@ void Tiles::init() { SafeWriteBatch(0x40, Tiles_40); SafeWriteBatch(0xC0, Tiles_C0); if (hrpVersionValid) { // Check HRP 4.1.8 - SafeWrite8(HRPAddressOffset(0xE1C0), 0x40); - SafeWrite8(HRPAddressOffset(0xE1DA), 0x3F); + SafeWrite8(HRPAddress(0x1000E1C0), 0x40); + SafeWrite8(HRPAddress(0x1000E1DA), 0x3F); } dlogr(" Done", DL_INIT); } diff --git a/sfall/main.cpp b/sfall/main.cpp index 0ff293ee..e55d34f6 100644 --- a/sfall/main.cpp +++ b/sfall/main.cpp @@ -98,8 +98,8 @@ static char translationIni[65]; DWORD modifiedIni; DWORD hrpDLLBaseAddr = 0x10000000; -DWORD HRPAddressOffset(DWORD offset) { - return (hrpDLLBaseAddr + offset); +DWORD HRPAddress(DWORD addr) { + return (hrpDLLBaseAddr + (addr & 0xFFFFF)); } int iniGetInt(const char* section, const char* setting, int defaultValue, const char* iniFile) { @@ -323,7 +323,7 @@ defaultIni: hrpIsEnabled = (*(DWORD*)0x4E4480 != 0x278805C7); // check if HRP is enabled if (hrpIsEnabled) { LoadHRPModule(); - if (strncmp((const char*)HRPAddressOffset(0x39940), "4.1.8", 5) == 0) hrpVersionValid = true; + if (strncmp((const char*)HRPAddress(0x10039940), "4.1.8", 5) == 0) hrpVersionValid = true; } InitModules(); diff --git a/sfall/main.h b/sfall/main.h index 43ca9509..81ac2f03 100644 --- a/sfall/main.h +++ b/sfall/main.h @@ -106,7 +106,7 @@ std::string Translate(const char* section, const char* setting, const char* defa // Translates given string using Sfall translation INI file and puts the result into given buffer. size_t Translate(const char* section, const char* setting, const char* defaultValue, char* buffer, size_t bufSize = 128); -DWORD HRPAddressOffset(DWORD offset); +DWORD HRPAddress(DWORD addr); extern const char ddrawIni[]; extern DWORD modifiedIni; diff --git a/sfall/version.h b/sfall/version.h index e48dc61a..1a9d0c4e 100644 --- a/sfall/version.h +++ b/sfall/version.h @@ -24,7 +24,7 @@ #define VERSION_MAJOR 4 #define VERSION_MINOR 2 -#define VERSION_BUILD 1 +#define VERSION_BUILD 2 #define VERSION_REV 0 -#define VERSION_STRING "4.2.1" +#define VERSION_STRING "4.2.2" From 534bc3e5cd78b2cd6e39cfcfdf86398eb0216c98 Mon Sep 17 00:00:00 2001 From: NovaRain Date: Mon, 11 Nov 2019 09:50:34 +0800 Subject: [PATCH 02/36] Implemented executing timed_event_p_proc in sfall global scripts Added "add_g_timer_event" and "remove_timer_event" script functions for timed_event_p_proc in global scripts. Fixed the check on the 'hidden' flag for previous commit. --- artifacts/scripting/headers/sfall.h | 3 + artifacts/scripting/sfall function notes.txt | 11 +++ sfall/FalloutEngine/Functions.cpp | 4 +- sfall/Modules/Interface.cpp | 6 +- sfall/Modules/ScriptExtender.cpp | 74 ++++++++++++++++++- sfall/Modules/ScriptExtender.h | 5 ++ sfall/Modules/Scripting/Handlers/Core.cpp | 12 +++ sfall/Modules/Scripting/Handlers/Core.h | 4 + sfall/Modules/Scripting/Handlers/Metarule.cpp | 3 + 9 files changed, 113 insertions(+), 9 deletions(-) diff --git a/artifacts/scripting/headers/sfall.h b/artifacts/scripting/headers/sfall.h index 490d995a..fa3dff18 100644 --- a/artifacts/scripting/headers/sfall.h +++ b/artifacts/scripting/headers/sfall.h @@ -249,6 +249,7 @@ // sfall_funcX macros #define add_extra_msg_file(name) sfall_func1("add_extra_msg_file", name) +#define add_global_timer_event(time, param) sfall_func2("add_g_timer_event", time, param) #define add_iface_tag sfall_func0("add_iface_tag") #define add_trait(traitId) sfall_func1("add_trait", traitId) #define art_cache_clear sfall_func0("art_cache_clear") @@ -293,6 +294,8 @@ #define obj_under_cursor(crSwitch, inclDude) sfall_func2("obj_under_cursor", crSwitch, inclDude) #define outlined_object sfall_func0("outlined_object") #define real_dude_obj sfall_func0("real_dude_obj") +#define remove_all_timer_events sfall_func0("remove_timer_event") +#define remove_timer_event(param) sfall_func1("remove_timer_event", param) #define set_can_rest_on_map(map, elev, value) sfall_func3("set_can_rest_on_map", map, elev, value) #define set_car_intface_art(artIndex) sfall_func1("set_car_intface_art", artIndex) #define set_cursor_mode(mode) sfall_func1("set_cursor_mode", mode) diff --git a/artifacts/scripting/sfall function notes.txt b/artifacts/scripting/sfall function notes.txt index 52132f1c..6311862b 100644 --- a/artifacts/scripting/sfall function notes.txt +++ b/artifacts/scripting/sfall function notes.txt @@ -633,6 +633,17 @@ optional argument: > int sfall_func0("get_inven_ap_cost") - returns the current AP cost to access the inventory in combat +> void sfall_func2("add_g_timer_event", int time, int param) +- adds a timer event that calls the timed_event_p_proc procedure in the current global script +- time: the number of ticks after which the event timer is triggered +- param: the value that is passed to the timed_event_p_proc procedure for the fixed_param function + +> void sfall_func0("remove_timer_event") +- clears all set timer events for the current global script + +> void sfall_func1("remove_timer_event", int param) +- removes all timer events with the specified 'param' value for the current global script + ------------------------ ------ MORE INFO ------- ------------------------ diff --git a/sfall/FalloutEngine/Functions.cpp b/sfall/FalloutEngine/Functions.cpp index 85ea645a..2a449a2d 100644 --- a/sfall/FalloutEngine/Functions.cpp +++ b/sfall/FalloutEngine/Functions.cpp @@ -26,7 +26,7 @@ namespace fo namespace func { -// prints debug message to debug.log file for develop build +// Prints debug message to debug.log file for develop build #ifndef NDEBUG void __declspec(naked) dev_printf(const char* fmt, ...) { __asm jmp fo::funcoffs::debug_printf_; @@ -181,7 +181,7 @@ long __stdcall db_dir_entry(const char *fileName, DWORD *sizeOut) { // prints message to debug.log file void __declspec(naked) debug_printf(const char* fmt, ...) { - __asm jmp fo::funcoffs::debug_printf_ + __asm jmp fo::funcoffs::debug_printf_; } // Displays message in main UI console window diff --git a/sfall/Modules/Interface.cpp b/sfall/Modules/Interface.cpp index 9f6a2db4..8520b0c3 100644 --- a/sfall/Modules/Interface.cpp +++ b/sfall/Modules/Interface.cpp @@ -591,8 +591,8 @@ static void __declspec(naked) gmouse_bk_process_hook() { retn; checkFlag: call fo::funcoffs::GNW_find_; - cmp [eax + 4], Hidden; // window flags - jnz skip; + test [eax + 4], Hidden; // window flags + jz skip; mov eax, ds:[FO_VAR_display_win]; // window is hidden, so return the number of the display_win skip: retn; @@ -609,7 +609,7 @@ void Interface::init() { // Fix for interface windows with 'Hidden' and 'ScriptWindow' flags // Hidden - will not toggle the mouse cursor when the cursor hovers over a hidden window - // ScriptWindow - prevents player's movement when clicking on the window if the 'Transparent' flag is not set + // ScriptWindow - prevents the player from moving when clicking on the window if the 'Transparent' flag is not set HookCall(0x44B737, gmouse_bk_process_hook); LoadGameHook::OnBeforeGameInit() += []() { if (hrpVersionValid) IFACE_BAR_MODE = *(BYTE*)HRPAddress(0x1006EB0C) != 0; diff --git a/sfall/Modules/ScriptExtender.cpp b/sfall/Modules/ScriptExtender.cpp index d6f3d5ff..9bad83f0 100644 --- a/sfall/Modules/ScriptExtender.cpp +++ b/sfall/Modules/ScriptExtender.cpp @@ -16,9 +16,7 @@ * along with this program. If not, see . */ -#include //#include -#include #include #include @@ -84,6 +82,18 @@ struct SelfOverrideObj { } }; +struct TimedEvent { + ScriptProgram* script; + long time; + long fixed_param; + + bool operator() (const TimedEvent &a, const TimedEvent &b) { + return a.time < b.time; + } +} *timedEvent = nullptr; + +static std::list timerEventScripts; + static std::vector globalScriptPathList; static std::map globalScriptFilesList; @@ -132,6 +142,11 @@ static DWORD _stdcall FindSid(fo::Program* script) { } // this will allow to use functions like roll_vs_skill, etc without calling set_self (they don't really need self object) if (sfallProgsMap.find(script) != sfallProgsMap.end()) { + if (timedEvent && timedEvent->script->ptr == script) { + overrideScriptStruct.fixedParam = timedEvent->fixed_param; + } else { + overrideScriptStruct.fixedParam = 0; + } overrideScriptStruct.targetObject = overrideScriptStruct.selfObject = 0; return -2; // override struct } @@ -499,7 +514,9 @@ static void PrepareGlobalScriptsListByMask() { static void LoadGlobalScripts() { static bool listIsPrepared = false; isGameLoading = false; + LoadHookScripts(); + dlogr("Loading global scripts:", DL_SCRIPT|DL_INIT); if (!listIsPrepared) { // only once PrepareGlobalScriptsListByMask(); @@ -528,6 +545,7 @@ static void ClearGlobalScripts() { globalScripts.clear(); selfOverrideMap.clear(); globalExportedVars.clear(); + timerEventScripts.clear(); HookScriptClear(); } @@ -617,6 +635,54 @@ static DWORD _stdcall HandleMapUpdateForScripts(const DWORD procId) { return procId; // restore eax (don't delete) } +static long HandleTimedEventScripts() { + long currentTime = fo::var::fallout_game_time; + bool wereRunning = false; + auto timerIt = timerEventScripts.cbegin(); + for (; timerIt != timerEventScripts.cend(); timerIt++) { + if (currentTime >= timerIt->time) { + timedEvent = const_cast(&(*timerIt)); + fo::func::dev_printf("\n[TimedEventScripts] run event: %d", timerIt->time); + RunScriptProc(timerIt->script, fo::ScriptProc::timed_event_p_proc); + wereRunning = true; + } else { + break; + } + } + if (wereRunning) { + for (auto _it = timerEventScripts.cbegin(); _it != timerIt; _it++) { + fo::func::dev_printf("\n[TimedEventScripts] delete events: %d", _it->time); + } + timerEventScripts.erase(timerEventScripts.cbegin(), timerIt); + } + timedEvent = nullptr; + return currentTime; +} + +void ScriptExtender::AddTimerEventScripts(fo::Program* script, long time, long param) { + ScriptProgram* scriptProg = &(sfallProgsMap.find(script)->second); + TimedEvent timer; + timer.script = scriptProg; + timer.fixed_param = param; + timer.time = fo::var::fallout_game_time + time; + timerEventScripts.push_back(std::move(timer)); + timerEventScripts.sort(TimedEvent()); +} + +void ScriptExtender::RemoveTimerEventScripts(fo::Program* script, long param) { + ScriptProgram* scriptProg = &(sfallProgsMap.find(script)->second); + timerEventScripts.remove_if([scriptProg, param] (TimedEvent timer) { + return timer.script == scriptProg && timer.fixed_param == param; + }); +} + +void ScriptExtender::RemoveTimerEventScripts(fo::Program* script) { + ScriptProgram* scriptProg = &(sfallProgsMap.find(script)->second); + timerEventScripts.remove_if([scriptProg] (TimedEvent timer) { + return timer.script == scriptProg; + }); +} + // run all global scripts of types 0 and 3 at specific procedure (if exist) void RunGlobalScriptsAtProc(DWORD procId) { for (DWORD d = 0; d < globalScripts.size(); d++) { @@ -754,9 +820,9 @@ void ScriptExtender::init() { alwaysFindScripts = isDebug && (iniGetInt("Debugging", "AlwaysFindScripts", 0, ::sfall::ddrawIni) != 0); if (alwaysFindScripts) dlogr("Always searching for global scripts behavior enabled.", DL_SCRIPT); - MakeJump(0x4A390C, FindSidHack); + MakeJump(0x4A390C, FindSidHack); // scr_find_sid_from_program_ MakeJump(0x4A5E34, ScrPtrHack); - + HookCall(0x4A26D6, HandleTimedEventScripts); // queue_process_ MakeJump(0x4A67F0, ExecMapScriptsHack); // this patch makes it possible to export variables from sfall global scripts diff --git a/sfall/Modules/ScriptExtender.h b/sfall/Modules/ScriptExtender.h index c491b08c..2d24ad2a 100644 --- a/sfall/Modules/ScriptExtender.h +++ b/sfall/Modules/ScriptExtender.h @@ -34,6 +34,10 @@ public: static std::string iniConfigFolder; + static void AddTimerEventScripts(fo::Program* script, long time, long param); + static void RemoveTimerEventScripts(fo::Program* script, long param); + static void RemoveTimerEventScripts(fo::Program* script); + // Called before map exit (before map_exit_p_proc handlers in normal scripts) static Delegate<>& OnMapExit(); }; @@ -67,6 +71,7 @@ void SetGlobals(GlobalVar* globals); long SetGlobalVar(const char* var, int val); void SetGlobalVarInt(DWORD var, int val); + long GetGlobalVar(const char* var); long GetGlobalVarInt(DWORD var); long GetGlobalVarInternal(__int64 val); diff --git a/sfall/Modules/Scripting/Handlers/Core.cpp b/sfall/Modules/Scripting/Handlers/Core.cpp index ea0a17e1..750957fb 100644 --- a/sfall/Modules/Scripting/Handlers/Core.cpp +++ b/sfall/Modules/Scripting/Handlers/Core.cpp @@ -184,6 +184,18 @@ void sf_register_hook(OpcodeContext& ctx) { RegisterHook(ctx.program(), ctx.arg(0).rawValue(), proc, specReg); } +void sf_add_g_timer_event(OpcodeContext& ctx) { + ScriptExtender::AddTimerEventScripts(ctx.program(), ctx.arg(0).rawValue(), ctx.arg(1).rawValue()); +} + +void sf_remove_timer_event(OpcodeContext& ctx) { + if (ctx.numArgs() > 0) { + ScriptExtender::RemoveTimerEventScripts(ctx.program(), ctx.arg(0).rawValue()); + } else { + ScriptExtender::RemoveTimerEventScripts(ctx.program()); // remove all + } +} + void sf_sfall_ver_major(OpcodeContext& ctx) { ctx.setReturn(VERSION_MAJOR); } diff --git a/sfall/Modules/Scripting/Handlers/Core.h b/sfall/Modules/Scripting/Handlers/Core.h index f3d76a91..c6682b2c 100644 --- a/sfall/Modules/Scripting/Handlers/Core.h +++ b/sfall/Modules/Scripting/Handlers/Core.h @@ -54,6 +54,10 @@ void __declspec() op_set_self(); // used for both register_hook and register_hook_proc void sf_register_hook(OpcodeContext&); +void sf_add_g_timer_event(OpcodeContext&); + +void sf_remove_timer_event(OpcodeContext&); + void sf_sfall_ver_major(OpcodeContext&); void sf_sfall_ver_minor(OpcodeContext&); diff --git a/sfall/Modules/Scripting/Handlers/Metarule.cpp b/sfall/Modules/Scripting/Handlers/Metarule.cpp index b65209b1..7f41beb2 100644 --- a/sfall/Modules/Scripting/Handlers/Metarule.cpp +++ b/sfall/Modules/Scripting/Handlers/Metarule.cpp @@ -21,6 +21,7 @@ #include "..\Arrays.h" #include "..\OpcodeContext.h" #include "Anims.h" +#include "Core.h" #include "Interface.h" #include "Misc.h" #include "Objects.h" @@ -61,6 +62,7 @@ static MetaruleTableType metaruleTable; static const SfallMetarule metarules[] = { {"add_extra_msg_file", sf_add_extra_msg_file, 1, 2, {ARG_STRING, ARG_INT}}, {"add_iface_tag", sf_add_iface_tag, 0, 0}, + {"add_g_timer_event", sf_add_g_timer_event, 2, 2, {ARG_INT, ARG_INT}}, {"add_trait", sf_add_trait, 1, 1, {ARG_INT}}, {"art_cache_clear", sf_art_cache_flush, 0, 0}, {"attack_is_aimed", sf_attack_is_aimed, 0, 0}, @@ -103,6 +105,7 @@ static const SfallMetarule metarules[] = { {"obj_under_cursor", sf_get_obj_under_cursor, 2, 2, {ARG_INT, ARG_INT}}, {"outlined_object", sf_outlined_object, 0, 0}, {"real_dude_obj", sf_real_dude_obj, 0, 0}, + {"remove_timer_event", sf_remove_timer_event, 0, 1, {ARG_INT}}, {"set_can_rest_on_map", sf_set_rest_on_map, 3, 3, {ARG_INT, ARG_INT, ARG_INT}}, {"set_car_intface_art", sf_set_car_intface_art, 1, 1, {ARG_INT}}, {"set_cursor_mode", sf_set_cursor_mode, 1, 1, {ARG_INT}}, From 99044236413f5b7d2d722c97ee517bdce8cdd6b0 Mon Sep 17 00:00:00 2001 From: NovaRain Date: Thu, 14 Nov 2019 12:49:06 +0800 Subject: [PATCH 03/36] Added "reg_anim_callback" script function(opcode) (#257) Updated documents. --- artifacts/scripting/sfall function notes.txt | 3 +++ artifacts/scripting/sfall opcode list.txt | 1 + sfall/FalloutEngine/Functions_def.h | 1 + sfall/Modules/DebugEditor.cpp | 2 +- sfall/Modules/Scripting/Handlers/Anims.cpp | 9 +++++++++ sfall/Modules/Scripting/Handlers/Anims.h | 2 ++ sfall/Modules/Scripting/Opcodes.cpp | 1 + 7 files changed, 18 insertions(+), 1 deletion(-) diff --git a/artifacts/scripting/sfall function notes.txt b/artifacts/scripting/sfall function notes.txt index 6311862b..052190a5 100644 --- a/artifacts/scripting/sfall function notes.txt +++ b/artifacts/scripting/sfall function notes.txt @@ -218,6 +218,9 @@ Some additional reg_anim_* functions were introduced. They all work in the same > void reg_anim_turn_towards(object, tile/target, delay) - makes object change its direction to face given tile num or target object. +> void reg_anim_callback(procedure proc) +- registers the given procedure and executes it after previously registered animations are finished. + > int/array metarule2_explosions(int arg1, int arg2, int arg3) was made as a dirty easy hack to allow dynamically change some explosion parameters (ranged attack). All changed parameters are reset to vanilla state automatically after each attack action. Following macros are available in sfall.h: diff --git a/artifacts/scripting/sfall opcode list.txt b/artifacts/scripting/sfall opcode list.txt index 79f0026f..7aedf936 100644 --- a/artifacts/scripting/sfall opcode list.txt +++ b/artifacts/scripting/sfall opcode list.txt @@ -358,6 +358,7 @@ 0x827c - any sfall_func6(string funcName, arg1, arg2, arg3, arg4, arg5, arg6) 0x827d - void register_hook_proc_spec(int hook, procedure proc) +0x827e - void reg_anim_callback(procedure proc) * These functions require AllowUnsafeScripting to be enabled in ddraw.ini diff --git a/sfall/FalloutEngine/Functions_def.h b/sfall/FalloutEngine/Functions_def.h index 439c1ce0..1669ae9c 100644 --- a/sfall/FalloutEngine/Functions_def.h +++ b/sfall/FalloutEngine/Functions_def.h @@ -28,6 +28,7 @@ WRAP_WATCOM_FFUNC7(void, make_straight_path_func, GameObject*, objFrom, DWORD, t WRAP_WATCOM_FFUNC4(long, mouse_click_in, long, x, long, y, long, x_end, long, y_end) WRAP_WATCOM_FFUNC3(GameObject*, obj_blocking_at, GameObject*, object, long, tile, long, elevation) WRAP_WATCOM_FFUNC3(long, object_under_mouse, long, crSwitch, long, inclDude, long, elevation) +WRAP_WATCOM_FFUNC4(void, register_object_call, long*, target, long*, source, void*, func, long, delay) WRAP_WATCOM_FFUNC3(long, scr_get_local_var, long, sid, long, varId, long*, value) WRAP_WATCOM_FFUNC3(long, scr_set_local_var, long, sid, long, varId, long, value) WRAP_WATCOM_FFUNC3(long, tile_num_in_direction, long, tile, long, rotation,long, distance) diff --git a/sfall/Modules/DebugEditor.cpp b/sfall/Modules/DebugEditor.cpp index afe5a0dc..d8190f7b 100644 --- a/sfall/Modules/DebugEditor.cpp +++ b/sfall/Modules/DebugEditor.cpp @@ -322,7 +322,7 @@ artNotExist: push artDbgMsg; call fo::funcoffs::debug_printf_; add esp, 8; - BREAKPOINT; // break program + int 3; // break program retn; } } diff --git a/sfall/Modules/Scripting/Handlers/Anims.cpp b/sfall/Modules/Scripting/Handlers/Anims.cpp index 2e5ee370..eda6b737 100644 --- a/sfall/Modules/Scripting/Handlers/Anims.cpp +++ b/sfall/Modules/Scripting/Handlers/Anims.cpp @@ -116,6 +116,15 @@ void sf_reg_anim_turn_towards(OpcodeContext& ctx) { } } +void sf_reg_anim_callback(OpcodeContext& ctx) { + fo::func::register_object_call( + reinterpret_cast(ctx.program()), + reinterpret_cast(ctx.arg(0).rawValue()), // callback procedure + reinterpret_cast(fo::funcoffs::executeProcedure_), + -1 + ); +} + void sf_explosions_metarule(OpcodeContext& ctx) { int mode = ctx.arg(0).asInt(), result = ExplosionsMetaruleFunc(mode, ctx.arg(1).asInt(), ctx.arg(2).asInt()); diff --git a/sfall/Modules/Scripting/Handlers/Anims.h b/sfall/Modules/Scripting/Handlers/Anims.h index 082bd1fc..8191cb65 100644 --- a/sfall/Modules/Scripting/Handlers/Anims.h +++ b/sfall/Modules/Scripting/Handlers/Anims.h @@ -37,6 +37,8 @@ void sf_reg_anim_change_fid(OpcodeContext&); void sf_reg_anim_take_out(OpcodeContext&); void sf_reg_anim_turn_towards(OpcodeContext&); +void sf_reg_anim_callback(OpcodeContext&); + void sf_explosions_metarule(OpcodeContext&); void sf_art_cache_flush(OpcodeContext&); diff --git a/sfall/Modules/Scripting/Opcodes.cpp b/sfall/Modules/Scripting/Opcodes.cpp index fb0f18cd..d64f2855 100644 --- a/sfall/Modules/Scripting/Opcodes.cpp +++ b/sfall/Modules/Scripting/Opcodes.cpp @@ -215,6 +215,7 @@ static SfallOpcodeInfo opcodeInfoArray[] = { {0x27c, "sfall_func6", HandleMetarule, 7, true}, // if you need more arguments - use arrays {0x27d, "register_hook_proc_spec", sf_register_hook, 2, false, {ARG_INT, ARG_INT}}, + {0x27e, "reg_anim_callback", sf_reg_anim_callback, 1, false, {ARG_INT}}, }; // A hash-table for opcode info, indexed by opcode. From 996dd293c01f840f218dd8a7bb238f3199c5116a Mon Sep 17 00:00:00 2001 From: NovaRain Date: Fri, 15 Nov 2019 09:57:31 +0800 Subject: [PATCH 04/36] Tweaked/fixed reg_anim_callback function --- artifacts/scripting/sfall function notes.txt | 2 +- sfall/Modules/HookScripts.cpp | 2 +- sfall/Modules/HookScripts/Common.cpp | 2 +- sfall/Modules/ScriptExtender.cpp | 36 +++++++++++++------- sfall/Modules/ScriptExtender.h | 21 +++++++----- sfall/Modules/Scripting/Handlers/Anims.cpp | 10 +++++- sfall/Modules/Scripting/Handlers/Anims.h | 1 - 7 files changed, 47 insertions(+), 27 deletions(-) diff --git a/artifacts/scripting/sfall function notes.txt b/artifacts/scripting/sfall function notes.txt index 052190a5..bfcfcba5 100644 --- a/artifacts/scripting/sfall function notes.txt +++ b/artifacts/scripting/sfall function notes.txt @@ -219,7 +219,7 @@ Some additional reg_anim_* functions were introduced. They all work in the same - makes object change its direction to face given tile num or target object. > void reg_anim_callback(procedure proc) -- registers the given procedure and executes it after previously registered animations are finished. +- adds the given procedure to an animation sequence-list and executes it in the registered sequence. > int/array metarule2_explosions(int arg1, int arg2, int arg3) was made as a dirty easy hack to allow dynamically change some explosion parameters (ranged attack). All changed parameters are reset to vanilla state automatically after each attack action. Following macros are available in sfall.h: diff --git a/sfall/Modules/HookScripts.cpp b/sfall/Modules/HookScripts.cpp index 3bf3ebed..7ea07aaa 100644 --- a/sfall/Modules/HookScripts.cpp +++ b/sfall/Modules/HookScripts.cpp @@ -183,7 +183,7 @@ void RegisterHook(fo::Program* script, int id, int procNum, bool specReg) { } if (procNum == 0) return; // prevent registration to first location in procedure when reusing "unregister" method - ScriptProgram *prog = GetGlobalScriptProgram(script); + ScriptProgram *prog = ScriptExtender::GetGlobalScriptProgram(script); if (prog) { dlog_f("Global script: %08x registered as hook ID %d\n", DL_HOOK, script, id); HookScript hook; diff --git a/sfall/Modules/HookScripts/Common.cpp b/sfall/Modules/HookScripts/Common.cpp index 102f1979..9f5ae8a0 100644 --- a/sfall/Modules/HookScripts/Common.cpp +++ b/sfall/Modules/HookScripts/Common.cpp @@ -48,7 +48,7 @@ bool LoadHookScript(const char* name, int id) { hook.callback = -1; hook.isGlobalScript = false; hooks[id].push_back(hook); - AddProgramToMap(prog); + ScriptExtender::AddProgramToMap(prog); } else { dlogr(" Error!", DL_HOOK); } diff --git a/sfall/Modules/ScriptExtender.cpp b/sfall/Modules/ScriptExtender.cpp index 9bad83f0..d683b42f 100644 --- a/sfall/Modules/ScriptExtender.cpp +++ b/sfall/Modules/ScriptExtender.cpp @@ -122,11 +122,17 @@ bool alwaysFindScripts; fo::ScriptInstance overrideScriptStruct = {0}; -static const DWORD scr_ptr_back = fo::funcoffs::scr_ptr_ + 5; -static const DWORD scr_find_sid_from_program = fo::funcoffs::scr_find_sid_from_program_ + 5; -static const DWORD scr_find_obj_from_program = fo::funcoffs::scr_find_obj_from_program_ + 7; +long ScriptExtender::GetScriptReturnValue() { + return overrideScriptStruct.returnValue; +} -static DWORD _stdcall FindSid(fo::Program* script) { +long ScriptExtender::GetResetScriptReturnValue() { + long val = GetScriptReturnValue(); + overrideScriptStruct.returnValue = 0; + return val; +} + +static DWORD __stdcall FindSid(fo::Program* script) { std::unordered_map::iterator overrideIt = selfOverrideMap.find(script); if (overrideIt != selfOverrideMap.end()) { DWORD scriptId = overrideIt->second.object->scriptId; // script @@ -147,12 +153,18 @@ static DWORD _stdcall FindSid(fo::Program* script) { } else { overrideScriptStruct.fixedParam = 0; } - overrideScriptStruct.targetObject = overrideScriptStruct.selfObject = 0; + overrideScriptStruct.targetObject = 0; + overrideScriptStruct.selfObject = 0; + overrideScriptStruct.returnValue = 0; return -2; // override struct } return -1; // change nothing } +static const DWORD scr_ptr_back = fo::funcoffs::scr_ptr_ + 5; +static const DWORD scr_find_sid_from_program = fo::funcoffs::scr_find_sid_from_program_ + 5; +//static const DWORD scr_find_obj_from_program = fo::funcoffs::scr_find_obj_from_program_ + 7; + static void __declspec(naked) FindSidHack() { __asm { push eax; @@ -439,11 +451,11 @@ void InitScriptProgram(ScriptProgram &prog) { } } -void AddProgramToMap(ScriptProgram &prog) { +void ScriptExtender::AddProgramToMap(ScriptProgram &prog) { sfallProgsMap[prog.ptr] = prog; } -ScriptProgram* GetGlobalScriptProgram(fo::Program* scriptPtr) { +ScriptProgram* ScriptExtender::GetGlobalScriptProgram(fo::Program* scriptPtr) { SfallProgsMap::iterator it = sfallProgsMap.find(scriptPtr); return (it == sfallProgsMap.end()) ? nullptr : &it->second ; // prog } @@ -469,7 +481,7 @@ static void LoadGlobalScriptsList() { GlobalScript gscript = GlobalScript(prog); gscript.startProc = prog.procLookup[fo::ScriptProc::start]; // get 'start' procedure position globalScripts.push_back(gscript); - AddProgramToMap(prog); + ScriptExtender::AddProgramToMap(prog); // initialize script (start proc will be executed for the first time) -- this needs to be after script is added to "globalScripts" array InitScriptProgram(prog); } else { @@ -559,19 +571,17 @@ void RunScriptProc(ScriptProgram* prog, const char* procName) { void RunScriptProc(ScriptProgram* prog, long procId) { if (procId > 0 && procId < fo::ScriptProc::count) { - fo::Program* sptr = prog->ptr; int procNum = prog->procLookup[procId]; if (procNum != -1) { - fo::func::executeProcedure(sptr, procNum); + fo::func::executeProcedure(prog->ptr, procNum); } } } int RunScriptStartProc(ScriptProgram* prog) { - fo::Program* sptr = prog->ptr; int procNum = prog->procLookup[fo::ScriptProc::start]; if (procNum != -1) { - fo::func::executeProcedure(sptr, procNum); + fo::func::executeProcedure(prog->ptr, procNum); } return procNum; } @@ -651,7 +661,7 @@ static long HandleTimedEventScripts() { } if (wereRunning) { for (auto _it = timerEventScripts.cbegin(); _it != timerIt; _it++) { - fo::func::dev_printf("\n[TimedEventScripts] delete events: %d", _it->time); + fo::func::dev_printf("\n[TimedEventScripts] delete event: %d", _it->time); } timerEventScripts.erase(timerEventScripts.cbegin(), timerIt); } diff --git a/sfall/Modules/ScriptExtender.h b/sfall/Modules/ScriptExtender.h index 2d24ad2a..43ad45da 100644 --- a/sfall/Modules/ScriptExtender.h +++ b/sfall/Modules/ScriptExtender.h @@ -27,6 +27,12 @@ namespace sfall { +typedef struct { + fo::Program* ptr = nullptr; + int procLookup[fo::ScriptProc::count]; + char initialized; +} ScriptProgram; + class ScriptExtender : public Module { public: const char* name() { return "ScriptExtender"; } @@ -34,6 +40,12 @@ public: static std::string iniConfigFolder; + static long GetScriptReturnValue(); + static long GetResetScriptReturnValue(); + + static void AddProgramToMap(ScriptProgram &prog); + static ScriptProgram* GetGlobalScriptProgram(fo::Program* scriptPtr); + static void AddTimerEventScripts(fo::Program* script, long time, long param); static void RemoveTimerEventScripts(fo::Program* script, long param); static void RemoveTimerEventScripts(fo::Program* script); @@ -50,12 +62,6 @@ struct GlobalVar { }; #pragma pack(pop) -typedef struct { - fo::Program* ptr = nullptr; - int procLookup[fo::ScriptProc::count]; - char initialized; -} ScriptProgram; - void __fastcall SetGlobalScriptRepeat(fo::Program* script, int frames); void __fastcall SetGlobalScriptType(fo::Program* script, int type); bool _stdcall IsGameScript(const char* filename); @@ -97,9 +103,6 @@ void RunScriptProc(ScriptProgram* prog, long procId); int RunScriptStartProc(ScriptProgram* prog); -void AddProgramToMap(ScriptProgram &prog); -ScriptProgram* GetGlobalScriptProgram(fo::Program* scriptPtr); - // variables extern DWORD isGlobalScriptLoading; extern DWORD availableGlobalScriptTypes; diff --git a/sfall/Modules/Scripting/Handlers/Anims.cpp b/sfall/Modules/Scripting/Handlers/Anims.cpp index eda6b737..1f12cf2c 100644 --- a/sfall/Modules/Scripting/Handlers/Anims.cpp +++ b/sfall/Modules/Scripting/Handlers/Anims.cpp @@ -19,6 +19,7 @@ #include "..\..\..\FalloutEngine\Fallout2.h" #include "..\..\..\SafeWrite.h" #include "..\..\Explosions.h" +#include "..\..\ScriptExtender.h" #include "..\OpcodeContext.h" #include "Anims.h" @@ -116,11 +117,18 @@ void sf_reg_anim_turn_towards(OpcodeContext& ctx) { } } +static void __declspec(naked) ExecuteCallback() { + __asm { + call fo::funcoffs::executeProcedure_; + jmp ScriptExtender::GetResetScriptReturnValue; + } +} + void sf_reg_anim_callback(OpcodeContext& ctx) { fo::func::register_object_call( reinterpret_cast(ctx.program()), reinterpret_cast(ctx.arg(0).rawValue()), // callback procedure - reinterpret_cast(fo::funcoffs::executeProcedure_), + reinterpret_cast(ExecuteCallback), -1 ); } diff --git a/sfall/Modules/Scripting/Handlers/Anims.h b/sfall/Modules/Scripting/Handlers/Anims.h index 8191cb65..4e494b3d 100644 --- a/sfall/Modules/Scripting/Handlers/Anims.h +++ b/sfall/Modules/Scripting/Handlers/Anims.h @@ -36,7 +36,6 @@ void sf_reg_anim_light(OpcodeContext&); void sf_reg_anim_change_fid(OpcodeContext&); void sf_reg_anim_take_out(OpcodeContext&); void sf_reg_anim_turn_towards(OpcodeContext&); - void sf_reg_anim_callback(OpcodeContext&); void sf_explosions_metarule(OpcodeContext&); From 18c10d8911f10ad78e549cc0e316a93f505ef41e Mon Sep 17 00:00:00 2001 From: NovaRain Date: Sat, 16 Nov 2019 22:36:53 +0800 Subject: [PATCH 05/36] Added four new script functions: "get_sfall_arg_at", "show_window", "hide_window", "set_window_flag" Improved the functionality of "inventory_redraw" function. --- artifacts/scripting/headers/define_extra.h | 8 +- artifacts/scripting/headers/sfall.h | 4 + artifacts/scripting/hookscripts.txt | 5 +- artifacts/scripting/sfall function notes.txt | 23 +++++- sfall/FalloutEngine/Structs.h | 19 +++++ sfall/FalloutEngine/VariableOffsets.h | 1 + sfall/FalloutEngine/Variables_def.h | 3 +- sfall/Modules/HookScripts.cpp | 14 ++-- sfall/Modules/HookScripts.h | 13 ++-- sfall/Modules/HookScripts/InventoryHs.cpp | 2 +- sfall/Modules/Scripting/Handlers/Core.cpp | 21 +++-- sfall/Modules/Scripting/Handlers/Core.h | 2 + .../Modules/Scripting/Handlers/Interface.cpp | 76 ++++++++++++++++++- sfall/Modules/Scripting/Handlers/Interface.h | 6 ++ sfall/Modules/Scripting/Handlers/Metarule.cpp | 8 +- 15 files changed, 179 insertions(+), 26 deletions(-) diff --git a/artifacts/scripting/headers/define_extra.h b/artifacts/scripting/headers/define_extra.h index 2de1909c..88e5c9d5 100644 --- a/artifacts/scripting/headers/define_extra.h +++ b/artifacts/scripting/headers/define_extra.h @@ -87,7 +87,6 @@ #define FLAG_SEEN (0x40000000) #define FLAG_SHOOTTHRU (0x80000000) - /* Critter Flags */ #define CFLG_BARTER 2 // 0x00000002 - Barter (can trade with) #define CFLG_NOSTEAL 32 // 0x00000020 - Steal (cannot steal from) @@ -101,6 +100,13 @@ #define CFLG_RANGED 8192 // 0x00002000 - Range (melee attack is possible at a distance) #define CFLG_NOKNOCKDOWN 16384 // 0x00004000 - Knock (cannot be knocked down) +/* Window Flags */ +#define WIN_FLAG_MOVEONTOP (0x4) +#define WIN_FLAG_HIDDEN (0x8) +#define WIN_FLAG_EXCLUSIVE (0x10) +#define WIN_FLAG_TRANSPARENT (0x20) + + //remove inven obj defines #define RMOBJ_CONSUME_DRUG 4666772 #define RMOBJ_CONTAINER 4683293 // same as RMOBJ_TRADE diff --git a/artifacts/scripting/headers/sfall.h b/artifacts/scripting/headers/sfall.h index fa3dff18..d616f8c1 100644 --- a/artifacts/scripting/headers/sfall.h +++ b/artifacts/scripting/headers/sfall.h @@ -277,9 +277,11 @@ #define get_object_ai_data(obj, aiParam) sfall_func2("get_object_ai_data", obj, aiParam) #define get_object_data(obj, offset) sfall_func2("get_object_data", obj, offset) #define get_outline(obj) sfall_func1("get_outline", obj) +#define get_sfall_arg_at(argNum) sfall_func1("get_sfall_arg_at", argNum) #define get_string_pointer(text) sfall_func1("get_string_pointer", text) #define has_fake_perk_npc(npc, perk) sfall_func2("has_fake_perk_npc", npc, perk) #define has_fake_trait_npc(npc, trait) sfall_func2("has_fake_trait_npc", npc, trait) +#define hide_window(winName) sfall_func1("hide_window", winName) #define intface_hide sfall_func0("intface_hide") #define intface_is_hidden sfall_func0("intface_is_hidden") #define intface_redraw sfall_func0("intface_redraw") @@ -312,6 +314,8 @@ #define set_unique_id(obj) sfall_func1("set_unique_id", obj) #define unset_unique_id(obj) sfall_func2("set_unique_id", obj, -1) #define set_unjam_locks_time(time) sfall_func1("set_unjam_locks_time", time) +#define set_window_flag(winID, flag, value) sfall_func3("set_window_flag", winID, flag, value) +#define show_window(winName) sfall_func1("show_window", winName) #define spatial_radius(obj) sfall_func1("spatial_radius", obj) #define tile_refresh_display sfall_func0("tile_refresh_display") #define unjam_lock(obj) sfall_func1("unjam_lock", obj) diff --git a/artifacts/scripting/hookscripts.txt b/artifacts/scripting/hookscripts.txt index 9e601a0d..8fdf0500 100644 --- a/artifacts/scripting/hookscripts.txt +++ b/artifacts/scripting/hookscripts.txt @@ -30,6 +30,7 @@ The hook script equivalent of game_loaded; it returns 2 when the script is first > mixed get_sfall_arg() Gets the next argument from sfall. Each time it's called it returns the next argument, or otherwise it returns 0 if there are no more arguments left. +You can arbitrarily get the value of any argument using the sfall_func1("get_sfall_arg_at", argNum) function. > array get_sfall_args() Returns all hook arguments as a new temp array. @@ -37,8 +38,8 @@ Returns all hook arguments as a new temp array. > void set_sfall_return(int value) Used to return the new values from the script. Each time it's called it sets the next value, or if you've already set all return values it does nothing. -> void set_sfall_arg(int argnum, int value) -Changes argument value. The argument number (argnum) is 0-indexed. This is useful if you have several hook scripts attached to one hook point (see below). +> void set_sfall_arg(int argNum, int value) +Changes argument value. The argument number (argNum) is 0-indexed. This is useful if you have several hook scripts attached to one hook point (see below). > void register_hook(int hooktype) Used from a normal global script if you want to run it at the same point a full hook script would normally run. In case of this function, "start" proc will be executed in a current global script. You can use all above functions like normal. diff --git a/artifacts/scripting/sfall function notes.txt b/artifacts/scripting/sfall function notes.txt index bfcfcba5..ea82897d 100644 --- a/artifacts/scripting/sfall function notes.txt +++ b/artifacts/scripting/sfall function notes.txt @@ -517,9 +517,10 @@ Some utility/math functions are available: - adds one custom box to the current boxes, and returns the number of the added tag (-1 if the tags limit is exceeded) - The maximum number of boxes is limited to 126 tags +> void sfall_func0("inventory_redraw") > void sfall_func1("inventory_redraw", int invSide) - 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) +- invSide specifies which side needs to be redrawn: 0 - the player, 1 - target (container/NPC in loot/barter screens), -1 - both sides > void sfall_func3("item_make_explosive", int pid, int activePid, int damage) > void sfall_func4("item_make_explosive", int pid, int activePid, int min, int max) @@ -647,6 +648,26 @@ optional argument: > void sfall_func1("remove_timer_event", int param) - removes all timer events with the specified 'param' value for the current global script +> mixed sfall_func1("get_sfall_arg_at", int argNum) +- gets the value of hook argument with the specified argument number (argNum, first argument starts from 0) + +> void sfall_func0("hide_window") +> void sfall_func1("hide_window", string winName) +- hides the specified or currently selected/active script window +- winName: the window name, assigned to the window by the CreateWin/create_win function + +> void sfall_func0("show_window") +> void sfall_func1("show_window", string winName) +- displays the specified hidden script window or the one previously hidden with the sfall_func0("hide_window") function +- winName: the window name, assigned to the window by the CreateWin/create_win function + +> void sfall_func3("set_window_flag", string/int winName/winID, int flag, bool value) +- changes the specified flag for the created script or game interface window +- winName: the window name, assigned to the window by the CreateWin/create_win function +- winID: the ID number of the interface or script window obtained with the get_window_under_mouse function +- flag: the flag to change (see WIN_FLAG_* constants in define_extra.h) +- value: true - set the flag, false - unset the flag + ------------------------ ------ MORE INFO ------- ------------------------ diff --git a/sfall/FalloutEngine/Structs.h b/sfall/FalloutEngine/Structs.h index 2ec0339f..e7153248 100644 --- a/sfall/FalloutEngine/Structs.h +++ b/sfall/FalloutEngine/Structs.h @@ -713,6 +713,25 @@ struct Window { long *drawFunc; }; +struct sWindow { + char name[32]; + long wID; + long width; + long height; + long unknown1; + long unknown2; + long unknown3; + long unknown4; + long *buttons; + long numButtons; + long unknown5; + long unknown6; + long clearColour; + long flags; + long unknown7; + long unknown8; +}; + #pragma pack(1) struct LSData { char signature[24]; diff --git a/sfall/FalloutEngine/VariableOffsets.h b/sfall/FalloutEngine/VariableOffsets.h index e71bbc8a..3cd61264 100644 --- a/sfall/FalloutEngine/VariableOffsets.h +++ b/sfall/FalloutEngine/VariableOffsets.h @@ -212,6 +212,7 @@ #define FO_VAR_stack_offset 0x59E844 #define FO_VAR_stat_data 0x51D53C #define FO_VAR_stat_flag 0x66452A +#define FO_VAR_sWindows 0x6727B0 #define FO_VAR_Tag_ 0x5708B0 #define FO_VAR_tag_skill 0x668070 #define FO_VAR_target_curr_stack 0x59E948 diff --git a/sfall/FalloutEngine/Variables_def.h b/sfall/FalloutEngine/Variables_def.h index 34d02728..c06edc8d 100644 --- a/sfall/FalloutEngine/Variables_def.h +++ b/sfall/FalloutEngine/Variables_def.h @@ -158,7 +158,7 @@ VAR_(pipmesg, DWORD) VAR_(preload_list_index, DWORD) VARA(procTableStrs, const char*, (int)ScriptProc::count) // table of procId (from define.h) => procName map VARA(proto_msg_files, MessageList, 6) // array of 6 elements -VAR_(proto_main_msg_file, MessageList) +VAR_(proto_main_msg_file, MessageList) VAR_(ptable, DWORD) VAR_(pud, DWORD) VAR_(queue, DWORD) @@ -181,6 +181,7 @@ VARA(stack, DWORD, 10) VARA(stack_offset, DWORD, 10) VARA(stat_data, StatInfo, STAT_real_max_stat) // dynamic array VAR_(stat_flag, DWORD) +VARA(sWindows, fo::sWindow, 16) VAR_(Tag_, DWORD) VAR_(tag_skill, DWORD) VAR_(target_curr_stack, DWORD) diff --git a/sfall/Modules/HookScripts.cpp b/sfall/Modules/HookScripts.cpp index 7ea07aaa..bad7d618 100644 --- a/sfall/Modules/HookScripts.cpp +++ b/sfall/Modules/HookScripts.cpp @@ -133,23 +133,27 @@ void HookScripts::GameModeChangeHook(DWORD exit) { } // END HOOKS -DWORD _stdcall GetHSArgCount() { +DWORD HookScripts::GetHSArgCount() { return argCount; } -DWORD _stdcall GetHSArg() { +DWORD HookScripts::GetHSArg() { return (cArg == argCount) ? 0 : args[cArg++]; } -void SetHSArg(DWORD id, DWORD value) { +void HookScripts::SetHSArg(DWORD id, DWORD value) { if (id < argCount) args[id] = value; } -DWORD* GetHSArgs() { +DWORD* HookScripts::GetHSArgs() { return args; } -void _stdcall SetHSReturn(DWORD value) { +DWORD HookScripts::GetHSArgAt(DWORD id) { + return args[id]; +} + +void __stdcall HookScripts::SetHSReturn(DWORD value) { if (cRetTmp < maxRets) { rets[cRetTmp++] = value; } diff --git a/sfall/Modules/HookScripts.h b/sfall/Modules/HookScripts.h index 75c0f8f1..2f5910ad 100644 --- a/sfall/Modules/HookScripts.h +++ b/sfall/Modules/HookScripts.h @@ -84,13 +84,14 @@ public: static void GameModeChangeHook(DWORD exit); static void KeyPressHook(DWORD* dxKey, bool pressed, DWORD vKey); -}; -DWORD _stdcall GetHSArgCount(); -DWORD _stdcall GetHSArg(); -DWORD* GetHSArgs(); -void SetHSArg(DWORD id, DWORD value); -void _stdcall SetHSReturn(DWORD d); + static DWORD GetHSArgCount(); + static DWORD GetHSArg(); + static DWORD GetHSArgAt(DWORD id); + static DWORD* GetHSArgs(); + static void SetHSArg(DWORD id, DWORD value); + static void __stdcall SetHSReturn(DWORD d); +}; // register hook by proc num (special values: -1 - use default (start) procedure, 0 - unregister) void RegisterHook(fo::Program* script, int id, int procNum, bool specReg); diff --git a/sfall/Modules/HookScripts/InventoryHs.cpp b/sfall/Modules/HookScripts/InventoryHs.cpp index febe5bac..4720c1db 100644 --- a/sfall/Modules/HookScripts/InventoryHs.cpp +++ b/sfall/Modules/HookScripts/InventoryHs.cpp @@ -80,7 +80,7 @@ static int __fastcall SwitchHandHook_Script(fo::GameObject* item, fo::GameObject int result = PartyControl::SwitchHandHook(item); if (result != -1) { cRetTmp = 0; - SetHSReturn(result); + HookScripts::SetHSReturn(result); } result = (cRet > 0) ? rets[0] : -1; EndHook(); diff --git a/sfall/Modules/Scripting/Handlers/Core.cpp b/sfall/Modules/Scripting/Handlers/Core.cpp index 750957fb..f77e396f 100644 --- a/sfall/Modules/Scripting/Handlers/Core.cpp +++ b/sfall/Modules/Scripting/Handlers/Core.cpp @@ -109,7 +109,7 @@ void __declspec(naked) op_get_sfall_arg() { __asm { push ecx; push eax; - call GetHSArg; + call HookScripts::GetHSArg; mov edx, eax; pop eax; _RET_VAL_INT(ecx); @@ -118,10 +118,21 @@ void __declspec(naked) op_get_sfall_arg() { } } +void sf_get_sfall_arg_at(OpcodeContext& ctx) { + long argVal = 0; + long id = ctx.arg(0).rawValue(); + if (id >= static_cast(HookScripts::GetHSArgCount()) || id < 0) { + ctx.printOpcodeError("%s() - invalid value for argument.", ctx.getMetaruleName()); + } else { + argVal = HookScripts::GetHSArgAt(id); + } + ctx.setReturn(argVal); +} + void sf_get_sfall_args(OpcodeContext& ctx) { - DWORD argCount = GetHSArgCount(); + DWORD argCount = HookScripts::GetHSArgCount(); DWORD id = TempArray(argCount, 0); - DWORD* args = GetHSArgs(); + DWORD* args = HookScripts::GetHSArgs(); for (DWORD i = 0; i < argCount; i++) { arrays[id].val[i].set(*(long*)&args[i]); } @@ -129,7 +140,7 @@ void sf_get_sfall_args(OpcodeContext& ctx) { } void sf_set_sfall_arg(OpcodeContext& ctx) { - SetHSArg(ctx.arg(0).rawValue(), ctx.arg(1).rawValue()); + HookScripts::SetHSArg(ctx.arg(0).rawValue(), ctx.arg(1).rawValue()); } void __declspec(naked) op_set_sfall_return() { @@ -137,7 +148,7 @@ void __declspec(naked) op_set_sfall_return() { push ecx; _GET_ARG_INT(end); push eax; - call SetHSReturn; + call HookScripts::SetHSReturn; end: pop ecx; retn; diff --git a/sfall/Modules/Scripting/Handlers/Core.h b/sfall/Modules/Scripting/Handlers/Core.h index c6682b2c..4289138a 100644 --- a/sfall/Modules/Scripting/Handlers/Core.h +++ b/sfall/Modules/Scripting/Handlers/Core.h @@ -41,6 +41,8 @@ void sf_get_sfall_global_float(OpcodeContext&); void __declspec() op_get_sfall_arg(); +void sf_get_sfall_arg_at(OpcodeContext&); + void sf_get_sfall_args(OpcodeContext&); void sf_set_sfall_arg(OpcodeContext&); diff --git a/sfall/Modules/Scripting/Handlers/Interface.cpp b/sfall/Modules/Scripting/Handlers/Interface.cpp index 8af61eaa..b8c4663c 100644 --- a/sfall/Modules/Scripting/Handlers/Interface.cpp +++ b/sfall/Modules/Scripting/Handlers/Interface.cpp @@ -318,10 +318,12 @@ void sf_inventory_redraw(OpcodeContext& ctx) { default: return; } - if (!ctx.arg(0).asBool()) { + long redrawSide = (ctx.numArgs() > 0) ? ctx.arg(0).rawValue() : -1; // -1 - both + if (redrawSide <= 0) { fo::var::stack_offset[fo::var::curr_stack] = 0; fo::func::display_inventory(0, -1, mode); - } else if (mode >= 2) { + } + if (redrawSide && mode >= 2) { fo::var::target_stack_offset[fo::var::target_curr_stack] = 0; fo::func::display_target_inventory(0, -1, fo::var::target_pud, mode); fo::func::win_draw(fo::var::i_wid); @@ -350,6 +352,76 @@ void sf_create_win(OpcodeContext& ctx) { } } +void sf_show_window(OpcodeContext& ctx) { + if (ctx.numArgs() > 0) { + const char* name = ctx.arg(0).strValue(); + for (size_t i = 0; i < 16; i++) { + if (_stricmp(name, fo::var::sWindows[i].name) == 0) { + fo::func::win_show(fo::var::sWindows[i].wID); + return; + } + } + ctx.printOpcodeError("%s() - window '%s' is not found.", ctx.getMetaruleName(), name); + } else { + __asm call fo::funcoffs::windowShow_; + } +} + +void sf_hide_window(OpcodeContext& ctx) { + if (ctx.numArgs() > 0) { + const char* name = ctx.arg(0).strValue(); + for (size_t i = 0; i < 16; i++) { + if (_stricmp(name, fo::var::sWindows[i].name) == 0) { + fo::func::win_hide(fo::var::sWindows[i].wID); + return; + } + } + ctx.printOpcodeError("%s() - window '%s' is not found.", ctx.getMetaruleName(), name); + } else { + __asm call fo::funcoffs::windowHide_; + } +} + +void sf_set_window_flag(OpcodeContext& ctx) { + long bitFlag = ctx.arg(1).rawValue(); + switch (bitFlag) { + case fo::WinFlags::MoveOnTop: + case fo::WinFlags::Hidden: + case fo::WinFlags::Exclusive: + case fo::WinFlags::Transparent: + break; + default: + return; // unsupported set flag + } + bool mode = ctx.arg(2).asBool(); + if (ctx.arg(0).isString()) { + const char* name = ctx.arg(0).strValue(); + for (size_t i = 0; i < 16; i++) { + if (_stricmp(name, fo::var::sWindows[i].name) == 0) { + fo::Window* win =fo::func::GNW_find(fo::var::sWindows[i].wID); + if (mode) { + fo::var::sWindows[i].flags |= bitFlag; + win->flags |= bitFlag; + } else { + fo::var::sWindows[i].flags &= ~bitFlag; + win->flags &= ~bitFlag; + } + return; + } + } + ctx.printOpcodeError("%s() - window '%s' is not found.", ctx.getMetaruleName(), name); + } else { + long wid = ctx.arg(0).rawValue(); + fo::Window* win = fo::func::GNW_find((wid > 0) ? wid : fo::var::i_wid); // i_wid - set flag to current game interface window + if (win == nullptr) return; + if (mode) { + win->flags |= bitFlag; + } else { + win->flags &= ~bitFlag; + } + } +} + static void DrawImage(OpcodeContext& ctx, bool isScaled) { if (*(DWORD*)FO_VAR_currentWindow == -1) { ctx.printOpcodeError("%s() - no created/selected window for the image.", ctx.getMetaruleName()); diff --git a/sfall/Modules/Scripting/Handlers/Interface.h b/sfall/Modules/Scripting/Handlers/Interface.h index f1bfd5ac..5623608b 100644 --- a/sfall/Modules/Scripting/Handlers/Interface.h +++ b/sfall/Modules/Scripting/Handlers/Interface.h @@ -99,6 +99,12 @@ void sf_dialog_message(OpcodeContext&); void sf_create_win(OpcodeContext&); +void sf_show_window(OpcodeContext&); + +void sf_hide_window(OpcodeContext&); + +void sf_set_window_flag(OpcodeContext&); + void sf_draw_image(OpcodeContext&); void sf_draw_image_scaled(OpcodeContext&); diff --git a/sfall/Modules/Scripting/Handlers/Metarule.cpp b/sfall/Modules/Scripting/Handlers/Metarule.cpp index 7f41beb2..8a55a855 100644 --- a/sfall/Modules/Scripting/Handlers/Metarule.cpp +++ b/sfall/Modules/Scripting/Handlers/Metarule.cpp @@ -88,14 +88,16 @@ static const SfallMetarule metarules[] = { {"get_object_ai_data", sf_get_object_ai_data, 2, 2, {ARG_OBJECT, ARG_INT}}, {"get_object_data", sf_get_object_data, 2, 2, {ARG_OBJECT, ARG_INT}}, {"get_outline", sf_get_outline, 1, 1, {ARG_OBJECT}}, + {"get_sfall_arg_at", sf_get_sfall_arg_at, 1, 1, {ARG_INT}}, {"get_string_pointer", sf_get_string_pointer, 1, 1, {ARG_STRING}}, {"has_fake_perk_npc", sf_has_fake_perk_npc, 2, 2, {ARG_OBJECT, ARG_STRING}}, {"has_fake_trait_npc", sf_has_fake_trait_npc, 2, 2, {ARG_OBJECT, ARG_STRING}}, + {"hide_window", sf_hide_window, 0, 1, {ARG_STRING}}, {"intface_hide", sf_intface_hide, 0, 0}, {"intface_is_hidden", sf_intface_is_hidden, 0, 0}, {"intface_redraw", sf_intface_redraw, 0, 0}, {"intface_show", sf_intface_show, 0, 0}, - {"inventory_redraw", sf_inventory_redraw, 1, 1, {ARG_INT}}, + {"inventory_redraw", sf_inventory_redraw, 0, 1, {ARG_INT}}, {"item_make_explosive", sf_item_make_explosive, 3, 4, {ARG_INT, ARG_INT, ARG_INT, ARG_INT}}, {"item_weight", sf_item_weight, 1, 1, {ARG_OBJECT}}, {"lock_is_jammed", sf_lock_is_jammed, 1, 1, {ARG_OBJECT}}, @@ -124,6 +126,8 @@ static const SfallMetarule metarules[] = { {"set_selectable_perk_npc", sf_set_selectable_perk_npc, 5, 5, {ARG_OBJECT, ARG_STRING, ARG_INT, ARG_INT, ARG_STRING}}, {"set_unique_id", sf_set_unique_id, 1, 2, {ARG_OBJECT, ARG_INT}}, {"set_unjam_locks_time", sf_set_unjam_locks_time, 1, 1, {ARG_INT}}, + {"set_window_flag", sf_set_window_flag, 3, 3, {ARG_INTSTR, ARG_INT, ARG_INT}}, + {"show_window", sf_show_window, 0, 1, {ARG_STRING}}, {"spatial_radius", sf_spatial_radius, 1, 1, {ARG_OBJECT}}, {"tile_refresh_display", sf_tile_refresh_display, 0, 0}, {"unjam_lock", sf_unjam_lock, 1, 1, {ARG_OBJECT}}, @@ -132,7 +136,7 @@ static const SfallMetarule metarules[] = { {"validate_test", sf_test, 2, 5, {ARG_INT, ARG_NUMBER, ARG_STRING, ARG_OBJECT, ARG_ANY}}, #endif }; - +// // returns current contents of metarule table static void sf_get_metarule_table(OpcodeContext& ctx) { DWORD arrId = TempArray(metaruleTable.size(), 0); From e93d878d1d77753128e6b6f9d9fd5ad3b6cbf2f6 Mon Sep 17 00:00:00 2001 From: NovaRain Date: Sun, 17 Nov 2019 08:32:03 +0800 Subject: [PATCH 06/36] Added a new hook: HOOK_STDPROCEDURE_END * as an extension to HOOK_STDPROCEDURE hook script. Updated documents. --- artifacts/scripting/headers/sfall.h | 13 +++--- artifacts/scripting/hookscripts.txt | 15 ++++--- artifacts/scripting/sfall function notes.txt | 25 ++++++----- sfall/Modules/HookScripts.cpp | 1 + sfall/Modules/HookScripts.h | 1 + sfall/Modules/HookScripts/ObjectHs.cpp | 46 +++++++++++++++++--- sfall/Modules/HookScripts/ObjectHs.h | 1 + 7 files changed, 70 insertions(+), 32 deletions(-) diff --git a/artifacts/scripting/headers/sfall.h b/artifacts/scripting/headers/sfall.h index d616f8c1..748de311 100644 --- a/artifacts/scripting/headers/sfall.h +++ b/artifacts/scripting/headers/sfall.h @@ -63,6 +63,7 @@ #define HOOK_SETLIGHTING (38) #define HOOK_SNEAK (39) #define HOOK_STDPROCEDURE (40) +#define HOOK_STDPROCEDURE_END (41) //Valid arguments to list_begin #define LIST_CRITTERS (0) @@ -249,9 +250,9 @@ // sfall_funcX macros #define add_extra_msg_file(name) sfall_func1("add_extra_msg_file", name) -#define add_global_timer_event(time, param) sfall_func2("add_g_timer_event", time, param) +#define add_global_timer_event(time, fixedParam) sfall_func2("add_g_timer_event", time, fixedParam) #define add_iface_tag sfall_func0("add_iface_tag") -#define add_trait(traitId) sfall_func1("add_trait", traitId) +#define add_trait(traitID) sfall_func1("add_trait", traitID) #define art_cache_clear sfall_func0("art_cache_clear") #define attack_is_aimed sfall_func0("attack_is_aimed") #define car_gas_amount sfall_func0("car_gas_amount") @@ -287,17 +288,17 @@ #define intface_redraw sfall_func0("intface_redraw") #define intface_show sfall_func0("intface_show") #define inventory_redraw(invSide) sfall_func1("inventory_redraw", invSide) -#define item_make_explosive(pid, aPid, min, max) sfall_func4("item_make_explosive", pid, aPid, min, max) +#define item_make_explosive(pid, activePid, min, max) sfall_func4("item_make_explosive", pid, activePid, min, max) #define item_weight(obj) sfall_func1("item_weight", obj) #define lock_is_jammed(obj) sfall_func1("lock_is_jammed", obj) #define loot_obj sfall_func0("loot_obj") -#define metarule_exist(metarule) sfall_func1("metarule_exist", metarule) +#define metarule_exist(metaruleName) sfall_func1("metarule_exist", metaruleName) #define npc_engine_level_up(toggle) sfall_func1("npc_engine_level_up", toggle) -#define obj_under_cursor(crSwitch, inclDude) sfall_func2("obj_under_cursor", crSwitch, inclDude) +#define obj_under_cursor(onlyCritter, includeDude) sfall_func2("obj_under_cursor", onlyCritter, includeDude) #define outlined_object sfall_func0("outlined_object") #define real_dude_obj sfall_func0("real_dude_obj") #define remove_all_timer_events sfall_func0("remove_timer_event") -#define remove_timer_event(param) sfall_func1("remove_timer_event", param) +#define remove_timer_event(fixedParam) sfall_func1("remove_timer_event", fixedParam) #define set_can_rest_on_map(map, elev, value) sfall_func3("set_can_rest_on_map", map, elev, value) #define set_car_intface_art(artIndex) sfall_func1("set_car_intface_art", artIndex) #define set_cursor_mode(mode) sfall_func1("set_cursor_mode", mode) diff --git a/artifacts/scripting/hookscripts.txt b/artifacts/scripting/hookscripts.txt index 8fdf0500..882d70ad 100644 --- a/artifacts/scripting/hookscripts.txt +++ b/artifacts/scripting/hookscripts.txt @@ -286,10 +286,10 @@ int ret1 - the new AP cost ------------------------------------------- (DEPRECATED) -hs_hexmoveblocking.int -hs_hexaiblocking.int -hs_hexshootblocking.int -hs_hexsightblocking.int +HOOK_HEXMOVEBLOCKING (hs_hexmoveblocking.int) +HOOK_HEXAIBLOCKING (hs_hexaiblocking.int) +HOOK_HEXSHOOTBLOCKING (hs_hexshootblocking.int) +HOOK_HEXSIGHTBLOCKING (hs_hexsightblocking.int) Runs when checking to see if a hex blocks movement or shooting. (or ai-ing, presumably...) @@ -636,13 +636,14 @@ int ret2 - overrides the duration time for the current result ------------------------------------------- -HOOK_STDPROCEDURE (hs_stdprocedure.int) +HOOK_STDPROCEDURE, HOOK_STDPROCEDURE_END (hs_stdprocedure.int) -Runs before Fallout executes a standard procedure (handler) in any script of any object. +Runs before or after Fallout engine executes a standard procedure (handler) in any script of any object. NOTE: this hook will not be executed for "start", critter_p_proc, timed_event_p_proc, and map_update_p_proc procedures. int arg1 - the number of the standard script handler (see define.h) Obj arg2 - the object that owns this handler (self_obj) Obj arg3 - the object that called this handler (source_obj, can be 0) +int arg4 - 1 after procedure execution (for HOOK_STDPROCEDURE_END), 0 otherwise -int ret1 - pass -1 to cancel the execution of the handler +int ret1 - pass -1 to cancel the execution of the handler (only for HOOK_STDPROCEDURE) diff --git a/artifacts/scripting/sfall function notes.txt b/artifacts/scripting/sfall function notes.txt index ea82897d..fbdf1c49 100644 --- a/artifacts/scripting/sfall function notes.txt +++ b/artifacts/scripting/sfall function notes.txt @@ -520,7 +520,7 @@ Some utility/math functions are available: > void sfall_func0("inventory_redraw") > void sfall_func1("inventory_redraw", int invSide) - 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 +- 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) > void sfall_func3("item_make_explosive", int pid, int activePid, int damage) > void sfall_func4("item_make_explosive", int pid, int activePid, int min, int max) @@ -559,10 +559,11 @@ Some utility/math functions are available: > object sfall_func0("dialog_obj") - returns a pointer to the object (critter) the player is having a conversation or bartering with -> object sfall_func2("obj_under_cursor", bool crSwitch, bool inclDude) +> object sfall_func2("obj_under_cursor", bool onlyCritter, bool includeDude) - returns the object under the cursor on the main game screen -- crSwitch: True - only checks critters and ignores their cover (roof tiles, walls, scenery, etc.), False - checks all objects (can't check critters under objects) -- passing False to the inclDude argument will ignore dude_obj +- onlyCritter: True - only checks critters and ignores their cover (roof tiles, walls, scenery, etc.) + False - checks all objects (can't check critters under objects) +- passing False to the includeDude argument will ignore dude_obj > object sfall_func0("loot_obj") - returns a pointer to the target object (container or critter) of the loot screen @@ -631,29 +632,29 @@ optional argument: - can take off player's equipped item when the inventory is opened, or the player is in the barter screen - slot: 0 - armor slot, 1 - right slot, 2 - left slot (see INVEN_TYPE_* in define.h) -> void sfall_func1("add_trait", int traitId) +> void sfall_func1("add_trait", int traitID) - adds the specified trait to the player > int sfall_func0("get_inven_ap_cost") - returns the current AP cost to access the inventory in combat -> void sfall_func2("add_g_timer_event", int time, int param) +> void sfall_func2("add_g_timer_event", int time, int fixedParam) - adds a timer event that calls the timed_event_p_proc procedure in the current global script - time: the number of ticks after which the event timer is triggered -- param: the value that is passed to the timed_event_p_proc procedure for the fixed_param function +- fixedParam: the value that is passed to the timed_event_p_proc procedure for the fixed_param function > void sfall_func0("remove_timer_event") - clears all set timer events for the current global script -> void sfall_func1("remove_timer_event", int param) -- removes all timer events with the specified 'param' value for the current global script +> void sfall_func1("remove_timer_event", int fixedParam) +- removes all timer events with the specified 'fixedParam' value for the current global script > mixed sfall_func1("get_sfall_arg_at", int argNum) -- gets the value of hook argument with the specified argument number (argNum, first argument starts from 0) +- gets the value of hook argument with the specified argument number (argNum, first argument of hook starts from 0) > void sfall_func0("hide_window") > void sfall_func1("hide_window", string winName) -- hides the specified or currently selected/active script window +- hides the specified or currently active (selected) script window - winName: the window name, assigned to the window by the CreateWin/create_win function > void sfall_func0("show_window") @@ -664,7 +665,7 @@ optional argument: > void sfall_func3("set_window_flag", string/int winName/winID, int flag, bool value) - changes the specified flag for the created script or game interface window - winName: the window name, assigned to the window by the CreateWin/create_win function -- winID: the ID number of the interface or script window obtained with the get_window_under_mouse function +- winID: the ID number of the interface or script window obtained with the get_window_under_mouse function, or 0 for the current game interface - flag: the flag to change (see WIN_FLAG_* constants in define_extra.h) - value: true - set the flag, false - unset the flag diff --git a/sfall/Modules/HookScripts.cpp b/sfall/Modules/HookScripts.cpp index bad7d618..1a8a50ae 100644 --- a/sfall/Modules/HookScripts.cpp +++ b/sfall/Modules/HookScripts.cpp @@ -91,6 +91,7 @@ static HooksInjectInfo injectHooks[] = { {HOOK_SETLIGHTING, Inject_SetLightingHook, false}, {HOOK_SNEAK, Inject_SneakCheckHook, false}, {HOOK_STDPROCEDURE, Inject_ScriptProcedureHook, false}, + {HOOK_STDPROCEDURE_END, Inject_ScriptProcedureHook2, false}, }; bool HookScripts::injectAllHooks; diff --git a/sfall/Modules/HookScripts.h b/sfall/Modules/HookScripts.h index 2f5910ad..0cc9eb86 100644 --- a/sfall/Modules/HookScripts.h +++ b/sfall/Modules/HookScripts.h @@ -67,6 +67,7 @@ enum HookType HOOK_SETLIGHTING = 38, HOOK_SNEAK = 39, HOOK_STDPROCEDURE = 40, + HOOK_STDPROCEDURE_END = 41, HOOK_COUNT }; diff --git a/sfall/Modules/HookScripts/ObjectHs.cpp b/sfall/Modules/HookScripts/ObjectHs.cpp index edc05be0..682b3ff0 100644 --- a/sfall/Modules/HookScripts/ObjectHs.cpp +++ b/sfall/Modules/HookScripts/ObjectHs.cpp @@ -72,7 +72,7 @@ static void __declspec(naked) UseObjHook() { mov args[4], edx; // object pushad; } - + argCount = 2; RunHookScript(HOOK_USEOBJ); @@ -237,16 +237,23 @@ skip: static DWORD __fastcall StdProcedureHook_Script(long numHandler, fo::ScriptInstance* script, DWORD procTable) { BeginHook(); - argCount = 3; + argCount = 4; args[0] = numHandler; args[1] = (DWORD)script->selfObject; args[2] = (DWORD)script->sourceObject; - RunHookScript(HOOK_STDPROCEDURE); - if (cRet > 0) { - long retval = rets[0]; - if (retval == -1) procTable = retval; + if (procTable) { + args[3] = 0; + RunHookScript(HOOK_STDPROCEDURE); + + if (cRet > 0) { + long retval = rets[0]; + if (retval == -1) procTable = retval; + } + } else { + args[3] = 1; + RunHookScript(HOOK_STDPROCEDURE_END); } EndHook(); return procTable; @@ -278,6 +285,26 @@ end: } } +static void __declspec(naked) After_ScriptStdProcedureHook() { + using namespace fo::ScriptProc; + __asm { + call fo::funcoffs::executeProcedure_; + cmp ecx, critter_p_proc; + je skip; + cmp ecx, timed_event_p_proc; + je skip; + cmp ecx, map_update_p_proc; + je skip; + cmp ecx, start; + jle skip; + mov edx, [esp + 0x28 - 0x18 + 4]; // script + push 0; // procTable + call StdProcedureHook_Script; // ecx - numHandler +skip: + retn; + } +} + void Inject_UseObjOnHook() { HookCalls(UseObjOnHook, { 0x49C606, 0x473619 }); @@ -311,6 +338,10 @@ void Inject_ScriptProcedureHook() { MakeCall(0x4A491F, ScriptStdProcedureHook); } +void Inject_ScriptProcedureHook2() { + HookCall(0x4A49A7, After_ScriptStdProcedureHook); +} + void InitObjectHookScripts() { LoadHookScript("hs_useobjon", HOOK_USEOBJON); @@ -318,7 +349,8 @@ void InitObjectHookScripts() { LoadHookScript("hs_useanimobj", HOOK_USEANIMOBJ); LoadHookScript("hs_descriptionobj", HOOK_DESCRIPTIONOBJ); LoadHookScript("hs_setlighting", HOOK_SETLIGHTING); - LoadHookScript("hs_stdprocedure", HOOK_STDPROCEDURE); + LoadHookScript("hs_stdprocedure", HOOK_STDPROCEDURE); // combo hook + LoadHookScript("hs_stdprocedure", HOOK_STDPROCEDURE_END); } } diff --git a/sfall/Modules/HookScripts/ObjectHs.h b/sfall/Modules/HookScripts/ObjectHs.h index f14084be..832f00df 100644 --- a/sfall/Modules/HookScripts/ObjectHs.h +++ b/sfall/Modules/HookScripts/ObjectHs.h @@ -10,4 +10,5 @@ namespace sfall void Inject_DescriptionObjHook(); void Inject_SetLightingHook(); void Inject_ScriptProcedureHook(); + void Inject_ScriptProcedureHook2(); } From f0f134d07784cb63350d5d506736c901f727aa90 Mon Sep 17 00:00:00 2001 From: NovaRain Date: Tue, 19 Nov 2019 14:19:58 +0800 Subject: [PATCH 07/36] Fixed a crash bug in CorpseLineOfFireFix (from commit 521b6c7) --- sfall/FalloutEngine/Functions.h | 2 +- sfall/Modules/BugFixes.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sfall/FalloutEngine/Functions.h b/sfall/FalloutEngine/Functions.h index f4488f6f..d2138d15 100644 --- a/sfall/FalloutEngine/Functions.h +++ b/sfall/FalloutEngine/Functions.h @@ -45,7 +45,7 @@ const char* __stdcall critter_name(GameObject* critter); // Change the name of playable character void critter_pc_set_name(const char* newName); -// Checks if given file exist in DB +// Checks if given file exists in DB bool __stdcall db_access(const char* fileName); long __stdcall db_fclose(DbFile* file); diff --git a/sfall/Modules/BugFixes.cpp b/sfall/Modules/BugFixes.cpp index f1d85247..90c45d01 100644 --- a/sfall/Modules/BugFixes.cpp +++ b/sfall/Modules/BugFixes.cpp @@ -2451,7 +2451,7 @@ static void __declspec(naked) obj_shoot_blocking_at_hack1() { call fo::funcoffs::critter_is_dead_; test eax, eax; jz endLoop; - jmp CorpseShotBlockFix_continue_loop[1]; + jmp CorpseShotBlockFix_continue_loop[4]; endLoop: mov eax, [edx]; pop ebp; From 5053ab564dad23ba8a507a2019158da3c4e34dee Mon Sep 17 00:00:00 2001 From: NovaRain Date: Wed, 20 Nov 2019 10:14:30 +0800 Subject: [PATCH 08/36] Added a new argument to HOOK_GAMEMODECHANGE * now the previous game mode can be checked with arg2 (#211) Fixed GAMEMODECHANGE hook being triggered even when the game mode is not changed. Added 'SPECIAL' flag to the game mode functions (when switching from DIALOG mode to BARTER). --- artifacts/scripting/headers/sfall.h | 2 +- artifacts/scripting/hookscripts.txt | 1 + sfall/Modules/HookScripts.cpp | 18 ++++++++++++------ sfall/Modules/LoadGameHook.cpp | 13 ++++++++++++- sfall/Modules/LoadGameHook.h | 6 +++--- 5 files changed, 29 insertions(+), 11 deletions(-) diff --git a/artifacts/scripting/headers/sfall.h b/artifacts/scripting/headers/sfall.h index 748de311..ad5c4e6a 100644 --- a/artifacts/scripting/headers/sfall.h +++ b/artifacts/scripting/headers/sfall.h @@ -1,6 +1,5 @@ //Recognised modes for set_shader_mode and get_game_mode #define WORLDMAP (0x1) -#define LOCALMAP (0x2) //No point hooking this: would always be 1 at any point at which scripts are running #define DIALOG (0x4) #define ESCMENU (0x8) #define SAVEGAME (0x10) @@ -20,6 +19,7 @@ #define HEROWIN (0x40000) #define DIALOGVIEW (0x80000) #define COUNTERWIN (0x100000) // counter window for moving multiple items or setting a timer +#define SPECIAL (0x80000000) //Valid arguments to register_hook #define HOOK_TOHIT (0) diff --git a/artifacts/scripting/hookscripts.txt b/artifacts/scripting/hookscripts.txt index 882d70ad..da2ce2ef 100644 --- a/artifacts/scripting/hookscripts.txt +++ b/artifacts/scripting/hookscripts.txt @@ -514,6 +514,7 @@ HOOK_GAMEMODECHANGE (hs_gamemodechange.int) Runs once every time when the game mode was changed, like opening/closing the inventory, character screen, pipboy, etc. int arg1 - event type: 1 - when the player exits the game, 0 - otherwise +int arg2 - the previous game mode ------------------------------------------- diff --git a/sfall/Modules/HookScripts.cpp b/sfall/Modules/HookScripts.cpp index 1a8a50ae..6c812360 100644 --- a/sfall/Modules/HookScripts.cpp +++ b/sfall/Modules/HookScripts.cpp @@ -124,13 +124,18 @@ void _stdcall MouseClickHook(DWORD button, bool pressed) { EndHook(); } +static unsigned long previousGameMode = 0; + void HookScripts::GameModeChangeHook(DWORD exit) { - if (!HookHasScript(HOOK_GAMEMODECHANGE)) return; - BeginHook(); - argCount = 1; - args[0] = exit; - RunHookScript(HOOK_GAMEMODECHANGE); - EndHook(); + if (HookHasScript(HOOK_GAMEMODECHANGE)) { + BeginHook(); + argCount = 2; + args[0] = exit; + args[1] = previousGameMode; + RunHookScript(HOOK_GAMEMODECHANGE); + EndHook(); + } + previousGameMode = GetLoopFlags(); } // END HOOKS @@ -237,6 +242,7 @@ void HookScriptClear() { hooks[i].clear(); } memset(hooksInfo, 0, HOOK_COUNT * sizeof(HooksPositionInfo)); + previousGameMode = 0; } void LoadHookScripts() { diff --git a/sfall/Modules/LoadGameHook.cpp b/sfall/Modules/LoadGameHook.cpp index 5138b5ee..1c3cdf8b 100644 --- a/sfall/Modules/LoadGameHook.cpp +++ b/sfall/Modules/LoadGameHook.cpp @@ -105,12 +105,13 @@ static void __stdcall GameModeChange(DWORD state) { } void _stdcall SetInLoop(DWORD mode, LoopFlag flag) { + DWORD _flag = GetLoopFlags(); if (mode) { SetLoopFlag(flag); } else { ClearLoopFlag(flag); } - GameModeChange(0); + if (GetLoopFlags() != _flag) GameModeChange(0); } void GetSavePath(char* buf, char* ftype) { @@ -579,6 +580,7 @@ static void __declspec(naked) LootContainerHook() { static void __declspec(naked) BarterInventoryHook() { __asm { + and inLoop, ~SPECIAL; // unset flag _InLoop(1, BARTER); push [esp + 4]; call fo::funcoffs::barter_inventory_; @@ -638,6 +640,13 @@ static void __declspec(naked) exit_move_timer_win_Hook() { } } +static void __declspec(naked) gdialog_bk_hook() { + __asm { + _InLoop2(1, SPECIAL); + jmp fo::funcoffs::gdialog_window_destroy_; + } +} + void LoadGameHook::init() { saveInCombatFix = GetConfigInt("Misc", "SaveInCombatFix", 1); if (saveInCombatFix > 2) saveInCombatFix = 0; @@ -697,6 +706,8 @@ void LoadGameHook::init() { HookCall(0x445D30, DialogReviewExitHook); HookCall(0x476AC6, setup_move_timer_win_Hook); // before init win HookCall(0x477067, exit_move_timer_win_Hook); + + HookCall(0x447A7E, gdialog_bk_hook); // Set the Special flag before animating the dialog interface when switching from dialog mode to barter } Delegate<>& LoadGameHook::OnBeforeGameInit() { diff --git a/sfall/Modules/LoadGameHook.h b/sfall/Modules/LoadGameHook.h index a93b6f8c..a9198726 100644 --- a/sfall/Modules/LoadGameHook.h +++ b/sfall/Modules/LoadGameHook.h @@ -71,9 +71,9 @@ DWORD InCombat(); DWORD InDialog(); -enum LoopFlag : unsigned long { +enum LoopFlag : long { WORLDMAP = 1 << 0, // 0x1 - LOCALMAP = 1 << 1, // 0x2 No point hooking this: would always be 1 at any point at which scripts are running +// RESERVED = 1 << 1, // 0x2 (unused) DIALOG = 1 << 2, // 0x4 ESCMENU = 1 << 3, // 0x8 SAVEGAME = 1 << 4, // 0x10 @@ -94,7 +94,7 @@ enum LoopFlag : unsigned long { DIALOGVIEW = 1 << 19, // 0x80000 COUNTERWIN = 1 << 20, // 0x100000 Counter window for moving multiple items or setting a timer - // RESERVED = 1 << 31 + SPECIAL = 1 << 31 // 0x80000000 Additional special flag for all modes }; DWORD GetLoopFlags(); From 0c42f6447de24aedc0c2ada84a9f71be5c613d97 Mon Sep 17 00:00:00 2001 From: NovaRain Date: Thu, 21 Nov 2019 00:45:10 +0800 Subject: [PATCH 09/36] Restored and fixed RemoveWindowRounding option Reorganized ddraw.ini a bit. --- artifacts/ddraw.ini | 68 ++++++++++++---------- sfall/Modules/MiscPatches.cpp | 8 +++ sfall/Modules/ScriptExtender.cpp | 10 ++-- sfall/Modules/Scripting/Handlers/Anims.cpp | 2 +- 4 files changed, 52 insertions(+), 36 deletions(-) diff --git a/artifacts/ddraw.ini b/artifacts/ddraw.ini index d50af44c..e3d40dbf 100644 --- a/artifacts/ddraw.ini +++ b/artifacts/ddraw.ini @@ -6,7 +6,7 @@ UseCommandLine=0 ;Uncomment and point to a file to get alternate translations for some sfall messages -;TranslationsINI=.\Translations.ini +;TranslationsINI=sfall\Translations.ini ;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX [ExtraPatches] @@ -288,9 +288,6 @@ Movie17=credits.mve ;LocalMapXLimit=480 ;LocalMapYLimit=400 -;To add additional perks to the game, uncomment the next line and set it to point to a file containing perk information -;PerksFile=Perks.ini - ;Set to 1 if you want the pipboy to be available at the start of the game ;Set to 2 to make the pipboy available by only skipping the vault suit movie check PipBoyAvailableAtGameStart=0 @@ -341,9 +338,6 @@ OverrideArtCacheSize=0 ;Set to 2 to block all saving in combat SaveInCombatFix=1 -;Point to an ini file containing elevator data -;ElevatorsFile=Elevators.ini - ;Uncomment and set a comma delimited list of numbers to use a custom xp table. ;Player's level is capped once the highest specified level is reached ;XPTable=50,100,200 @@ -372,8 +366,6 @@ AdditionalWeaponAnims=1 ;If the ExtraKillTypes option is enabled, this should be set to 3, with containing entries for any new types ;Must be non-zero to use the edit/get/reset_critical script functions OverrideCriticalTable=2 -;To change the path and filename of the critical table file, uncomment the next line -;OverrideCriticalFile=CriticalOverrides.ini ;Set to 1 to get notification of karma changes in the notification window DisplayKarmaChanges=0 @@ -435,6 +427,9 @@ Doctor=293 Science=293 Repair=293 +;Set to 1 to remove window position rounding for script-created windows +RemoveWindowRounding=1 + ;Set to 1 to add scroll buttons to the pipboy quest list, and remove the quests per area limit ;Set to 2 to use a different set of scroll buttons UseScrollingQuestsList=1 @@ -455,7 +450,7 @@ UseScrollingQuestsList=1 ;CityRepsList=0:47,2:48,1:49,4:50,5:51,3:52,8:53,6:54,7:55,13:56,10:57,11:59,14:61,17:63,19:64,18:65,25:66,9:294,20:308 ;Set this to a valid path to save a copy of the console contents -;ConsoleOutputPath="bingle.txt" +;ConsoleOutputPath=console.txt ;Set to 1 to add additional pages of save slots ExtraSaveSlots=0 @@ -522,18 +517,6 @@ FastShotFix=1 ;Set to 1 to boost the maximum number of script names from 1450 to 10000 BoostScriptDialogLimit=0 -;Allows you to edit the skill tables -;Point the next line to an ini file containing the replacement skill data -;SkillsFile=Skills.ini - -;To change the relationship between SPECIAL stats and derived stats, uncomment the next line -;See the Stats.ini in the modders pack for an example file -;DerivedStats=Stats.ini - -;Allows you to change some parameters for drugs and their addictions -;See the Drugs.ini in the modders pack for an example file -;DrugsFile=Drugs.ini - ;These options modify the checks to see if a critter can carry an additional item, changing which items are counted towards the weight limit and adding an additional size check ;Set the mode to 0 to disable the size check, 1 to apply to the PC only, 2 to apply to the PC and party members, or 3 to apply to all critters ;Only the PC uses CritterInvSizeLimit. Other critters will use the unused stat (STAT_unused = 10) or have the size limit of 100 if the stat is not set @@ -614,17 +597,13 @@ ExplosionsEmitLight=0 ;MovieTimer_artimer3=270 ;MovieTimer_artimer4=360 -;Set to 1 to enable the new arrays behavior +;Set to 1 to enable the new arrays behavior (default behavior) ;Set to 0 for backward compatibility with pre-3.4 scripts arraysBehavior=1 ;Set to 1 to add proper checks if there is enough ammo to use weapons that use multiple ammo per shot CheckWeaponAmmoCost=0 -;To add additional books to the game, uncomment the next line and point to a file containing book information -;See the Books.ini in the modders pack for an example file -;BooksFile=Books.ini - ;Controls the speed of combat panel animations (lower - faster; valid range: 0..65535) CombatPanelAnimDelay=1000 ;Controls the speed of dialog panel animations (lower - faster; valid range: 0..255) @@ -723,6 +702,35 @@ KnockoutTime=35 ;Set to 1 to display sfall built-in credits at the bottom of credits.txt contents instead of at the top CreditsAtBottom=0 +;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +; Configuration ini files +;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + +;To change the path and filename of the critical table file, uncomment the next line +;OverrideCriticalFile=CriticalOverrides.ini + +;To change the relationship between SPECIAL stats and derived stats, uncomment the next line +;See the Stats.ini in the modders pack for an example file +;DerivedStats=Stats.ini + +;Allows you to edit the skill tables +;Point the next line to an ini file containing the replacement skill data +;SkillsFile=Skills.ini + +;To add additional perks to the game, uncomment the next line and set it to point to a file containing perk information +;PerksFile=Perks.ini + +;To add additional books to the game, uncomment the next line and point to a file containing book information +;See the Books.ini in the modders pack for an example file +;BooksFile=Books.ini + +;Allows you to change some parameters for drugs and their addictions +;See the Drugs.ini in the modders pack for an example file +;DrugsFile=Drugs.ini + +;Point to an ini file containing elevator data +;ElevatorsFile=Elevators.ini + ;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX [Scripts] ;Comma-separated list of masked paths to load global scripts from @@ -733,7 +741,7 @@ GlobalScriptPaths=scripts\gl*.int,scripts\sfall\gl*.int ;Uncomment the option to specify a common folder path for all ini files used by scripts ;You will have to put all the available ini files of the mods in this directory ;The maximum length of the specified path is 61 characters -;IniConfigFolder=IniConfig +;IniConfigFolder=mods\iniConfigs ;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX [Debugging] @@ -755,8 +763,8 @@ Enable=0 ;show_script_messages=1 ;show_tile_num=1 ;[sound] -;debug=1 -;debug_sfxc=1 +;debug=0 +;debug_sfxc=0 ;------- DebugMode=0 diff --git a/sfall/Modules/MiscPatches.cpp b/sfall/Modules/MiscPatches.cpp index 4990fd9a..52d57f7a 100644 --- a/sfall/Modules/MiscPatches.cpp +++ b/sfall/Modules/MiscPatches.cpp @@ -466,6 +466,13 @@ void AlwaysReloadMsgs() { } } +void RemoveWindowRoundingPatch() { + if (GetConfigInt("Misc", "RemoveWindowRounding", 1)) { + SafeWriteBatch(0xEB, {0x4D6EDD, 0x4D6F12}); + //SafeWrite16(0x4B8090, 0x04EB); // jmps 0x4B8096 (old) + } +} + void InventoryCharacterRotationSpeedPatch() { DWORD setting = GetConfigInt("Misc", "SpeedInventoryPCRotation", 166); if (setting != 166 && setting <= 1000) { @@ -666,6 +673,7 @@ void MiscPatches::init() { PlayIdleAnimOnReloadPatch(); SkilldexImagesPatch(); + RemoveWindowRoundingPatch(); ScienceOnCrittersPatch(); InventoryCharacterRotationSpeedPatch(); diff --git a/sfall/Modules/ScriptExtender.cpp b/sfall/Modules/ScriptExtender.cpp index d683b42f..7eb7ed9c 100644 --- a/sfall/Modules/ScriptExtender.cpp +++ b/sfall/Modules/ScriptExtender.cpp @@ -604,9 +604,8 @@ static void ResetStateAfterFrame() { } static inline void RunGlobalScripts(int mode1, int mode2) { - if (idle > -1 && idle <= 127) { - Sleep(idle); - } + if (idle > -1) Sleep(idle); + for (DWORD d = 0; d < globalScripts.size(); d++) { if (globalScripts[d].repeat && (globalScripts[d].mode == mode1 || globalScripts[d].mode == mode2) @@ -800,9 +799,10 @@ void ScriptExtender::init() { } idle = GetConfigInt("Misc", "ProcessorIdle", -1); - if (idle > -1 && idle <= 127) { + if (idle > -1) { + if (idle > 127) idle = 127; fo::var::idle_func = reinterpret_cast(Sleep); - SafeWrite8(0x4C9F12, 0x6A); // push + SafeWrite8(0x4C9F12, 0x6A); // push idle SafeWrite8(0x4C9F13, idle); } diff --git a/sfall/Modules/Scripting/Handlers/Anims.cpp b/sfall/Modules/Scripting/Handlers/Anims.cpp index 1f12cf2c..4ac42aa8 100644 --- a/sfall/Modules/Scripting/Handlers/Anims.cpp +++ b/sfall/Modules/Scripting/Handlers/Anims.cpp @@ -120,7 +120,7 @@ void sf_reg_anim_turn_towards(OpcodeContext& ctx) { static void __declspec(naked) ExecuteCallback() { __asm { call fo::funcoffs::executeProcedure_; - jmp ScriptExtender::GetResetScriptReturnValue; + jmp ScriptExtender::GetResetScriptReturnValue; // return callback result from scr_return script function: -1 - break registered sequence } } From 977e6a5ad7539287024bff2a06587e13aaa16bb6 Mon Sep 17 00:00:00 2001 From: NovaRain Date: Sat, 23 Nov 2019 20:18:06 +0800 Subject: [PATCH 10/36] Added "get_text_width" and "string_compare" script functions Fixed the return value of charcode script function for characters in the extended ASCII character set. Rewrote set_dm/df_model, set_movie_path functions in C++. --- artifacts/scripting/headers/sfall.h | 5 +- artifacts/scripting/sfall function notes.txt | 13 +- sfall/FalloutEngine/EngineUtils.cpp | 5 +- sfall/FalloutEngine/EngineUtils.h | 2 +- sfall/Modules/Scripting/Handlers/Metarule.cpp | 4 +- sfall/Modules/Scripting/Handlers/Misc.cpp | 157 +++--------------- sfall/Modules/Scripting/Handlers/Misc.h | 8 +- sfall/Modules/Scripting/Handlers/Utils.cpp | 98 +++++++++-- sfall/Modules/Scripting/Handlers/Utils.h | 6 +- sfall/Modules/Scripting/OpcodeContext.cpp | 6 +- sfall/Modules/Scripting/OpcodeContext.h | 12 +- sfall/Modules/Scripting/Opcodes.cpp | 11 +- 12 files changed, 155 insertions(+), 172 deletions(-) diff --git a/artifacts/scripting/headers/sfall.h b/artifacts/scripting/headers/sfall.h index ad5c4e6a..52a11781 100644 --- a/artifacts/scripting/headers/sfall.h +++ b/artifacts/scripting/headers/sfall.h @@ -280,6 +280,7 @@ #define get_outline(obj) sfall_func1("get_outline", obj) #define get_sfall_arg_at(argNum) sfall_func1("get_sfall_arg_at", argNum) #define get_string_pointer(text) sfall_func1("get_string_pointer", text) +#define get_text_width(text) sfall_func1("get_text_width", text) #define has_fake_perk_npc(npc, perk) sfall_func2("has_fake_perk_npc", npc, perk) #define has_fake_trait_npc(npc, trait) sfall_func2("has_fake_trait_npc", npc, trait) #define hide_window(winName) sfall_func1("hide_window", winName) @@ -313,13 +314,15 @@ #define set_rest_heal_time(time) sfall_func1("set_rest_heal_time", time) #define set_rest_mode(mode) sfall_func1("set_rest_mode", mode) #define set_unique_id(obj) sfall_func1("set_unique_id", obj) -#define unset_unique_id(obj) sfall_func2("set_unique_id", obj, -1) #define set_unjam_locks_time(time) sfall_func1("set_unjam_locks_time", time) #define set_window_flag(winID, flag, value) sfall_func3("set_window_flag", winID, flag, value) #define show_window(winName) sfall_func1("show_window", winName) #define spatial_radius(obj) sfall_func1("spatial_radius", obj) +#define string_compare(str1, str2) sfall_func2("string_compare", str1, str2) +#define string_compare_locale(str1, str2, codePage) sfall_func3("string_compare", str1, str2, codePage) #define tile_refresh_display sfall_func0("tile_refresh_display") #define unjam_lock(obj) sfall_func1("unjam_lock", obj) +#define unset_unique_id(obj) sfall_func2("set_unique_id", obj, -1) #define unwield_slot(critter, slot) sfall_func2("unwield_slot", critter, slot) #define set_fake_perk_npc(npc, perk, level, image, desc) sfall_func5("set_fake_perk_npc", npc, perk, level, image, desc) diff --git a/artifacts/scripting/sfall function notes.txt b/artifacts/scripting/sfall function notes.txt index fbdf1c49..4a7ee35b 100644 --- a/artifacts/scripting/sfall function notes.txt +++ b/artifacts/scripting/sfall function notes.txt @@ -601,8 +601,8 @@ Some utility/math functions are available: > void sfall_func5("draw_image", string/int pathFile/artId, int frame, int x, int y, bool noTransparent) > void sfall_func6("draw_image_scaled", string/int pathFile/artId, int frame, int x, int y, int width, int height) -- displays the specified FRM image in the active window created by vanilla CreateWin or sfall's create_win script function -- pathFile/artId: path to the FRM file (e.g. "art\\inven\\5mmap.frm"), or its FRM ID number (e.g. 117440550, see specification of the FID format) +- displays the specified PCX or FRM image in the active window created by vanilla CreateWin or sfall's create_win script function +- pathFile/artId: path to the PCX/FRM file (e.g. "art\\inven\\5mmap.frm"), or its FRM ID number (e.g. 117440550, see specification of the FID format) optional arguments: - frame: frame number, the first frame starts from zero - x/y: offset relative to the top-left corner of the window @@ -669,6 +669,15 @@ optional argument: - flag: the flag to change (see WIN_FLAG_* constants in define_extra.h) - value: true - set the flag, false - unset the flag +> int sfall_func1("get_text_width", string text) +- returns the text width in pixels for the currently set font + +> bool sfall_func2("string_compare", string str1, string str2) +> bool sfall_func3("string_compare", string str1, string str2, int codePage) +- compares two strings case-insensitive, and returns True if the two strings are matched +- codePage: code page number to properly compare national characters in the range 128-255 of the ASCII code table + available encodings: 1250-1252, 866 + ------------------------ ------ MORE INFO ------- ------------------------ diff --git a/sfall/FalloutEngine/EngineUtils.cpp b/sfall/FalloutEngine/EngineUtils.cpp index 17e7c3dc..7c481ec9 100644 --- a/sfall/FalloutEngine/EngineUtils.cpp +++ b/sfall/FalloutEngine/EngineUtils.cpp @@ -211,14 +211,11 @@ DWORD GetTextHeight() { //--------------------------------------------------------- //gets the length of a string using the currently selected font -DWORD GetTextWidth(char *TextMsg) { - DWORD TxtWidth; +DWORD GetTextWidth(const char *TextMsg) { __asm { mov eax, TextMsg; call dword ptr ds:[FO_VAR_text_width]; //get text width - mov TxtWidth, eax; } - return TxtWidth; } //--------------------------------------------------------- diff --git a/sfall/FalloutEngine/EngineUtils.h b/sfall/FalloutEngine/EngineUtils.h index 8832ac18..dbe177b9 100644 --- a/sfall/FalloutEngine/EngineUtils.h +++ b/sfall/FalloutEngine/EngineUtils.h @@ -81,7 +81,7 @@ void PrintText(char *displayText, BYTE colorIndex, DWORD x, DWORD y, DWORD textW // gets the height of the currently selected font DWORD GetTextHeight(); // gets the length of a string using the currently selected font -DWORD GetTextWidth(char *textMsg); +DWORD GetTextWidth(const char *textMsg); // get width of Char for current font DWORD GetCharWidth(char charVal); // get maximum string length for current font - if all characters were maximum width diff --git a/sfall/Modules/Scripting/Handlers/Metarule.cpp b/sfall/Modules/Scripting/Handlers/Metarule.cpp index 8a55a855..01e749ca 100644 --- a/sfall/Modules/Scripting/Handlers/Metarule.cpp +++ b/sfall/Modules/Scripting/Handlers/Metarule.cpp @@ -90,6 +90,7 @@ static const SfallMetarule metarules[] = { {"get_outline", sf_get_outline, 1, 1, {ARG_OBJECT}}, {"get_sfall_arg_at", sf_get_sfall_arg_at, 1, 1, {ARG_INT}}, {"get_string_pointer", sf_get_string_pointer, 1, 1, {ARG_STRING}}, + {"get_text_width", sf_get_text_width, 1, 1}, {"has_fake_perk_npc", sf_has_fake_perk_npc, 2, 2, {ARG_OBJECT, ARG_STRING}}, {"has_fake_trait_npc", sf_has_fake_trait_npc, 2, 2, {ARG_OBJECT, ARG_STRING}}, {"hide_window", sf_hide_window, 0, 1, {ARG_STRING}}, @@ -129,6 +130,7 @@ static const SfallMetarule metarules[] = { {"set_window_flag", sf_set_window_flag, 3, 3, {ARG_INTSTR, ARG_INT, ARG_INT}}, {"show_window", sf_show_window, 0, 1, {ARG_STRING}}, {"spatial_radius", sf_spatial_radius, 1, 1, {ARG_OBJECT}}, + {"string_compare", sf_string_compare, 2, 3, {ARG_STRING, ARG_STRING, ARG_INT}}, {"tile_refresh_display", sf_tile_refresh_display, 0, 0}, {"unjam_lock", sf_unjam_lock, 1, 1, {ARG_OBJECT}}, {"unwield_slot", sf_unwield_slot, 2, 2, {ARG_OBJECT, ARG_INT}}, @@ -136,7 +138,7 @@ static const SfallMetarule metarules[] = { {"validate_test", sf_test, 2, 5, {ARG_INT, ARG_NUMBER, ARG_STRING, ARG_OBJECT, ARG_ANY}}, #endif }; -// + // returns current contents of metarule table static void sf_get_metarule_table(OpcodeContext& ctx) { DWORD arrId = TempArray(metaruleTable.size(), 0); diff --git a/sfall/Modules/Scripting/Handlers/Misc.cpp b/sfall/Modules/Scripting/Handlers/Misc.cpp index ce06d7fc..1072056a 100644 --- a/sfall/Modules/Scripting/Handlers/Misc.cpp +++ b/sfall/Modules/Scripting/Handlers/Misc.cpp @@ -42,129 +42,35 @@ namespace sfall namespace script { -static DWORD defaultMaleModelNamePtr = (DWORD)defaultMaleModelName; -static DWORD defaultFemaleModelNamePtr = (DWORD)defaultFemaleModelName; -static DWORD movieNamesPtr = (DWORD)MoviePaths; +const char* stringTooLong = "%s() - the string length exceeds maximum of 64 characters."; - -//// *** End Helios *** /// -static void _stdcall strcpy_p(char* to, const char* from) { - strcpy_s(to, 64, from); +void sf_set_dm_model(OpcodeContext& ctx) { + auto model = ctx.arg(0).strValue(); + if (strlen(model) > 64) { + ctx.printOpcodeError(stringTooLong, ctx.getOpcodeName()); + return; + } + strcpy(defaultMaleModelName, model); } -/************************************************************************/ -/* TODO: Rewrite these raw handlers using OpcodeContext */ -/************************************************************************/ - -void __declspec(naked) op_set_dm_model() { - __asm { - push ebx; - push ecx; - push edx; - push edi; - mov edi, eax; - call fo::funcoffs::interpretPopShort_; - mov edx, eax; - mov eax, edi; - call fo::funcoffs::interpretPopLong_; - cmp dx, VAR_TYPE_STR2; - jz next; - cmp dx, VAR_TYPE_STR; - jnz end; -next: - mov ebx, eax; - mov eax, edi; - call fo::funcoffs::interpretGetString_; - push eax; - push defaultMaleModelNamePtr; - call strcpy_p; -end: - pop edi; - pop edx; - pop ecx; - pop ebx; - retn; +void sf_set_df_model(OpcodeContext& ctx) { + auto model = ctx.arg(0).strValue(); + if (strlen(model) > 64) { + ctx.printOpcodeError(stringTooLong, ctx.getOpcodeName()); + return; } + strcpy(defaultFemaleModelName, model); } -void __declspec(naked) op_set_df_model() { - __asm { - push ebx; - push ecx; - push edx; - push edi; - mov edi, eax; - call fo::funcoffs::interpretPopShort_; - mov edx, eax; - mov eax, edi; - call fo::funcoffs::interpretPopLong_; - cmp dx, VAR_TYPE_STR2; - jz next; - cmp dx, VAR_TYPE_STR; - jnz end; -next: - mov ebx, eax; - mov eax, edi; - call fo::funcoffs::interpretGetString_; - push eax; - push defaultFemaleModelNamePtr; - call strcpy_p; -end: - pop edi; - pop edx; - pop ecx; - pop ebx; - retn; - } -} - -void __declspec(naked) op_set_movie_path() { - __asm { - push ebx; - push ecx; - push edx; - push edi; - push esi; - mov edi, eax; - call fo::funcoffs::interpretPopShort_; - mov ebx, eax; - mov eax, edi; - call fo::funcoffs::interpretPopLong_; - mov esi, eax; - mov eax, edi; - call fo::funcoffs::interpretPopShort_; - mov edx, eax; - mov eax, edi; - call fo::funcoffs::interpretPopLong_; - cmp dx, VAR_TYPE_STR2; - jz next; - cmp dx, VAR_TYPE_STR; - jnz end; -next: - cmp bx, VAR_TYPE_INT; - jnz end; - cmp esi, 0; - jl end; - cmp esi, MaxMovies; - jge end; - mov ebx, eax; - mov eax, edi; - call fo::funcoffs::interpretGetString_; - push eax; - mov eax, esi; - mov esi, 65; - mul si; - add eax, movieNamesPtr; - push eax; - call strcpy_p; -end: - pop esi; - pop edi; - pop edx; - pop ecx; - pop ebx; - retn; +void sf_set_movie_path(OpcodeContext& ctx) { + long movieID = ctx.arg(1).rawValue(); + if (movieID < 0 || movieID >= MaxMovies) return; + auto fileName = ctx.arg(0).strValue(); + if (strlen(fileName) > 64) { + ctx.printOpcodeError(stringTooLong, ctx.getOpcodeName()); + return; } + strcpy(&MoviePaths[movieID * 65], fileName); } void sf_get_year(OpcodeContext& ctx) { @@ -181,14 +87,12 @@ void sf_get_year(OpcodeContext& ctx) { void __declspec(naked) op_game_loaded() { __asm { push ecx; - push edx; push eax; push eax; // script call ScriptHasLoaded; movzx edx, al; pop eax; _RET_VAL_INT(ecx); - pop edx; pop ecx; retn; } @@ -197,15 +101,13 @@ void __declspec(naked) op_game_loaded() { void __declspec(naked) op_set_pipboy_available() { __asm { push ecx; - push edx; _GET_ARG_INT(end); cmp eax, 0; jl end; cmp eax, 1; jg end; - mov byte ptr ds:[FO_VAR_gmovie_played_list + 0x3], al; + mov byte ptr ds:[FO_VAR_gmovie_played_list][0x3], al; end: - pop edx; pop ecx; retn; } @@ -795,17 +697,6 @@ end: //numbers subgame functions void __declspec(naked) op_nb_create_char() { __asm { - /*pushad; - push eax; - call NumbersCreateChar; - mov edx, eax; - pop eax; - mov ecx, eax; - call fo::funcoffs::interpretPushLong_; - mov eax, ecx; - mov edx, VAR_TYPE_INT; - call fo::funcoffs::interpretPushShort_; - popad;*/ retn; } } @@ -959,7 +850,7 @@ end: void __declspec(naked) op_modified_ini() { __asm { push ecx; - mov edx, modifiedIni; + mov edx, modifiedIni; _RET_VAL_INT(ecx); pop ecx; retn; diff --git a/sfall/Modules/Scripting/Handlers/Misc.h b/sfall/Modules/Scripting/Handlers/Misc.h index dfbf73a8..26310c87 100644 --- a/sfall/Modules/Scripting/Handlers/Misc.h +++ b/sfall/Modules/Scripting/Handlers/Misc.h @@ -29,13 +29,11 @@ namespace script class OpcodeContext; -// TODO: rewrite all op_* functions using OpcodeContext +void sf_set_dm_model(OpcodeContext&); -void __declspec() op_set_dm_model(); +void sf_set_df_model(OpcodeContext&); -void __declspec() op_set_df_model(); - -void __declspec() op_set_movie_path(); +void sf_set_movie_path(OpcodeContext&); void sf_get_year(OpcodeContext&); diff --git a/sfall/Modules/Scripting/Handlers/Utils.cpp b/sfall/Modules/Scripting/Handlers/Utils.cpp index 55e04f66..0894c898 100644 --- a/sfall/Modules/Scripting/Handlers/Utils.cpp +++ b/sfall/Modules/Scripting/Handlers/Utils.cpp @@ -19,6 +19,7 @@ #include #include "..\..\..\FalloutEngine\Fallout2.h" +#include "..\..\..\FalloutEngine\EngineUtils.h" #include "..\..\ScriptExtender.h" #include "..\..\FileSystem.h" #include "..\..\Message.h" @@ -33,6 +34,69 @@ namespace sfall namespace script { +// compares strings case-insensitive with specifics for Fallout +static bool FalloutStringCompare(const char* str1, const char* str2, long codePage) { + while (true) { + unsigned char c1 = *str1; + unsigned char c2 = *str2; + if (c1 == 0 && c2 == 0) return true; // end - strings are equal + if (c1 == 0 || c2 == 0) return false; // strings are not equal + str1++; + str2++; + if (c1 == c2) continue; + if (codePage == 866) { + // replace Russian 'x' to English (Fallout specific) + if (c1 == 229) c1 -= 229 - 'x'; + if (c2 == 229) c2 -= 229 - 'x'; + } + + // 0 - 127 (standard ASCII) + // upper to lower case + if (c1 >= 'A' && c1 <= 'Z') c1 |= 32; + if (c2 >= 'A' && c2 <= 'Z') c2 |= 32; + if (c1 == c2) continue; + if (c1 < 128 || c2 < 128) return false; + + // 128 - 255 (international/extended) + switch (codePage) { + case 866: + if (c1 != 149 && c2 != 149) { + // upper to lower case + if (c1 >= 0x80 && c1 <= 0x9F) { + c1 |= 32; + } else if (c1 >= 224 && c1 <= 239) { + c1 -= 48; // shift lower range + } else if (c1 == 240) { + c1++; + } + if (c2 >= 0x80 && c2 <= 0x9F) { + c2 |= 32; + } else if (c2 >= 224 && c2 <= 239) { + c2 -= 48; // shift lower range + } else if (c2 == 240) { + c2++; + } + } + break; + case 1251: + // upper to lower case + if (c1 >= 0xC0 && c1 <= 0xDF) c1 |= 32; + if (c2 >= 0xC0 && c2 <= 0xDF) c2 |= 32; + if (c1 == 0xA8) c1 += 16; + if (c2 == 0xA8) c2 += 16; + break; + case 1250: + case 1252: + if (c1 != 0xD7 && c1 != 0xF7 && c2 != 0xD7 && c2 != 0xF7) { + if (c1 >= 0xC0 && c1 <= 0xDE) c1 |= 32; + if (c2 >= 0xC0 && c2 <= 0xDE) c2 |= 32; + } + break; + } + if (c1 != c2) return false; // strings are not equal + } +} + void sf_sqrt(OpcodeContext& ctx) { ctx.setReturn(sqrt(ctx.arg(0).asFloat())); } @@ -68,22 +132,22 @@ void sf_strlen(OpcodeContext& ctx) { } void sf_atoi(OpcodeContext& ctx) { - auto str = ctx.arg(0).asString(); + auto str = ctx.arg(0).strValue(); ctx.setReturn( static_cast(strtol(str, (char**)nullptr, 0)) // auto-determine radix ); } void sf_atof(OpcodeContext& ctx) { - auto str = ctx.arg(0).asString(); + auto str = ctx.arg(0).strValue(); ctx.setReturn( static_cast(atof(str)) ); } void sf_ord(OpcodeContext& ctx) { - char firstChar = ctx.arg(0).asString()[0]; - ctx.setReturn(static_cast(firstChar)); + unsigned char firstChar = ctx.arg(0).strValue()[0]; + ctx.setReturn(static_cast(firstChar)); } void sf_typeof(OpcodeContext& ctx) { @@ -123,10 +187,10 @@ static int _stdcall StringSplit(const char* str, const char* split) { } void sf_string_split(OpcodeContext& ctx) { - ctx.setReturn(StringSplit(ctx.arg(0).asString(), ctx.arg(1).asString())); + ctx.setReturn(StringSplit(ctx.arg(0).strValue(), ctx.arg(1).strValue())); } -char* _stdcall Substring(const char* str, int pos, int length) { +char* Substring(const char* str, int pos, int length) { char* newstr; int srclen; srclen = strlen(str); @@ -138,7 +202,7 @@ char* _stdcall Substring(const char* str, int pos, int length) { length = 0; else if (length + pos > srclen) length = srclen - pos; - newstr = new char[length + 1]; + newstr = new char[length + 1]; // memory leak!!! if (length > 0) memcpy(newstr, &str[pos], length); newstr[length] = '\0'; @@ -147,10 +211,20 @@ char* _stdcall Substring(const char* str, int pos, int length) { void sf_substr(OpcodeContext& ctx) { ctx.setReturn( - Substring(ctx.arg(0).asString(), ctx.arg(1).asInt(), ctx.arg(2).asInt()) + Substring(ctx.arg(0).strValue(), ctx.arg(1).rawValue(), ctx.arg(2).rawValue()) ); } +void sf_string_compare(OpcodeContext& ctx) { + if (ctx.numArgs() < 3) { + ctx.setReturn( + (_stricmp(ctx.arg(0).strValue(), ctx.arg(1).strValue()) ? 0 : 1) + ); + } else { + ctx.setReturn(FalloutStringCompare(ctx.arg(0).strValue(), ctx.arg(1).strValue(), ctx.arg(2).rawValue())); + } +} + static char* sprintfbuf = nullptr; // A safer version of sprintf for using in user scripts. static char* _stdcall sprintf_lite(const char* format, ScriptValue value) { @@ -232,7 +306,7 @@ static char* _stdcall sprintf_lite(const char* format, ScriptValue value) { void sf_sprintf(OpcodeContext& ctx) { ctx.setReturn( - sprintf_lite(ctx.arg(0).asString(), ctx.arg(1)) + sprintf_lite(ctx.arg(0).strValue(), ctx.arg(1)) ); } @@ -315,7 +389,11 @@ void sf_floor2(OpcodeContext& ctx) { } void sf_get_string_pointer(OpcodeContext& ctx) { - ctx.setReturn(reinterpret_cast(ctx.arg(0).asString()), DataType::INT); + ctx.setReturn(reinterpret_cast(ctx.arg(0).strValue()), DataType::INT); +} + +void sf_get_text_width(OpcodeContext& ctx) { + ctx.setReturn(fo::GetTextWidth(ctx.arg(0).asString())); } } diff --git a/sfall/Modules/Scripting/Handlers/Utils.h b/sfall/Modules/Scripting/Handlers/Utils.h index a2b8bbd6..ed2be87a 100644 --- a/sfall/Modules/Scripting/Handlers/Utils.h +++ b/sfall/Modules/Scripting/Handlers/Utils.h @@ -23,6 +23,8 @@ namespace sfall namespace script { +char* Substring(const char* str, int pos, int length); + class OpcodeContext; void sf_sqrt(OpcodeContext&); @@ -47,6 +49,8 @@ void sf_substr(OpcodeContext&); void sf_strlen(OpcodeContext&); +void sf_string_compare(OpcodeContext&); + void sf_sprintf(OpcodeContext&); void sf_ord(OpcodeContext&); @@ -71,7 +75,7 @@ void sf_floor2(OpcodeContext&); void sf_get_string_pointer(OpcodeContext&); -char* _stdcall Substring(const char* str, int pos, int length); +void sf_get_text_width(OpcodeContext&); } } diff --git a/sfall/Modules/Scripting/OpcodeContext.cpp b/sfall/Modules/Scripting/OpcodeContext.cpp index 4ee830b0..6de2d1fb 100644 --- a/sfall/Modules/Scripting/OpcodeContext.cpp +++ b/sfall/Modules/Scripting/OpcodeContext.cpp @@ -33,10 +33,10 @@ OpcodeContext::OpcodeContext(fo::Program* program, DWORD opcode, int argNum, boo assert(argNum < OP_MAX_ARGUMENTS); } -OpcodeContext::OpcodeContext(fo::Program* program, DWORD opcode, int argNum, bool hasReturn, const char* opcodeName) - : OpcodeContext::OpcodeContext(program, opcode, argNum, hasReturn) +OpcodeContext::OpcodeContext(fo::Program* program, const SfallOpcodeInfo* info) + : _program(program), _opcode(info->opcode), _numArgs(info->argNum), _hasReturn(info->hasReturn), _opcodeName(info->name), _argShift(0) { - _opcodeName = opcodeName; + assert(_numArgs < OP_MAX_ARGUMENTS); } const char* OpcodeContext::getOpcodeName() const { diff --git a/sfall/Modules/Scripting/OpcodeContext.h b/sfall/Modules/Scripting/OpcodeContext.h index e0caafbb..f85d1766 100644 --- a/sfall/Modules/Scripting/OpcodeContext.h +++ b/sfall/Modules/Scripting/OpcodeContext.h @@ -92,9 +92,9 @@ public: // hasReturn - true if opcode has return value (is expression) // opcodeName - name of a function (for logging) OpcodeContext(fo::Program* program, DWORD opcode, int argNum, bool hasReturn); - OpcodeContext(fo::Program* program, DWORD opcode, int argNum, bool hasReturn, const char* opcodeName); + OpcodeContext(fo::Program* program, const SfallOpcodeInfo* info); - const char* getOpcodeName() const; + const char* getOpcodeName() const; const char* getMetaruleName() const; // currently executed metarule func @@ -115,7 +115,7 @@ public: // returns argument with given index, possible shifted by argShift const ScriptValue& arg(int index) const; - + // current return value const ScriptValue& returnValue() const; @@ -124,13 +124,13 @@ public: // current opcode number DWORD opcode() const; - + // set return value for current opcode void setReturn(unsigned long value, DataType type); - + // set return value for current opcode void setReturn(const ScriptValue& val); - + // writes error message to debug.log along with the name of script & procedure void printOpcodeError(const char* fmt, ...) const; diff --git a/sfall/Modules/Scripting/Opcodes.cpp b/sfall/Modules/Scripting/Opcodes.cpp index d64f2855..3f96265d 100644 --- a/sfall/Modules/Scripting/Opcodes.cpp +++ b/sfall/Modules/Scripting/Opcodes.cpp @@ -74,6 +74,9 @@ static SfallOpcodeInfo opcodeInfoArray[] = { {0x163, "get_year", sf_get_year, 0, true}, {0x16c, "key_pressed", sf_key_pressed, 1, true, {ARG_INT}}, {0x171, "force_encounter", sf_force_encounter, 1, false, {ARG_INT}}, + {0x175, "set_dm_model", sf_set_dm_model, 1, false, {ARG_STRING}}, + {0x176, "set_df_model", sf_set_df_model, 1, false, {ARG_STRING}}, + {0x177, "set_movie_path", sf_set_movie_path, 2, false, {ARG_STRING, ARG_INT}}, {0x190, "get_perk_available", sf_get_perk_available, 1, true, {ARG_INT}}, {0x195, "set_weapon_knockback", sf_set_object_knockback, 3, false, {ARG_OBJECT, ARG_INT, ARG_NUMBER}}, @@ -165,7 +168,7 @@ static SfallOpcodeInfo opcodeInfoArray[] = { {0x241, "get_npc_level", sf_get_npc_level, 1, true, {ARG_INTSTR}}, {0x24e, "substr", sf_substr, 3, true, {ARG_STRING, ARG_INT, ARG_INT}}, - {0x24f, "strlen", sf_strlen, 1, true, {ARG_STRING}}, + {0x24f, "strlen", sf_strlen, 1, true}, {0x250, "sprintf", sf_sprintf, 2, true, {ARG_STRING, ARG_ANY}}, {0x251, "charcode", sf_ord, 1, true, {ARG_STRING}}, // 0x252 // RESERVED @@ -238,7 +241,7 @@ void __fastcall defaultOpcodeHandlerCall(fo::Program* program, DWORD opcodeOffse auto iter = opcodeInfoMap.find(opcode); if (iter != opcodeInfoMap.end()) { auto info = iter->second; - OpcodeContext ctx(program, opcode, info->argNum, info->hasReturn, info->name); + OpcodeContext ctx(program, info); ctx.handleOpcode(info->handler, info->argValidation); } else { fo::func::interpretError("Unknown opcode: %d", opcode); @@ -298,9 +301,7 @@ void InitNewOpcodes() { opcodes[0x172] = op_set_world_map_pos; opcodes[0x173] = op_get_world_map_x_pos; opcodes[0x174] = op_get_world_map_y_pos; - opcodes[0x175] = op_set_dm_model; - opcodes[0x176] = op_set_df_model; - opcodes[0x177] = op_set_movie_path; + for (int i = 0x178; i < 0x189; i++) { opcodes[i] = op_set_perk_value; } From accf564e6e3191abfb5763be7539ef7fd2913af3 Mon Sep 17 00:00:00 2001 From: NovaRain Date: Mon, 25 Nov 2019 07:28:08 +0800 Subject: [PATCH 11/36] Re-fixed GAMEMODECHANGE hook being triggered unexpectedly in dialog (undo fix in commit 5053ab5) Restored calling original engine functions from HRP hacks. --- sfall/Modules/BugFixes.cpp | 10 ++++++++-- sfall/Modules/LoadGameHook.cpp | 7 +++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/sfall/Modules/BugFixes.cpp b/sfall/Modules/BugFixes.cpp index 90c45d01..631f2547 100644 --- a/sfall/Modules/BugFixes.cpp +++ b/sfall/Modules/BugFixes.cpp @@ -68,8 +68,14 @@ void ResetBodyState() { __asm mov weightOnBody, 0; } -static void MusicVolInitialization() { +static void Initialization() { *(DWORD*)FO_VAR_gDialogMusicVol = *(DWORD*)FO_VAR_background_volume; // fix dialog music + + // Restore calling original engine functions from HRP hacks (there is no difference in HRP functions) + long long data = 0xC189565153; + SafeWriteBytes(0x4D78CC, (BYTE*)&data, 5); // win_get_top_win_ + data = 0xC389565153; + SafeWriteBytes(0x4CA9DC, (BYTE*)&data, 5); // mouse_get_position_ } // fix for vanilla negate operator not working on floats @@ -2470,7 +2476,7 @@ void BugFixes::init() #endif // Missing game initialization - LoadGameHook::OnBeforeGameInit() += MusicVolInitialization; + LoadGameHook::OnBeforeGameInit() += Initialization; // Fix vanilla negate operator on float values MakeCall(0x46AB68, NegateFixHack); diff --git a/sfall/Modules/LoadGameHook.cpp b/sfall/Modules/LoadGameHook.cpp index 1c3cdf8b..c4eb896a 100644 --- a/sfall/Modules/LoadGameHook.cpp +++ b/sfall/Modules/LoadGameHook.cpp @@ -105,13 +105,12 @@ static void __stdcall GameModeChange(DWORD state) { } void _stdcall SetInLoop(DWORD mode, LoopFlag flag) { - DWORD _flag = GetLoopFlags(); if (mode) { SetLoopFlag(flag); } else { ClearLoopFlag(flag); } - if (GetLoopFlags() != _flag) GameModeChange(0); + GameModeChange(0); } void GetSavePath(char* buf, char* ftype) { @@ -524,6 +523,10 @@ end: static void __declspec(naked) DialogHook() { __asm { + cmp dword ptr [esp + 0x14], 0x45A5C9; // call from op_gsay_end_ + je changeMode; + jmp fo::funcoffs::gdProcess_; +changeMode: _InLoop(1, DIALOG); call fo::funcoffs::gdProcess_; _InLoop(0, DIALOG); From d1851cbde1f0535e7d014c5d499801787e19e30e Mon Sep 17 00:00:00 2001 From: NovaRain Date: Tue, 26 Nov 2019 07:43:58 +0800 Subject: [PATCH 12/36] Added set/unset Special game mode flag when a party member joins/leaves in dialog Re-fixed commit 227a062 (correct variant) --- sfall/FalloutEngine/EngineUtils.cpp | 15 +++++++++++++++ sfall/FalloutEngine/EngineUtils.h | 2 ++ sfall/Modules/Interface.cpp | 14 ++------------ sfall/Modules/LoadGameHook.cpp | 22 +++++++++++++++++++++- 4 files changed, 40 insertions(+), 13 deletions(-) diff --git a/sfall/FalloutEngine/EngineUtils.cpp b/sfall/FalloutEngine/EngineUtils.cpp index 7c481ec9..7fc58706 100644 --- a/sfall/FalloutEngine/EngineUtils.cpp +++ b/sfall/FalloutEngine/EngineUtils.cpp @@ -181,6 +181,21 @@ long GetScriptLocalVars(long sid) { return (script) ? script->numLocalVars : 0; } +// Returns window ID by x/y coordinate (hidden windows are ignored) +long __fastcall GetTopWindowID(long xPos, long yPos) { + fo::Window* win = nullptr; + long countWin = *(DWORD*)FO_VAR_num_windows - 1; + for (int n = countWin; n >= 0; n--) { + win = fo::var::window[n]; + if (xPos >= win->wRect.left && xPos <= win->wRect.right && yPos >= win->wRect.top && yPos <= win->wRect.bottom) { + if (!(win->flags & fo::WinFlags::Hidden)) { + break; + } + } + } + return win->wID; +} + //--------------------------------------------------------- //print text to surface void PrintText(char *DisplayText, BYTE ColourIndex, DWORD Xpos, DWORD Ypos, DWORD TxtWidth, DWORD ToWidth, BYTE *ToSurface) { diff --git a/sfall/FalloutEngine/EngineUtils.h b/sfall/FalloutEngine/EngineUtils.h index dbe177b9..a33c382f 100644 --- a/sfall/FalloutEngine/EngineUtils.h +++ b/sfall/FalloutEngine/EngineUtils.h @@ -76,6 +76,8 @@ bool IsPartyMember(fo::GameObject* critter); // Returns the number of local variables of the object script long GetScriptLocalVars(long sid); +long __fastcall GetTopWindowID(long xPos, long yPos); + // Print text to 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 diff --git a/sfall/Modules/Interface.cpp b/sfall/Modules/Interface.cpp index 8520b0c3..fd90d9ae 100644 --- a/sfall/Modules/Interface.cpp +++ b/sfall/Modules/Interface.cpp @@ -583,19 +583,9 @@ static long gmouse_handle_event_hook() { } static void __declspec(naked) gmouse_bk_process_hook() { - using namespace fo::WinFlags; __asm { - call fo::funcoffs::win_get_top_win_; - cmp eax, ds:[FO_VAR_display_win]; - jnz checkFlag; - retn; -checkFlag: - call fo::funcoffs::GNW_find_; - test [eax + 4], Hidden; // window flags - jz skip; - mov eax, ds:[FO_VAR_display_win]; // window is hidden, so return the number of the display_win -skip: - retn; + mov ecx, eax; + jmp fo::GetTopWindowID; } } diff --git a/sfall/Modules/LoadGameHook.cpp b/sfall/Modules/LoadGameHook.cpp index c4eb896a..eb4ec310 100644 --- a/sfall/Modules/LoadGameHook.cpp +++ b/sfall/Modules/LoadGameHook.cpp @@ -650,6 +650,23 @@ static void __declspec(naked) gdialog_bk_hook() { } } +static void __declspec(naked) gdialogUpdatePartyStatus_hook1() { + __asm { + push edx; + _InLoop2(1, SPECIAL); + pop edx; + jmp fo::funcoffs::gdialog_window_destroy_; + } +} + +static void __declspec(naked) gdialogUpdatePartyStatus_hook0() { + __asm { + call fo::funcoffs::gdialog_window_create_; + _InLoop2(0, SPECIAL); + retn; + } +} + void LoadGameHook::init() { saveInCombatFix = GetConfigInt("Misc", "SaveInCombatFix", 1); if (saveInCombatFix > 2) saveInCombatFix = 0; @@ -710,7 +727,10 @@ void LoadGameHook::init() { HookCall(0x476AC6, setup_move_timer_win_Hook); // before init win HookCall(0x477067, exit_move_timer_win_Hook); - HookCall(0x447A7E, gdialog_bk_hook); // Set the Special flag before animating the dialog interface when switching from dialog mode to barter + // Set and unset the Special flag of game mode when animating the dialog interface panel + HookCall(0x447A7E, gdialog_bk_hook); // set when switching from dialog mode to barter mode (unset when entering barter) + HookCall(0x4457B1, gdialogUpdatePartyStatus_hook1); // set when a party member joins/leaves + HookCall(0x4457BC, gdialogUpdatePartyStatus_hook0); // unset } Delegate<>& LoadGameHook::OnBeforeGameInit() { From 04d4a8dc5bb1e220febc1849641d3dff783faf69 Mon Sep 17 00:00:00 2001 From: NovaRain Date: Wed, 27 Nov 2019 12:31:45 +0800 Subject: [PATCH 13/36] Minor code refactoring for opcodes/metarules Added separate settable value of return error for invalid arguments of script functions. --- sfall/Modules/Scripting/Handlers/Metarule.cpp | 121 ++++---- sfall/Modules/Scripting/OpcodeContext.cpp | 17 +- sfall/Modules/Scripting/OpcodeContext.h | 19 +- sfall/Modules/Scripting/Opcodes.cpp | 279 +++++++++--------- sfall/Modules/Scripting/ScriptValue.h | 2 +- 5 files changed, 223 insertions(+), 215 deletions(-) diff --git a/sfall/Modules/Scripting/Handlers/Metarule.cpp b/sfall/Modules/Scripting/Handlers/Metarule.cpp index 01e749ca..257ac4d9 100644 --- a/sfall/Modules/Scripting/Handlers/Metarule.cpp +++ b/sfall/Modules/Scripting/Handlers/Metarule.cpp @@ -53,89 +53,90 @@ static MetaruleTableType metaruleTable; Add your custom scripting functions here. Format is as follows: - { name, handler, minArgs, maxArgs, {arg1, arg2, ...} } + { name, handler, minArgs, maxArgs, error, {arg1, arg2, ...} } - name - name of function that will be used in scripts, - handler - pointer to handler function (see examples below), - minArgs/maxArgs - minimum and maximum number of arguments allowed for this function (max 6) + - returned error value for argument validation, - arg1, arg2, ... - argument types for automatic validation */ static const SfallMetarule metarules[] = { - {"add_extra_msg_file", sf_add_extra_msg_file, 1, 2, {ARG_STRING, ARG_INT}}, + {"add_extra_msg_file", sf_add_extra_msg_file, 1, 2, -1, {ARG_STRING, ARG_INT}}, {"add_iface_tag", sf_add_iface_tag, 0, 0}, - {"add_g_timer_event", sf_add_g_timer_event, 2, 2, {ARG_INT, ARG_INT}}, - {"add_trait", sf_add_trait, 1, 1, {ARG_INT}}, + {"add_g_timer_event", sf_add_g_timer_event, 2, 2, -1, {ARG_INT, ARG_INT}}, + {"add_trait", sf_add_trait, 1, 1, -1, {ARG_INT}}, {"art_cache_clear", sf_art_cache_flush, 0, 0}, {"attack_is_aimed", sf_attack_is_aimed, 0, 0}, {"car_gas_amount", sf_car_gas_amount, 0, 0}, - {"create_win", sf_create_win, 5, 6, {ARG_STRING, ARG_INT, ARG_INT, ARG_INT, ARG_INT, ARG_INT}}, - {"critter_inven_obj2", sf_critter_inven_obj2, 2, 2, {ARG_OBJECT, ARG_INT}}, - {"dialog_message", sf_dialog_message, 1, 1, {ARG_STRING}}, + {"create_win", sf_create_win, 5, 6, -1, {ARG_STRING, ARG_INT, ARG_INT, ARG_INT, ARG_INT, ARG_INT}}, + {"critter_inven_obj2", sf_critter_inven_obj2, 2, 2, 0, {ARG_OBJECT, ARG_INT}}, + {"dialog_message", sf_dialog_message, 1, 1, -1, {ARG_STRING}}, {"dialog_obj", sf_get_dialog_object, 0, 0}, {"display_stats", sf_display_stats, 0, 0}, // refresh - {"draw_image", sf_draw_image, 1, 5, {ARG_INTSTR, ARG_INT, ARG_INT, ARG_INT, ARG_INT}}, - {"draw_image_scaled", sf_draw_image_scaled, 1, 6, {ARG_INTSTR, ARG_INT, ARG_INT, ARG_INT, ARG_INT, ARG_INT}}, + {"draw_image", sf_draw_image, 1, 5, -1, {ARG_INTSTR, ARG_INT, ARG_INT, ARG_INT, ARG_INT}}, + {"draw_image_scaled", sf_draw_image_scaled, 1, 6, -1, {ARG_INTSTR, ARG_INT, ARG_INT, ARG_INT, ARG_INT, ARG_INT}}, {"exec_map_update_scripts", sf_exec_map_update_scripts, 0, 0}, - {"floor2", sf_floor2, 1, 1, {ARG_NUMBER}}, - {"get_can_rest_on_map", sf_get_rest_on_map, 2, 2, {ARG_INT, ARG_INT}}, - {"get_current_inven_size", sf_get_current_inven_size, 1, 1, {ARG_OBJECT}}, + {"floor2", sf_floor2, 1, 1, 0, {ARG_NUMBER}}, + {"get_can_rest_on_map", sf_get_rest_on_map, 2, 2, -1, {ARG_INT, ARG_INT}}, + {"get_current_inven_size", sf_get_current_inven_size, 1, 1, 0, {ARG_OBJECT}}, {"get_cursor_mode", sf_get_cursor_mode, 0, 0}, - {"get_flags", sf_get_flags, 1, 1, {ARG_OBJECT}}, - {"get_ini_section", sf_get_ini_section, 2, 2, {ARG_STRING, ARG_STRING}}, - {"get_ini_sections", sf_get_ini_sections, 1, 1, {ARG_STRING}}, + {"get_flags", sf_get_flags, 1, 1, 0, {ARG_OBJECT}}, + {"get_ini_section", sf_get_ini_section, 2, 2, -1, {ARG_STRING, ARG_STRING}}, + {"get_ini_sections", sf_get_ini_sections, 1, 1, -1, {ARG_STRING}}, {"get_inven_ap_cost", sf_get_inven_ap_cost, 0, 0}, {"get_map_enter_position", sf_get_map_enter_position, 0, 0}, {"get_metarule_table", sf_get_metarule_table, 0, 0}, - {"get_object_ai_data", sf_get_object_ai_data, 2, 2, {ARG_OBJECT, ARG_INT}}, - {"get_object_data", sf_get_object_data, 2, 2, {ARG_OBJECT, ARG_INT}}, - {"get_outline", sf_get_outline, 1, 1, {ARG_OBJECT}}, - {"get_sfall_arg_at", sf_get_sfall_arg_at, 1, 1, {ARG_INT}}, - {"get_string_pointer", sf_get_string_pointer, 1, 1, {ARG_STRING}}, - {"get_text_width", sf_get_text_width, 1, 1}, - {"has_fake_perk_npc", sf_has_fake_perk_npc, 2, 2, {ARG_OBJECT, ARG_STRING}}, - {"has_fake_trait_npc", sf_has_fake_trait_npc, 2, 2, {ARG_OBJECT, ARG_STRING}}, - {"hide_window", sf_hide_window, 0, 1, {ARG_STRING}}, + {"get_object_ai_data", sf_get_object_ai_data, 2, 2, 0, {ARG_OBJECT, ARG_INT}}, + {"get_object_data", sf_get_object_data, 2, 2, 0, {ARG_OBJECT, ARG_INT}}, + {"get_outline", sf_get_outline, 1, 1, 0, {ARG_OBJECT}}, + {"get_sfall_arg_at", sf_get_sfall_arg_at, 1, 1, 0, {ARG_INT}}, + {"get_string_pointer", sf_get_string_pointer, 1, 1, 0, {ARG_STRING}}, + {"get_text_width", sf_get_text_width, 1, 1, 0, {ARG_STRING}}, + {"has_fake_perk_npc", sf_has_fake_perk_npc, 2, 2, 0, {ARG_OBJECT, ARG_STRING}}, + {"has_fake_trait_npc", sf_has_fake_trait_npc, 2, 2, 0, {ARG_OBJECT, ARG_STRING}}, + {"hide_window", sf_hide_window, 0, 1, -1, {ARG_STRING}}, {"intface_hide", sf_intface_hide, 0, 0}, {"intface_is_hidden", sf_intface_is_hidden, 0, 0}, {"intface_redraw", sf_intface_redraw, 0, 0}, {"intface_show", sf_intface_show, 0, 0}, - {"inventory_redraw", sf_inventory_redraw, 0, 1, {ARG_INT}}, - {"item_make_explosive", sf_item_make_explosive, 3, 4, {ARG_INT, ARG_INT, ARG_INT, ARG_INT}}, - {"item_weight", sf_item_weight, 1, 1, {ARG_OBJECT}}, - {"lock_is_jammed", sf_lock_is_jammed, 1, 1, {ARG_OBJECT}}, + {"inventory_redraw", sf_inventory_redraw, 0, 1, -1, {ARG_INT}}, + {"item_make_explosive", sf_item_make_explosive, 3, 4, -1, {ARG_INT, ARG_INT, ARG_INT, ARG_INT}}, + {"item_weight", sf_item_weight, 1, 1, 0, {ARG_OBJECT}}, + {"lock_is_jammed", sf_lock_is_jammed, 1, 1, 0, {ARG_OBJECT}}, {"loot_obj", sf_get_loot_object, 0, 0}, {"metarule_exist", sf_metarule_exist, 1, 1}, // no arg check - {"npc_engine_level_up", sf_npc_engine_level_up, 1, 1, {ARG_ANY}}, - {"obj_under_cursor", sf_get_obj_under_cursor, 2, 2, {ARG_INT, ARG_INT}}, + {"npc_engine_level_up", sf_npc_engine_level_up, 1, 1, 0, {ARG_ANY}}, + {"obj_under_cursor", sf_get_obj_under_cursor, 2, 2, 0, {ARG_INT, ARG_INT}}, {"outlined_object", sf_outlined_object, 0, 0}, {"real_dude_obj", sf_real_dude_obj, 0, 0}, - {"remove_timer_event", sf_remove_timer_event, 0, 1, {ARG_INT}}, - {"set_can_rest_on_map", sf_set_rest_on_map, 3, 3, {ARG_INT, ARG_INT, ARG_INT}}, - {"set_car_intface_art", sf_set_car_intface_art, 1, 1, {ARG_INT}}, - {"set_cursor_mode", sf_set_cursor_mode, 1, 1, {ARG_INT}}, - {"set_drugs_data", sf_set_drugs_data, 3, 3, {ARG_INT, ARG_INT, ARG_INT}}, - {"set_dude_obj", sf_set_dude_obj, 1, 1, {ARG_INT}}, - {"set_fake_perk_npc", sf_set_fake_perk_npc, 5, 5, {ARG_OBJECT, ARG_STRING, ARG_INT, ARG_INT, ARG_STRING}}, - {"set_fake_trait_npc", sf_set_fake_trait_npc, 5, 5, {ARG_OBJECT, ARG_STRING, ARG_INT, ARG_INT, ARG_STRING}}, - {"set_flags", sf_set_flags, 2, 2, {ARG_OBJECT, ARG_INT}}, - {"set_iface_tag_text", sf_set_iface_tag_text, 3, 3, {ARG_INT, ARG_STRING, ARG_INT}}, - {"set_ini_setting", sf_set_ini_setting, 2, 2, {ARG_STRING, ARG_INTSTR}}, - {"set_map_enter_position", sf_set_map_enter_position, 3, 3, {ARG_INT, ARG_INT, ARG_INT}}, - {"set_object_data", sf_set_object_data, 3, 3, {ARG_OBJECT, ARG_INT, ARG_INT}}, - {"set_outline", sf_set_outline, 2, 2, {ARG_OBJECT, ARG_INT}}, - {"set_rest_heal_time", sf_set_rest_heal_time, 1, 1, {ARG_INT}}, - {"set_rest_mode", sf_set_rest_mode, 1, 1, {ARG_INT}}, - {"set_selectable_perk_npc", sf_set_selectable_perk_npc, 5, 5, {ARG_OBJECT, ARG_STRING, ARG_INT, ARG_INT, ARG_STRING}}, - {"set_unique_id", sf_set_unique_id, 1, 2, {ARG_OBJECT, ARG_INT}}, - {"set_unjam_locks_time", sf_set_unjam_locks_time, 1, 1, {ARG_INT}}, - {"set_window_flag", sf_set_window_flag, 3, 3, {ARG_INTSTR, ARG_INT, ARG_INT}}, - {"show_window", sf_show_window, 0, 1, {ARG_STRING}}, - {"spatial_radius", sf_spatial_radius, 1, 1, {ARG_OBJECT}}, - {"string_compare", sf_string_compare, 2, 3, {ARG_STRING, ARG_STRING, ARG_INT}}, + {"remove_timer_event", sf_remove_timer_event, 0, 1, -1, {ARG_INT}}, + {"set_can_rest_on_map", sf_set_rest_on_map, 3, 3, -1, {ARG_INT, ARG_INT, ARG_INT}}, + {"set_car_intface_art", sf_set_car_intface_art, 1, 1, -1, {ARG_INT}}, + {"set_cursor_mode", sf_set_cursor_mode, 1, 1, -1, {ARG_INT}}, + {"set_drugs_data", sf_set_drugs_data, 3, 3, -1, {ARG_INT, ARG_INT, ARG_INT}}, + {"set_dude_obj", sf_set_dude_obj, 1, 1, -1, {ARG_INT}}, + {"set_fake_perk_npc", sf_set_fake_perk_npc, 5, 5, -1, {ARG_OBJECT, ARG_STRING, ARG_INT, ARG_INT, ARG_STRING}}, + {"set_fake_trait_npc", sf_set_fake_trait_npc, 5, 5, -1, {ARG_OBJECT, ARG_STRING, ARG_INT, ARG_INT, ARG_STRING}}, + {"set_flags", sf_set_flags, 2, 2, -1, {ARG_OBJECT, ARG_INT}}, + {"set_iface_tag_text", sf_set_iface_tag_text, 3, 3, -1, {ARG_INT, ARG_STRING, ARG_INT}}, + {"set_ini_setting", sf_set_ini_setting, 2, 2, -1, {ARG_STRING, ARG_INTSTR}}, + {"set_map_enter_position", sf_set_map_enter_position, 3, 3, -1, {ARG_INT, ARG_INT, ARG_INT}}, + {"set_object_data", sf_set_object_data, 3, 3, -1, {ARG_OBJECT, ARG_INT, ARG_INT}}, + {"set_outline", sf_set_outline, 2, 2, -1, {ARG_OBJECT, ARG_INT}}, + {"set_rest_heal_time", sf_set_rest_heal_time, 1, 1, -1, {ARG_INT}}, + {"set_rest_mode", sf_set_rest_mode, 1, 1, -1, {ARG_INT}}, + {"set_selectable_perk_npc", sf_set_selectable_perk_npc, 5, 5, -1, {ARG_OBJECT, ARG_STRING, ARG_INT, ARG_INT, ARG_STRING}}, + {"set_unique_id", sf_set_unique_id, 1, 2, -1, {ARG_OBJECT, ARG_INT}}, + {"set_unjam_locks_time", sf_set_unjam_locks_time, 1, 1, -1, {ARG_INT}}, + {"set_window_flag", sf_set_window_flag, 3, 3, -1, {ARG_INTSTR, ARG_INT, ARG_INT}}, + {"show_window", sf_show_window, 0, 1, -1, {ARG_STRING}}, + {"spatial_radius", sf_spatial_radius, 1, 1, 0, {ARG_OBJECT}}, + {"string_compare", sf_string_compare, 2, 3, 0, {ARG_STRING, ARG_STRING, ARG_INT}}, {"tile_refresh_display", sf_tile_refresh_display, 0, 0}, - {"unjam_lock", sf_unjam_lock, 1, 1, {ARG_OBJECT}}, - {"unwield_slot", sf_unwield_slot, 2, 2, {ARG_OBJECT, ARG_INT}}, + {"unjam_lock", sf_unjam_lock, 1, 1, -1, {ARG_OBJECT}}, + {"unwield_slot", sf_unwield_slot, 2, 2, -1, {ARG_OBJECT, ARG_INT}}, #ifndef NDEBUG - {"validate_test", sf_test, 2, 5, {ARG_INT, ARG_NUMBER, ARG_STRING, ARG_OBJECT, ARG_ANY}}, + {"validate_test", sf_test, 2, 5, -1, {ARG_INT, ARG_NUMBER, ARG_STRING, ARG_OBJECT, ARG_ANY}}, #endif }; @@ -173,7 +174,7 @@ void InitMetaruleTable() { // Validates arguments against metarule specification. // On error prints to debug.log and returns false. static bool ValidateMetaruleArguments(OpcodeContext& ctx) { - const SfallMetarule* metaruleInfo = ctx.metarule; + const SfallMetarule* metaruleInfo = ctx.getMetarule(); int argCount = ctx.numArgs(); if (argCount < metaruleInfo->minArgs || argCount > metaruleInfo->maxArgs) { ctx.printOpcodeError( @@ -196,14 +197,14 @@ void HandleMetarule(OpcodeContext& ctx) { const char* name = nameArg.strValue(); MetaruleTableType::iterator lookup = metaruleTable.find(name); if (lookup != metaruleTable.end()) { - ctx.metarule = lookup->second; + ctx.setMetarule(lookup->second); // shift function name away, so argument #0 will correspond to actual first argument of function // this allows to use the same handlers for opcodes and metarule functions ctx.setArgShift(1); if (ValidateMetaruleArguments(ctx)) { - ctx.metarule->func(ctx); + ctx.getMetarule()->func(ctx); } else if (ctx.hasReturn()) { - ctx.setReturn(-1); + ctx.setReturn(ctx.getMetarule()->errValue); } } else { ctx.printOpcodeError("%s(\"%s\", ...) - metarule function is unknown.", ctx.getOpcodeName(), name); diff --git a/sfall/Modules/Scripting/OpcodeContext.cpp b/sfall/Modules/Scripting/OpcodeContext.cpp index 6de2d1fb..cce8bc2c 100644 --- a/sfall/Modules/Scripting/OpcodeContext.cpp +++ b/sfall/Modules/Scripting/OpcodeContext.cpp @@ -28,13 +28,14 @@ namespace script { OpcodeContext::OpcodeContext(fo::Program* program, DWORD opcode, int argNum, bool hasReturn) - : _program(program), _opcode(opcode), _numArgs(argNum), _hasReturn(hasReturn), _argShift(0) + : _program(program), _opcode(opcode), _numArgs(argNum), _hasReturn(hasReturn), _errorVal(-1), _argShift(0) { assert(argNum < OP_MAX_ARGUMENTS); } OpcodeContext::OpcodeContext(fo::Program* program, const SfallOpcodeInfo* info) - : _program(program), _opcode(info->opcode), _numArgs(info->argNum), _hasReturn(info->hasReturn), _opcodeName(info->name), _argShift(0) + : _program(program), _opcode(info->opcode), _numArgs(info->argNum), _hasReturn(info->hasReturn), + _opcodeName(info->name), _errorVal(info->errValue), _argShift(0) { assert(_numArgs < OP_MAX_ARGUMENTS); } @@ -44,7 +45,15 @@ const char* OpcodeContext::getOpcodeName() const { } const char* OpcodeContext::getMetaruleName() const { - return metarule->name; + return _metarule->name; +} + +const SfallMetarule* OpcodeContext::getMetarule() const { + return _metarule; +} + +void OpcodeContext::setMetarule(const SfallMetarule* metarule) { + _metarule = metarule; } int OpcodeContext::numArgs() const { @@ -145,7 +154,7 @@ void OpcodeContext::handleOpcode(ScriptingFunctionHandler func, const OpcodeArgu if (!_numArgs || validateArguments(argTypes, _opcodeName)) { func(*this); } else if (_hasReturn) { - setReturn(-1); // is a common practice to return -1 in case of errors in fallout engine + setReturn(_errorVal); // is a common practice to return -1 in case of errors in fallout engine } if (_hasReturn) _pushReturnValue(); diff --git a/sfall/Modules/Scripting/OpcodeContext.h b/sfall/Modules/Scripting/OpcodeContext.h index f85d1766..b981c300 100644 --- a/sfall/Modules/Scripting/OpcodeContext.h +++ b/sfall/Modules/Scripting/OpcodeContext.h @@ -37,7 +37,7 @@ typedef void(*ScriptingFunctionHandler)(OpcodeContext&); // The type of argument, not the same as actual data type. Useful for validation. enum OpcodeArgumentType { - ARG_ANY = 0, // no validation + ARG_ANY = 0, // no validation (default) ARG_INT, // integer only ARG_NUMBER, // float OR integer ARG_STRING, // string only @@ -61,6 +61,9 @@ typedef struct SfallOpcodeInfo { // has return value or not bool hasReturn; + // default return value when an error occurs during validation of function arguments + long errValue; + // argument validation settings OpcodeArgumentType argValidation[OP_MAX_ARGUMENTS]; } SfallOpcodeInfo; @@ -79,6 +82,9 @@ typedef struct SfallMetarule { // maximum number of arguments short maxArgs; + // default return value when an error occurs during validation of function arguments + long errValue; + // argument validation settings OpcodeArgumentType argValidation[OP_MAX_ARGUMENTS]; } SfallMetarule; @@ -90,15 +96,15 @@ public: // opcode - opcode number // argNum - number of arguments for this opcode // hasReturn - true if opcode has return value (is expression) - // opcodeName - name of a function (for logging) OpcodeContext(fo::Program* program, DWORD opcode, int argNum, bool hasReturn); OpcodeContext(fo::Program* program, const SfallOpcodeInfo* info); const char* getOpcodeName() const; const char* getMetaruleName() const; - // currently executed metarule func - const SfallMetarule* metarule; + // currently executed metarule function + void setMetarule(const SfallMetarule* metarule); + const SfallMetarule* getMetarule() const; // number of arguments, possibly reduced by argShift int numArgs() const; @@ -145,7 +151,6 @@ public: // Handle opcodes with argument validation // func - opcode handler // argTypes - argument types for validation - // opcodeName - name of a function (for logging) void handleOpcode(ScriptingFunctionHandler func, const OpcodeArgumentType argTypes[]); // handles opcode using default instance @@ -165,14 +170,16 @@ private: fo::Program* _program; DWORD _opcode; + const char* _opcodeName; // name of opcode function (for error logging) - const char* _opcodeName; + const SfallMetarule* _metarule; int _numArgs; bool _hasReturn; int _argShift; std::array _args; ScriptValue _ret; + long _errorVal; // error value for incorrect arguments }; } diff --git a/sfall/Modules/Scripting/Opcodes.cpp b/sfall/Modules/Scripting/Opcodes.cpp index 3f96265d..679def8e 100644 --- a/sfall/Modules/Scripting/Opcodes.cpp +++ b/sfall/Modules/Scripting/Opcodes.cpp @@ -60,153 +60,153 @@ typedef std::unordered_map OpcodeInfoMapType; // function handler, // number of arguments (max 7), // has return value, +// returned error value for argument validation, // { argument 1 type, argument 2 type, ...} // } static SfallOpcodeInfo opcodeInfoArray[] = { - {0x15a, "set_pc_base_stat", sf_set_pc_base_stat, 2, false, {ARG_INT, ARG_INT}}, - {0x15b, "set_pc_extra_stat", sf_set_pc_extra_stat, 2, false, {ARG_INT, ARG_INT}}, - {0x15c, "get_pc_base_stat", sf_get_pc_base_stat, 1, true, {ARG_INT}}, - {0x15d, "get_pc_extra_stat", sf_get_pc_extra_stat, 1, true, {ARG_INT}}, - {0x15e, "set_critter_base_stat", sf_set_critter_base_stat, 3, false, {ARG_OBJECT, ARG_INT, ARG_INT}}, - {0x15f, "set_critter_extra_stat", sf_set_critter_extra_stat, 3, false, {ARG_OBJECT, ARG_INT, ARG_INT}}, - {0x160, "get_critter_base_stat", sf_get_critter_base_stat, 2, true, {ARG_OBJECT, ARG_INT}}, - {0x161, "get_critter_extra_stat", sf_get_critter_extra_stat, 2, true, {ARG_OBJECT, ARG_INT}}, + {0x15a, "set_pc_base_stat", sf_set_pc_base_stat, 2, false, 0, {ARG_INT, ARG_INT}}, + {0x15b, "set_pc_extra_stat", sf_set_pc_extra_stat, 2, false, 0, {ARG_INT, ARG_INT}}, + {0x15c, "get_pc_base_stat", sf_get_pc_base_stat, 1, true, 0, {ARG_INT}}, + {0x15d, "get_pc_extra_stat", sf_get_pc_extra_stat, 1, true, 0, {ARG_INT}}, + {0x15e, "set_critter_base_stat", sf_set_critter_base_stat, 3, false, 0, {ARG_OBJECT, ARG_INT, ARG_INT}}, + {0x15f, "set_critter_extra_stat", sf_set_critter_extra_stat, 3, false, 0, {ARG_OBJECT, ARG_INT, ARG_INT}}, + {0x160, "get_critter_base_stat", sf_get_critter_base_stat, 2, true, 0, {ARG_OBJECT, ARG_INT}}, + {0x161, "get_critter_extra_stat", sf_get_critter_extra_stat, 2, true, 0, {ARG_OBJECT, ARG_INT}}, {0x163, "get_year", sf_get_year, 0, true}, - {0x16c, "key_pressed", sf_key_pressed, 1, true, {ARG_INT}}, - {0x171, "force_encounter", sf_force_encounter, 1, false, {ARG_INT}}, - {0x175, "set_dm_model", sf_set_dm_model, 1, false, {ARG_STRING}}, - {0x176, "set_df_model", sf_set_df_model, 1, false, {ARG_STRING}}, - {0x177, "set_movie_path", sf_set_movie_path, 2, false, {ARG_STRING, ARG_INT}}, + {0x16c, "key_pressed", sf_key_pressed, 1, true, 0, {ARG_INT}}, + {0x171, "force_encounter", sf_force_encounter, 1, false, 0, {ARG_INT}}, + {0x175, "set_dm_model", sf_set_dm_model, 1, false, 0, {ARG_STRING}}, + {0x176, "set_df_model", sf_set_df_model, 1, false, 0, {ARG_STRING}}, + {0x177, "set_movie_path", sf_set_movie_path, 2, false, 0, {ARG_STRING, ARG_INT}}, - {0x190, "get_perk_available", sf_get_perk_available, 1, true, {ARG_INT}}, - {0x195, "set_weapon_knockback", sf_set_object_knockback, 3, false, {ARG_OBJECT, ARG_INT, ARG_NUMBER}}, - {0x196, "set_target_knockback", sf_set_object_knockback, 3, false, {ARG_OBJECT, ARG_INT, ARG_NUMBER}}, - {0x197, "set_attacker_knockback", sf_set_object_knockback, 3, false, {ARG_OBJECT, ARG_INT, ARG_NUMBER}}, - {0x198, "remove_weapon_knockback", sf_remove_object_knockback, 1, false, {ARG_OBJECT}}, - {0x199, "remove_target_knockback", sf_remove_object_knockback, 1, false, {ARG_OBJECT}}, - {0x19a, "remove_attacker_knockback", sf_remove_object_knockback, 1, false, {ARG_OBJECT}}, - {0x19d, "set_sfall_global", sf_set_sfall_global, 2, false, {ARG_INTSTR, ARG_NUMBER}}, - {0x19e, "get_sfall_global_int", sf_get_sfall_global_int, 1, true, {ARG_INTSTR}}, - {0x19f, "get_sfall_global_float", sf_get_sfall_global_float, 1, true, {ARG_INTSTR}}, - {0x1a5, "inc_npc_level", sf_inc_npc_level, 1, false, {ARG_INTSTR}}, - {0x1aa, "set_xp_mod", sf_set_xp_mod, 1, false, {ARG_INT}}, - {0x1ac, "get_ini_setting", sf_get_ini_setting, 1, true, {ARG_STRING}}, + {0x190, "get_perk_available", sf_get_perk_available, 1, true, 0, {ARG_INT}}, + {0x195, "set_weapon_knockback", sf_set_object_knockback, 3, false, 0, {ARG_OBJECT, ARG_INT, ARG_NUMBER}}, + {0x196, "set_target_knockback", sf_set_object_knockback, 3, false, 0, {ARG_OBJECT, ARG_INT, ARG_NUMBER}}, + {0x197, "set_attacker_knockback", sf_set_object_knockback, 3, false, 0, {ARG_OBJECT, ARG_INT, ARG_NUMBER}}, + {0x198, "remove_weapon_knockback", sf_remove_object_knockback, 1, false, 0, {ARG_OBJECT}}, + {0x199, "remove_target_knockback", sf_remove_object_knockback, 1, false, 0, {ARG_OBJECT}}, + {0x19a, "remove_attacker_knockback", sf_remove_object_knockback, 1, false, 0, {ARG_OBJECT}}, + {0x19d, "set_sfall_global", sf_set_sfall_global, 2, false, 0, {ARG_INTSTR, ARG_NUMBER}}, + {0x19e, "get_sfall_global_int", sf_get_sfall_global_int, 1, true, 0, {ARG_INTSTR}}, + {0x19f, "get_sfall_global_float", sf_get_sfall_global_float, 1, true, 0, {ARG_INTSTR}}, + {0x1a5, "inc_npc_level", sf_inc_npc_level, 1, false, 0, {ARG_INTSTR}}, + {0x1aa, "set_xp_mod", sf_set_xp_mod, 1, false, 0, {ARG_INT}}, + {0x1ac, "get_ini_setting", sf_get_ini_setting, 1, true, -1, {ARG_STRING}}, - {0x1bb, "set_fake_perk", sf_set_fake_perk, 4, false, {ARG_STRING, ARG_INT, ARG_INT, ARG_STRING}}, - {0x1bc, "set_fake_trait", sf_set_fake_trait, 4, false, {ARG_STRING, ARG_INT, ARG_INT, ARG_STRING}}, - {0x1bd, "set_selectable_perk", sf_set_selectable_perk, 4, false, {ARG_STRING, ARG_INT, ARG_INT, ARG_STRING}}, - {0x1c1, "has_fake_perk", sf_has_fake_perk, 1, true, {ARG_INTSTR}}, - {0x1c2, "has_fake_trait", sf_has_fake_trait, 1, true, {ARG_STRING}}, + {0x1bb, "set_fake_perk", sf_set_fake_perk, 4, false, 0, {ARG_STRING, ARG_INT, ARG_INT, ARG_STRING}}, + {0x1bc, "set_fake_trait", sf_set_fake_trait, 4, false, 0, {ARG_STRING, ARG_INT, ARG_INT, ARG_STRING}}, + {0x1bd, "set_selectable_perk", sf_set_selectable_perk, 4, false, 0, {ARG_STRING, ARG_INT, ARG_INT, ARG_STRING}}, + {0x1c1, "has_fake_perk", sf_has_fake_perk, 1, true, 0, {ARG_INTSTR}}, + {0x1c2, "has_fake_trait", sf_has_fake_trait, 1, true, 0, {ARG_STRING}}, - {0x1dc, "show_iface_tag", sf_show_iface_tag, 1, false, {ARG_INT}}, - {0x1dd, "hide_iface_tag", sf_hide_iface_tag, 1, false, {ARG_INT}}, - {0x1de, "is_iface_tag_active", sf_is_iface_tag_active, 1, true, {ARG_INT}}, - {0x1e1, "set_critical_table", sf_set_critical_table, 5, false, {ARG_INT, ARG_INT, ARG_INT, ARG_INT, ARG_INT}}, - {0x1e2, "get_critical_table", sf_get_critical_table, 4, true, {ARG_INT, ARG_INT, ARG_INT, ARG_INT}}, - {0x1e3, "reset_critical_table", sf_reset_critical_table, 4, false, {ARG_INT, ARG_INT, ARG_INT, ARG_INT}}, - {0x1eb, "get_ini_string", sf_get_ini_string, 1, true, {ARG_STRING}}, - {0x1ec, "sqrt", sf_sqrt, 1, true, {ARG_NUMBER}}, - {0x1ed, "abs", sf_abs, 1, true, {ARG_NUMBER}}, - {0x1ee, "sin", sf_sin, 1, true, {ARG_NUMBER}}, - {0x1ef, "cos", sf_cos, 1, true, {ARG_NUMBER}}, - {0x1f0, "tan", sf_tan, 1, true, {ARG_NUMBER}}, - {0x1f1, "arctan", sf_arctan, 2, true, {ARG_NUMBER, ARG_NUMBER}}, - {0x1f5, "get_script", sf_get_script, 1, true, {ARG_OBJECT}}, + {0x1dc, "show_iface_tag", sf_show_iface_tag, 1, false, 0, {ARG_INT}}, + {0x1dd, "hide_iface_tag", sf_hide_iface_tag, 1, false, 0, {ARG_INT}}, + {0x1de, "is_iface_tag_active", sf_is_iface_tag_active, 1, true, 0, {ARG_INT}}, + {0x1e1, "set_critical_table", sf_set_critical_table, 5, false, 0, {ARG_INT, ARG_INT, ARG_INT, ARG_INT, ARG_INT}}, + {0x1e2, "get_critical_table", sf_get_critical_table, 4, true, 0, {ARG_INT, ARG_INT, ARG_INT, ARG_INT}}, + {0x1e3, "reset_critical_table", sf_reset_critical_table, 4, false, 0, {ARG_INT, ARG_INT, ARG_INT, ARG_INT}}, + {0x1eb, "get_ini_string", sf_get_ini_string, 1, true, -1, {ARG_STRING}}, + {0x1ec, "sqrt", sf_sqrt, 1, true, 0, {ARG_NUMBER}}, + {0x1ed, "abs", sf_abs, 1, true, 0, {ARG_NUMBER}}, + {0x1ee, "sin", sf_sin, 1, true, 0, {ARG_NUMBER}}, + {0x1ef, "cos", sf_cos, 1, true, 0, {ARG_NUMBER}}, + {0x1f0, "tan", sf_tan, 1, true, 0, {ARG_NUMBER}}, + {0x1f1, "arctan", sf_arctan, 2, true, 0, {ARG_NUMBER, ARG_NUMBER}}, + {0x1f5, "get_script", sf_get_script, 1, true, -1, {ARG_OBJECT}}, - {0x1f7, "fs_create", sf_fs_create, 2, true, {ARG_STRING, ARG_INT}}, - {0x1f8, "fs_copy", sf_fs_copy, 2, true, {ARG_STRING, ARG_STRING}}, - {0x1f9, "fs_find", sf_fs_find, 1, true, {ARG_STRING}}, - {0x1fa, "fs_write_byte", sf_fs_write_byte, 2, false, {ARG_INT, ARG_INT}}, - {0x1fb, "fs_write_short", sf_fs_write_short, 2, false, {ARG_INT, ARG_INT}}, - {0x1fc, "fs_write_int", sf_fs_write_int, 2, false, {ARG_INT, ARG_INT}}, - {0x1fd, "fs_write_int", sf_fs_write_int, 2, false, {ARG_INT, ARG_INT}}, - {0x1fe, "fs_write_string", sf_fs_write_string, 2, false, {ARG_INT, ARG_STRING}}, - {0x1ff, "fs_delete", sf_fs_delete, 1, false, {ARG_INT}}, - {0x200, "fs_size", sf_fs_size, 1, true, {ARG_INT}}, - {0x201, "fs_pos", sf_fs_pos, 1, true, {ARG_INT}}, - {0x202, "fs_seek", sf_fs_seek, 2, false, {ARG_INT, ARG_INT}}, - {0x203, "fs_resize", sf_fs_resize, 2, false, {ARG_INT, ARG_INT}}, - {0x204, "get_proto_data", sf_get_proto_data, 2, true, {ARG_INT, ARG_INT}}, - {0x205, "set_proto_data", sf_set_proto_data, 3, false, {ARG_INT, ARG_INT, ARG_INT}}, - {0x207, "register_hook", sf_register_hook, 1, false, {ARG_INT}}, - {0x208, "fs_write_bstring", sf_fs_write_bstring, 2, false, {ARG_INT, ARG_STRING}}, - {0x209, "fs_read_byte", sf_fs_read_byte, 1, true, {ARG_INT}}, - {0x20a, "fs_read_short", sf_fs_read_short, 1, true, {ARG_INT}}, - {0x20b, "fs_read_int", sf_fs_read_int, 1, true, {ARG_INT}}, - {0x20c, "fs_read_float", sf_fs_read_float, 1, true, {ARG_INT}}, - {0x20d, "list_begin", sf_list_begin, 1, true, {ARG_INT}}, - {0x20e, "list_next", sf_list_next, 1, true, {ARG_INT}}, - {0x20f, "list_end", sf_list_end, 1, false, {ARG_INT}}, + {0x1f7, "fs_create", sf_fs_create, 2, true, -1, {ARG_STRING, ARG_INT}}, + {0x1f8, "fs_copy", sf_fs_copy, 2, true, -1, {ARG_STRING, ARG_STRING}}, + {0x1f9, "fs_find", sf_fs_find, 1, true, -1, {ARG_STRING}}, + {0x1fa, "fs_write_byte", sf_fs_write_byte, 2, false, 0, {ARG_INT, ARG_INT}}, + {0x1fb, "fs_write_short", sf_fs_write_short, 2, false, 0, {ARG_INT, ARG_INT}}, + {0x1fc, "fs_write_int", sf_fs_write_int, 2, false, 0, {ARG_INT, ARG_INT}}, + {0x1fd, "fs_write_int", sf_fs_write_int, 2, false, 0, {ARG_INT, ARG_INT}}, + {0x1fe, "fs_write_string", sf_fs_write_string, 2, false, 0, {ARG_INT, ARG_STRING}}, + {0x1ff, "fs_delete", sf_fs_delete, 1, false, 0, {ARG_INT}}, + {0x200, "fs_size", sf_fs_size, 1, true, 0, {ARG_INT}}, + {0x201, "fs_pos", sf_fs_pos, 1, true, -1, {ARG_INT}}, + {0x202, "fs_seek", sf_fs_seek, 2, false, 0, {ARG_INT, ARG_INT}}, + {0x203, "fs_resize", sf_fs_resize, 2, false, 0, {ARG_INT, ARG_INT}}, + {0x204, "get_proto_data", sf_get_proto_data, 2, true, -1, {ARG_INT, ARG_INT}}, + {0x205, "set_proto_data", sf_set_proto_data, 3, false, 0, {ARG_INT, ARG_INT, ARG_INT}}, + {0x207, "register_hook", sf_register_hook, 1, false, 0, {ARG_INT}}, + {0x208, "fs_write_bstring", sf_fs_write_bstring, 2, false, 0, {ARG_INT, ARG_STRING}}, + {0x209, "fs_read_byte", sf_fs_read_byte, 1, true, 0, {ARG_INT}}, + {0x20a, "fs_read_short", sf_fs_read_short, 1, true, 0, {ARG_INT}}, + {0x20b, "fs_read_int", sf_fs_read_int, 1, true, 0, {ARG_INT}}, + {0x20c, "fs_read_float", sf_fs_read_float, 1, true, 0, {ARG_INT}}, + {0x20d, "list_begin", sf_list_begin, 1, true, -1, {ARG_INT}}, + {0x20e, "list_next", sf_list_next, 1, true, -1, {ARG_INT}}, + {0x20f, "list_end", sf_list_end, 1, false, 0, {ARG_INT}}, {0x210, "sfall_ver_major", sf_sfall_ver_major, 0, true}, {0x211, "sfall_ver_minor", sf_sfall_ver_minor, 0, true}, {0x212, "sfall_ver_build", sf_sfall_ver_build, 0, true}, - {0x216, "set_critter_burst_disable", sf_set_critter_burst_disable, 2, false, {ARG_OBJECT, ARG_INT}}, - {0x217, "get_weapon_ammo_pid", sf_get_weapon_ammo_pid, 1, true, {ARG_OBJECT}}, - {0x218, "set_weapon_ammo_pid", sf_set_weapon_ammo_pid, 2, false, {ARG_OBJECT, ARG_INT}}, - {0x219, "get_weapon_ammo_count", sf_get_weapon_ammo_count, 1, true, {ARG_OBJECT}}, - {0x21a, "set_weapon_ammo_count", sf_set_weapon_ammo_count, 2, false, {ARG_OBJECT, ARG_INT}}, + {0x216, "set_critter_burst_disable", sf_set_critter_burst_disable, 2, false, 0, {ARG_OBJECT, ARG_INT}}, + {0x217, "get_weapon_ammo_pid", sf_get_weapon_ammo_pid, 1, true, -1, {ARG_OBJECT}}, + {0x218, "set_weapon_ammo_pid", sf_set_weapon_ammo_pid, 2, false, 0, {ARG_OBJECT, ARG_INT}}, + {0x219, "get_weapon_ammo_count", sf_get_weapon_ammo_count, 1, true, 0, {ARG_OBJECT}}, + {0x21a, "set_weapon_ammo_count", sf_set_weapon_ammo_count, 2, false, 0, {ARG_OBJECT, ARG_INT}}, {0x21e, "get_mouse_buttons", sf_get_mouse_buttons, 0, true}, - {0x224, "create_message_window", sf_create_message_window, 1, false, {ARG_STRING}}, + {0x224, "create_message_window", sf_create_message_window, 1, false, 0, {ARG_STRING}}, {0x228, "get_attack_type", sf_get_attack_type, 0, true}, - {0x229, "force_encounter_with_flags", sf_force_encounter, 2, false, {ARG_INT, ARG_INT}}, - {0x22d, "create_array", sf_create_array, 2, true, {ARG_INT, ARG_INT}}, - {0x22e, "set_array", sf_set_array, 3, false, {ARG_OBJECT, ARG_ANY, ARG_ANY}}, - {0x22f, "get_array", sf_get_array, 2, true, {ARG_ANY, ARG_ANY}}, // can also be used on strings - {0x230, "free_array", sf_free_array, 1, false, {ARG_OBJECT}}, - {0x231, "len_array", sf_len_array, 1, true, {ARG_INT}}, - {0x232, "resize_array", sf_resize_array, 2, false, {ARG_OBJECT, ARG_INT}}, - {0x233, "temp_array", sf_temp_array, 2, true, {ARG_INT, ARG_INT}}, - {0x234, "fix_array", sf_fix_array, 1, false, {ARG_INT}}, - {0x235, "string_split", sf_string_split, 2, true, {ARG_STRING, ARG_STRING}}, - {0x236, "list_as_array", sf_list_as_array, 1, true, {ARG_INT}}, - {0x237, "atoi", sf_atoi, 1, true, {ARG_STRING}}, - {0x238, "atof", sf_atof, 1, true, {ARG_STRING}}, - {0x239, "scan_array", sf_scan_array, 2, true, {ARG_OBJECT, ARG_ANY}}, + {0x229, "force_encounter_with_flags", sf_force_encounter, 2, false, 0, {ARG_INT, ARG_INT}}, + {0x22d, "create_array", sf_create_array, 2, true, -1, {ARG_INT, ARG_INT}}, + {0x22e, "set_array", sf_set_array, 3, false, 0, {ARG_OBJECT, ARG_ANY, ARG_ANY}}, + {0x22f, "get_array", sf_get_array, 2, true}, // can also be used on strings + {0x230, "free_array", sf_free_array, 1, false, 0, {ARG_OBJECT}}, + {0x231, "len_array", sf_len_array, 1, true, 0, {ARG_INT}}, + {0x232, "resize_array", sf_resize_array, 2, false, 0, {ARG_OBJECT, ARG_INT}}, + {0x233, "temp_array", sf_temp_array, 2, true, -1, {ARG_INT, ARG_INT}}, + {0x234, "fix_array", sf_fix_array, 1, false, 0, {ARG_INT}}, + {0x235, "string_split", sf_string_split, 2, true, -1, {ARG_STRING, ARG_STRING}}, + {0x236, "list_as_array", sf_list_as_array, 1, true, -1, {ARG_INT}}, + {0x237, "atoi", sf_atoi, 1, true, 0, {ARG_STRING}}, + {0x238, "atof", sf_atof, 1, true, 0, {ARG_STRING}}, + {0x239, "scan_array", sf_scan_array, 2, true, -1, {ARG_OBJECT, ARG_ANY}}, {0x23c, "get_sfall_args", sf_get_sfall_args, 0, true}, - {0x23d, "set_sfall_arg", sf_set_sfall_arg, 2, false, {ARG_INT, ARG_INT}}, - {0x241, "get_npc_level", sf_get_npc_level, 1, true, {ARG_INTSTR}}, + {0x23d, "set_sfall_arg", sf_set_sfall_arg, 2, false, 0, {ARG_INT, ARG_INT}}, + {0x241, "get_npc_level", sf_get_npc_level, 1, true, 0, {ARG_INTSTR}}, - {0x24e, "substr", sf_substr, 3, true, {ARG_STRING, ARG_INT, ARG_INT}}, - {0x24f, "strlen", sf_strlen, 1, true}, - {0x250, "sprintf", sf_sprintf, 2, true, {ARG_STRING, ARG_ANY}}, - {0x251, "charcode", sf_ord, 1, true, {ARG_STRING}}, + {0x24e, "substr", sf_substr, 3, true, -1, {ARG_STRING, ARG_INT, ARG_INT}}, + {0x24f, "strlen", sf_strlen, 1, true, 0, {ARG_STRING}}, + {0x250, "sprintf", sf_sprintf, 2, true, 0, {ARG_STRING, ARG_ANY}}, + {0x251, "charcode", sf_ord, 1, true, 0, {ARG_STRING}}, // 0x252 // RESERVED - {0x253, "typeof", sf_typeof, 1, true, {ARG_ANY}}, - - {0x254, "save_array", sf_save_array, 2, false, {ARG_ANY, ARG_OBJECT}}, - {0x255, "load_array", sf_load_array, 1, true, {ARG_ANY}}, - {0x256, "array_key", sf_get_array_key, 2, true, {ARG_INT, ARG_INT}}, - {0x257, "arrayexpr", sf_stack_array, 2, true, {ARG_ANY, ARG_ANY}}, + {0x253, "typeof", sf_typeof, 1, true}, + {0x254, "save_array", sf_save_array, 2, false, 0, {ARG_ANY, ARG_OBJECT}}, + {0x255, "load_array", sf_load_array, 1, true, -1, {ARG_INTSTR}}, + {0x256, "array_key", sf_get_array_key, 2, true, 0, {ARG_INT, ARG_INT}}, + {0x257, "arrayexpr", sf_stack_array, 2, true}, // 0x258 // RESERVED for arrays // 0x259 // RESERVED for arrays - - {0x25a, "reg_anim_destroy", sf_reg_anim_destroy, 1, false, {ARG_OBJECT}}, - {0x25b, "reg_anim_animate_and_hide", sf_reg_anim_animate_and_hide, 3, false, {ARG_OBJECT, ARG_INT, ARG_INT}}, - {0x25c, "reg_anim_combat_check", sf_reg_anim_combat_check, 1, false, {ARG_INT}}, - {0x25d, "reg_anim_light", sf_reg_anim_light, 3, false, {ARG_OBJECT, ARG_INT, ARG_INT}}, - {0x25e, "reg_anim_change_fid", sf_reg_anim_change_fid, 3, false, {ARG_OBJECT, ARG_INT, ARG_INT}}, - {0x25f, "reg_anim_take_out", sf_reg_anim_take_out, 3, false, {ARG_OBJECT, ARG_INT, ARG_INT}}, - {0x260, "reg_anim_turn_towards", sf_reg_anim_turn_towards, 3, false, {ARG_OBJECT, ARG_INT, ARG_INT}}, - - {0x261, "metarule2_explosions", sf_explosions_metarule, 3, true, {ARG_INT, ARG_INT, ARG_INT}}, - {0x262, "register_hook_proc", sf_register_hook, 2, false, {ARG_INT, ARG_INT}}, - {0x263, "power", sf_power, 2, true, {ARG_NUMBER, ARG_NUMBER}}, - {0x264, "log", sf_log, 1, true, {ARG_NUMBER}}, - {0x265, "exponent", sf_exponent, 1, true, {ARG_NUMBER}}, - {0x266, "ceil", sf_ceil, 1, true, {ARG_NUMBER}}, - {0x267, "round", sf_round, 1, true, {ARG_NUMBER}}, - {0x26b, "message_str_game", sf_message_str_game, 2, true, {ARG_INT, ARG_INT}}, + {0x25a, "reg_anim_destroy", sf_reg_anim_destroy, 1, false, 0, {ARG_OBJECT}}, + {0x25b, "reg_anim_animate_and_hide", sf_reg_anim_animate_and_hide, 3, false, 0, {ARG_OBJECT, ARG_INT, ARG_INT}}, + {0x25c, "reg_anim_combat_check", sf_reg_anim_combat_check, 1, false, 0, {ARG_INT}}, + {0x25d, "reg_anim_light", sf_reg_anim_light, 3, false, 0, {ARG_OBJECT, ARG_INT, ARG_INT}}, + {0x25e, "reg_anim_change_fid", sf_reg_anim_change_fid, 3, false, 0, {ARG_OBJECT, ARG_INT, ARG_INT}}, + {0x25f, "reg_anim_take_out", sf_reg_anim_take_out, 3, false, 0, {ARG_OBJECT, ARG_INT, ARG_INT}}, + {0x260, "reg_anim_turn_towards", sf_reg_anim_turn_towards, 3, false, 0, {ARG_OBJECT, ARG_INT, ARG_INT}}, + {0x261, "metarule2_explosions", sf_explosions_metarule, 3, true, -1, {ARG_INT, ARG_INT, ARG_INT}}, + {0x262, "register_hook_proc", sf_register_hook, 2, false, 0, {ARG_INT, ARG_INT}}, + {0x263, "power", sf_power, 2, true, 0, {ARG_NUMBER, ARG_NUMBER}}, + {0x264, "log", sf_log, 1, true, 0, {ARG_NUMBER}}, + {0x265, "exponent", sf_exponent, 1, true, 0, {ARG_NUMBER}}, + {0x266, "ceil", sf_ceil, 1, true, 0, {ARG_NUMBER}}, + {0x267, "round", sf_round, 1, true, 0, {ARG_NUMBER}}, + // 0x268 RESERVED + // 0x269 RESERVED + {0x26b, "message_str_game", sf_message_str_game, 2, true, 0, {ARG_INT, ARG_INT}}, {0x26c, "sneak_success", sf_sneak_success, 0, true}, - {0x26d, "tile_light", sf_tile_light, 2, true, {ARG_INT, ARG_INT}}, - {0x26e, "obj_blocking_line", sf_make_straight_path, 3, true, {ARG_OBJECT, ARG_INT, ARG_INT}}, - {0x26f, "obj_blocking_tile", sf_obj_blocking_at, 3, true, {ARG_INT, ARG_INT, ARG_INT}}, - {0x270, "tile_get_objs", sf_tile_get_objects, 2, true, {ARG_INT, ARG_INT}}, - {0x271, "party_member_list", sf_get_party_members, 1, true, {ARG_INT}}, - {0x272, "path_find_to", sf_make_path, 3, true, {ARG_OBJECT, ARG_INT, ARG_INT}}, - {0x273, "create_spatial", sf_create_spatial, 4, true, {ARG_INT, ARG_INT, ARG_INT, ARG_INT}}, - {0x274, "art_exists", sf_art_exists, 1, true, {ARG_INT}}, - {0x275, "obj_is_carrying_obj", sf_obj_is_carrying_obj, 2, true, {ARG_OBJECT, ARG_OBJECT}}, + {0x26d, "tile_light", sf_tile_light, 2, true, -1, {ARG_INT, ARG_INT}}, + {0x26e, "obj_blocking_line", sf_make_straight_path, 3, true, 0, {ARG_OBJECT, ARG_INT, ARG_INT}}, + {0x26f, "obj_blocking_tile", sf_obj_blocking_at, 3, true, 0, {ARG_INT, ARG_INT, ARG_INT}}, + {0x270, "tile_get_objs", sf_tile_get_objects, 2, true, -1, {ARG_INT, ARG_INT}}, + {0x271, "party_member_list", sf_get_party_members, 1, true, -1, {ARG_INT}}, + {0x272, "path_find_to", sf_make_path, 3, true, -1, {ARG_OBJECT, ARG_INT, ARG_INT}}, + {0x273, "create_spatial", sf_create_spatial, 4, true, 0, {ARG_INT, ARG_INT, ARG_INT, ARG_INT}}, + {0x274, "art_exists", sf_art_exists, 1, true, 0, {ARG_INT}}, + {0x275, "obj_is_carrying_obj", sf_obj_is_carrying_obj, 2, true, 0, {ARG_OBJECT, ARG_OBJECT}}, // universal opcodes: {0x276, "sfall_func0", HandleMetarule, 1, true}, @@ -217,8 +217,8 @@ static SfallOpcodeInfo opcodeInfoArray[] = { {0x27b, "sfall_func5", HandleMetarule, 6, true}, {0x27c, "sfall_func6", HandleMetarule, 7, true}, // if you need more arguments - use arrays - {0x27d, "register_hook_proc_spec", sf_register_hook, 2, false, {ARG_INT, ARG_INT}}, - {0x27e, "reg_anim_callback", sf_reg_anim_callback, 1, false, {ARG_INT}}, + {0x27d, "register_hook_proc_spec", sf_register_hook, 2, false, 0, {ARG_INT, ARG_INT}}, + {0x27e, "reg_anim_callback", sf_reg_anim_callback, 1, false, 0, {ARG_INT}}, }; // A hash-table for opcode info, indexed by opcode. @@ -236,7 +236,10 @@ void InitOpcodeInfoTable() { // Default handler for Sfall Opcodes. // Searches current opcode in Opcode Info table and executes the appropriate handler. -void __fastcall defaultOpcodeHandlerCall(fo::Program* program, DWORD opcodeOffset) { +void __fastcall defaultOpcodeHandler(fo::Program* program, DWORD opcodeOffset) { // eax/ebx - program, edx - opcodeOffset + __asm push ecx; + __asm mov program, ebx; + int opcode = opcodeOffset / 4; auto iter = opcodeInfoMap.find(opcode); if (iter != opcodeInfoMap.end()) { @@ -246,17 +249,7 @@ void __fastcall defaultOpcodeHandlerCall(fo::Program* program, DWORD opcodeOffse } else { fo::func::interpretError("Unknown opcode: %d", opcode); } -} - -// Default handler for Sfall opcodes (naked function for integration with the engine). -void __declspec(naked) defaultOpcodeHandler() { - __asm { - push ecx; - mov ecx, eax; // ecx - program - call defaultOpcodeHandlerCall; // edx - opcodeOffset - pop ecx; - retn; - } + __asm pop ecx; } void InitNewOpcodes() { @@ -414,9 +407,7 @@ void InitNewOpcodes() { opcodes[0x24c] = op_gdialog_get_barter_mod; opcodes[0x24d] = op_set_inven_ap_cost; - // opcodes[0x268]= RESERVED - // opcodes[0x269]= RESERVED - // opcodes[0x26a]=op_game_ui_redraw; + //opcodes[0x26a]=op_game_ui_redraw; // configure default opcode handler for (int i = sfallOpcodeStart; i < opcodeCount; i++) { diff --git a/sfall/Modules/Scripting/ScriptValue.h b/sfall/Modules/Scripting/ScriptValue.h index 34d01958..8793c04f 100644 --- a/sfall/Modules/Scripting/ScriptValue.h +++ b/sfall/Modules/Scripting/ScriptValue.h @@ -87,7 +87,7 @@ private: fo::GameObject* gObj; } _val; - DataType _type; // TODO: replace with enum class + DataType _type; }; } From 6d124817545520397dc35ca5ddcb4c91b117681f Mon Sep 17 00:00:00 2001 From: NovaRain Date: Thu, 28 Nov 2019 14:05:36 +0800 Subject: [PATCH 14/36] Refactored script function handlers --- sfall/FalloutEngine/Fallout2.h | 6 +- sfall/FalloutEngine/Functions_def.h | 4 +- sfall/Modules/Scripting/Arrays.cpp | 6 +- sfall/Modules/Scripting/Handlers/Anims.cpp | 38 +++--- sfall/Modules/Scripting/Handlers/Arrays.cpp | 71 +++++----- sfall/Modules/Scripting/Handlers/Core.cpp | 9 +- .../Modules/Scripting/Handlers/FileSystem.cpp | 34 ++--- sfall/Modules/Scripting/Handlers/Graphics.cpp | 7 +- .../Modules/Scripting/Handlers/Interface.cpp | 79 ++++++----- sfall/Modules/Scripting/Handlers/Metarule.cpp | 4 +- sfall/Modules/Scripting/Handlers/Misc.cpp | 65 ++++----- sfall/Modules/Scripting/Handlers/Objects.cpp | 125 +++++++++--------- sfall/Modules/Scripting/Handlers/Perks.cpp | 16 ++- sfall/Modules/Scripting/Handlers/Stats.cpp | 107 ++++----------- sfall/Modules/Scripting/Handlers/Utils.cpp | 8 +- sfall/Modules/Scripting/Handlers/Worldmap.cpp | 40 +++--- sfall/Modules/Scripting/Opcodes.cpp | 8 +- sfall/Modules/Scripting/ScriptValue.cpp | 12 +- sfall/Modules/Scripting/ScriptValue.h | 2 + 19 files changed, 304 insertions(+), 337 deletions(-) diff --git a/sfall/FalloutEngine/Fallout2.h b/sfall/FalloutEngine/Fallout2.h index e860bbb9..bbd3ebba 100644 --- a/sfall/FalloutEngine/Fallout2.h +++ b/sfall/FalloutEngine/Fallout2.h @@ -69,12 +69,10 @@ Returns the value to the script eax register must contain the script_ptr edx register must contain the returned value - rscript - register name for temporarily saving script_ptr */ -#define _RET_VAL_INT(rscript) __asm { \ - __asm mov rscript, eax \ +#define _RET_VAL_INT __asm { \ __asm call fo::funcoffs::interpretPushLong_ \ __asm mov edx, VAR_TYPE_INT \ - __asm mov eax, rscript \ + __asm mov eax, ebx \ __asm call fo::funcoffs::interpretPushShort_ \ } diff --git a/sfall/FalloutEngine/Functions_def.h b/sfall/FalloutEngine/Functions_def.h index 1669ae9c..bce59504 100644 --- a/sfall/FalloutEngine/Functions_def.h +++ b/sfall/FalloutEngine/Functions_def.h @@ -62,11 +62,13 @@ WRAP_WATCOM_FUNC1(long, folder_print_line, const char*, text) WRAP_WATCOM_FUNC1(long, folder_print_seperator, const char*, text) WRAP_WATCOM_FUNC1(long, game_get_global_var, long, globalVar) WRAP_WATCOM_FUNC1(void, gdialogDisplayMsg, const char*, message) -WRAP_WATCOM_FUNC1(long, gmouse_3d_set_mode, long, mode) +WRAP_WATCOM_FUNC0(long, gmouse_3d_get_mode) +WRAP_WATCOM_FUNC1(void, gmouse_3d_set_mode, long, mode) WRAP_WATCOM_FUNC1(long, gmouse_set_cursor, long, picNum) WRAP_WATCOM_FUNC1(Window*, GNW_find, long, winRef) WRAP_WATCOM_FUNC2(long, intface_get_attack, DWORD*, hitMode, DWORD*, isSecondary) WRAP_WATCOM_FUNC0(long, intface_is_item_right_hand) +WRAP_WATCOM_FUNC0(long, intface_is_hidden) // redraws the main game interface windows (useful after changing some data like active hand, etc.) WRAP_WATCOM_FUNC0(void, intface_redraw) WRAP_WATCOM_FUNC0(void, intface_toggle_item_state) diff --git a/sfall/Modules/Scripting/Arrays.cpp b/sfall/Modules/Scripting/Arrays.cpp index fd1adff2..67897087 100644 --- a/sfall/Modules/Scripting/Arrays.cpp +++ b/sfall/Modules/Scripting/Arrays.cpp @@ -670,7 +670,7 @@ ScriptValue ScanArray(DWORD id, const ScriptValue& val) { } DWORD LoadArray(const ScriptValue& key) { - if (!key.isInt() || key.asInt() != 0) { // returns arrayId by it's key (ignoring int(0) because it is used to "unsave" array) + if (!key.isInt() || key.rawValue() != 0) { // returns arrayId by it's key (ignoring int(0) because it is used to "unsave" array) sArrayElement keyEl = sArrayElement(key.rawValue(), key.type()); if (keyEl.type == DataType::STR && strcmp(keyEl.strVal, get_all_arrays_special_key) == 0) { // this is a special case to get temp array containing all saved arrays @@ -698,7 +698,7 @@ DWORD LoadArray(const ScriptValue& key) { void SaveArray(const ScriptValue& key, DWORD id) { array_itr it, itArray = arrays.find(id); // arrayId => arrayVar if (itArray != arrays.end()) { - if (!key.isInt() || key.asInt() != 0) { + if (!key.isInt() || key.rawValue() != 0) { // make array permanent FixArray(itArray->first); // if another array is saved under the same key, clear it @@ -741,7 +741,7 @@ long StackArray(const ScriptValue& key, const ScriptValue& val) { // automatically resize array to fit one new element ResizeArray(id, arrays[id].size() + 1); } - SetArray(id, key, val, 0); + SetArray(id, key, val, false); return 0; } diff --git a/sfall/Modules/Scripting/Handlers/Anims.cpp b/sfall/Modules/Scripting/Handlers/Anims.cpp index 4ac42aa8..f7badde9 100644 --- a/sfall/Modules/Scripting/Handlers/Anims.cpp +++ b/sfall/Modules/Scripting/Handlers/Anims.cpp @@ -52,21 +52,21 @@ bool checkCombatMode() { } void sf_reg_anim_combat_check(OpcodeContext& ctx) { - RegAnimCombatCheck(ctx.arg(0).asInt()); + RegAnimCombatCheck(ctx.arg(0).rawValue()); } void sf_reg_anim_destroy(OpcodeContext& ctx) { if (!checkCombatMode()) { - auto obj = ctx.arg(0).asObject(); + auto obj = ctx.arg(0).object(); fo::func::register_object_must_erase(obj); } } void sf_reg_anim_animate_and_hide(OpcodeContext& ctx) { if (!checkCombatMode()) { - auto obj = ctx.arg(0).asObject(); - int animId = ctx.arg(1).asInt(), - delay = ctx.arg(2).asInt(); + auto obj = ctx.arg(0).object(); + int animId = ctx.arg(1).rawValue(), + delay = ctx.arg(2).rawValue(); fo::func::register_object_animate_and_hide(obj, animId, delay); } @@ -74,9 +74,9 @@ void sf_reg_anim_animate_and_hide(OpcodeContext& ctx) { void sf_reg_anim_light(OpcodeContext& ctx) { if (!checkCombatMode()) { - auto obj = ctx.arg(0).asObject(); - int radius = ctx.arg(1).asInt(), - delay = ctx.arg(2).asInt(); + auto obj = ctx.arg(0).object(); + int radius = ctx.arg(1).rawValue(), + delay = ctx.arg(2).rawValue(); if (radius < 0) { radius = 0; @@ -89,9 +89,9 @@ void sf_reg_anim_light(OpcodeContext& ctx) { void sf_reg_anim_change_fid(OpcodeContext& ctx) { if (!checkCombatMode()) { - auto obj = ctx.arg(0).asObject(); - int fid = ctx.arg(1).asInt(), - delay = ctx.arg(2).asInt(); + auto obj = ctx.arg(0).object(); + int fid = ctx.arg(1).rawValue(), + delay = ctx.arg(2).rawValue(); fo::func::register_object_change_fid(obj, fid, delay); } @@ -99,9 +99,9 @@ void sf_reg_anim_change_fid(OpcodeContext& ctx) { void sf_reg_anim_take_out(OpcodeContext& ctx) { if (!checkCombatMode()) { - auto obj = ctx.arg(0).asObject(); - int holdFrame = ctx.arg(1).asInt(), - nothing = ctx.arg(2).asInt(); // not used by engine + auto obj = ctx.arg(0).object(); + int holdFrame = ctx.arg(1).rawValue(), + nothing = ctx.arg(2).rawValue(); // not used by engine fo::func::register_object_take_out(obj, holdFrame, nothing); } @@ -109,9 +109,9 @@ void sf_reg_anim_take_out(OpcodeContext& ctx) { void sf_reg_anim_turn_towards(OpcodeContext& ctx) { if (!checkCombatMode()) { - auto obj = ctx.arg(0).asObject(); - int tile = ctx.arg(1).asInt(), - nothing = ctx.arg(2).asInt(); + auto obj = ctx.arg(0).object(); + int tile = ctx.arg(1).rawValue(), + nothing = ctx.arg(2).rawValue(); fo::func::register_object_turn_towards(obj, tile, nothing); } @@ -134,8 +134,8 @@ void sf_reg_anim_callback(OpcodeContext& ctx) { } void sf_explosions_metarule(OpcodeContext& ctx) { - int mode = ctx.arg(0).asInt(), - result = ExplosionsMetaruleFunc(mode, ctx.arg(1).asInt(), ctx.arg(2).asInt()); + int mode = ctx.arg(0).rawValue(), + result = ExplosionsMetaruleFunc(mode, ctx.arg(1).rawValue(), ctx.arg(2).rawValue()); if (result == -1) { ctx.printOpcodeError("%s() - mode (%d) is not supported for the function.", ctx.getOpcodeName(), mode); diff --git a/sfall/Modules/Scripting/Handlers/Arrays.cpp b/sfall/Modules/Scripting/Handlers/Arrays.cpp index 21aa1a3f..a88780b8 100644 --- a/sfall/Modules/Scripting/Handlers/Arrays.cpp +++ b/sfall/Modules/Scripting/Handlers/Arrays.cpp @@ -46,6 +46,7 @@ void sf_set_array(OpcodeContext& ctx) { /* used in place of [] operator when compiling in sslc so it works as get_array if first argument is int and as substr(x, y, 1) if first argument is string + example: vartext[5] */ void sf_get_array(OpcodeContext& ctx) { if (ctx.arg(0).isInt()) { @@ -55,7 +56,7 @@ void sf_get_array(OpcodeContext& ctx) { } else if (ctx.arg(0).isString()) { if (ctx.arg(1).isInt()) { auto str = Substring(ctx.arg(0).strValue(), ctx.arg(1).rawValue(), 1); - ctx.setReturn(str); + ctx.setReturn(str); // returns char of string } else { ctx.printOpcodeError("%s() - index must be numeric when used on a string.", ctx.getOpcodeName()); } @@ -132,7 +133,7 @@ struct sList { } }; -static DWORD listID = 0xCCCCCC; +static DWORD listID = 0xCCCCCC; // start ID struct ListId { sList* list; @@ -186,14 +187,6 @@ static void FillListVector(DWORD type, std::vector& vec) { } } -static DWORD ListBegin(DWORD type) { - std::vector vec = std::vector(); - FillListVector(type, vec); - sList* list = new sList(&vec); - mList.emplace_back(list); - return listID; -} - static DWORD ListAsArray(DWORD type) { std::vector vec = std::vector(); FillListVector(type, vec); @@ -205,12 +198,45 @@ static DWORD ListAsArray(DWORD type) { return id; } +void sf_list_as_array(OpcodeContext& ctx) { + auto arrayId = ListAsArray(ctx.arg(0).rawValue()); + ctx.setReturn(arrayId, DataType::INT); +} + +static DWORD ListBegin(DWORD type) { + std::vector vec = std::vector(); + FillListVector(type, vec); + sList* list = new sList(&vec); + mList.emplace_back(list); + return listID; +} + +void sf_list_begin(OpcodeContext& ctx) { + ctx.setReturn(ListBegin(ctx.arg(0).rawValue()), DataType::INT); +} + static fo::GameObject* ListNext(sList* list) { - if (!list || list->pos == list->len) return 0; - else return list->obj[list->pos++]; + return (!list || list->pos == list->len) ? 0 : list->obj[list->pos++]; +} + +void sf_list_next(OpcodeContext& ctx) { + fo::GameObject* obj = nullptr; + auto id = ctx.arg(0).rawValue(); + if (id != 0) { + sList* list = nullptr; + for (std::vector::const_iterator it = mList.cbegin(), it_end = mList.cend(); it != it_end; ++it) { + if (it->id == id) { + list = it->list; + break; + } + } + obj = ListNext(list); + } + ctx.setReturn(obj); } static void ListEnd(DWORD id) { + if (id == 0) return; for (std::vector::const_iterator it = mList.cbegin(), it_end = mList.cend(); it != it_end; ++it) { if (it->id == id) { delete[] it->list->obj; @@ -221,27 +247,6 @@ static void ListEnd(DWORD id) { } } -void sf_list_begin(OpcodeContext& ctx) { - ctx.setReturn(ListBegin(ctx.arg(0).rawValue()), DataType::INT); -} - -void sf_list_as_array(OpcodeContext& ctx) { - auto arrayId = ListAsArray(ctx.arg(0).rawValue()); - ctx.setReturn(arrayId, DataType::INT); -} - -void sf_list_next(OpcodeContext& ctx) { - auto id = ctx.arg(0).rawValue(); - sList* list = nullptr; - for (std::vector::const_iterator it = mList.cbegin(), it_end = mList.cend(); it != it_end; ++it) { - if (it->id == id) { - list = it->list; - break; - } - } - ctx.setReturn(ListNext(list)); -} - void sf_list_end(OpcodeContext& ctx) { ListEnd(ctx.arg(0).rawValue()); } diff --git a/sfall/Modules/Scripting/Handlers/Core.cpp b/sfall/Modules/Scripting/Handlers/Core.cpp index f77e396f..20acd0de 100644 --- a/sfall/Modules/Scripting/Handlers/Core.cpp +++ b/sfall/Modules/Scripting/Handlers/Core.cpp @@ -62,7 +62,7 @@ void __declspec(naked) op_available_global_script_types() { __asm { push ecx; mov edx, availableGlobalScriptTypes; - _RET_VAL_INT(ecx); + _RET_VAL_INT; pop ecx; retn; } @@ -108,11 +108,10 @@ void sf_get_sfall_global_float(OpcodeContext& ctx) { void __declspec(naked) op_get_sfall_arg() { __asm { push ecx; - push eax; call HookScripts::GetHSArg; mov edx, eax; - pop eax; - _RET_VAL_INT(ecx); + mov eax, ebx; + _RET_VAL_INT; pop ecx; retn; } @@ -159,7 +158,7 @@ void __declspec(naked) op_init_hook() { __asm { push ecx; mov edx, initingHookScripts; - _RET_VAL_INT(ecx); + _RET_VAL_INT; pop ecx; retn; } diff --git a/sfall/Modules/Scripting/Handlers/FileSystem.cpp b/sfall/Modules/Scripting/Handlers/FileSystem.cpp index 84cfad0c..ebd773a4 100644 --- a/sfall/Modules/Scripting/Handlers/FileSystem.cpp +++ b/sfall/Modules/Scripting/Handlers/FileSystem.cpp @@ -29,71 +29,71 @@ namespace script { void sf_fs_create(OpcodeContext& ctx) { - ctx.setReturn(FScreate(ctx.arg(0).asString(), ctx.arg(1).asInt())); + ctx.setReturn(FScreate(ctx.arg(0).strValue(), ctx.arg(1).rawValue())); } void sf_fs_copy(OpcodeContext& ctx) { - ctx.setReturn(FScopy(ctx.arg(0).asString(), ctx.arg(1).asString())); + ctx.setReturn(FScopy(ctx.arg(0).strValue(), ctx.arg(1).strValue())); } void sf_fs_find(OpcodeContext& ctx) { - ctx.setReturn(FSfind(ctx.arg(0).asString())); + ctx.setReturn(FSfind(ctx.arg(0).strValue())); } void sf_fs_write_byte(OpcodeContext& ctx) { - FSwrite_byte(ctx.arg(0).asInt(), ctx.arg(1).asInt()); + FSwrite_byte(ctx.arg(0).rawValue(), ctx.arg(1).rawValue()); } void sf_fs_write_short(OpcodeContext& ctx) { - FSwrite_short(ctx.arg(0).asInt(), ctx.arg(1).asInt()); + FSwrite_short(ctx.arg(0).rawValue(), ctx.arg(1).rawValue()); } void sf_fs_write_int(OpcodeContext& ctx) { - FSwrite_int(ctx.arg(0).asInt(), ctx.arg(1).asInt()); + FSwrite_int(ctx.arg(0).rawValue(), ctx.arg(1).rawValue()); } void sf_fs_write_string(OpcodeContext& ctx) { - FSwrite_string(ctx.arg(0).asInt(), ctx.arg(1).asString()); + FSwrite_string(ctx.arg(0).rawValue(), ctx.arg(1).strValue()); } void sf_fs_write_bstring(OpcodeContext& ctx) { - FSwrite_bstring(ctx.arg(0).asInt(), ctx.arg(1).asString()); + FSwrite_bstring(ctx.arg(0).rawValue(), ctx.arg(1).strValue()); } void sf_fs_read_byte(OpcodeContext& ctx) { - ctx.setReturn(FSread_byte(ctx.arg(0).asInt())); + ctx.setReturn(FSread_byte(ctx.arg(0).rawValue())); } void sf_fs_read_short(OpcodeContext& ctx) { - ctx.setReturn(FSread_short(ctx.arg(0).asInt())); + ctx.setReturn(FSread_short(ctx.arg(0).rawValue())); } void sf_fs_read_int(OpcodeContext& ctx) { - ctx.setReturn(FSread_int(ctx.arg(0).asInt())); + ctx.setReturn(FSread_int(ctx.arg(0).rawValue())); } void sf_fs_read_float(OpcodeContext& ctx) { - ctx.setReturn(FSread_int(ctx.arg(0).asInt()), DataType::FLOAT); + ctx.setReturn(FSread_int(ctx.arg(0).rawValue()), DataType::FLOAT); } void sf_fs_delete(OpcodeContext& ctx) { - FSdelete(ctx.arg(0).asInt()); + FSdelete(ctx.arg(0).rawValue()); } void sf_fs_size(OpcodeContext& ctx) { - ctx.setReturn(FSsize(ctx.arg(0).asInt())); + ctx.setReturn(FSsize(ctx.arg(0).rawValue())); } void sf_fs_pos(OpcodeContext& ctx) { - ctx.setReturn(FSpos(ctx.arg(0).asInt())); + ctx.setReturn(FSpos(ctx.arg(0).rawValue())); } void sf_fs_seek(OpcodeContext& ctx) { - FSseek(ctx.arg(0).asInt(), ctx.arg(1).asInt()); + FSseek(ctx.arg(0).rawValue(), ctx.arg(1).rawValue()); } void sf_fs_resize(OpcodeContext& ctx) { - FSresize(ctx.arg(0).asInt(), ctx.arg(1).asInt()); + FSresize(ctx.arg(0).rawValue(), ctx.arg(1).rawValue()); } } diff --git a/sfall/Modules/Scripting/Handlers/Graphics.cpp b/sfall/Modules/Scripting/Handlers/Graphics.cpp index 18b4c9d5..6f9a6c76 100644 --- a/sfall/Modules/Scripting/Handlers/Graphics.cpp +++ b/sfall/Modules/Scripting/Handlers/Graphics.cpp @@ -36,7 +36,7 @@ void __declspec(naked) op_graphics_funcs_available() { cmp Graphics::mode, 3; seta dl; and edx, 0xFF; - _RET_VAL_INT(ecx); + _RET_VAL_INT; pop ecx; retn; } @@ -406,11 +406,10 @@ end: void __declspec(naked) op_get_shader_version() { __asm { push ecx; - push eax; call GetShaderVersion; mov edx, eax; - pop eax; - _RET_VAL_INT(ecx); + mov eax, ebx; + _RET_VAL_INT; pop ecx; retn; } diff --git a/sfall/Modules/Scripting/Handlers/Interface.cpp b/sfall/Modules/Scripting/Handlers/Interface.cpp index b8c4663c..322dbb00 100644 --- a/sfall/Modules/Scripting/Handlers/Interface.cpp +++ b/sfall/Modules/Scripting/Handlers/Interface.cpp @@ -36,14 +36,14 @@ void __declspec(naked) op_input_funcs_available() { __asm { push ecx; mov edx, 1; // They're always available from 2.9 on - _RET_VAL_INT(ecx); + _RET_VAL_INT; pop ecx; retn; } } void sf_key_pressed(OpcodeContext& ctx) { - ctx.setReturn(static_cast(KeyDown(ctx.arg(0).rawValue()))); + ctx.setReturn(KeyDown(ctx.arg(0).rawValue())); } void __declspec(naked) op_tap_key() { @@ -67,7 +67,7 @@ void __declspec(naked) op_get_mouse_x() { push ecx; mov edx, ds:[FO_VAR_mouse_x_]; add edx, ds:[FO_VAR_mouse_hotx]; - _RET_VAL_INT(ecx); + _RET_VAL_INT; pop ecx; retn; } @@ -78,7 +78,7 @@ void __declspec(naked) op_get_mouse_y() { push ecx; mov edx, ds:[FO_VAR_mouse_y_]; add edx, ds:[FO_VAR_mouse_hoty]; - _RET_VAL_INT(ecx); + _RET_VAL_INT; pop ecx; retn; } @@ -97,7 +97,7 @@ void __declspec(naked) op_get_window_under_mouse() { __asm { push ecx; mov edx, ds:[FO_VAR_last_button_winID]; - _RET_VAL_INT(ecx); + _RET_VAL_INT; pop ecx; retn; } @@ -109,7 +109,7 @@ void __declspec(naked) op_get_screen_width() { mov edx, ds:[FO_VAR_scr_size + 8]; // _scr_size.offx sub edx, ds:[FO_VAR_scr_size]; // _scr_size.x inc edx; - _RET_VAL_INT(ecx); + _RET_VAL_INT; pop ecx; retn; } @@ -121,7 +121,7 @@ void __declspec(naked) op_get_screen_height() { mov edx, ds:[FO_VAR_scr_size + 12]; // _scr_size.offy sub edx, ds:[FO_VAR_scr_size + 4]; // _scr_size.y inc edx; - _RET_VAL_INT(ecx); + _RET_VAL_INT; pop ecx; retn; } @@ -153,7 +153,7 @@ void __declspec(naked) op_get_viewport_x() { __asm { push ecx; mov edx, ds:[FO_VAR_wmWorldOffsetX]; - _RET_VAL_INT(ecx); + _RET_VAL_INT; pop ecx; retn; } @@ -163,7 +163,7 @@ void __declspec(naked) op_get_viewport_y() { __asm { push ecx; mov edx, ds:[FO_VAR_wmWorldOffsetY]; - _RET_VAL_INT(ecx); + _RET_VAL_INT; pop ecx; retn; } @@ -198,7 +198,7 @@ void sf_add_iface_tag(OpcodeContext &ctx) { } void sf_show_iface_tag(OpcodeContext &ctx) { - int tag = ctx.arg(0).asInt(); + int tag = ctx.arg(0).rawValue(); if (tag == 3 || tag == 4) { __asm mov eax, tag; __asm call fo::funcoffs::pc_flag_on_; @@ -208,7 +208,7 @@ void sf_show_iface_tag(OpcodeContext &ctx) { } void sf_hide_iface_tag(OpcodeContext &ctx) { - int tag = ctx.arg(0).asInt(); + int tag = ctx.arg(0).rawValue(); if (tag == 3 || tag == 4) { __asm mov eax, tag; __asm call fo::funcoffs::pc_flag_off_; @@ -219,7 +219,7 @@ void sf_hide_iface_tag(OpcodeContext &ctx) { void sf_is_iface_tag_active(OpcodeContext &ctx) { bool result = false; - int tag = ctx.arg(0).asInt(); + int tag = ctx.arg(0).rawValue(); if (tag >= 0 && tag < 5) { if (tag == 1 || tag == 2) { // Poison/Radiation tag += 2; @@ -256,12 +256,7 @@ void sf_intface_hide(OpcodeContext& ctx) { } void sf_intface_is_hidden(OpcodeContext& ctx) { - int isHidden; - __asm { - call fo::funcoffs::intface_is_hidden_; - mov isHidden, eax; - } - ctx.setReturn(isHidden); + ctx.setReturn(fo::func::intface_is_hidden()); } void sf_tile_refresh_display(OpcodeContext& ctx) { @@ -269,16 +264,11 @@ void sf_tile_refresh_display(OpcodeContext& ctx) { } void sf_get_cursor_mode(OpcodeContext& ctx) { - int cursorMode; - __asm { - call fo::funcoffs::gmouse_3d_get_mode_; - mov cursorMode, eax; - } - ctx.setReturn(cursorMode); + ctx.setReturn(fo::func::gmouse_3d_get_mode()); } void sf_set_cursor_mode(OpcodeContext& ctx) { - fo::func::gmouse_3d_set_mode(ctx.arg(0).asInt()); + fo::func::gmouse_3d_set_mode(ctx.arg(0).rawValue()); } void sf_display_stats(OpcodeContext& ctx) { @@ -289,13 +279,14 @@ void sf_display_stats(OpcodeContext& ctx) { } void sf_set_iface_tag_text(OpcodeContext& ctx) { - int boxTag = ctx.arg(0).asInt(); + int boxTag = ctx.arg(0).rawValue(); int maxBox = BarBoxes::MaxBox(); if (boxTag > 4 && boxTag <= maxBox) { - BarBoxes::SetText(boxTag, ctx.arg(1).strValue(), ctx.arg(2).asInt()); + BarBoxes::SetText(boxTag, ctx.arg(1).strValue(), ctx.arg(2).rawValue()); } else { ctx.printOpcodeError("%s() - tag value must be in the range of 5 to %d.", ctx.getMetaruleName(), maxBox); + ctx.setReturn(-1); } } @@ -333,22 +324,23 @@ void sf_inventory_redraw(OpcodeContext& ctx) { void sf_dialog_message(OpcodeContext& ctx) { DWORD loopFlag = GetLoopFlags(); if ((loopFlag & DIALOGVIEW) == 0 && (loopFlag & DIALOG)) { - const char* message = ctx.arg(0).asString(); + const char* message = ctx.arg(0).strValue(); fo::func::gdialogDisplayMsg(message); } } void sf_create_win(OpcodeContext& ctx) { - int flags = (ctx.arg(5).type() != DataType::NONE) - ? ctx.arg(5).asInt() + int flags = (ctx.numArgs() > 5) + ? ctx.arg(5).rawValue() : fo::WinFlags::MoveOnTop; - if (fo::func::createWindow(ctx.arg(0).asString(), - ctx.arg(1).asInt(), ctx.arg(2).asInt(), // y, x - ctx.arg(3).asInt(), ctx.arg(4).asInt(), // w, h + if (fo::func::createWindow(ctx.arg(0).strValue(), + ctx.arg(1).rawValue(), ctx.arg(2).rawValue(), // y, x + ctx.arg(3).rawValue(), ctx.arg(4).rawValue(), // w, h 256, flags) == -1) { ctx.printOpcodeError("%s() - couldn't create window.", ctx.getMetaruleName()); + ctx.setReturn(-1); } } @@ -422,16 +414,16 @@ void sf_set_window_flag(OpcodeContext& ctx) { } } -static void DrawImage(OpcodeContext& ctx, bool isScaled) { +static long DrawImage(OpcodeContext& ctx, bool isScaled) { if (*(DWORD*)FO_VAR_currentWindow == -1) { ctx.printOpcodeError("%s() - no created/selected window for the image.", ctx.getMetaruleName()); - return; + return 0; } long direction = 0; const char* file = nullptr; if (ctx.arg(0).isInt()) { // art id long fid = ctx.arg(0).rawValue(); - if (fid == -1) return; + if (fid == -1) return -1; long _fid = fid & 0xFFFFFFF; file = fo::func::art_get_name(_fid); // .frm if (_fid >> 24 == fo::OBJ_TYPE_CRITTER) { @@ -441,12 +433,12 @@ static void DrawImage(OpcodeContext& ctx, bool isScaled) { } } } else { - file = ctx.arg(0).strValue(); // path to frm file + file = ctx.arg(0).strValue(); // path to frm/pcx file } fo::FrmFile* frmPtr = nullptr; if (fo::func::load_frame(file, &frmPtr)) { ctx.printOpcodeError("%s() - cannot open the file: %s", ctx.getMetaruleName(), file); - return; + return -1; } fo::FrmFrameData* framePtr = (fo::FrmFrameData*)&frmPtr->width; if (direction > 0 && direction < 6) { @@ -483,7 +475,7 @@ static void DrawImage(OpcodeContext& ctx, bool isScaled) { } else if (s_height <= -1 && s_width > 0) { s_height = s_width * framePtr->height / framePtr->width; } - if (s_width <= 0 || s_height <= 0) return; + if (s_width <= 0 || s_height <= 0) return 0; long w_width = fo::func::windowWidth(); long xy_pos = (y * w_width) + x; @@ -496,25 +488,28 @@ static void DrawImage(OpcodeContext& ctx, bool isScaled) { mov eax, frmPtr; call fo::funcoffs::mem_free_; } + return 1; } void sf_draw_image(OpcodeContext& ctx) { - DrawImage(ctx, false); + ctx.setReturn(DrawImage(ctx, false)); } void sf_draw_image_scaled(OpcodeContext& ctx) { - DrawImage(ctx, true); + ctx.setReturn(DrawImage(ctx, true)); } void sf_unwield_slot(OpcodeContext& ctx) { fo::InvenType slot = static_cast(ctx.arg(1).rawValue()); if (slot < fo::INVEN_TYPE_WORN || slot > fo::INVEN_TYPE_LEFT_HAND) { ctx.printOpcodeError("%s() - incorrect slot number.", ctx.getMetaruleName()); + ctx.setReturn(-1); return; } - fo::GameObject* critter = ctx.arg(0).asObject(); + fo::GameObject* critter = ctx.arg(0).object(); if (critter->Type() != fo::ObjType::OBJ_TYPE_CRITTER) { ctx.printOpcodeError("%s() - the object is not a critter.", ctx.getMetaruleName()); + ctx.setReturn(-1); return; } bool isDude = (critter == fo::var::obj_dude); diff --git a/sfall/Modules/Scripting/Handlers/Metarule.cpp b/sfall/Modules/Scripting/Handlers/Metarule.cpp index 257ac4d9..b79b876c 100644 --- a/sfall/Modules/Scripting/Handlers/Metarule.cpp +++ b/sfall/Modules/Scripting/Handlers/Metarule.cpp @@ -86,7 +86,7 @@ static const SfallMetarule metarules[] = { {"get_inven_ap_cost", sf_get_inven_ap_cost, 0, 0}, {"get_map_enter_position", sf_get_map_enter_position, 0, 0}, {"get_metarule_table", sf_get_metarule_table, 0, 0}, - {"get_object_ai_data", sf_get_object_ai_data, 2, 2, 0, {ARG_OBJECT, ARG_INT}}, + {"get_object_ai_data", sf_get_object_ai_data, 2, 2, -1, {ARG_OBJECT, ARG_INT}}, {"get_object_data", sf_get_object_data, 2, 2, 0, {ARG_OBJECT, ARG_INT}}, {"get_outline", sf_get_outline, 1, 1, 0, {ARG_OBJECT}}, {"get_sfall_arg_at", sf_get_sfall_arg_at, 1, 1, 0, {ARG_INT}}, @@ -105,7 +105,7 @@ static const SfallMetarule metarules[] = { {"lock_is_jammed", sf_lock_is_jammed, 1, 1, 0, {ARG_OBJECT}}, {"loot_obj", sf_get_loot_object, 0, 0}, {"metarule_exist", sf_metarule_exist, 1, 1}, // no arg check - {"npc_engine_level_up", sf_npc_engine_level_up, 1, 1, 0, {ARG_ANY}}, + {"npc_engine_level_up", sf_npc_engine_level_up, 1, 1}, {"obj_under_cursor", sf_get_obj_under_cursor, 2, 2, 0, {ARG_INT, ARG_INT}}, {"outlined_object", sf_outlined_object, 0, 0}, {"real_dude_obj", sf_real_dude_obj, 0, 0}, diff --git a/sfall/Modules/Scripting/Handlers/Misc.cpp b/sfall/Modules/Scripting/Handlers/Misc.cpp index 1072056a..de65a97e 100644 --- a/sfall/Modules/Scripting/Handlers/Misc.cpp +++ b/sfall/Modules/Scripting/Handlers/Misc.cpp @@ -87,12 +87,11 @@ void sf_get_year(OpcodeContext& ctx) { void __declspec(naked) op_game_loaded() { __asm { push ecx; - push eax; push eax; // script call ScriptHasLoaded; movzx edx, al; - pop eax; - _RET_VAL_INT(ecx); + mov eax, ebx; + _RET_VAL_INT; pop ecx; retn; } @@ -108,7 +107,7 @@ void __declspec(naked) op_set_pipboy_available() { jg end; mov byte ptr ds:[FO_VAR_gmovie_played_list][0x3], al; end: - pop ecx; + pop ecx; retn; } } @@ -192,7 +191,7 @@ void sf_set_object_knockback(OpcodeContext& ctx) { mode = 2; break; } - fo::GameObject* object = ctx.arg(0).asObject(); + fo::GameObject* object = ctx.arg(0).object(); if (mode) { if (object->Type() != fo::OBJ_TYPE_CRITTER) { ctx.printOpcodeError("%s() - the object is not a critter.", ctx.getOpcodeName()); @@ -217,7 +216,7 @@ void sf_remove_object_knockback(OpcodeContext& ctx) { mode = 2; break; } - KnockbackRemoveMod(ctx.arg(0).asObject(), mode); + KnockbackRemoveMod(ctx.arg(0).object(), mode); } void __declspec(naked) op_get_kill_counter2() { @@ -290,7 +289,7 @@ void __declspec(naked) op_active_hand() { __asm { push ecx; mov edx, dword ptr ds:[FO_VAR_itemCurrentItem]; - _RET_VAL_INT(ecx); + _RET_VAL_INT; pop ecx; retn; } @@ -307,7 +306,7 @@ void __declspec(naked) op_eax_available() { __asm { push ecx; xor edx, edx - _RET_VAL_INT(ecx); + _RET_VAL_INT; pop ecx; retn; } @@ -483,11 +482,10 @@ void sf_get_ini_string(OpcodeContext& ctx) { void __declspec(naked) op_get_uptime() { __asm { push ecx; - push eax; call GetTickCount; mov edx, eax; - pop eax; - _RET_VAL_INT(ecx); + mov eax, ebx; + _RET_VAL_INT; pop ecx; retn; } @@ -504,7 +502,7 @@ end: } } -void __declspec(naked) op_set_hp_per_level_mod() { // rewrite to c++ +void __declspec(naked) op_set_hp_per_level_mod() { __asm { push ecx; _GET_ARG_INT(end); @@ -583,23 +581,23 @@ end: static const char* valueOutRange = "%s() - argument values out of range."; void sf_set_critical_table(OpcodeContext& ctx) { - DWORD critter = ctx.arg(0).asInt(), - bodypart = ctx.arg(1).asInt(), - slot = ctx.arg(2).asInt(), - element = ctx.arg(3).asInt(); + DWORD critter = ctx.arg(0).rawValue(), + bodypart = ctx.arg(1).rawValue(), + slot = ctx.arg(2).rawValue(), + element = ctx.arg(3).rawValue(); if (critter >= Criticals::critTableCount || bodypart >= 9 || slot >= 6 || element >= 7) { ctx.printOpcodeError(valueOutRange, ctx.getOpcodeName()); } else { - Criticals::SetCriticalTable(critter, bodypart, slot, element, ctx.arg(4).asInt()); + Criticals::SetCriticalTable(critter, bodypart, slot, element, ctx.arg(4).rawValue()); } } void sf_get_critical_table(OpcodeContext& ctx) { - DWORD critter = ctx.arg(0).asInt(), - bodypart = ctx.arg(1).asInt(), - slot = ctx.arg(2).asInt(), - element = ctx.arg(3).asInt(); + DWORD critter = ctx.arg(0).rawValue(), + bodypart = ctx.arg(1).rawValue(), + slot = ctx.arg(2).rawValue(), + element = ctx.arg(3).rawValue(); if (critter >= Criticals::critTableCount || bodypart >= 9 || slot >= 6 || element >= 7) { ctx.printOpcodeError(valueOutRange, ctx.getOpcodeName()); @@ -609,10 +607,10 @@ void sf_get_critical_table(OpcodeContext& ctx) { } void sf_reset_critical_table(OpcodeContext& ctx) { - DWORD critter = ctx.arg(0).asInt(), - bodypart = ctx.arg(1).asInt(), - slot = ctx.arg(2).asInt(), - element = ctx.arg(3).asInt(); + DWORD critter = ctx.arg(0).rawValue(), + bodypart = ctx.arg(1).rawValue(), + slot = ctx.arg(2).rawValue(), + element = ctx.arg(3).rawValue(); if (critter >= Criticals::critTableCount || bodypart >= 9 || slot >= 6 || element >= 7) { ctx.printOpcodeError(valueOutRange, ctx.getOpcodeName()); @@ -636,7 +634,7 @@ void __declspec(naked) op_get_unspent_ap_bonus() { __asm { push ecx; mov edx, Stats::standardApAcBonus; - _RET_VAL_INT(ecx); + _RET_VAL_INT; pop ecx; retn; } @@ -657,7 +655,7 @@ void __declspec(naked) op_get_unspent_ap_perk_bonus() { __asm { push ecx; mov edx, Stats::extraApAcBonus; - _RET_VAL_INT(ecx); + _RET_VAL_INT; pop ecx; retn; } @@ -741,7 +739,7 @@ void __declspec(naked) op_get_light_level() { __asm { push ecx; mov edx, ds:[FO_VAR_ambient_light]; - _RET_VAL_INT(ecx); + _RET_VAL_INT; pop ecx; retn; } @@ -851,7 +849,7 @@ void __declspec(naked) op_modified_ini() { __asm { push ecx; mov edx, modifiedIni; - _RET_VAL_INT(ecx); + _RET_VAL_INT; pop ecx; retn; } @@ -994,7 +992,7 @@ void __declspec(naked) op_gdialog_get_barter_mod() { __asm { push ecx; mov edx, dword ptr ds:[FO_VAR_gdBarterMod]; - _RET_VAL_INT(ecx); + _RET_VAL_INT; pop ecx; retn; } @@ -1026,7 +1024,7 @@ void sf_sneak_success(OpcodeContext& ctx) { } void sf_tile_light(OpcodeContext& ctx) { - int lightLevel = fo::func::light_get_tile(ctx.arg(0).asInt(), ctx.arg(1).asInt()); + int lightLevel = fo::func::light_get_tile(ctx.arg(0).rawValue(), ctx.arg(1).rawValue()); ctx.setReturn(lightLevel); } @@ -1058,7 +1056,10 @@ void sf_set_ini_setting(OpcodeContext& ctx) { case -1: ctx.printOpcodeError("%s() - invalid setting argument.", ctx.getMetaruleName()); break; + default: + return; } + ctx.setReturn(-1); } static std::string GetIniFilePath(const ScriptValue& arg) { @@ -1097,7 +1098,7 @@ void sf_get_ini_sections(OpcodeContext& ctx) { } void sf_get_ini_section(OpcodeContext& ctx) { - auto section = ctx.arg(1).asString(); + auto section = ctx.arg(1).strValue(); GetPrivateProfileSectionA(section, getIniSectionBuf, 5120, GetIniFilePath(ctx.arg(0)).data()); int arrayId = TempArray(-1, 0); // associative auto& arr = arrays[arrayId]; diff --git a/sfall/Modules/Scripting/Handlers/Objects.cpp b/sfall/Modules/Scripting/Handlers/Objects.cpp index 0af741c7..70cc5c6f 100644 --- a/sfall/Modules/Scripting/Handlers/Objects.cpp +++ b/sfall/Modules/Scripting/Handlers/Objects.cpp @@ -124,10 +124,10 @@ end: } void sf_create_spatial(OpcodeContext& ctx) { - DWORD scriptIndex = ctx.arg(0).asInt(), - tile = ctx.arg(1).asInt(), - elevation = ctx.arg(2).asInt(), - radius = ctx.arg(3).asInt(), + DWORD scriptIndex = ctx.arg(0).rawValue(), + tile = ctx.arg(1).rawValue(), + elevation = ctx.arg(2).rawValue(), + radius = ctx.arg(3).rawValue(), scriptId, tmp, objectPtr, scriptPtr; __asm { @@ -136,16 +136,14 @@ void sf_create_spatial(OpcodeContext& ctx) { call fo::funcoffs::scr_new_; mov tmp, eax; } - if (tmp == -1) - return; + if (tmp == -1) return; __asm { mov eax, scriptId; lea edx, scriptPtr; call fo::funcoffs::scr_ptr_; mov tmp, eax; } - if (tmp == -1) - return; + if (tmp == -1) return; // fill spatial script properties: *(DWORD*)(scriptPtr + 0x14) = scriptIndex - 1; *(DWORD*)(scriptPtr + 0x8) = (elevation << 29) & 0xE0000000 | tile; @@ -160,11 +158,11 @@ void sf_create_spatial(OpcodeContext& ctx) { call fo::funcoffs::scr_find_obj_from_program_; mov objectPtr, eax; } - ctx.setReturn((int)objectPtr); + ctx.setReturn(objectPtr); } void sf_spatial_radius(OpcodeContext& ctx) { - auto spatialObj = ctx.arg(0).asObject(); + auto spatialObj = ctx.arg(0).object(); fo::ScriptInstance* script; if (fo::func::scr_ptr(spatialObj->scriptId, &script) != -1) { ctx.setReturn(script->spatialRadius); @@ -172,32 +170,32 @@ void sf_spatial_radius(OpcodeContext& ctx) { } void sf_get_script(OpcodeContext& ctx) { - auto obj = ctx.arg(0).asObject(); + auto obj = ctx.arg(0).object(); ctx.setReturn(obj->scriptIndex); } void sf_set_critter_burst_disable(OpcodeContext& ctx) { - SetNoBurstMode(ctx.arg(0).asObject(), ctx.arg(1).asBool()); + SetNoBurstMode(ctx.arg(0).object(), ctx.arg(1).asBool()); } void sf_get_weapon_ammo_pid(OpcodeContext& ctx) { - auto obj = ctx.arg(0).asObject(); + auto obj = ctx.arg(0).object(); ctx.setReturn(obj->item.ammoPid); } void sf_set_weapon_ammo_pid(OpcodeContext& ctx) { - auto obj = ctx.arg(0).asObject(); - obj->item.ammoPid = ctx.arg(1).asInt(); + auto obj = ctx.arg(0).object(); + obj->item.ammoPid = ctx.arg(1).rawValue(); } void sf_get_weapon_ammo_count(OpcodeContext& ctx) { - auto obj = ctx.arg(0).asObject(); + auto obj = ctx.arg(0).object(); ctx.setReturn(obj->item.charges); } void sf_set_weapon_ammo_count(OpcodeContext& ctx) { - auto obj = ctx.arg(0).asObject(); - obj->item.charges = ctx.arg(1).asInt(); + auto obj = ctx.arg(0).object(); + obj->item.charges = ctx.arg(1).rawValue(); } #define BLOCKING_TYPE_BLOCK (0) @@ -223,20 +221,20 @@ static DWORD getBlockingFunc(DWORD type) { } void sf_make_straight_path(OpcodeContext& ctx) { - auto objFrom = ctx.arg(0).asObject(); - DWORD tileTo = ctx.arg(1).asInt(), - type = ctx.arg(2).asInt(); + auto objFrom = ctx.arg(0).object(); + DWORD tileTo = ctx.arg(1).rawValue(), + type = ctx.arg(2).rawValue(); long flag = (type == BLOCKING_TYPE_SHOOT) ? 32 : 0; DWORD resultObj = 0; fo::func::make_straight_path_func(objFrom, objFrom->tile, tileTo, 0, &resultObj, flag, (void*)getBlockingFunc(type)); - ctx.setReturn(resultObj, DataType::INT); + ctx.setReturn(resultObj); } void sf_make_path(OpcodeContext& ctx) { - auto objFrom = ctx.arg(0).asObject(); - auto tileTo = ctx.arg(1).asInt(), - type = ctx.arg(2).asInt(); + auto objFrom = ctx.arg(0).object(); + auto tileTo = ctx.arg(1).rawValue(), + type = ctx.arg(2).rawValue(); auto func = getBlockingFunc(type); // if the object is not a critter, then there is no need to check tile (tileTo) for blocking @@ -248,36 +246,36 @@ void sf_make_path(OpcodeContext& ctx) { for (int i = 0; i < pathLength; i++) { arrays[arrayId].val[i].set((long)pathData[i]); } - ctx.setReturn(arrayId, DataType::INT); + ctx.setReturn(arrayId); } void sf_obj_blocking_at(OpcodeContext& ctx) { - DWORD tile = ctx.arg(0).asInt(), - elevation = ctx.arg(1).asInt(), - type = ctx.arg(2).asInt(); + DWORD tile = ctx.arg(0).rawValue(), + elevation = ctx.arg(1).rawValue(), + type = ctx.arg(2).rawValue(); fo::GameObject* resultObj = fo::func::obj_blocking_at_wrapper(0, tile, elevation, (void*)getBlockingFunc(type)); if (resultObj && type == BLOCKING_TYPE_SHOOT && (resultObj->flags & fo::ObjectFlag::ShootThru)) { // don't know what this flag means, copy-pasted from the engine code // this check was added because the engine always does exactly this when using shoot blocking checks resultObj = nullptr; } - ctx.setReturn((DWORD)resultObj, DataType::INT); + ctx.setReturn(resultObj); } void sf_tile_get_objects(OpcodeContext& ctx) { - DWORD tile = ctx.arg(0).asInt(), - elevation = ctx.arg(1).asInt(); + DWORD tile = ctx.arg(0).rawValue(), + elevation = ctx.arg(1).rawValue(); DWORD arrayId = TempArray(0, 4); auto obj = fo::func::obj_find_first_at_tile(elevation, tile); while (obj) { arrays[arrayId].push_back(reinterpret_cast(obj)); obj = fo::func::obj_find_next_at_tile(); } - ctx.setReturn(arrayId, DataType::INT); + ctx.setReturn(arrayId); } void sf_get_party_members(OpcodeContext& ctx) { - auto includeHidden = ctx.arg(0).asInt(); + auto includeHidden = ctx.arg(0).rawValue(); int actualCount = fo::var::partyMemberCount; DWORD arrayId = TempArray(0, 4); auto partyMemberList = fo::var::partyMemberList; @@ -287,11 +285,11 @@ void sf_get_party_members(OpcodeContext& ctx) { arrays[arrayId].push_back((long)obj); } } - ctx.setReturn(arrayId, DataType::INT); + ctx.setReturn(arrayId); } void sf_art_exists(OpcodeContext& ctx) { - ctx.setReturn(fo::func::art_exists(ctx.arg(0).asInt())); + ctx.setReturn(fo::func::art_exists(ctx.arg(0).rawValue())); } void sf_obj_is_carrying_obj(OpcodeContext& ctx) { @@ -299,8 +297,8 @@ void sf_obj_is_carrying_obj(OpcodeContext& ctx) { const ScriptValue &invenObjArg = ctx.arg(0), &itemObjArg = ctx.arg(1); - fo::GameObject *invenObj = invenObjArg.asObject(), - *itemObj = itemObjArg.asObject(); + fo::GameObject *invenObj = invenObjArg.object(), + *itemObj = itemObjArg.object(); if (invenObj != nullptr && itemObj != nullptr) { for (int i = 0; i < invenObj->invenSize; i++) { if (invenObj->invenTable[i].object == itemObj) { @@ -313,8 +311,8 @@ void sf_obj_is_carrying_obj(OpcodeContext& ctx) { } void sf_critter_inven_obj2(OpcodeContext& ctx) { - fo::GameObject* critter = ctx.arg(0).asObject(); - int slot = ctx.arg(1).asInt(); + fo::GameObject* critter = ctx.arg(0).object(); + int slot = ctx.arg(1).rawValue(); switch (slot) { case 0: ctx.setReturn(fo::func::inven_worn(critter)); @@ -334,24 +332,24 @@ void sf_critter_inven_obj2(OpcodeContext& ctx) { } void sf_set_outline(OpcodeContext& ctx) { - auto obj = ctx.arg(0).asObject(); - int color = ctx.arg(1).asInt(); + auto obj = ctx.arg(0).object(); + int color = ctx.arg(1).rawValue(); obj->outline = color; } void sf_get_outline(OpcodeContext& ctx) { - auto obj = ctx.arg(0).asObject(); + auto obj = ctx.arg(0).object(); ctx.setReturn(obj->outline); } void sf_set_flags(OpcodeContext& ctx) { - auto obj = ctx.arg(0).asObject(); - int flags = ctx.arg(1).asInt(); + auto obj = ctx.arg(0).object(); + int flags = ctx.arg(1).rawValue(); obj->flags = flags; } void sf_get_flags(OpcodeContext& ctx) { - auto obj = ctx.arg(0).asObject(); + auto obj = ctx.arg(0).object(); ctx.setReturn(obj->flags); } @@ -360,12 +358,12 @@ void sf_outlined_object(OpcodeContext& ctx) { } void sf_item_weight(OpcodeContext& ctx) { - ctx.setReturn(fo::func::item_weight(ctx.arg(0).asObject())); + ctx.setReturn(fo::func::item_weight(ctx.arg(0).object())); } void sf_set_dude_obj(OpcodeContext& ctx) { - auto obj = ctx.arg(0).asObject(); - if (obj == nullptr || obj->Type() == fo::OBJ_TYPE_CRITTER) { + auto obj = ctx.arg(0).object(); + if (obj == nullptr || obj->Type() == fo::ObjType::OBJ_TYPE_CRITTER) { //if (!InCombat && obj && obj != PartyControl::RealDudeObject()) { // ctx.printOpcodeError("%s() - controlling of the critter is only allowed in combat mode.", ctx.getMetaruleName()); //} else { @@ -373,6 +371,7 @@ void sf_set_dude_obj(OpcodeContext& ctx) { //} } else { ctx.printOpcodeError("%s() - the object is not a critter.", ctx.getMetaruleName()); + ctx.setReturn(-1); } } @@ -385,17 +384,18 @@ void sf_car_gas_amount(OpcodeContext& ctx) { } void sf_lock_is_jammed(OpcodeContext& ctx) { - ctx.setReturn(fo::func::obj_lock_is_jammed(ctx.arg(0).asObject())); + ctx.setReturn(fo::func::obj_lock_is_jammed(ctx.arg(0).object())); } void sf_unjam_lock(OpcodeContext& ctx) { - fo::func::obj_unjam_lock(ctx.arg(0).asObject()); + fo::func::obj_unjam_lock(ctx.arg(0).object()); } void sf_set_unjam_locks_time(OpcodeContext& ctx) { - int time = ctx.arg(0).asInt(); + int time = ctx.arg(0).rawValue(); if (time < 0 || time > 127) { ctx.printOpcodeError("%s() - time argument must be in the range of 0 to 127.", ctx.getMetaruleName()); + ctx.setReturn(-1); } else { Objects::SetAutoUnjamLockTime(time); } @@ -416,11 +416,12 @@ void sf_item_make_explosive(OpcodeContext& ctx) { Explosions::AddToExplosives(pid, pidActive, min, max); } else { ctx.printOpcodeError("%s() - invalid PID number, must be greater than 0.", ctx.getMetaruleName()); + ctx.setReturn(-1); } } void sf_get_current_inven_size(OpcodeContext& ctx) { - ctx.setReturn(sf_item_total_size(ctx.arg(0).asObject())); + ctx.setReturn(sf_item_total_size(ctx.arg(0).object())); } void sf_get_dialog_object(OpcodeContext& ctx) { @@ -470,22 +471,23 @@ void sf_get_object_data(OpcodeContext& ctx) { } else { result = *(long*)((BYTE*)object_ptr + ctx.arg(1).rawValue()); } - ctx.setReturn(result, DataType::INT); + ctx.setReturn(result); } void sf_set_object_data(OpcodeContext& ctx) { DWORD* object_ptr = (DWORD*)ctx.arg(0).rawValue(); if (*(object_ptr - 1) != 0xFEEDFACE) { ctx.printOpcodeError("%s() - invalid object pointer.", ctx.getMetaruleName()); + ctx.setReturn(-1); } else { *(long*)((BYTE*)object_ptr + ctx.arg(1).rawValue()) = ctx.arg(2).rawValue(); } } void sf_get_object_ai_data(OpcodeContext& ctx) { - fo::AIcap* cap = fo::func::ai_cap(ctx.arg(0).asObject()); + fo::AIcap* cap = fo::func::ai_cap(ctx.arg(0).object()); DWORD arrayId, value = -1; - switch (ctx.arg(1).asInt()) { + switch (ctx.arg(1).rawValue()) { case 0: value = cap->aggression; break; @@ -538,7 +540,7 @@ void sf_get_object_ai_data(OpcodeContext& ctx) { default: ctx.printOpcodeError("%s() - invalid value for AI argument.", ctx.getMetaruleName()); } - ctx.setReturn(value, DataType::INT); + ctx.setReturn(value); } void sf_set_drugs_data(OpcodeContext& ctx) { @@ -557,13 +559,16 @@ void sf_set_drugs_data(OpcodeContext& ctx) { ctx.printOpcodeError("%s() - invalid value for type argument.", ctx.getMetaruleName()); return; } - if (result) ctx.printOpcodeError("%s() - drug PID not found in the configuration file.", ctx.getMetaruleName()); + if (result) { + ctx.printOpcodeError("%s() - drug PID not found in the configuration file.", ctx.getMetaruleName()); + ctx.setReturn(-1); + } } void sf_set_unique_id(OpcodeContext& ctx) { - fo::GameObject* obj = ctx.arg(0).asObject(); + fo::GameObject* obj = ctx.arg(0).object(); long id; - if (ctx.arg(1).asInt() == -1) { + if (ctx.arg(1).rawValue() == -1) { id = fo::func::new_obj_id(); obj->id = id; } else { diff --git a/sfall/Modules/Scripting/Handlers/Perks.cpp b/sfall/Modules/Scripting/Handlers/Perks.cpp index e33371e3..f729f920 100644 --- a/sfall/Modules/Scripting/Handlers/Perks.cpp +++ b/sfall/Modules/Scripting/Handlers/Perks.cpp @@ -32,7 +32,7 @@ void __declspec(naked) op_get_perk_owed() { __asm { push ecx; movzx edx, byte ptr ds:[FO_VAR_free_perk]; - _RET_VAL_INT(ecx); + _RET_VAL_INT; pop ecx; retn; } @@ -191,29 +191,32 @@ void sf_set_fake_trait(OpcodeContext& ctx) { const char* notPartyMemberErr = "%s() - the object is not a party member."; void sf_set_selectable_perk_npc(OpcodeContext& ctx) { - auto obj = ctx.arg(0).asObject(); + auto obj = ctx.arg(0).object(); if (obj->Type() == fo::ObjType::OBJ_TYPE_CRITTER && fo::func::isPartyMember(obj)) { Perks::SetSelectablePerk(ctx.arg(1).strValue(), ctx.arg(2).rawValue(), ctx.arg(3).rawValue(), ctx.arg(4).strValue(), (obj->id != PLAYER_ID) ? obj->id : 0); } else { ctx.printOpcodeError(notPartyMemberErr, ctx.getMetaruleName()); + ctx.setReturn(-1); } } void sf_set_fake_perk_npc(OpcodeContext& ctx) { - auto obj = ctx.arg(0).asObject(); + auto obj = ctx.arg(0).object(); if (obj->Type() == fo::ObjType::OBJ_TYPE_CRITTER && fo::func::isPartyMember(obj)) { Perks::SetFakePerk(ctx.arg(1).strValue(), ctx.arg(2).rawValue(), ctx.arg(3).rawValue(), ctx.arg(4).strValue(), (obj->id != PLAYER_ID) ? obj->id : 0); } else { ctx.printOpcodeError(notPartyMemberErr, ctx.getMetaruleName()); + ctx.setReturn(-1); } } void sf_set_fake_trait_npc(OpcodeContext& ctx) { - auto obj = ctx.arg(0).asObject(); + auto obj = ctx.arg(0).object(); if (obj->Type() == fo::ObjType::OBJ_TYPE_CRITTER && fo::func::isPartyMember(obj)) { Perks::SetFakeTrait(ctx.arg(1).strValue(), ctx.arg(2).rawValue(), ctx.arg(3).rawValue(), ctx.arg(4).strValue(), (obj->id != PLAYER_ID) ? obj->id : 0); } else { ctx.printOpcodeError(notPartyMemberErr, ctx.getMetaruleName()); + ctx.setReturn(-1); } } @@ -280,7 +283,7 @@ void sf_has_fake_trait(OpcodeContext& ctx) { void sf_has_fake_perk_npc(OpcodeContext& ctx) { long result = 0; - auto obj = ctx.arg(0).asObject(); + auto obj = ctx.arg(0).object(); if (obj->Type() == fo::ObjType::OBJ_TYPE_CRITTER && fo::func::isPartyMember(obj)) { result = Perks::HasFakePerkOwner(ctx.arg(1).strValue(), (obj->id != PLAYER_ID) ? obj->id : 0); } else { @@ -291,7 +294,7 @@ void sf_has_fake_perk_npc(OpcodeContext& ctx) { void sf_has_fake_trait_npc(OpcodeContext& ctx) { long result = 0; - auto obj = ctx.arg(0).asObject(); + auto obj = ctx.arg(0).object(); if (obj->Type() == fo::ObjType::OBJ_TYPE_CRITTER && fo::func::isPartyMember(obj)) { result = Perks::HasFakeTraitOwner(ctx.arg(1).strValue(), (obj->id != PLAYER_ID) ? obj->id : 0); } else { @@ -408,6 +411,7 @@ end: void sf_add_trait(OpcodeContext& ctx) { if (fo::var::obj_dude->protoId != fo::PID_Player) { ctx.printOpcodeError("%s() - traits can be added only to the player.", ctx.getMetaruleName()); + ctx.setReturn(-1); return; } long traitId = ctx.arg(0).rawValue(); diff --git a/sfall/Modules/Scripting/Handlers/Stats.cpp b/sfall/Modules/Scripting/Handlers/Stats.cpp index b4db3776..7efa3aa0 100644 --- a/sfall/Modules/Scripting/Handlers/Stats.cpp +++ b/sfall/Modules/Scripting/Handlers/Stats.cpp @@ -76,7 +76,7 @@ void sf_get_pc_extra_stat(OpcodeContext& ctx) { } void sf_set_critter_base_stat(OpcodeContext& ctx) { - fo::GameObject* obj = ctx.arg(0).asObject(); + fo::GameObject* obj = ctx.arg(0).object(); if (obj && obj->Type() == fo::OBJ_TYPE_CRITTER) { int stat = ctx.arg(1).rawValue(); if (stat >= 0 && stat < fo::STAT_max_stat) { @@ -90,7 +90,7 @@ void sf_set_critter_base_stat(OpcodeContext& ctx) { } void sf_set_critter_extra_stat(OpcodeContext& ctx) { - fo::GameObject* obj = ctx.arg(0).asObject(); + fo::GameObject* obj = ctx.arg(0).object(); if (obj && obj->Type() == fo::OBJ_TYPE_CRITTER) { int stat = ctx.arg(1).rawValue(); if (stat >= 0 && stat < fo::STAT_max_stat) { @@ -105,7 +105,7 @@ void sf_set_critter_extra_stat(OpcodeContext& ctx) { void sf_get_critter_base_stat(OpcodeContext& ctx) { int result = 0; - fo::GameObject* obj = ctx.arg(0).asObject(); + fo::GameObject* obj = ctx.arg(0).object(); if (obj && obj->Type() == fo::OBJ_TYPE_CRITTER) { int stat = ctx.arg(1).rawValue(); if (stat >= 0 && stat < fo::STAT_max_stat) { @@ -121,7 +121,7 @@ void sf_get_critter_base_stat(OpcodeContext& ctx) { void sf_get_critter_extra_stat(OpcodeContext& ctx) { int result = 0; - fo::GameObject* obj = ctx.arg(0).asObject(); + fo::GameObject* obj = ctx.arg(0).object(); if (obj && obj->Type() == fo::OBJ_TYPE_CRITTER) { int stat = ctx.arg(1).rawValue(); if (stat >= 0 && stat < fo::STAT_max_stat) { @@ -251,7 +251,7 @@ void __declspec(naked) op_get_available_skill_points() { __asm { push ecx; mov edx, dword ptr ds:[FO_VAR_curr_pc_stat]; - _RET_VAL_INT(ecx); + _RET_VAL_INT; pop ecx; retn; } @@ -261,11 +261,13 @@ void __declspec(naked) op_mod_skill_points_per_level() { __asm { push ecx; _GET_ARG_INT(end); - cmp eax, 100; - jg end; - cmp eax, -100; - jl end; - add eax, 5; + mov ecx, 100; + cmp eax, ecx; + cmovg eax, ecx; + neg ecx; // -100 + cmp eax, ecx; + cmovl eax, ecx; + add eax, 5; // add fallout default points push eax; push 0x43C27A; call SafeWrite8; @@ -354,58 +356,34 @@ end: void __declspec(naked) op_set_pickpocket_max() { __asm { - push ebx; push ecx; - push edx; - push edi; - mov ecx, eax; - call fo::funcoffs::interpretPopShort_; - mov edx, eax; - mov eax, ecx; - call fo::funcoffs::interpretPopLong_; - cmp dx, VAR_TYPE_INT; - jnz end; - and eax, 0xFF; - cmp eax, 100; - jg end; + _GET_ARG_INT(end); + mov ecx, 100; + cmp eax, ecx; + cmova eax, ecx; // 0 - 100 push 0; push eax; push 0xFFFFFFFF; call SetPickpocketMax; end: - pop edi; - pop edx; - pop ecx; - pop ebx; + pop ecx; retn; } } void __declspec(naked) op_set_hit_chance_max() { __asm { - push ebx; push ecx; - push edx; - push edi; - mov ecx, eax; - call fo::funcoffs::interpretPopShort_; - mov edx, eax; - mov eax, ecx; - call fo::funcoffs::interpretPopLong_; - cmp dx, VAR_TYPE_INT; - jnz end; - and eax, 0xFF; - cmp eax, 100; - jg end; + _GET_ARG_INT(end); + mov ecx, 100; + cmp eax, ecx; + cmova eax, ecx; // 0 - 100 push 0; push eax; push 0xFFFFFFFF; call SetHitChanceMax; end: - pop edi; - pop edx; - pop ecx; - pop ebx; + pop ecx; retn; } } @@ -598,58 +576,31 @@ end: } } -void __declspec(naked) op_set_base_skill_mod() { +void __declspec(naked) op_set_base_skill_mod() { // same as set_skill_max __asm { - push ebx; push ecx; - push edx; - push edi; - mov edi, eax; - xor ebx, ebx - call fo::funcoffs::interpretPopShort_; - cmp ax, VAR_TYPE_INT; - cmovne ebx, edi; - mov eax, edi; - call fo::funcoffs::interpretPopLong_; - mov ecx, eax; - test ebx, ebx; - jnz end; + _GET_ARG_INT(end); push eax; push 0xFFFFFFFF; call SetSkillMax; end: - pop edi; - pop edx; pop ecx; - pop ebx; retn; } } void __declspec(naked) op_set_skill_max() { __asm { - push ebx; push ecx; - push edx; - push edi; - mov ecx, eax; - call fo::funcoffs::interpretPopShort_; - mov edx, eax; - mov eax, ecx; - call fo::funcoffs::interpretPopLong_; - cmp dx, VAR_TYPE_INT; - jnz end; - and eax, 0xFFFF; - cmp eax, 300; - jg end; + _GET_ARG_INT(end); + mov ecx, 300; + cmp eax, ecx; + cmova eax, ecx; // 0 - 300 push eax; push 0xFFFFFFFF; call SetSkillMax; end: - pop edi; - pop edx; - pop ecx; - pop ebx; + pop ecx; retn; } } diff --git a/sfall/Modules/Scripting/Handlers/Utils.cpp b/sfall/Modules/Scripting/Handlers/Utils.cpp index 0894c898..6f542d81 100644 --- a/sfall/Modules/Scripting/Handlers/Utils.cpp +++ b/sfall/Modules/Scripting/Handlers/Utils.cpp @@ -127,7 +127,7 @@ void sf_arctan(OpcodeContext& ctx) { void sf_strlen(OpcodeContext& ctx) { ctx.setReturn( - static_cast(strlen(ctx.arg(0).asString())) + static_cast(strlen(ctx.arg(0).strValue())) ); } @@ -286,7 +286,7 @@ static char* _stdcall sprintf_lite(const char* format, ScriptValue value) { } else if (specifier == 'c') { buflen = j; } else if (specifier == 's') { - buflen = j + strlen(value.asString()); + buflen = j + strlen(value.strValue()); } else { buflen = j + 30; // numbers } @@ -317,7 +317,7 @@ void sf_power(OpcodeContext& ctx) { if (power.isFloat()) result = pow(base.asFloat(), power.floatValue()); else - result = pow(base.asFloat(), power.asInt()); + result = pow(base.asFloat(), (int)power.rawValue()); if (base.isInt() && power.isInt()) { ctx.setReturn(static_cast(result)); @@ -393,7 +393,7 @@ void sf_get_string_pointer(OpcodeContext& ctx) { } void sf_get_text_width(OpcodeContext& ctx) { - ctx.setReturn(fo::GetTextWidth(ctx.arg(0).asString())); + ctx.setReturn(fo::GetTextWidth(ctx.arg(0).strValue())); } } diff --git a/sfall/Modules/Scripting/Handlers/Worldmap.cpp b/sfall/Modules/Scripting/Handlers/Worldmap.cpp index 7afb68fd..12860734 100644 --- a/sfall/Modules/Scripting/Handlers/Worldmap.cpp +++ b/sfall/Modules/Scripting/Handlers/Worldmap.cpp @@ -89,11 +89,10 @@ void sf_force_encounter(OpcodeContext& cxt) { void __declspec(naked) op_in_world_map() { __asm { push ecx; - push eax; call InWorldMap; mov edx, eax; - pop eax; - _RET_VAL_INT(ecx); + mov eax, ebx; + _RET_VAL_INT; pop ecx; retn; } @@ -102,11 +101,10 @@ void __declspec(naked) op_in_world_map() { void __declspec(naked) op_get_game_mode() { __asm { push ecx; - push eax; call GetLoopFlags; mov edx, eax; - pop eax; - _RET_VAL_INT(ecx); + mov eax, ebx; + _RET_VAL_INT; pop ecx; retn; } @@ -116,7 +114,7 @@ void __declspec(naked) op_get_world_map_x_pos() { __asm { push ecx; mov edx, ds:[FO_VAR_world_xpos]; - _RET_VAL_INT(ecx); + _RET_VAL_INT; pop ecx; retn; } @@ -126,7 +124,7 @@ void __declspec(naked) op_get_world_map_y_pos() { __asm { push ecx; mov edx, ds:[FO_VAR_world_ypos]; - _RET_VAL_INT(ecx); + _RET_VAL_INT; pop ecx; retn; } @@ -197,13 +195,13 @@ fail: } void sf_set_car_intface_art(OpcodeContext& ctx) { - Worldmap::SetCarInterfaceArt(ctx.arg(0).asInt()); + Worldmap::SetCarInterfaceArt(ctx.arg(0).rawValue()); } void sf_set_map_enter_position(OpcodeContext& ctx) { - int tile = ctx.arg(0).asInt(); - int elev = ctx.arg(1).asInt(); - int rot = ctx.arg(2).asInt(); + int tile = ctx.arg(0).rawValue(); + int elev = ctx.arg(1).rawValue(); + int rot = ctx.arg(2).rawValue(); if (tile > -1 && tile < 40000) { fo::var::tile = tile; @@ -221,38 +219,42 @@ void sf_get_map_enter_position(OpcodeContext& ctx) { arrays[id].val[0].set((long)fo::var::tile); arrays[id].val[1].set((long)fo::var::elevation); arrays[id].val[2].set((long)fo::var::rotation); - ctx.setReturn(id, DataType::INT); + ctx.setReturn(id); } void sf_set_rest_heal_time(OpcodeContext& ctx) { - Worldmap::SetRestHealTime(ctx.arg(0).asInt()); + Worldmap::SetRestHealTime(ctx.arg(0).rawValue()); } void sf_set_rest_mode(OpcodeContext& ctx) { - Worldmap::SetRestMode(ctx.arg(0).asInt()); + Worldmap::SetRestMode(ctx.arg(0).rawValue()); } void sf_set_rest_on_map(OpcodeContext& ctx) { - long mapId = ctx.arg(0).asInt(); + long mapId = ctx.arg(0).rawValue(); if (mapId < 0) { ctx.printOpcodeError("%s() - invalid map number argument.", ctx.getMetaruleName()); + ctx.setReturn(-1); return; } - long elev = ctx.arg(1).asInt(); + long elev = ctx.arg(1).rawValue(); if (elev < -1 || elev > 2) { ctx.printOpcodeError("%s() - invalid map elevation argument.", ctx.getMetaruleName()); + ctx.setReturn(-1); } else { Worldmap::SetRestMapLevel(mapId, elev, ctx.arg(2).asBool()); } } void sf_get_rest_on_map(OpcodeContext& ctx) { - long elev = ctx.arg(1).asInt(); + long result = -1; + long elev = ctx.arg(1).rawValue(); if (elev < 0 || elev > 2) { ctx.printOpcodeError("%s() - invalid map elevation argument.", ctx.getMetaruleName()); } else { - ctx.setReturn(Worldmap::GetRestMapLevel(elev, ctx.arg(0).asInt())); + result = Worldmap::GetRestMapLevel(elev, ctx.arg(0).rawValue()); } + ctx.setReturn(result); } } diff --git a/sfall/Modules/Scripting/Opcodes.cpp b/sfall/Modules/Scripting/Opcodes.cpp index 679def8e..9906386e 100644 --- a/sfall/Modules/Scripting/Opcodes.cpp +++ b/sfall/Modules/Scripting/Opcodes.cpp @@ -135,8 +135,8 @@ static SfallOpcodeInfo opcodeInfoArray[] = { {0x20a, "fs_read_short", sf_fs_read_short, 1, true, 0, {ARG_INT}}, {0x20b, "fs_read_int", sf_fs_read_int, 1, true, 0, {ARG_INT}}, {0x20c, "fs_read_float", sf_fs_read_float, 1, true, 0, {ARG_INT}}, - {0x20d, "list_begin", sf_list_begin, 1, true, -1, {ARG_INT}}, - {0x20e, "list_next", sf_list_next, 1, true, -1, {ARG_INT}}, + {0x20d, "list_begin", sf_list_begin, 1, true, 0, {ARG_INT}}, + {0x20e, "list_next", sf_list_next, 1, true, 0, {ARG_INT}}, {0x20f, "list_end", sf_list_end, 1, false, 0, {ARG_INT}}, {0x210, "sfall_ver_major", sf_sfall_ver_major, 0, true}, {0x211, "sfall_ver_minor", sf_sfall_ver_minor, 0, true}, @@ -158,7 +158,7 @@ static SfallOpcodeInfo opcodeInfoArray[] = { {0x231, "len_array", sf_len_array, 1, true, 0, {ARG_INT}}, {0x232, "resize_array", sf_resize_array, 2, false, 0, {ARG_OBJECT, ARG_INT}}, {0x233, "temp_array", sf_temp_array, 2, true, -1, {ARG_INT, ARG_INT}}, - {0x234, "fix_array", sf_fix_array, 1, false, 0, {ARG_INT}}, + {0x234, "fix_array", sf_fix_array, 1, false, 0, {ARG_OBJECT}}, {0x235, "string_split", sf_string_split, 2, true, -1, {ARG_STRING, ARG_STRING}}, {0x236, "list_as_array", sf_list_as_array, 1, true, -1, {ARG_INT}}, {0x237, "atoi", sf_atoi, 1, true, 0, {ARG_STRING}}, @@ -166,7 +166,7 @@ static SfallOpcodeInfo opcodeInfoArray[] = { {0x239, "scan_array", sf_scan_array, 2, true, -1, {ARG_OBJECT, ARG_ANY}}, {0x23c, "get_sfall_args", sf_get_sfall_args, 0, true}, {0x23d, "set_sfall_arg", sf_set_sfall_arg, 2, false, 0, {ARG_INT, ARG_INT}}, - {0x241, "get_npc_level", sf_get_npc_level, 1, true, 0, {ARG_INTSTR}}, + {0x241, "get_npc_level", sf_get_npc_level, 1, true, -1, {ARG_INTSTR}}, {0x24e, "substr", sf_substr, 3, true, -1, {ARG_STRING, ARG_INT, ARG_INT}}, {0x24f, "strlen", sf_strlen, 1, true, 0, {ARG_STRING}}, diff --git a/sfall/Modules/Scripting/ScriptValue.cpp b/sfall/Modules/Scripting/ScriptValue.cpp index e5911f6f..6d34ebbf 100644 --- a/sfall/Modules/Scripting/ScriptValue.cpp +++ b/sfall/Modules/Scripting/ScriptValue.cpp @@ -82,10 +82,6 @@ bool ScriptValue::isString() const { return _type == DataType::STR; } -unsigned long ScriptValue::rawValue() const { - return _val.dw; -} - int ScriptValue::asInt() const { switch (_type) { case DataType::INT: @@ -131,6 +127,14 @@ fo::GameObject* ScriptValue::asObject() const { : nullptr; } +fo::GameObject* ScriptValue::object() const { + return _val.gObj; +} + +unsigned long ScriptValue::rawValue() const { + return _val.dw; +} + const char* ScriptValue::strValue() const { return _val.str; } diff --git a/sfall/Modules/Scripting/ScriptValue.h b/sfall/Modules/Scripting/ScriptValue.h index 8793c04f..41d930ea 100644 --- a/sfall/Modules/Scripting/ScriptValue.h +++ b/sfall/Modules/Scripting/ScriptValue.h @@ -61,6 +61,8 @@ public: const char* strValue() const; + fo::GameObject* object() const; + // returns value as integer, converting if needed int asInt() const; From 11fee91f2f16e58d1bc03fde9c79b5afc50f2d28 Mon Sep 17 00:00:00 2001 From: NovaRain Date: Thu, 28 Nov 2019 14:17:22 +0800 Subject: [PATCH 15/36] Changed the error msg due to missing critter frame * now the game only "crashes" when both Enable and DebugMode are enabled in [Debugging] in ddraw.ini. --- sfall/FalloutEngine/Variables_def.h | 2 +- sfall/Modules/DebugEditor.cpp | 5 ++++- sfall/Modules/PartyControl.cpp | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/sfall/FalloutEngine/Variables_def.h b/sfall/FalloutEngine/Variables_def.h index c06edc8d..8df52fb1 100644 --- a/sfall/FalloutEngine/Variables_def.h +++ b/sfall/FalloutEngine/Variables_def.h @@ -39,7 +39,7 @@ VAR_(DarkGreenColor, BYTE) VAR_(DarkGreenGreyColor, BYTE) VAR_(dialogue_state, DWORD) VAR_(dialogue_switch_mode, DWORD) -VAR_(dialog_target, DWORD) +VAR_(dialog_target, GameObject*) VAR_(dialog_target_is_party, DWORD) VAR_(dropped_explosive, DWORD) VARA(drugInfoList, DrugInfoList, 9) diff --git a/sfall/Modules/DebugEditor.cpp b/sfall/Modules/DebugEditor.cpp index d8190f7b..05836a2b 100644 --- a/sfall/Modules/DebugEditor.cpp +++ b/sfall/Modules/DebugEditor.cpp @@ -310,7 +310,7 @@ hide: } } -static char* artDbgMsg = "Error: file not found: %s\n"; +static char* artDbgMsg = "\nError: file not found: %s\n"; static void __declspec(naked) art_data_size_hook() { __asm { test edi, edi; @@ -322,7 +322,10 @@ artNotExist: push artDbgMsg; call fo::funcoffs::debug_printf_; add esp, 8; + cmp isDebug, 0; + jz skip; int 3; // break program +skip: retn; } } diff --git a/sfall/Modules/PartyControl.cpp b/sfall/Modules/PartyControl.cpp index 789bec74..262852bb 100644 --- a/sfall/Modules/PartyControl.cpp +++ b/sfall/Modules/PartyControl.cpp @@ -489,7 +489,7 @@ static void __fastcall PartyMemberPrintStat(BYTE* surface, DWORD toWidth) { const char* fmt = "%s %d"; char lvlMsg[16], acMsg[16]; - fo::GameObject* partyMember = (fo::GameObject*)fo::var::dialog_target; + fo::GameObject* partyMember = fo::var::dialog_target; int xPos = 350; int level = fo::func::partyMemberGetCurLevel(partyMember); From 84076a2586c0a943a8fb52b6f78425d9effe8799 Mon Sep 17 00:00:00 2001 From: NovaRain Date: Fri, 29 Nov 2019 11:52:35 +0800 Subject: [PATCH 16/36] Refactored the code for "substr" script function * fixed the crashing bug and added a new behavior. Changed the error message due to missing critter frame (again): * replaced the interruption with printing the error message to in-game message window. --- artifacts/scripting/sfall function notes.txt | 6 +- sfall/Modules/DebugEditor.cpp | 17 ++--- sfall/Modules/LoadGameHook.cpp | 6 +- sfall/Modules/Scripting/Handlers/Arrays.cpp | 10 +-- sfall/Modules/Scripting/Handlers/Misc.cpp | 2 +- sfall/Modules/Scripting/Handlers/Utils.cpp | 66 +++++++++++--------- sfall/Modules/Scripting/Handlers/Utils.h | 2 +- 7 files changed, 63 insertions(+), 46 deletions(-) diff --git a/artifacts/scripting/sfall function notes.txt b/artifacts/scripting/sfall function notes.txt index 4a7ee35b..2bdb4e27 100644 --- a/artifacts/scripting/sfall function notes.txt +++ b/artifacts/scripting/sfall function notes.txt @@ -262,7 +262,10 @@ Some utility/math functions are available: - you can use this to search for a substring in a string like this: strlen(get_array(string_split(haystack, needle), 0)) > string substr(string, start, length) -- cuts a substring from a string starting at "start" up to "length" characters. If start is negative - it indicates starting position from the end of the string (for example substr("test", -2, 2) will return last 2 charactes: "st"). If length is negative - it means so many characters will be omitted from the end of string (example: substr("test", 0, -2) will return string without last 2 characters: "te") +- cuts a substring from a string starting at "start" up to "length" characters. The first character position starts with 0 (zero). +- If start is negative - it indicates starting position from the end of the string (for example substr("test", -2, 2) will return last 2 charactes: "st"). +- If length is negative - it means so many characters will be omitted from the end of string (example: substr("test", 0, -2) will return string without last 2 characters: "te"). +- If length is zero - it will return a string from the starting position to the end of the string **New behavior** for sfall 4.2.2/3.8.22 > int strlen(string string) - returns string length @@ -452,6 +455,7 @@ Some utility/math functions are available: > array sfall_func2("get_ini_section", string fileName, string section) - returns an associative array of keys and values for a given INI file and section +- NOTE: all keys and their values will be of String type > int sfall_func0("car_gas_amount") - returns current amount of fuel in player's car (between 0 and 80000) diff --git a/sfall/Modules/DebugEditor.cpp b/sfall/Modules/DebugEditor.cpp index 05836a2b..2e91c419 100644 --- a/sfall/Modules/DebugEditor.cpp +++ b/sfall/Modules/DebugEditor.cpp @@ -321,12 +321,15 @@ artNotExist: push eax; push artDbgMsg; call fo::funcoffs::debug_printf_; - add esp, 8; - cmp isDebug, 0; - jz skip; - int 3; // break program -skip: - retn; + mov eax, [esp + 0x124 - 0x1C + 12]; // filename + push eax; + push artDbgMsg; + lea eax, [esp + 0x124 - 0x124 + 20]; // buf + push eax; + call fo::funcoffs::sprintf_; + add esp, 20; + lea eax, [esp + 4]; + jmp fo::funcoffs::display_print_; } } @@ -365,7 +368,7 @@ static void DebugModePatch() { if (iniGetInt("Debugging", "HideObjIsNullMsg", 0, ::sfall::ddrawIni)) { MakeJump(0x453FD2, dbg_error_hack); } - // prints a debug message about missing art file for critters and interrupts game execution + // prints a debug message about missing art file for critters to debug.log and the message window HookCall(0x419B65, art_data_size_hook); dlogr(" Done", DL_INIT); diff --git a/sfall/Modules/LoadGameHook.cpp b/sfall/Modules/LoadGameHook.cpp index eb4ec310..2147ff27 100644 --- a/sfall/Modules/LoadGameHook.cpp +++ b/sfall/Modules/LoadGameHook.cpp @@ -523,9 +523,9 @@ end: static void __declspec(naked) DialogHook() { __asm { - cmp dword ptr [esp + 0x14], 0x45A5C9; // call from op_gsay_end_ - je changeMode; - jmp fo::funcoffs::gdProcess_; + test inLoop, DIALOG; // check bit flag + jz changeMode; + jmp fo::funcoffs::gdProcess_; changeMode: _InLoop(1, DIALOG); call fo::funcoffs::gdProcess_; diff --git a/sfall/Modules/Scripting/Handlers/Arrays.cpp b/sfall/Modules/Scripting/Handlers/Arrays.cpp index a88780b8..7d76a586 100644 --- a/sfall/Modules/Scripting/Handlers/Arrays.cpp +++ b/sfall/Modules/Scripting/Handlers/Arrays.cpp @@ -31,7 +31,7 @@ namespace script void sf_create_array(OpcodeContext& ctx) { auto arrayId = CreateArray(ctx.arg(0).rawValue(), ctx.arg(1).rawValue()); - ctx.setReturn(arrayId, DataType::INT); + ctx.setReturn(arrayId); } void sf_set_array(OpcodeContext& ctx) { @@ -55,7 +55,7 @@ void sf_get_array(OpcodeContext& ctx) { ); } else if (ctx.arg(0).isString()) { if (ctx.arg(1).isInt()) { - auto str = Substring(ctx.arg(0).strValue(), ctx.arg(1).rawValue(), 1); + const char* str = Substring(ctx.arg(0).strValue(), ctx.arg(1).rawValue(), 1); ctx.setReturn(str); // returns char of string } else { ctx.printOpcodeError("%s() - index must be numeric when used on a string.", ctx.getOpcodeName()); @@ -83,7 +83,7 @@ void sf_resize_array(OpcodeContext& ctx) { void sf_temp_array(OpcodeContext& ctx) { auto arrayId = TempArray(ctx.arg(0).rawValue(), ctx.arg(1).rawValue()); - ctx.setReturn(arrayId, DataType::INT); + ctx.setReturn(arrayId); } void sf_fix_array(OpcodeContext& ctx) { @@ -200,7 +200,7 @@ static DWORD ListAsArray(DWORD type) { void sf_list_as_array(OpcodeContext& ctx) { auto arrayId = ListAsArray(ctx.arg(0).rawValue()); - ctx.setReturn(arrayId, DataType::INT); + ctx.setReturn(arrayId); } static DWORD ListBegin(DWORD type) { @@ -212,7 +212,7 @@ static DWORD ListBegin(DWORD type) { } void sf_list_begin(OpcodeContext& ctx) { - ctx.setReturn(ListBegin(ctx.arg(0).rawValue()), DataType::INT); + ctx.setReturn(ListBegin(ctx.arg(0).rawValue())); } static fo::GameObject* ListNext(sList* list) { diff --git a/sfall/Modules/Scripting/Handlers/Misc.cpp b/sfall/Modules/Scripting/Handlers/Misc.cpp index de65a97e..dc6aa34f 100644 --- a/sfall/Modules/Scripting/Handlers/Misc.cpp +++ b/sfall/Modules/Scripting/Handlers/Misc.cpp @@ -92,7 +92,7 @@ void __declspec(naked) op_game_loaded() { movzx edx, al; mov eax, ebx; _RET_VAL_INT; - pop ecx; + pop ecx; retn; } } diff --git a/sfall/Modules/Scripting/Handlers/Utils.cpp b/sfall/Modules/Scripting/Handlers/Utils.cpp index 6f542d81..3f640b47 100644 --- a/sfall/Modules/Scripting/Handlers/Utils.cpp +++ b/sfall/Modules/Scripting/Handlers/Utils.cpp @@ -190,29 +190,40 @@ void sf_string_split(OpcodeContext& ctx) { ctx.setReturn(StringSplit(ctx.arg(0).strValue(), ctx.arg(1).strValue())); } -char* Substring(const char* str, int pos, int length) { - char* newstr; - int srclen; - srclen = strlen(str); - if (pos < 0) - pos = srclen + pos; - if (length < 0) - length = srclen - pos + length; - if (pos >= srclen) - length = 0; - else if (length + pos > srclen) - length = srclen - pos; - newstr = new char[length + 1]; // memory leak!!! - if (length > 0) - memcpy(newstr, &str[pos], length); - newstr[length] = '\0'; - return newstr; +static char* tempTextBuf = nullptr; + +char* Substring(const char* str, int startPos, int length) { + int len = strlen(str); + + if (startPos < 0) { + startPos += len; // start from end + if (startPos < 0) startPos = 0; + } + if (length < 0) { + length += len - startPos; // cutoff at end + if (length == 0) { + return ""; + } else if (length < 0) { + length = -length; // length can't be negative + } + } + // check position + if (startPos >= len) return ""; // start position is out of string length, return empty string + if (length == 0 || length + startPos > len) { + length = len - startPos; // set the correct length, the length of characters goes beyond the end of the string + } + + if (tempTextBuf) delete[] tempTextBuf; + tempTextBuf = new char[length + 1]; + memcpy(tempTextBuf, &str[startPos], length); + tempTextBuf[length] = '\0'; + return tempTextBuf; } void sf_substr(OpcodeContext& ctx) { - ctx.setReturn( - Substring(ctx.arg(0).strValue(), ctx.arg(1).rawValue(), ctx.arg(2).rawValue()) - ); + const char* str = ctx.arg(0).strValue(); + if (*str != '\0') str = Substring(str, ctx.arg(1).rawValue(), ctx.arg(2).rawValue()); + ctx.setReturn(str); } void sf_string_compare(OpcodeContext& ctx) { @@ -225,7 +236,6 @@ void sf_string_compare(OpcodeContext& ctx) { } } -static char* sprintfbuf = nullptr; // A safer version of sprintf for using in user scripts. static char* _stdcall sprintf_lite(const char* format, ScriptValue value) { int fmtlen = strlen(format); @@ -290,18 +300,18 @@ static char* _stdcall sprintf_lite(const char* format, ScriptValue value) { } else { buflen = j + 30; // numbers } - if (sprintfbuf) { - delete[] sprintfbuf; + if (tempTextBuf) { + delete[] tempTextBuf; } - sprintfbuf = new char[buflen + 1]; + tempTextBuf = new char[buflen + 1]; if (value.isFloat()) { - _snprintf(sprintfbuf, buflen, newfmt, value.floatValue()); + _snprintf(tempTextBuf, buflen, newfmt, value.floatValue()); } else { - _snprintf(sprintfbuf, buflen, newfmt, value.rawValue()); + _snprintf(tempTextBuf, buflen, newfmt, value.rawValue()); } - sprintfbuf[buflen] = '\0'; // just in case + tempTextBuf[buflen] = '\0'; // just in case delete[] newfmt; - return sprintfbuf; + return tempTextBuf; } void sf_sprintf(OpcodeContext& ctx) { diff --git a/sfall/Modules/Scripting/Handlers/Utils.h b/sfall/Modules/Scripting/Handlers/Utils.h index ed2be87a..0477c169 100644 --- a/sfall/Modules/Scripting/Handlers/Utils.h +++ b/sfall/Modules/Scripting/Handlers/Utils.h @@ -23,7 +23,7 @@ namespace sfall namespace script { -char* Substring(const char* str, int pos, int length); +char* Substring(const char* str, int startPos, int length); class OpcodeContext; From 2a2849537c9ea7e47b42923cd545192379d7936b Mon Sep 17 00:00:00 2001 From: NovaRain Date: Sun, 1 Dec 2019 11:58:13 +0800 Subject: [PATCH 17/36] Added "string_format" script function Some code refactoring. --- artifacts/ddraw.ini | 14 +-- artifacts/scripting/headers/sfall.h | 1 + artifacts/scripting/sfall function notes.txt | 6 +- sfall/Modules/DebugEditor.cpp | 11 ++- sfall/Modules/LoadGameHook.cpp | 2 +- sfall/Modules/ScriptExtender.cpp | 2 + sfall/Modules/ScriptExtender.h | 5 ++ sfall/Modules/Scripting/Handlers/Metarule.cpp | 1 + sfall/Modules/Scripting/Handlers/Misc.cpp | 23 +++-- sfall/Modules/Scripting/Handlers/Utils.cpp | 89 ++++++++++++++----- sfall/Modules/Scripting/Handlers/Utils.h | 2 + 11 files changed, 105 insertions(+), 51 deletions(-) diff --git a/artifacts/ddraw.ini b/artifacts/ddraw.ini index e3d40dbf..30366a6e 100644 --- a/artifacts/ddraw.ini +++ b/artifacts/ddraw.ini @@ -707,29 +707,29 @@ CreditsAtBottom=0 ;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ;To change the path and filename of the critical table file, uncomment the next line -;OverrideCriticalFile=CriticalOverrides.ini +;OverrideCriticalFile=sfall\CriticalOverrides.ini ;To change the relationship between SPECIAL stats and derived stats, uncomment the next line ;See the Stats.ini in the modders pack for an example file -;DerivedStats=Stats.ini +;DerivedStats=sfall\Stats.ini ;Allows you to edit the skill tables ;Point the next line to an ini file containing the replacement skill data -;SkillsFile=Skills.ini +;SkillsFile=sfall\Skills.ini ;To add additional perks to the game, uncomment the next line and set it to point to a file containing perk information -;PerksFile=Perks.ini +;PerksFile=sfall\Perks.ini ;To add additional books to the game, uncomment the next line and point to a file containing book information ;See the Books.ini in the modders pack for an example file -;BooksFile=Books.ini +;BooksFile=sfall\Books.ini ;Allows you to change some parameters for drugs and their addictions ;See the Drugs.ini in the modders pack for an example file -;DrugsFile=Drugs.ini +;DrugsFile=sfall\Drugs.ini ;Point to an ini file containing elevator data -;ElevatorsFile=Elevators.ini +;ElevatorsFile=sfall\Elevators.ini ;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX [Scripts] diff --git a/artifacts/scripting/headers/sfall.h b/artifacts/scripting/headers/sfall.h index 52a11781..173da3d1 100644 --- a/artifacts/scripting/headers/sfall.h +++ b/artifacts/scripting/headers/sfall.h @@ -320,6 +320,7 @@ #define spatial_radius(obj) sfall_func1("spatial_radius", obj) #define string_compare(str1, str2) sfall_func2("string_compare", str1, str2) #define string_compare_locale(str1, str2, codePage) sfall_func3("string_compare", str1, str2, codePage) +#define string_format(format, a1, a2) sfall_func3("string_format", format, a1, a2) #define tile_refresh_display sfall_func0("tile_refresh_display") #define unjam_lock(obj) sfall_func1("unjam_lock", obj) #define unset_unique_id(obj) sfall_func2("set_unique_id", obj, -1) diff --git a/artifacts/scripting/sfall function notes.txt b/artifacts/scripting/sfall function notes.txt index 2bdb4e27..29ce6aad 100644 --- a/artifacts/scripting/sfall function notes.txt +++ b/artifacts/scripting/sfall function notes.txt @@ -262,7 +262,7 @@ Some utility/math functions are available: - you can use this to search for a substring in a string like this: strlen(get_array(string_split(haystack, needle), 0)) > string substr(string, start, length) -- cuts a substring from a string starting at "start" up to "length" characters. The first character position starts with 0 (zero). +- cuts a substring from a string starting at "start" up to "length" characters. The first character position is 0 (zero). - If start is negative - it indicates starting position from the end of the string (for example substr("test", -2, 2) will return last 2 charactes: "st"). - If length is negative - it means so many characters will be omitted from the end of string (example: substr("test", 0, -2) will return string without last 2 characters: "te"). - If length is zero - it will return a string from the starting position to the end of the string **New behavior** for sfall 4.2.2/3.8.22 @@ -682,6 +682,10 @@ optional argument: - codePage: code page number to properly compare national characters in the range 128-255 of the ASCII code table available encodings: 1250-1252, 866 +> string sfall_func3("string_format", string format, any val1, any val2, ...) +- formats given value using standard syntax of C printf function (google "printf" for format details). However it is limited to formatting up to 4 values +- formatting is only supported for %s and %d. The format string is limited to 1024 characters + ------------------------ ------ MORE INFO ------- ------------------------ diff --git a/sfall/Modules/DebugEditor.cpp b/sfall/Modules/DebugEditor.cpp index 2e91c419..d3a69d30 100644 --- a/sfall/Modules/DebugEditor.cpp +++ b/sfall/Modules/DebugEditor.cpp @@ -310,19 +310,18 @@ hide: } } -static char* artDbgMsg = "\nError: file not found: %s\n"; +static char* artDbgMsg = "\nERROR: File not found: %s\n"; static void __declspec(naked) art_data_size_hook() { __asm { test edi, edi; jz artNotExist; retn; artNotExist: - mov eax, [esp + 0x124 - 0x1C + 4]; // filename - push eax; + mov edx, [esp + 0x124 - 0x1C + 4]; // filename + push edx; push artDbgMsg; call fo::funcoffs::debug_printf_; - mov eax, [esp + 0x124 - 0x1C + 12]; // filename - push eax; + push edx; // filename push artDbgMsg; lea eax, [esp + 0x124 - 0x124 + 20]; // buf push eax; @@ -368,7 +367,7 @@ static void DebugModePatch() { if (iniGetInt("Debugging", "HideObjIsNullMsg", 0, ::sfall::ddrawIni)) { MakeJump(0x453FD2, dbg_error_hack); } - // prints a debug message about missing art file for critters to debug.log and the message window + // prints a debug message about missing art file for critters to both debug.log and the message window HookCall(0x419B65, art_data_size_hook); dlogr(" Done", DL_INIT); diff --git a/sfall/Modules/LoadGameHook.cpp b/sfall/Modules/LoadGameHook.cpp index 2147ff27..51ed2c0d 100644 --- a/sfall/Modules/LoadGameHook.cpp +++ b/sfall/Modules/LoadGameHook.cpp @@ -523,7 +523,7 @@ end: static void __declspec(naked) DialogHook() { __asm { - test inLoop, DIALOG; // check bit flag + test inLoop, DIALOG; // check byte flag jz changeMode; jmp fo::funcoffs::gdProcess_; changeMode: diff --git a/sfall/Modules/ScriptExtender.cpp b/sfall/Modules/ScriptExtender.cpp index 7eb7ed9c..07365dce 100644 --- a/sfall/Modules/ScriptExtender.cpp +++ b/sfall/Modules/ScriptExtender.cpp @@ -51,6 +51,8 @@ static DWORD _stdcall HandleMapUpdateForScripts(const DWORD procId); static int idle; +char ScriptExtender::gTextBuffer[5120]; // used as global temp text buffer for script functions + std::string ScriptExtender::iniConfigFolder; struct GlobalScript { diff --git a/sfall/Modules/ScriptExtender.h b/sfall/Modules/ScriptExtender.h index 43ad45da..cf852a28 100644 --- a/sfall/Modules/ScriptExtender.h +++ b/sfall/Modules/ScriptExtender.h @@ -40,6 +40,11 @@ public: static std::string iniConfigFolder; + static char gTextBuffer[5120]; + + // returns the size of the global text buffer + inline static const long TextBufferSize() { return sizeof(gTextBuffer); } + static long GetScriptReturnValue(); static long GetResetScriptReturnValue(); diff --git a/sfall/Modules/Scripting/Handlers/Metarule.cpp b/sfall/Modules/Scripting/Handlers/Metarule.cpp index b79b876c..13e9184b 100644 --- a/sfall/Modules/Scripting/Handlers/Metarule.cpp +++ b/sfall/Modules/Scripting/Handlers/Metarule.cpp @@ -132,6 +132,7 @@ static const SfallMetarule metarules[] = { {"show_window", sf_show_window, 0, 1, -1, {ARG_STRING}}, {"spatial_radius", sf_spatial_radius, 1, 1, 0, {ARG_OBJECT}}, {"string_compare", sf_string_compare, 2, 3, 0, {ARG_STRING, ARG_STRING, ARG_INT}}, + {"string_format", sf_string_format, 2, 5, 0, {ARG_STRING, ARG_ANY, ARG_ANY, ARG_ANY, ARG_ANY}}, {"tile_refresh_display", sf_tile_refresh_display, 0, 0}, {"unjam_lock", sf_unjam_lock, 1, 1, -1, {ARG_OBJECT}}, {"unwield_slot", sf_unwield_slot, 2, 2, -1, {ARG_OBJECT, ARG_INT}}, diff --git a/sfall/Modules/Scripting/Handlers/Misc.cpp b/sfall/Modules/Scripting/Handlers/Misc.cpp index dc6aa34f..a2b9cbae 100644 --- a/sfall/Modules/Scripting/Handlers/Misc.cpp +++ b/sfall/Modules/Scripting/Handlers/Misc.cpp @@ -42,7 +42,7 @@ namespace sfall namespace script { -const char* stringTooLong = "%s() - the string length exceeds maximum of 64 characters."; +const char* stringTooLong = "%s() - the string exceeds maximum length of 64 characters."; void sf_set_dm_model(OpcodeContext& ctx) { auto model = ctx.arg(0).strValue(); @@ -453,7 +453,6 @@ specialIni: return 1; } -static char IniStrBuffer[256]; static DWORD GetIniSetting(const char* str, bool isString) { const char* key; char section[33], file[128]; @@ -462,9 +461,9 @@ static DWORD GetIniSetting(const char* str, bool isString) { return -1; } if (isString) { - IniStrBuffer[0] = 0; - iniGetString(section, key, "", IniStrBuffer, 256, file); - return (DWORD)&IniStrBuffer[0]; + ScriptExtender::gTextBuffer[0] = 0; + iniGetString(section, key, "", ScriptExtender::gTextBuffer, 256, file); + return (DWORD)&ScriptExtender::gTextBuffer[0]; } else { return iniGetInt(section, key, -1, file); } @@ -1037,8 +1036,8 @@ void sf_set_ini_setting(OpcodeContext& ctx) { const char* saveValue; if (argVal.isInt()) { - _itoa_s(argVal.rawValue(), IniStrBuffer, 10); - saveValue = IniStrBuffer; + _itoa_s(argVal.rawValue(), ScriptExtender::gTextBuffer, 10); + saveValue = ScriptExtender::gTextBuffer; } else { saveValue = argVal.strValue(); } @@ -1075,12 +1074,10 @@ static std::string GetIniFilePath(const ScriptValue& arg) { return fileName; } -char getIniSectionBuf[5120]; - void sf_get_ini_sections(OpcodeContext& ctx) { - GetPrivateProfileSectionNamesA(getIniSectionBuf, 5120, GetIniFilePath(ctx.arg(0)).data()); + GetPrivateProfileSectionNamesA(ScriptExtender::gTextBuffer, ScriptExtender::TextBufferSize(), GetIniFilePath(ctx.arg(0)).data()); std::vector sections; - char* section = getIniSectionBuf; + char* section = ScriptExtender::gTextBuffer; while (*section != 0) { sections.push_back(section); // position section += std::strlen(section) + 1; @@ -1099,10 +1096,10 @@ void sf_get_ini_sections(OpcodeContext& ctx) { void sf_get_ini_section(OpcodeContext& ctx) { auto section = ctx.arg(1).strValue(); - GetPrivateProfileSectionA(section, getIniSectionBuf, 5120, GetIniFilePath(ctx.arg(0)).data()); + GetPrivateProfileSectionA(section, ScriptExtender::gTextBuffer, ScriptExtender::TextBufferSize(), GetIniFilePath(ctx.arg(0)).data()); int arrayId = TempArray(-1, 0); // associative auto& arr = arrays[arrayId]; - char *key = getIniSectionBuf, *val = nullptr; + char *key = ScriptExtender::gTextBuffer, *val = nullptr; while (*key != 0) { char* val = std::strpbrk(key, "="); if (val != nullptr) { diff --git a/sfall/Modules/Scripting/Handlers/Utils.cpp b/sfall/Modules/Scripting/Handlers/Utils.cpp index 3f640b47..5576b8d6 100644 --- a/sfall/Modules/Scripting/Handlers/Utils.cpp +++ b/sfall/Modules/Scripting/Handlers/Utils.cpp @@ -190,8 +190,6 @@ void sf_string_split(OpcodeContext& ctx) { ctx.setReturn(StringSplit(ctx.arg(0).strValue(), ctx.arg(1).strValue())); } -static char* tempTextBuf = nullptr; - char* Substring(const char* str, int startPos, int length) { int len = strlen(str); @@ -201,11 +199,8 @@ char* Substring(const char* str, int startPos, int length) { } if (length < 0) { length += len - startPos; // cutoff at end - if (length == 0) { - return ""; - } else if (length < 0) { - length = -length; // length can't be negative - } + if (length == 0) return ""; + abs(length); // length can't be negative } // check position if (startPos >= len) return ""; // start position is out of string length, return empty string @@ -213,11 +208,12 @@ char* Substring(const char* str, int startPos, int length) { length = len - startPos; // set the correct length, the length of characters goes beyond the end of the string } - if (tempTextBuf) delete[] tempTextBuf; - tempTextBuf = new char[length + 1]; - memcpy(tempTextBuf, &str[startPos], length); - tempTextBuf[length] = '\0'; - return tempTextBuf; + const int bufMax = ScriptExtender::TextBufferSize() - 1; + if (length > bufMax) length = bufMax; + + memcpy(ScriptExtender::gTextBuffer, &str[startPos], length); + ScriptExtender::gTextBuffer[length] = '\0'; + return ScriptExtender::gTextBuffer; } void sf_substr(OpcodeContext& ctx) { @@ -241,8 +237,7 @@ static char* _stdcall sprintf_lite(const char* format, ScriptValue value) { int fmtlen = strlen(format); int buflen = fmtlen + 1; for (int i = 0; i < fmtlen; i++) { - if (format[i] == '%') - buflen++; // will possibly be escaped, need space for that + if (format[i] == '%') buflen++; // will possibly be escaped, need space for that } // parse format to make it safe char* newfmt = new char[buflen]; @@ -290,7 +285,8 @@ static char* _stdcall sprintf_lite(const char* format, ScriptValue value) { newfmt[j++] = c; } newfmt[j] = '\0'; - // calculate required memory + + // calculate required length if (hasDigits) { buflen = 254; } else if (specifier == 'c') { @@ -300,18 +296,18 @@ static char* _stdcall sprintf_lite(const char* format, ScriptValue value) { } else { buflen = j + 30; // numbers } - if (tempTextBuf) { - delete[] tempTextBuf; - } - tempTextBuf = new char[buflen + 1]; + + const long bufMaxLen = ScriptExtender::TextBufferSize() - 1; + if (buflen > bufMaxLen - 1) buflen = bufMaxLen - 1; + ScriptExtender::gTextBuffer[bufMaxLen] = '\0'; + if (value.isFloat()) { - _snprintf(tempTextBuf, buflen, newfmt, value.floatValue()); + _snprintf(ScriptExtender::gTextBuffer, buflen, newfmt, value.floatValue()); } else { - _snprintf(tempTextBuf, buflen, newfmt, value.rawValue()); + _snprintf(ScriptExtender::gTextBuffer, buflen, newfmt, value.rawValue()); } - tempTextBuf[buflen] = '\0'; // just in case delete[] newfmt; - return tempTextBuf; + return ScriptExtender::gTextBuffer; } void sf_sprintf(OpcodeContext& ctx) { @@ -320,6 +316,53 @@ void sf_sprintf(OpcodeContext& ctx) { ); } +void sf_string_format(OpcodeContext& ctx) { + const char* format = ctx.arg(0).strValue(); + + int fmtLen = strlen(format); + if (fmtLen == 0) { + ctx.setReturn(format); + return; + } + if (fmtLen > 1024) { + ctx.printOpcodeError("%s() - the format string exceeds maximum length of 1024 characters.", ctx.getMetaruleName()); + ctx.setReturn("Error"); + } else { + char* newFmt = new char[fmtLen + 1]; + newFmt[fmtLen] = '\0'; + // parse format to make it safe + int i = 0; + do { + char c = format[i]; + if (c == '%') { + char cf = format[i + 1]; + if (cf != 's' && cf != 'd' && cf != '%') c = ' '; // unsupported format + } + newFmt[i] = c; + } while (++i < fmtLen); + + const long bufMaxLen = ScriptExtender::TextBufferSize() - 1; + + switch (ctx.numArgs()) { + case 2 : + _snprintf(ScriptExtender::gTextBuffer, bufMaxLen, newFmt, ctx.arg(1).rawValue()); + break; + case 3 : + _snprintf(ScriptExtender::gTextBuffer, bufMaxLen, newFmt, ctx.arg(1).rawValue(), ctx.arg(2).rawValue()); + break; + case 4 : + _snprintf(ScriptExtender::gTextBuffer, bufMaxLen, newFmt, ctx.arg(1).rawValue(), ctx.arg(2).rawValue(), ctx.arg(3).rawValue()); + break; + case 5 : + _snprintf(ScriptExtender::gTextBuffer, bufMaxLen, newFmt, ctx.arg(1).rawValue(), ctx.arg(2).rawValue(), ctx.arg(3).rawValue(), ctx.arg(4).rawValue()); + } + ScriptExtender::gTextBuffer[bufMaxLen] = '\0'; // just in case + + delete[] newFmt; + ctx.setReturn(ScriptExtender::gTextBuffer); + } +} + void sf_power(OpcodeContext& ctx) { const ScriptValue &base = ctx.arg(0), &power = ctx.arg(1); diff --git a/sfall/Modules/Scripting/Handlers/Utils.h b/sfall/Modules/Scripting/Handlers/Utils.h index 0477c169..f82b427b 100644 --- a/sfall/Modules/Scripting/Handlers/Utils.h +++ b/sfall/Modules/Scripting/Handlers/Utils.h @@ -53,6 +53,8 @@ void sf_string_compare(OpcodeContext&); void sf_sprintf(OpcodeContext&); +void sf_string_format(OpcodeContext&); + void sf_ord(OpcodeContext&); void sf_typeof(OpcodeContext&); From 1bb04a6d45171ba4d0928d8f3eb2b1d808047223 Mon Sep 17 00:00:00 2001 From: NovaRain Date: Tue, 3 Dec 2019 10:48:30 +0800 Subject: [PATCH 18/36] Improved handling of incorrect formatting for "string_format" script function Rewrote ASM code of opcode handlers in Memory.cpp. --- sfall/FalloutEngine/Fallout2.h | 10 +- sfall/FalloutEngine/Functions.cpp | 4 +- sfall/FalloutEngine/Functions.h | 2 +- sfall/Modules/HeroAppearance.cpp | 6 +- sfall/Modules/Scripting/Handlers/Memory.cpp | 353 ++++++++------------ sfall/Modules/Scripting/Handlers/Stats.cpp | 2 +- sfall/Modules/Scripting/Handlers/Utils.cpp | 23 +- 7 files changed, 172 insertions(+), 228 deletions(-) diff --git a/sfall/FalloutEngine/Fallout2.h b/sfall/FalloutEngine/Fallout2.h index bbd3ebba..37e69ff0 100644 --- a/sfall/FalloutEngine/Fallout2.h +++ b/sfall/FalloutEngine/Fallout2.h @@ -17,7 +17,7 @@ */ #pragma once -/* +/* * FALLOUT2.EXE structs, function offsets and wrappers are included from here. */ @@ -55,6 +55,7 @@ Gets argument from stack to eax and puts its type to edx register eax register must contain the script_ptr jlabel - name of the jump label in case the value type is not INT + return: eax - arg value */ #define _GET_ARG_INT(jlabel) __asm { \ __asm mov edx, eax \ @@ -76,3 +77,10 @@ __asm mov eax, ebx \ __asm call fo::funcoffs::interpretPushShort_ \ } + +#define _J_RET_VAL_TYPE(type) __asm { \ + __asm call fo::funcoffs::interpretPushLong_ \ + __asm mov edx, type \ + __asm mov eax, ebx \ + __asm jmp fo::funcoffs::interpretPushShort_ \ +} diff --git a/sfall/FalloutEngine/Functions.cpp b/sfall/FalloutEngine/Functions.cpp index 2a449a2d..719c5e8f 100644 --- a/sfall/FalloutEngine/Functions.cpp +++ b/sfall/FalloutEngine/Functions.cpp @@ -242,8 +242,8 @@ DWORD __stdcall interpretAddString(Program* scriptPtr, const char* strval) { WRAP_WATCOM_CALL2(interpretAddString_, scriptPtr, strval) } -const char* __stdcall interpretGetString(Program* scriptPtr, DWORD dataType, DWORD strId) { - WRAP_WATCOM_CALL3(interpretGetString_, scriptPtr, dataType, strId) +const char* __fastcall interpretGetString(Program* scriptPtr, DWORD dataType, DWORD strId) { + WRAP_WATCOM_FCALL3(interpretGetString_, scriptPtr, dataType, strId) } // prints scripting error in debug.log and stops current script execution by performing longjmp diff --git a/sfall/FalloutEngine/Functions.h b/sfall/FalloutEngine/Functions.h index d2138d15..e0489fe7 100644 --- a/sfall/FalloutEngine/Functions.h +++ b/sfall/FalloutEngine/Functions.h @@ -122,7 +122,7 @@ void __stdcall interpretPushLong(Program* scriptPtr, DWORD val); // pushes value type to Data stack (must be preceded by InterpretPushLong) void __stdcall interpretPushShort(Program* scriptPtr, DWORD valType); -const char* __stdcall interpretGetString(Program* scriptPtr, DWORD dataType, DWORD strId); +const char* __fastcall interpretGetString(Program* scriptPtr, DWORD dataType, DWORD strId); DWORD __stdcall interpretAddString(Program* scriptPtr, const char* str); diff --git a/sfall/Modules/HeroAppearance.cpp b/sfall/Modules/HeroAppearance.cpp index 70fd03bc..18a04aab 100644 --- a/sfall/Modules/HeroAppearance.cpp +++ b/sfall/Modules/HeroAppearance.cpp @@ -399,11 +399,9 @@ void UpdateHeroArt() { if (item->flags & fo::ObjectFlag::Right_Hand) { fo::var::i_rhand = item; - } - else if (item->flags & fo::ObjectFlag::Left_Hand) { + } else if (item->flags & fo::ObjectFlag::Left_Hand) { fo::var::i_lhand = item; - } - else if (item->flags & fo::ObjectFlag::Worn) { + } else if (item->flags & fo::ObjectFlag::Worn) { fo::var::i_worn = item; } } diff --git a/sfall/Modules/Scripting/Handlers/Memory.cpp b/sfall/Modules/Scripting/Handlers/Memory.cpp index 54ac9458..6b4d8e9a 100644 --- a/sfall/Modules/Scripting/Handlers/Memory.cpp +++ b/sfall/Modules/Scripting/Handlers/Memory.cpp @@ -18,7 +18,7 @@ #include "..\..\..\FalloutEngine\Fallout2.h" #include "..\..\..\SafeWrite.h" -#include "..\..\ScriptExtender.h" +//#include "..\..\ScriptExtender.h" #include "Memory.h" @@ -27,310 +27,233 @@ namespace sfall namespace script { +#define START_VALID_ADDR 0x410000 +#define END_VALID_ADDR 0x6B403F + void __declspec(naked) op_read_byte() { __asm { - push ebx; - push ecx; - push edx; - mov ecx, eax; - call fo::funcoffs::interpretPopShort_; - mov edx, eax; - mov eax, ecx; - call fo::funcoffs::interpretPopLong_; - cmp dx, VAR_TYPE_INT; - jnz error; - movzx edx, byte ptr ds:[eax]; - jmp result; -error: - mov edx, 0; + _GET_ARG_INT(error); + test eax, eax; + jz error; + movzx edx, byte ptr ds:[eax]; // read memory result: - mov eax, ecx; - call fo::funcoffs::interpretPushLong_; - mov edx, VAR_TYPE_INT; - mov eax, ecx; - call fo::funcoffs::interpretPushShort_; - pop edx; - pop ecx; - pop ebx; - retn; + mov eax, ebx; + _J_RET_VAL_TYPE(VAR_TYPE_INT); +// retn; +error: + xor edx, edx; + jmp result; } } void __declspec(naked) op_read_short() { __asm { - push ebx; - push ecx; - push edx; - mov ecx, eax; - call fo::funcoffs::interpretPopShort_; - mov edx, eax; - mov eax, ecx; - call fo::funcoffs::interpretPopLong_; - cmp dx, VAR_TYPE_INT; - jnz error; - movzx edx, word ptr ds:[eax]; - jmp result; -error: - mov edx, 0; + _GET_ARG_INT(error); + test eax, eax; + jz error; + movzx edx, word ptr ds:[eax]; // read memory result: - mov eax, ecx; - call fo::funcoffs::interpretPushLong_; - mov edx, VAR_TYPE_INT; - mov eax, ecx; - call fo::funcoffs::interpretPushShort_; - pop edx; - pop ecx; - pop ebx; - retn; + mov eax, ebx; + _J_RET_VAL_TYPE(VAR_TYPE_INT); +// retn; +error: + xor edx, edx; + jmp result; } } void __declspec(naked) op_read_int() { __asm { - push ebx; - push ecx; - push edx; - mov ecx, eax; - call fo::funcoffs::interpretPopShort_; - mov edx, eax; - mov eax, ecx; - call fo::funcoffs::interpretPopLong_; - cmp dx, VAR_TYPE_INT; - jnz error; - mov edx, dword ptr ds:[eax]; - jmp result; -error: - mov edx, 0; + _GET_ARG_INT(error); + test eax, eax; + jz error; + mov edx, dword ptr ds:[eax]; // read memory result: - mov eax, ecx; - call fo::funcoffs::interpretPushLong_; - mov edx, VAR_TYPE_INT; - mov eax, ecx; - call fo::funcoffs::interpretPushShort_; - pop edx; - pop ecx; - pop ebx; - retn; + mov eax, ebx; + _J_RET_VAL_TYPE(VAR_TYPE_INT); +// retn; +error: + xor edx, edx; + jmp result; } } void __declspec(naked) op_read_string() { __asm { - push ebx; - push ecx; - push edx; - mov ecx, eax; - call fo::funcoffs::interpretPopShort_; - mov edx, eax; - mov eax, ecx; - call fo::funcoffs::interpretPopLong_; - cmp dx, VAR_TYPE_INT; - jnz error; - mov edx, eax; - jmp result; -error: - mov edx, 0; + _GET_ARG_INT(error); + test eax, eax; + jz error; + mov edx, eax; result: - mov eax, ecx; - call fo::funcoffs::interpretPushLong_; - mov edx, VAR_TYPE_STR; - mov eax, ecx; - call fo::funcoffs::interpretPushShort_; - pop edx; - pop ecx; - pop ebx; - retn; + mov eax, ebx; + _J_RET_VAL_TYPE(VAR_TYPE_STR); +// retn; +error: + xor edx, edx; + jmp result; } } void __declspec(naked) op_write_byte() { __asm { - pushad - mov ecx, eax; + push ecx; call fo::funcoffs::interpretPopShort_; - mov esi, eax; - mov eax, ecx; + mov ecx, eax; // type + mov eax, ebx; call fo::funcoffs::interpretPopLong_; - mov edx, eax; - mov eax, ecx; - call fo::funcoffs::interpretPopShort_; - mov edi, eax; - mov eax, ecx; - call fo::funcoffs::interpretPopLong_; - cmp di, VAR_TYPE_INT; - jnz end; - cmp si, VAR_TYPE_INT; - jnz end; - //mov byte ptr ds:[eax], dl; - and edx, 0xff; - push edx; + mov esi, eax; // write value + mov eax, ebx; + _GET_ARG_INT(end); + cmp cx, VAR_TYPE_INT; + jnz end; + // check valid addr + cmp eax, START_VALID_ADDR; + jb end; + cmp eax, END_VALID_ADDR; + ja end; + and esi, 0xFF; + push esi; push eax; call SafeWrite8; end: - popad; + pop ecx; retn; } } void __declspec(naked) op_write_short() { __asm { - pushad; - mov ecx, eax; + push ecx; call fo::funcoffs::interpretPopShort_; - mov esi, eax; - mov eax, ecx; + mov ecx, eax; // type + mov eax, ebx; call fo::funcoffs::interpretPopLong_; - mov edx, eax; - mov eax, ecx; - call fo::funcoffs::interpretPopShort_; - mov edi, eax; - mov eax, ecx; - call fo::funcoffs::interpretPopLong_; - cmp di, VAR_TYPE_INT; - jnz end; - cmp si, VAR_TYPE_INT; - jnz end; - //mov word ptr ds:[eax], dx; - and edx, 0xffff; - push edx; + mov esi, eax; // write value + mov eax, ebx; + _GET_ARG_INT(end); + cmp cx, VAR_TYPE_INT; + jnz end; + // check valid addr + cmp eax, START_VALID_ADDR; + jb end; + cmp eax, END_VALID_ADDR; + ja end; + and esi, 0xFFFF; + push esi; push eax; call SafeWrite16; end: - popad; + pop ecx; retn; } } void __declspec(naked) op_write_int() { __asm { - pushad - mov ecx, eax; + push ecx; call fo::funcoffs::interpretPopShort_; - mov esi, eax; - mov eax, ecx; + mov ecx, eax; // type + mov eax, ebx; call fo::funcoffs::interpretPopLong_; - mov edx, eax; - mov eax, ecx; - call fo::funcoffs::interpretPopShort_; - mov edi, eax; - mov eax, ecx; - call fo::funcoffs::interpretPopLong_; - cmp di, VAR_TYPE_INT; - jnz end; - cmp si, VAR_TYPE_INT; - jnz end; - //mov dword ptr ds:[eax], edx; - push edx; + mov esi, eax; // write value + mov eax, ebx; + _GET_ARG_INT(end); + cmp cx, VAR_TYPE_INT; + jnz end; + // check valid addr + cmp eax, START_VALID_ADDR; + jb end; + cmp eax, END_VALID_ADDR; + ja end; + push esi; push eax; call SafeWrite32; end: - popad + pop ecx; retn; } } -static void _stdcall WriteStringInternal(const char* str, char* addr) { - bool hitnull = false; +static void __fastcall WriteStringInternal(char* addr, long type, long strID, fo::Program* script) { + const char* str = fo::func::interpretGetString(script, type, strID); while (*str) { - if (!*addr) hitnull = true; - if (hitnull && addr[1]) break; - *addr = *str; - addr++; - str++; + if (!addr[0] && addr[1]) break; // addr[1] as *(addr + 1) + *addr++ = *str++; } *addr = 0; } void __declspec(naked) op_write_string() { __asm { - pushad; - mov ecx, eax; + push ecx; call fo::funcoffs::interpretPopShort_; - mov esi, eax; - mov eax, ecx; + mov ecx, eax; // type + mov eax, ebx; call fo::funcoffs::interpretPopLong_; - mov edi, eax; - mov eax, ecx; - call fo::funcoffs::interpretPopShort_; - mov edx, eax; - mov eax, ecx; - call fo::funcoffs::interpretPopLong_; - cmp dx, VAR_TYPE_INT; - jnz end; - cmp si, VAR_TYPE_STR2; - jz next; - cmp si, VAR_TYPE_STR; - jnz end; + mov esi, eax; // str value + mov eax, ebx; + _GET_ARG_INT(end); + cmp cx, VAR_TYPE_STR2; + je next; + cmp cx, VAR_TYPE_STR; + jnz end; next: - mov ebx, edi; - mov edx, esi; - mov esi, eax; - mov eax, ecx; - call fo::funcoffs::interpretGetString_; - push esi; - push eax; + // ecx - type, esi - value + // edx - type, eax - addr + // check valid address + cmp eax, START_VALID_ADDR; + jb end; + cmp eax, END_VALID_ADDR; + ja end; + push ebx; // script + push esi; // str value + mov edx, ecx; // type + mov ecx, eax; // addr call WriteStringInternal; - jmp end; end: - popad; + pop ecx; retn; } } -static void _stdcall CallOffsetInternal(DWORD func, DWORD script) { +static void __fastcall CallOffsetInternal(fo::Program* script, DWORD func) { func = (func >> 2) - 0x1d2; - bool ret = func >= 5; - int argcount = func % 5; DWORD args[5]; - DWORD illegalarg = 0; - for (int i = argcount * 4; i >= 0; i -= 4) { - __asm { - mov eax, script; - call fo::funcoffs::interpretPopShort_; - cmp ax, VAR_TYPE_INT; - jz legal; - inc illegalarg; -legal: - mov eax, script; - call fo::funcoffs::interpretPopLong_; - lea ecx, args; - add ecx, i; - mov [ecx], eax; - } - } + DWORD illegalArg = 0; + int argCount = func % 5; - if (illegalarg) { + for (int i = argCount; i >= 0; i--) { + if ((short)fo::func::interpretPopShort(script) != (short)VAR_TYPE_INT) illegalArg++; + args[i] = fo::func::interpretPopLong(script); + } + if (illegalArg || args[0] < 0x410010 || args[0] > 0x4FCE34) { args[0] = 0; } else { __asm { - mov eax, args[4]; - mov edx, args[8]; - mov ebx, args[12]; - mov ecx, args[16]; - mov edi, args[0]; - call edi; - mov args[0], eax; + mov eax, args[4]; + mov edx, args[8]; + mov ebx, args[12]; + mov ecx, args[16]; + call args[0]; + mov args[0], eax; } } - if (ret) { + if (func >= 5) { // has return __asm { mov eax, script; mov edx, args[0]; - call fo::funcoffs::interpretPushLong_; - mov eax, script; - mov edx, VAR_TYPE_INT; - call fo::funcoffs::interpretPushShort_; + mov ebx, eax; + _RET_VAL_INT; } } } void __declspec(naked) op_call_offset() { __asm { - pushad; - push eax; - push edx; - call CallOffsetInternal; - popad; + push ecx; + mov ecx, eax; + call CallOffsetInternal; // edx - func + pop ecx; retn; } } diff --git a/sfall/Modules/Scripting/Handlers/Stats.cpp b/sfall/Modules/Scripting/Handlers/Stats.cpp index 7efa3aa0..12b251a9 100644 --- a/sfall/Modules/Scripting/Handlers/Stats.cpp +++ b/sfall/Modules/Scripting/Handlers/Stats.cpp @@ -584,7 +584,7 @@ void __declspec(naked) op_set_base_skill_mod() { // same as set_skill_max push 0xFFFFFFFF; call SetSkillMax; end: - pop ecx; + pop ecx; retn; } } diff --git a/sfall/Modules/Scripting/Handlers/Utils.cpp b/sfall/Modules/Scripting/Handlers/Utils.cpp index 5576b8d6..e551bf9e 100644 --- a/sfall/Modules/Scripting/Handlers/Utils.cpp +++ b/sfall/Modules/Scripting/Handlers/Utils.cpp @@ -200,7 +200,7 @@ char* Substring(const char* str, int startPos, int length) { if (length < 0) { length += len - startPos; // cutoff at end if (length == 0) return ""; - abs(length); // length can't be negative + length = abs(length); // length can't be negative } // check position if (startPos >= len) return ""; // start position is out of string length, return empty string @@ -331,19 +331,34 @@ void sf_string_format(OpcodeContext& ctx) { char* newFmt = new char[fmtLen + 1]; newFmt[fmtLen] = '\0'; // parse format to make it safe - int i = 0; + int i = 0, arg = 0, totalArg = ctx.numArgs(); // total passed args do { char c = format[i]; if (c == '%') { char cf = format[i + 1]; - if (cf != 's' && cf != 'd' && cf != '%') c = ' '; // unsupported format + if (cf != '%') { + if (arg >= 0) { + arg++; + if (arg == totalArg) arg = -1; // format '%' prefixes in the format string exceed the number of passed value args + } + if (arg < 0) { // have % more than passed value args + c = '^'; // delete % + } + // check string is valid or replace unsupported format + else if ((cf == 's' && (arg > 0 && !ctx.arg(arg).isString())) || (cf != 's' && cf != 'd')) { + newFmt[i++] = c; + c = 'd'; // replace with %d + } + } else { + newFmt[i++] = cf; // skip %% + } } newFmt[i] = c; } while (++i < fmtLen); const long bufMaxLen = ScriptExtender::TextBufferSize() - 1; - switch (ctx.numArgs()) { + switch (totalArg) { case 2 : _snprintf(ScriptExtender::gTextBuffer, bufMaxLen, newFmt, ctx.arg(1).rawValue()); break; From 4cffc455e23350cfa610bda117fba1e208c65f52 Mon Sep 17 00:00:00 2001 From: NovaRain Date: Wed, 4 Dec 2019 10:39:35 +0800 Subject: [PATCH 19/36] Code refactoring for opcodes/functions --- sfall/Modules/Scripting/Handlers/Arrays.cpp | 11 +- sfall/Modules/Scripting/Handlers/Core.cpp | 32 +++--- sfall/Modules/Scripting/Handlers/Graphics.cpp | 26 +++-- .../Modules/Scripting/Handlers/Interface.cpp | 56 ++++------ sfall/Modules/Scripting/Handlers/Memory.cpp | 8 +- sfall/Modules/Scripting/Handlers/Misc.cpp | 102 +++++++----------- sfall/Modules/Scripting/Handlers/Perks.cpp | 53 ++++----- sfall/Modules/Scripting/Handlers/Stats.cpp | 30 +++--- sfall/Modules/Scripting/Handlers/Utils.cpp | 6 +- sfall/Modules/Scripting/Handlers/Utils.h | 2 - sfall/Modules/Scripting/Handlers/Worldmap.cpp | 20 ++-- sfall/Modules/Scripting/OpcodeContext.cpp | 46 +++++--- sfall/Modules/Scripting/OpcodeContext.h | 6 +- sfall/Modules/Worldmap.cpp | 14 ++- 14 files changed, 184 insertions(+), 228 deletions(-) diff --git a/sfall/Modules/Scripting/Handlers/Arrays.cpp b/sfall/Modules/Scripting/Handlers/Arrays.cpp index 7d76a586..68531f92 100644 --- a/sfall/Modules/Scripting/Handlers/Arrays.cpp +++ b/sfall/Modules/Scripting/Handlers/Arrays.cpp @@ -20,7 +20,6 @@ #include "..\..\ScriptExtender.h" #include "..\Arrays.h" #include "..\OpcodeContext.h" -#include "Utils.h" #include "Arrays.h" @@ -55,8 +54,14 @@ void sf_get_array(OpcodeContext& ctx) { ); } else if (ctx.arg(0).isString()) { if (ctx.arg(1).isInt()) { - const char* str = Substring(ctx.arg(0).strValue(), ctx.arg(1).rawValue(), 1); - ctx.setReturn(str); // returns char of string + size_t index = ctx.arg(1).rawValue(); + if (index < strlen(ctx.arg(0).strValue())) { + wchar_t c = ((unsigned char*)ctx.arg(0).strValue())[index]; + ((wchar_t*)ScriptExtender::gTextBuffer)[0] = c; + } else { + ScriptExtender::gTextBuffer[0] = '\0'; + } + ctx.setReturn(ScriptExtender::gTextBuffer); // returns char of string } else { ctx.printOpcodeError("%s() - index must be numeric when used on a string.", ctx.getOpcodeName()); } diff --git a/sfall/Modules/Scripting/Handlers/Core.cpp b/sfall/Modules/Scripting/Handlers/Core.cpp index 20acd0de..a1c595fb 100644 --- a/sfall/Modules/Scripting/Handlers/Core.cpp +++ b/sfall/Modules/Scripting/Handlers/Core.cpp @@ -34,37 +34,35 @@ namespace script void __declspec(naked) op_set_global_script_repeat() { __asm { - push ecx; + mov esi, ecx; mov ecx, eax; _GET_ARG_INT(end); mov edx, eax; // frames call SetGlobalScriptRepeat; // ecx - script end: - pop ecx; + mov ecx, esi; retn; } } void __declspec(naked) op_set_global_script_type() { __asm { - push ecx; + mov esi, ecx; mov ecx, eax; _GET_ARG_INT(end); mov edx, eax; // type call SetGlobalScriptType; // ecx - script end: - pop ecx; + mov ecx, esi; retn; } } void __declspec(naked) op_available_global_script_types() { __asm { - push ecx; mov edx, availableGlobalScriptTypes; - _RET_VAL_INT; - pop ecx; - retn; + _J_RET_VAL_TYPE(VAR_TYPE_INT); +// retn; } } @@ -107,12 +105,12 @@ void sf_get_sfall_global_float(OpcodeContext& ctx) { void __declspec(naked) op_get_sfall_arg() { __asm { - push ecx; + mov esi, ecx; call HookScripts::GetHSArg; mov edx, eax; mov eax, ebx; _RET_VAL_INT; - pop ecx; + mov ecx, esi; retn; } } @@ -144,35 +142,33 @@ void sf_set_sfall_arg(OpcodeContext& ctx) { void __declspec(naked) op_set_sfall_return() { __asm { - push ecx; + mov esi, ecx; _GET_ARG_INT(end); push eax; call HookScripts::SetHSReturn; end: - pop ecx; + mov ecx, esi; retn; } } void __declspec(naked) op_init_hook() { __asm { - push ecx; mov edx, initingHookScripts; - _RET_VAL_INT; - pop ecx; - retn; + _J_RET_VAL_TYPE(VAR_TYPE_INT); +// retn; } } void __declspec(naked) op_set_self() { __asm { - push ecx; + mov esi, ecx; mov ecx, eax; _GET_ARG_INT(end); mov edx, eax; // object call SetSelfObject; // ecx - script end: - pop ecx; + mov ecx, esi; retn; } } diff --git a/sfall/Modules/Scripting/Handlers/Graphics.cpp b/sfall/Modules/Scripting/Handlers/Graphics.cpp index 6f9a6c76..b9376dbe 100644 --- a/sfall/Modules/Scripting/Handlers/Graphics.cpp +++ b/sfall/Modules/Scripting/Handlers/Graphics.cpp @@ -32,13 +32,11 @@ namespace script void __declspec(naked) op_graphics_funcs_available() { __asm { - push ecx; cmp Graphics::mode, 3; seta dl; and edx, 0xFF; - _RET_VAL_INT; - pop ecx; - retn; + _J_RET_VAL_TYPE(VAR_TYPE_INT); +// retn; } } @@ -83,36 +81,36 @@ result: void __declspec(naked) op_free_shader() { __asm { - push ecx; + mov esi, ecx; _GET_ARG_INT(end); push eax; call FreeShader; end: - pop ecx; + mov ecx, esi; retn; } } void __declspec(naked) op_activate_shader() { __asm { - push ecx; + mov esi, ecx; _GET_ARG_INT(end); push eax; call ActivateShader; end: - pop ecx; + mov ecx, esi; retn; } } void __declspec(naked) op_deactivate_shader() { __asm { - push ecx; + mov esi, ecx; _GET_ARG_INT(end); push eax; call DeactivateShader; end: - pop ecx; + mov ecx, esi; retn; } } @@ -405,12 +403,12 @@ end: void __declspec(naked) op_get_shader_version() { __asm { - push ecx; + mov esi, ecx; call GetShaderVersion; mov edx, eax; mov eax, ebx; _RET_VAL_INT; - pop ecx; + mov ecx, esi; retn; } } @@ -452,12 +450,12 @@ end: void __declspec(naked) op_force_graphics_refresh() { __asm { - push ecx; + mov esi, ecx; _GET_ARG_INT(end); push eax; call ForceGraphicsRefresh; end: - pop ecx; + mov ecx, esi; retn; } } diff --git a/sfall/Modules/Scripting/Handlers/Interface.cpp b/sfall/Modules/Scripting/Handlers/Interface.cpp index 322dbb00..954ec23f 100644 --- a/sfall/Modules/Scripting/Handlers/Interface.cpp +++ b/sfall/Modules/Scripting/Handlers/Interface.cpp @@ -34,11 +34,9 @@ namespace script void __declspec(naked) op_input_funcs_available() { __asm { - push ecx; mov edx, 1; // They're always available from 2.9 on - _RET_VAL_INT; - pop ecx; - retn; + _J_RET_VAL_TYPE(VAR_TYPE_INT); +// retn; } } @@ -48,7 +46,7 @@ void sf_key_pressed(OpcodeContext& ctx) { void __declspec(naked) op_tap_key() { __asm { - push ecx; + mov esi, ecx; _GET_ARG_INT(end); test eax, eax; jl end; @@ -57,30 +55,26 @@ void __declspec(naked) op_tap_key() { push eax; call TapKey; end: - pop ecx; + mov ecx, esi; retn; } } void __declspec(naked) op_get_mouse_x() { __asm { - push ecx; mov edx, ds:[FO_VAR_mouse_x_]; add edx, ds:[FO_VAR_mouse_hotx]; - _RET_VAL_INT; - pop ecx; - retn; + _J_RET_VAL_TYPE(VAR_TYPE_INT); +// retn; } } void __declspec(naked) op_get_mouse_y() { __asm { - push ecx; mov edx, ds:[FO_VAR_mouse_y_]; add edx, ds:[FO_VAR_mouse_hoty]; - _RET_VAL_INT; - pop ecx; - retn; + _J_RET_VAL_TYPE(VAR_TYPE_INT); +// retn; } } @@ -95,35 +89,29 @@ void sf_get_mouse_buttons(OpcodeContext& ctx) { void __declspec(naked) op_get_window_under_mouse() { __asm { - push ecx; mov edx, ds:[FO_VAR_last_button_winID]; - _RET_VAL_INT; - pop ecx; - retn; + _J_RET_VAL_TYPE(VAR_TYPE_INT); +// retn; } } void __declspec(naked) op_get_screen_width() { __asm { - push ecx; mov edx, ds:[FO_VAR_scr_size + 8]; // _scr_size.offx sub edx, ds:[FO_VAR_scr_size]; // _scr_size.x inc edx; - _RET_VAL_INT; - pop ecx; - retn; + _J_RET_VAL_TYPE(VAR_TYPE_INT); +// retn; } } void __declspec(naked) op_get_screen_height() { __asm { - push ecx; mov edx, ds:[FO_VAR_scr_size + 12]; // _scr_size.offy sub edx, ds:[FO_VAR_scr_size + 4]; // _scr_size.y inc edx; - _RET_VAL_INT; - pop ecx; - retn; + _J_RET_VAL_TYPE(VAR_TYPE_INT); +// retn; } } @@ -151,42 +139,34 @@ void sf_create_message_window(OpcodeContext &ctx) { void __declspec(naked) op_get_viewport_x() { __asm { - push ecx; mov edx, ds:[FO_VAR_wmWorldOffsetX]; - _RET_VAL_INT; - pop ecx; - retn; + _J_RET_VAL_TYPE(VAR_TYPE_INT); +// retn; } } void __declspec(naked) op_get_viewport_y() { __asm { - push ecx; mov edx, ds:[FO_VAR_wmWorldOffsetY]; - _RET_VAL_INT; - pop ecx; - retn; + _J_RET_VAL_TYPE(VAR_TYPE_INT); +// retn; } } void __declspec(naked) op_set_viewport_x() { __asm { - push ecx; _GET_ARG_INT(end); mov ds:[FO_VAR_wmWorldOffsetX], eax; end: - pop ecx; retn; } } void __declspec(naked) op_set_viewport_y() { __asm { - push ecx; _GET_ARG_INT(end); mov ds:[FO_VAR_wmWorldOffsetY], eax; end: - pop ecx; retn; } } diff --git a/sfall/Modules/Scripting/Handlers/Memory.cpp b/sfall/Modules/Scripting/Handlers/Memory.cpp index 6b4d8e9a..28f135c8 100644 --- a/sfall/Modules/Scripting/Handlers/Memory.cpp +++ b/sfall/Modules/Scripting/Handlers/Memory.cpp @@ -27,8 +27,8 @@ namespace sfall namespace script { -#define START_VALID_ADDR 0x410000 -#define END_VALID_ADDR 0x6B403F +#define START_VALID_ADDR 0x410000 +#define END_VALID_ADDR 0x6B403F void __declspec(naked) op_read_byte() { __asm { @@ -250,10 +250,10 @@ static void __fastcall CallOffsetInternal(fo::Program* script, DWORD func) { void __declspec(naked) op_call_offset() { __asm { - push ecx; + mov esi, ecx; mov ecx, eax; call CallOffsetInternal; // edx - func - pop ecx; + mov ecx, esi; retn; } } diff --git a/sfall/Modules/Scripting/Handlers/Misc.cpp b/sfall/Modules/Scripting/Handlers/Misc.cpp index a2b9cbae..aef4a851 100644 --- a/sfall/Modules/Scripting/Handlers/Misc.cpp +++ b/sfall/Modules/Scripting/Handlers/Misc.cpp @@ -86,20 +86,19 @@ void sf_get_year(OpcodeContext& ctx) { void __declspec(naked) op_game_loaded() { __asm { - push ecx; + mov esi, ecx; push eax; // script call ScriptHasLoaded; movzx edx, al; mov eax, ebx; _RET_VAL_INT; - pop ecx; + mov ecx, esi; retn; } } void __declspec(naked) op_set_pipboy_available() { __asm { - push ecx; _GET_ARG_INT(end); cmp eax, 0; jl end; @@ -107,12 +106,10 @@ void __declspec(naked) op_set_pipboy_available() { jg end; mov byte ptr ds:[FO_VAR_gmovie_played_list][0x3], al; end: - pop ecx; retn; } } - // Kill counters void __declspec(naked) op_get_kill_counter() { __asm { @@ -131,7 +128,6 @@ void __declspec(naked) op_get_kill_counter() { mov edx, ds:[FO_VAR_pc_kill_counts+eax*4]; jmp end; fail: - xor edx, edx; end: mov eax, ecx @@ -287,11 +283,9 @@ end: void __declspec(naked) op_active_hand() { __asm { - push ecx; mov edx, dword ptr ds:[FO_VAR_itemCurrentItem]; - _RET_VAL_INT; - pop ecx; - retn; + _J_RET_VAL_TYPE(VAR_TYPE_INT); +// retn; } } @@ -304,11 +298,9 @@ void __declspec(naked) op_toggle_active_hand() { void __declspec(naked) op_eax_available() { __asm { - push ecx; xor edx, edx - _RET_VAL_INT; - pop ecx; - retn; + _J_RET_VAL_TYPE(VAR_TYPE_INT); +// retn; } } @@ -480,36 +472,34 @@ void sf_get_ini_string(OpcodeContext& ctx) { void __declspec(naked) op_get_uptime() { __asm { - push ecx; + mov esi, ecx; call GetTickCount; mov edx, eax; mov eax, ebx; _RET_VAL_INT; - pop ecx; + mov ecx, esi; retn; } } void __declspec(naked) op_set_car_current_town() { __asm { - push ecx; _GET_ARG_INT(end); mov ds:[FO_VAR_carCurrentArea], eax; end: - pop ecx; retn; } } void __declspec(naked) op_set_hp_per_level_mod() { __asm { - push ecx; + mov esi, ecx; _GET_ARG_INT(end); push eax; // allowed -/+127 push 0x4AFBC1; call SafeWrite8; end: - pop ecx; + mov ecx, esi; retn; } } @@ -620,43 +610,35 @@ void sf_reset_critical_table(OpcodeContext& ctx) { void __declspec(naked) op_set_unspent_ap_bonus() { __asm { - push ecx; _GET_ARG_INT(end); mov Stats::standardApAcBonus, eax; end: - pop ecx; retn; } } void __declspec(naked) op_get_unspent_ap_bonus() { __asm { - push ecx; mov edx, Stats::standardApAcBonus; - _RET_VAL_INT; - pop ecx; - retn; + _J_RET_VAL_TYPE(VAR_TYPE_INT); +// retn; } } void __declspec(naked) op_set_unspent_ap_perk_bonus() { __asm { - push ecx; _GET_ARG_INT(end); mov Stats::extraApAcBonus, eax; end: - pop ecx; retn; } } void __declspec(naked) op_get_unspent_ap_perk_bonus() { __asm { - push ecx; mov edx, Stats::extraApAcBonus; - _RET_VAL_INT; - pop ecx; - retn; + _J_RET_VAL_TYPE(VAR_TYPE_INT); +// retn; } } @@ -700,55 +682,53 @@ void __declspec(naked) op_nb_create_char() { void __declspec(naked) op_hero_select_win() { // for opening the appearance selection window __asm { - push ecx; + mov esi, ecx; _GET_ARG_INT(fail); push eax; call HeroSelectWindow; fail: - pop ecx; + mov ecx, esi; retn; } } void __declspec(naked) op_set_hero_style() { // for setting the hero style/appearance takes an 1 int __asm { - push ecx; + mov esi, ecx; _GET_ARG_INT(fail); push eax; call SetHeroStyle; fail: - pop ecx; + mov ecx, esi; retn; } } void __declspec(naked) op_set_hero_race() { // for setting the hero race takes an 1 int __asm { - push ecx; + mov esi, ecx; _GET_ARG_INT(fail); push eax; call SetHeroRace; fail: - pop ecx; + mov ecx, esi; retn; } } void __declspec(naked) op_get_light_level() { __asm { - push ecx; mov edx, ds:[FO_VAR_ambient_light]; - _RET_VAL_INT; - pop ecx; - retn; + _J_RET_VAL_TYPE(VAR_TYPE_INT); +// retn; } } void __declspec(naked) op_refresh_pc_art() { __asm { - push ecx; + mov esi, ecx; call RefreshPCArt; - pop ecx; + mov ecx, esi; retn; } } @@ -800,12 +780,12 @@ end: void __declspec(naked) op_stop_sfall_sound() { __asm { - push ecx; + mov esi, ecx; _GET_ARG_INT(end); push eax; call StopSfallSound; end: - pop ecx; + mov ecx, esi; retn; } } @@ -846,41 +826,38 @@ end: void __declspec(naked) op_modified_ini() { __asm { - push ecx; mov edx, modifiedIni; - _RET_VAL_INT; - pop ecx; - retn; + _J_RET_VAL_TYPE(VAR_TYPE_INT); +// retn; } } void __declspec(naked) op_force_aimed_shots() { __asm { - push ecx; + mov esi, ecx; _GET_ARG_INT(end); push eax; call ForceAimedShots; end: - pop ecx; + mov ecx, esi; retn; } } void __declspec(naked) op_disable_aimed_shots() { __asm { - push ecx; + mov esi, ecx; _GET_ARG_INT(end); push eax; call DisableAimedShots; end: - pop ecx; + mov ecx, esi; retn; } } void __declspec(naked) op_mark_movie_played() { __asm { - push ecx; _GET_ARG_INT(end); test eax, eax; jl end; @@ -888,7 +865,6 @@ void __declspec(naked) op_mark_movie_played() { jge end; mov byte ptr ds:[eax + FO_VAR_gmovie_played_list], 1; end: - pop ecx; retn; } } @@ -949,12 +925,12 @@ end: void __declspec(naked) op_block_combat() { __asm { - push ecx; + mov esi, ecx; _GET_ARG_INT(end); push eax; call AIBlockCombat; end: - pop ecx; + mov ecx, esi; retn; } } @@ -989,22 +965,20 @@ void __declspec(naked) op_tile_under_cursor() { void __declspec(naked) op_gdialog_get_barter_mod() { __asm { - push ecx; mov edx, dword ptr ds:[FO_VAR_gdBarterMod]; - _RET_VAL_INT; - pop ecx; - retn; + _J_RET_VAL_TYPE(VAR_TYPE_INT); +// retn; } } void __declspec(naked) op_set_inven_ap_cost() { __asm { - push ecx; + mov esi, ecx; _GET_ARG_INT(end); mov ecx, eax; call Inventory::SetInvenApCost; end: - pop ecx; + mov ecx, esi; retn; } } diff --git a/sfall/Modules/Scripting/Handlers/Perks.cpp b/sfall/Modules/Scripting/Handlers/Perks.cpp index f729f920..be436fad 100644 --- a/sfall/Modules/Scripting/Handlers/Perks.cpp +++ b/sfall/Modules/Scripting/Handlers/Perks.cpp @@ -30,36 +30,32 @@ namespace script void __declspec(naked) op_get_perk_owed() { __asm { - push ecx; movzx edx, byte ptr ds:[FO_VAR_free_perk]; - _RET_VAL_INT; - pop ecx; - retn; + _J_RET_VAL_TYPE(VAR_TYPE_INT); +// retn; } } void __declspec(naked) op_set_perk_owed() { __asm { - push ecx; _GET_ARG_INT(end); and eax, 0xFF; cmp eax, 250; jg end; mov byte ptr ds:[FO_VAR_free_perk], al; end: - pop ecx; retn; } } void __declspec(naked) op_set_perk_freq() { __asm { - push ecx; + mov esi, ecx; _GET_ARG_INT(end); push eax; call SetPerkFreq; end: - pop ecx; + mov ecx, esi; retn; } } @@ -248,27 +244,27 @@ end: void __declspec(naked) op_hide_real_perks() { __asm { - push ecx; + mov esi, ecx; call IgnoreDefaultPerks; - pop ecx; + mov ecx, esi; retn; } } void __declspec(naked) op_show_real_perks() { __asm { - push ecx; + mov esi, ecx; call RestoreDefaultPerks; - pop ecx; + mov ecx, esi; retn; } } void __declspec(naked) op_clear_selectable_perks() { __asm { - push ecx; + mov esi, ecx; call ClearSelectablePerks; - pop ecx; + mov ecx, esi; retn; } } @@ -305,71 +301,68 @@ void sf_has_fake_trait_npc(OpcodeContext& ctx) { void __declspec(naked) op_perk_add_mode() { __asm { - push ecx; + mov esi, ecx; _GET_ARG_INT(end); push eax; call AddPerkMode; end: - pop ecx; + mov ecx, esi; retn; } } void __declspec(naked) op_remove_trait() { __asm { - push ecx; _GET_ARG_INT(end); test eax, eax; jl end; mov edx, -1; cmp eax, ds:[FO_VAR_pc_trait]; jne next; - mov ecx, ds:[FO_VAR_pc_trait2]; - mov ds:[FO_VAR_pc_trait], ecx; + mov esi, ds:[FO_VAR_pc_trait2]; + mov ds:[FO_VAR_pc_trait], esi; mov ds:[FO_VAR_pc_trait2], edx; -end: - pop ecx; retn; next: cmp eax, ds:[FO_VAR_pc_trait2]; jne end; mov ds:[FO_VAR_pc_trait2], edx; - pop ecx; +end: retn; } } void __declspec(naked) op_set_pyromaniac_mod() { __asm { - push ecx; + mov esi, ecx; _GET_ARG_INT(end); push eax; push 0x424AB6; call SafeWrite8; end: - pop ecx; + mov ecx, esi; retn; } } void __declspec(naked) op_apply_heaveho_fix() { __asm { - push ecx; + mov esi, ecx; call ApplyHeaveHoFix; - pop ecx; + mov ecx, esi; retn; } } void __declspec(naked) op_set_swiftlearner_mod() { __asm { - push ecx; + mov esi, ecx; _GET_ARG_INT(end); push eax; push 0x4AFAE2; call SafeWrite32; end: - pop ecx; + mov ecx, esi; retn; } } @@ -398,12 +391,12 @@ static void __fastcall SetPerkLevelMod(long mod) { void __declspec(naked) op_set_perk_level_mod() { __asm { - push ecx; + mov esi, ecx; _GET_ARG_INT(end); mov ecx, eax; call SetPerkLevelMod; end: - pop ecx; + mov ecx, esi; retn; } } diff --git a/sfall/Modules/Scripting/Handlers/Stats.cpp b/sfall/Modules/Scripting/Handlers/Stats.cpp index 12b251a9..e4c618cd 100644 --- a/sfall/Modules/Scripting/Handlers/Stats.cpp +++ b/sfall/Modules/Scripting/Handlers/Stats.cpp @@ -236,30 +236,28 @@ end: void __declspec(naked) op_set_available_skill_points() { __asm { - push ecx; + mov esi, ecx; _GET_ARG_INT(end); mov edx, eax; xor eax, eax; call fo::funcoffs::stat_pc_set_; end: - pop ecx; + mov ecx, esi; retn; } } void __declspec(naked) op_get_available_skill_points() { __asm { - push ecx; mov edx, dword ptr ds:[FO_VAR_curr_pc_stat]; - _RET_VAL_INT; - pop ecx; - retn; + _J_RET_VAL_TYPE(VAR_TYPE_INT); +// retn; } } void __declspec(naked) op_mod_skill_points_per_level() { __asm { - push ecx; + mov esi, ecx; _GET_ARG_INT(end); mov ecx, 100; cmp eax, ecx; @@ -272,7 +270,7 @@ void __declspec(naked) op_mod_skill_points_per_level() { push 0x43C27A; call SafeWrite8; end: - pop ecx; + mov ecx, esi; retn; } } @@ -356,7 +354,7 @@ end: void __declspec(naked) op_set_pickpocket_max() { __asm { - push ecx; + mov esi, ecx; _GET_ARG_INT(end); mov ecx, 100; cmp eax, ecx; @@ -366,14 +364,14 @@ void __declspec(naked) op_set_pickpocket_max() { push 0xFFFFFFFF; call SetPickpocketMax; end: - pop ecx; + mov ecx, esi; retn; } } void __declspec(naked) op_set_hit_chance_max() { __asm { - push ecx; + mov esi, ecx; _GET_ARG_INT(end); mov ecx, 100; cmp eax, ecx; @@ -383,7 +381,7 @@ void __declspec(naked) op_set_hit_chance_max() { push 0xFFFFFFFF; call SetHitChanceMax; end: - pop ecx; + mov ecx, esi; retn; } } @@ -578,20 +576,20 @@ end: void __declspec(naked) op_set_base_skill_mod() { // same as set_skill_max __asm { - push ecx; + mov esi, ecx; _GET_ARG_INT(end); push eax; push 0xFFFFFFFF; call SetSkillMax; end: - pop ecx; + mov ecx, esi; retn; } } void __declspec(naked) op_set_skill_max() { __asm { - push ecx; + mov esi, ecx; _GET_ARG_INT(end); mov ecx, 300; cmp eax, ecx; @@ -600,7 +598,7 @@ void __declspec(naked) op_set_skill_max() { push 0xFFFFFFFF; call SetSkillMax; end: - pop ecx; + mov ecx, esi; retn; } } diff --git a/sfall/Modules/Scripting/Handlers/Utils.cpp b/sfall/Modules/Scripting/Handlers/Utils.cpp index e551bf9e..6d56ee8a 100644 --- a/sfall/Modules/Scripting/Handlers/Utils.cpp +++ b/sfall/Modules/Scripting/Handlers/Utils.cpp @@ -190,7 +190,7 @@ void sf_string_split(OpcodeContext& ctx) { ctx.setReturn(StringSplit(ctx.arg(0).strValue(), ctx.arg(1).strValue())); } -char* Substring(const char* str, int startPos, int length) { +static char* SubString(const char* str, int startPos, int length) { int len = strlen(str); if (startPos < 0) { @@ -218,7 +218,7 @@ char* Substring(const char* str, int startPos, int length) { void sf_substr(OpcodeContext& ctx) { const char* str = ctx.arg(0).strValue(); - if (*str != '\0') str = Substring(str, ctx.arg(1).rawValue(), ctx.arg(2).rawValue()); + if (*str != '\0') str = SubString(str, ctx.arg(1).rawValue(), ctx.arg(2).rawValue()); ctx.setReturn(str); } @@ -233,7 +233,7 @@ void sf_string_compare(OpcodeContext& ctx) { } // A safer version of sprintf for using in user scripts. -static char* _stdcall sprintf_lite(const char* format, ScriptValue value) { +static char* sprintf_lite(const char* format, ScriptValue value) { int fmtlen = strlen(format); int buflen = fmtlen + 1; for (int i = 0; i < fmtlen; i++) { diff --git a/sfall/Modules/Scripting/Handlers/Utils.h b/sfall/Modules/Scripting/Handlers/Utils.h index f82b427b..100606b7 100644 --- a/sfall/Modules/Scripting/Handlers/Utils.h +++ b/sfall/Modules/Scripting/Handlers/Utils.h @@ -23,8 +23,6 @@ namespace sfall namespace script { -char* Substring(const char* str, int startPos, int length); - class OpcodeContext; void sf_sqrt(OpcodeContext&); diff --git a/sfall/Modules/Scripting/Handlers/Worldmap.cpp b/sfall/Modules/Scripting/Handlers/Worldmap.cpp index 12860734..6d4089ce 100644 --- a/sfall/Modules/Scripting/Handlers/Worldmap.cpp +++ b/sfall/Modules/Scripting/Handlers/Worldmap.cpp @@ -88,45 +88,41 @@ void sf_force_encounter(OpcodeContext& cxt) { // world_map_functions void __declspec(naked) op_in_world_map() { __asm { - push ecx; + mov esi, ecx; call InWorldMap; mov edx, eax; mov eax, ebx; _RET_VAL_INT; - pop ecx; + mov ecx, esi; retn; } } void __declspec(naked) op_get_game_mode() { __asm { - push ecx; + mov esi, ecx; call GetLoopFlags; mov edx, eax; mov eax, ebx; _RET_VAL_INT; - pop ecx; + mov ecx, esi; retn; } } void __declspec(naked) op_get_world_map_x_pos() { __asm { - push ecx; mov edx, ds:[FO_VAR_world_xpos]; - _RET_VAL_INT; - pop ecx; - retn; + _J_RET_VAL_TYPE(VAR_TYPE_INT); +// retn; } } void __declspec(naked) op_get_world_map_y_pos() { __asm { - push ecx; mov edx, ds:[FO_VAR_world_ypos]; - _RET_VAL_INT; - pop ecx; - retn; + _J_RET_VAL_TYPE(VAR_TYPE_INT); +// retn; } } diff --git a/sfall/Modules/Scripting/OpcodeContext.cpp b/sfall/Modules/Scripting/OpcodeContext.cpp index cce8bc2c..4b67ca14 100644 --- a/sfall/Modules/Scripting/OpcodeContext.cpp +++ b/sfall/Modules/Scripting/OpcodeContext.cpp @@ -119,22 +119,36 @@ bool OpcodeContext::validateArguments(const OpcodeArgumentType argTypes[], const // exception is when type set to if (actualType == DataType::NONE) break; auto argType = argTypes[i]; - if (argType == ARG_ANY) continue; - if ((argType == ARG_INT || argType == ARG_OBJECT) && !(actualType == DataType::INT)) { - printOpcodeError("%s() - argument #%d is not an integer.", opcodeName, ++i); - return false; - } else if (argType == ARG_NUMBER && !(actualType == DataType::INT || actualType == DataType::FLOAT)) { - printOpcodeError("%s() - argument #%d is not a number.", opcodeName, ++i); - return false; - } else if (argType == ARG_STRING && !(actualType == DataType::STR)) { - printOpcodeError("%s() - argument #%d is not a string.", opcodeName, ++i); - return false; - } else if (argType == ARG_OBJECT && arg(i).rawValue() == 0) { - printOpcodeError("%s() - argument #%d is null.", opcodeName, ++i); - return false; - } else if (argType == ARG_INTSTR && !(actualType == DataType::INT || actualType == DataType::STR)) { - printOpcodeError("%s() - argument #%d is not an integer or a string.", opcodeName, ++i); - return false; + switch (argType) { + case ARG_ANY: + continue; + case ARG_INT: + case ARG_OBJECT: + if (actualType != DataType::INT) { + printOpcodeError("%s() - argument #%d is not an integer.", opcodeName, ++i); + return false; + } else if (argType == ARG_OBJECT && arg(i).rawValue() == 0) { + printOpcodeError("%s() - argument #%d is null.", opcodeName, ++i); + return false; + } + break; + case ARG_STRING: + if (actualType != DataType::STR) { + printOpcodeError("%s() - argument #%d is not a string.", opcodeName, ++i); + return false; + } + break; + case ARG_INTSTR: + if (actualType != DataType::STR && actualType != DataType::INT) { + printOpcodeError("%s() - argument #%d is not an integer or a string.", opcodeName, ++i); + return false; + } + break; + case ARG_NUMBER: + if (actualType != DataType::FLOAT && actualType != DataType::INT) { + printOpcodeError("%s() - argument #%d is not a number.", opcodeName, ++i); + return false; + } } } return true; diff --git a/sfall/Modules/Scripting/OpcodeContext.h b/sfall/Modules/Scripting/OpcodeContext.h index b981c300..cfc41824 100644 --- a/sfall/Modules/Scripting/OpcodeContext.h +++ b/sfall/Modules/Scripting/OpcodeContext.h @@ -39,10 +39,10 @@ typedef void(*ScriptingFunctionHandler)(OpcodeContext&); enum OpcodeArgumentType { ARG_ANY = 0, // no validation (default) ARG_INT, // integer only - ARG_NUMBER, // float OR integer - ARG_STRING, // string only ARG_OBJECT, // integer that is not 0 - ARG_INTSTR // integer OR string + ARG_STRING, // string only + ARG_INTSTR, // integer OR string + ARG_NUMBER, // float OR integer }; typedef struct SfallOpcodeInfo { diff --git a/sfall/Modules/Worldmap.cpp b/sfall/Modules/Worldmap.cpp index 8439c8ca..e311736f 100644 --- a/sfall/Modules/Worldmap.cpp +++ b/sfall/Modules/Worldmap.cpp @@ -434,16 +434,20 @@ void StartingStatePatches() { SafeWrite32(0x4A336C, date); dlogr(" Done", DL_INIT); } - date = GetConfigInt("Misc", "StartMonth", -1); - if (date >= 0) { - if (date > 11) date = 11; + int month = GetConfigInt("Misc", "StartMonth", -1); + if (month >= 0) { + if (month > 11) month = 11; dlog("Applying starting month patch.", DL_INIT); - SafeWrite32(0x4A3382, date); + SafeWrite32(0x4A3382, month); dlogr(" Done", DL_INIT); } date = GetConfigInt("Misc", "StartDay", -1); if (date >= 0) { - if (date > 30) date = 30; + if (month == 1 && date > 28) { // for February + date = 28; // set 29th day + } else if (date > 30) { + date = 30; // set 31st day + } dlog("Applying starting day patch.", DL_INIT); SafeWrite8(0x4A3356, static_cast(date)); dlogr(" Done", DL_INIT); From 80cf1a9ca7823d8c86c3cee04c0524e132785b77 Mon Sep 17 00:00:00 2001 From: NovaRain Date: Wed, 4 Dec 2019 12:57:16 +0800 Subject: [PATCH 20/36] Fix for printing game messages to debug log --- sfall/FalloutEngine/VariableOffsets.h | 1 + sfall/Modules/DebugEditor.cpp | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/sfall/FalloutEngine/VariableOffsets.h b/sfall/FalloutEngine/VariableOffsets.h index 3cd61264..7556878f 100644 --- a/sfall/FalloutEngine/VariableOffsets.h +++ b/sfall/FalloutEngine/VariableOffsets.h @@ -46,6 +46,7 @@ #define FO_VAR_curr_stack 0x59E96C #define FO_VAR_currentWindow 0x51DCB8 #define FO_VAR_cursor_line 0x664514 +#define FO_VAR_debug_func 0x51DF04 #define FO_VAR_dialogue_head 0x518850 #define FO_VAR_dialogue_state 0x518714 #define FO_VAR_dialogue_switch_mode 0x518718 diff --git a/sfall/Modules/DebugEditor.cpp b/sfall/Modules/DebugEditor.cpp index d3a69d30..02e1c8a6 100644 --- a/sfall/Modules/DebugEditor.cpp +++ b/sfall/Modules/DebugEditor.cpp @@ -341,6 +341,16 @@ static void __declspec(naked) win_debug_hook() { } } +static void __declspec(naked) op_display_msg_hook() { + __asm { + cmp dword ptr ds:FO_VAR_debug_func, 0; + jne debug; + retn; +debug: + jmp fo::funcoffs::config_get_value_; + } +} + static void DebugModePatch() { DWORD dbgMode = iniGetInt("Debugging", "DebugMode", 0, ::sfall::ddrawIni); if (dbgMode) { @@ -370,8 +380,14 @@ static void DebugModePatch() { // prints a debug message about missing art file for critters to both debug.log and the message window HookCall(0x419B65, art_data_size_hook); + // replace calling debug_printf_ with _debug_func (to prevent a crash if there is a '%' character in the printed message) + long long data = 0x51DF0415FFF08990; // mov eax, esi; call ds:_debug_func + SafeWriteBytes(0x455419, (BYTE*)&data, 8); // op_display_msg_ + dlogr(" Done", DL_INIT); } + // Just for speeding up the process + HookCall(0x455404, op_display_msg_hook); } static void DontDeleteProtosPatch() { From a5bcfa8a6b8bcadb9f4fcb5425d7ae01a361c4c2 Mon Sep 17 00:00:00 2001 From: NovaRain Date: Thu, 5 Dec 2019 09:38:37 +0800 Subject: [PATCH 21/36] Re-fixed the previous commit --- sfall/Modules/DebugEditor.cpp | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/sfall/Modules/DebugEditor.cpp b/sfall/Modules/DebugEditor.cpp index 02e1c8a6..25ae9009 100644 --- a/sfall/Modules/DebugEditor.cpp +++ b/sfall/Modules/DebugEditor.cpp @@ -341,6 +341,17 @@ static void __declspec(naked) win_debug_hook() { } } +static void __declspec(naked) debug_log_hack() { + __asm { + push eax; // text + push 0x5016EC; // fmt '%s' + push ebx; // log file + call fo::funcoffs::fprintf_; + add esp, 12; + retn; + } +} + static void __declspec(naked) op_display_msg_hook() { __asm { cmp dword ptr ds:FO_VAR_debug_func, 0; @@ -352,8 +363,8 @@ debug: } static void DebugModePatch() { - DWORD dbgMode = iniGetInt("Debugging", "DebugMode", 0, ::sfall::ddrawIni); - if (dbgMode) { + int dbgMode = iniGetInt("Debugging", "DebugMode", 0, ::sfall::ddrawIni); + if (dbgMode > 0) { dlog("Applying debugmode patch.", DL_INIT); // If the player is using an exe with the debug patch already applied, just skip this block without erroring if (*((DWORD*)0x444A64) != 0x082327E8) { @@ -380,13 +391,18 @@ static void DebugModePatch() { // prints a debug message about missing art file for critters to both debug.log and the message window HookCall(0x419B65, art_data_size_hook); - // replace calling debug_printf_ with _debug_func (to prevent a crash if there is a '%' character in the printed message) + // Fix crash when there is a '%' character in the printed message + if (dbgMode > 1) { + MakeCall(0x4C703F, debug_log_hack); + BlockCall(0x4C7044); // just nop code + } + // replace calling debug_printf_ with _debug_func long long data = 0x51DF0415FFF08990; // mov eax, esi; call ds:_debug_func SafeWriteBytes(0x455419, (BYTE*)&data, 8); // op_display_msg_ dlogr(" Done", DL_INIT); } - // Just for speeding up the process + // Just for speeding up display_msg function (optional) HookCall(0x455404, op_display_msg_hook); } From b149aaa00c990506514efbea7801b2739705d3e6 Mon Sep 17 00:00:00 2001 From: NovaRain Date: Sat, 7 Dec 2019 09:41:06 +0800 Subject: [PATCH 22/36] Changed the behavior of IniConfigFolder option --- artifacts/ddraw.ini | 5 +- sfall/Modules/Scripting/Handlers/Misc.cpp | 78 +++++++++++++---------- 2 files changed, 47 insertions(+), 36 deletions(-) diff --git a/artifacts/ddraw.ini b/artifacts/ddraw.ini index 30366a6e..f83d7498 100644 --- a/artifacts/ddraw.ini +++ b/artifacts/ddraw.ini @@ -738,8 +738,9 @@ CreditsAtBottom=0 ;Paths outside of scripts folder are supported GlobalScriptPaths=scripts\gl*.int,scripts\sfall\gl*.int -;Uncomment the option to specify a common folder path for all ini files used by scripts -;You will have to put all the available ini files of the mods in this directory +;Uncomment the option to specify an additional folder path for ini files used by scripts +;The game will search for the ini files relative to this directory +;If the file is not found, the search will proceed relative to the root directory of the game ;The maximum length of the specified path is 61 characters ;IniConfigFolder=mods\iniConfigs diff --git a/sfall/Modules/Scripting/Handlers/Misc.cpp b/sfall/Modules/Scripting/Handlers/Misc.cpp index aef4a851..0513c215 100644 --- a/sfall/Modules/Scripting/Handlers/Misc.cpp +++ b/sfall/Modules/Scripting/Handlers/Misc.cpp @@ -402,6 +402,14 @@ void sf_get_npc_level(OpcodeContext& ctx) { ctx.setReturn(level); } +static bool IsSpecialIni(const char* str, const char* end) { + const char* pos = strfind(str, &::sfall::ddrawIni[2]); + if (pos && pos < end) return true; + pos = strfind(str, "f2_res.ini"); + if (pos && pos < end) return true; + return false; +} + static int ParseIniSetting(const char* iniString, const char* &key, char section[], char file[]) { key = strstr(iniString, "|"); if (!key) return -1; @@ -416,28 +424,18 @@ static int ParseIniSetting(const char* iniString, const char* &key, char section DWORD seclen = (DWORD)key - ((DWORD)iniString + filelen + 1); if (seclen > 32) return -1; + long startAt = 2; file[0] = '.'; file[1] = '\\'; - if (!ScriptExtender::iniConfigFolder.empty()) { - const char* pos = strfind(iniString, &::sfall::ddrawIni[2]); - if (pos && pos < fileEnd) goto specialIni; - pos = strfind(iniString, "f2_res.ini"); - if (pos && pos < fileEnd) goto specialIni; + + if (!ScriptExtender::iniConfigFolder.empty() && !IsSpecialIni(iniString, fileEnd)) { size_t len = ScriptExtender::iniConfigFolder.length(); // limit up to 62 characters memcpy(&file[2], ScriptExtender::iniConfigFolder.c_str(), len); - int n = 0; // position of the beginning of the file name - for (int i = filelen - 4; i > 0; i--) { - if (iniString[i] == '\\' || iniString[i] == '/') { - n = i + 1; - break; - } - } - strncpy_s(&file[2 + len], (128 - 2) - len, &iniString[n], filelen - n); // copy filename - } else { -specialIni: - memcpy(&file[2], iniString, filelen); - file[filelen + 2] = 0; + DWORD attr = GetFileAttributesA(file); + if (!(attr & FILE_ATTRIBUTE_DIRECTORY) /*&& attr != INVALID_FILE_ATTRIBUTES*/) startAt += len + 1; } + memcpy(&file[startAt], iniString, filelen); + file[startAt + filelen] = 0; memcpy(section, &iniString[filelen + 1], seclen); section[seclen] = 0; @@ -1035,21 +1033,31 @@ void sf_set_ini_setting(OpcodeContext& ctx) { ctx.setReturn(-1); } -static std::string GetIniFilePath(const ScriptValue& arg) { +static std::string GetIniFilePath(const ScriptValue &arg) { std::string fileName(".\\"); if (ScriptExtender::iniConfigFolder.empty()) { fileName += arg.strValue(); } else { fileName += ScriptExtender::iniConfigFolder; - std::string name(arg.strValue()); - int pos = name.find_last_of("\\/"); - fileName += (pos > 0) ? name.substr(pos + 1) : name; + fileName += arg.strValue(); + DWORD attr = GetFileAttributesA(fileName.c_str()); + if ((attr & FILE_ATTRIBUTE_DIRECTORY) /*|| attr == INVALID_FILE_ATTRIBUTES*/) { + auto str = arg.strValue(); + for (size_t i = 2; ; i++, str++) { + //if (*str == '.') str += (str[1] == '.') ? 3 : 2; // skip '.\' or '..\' + fileName[i] = *str; + if (!*str) break; + } + } } return fileName; } void sf_get_ini_sections(OpcodeContext& ctx) { - GetPrivateProfileSectionNamesA(ScriptExtender::gTextBuffer, ScriptExtender::TextBufferSize(), GetIniFilePath(ctx.arg(0)).data()); + if (!GetPrivateProfileSectionNamesA(ScriptExtender::gTextBuffer, ScriptExtender::TextBufferSize(), GetIniFilePath(ctx.arg(0)).c_str())) { + ctx.setReturn(TempArray(0, 0)); + return; + } std::vector sections; char* section = ScriptExtender::gTextBuffer; while (*section != 0) { @@ -1070,21 +1078,23 @@ void sf_get_ini_sections(OpcodeContext& ctx) { void sf_get_ini_section(OpcodeContext& ctx) { auto section = ctx.arg(1).strValue(); - GetPrivateProfileSectionA(section, ScriptExtender::gTextBuffer, ScriptExtender::TextBufferSize(), GetIniFilePath(ctx.arg(0)).data()); int arrayId = TempArray(-1, 0); // associative - auto& arr = arrays[arrayId]; - char *key = ScriptExtender::gTextBuffer, *val = nullptr; - while (*key != 0) { - char* val = std::strpbrk(key, "="); - if (val != nullptr) { - *val = '\0'; - val += 1; - SetArray(arrayId, ScriptValue(key), ScriptValue(val), false); + if (GetPrivateProfileSectionA(section, ScriptExtender::gTextBuffer, ScriptExtender::TextBufferSize(), GetIniFilePath(ctx.arg(0)).c_str())) { + auto& arr = arrays[arrayId]; + char *key = ScriptExtender::gTextBuffer, *val = nullptr; + while (*key != 0) { + char* val = std::strpbrk(key, "="); + if (val != nullptr) { + *val = '\0'; + val += 1; - key = val + std::strlen(val) + 1; - } else { - key += std::strlen(key) + 1; + SetArray(arrayId, ScriptValue(key), ScriptValue(val), false); + + key = val + std::strlen(val) + 1; + } else { + key += std::strlen(key) + 1; + } } } ctx.setReturn(arrayId); From b539cab67d3e50655ae83590ee56cca777c5062f Mon Sep 17 00:00:00 2001 From: NovaRain Date: Sat, 7 Dec 2019 09:54:40 +0800 Subject: [PATCH 23/36] Change container from Map to Array for sfall opcodes --- sfall/Modules/DebugEditor.cpp | 2 +- sfall/Modules/MiscPatches.cpp | 2 +- sfall/Modules/Scripting/Arrays.cpp | 17 ++++++----- sfall/Modules/Scripting/OpcodeContext.cpp | 3 +- sfall/Modules/Scripting/Opcodes.cpp | 37 +++++++++++------------ 5 files changed, 30 insertions(+), 31 deletions(-) diff --git a/sfall/Modules/DebugEditor.cpp b/sfall/Modules/DebugEditor.cpp index 25ae9009..33083ef1 100644 --- a/sfall/Modules/DebugEditor.cpp +++ b/sfall/Modules/DebugEditor.cpp @@ -391,7 +391,7 @@ static void DebugModePatch() { // prints a debug message about missing art file for critters to both debug.log and the message window HookCall(0x419B65, art_data_size_hook); - // Fix crash when there is a '%' character in the printed message + // Fix to prevent crashes when there is a '%' character in the printed message if (dbgMode > 1) { MakeCall(0x4C703F, debug_log_hack); BlockCall(0x4C7044); // just nop code diff --git a/sfall/Modules/MiscPatches.cpp b/sfall/Modules/MiscPatches.cpp index 52d57f7a..e1fcf0a9 100644 --- a/sfall/Modules/MiscPatches.cpp +++ b/sfall/Modules/MiscPatches.cpp @@ -569,7 +569,7 @@ void SkipLoadingGameSettingsPatch() { } void InterfaceDontMoveOnTopPatch() { - if (GetConfigInt("Misc", "InterfaceDontMoveOnTop", 0)) { + if (GetConfigInt("Misc", "InterfaceDontMoveOnTop", 0)) { // TODO: remove option? (obsolete) dlog("Applying InterfaceDontMoveOnTop patch.", DL_INIT); SafeWrite8(0x46ECE9, fo::WinFlags::Exclusive); // Player Inventory/Loot/UseOn SafeWrite8(0x41B966, fo::WinFlags::Exclusive); // Automap diff --git a/sfall/Modules/Scripting/Arrays.cpp b/sfall/Modules/Scripting/Arrays.cpp index 67897087..31b67611 100644 --- a/sfall/Modules/Scripting/Arrays.cpp +++ b/sfall/Modules/Scripting/Arrays.cpp @@ -19,12 +19,13 @@ DWORD arraysBehavior = 1; // 0 - backward compatible with pre-3.4, 1 - permanent // arrays map: arrayId => arrayVar ArraysMap arrays; -// auto-incremented ID -DWORD nextArrayID = 1; // temp arrays: set of arrayId -std::unordered_set tempArrays; +std::unordered_set temporaryArrays; // saved arrays: arrayKey => arrayId ArrayKeysMap savedArrays; + +// auto-incremented ID +DWORD nextArrayID = 1; // special array ID for array expressions DWORD stackArrayId; @@ -405,7 +406,7 @@ DWORD CreateArray(int len, DWORD flags) { DWORD TempArray(DWORD len, DWORD flags) { DWORD id = CreateArray(len, flags); - tempArrays.insert(id); + temporaryArrays.insert(id); return id; } @@ -419,11 +420,11 @@ void FreeArray(DWORD id) { } void DeleteAllTempArrays() { - if (!tempArrays.empty()) { - for (std::unordered_set::iterator it = tempArrays.begin(); it != tempArrays.end(); ++it) { + if (!temporaryArrays.empty()) { + for (std::unordered_set::iterator it = temporaryArrays.begin(); it != temporaryArrays.end(); ++it) { FreeArray(*it); } - tempArrays.clear(); + temporaryArrays.clear(); } } @@ -642,7 +643,7 @@ long ResizeArray(DWORD id, int newlen) { } void FixArray(DWORD id) { - tempArrays.erase(id); + temporaryArrays.erase(id); } ScriptValue ScanArray(DWORD id, const ScriptValue& val) { diff --git a/sfall/Modules/Scripting/OpcodeContext.cpp b/sfall/Modules/Scripting/OpcodeContext.cpp index 4b67ca14..00d6c30d 100644 --- a/sfall/Modules/Scripting/OpcodeContext.cpp +++ b/sfall/Modules/Scripting/OpcodeContext.cpp @@ -127,7 +127,8 @@ bool OpcodeContext::validateArguments(const OpcodeArgumentType argTypes[], const if (actualType != DataType::INT) { printOpcodeError("%s() - argument #%d is not an integer.", opcodeName, ++i); return false; - } else if (argType == ARG_OBJECT && arg(i).rawValue() == 0) { + } + if (argType == ARG_OBJECT && arg(i).rawValue() == 0) { printOpcodeError("%s() - argument #%d is null.", opcodeName, ++i); return false; } diff --git a/sfall/Modules/Scripting/Opcodes.cpp b/sfall/Modules/Scripting/Opcodes.cpp index 9906386e..dc0fe738 100644 --- a/sfall/Modules/Scripting/Opcodes.cpp +++ b/sfall/Modules/Scripting/Opcodes.cpp @@ -51,8 +51,6 @@ static const short opcodeCount = 0x300; // Other half is filled by sfall here. static void* opcodes[opcodeCount]; -typedef std::unordered_map OpcodeInfoMapType; - // Opcode Table. Add additional (sfall) opcodes here. // Format: { // opcode number, @@ -221,16 +219,16 @@ static SfallOpcodeInfo opcodeInfoArray[] = { {0x27e, "reg_anim_callback", sf_reg_anim_callback, 1, false, 0, {ARG_INT}}, }; -// A hash-table for opcode info, indexed by opcode. +// An array for opcode info, indexed by opcode. // Initialized at run time from the array above. -OpcodeInfoMapType opcodeInfoMap; +std::array opcodeInfoTable; // Initializes the opcode info table. void InitOpcodeInfoTable() { int length = sizeof(opcodeInfoArray) / sizeof(opcodeInfoArray[0]); for (int i = 0; i < length; ++i) { - // key: opcode, value: reference to opcode element in the opcodeInfoArray array - opcodeInfoMap[opcodeInfoArray[i].opcode] = &opcodeInfoArray[i]; + // index: opcode, value: reference to opcode element in the opcodeInfoArray array + opcodeInfoTable[opcodeInfoArray[i].opcode - sfallOpcodeStart] = &opcodeInfoArray[i]; } } @@ -241,11 +239,10 @@ void __fastcall defaultOpcodeHandler(fo::Program* program, DWORD opcodeOffset) { __asm mov program, ebx; int opcode = opcodeOffset / 4; - auto iter = opcodeInfoMap.find(opcode); - if (iter != opcodeInfoMap.end()) { - auto info = iter->second; - OpcodeContext ctx(program, info); - ctx.handleOpcode(info->handler, info->argValidation); + auto opcodeInfo = opcodeInfoTable[opcode - sfallOpcodeStart]; + if (opcodeInfo != nullptr) { + OpcodeContext ctx(program, opcodeInfo); + ctx.handleOpcode(opcodeInfo->handler, opcodeInfo->argValidation); } else { fo::func::interpretError("Unknown opcode: %d", opcode); } @@ -260,6 +257,15 @@ void InitNewOpcodes() { SafeWrite32(0x46CE6C, (DWORD)opcodes); // call that actually jumps to the opcode SafeWrite32(0x46E390, (DWORD)opcodes); // mov that writes to the opcode + // see opcodeMetaArray above for additional scripting functions via "metarule" + + InitOpcodeInfoTable(); + InitMetaruleTable(); + + LoadGameHook::OnGameReset() += []() { + ForceEncounterRestore(); // restore if the encounter did not happen + }; + if (iniGetInt("Debugging", "AllowUnsafeScripting", 0, ::sfall::ddrawIni)) { dlogr(" Unsafe opcodes enabled.", DL_SCRIPT); opcodes[0x1cf] = op_write_byte; @@ -415,15 +421,6 @@ void InitNewOpcodes() { opcodes[i] = defaultOpcodeHandler; } } - - // see opcodeMetaArray above for additional scripting functions via "metarule" - - InitOpcodeInfoTable(); - InitMetaruleTable(); - - LoadGameHook::OnGameReset() += []() { - ForceEncounterRestore(); // restore if the encounter did not happen - }; } } From ddf0ec70852b5ae542adf9f118dba1934221cd7b Mon Sep 17 00:00:00 2001 From: NovaRain Date: Sun, 8 Dec 2019 06:40:51 +0800 Subject: [PATCH 24/36] Added missing code for commit b149aaa Some edits to script related namesapce in Enums.h. --- sfall/FalloutEngine/Enums.h | 23 ++++++++++++----------- sfall/FalloutEngine/Functions_def.h | 2 +- sfall/FalloutEngine/Variables_def.h | 2 +- sfall/Modules/BugFixes.cpp | 2 +- sfall/Modules/HookScripts/ObjectHs.cpp | 4 ++-- sfall/Modules/ScriptExtender.cpp | 20 ++++++++++---------- sfall/Modules/ScriptExtender.h | 2 +- sfall/Modules/Scripting/Handlers/Misc.cpp | 15 ++++++++------- 8 files changed, 36 insertions(+), 34 deletions(-) diff --git a/sfall/FalloutEngine/Enums.h b/sfall/FalloutEngine/Enums.h index 3b6adea2..d2b2c630 100644 --- a/sfall/FalloutEngine/Enums.h +++ b/sfall/FalloutEngine/Enums.h @@ -445,15 +445,6 @@ enum class ScenerySubType : long GENERIC = 5 }; -enum ScriptTypes -{ - SCRIPT_SYSTEM = 0, - SCRIPT_SPATIAL = 1, - SCRIPT_TIME = 2, - SCRIPT_ITEM = 3, - SCRIPT_CRITTER = 4, -}; - // proto.h: stats // enum Stat : long @@ -505,8 +496,9 @@ enum Stat : long STAT_real_max_stat = 38 }; -namespace ScriptProc { - enum ScriptProc : long { +namespace Scripts { + enum ScriptProc : long + { no_p_proc = 0, start = 1, spatial_p_proc = 2, @@ -537,6 +529,15 @@ namespace ScriptProc { combat_is_over_p_proc = 27, count = 28 }; + + enum ScriptTypes : long + { + SCRIPT_SYSTEM = 0, + SCRIPT_SPATIAL = 1, + SCRIPT_TIME = 2, + SCRIPT_ITEM = 3, + SCRIPT_CRITTER = 4, + }; } #define STAT_max_derived STAT_poison_resist diff --git a/sfall/FalloutEngine/Functions_def.h b/sfall/FalloutEngine/Functions_def.h index bce59504..1072f8a8 100644 --- a/sfall/FalloutEngine/Functions_def.h +++ b/sfall/FalloutEngine/Functions_def.h @@ -18,7 +18,7 @@ // because the compiler builds the better/optimized code when calling the engine functions WRAP_WATCOM_FFUNC4(long, _word_wrap, const char*, text, int, maxWidth, DWORD*, buf, BYTE*, count) WRAP_WATCOM_FFUNC3(void, correctFidForRemovedItem, GameObject*, critter, GameObject*, item, long, slotFlag) -WRAP_WATCOM_FFUNC7(long, createWindow, const char*, winName, long, x, long, y, long, width, long, height, long, bgColorIndex, long, flags) +WRAP_WATCOM_FFUNC7(long, createWindow, const char*, winName, DWORD, x, DWORD, y, DWORD, width, DWORD, height, long, color, long, flags) WRAP_WATCOM_FFUNC3(void, display_inventory, long, inventoryOffset, long, visibleOffset, long, mode) WRAP_WATCOM_FFUNC4(void, display_target_inventory, long, inventoryOffset, long, visibleOffset, DWORD*, targetInventory, long, mode) WRAP_WATCOM_FFUNC3(FrmFrameData*, frame_ptr, FrmHeaderData*, frm, long, frame, long, direction) diff --git a/sfall/FalloutEngine/Variables_def.h b/sfall/FalloutEngine/Variables_def.h index 8df52fb1..73006b7b 100644 --- a/sfall/FalloutEngine/Variables_def.h +++ b/sfall/FalloutEngine/Variables_def.h @@ -156,7 +156,7 @@ VAR_(pip_win, DWORD) VAR_(pipboy_message_file, MessageList) VAR_(pipmesg, DWORD) VAR_(preload_list_index, DWORD) -VARA(procTableStrs, const char*, (int)ScriptProc::count) // table of procId (from define.h) => procName map +VARA(procTableStrs, const char*, (int)Scripts::ScriptProc::count) // table of procId (from define.h) => procName map VARA(proto_msg_files, MessageList, 6) // array of 6 elements VAR_(proto_main_msg_file, MessageList) VAR_(ptable, DWORD) diff --git a/sfall/Modules/BugFixes.cpp b/sfall/Modules/BugFixes.cpp index 631f2547..9e0d248c 100644 --- a/sfall/Modules/BugFixes.cpp +++ b/sfall/Modules/BugFixes.cpp @@ -1134,7 +1134,7 @@ end: } static void __declspec(naked) action_explode_hack() { - using fo::ScriptProc::destroy_p_proc; + using namespace fo::Scripts; __asm { mov edx, destroy_p_proc mov eax, [esi + scriptId] // pobj.sid diff --git a/sfall/Modules/HookScripts/ObjectHs.cpp b/sfall/Modules/HookScripts/ObjectHs.cpp index 682b3ff0..9e193b0c 100644 --- a/sfall/Modules/HookScripts/ObjectHs.cpp +++ b/sfall/Modules/HookScripts/ObjectHs.cpp @@ -260,7 +260,7 @@ static DWORD __fastcall StdProcedureHook_Script(long numHandler, fo::ScriptInsta } static void __declspec(naked) ScriptStdProcedureHook() { - using namespace fo::ScriptProc; + using namespace fo::Scripts; __asm { mov eax, [eax + 0x54]; // Script.procedure_table test eax, eax; @@ -286,7 +286,7 @@ end: } static void __declspec(naked) After_ScriptStdProcedureHook() { - using namespace fo::ScriptProc; + using namespace fo::Scripts; __asm { call fo::funcoffs::executeProcedure_; cmp ecx, critter_p_proc; diff --git a/sfall/Modules/ScriptExtender.cpp b/sfall/Modules/ScriptExtender.cpp index 07365dce..7a873efb 100644 --- a/sfall/Modules/ScriptExtender.cpp +++ b/sfall/Modules/ScriptExtender.cpp @@ -324,7 +324,7 @@ static void __declspec(naked) FreeProgramHook() { } static void __declspec(naked) CombatBeginHook() { - using fo::ScriptProc::combat_is_starting_p_proc; + using namespace fo::Scripts; __asm { push eax; call fo::funcoffs::scr_set_ext_param_; @@ -335,7 +335,7 @@ static void __declspec(naked) CombatBeginHook() { } static void __declspec(naked) CombatOverHook() { - using fo::ScriptProc::combat_is_over_p_proc; + using namespace fo::Scripts; __asm { push eax; call fo::funcoffs::scr_set_ext_param_; @@ -436,7 +436,7 @@ void LoadScriptProgram(ScriptProgram &prog, const char* fileName, bool fullPath) const char** procTable = fo::var::procTableStrs; prog.ptr = scriptPtr; // fill lookup table - for (int i = 0; i < fo::ScriptProc::count; ++i) { + for (int i = 0; i < fo::Scripts::ScriptProc::count; ++i) { prog.procLookup[i] = fo::func::interpretFindProcedure(prog.ptr, procTable[i]); } prog.initialized = 0; @@ -481,7 +481,7 @@ static void LoadGlobalScriptsList() { if (prog.ptr) { dlogr(" Done", DL_SCRIPT); GlobalScript gscript = GlobalScript(prog); - gscript.startProc = prog.procLookup[fo::ScriptProc::start]; // get 'start' procedure position + gscript.startProc = prog.procLookup[fo::Scripts::ScriptProc::start]; // get 'start' procedure position globalScripts.push_back(gscript); ScriptExtender::AddProgramToMap(prog); // initialize script (start proc will be executed for the first time) -- this needs to be after script is added to "globalScripts" array @@ -572,7 +572,7 @@ void RunScriptProc(ScriptProgram* prog, const char* procName) { } void RunScriptProc(ScriptProgram* prog, long procId) { - if (procId > 0 && procId < fo::ScriptProc::count) { + if (procId > 0 && procId < fo::Scripts::ScriptProc::count) { int procNum = prog->procLookup[procId]; if (procNum != -1) { fo::func::executeProcedure(prog->ptr, procNum); @@ -581,7 +581,7 @@ void RunScriptProc(ScriptProgram* prog, long procId) { } int RunScriptStartProc(ScriptProgram* prog) { - int procNum = prog->procLookup[fo::ScriptProc::start]; + int procNum = prog->procLookup[fo::Scripts::ScriptProc::start]; if (procNum != -1) { fo::func::executeProcedure(prog->ptr, procNum); } @@ -633,12 +633,12 @@ static void RunGlobalScriptsOnWorldMap() { } static DWORD _stdcall HandleMapUpdateForScripts(const DWORD procId) { - if (procId == fo::ScriptProc::map_enter_p_proc) { + if (procId == fo::Scripts::ScriptProc::map_enter_p_proc) { // map changed, all game objects were destroyed and scripts detached, need to re-insert global scripts into the game for (std::vector::const_iterator it = globalScripts.cbegin(); it != globalScripts.cend(); it++) { fo::func::runProgram(it->prog.ptr); } - } else if (procId == fo::ScriptProc::map_exit_p_proc) onMapExit.invoke(); + } else if (procId == fo::Scripts::ScriptProc::map_exit_p_proc) onMapExit.invoke(); RunGlobalScriptsAtProc(procId); // gl* scripts of types 0 and 3 RunHookScriptsAtProc(procId); // all hs_ scripts @@ -654,7 +654,7 @@ static long HandleTimedEventScripts() { if (currentTime >= timerIt->time) { timedEvent = const_cast(&(*timerIt)); fo::func::dev_printf("\n[TimedEventScripts] run event: %d", timerIt->time); - RunScriptProc(timerIt->script, fo::ScriptProc::timed_event_p_proc); + RunScriptProc(timerIt->script, fo::Scripts::ScriptProc::timed_event_p_proc); wereRunning = true; } else { break; @@ -823,7 +823,7 @@ void ScriptExtender::init() { bool pathSeparator = (c == '\\' || c == '/'); if (len > 62 || (len == 62 && !pathSeparator)) { iniConfigFolder.clear(); - dlogr("Error: IniConfigFolder path is too long.", DL_SCRIPT); + dlogr("Error: IniConfigFolder path is too long.", DL_INIT|DL_SCRIPT); } else if (!pathSeparator) { iniConfigFolder += '\\'; } diff --git a/sfall/Modules/ScriptExtender.h b/sfall/Modules/ScriptExtender.h index cf852a28..7a89df64 100644 --- a/sfall/Modules/ScriptExtender.h +++ b/sfall/Modules/ScriptExtender.h @@ -29,7 +29,7 @@ namespace sfall typedef struct { fo::Program* ptr = nullptr; - int procLookup[fo::ScriptProc::count]; + int procLookup[fo::Scripts::ScriptProc::count]; char initialized; } ScriptProgram; diff --git a/sfall/Modules/Scripting/Handlers/Misc.cpp b/sfall/Modules/Scripting/Handlers/Misc.cpp index 0513c215..e6ceff47 100644 --- a/sfall/Modules/Scripting/Handlers/Misc.cpp +++ b/sfall/Modules/Scripting/Handlers/Misc.cpp @@ -424,18 +424,20 @@ static int ParseIniSetting(const char* iniString, const char* &key, char section DWORD seclen = (DWORD)key - ((DWORD)iniString + filelen + 1); if (seclen > 32) return -1; - long startAt = 2; file[0] = '.'; file[1] = '\\'; if (!ScriptExtender::iniConfigFolder.empty() && !IsSpecialIni(iniString, fileEnd)) { size_t len = ScriptExtender::iniConfigFolder.length(); // limit up to 62 characters memcpy(&file[2], ScriptExtender::iniConfigFolder.c_str(), len); - DWORD attr = GetFileAttributesA(file); - if (!(attr & FILE_ATTRIBUTE_DIRECTORY) /*&& attr != INVALID_FILE_ATTRIBUTES*/) startAt += len + 1; + memcpy(&file[2 + len], iniString, filelen); // copy path and file + file[2 + len + filelen] = 0; + if (GetFileAttributesA(file) & FILE_ATTRIBUTE_DIRECTORY) goto defRoot; // also file not found + } else { +defRoot: + memcpy(&file[2], iniString, filelen); + file[2 + filelen] = 0; } - memcpy(&file[startAt], iniString, filelen); - file[startAt + filelen] = 0; memcpy(section, &iniString[filelen + 1], seclen); section[seclen] = 0; @@ -1040,8 +1042,7 @@ static std::string GetIniFilePath(const ScriptValue &arg) { } else { fileName += ScriptExtender::iniConfigFolder; fileName += arg.strValue(); - DWORD attr = GetFileAttributesA(fileName.c_str()); - if ((attr & FILE_ATTRIBUTE_DIRECTORY) /*|| attr == INVALID_FILE_ATTRIBUTES*/) { + if (GetFileAttributesA(fileName.c_str()) & FILE_ATTRIBUTE_DIRECTORY) { auto str = arg.strValue(); for (size_t i = 2; ; i++, str++) { //if (*str == '.') str += (str[1] == '.') ? 3 : 2; // skip '.\' or '..\' From 702e62e61028f36cfcdc12a4d97aae1194fee2c3 Mon Sep 17 00:00:00 2001 From: NovaRain Date: Sun, 8 Dec 2019 23:16:56 +0800 Subject: [PATCH 25/36] Rewrote remove_script/set_script functions in C++ Fixed the return value of get_script function. --- sfall/FalloutEngine/Functions_def.h | 3 + sfall/Modules/Scripting/Handlers/Objects.cpp | 170 +++++++------------ sfall/Modules/Scripting/Handlers/Objects.h | 8 +- sfall/Modules/Scripting/Opcodes.cpp | 10 +- 4 files changed, 67 insertions(+), 124 deletions(-) diff --git a/sfall/FalloutEngine/Functions_def.h b/sfall/FalloutEngine/Functions_def.h index 1072f8a8..99804025 100644 --- a/sfall/FalloutEngine/Functions_def.h +++ b/sfall/FalloutEngine/Functions_def.h @@ -27,6 +27,7 @@ WRAP_WATCOM_FFUNC3(long, item_add_force, GameObject*, critter, GameObject*, item WRAP_WATCOM_FFUNC7(void, make_straight_path_func, GameObject*, objFrom, DWORD, tileFrom, DWORD, tileTo, void*, rotationPtr, DWORD*, result, long, flags, void*, func) WRAP_WATCOM_FFUNC4(long, mouse_click_in, long, x, long, y, long, x_end, long, y_end) WRAP_WATCOM_FFUNC3(GameObject*, obj_blocking_at, GameObject*, object, long, tile, long, elevation) +WRAP_WATCOM_FFUNC3(long, obj_new_sid_inst, GameObject*, object, long, sType, long, scriptIndex) WRAP_WATCOM_FFUNC3(long, object_under_mouse, long, crSwitch, long, inclDude, long, elevation) WRAP_WATCOM_FFUNC4(void, register_object_call, long*, target, long*, source, void*, func, long, delay) WRAP_WATCOM_FFUNC3(long, scr_get_local_var, long, sid, long, varId, long*, value) @@ -145,6 +146,8 @@ WRAP_WATCOM_FUNC1(long, register_object_must_erase, GameObject*, object) WRAP_WATCOM_FUNC3(long, register_object_take_out, GameObject*, object, long, holdFrameId, long, nothing) WRAP_WATCOM_FUNC3(long, register_object_turn_towards, GameObject*, object, long, tileNum, long, nothing) WRAP_WATCOM_FUNC2(long, roll_random, long, minValue, long, maxValue) +WRAP_WATCOM_FUNC2(long, scr_new, long*, scriptID, long, sType) +WRAP_WATCOM_FUNC1(long, scr_remove, long, scriptID) WRAP_WATCOM_FUNC2(long, skill_dec_point_force, GameObject*, critter, long, skill) WRAP_WATCOM_FUNC2(long, skill_inc_point_force, GameObject*, critter, long, skill) WRAP_WATCOM_FUNC1(long, skill_is_tagged, long, skill) diff --git a/sfall/Modules/Scripting/Handlers/Objects.cpp b/sfall/Modules/Scripting/Handlers/Objects.cpp index 70cc5c6f..b2e71708 100644 --- a/sfall/Modules/Scripting/Handlers/Objects.cpp +++ b/sfall/Modules/Scripting/Handlers/Objects.cpp @@ -36,129 +36,73 @@ namespace sfall namespace script { -void __declspec(naked) op_remove_script() { - __asm { - push ebx; - push ecx; - push edx; - mov ecx, eax; - call fo::funcoffs::interpretPopShort_; - mov edx, eax; - mov eax, ecx; - call fo::funcoffs::interpretPopLong_; - cmp dx, VAR_TYPE_INT; - jnz end; - test eax, eax; - jz end; - mov edx, eax; - mov eax, [eax + 0x78]; - cmp eax, 0xffffffff; - jz end; - call fo::funcoffs::scr_remove_; - mov dword ptr [edx + 0x78], 0xffffffff; -end: - pop edx; - pop ecx; - pop ebx; - retn; +#define exec_script_proc(script, stype) __asm { \ + __asm mov eax, script \ + __asm mov edx, stype \ + __asm call fo::funcoffs::exec_script_proc_ \ +} + +void sf_remove_script(OpcodeContext& ctx) { + auto object = ctx.arg(0).object(); + if (object->scriptId != 0xFFFFFFFF) { + fo::func::scr_remove(object->scriptId); + object->scriptId = 0xFFFFFFFF; } } -void __declspec(naked) op_set_script() { - __asm { - pushad; - mov ecx, eax; - call fo::funcoffs::interpretPopShort_; - mov edx, eax; - mov eax, ecx; - call fo::funcoffs::interpretPopLong_; - mov ebx, eax; - mov eax, ecx; - call fo::funcoffs::interpretPopShort_; - mov edi, eax; - mov eax, ecx; - call fo::funcoffs::interpretPopLong_; - cmp dx, VAR_TYPE_INT; - jnz end; - cmp di, VAR_TYPE_INT; - jnz end; - test eax, eax; - jz end; - mov esi, [eax + 0x78]; - cmp esi, 0xffffffff; - jz newscript; - push eax; - mov eax, esi; - call fo::funcoffs::scr_remove_; - pop eax; - mov dword ptr [eax + 0x78], 0xffffffff; -newscript: - mov esi, 1; - test ebx, 0x80000000; - jz execMapEnter; - xor esi, esi; - xor ebx, 0x80000000; -execMapEnter: - mov ecx, eax; - mov edx, 3; // script_type_item - mov edi, [eax + 0x64]; - shr edi, 24; - cmp edi, 1; - jnz notCritter; - inc edx; // 4 - "critter" type script -notCritter: - dec ebx; - call fo::funcoffs::obj_new_sid_inst_; - mov eax, [ecx + 0x78]; - mov edx, 1; // start - call fo::funcoffs::exec_script_proc_; - cmp esi, 1; // run map enter? - jnz end; - mov eax, [ecx + 0x78]; - mov edx, 0xf; // map_enter_p_proc - call fo::funcoffs::exec_script_proc_; -end: - popad; - retn; +void sf_set_script(OpcodeContext& ctx) { + using fo::Scripts::start; + using fo::Scripts::map_enter_p_proc; + + long scriptType; + auto object = ctx.arg(0).object(); + DWORD scriptIndex = ctx.arg(1).rawValue(); + + if ((scriptIndex & ~0x80000000) == 0) { + ctx.printOpcodeError("%s() - the script index number is incorrect.", ctx.getOpcodeName()); + return; } + bool runMapEnter = (scriptIndex & 0x80000000) == 0; + if (!runMapEnter) scriptIndex ^= 0x80000000; + scriptIndex--; + + if (object->scriptId != 0xFFFFFFFF) { + fo::func::scr_remove(object->scriptId); + object->scriptId = 0xFFFFFFFF; + } + if (object->Type() == fo::ObjType::OBJ_TYPE_CRITTER) { + scriptType = fo::Scripts::ScriptTypes::SCRIPT_CRITTER; + } else { + scriptType = fo::Scripts::ScriptTypes::SCRIPT_ITEM; + } + fo::func::obj_new_sid_inst(object, scriptType, scriptIndex); + + long scriptId = object->scriptId; + exec_script_proc(scriptId, start); + if (runMapEnter) exec_script_proc(scriptId, map_enter_p_proc); } void sf_create_spatial(OpcodeContext& ctx) { + using fo::Scripts::start; + DWORD scriptIndex = ctx.arg(0).rawValue(), tile = ctx.arg(1).rawValue(), elevation = ctx.arg(2).rawValue(), - radius = ctx.arg(3).rawValue(), - scriptId, tmp, objectPtr, - scriptPtr; - __asm { - lea eax, scriptId; - mov edx, 1; - call fo::funcoffs::scr_new_; - mov tmp, eax; - } - if (tmp == -1) return; - __asm { - mov eax, scriptId; - lea edx, scriptPtr; - call fo::funcoffs::scr_ptr_; - mov tmp, eax; - } - if (tmp == -1) return; - // fill spatial script properties: - *(DWORD*)(scriptPtr + 0x14) = scriptIndex - 1; - *(DWORD*)(scriptPtr + 0x8) = (elevation << 29) & 0xE0000000 | tile; - *(DWORD*)(scriptPtr + 0xC) = radius; + radius = ctx.arg(3).rawValue(); + + long scriptId; + fo::ScriptInstance* scriptPtr; + if (fo::func::scr_new(&scriptId, fo::Scripts::ScriptTypes::SCRIPT_SPATIAL) == -1 || fo::func::scr_ptr(scriptId, &scriptPtr) == -1) return; + + // set spatial script properties: + scriptPtr->scriptIdx = scriptIndex - 1; + scriptPtr->elevationAndTile = (elevation << 29) & 0xE0000000 | tile; + scriptPtr->spatialRadius = radius; + // this will load appropriate script program and link it to the script instance we just created: - __asm { - mov eax, scriptId; - mov edx, 1; // start_p_proc - call fo::funcoffs::exec_script_proc_; - mov eax, scriptPtr; - mov eax, [eax + 0x18]; // program pointer - call fo::funcoffs::scr_find_obj_from_program_; - mov objectPtr, eax; - } - ctx.setReturn(objectPtr); + exec_script_proc(scriptId, start); + + ctx.setReturn(fo::func::scr_find_obj_from_program(scriptPtr->program)); } void sf_spatial_radius(OpcodeContext& ctx) { @@ -171,7 +115,7 @@ void sf_spatial_radius(OpcodeContext& ctx) { void sf_get_script(OpcodeContext& ctx) { auto obj = ctx.arg(0).object(); - ctx.setReturn(obj->scriptIndex); + ctx.setReturn(++obj->scriptIndex); } void sf_set_critter_burst_disable(OpcodeContext& ctx) { diff --git a/sfall/Modules/Scripting/Handlers/Objects.h b/sfall/Modules/Scripting/Handlers/Objects.h index 8c048b02..f40a0e2d 100644 --- a/sfall/Modules/Scripting/Handlers/Objects.h +++ b/sfall/Modules/Scripting/Handlers/Objects.h @@ -27,13 +27,9 @@ namespace sfall namespace script { -//script control functions +void sf_remove_script(OpcodeContext&); -class OpcodeContext; - -void __declspec() op_remove_script(); - -void __declspec() op_set_script(); +void sf_set_script(OpcodeContext&); void sf_create_spatial(OpcodeContext&); diff --git a/sfall/Modules/Scripting/Opcodes.cpp b/sfall/Modules/Scripting/Opcodes.cpp index dc0fe738..69966fcc 100644 --- a/sfall/Modules/Scripting/Opcodes.cpp +++ b/sfall/Modules/Scripting/Opcodes.cpp @@ -110,6 +110,8 @@ static SfallOpcodeInfo opcodeInfoArray[] = { {0x1ef, "cos", sf_cos, 1, true, 0, {ARG_NUMBER}}, {0x1f0, "tan", sf_tan, 1, true, 0, {ARG_NUMBER}}, {0x1f1, "arctan", sf_arctan, 2, true, 0, {ARG_NUMBER, ARG_NUMBER}}, + {0x1f3, "remove_script", sf_remove_script, 1, false, 0, {ARG_OBJECT}}, + {0x1f4, "set_script", sf_set_script, 2, false, 0, {ARG_OBJECT, ARG_INT}}, {0x1f5, "get_script", sf_get_script, 1, true, -1, {ARG_OBJECT}}, {0x1f7, "fs_create", sf_fs_create, 2, true, -1, {ARG_STRING, ARG_INT}}, @@ -221,10 +223,10 @@ static SfallOpcodeInfo opcodeInfoArray[] = { // An array for opcode info, indexed by opcode. // Initialized at run time from the array above. -std::array opcodeInfoTable; +static std::array opcodeInfoTable; // Initializes the opcode info table. -void InitOpcodeInfoTable() { +static void InitOpcodeInfoTable() { int length = sizeof(opcodeInfoArray) / sizeof(opcodeInfoArray[0]); for (int i = 0; i < length; ++i) { // index: opcode, value: reference to opcode element in the opcodeInfoArray array @@ -234,7 +236,7 @@ void InitOpcodeInfoTable() { // Default handler for Sfall Opcodes. // Searches current opcode in Opcode Info table and executes the appropriate handler. -void __fastcall defaultOpcodeHandler(fo::Program* program, DWORD opcodeOffset) { // eax/ebx - program, edx - opcodeOffset +static void __fastcall defaultOpcodeHandler(fo::Program* program, DWORD opcodeOffset) { // eax/ebx - program, edx - opcodeOffset __asm push ecx; __asm mov program, ebx; @@ -372,8 +374,6 @@ void InitNewOpcodes() { opcodes[0x1e9] = op_get_unspent_ap_perk_bonus; opcodes[0x1ea] = op_init_hook; opcodes[0x1f2] = op_set_palette; - opcodes[0x1f3] = op_remove_script; - opcodes[0x1f4] = op_set_script; opcodes[0x1f6] = op_nb_create_char; opcodes[0x206] = op_set_self; From 439de4aecc765029608946d9dcb7a02d01a68c14 Mon Sep 17 00:00:00 2001 From: NovaRain Date: Mon, 9 Dec 2019 10:18:29 +0800 Subject: [PATCH 26/36] Re-fixed the return value of get_script function --- artifacts/scripting/sfall function notes.txt | 5 +++-- sfall/Modules/Scripting/Handlers/Objects.cpp | 17 ++++++++--------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/artifacts/scripting/sfall function notes.txt b/artifacts/scripting/sfall function notes.txt index 29ce6aad..0c705c98 100644 --- a/artifacts/scripting/sfall function notes.txt +++ b/artifacts/scripting/sfall function notes.txt @@ -153,12 +153,13 @@ array - array ID to be used with array-related functions (actually an integer) - accepts a pointer to an object and will remove the script from that object. > void set_script(object, int scriptid) -- accepts a pointer to an object and scriptID, and applies the given script to an object (scriptID accept the same values as create_object_sid from sfall 3.6) +- accepts a pointer to an object and scriptID, and applies the given script to an object (scriptID accepts the same values as create_object_sid) - If used on an object that is already scripted, it will remove the existing script first; you cannot have multiple scripts attached to a single object. Calling set_script on self_obj will have all sorts of wacky side effects, and should be avoided. - if you add 0x80000000 to the sid when calling set_script, map_enter_p_proc will be SKIPPED. The start proc will always be run. > int get_script(object) -- accepts a pointer to an object and returns its scriptID (line number in scripts.lst), or -1 if the object is unscripted. +- accepts a pointer to an object and returns its scriptID (line number in scripts.lst), or 0 if the object is unscripted. +- returns -1 on argument error. > void set_self(int obj) - overrides the scripts self_obj for the next function call. diff --git a/sfall/Modules/Scripting/Handlers/Objects.cpp b/sfall/Modules/Scripting/Handlers/Objects.cpp index b2e71708..d8a6b83f 100644 --- a/sfall/Modules/Scripting/Handlers/Objects.cpp +++ b/sfall/Modules/Scripting/Handlers/Objects.cpp @@ -36,9 +36,9 @@ namespace sfall namespace script { -#define exec_script_proc(script, stype) __asm { \ +#define exec_script_proc(script, proc) __asm { \ __asm mov eax, script \ - __asm mov edx, stype \ + __asm mov edx, proc \ __asm call fo::funcoffs::exec_script_proc_ \ } @@ -56,14 +56,13 @@ void sf_set_script(OpcodeContext& ctx) { long scriptType; auto object = ctx.arg(0).object(); - DWORD scriptIndex = ctx.arg(1).rawValue(); + unsigned long valArg = ctx.arg(1).rawValue(); - if ((scriptIndex & ~0x80000000) == 0) { + long scriptIndex = valArg & ~0xF0000000; + if (scriptIndex == 0 || valArg > 0x8FFFFFFF) { // negative values are not allowed ctx.printOpcodeError("%s() - the script index number is incorrect.", ctx.getOpcodeName()); return; } - bool runMapEnter = (scriptIndex & 0x80000000) == 0; - if (!runMapEnter) scriptIndex ^= 0x80000000; scriptIndex--; if (object->scriptId != 0xFFFFFFFF) { @@ -79,7 +78,7 @@ void sf_set_script(OpcodeContext& ctx) { long scriptId = object->scriptId; exec_script_proc(scriptId, start); - if (runMapEnter) exec_script_proc(scriptId, map_enter_p_proc); + if ((valArg & 0x80000000) == 0) exec_script_proc(scriptId, map_enter_p_proc); } void sf_create_spatial(OpcodeContext& ctx) { @@ -114,8 +113,8 @@ void sf_spatial_radius(OpcodeContext& ctx) { } void sf_get_script(OpcodeContext& ctx) { - auto obj = ctx.arg(0).object(); - ctx.setReturn(++obj->scriptIndex); + auto scriptIndex = ctx.arg(0).object()->scriptIndex; + ctx.setReturn((scriptIndex >= 0) ? ++scriptIndex : 0); } void sf_set_critter_burst_disable(OpcodeContext& ctx) { From a325dd18ea21ed20a21f6ff9ed16c4f236305b7a Mon Sep 17 00:00:00 2001 From: NovaRain Date: Wed, 11 Dec 2019 15:52:03 +0800 Subject: [PATCH 27/36] WIP - Added a new hook: HOOK_TARGETOBJ Added "get_tile" and "get_objects_at_radius" script functions. * their names are not final and documents are missing. --- sfall/FalloutEngine/EngineUtils.cpp | 14 ++++ sfall/FalloutEngine/EngineUtils.h | 2 + sfall/FalloutEngine/Functions.cpp | 6 ++ sfall/FalloutEngine/Functions.h | 2 + sfall/FalloutEngine/Functions_def.h | 2 + sfall/Modules/HookScripts.cpp | 1 + sfall/Modules/HookScripts.h | 1 + sfall/Modules/HookScripts/CombatHs.cpp | 72 ++++++++++++++++++- sfall/Modules/HookScripts/CombatHs.h | 1 + sfall/Modules/Scripting/Handlers/Metarule.cpp | 2 + sfall/Modules/Scripting/Handlers/Objects.cpp | 17 +++++ sfall/Modules/Scripting/Handlers/Objects.h | 2 + sfall/Modules/Scripting/Handlers/Worldmap.cpp | 4 ++ sfall/Modules/Scripting/Handlers/Worldmap.h | 2 + 14 files changed, 126 insertions(+), 2 deletions(-) diff --git a/sfall/FalloutEngine/EngineUtils.cpp b/sfall/FalloutEngine/EngineUtils.cpp index 7fc58706..37c7c4b2 100644 --- a/sfall/FalloutEngine/EngineUtils.cpp +++ b/sfall/FalloutEngine/EngineUtils.cpp @@ -196,6 +196,20 @@ long __fastcall GetTopWindowID(long xPos, long yPos) { return win->wID; } +// Returns an array of objects within the specified tile radius, objects at the source tile will not be included in the array +void GetObjectsTileRadius(std::vector &objs, long tile, long radius, long elev, long type = -1) { + for (; radius > 0; radius--) { + for (long rotation = 0; rotation < 6; rotation++) { + long _tile = fo::func::tile_num_in_direction(tile, rotation, radius); + fo::GameObject* obj = fo::func::obj_find_first_at_tile(elev, _tile); + while (obj) { + if (type == -1 || type == obj->Type()) objs.push_back(obj); + obj = fo::func::obj_find_next_at_tile(); + } + } + } +} + //--------------------------------------------------------- //print text to surface void PrintText(char *DisplayText, BYTE ColourIndex, DWORD Xpos, DWORD Ypos, DWORD TxtWidth, DWORD ToWidth, BYTE *ToSurface) { diff --git a/sfall/FalloutEngine/EngineUtils.h b/sfall/FalloutEngine/EngineUtils.h index a33c382f..72c94a39 100644 --- a/sfall/FalloutEngine/EngineUtils.h +++ b/sfall/FalloutEngine/EngineUtils.h @@ -78,6 +78,8 @@ long GetScriptLocalVars(long sid); long __fastcall GetTopWindowID(long xPos, long yPos); +void GetObjectsTileRadius(std::vector &objs, long tile, long radius, long elev, long type); + // Print text to 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 diff --git a/sfall/FalloutEngine/Functions.cpp b/sfall/FalloutEngine/Functions.cpp index 719c5e8f..f951e1cd 100644 --- a/sfall/FalloutEngine/Functions.cpp +++ b/sfall/FalloutEngine/Functions.cpp @@ -315,6 +315,12 @@ long __stdcall message_exit(MessageList *msgList) { WRAP_WATCOM_CALL1(message_exit_, msgList) } +long __fastcall tile_num(long x, long y) { + __asm push ebx; // don't delete (bug in tile_num_) + WRAP_WATCOM_FCALL2(tile_num_, x, x); + __asm pop ebx; +} + GameObject* __fastcall obj_blocking_at_wrapper(GameObject* obj, DWORD tile, DWORD elevation, void* func) { __asm { mov eax, ecx; diff --git a/sfall/FalloutEngine/Functions.h b/sfall/FalloutEngine/Functions.h index e0489fe7..4f4abc72 100644 --- a/sfall/FalloutEngine/Functions.h +++ b/sfall/FalloutEngine/Functions.h @@ -165,6 +165,8 @@ long __stdcall message_load(MessageList *msgList, const char *msgFilePath); // destroys message list long __stdcall message_exit(MessageList *msgList); +long __fastcall tile_num(long x, long y); + GameObject* __fastcall obj_blocking_at_wrapper(GameObject* obj, DWORD tile, DWORD elevation, void* func); GameObject* __stdcall obj_find_first_at_tile(long elevation, long tileNum); diff --git a/sfall/FalloutEngine/Functions_def.h b/sfall/FalloutEngine/Functions_def.h index 99804025..81391292 100644 --- a/sfall/FalloutEngine/Functions_def.h +++ b/sfall/FalloutEngine/Functions_def.h @@ -27,6 +27,7 @@ WRAP_WATCOM_FFUNC3(long, item_add_force, GameObject*, critter, GameObject*, item WRAP_WATCOM_FFUNC7(void, make_straight_path_func, GameObject*, objFrom, DWORD, tileFrom, DWORD, tileTo, void*, rotationPtr, DWORD*, result, long, flags, void*, func) WRAP_WATCOM_FFUNC4(long, mouse_click_in, long, x, long, y, long, x_end, long, y_end) WRAP_WATCOM_FFUNC3(GameObject*, obj_blocking_at, GameObject*, object, long, tile, long, elevation) +WRAP_WATCOM_FFUNC4(long, obj_move_to_tile, GameObject*, object, long, tile, long, elevation, RECT*, rect) WRAP_WATCOM_FFUNC3(long, obj_new_sid_inst, GameObject*, object, long, sType, long, scriptIndex) WRAP_WATCOM_FFUNC3(long, object_under_mouse, long, crSwitch, long, inclDude, long, elevation) WRAP_WATCOM_FFUNC4(void, register_object_call, long*, target, long*, source, void*, func, long, delay) @@ -105,6 +106,7 @@ WRAP_WATCOM_FUNC6(long, make_path_func, GameObject*, objectFrom, long, tileFrom, WRAP_WATCOM_FUNC0(long, new_obj_id) // calculates bounding box (rectangle) for a given object WRAP_WATCOM_FUNC2(void, obj_bound, GameObject*, object, BoundRect*, boundRect) +WRAP_WATCOM_FUNC1(long, obj_destroy, GameObject*, object) WRAP_WATCOM_FUNC2(long, obj_erase_object, GameObject*, object, BoundRect*, boundRect) WRAP_WATCOM_FUNC0(GameObject*, obj_find_first) WRAP_WATCOM_FUNC0(GameObject*, obj_find_next) diff --git a/sfall/Modules/HookScripts.cpp b/sfall/Modules/HookScripts.cpp index 6c812360..c331985c 100644 --- a/sfall/Modules/HookScripts.cpp +++ b/sfall/Modules/HookScripts.cpp @@ -92,6 +92,7 @@ static HooksInjectInfo injectHooks[] = { {HOOK_SNEAK, Inject_SneakCheckHook, false}, {HOOK_STDPROCEDURE, Inject_ScriptProcedureHook, false}, {HOOK_STDPROCEDURE_END, Inject_ScriptProcedureHook2, false}, + {HOOK_TARGETOBJ, Inject_TargetObjectHook, false}, }; bool HookScripts::injectAllHooks; diff --git a/sfall/Modules/HookScripts.h b/sfall/Modules/HookScripts.h index 0cc9eb86..4b140449 100644 --- a/sfall/Modules/HookScripts.h +++ b/sfall/Modules/HookScripts.h @@ -68,6 +68,7 @@ enum HookType HOOK_SNEAK = 39, HOOK_STDPROCEDURE = 40, HOOK_STDPROCEDURE_END = 41, + HOOK_TARGETOBJ = 42, HOOK_COUNT }; diff --git a/sfall/Modules/HookScripts/CombatHs.cpp b/sfall/Modules/HookScripts/CombatHs.cpp index 3d1496cb..08068757 100644 --- a/sfall/Modules/HookScripts/CombatHs.cpp +++ b/sfall/Modules/HookScripts/CombatHs.cpp @@ -25,14 +25,14 @@ static void __declspec(naked) ToHitHook() { mov eax, args[4]; // restore call fo::funcoffs::determine_to_hit_func_; mov args[0], eax; - pushad; + pushadc; } argCount = 7; RunHookScript(HOOK_TOHIT); __asm { - popad; + popadc; cmp cRet, 1; cmovnb eax, rets[0]; HookEnd; @@ -484,6 +484,65 @@ skip: } } +DWORD targetRet = 0; +static long __fastcall TargetObjectHook(DWORD isValid, DWORD object, long type) { + if (isValid > 1) isValid = 1; + + BeginHook(); + argCount = 3; + + args[0] = type; // 0 - mouse hover on target, 1 - mouse click target + args[1] = isValid; // 1 - target is valid + args[2] = object; // target object + + if (isValid == 0) object = 0; + + RunHookScript(HOOK_TARGETOBJ); + + if (type == 0) { + targetRet = 0; + if (cRet > 0) { + targetRet = (rets[0] != 0) ? rets[0] : object; // 0 - default object, -1 - invalid target, or object override + object = (targetRet != -1) ? targetRet : 0; // object can't be -1 + } + } else if (targetRet) { + if (targetRet != -1) object = targetRet; + targetRet = 0; + } + EndHook(); + + return object; // null or object +} + +static void __declspec(naked) gmouse_bk_process_hook() { + __asm { + push 0; // type + mov ecx, eax; // valid or object + mov edx, edi; // object + call TargetObjectHook; + mov edi, eax; + retn; + } +} + +static void __declspec(naked) gmouse_handle_event_hook() { + __asm { + push 1; // type + mov ecx, eax; + cmp dword ptr ds:[targetRet], 0; + je default; + // override + xor edx, edx; + cmp dword ptr ds:[targetRet], -1; + cmovne edx, targetRet; // 0 or object + mov ecx, edx; // set valid +default: + call TargetObjectHook; + mov edx, eax; + retn; + } +} + void Inject_ToHitHook() { HookCalls(ToHitHook, { 0x421686, // combat_safety_invalidate_weapon_func_ @@ -559,6 +618,14 @@ void Inject_SubCombatDamageHook() { MakeJump(0x42499C, SubComputeDamageHook); } +void Inject_TargetObjectHook() { + MakeCall(0x44BB16, gmouse_bk_process_hook, 1); + SafeWrite8(0x44BB00, 0x15); + + MakeCall(0x44C286, gmouse_handle_event_hook, 1); + SafeWrite8(0x44C26E, 0x17); +} + void InitCombatHookScripts() { LoadHookScript("hs_tohit", HOOK_TOHIT); LoadHookScript("hs_afterhitroll", HOOK_AFTERHITROLL); @@ -570,6 +637,7 @@ void InitCombatHookScripts() { LoadHookScript("hs_combatturn", HOOK_COMBATTURN); LoadHookScript("hs_onexplosion", HOOK_ONEXPLOSION); LoadHookScript("hs_subcombatdmg", HOOK_SUBCOMBATDAMAGE); + LoadHookScript("hs_targetobj", HOOK_TARGETOBJ); } } diff --git a/sfall/Modules/HookScripts/CombatHs.h b/sfall/Modules/HookScripts/CombatHs.h index f530ec97..7bb1c62d 100644 --- a/sfall/Modules/HookScripts/CombatHs.h +++ b/sfall/Modules/HookScripts/CombatHs.h @@ -17,5 +17,6 @@ void Inject_AmmoCostHook(); void Inject_CombatTurnHook(); void Inject_OnExplosionHook(); void Inject_SubCombatDamageHook(); +void Inject_TargetObjectHook(); } diff --git a/sfall/Modules/Scripting/Handlers/Metarule.cpp b/sfall/Modules/Scripting/Handlers/Metarule.cpp index 13e9184b..3aa21e19 100644 --- a/sfall/Modules/Scripting/Handlers/Metarule.cpp +++ b/sfall/Modules/Scripting/Handlers/Metarule.cpp @@ -88,10 +88,12 @@ static const SfallMetarule metarules[] = { {"get_metarule_table", sf_get_metarule_table, 0, 0}, {"get_object_ai_data", sf_get_object_ai_data, 2, 2, -1, {ARG_OBJECT, ARG_INT}}, {"get_object_data", sf_get_object_data, 2, 2, 0, {ARG_OBJECT, ARG_INT}}, + {"get_objects_at_radius", sf_get_objects_at_radius, 3, 4, 0, {ARG_INT, ARG_INT, ARG_INT, ARG_INT}}, {"get_outline", sf_get_outline, 1, 1, 0, {ARG_OBJECT}}, {"get_sfall_arg_at", sf_get_sfall_arg_at, 1, 1, 0, {ARG_INT}}, {"get_string_pointer", sf_get_string_pointer, 1, 1, 0, {ARG_STRING}}, {"get_text_width", sf_get_text_width, 1, 1, 0, {ARG_STRING}}, + {"get_tile", sf_get_tile, 2, 2, -1, {ARG_INT, ARG_INT}}, {"has_fake_perk_npc", sf_has_fake_perk_npc, 2, 2, 0, {ARG_OBJECT, ARG_STRING}}, {"has_fake_trait_npc", sf_has_fake_trait_npc, 2, 2, 0, {ARG_OBJECT, ARG_STRING}}, {"hide_window", sf_hide_window, 0, 1, -1, {ARG_STRING}}, diff --git a/sfall/Modules/Scripting/Handlers/Objects.cpp b/sfall/Modules/Scripting/Handlers/Objects.cpp index d8a6b83f..65310156 100644 --- a/sfall/Modules/Scripting/Handlers/Objects.cpp +++ b/sfall/Modules/Scripting/Handlers/Objects.cpp @@ -520,5 +520,22 @@ void sf_set_unique_id(OpcodeContext& ctx) { ctx.setReturn(id); } +void sf_get_objects_at_radius(OpcodeContext& ctx) { + long radius = ctx.arg(1).rawValue(); + if (radius <= 0) radius = 1; + long elev = ctx.arg(2).rawValue(); + if (elev < 0) elev = 0; else if (elev > 2) elev = 2; + long type = (ctx.numArgs() > 3) ? ctx.arg(3).rawValue() : -1; + + std::vector objects; + fo::GetObjectsTileRadius(objects, ctx.arg(0).rawValue(), radius, elev, type); + size_t sz = objects.size(); + DWORD id = TempArray(sz, 0); + for (size_t i = 0; i < sz; i++) { + arrays[id].val[i].set((long)objects[i]); + } + ctx.setReturn(id); +} + } } diff --git a/sfall/Modules/Scripting/Handlers/Objects.h b/sfall/Modules/Scripting/Handlers/Objects.h index f40a0e2d..afd4f946 100644 --- a/sfall/Modules/Scripting/Handlers/Objects.h +++ b/sfall/Modules/Scripting/Handlers/Objects.h @@ -111,5 +111,7 @@ void sf_set_drugs_data(OpcodeContext&); void sf_set_unique_id(OpcodeContext&); +void sf_get_objects_at_radius(OpcodeContext&); + } } diff --git a/sfall/Modules/Scripting/Handlers/Worldmap.cpp b/sfall/Modules/Scripting/Handlers/Worldmap.cpp index 6d4089ce..353da54d 100644 --- a/sfall/Modules/Scripting/Handlers/Worldmap.cpp +++ b/sfall/Modules/Scripting/Handlers/Worldmap.cpp @@ -253,5 +253,9 @@ void sf_get_rest_on_map(OpcodeContext& ctx) { ctx.setReturn(result); } +void sf_get_tile(OpcodeContext& ctx) { + ctx.setReturn(fo::func::tile_num(ctx.arg(0).rawValue(), ctx.arg(1).rawValue())); +} + } } diff --git a/sfall/Modules/Scripting/Handlers/Worldmap.h b/sfall/Modules/Scripting/Handlers/Worldmap.h index 44a88a72..1d2a7c1f 100644 --- a/sfall/Modules/Scripting/Handlers/Worldmap.h +++ b/sfall/Modules/Scripting/Handlers/Worldmap.h @@ -56,5 +56,7 @@ void sf_set_rest_on_map(OpcodeContext&); void sf_get_rest_on_map(OpcodeContext&); +void sf_get_tile(OpcodeContext&); + } } From 0bf9a322c1cd8e34153bd3aa54d0ca069835a61b Mon Sep 17 00:00:00 2001 From: NovaRain Date: Fri, 13 Dec 2019 10:36:37 +0800 Subject: [PATCH 28/36] Corrected code and renamed hook/functions from the previous commit --- artifacts/scripting/headers/sfall.h | 3 ++ artifacts/scripting/hookscripts.txt | 12 ++++++ artifacts/scripting/sfall function notes.txt | 12 +++++- sfall/FalloutEngine/EngineUtils.cpp | 20 +++++----- sfall/FalloutEngine/EngineUtils.h | 2 +- sfall/Modules/HookScripts.cpp | 2 +- sfall/Modules/HookScripts.h | 2 +- sfall/Modules/HookScripts/CombatHs.cpp | 39 +++++++++---------- sfall/Modules/Scripting/Handlers/Metarule.cpp | 6 +-- sfall/Modules/Scripting/Handlers/Objects.cpp | 7 ++-- sfall/Modules/Scripting/Handlers/Objects.h | 4 +- sfall/Modules/Scripting/Handlers/Worldmap.cpp | 2 +- sfall/Modules/Scripting/Handlers/Worldmap.h | 2 +- 13 files changed, 69 insertions(+), 44 deletions(-) diff --git a/artifacts/scripting/headers/sfall.h b/artifacts/scripting/headers/sfall.h index 173da3d1..8af61cfa 100644 --- a/artifacts/scripting/headers/sfall.h +++ b/artifacts/scripting/headers/sfall.h @@ -64,6 +64,7 @@ #define HOOK_SNEAK (39) #define HOOK_STDPROCEDURE (40) #define HOOK_STDPROCEDURE_END (41) +#define HOOK_TARGETOBJECT (42) //Valid arguments to list_begin #define LIST_CRITTERS (0) @@ -296,6 +297,7 @@ #define metarule_exist(metaruleName) sfall_func1("metarule_exist", metaruleName) #define npc_engine_level_up(toggle) sfall_func1("npc_engine_level_up", toggle) #define obj_under_cursor(onlyCritter, includeDude) sfall_func2("obj_under_cursor", onlyCritter, includeDude) +#define objects_in_radius(tile, radius, elev, type) sfall_func4("objects_in_radius", tile, radius, elev, type) #define outlined_object sfall_func0("outlined_object") #define real_dude_obj sfall_func0("real_dude_obj") #define remove_all_timer_events sfall_func0("remove_timer_event") @@ -321,6 +323,7 @@ #define string_compare(str1, str2) sfall_func2("string_compare", str1, str2) #define string_compare_locale(str1, str2, codePage) sfall_func3("string_compare", str1, str2, codePage) #define string_format(format, a1, a2) sfall_func3("string_format", format, a1, a2) +#define tile_by_position(x, y) sfall_func2("tile_by_position", x, y) #define tile_refresh_display sfall_func0("tile_refresh_display") #define unjam_lock(obj) sfall_func1("unjam_lock", obj) #define unset_unique_id(obj) sfall_func2("set_unique_id", obj, -1) diff --git a/artifacts/scripting/hookscripts.txt b/artifacts/scripting/hookscripts.txt index da2ce2ef..05187799 100644 --- a/artifacts/scripting/hookscripts.txt +++ b/artifacts/scripting/hookscripts.txt @@ -648,3 +648,15 @@ Obj arg3 - the object that called this handler (source_obj, can be 0) int arg4 - 1 after procedure execution (for HOOK_STDPROCEDURE_END), 0 otherwise int ret1 - pass -1 to cancel the execution of the handler (only for HOOK_STDPROCEDURE) + +------------------------------------------- + +HOOK_TARGETOBJECT (hs_targetobject.int) + +Runs whenever the targeting cursor hovers over something. + +int arg1 - event type: 0 - when hovering over the target, 1 - when clicking on the target +int arg2 - 1 if the target is a valid object, 0 otherwise +Obj arg3 - the target object + +int ret1 - overrides the target object diff --git a/artifacts/scripting/sfall function notes.txt b/artifacts/scripting/sfall function notes.txt index 0c705c98..01ca2330 100644 --- a/artifacts/scripting/sfall function notes.txt +++ b/artifacts/scripting/sfall function notes.txt @@ -685,7 +685,17 @@ optional argument: > string sfall_func3("string_format", string format, any val1, any val2, ...) - formats given value using standard syntax of C printf function (google "printf" for format details). However it is limited to formatting up to 4 values -- formatting is only supported for %s and %d. The format string is limited to 1024 characters +- formatting is only supported for %s and %d, and the format string is limited to 1024 characters + +> array sfall_func3("objects_in_radius", int tile, int radius, int elevation) +> array sfall_func4("objects_in_radius", int tile, int radius, int elevation, int type) +- returns an array of objects of a type (see OBJ_TYPE_* constants in define_extra.h) within the specified radius from the given tile +- The radius is limited to 50 hexes +- passing -1 to the type argument or not specifying it will return all types of objects + +> int sfall_func2("tile_by_position", int x, int y) +- returns the tile number at the x/y position relative to the top-left corner of the screen +- returns -1 if the position is outside of the screen ------------------------ ------ MORE INFO ------- diff --git a/sfall/FalloutEngine/EngineUtils.cpp b/sfall/FalloutEngine/EngineUtils.cpp index 37c7c4b2..c27a79c8 100644 --- a/sfall/FalloutEngine/EngineUtils.cpp +++ b/sfall/FalloutEngine/EngineUtils.cpp @@ -196,16 +196,18 @@ long __fastcall GetTopWindowID(long xPos, long yPos) { return win->wID; } -// Returns an array of objects within the specified tile radius, objects at the source tile will not be included in the array -void GetObjectsTileRadius(std::vector &objs, long tile, long radius, long elev, long type = -1) { - for (; radius > 0; radius--) { - for (long rotation = 0; rotation < 6; rotation++) { - long _tile = fo::func::tile_num_in_direction(tile, rotation, radius); - fo::GameObject* obj = fo::func::obj_find_first_at_tile(elev, _tile); - while (obj) { - if (type == -1 || type == obj->Type()) objs.push_back(obj); - obj = fo::func::obj_find_next_at_tile(); +// Returns an array of objects within the specified radius from the source tile +void GetObjectsTileRadius(std::vector &objs, long sourceTile, long radius, long elev, long type = -1) { + for (long tile = 0; tile < 40000; tile++) { + fo::GameObject* obj = fo::func::obj_find_first_at_tile(elev, tile); + while (obj) { + if (type == -1 || type == obj->Type()) { + bool multiHex = (obj->flags & fo::ObjectFlag::MultiHex) ? true : false; + if (fo::func::tile_dist(sourceTile, obj->tile) <= (radius + multiHex)) { + objs.push_back(obj); + } } + obj = fo::func::obj_find_next_at_tile(); } } } diff --git a/sfall/FalloutEngine/EngineUtils.h b/sfall/FalloutEngine/EngineUtils.h index 72c94a39..f9a3c812 100644 --- a/sfall/FalloutEngine/EngineUtils.h +++ b/sfall/FalloutEngine/EngineUtils.h @@ -78,7 +78,7 @@ long GetScriptLocalVars(long sid); long __fastcall GetTopWindowID(long xPos, long yPos); -void GetObjectsTileRadius(std::vector &objs, long tile, long radius, long elev, long type); +void GetObjectsTileRadius(std::vector &objs, long sourceTile, long radius, long elev, long type); // Print text to surface void PrintText(char *displayText, BYTE colorIndex, DWORD x, DWORD y, DWORD textWidth, DWORD destWidth, BYTE *surface); diff --git a/sfall/Modules/HookScripts.cpp b/sfall/Modules/HookScripts.cpp index c331985c..2775a29f 100644 --- a/sfall/Modules/HookScripts.cpp +++ b/sfall/Modules/HookScripts.cpp @@ -92,7 +92,7 @@ static HooksInjectInfo injectHooks[] = { {HOOK_SNEAK, Inject_SneakCheckHook, false}, {HOOK_STDPROCEDURE, Inject_ScriptProcedureHook, false}, {HOOK_STDPROCEDURE_END, Inject_ScriptProcedureHook2, false}, - {HOOK_TARGETOBJ, Inject_TargetObjectHook, false}, + {HOOK_TARGETOBJECT, Inject_TargetObjectHook, false}, }; bool HookScripts::injectAllHooks; diff --git a/sfall/Modules/HookScripts.h b/sfall/Modules/HookScripts.h index 4b140449..be0a9bc9 100644 --- a/sfall/Modules/HookScripts.h +++ b/sfall/Modules/HookScripts.h @@ -68,7 +68,7 @@ enum HookType HOOK_SNEAK = 39, HOOK_STDPROCEDURE = 40, HOOK_STDPROCEDURE_END = 41, - HOOK_TARGETOBJ = 42, + HOOK_TARGETOBJECT = 42, HOOK_COUNT }; diff --git a/sfall/Modules/HookScripts/CombatHs.cpp b/sfall/Modules/HookScripts/CombatHs.cpp index 08068757..c8e2c39a 100644 --- a/sfall/Modules/HookScripts/CombatHs.cpp +++ b/sfall/Modules/HookScripts/CombatHs.cpp @@ -491,34 +491,30 @@ static long __fastcall TargetObjectHook(DWORD isValid, DWORD object, long type) BeginHook(); argCount = 3; - args[0] = type; // 0 - mouse hover on target, 1 - mouse click target + args[0] = type; // 0 - mouse hovering over target, 1 - mouse clicking on target args[1] = isValid; // 1 - target is valid args[2] = object; // target object - if (isValid == 0) object = 0; + if (isValid == 0) object = 0; // ??? + if (type == 0) targetRet = 0; // unset ret from the previous execution of the hook - RunHookScript(HOOK_TARGETOBJ); + RunHookScript(HOOK_TARGETOBJECT); - if (type == 0) { - targetRet = 0; - if (cRet > 0) { - targetRet = (rets[0] != 0) ? rets[0] : object; // 0 - default object, -1 - invalid target, or object override - object = (targetRet != -1) ? targetRet : 0; // object can't be -1 - } - } else if (targetRet) { - if (targetRet != -1) object = targetRet; - targetRet = 0; + if (cRet > 0) { + targetRet = (rets[0] != 0) ? rets[0] : object; // 0 - default object, -1 - invalid target, or object override + object = (targetRet != -1) ? targetRet : 0; // object can't be -1 + } else if (type == 1 && targetRet != -1) { + object = targetRet; } EndHook(); - return object; // null or object } static void __declspec(naked) gmouse_bk_process_hook() { __asm { push 0; // type - mov ecx, eax; // valid or object - mov edx, edi; // object + mov ecx, eax; // 1 - valid (object) or 0 - invalid + mov edx, edi; // object under mouse call TargetObjectHook; mov edi, eax; retn; @@ -527,15 +523,16 @@ static void __declspec(naked) gmouse_bk_process_hook() { static void __declspec(naked) gmouse_handle_event_hook() { __asm { - push 1; // type - mov ecx, eax; + push 1; // type + mov ecx, eax; // 1 - valid (object) or 0 - invalid cmp dword ptr ds:[targetRet], 0; je default; // override - xor edx, edx; + mov ecx, 1; + xor eax, eax; cmp dword ptr ds:[targetRet], -1; - cmovne edx, targetRet; // 0 or object - mov ecx, edx; // set valid + cmove ecx, eax; // if true - set invalid + cmovne edx, targetRet; // if false - set override object default: call TargetObjectHook; mov edx, eax; @@ -637,7 +634,7 @@ void InitCombatHookScripts() { LoadHookScript("hs_combatturn", HOOK_COMBATTURN); LoadHookScript("hs_onexplosion", HOOK_ONEXPLOSION); LoadHookScript("hs_subcombatdmg", HOOK_SUBCOMBATDAMAGE); - LoadHookScript("hs_targetobj", HOOK_TARGETOBJ); + LoadHookScript("hs_targetobject", HOOK_TARGETOBJECT); } } diff --git a/sfall/Modules/Scripting/Handlers/Metarule.cpp b/sfall/Modules/Scripting/Handlers/Metarule.cpp index 3aa21e19..7ed9ed44 100644 --- a/sfall/Modules/Scripting/Handlers/Metarule.cpp +++ b/sfall/Modules/Scripting/Handlers/Metarule.cpp @@ -88,12 +88,10 @@ static const SfallMetarule metarules[] = { {"get_metarule_table", sf_get_metarule_table, 0, 0}, {"get_object_ai_data", sf_get_object_ai_data, 2, 2, -1, {ARG_OBJECT, ARG_INT}}, {"get_object_data", sf_get_object_data, 2, 2, 0, {ARG_OBJECT, ARG_INT}}, - {"get_objects_at_radius", sf_get_objects_at_radius, 3, 4, 0, {ARG_INT, ARG_INT, ARG_INT, ARG_INT}}, {"get_outline", sf_get_outline, 1, 1, 0, {ARG_OBJECT}}, {"get_sfall_arg_at", sf_get_sfall_arg_at, 1, 1, 0, {ARG_INT}}, {"get_string_pointer", sf_get_string_pointer, 1, 1, 0, {ARG_STRING}}, {"get_text_width", sf_get_text_width, 1, 1, 0, {ARG_STRING}}, - {"get_tile", sf_get_tile, 2, 2, -1, {ARG_INT, ARG_INT}}, {"has_fake_perk_npc", sf_has_fake_perk_npc, 2, 2, 0, {ARG_OBJECT, ARG_STRING}}, {"has_fake_trait_npc", sf_has_fake_trait_npc, 2, 2, 0, {ARG_OBJECT, ARG_STRING}}, {"hide_window", sf_hide_window, 0, 1, -1, {ARG_STRING}}, @@ -108,7 +106,8 @@ static const SfallMetarule metarules[] = { {"loot_obj", sf_get_loot_object, 0, 0}, {"metarule_exist", sf_metarule_exist, 1, 1}, // no arg check {"npc_engine_level_up", sf_npc_engine_level_up, 1, 1}, - {"obj_under_cursor", sf_get_obj_under_cursor, 2, 2, 0, {ARG_INT, ARG_INT}}, + {"obj_under_cursor", sf_obj_under_cursor, 2, 2, 0, {ARG_INT, ARG_INT}}, + {"objects_in_radius", sf_objects_in_radius, 3, 4, 0, {ARG_INT, ARG_INT, ARG_INT, ARG_INT}}, {"outlined_object", sf_outlined_object, 0, 0}, {"real_dude_obj", sf_real_dude_obj, 0, 0}, {"remove_timer_event", sf_remove_timer_event, 0, 1, -1, {ARG_INT}}, @@ -135,6 +134,7 @@ static const SfallMetarule metarules[] = { {"spatial_radius", sf_spatial_radius, 1, 1, 0, {ARG_OBJECT}}, {"string_compare", sf_string_compare, 2, 3, 0, {ARG_STRING, ARG_STRING, ARG_INT}}, {"string_format", sf_string_format, 2, 5, 0, {ARG_STRING, ARG_ANY, ARG_ANY, ARG_ANY, ARG_ANY}}, + {"tile_by_position", sf_tile_by_position, 2, 2, -1, {ARG_INT, ARG_INT}}, {"tile_refresh_display", sf_tile_refresh_display, 0, 0}, {"unjam_lock", sf_unjam_lock, 1, 1, -1, {ARG_OBJECT}}, {"unwield_slot", sf_unwield_slot, 2, 2, -1, {ARG_OBJECT, ARG_INT}}, diff --git a/sfall/Modules/Scripting/Handlers/Objects.cpp b/sfall/Modules/Scripting/Handlers/Objects.cpp index 65310156..e3fb6eb0 100644 --- a/sfall/Modules/Scripting/Handlers/Objects.cpp +++ b/sfall/Modules/Scripting/Handlers/Objects.cpp @@ -371,7 +371,7 @@ void sf_get_dialog_object(OpcodeContext& ctx) { ctx.setReturn(InDialog() ? fo::var::dialog_target : 0); } -void sf_get_obj_under_cursor(OpcodeContext& ctx) { +void sf_obj_under_cursor(OpcodeContext& ctx) { ctx.setReturn(fo::func::object_under_mouse(ctx.arg(0).asBool() ? 1 : -1, ctx.arg(1).rawValue(), fo::var::map_elevation)); } @@ -520,14 +520,15 @@ void sf_set_unique_id(OpcodeContext& ctx) { ctx.setReturn(id); } -void sf_get_objects_at_radius(OpcodeContext& ctx) { +void sf_objects_in_radius(OpcodeContext& ctx) { long radius = ctx.arg(1).rawValue(); - if (radius <= 0) radius = 1; + if (radius <= 0) radius = 1; else if (radius > 50) radius = 50; long elev = ctx.arg(2).rawValue(); if (elev < 0) elev = 0; else if (elev > 2) elev = 2; long type = (ctx.numArgs() > 3) ? ctx.arg(3).rawValue() : -1; std::vector objects; + objects.reserve(25); fo::GetObjectsTileRadius(objects, ctx.arg(0).rawValue(), radius, elev, type); size_t sz = objects.size(); DWORD id = TempArray(sz, 0); diff --git a/sfall/Modules/Scripting/Handlers/Objects.h b/sfall/Modules/Scripting/Handlers/Objects.h index afd4f946..47b4f22c 100644 --- a/sfall/Modules/Scripting/Handlers/Objects.h +++ b/sfall/Modules/Scripting/Handlers/Objects.h @@ -93,7 +93,7 @@ void sf_get_current_inven_size(OpcodeContext&); void sf_get_dialog_object(OpcodeContext&); -void sf_get_obj_under_cursor(OpcodeContext&); +void sf_obj_under_cursor(OpcodeContext&); void sf_get_loot_object(OpcodeContext&); @@ -111,7 +111,7 @@ void sf_set_drugs_data(OpcodeContext&); void sf_set_unique_id(OpcodeContext&); -void sf_get_objects_at_radius(OpcodeContext&); +void sf_objects_in_radius(OpcodeContext&); } } diff --git a/sfall/Modules/Scripting/Handlers/Worldmap.cpp b/sfall/Modules/Scripting/Handlers/Worldmap.cpp index 353da54d..8b44a11d 100644 --- a/sfall/Modules/Scripting/Handlers/Worldmap.cpp +++ b/sfall/Modules/Scripting/Handlers/Worldmap.cpp @@ -253,7 +253,7 @@ void sf_get_rest_on_map(OpcodeContext& ctx) { ctx.setReturn(result); } -void sf_get_tile(OpcodeContext& ctx) { +void sf_tile_by_position(OpcodeContext& ctx) { ctx.setReturn(fo::func::tile_num(ctx.arg(0).rawValue(), ctx.arg(1).rawValue())); } diff --git a/sfall/Modules/Scripting/Handlers/Worldmap.h b/sfall/Modules/Scripting/Handlers/Worldmap.h index 1d2a7c1f..ed506622 100644 --- a/sfall/Modules/Scripting/Handlers/Worldmap.h +++ b/sfall/Modules/Scripting/Handlers/Worldmap.h @@ -56,7 +56,7 @@ void sf_set_rest_on_map(OpcodeContext&); void sf_get_rest_on_map(OpcodeContext&); -void sf_get_tile(OpcodeContext&); +void sf_tile_by_position(OpcodeContext&); } } From a68498453c6b13d944f469fbecd301d9a5ed0e80 Mon Sep 17 00:00:00 2001 From: NovaRain Date: Sat, 14 Dec 2019 00:32:16 +0800 Subject: [PATCH 29/36] Edited the description of hook/functions in the previous commit Rewrote ASM code for tile_under_cursor script function. --- artifacts/scripting/hookscripts.txt | 9 +++--- artifacts/scripting/sfall function notes.txt | 8 ++--- sfall/FalloutEngine/Fallout2.h | 2 +- sfall/Modules/Perks.cpp | 7 ++-- sfall/Modules/Scripting/Handlers/Misc.cpp | 34 +++++++------------- 5 files changed, 26 insertions(+), 34 deletions(-) diff --git a/artifacts/scripting/hookscripts.txt b/artifacts/scripting/hookscripts.txt index 05187799..29c6b4ff 100644 --- a/artifacts/scripting/hookscripts.txt +++ b/artifacts/scripting/hookscripts.txt @@ -653,10 +653,11 @@ int ret1 - pass -1 to cancel the execution of the handler (only for HOOK_STD HOOK_TARGETOBJECT (hs_targetobject.int) -Runs whenever the targeting cursor hovers over something. +Runs when the targeting cursor moves over an object, or when the player tries to attack the target object. +You can override the target object or prevent the player from attacking the chosen target. -int arg1 - event type: 0 - when hovering over the target, 1 - when clicking on the target -int arg2 - 1 if the target is a valid object, 0 otherwise +int arg1 - event type: 0 - when the cursor moves over the object, 1 - when trying to attack the target object +int arg2 - 1 when the target object is valid to attack, 0 otherwise Obj arg3 - the target object -int ret1 - overrides the target object +mixed ret1 - overrides the target object, or pass -1 to prevent the player from attacking the object diff --git a/artifacts/scripting/sfall function notes.txt b/artifacts/scripting/sfall function notes.txt index 01ca2330..6a1b8518 100644 --- a/artifacts/scripting/sfall function notes.txt +++ b/artifacts/scripting/sfall function notes.txt @@ -690,12 +690,12 @@ optional argument: > array sfall_func3("objects_in_radius", int tile, int radius, int elevation) > array sfall_func4("objects_in_radius", int tile, int radius, int elevation, int type) - returns an array of objects of a type (see OBJ_TYPE_* constants in define_extra.h) within the specified radius from the given tile -- The radius is limited to 50 hexes -- passing -1 to the type argument or not specifying it will return all types of objects +- passing -1 to the type argument or not specifying it will return all objects within the radius +- the radius is limited to 50 hexes > int sfall_func2("tile_by_position", int x, int y) -- returns the tile number at the x/y position relative to the top-left corner of the screen -- returns -1 if the position is outside of the screen +- returns the tile number at the x, y position relative to the top-left corner of the screen +- if the position is outside of the range of tiles, it will return -1 ------------------------ ------ MORE INFO ------- diff --git a/sfall/FalloutEngine/Fallout2.h b/sfall/FalloutEngine/Fallout2.h index 37e69ff0..6a6d8d1a 100644 --- a/sfall/FalloutEngine/Fallout2.h +++ b/sfall/FalloutEngine/Fallout2.h @@ -68,7 +68,7 @@ /* Returns the value to the script - eax register must contain the script_ptr + eax and ebx register must contain the script_ptr edx register must contain the returned value */ #define _RET_VAL_INT __asm { \ diff --git a/sfall/Modules/Perks.cpp b/sfall/Modules/Perks.cpp index 37dfde0b..b259520d 100644 --- a/sfall/Modules/Perks.cpp +++ b/sfall/Modules/Perks.cpp @@ -689,7 +689,7 @@ normalPerk: pop edx; test eax, eax; jnz end; - // fix gain perks + // fix gain perks (add to base stats instead of bonus stats) cmp edx, PERK_gain_strength_perk; jl end; cmp edx, PERK_gain_luck_perk; @@ -868,7 +868,7 @@ static void PerkSetup() { if (perksEnable) { char num[4]; for (int i = 0; i < PERK_count; i++) { - _itoa_s(i, num, 10); + _itoa(i, num, 10); if (iniGetString(num, "Name", "", &Name[i * maxNameLen], maxNameLen - 1, perksFile)) { perks[i].name = &Name[i * maxNameLen]; } @@ -1450,10 +1450,11 @@ void Perks::init() { FastShotTraitFix(); + // Disable gain perks for bonus stats for (int i = STAT_st; i <= STAT_lu; i++) SafeWrite8(GainStatPerks[i][0], (BYTE)GainStatPerks[i][1]); PerkEngineInit(); - HookCall(0x442729, PerkInitWrapper); // game_init_ + HookCall(0x442729, PerkInitWrapper); // game_init_ if (GetConfigString("Misc", "PerksFile", "", &perksFile[2], MAX_PATH - 3)) { perksFile[0] = '.'; diff --git a/sfall/Modules/Scripting/Handlers/Misc.cpp b/sfall/Modules/Scripting/Handlers/Misc.cpp index e6ceff47..7e2394cd 100644 --- a/sfall/Modules/Scripting/Handlers/Misc.cpp +++ b/sfall/Modules/Scripting/Handlers/Misc.cpp @@ -937,29 +937,19 @@ end: void __declspec(naked) op_tile_under_cursor() { __asm { - push edx; - push ecx; - push ebx; - mov ecx, eax; - sub esp, 8; - lea edx, [esp]; - lea eax, [esp+4]; + mov esi, ebx; + sub esp, 8; + lea edx, [esp]; + lea eax, [esp + 4]; call fo::funcoffs::mouse_get_position_; - mov ebx, dword ptr ds:[FO_VAR_map_elevation]; - mov edx, [esp]; - mov eax, [esp+4]; - call fo::funcoffs::tile_num_; - mov edx, eax; - mov eax, ecx; - call fo::funcoffs::interpretPushLong_; - mov eax, ecx; - mov edx, VAR_TYPE_INT; - call fo::funcoffs::interpretPushShort_; - add esp, 8; - pop ebx; - pop ecx; - pop edx; - retn; + pop edx; + pop eax; + call fo::funcoffs::tile_num_; // ebx - unused + mov edx, eax; // tile + mov ebx, esi; + mov eax, esi; + _J_RET_VAL_TYPE(VAR_TYPE_INT); +// retn; } } From 9f3299b47a358ec47a504dfe9d15ca1c5664e6ad Mon Sep 17 00:00:00 2001 From: NovaRain Date: Sat, 14 Dec 2019 10:49:08 +0800 Subject: [PATCH 30/36] Restored loading global shaders at game start from pre-3.0 Added GlobalShaderFile option to ddraw.ini. Added an example global shader file to modderspack. --- artifacts/ddraw.ini | 4 + .../example_mods/GlobalShaders/global.fx | 116 ++++++++++++++++++ sfall/Modules/Graphics.cpp | 15 ++- sfall/Modules/ScriptShaders.cpp | 34 ++++- sfall/Modules/ScriptShaders.h | 2 + 5 files changed, 159 insertions(+), 12 deletions(-) create mode 100644 artifacts/example_mods/GlobalShaders/global.fx diff --git a/artifacts/ddraw.ini b/artifacts/ddraw.ini index f83d7498..fadd2440 100644 --- a/artifacts/ddraw.ini +++ b/artifacts/ddraw.ini @@ -80,6 +80,10 @@ Mode=0 GraphicsWidth=0 GraphicsHeight=0 +;Uncomment the option to use a hardware shader (requires DX9 graphics mode 4 or 5) +;The shader file .fx must be placed in data\shaders\ and must contain one technique with one or more passes +;GlobalShaderFile=global.fx + ;Set to 1 to do the palette conversion on the GPU ;Set to 2 to do the palette conversion on the CPU ;Set to 0 to pick automatically diff --git a/artifacts/example_mods/GlobalShaders/global.fx b/artifacts/example_mods/GlobalShaders/global.fx new file mode 100644 index 00000000..9f68917a --- /dev/null +++ b/artifacts/example_mods/GlobalShaders/global.fx @@ -0,0 +1,116 @@ +// Status: WIP + +texture bloomMap; +sampler s0; + +sampler BloomSampler : samplerstate +{ + Texture = bloomMap; + MinFilter = Linear; + MagFilter = Linear; + AddressU = Clamp; + AddressV = Clamp; +}; + +// pseudo gauss blur +static const float2 offsets[12] = { + -0.326212, -0.405805, + -0.840144, -0.073580, + -0.695914, 0.457137, + -0.203345, 0.620716, + 0.962340, -0.194983, + 0.473434, -0.480026, + 0.519456, 0.767022, + 0.185461, -0.893124, + 0.507431, 0.064425, + 0.896420, 0.412458, + -0.321940, -0.932615, + -0.791559, -0.597705, +}; + +float w; +float h; +static const float2 resolution = float2(w, h); + +// blur setting +static const float blurFalloff = 8; +static const float sharpness = 4; + +float4 AdjustSaturation(float4 color, float saturation) { + float grey = dot(color, float3(0.3, 0.59, 0.11)); + + return lerp(grey, color, saturation); +} + +float Brightness(float3 color) +{ + return color.r * color.g - 0.75 * color.b; +} + +float3 BlurFunction(float2 uv, float brightness, inout float totalWeight) +{ + float3 pointColor =(tex2Dlod(s0, float4(uv, 0, 0)).rgb); + float diff = abs(brightness - Brightness(pointColor)); + float weight = exp2(-0.25 * blurFalloff - diff * sharpness); + totalWeight += weight; + + return pointColor * weight; +} + +float4 BlurPS(float2 uv : TEXCOORD0, uniform float2 delta) : COLOR0 +{ + float3 sumColor = (tex2D(s0, uv).rgb); + float totalWeight = 1; + float brightness = Brightness(sumColor); + delta *= (1 / resolution); + float radius = 1; + + float2 pointUV = uv + delta * radius; + sumColor += BlurFunction(pointUV, brightness, totalWeight); + pointUV = uv - delta * radius; + sumColor += BlurFunction(pointUV, brightness, totalWeight); + + return float4((sumColor / totalWeight),1); +// return AdjustSaturation(Color, 0.5); +} + +float4 BloomT(float2 texCoord : TEXCOORD0) : COLOR0 { + float4 c = tex2D(s0, texCoord); + float BloomThreshold = 0.2; + + return saturate((c - BloomThreshold) / (1 - BloomThreshold)); +} + +float4 Bloom(float4 color : COLOR0, float2 texCoord : TEXCOORD0) : COLOR0 { + float BaseIntensity = 1.0; + float BaseSaturation = 1; + float BloomIntensity = 1; // 0.4 + float BloomSaturation = 0.5; + float BlurPower = 0.01; + float BloomThreshold = 0.5; + + float4 original = tex2D(s0, texCoord); + + // blur + float4 sum = tex2D(s0, texCoord); + for(int i = 0; i < 12; i++){ + sum += tex2D(s0, texCoord + BlurPower * offsets[i]); + } + sum /= 13; + + original = AdjustSaturation(original, BaseSaturation) * BaseIntensity; + sum = AdjustSaturation(sum, BloomSaturation) * BloomIntensity; + + sum = saturate((sum - BloomThreshold) / (1 - BloomThreshold)); + + return sum + original; +// return sum; +} + +technique Blur +{ +// pass P2 { PixelShader = compile ps_2_0 BloomT(); } +// pass P3 { PixelShader = compile ps_2_0 Bloom(); } + Pass P0 { PixelShader = compile ps_3_0 BlurPS(float2(1, 0)); } + Pass P1 { PixelShader = compile ps_3_0 BlurPS(float2(0, 1)); } +} diff --git a/sfall/Modules/Graphics.cpp b/sfall/Modules/Graphics.cpp index 49ed7a78..ee833211 100644 --- a/sfall/Modules/Graphics.cpp +++ b/sfall/Modules/Graphics.cpp @@ -183,9 +183,11 @@ int _stdcall GetShaderVersion() { return ShaderVersion; } -static void rcpresInit() { +static void WindowInit() { + windowInit = true; rcpres[0] = 1.0f / (float)Graphics::GetGameWidthRes(); rcpres[1] = 1.0f / (float)Graphics::GetGameHeightRes(); + ScriptShaders::LoadGlobalShader(); } const float* Graphics::rcpresGet() { @@ -962,6 +964,9 @@ HRESULT _stdcall FakeDirectDrawCreate2_Init(void*, IDirectDraw** b, void*) { ScrollWindowKey = GetConfigInt("Input", "WindowScrollKey", 0); } else ScrollWindowKey = 0; + rcpres[0] = 1.0f / (float)gWidth; + rcpres[1] = 1.0f / (float)gHeight; + *b = (IDirectDraw*)new FakeDirectDraw2(); dlogr(" Done.", DL_MAIN); @@ -970,7 +975,9 @@ HRESULT _stdcall FakeDirectDrawCreate2_Init(void*, IDirectDraw** b, void*) { static __declspec(naked) void game_init_hook() { __asm { - mov windowInit, 1; + push ecx; + call WindowInit; + pop ecx; jmp fo::funcoffs::palette_init_; } } @@ -1016,10 +1023,6 @@ void Graphics::init() { fadeMulti = ((double)fadeMulti) / 100.0; dlogr(" Done", DL_INIT); } - - if (Graphics::mode) { - LoadGameHook::OnAfterGameInit() += rcpresInit; - } } void Graphics::exit() { diff --git a/sfall/Modules/ScriptShaders.cpp b/sfall/Modules/ScriptShaders.cpp index 37f58554..a8cadba9 100644 --- a/sfall/Modules/ScriptShaders.cpp +++ b/sfall/Modules/ScriptShaders.cpp @@ -29,6 +29,9 @@ namespace sfall static size_t shadersSize; +static bool globalShaderActive = false; +std::string gShaderFile; + struct sShader { ID3DXEffect* Effect; D3DXHANDLE ehTicks; @@ -55,14 +58,17 @@ void _stdcall SetShaderMode(DWORD d, DWORD mode) { } } -int _stdcall LoadShader(const char* path) { - if (!Graphics::mode || strstr(path, "..") || strstr(path, ":")) return -1; +int _stdcall LoadShader(const char* file) { + if (!Graphics::mode || strstr(file, "..") || strstr(file, ":")) return -1; char buf[MAX_PATH]; - sprintf_s(buf, "%s\\shaders\\%s", fo::var::patches, path); + sprintf_s(buf, "%s\\shaders\\%s", fo::var::paths->path, file); // fo::var::patches for (DWORD d = 0; d < shadersSize; d++) { if (!shaders[d].Effect) { - if (FAILED(D3DXCreateEffectFromFile(d3d9Device, buf, 0, 0, 0, 0, &shaders[d].Effect, 0))) return -1; - else return d; + if (FAILED(D3DXCreateEffectFromFile(d3d9Device, buf, 0, 0, 0, 0, &shaders[d].Effect, 0))) { + return -1; + } else { + return d; + } } } sShader shader = sShader(); @@ -89,6 +95,17 @@ int _stdcall LoadShader(const char* path) { return shadersSize - 1; } +void ScriptShaders::LoadGlobalShader() { + if (!globalShaderActive) return; + long index = LoadShader(gShaderFile.c_str()); + if (index != -1) { + shaders[index].Effect->SetInt("w", Graphics::GetGameWidthRes()); + shaders[index].Effect->SetInt("h", Graphics::GetGameHeightRes()); + shaders[index].Active = true; + dlogr("Global shader file loaded.", DL_INIT); + } +} + void _stdcall ActivateShader(DWORD d) { if (d < shadersSize && shaders[d].Effect) shaders[d].Active = true; } @@ -137,10 +154,11 @@ void _stdcall SetShaderTexture(DWORD d, const char* param, DWORD value) { shaders[d].Effect->SetTexture(param, shaderTextures[value]); } -void ResetShaders() { +static void ResetShaders() { for (DWORD d = 0; d < shadersSize; d++) SAFERELEASE(shaders[d].Effect); shaders.clear(); shadersSize = 0; + ScriptShaders::LoadGlobalShader(); } void ScriptShaders::Refresh(IDirect3DSurface9* sSurf1, IDirect3DSurface9* sSurf2, IDirect3DTexture9* sTex2) { @@ -177,6 +195,7 @@ void ScriptShaders::OnLostDevice() { } void ScriptShaders::Release() { + globalShaderActive = false; ResetShaders(); for (DWORD d = 0; d < shaderTextures.size(); d++) shaderTextures[d]->Release(); shaderTextures.clear(); @@ -184,6 +203,9 @@ void ScriptShaders::Release() { void ScriptShaders::init() { if (Graphics::mode) { + gShaderFile = GetConfigString("Graphics", "GlobalShaderFile", "", MAX_PATH); + globalShaderActive = (gShaderFile.length() > 3); + LoadGameHook::OnGameReset() += ResetShaders; } } diff --git a/sfall/Modules/ScriptShaders.h b/sfall/Modules/ScriptShaders.h index a76d0fea..9d0bbfb3 100644 --- a/sfall/Modules/ScriptShaders.h +++ b/sfall/Modules/ScriptShaders.h @@ -18,6 +18,8 @@ public: static void OnResetDevice(); static void OnLostDevice(); static void Release(); + + static void LoadGlobalShader(); }; int _stdcall LoadShader(const char*); From 7ed8dd286c250e0a0a0c4b78f124fce8ec7c72d5 Mon Sep 17 00:00:00 2001 From: NovaRain Date: Sun, 15 Dec 2019 09:19:57 +0800 Subject: [PATCH 31/36] Fixed the path to load global shaders in the previous commit * when DataLoadOrderPatch is disabled, the path was set to "sfall.dat\shaders\". --- artifacts/ddraw.ini | 2 +- artifacts/scripting/sfall function notes.txt | 2 +- sfall/FalloutEngine/VariableOffsets.h | 1 + sfall/FalloutEngine/Variables_def.h | 4 +-- sfall/Modules/Graphics.cpp | 2 +- sfall/Modules/LoadOrder.cpp | 26 +++++++++++++++----- sfall/Modules/ScriptShaders.cpp | 4 +-- 7 files changed, 28 insertions(+), 13 deletions(-) diff --git a/artifacts/ddraw.ini b/artifacts/ddraw.ini index fadd2440..b909b993 100644 --- a/artifacts/ddraw.ini +++ b/artifacts/ddraw.ini @@ -81,7 +81,7 @@ GraphicsWidth=0 GraphicsHeight=0 ;Uncomment the option to use a hardware shader (requires DX9 graphics mode 4 or 5) -;The shader file .fx must be placed in data\shaders\ and must contain one technique with one or more passes +;The shader file .fx must be placed in \\shaders\ and must contain one technique with one or more passes ;GlobalShaderFile=global.fx ;Set to 1 to do the palette conversion on the GPU diff --git a/artifacts/scripting/sfall function notes.txt b/artifacts/scripting/sfall function notes.txt index 6a1b8518..513d0c04 100644 --- a/artifacts/scripting/sfall function notes.txt +++ b/artifacts/scripting/sfall function notes.txt @@ -24,7 +24,7 @@ The input functions are only available if the user has the input hook turned on The graphics functions are only available if the user is using graphics mode 4 or 5. Use graphics_funcs_available to check; it returns 1 if you can use them or 0 if you can't. Calling graphics functions when graphics_funcs_available returns 0 will do nothing. -load_shader takes a path relative to the data\shaders\ directory as an argument and returns a shader ID. That ID should be passed as the first argument to all other shader functions, and is valid until free_shader is called on the ID, the player loads a saved game or the player quits to the main menu. +load_shader takes a path relative to the \\shaders\ directory as an argument and returns a shader ID. That ID should be passed as the first argument to all other shader functions, and is valid until free_shader is called on the ID, the player loads a saved game or the player quits to the main menu. get_shader_version gives you the highest shader version supported by the player's graphics cards. Possible return values are 11, 12, 13, 14, 20, 21 and 30. diff --git a/sfall/FalloutEngine/VariableOffsets.h b/sfall/FalloutEngine/VariableOffsets.h index 7556878f..6506e952 100644 --- a/sfall/FalloutEngine/VariableOffsets.h +++ b/sfall/FalloutEngine/VariableOffsets.h @@ -77,6 +77,7 @@ #define FO_VAR_game_global_vars 0x5186C0 #define FO_VAR_game_ui_disabled 0x5186B4 #define FO_VAR_game_user_wants_to_quit 0x5186CC +#define FO_VAR_gconfig_file_name 0x58E978 #define FO_VAR_gcsd 0x51094C #define FO_VAR_gdBarterMod 0x51873C #define FO_VAR_gDialogMusicVol 0x5187D8 diff --git a/sfall/FalloutEngine/Variables_def.h b/sfall/FalloutEngine/Variables_def.h index 73006b7b..e2952ce2 100644 --- a/sfall/FalloutEngine/Variables_def.h +++ b/sfall/FalloutEngine/Variables_def.h @@ -27,7 +27,7 @@ VAR_(combat_state, DWORD) VAR_(combat_turn_running, DWORD) VAR_(combatNumTurns, DWORD) VAR3(crit_succ_eff, CritInfo, 20, 9, 6) // 20 critters with 9 body parts and 6 effects each -VAR_(critter_db_handle, DWORD) +VAR_(critter_db_handle, PathNode*) VAR_(critterClearObj, DWORD) VAR_(crnt_func, DWORD) VAR_(curr_font_num, DWORD) @@ -108,7 +108,7 @@ VAR_(main_ctd, DWORD) VAR_(main_window, DWORD) VAR_(map_elevation, DWORD) VAR_(map_global_vars, long*) // array -VAR_(master_db_handle, DWORD) +VAR_(master_db_handle, PathNode*) VAR_(max, DWORD) VAR_(maxScriptNum, long) VAR_(Meet_Frank_Horrigan, bool) diff --git a/sfall/Modules/Graphics.cpp b/sfall/Modules/Graphics.cpp index ee833211..c2f895cc 100644 --- a/sfall/Modules/Graphics.cpp +++ b/sfall/Modules/Graphics.cpp @@ -1006,11 +1006,11 @@ void Graphics::init() { if (!h) { MessageBoxA(0, "You have selected graphics mode 4 or 5, but " _DLL_NAME " is missing.\n" "Switch back to mode 0, or install an up to date version of DirectX.", "Error", MB_TASKMODAL | MB_ICONERROR); +#undef _DLL_NAME ExitProcess(-1); } else { FreeLibrary(h); } -#undef _DLL_NAME SafeWrite8(0x50FB6B, '2'); // Set call DirectDrawCreate2 HookCall(0x44260C, game_init_hook); dlogr(" Done", DL_INIT); diff --git a/sfall/Modules/LoadOrder.cpp b/sfall/Modules/LoadOrder.cpp index 2f140a34..ee6ee12e 100644 --- a/sfall/Modules/LoadOrder.cpp +++ b/sfall/Modules/LoadOrder.cpp @@ -153,7 +153,7 @@ end: } } -static void __stdcall InitExtraPatches() { +static void InitExtraPatches() { for (auto it = patchFiles.begin(); it != patchFiles.end(); it++) { if (!it->empty()) fo::func::db_init(it->c_str(), 0); } @@ -163,11 +163,11 @@ static void __stdcall InitExtraPatches() { } static void __fastcall game_init_databases_hook() { // eax = _master_db_handle - fo::PathNode* master_patches = (fo::PathNode*)fo::var::master_db_handle; + fo::PathNode* master_patches = fo::var::master_db_handle; - if (!patchFiles.empty()) InitExtraPatches(); + /*if (!patchFiles.empty())*/ InitExtraPatches(); - fo::PathNode* critter_patches = (fo::PathNode*)fo::var::critter_db_handle; + fo::PathNode* critter_patches = fo::var::critter_db_handle; fo::PathNode* paths = fo::var::paths; // beginning of the chain of paths // insert master_patches/critter_patches at the beginning of the chain of paths if (critter_patches) { @@ -178,6 +178,20 @@ static void __fastcall game_init_databases_hook() { // eax = _master_db_handle fo::var::paths = master_patches; // set master_patches node at the beginning of the chain of paths } +static void __fastcall game_init_databases_hook1() { + char masterPatch[MAX_PATH]; + iniGetString("system", "master_patches", "", masterPatch, MAX_PATH - 1, (const char*)FO_VAR_gconfig_file_name); + + fo::PathNode* node = fo::var::paths; + while (node->next) { + if (!_stricmp(node->path, masterPatch)) break; + node = node->next; + } + fo::var::master_db_handle = node; // set pointer to master_patches node + + InitExtraPatches(); +} + static bool NormalizePath(std::string &path) { if (path.find(':') != std::string::npos) return false; int pos = 0; @@ -388,8 +402,8 @@ void LoadOrder::init() { HookCall(0x44436D, game_init_databases_hook); SafeWrite8(0x4DFAEC, 0x1D); // error correction (ecx > ebx) dlogr(" Done", DL_INIT); - } else if (!patchFiles.empty()) { - HookCall(0x44436D, InitExtraPatches); + } else /*if (!patchFiles.empty())*/ { + HookCall(0x44436D, game_init_databases_hook1); } femaleMsgs = GetConfigInt("Misc", "FemaleDialogMsgs", 0); diff --git a/sfall/Modules/ScriptShaders.cpp b/sfall/Modules/ScriptShaders.cpp index a8cadba9..784b3ffd 100644 --- a/sfall/Modules/ScriptShaders.cpp +++ b/sfall/Modules/ScriptShaders.cpp @@ -61,7 +61,7 @@ void _stdcall SetShaderMode(DWORD d, DWORD mode) { int _stdcall LoadShader(const char* file) { if (!Graphics::mode || strstr(file, "..") || strstr(file, ":")) return -1; char buf[MAX_PATH]; - sprintf_s(buf, "%s\\shaders\\%s", fo::var::paths->path, file); // fo::var::patches + sprintf_s(buf, "%s\\shaders\\%s", fo::var::master_db_handle->path, file); // fo::var::patches for (DWORD d = 0; d < shadersSize; d++) { if (!shaders[d].Effect) { if (FAILED(D3DXCreateEffectFromFile(d3d9Device, buf, 0, 0, 0, 0, &shaders[d].Effect, 0))) { @@ -82,7 +82,7 @@ int _stdcall LoadShader(const char* file) { sprintf(buf, "texname%d", i); if (FAILED(shader.Effect->GetString(buf, &name))) break; - sprintf_s(buf, "%s\\art\\stex\\%s", fo::var::patches, name); + sprintf_s(buf, "%s\\art\\stex\\%s", fo::var::master_db_handle->path, name); // fo::var::patches if (FAILED(D3DXCreateTextureFromFileA(d3d9Device, buf, &tex))) continue; sprintf(buf, "tex%d", i); shader.Effect->SetTexture(buf, tex); From cb9f759d7147f3c8c44a5a36e361480c5a076fe1 Mon Sep 17 00:00:00 2001 From: NovaRain Date: Mon, 16 Dec 2019 15:31:50 +0800 Subject: [PATCH 32/36] Replaced _stricmp in previous commit with strcmp. Minor edits to some code. --- artifacts/scripting/hookscripts.txt | 4 ++-- sfall/Modules/Drugs.cpp | 2 +- sfall/Modules/Elevators.cpp | 6 +++--- sfall/Modules/HookScripts/CombatHs.cpp | 4 ++-- sfall/Modules/Inventory.cpp | 8 ++++---- sfall/Modules/LoadOrder.cpp | 2 +- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/artifacts/scripting/hookscripts.txt b/artifacts/scripting/hookscripts.txt index 29c6b4ff..691fd1a9 100644 --- a/artifacts/scripting/hookscripts.txt +++ b/artifacts/scripting/hookscripts.txt @@ -653,10 +653,10 @@ int ret1 - pass -1 to cancel the execution of the handler (only for HOOK_STD HOOK_TARGETOBJECT (hs_targetobject.int) -Runs when the targeting cursor moves over an object, or when the player tries to attack the target object. +Runs when the targeting cursor hovers over an object, or when the player tries to attack the target object. You can override the target object or prevent the player from attacking the chosen target. -int arg1 - event type: 0 - when the cursor moves over the object, 1 - when trying to attack the target object +int arg1 - event type: 0 - when the cursor hovers over the object, 1 - when trying to attack the target object int arg2 - 1 when the target object is valid to attack, 0 otherwise Obj arg3 - the target object diff --git a/sfall/Modules/Drugs.cpp b/sfall/Modules/Drugs.cpp index 6b697448..088471e4 100644 --- a/sfall/Modules/Drugs.cpp +++ b/sfall/Modules/Drugs.cpp @@ -278,7 +278,7 @@ void Drugs::init() { int set = -1; char section[4]; for (int i = 1; i <= count; i++) { - _itoa_s(i, section, 10); + _itoa(i, section, 10); int pid = iniGetInt(section, "PID", 0, iniDrugs); if (pid > 0) { CheckEngineNumEffects(set, pid); diff --git a/sfall/Modules/Elevators.cpp b/sfall/Modules/Elevators.cpp index 7c210234..048ef0cc 100644 --- a/sfall/Modules/Elevators.cpp +++ b/sfall/Modules/Elevators.cpp @@ -127,11 +127,11 @@ static void LoadElevators(const char* elevFile) { elevatorsFrms[i].buttons = iniGetInt(section, "ButtonsFrm", elevatorsFrms[i].buttons, elevFile); char setting[32]; for (int j = 0; j < exitsPerElevator; j++) { - sprintf_s(setting, "ID%d", j + 1); + sprintf(setting, "ID%d", j + 1); elevatorExits[i][j].id = iniGetInt(section, setting, elevatorExits[i][j].id, elevFile); - sprintf_s(setting, "Elevation%d", j + 1); + sprintf(setting, "Elevation%d", j + 1); elevatorExits[i][j].elevation = iniGetInt(section, setting, elevatorExits[i][j].elevation, elevFile); - sprintf_s(setting, "Tile%d", j + 1); + sprintf(setting, "Tile%d", j + 1); elevatorExits[i][j].tile = iniGetInt(section, setting, elevatorExits[i][j].tile, elevFile); } } diff --git a/sfall/Modules/HookScripts/CombatHs.cpp b/sfall/Modules/HookScripts/CombatHs.cpp index c8e2c39a..fd2d3671 100644 --- a/sfall/Modules/HookScripts/CombatHs.cpp +++ b/sfall/Modules/HookScripts/CombatHs.cpp @@ -513,7 +513,7 @@ static long __fastcall TargetObjectHook(DWORD isValid, DWORD object, long type) static void __declspec(naked) gmouse_bk_process_hook() { __asm { push 0; // type - mov ecx, eax; // 1 - valid (object) or 0 - invalid + mov ecx, eax; // 1 - valid(object) or 0 - invalid mov edx, edi; // object under mouse call TargetObjectHook; mov edi, eax; @@ -524,7 +524,7 @@ static void __declspec(naked) gmouse_bk_process_hook() { static void __declspec(naked) gmouse_handle_event_hook() { __asm { push 1; // type - mov ecx, eax; // 1 - valid (object) or 0 - invalid + mov ecx, eax; // 1 - valid(object) or 0 - invalid cmp dword ptr ds:[targetRet], 0; je default; // override diff --git a/sfall/Modules/Inventory.cpp b/sfall/Modules/Inventory.cpp index 233852aa..02d805ef 100644 --- a/sfall/Modules/Inventory.cpp +++ b/sfall/Modules/Inventory.cpp @@ -33,12 +33,12 @@ static Delegate onAdjustFid; static DWORD sizeLimitMode; static DWORD invSizeMaxLimit; + static DWORD reloadWeaponKey = 0; static DWORD itemFastMoveKey = 0; static DWORD skipFromContainer = 0; void InventoryKeyPressedHook(DWORD dxKey, bool pressed) { - // TODO: move this out into a script if (pressed && reloadWeaponKey && dxKey == reloadWeaponKey && IsMapLoaded() && (GetLoopFlags() & ~(COMBAT | PCOMBAT)) == 0) { DWORD maxAmmo, curAmmo; fo::GameObject* item = fo::GetActiveItem(); @@ -282,13 +282,13 @@ static const char* _stdcall SizeInfoMessage(fo::GameObject* item) { if (size == 1) { const char* message = fo::MessageSearch(&fo::var::proto_main_msg_file, 543); if (message == nullptr) - strncpy_s(SizeMsgBuf, "It occupies 1 unit.", _TRUNCATE); + strcpy(SizeMsgBuf, "It occupies 1 unit."); else - _snprintf_s(SizeMsgBuf, _TRUNCATE, message, size); + strncpy_s(SizeMsgBuf, message, _TRUNCATE); } else { const char* message = fo::MessageSearch(&fo::var::proto_main_msg_file, 542); if (message == nullptr) - _snprintf_s(SizeMsgBuf, _TRUNCATE, "It occupies %d units.", size); + sprintf(SizeMsgBuf, "It occupies %d units.", size); else _snprintf_s(SizeMsgBuf, _TRUNCATE, message, size); } diff --git a/sfall/Modules/LoadOrder.cpp b/sfall/Modules/LoadOrder.cpp index ee6ee12e..4f51b262 100644 --- a/sfall/Modules/LoadOrder.cpp +++ b/sfall/Modules/LoadOrder.cpp @@ -184,7 +184,7 @@ static void __fastcall game_init_databases_hook1() { fo::PathNode* node = fo::var::paths; while (node->next) { - if (!_stricmp(node->path, masterPatch)) break; + if (!strcmp(node->path, masterPatch)) break; node = node->next; } fo::var::master_db_handle = node; // set pointer to master_patches node From 7990d230f266c4f083ac5ac7795752a44f454477 Mon Sep 17 00:00:00 2001 From: NovaRain Date: Mon, 23 Dec 2019 23:41:11 +0800 Subject: [PATCH 33/36] Fixed the return value/object for HOOK_TARGETOBJECT --- sfall/Modules/Combat.cpp | 1 - sfall/Modules/HookScripts/CombatHs.cpp | 13 +++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/sfall/Modules/Combat.cpp b/sfall/Modules/Combat.cpp index 3f965dda..1937c82e 100644 --- a/sfall/Modules/Combat.cpp +++ b/sfall/Modules/Combat.cpp @@ -17,7 +17,6 @@ */ #include -#include #include "..\main.h" #include "..\FalloutEngine\Fallout2.h" diff --git a/sfall/Modules/HookScripts/CombatHs.cpp b/sfall/Modules/HookScripts/CombatHs.cpp index fd2d3671..673b2179 100644 --- a/sfall/Modules/HookScripts/CombatHs.cpp +++ b/sfall/Modules/HookScripts/CombatHs.cpp @@ -484,7 +484,9 @@ skip: } } -DWORD targetRet = 0; +static DWORD targetRet = 0; +static bool targetObjHookHasRet = false; + static long __fastcall TargetObjectHook(DWORD isValid, DWORD object, long type) { if (isValid > 1) isValid = 1; @@ -495,7 +497,7 @@ static long __fastcall TargetObjectHook(DWORD isValid, DWORD object, long type) args[1] = isValid; // 1 - target is valid args[2] = object; // target object - if (isValid == 0) object = 0; // ??? + if (isValid == 0) object = 0; // it is necessary for the proper operation of the engine code if (type == 0) targetRet = 0; // unset ret from the previous execution of the hook RunHookScript(HOOK_TARGETOBJECT); @@ -503,8 +505,11 @@ static long __fastcall TargetObjectHook(DWORD isValid, DWORD object, long type) if (cRet > 0) { targetRet = (rets[0] != 0) ? rets[0] : object; // 0 - default object, -1 - invalid target, or object override object = (targetRet != -1) ? targetRet : 0; // object can't be -1 - } else if (type == 1 && targetRet != -1) { - object = targetRet; + targetObjHookHasRet = true; + } + else if (targetObjHookHasRet && type == 1) { + targetObjHookHasRet = false; + if (targetRet != -1) object = targetRet; } EndHook(); return object; // null or object From 7cef7e8e544fb2c6d1b08babf6d3e6c7be9e00db Mon Sep 17 00:00:00 2001 From: NovaRain Date: Tue, 24 Dec 2019 00:55:31 +0800 Subject: [PATCH 34/36] Some edits to hookscripts.txt & function notes.txt --- artifacts/scripting/hookscripts.txt | 50 ++++++++++---------- artifacts/scripting/sfall function notes.txt | 2 +- 2 files changed, 27 insertions(+), 25 deletions(-) diff --git a/artifacts/scripting/hookscripts.txt b/artifacts/scripting/hookscripts.txt index 691fd1a9..72dbb80b 100644 --- a/artifacts/scripting/hookscripts.txt +++ b/artifacts/scripting/hookscripts.txt @@ -41,13 +41,13 @@ Used to return the new values from the script. Each time it's called it sets the > void set_sfall_arg(int argNum, int value) Changes argument value. The argument number (argNum) is 0-indexed. This is useful if you have several hook scripts attached to one hook point (see below). -> void register_hook(int hooktype) +> void register_hook(int hookID) Used from a normal global script if you want to run it at the same point a full hook script would normally run. In case of this function, "start" proc will be executed in a current global script. You can use all above functions like normal. -> void register_hook_proc(int hook, procedure proc) +> void register_hook_proc(int hookID, procedure proc) The same as register_hook, except that you specifically define which procedure in the current script should be called as a hook (instead of "start" by default). Pass procedure the same as how you use dialog option functions. This IS the recommended way to use hook scripts, as it gives both modularity (each mod logic in a separate global script, no conflicts if you don't use "hs_*.int" scripts) and flexibility (you can place all related hook scripts for specific mod in a single script!). -> void register_hook_proc_spec(int hook, procedure proc) +> void register_hook_proc_spec(int hookID, procedure proc) Works very similar to register_hook_proc, except that it registers the current script at the end of the hook script execution chain (i.e. the script will be executed after all previously registered scripts for the same hook, including the hs_*.int script). All scripts hooked to a single hook point with this function are executed in exact order of how they were registered, as opposed to the description below, which refers to using register_hook/register_hook_proc functions. NOTE: you can hook several scripts to a single hook point, for example if it's different mods from different authors or just some different aspects of one larger mod. In this case scripts are executed in reverse order of how they were registered. When one of the scripts in a chain returns value with "set_sfall_return", the next script may override this value if calls "set_sfall_return" again. Sometimes you need to multiply certain value in a chain of hook scripts. @@ -69,7 +69,7 @@ This basically changes hook argument for the next script. Mod B code: So if you combine both mods together, they will run in chain and the end result will be a 75% from original hit chance (hook register order doesn't matter in this case, if you use "set_sfall_arg" in both hooks). -The defines to use for the hooktype are in sfall.h. +The defines to use for the hookID are in sfall.h. ------------------------------------------- ----------- HOOK SCRIPT TYPES ------------- @@ -189,9 +189,11 @@ Critter arg1 - The critter that just died HOOK_FINDTARGET (hs_findtarget.int) -Runs when the ai is trying to pick a target in combat. Fallout first chooses a list of 4 likely suspects, then normally sorts them in order of weakness/distance/etc depending on the ai caps of the attacker. This hook replaces that sorting function, allowing you to sort the targets in some arbitrary way. Use sfall_return to give the 4 targets, in order of preference. If you want to specify less than 4 targets, fill in the extra spaces with 0's. Pass -1 to skip the return value. +Runs when the ai is trying to pick a target in combat. Fallout first chooses a list of 4 likely suspects, then normally sorts them in order of weakness/distance/etc depending on the ai caps of the attacker. +This hook replaces that sorting function, allowing you to sort the targets in some arbitrary way. The return values can include critters that weren't in the list of possible targets, but the additional targets may still be discarded later on in the combat turn if they are out of the attackers perception or the chance of a successful hit is too low. The list of possible targets often includes duplicated entries. +Use sfall_return to give the 4 targets, in order of preference. If you want to specify less than 4 targets, fill in the extra spaces with 0's or pass -1 to skip the return value. critter arg1 - The attacker critter arg2 - A possible target @@ -559,8 +561,8 @@ 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 arg1 - The user critter (usually dude_obj) -Obj arg2 - The target object/critter +Critter arg1 - the user critter (usually dude_obj) +Obj arg2 - the target object/critter int arg3 - skill being used int ret1 - a new critter to override the user critter. Pass -1 to cancel the skill use, pass 0 to skip this return value @@ -574,11 +576,11 @@ Runs when Fallout is checking all the tiles within the explosion radius for targ The tile checking will be interrupted when 6 additional targets (critters) are received. int arg1 - event type: 1 - when checking objects within the explosion radius without causing damage (e.g. the player drops an active explosive), 0 - otherwise -Critter arg2 - The attacker -int arg3 - The tile on which the explosion occurs +Critter arg2 - the attacker +int arg3 - the tile on which the explosion occurs int arg4 - checked tile within the explosion radius Obj arg5 - first found object on the checked tile as an additional target -Critter arg6 - The target critter, may be 0 or equal to the attacker +Critter arg6 - the target critter, may be 0 or equal to the attacker int arg7 - 1 when using throwing weapons (e.g. grenades), 0 otherwise int ret1 - overrides the found object on the checked tile, pass 0 to skip the object @@ -593,20 +595,20 @@ Runs when: 2) AI decides whether it is safe to use area attack (burst, grenades), if he might hit friendlies. Does not run for misses, non-combat damage like dynamite explosions, or if one of the damage formulas is selected in ddraw.ini. -Critter arg1 - The attacker -Critter arg2 - The target -Obj arg3 - The weapon used in the attack -int arg4 - Attack Type (see ATKTYPE_* constants) -int arg5 - Number of bullets actually hit the target (1 for melee attacks) -int arg6 - Target's Damage Resistance (DR) value (affected by critical hit effects, perks, traits, and special unarmed attacks) -int arg7 - Target's Damage Threshold (DT) value (affected by critical hit effects, perks, traits, and special unarmed attacks) -int arg8 - Bonus ranged damage from the perk -int arg9 - Damage Multiplier (this is divided by 2, so a value of 3 does 1.5x damage, and 8 does 4x damage. Usually it's 2; for critical hits, the value is taken from the critical table; with Silent Death perk and the corresponding attack conditions, the value will be doubled) -int arg10 - Combat Difficulty multiplier (125 - rough, 100 - normal, 75 - wimpy; for player or party members it's always 100) -int arg11 - The calculated amount of damage (usually 0, required when using multiple hook scripts to calculate damage and using the set_sfall_arg function) -mixed arg12 - Computed attack results (see C_ATTACK_* for offsets and use get/set_object_data functions to get/set the data) +Critter arg1 - the attacker +Critter arg2 - the target +Obj arg3 - the weapon used in the attack +int arg4 - attack type (see ATKTYPE_* constants) +int arg5 - number of bullets actually hit the target (1 for melee attacks) +int arg6 - target's Damage Resistance (DR) value (affected by critical hit effects, perks, traits, and special unarmed attacks) +int arg7 - target's Damage Threshold (DT) value (affected by critical hit effects, perks, traits, and special unarmed attacks) +int arg8 - bonus ranged damage from the perk +int arg9 - damage multiplier (this is divided by 2, so a value of 3 does 1.5x damage, and 8 does 4x damage. Usually it's 2; for critical hits, the value is taken from the critical table; with Silent Death perk and the corresponding attack conditions, the value will be doubled) +int arg10 - combat difficulty multiplier (125 - rough, 100 - normal, 75 - wimpy; for player or party members it's always 100) +int arg11 - the calculated amount of damage (usually 0, required when using multiple hook scripts to calculate damage and using the set_sfall_arg function) +mixed arg12 - computed attack results (see C_ATTACK_* for offsets and use get/set_object_data functions to get/set the data) -int ret1 - The returned amount of damage +int ret1 - the returned amount of damage ------------------------------------------- @@ -656,7 +658,7 @@ HOOK_TARGETOBJECT (hs_targetobject.int) Runs when the targeting cursor hovers over an object, or when the player tries to attack the target object. You can override the target object or prevent the player from attacking the chosen target. -int arg1 - event type: 0 - when the cursor hovers over the object, 1 - when trying to attack the target object +int arg1 - event type: 0 - when the targeting cursor hovers over the object, 1 - when trying to attack the target object int arg2 - 1 when the target object is valid to attack, 0 otherwise Obj arg3 - the target object diff --git a/artifacts/scripting/sfall function notes.txt b/artifacts/scripting/sfall function notes.txt index 513d0c04..76a417b4 100644 --- a/artifacts/scripting/sfall function notes.txt +++ b/artifacts/scripting/sfall function notes.txt @@ -655,7 +655,7 @@ optional argument: - removes all timer events with the specified 'fixedParam' value for the current global script > mixed sfall_func1("get_sfall_arg_at", int argNum) -- gets the value of hook argument with the specified argument number (argNum, first argument of hook starts from 0) +- gets the value of hook argument with the specified argument number (first argument of hook starts from 0) > void sfall_func0("hide_window") > void sfall_func1("hide_window", string winName) From 71803ff06cf04075e8f13ac025735d810dc609b1 Mon Sep 17 00:00:00 2001 From: NovaRain Date: Tue, 24 Dec 2019 20:50:52 +0800 Subject: [PATCH 35/36] Added a comment to Handlers\Utils.cpp --- sfall/Modules/Scripting/Handlers/Utils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sfall/Modules/Scripting/Handlers/Utils.cpp b/sfall/Modules/Scripting/Handlers/Utils.cpp index 6d56ee8a..380d05c3 100644 --- a/sfall/Modules/Scripting/Handlers/Utils.cpp +++ b/sfall/Modules/Scripting/Handlers/Utils.cpp @@ -60,7 +60,7 @@ static bool FalloutStringCompare(const char* str1, const char* str2, long codePa // 128 - 255 (international/extended) switch (codePage) { case 866: - if (c1 != 149 && c2 != 149) { + if (c1 != 149 && c2 != 149) { // code used for the 'bullet' character in Fallout font // upper to lower case if (c1 >= 0x80 && c1 <= 0x9F) { c1 |= 32; From b984568b328a481757b204bec18eda19fb6cb62d Mon Sep 17 00:00:00 2001 From: NovaRain Date: Wed, 25 Dec 2019 09:15:35 +0800 Subject: [PATCH 36/36] Added StrNormalizePath() to Utils.cpp. --- sfall/Utils.cpp | 8 ++++++++ sfall/Utils.h | 2 ++ 2 files changed, 10 insertions(+) diff --git a/sfall/Utils.cpp b/sfall/Utils.cpp index 8753b06c..a7ace7f0 100644 --- a/sfall/Utils.cpp +++ b/sfall/Utils.cpp @@ -61,4 +61,12 @@ const char* strfind(const char* source, const char* word) { return 0; } +// replace all '/' chars to '\' +void StrNormalizePath(char* path) { + if (*path == 0) return; + do { + if (*path == '/') *path = '\\'; + } while (*(++path) != 0); +} + } diff --git a/sfall/Utils.h b/sfall/Utils.h index 9980376b..d3b37e52 100644 --- a/sfall/Utils.h +++ b/sfall/Utils.h @@ -34,4 +34,6 @@ void strtrim(char* str); const char* strfind(const char* source, const char* word); +void StrNormalizePath(char* path); + }