From 744213dea131bfb1346b00dcd16b575cbf7586b4 Mon Sep 17 00:00:00 2001 From: NovaRain Date: Fri, 9 Aug 2019 11:47:33 +0800 Subject: [PATCH 01/34] Fixed unable to reach the specified max level in XPTable Fixed the AP cost display issue in NPC combat control mod. Added a tweak for weapon perk modifier to AIBestWeaponFix. Minor edits to code/documents. Updated version number. --- artifacts/ddraw.ini | 14 +++++++------- artifacts/mods/gl_partycontrol.int | Bin 4120 -> 4148 bytes artifacts/mods/gl_partycontrol.ssl | 1 + artifacts/scripting/headers/sfall.h | 7 ++++--- sfall/Modules/BugFixes.cpp | 2 ++ sfall/Modules/Stats.cpp | 2 +- sfall/Modules/Worldmap.cpp | 2 +- sfall/version.h | 10 ++++------ 8 files changed, 20 insertions(+), 18 deletions(-) diff --git a/artifacts/ddraw.ini b/artifacts/ddraw.ini index 181bc1b4..353619b2 100644 --- a/artifacts/ddraw.ini +++ b/artifacts/ddraw.ini @@ -1,5 +1,5 @@ ;sfall configuration settings -;v4.1.9.1 +;v4.2 [Main] ;Change to 1 if you want to use command line args to tell sfall to use another ini file. @@ -272,7 +272,7 @@ Movie16=artimer4.mve Movie17=credits.mve ;To change the starting year, month or day, uncomment the next 3 lines -;Both StartMonth and StartDay are 0-indexed +;Both StartMonth and StartDay are 0-indexed (i.e. 0 is January or the first day of a month) ;StartYear=-1 ;StartMonth=-1 ;StartDay=-1 @@ -703,7 +703,7 @@ SpecialDeathGVAR=491 ;Note that enabling this option can break the map changes in Modoc and Vault 15 DisableSpecialMapIDs=0 -;Changes the base value of the duration of the knockout effect (valid range: 35..100) +;Changes the base value of the duration of the knockout effect (valid range: 35..100; default is 35) ;The formula for the duration in ticks is: 10 * (value - 3 * EN) KnockoutTime=35 @@ -742,6 +742,10 @@ Enable=0 ;------- DebugMode=0 +;Set to 1 to hide error messages in debug output when a null value is passed to the function as an object +;Requires DebugMode to be enabled +HideObjIsNullMsg=0 + ;Change to 1 to skip the compatibility mode check SkipCompatModeCheck=0 @@ -767,10 +771,6 @@ AlwaysFindScripts=0 ;Set to 1 to force sfall to inject all hooks code into the game, even if corresponding hook scripts don't exist InjectAllGameHooks=0 -;Set to 1 to hide error messages in debug output when a null value is passed to the function as an object -;Requires DebugMode to be enabled -HideObjIsNullMsg=0 - ;Set to 1 to force critters to display combat float messages ;Requires AllowSoundForFloats to be enabled Test_ForceFloats=0 diff --git a/artifacts/mods/gl_partycontrol.int b/artifacts/mods/gl_partycontrol.int index 4617d653739379f994d0e39036f5394e4e3e5d51..b853c50249a049c15a5a47bcbf8514b32c61ce39 100644 GIT binary patch delta 1099 zcmbQCuthPKQpf+EipMYz9==ND6yPjvoo^^BcsRU3KnO^ z50m$>)Jo0(g$M&vgV6!T|NsC0Z!m6<28poNOb%krWPCSy534R?#N=13k&J&PyRaz> zJ^;xwFg6H5)Uj=uT)}3@cxLh{wsyf;U^%u1p$2^ri@jm86MHOU+vEl8VtgEY4H6Cd z4N_oyate2>EyNs9*g!=8L4u?430O6AgE7RnJOK^+ z8yOBTGBPkMgvAAq+2nuRQ>_i4Dvcm6;JF4d0W1oRKTv{$>EUHT$nvsGKF6aVz{m$y z3JqdLsmcF%9Jys0Qy|9imQD8JHRj*bzzYgcMl~>LHo1pak@3~!b-b$TGGN^d4MI@b z0^(pkEfgJglY4k2t=SudL8|zZKqNTW^}*)(G?jrx7#a-0{1_@u@OinmmV3PKqC#gg}OZq!?IXF~ZL^`3j%Dx)CHc8DKF6 jb4nD7GiQOEDb4|PW($hky2(-e_WYMn#CA+x!CwFXTjA(7 delta 1033 zcmdm@FhfD0f$0Dv0|SFl122eXQJW}WSYO2e0TK`jMzVm!nHd-uRA3?yn&k%r0|OHS z1A_^K4iXsg-O4g#ZIngV6!T z|NsC0Z!m6<28pm{O-^CWWV|!^9IGy)+vGp2k&Le1P=h{*#hy1gggsU@3&dt%0NDTv0+KzR%U{zOO zdcbr}Qw5l2Z4idYPHy6KY1$umBi z?7?L%X#!EtZ30%q1@jiyqRDf()YT!PFmEw5m_q`D>s^CHgDcEqT+b%I;!fJcUp=LI4(d0tF@#iJm=@CU3C8jg${lcjhaxfvQ$Ao_TtCa3Wl^Dk)N z1%(i!2$)ovynr58oQ;c>4n+pmveXuU8rZTVyLxUlh z?*-)2|Blgs$+`M05nEt`CVzW@Mk&DQ(? diff --git a/artifacts/mods/gl_partycontrol.ssl b/artifacts/mods/gl_partycontrol.ssl index addad920..8bf38d85 100644 --- a/artifacts/mods/gl_partycontrol.ssl +++ b/artifacts/mods/gl_partycontrol.ssl @@ -58,6 +58,7 @@ procedure combatturn_handler begin critter_add_trait(critter, TRAIT_PERK, perkID, level); end end + intface_redraw; lightDist := get_object_data(critter, OBJ_DATA_LIGHT_DISTANCE); lightInt := get_object_data(critter, OBJ_DATA_LIGHT_INTENSITY); obj_set_light_level(critter, 100, 4); diff --git a/artifacts/scripting/headers/sfall.h b/artifacts/scripting/headers/sfall.h index 5320b911..38ed754f 100644 --- a/artifacts/scripting/headers/sfall.h +++ b/artifacts/scripting/headers/sfall.h @@ -142,12 +142,13 @@ #define reverse_array(array) resize_array(array, -4) // randomly shuffle elements in list/map #define shuffle_array(array) resize_array(array, -5) -// sort map in ascending order by value +// sort map in ascending order by key value #define sort_map_value(array) resize_array(array, -6) -// sort map in descending order by value -#define sort_map_value_desc(array) resize_array(array, -7) +// sort map in descending order by key value +#define sort_map_reverse(array) resize_array(array, -7) // remove element from map or just replace value with 0 for list #define unset_array(array, item) set_array(array, item, 0) + // same as "key_pressed" but checks VK codes instead of DX codes #define key_pressed_vk(key) (key_pressed(key bwor 0x80000000)) diff --git a/sfall/Modules/BugFixes.cpp b/sfall/Modules/BugFixes.cpp index 43483c3a..21621144 100644 --- a/sfall/Modules/BugFixes.cpp +++ b/sfall/Modules/BugFixes.cpp @@ -2656,6 +2656,8 @@ void BugFixes::init() if (GetConfigInt("Misc", "AIBestWeaponFix", 0)) { dlog("Applying AI best weapon choice fix.", DL_INIT); HookCall(0x42954B, ai_best_weapon_hook); + // also change the modifier for having weapon perk to 3x (was 5x) + SafeWriteBatch(0x55, {0x42955E, 0x4296E7}); dlogr(" Done", DL_INIT); } diff --git a/sfall/Modules/Stats.cpp b/sfall/Modules/Stats.cpp index 90f1a28c..49fc22ec 100644 --- a/sfall/Modules/Stats.cpp +++ b/sfall/Modules/Stats.cpp @@ -598,7 +598,7 @@ void Stats::init() { ? atoi(xpTableList[i].c_str()) : -1; } - SafeWrite8(0x4AFB1B, static_cast(numLevels)); + SafeWrite8(0x4AFB1B, static_cast(numLevels + 1)); } auto statsFile = GetConfigString("Misc", "DerivedStats", "", MAX_PATH); diff --git a/sfall/Modules/Worldmap.cpp b/sfall/Modules/Worldmap.cpp index d6d1afd1..c043f499 100644 --- a/sfall/Modules/Worldmap.cpp +++ b/sfall/Modules/Worldmap.cpp @@ -443,7 +443,7 @@ void StartingStatePatches() { date = GetConfigInt("Misc", "StartDay", -1); if (date >= 0 && date < 31) { dlog("Applying starting day patch.", DL_INIT); - SafeWrite8(0x4A3356, date); + SafeWrite8(0x4A3356, static_cast(date)); dlogr(" Done", DL_INIT); } diff --git a/sfall/version.h b/sfall/version.h index ced3fc39..623cf8b3 100644 --- a/sfall/version.h +++ b/sfall/version.h @@ -23,10 +23,8 @@ #define LEGAL_COPYRIGHT "Copyright (C) 2006-2019, sfall team" #define VERSION_MAJOR 4 -#define VERSION_MINOR 1 -#define VERSION_BUILD 9 -#define VERSION_REV 1 +#define VERSION_MINOR 2 +#define VERSION_BUILD 0 +#define VERSION_REV 0 -#define VERSION_STRING "4.1.9.1" - -//#define CHECK_VAL (4) +#define VERSION_STRING "4.2" From f59d1049750004d459f3fe1238d96838a4f6dd55 Mon Sep 17 00:00:00 2001 From: NovaRain Date: Sun, 11 Aug 2019 10:58:32 +0800 Subject: [PATCH 02/34] Fixed create_message_window script function * prevent it from creating multiple message windows. Capped StartMonth/StartDay in case people didn't notice "0-indexed". Minor fix for assigning an ID to objects when loading a map. Small optimization for sfall gl/hs scripts. --- sfall/Modules/HookScripts.cpp | 6 +++--- sfall/Modules/HookScripts/Common.cpp | 7 ++++--- sfall/Modules/Objects.cpp | 3 +++ sfall/Modules/ScriptExtender.cpp | 11 ++--------- sfall/Modules/Scripting/Handlers/Interface.cpp | 5 ++++- sfall/Modules/Worldmap.cpp | 6 ++++-- 6 files changed, 20 insertions(+), 18 deletions(-) diff --git a/sfall/Modules/HookScripts.cpp b/sfall/Modules/HookScripts.cpp index db97436a..ba142d79 100644 --- a/sfall/Modules/HookScripts.cpp +++ b/sfall/Modules/HookScripts.cpp @@ -224,7 +224,7 @@ void LoadHookScripts() { HookScriptInit(); initingHookScripts = 1; for (int i = 0; i < numHooks; i++) { - if (hooks[i].size()) { + if (!hooks[i].empty()) { InitScriptProgram(hooks[i][0].prog); // zero hook is always hs_*.int script because Hook scripts are loaded BEFORE global scripts } } @@ -235,8 +235,8 @@ void LoadHookScripts() { // run specific event procedure for all hook scripts void _stdcall RunHookScriptsAtProc(DWORD procId) { for (int i = 0; i < numHooks; i++) { - if (hooks[i].size() > 0 && !hooks[i][0].isGlobalScript) { - RunScriptProc(&hooks[i][0].prog, procId); + if (!hooks[i].empty() && !hooks[i][0].isGlobalScript) { + RunScriptProc(&hooks[i][0].prog, procId); // run hs_*.int } } } diff --git a/sfall/Modules/HookScripts/Common.cpp b/sfall/Modules/HookScripts/Common.cpp index 23ad8618..91b06f65 100644 --- a/sfall/Modules/HookScripts/Common.cpp +++ b/sfall/Modules/HookScripts/Common.cpp @@ -108,7 +108,7 @@ static void _stdcall RunSpecificHookScript(HookScript *hook) { void _stdcall RunHookScript(DWORD hook) { cRet = 0; - if (hooks[hook].size()) { + if (!hooks[hook].empty()) { if (callDepth > 1) { if (CheckRecursiveHooks(hook) || callDepth > 8) { fo::func::debug_printf("\n[SFALL] The hook ID: %d cannot be executed.", hook); @@ -117,8 +117,9 @@ void _stdcall RunHookScript(DWORD hook) { } } currentRunHook = hook; - dlog_f("Running hook %d, which has %0d entries attached, depth: %d\n", DL_HOOK, hook, hooks[hook].size(), callDepth); - for (int i = hooks[hook].size() - 1; i >= 0; i--) { + size_t hooksCount = hooks[hook].size(); + dlog_f("Running hook %d, which has %0d entries attached, depth: %d\n", DL_HOOK, hook, hooksCount, callDepth); + for (int i = hooksCount - 1; i >= 0; i--) { RunSpecificHookScript(&hooks[hook][i]); // for debugging diff --git a/sfall/Modules/Objects.cpp b/sfall/Modules/Objects.cpp index 52178d0b..25388650 100644 --- a/sfall/Modules/Objects.cpp +++ b/sfall/Modules/Objects.cpp @@ -100,12 +100,15 @@ static void __declspec(naked) map_load_file_hack() { jz mapVirgin; retn; mapVirgin: + test eax, eax; + jl skip; // check map index > -1 call fo::funcoffs::wmMapIsSaveable_; test eax, eax; jnz saveable; retn; saveable: call map_fix_critter_id; +skip: xor eax, eax; // set ZF retn; } diff --git a/sfall/Modules/ScriptExtender.cpp b/sfall/Modules/ScriptExtender.cpp index d6fb399d..505f8de6 100644 --- a/sfall/Modules/ScriptExtender.cpp +++ b/sfall/Modules/ScriptExtender.cpp @@ -369,18 +369,11 @@ long SetGlobalVar(const char* var, int val) { long GetGlobalVarInternal(__int64 val) { glob_citr itr = globalVars.find(val); - if (itr == globalVars.end()) { - return 0; - } else { - return itr->second; - } + return (itr != globalVars.end()) ? itr->second : 0; } long GetGlobalVar(const char* var) { - if (strlen(var) != 8) { - return 0; - } - return GetGlobalVarInternal(*(__int64*)var); + return (strlen(var) == 8) ? GetGlobalVarInternal(*(__int64*)var) : 0; } long GetGlobalVarInt(DWORD var) { diff --git a/sfall/Modules/Scripting/Handlers/Interface.cpp b/sfall/Modules/Scripting/Handlers/Interface.cpp index a0cb2272..76fcf180 100644 --- a/sfall/Modules/Scripting/Handlers/Interface.cpp +++ b/sfall/Modules/Scripting/Handlers/Interface.cpp @@ -151,11 +151,14 @@ void __declspec(naked) op_resume_game() { } } +static bool dialogShow = false; void sf_create_message_window(OpcodeContext &ctx) { + if (dialogShow) return; const char* str = ctx.arg(0).strValue(); if (!str || str[0] == 0) return; - //if (fo::var::curr_font_num == 101) return; + dialogShow = true; fo::func::DialogOut(str); + dialogShow = false; } void __declspec(naked) op_get_viewport_x() { diff --git a/sfall/Modules/Worldmap.cpp b/sfall/Modules/Worldmap.cpp index c043f499..16223792 100644 --- a/sfall/Modules/Worldmap.cpp +++ b/sfall/Modules/Worldmap.cpp @@ -435,13 +435,15 @@ void StartingStatePatches() { dlogr(" Done", DL_INIT); } date = GetConfigInt("Misc", "StartMonth", -1); - if (date >= 0 && date < 12) { + if (date >= 0) { + if (date > 11) date = 11; dlog("Applying starting month patch.", DL_INIT); SafeWrite32(0x4A3382, date); dlogr(" Done", DL_INIT); } date = GetConfigInt("Misc", "StartDay", -1); - if (date >= 0 && date < 31) { + if (date >= 0) { + if (date > 30) date = 30; dlog("Applying starting day patch.", DL_INIT); SafeWrite8(0x4A3356, static_cast(date)); dlogr(" Done", DL_INIT); From 50f4885407194482a5c716c4d79f831cecd24963 Mon Sep 17 00:00:00 2001 From: NovaRain Date: Sun, 11 Aug 2019 23:46:26 +0800 Subject: [PATCH 03/34] Fixed obj_art_fid for dude_obj when the hero appearance mod is enabled --- artifacts/ddraw.ini | 1 + sfall/Modules/HeroAppearance.cpp | 25 ++++++++++++++++++++----- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/artifacts/ddraw.ini b/artifacts/ddraw.ini index 353619b2..dd624133 100644 --- a/artifacts/ddraw.ini +++ b/artifacts/ddraw.ini @@ -389,6 +389,7 @@ CorpseDeleteTime=6 ProcessorIdle=-1 ;Set to 1 if using the hero appearance mod +;Set to 2 for backward compatibility with old scripts that manually fix obj_art_fid script function for dude_obj ;You can add AppChCrt.frm and AppChEdt.frm files to art\intrface\ to set a custom background for the character screen EnableHeroAppearanceMod=0 diff --git a/sfall/Modules/HeroAppearance.cpp b/sfall/Modules/HeroAppearance.cpp index ea5e7a5f..c5baea0d 100644 --- a/sfall/Modules/HeroAppearance.cpp +++ b/sfall/Modules/HeroAppearance.cpp @@ -182,7 +182,6 @@ static void GetAppearanceGlobals(int *race, int *style) { } static __declspec(noinline) int _stdcall LoadHeroDat(unsigned int race, unsigned int style, bool flush = false) { - if (flush) fo::func::art_flush(); if (heroPathPtr->pDat) { // unload previous Dats @@ -285,7 +284,6 @@ static void AdjustHeroArmorArt(DWORD fid) { } static void _stdcall SetHeroArt(bool newArtFlag) { - fo::GameObject* hero = fo::var::obj_dude; // hero state struct long heroFID = hero->artFid; // get hero FrmID DWORD fidBase = heroFID & 0xFFF; // mask out current weapon flag @@ -438,7 +436,6 @@ void _stdcall SetHeroStyle(int newStyleVal) { // op_set_hero_race void _stdcall SetHeroRace(int newRaceVal) { - if (!HeroAppearance::appModEnabled || newRaceVal == currentRaceVal) return; if (LoadHeroDat(newRaceVal, 0, true) != 0) { // if new race fails with style at 0 @@ -514,7 +511,6 @@ static void DrawBody(DWORD critNum, BYTE* surface) { } static void DrawPCConsole() { - DWORD NewTick = *(DWORD*)0x5709C4; // char scrn gettickcount ret DWORD RotSpeed = *(DWORD*)0x47066B; // get rotation speed - inventory rotation speed @@ -1311,6 +1307,22 @@ endFunc: } } +static const DWORD op_obj_art_fid_Ret = 0x45C5D9; +static void __declspec(naked) op_obj_art_fid_hack() { + using namespace Fields; + __asm { + mov esi, [edi + artFid]; + push ecx; + call PartyControl::RealDudeObject; + pop ecx; + cmp eax, edi; // object is dude? + jnz skip; + sub esi, critterListSize; // fix hero FrmID +skip: + jmp op_obj_art_fid_Ret; + } +} + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Load Appearance data from GCD file @@ -1473,9 +1485,12 @@ static void HeroAppearanceModExit() { } void HeroAppearance::init() { - if (GetConfigInt("Misc", "EnableHeroAppearanceMod", 0)) { + int heroAppearanceMod = GetConfigInt("Misc", "EnableHeroAppearanceMod", 0); + if (heroAppearanceMod > 0) { dlog("Setting up Appearance Char Screen buttons.", DL_INIT); EnableHeroAppearanceMod(); + // Hero FrmID fix for obj_art_fid script function + if (heroAppearanceMod != 2) MakeJump(0x45C5C3, op_obj_art_fid_hack); LoadGameHook::OnAfterNewGame() += []() { SetNewCharAppearanceGlobals(); From da864c97892c40f65cff44c3b12b229c8b528b5f Mon Sep 17 00:00:00 2001 From: NovaRain Date: Mon, 12 Aug 2019 12:06:45 +0800 Subject: [PATCH 04/34] Rewrote the code of force_encounter functions Added a new flag to force_encounter_with_flags. --- artifacts/scripting/headers/sfall.h | 3 +- sfall/FalloutEngine/VariableOffsets.h | 1 + sfall/Modules/Scripting/Handlers/Worldmap.cpp | 103 +++++++----------- sfall/Modules/Scripting/Handlers/Worldmap.h | 4 +- sfall/Modules/Scripting/Opcodes.cpp | 9 +- 5 files changed, 54 insertions(+), 66 deletions(-) diff --git a/artifacts/scripting/headers/sfall.h b/artifacts/scripting/headers/sfall.h index 38ed754f..beefc5dc 100644 --- a/artifacts/scripting/headers/sfall.h +++ b/artifacts/scripting/headers/sfall.h @@ -73,7 +73,8 @@ #define LIST_ALL (9) //Valid flags for force_encounter_with_flags -#define ENCOUNTER_FLAG_NO_CAR (1) +#define ENCOUNTER_FLAG_NO_CAR (1) +#define ENCOUNTER_FLAG_LOCK (2) // block new forced encounter by the next function call until the current specified encounter occurs //The attack types returned by get_attack_type #define ATKTYPE_LWEP1 (0) diff --git a/sfall/FalloutEngine/VariableOffsets.h b/sfall/FalloutEngine/VariableOffsets.h index 38488da5..1653b2d8 100644 --- a/sfall/FalloutEngine/VariableOffsets.h +++ b/sfall/FalloutEngine/VariableOffsets.h @@ -132,6 +132,7 @@ #define FO_VAR_max 0x56FB50 #define FO_VAR_maxScriptNum 0x51C7CC #define FO_VAR_Meet_Frank_Horrigan 0x672E04 +#define FO_VAR_Move_on_Car 0x672E64 #define FO_VAR_mouse_hotx 0x6AC7D0 #define FO_VAR_mouse_hoty 0x6AC7CC #define FO_VAR_mouse_is_hidden 0x6AC790 diff --git a/sfall/Modules/Scripting/Handlers/Worldmap.cpp b/sfall/Modules/Scripting/Handlers/Worldmap.cpp index 16da6813..f8eb6dd2 100644 --- a/sfall/Modules/Scripting/Handlers/Worldmap.cpp +++ b/sfall/Modules/Scripting/Handlers/Worldmap.cpp @@ -31,77 +31,58 @@ namespace sfall namespace script { -static DWORD EncounteredHorrigan; -static DWORD ForceEnconterMapID; +static DWORD ForceEnconterMapID = -1; +static DWORD ForceEnconterFlags; -static void _stdcall ForceEncounterRestore() { - *(DWORD*)0x672E04 = EncounteredHorrigan; - SafeWrite32(0x4C070E, *(DWORD*)0x4C0718); // map id - SafeWrite16(0x4C06D8, 0x5175); - SafeWrite32(0x4C071D, 0xFFFC2413); - SafeWrite8(0x4C0706, 0x75); +DWORD ForceEncounterRestore() { + long long data = 0x672E043D83; // cmp ds:_Meet_Frank_Horrigan, 0 + SafeWriteBytes(0x4C06D1, (BYTE*)&data, 5); + ForceEnconterFlags = 0; + DWORD mapID = ForceEnconterMapID; + ForceEnconterMapID = -1; + return mapID; } -static void __declspec(naked) wmRndEncounterOccurred_hook() { +static void __declspec(naked) wmRndEncounterOccurred_hack() { __asm { - push ecx; - call ForceEncounterRestore; - pop ecx; + test ForceEnconterFlags, 0x1; // _NoCar flag + jnz noCar; + cmp ds:[FO_VAR_Move_on_Car], 0; + jz noCar; + mov edx, FO_VAR_carCurrentArea; mov eax, ForceEnconterMapID; - jmp fo::funcoffs::map_load_idx_; + call fo::funcoffs::wmMatchAreaContainingMapIdx_; +noCar: + //push ecx; + // TODO: implement a blinking red icon + call ForceEncounterRestore; + //pop ecx; + push 0x4C0721; // return addr + jmp fo::funcoffs::map_load_idx_; // eax - mapID } } -static void __fastcall ForceEncounter(DWORD mapID, DWORD flags) { - EncounteredHorrigan = *(DWORD*)0x672E04; +void sf_force_encounter(OpcodeContext& cxt) { + if (ForceEnconterFlags & (1 << 31)) return; // wait prev. encounter + + DWORD mapID = cxt.arg(0).rawValue(); + if (mapID < 0) { + cxt.printOpcodeError("%s() - invalid map number.", cxt.getOpcodeName()); + return; + } + if (ForceEnconterMapID == -1) MakeJump(0x4C06D1, wmRndEncounterOccurred_hack); + ForceEnconterMapID = mapID; - SafeWrite32(0x4C070E, mapID); - SafeWrite16(0x4C06D8, 0x13EB); // jmp 0x4C06ED - HookCall(0x4C071C, wmRndEncounterOccurred_hook); - - if (flags & 1) SafeWrite8(0x4C0706, 0xEB); -} - -void __declspec(naked) op_force_encounter() { - __asm { - push ecx; - push edx; - _GET_ARG_INT(end); - xor edx, edx; // flags - mov ecx, eax; // mapID - call ForceEncounter; -end: - pop edx; - pop ecx; - retn; - } -} - -void __declspec(naked) op_force_encounter_with_flags() { - __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; - mov edx, ebx; // flags - mov ecx, eax; // mapID - call ForceEncounter; -end: - popad; - retn; + DWORD flags = 0; + if (cxt.numArgs() > 1) { + flags = cxt.arg(1).rawValue(); + if (flags & 2) { // _Lock flag + flags |= (1 << 31); // set bit 31 + } else { + flags = flags & ~(1 << 31); + } } + ForceEnconterFlags = flags; } // world_map_functions diff --git a/sfall/Modules/Scripting/Handlers/Worldmap.h b/sfall/Modules/Scripting/Handlers/Worldmap.h index 1b6d4788..44a88a72 100644 --- a/sfall/Modules/Scripting/Handlers/Worldmap.h +++ b/sfall/Modules/Scripting/Handlers/Worldmap.h @@ -25,9 +25,9 @@ namespace script class OpcodeContext; -void __declspec() op_force_encounter(); +void sf_force_encounter(OpcodeContext&); -void __declspec() op_force_encounter_with_flags(); +DWORD ForceEncounterRestore(); // world_map_functions void __declspec() op_in_world_map(); diff --git a/sfall/Modules/Scripting/Opcodes.cpp b/sfall/Modules/Scripting/Opcodes.cpp index 7503ff16..d9490aa7 100644 --- a/sfall/Modules/Scripting/Opcodes.cpp +++ b/sfall/Modules/Scripting/Opcodes.cpp @@ -18,6 +18,7 @@ #include "..\..\FalloutEngine\Fallout2.h" #include "..\KillCounter.h" +#include "..\LoadGameHook.h" #include "Handlers\Anims.h" #include "Handlers\Arrays.h" @@ -72,6 +73,7 @@ static SfallOpcodeInfo opcodeInfoArray[] = { {0x161, "get_critter_extra_stat", sf_get_critter_extra_stat, 2, true, {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}}, {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}}, @@ -143,6 +145,7 @@ static SfallOpcodeInfo opcodeInfoArray[] = { {0x224, "create_message_window", sf_create_message_window, 1, false, {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 @@ -286,7 +289,6 @@ void InitNewOpcodes() { opcodes[0x16e] = op_set_shader_float; opcodes[0x16f] = op_set_shader_vector; opcodes[0x170] = op_in_world_map; - opcodes[0x171] = op_force_encounter; opcodes[0x172] = op_set_world_map_pos; opcodes[0x173] = op_get_world_map_x_pos; opcodes[0x174] = op_get_world_map_y_pos; @@ -384,7 +386,6 @@ void InitNewOpcodes() { opcodes[0x225] = op_remove_trait; opcodes[0x226] = op_get_light_level; opcodes[0x227] = op_refresh_pc_art; - opcodes[0x229] = op_force_encounter_with_flags; opcodes[0x22a] = op_set_map_time_multi; opcodes[0x22b] = op_play_sfall_sound; opcodes[0x22c] = op_stop_sfall_sound; @@ -424,6 +425,10 @@ void InitNewOpcodes() { InitOpcodeInfoTable(); InitMetaruleTable(); + + LoadGameHook::OnGameReset() += []() { + ForceEncounterRestore(); // restore if the encounter did not happen + }; } } From 8855326e413d9342d14a98a44598e39e1f2600ef Mon Sep 17 00:00:00 2001 From: NovaRain Date: Tue, 13 Aug 2019 13:53:41 +0800 Subject: [PATCH 05/34] Fixed the lost car bug with using the Town/World button (http://www.nma-fallout.com/threads/fallout-2-vehicle-bug.217993/) --- artifacts/ddraw.ini | 6 +++++- sfall/FalloutEngine/Enums.h | 1 + sfall/Modules/BugFixes.cpp | 22 ++++++++++++++++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/artifacts/ddraw.ini b/artifacts/ddraw.ini index dd624133..cf7936cc 100644 --- a/artifacts/ddraw.ini +++ b/artifacts/ddraw.ini @@ -650,10 +650,14 @@ UseWalkDistance=3 ;Set to 1 to fix the bug of being unable to sell used geiger counters or stealth boys CanSellUsedGeiger=1 -;Set to 1 to fix the issue with being able to charge the car by using cells on other scenary/critters +;Set to 1 to fix the issue with being able to charge the car by using cells on other scenery/critters ;Set to 0 if another mod you're using has custom vehicles CarChargingFix=1 +;Set to 1 to prevent the car from being lost when entering a location via the Town/World button and then leaving on foot +;Note that the global variable 633 (GVAR_CAR_PLACED_TILE) will be set to -1 when the player leaves a location +CarPlacedTileFix=1 + ;Set to 1 to skip weapon equip/unequip animations when performing various actions InstantWeaponEquip=0 diff --git a/sfall/FalloutEngine/Enums.h b/sfall/FalloutEngine/Enums.h index b21b8e5f..9af46a21 100644 --- a/sfall/FalloutEngine/Enums.h +++ b/sfall/FalloutEngine/Enums.h @@ -159,6 +159,7 @@ enum GlobalVar : long GVAR_NEW_RENO_SUPER_CAR = 456, GVAR_MODOC_SHITTY_DEATH = 491, GVAR_FALLOUT_2 = 494, + GVAR_CAR_PLACED_TILE = 633, }; // Physical material type, used for items and tiles. diff --git a/sfall/Modules/BugFixes.cpp b/sfall/Modules/BugFixes.cpp index 21621144..485f60c7 100644 --- a/sfall/Modules/BugFixes.cpp +++ b/sfall/Modules/BugFixes.cpp @@ -2252,6 +2252,20 @@ break: } } +static void __declspec(naked) wmInterfaceInit_hack() { + __asm { + mov eax, GVAR_CAR_PLACED_TILE; + cmp eax, dword ptr ds:[FO_VAR_num_game_global_vars]; + jge skip; + mov edx, ds:[FO_VAR_game_global_vars]; + lea edx, [edx + eax * 4]; + mov dword ptr [edx], -1; // set gvar +skip: + mov edx, 12; + retn; + } +} + void BugFixes::init() { #ifndef NDEBUG @@ -2841,6 +2855,14 @@ void BugFixes::init() // Fix for combat not ending automatically when there are no hostile critters MakeCall(0x422CF3, combat_should_end_hack); SafeWrite16(0x422CEA, 0x0C74); // jz 0x422CF8 (skip party members) + + // Fix for the car being lost when entering a location via the Town/World button and then leaving on foot + // (sets GVAR_CAR_PLACED_TILE (633) to -1 on exit to the world map) + if (GetConfigInt("Misc", "CarPlacedTileFix", 1)) { + dlog("Applying car placed tile fix.", DL_INIT); + MakeCall(0x4C2367, wmInterfaceInit_hack); + dlogr(" Done", DL_INIT); + } } } From cce9697dfd1c5efc955ebb81080bcd498e73007f Mon Sep 17 00:00:00 2001 From: NovaRain Date: Wed, 14 Aug 2019 11:48:45 +0800 Subject: [PATCH 06/34] Set DAM_BACKWASH_ flag for attacker before calling combat damage (#246) --- sfall/Modules/HookScripts/CombatHs.cpp | 3 ++- sfall/Modules/ScriptExtender.cpp | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/sfall/Modules/HookScripts/CombatHs.cpp b/sfall/Modules/HookScripts/CombatHs.cpp index 1302525a..3d1496cb 100644 --- a/sfall/Modules/HookScripts/CombatHs.cpp +++ b/sfall/Modules/HookScripts/CombatHs.cpp @@ -523,11 +523,12 @@ void Inject_CombatDamageHook() { 0x4233E3, // shoot_along_path() - for extra burst targets 0x423AB7, // compute_attack() 0x423BBF, // compute_attack() - 0x423DE7, // compute_explosion_on_extras() +// 0x423DE7, // compute_explosion_on_extras() 0x423E69, // compute_explosion_on_extras() 0x424220, // attack_crit_failure() 0x4242FB, // attack_crit_failure() }); + MakeCall(0x423DEB, ComputeDamageHook); // compute_explosion_on_extras() - for the attacker } void Inject_FindTargetHook() { diff --git a/sfall/Modules/ScriptExtender.cpp b/sfall/Modules/ScriptExtender.cpp index 505f8de6..fbf6c1d2 100644 --- a/sfall/Modules/ScriptExtender.cpp +++ b/sfall/Modules/ScriptExtender.cpp @@ -744,6 +744,14 @@ void ScriptExtender::init() { long long data = 0x397401C1F6; // test cl, 1; jz 0x483CF2 SafeWriteBytes(0x483CB4, (BYTE*)&data, 5); + // Set the DAM_BACKWASH_ flag for the attacker before calling compute_damage_ + SafeWrite32(0x423DE7, 0x40164E80); // or [esi+ctd.flags3Source], DAM_BACKWASH_ + long idata = 0x146E09; // or dword ptr [esi+ctd.flagsSource], ebp + SafeWriteBytes(0x423DF0, (BYTE*)&idata, 3); + if (*(BYTE*)0x423DEB != 0xE8) { // not hook call + MakeCall(0x423DEB, (void*)fo::funcoffs::compute_damage_); + } + InitNewOpcodes(); } From 2dfbe903e787e6140567bc0f2162a7504fb176e7 Mon Sep 17 00:00:00 2001 From: NovaRain Date: Fri, 16 Aug 2019 15:02:14 +0800 Subject: [PATCH 07/34] Added an additional 'Energy Weapon' flag to ext flags in weapon protos * it forces the weapon to use EW skill regardless of its damage type. Placed an object with PID 0x5000004 (corpse blood) to the lower z-layer of the tile. --- artifacts/scripting/headers/define_extra.h | 1 + sfall/Modules/Objects.cpp | 21 +++++++++++++++++++++ sfall/Modules/Skills.cpp | 18 ++++++++++++++++++ 3 files changed, 40 insertions(+) diff --git a/artifacts/scripting/headers/define_extra.h b/artifacts/scripting/headers/define_extra.h index 92a095f2..8831c1bf 100644 --- a/artifacts/scripting/headers/define_extra.h +++ b/artifacts/scripting/headers/define_extra.h @@ -31,6 +31,7 @@ #define WEAPON_BIGGUN 256 // 0x00000100 - Big Gun #define WEAPON_2HAND 512 // 0x00000200 - 2Hnd (weapon is two-handed) +#define WEAPON_ENERGYWEAPON 1024 // 0x00000400 - Energy Weapon (forces weapon to use Energy Weapons skill) #define ATKMODE_PRI_NONE 0 #define ATKMODE_PRI_PUNCH 1 // 0001 diff --git a/sfall/Modules/Objects.cpp b/sfall/Modules/Objects.cpp index 25388650..d20aa9ae 100644 --- a/sfall/Modules/Objects.cpp +++ b/sfall/Modules/Objects.cpp @@ -198,6 +198,24 @@ void Objects::LoadProtoAutoMaxLimit() { MakeCall(0x4A21B2, proto_ptr_hack); } +static void __declspec(naked) obj_insert_hack() { + using namespace fo::Fields; + __asm { + mov edi, [ebx]; + mov [esp + 0x38 - 0x1C + 4], esi; // 0 + test edi, edi; + jnz insert; + retn; +insert: + mov esi, [ecx]; // esi - inserted object + cmp dword ptr [esi + protoId], 0x5000004; // corpse blood pid + jnz skip; + xor edi, edi; +skip: + retn; + } +} + void Objects::init() { LoadGameHook::OnGameReset() += []() { RestoreObjUnjamAllLocks(); @@ -213,6 +231,9 @@ void Objects::init() { SafeWrite8(0x482E71, 0x85); // jz > jnz // Additionally fix object IDs for queued events MakeCall(0x4A25BA, queue_add_hack); + + // Place some objects on the tile to the lower z-layer + MakeCall(0x48D918, obj_insert_hack, 1); } } diff --git a/sfall/Modules/Skills.cpp b/sfall/Modules/Skills.cpp index 1a31b34a..36dabfa5 100644 --- a/sfall/Modules/Skills.cpp +++ b/sfall/Modules/Skills.cpp @@ -65,6 +65,19 @@ static ChanceModifier basePickpocket; static int skillNegPoints; // skill raw points (w/o limit) +static void __declspec(naked) item_w_skill_hook() { + __asm { + mov edx, [esp + 4]; // item proto + test byte ptr [edx + 0x19], 4; // weapon.flags_ext + jnz energy; + mov edx, ebx; + jmp fo::funcoffs::item_w_damage_type_; +energy: + inc eax; // DMG_laser + retn; + } +} + static int __fastcall PickpocketMod(int base, fo::GameObject* critter) { for (DWORD i = 0; i < pickpocketMods.size(); i++) { if (critter->id == pickpocketMods[i].id) { @@ -313,6 +326,11 @@ void Skills::init() { SafeWrite8(0x4ABC67, 0x89); // mov [esp + 0x54], eax SafeWrite32(0x4ABC6B, 0x90909090); + // Add an additional 'Energy Weapon' flag to the weapon flags (offset 0x0018) + // Weapon Flags: + // 0x00000400 - Energy Weapon (forces weapon to use Energy Weapons skill) + HookCall(0x47831E, item_w_skill_hook); + char buf[512], key[16]; auto skillsFile = GetConfigString("Misc", "SkillsFile", ""); if (!skillsFile.empty()) { From 2b93bf6045a86194273d3bd5874b7ede3108be20 Mon Sep 17 00:00:00 2001 From: NovaRain Date: Sat, 17 Aug 2019 20:44:41 +0800 Subject: [PATCH 08/34] Added new game mode COUNTERWIN. Improved add_extra_msg_file function to use "English" as the fallback language for loading msg files (#256) --- artifacts/scripting/headers/define_extra.h | 2 +- artifacts/scripting/headers/sfall.h | 1 + artifacts/scripting/sfall function notes.txt | 1 + sfall/FalloutEngine/Enums.h | 5 ++++- sfall/Modules/LoadGameHook.cpp | 22 ++++++++++++++++++-- sfall/Modules/LoadGameHook.h | 1 + sfall/Modules/Message.cpp | 17 ++++++++------- sfall/Modules/Objects.cpp | 5 +++-- 8 files changed, 41 insertions(+), 13 deletions(-) diff --git a/artifacts/scripting/headers/define_extra.h b/artifacts/scripting/headers/define_extra.h index 8831c1bf..80db7216 100644 --- a/artifacts/scripting/headers/define_extra.h +++ b/artifacts/scripting/headers/define_extra.h @@ -31,7 +31,7 @@ #define WEAPON_BIGGUN 256 // 0x00000100 - Big Gun #define WEAPON_2HAND 512 // 0x00000200 - 2Hnd (weapon is two-handed) -#define WEAPON_ENERGYWEAPON 1024 // 0x00000400 - Energy Weapon (forces weapon to use Energy Weapons skill) +#define WEAPON_ENERGY 1024 // 0x00000400 - Energy Weapon (forces weapon to use Energy Weapons skill) #define ATKMODE_PRI_NONE 0 #define ATKMODE_PRI_PUNCH 1 // 0001 diff --git a/artifacts/scripting/headers/sfall.h b/artifacts/scripting/headers/sfall.h index beefc5dc..2f6e1927 100644 --- a/artifacts/scripting/headers/sfall.h +++ b/artifacts/scripting/headers/sfall.h @@ -19,6 +19,7 @@ #define BARTER (0x20000) #define HEROWIN (0x40000) #define DIALOGVIEW (0x80000) +#define COUNTERWIN (0x100000) // counter window for moving multiple items or setting a timer //Valid arguments to register_hook #define HOOK_TOHIT (0) diff --git a/artifacts/scripting/sfall function notes.txt b/artifacts/scripting/sfall function notes.txt index 0da5a1df..6bb2e18b 100644 --- a/artifacts/scripting/sfall function notes.txt +++ b/artifacts/scripting/sfall function notes.txt @@ -613,6 +613,7 @@ optional arguments: > int sfall_func2("add_extra_msg_file", string fileName, int fileNumber) - loads the custom message file, and returns the file ID number assigned to it in range from 0x3000 to 0x3FFF for the message_str_game function to get messages from the file - fileName: the name of the custom message file (including the .msg extension) in "text\\game\" directory +- NOTE: if the msg file does not exist in the current language directory, the function will try to load it from "text\English\game\" directory optional argument: - fileNumber: the file ID number for the message_str_game function. The available range is from 0x2000 to 0x2FFF (see ExtraGameMsgFileList setting in ddraw.ini) use fileNumber only if you want to add a message file without editing ddraw.ini or existing scripts to support the old way diff --git a/sfall/FalloutEngine/Enums.h b/sfall/FalloutEngine/Enums.h index 9af46a21..5eb62138 100644 --- a/sfall/FalloutEngine/Enums.h +++ b/sfall/FalloutEngine/Enums.h @@ -274,7 +274,10 @@ enum ProtoId : long PID_JESSE_CONTAINER = 467, PID_Player = 16777216, - PID_DRIVABLE_CAR = 33555441 + PID_DRIVABLE_CAR = 33555441, + + // misc type + PID_CORPSE_BLOOD = 0x05000004, }; //XXXXXXXXXXXXXXXXXXXXX diff --git a/sfall/Modules/LoadGameHook.cpp b/sfall/Modules/LoadGameHook.cpp index b9099f46..bdb88b42 100644 --- a/sfall/Modules/LoadGameHook.cpp +++ b/sfall/Modules/LoadGameHook.cpp @@ -417,7 +417,7 @@ static void __declspec(naked) game_close_hook() { static void __declspec(naked) WorldMapHook() { __asm { _InLoop(1, WORLDMAP); - xor eax, eax; + xor eax, eax; // unused call fo::funcoffs::wmWorldMapFunc_; _InLoop(0, WORLDMAP); retn; @@ -617,6 +617,22 @@ static void __declspec(naked) DialogReviewExitHook() { } } +static void __declspec(naked) setup_move_timer_win_Hook() { + __asm { + _InLoop2(1, COUNTERWIN); + jmp fo::funcoffs::text_curr_; + } +} + +static void __declspec(naked) exit_move_timer_win_Hook() { + __asm { + push eax; + _InLoop2(0, COUNTERWIN); + pop eax; + jmp fo::funcoffs::win_delete_; + } +} + void LoadGameHook::init() { saveInCombatFix = GetConfigInt("Misc", "SaveInCombatFix", 1); if (saveInCombatFix > 2) saveInCombatFix = 0; @@ -650,7 +666,7 @@ void LoadGameHook::init() { 0x480CA7, // gnw_main_ //0x480D45 // main_exit_system_ (never called) }); - + // game modes HookCalls(WorldMapHook, {0x483668, 0x4A4073}); HookCalls(WorldMapHook2, {0x4C4855}); HookCalls(CombatHook, {0x426A29, 0x4432BE, 0x45F6D2, 0x4A4020, 0x4A403D}); @@ -673,6 +689,8 @@ void LoadGameHook::init() { HookCalls(AutomapHook, {0x44396D, 0x479519}); HookCall(0x445CA7, DialogReviewInitHook); HookCall(0x445D30, DialogReviewExitHook); + HookCall(0x476AC6, setup_move_timer_win_Hook); // before init win + HookCall(0x477067, exit_move_timer_win_Hook); } Delegate<>& LoadGameHook::OnGameInit() { diff --git a/sfall/Modules/LoadGameHook.h b/sfall/Modules/LoadGameHook.h index 17dfacd6..9aaa0402 100644 --- a/sfall/Modules/LoadGameHook.h +++ b/sfall/Modules/LoadGameHook.h @@ -89,6 +89,7 @@ enum LoopFlag : unsigned long { BARTER = 1 << 17, // 0x20000 HEROWIN = 1 << 18, // 0x40000 Hero Appearance mod DIALOGVIEW = 1 << 19, // 0x80000 + COUNTERWIN = 1 << 20, // 0x100000 Counter window for moving multiple items or setting a timer // RESERVED = 1 << 31 }; diff --git a/sfall/Modules/Message.cpp b/sfall/Modules/Message.cpp index 638a99b5..dd79f94c 100644 --- a/sfall/Modules/Message.cpp +++ b/sfall/Modules/Message.cpp @@ -105,7 +105,7 @@ static void ReadExtraGameMsgFiles() { } path += ".msg"; fo::MessageList* list = new fo::MessageList(); - if (fo::func::message_load(list, (char*)path.data()) == 1) { + if (fo::func::message_load(list, path.c_str()) == 1) { gExtraGameMsgLists.insert(std::make_pair(0x2000 + number, list)); } else { delete list; @@ -125,13 +125,16 @@ long Message::AddExtraMsgFile(const char* msgName, long msgNumber) { std::string path("game\\"); path += msgName; fo::MessageList* list = new fo::MessageList(); - if (fo::func::message_load(list, path.c_str())) { - if (msgNumber == 0) msgNumber = msgNumCounter++; - gExtraGameMsgLists.emplace(msgNumber, list); - } else { - delete list; - msgNumber = -2; + if (!fo::func::message_load(list, path.c_str())) { + // change current language folder + path.insert(0, "..\\english\\"); + if (!fo::func::message_load(list, path.c_str())) { + delete list; + return -2; + } } + if (msgNumber == 0) msgNumber = msgNumCounter++; + gExtraGameMsgLists.emplace(msgNumber, list); return msgNumber; } diff --git a/sfall/Modules/Objects.cpp b/sfall/Modules/Objects.cpp index d20aa9ae..7bfe3f68 100644 --- a/sfall/Modules/Objects.cpp +++ b/sfall/Modules/Objects.cpp @@ -199,7 +199,8 @@ void Objects::LoadProtoAutoMaxLimit() { } static void __declspec(naked) obj_insert_hack() { - using namespace fo::Fields; + using namespace fo; + using namespace Fields; __asm { mov edi, [ebx]; mov [esp + 0x38 - 0x1C + 4], esi; // 0 @@ -208,7 +209,7 @@ static void __declspec(naked) obj_insert_hack() { retn; insert: mov esi, [ecx]; // esi - inserted object - cmp dword ptr [esi + protoId], 0x5000004; // corpse blood pid + cmp dword ptr [esi + protoId], PID_CORPSE_BLOOD; jnz skip; xor edi, edi; skip: From 9d0b061d1b71cca3615ef51d01d17d02e870f77d Mon Sep 17 00:00:00 2001 From: NovaRain Date: Mon, 19 Aug 2019 14:12:03 +0800 Subject: [PATCH 09/34] Added OverrideCriticalFile option for specifying alternative crit file Added code for preparing clearing map_update_event in queue. --- artifacts/ddraw.ini | 6 ++++-- sfall/Modules/Criticals.cpp | 12 +++++++----- sfall/Modules/ScriptExtender.cpp | 15 +++++++++++++-- 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/artifacts/ddraw.ini b/artifacts/ddraw.ini index cf7936cc..fbbc411f 100644 --- a/artifacts/ddraw.ini +++ b/artifacts/ddraw.ini @@ -336,7 +336,7 @@ OverrideArtCacheSize=0 ;Set to 2 to block all saving in combat SaveInCombatFix=1 -;Point at an ini file containing elevator data +;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. @@ -364,6 +364,8 @@ 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 +;Uncomment the next line to specify an alternative path and file name for the critical table file +;OverrideCriticalFile=CriticalOverrides.ini ;Set to 1 to get notification of karma changes in the notification window DisplayKarmaChanges=0 @@ -515,7 +517,7 @@ FastShotFix=1 BoostScriptDialogLimit=0 ;Allows you to edit the skill tables -;Point the next line at an ini file containing the replacement skill data +;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 diff --git a/sfall/Modules/Criticals.cpp b/sfall/Modules/Criticals.cpp index 630df8f4..3df9c377 100644 --- a/sfall/Modules/Criticals.cpp +++ b/sfall/Modules/Criticals.cpp @@ -28,7 +28,8 @@ namespace sfall { -static const char* critTableFile = ".\\CriticalOverrides.ini"; +static std::string critTableFile(".\\"); + const DWORD Criticals::critTableCount = 2 * 19 + 1; // Number of species in new critical table static DWORD mode; @@ -88,7 +89,7 @@ static void CritLoad() { fo::CritInfo& newEffect = baseCritTable[newCritter][part][crit]; fo::CritInfo& defaultEffect = fo::var::crit_succ_eff[critter][part][crit]; for (int i = 0; i < 7; i++) { - newEffect.values[i] = GetPrivateProfileIntA(section, critNames[i], defaultEffect.values[i], critTableFile); + newEffect.values[i] = GetPrivateProfileIntA(section, critNames[i], defaultEffect.values[i], critTableFile.c_str()); if (isDebug) { char logmsg[256]; if (newEffect.values[i] != defaultEffect.values[i]) { @@ -111,11 +112,11 @@ static void CritLoad() { for (int critter = 0; critter < Criticals::critTableCount; critter++) { sprintf_s(buf, "c_%02d", critter); int all; - if (!(all = GetPrivateProfileIntA(buf, "Enabled", 0, critTableFile))) continue; + if (!(all = GetPrivateProfileIntA(buf, "Enabled", 0, critTableFile.c_str()))) continue; for (int part = 0; part < 9; part++) { if (all < 2) { sprintf_s(buf2, "Part_%d", part); - if (!GetPrivateProfileIntA(buf, buf2, 0, critTableFile)) continue; + if (!GetPrivateProfileIntA(buf, buf2, 0, critTableFile.c_str())) continue; } sprintf_s(buf2, "c_%02d_%d", critter, part); @@ -123,7 +124,7 @@ static void CritLoad() { fo::CritInfo& effect = baseCritTable[critter][part][crit]; for (int i = 0; i < 7; i++) { sprintf_s(buf3, "e%d_%s", crit, critNames[i]); - effect.values[i] = GetPrivateProfileIntA(buf2, buf3, effect.values[i], critTableFile); + effect.values[i] = GetPrivateProfileIntA(buf2, buf3, effect.values[i], critTableFile.c_str()); } } } @@ -260,6 +261,7 @@ void Criticals::init() { mode = GetConfigInt("Misc", "OverrideCriticalTable", 2); if (mode < 0 || mode > 3) mode = 0; if (mode) { + critTableFile += GetConfigString("Misc", "OverrideCriticalFile", "CriticalOverrides.ini", MAX_PATH - 3); CriticalTableOverride(); } diff --git a/sfall/Modules/ScriptExtender.cpp b/sfall/Modules/ScriptExtender.cpp index fbf6c1d2..e6ee6327 100644 --- a/sfall/Modules/ScriptExtender.cpp +++ b/sfall/Modules/ScriptExtender.cpp @@ -681,7 +681,16 @@ static void __declspec(naked) map_save_in_game_hook() { jmp fo::funcoffs::game_time_; } } - +/* +static void ClearEventUpdateMapProc() { + using namespace fo; + __asm { + mov eax, map_update_event; // type + xor edx, edx; // func + call fo::funcoffs::queue_clear_type_; + } +} +*/ void ScriptExtender::init() { LoadGameHook::OnAfterGameStarted() += LoadGlobalScripts; LoadGameHook::OnGameReset() += [] () { @@ -744,7 +753,9 @@ void ScriptExtender::init() { long long data = 0x397401C1F6; // test cl, 1; jz 0x483CF2 SafeWriteBytes(0x483CB4, (BYTE*)&data, 5); - // Set the DAM_BACKWASH_ flag for the attacker before calling compute_damage_ + //ScriptExtender::OnMapExit() += ClearEventUpdateMapProc; + + // Set the DAM_BACKWASH flag for the attacker before calling compute_damage_ SafeWrite32(0x423DE7, 0x40164E80); // or [esi+ctd.flags3Source], DAM_BACKWASH_ long idata = 0x146E09; // or dword ptr [esi+ctd.flagsSource], ebp SafeWriteBytes(0x423DF0, (BYTE*)&idata, 3); From 5c4eb31bc3372c5f23ebdff38a9181708621f180 Mon Sep 17 00:00:00 2001 From: NovaRain Date: Tue, 20 Aug 2019 00:26:35 +0800 Subject: [PATCH 10/34] Added IniConfigFolder option to override the path of all scripted ini Fixed a crash bug in message_str_game function when passing a negative fileId value. --- artifacts/ddraw.ini | 7 +++- sfall/Modules/ScriptExtender.cpp | 15 +++++++ sfall/Modules/ScriptExtender.h | 2 + sfall/Modules/Scripting/Handlers/Misc.cpp | 46 +++++++++++++++++----- sfall/Modules/Scripting/Handlers/Utils.cpp | 2 +- sfall/Utils.cpp | 13 ++++++ sfall/Utils.h | 2 + 7 files changed, 76 insertions(+), 11 deletions(-) diff --git a/artifacts/ddraw.ini b/artifacts/ddraw.ini index fbbc411f..b2c8d5b9 100644 --- a/artifacts/ddraw.ini +++ b/artifacts/ddraw.ini @@ -364,7 +364,7 @@ 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 -;Uncomment the next line to specify an alternative path and file name for the critical table file +;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 @@ -724,6 +724,11 @@ 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 scripted ini configuration files +;You will need to place all the available ini files of the mods to this directory +;The maximum length of the specified path is 61 characters +;IniConfigFolder=IniConfig + ;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX [Debugging] ;Extra sfall configuration settings that can be used by modders diff --git a/sfall/Modules/ScriptExtender.cpp b/sfall/Modules/ScriptExtender.cpp index e6ee6327..eff5a4a7 100644 --- a/sfall/Modules/ScriptExtender.cpp +++ b/sfall/Modules/ScriptExtender.cpp @@ -53,6 +53,8 @@ static DWORD _stdcall HandleMapUpdateForScripts(const DWORD procId); static int idle; +std::string ScriptExtender::iniConfigFolder; + struct GlobalScript { ScriptProgram prog; int count; @@ -724,6 +726,19 @@ void ScriptExtender::init() { dlogr("Arrays in backward-compatiblity mode.", DL_SCRIPT); } + iniConfigFolder = GetConfigString("Scripts", "IniConfigFolder", ""); + size_t len = iniConfigFolder.length(); + if (len) { + char c = iniConfigFolder[len - 1]; + bool pathSeparator = (c == '\\' || c == '/'); + if (len > 62 || (len == 62 && !pathSeparator)) { + iniConfigFolder.clear(); + dlogr("Error: IniConfigFolder path is too long.", DL_SCRIPT); + } else if (!pathSeparator) { + iniConfigFolder += '\\'; + } + } + alwaysFindScripts = isDebug && (GetPrivateProfileIntA("Debugging", "AlwaysFindScripts", 0, ::sfall::ddrawIni) != 0); if (alwaysFindScripts) dlogr("Always searching for global scripts behavior enabled.", DL_SCRIPT); diff --git a/sfall/Modules/ScriptExtender.h b/sfall/Modules/ScriptExtender.h index bde1fb27..c7473730 100644 --- a/sfall/Modules/ScriptExtender.h +++ b/sfall/Modules/ScriptExtender.h @@ -32,6 +32,8 @@ public: const char* name() { return "ScriptExtender"; } void init(); + static std::string iniConfigFolder; + // Called before map exit (before map_exit_p_proc handlers in normal scripts) static Delegate<>& OnMapExit(); }; diff --git a/sfall/Modules/Scripting/Handlers/Misc.cpp b/sfall/Modules/Scripting/Handlers/Misc.cpp index ec107999..3a9087d1 100644 --- a/sfall/Modules/Scripting/Handlers/Misc.cpp +++ b/sfall/Modules/Scripting/Handlers/Misc.cpp @@ -18,6 +18,7 @@ #include +#include "..\..\..\Utils.h" #include "..\..\..\FalloutEngine\Fallout2.h" #include "..\..\AI.h" #include "..\..\Combat.h" @@ -523,7 +524,8 @@ static int ParseIniSetting(const char* iniString, const char* &key, char section if (!key) return -1; DWORD filelen = (DWORD)key - (DWORD)iniString; - if (filelen >= 64) return -1; + if (ScriptExtender::iniConfigFolder.empty() && filelen >= 64) return -1; + const char* fileEnd = key; key = strstr(key + 1, "|"); if (!key) return -1; @@ -533,9 +535,24 @@ static int ParseIniSetting(const char* iniString, const char* &key, char section file[0] = '.'; file[1] = '\\'; - memcpy(&file[2], iniString, filelen); - file[filelen + 2] = 0; - + if (!ScriptExtender::iniConfigFolder.empty()) { + const char* pos = strfind(iniString, &::sfall::ddrawIni[2]); + if (pos && pos < fileEnd) goto ddraw; + size_t len = ScriptExtender::iniConfigFolder.length(); // limit 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 (max len 63) + } else { +ddraw: + memcpy(&file[2], iniString, filelen); + file[filelen + 2] = 0; + } memcpy(section, &iniString[filelen + 1], seclen); section[seclen] = 0; @@ -546,7 +563,7 @@ static int ParseIniSetting(const char* iniString, const char* &key, char section static char IniStrBuffer[256]; static DWORD GetIniSetting(const char* str, bool isString) { const char* key; - char section[33], file[67]; + char section[33], file[128]; if (ParseIniSetting(str, key, section, file) < 0) { return -1; @@ -1232,11 +1249,23 @@ void sf_set_ini_setting(OpcodeContext& ctx) { } } +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; + } + return fileName; +} + char getIniSectionBuf[5120]; void sf_get_ini_sections(OpcodeContext& ctx) { - auto fileName = std::string(".\\") + ctx.arg(0).asString(); - GetPrivateProfileSectionNamesA(getIniSectionBuf, 5120, fileName.data()); + GetPrivateProfileSectionNamesA(getIniSectionBuf, 5120, GetIniFilePath(ctx.arg(0)).data()); std::vector sections; char* section = getIniSectionBuf; while (*section != 0) { @@ -1256,9 +1285,8 @@ void sf_get_ini_sections(OpcodeContext& ctx) { } void sf_get_ini_section(OpcodeContext& ctx) { - auto fileName = std::string(".\\") + ctx.arg(0).asString(); auto section = ctx.arg(1).asString(); - GetPrivateProfileSectionA(section, getIniSectionBuf, 5120, fileName.data()); + GetPrivateProfileSectionA(section, getIniSectionBuf, 5120, GetIniFilePath(ctx.arg(0)).data()); int arrayId = TempArray(-1, 0); // associative auto& arr = arrays[arrayId]; char *key = getIniSectionBuf, *val = nullptr; diff --git a/sfall/Modules/Scripting/Handlers/Utils.cpp b/sfall/Modules/Scripting/Handlers/Utils.cpp index 1feda9cd..d5f85ee6 100644 --- a/sfall/Modules/Scripting/Handlers/Utils.cpp +++ b/sfall/Modules/Scripting/Handlers/Utils.cpp @@ -281,7 +281,7 @@ void sf_message_str_game(OpcodeContext& ctx) { int fileId = fileIdArg.asInt(); int msgId = msgIdArg.asInt(); - if (fileId < 20) { // main msg files + if (fileId >= 0 && fileId <= 19) { // main msg files msg = fo::GetMessageStr(gameMsgFiles[fileId], msgId); } else if (fileId >= 0x1000 && fileId <= 0x1005) { // proto msg files msg = fo::GetMessageStr(&fo::var::proto_msg_files[fileId - 0x1000], msgId); diff --git a/sfall/Utils.cpp b/sfall/Utils.cpp index d8137911..bbf36fba 100644 --- a/sfall/Utils.cpp +++ b/sfall/Utils.cpp @@ -47,4 +47,17 @@ void strtrim(char* str) { } } +// returns position, find word must be lowercase +const char* strfind(const char* source, const char* word) { + if (source == 0 || word == 0 || *word == 0) return 0; + const char *w_pos, *s_pos; + while(*source != 0) { + w_pos = word, s_pos = source++; + while (tolower(*s_pos++) == *w_pos++) { + if (*w_pos == 0) return s_pos - (w_pos - word); + } + } + return 0; +} + } diff --git a/sfall/Utils.h b/sfall/Utils.h index 4231203c..9980376b 100644 --- a/sfall/Utils.h +++ b/sfall/Utils.h @@ -32,4 +32,6 @@ bool isSpace(char c); void strtrim(char* str); +const char* strfind(const char* source, const char* word); + } From 09e282cd65d190d4cbd442a007b00c049b4c607a Mon Sep 17 00:00:00 2001 From: NovaRain Date: Tue, 20 Aug 2019 14:53:33 +0800 Subject: [PATCH 11/34] Corrected/edited some code. --- artifacts/ddraw.ini | 4 ++-- sfall/Modules/Criticals.cpp | 2 +- sfall/Modules/ScriptExtender.cpp | 7 +++--- sfall/Modules/Scripting/Handlers/Utils.cpp | 28 ++++++++++------------ sfall/Modules/Scripting/ScriptValue.cpp | 3 +-- sfall/Modules/Skills.cpp | 2 +- sfall/main.cpp | 2 +- 7 files changed, 22 insertions(+), 26 deletions(-) diff --git a/artifacts/ddraw.ini b/artifacts/ddraw.ini index b2c8d5b9..a61cc0dd 100644 --- a/artifacts/ddraw.ini +++ b/artifacts/ddraw.ini @@ -724,8 +724,8 @@ 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 scripted ini configuration files -;You will need to place all the available ini files of the mods to this directory +;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 diff --git a/sfall/Modules/Criticals.cpp b/sfall/Modules/Criticals.cpp index 3df9c377..29a3ec6a 100644 --- a/sfall/Modules/Criticals.cpp +++ b/sfall/Modules/Criticals.cpp @@ -261,7 +261,7 @@ void Criticals::init() { mode = GetConfigInt("Misc", "OverrideCriticalTable", 2); if (mode < 0 || mode > 3) mode = 0; if (mode) { - critTableFile += GetConfigString("Misc", "OverrideCriticalFile", "CriticalOverrides.ini", MAX_PATH - 3); + critTableFile += GetConfigString("Misc", "OverrideCriticalFile", "CriticalOverrides.ini", MAX_PATH); CriticalTableOverride(); } diff --git a/sfall/Modules/ScriptExtender.cpp b/sfall/Modules/ScriptExtender.cpp index eff5a4a7..3c17303e 100644 --- a/sfall/Modules/ScriptExtender.cpp +++ b/sfall/Modules/ScriptExtender.cpp @@ -602,12 +602,11 @@ static DWORD _stdcall HandleMapUpdateForScripts(const DWORD procId) { for (SfallProgsMap::iterator it = sfallProgsMap.begin(); it != sfallProgsMap.end(); it++) { fo::func::runProgram(it->second.ptr); } - } + } else if (procId == fo::ScriptProc::map_exit_p_proc) onMapExit.invoke(); + RunGlobalScriptsAtProc(procId); // gl* scripts of types 0 and 3 RunHookScriptsAtProc(procId); // all hs_ scripts - if (procId == fo::ScriptProc::map_exit_p_proc) onMapExit.invoke(); - return procId; // restore eax (don't delete) } @@ -726,7 +725,7 @@ void ScriptExtender::init() { dlogr("Arrays in backward-compatiblity mode.", DL_SCRIPT); } - iniConfigFolder = GetConfigString("Scripts", "IniConfigFolder", ""); + iniConfigFolder = GetConfigString("Scripts", "IniConfigFolder", "", 64); size_t len = iniConfigFolder.length(); if (len) { char c = iniConfigFolder[len - 1]; diff --git a/sfall/Modules/Scripting/Handlers/Utils.cpp b/sfall/Modules/Scripting/Handlers/Utils.cpp index d5f85ee6..55e04f66 100644 --- a/sfall/Modules/Scripting/Handlers/Utils.cpp +++ b/sfall/Modules/Scripting/Handlers/Utils.cpp @@ -276,24 +276,22 @@ void sf_round(OpcodeContext& ctx) { void sf_message_str_game(OpcodeContext& ctx) { const char* msg = nullptr; - const ScriptValue &fileIdArg = ctx.arg(0), - &msgIdArg = ctx.arg(1); - int fileId = fileIdArg.asInt(); - int msgId = msgIdArg.asInt(); - if (fileId >= 0 && fileId <= 19) { // main msg files - msg = fo::GetMessageStr(gameMsgFiles[fileId], msgId); - } else if (fileId >= 0x1000 && fileId <= 0x1005) { // proto msg files - msg = fo::GetMessageStr(&fo::var::proto_msg_files[fileId - 0x1000], msgId); - } else if (fileId >= 0x2000) { // Extra game message files. - ExtraGameMessageListsMap::iterator it = gExtraGameMsgLists.find(fileId); - if (it != gExtraGameMsgLists.end()) { - msg = GetMsg(it->second.get(), msgId, 2); + int fileId = ctx.arg(0).rawValue(); + if (fileId >= 0) { + int msgId = ctx.arg(1).rawValue(); + if (fileId < 20) { // main msg files + msg = fo::GetMessageStr(gameMsgFiles[fileId], msgId); + } else if (fileId >= 0x1000 && fileId <= 0x1005) { // proto msg files + msg = fo::GetMessageStr(&fo::var::proto_msg_files[fileId - 0x1000], msgId); + } else if (fileId >= 0x2000) { // Extra game message files. + ExtraGameMessageListsMap::iterator it = gExtraGameMsgLists.find(fileId); + if (it != gExtraGameMsgLists.end()) { + msg = GetMsg(it->second.get(), msgId, 2); + } } } - if (msg == nullptr) { - msg = "Error"; - } + if (msg == nullptr) msg = "Error"; ctx.setReturn(msg); } diff --git a/sfall/Modules/Scripting/ScriptValue.cpp b/sfall/Modules/Scripting/ScriptValue.cpp index 187b5763..e5911f6f 100644 --- a/sfall/Modules/Scripting/ScriptValue.cpp +++ b/sfall/Modules/Scripting/ScriptValue.cpp @@ -70,8 +70,7 @@ ScriptValue::ScriptValue(fo::GameObject* obj) { _type = DataType::INT; } -bool ScriptValue::isInt() const -{ +bool ScriptValue::isInt() const { return _type == DataType::INT; } diff --git a/sfall/Modules/Skills.cpp b/sfall/Modules/Skills.cpp index 36dabfa5..45bbb4da 100644 --- a/sfall/Modules/Skills.cpp +++ b/sfall/Modules/Skills.cpp @@ -332,7 +332,7 @@ void Skills::init() { HookCall(0x47831E, item_w_skill_hook); char buf[512], key[16]; - auto skillsFile = GetConfigString("Misc", "SkillsFile", ""); + auto skillsFile = GetConfigString("Misc", "SkillsFile", "", MAX_PATH); if (!skillsFile.empty()) { fo::SkillInfo *skills = fo::var::skill_data; diff --git a/sfall/main.cpp b/sfall/main.cpp index 2fa8dc68..e4cf2cdb 100644 --- a/sfall/main.cpp +++ b/sfall/main.cpp @@ -285,7 +285,7 @@ inline void SfallInit() { strcpy_s(ini, ::sfall::ddrawIni); } - GetConfigString("Main", "TranslationsINI", "./Translations.ini", translationIni, 65); + GetConfigString("Main", "TranslationsINI", ".\\Translations.ini", translationIni, 65); modifiedIni = GetConfigInt("Main", "ModifiedIni", 0); InitModules(); From 9fea48093ef09b54a2f2cac434be872cf72fdb30 Mon Sep 17 00:00:00 2001 From: NovaRain Date: Thu, 22 Aug 2019 11:32:33 +0800 Subject: [PATCH 12/34] Fixed player's position if entrance tile is blocked when entering a map Fixed player's direction after leaving a map via ladder/stairs. Other minor fixes in sfall code. --- sfall/FalloutEngine/EngineUtils.cpp | 2 -- sfall/FalloutEngine/Functions_def.h | 2 ++ sfall/Modules/AnimationsAtOnceLimit.cpp | 3 ++ sfall/Modules/BugFixes.cpp | 28 +++++++++++++++++++ sfall/Modules/LoadOrder.cpp | 1 + sfall/Modules/Scripting/Handlers/Misc.cpp | 10 +++---- sfall/Modules/Scripting/Handlers/Worldmap.cpp | 24 ++++++++-------- sfall/Utils.cpp | 5 ++-- 8 files changed, 54 insertions(+), 21 deletions(-) diff --git a/sfall/FalloutEngine/EngineUtils.cpp b/sfall/FalloutEngine/EngineUtils.cpp index 1c6675bd..a8b06869 100644 --- a/sfall/FalloutEngine/EngineUtils.cpp +++ b/sfall/FalloutEngine/EngineUtils.cpp @@ -297,7 +297,6 @@ static bool LoadFrmHeader(UnlistedFrm *frmHeader, fo::DbFile* frmStream) { } static bool LoadFrmFrame(UnlistedFrm::Frame *frame, fo::DbFile* frmStream) { - //FRMframe *frameHeader = (FRMframe*)frameMEM; //BYTE* frameBuff = frame + sizeof(FRMframe); @@ -320,7 +319,6 @@ static bool LoadFrmFrame(UnlistedFrm::Frame *frame, fo::DbFile* frmStream) { } UnlistedFrm *LoadUnlistedFrm(char *frmName, unsigned int folderRef) { - if (folderRef > fo::OBJ_TYPE_SKILLDEX) return nullptr; char *artfolder = fo::var::art[folderRef].path; // address of art type name diff --git a/sfall/FalloutEngine/Functions_def.h b/sfall/FalloutEngine/Functions_def.h index 771d0709..c3ca3971 100644 --- a/sfall/FalloutEngine/Functions_def.h +++ b/sfall/FalloutEngine/Functions_def.h @@ -22,9 +22,11 @@ WRAP_WATCOM_FFUNC3(void, display_inventory, long, inventoryOffset, long, visible 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) WRAP_WATCOM_FFUNC7(void, make_straight_path_func, GameObject*, objFrom, DWORD, tileFrom, DWORD, tileTo, void*, rotationPtr, DWORD*, result, long, flags, void*, func) +WRAP_WATCOM_FFUNC3(long, 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) 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) // stdcall WRAP_WATCOM_FUNC1(AIcap*, ai_cap, GameObject*, critter) diff --git a/sfall/Modules/AnimationsAtOnceLimit.cpp b/sfall/Modules/AnimationsAtOnceLimit.cpp index 5882e09f..2d7464d2 100644 --- a/sfall/Modules/AnimationsAtOnceLimit.cpp +++ b/sfall/Modules/AnimationsAtOnceLimit.cpp @@ -321,6 +321,9 @@ void AnimationsAtOnce::init() { // Fix crash when the critter goes through a door with animation trigger MakeJump(0x41755E, object_move_hack); + + // Fix player's direction after leaving a map via ladder/stairs + SafeWriteBatch(25, {0x49C972, 0x49CA16, 0x49CABA}); } void AnimationsAtOnce::exit() { diff --git a/sfall/Modules/BugFixes.cpp b/sfall/Modules/BugFixes.cpp index 485f60c7..8d685cae 100644 --- a/sfall/Modules/BugFixes.cpp +++ b/sfall/Modules/BugFixes.cpp @@ -2266,6 +2266,31 @@ skip: } } +static long __fastcall GetFreeTilePlacement(long elev, long tile) { + long count = 0, dist = 1; + long freeTile = tile; + long rotation = fo::var::rotation; + while (fo::func::obj_blocking_at(0, freeTile, elev)) { + freeTile = fo::func::tile_num_in_direction(freeTile, rotation, dist); + if (++count > 5 && ++dist > 5) return tile; + if (++rotation > 5) rotation = 0; + } + return freeTile; +} + +static void __declspec(naked) map_check_state_hook() { + __asm { + mov ecx, esi; // elev + call GetFreeTilePlacement; // edx - tile + mov edx, eax; // tile + // restore + mov ebx, esi; // elev + mov eax, ds:[FO_VAR_obj_dude]; + xor ecx, ecx; + jmp fo::funcoffs::obj_move_to_tile_; + } +} + void BugFixes::init() { #ifndef NDEBUG @@ -2863,6 +2888,9 @@ void BugFixes::init() MakeCall(0x4C2367, wmInterfaceInit_hack); dlogr(" Done", DL_INIT); } + + // Place the player on a nearby empty tile if the entrance tile is blocked by another object when entering a map + HookCall(0x4836F8, map_check_state_hook); } } diff --git a/sfall/Modules/LoadOrder.cpp b/sfall/Modules/LoadOrder.cpp index 691264e1..1c144cf8 100644 --- a/sfall/Modules/LoadOrder.cpp +++ b/sfall/Modules/LoadOrder.cpp @@ -203,6 +203,7 @@ static void GetExtraPatches() { if (hFind != INVALID_HANDLE_VALUE) { do { std::string name(searchPath + findData.cFileName); + if ((name.length() - name.find_last_of('.')) > 4) continue; dlog_f("> %s\n", DL_MAIN, name.c_str()); patchFiles.push_back(name); } while (FindNextFile(hFind, &findData)); diff --git a/sfall/Modules/Scripting/Handlers/Misc.cpp b/sfall/Modules/Scripting/Handlers/Misc.cpp index 3a9087d1..32893a7a 100644 --- a/sfall/Modules/Scripting/Handlers/Misc.cpp +++ b/sfall/Modules/Scripting/Handlers/Misc.cpp @@ -538,7 +538,7 @@ static int ParseIniSetting(const char* iniString, const char* &key, char section if (!ScriptExtender::iniConfigFolder.empty()) { const char* pos = strfind(iniString, &::sfall::ddrawIni[2]); if (pos && pos < fileEnd) goto ddraw; - size_t len = ScriptExtender::iniConfigFolder.length(); // limit to 62 characters + 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--) { @@ -547,7 +547,7 @@ static int ParseIniSetting(const char* iniString, const char* &key, char section break; } } - strncpy_s(&file[2 + len], (128 - 2) - len, &iniString[n], filelen - n); // copy filename (max len 63) + strncpy_s(&file[2 + len], (128 - 2) - len, &iniString[n], filelen - n); // copy filename } else { ddraw: memcpy(&file[2], iniString, filelen); @@ -1233,7 +1233,7 @@ void sf_set_ini_setting(OpcodeContext& ctx) { saveValue = argVal.strValue(); } const char* key; - char section[33], file[67]; + char section[33], file[128]; int result = ParseIniSetting(ctx.arg(0).strValue(), key, section, file); if (result > 0) { result = WritePrivateProfileString(section, key, saveValue, file); @@ -1269,7 +1269,7 @@ void sf_get_ini_sections(OpcodeContext& ctx) { std::vector sections; char* section = getIniSectionBuf; while (*section != 0) { - sections.push_back(section); + sections.push_back(section); // position section += std::strlen(section) + 1; } size_t sz = sections.size(); @@ -1279,7 +1279,7 @@ void sf_get_ini_sections(OpcodeContext& ctx) { for (size_t i = 0; i < sz; ++i) { size_t j = i + 1; int len = (j < sz) ? sections[j] - sections[i] - 1 : -1; - arr.val[i].set(sections[i], len); + arr.val[i].set(sections[i], len); // copy string from buffer } ctx.setReturn(arrayId); } diff --git a/sfall/Modules/Scripting/Handlers/Worldmap.cpp b/sfall/Modules/Scripting/Handlers/Worldmap.cpp index f8eb6dd2..50bc6bd7 100644 --- a/sfall/Modules/Scripting/Handlers/Worldmap.cpp +++ b/sfall/Modules/Scripting/Handlers/Worldmap.cpp @@ -31,26 +31,26 @@ namespace sfall namespace script { -static DWORD ForceEnconterMapID = -1; -static DWORD ForceEnconterFlags; +static DWORD ForceEncounterMapID = -1; +static DWORD ForceEncounterFlags; DWORD ForceEncounterRestore() { long long data = 0x672E043D83; // cmp ds:_Meet_Frank_Horrigan, 0 SafeWriteBytes(0x4C06D1, (BYTE*)&data, 5); - ForceEnconterFlags = 0; - DWORD mapID = ForceEnconterMapID; - ForceEnconterMapID = -1; + ForceEncounterFlags = 0; + DWORD mapID = ForceEncounterMapID; + ForceEncounterMapID = -1; return mapID; } static void __declspec(naked) wmRndEncounterOccurred_hack() { __asm { - test ForceEnconterFlags, 0x1; // _NoCar flag + test ForceEncounterFlags, 0x1; // _NoCar flag jnz noCar; cmp ds:[FO_VAR_Move_on_Car], 0; jz noCar; mov edx, FO_VAR_carCurrentArea; - mov eax, ForceEnconterMapID; + mov eax, ForceEncounterMapID; call fo::funcoffs::wmMatchAreaContainingMapIdx_; noCar: //push ecx; @@ -63,26 +63,26 @@ noCar: } void sf_force_encounter(OpcodeContext& cxt) { - if (ForceEnconterFlags & (1 << 31)) return; // wait prev. encounter + if (ForceEncounterFlags & (1 << 31)) return; // wait prev. encounter DWORD mapID = cxt.arg(0).rawValue(); if (mapID < 0) { cxt.printOpcodeError("%s() - invalid map number.", cxt.getOpcodeName()); return; } - if (ForceEnconterMapID == -1) MakeJump(0x4C06D1, wmRndEncounterOccurred_hack); + if (ForceEncounterMapID == -1) MakeJump(0x4C06D1, wmRndEncounterOccurred_hack); - ForceEnconterMapID = mapID; + ForceEncounterMapID = mapID; DWORD flags = 0; if (cxt.numArgs() > 1) { flags = cxt.arg(1).rawValue(); if (flags & 2) { // _Lock flag flags |= (1 << 31); // set bit 31 } else { - flags = flags & ~(1 << 31); + flags &= ~(1 << 31); } } - ForceEnconterFlags = flags; + ForceEncounterFlags = flags; } // world_map_functions diff --git a/sfall/Utils.cpp b/sfall/Utils.cpp index bbf36fba..8753b06c 100644 --- a/sfall/Utils.cpp +++ b/sfall/Utils.cpp @@ -53,8 +53,9 @@ const char* strfind(const char* source, const char* word) { const char *w_pos, *s_pos; while(*source != 0) { w_pos = word, s_pos = source++; - while (tolower(*s_pos++) == *w_pos++) { - if (*w_pos == 0) return s_pos - (w_pos - word); + while (tolower(*s_pos) == *w_pos) { + s_pos++; + if (*++w_pos == 0) return s_pos - (w_pos - word); } } return 0; From 477ff9668c8ab3e907c25815a465eaa7e0988f4b Mon Sep 17 00:00:00 2001 From: NovaRain Date: Thu, 22 Aug 2019 13:12:32 +0800 Subject: [PATCH 13/34] Added a new argument to HOOK_ADJUSTFID. Fixed incorrect FRM being displayed for opened bag/backpack in the inventory when Hero Appearance Mod is enabled (#262) Fixed MainMenuFontColour not changing the font color for the copyright text on the main menu (#261) Code correction in main.cpp. --- artifacts/ddraw.ini | 2 +- artifacts/scripting/hookscripts.txt | 3 ++- sfall/Modules/HeroAppearance.cpp | 18 +++++++-------- sfall/Modules/HookScripts/InventoryHs.cpp | 5 ++-- sfall/Modules/Interface.cpp | 4 ---- sfall/Modules/MainMenu.cpp | 28 ++++++++++++----------- sfall/main.cpp | 15 ++++++++---- sfall/main.h | 1 + 8 files changed, 41 insertions(+), 35 deletions(-) diff --git a/artifacts/ddraw.ini b/artifacts/ddraw.ini index a61cc0dd..095f2eec 100644 --- a/artifacts/ddraw.ini +++ b/artifacts/ddraw.ini @@ -503,7 +503,7 @@ RemoveCriticalTimelimits=0 ;Set to 1 to enable party members with level 6 protos to reach level 6 NPCStage6Fix=0 -;Change the colour of the font used on the main menu for the Fallout/sfall version number +;Change the colour of the font used on the main menu for the Fallout/sfall version number and copyright text ;It's the last byte ('3c' by default) that picks the colour used. The first byte supplies additional flags ;MainMenuFontColour=0x0600003c diff --git a/artifacts/scripting/hookscripts.txt b/artifacts/scripting/hookscripts.txt index 42d29c69..d7da9086 100644 --- a/artifacts/scripting/hookscripts.txt +++ b/artifacts/scripting/hookscripts.txt @@ -452,7 +452,8 @@ Also happens on other screens, like barter. NOTE: FID has following format: 0x0ABBCDDD, where A is object type, BB - animation code (always 0 in this case), C - weapon code, DDD - FRM index in LST file. -int arg1 - the vanilla fid calculated by the engine according to critter base FID and armor/weapon being used +int arg1 - the vanilla FID calculated by the engine according to critter base FID and armor/weapon being used +int arg2 - the modified FID calculated by the internal sfall code (like Hero Appearance Mod) int ret1 - overrides the calculated FID with provided value diff --git a/sfall/Modules/HeroAppearance.cpp b/sfall/Modules/HeroAppearance.cpp index c5baea0d..93e176d8 100644 --- a/sfall/Modules/HeroAppearance.cpp +++ b/sfall/Modules/HeroAppearance.cpp @@ -63,7 +63,7 @@ typedef struct LineNode { } } LineNode; -/////////////////////////////////////////////////////////////////TEXT FUNCTIONS//////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////TEXT FUNCTIONS////////////////////////////////////////////////////////////////////// static void SetFont(long ref) { fo::func::text_font(ref); @@ -141,7 +141,7 @@ static void DeleteWordWrapList(LineNode *CurrentLine) { } } -/////////////////////////////////////////////////////////////////DAT FUNCTIONS//////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////DAT FUNCTIONS/////////////////////////////////////////////////////////////////////// static void* LoadDat(char*fileName) { return fo::func::dbase_open(fileName); @@ -151,7 +151,7 @@ static void UnloadDat(void *dat) { fo::func::dbase_close(dat); } -/////////////////////////////////////////////////////////////////OTHER FUNCTIONS//////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////OTHER FUNCTIONS///////////////////////////////////////////////////////////////////// static DWORD BuildFrmId(DWORD lstRef, DWORD lstNum) { return fo::func::art_id(lstRef, lstNum, 0, 0, 0); @@ -164,7 +164,7 @@ static void PlayAcm(char *acmName) { } } -/////////////////////////////////////////////////////////////////APP MOD FUNCTIONS//////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////APP MOD FUNCTIONS/////////////////////////////////////////////////////////////////// static char GetSex() { return (fo::HeroIsFemale()) ? 'F' : 'M'; @@ -275,7 +275,7 @@ static void __declspec(naked) AdjustHeroBaseArt() { // adjust armor art if num below hero art range static void AdjustHeroArmorArt(DWORD fid) { - if (!PartyControl::IsNpcControlled()) { + if ((fid & 0xF000000) == (fo::OBJ_TYPE_CRITTER << 24) && !PartyControl::IsNpcControlled()) { DWORD fidBase = fid & 0xFFF; if (fidBase <= critterListSize) { fo::var::i_fid += critterListSize; @@ -349,7 +349,7 @@ static long _stdcall AddHeroCritNames() { // art_init_ return critterArt.total; } -///////////////////////////////////////////////////////////////GRAPHICS HERO FUNCTIONS////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////GRAPHICS HERO FUNCTIONS/////////////////////////////////////////////////////////////// static void DrawPC() { fo::RedrawObject(fo::var::obj_dude); @@ -479,7 +479,7 @@ endFunc: } } -/////////////////////////////////////////////////////////////////INTERFACE FUNCTIONS/////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////INTERFACE FUNCTIONS///////////////////////////////////////////////////////////////// static void sub_draw(long subWidth, long subHeight, long fromWidth, long fromHeight, long fromX, long fromY, BYTE *fromBuff, long toWidth, long toHeight, long toX, long toY, BYTE *toBuff, int maskRef) { @@ -1263,7 +1263,7 @@ static void __declspec(naked) CharScrnEnd() { } } -//////////////////////////////////////////////////////////////////////FIX FUNCTIONS//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////FIX FUNCTIONS////////////////////////////////////////////////////////////////// // Adjust PC SFX acm name. Skip Underscore char at the start of PC App Name static void __declspec(naked) FixPcSFX() { @@ -1323,7 +1323,7 @@ skip: } } -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Load Appearance data from GCD file static void __fastcall LoadGCDAppearance(fo::DbFile* fileStream) { diff --git a/sfall/Modules/HookScripts/InventoryHs.cpp b/sfall/Modules/HookScripts/InventoryHs.cpp index bf8ff054..6a54abe9 100644 --- a/sfall/Modules/HookScripts/InventoryHs.cpp +++ b/sfall/Modules/HookScripts/InventoryHs.cpp @@ -432,8 +432,9 @@ void AdjustFidHook(DWORD vanillaFid) { if (!HookScripts::HookHasScript(HOOK_ADJUSTFID)) return; BeginHook(); - argCount = 1; + argCount = 2; args[0] = vanillaFid; + args[1] = fo::var::i_fid; // modified FID by sfall code RunHookScript(HOOK_ADJUSTFID); if (cRet > 0) { fo::var::i_fid = rets[0]; @@ -502,7 +503,7 @@ void InitInventoryHookScripts() { LoadHookScript("hs_invenwield", HOOK_INVENWIELD); LoadHookScript("hs_adjustfid", HOOK_ADJUSTFID); - Inventory::OnAdjustFid() += AdjustFidHook; + Inventory::OnAdjustFid() += AdjustFidHook; // should be registered last } } diff --git a/sfall/Modules/Interface.cpp b/sfall/Modules/Interface.cpp index ab08acbb..bba7f6fd 100644 --- a/sfall/Modules/Interface.cpp +++ b/sfall/Modules/Interface.cpp @@ -26,8 +26,6 @@ namespace sfall { -bool hrpIsEnabled = false; - static BYTE movePointBackground[16 * 9 * 5]; static fo::UnlistedFrm* ifaceFrm = nullptr; @@ -447,8 +445,6 @@ static void WorldMapInterfacePatch() { } void Interface::init() { - hrpIsEnabled = (*(DWORD*)0x4E4480 != 0x278805C7); // check if HRP is enabled - if (GetConfigInt("Interface", "ActionPointsBar", 0)) { ActionPointsBarPatch(); if (hrpIsEnabled) LoadGameHook::OnAfterGameInit() += APBarRectPatch; diff --git a/sfall/Modules/MainMenu.cpp b/sfall/Modules/MainMenu.cpp index b9ad291b..82a2bdf0 100644 --- a/sfall/Modules/MainMenu.cpp +++ b/sfall/Modules/MainMenu.cpp @@ -25,9 +25,17 @@ namespace sfall { +#ifdef NDEBUG +static const char* VerString1 = "SFALL " VERSION_STRING; +#else +static const char* VerString1 = "SFALL " VERSION_STRING " Debug Build"; +#endif + static DWORD MainMenuYOffset; static DWORD MainMenuTextOffset; +static long OverrideColour; + static const DWORD MainMenuButtonYHookRet = 0x48184A; static void __declspec(naked) MainMenuButtonYHook() { __asm { @@ -45,23 +53,16 @@ static void __declspec(naked) MainMenuTextYHook() { } } -#ifdef NDEBUG -static const char* VerString1 = "SFALL " VERSION_STRING; -#else -static const char* VerString1 = "SFALL " VERSION_STRING " Debug Build"; -#endif - -static DWORD OverrideColour; static void __declspec(naked) FontColour() { __asm { cmp OverrideColour, 0; - je skip; - mov eax, OverrideColour; - retn; -skip: + jg override; movzx eax, byte ptr ds:[0x6A8B33]; or eax, 0x6000000; retn; +override: + mov eax, OverrideColour; + retn; } } @@ -121,8 +122,9 @@ void MainMenu::init() { MakeJump(0x4817AB, MainMenuTextHook); OverrideColour = GetConfigInt("Misc", "MainMenuFontColour", 0); - if (OverrideColour) { - MakeCall(0x48174C, FontColour); + if (OverrideColour > 0) { + OverrideColour |= 0x6000000; + SafeWrite32(0x481748, (DWORD)&OverrideColour); } } diff --git a/sfall/main.cpp b/sfall/main.cpp index e4cf2cdb..6ba8ebac 100644 --- a/sfall/main.cpp +++ b/sfall/main.cpp @@ -86,10 +86,12 @@ namespace sfall { bool isDebug = false; +bool hrpIsEnabled = false; const char ddrawIni[] = ".\\ddraw.ini"; static char ini[65] = ".\\"; static char translationIni[65]; + DWORD modifiedIni; unsigned int GetConfigInt(const char* section, const char* setting, int defaultValue) { @@ -144,6 +146,8 @@ static void InitModules() { manager.add(); manager.add(); manager.add(); + manager.add(); + manager.add(); manager.add(); manager.add(); manager.add(); @@ -163,7 +167,6 @@ static void InitModules() { manager.add(); manager.add(); manager.add(); - manager.add(); manager.add(); // should be loaded before PartyControl manager.add(); manager.add(); @@ -172,7 +175,6 @@ static void InitModules() { manager.add(); manager.add(); manager.add(); - manager.add(); // manager.add(); manager.add(); @@ -271,23 +273,26 @@ inline void SfallInit() { } } - if (cmdlineexists && strlen(cmdline)) { + if (cmdlineexists && *cmdline != 0) { HANDLE h = CreateFileA(cmdline, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0); if (h != INVALID_HANDLE_VALUE) { CloseHandle(h); strcat_s(ini, cmdline); } else { MessageBox(0, "You gave a command line argument to fallout, but it couldn't be matched to a file\n" \ - "Using default ddraw.ini instead", "Warning", MB_TASKMODAL); - strcpy_s(ini, ::sfall::ddrawIni); + "Using default ddraw.ini instead", "Warning", MB_TASKMODAL); + goto defaultIni; } } else { +defaultIni: strcpy_s(ini, ::sfall::ddrawIni); } GetConfigString("Main", "TranslationsINI", ".\\Translations.ini", translationIni, 65); modifiedIni = GetConfigInt("Main", "ModifiedIni", 0); + hrpIsEnabled = (*(DWORD*)0x4E4480 != 0x278805C7); // check if HRP is enabled + InitModules(); } diff --git a/sfall/main.h b/sfall/main.h index ba68c901..7cf9bc53 100644 --- a/sfall/main.h +++ b/sfall/main.h @@ -92,5 +92,6 @@ size_t Translate(const char* section, const char* setting, const char* defaultVa extern const char ddrawIni[]; extern DWORD modifiedIni; +extern bool hrpIsEnabled; } From 7f59d7a7fd2be089d6cffc6dcded00f109bcc46e Mon Sep 17 00:00:00 2001 From: NovaRain Date: Fri, 23 Aug 2019 10:32:07 +0800 Subject: [PATCH 14/34] Added MainMenuBigFontColour option to change button text color Changed the fix for player's direction after climbing ladder (9fea480) --- artifacts/ddraw.ini | 6 ++++-- sfall/FalloutEngine/Functions_def.h | 2 +- sfall/Modules/AnimationsAtOnceLimit.cpp | 4 ++-- sfall/Modules/MainMenu.cpp | 18 +++++++++++------- 4 files changed, 18 insertions(+), 12 deletions(-) diff --git a/artifacts/ddraw.ini b/artifacts/ddraw.ini index 095f2eec..23c57802 100644 --- a/artifacts/ddraw.ini +++ b/artifacts/ddraw.ini @@ -504,8 +504,10 @@ RemoveCriticalTimelimits=0 NPCStage6Fix=0 ;Change the colour of the font used on the main menu for the Fallout/sfall version number and copyright text -;It's the last byte ('3c' by default) that picks the colour used. The first byte supplies additional flags -;MainMenuFontColour=0x0600003c +;It's the last byte ('3C' by default) that picks the colour used. The first byte supplies additional flags for this option +;MainMenuFontColour=0x00003C +;Change the colour of the font used on the main menu for the button text +;MainMenuBigFontColour=0x3C ;Two alternate fixes to the interaction between HtH attacks and the fast shot trait ;0 - Fallout 2 original behaviour diff --git a/sfall/FalloutEngine/Functions_def.h b/sfall/FalloutEngine/Functions_def.h index c3ca3971..06657ba0 100644 --- a/sfall/FalloutEngine/Functions_def.h +++ b/sfall/FalloutEngine/Functions_def.h @@ -22,7 +22,7 @@ WRAP_WATCOM_FFUNC3(void, display_inventory, long, inventoryOffset, long, visible 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) WRAP_WATCOM_FFUNC7(void, make_straight_path_func, GameObject*, objFrom, DWORD, tileFrom, DWORD, tileTo, void*, rotationPtr, DWORD*, result, long, flags, void*, func) -WRAP_WATCOM_FFUNC3(long, obj_blocking_at, GameObject*, object, long, tile, long, elevation) +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) WRAP_WATCOM_FFUNC3(long, scr_set_local_var, long, sid, long, varId, long, value) diff --git a/sfall/Modules/AnimationsAtOnceLimit.cpp b/sfall/Modules/AnimationsAtOnceLimit.cpp index 2d7464d2..b1bc2fe5 100644 --- a/sfall/Modules/AnimationsAtOnceLimit.cpp +++ b/sfall/Modules/AnimationsAtOnceLimit.cpp @@ -322,8 +322,8 @@ void AnimationsAtOnce::init() { // Fix crash when the critter goes through a door with animation trigger MakeJump(0x41755E, object_move_hack); - // Fix player's direction after leaving a map via ladder/stairs - SafeWriteBatch(25, {0x49C972, 0x49CA16, 0x49CABA}); + // Fix player's direction after ladder climbing animation + SafeWrite16(0x49CA14, 0xB190); // mov cl, 26 (skip setting the direction) } void AnimationsAtOnce::exit() { diff --git a/sfall/Modules/MainMenu.cpp b/sfall/Modules/MainMenu.cpp index 82a2bdf0..a3c03044 100644 --- a/sfall/Modules/MainMenu.cpp +++ b/sfall/Modules/MainMenu.cpp @@ -34,7 +34,7 @@ static const char* VerString1 = "SFALL " VERSION_STRING " Debug Build"; static DWORD MainMenuYOffset; static DWORD MainMenuTextOffset; -static long OverrideColour; +static long OverrideColour, OverrideColour2; static const DWORD MainMenuButtonYHookRet = 0x48184A; static void __declspec(naked) MainMenuButtonYHook() { @@ -55,13 +55,13 @@ static void __declspec(naked) MainMenuTextYHook() { static void __declspec(naked) FontColour() { __asm { - cmp OverrideColour, 0; - jg override; + test OverrideColour, 0xFF; + jnz override; movzx eax, byte ptr ds:[0x6A8B33]; - or eax, 0x6000000; + or eax, 0x6000000; retn; override: - mov eax, OverrideColour; + mov eax, OverrideColour; retn; } } @@ -121,11 +121,15 @@ void MainMenu::init() { } MakeJump(0x4817AB, MainMenuTextHook); + OverrideColour = GetConfigInt("Misc", "MainMenuFontColour", 0); - if (OverrideColour > 0) { - OverrideColour |= 0x6000000; + if (OverrideColour & 0xFF) { + OverrideColour &= 0x00FF00FF; + OverrideColour |= 0x06000000; SafeWrite32(0x481748, (DWORD)&OverrideColour); } + OverrideColour2 = GetConfigInt("Misc", "MainMenuBigFontColour", 0) & 0xFF; + if (OverrideColour2) SafeWrite32(0x481906, (DWORD)&OverrideColour2); } } From ab5d7fdf97f129c4d8b528b05047be686964a796 Mon Sep 17 00:00:00 2001 From: NovaRain Date: Sun, 25 Aug 2019 10:09:17 +0800 Subject: [PATCH 15/34] Fixed player stuck at "climbing" frame after ladder climbing animation Removed the previous fix about player's direction. Some code format edits. --- sfall/Modules/AnimationsAtOnceLimit.cpp | 31 +++++++++++++++----- sfall/Modules/DamageMod.cpp | 2 +- sfall/Modules/HookScripts/MiscHs.cpp | 2 +- sfall/Modules/MiscPatches.cpp | 2 +- sfall/Modules/Scripting/Handlers/Objects.cpp | 4 +-- 5 files changed, 29 insertions(+), 12 deletions(-) diff --git a/sfall/Modules/AnimationsAtOnceLimit.cpp b/sfall/Modules/AnimationsAtOnceLimit.cpp index b1bc2fe5..74ad3cde 100644 --- a/sfall/Modules/AnimationsAtOnceLimit.cpp +++ b/sfall/Modules/AnimationsAtOnceLimit.cpp @@ -153,17 +153,16 @@ static const DWORD sad_28[] = { 0x4173CE, 0x4174C1, 0x4175F1, 0x417730, }; -static DWORD __fastcall AnimCombatFix(DWORD* scr, BYTE combatFlag) { +static DWORD __fastcall AnimCombatFix(DWORD* src, BYTE combatFlag) { DWORD animAddr = animSetAddr; if (animationLimit > 32) { - animAddr += animRecordSize; // include a dummy + animAddr += animRecordSize; // include a dummy } - if (combatFlag & 2) { // combat flag is set - _asm call fo::funcoffs::combat_anim_finished_; + if (combatFlag & 2) { // combat flag is set + __asm call fo::funcoffs::combat_anim_finished_; } - return animAddr; } @@ -205,6 +204,24 @@ end: } } +static void __declspec(naked) action_climb_ladder_hook() { + __asm { + cmp word ptr [edi + 0x40], 0xFFFF; // DestTile + je skip; + cmp dword ptr [edi + 0x3C], 0; // DestMap + je reset; + push edx; + mov edx, ds:[FO_VAR_map_number]; + cmp dword ptr [edi + 0x3C], edx; + pop edx; + jne skip; +reset: + and al, ~0x4; // reset RB_DONTSTAND flag +skip: + jmp fo::funcoffs::register_begin_; + } +} + void ApplyAnimationsAtOncePatches(signed char aniMax) { if (aniMax <= 32) return; @@ -322,8 +339,8 @@ void AnimationsAtOnce::init() { // Fix crash when the critter goes through a door with animation trigger MakeJump(0x41755E, object_move_hack); - // Fix player's direction after ladder climbing animation - SafeWrite16(0x49CA14, 0xB190); // mov cl, 26 (skip setting the direction) + // Fix for the player stuck at "climbing" frame after ladder climbing animation + HookCall(0x411E1F, action_climb_ladder_hook); } void AnimationsAtOnce::exit() { diff --git a/sfall/Modules/DamageMod.cpp b/sfall/Modules/DamageMod.cpp index 2eb383ad..a768c5e0 100644 --- a/sfall/Modules/DamageMod.cpp +++ b/sfall/Modules/DamageMod.cpp @@ -379,7 +379,7 @@ static void __declspec(naked) DisplayBonusHtHDmg2_hack() { shl eax, 1; add eax, 1; push eax; // min dmg + bonus - mov ecx, dword ptr[esp + 0x98 + 0x4]; + mov ecx, dword ptr [esp + 0x98 + 0x4]; push ecx; // message push 0x509EDC; // '%s %d-%d' lea eax, [esp + 0x0C + 0x4]; diff --git a/sfall/Modules/HookScripts/MiscHs.cpp b/sfall/Modules/HookScripts/MiscHs.cpp index 791cd939..cc8953fd 100644 --- a/sfall/Modules/HookScripts/MiscHs.cpp +++ b/sfall/Modules/HookScripts/MiscHs.cpp @@ -291,7 +291,7 @@ static void __declspec(naked) PerceptionRangeSeeHook() { cmp eax, 2; jne nevermind; // normal return dec eax; - mov dword ptr[esp + 0x2C - 0x1C + 4], eax; // set 1, skip blocking check + mov dword ptr [esp + 0x2C - 0x1C + 4], eax; // set 1, skip blocking check dec eax; nevermind: retn; diff --git a/sfall/Modules/MiscPatches.cpp b/sfall/Modules/MiscPatches.cpp index 4aad6fae..3c561b9c 100644 --- a/sfall/Modules/MiscPatches.cpp +++ b/sfall/Modules/MiscPatches.cpp @@ -370,7 +370,7 @@ skip: jz end; retn; end: - mov dword ptr[esp], 0x4715B7; + mov dword ptr [esp], 0x4715B7; retn; } } diff --git a/sfall/Modules/Scripting/Handlers/Objects.cpp b/sfall/Modules/Scripting/Handlers/Objects.cpp index 48860e7f..86a37ff2 100644 --- a/sfall/Modules/Scripting/Handlers/Objects.cpp +++ b/sfall/Modules/Scripting/Handlers/Objects.cpp @@ -54,7 +54,7 @@ void __declspec(naked) op_remove_script() { cmp eax, 0xffffffff; jz end; call fo::funcoffs::scr_remove_; - mov dword ptr[edx + 0x78], 0xffffffff; + mov dword ptr [edx + 0x78], 0xffffffff; end: pop edx; pop ecx; @@ -90,7 +90,7 @@ void __declspec(naked) op_set_script() { mov eax, esi; call fo::funcoffs::scr_remove_; pop eax; - mov dword ptr[eax + 0x78], 0xffffffff; + mov dword ptr [eax + 0x78], 0xffffffff; newscript: mov esi, 1; test ebx, 0x80000000; From b2fa359e3c4d139c37208ee0b322a59b89a58892 Mon Sep 17 00:00:00 2001 From: NovaRain Date: Mon, 26 Aug 2019 21:47:52 +0800 Subject: [PATCH 16/34] Replaced 'Take All' hotkey mod with UI hotkeys mod Minor edits to documents. --- artifacts/example_mods/StealCheat/readme.txt | 4 +- .../TakeAllHotkey/gl_takeallkey.int | Bin 300 -> 0 bytes .../TakeAllHotkey/gl_takeallkey.ssl | 20 ------- .../example_mods/TakeAllHotkey/readme.txt | 9 --- .../example_mods/UIHotkeys/gl_uihotkeys.int | Bin 0 -> 552 bytes .../example_mods/UIHotkeys/gl_uihotkeys.ssl | 52 ++++++++++++++++++ artifacts/example_mods/UIHotkeys/readme.txt | 22 ++++++++ artifacts/scripting/hookscripts.txt | 2 +- sfall/Modules/HookScripts.h | 1 + sfall/Modules/LoadOrder.cpp | 16 +++--- sfall/Modules/Scripting/Handlers/Core.cpp | 2 +- 11 files changed, 87 insertions(+), 41 deletions(-) delete mode 100644 artifacts/example_mods/TakeAllHotkey/gl_takeallkey.int delete mode 100644 artifacts/example_mods/TakeAllHotkey/gl_takeallkey.ssl delete mode 100644 artifacts/example_mods/TakeAllHotkey/readme.txt create mode 100644 artifacts/example_mods/UIHotkeys/gl_uihotkeys.int create mode 100644 artifacts/example_mods/UIHotkeys/gl_uihotkeys.ssl create mode 100644 artifacts/example_mods/UIHotkeys/readme.txt diff --git a/artifacts/example_mods/StealCheat/readme.txt b/artifacts/example_mods/StealCheat/readme.txt index f11409e0..e1ee2499 100644 --- a/artifacts/example_mods/StealCheat/readme.txt +++ b/artifacts/example_mods/StealCheat/readme.txt @@ -1,5 +1,5 @@ -Master Theif cheat for fallout 2 by phobos2077 ----------------------------------------- +Master Thief cheat for fallout 2 by phobos2077 +---------------------------------------------- - forces all steal attempts to succeed diff --git a/artifacts/example_mods/TakeAllHotkey/gl_takeallkey.int b/artifacts/example_mods/TakeAllHotkey/gl_takeallkey.int deleted file mode 100644 index 6e585781c17c754eb5383241ccd0307cde6d7f32..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 300 zcmZo*I>5-lz#!DX3#MBdSQ-Qxq#6_&q#6_(q#BeOq#BePR2ozp)EF2Tm>3us*ced3 zG$>aBO2a6axDa6= VjL*=(+aTGX)u7qH(;x$;WdIevKDht@ diff --git a/artifacts/example_mods/TakeAllHotkey/gl_takeallkey.ssl b/artifacts/example_mods/TakeAllHotkey/gl_takeallkey.ssl deleted file mode 100644 index 657ce5c0..00000000 --- a/artifacts/example_mods/TakeAllHotkey/gl_takeallkey.ssl +++ /dev/null @@ -1,20 +0,0 @@ -procedure start; -// adjust include paths if needed -#include "..\..\scripting_docs\headers\sfall.h" -#include "..\..\scripting_docs\headers\dik.h" - -procedure start begin - if game_loaded then begin - register_hook(HOOK_KEYPRESS); - end else begin - variable - event := get_sfall_arg, - keyDX := get_sfall_arg; - - if (event == 1) and (keyDX == DIK_SEMICOLON) then begin - tap_key(DIK_CAPITAL); - tap_key(DIK_A); - tap_key(DIK_CAPITAL); - end - end -end diff --git a/artifacts/example_mods/TakeAllHotkey/readme.txt b/artifacts/example_mods/TakeAllHotkey/readme.txt deleted file mode 100644 index dedd0668..00000000 --- a/artifacts/example_mods/TakeAllHotkey/readme.txt +++ /dev/null @@ -1,9 +0,0 @@ -"Take All" Hotkey mod for Fallout 2 by NovaRain ----------------------------------------- - -- makes pressing the semicolon key send an uppercase A after a semicolon. -- Note: the uppercase A (shift + A if Caps Lock is off) is the built-in hotkey for the TAKE ALL button on the loot screen. - -To use, copy *.int file to your scripts folder. - -This mod is a simple example of how you can use HOOK_KEYPRESS hooks. diff --git a/artifacts/example_mods/UIHotkeys/gl_uihotkeys.int b/artifacts/example_mods/UIHotkeys/gl_uihotkeys.int new file mode 100644 index 0000000000000000000000000000000000000000..4fab3d8a14f14a89cc1551195588d09f7ac5d91a GIT binary patch literal 552 zcmZo*I>5-lz#!DX3#MBdSQ-Qxq#6_&q#6_(q#BeOq#BePR2ozp)EF2Tm>3us*ced3 zG$>aBO2a6axD)8yOpnz@p*}#$cKW z!WUp@aA>fD+QZnG1d;>U1ELwzKqLc9r#z}o2Yfmgpy<>=)yaZKC)6J>J1rZGA#P** z05%cgYDNZ-36QX{g1DJM4oXA$Fh4-egXm*I(q{x!&(OfzAlabRpxMCFAOogl0N1r~ AfdBvi literal 0 HcmV?d00001 diff --git a/artifacts/example_mods/UIHotkeys/gl_uihotkeys.ssl b/artifacts/example_mods/UIHotkeys/gl_uihotkeys.ssl new file mode 100644 index 00000000..0288b17b --- /dev/null +++ b/artifacts/example_mods/UIHotkeys/gl_uihotkeys.ssl @@ -0,0 +1,52 @@ +/* + +UI Hotkeys mod for Fallout 2 by NovaRain +---------------------------------------- + +- closes some game interfaces by pressing their hotkeys again: + * 'I' key for Inventory + * 'S' key for Skilldex + * 'Z' key for Pip-Boy + +- extends the functionality of the 'Space' key (Caps Lock must be off): + * takes all items in the loot screen + * makes the offer in the barter screen + +Requires sfall 3.5 or higher + +*/ + +#include "..\headers\sfall\sfall.h" +#include "..\headers\sfall\dik.h" + +procedure start; + +procedure start begin + if game_loaded then begin + register_hook(HOOK_KEYPRESS); + end else begin + variable + event := get_sfall_arg, + keyDX := get_sfall_arg, + mode; + + if (event) then begin + mode := get_game_mode; + if (keyDX == DIK_I and (mode bwand INVENTORY)) then begin + tap_key(DIK_ESCAPE); + end else if (keyDX == DIK_S and (mode bwand SKILLDEX)) then begin + tap_key(DIK_ESCAPE); + end else if (keyDX == DIK_Z and (mode bwand PIPBOY)) then begin + tap_key(DIK_ESCAPE); + end else if (keyDX == DIK_SPACE) then begin + if (mode bwand INTFACELOOT) then begin + tap_key(DIK_CAPITAL); + tap_key(DIK_A); + tap_key(DIK_CAPITAL); + end else if (mode bwand BARTER) then begin + tap_key(DIK_M); + end + end + end + end +end diff --git a/artifacts/example_mods/UIHotkeys/readme.txt b/artifacts/example_mods/UIHotkeys/readme.txt new file mode 100644 index 00000000..d530f482 --- /dev/null +++ b/artifacts/example_mods/UIHotkeys/readme.txt @@ -0,0 +1,22 @@ +UI Hotkeys mod for Fallout 2 by NovaRain +---------------------------------------- + +- closes some game interfaces by pressing their hotkeys again: + * 'I' key for Inventory + * 'S' key for Skilldex + * 'Z' key for Pip-Boy + +- extends the functionality of the 'Space' key (Caps Lock must be off): + * takes all items in the loot screen + * makes the offer in the barter screen + +- Note: + * the uppercase 'A' (shift + A if Caps Lock is off) is the built-in hotkey for the TAKE ALL button on the loot screen + * the lowercase 'm' is the built-in hotkey for the OFFER button on the barter screen + + +Requires sfall 3.5 or higher + +To use, copy gl_uihotkeys.int to your scripts folder. + +This mod is a simple example of how you can use HOOK_KEYPRESS hooks. diff --git a/artifacts/scripting/hookscripts.txt b/artifacts/scripting/hookscripts.txt index d7da9086..14a0e292 100644 --- a/artifacts/scripting/hookscripts.txt +++ b/artifacts/scripting/hookscripts.txt @@ -47,7 +47,7 @@ Changes argument value. The argument number (argnum) is 0-indexed. This is usefu > 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. -> void register_hook_proc(int hooktype, proc procedure) +> void register_hook_proc(int hook, 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"). 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!). 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. diff --git a/sfall/Modules/HookScripts.h b/sfall/Modules/HookScripts.h index f36c3b85..3db160ac 100644 --- a/sfall/Modules/HookScripts.h +++ b/sfall/Modules/HookScripts.h @@ -90,6 +90,7 @@ DWORD _stdcall GetHSArg(); DWORD* _stdcall GetHSArgs(); void _stdcall SetHSArg(DWORD id, DWORD value); void _stdcall SetHSReturn(DWORD d); + // register hook by proc num (special values: -1 - use default (start) procedure, 0 - unregister) void _stdcall RegisterHook(fo::Program* script, int id, int procNum); diff --git a/sfall/Modules/LoadOrder.cpp b/sfall/Modules/LoadOrder.cpp index 1c144cf8..82af8a54 100644 --- a/sfall/Modules/LoadOrder.cpp +++ b/sfall/Modules/LoadOrder.cpp @@ -103,7 +103,7 @@ static void __declspec(naked) gnw_main_hack() { } static fo::PathNode* __fastcall RemoveDatabase(const char* pathPatches) { - auto paths = fo::var::paths; // curr.node (beginning of the load order) + auto paths = fo::var::paths; // curr.node (beginning of the chain of paths) auto _paths = paths; // prev.node while (paths) { @@ -111,9 +111,9 @@ static fo::PathNode* __fastcall RemoveDatabase(const char* pathPatches) { fo::PathNode* nextPaths = paths->next; // pointer to the node of the next path // TODO: need to check if this condition is used correctly if (paths != _paths) - _paths->next = nextPaths; // replace the pointer in the previous node, removing the current(found) path from the load order + _paths->next = nextPaths; // replace the pointer in the previous node, removing the current(found) path from the chain else // if the current node is equal to the previous node - fo::var::paths = nextPaths; // set the next node at the beginning of the load order + fo::var::paths = nextPaths; // set the next node at the beginning of the chain return paths; // return the pointer of the current removed node (save the pointer) } _paths = paths; // prev.node <- curr.node @@ -122,7 +122,7 @@ static fo::PathNode* __fastcall RemoveDatabase(const char* pathPatches) { return nullptr; // it's possible that this will create an exceptional situation for the game, although such a situation should not arise } -// Remove master_patches from the load order +// Remove master_patches from the chain static void __declspec(naked) game_init_databases_hack1() { __asm { cmp eax, -1; @@ -135,7 +135,7 @@ skip: } } -// Remove critter_patches from the load order +// Remove critter_patches from the chain static void __declspec(naked) game_init_databases_hack2() { __asm { cmp eax, -1; @@ -169,14 +169,14 @@ static void __fastcall game_init_databases_hook() { if (!patchFiles.empty()) InitExtraPatches(); fo::PathNode* critter_patches = (fo::PathNode*)fo::var::critter_db_handle; - fo::PathNode* paths = fo::var::paths; // beginning of the load order - // insert master_patches/critter_patches at the beginning of the load order + 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) { critter_patches->next = paths; // critter_patches.next -> paths paths = critter_patches; } master_patches->next = paths; // master_patches.next -> paths - fo::var::paths = master_patches; // set master_patches node at the beginning of the load order + fo::var::paths = master_patches; // set master_patches node at the beginning of the chain of paths } static bool NormalizePath(std::string &path) { diff --git a/sfall/Modules/Scripting/Handlers/Core.cpp b/sfall/Modules/Scripting/Handlers/Core.cpp index 08dc20c9..671e7c82 100644 --- a/sfall/Modules/Scripting/Handlers/Core.cpp +++ b/sfall/Modules/Scripting/Handlers/Core.cpp @@ -94,7 +94,7 @@ static long GetGlobalVarNameString(OpcodeContext& ctx) { } static void GetGlobalVar(OpcodeContext& ctx, DataType type) { - long result; + long result; if (ctx.arg(0).isString()) { result = GetGlobalVarNameString(ctx); } else { From 5e693aa9550b5eef84120929307f6b3a560a1017 Mon Sep 17 00:00:00 2001 From: NovaRain Date: Tue, 27 Aug 2019 12:40:20 +0800 Subject: [PATCH 17/34] Fixed some args in HOOK_BARTERPRICE hook when trading with PM And added a new argument to indicate trading with a party member. --- artifacts/scripting/hookscripts.txt | 6 ++-- sfall/FalloutEngine/Functions_def.h | 1 + sfall/Modules/HookScripts.cpp | 2 +- sfall/Modules/HookScripts/MiscHs.cpp | 41 ++++++++++++++-------------- sfall/Modules/Scripting/Opcodes.cpp | 2 +- 5 files changed, 27 insertions(+), 25 deletions(-) diff --git a/artifacts/scripting/hookscripts.txt b/artifacts/scripting/hookscripts.txt index 14a0e292..4c76758c 100644 --- a/artifacts/scripting/hookscripts.txt +++ b/artifacts/scripting/hookscripts.txt @@ -248,13 +248,14 @@ Obj arg1 - the owner that the object is being removed from item arg2 - the item that is being removed int arg3 - the number of items to remove int arg4 - The reason the object is being removed (see RMOBJ_* constants) -Obj arg5 - The destination object when the item is moved to another object, otherwise 0 +Obj arg5 - The destination object when the item is moved to another object, 0 otherwise ------------------------------------------- HOOK_BARTERPRICE (hs_barterprice.int) Runs whenever the value of goods being purchased is calculated +NOTE: the hook is executed twice when entering the barter screen or after transaction: the first time is for the player and the second time is for NPC critter arg1 - the critter doing the bartering (either dude_obj or inven_dude) critter arg2 - the critter being bartered with @@ -264,7 +265,8 @@ int arg5 - the amount of actual caps in the barter stack (as opposed to good int arg6 - the value of all goods being traded before skill modifications critter arg7 - table of offered goods (being sold to NPC) int arg8 - the total cost of the goods offered by the player -int arg9 - set 1 if the "offers" button was pressed (not for a party member), otherwise 0 +int arg9 - 1 if the "offers" button was pressed (not for a party member), 0 otherwise +int arg10 - 1 if trading with a party member, 0 otherwise int ret1 - the modified value of all of the goods (pass -1 if you just want to modify offered goods) int ret2 - the modified value of all offered goods diff --git a/sfall/FalloutEngine/Functions_def.h b/sfall/FalloutEngine/Functions_def.h index 06657ba0..5357592d 100644 --- a/sfall/FalloutEngine/Functions_def.h +++ b/sfall/FalloutEngine/Functions_def.h @@ -71,6 +71,7 @@ WRAP_WATCOM_FUNC1(long, item_c_curr_size, GameObject*, critter) WRAP_WATCOM_FUNC1(long, item_caps_total, GameObject*, object) WRAP_WATCOM_FUNC1(long, item_size, GameObject*, item) WRAP_WATCOM_FUNC1(long, item_total_cost, GameObject*, object) +WRAP_WATCOM_FUNC1(long, item_total_weight, GameObject*, object) WRAP_WATCOM_FUNC1(long, item_w_anim_code, GameObject*, item) WRAP_WATCOM_FUNC2(long, item_w_anim_weap, GameObject*, item, DWORD, hitMode) WRAP_WATCOM_FUNC2(long, item_w_compute_ammo_cost, GameObject*, item, DWORD*, rounds) diff --git a/sfall/Modules/HookScripts.cpp b/sfall/Modules/HookScripts.cpp index ba142d79..37ee7605 100644 --- a/sfall/Modules/HookScripts.cpp +++ b/sfall/Modules/HookScripts.cpp @@ -178,7 +178,7 @@ void _stdcall RegisterHook(fo::Program* script, int id, int procNum) { ScriptProgram *prog = GetGlobalScriptProgram(script); if (prog) { - dlog_f("Global script %08x registered as hook id %d\n", DL_HOOK, script, id); + dlog_f("Global script %08x registered as hook ID %d\n", DL_HOOK, script, id); HookScript hook; hook.prog = *prog; hook.callback = procNum; diff --git a/sfall/Modules/HookScripts/MiscHs.cpp b/sfall/Modules/HookScripts/MiscHs.cpp index cc8953fd..32fdb109 100644 --- a/sfall/Modules/HookScripts/MiscHs.cpp +++ b/sfall/Modules/HookScripts/MiscHs.cpp @@ -11,17 +11,17 @@ namespace sfall { -// TODO: For now the hook gets executed twice, the first time for the player and the second time for the trader. -// it is necessary to change the implementation so that the hook only gets executed once +// The hook is executed twice when entering the barter screen or after transaction: the first time is for the player and the second time is for NPC static DWORD __fastcall BarterPriceHook_Script(register fo::GameObject* source, register fo::GameObject* target, DWORD callAddr) { + bool barterIsParty = (*(DWORD*)FO_VAR_dialog_target_is_party != 0); int computeCost = fo::func::barter_compute_value(source, target); BeginHook(); - argCount = 9; + argCount = 10; args[0] = (DWORD)source; args[1] = (DWORD)target; - args[2] = computeCost; + args[2] = !barterIsParty ? computeCost : 0; fo::GameObject* bTable = (fo::GameObject*)fo::var::btable; args[3] = (DWORD)bTable; @@ -30,24 +30,24 @@ static DWORD __fastcall BarterPriceHook_Script(register fo::GameObject* source, fo::GameObject* pTable = (fo::GameObject*)fo::var::ptable; args[6] = (DWORD)pTable; - int pcCost = fo::func::item_total_cost(pTable); - args[7] = pcCost; + int pcCost = !barterIsParty ? fo::func::item_total_cost(pTable) : fo::func::item_total_weight(pTable); + args[7] = !barterIsParty ? pcCost : 0; - args[8] = (DWORD)(callAddr == 0x474D51); // check offers button + args[8] = (DWORD)(callAddr == 0x474D51); // offers button is pressed + args[9] = (DWORD)barterIsParty; RunHookScript(HOOK_BARTERPRICE); - bool isPCHook = (callAddr == 0x47551F); + bool isPCHook = (callAddr == -1); int cost = isPCHook ? pcCost : computeCost; - if (cRet > 0) { + if (!barterIsParty && cRet > 0) { if (isPCHook) { if (cRet > 1) cost = rets[1]; // new cost for pc } else if ((int)rets[0] > -1) { - cost = rets[0]; // new cost for trader + cost = rets[0]; // new cost for npc } } EndHook(); - return cost; } @@ -65,13 +65,13 @@ static void __declspec(naked) BarterPriceHook() { } } -static DWORD offersGoodsCost; // keep last cost +static DWORD offersGoodsCost; // keep last cost for pc static void __declspec(naked) PC_BarterPriceHook() { __asm { push edx; push ecx; //------- - push [esp + 8]; // address on call stack + push -1; // address on call stack mov ecx, dword ptr ds:[FO_VAR_obj_dude]; // source mov edx, dword ptr ds:[FO_VAR_target_stack]; // target call BarterPriceHook_Script; @@ -82,11 +82,10 @@ static void __declspec(naked) PC_BarterPriceHook() { } } -static const DWORD OverrideCostRet = 0x474D44; -static void __declspec(naked) OverrideCost_BarterPriceHack() { +static void __declspec(naked) OverrideCost_BarterPriceHook() { __asm { mov eax, offersGoodsCost; - jmp OverrideCostRet; + retn; } } @@ -522,12 +521,12 @@ end: void Inject_BarterPriceHook() { HookCalls(BarterPriceHook, { - 0x474D4C, - 0x475735, - 0x475762 + 0x474D4C, // barter_attempt_transaction_ (offers button) + 0x475735, // display_table_inventories_ (for party members) + 0x475762 // display_table_inventories_ }); - HookCall(0X47551A, PC_BarterPriceHook); - MakeJump(0x474D3F, OverrideCost_BarterPriceHack); // just overrides cost of offered goods + HookCalls(PC_BarterPriceHook, {0x4754F4, 0x47551A}); // display_table_inventories_ + HookCall(0x474D3F, OverrideCost_BarterPriceHook); // barter_attempt_transaction_ (just overrides cost of offered goods) } void Inject_UseSkillHook() { diff --git a/sfall/Modules/Scripting/Opcodes.cpp b/sfall/Modules/Scripting/Opcodes.cpp index d9490aa7..dd3b6d73 100644 --- a/sfall/Modules/Scripting/Opcodes.cpp +++ b/sfall/Modules/Scripting/Opcodes.cpp @@ -184,7 +184,7 @@ static SfallOpcodeInfo opcodeInfoArray[] = { {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}}, + {0x262, "register_hook_proc", sf_register_hook, 2, false, {ARG_INT, ARG_ANY}}, {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}}, From 241f174bae40e2136593c0509bdec5a718cd7c0f Mon Sep 17 00:00:00 2001 From: NovaRain Date: Sat, 31 Aug 2019 19:38:59 +0800 Subject: [PATCH 18/34] Tweaked reordering the execution of functions before exiting the map Added event clearing for active explosives before exiting the map. Minor code edit to BARTERPRICE hook. --- sfall/Modules/HookScripts/MiscHs.cpp | 20 +++++++++++------ sfall/Modules/ScriptExtender.cpp | 33 ++++++++++++++++------------ 2 files changed, 32 insertions(+), 21 deletions(-) diff --git a/sfall/Modules/HookScripts/MiscHs.cpp b/sfall/Modules/HookScripts/MiscHs.cpp index 32fdb109..f0b46e64 100644 --- a/sfall/Modules/HookScripts/MiscHs.cpp +++ b/sfall/Modules/HookScripts/MiscHs.cpp @@ -11,10 +11,10 @@ namespace sfall { -// The hook is executed twice when entering the barter screen or after transaction: the first time is for the player and the second time is for NPC +// The hook is executed twice when entering the barter screen and after transaction: the first time is for the player; the second time is for NPC static DWORD __fastcall BarterPriceHook_Script(register fo::GameObject* source, register fo::GameObject* target, DWORD callAddr) { bool barterIsParty = (*(DWORD*)FO_VAR_dialog_target_is_party != 0); - int computeCost = fo::func::barter_compute_value(source, target); + long computeCost = fo::func::barter_compute_value(source, target); BeginHook(); argCount = 10; @@ -30,20 +30,26 @@ static DWORD __fastcall BarterPriceHook_Script(register fo::GameObject* source, fo::GameObject* pTable = (fo::GameObject*)fo::var::ptable; args[6] = (DWORD)pTable; - int pcCost = !barterIsParty ? fo::func::item_total_cost(pTable) : fo::func::item_total_weight(pTable); - args[7] = !barterIsParty ? pcCost : 0; - args[8] = (DWORD)(callAddr == 0x474D51); // offers button is pressed + long pcCost = 0; + if (barterIsParty) { + args[7] = pcCost; + pcCost = fo::func::item_total_weight(pTable); + } else { + args[7] = pcCost = fo::func::item_total_cost(pTable); + } + + args[8] = (DWORD)(callAddr == 0x474D51); // offers button pressed args[9] = (DWORD)barterIsParty; RunHookScript(HOOK_BARTERPRICE); bool isPCHook = (callAddr == -1); - int cost = isPCHook ? pcCost : computeCost; + long cost = isPCHook ? pcCost : computeCost; if (!barterIsParty && cRet > 0) { if (isPCHook) { if (cRet > 1) cost = rets[1]; // new cost for pc - } else if ((int)rets[0] > -1) { + } else if ((long)rets[0] > -1) { cost = rets[0]; // new cost for npc } } diff --git a/sfall/Modules/ScriptExtender.cpp b/sfall/Modules/ScriptExtender.cpp index 3c17303e..c6bb5b66 100644 --- a/sfall/Modules/ScriptExtender.cpp +++ b/sfall/Modules/ScriptExtender.cpp @@ -678,20 +678,26 @@ void SetGlobals(GlobalVar* globals) { static void __declspec(naked) map_save_in_game_hook() { __asm { - call fo::funcoffs::partyMemberSaveProtos_; - jmp fo::funcoffs::game_time_; + test cl, 1; + jz skip; + jmp fo::funcoffs::scr_exec_map_exit_scripts_; +skip: + jmp fo::funcoffs::partyMemberSaveProtos_; } } -/* -static void ClearEventUpdateMapProc() { + +static void ClearEventsOnMapExit() { using namespace fo; __asm { - mov eax, map_update_event; // type - xor edx, edx; // func + mov eax, explode_event; // type + mov edx, fo::funcoffs::queue_explode_exit_; // func + call fo::funcoffs::queue_clear_type_; + mov eax, explode_fail_event; + mov edx, fo::funcoffs::queue_explode_exit_; call fo::funcoffs::queue_clear_type_; } } -*/ + void ScriptExtender::init() { LoadGameHook::OnAfterGameStarted() += LoadGlobalScripts; LoadGameHook::OnGameReset() += [] () { @@ -761,13 +767,10 @@ void ScriptExtender::init() { // Reorder the execution of functions before exiting the map // Call saving party member prototypes and removing the drug effects for NPC after executing map_exit_p_proc procedure - HookCall(0x483CF9, map_save_in_game_hook); - MakeCall(0x483CB9, (void*)fo::funcoffs::scr_exec_map_exit_scripts_); - BlockCall(0x483CCD); // scr_exec_map_exit_scripts_ - long long data = 0x397401C1F6; // test cl, 1; jz 0x483CF2 - SafeWriteBytes(0x483CB4, (BYTE*)&data, 5); - - //ScriptExtender::OnMapExit() += ClearEventUpdateMapProc; + HookCall(0x483CB4, map_save_in_game_hook); + HookCall(0x483CC3, (void*)fo::funcoffs::partyMemberSaveProtos_); + HookCall(0x483CC8, (void*)fo::funcoffs::partyMemberPrepLoad_); + HookCall(0x483CCD, (void*)fo::funcoffs::partyMemberPrepItemSaveAll_); // Set the DAM_BACKWASH flag for the attacker before calling compute_damage_ SafeWrite32(0x423DE7, 0x40164E80); // or [esi+ctd.flags3Source], DAM_BACKWASH_ @@ -778,6 +781,8 @@ void ScriptExtender::init() { } InitNewOpcodes(); + + ScriptExtender::OnMapExit() += ClearEventsOnMapExit; // for reordering the execution of functions before exiting the map } Delegate<>& ScriptExtender::OnMapExit() { From fb718e6fadfaace6e4ce358603d55bc269bc3bf3 Mon Sep 17 00:00:00 2001 From: NovaRain Date: Sun, 1 Sep 2019 09:00:27 +0800 Subject: [PATCH 19/34] Increased the width of the car fuel gauge to 2px --- sfall/Modules/Interface.cpp | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/sfall/Modules/Interface.cpp b/sfall/Modules/Interface.cpp index bba7f6fd..a835d7d3 100644 --- a/sfall/Modules/Interface.cpp +++ b/sfall/Modules/Interface.cpp @@ -138,6 +138,7 @@ static void __declspec(naked) wmInterfaceInit_text_font_hook() { static DWORD wmTownMapSubButtonIds[WMAP_TOWN_BUTTONS + 1]; // replace _wmTownMapSubButtonIds (index 0 - unused element) static int worldmapInterface; +static long wmapWinWidth = 640; // Window width static const DWORD wmWinWidth[] = { @@ -156,7 +157,7 @@ static const DWORD wmWinWidth[] = { // wmRefreshInterfaceOverlay_ 0x4C50FD, 0x4C51CF, 0x4C51F8, 0x4C517F, // wmInterfaceRefreshCarFuel_ - 0x4C528B, 0x4C529F, 0x4C52AA, + 0x4C52AA, /*0x4C528B, 0x4C529F, - Conflict with fuel gauge patch*/ // wmInterfaceDrawSubTileList_ 0x4C41C1, 0x4C41D2, // wmTownMapRefresh_ @@ -299,6 +300,7 @@ static void WorldmapViewportPatch() { if (!fo::func::db_access("art\\intrface\\worldmap.frm")) return; dlog("Applying expanded world map interface patch.", DL_INIT); + wmapWinWidth = WMAP_WIN_WIDTH; mapSlotsScrollMax -= 216; if (mapSlotsScrollMax < 0) mapSlotsScrollMax = 0; @@ -408,6 +410,23 @@ static void WorldmapViewportPatch() { dlogr(" Done", DL_INIT); } +static void __declspec(naked) wmInterfaceRefreshCarFuel_hack_empty() { + __asm { + mov byte ptr [eax - 1], 14; + add eax, wmapWinWidth; + retn; + } +} + +static void __declspec(naked) wmInterfaceRefreshCarFuel_hack() { + __asm { + mov byte ptr [eax - 1], 196; + add eax, wmapWinWidth; + mov byte ptr [eax - 1], 14; + retn; + } +} + static void WorldMapInterfacePatch() { if (GetConfigInt("Misc", "WorldMapFontPatch", 0)) { dlog("Applying world map font patch.", DL_INIT); @@ -442,6 +461,9 @@ static void WorldMapInterfacePatch() { LoadGameHook::OnAfterGameInit() += WorldmapViewportPatch; // Note: must be applied after WorldMapSlots patch } } + // Car fuel gauge graphics patch + MakeCall(0x4C528A, wmInterfaceRefreshCarFuel_hack_empty); + MakeCall(0x4C529E, wmInterfaceRefreshCarFuel_hack); } void Interface::init() { From 694b3995ef2913ad72e3c7d25fd91745a0f37f32 Mon Sep 17 00:00:00 2001 From: NovaRain Date: Sun, 1 Sep 2019 11:37:04 +0800 Subject: [PATCH 20/34] Added a compute damage example script. Updated compiled scripts due to main.h changes. --- .../ComputeDamage/gl_compute_damage.ssl | 206 ++++++++++++++++++ artifacts/mods/gl_highlighting.int | Bin 3964 -> 3984 bytes artifacts/mods/gl_npcarmor.int | Bin 4408 -> 4418 bytes artifacts/mods/gl_partycontrol.int | Bin 4148 -> 4158 bytes artifacts/mods/main.h | 24 +- 5 files changed, 225 insertions(+), 5 deletions(-) create mode 100644 artifacts/example_mods/ComputeDamage/gl_compute_damage.ssl diff --git a/artifacts/example_mods/ComputeDamage/gl_compute_damage.ssl b/artifacts/example_mods/ComputeDamage/gl_compute_damage.ssl new file mode 100644 index 00000000..44d32934 --- /dev/null +++ b/artifacts/example_mods/ComputeDamage/gl_compute_damage.ssl @@ -0,0 +1,206 @@ +/* + Example algorithm of how the game engine calculates combat damage +*/ + +#include "..\headers\define.h" +#include "..\headers\command.h" +#include "..\headers\sfall\sfall.h" +#include "..\headers\sfall\define_extra.h" + +procedure start; +procedure compute_damage_F2; +procedure item_w_damage_hook; +procedure item_w_subtype(variable weapon, variable hit_mode); +procedure get_ammo_value(variable weapon, variable param); + +variable item_w_damage; + +procedure start begin + if game_loaded then begin + register_hook_proc(HOOK_COMBATDAMAGE, compute_damage_F2); + register_hook_proc(HOOK_ITEMDAMAGE, item_w_damage_hook); + end +end + +procedure compute_damage_F2 begin + variable dmg_type, weapon_perk, dmg_thresh, dmg_resist, weapon_subtype, bonus_ranged, difficulty, i, dmg_mult, dmg_div, damage; + variable target, flags, knockback, amount; + + variable + ctdTarget := get_sfall_arg, + ctdSource := get_sfall_arg, + amountTarget := get_sfall_arg, + amountSource := get_sfall_arg, + flagsTarget := get_sfall_arg, + flagsSource := get_sfall_arg, + weapon := get_sfall_arg, + bodypart := get_sfall_arg, + damageMultiplier := get_sfall_arg, + rounds := get_sfall_arg, + amountKnockback := get_sfall_arg, + hit_mode := get_sfall_arg; + + if (flagsSource bwand DAM_HIT) then begin + target := ctdTarget; + flags := flagsTarget; + knockback := 1; + end else begin + target := ctdSource; + flags := flagsSource; + knockback := 0; + end + + //amount := 0; + if target and (obj_type(target) == OBJ_TYPE_CRITTER) then begin + if weapon then begin + dmg_type := weapon_dmg_type(weapon); + weapon_perk := get_proto_data(obj_pid(weapon), PROTO_WP_PERK); + end else begin + dmg_type := DMG_normal_dam; + weapon_perk := -1; + end + + dmg_thresh := get_critter_stat(target, STAT_dmg_thresh + dmg_type); + dmg_resist := get_critter_stat(target, STAT_dmg_resist + dmg_type); + + if (flags bwand DAM_BYPASS) and (dmg_type != DMG_emp) then begin + dmg_thresh := dmg_thresh * 20 / 100; + dmg_resist := dmg_resist * 20 / 100; + end else begin + if (weapon_perk == PERK_weapon_penetrate) + or (hit_mode == ATKTYPE_PALMSTRIKE or hit_mode == ATKTYPE_PIERCINGSTRIKE + or hit_mode == ATKTYPE_HOOKKICK or hit_mode == ATKTYPE_PIERCINGKICK) then + dmg_thresh := dmg_thresh * 20 / 100; + + if ctdSource == dude_obj and has_trait(TRAIT_TRAIT, ctdSource, TRAIT_finesse) then + dmg_resist += 30; + end + + weapon_subtype := item_w_subtype(weapon, hit_mode); // item_w_subtype_ + if (ctdSource != dude_obj) or (weapon_subtype != WEAPON_TYPE_GUNS) then + bonus_ranged := 0; + else + bonus_ranged := has_trait(TRAIT_PERK, ctdSource, PERK_bonus_ranged_damage) * 2; + + difficulty := 100; + if (has_trait(TRAIT_OBJECT, ctdSource, OBJECT_TEAM_NUM) != has_trait(TRAIT_OBJECT, dude_obj, OBJECT_TEAM_NUM)) then begin + if (combat_difficulty == 0) then + difficulty := 75; + else if (combat_difficulty == 2) then + difficulty := 125; + end + + // F2 default start + // Damage = (1 - (DR_armor + DR_ammo_adjust) * (((raw_damage * (dmg_mult * damageMultiplier)) / dmg_div) - dmg_thresh) + dmg_resist += get_ammo_value(weapon, PROTO_AM_DR_MOD); // item_w_dr_adjust_ (DR Adjust %) + if (dmg_resist < 100) then begin + if (dmg_resist < 0) then dmg_resist := 0; + + dmg_mult := damageMultiplier * get_ammo_value(weapon, PROTO_AM_DMG_MULT); // item_w_dam_mult_ (Dmg mod A) + dmg_div := get_ammo_value(weapon, PROTO_AM_DMG_DIV); // item_w_dam_div_ (Dmg mod B) + + for (i := 1; i <= rounds; i++) begin + damage := (item_w_damage + bonus_ranged) * dmg_mult; // item_w_damage_ (raw_damage) + if dmg_div then damage /= dmg_div; + + damage := (((damage / 2) * difficulty) / 100) - dmg_thresh; + if (damage > 0) then begin + damage := damage - ((damage * dmg_resist) / 100); // reduce damage by the percentage of DR_armor + DR_Ammo + if (damage > 0) then amount += damage; + end + end + end + // F2 default end + + if (ctdSource == dude_obj) then begin + if has_trait(TRAIT_PERK, ctdSource, PERK_living_anatomy_perk) and (critter_kill_type(ctdTarget) != KILL_TYPE_robot_kills) + and (critter_kill_type(ctdTarget) != KILL_TYPE_alien_kills) then + amount += 5; + if has_trait(TRAIT_PERK, ctdSource, PERK_pyromaniac_perk) and (dmg_type == DMG_fire) then + amount += 5; + end + + if knockback and ((get_flags(target) bwand FLAG_MULTIHEX) == 0) + and (dmg_type == DMG_explosion or weapon == 0 or weapon_subtype == WEAPON_TYPE_MELEE) + and ((get_proto_data(obj_pid(target), PROTO_CR_FLAGS) bwand CFLG_NOKNOCKDOWN) == 0) then begin // critter_flag_check_ + damage := 0; + if (target == dude_obj) and has_trait(TRAIT_PERK, target, PERK_stonewall_perk) then begin + damage := 1; + if (random(0, 100) < 50) then knockback := 0; + end + if knockback then begin + if (weapon_perk == PERK_weapon_knockback) then + amountKnockback := amount / 5; + else + amountKnockback := amount / 10; + + if damage then amountKnockback /= 2; + end + end + end + + if (flagsSource bwand DAM_HIT) then begin + display_msg("amountTarget = " + amountTarget+ ", amount = " + amount); + amountTarget := amount; + flagsTarget := flags; + end else begin + display_msg("amountSource = " + amountSource+ ", amount = " + amount); + amountSource := amount; + flagsSource := flags; + end + + set_sfall_return(amountTarget); + set_sfall_return(amountSource); + set_sfall_return(flagsTarget); + set_sfall_return(flagsSource); + set_sfall_return(amountKnockback); +end + +procedure item_w_damage_hook begin + variable + item_w_damage_min := get_sfall_arg, + item_w_damage_max := get_sfall_arg; + //weapon := get_sfall_arg, + //source := get_sfall_arg, + //hit_mode := get_sfall_arg, + //isMelee := get_sfall_arg; + + item_w_damage := (item_w_damage_min + (item_w_damage_max - item_w_damage_min) / 2); + display_msg("item_w_damage_ = " + item_w_damage); + + set_sfall_return(item_w_damage); +end + +procedure item_w_subtype(variable weapon, variable hit_mode) begin + variable attack_mode, type := WEAPON_TYPE_UNARMED; + + if weapon and (hit_mode <= ATKTYPE_RWEP2) then begin + attack_mode := (get_proto_data(obj_pid(weapon), PROTO_IT_FLAGS)); + + if (hit_mode == ATKTYPE_LWEP2) or (hit_mode == ATKTYPE_RWEP2) then + attack_mode := (attack_mode bwand 0xF0) / 16; // shift 4 bits to the right + else + attack_mode := (attack_mode bwand 0x0F); + + if (attack_mode > ATKMODE_PRI_THROW) then + type := WEAPON_TYPE_GUNS; + else if (attack_mode == ATKMODE_PRI_THROW) then + type := WEAPON_TYPE_THROWN; + else if (attack_mode > ATKMODE_PRI_KICK) then + type := WEAPON_TYPE_MELEE; + end + + return type; +end + +procedure get_ammo_value(variable weapon, variable param) begin + variable pid := -1, value := 0; // default DR value + + if weapon then begin + pid := get_weapon_ammo_pid(weapon); + if (pid > -1) then value := get_proto_data(pid, param); + end + if (pid == -1 and param != PROTO_AM_DR_MOD) then value := 1; // default value for Mult/Div + + return value; +end diff --git a/artifacts/mods/gl_highlighting.int b/artifacts/mods/gl_highlighting.int index df28f02bb2005915aaa0f187b24d34f8a8d367c7..3112fa38a9574867c6fd659c1b05d2cb0ac44bdf 100644 GIT binary patch delta 1585 zcmew(H$h&Yf$0Dv0|SFl122eXSvOI@u>KDN1ZY7h7|8+>XJKGquz`s{Xx2Fl3=GT+ z3=AF+K8$3OVqjokVqjnhfr&t9wj~gn;S-dGQfyx!>gyS~pdwI;17r{*0|TQBQ~*kG zB|sRAdJq~$@&rLxj7|_5M)ItHuo$BtG>qgGgRmHjz_c)!1QEPhAd-QBv1D=(qpFO6 zduoYiUZ!(?URq{)a7hsZ1DAVh352_OBBK=}t*I; zGB7Z3rKA)kmV-E(E163eg|ix18ni({&cFaBnH!8I|6@^&d=2KYG#G+t)+P|iz|vrJ zfbswT|Nk3|8>B%ZtOX4c4XzFP4N@Q;NPYq+64@Gz8}z{9&$6G%95GEF|mVb59B09L_eH(89+p3!b{7^kna9M}qw+kZn@(6HzH zGI<}TCZo*ca~$fE`MD%Gv!GV~nry)3&uBHdflIRf0L1@|>klx3az=v#D7HZ6@bH01 z22e;rVu}&WXKD}z(cCXUBm;YcP=kKMelXj*5u7m@m>?;F`x;o32^16{7WW1a3DNz5wEs#MPmviauJ@yv73l)Mk5mr(_*2I4ekFq^4?6-+ZiB7yfGih3=u95(gL5cPa| zF!hrS_{8gBrm;X}<6yF2+nsQl#tK#6hoasOtRCVoRM)aW)$akTU~3Qp(~Og+@rm0% pK`|o^EQ8Gl><}~f`5@{U^T2LwnEZxMn%@E{wgN1+YqA)B8UVTXJJ|pL delta 1489 zcmbOr|3^-sf$0Dv0|SFl122eXSu|0=u>J!B1ZY7h7|8+>XJ%kvFoTIeXx2Oi1_qG0 z8-x!d*?1Tj7?>Cs7=mCT5Sncogk}Jl2W5gNwg(XP^^6=KE&_9~F)%PNGB7YoA;dr| zE*}udz`&>jp7k<|Ns9D z#tqUS5!MF{5)G~m`VCSb9!P!yD9+d>zhR9Rt^tcNH5fIxLR7P5O^##p)jrU`3s%(v zCVRl-Jg{13Fv--w3Z@wvgeOnnk#k{x2jv<;jA8d^kbqmsh%laA3B@t24MH%B**zxP zv8&`j4PtH(f*MqTWDrAxQNu~FMT{`tu`hxd2zKKUuq;D^QBygHCBV?&0P;JC&3+9; zGJqVT4_13&@;P=1F7|f~EKu$T_D)8D$yFRaG8`XZYQa+Xn#vkX8uUS89PcLo;Z&c@ z$tl5^*1!T1;nJF{#c9u|H93pZSDFhX0=DBflm(3i&Igk(acVMhO#Z`Z&m9J}@Obx;ynnz`F1&=vn&EzN^S;i_5TksV~oPh}zZ#;V@AK+1Cd@;F# z$4rRV4pov@YqB1%IOB!MZoJy{DUB(RsOE)6b&~*CHA{mTB*=LuK#CWr6IdEpAs*!2 z1x=(|K#{}G?6kw9L@U*MZE&p7KVB-g{pxWqJd8eMS~t#B{mCKpz6I) z)LY9 zO_pJkWDJ>X#+K}S0%R%!V}ntH0f@!k0V2WtlVBEegK>jChy|5lX%GVO7#JFi8>B%j zcBaX9*pwL!Cb1hcc1$*5cV|2?xrW`IamnO8?A7W@ASDb84Mq^f90?5)4f+jIU{R2z zU?Vu{CdYBeGOnA%E$uap9hatB>1Iq+ZEIw-t znJmX9=ElVZRt~p=(+!&)oGD16%pjvdi2>$0&PkJ#IJFrMfLySYQ-hIf@;Od1CN7T2 z&o~1)g&OvQEM(-F?7$_q>JXcm8;qJtAWmdpZZK+m3UUv(5W<<0 zxO66Safq0J#Ca58rZttpJjSB{k^u)gBs$=M%RO&$4Yxewoyl{!Wf|{G-o-7?$TvBT zOMNmU&lGO0#uP}X^6Z+tfX9(n2INYHePHt3d_|=7PAd4~F zXwq-eo4kx)e)3a(4aNhL`2>6!7fg;32$z(ABv}R~c#?scaz+4Q%Rd2m=8guINn8>D D79Zod delta 1063 zcmX@4v_natf$0Dv0|SFl122eX=9?&BSZ~Jw0Z|YNMlyrMnHd-u3Sc4-nq>(C0|Q9B z4Z??!Y)K3Z3?Rd2!9*Z5`x6MwumMU#DJ}^JLyO@EgaISD6`-s;P#Q|{=uLd}meFYP z4aS)wB6|8EMTvREIf*5i`FX{9nR%HEllz&rvFbIjG-yv&U>0Ly>6!e1#ms{F0LZ5d zObtd2t_}KNHgki~1dv0IH0d|+Hb^#THE1^QG{}JDSb7>*AoS!5%=U~5lLc7RB`ZK8 z46F@CV49)9xIr4kVu_m^z@pB0WO4;dI-}R*2Q21{7Lz4d4JFSt>4VKLYcd6?V_KGUsj2a9;EcQAO z3Fe;!vzQxGOZAWOjB;3%1#!6D1IWbzD7)ybR|w=rhAm+7$mAb9mW*pA8}T-3Yk<-b$XAfCY0?GLj19sdm5fNb z7*!_Uo6N`8%E&i)3ST2**JLq%c}AAWX8hXhP?eK2_~rQ(z-BN&QZJ*?N-rP7?@c$0|OHS z1A_^K4*~ld)V3qmw@Hi8iX43K`i#J$sz2q zsuMtL1_qE7pdf--!6DHg(V*WT1(Id|G5H$1JfpTLj15LjuJ2`~Il`(GeERJBtbCch3NHW?@=HT=+T++Y-mcQ3j z)*uCDeE{oagt{xIsRGPrZ4idYPHy6KwE@Q{!U!1ar9Uu_~c322- zc_I7(ay3{t)QuoEBhzFLE^Emsh)LX0VBK7>FycBfc@CGlIz$u}Mhp$+kkI88X^?1e zg@qC~&*WEJ>e5jS`$6V1AlbpN4+%1 z#YLkU_BS#dU}R)qSO|+39*4F(vE0Vw3mrOHS_O*Wted69K#K(Bvch1pv`?`+ooc delta 1258 zcmdm|utht(lz1n#uTX@(os9#)!#mY!OVX ze${I`{ z7I371Wk43XHt0iSC&zKPGKNfE!VxTeq^S%n&)i_t@EOcvX)pxQoFb z3%MY&?65%LazJLL24hGF^8_^PZ)7;Y$jHF35EgMfW|LKTrV1KBRT@DO0?)O{ zr+8#}Ss>%YfJn3=Kk1+5+M}J}nfTc9RwO6r+>CYCy38P7{ni&~(7i zU8l$-b;B|QJH(&-Q7Ar{H93G^k-r5+Y~ADr QetZ5)C}KM%pW`n80EOTaGynhq diff --git a/artifacts/mods/main.h b/artifacts/mods/main.h index 84a4c50a..8e09b02d 100644 --- a/artifacts/mods/main.h +++ b/artifacts/mods/main.h @@ -10,21 +10,35 @@ variable ini := "sfall-mods.ini"; variable translationIni; -// Gets the integer value from ini +// Gets the integer value from the specified ini +procedure GetIniConfig(variable section, variable key, variable def, variable inifile) begin + variable val := get_ini_setting(inifile + "|" + section + "|" + key); + if val == -1 then val := def; + return val; +end + +// Gets the string value from the specified ini +procedure GetIniConfigStr(variable section, variable key, variable def, variable inifile) begin + variable val := get_ini_string(inifile + "|" + section + "|" + key); + if val == -1 or val == "" then val := def; + return val; +end + +// Gets the integer value from sfall-mods.ini procedure GetConfig(variable section, variable key, variable def) begin variable val := get_ini_setting(ini + "|" + section + "|" + key); if val == -1 then val := def; return val; end -// Gets the string value from ini +// Gets the string value from sfall-mods.ini procedure GetConfigStr(variable section, variable key, variable def) begin variable val := get_ini_string(ini + "|" + section + "|" + key); if val == -1 or val == "" then val := def; return val; end -// Gets the value from ini as a temp array of strings +// Gets the value from sfall-mods.ini as a temp array of strings procedure GetConfigList(variable section, variable key) begin variable val := get_ini_string(ini + "|" + section + "|" + key); if val == -1 or val == "" then return []; @@ -32,7 +46,7 @@ procedure GetConfigList(variable section, variable key) begin return string_split(val, ","); end -// Gets the value from ini as a temp array of ints +// Gets the value from sfall-mods.ini as a temp array of ints procedure GetConfigListInt(variable section, variable key) begin variable arr, i, item; @@ -53,5 +67,5 @@ procedure Translate(variable id, variable def) begin end procedure InitConfigs begin - translationIni := GetConfigStr("Main", "TranslationsINI", "./Translations.ini"); + translationIni := GetIniConfigStr("Main", "TranslationsINI", "Translations.ini", "ddraw.ini"); end From 19252ba5075cb3acff96e92b13c497bb5b645340 Mon Sep 17 00:00:00 2001 From: NovaRain Date: Fri, 6 Sep 2019 12:32:47 +0800 Subject: [PATCH 21/34] Added options for tweaking some engine perks to the perks ini file Added some engine function defines & util func for possible later use. --- artifacts/config_files/Perks.ini | 21 +++++++++++++++++++++ artifacts/mods/gl_npcarmor.int | Bin 4418 -> 4434 bytes artifacts/mods/gl_npcarmor.ssl | 1 + sfall/FalloutEngine/EngineUtils.cpp | 10 +++++++++- sfall/FalloutEngine/EngineUtils.h | 2 ++ sfall/FalloutEngine/Functions_def.h | 6 +++++- sfall/FalloutEngine/VariableOffsets.h | 1 + sfall/FalloutEngine/Variables_def.h | 2 +- sfall/Modules/BugFixes.cpp | 8 ++++---- sfall/Modules/Perks.cpp | 23 ++++++++++++++++++++++- sfall/ddraw.vcxproj | 16 ++++++++-------- sfall/main.cpp | 3 +++ sfall/main.h | 2 ++ 13 files changed, 79 insertions(+), 16 deletions(-) diff --git a/artifacts/config_files/Perks.ini b/artifacts/config_files/Perks.ini index a04da032..9c951297 100644 --- a/artifacts/config_files/Perks.ini +++ b/artifacts/config_files/Perks.ini @@ -7,6 +7,26 @@ ; have an additional NoHardcode option in this file which can ; be used to remove their hardcoded effects, and add new stat/skill effects +[PerksTweak] +;Change the penalty distance and distance bonus for 'Weapon Scope Range' weapon perk +;0 - no penalty, 8 - default penalty +WeaponScopeRangePenalty=8 +;2 - no bonus, 5 - default bonus +WeaponScopeRangeBonus=5 + +;Changes the distance bonus for 'Weapon Long Range' weapon perk +;2 - no bonus, 4 - default bonus +WeaponLongRangeBonus=4 + +;Changes the hit chance bonus for 'Weapon Accurate' weapon perk +;0 - no bonus, 200 - maximum bonus, 20 - default bonus +WeaponAccurateBonus=20 + +;Changes the strength bonus for 'Weapon Handling' perk +;0 - no bonus, 10 - maximum bonus, 3 - default bonus +WeaponHandlingBonus=3 + +;############################################################################## ;Name=The name of the perk (max 63 characters) ;Desc=The description of the perk (max 255 characters) ;Image=The line number (0-indexed) of the corresponding FRM in skilldex.lst @@ -43,6 +63,7 @@ ;Skill3-Skill5 = The skill to be modified (or -1 for none) ;Skill3Mod-Skill5Mod = The increased/decreased value to the modified skill +;############################################################################## ;This is a modification to vanilla perk 0 [0] Name=Example diff --git a/artifacts/mods/gl_npcarmor.int b/artifacts/mods/gl_npcarmor.int index d31041e9c3d98816f55f902de47afc64fa1ec1d3..fe34af4906aa2e6e497a663608ebc08d9d588b7b 100644 GIT binary patch delta 301 zcmX@4bV+H#MM*0L2)F~GU?h*n##hlytR4+44ce0xn8h|HFh68tRGRF>70akJc>$NV zuG9fW1_lP^2BW5u27NG_0Zf8KY+N7UP#^l$&hEAINAm jxr4u-@xo*Y0bj-qlhXvk88s&F6Ubzq(ZDiUfky%Wc!W%$ delta 285 zcmcblbVzB!MM*6N2)F~GU?h*l##hlytQHL{4ce0xn8h|HFh68t6q@YB70W0zc>$NV zEY|@>1_lP^2BW5u27MU2@hOPKEi_qz+h0_nVgCe11_nl+rm_YTh#DS+$u-=PjPoW> zXW5;rf_RDra<)Y z?3%obr=4-zWGCK6Mx)6`cq17tCd=`)GD=Kd!q><+WwIK-JR{#^H~v6It;sX^`xy^R c))4SzTrjyzAe>QR@;QM_=8guI$p$numLocalVars : 0; } +fo::GameObject* __fastcall LineOfSight(fo::GameObject* obj) { + long objTile = obj->tile; + fo::GameObject* object = fo::func::obj_blocking_at_wrapper(obj, objTile, obj->elevation, (void*)fo::funcoffs::obj_sight_blocking_at_); + if (object) objTile = fo::func::tile_num_in_direction(objTile, fo::func::tile_dir(objTile, fo::var::obj_dude->tile), 1); + fo::func::make_straight_path_func(fo::var::obj_dude, fo::var::obj_dude->tile, objTile, 0, (DWORD*)&object, 4, (void*)fo::funcoffs::obj_sight_blocking_at_); + return object; +} + //--------------------------------------------------------- //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 8832ac18..35835cc1 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); +fo::GameObject* __fastcall LineOfSight(fo::GameObject* obj); + // 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_def.h b/sfall/FalloutEngine/Functions_def.h index 5357592d..b31ba0c5 100644 --- a/sfall/FalloutEngine/Functions_def.h +++ b/sfall/FalloutEngine/Functions_def.h @@ -80,8 +80,8 @@ WRAP_WATCOM_FUNC2(long, item_w_damage, GameObject*, critter, long, hitMode) WRAP_WATCOM_FUNC1(long, item_w_dam_div, GameObject*, item) WRAP_WATCOM_FUNC1(long, item_w_dam_mult, GameObject*, item) WRAP_WATCOM_FUNC1(long, item_w_dr_adjust, GameObject*, item) -WRAP_WATCOM_FUNC1(long, item_w_rounds, GameObject*, item) WRAP_WATCOM_FUNC1(long, item_w_max_ammo, GameObject*, item) +WRAP_WATCOM_FUNC1(long, item_w_rounds, GameObject*, item) WRAP_WATCOM_FUNC1(long, item_weight, GameObject*, item) // returns light level at given tile WRAP_WATCOM_FUNC2(long, light_get_tile, long, elevation, long, tileNum) @@ -135,15 +135,19 @@ WRAP_WATCOM_FUNC1(long, register_object_must_erase, GameObject*, object) // WRAP_WATCOM_FUNC3(long, register_object_run_to_tile_, 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, 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) +WRAP_WATCOM_FUNC2(long, skill_level, GameObject*, critter, long, statID) WRAP_WATCOM_FUNC2(long, stat_get_base_direct, GameObject*, critter, long, statID) WRAP_WATCOM_FUNC2(long, stat_get_bonus, GameObject*, critter, long, statID) WRAP_WATCOM_FUNC3(long, stat_set_bonus, GameObject*, critter, long, statID, long, amount) // adds experience points to PC WRAP_WATCOM_FUNC1(void, stat_pc_add_experience, long, amount) WRAP_WATCOM_FUNC1(long, text_font, long, fontNum) +WRAP_WATCOM_FUNC2(long, tile_dist, long, scrTile, long, dstTile) +WRAP_WATCOM_FUNC2(long, tile_dir, long, scrTile, long, dstTile) // redraws the whole screen WRAP_WATCOM_FUNC0(void, tile_refresh_display) // redraws the given rectangle on screen diff --git a/sfall/FalloutEngine/VariableOffsets.h b/sfall/FalloutEngine/VariableOffsets.h index 1653b2d8..448418ce 100644 --- a/sfall/FalloutEngine/VariableOffsets.h +++ b/sfall/FalloutEngine/VariableOffsets.h @@ -149,6 +149,7 @@ #define FO_VAR_card_old_fid1 0x5709EC #define FO_VAR_obj_dude 0x6610B8 #define FO_VAR_obj_seen 0x662445 +#define FO_VAR_obj_seen_check 0x6610BC #define FO_VAR_objectTable 0x639DA0 #define FO_VAR_objItemOutlineState 0x519798 #define FO_VAR_optionRect 0x58ECC0 diff --git a/sfall/FalloutEngine/Variables_def.h b/sfall/FalloutEngine/Variables_def.h index 5f3fac8e..6de8226d 100644 --- a/sfall/FalloutEngine/Variables_def.h +++ b/sfall/FalloutEngine/Variables_def.h @@ -123,7 +123,7 @@ VAR_(name_sort_list, DWORD) VAR_(num_game_global_vars, DWORD) VAR_(num_map_global_vars, DWORD) VAR_(obj_dude, GameObject*) -VAR_(objectTable, DWORD) +VARA(objectTable, DWORD*, 40000) VAR_(objItemOutlineState, DWORD) VAR_(optionRect, DWORD) VAR_(optionsButtonDown, DWORD) diff --git a/sfall/Modules/BugFixes.cpp b/sfall/Modules/BugFixes.cpp index 8d685cae..aacfb99a 100644 --- a/sfall/Modules/BugFixes.cpp +++ b/sfall/Modules/BugFixes.cpp @@ -1426,15 +1426,15 @@ mapLeave: static void __declspec(naked) obj_move_to_tile_hack_seen() { __asm { cmp ds:[FO_VAR_loadingGame], 0; // loading saved game - jnz fix; + jnz end; // fix // if (map_state <= 0 && mapEntranceTileNum != -1) then fix cmp dword ptr ds:[FO_VAR_map_state], 0; // map number, -1 exit to worldmap jle skip; cmp dword ptr ds:[FO_VAR_mapEntranceTileNum], -1; - jne fix; + jne end; // fix skip: - or byte ptr ds:[FO_VAR_obj_seen][eax], dl; -fix: + or byte ptr ds:[FO_VAR_obj_seen][eax], dl; +end: retn; } } diff --git a/sfall/Modules/Perks.cpp b/sfall/Modules/Perks.cpp index 738ab498..bdb94555 100644 --- a/sfall/Modules/Perks.cpp +++ b/sfall/Modules/Perks.cpp @@ -1408,8 +1408,29 @@ void Perks::init() { perksFile[0] = '.'; perksFile[1] = '\\'; HookCall(0x44272E, TraitInitWrapper); // game_init_ - } + // Engine perks settings + long enginePerkMod = GetPrivateProfileIntA("PerksTweak", "WeaponScopeRangePenalty", -1, perksFile); + if (enginePerkMod >= 0 && enginePerkMod != 8) SafeWrite32(0x42448E, enginePerkMod); + enginePerkMod = GetPrivateProfileIntA("PerksTweak", "WeaponScopeRangeBonus", -1, perksFile); + if (enginePerkMod >= 2 && enginePerkMod != 5) SafeWrite32(0x424489, enginePerkMod); + + enginePerkMod = GetPrivateProfileIntA("PerksTweak", "WeaponLongRangeBonus", -1, perksFile); + if (enginePerkMod >= 2 && enginePerkMod != 4) SafeWrite32(0x424474, enginePerkMod); + + enginePerkMod = GetPrivateProfileIntA("PerksTweak", "WeaponAccurateBonus", -1, perksFile); + if (enginePerkMod >= 0 && enginePerkMod != 20) { + if (enginePerkMod > 200) enginePerkMod = 200; + SafeWrite8(0x42465D, static_cast(enginePerkMod)); + } + + enginePerkMod = GetPrivateProfileIntA("PerksTweak", "WeaponHandlingBonus", -1, perksFile); + if (enginePerkMod >= 0 && enginePerkMod != 3) { + if (enginePerkMod > 10) enginePerkMod = 10; + SafeWrite8(0x424636, static_cast(enginePerkMod)); + SafeWrite8(0x4251CE, static_cast(-enginePerkMod)); + } + } LoadGameHook::OnGameReset() += PerksReset; } diff --git a/sfall/ddraw.vcxproj b/sfall/ddraw.vcxproj index 4f293bf3..47443b8f 100644 --- a/sfall/ddraw.vcxproj +++ b/sfall/ddraw.vcxproj @@ -83,20 +83,20 @@ true - $(DXSDK_DIR)Include;$(IncludePath);$(ProjectDir) - $(DXSDK_DIR)lib\x86\;$(LibraryPath) + $(DXSDK_DIR)Include;$(IncludePath);$(ProjectDir) + $(DXSDK_DIR)lib\x86;$(LibraryPath) - $(DXSDK_DIR)Include;$(IncludePath);$(ProjectDir) - $(DXSDK_DIR)lib\x86\;$(LibraryPath) + $(DXSDK_DIR)Include;$(IncludePath);$(ProjectDir) + $(DXSDK_DIR)lib\x86;$(LibraryPath) - $(DXSDK_DIR)Include;$(IncludePath);$(ProjectDir) - $(DXSDK_DIR)lib\x86\;$(LibraryPath) + $(DXSDK_DIR)Include;$(IncludePath);$(ProjectDir) + $(DXSDK_DIR)lib\x86;$(LibraryPath) - $(DXSDK_DIR)Include;$(IncludePath);$(ProjectDir) - $(DXSDK_DIR)lib\x86\;$(LibraryPath) + $(DXSDK_DIR)Include;$(IncludePath);$(ProjectDir) + $(DXSDK_DIR)lib\x86;$(LibraryPath) diff --git a/sfall/main.cpp b/sfall/main.cpp index 6ba8ebac..8e9cd81e 100644 --- a/sfall/main.cpp +++ b/sfall/main.cpp @@ -86,7 +86,9 @@ namespace sfall { bool isDebug = false; + bool hrpIsEnabled = false; +//bool hrpVersionValid = false; // HRP 4.1.8 version validation const char ddrawIni[] = ".\\ddraw.ini"; static char ini[65] = ".\\"; @@ -292,6 +294,7 @@ defaultIni: modifiedIni = GetConfigInt("Main", "ModifiedIni", 0); hrpIsEnabled = (*(DWORD*)0x4E4480 != 0x278805C7); // check if HRP is enabled + //if (hrpIsEnabled && strncmp((const char*)0x10039940, "4.1.8", 5) == 0) hrpVersionValid = true; InitModules(); } diff --git a/sfall/main.h b/sfall/main.h index 7cf9bc53..f1760561 100644 --- a/sfall/main.h +++ b/sfall/main.h @@ -92,6 +92,8 @@ size_t Translate(const char* section, const char* setting, const char* defaultVa extern const char ddrawIni[]; extern DWORD modifiedIni; + extern bool hrpIsEnabled; +//extern bool hrpVersionValid; } From 7e317134dc1fc8b262e954aa9258c7694748ce0e Mon Sep 17 00:00:00 2001 From: NovaRain Date: Sat, 7 Sep 2019 20:03:29 +0800 Subject: [PATCH 22/34] Removed the dependency of Body_Torso from Body_Uncalled hit modifier Re-added BodyHit_Torso to ddraw.ini. Reverted set_bodypart_hit_modifier function to pre-3.7.4 behavior. --- artifacts/ddraw.ini | 5 ++- artifacts/scripting/headers/define_extra.h | 10 +++++ sfall/Modules/Combat.cpp | 45 +++++++++++++++++++++- sfall/Modules/Combat.h | 4 +- sfall/Modules/MiscPatches.cpp | 15 -------- sfall/Modules/Perks.cpp | 4 +- sfall/Modules/Scripting/Handlers/Misc.cpp | 24 ++++-------- 7 files changed, 69 insertions(+), 38 deletions(-) diff --git a/artifacts/ddraw.ini b/artifacts/ddraw.ini index 23c57802..c6a94a7b 100644 --- a/artifacts/ddraw.ini +++ b/artifacts/ddraw.ini @@ -349,14 +349,17 @@ SaveInCombatFix=1 AdditionalWeaponAnims=1 ;Uncomment these lines to modify the default modifiers for aimed shots at specific bodyparts +;Modifiers affect both NPCs and the player ;BodyHit_Head=-40 ;BodyHit_Left_Arm=-30 ;BodyHit_Right_Arm=-30 -;BodyHit_Torso_Uncalled=0 +;BodyHit_Torso=0 ;BodyHit_Right_Leg=-20 ;BodyHit_Left_Leg=-20 ;BodyHit_Eyes=-60 ;BodyHit_Groin=-30 +;The modifier for unaimed shots +;BodyHit_Torso_Uncalled=0 ;Set to 1 to use a CriticalOverrides.ini file to override the default critical table ;Set to 2 to use the default critical with bug fixes (doesn't require an ini) diff --git a/artifacts/scripting/headers/define_extra.h b/artifacts/scripting/headers/define_extra.h index 80db7216..4884a008 100644 --- a/artifacts/scripting/headers/define_extra.h +++ b/artifacts/scripting/headers/define_extra.h @@ -2,6 +2,16 @@ #ifndef DEFINE_EXTRA_H #define DEFINE_EXTRA_H +#define BODY_HIT_HEAD (0) +#define BODY_HIT_LEFT_ARM (1) +#define BODY_HIT_RIGHT_ARM (2) +#define BODY_HIT_TORSO (3) +#define BODY_HIT_RIGHT_LEG (4) +#define BODY_HIT_LEFT_LEG (5) +#define BODY_HIT_EYES (6) +#define BODY_HIT_GROIN (7) +#define BODY_UNCALLED (8) + #define OBJ_TYPE_ITEM (0) #define OBJ_TYPE_CRITTER (1) #define OBJ_TYPE_SCENERY (2) diff --git a/sfall/Modules/Combat.cpp b/sfall/Modules/Combat.cpp index 7d9567af..b3531e0f 100644 --- a/sfall/Modules/Combat.cpp +++ b/sfall/Modules/Combat.cpp @@ -30,14 +30,14 @@ namespace sfall { -static std::vector noBursts; // object id - struct KnockbackModifier { long id; DWORD type; double value; }; +static std::vector noBursts; // object id + static std::vector mTargets; static std::vector mAttackers; static std::vector mWeapons; @@ -391,6 +391,39 @@ void _stdcall ForceAimedShots(DWORD pid) { forcedAS.push_back(pid); } +static const DWORD bodypartAddr[] = { + 0x425562, // combat_display_ + 0x42A68F, 0x42A739, // ai_called_shot_ + 0x429E82, 0x429EC2, 0x429EFF, // ai_pick_hit_mode_ + 0x423231, 0x423268, // check_ranged_miss_ + 0x4242D4, // attack_crit_failure_ + // for combat_ctd_init_ func + 0x412E9C, // action_explode_ + 0x413519, // action_dmg_ + 0x421656, 0x421675, // combat_safety_invalidate_weapon_func_ + 0x421CC0, // combat_begin_extra_ + 0x4229A9, // combat_turn_ + 0x42330E, 0x4233AB, // shoot_along_path_ + 0x423E25, 0x423E2A, // compute_explosion_on_extras_ + 0x425F83, // combat_anim_finished_ + 0x42946D, // ai_best_weapon_ + 0x46FCC8, // exit_inventory_ + 0x49C00C, // protinstTestDroppedExplosive_ +}; + +static void BodypartHitChances() { + using fo::var::hit_location_penalty; + hit_location_penalty[0] = static_cast(GetConfigInt("Misc", "BodyHit_Head", -40)); + hit_location_penalty[1] = static_cast(GetConfigInt("Misc", "BodyHit_Left_Arm", -30)); + hit_location_penalty[2] = static_cast(GetConfigInt("Misc", "BodyHit_Right_Arm", -30)); + hit_location_penalty[3] = static_cast(GetConfigInt("Misc", "BodyHit_Torso", 0)); + hit_location_penalty[4] = static_cast(GetConfigInt("Misc", "BodyHit_Right_Leg", -20)); + hit_location_penalty[5] = static_cast(GetConfigInt("Misc", "BodyHit_Left_Leg", -20)); + hit_location_penalty[6] = static_cast(GetConfigInt("Misc", "BodyHit_Eyes", -60)); + hit_location_penalty[7] = static_cast(GetConfigInt("Misc", "BodyHit_Groin", -30)); + hit_location_penalty[8] = static_cast(GetConfigInt("Misc", "BodyHit_Torso_Uncalled", 0)); +} + static void Combat_OnGameLoad() { baseHitChance.SetDefault(); mTargets.clear(); @@ -418,6 +451,14 @@ void Combat::init() { HookCall(0x42A95D, ai_try_attack_hook); // jz func MakeCall(0x4234B3, compute_spray_hack, 1); } + + // Remove the dependency of Body_Torso from Body_Uncalled + SafeWrite8(0x423830, 0xEB); // compute_attack_ + BlockCall(0x42303F); // block Body_Torso check (combat_attack_) + SafeWrite8(0x42A713, 7); // Body_Uncalled > Body_Groin (ai_called_shot_) + SafeWriteBatch(8, bodypartAddr); // replace Body_Torso with Body_Uncalled + LoadGameHook::OnBeforeGameStart() += BodypartHitChances; // set on start & load + LoadGameHook::OnGameReset() += Combat_OnGameLoad; } diff --git a/sfall/Modules/Combat.h b/sfall/Modules/Combat.h index 0bd55d48..a18b30ec 100644 --- a/sfall/Modules/Combat.h +++ b/sfall/Modules/Combat.h @@ -49,8 +49,8 @@ struct ChanceModifier { }; void _stdcall SetHitChanceMax(fo::GameObject* critter, DWORD maximum, DWORD mod); -void _stdcall KnockbackSetMod(fo::GameObject* object, DWORD type, float val, DWORD on); -void _stdcall KnockbackRemoveMod(fo::GameObject* object, DWORD on); +void _stdcall KnockbackSetMod(fo::GameObject* object, DWORD type, float val, DWORD mode); +void _stdcall KnockbackRemoveMod(fo::GameObject* object, DWORD mode); void _stdcall SetNoBurstMode(fo::GameObject* critter, bool on); void _stdcall DisableAimedShots(DWORD pid); diff --git a/sfall/Modules/MiscPatches.cpp b/sfall/Modules/MiscPatches.cpp index 3c561b9c..d089b976 100644 --- a/sfall/Modules/MiscPatches.cpp +++ b/sfall/Modules/MiscPatches.cpp @@ -774,19 +774,6 @@ void UseWalkDistancePatch() { } } -void BodypartHitChances() { - using fo::var::hit_location_penalty; - hit_location_penalty[0] = static_cast(GetConfigInt("Misc", "BodyHit_Head", -40)); - hit_location_penalty[1] = static_cast(GetConfigInt("Misc", "BodyHit_Left_Arm", -30)); - hit_location_penalty[2] = static_cast(GetConfigInt("Misc", "BodyHit_Right_Arm", -30)); - hit_location_penalty[3] = static_cast(GetConfigInt("Misc", "BodyHit_Torso_Uncalled", 0)); - hit_location_penalty[4] = static_cast(GetConfigInt("Misc", "BodyHit_Right_Leg", -20)); - hit_location_penalty[5] = static_cast(GetConfigInt("Misc", "BodyHit_Left_Leg", -20)); - hit_location_penalty[6] = static_cast(GetConfigInt("Misc", "BodyHit_Eyes", -60)); - hit_location_penalty[7] = static_cast(GetConfigInt("Misc", "BodyHit_Groin", -30)); - hit_location_penalty[8] = static_cast(GetConfigInt("Misc", "BodyHit_Torso_Uncalled", 0)); -} - void MiscPatches::init() { mapName[64] = 0; if (GetConfigString("Misc", "StartingMap", "", mapName, 64)) { @@ -860,8 +847,6 @@ void MiscPatches::init() { // Increase the max text width of the information card in the character screen SafeWriteBatch(144, {0x43ACD5, 0x43DD37}); // 136, 133 - LoadGameHook::OnBeforeGameStart() += BodypartHitChances; // set on start & load - CombatProcFix(); DialogueFix(); AdditionalWeaponAnimsPatch(); diff --git a/sfall/Modules/Perks.cpp b/sfall/Modules/Perks.cpp index bdb94555..5499646b 100644 --- a/sfall/Modules/Perks.cpp +++ b/sfall/Modules/Perks.cpp @@ -1427,8 +1427,8 @@ void Perks::init() { enginePerkMod = GetPrivateProfileIntA("PerksTweak", "WeaponHandlingBonus", -1, perksFile); if (enginePerkMod >= 0 && enginePerkMod != 3) { if (enginePerkMod > 10) enginePerkMod = 10; - SafeWrite8(0x424636, static_cast(enginePerkMod)); - SafeWrite8(0x4251CE, static_cast(-enginePerkMod)); + SafeWrite8(0x424636, static_cast(enginePerkMod)); + SafeWrite8(0x4251CE, static_cast(-enginePerkMod)); } } LoadGameHook::OnGameReset() += PerksReset; diff --git a/sfall/Modules/Scripting/Handlers/Misc.cpp b/sfall/Modules/Scripting/Handlers/Misc.cpp index 32893a7a..75d8faef 100644 --- a/sfall/Modules/Scripting/Handlers/Misc.cpp +++ b/sfall/Modules/Scripting/Handlers/Misc.cpp @@ -466,12 +466,12 @@ void sf_inc_npc_level(OpcodeContext& ctx) { // restore code SafeWrite32(0x495C50, 0x01FB840F); - SafeWrite16(0x495C77, 0x8C0F); - SafeWrite32(0x495C79, 0x000001D4); + long long data = 0x01D48C0F; + SafeWriteBytes(0x495C77, (BYTE*)&data, 6); //SafeWrite16(0x495C8C, 0x8D0F); //SafeWrite32(0x495C8E, 0x000001BF); - SafeWrite16(0x495CEC, 0x850F); - SafeWrite32(0x495CEE, 0x00000130); + data = 0x0130850F; + SafeWriteBytes(0x495CEC, (BYTE*)&data, 6); if (!npcAutoLevelEnabled) { SafeWrite8(0x495CFB, 0x74); } @@ -640,10 +640,10 @@ void __declspec(naked) op_get_bodypart_hit_modifier() { call fo::funcoffs::interpretPopLong_ cmp dx, VAR_TYPE_INT jnz fail - cmp eax, 8 // Body_Uncalled? - jg fail test eax, eax jl fail + cmp eax, 8 // Body_Uncalled? + jg fail mov edx, ds:[FO_VAR_hit_location_penalty+eax*4] jmp end fail: @@ -679,19 +679,11 @@ void __declspec(naked) op_set_bodypart_hit_modifier() { jnz end cmp cx, VAR_TYPE_INT jnz end - cmp eax, 8 // Body_Uncalled? - jg end - cmp eax, 3 // Body_Torso? - jne skip // No - add eax, 5 -skip: test eax, eax jl end - mov ds:[FO_VAR_hit_location_penalty+eax*4], ebx cmp eax, 8 // Body_Uncalled? - jne end // No - sub eax, 5 // Body_Torso - jmp skip + jg end + mov ds:[FO_VAR_hit_location_penalty+eax*4], ebx end: pop edx pop ecx From 28cd8f6f43b8dd555465d21123d4eaa031a0012d Mon Sep 17 00:00:00 2001 From: NovaRain Date: Sat, 7 Sep 2019 20:59:38 +0800 Subject: [PATCH 23/34] Changed CriticalOverrides.ini to be parsed only once at game start * before the ini was always reparsed after each game load/reset. This should improve the performance. --- sfall/Modules/Criticals.cpp | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/sfall/Modules/Criticals.cpp b/sfall/Modules/Criticals.cpp index 29a3ec6a..2d1b59fd 100644 --- a/sfall/Modules/Criticals.cpp +++ b/sfall/Modules/Criticals.cpp @@ -76,10 +76,9 @@ void Criticals::ResetCriticalTable(DWORD critter, DWORD bodypart, DWORD slot, DW critTable[critter][bodypart][slot].values[element] = baseCritTable[critter][bodypart][slot].values[element]; } -static void CritLoad() { - if (!Inited) return; +static void CritTableLoad() { if (mode == 1) { - dlogr("Setting up critical hit table using CriticalOverrides.ini", DL_CRITICALS); + dlogr("Setting up critical hit table using CriticalOverrides.ini (old fmt)", DL_CRITICALS); char section[16]; for (DWORD critter = 0; critter < 20; critter++) { for (DWORD part = 0; part < 9; part++) { @@ -93,7 +92,7 @@ static void CritLoad() { if (isDebug) { char logmsg[256]; if (newEffect.values[i] != defaultEffect.values[i]) { - sprintf_s(logmsg, "Entry %s value %d changed from %d to %d", section, i, defaultEffect.values[i], newEffect.values[i]); + sprintf_s(logmsg, " Entry %s value %d changed from %d to %d", section, i, defaultEffect.values[i], newEffect.values[i]); dlogr(logmsg, DL_CRITICALS); } } @@ -102,12 +101,13 @@ static void CritLoad() { } } } else { - dlogr("Setting up critical hit table using RP fixes", DL_CRITICALS); - memcpy(baseCritTable, fo::var::crit_succ_eff, sizeof(critTable)); - //memset(&baseCritTable[19], 0, 6 * 9 * 19 * sizeof(fo::CritInfo)); + dlog("Setting up critical hit table using RP fixes", DL_CRITICALS); + memcpy(baseCritTable, fo::var::crit_succ_eff, 19 * 6 * 9 * sizeof(fo::CritInfo)); + //memset(&baseCritTable[19], 0, 19 * 6 * 9 * sizeof(fo::CritInfo)); memcpy(&baseCritTable[38], fo::var::pc_crit_succ_eff, 6 * 9 * sizeof(fo::CritInfo)); // PC crit table if (mode == 3) { + dlog(" and CriticalOverrides.ini (new fmt)", DL_CRITICALS); char buf[32], buf2[32], buf3[32]; for (int critter = 0; critter < Criticals::critTableCount; critter++) { sprintf_s(buf, "c_%02d", critter); @@ -130,15 +130,14 @@ static void CritLoad() { } } } + dlog("\n", DL_CRITICALS); } - memcpy(critTable, baseCritTable, sizeof(critTable)); // Apply base critical table - dlogr("Completed critical hit table.", DL_CRITICALS); } #define SetEntry(critter, bodypart, effect, param, value) fo::var::crit_succ_eff[critter][bodypart][effect].values[param] = value; static void CriticalTableOverride() { - dlog("Initializing critical table override.", DL_INIT); + dlogr("Initializing critical table override...", DL_INIT); playerCrit = &critTable[38]; SafeWrite32(0x423F96, (DWORD)playerCrit); SafeWrite32(0x423FB3, (DWORD)critTable); @@ -240,8 +239,9 @@ static void CriticalTableOverride() { SetEntry(18, 7, 5, 5, 7101); } + CritTableLoad(); + dlogr("Completed applying critical hit table.", DL_INIT); Inited = true; - dlogr(" Done", DL_INIT); } #undef SetEntry @@ -256,13 +256,14 @@ static void RemoveCriticalTimeLimitsPatch() { } void Criticals::init() { - LoadGameHook::OnAfterGameStarted() += CritLoad; - mode = GetConfigInt("Misc", "OverrideCriticalTable", 2); if (mode < 0 || mode > 3) mode = 0; if (mode) { critTableFile += GetConfigString("Misc", "OverrideCriticalFile", "CriticalOverrides.ini", MAX_PATH); CriticalTableOverride(); + LoadGameHook::OnBeforeGameStart() += []() { + memcpy(critTable, baseCritTable, sizeof(critTable)); // Apply loaded critical table + }; } RemoveCriticalTimeLimitsPatch(); From fee142342cfc5b591211810773efbf811f027dc0 Mon Sep 17 00:00:00 2001 From: NovaRain Date: Sun, 8 Sep 2019 10:33:16 +0800 Subject: [PATCH 24/34] Added a call to load f2_res.dll library from sfall code Fixed some code that depends on HRP addresses. --- sfall/Modules/Combat.cpp | 88 ++++++++++++++++++++++++------------- sfall/Modules/Interface.cpp | 9 ++-- sfall/Modules/Perks.cpp | 2 +- sfall/Modules/Tiles.cpp | 6 +-- sfall/main.cpp | 22 +++++++++- sfall/main.h | 4 +- 6 files changed, 89 insertions(+), 42 deletions(-) diff --git a/sfall/Modules/Combat.cpp b/sfall/Modules/Combat.cpp index b3531e0f..0f8a7857 100644 --- a/sfall/Modules/Combat.cpp +++ b/sfall/Modules/Combat.cpp @@ -30,6 +30,38 @@ namespace sfall { +static const DWORD bodypartAddr[] = { + 0x425562, // combat_display_ + 0x42A68F, 0x42A739, // ai_called_shot_ + 0x429E82, 0x429EC2, 0x429EFF, // ai_pick_hit_mode_ + 0x423231, 0x423268, // check_ranged_miss_ + 0x4242D4, // attack_crit_failure_ + // for combat_ctd_init_ func + 0x412E9C, // action_explode_ + 0x413519, // action_dmg_ + 0x421656, 0x421675, // combat_safety_invalidate_weapon_func_ + 0x421CC0, // combat_begin_extra_ + 0x4229A9, // combat_turn_ + 0x42330E, 0x4233AB, // shoot_along_path_ + 0x423E25, 0x423E2A, // compute_explosion_on_extras_ + 0x425F83, // combat_anim_finished_ + 0x42946D, // ai_best_weapon_ + 0x46FCC8, // exit_inventory_ + 0x49C00C, // protinstTestDroppedExplosive_ +}; + +static struct BodyParts { + long Head; + long Left_Arm; + long Right_Arm; + long Torso; + long Right_Leg; + long Left_Leg; + long Eyes; + long Groin; + long Uncalled; +} bodypartHit; + struct KnockbackModifier { long id; DWORD type; @@ -391,37 +423,29 @@ void _stdcall ForceAimedShots(DWORD pid) { forcedAS.push_back(pid); } -static const DWORD bodypartAddr[] = { - 0x425562, // combat_display_ - 0x42A68F, 0x42A739, // ai_called_shot_ - 0x429E82, 0x429EC2, 0x429EFF, // ai_pick_hit_mode_ - 0x423231, 0x423268, // check_ranged_miss_ - 0x4242D4, // attack_crit_failure_ - // for combat_ctd_init_ func - 0x412E9C, // action_explode_ - 0x413519, // action_dmg_ - 0x421656, 0x421675, // combat_safety_invalidate_weapon_func_ - 0x421CC0, // combat_begin_extra_ - 0x4229A9, // combat_turn_ - 0x42330E, 0x4233AB, // shoot_along_path_ - 0x423E25, 0x423E2A, // compute_explosion_on_extras_ - 0x425F83, // combat_anim_finished_ - 0x42946D, // ai_best_weapon_ - 0x46FCC8, // exit_inventory_ - 0x49C00C, // protinstTestDroppedExplosive_ -}; - static void BodypartHitChances() { using fo::var::hit_location_penalty; - hit_location_penalty[0] = static_cast(GetConfigInt("Misc", "BodyHit_Head", -40)); - hit_location_penalty[1] = static_cast(GetConfigInt("Misc", "BodyHit_Left_Arm", -30)); - hit_location_penalty[2] = static_cast(GetConfigInt("Misc", "BodyHit_Right_Arm", -30)); - hit_location_penalty[3] = static_cast(GetConfigInt("Misc", "BodyHit_Torso", 0)); - hit_location_penalty[4] = static_cast(GetConfigInt("Misc", "BodyHit_Right_Leg", -20)); - hit_location_penalty[5] = static_cast(GetConfigInt("Misc", "BodyHit_Left_Leg", -20)); - hit_location_penalty[6] = static_cast(GetConfigInt("Misc", "BodyHit_Eyes", -60)); - hit_location_penalty[7] = static_cast(GetConfigInt("Misc", "BodyHit_Groin", -30)); - hit_location_penalty[8] = static_cast(GetConfigInt("Misc", "BodyHit_Torso_Uncalled", 0)); + hit_location_penalty[0] = bodypartHit.Head; + hit_location_penalty[1] = bodypartHit.Left_Arm; + hit_location_penalty[2] = bodypartHit.Right_Arm; + hit_location_penalty[3] = bodypartHit.Torso; + hit_location_penalty[4] = bodypartHit.Right_Leg; + hit_location_penalty[5] = bodypartHit.Left_Leg; + hit_location_penalty[6] = bodypartHit.Eyes; + hit_location_penalty[7] = bodypartHit.Groin; + hit_location_penalty[8] = bodypartHit.Uncalled; +} + +static void BodypartHitReadConfig() { + bodypartHit.Head = static_cast(GetConfigInt("Misc", "BodyHit_Head", -40)); + bodypartHit.Left_Arm = static_cast(GetConfigInt("Misc", "BodyHit_Left_Arm", -30)); + bodypartHit.Right_Arm = static_cast(GetConfigInt("Misc", "BodyHit_Right_Arm", -30)); + bodypartHit.Torso = static_cast(GetConfigInt("Misc", "BodyHit_Torso", 0)); + bodypartHit.Right_Leg = static_cast(GetConfigInt("Misc", "BodyHit_Right_Leg", -20)); + bodypartHit.Left_Leg = static_cast(GetConfigInt("Misc", "BodyHit_Left_Leg", -20)); + bodypartHit.Eyes = static_cast(GetConfigInt("Misc", "BodyHit_Eyes", -60)); + bodypartHit.Groin = static_cast(GetConfigInt("Misc", "BodyHit_Groin", -30)); + bodypartHit.Uncalled = static_cast(GetConfigInt("Misc", "BodyHit_Torso_Uncalled", 0)); } static void Combat_OnGameLoad() { @@ -446,18 +470,20 @@ void Combat::init() { MakeCall(0x429E44, ai_pick_hit_mode_hack, 1); // NoBurst if (GetConfigInt("Misc", "CheckWeaponAmmoCost", 0)) { + MakeCall(0x4234B3, compute_spray_hack, 1); HookCall(0x4266E9, combat_check_bad_shot_hook); HookCall(0x429A37, ai_search_inven_weap_hook); HookCall(0x42A95D, ai_try_attack_hook); // jz func - MakeCall(0x4234B3, compute_spray_hack, 1); } + BodypartHitReadConfig(); + LoadGameHook::OnBeforeGameStart() += BodypartHitChances; // set on start & load + // Remove the dependency of Body_Torso from Body_Uncalled SafeWrite8(0x423830, 0xEB); // compute_attack_ BlockCall(0x42303F); // block Body_Torso check (combat_attack_) SafeWrite8(0x42A713, 7); // Body_Uncalled > Body_Groin (ai_called_shot_) SafeWriteBatch(8, bodypartAddr); // replace Body_Torso with Body_Uncalled - LoadGameHook::OnBeforeGameStart() += BodypartHitChances; // set on start & load LoadGameHook::OnGameReset() += Combat_OnGameLoad; } diff --git a/sfall/Modules/Interface.cpp b/sfall/Modules/Interface.cpp index a835d7d3..7f037d4d 100644 --- a/sfall/Modules/Interface.cpp +++ b/sfall/Modules/Interface.cpp @@ -81,11 +81,13 @@ static void ActionPointsBarPatch() { dlog("Applying expanded action points bar patch.", DL_INIT); if (hrpIsEnabled) { // check valid data - if (!_stricmp((const char*)0x10039358, "HR_IFACE_%i.frm")) { - SafeWriteStr(0x10039363, "E.frm"); // patching HRP + if (hrpVersionValid && !_stricmp((const char*)HRPAddressOffset(0x39358), "HR_IFACE_%i.frm")) { + SafeWriteStr(HRPAddressOffset(0x39363), "E.frm"); // patching HRP } else { dlog(" Incorrect HRP version!", DL_INIT); + return; } + LoadGameHook::OnAfterGameInit() += APBarRectPatch; } else { APBarRectPatch(); } @@ -456,7 +458,7 @@ static void WorldMapInterfacePatch() { dlogr(" Done", DL_INIT); } - if (hrpIsEnabled) { + if (hrpIsEnabled && hrpVersionValid) { if (worldmapInterface = GetConfigInt("Interface", "ExpandWorldMap", 0)) { LoadGameHook::OnAfterGameInit() += WorldmapViewportPatch; // Note: must be applied after WorldMapSlots patch } @@ -469,7 +471,6 @@ static void WorldMapInterfacePatch() { void Interface::init() { if (GetConfigInt("Interface", "ActionPointsBar", 0)) { ActionPointsBarPatch(); - if (hrpIsEnabled) LoadGameHook::OnAfterGameInit() += APBarRectPatch; } WorldMapInterfacePatch(); diff --git a/sfall/Modules/Perks.cpp b/sfall/Modules/Perks.cpp index 5499646b..22824400 100644 --- a/sfall/Modules/Perks.cpp +++ b/sfall/Modules/Perks.cpp @@ -1427,7 +1427,7 @@ void Perks::init() { enginePerkMod = GetPrivateProfileIntA("PerksTweak", "WeaponHandlingBonus", -1, perksFile); if (enginePerkMod >= 0 && enginePerkMod != 3) { if (enginePerkMod > 10) enginePerkMod = 10; - SafeWrite8(0x424636, static_cast(enginePerkMod)); + SafeWrite8(0x424636, static_cast(enginePerkMod)); SafeWrite8(0x4251CE, static_cast(-enginePerkMod)); } } diff --git a/sfall/Modules/Tiles.cpp b/sfall/Modules/Tiles.cpp index b246099b..15b86f47 100644 --- a/sfall/Modules/Tiles.cpp +++ b/sfall/Modules/Tiles.cpp @@ -272,9 +272,9 @@ void Tiles::init() { SafeWriteBatch(0x3F, Tiles_3F); SafeWriteBatch(0x40, Tiles_40); SafeWriteBatch(0xC0, Tiles_C0); - if (*(long*)0x1000E1BF == 0x1000 && *(long*)0x1000E1D9 == 0x0FFF) { // Check HRP 4.1.8 - SafeWrite8(0x1000E1C0, 0x40); - SafeWrite8(0x1000E1DA, 0x3F); + if (hrpVersionValid) { // Check HRP 4.1.8 + SafeWrite8(HRPAddressOffset(0xE1C0), 0x40); + SafeWrite8(HRPAddressOffset(0xE1DA), 0x3F); } dlogr(" Done", DL_INIT); } diff --git a/sfall/main.cpp b/sfall/main.cpp index 8e9cd81e..fbe41088 100644 --- a/sfall/main.cpp +++ b/sfall/main.cpp @@ -88,13 +88,18 @@ namespace sfall bool isDebug = false; bool hrpIsEnabled = false; -//bool hrpVersionValid = false; // HRP 4.1.8 version validation +bool hrpVersionValid = false; // HRP 4.1.8 version validation const char ddrawIni[] = ".\\ddraw.ini"; static char ini[65] = ".\\"; static char translationIni[65]; DWORD modifiedIni; +DWORD hrpDLLBaseAddr = 0x10000000; + +DWORD HRPAddressOffset(DWORD offset) { + return (hrpDLLBaseAddr + offset); +} unsigned int GetConfigInt(const char* section, const char* setting, int defaultValue) { return GetPrivateProfileIntA(section, setting, defaultValue, ini); @@ -198,6 +203,15 @@ static void InitModules() { dlogr("Leave InitModules", DL_MAIN); } +static const DWORD loadFunc = 0x4FE1D0; +static void LoadHRPModule() { + HMODULE dll; + __asm call loadFunc; // load HRP dll + __asm mov dll, eax; + if (dll != NULL) hrpDLLBaseAddr = (DWORD)dll; + dlog_f("Loaded f2_res.dll library at the memory address: 0x%x\n", DL_MAIN, dll); +} + static void CompatModeCheck(HKEY root, const char* filepath, int extra) { HKEY key; char buf[MAX_PATH]; @@ -294,7 +308,11 @@ defaultIni: modifiedIni = GetConfigInt("Main", "ModifiedIni", 0); hrpIsEnabled = (*(DWORD*)0x4E4480 != 0x278805C7); // check if HRP is enabled - //if (hrpIsEnabled && strncmp((const char*)0x10039940, "4.1.8", 5) == 0) hrpVersionValid = true; + if (hrpIsEnabled) { + BlockCall(0x4E4480); + LoadHRPModule(); + if (strncmp((const char*)HRPAddressOffset(0x39940), "4.1.8", 5) == 0) hrpVersionValid = true; + } InitModules(); } diff --git a/sfall/main.h b/sfall/main.h index f1760561..bc820d3d 100644 --- a/sfall/main.h +++ b/sfall/main.h @@ -90,10 +90,12 @@ 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); + extern const char ddrawIni[]; extern DWORD modifiedIni; extern bool hrpIsEnabled; -//extern bool hrpVersionValid; +extern bool hrpVersionValid; } From 4975177c34506c61e1cd8dadb83afe01c7a012ee Mon Sep 17 00:00:00 2001 From: NovaRain Date: Mon, 9 Sep 2019 12:55:24 +0800 Subject: [PATCH 25/34] Added printing a list of all mem addrs in the log for debug version * for checking hidden memory conflicts. --- sfall/Modules/BugFixes.cpp | 1 + sfall/SafeWrite.cpp | 82 +++++++++++++++++++++++++------------- sfall/SafeWrite.h | 12 +++--- 3 files changed, 62 insertions(+), 33 deletions(-) diff --git a/sfall/Modules/BugFixes.cpp b/sfall/Modules/BugFixes.cpp index aacfb99a..0f4bf061 100644 --- a/sfall/Modules/BugFixes.cpp +++ b/sfall/Modules/BugFixes.cpp @@ -2294,6 +2294,7 @@ static void __declspec(naked) map_check_state_hook() { void BugFixes::init() { #ifndef NDEBUG + LoadGameHook::OnBeforeGameClose() += PrintAddrList; if (isDebug && (GetPrivateProfileIntA("Debugging", "BugFixes", 1, ::sfall::ddrawIni) == 0)) return; #endif diff --git a/sfall/SafeWrite.cpp b/sfall/SafeWrite.cpp index e6adf9d6..371707c0 100644 --- a/sfall/SafeWrite.cpp +++ b/sfall/SafeWrite.cpp @@ -1,7 +1,3 @@ -#ifndef NDEBUG -#include -#endif - #include "main.h" #pragma warning(disable:4996) @@ -16,21 +12,45 @@ enum CodeType : BYTE { }; #ifndef NDEBUG -std::list writeAddress; +std::multimap writeAddress; -void CheckConflict(DWORD addr) { - bool exist = false; +/* Checking for conflicts requires all options in ddraw.ini to be enabled */ +void PrintAddrList() { + unsigned long prev = 0, plen = 0; for (const auto &wa : writeAddress) { - if (addr == wa) { - exist = true; - char buf[256]; - dlog_f("Memory writing conflict at address: 0x%x\n", DL_MAIN, addr); - sprintf_s(buf, "Memory writing conflict at address 0x%x. The address has already been overwritten by other code.", addr); - MessageBoxA(0, buf, "Conflict Detected", MB_TASKMODAL); + unsigned long diff = (prev) ? (wa.first - prev) : -1; // length between two addresses + if (diff == 0 || diff < plen) { + dlog_f("0x%x L:%d [Conflict]\n", DL_MAIN, wa.first, wa.second); + } else if (diff == plen) { + dlog_f("0x%x L:%d [Warning] PL:%d\n", DL_MAIN, wa.first, wa.second, plen); + } else { + dlog_f("0x%x L:%d\n", DL_MAIN, wa.first, wa.second); } + prev = wa.first; + plen = wa.second; } - if (!exist) writeAddress.push_back(addr); } + +void CheckConflict(DWORD addr, long len) { + writeAddress.begin()->first; + if (writeAddress.find(addr) != writeAddress.cend()) { + char buf[64]; + sprintf_s(buf, "Memory writing conflict at address 0x%x.", addr); + //dlogr(buf, DL_MAIN); + MessageBoxA(0, buf, "", MB_TASKMODAL); + } + writeAddress.emplace(addr, len); +} + +void AddrAddToList(DWORD addr, long len) { + writeAddress.emplace(addr, len); +} + +#define _CheckConflict(addr, len) CheckConflict(addr, len); +#define _AddrAddToList(addr, len) AddrAddToList(addr, len); +#else +#define _CheckConflict(a, b) +#define _AddrAddToList(a, b) #endif static void _stdcall SafeWriteFunc(BYTE code, DWORD addr, void* func) { @@ -41,9 +61,7 @@ static void _stdcall SafeWriteFunc(BYTE code, DWORD addr, void* func) { *((DWORD*)(addr + 1)) = data; VirtualProtect((void *)addr, 5, oldProtect, &oldProtect); - #ifndef NDEBUG - CheckConflict(addr); - #endif + _CheckConflict(addr, 5); } static __declspec(noinline) void _stdcall SafeWriteFunc(BYTE code, DWORD addr, void* func, DWORD len) { @@ -61,9 +79,7 @@ static __declspec(noinline) void _stdcall SafeWriteFunc(BYTE code, DWORD addr, v } VirtualProtect((void *)addr, protectLen, oldProtect, &oldProtect); - #ifndef NDEBUG - CheckConflict(addr); - #endif + _CheckConflict(addr, protectLen); } void SafeWriteBytes(DWORD addr, BYTE* data, int count) { @@ -72,6 +88,8 @@ void SafeWriteBytes(DWORD addr, BYTE* data, int count) { VirtualProtect((void *)addr, count, PAGE_EXECUTE_READWRITE, &oldProtect); memcpy((void*)addr, data, count); VirtualProtect((void *)addr, count, oldProtect, &oldProtect); + + _AddrAddToList(addr, count) } void _stdcall SafeWrite8(DWORD addr, BYTE data) { @@ -80,6 +98,8 @@ void _stdcall SafeWrite8(DWORD addr, BYTE data) { VirtualProtect((void *)addr, 1, PAGE_EXECUTE_READWRITE, &oldProtect); *((BYTE*)addr) = data; VirtualProtect((void *)addr, 1, oldProtect, &oldProtect); + + _AddrAddToList(addr, 1) } void _stdcall SafeWrite16(DWORD addr, WORD data) { @@ -88,6 +108,8 @@ void _stdcall SafeWrite16(DWORD addr, WORD data) { VirtualProtect((void *)addr, 2, PAGE_EXECUTE_READWRITE, &oldProtect); *((WORD*)addr) = data; VirtualProtect((void *)addr, 2, oldProtect, &oldProtect); + + _AddrAddToList(addr, 2) } void _stdcall SafeWrite32(DWORD addr, DWORD data) { @@ -96,21 +118,25 @@ void _stdcall SafeWrite32(DWORD addr, DWORD data) { VirtualProtect((void *)addr, 4, PAGE_EXECUTE_READWRITE, &oldProtect); *((DWORD*)addr) = data; VirtualProtect((void *)addr, 4, oldProtect, &oldProtect); + + _AddrAddToList(addr, 4) } void _stdcall SafeWriteStr(DWORD addr, const char* data) { DWORD oldProtect; + long len = strlen(data) + 1; - VirtualProtect((void *)addr, strlen(data) + 1, PAGE_EXECUTE_READWRITE, &oldProtect); + VirtualProtect((void *)addr, len, PAGE_EXECUTE_READWRITE, &oldProtect); strcpy((char *)addr, data); - VirtualProtect((void *)addr, strlen(data) + 1, oldProtect, &oldProtect); + VirtualProtect((void *)addr, len, oldProtect, &oldProtect); + + _AddrAddToList(addr, len) } void HookCall(DWORD addr, void* func) { SafeWrite32(addr + 1, (DWORD)func - (addr + 5)); - #ifndef NDEBUG - CheckConflict(addr); - #endif + + _CheckConflict(addr, 1) } void MakeCall(DWORD addr, void* func) { @@ -147,6 +173,8 @@ void SafeMemSet(DWORD addr, BYTE val, int len) { VirtualProtect((void *)addr, len, PAGE_EXECUTE_READWRITE, &oldProtect); memset((void*)addr, val, len); VirtualProtect((void *)addr, len, oldProtect, &oldProtect); + + _AddrAddToList(addr, len) } void BlockCall(DWORD addr) { @@ -157,9 +185,7 @@ void BlockCall(DWORD addr) { *((BYTE*)(addr + 4)) = 0x00; VirtualProtect((void *)addr, 5, oldProtect, &oldProtect); - #ifndef NDEBUG - CheckConflict(addr); - #endif + _CheckConflict(addr, 5) } } diff --git a/sfall/SafeWrite.h b/sfall/SafeWrite.h index 8618f41a..e7e6ca8d 100644 --- a/sfall/SafeWrite.h +++ b/sfall/SafeWrite.h @@ -5,18 +5,20 @@ namespace sfall { + #ifndef NDEBUG -void CheckConflict(DWORD addr); +void AddrAddToList(DWORD addr, long len); +void PrintAddrList(); #endif template void _stdcall SafeWrite(DWORD addr, T data) { - DWORD oldProtect; - VirtualProtect((void *)addr, sizeof(T), PAGE_EXECUTE_READWRITE, &oldProtect); + DWORD oldProtect; + VirtualProtect((void*)addr, sizeof(T), PAGE_EXECUTE_READWRITE, &oldProtect); *((T*)addr) = data; - VirtualProtect((void *)addr, sizeof(T), oldProtect, &oldProtect); + VirtualProtect((void*)addr, sizeof(T), oldProtect, &oldProtect); #ifndef NDEBUG - if (GetPrivateProfileIntA("Debugging", "Enable", 0, ::sfall::ddrawIni) == 2) CheckConflict(addr); + AddrAddToList(addr, sizeof(T)); #endif } From 4afde14f049c58d206cc269d9d9d3c735bf6b001 Mon Sep 17 00:00:00 2001 From: NovaRain Date: Mon, 9 Sep 2019 14:46:54 +0800 Subject: [PATCH 26/34] Added F1EngineBehavior option to change some of F2 engine funcs to F1 * disables playing the final movie/credits after the endgame slideshow. * disables halving the weight for power armor items. Moved FastShotFix and Ray's combat_p_proc fix from MiscPatches to other modules. --- artifacts/ddraw.ini | 5 +++ sfall/Modules/Combat.cpp | 25 +++++++++++ sfall/Modules/Criticals.cpp | 6 +-- sfall/Modules/MiscPatches.cpp | 80 ++++++----------------------------- sfall/Modules/Perks.cpp | 48 ++++++++++++++++++++- 5 files changed, 91 insertions(+), 73 deletions(-) diff --git a/artifacts/ddraw.ini b/artifacts/ddraw.ini index c6a94a7b..feaa540b 100644 --- a/artifacts/ddraw.ini +++ b/artifacts/ddraw.ini @@ -191,6 +191,11 @@ DebugEditorKey=0 ;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX [Misc] +;Changes some of Fallout 2 engine features to Fallout 1 behavior: +;- disables playing the final movie/credits after the endgame slideshow +;- disables halving the weight for power armor items +F1EngineBehavior=0 + ;Time limit in years. Must be between -3 and 13 ;Set to 0 if you want to die the instant you leave arroyo ;Set to -1 to remove the time limit, and automatically reset the date back to 2241 each time you would have reached it diff --git a/sfall/Modules/Combat.cpp b/sfall/Modules/Combat.cpp index 0f8a7857..3f965dda 100644 --- a/sfall/Modules/Combat.cpp +++ b/sfall/Modules/Combat.cpp @@ -21,6 +21,7 @@ #include "..\main.h" #include "..\FalloutEngine\Fallout2.h" +#include "..\SimplePatch.h" #include "HookScripts.h" #include "LoadGameHook.h" #include "Objects.h" @@ -448,6 +449,26 @@ static void BodypartHitReadConfig() { bodypartHit.Uncalled = static_cast(GetConfigInt("Misc", "BodyHit_Torso_Uncalled", 0)); } +static void __declspec(naked) apply_damage_hack() { + __asm { + xor edx, edx; + inc edx; // COMBAT_SUBTYPE_WEAPON_USED + test [esi + 0x15], dl; // ctd.flags2Source & DAM_HIT_ + jz end; // no hit + inc edx; // COMBAT_SUBTYPE_HIT_SUCCEEDED +end: + retn; + } +} + +static void CombatProcFix() { + //Ray's combat_p_proc fix + dlog("Applying Ray's combat_p_proc patch.", DL_INIT); + MakeCall(0x424DD9, apply_damage_hack); + SafeWrite16(0x424DC6, 0x9090); + dlogr(" Done", DL_INIT); +} + static void Combat_OnGameLoad() { baseHitChance.SetDefault(); mTargets.clear(); @@ -460,6 +481,8 @@ static void Combat_OnGameLoad() { } void Combat::init() { + CombatProcFix(); + MakeCall(0x424B76, compute_damage_hack, 2); // KnockbackMod MakeJump(0x4136D3, compute_dmg_damage_hack); // for op_critter_dmg @@ -476,6 +499,8 @@ void Combat::init() { HookCall(0x42A95D, ai_try_attack_hook); // jz func } + SimplePatch(0x424FA7, "Misc", "KnockoutTime", 35, 35, 100); + BodypartHitReadConfig(); LoadGameHook::OnBeforeGameStart() += BodypartHitChances; // set on start & load diff --git a/sfall/Modules/Criticals.cpp b/sfall/Modules/Criticals.cpp index 2d1b59fd..14579e3b 100644 --- a/sfall/Modules/Criticals.cpp +++ b/sfall/Modules/Criticals.cpp @@ -248,9 +248,9 @@ static void CriticalTableOverride() { static void RemoveCriticalTimeLimitsPatch() { if (GetConfigInt("Misc", "RemoveCriticalTimelimits", 0)) { dlog("Removing critical time limits.", DL_INIT); - SafeWrite8(0x424118, 0xEB); // jump to 0x424131 - SafeWrite8(0x4A3053, 0x0); - SafeWrite8(0x4A3094, 0x0); + SafeWrite8(0x424118, 0xEB); // jump to 0x424131 + SafeWrite16(0x4A3052, 0x9090); + SafeWrite16(0x4A3093, 0x9090); dlogr(" Done", DL_INIT); } } diff --git a/sfall/Modules/MiscPatches.cpp b/sfall/Modules/MiscPatches.cpp index d089b976..a43b5aef 100644 --- a/sfall/Modules/MiscPatches.cpp +++ b/sfall/Modules/MiscPatches.cpp @@ -47,10 +47,6 @@ static const DWORD PutAwayWeapon[] = { 0x472996, // invenWieldFunc_ }; -static const DWORD FastShotFixF1[] = { - 0x478BB8, 0x478BC7, 0x478BD6, 0x478BEA, 0x478BF9, 0x478C08, 0x478C2F, -}; - static const DWORD script_dialog_msgs[] = { 0x4A50C2, 0x4A5169, 0x4A52FA, 0x4A5302, 0x4A6B86, 0x4A6BE0, 0x4A6C37, }; @@ -59,18 +55,6 @@ static const DWORD walkDistanceAddr[] = { 0x411FF0, 0x4121C4, 0x412475, 0x412906, }; -static void __declspec(naked) apply_damage_hack() { - __asm { - xor edx, edx; - inc edx; // COMBAT_SUBTYPE_WEAPON_USED - test [esi + 0x15], dl; // ctd.flags2Source & DAM_HIT_ - jz end; // no hit - inc edx; // COMBAT_SUBTYPE_HIT_SUCCEEDED -end: - retn; - } -} - static void __declspec(naked) WeaponAnimHook() { __asm { cmp edx, 11; @@ -158,26 +142,6 @@ end: } } -static const DWORD FastShotTraitFixEnd1 = 0x478E7F; -static const DWORD FastShotTraitFixEnd2 = 0x478E7B; -static void __declspec(naked) FastShotTraitFix() { - __asm { - test eax, eax; // does player have Fast Shot trait? - je ajmp; // skip ahead if no - mov edx, ecx; // argument for item_w_range_: hit_mode - mov eax, ebx; // argument for item_w_range_: pointer to source_obj (always dude_obj due to code path) - call fo::funcoffs::item_w_range_; // get weapon's range - cmp eax, 0x2; // is weapon range less than or equal 2 (i.e. melee/unarmed attack)? - jle ajmp; // skip ahead if yes - xor eax, eax; // otherwise, disallow called shot attempt - jmp bjmp; -ajmp: - jmp FastShotTraitFixEnd1; // continue processing called shot attempt -bjmp: - jmp FastShotTraitFixEnd2; // clean up and exit function item_w_called_shot - } -} - static void __declspec(naked) ReloadHook() { __asm { push eax; @@ -469,24 +433,6 @@ void ScienceOnCrittersPatch() { } } -void FashShotTraitFix() { - switch (GetConfigInt("Misc", "FastShotFix", 1)) { - case 1: - dlog("Applying Fast Shot Trait Fix.", DL_INIT); - MakeJump(0x478E75, FastShotTraitFix); - dlogr(" Done", DL_INIT); - break; - case 2: - dlog("Applying Fast Shot Trait Fix. (Fallout 1 version)", DL_INIT); - SafeWrite8(0x478CA0, 0x0); - for (int i = 0; i < sizeof(FastShotFixF1) / 4; i++) { - HookCall(FastShotFixF1[i], (void*)0x478C7D); - } - dlogr(" Done", DL_INIT); - break; - } -} - void BoostScriptDialogLimitPatch() { if (GetConfigInt("Misc", "BoostScriptDialogLimit", 0)) { const int scriptDialogCount = 10000; @@ -540,14 +486,6 @@ void DontTurnOffSneakIfYouRunPatch() { } } -void CombatProcFix() { - //Ray's combat_p_proc fix - dlog("Applying Ray's combat_p_proc patch.", DL_INIT); - MakeCall(0x424DD9, apply_damage_hack); - SafeWrite8(0x424DC7, 0x0); - dlogr(" Done", DL_INIT); -} - void MultiPatchesPatch() { //if (GetConfigInt("Misc", "MultiPatches", 0)) { dlog("Applying load multiple patches patch.", DL_INIT); @@ -592,7 +530,7 @@ void MotionScannerFlagsPatch() { if (flags & 1) MakeJump(0x41BBE9, ScannerAutomapHook); if (flags & 2) { // automap_ - SafeWrite8(0x41BC25, 0x0); + SafeWrite16(0x41BC24, 0x9090); BlockCall(0x41BC3C); // item_m_use_charged_item_ SafeWrite8(0x4794B3, 0x5E); // jbe short 0x479512 @@ -774,6 +712,15 @@ void UseWalkDistancePatch() { } } +void F1EngineBehaviorPatch() { + if (GetConfigInt("Misc", "F1EngineBehavior", 0)) { + dlog("Applying Fallout 1 engine behavior patch.", DL_INIT); + BlockCall(0x4A4343); // disable playing the final movie/credits after the endgame slideshow + SafeWrite8(0x477C71, 0xEB); // disable halving the weight for power armor items + dlogr(" Done", DL_INIT); + } +} + void MiscPatches::init() { mapName[64] = 0; if (GetConfigString("Misc", "StartingMap", "", mapName, 64)) { @@ -814,7 +761,7 @@ void MiscPatches::init() { if (GetConfigInt("Misc", "OverrideArtCacheSize", 0)) { dlog("Applying override art cache size patch.", DL_INIT); - SafeWrite8(0x41886A, 0x0); + SafeWrite32(0x418867, 0x90909090); SafeWrite32(0x418872, 256); dlogr(" Done", DL_INIT); } @@ -842,12 +789,10 @@ void MiscPatches::init() { dlogr(" Done", DL_INIT); } - SimplePatch(0x424FA7, "Misc", "KnockoutTime", 35, 35, 100); - // Increase the max text width of the information card in the character screen SafeWriteBatch(144, {0x43ACD5, 0x43DD37}); // 136, 133 - CombatProcFix(); + F1EngineBehaviorPatch(); DialogueFix(); AdditionalWeaponAnimsPatch(); MultiPatchesPatch(); @@ -866,7 +811,6 @@ void MiscPatches::init() { OverrideMusicDirPatch(); NpcStage6Fix(); - FashShotTraitFix(); BoostScriptDialogLimitPatch(); MotionScannerFlagsPatch(); EncounterTableSizePatch(); diff --git a/sfall/Modules/Perks.cpp b/sfall/Modules/Perks.cpp index 22824400..758234b9 100644 --- a/sfall/Modules/Perks.cpp +++ b/sfall/Modules/Perks.cpp @@ -1212,6 +1212,48 @@ static __declspec(naked) void TraitInitWrapper() { } } +static const DWORD FastShotTraitFixEnd1 = 0x478E7F; +static const DWORD FastShotTraitFixEnd2 = 0x478E7B; +static void __declspec(naked) item_w_called_shot_hack() { + __asm { + test eax, eax; // does player have Fast Shot trait? + je ajmp; // skip ahead if no + mov edx, ecx; // argument for item_w_range_: hit_mode + mov eax, ebx; // argument for item_w_range_: pointer to source_obj (always dude_obj due to code path) + call fo::funcoffs::item_w_range_; // get weapon's range + cmp eax, 0x2; // is weapon range less than or equal 2 (i.e. melee/unarmed attack)? + jle ajmp; // skip ahead if yes + xor eax, eax; // otherwise, disallow called shot attempt + jmp bjmp; +ajmp: + jmp FastShotTraitFixEnd1; // continue processing called shot attempt +bjmp: + jmp FastShotTraitFixEnd2; // clean up and exit function item_w_called_shot + } +} + +static const DWORD FastShotFixF1[] = { + 0x478BB8, 0x478BC7, 0x478BD6, 0x478BEA, 0x478BF9, 0x478C08, 0x478C2F, +}; + +static void FastShotTraitFix() { + switch (GetConfigInt("Misc", "FastShotFix", 1)) { + case 1: + dlog("Applying Fast Shot Trait Fix.", DL_INIT); + MakeJump(0x478E75, item_w_called_shot_hack); + goto done; + case 2: + dlog("Applying Fast Shot Trait Fix. (Fallout 1 version)", DL_INIT); + SafeWrite16(0x478C9F, 0x9090); + for (int i = 0; i < sizeof(FastShotFixF1) / 4; i++) { + HookCall(FastShotFixF1[i], (void*)0x478C7D); + } + done: + dlogr(" Done", DL_INIT); + break; + } +} + /////////////////////////////////////////////////////////////////////////////// void _stdcall SetPerkValue(int id, int value, DWORD offset) { @@ -1248,10 +1290,10 @@ void PerksReset() { } // Reset some settable game values back to the defaults + // Perk level mod + SafeWrite32(0x496880, 0x019078); // Pyromaniac bonus SafeWrite8(0x424AB6, 5); - // Perk level mod - SafeWrite32(0x496880, 0x00019078); // Restore 'Heave Ho' modify fix if (perkHeaveHoModFix) { SafeWrite8(0x478AC4, 0xBA); @@ -1400,6 +1442,8 @@ void PerksAcceptCharScreen() { } void Perks::init() { + FastShotTraitFix(); + for (int i = STAT_st; i <= STAT_lu; i++) SafeWrite8(GainStatPerks[i][0], (BYTE)GainStatPerks[i][1]); PerkEngineInit(); From 66d4db10ad4828a3444ec38ef6b2a7903b9b7e92 Mon Sep 17 00:00:00 2001 From: NovaRain Date: Tue, 10 Sep 2019 06:48:37 +0800 Subject: [PATCH 27/34] Fixed some non-critical memory address conflicts Renamed F1EngineBehavior option to Fallout1Behavior. --- artifacts/ddraw.ini | 4 +- sfall/Modules/AnimationsAtOnceLimit.cpp | 10 +++-- sfall/Modules/Interface.cpp | 2 - sfall/Modules/MiscPatches.cpp | 2 +- sfall/SafeWrite.cpp | 49 ++++++++++++------------- 5 files changed, 33 insertions(+), 34 deletions(-) diff --git a/artifacts/ddraw.ini b/artifacts/ddraw.ini index feaa540b..0719c356 100644 --- a/artifacts/ddraw.ini +++ b/artifacts/ddraw.ini @@ -191,10 +191,10 @@ DebugEditorKey=0 ;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX [Misc] -;Changes some of Fallout 2 engine features to Fallout 1 behavior: +;Changes some of Fallout 2 engine functions to Fallout 1 behavior: ;- disables playing the final movie/credits after the endgame slideshow ;- disables halving the weight for power armor items -F1EngineBehavior=0 +Fallout1Behavior=0 ;Time limit in years. Must be between -3 and 13 ;Set to 0 if you want to die the instant you leave arroyo diff --git a/sfall/Modules/AnimationsAtOnceLimit.cpp b/sfall/Modules/AnimationsAtOnceLimit.cpp index 74ad3cde..13a9fef4 100644 --- a/sfall/Modules/AnimationsAtOnceLimit.cpp +++ b/sfall/Modules/AnimationsAtOnceLimit.cpp @@ -60,7 +60,7 @@ static const DWORD anim_set_0[] = { 0x414E48, 0x414EDA, 0x414F5E, 0x414FEE, 0x41505C, 0x4150D0, 0x415158, 0x4151B8, 0x415286, 0x41535C, 0x4153D0, 0x41544A, 0x4154EC, 0x4155EA, 0x4156C0, 0x4156D5, 0x4156F2, 0x41572F, 0x41573E, 0x415B1B, 0x415B56, - 0x415BB6, 0x415C7C, 0x415CA3, 0x415DE4, + 0x415BB6, 0x415C7C, 0x415CA3, /*0x415DE4, - conflct with 0x415DE2*/ }; static const DWORD anim_set_4[] = { @@ -153,14 +153,14 @@ static const DWORD sad_28[] = { 0x4173CE, 0x4174C1, 0x4175F1, 0x417730, }; -static DWORD __fastcall AnimCombatFix(DWORD* src, BYTE combatFlag) { +static DWORD __fastcall AnimCombatFix(fo::GameObject* src, BYTE combatFlag) { DWORD animAddr = animSetAddr; if (animationLimit > 32) { animAddr += animRecordSize; // include a dummy } - if (combatFlag & 2) { // combat flag is set + if (combatFlag & 2) { // combat flag is set __asm call fo::funcoffs::combat_anim_finished_; } return animAddr; @@ -168,9 +168,11 @@ static DWORD __fastcall AnimCombatFix(DWORD* src, BYTE combatFlag) { static void __declspec(naked) anim_set_end_hack() { __asm { + push ecx; call AnimCombatFix; mov [eax][esi], ebx; - xor dl, dl; // goto 0x415DF2; + pop ecx; + xor dl, dl; // for goto 0x415DF2; retn; } } diff --git a/sfall/Modules/Interface.cpp b/sfall/Modules/Interface.cpp index 7f037d4d..86b9b894 100644 --- a/sfall/Modules/Interface.cpp +++ b/sfall/Modules/Interface.cpp @@ -166,8 +166,6 @@ static const DWORD wmWinWidth[] = { 0x4C4BDF, // wmDrawCursorStopped_ 0x4C42EE, 0x4C43C8, 0x4C445F, - // wmTownMapRefresh_ - 0x4C4BDF }; // Right limit of the viewport (450) diff --git a/sfall/Modules/MiscPatches.cpp b/sfall/Modules/MiscPatches.cpp index a43b5aef..80c0a54a 100644 --- a/sfall/Modules/MiscPatches.cpp +++ b/sfall/Modules/MiscPatches.cpp @@ -713,7 +713,7 @@ void UseWalkDistancePatch() { } void F1EngineBehaviorPatch() { - if (GetConfigInt("Misc", "F1EngineBehavior", 0)) { + if (GetConfigInt("Misc", "Fallout1Behavior", 0)) { dlog("Applying Fallout 1 engine behavior patch.", DL_INIT); BlockCall(0x4A4343); // disable playing the final movie/credits after the endgame slideshow SafeWrite8(0x477C71, 0xEB); // disable halving the weight for power armor items diff --git a/sfall/SafeWrite.cpp b/sfall/SafeWrite.cpp index 371707c0..ec786711 100644 --- a/sfall/SafeWrite.cpp +++ b/sfall/SafeWrite.cpp @@ -16,23 +16,25 @@ std::multimap writeAddress; /* Checking for conflicts requires all options in ddraw.ini to be enabled */ void PrintAddrList() { - unsigned long prev = 0, plen = 0; + bool level = (GetPrivateProfileIntA("Debugging", "Enable", 0, ::sfall::ddrawIni) > 1); + unsigned long pa = 0, pl = 0; for (const auto &wa : writeAddress) { - unsigned long diff = (prev) ? (wa.first - prev) : -1; // length between two addresses - if (diff == 0 || diff < plen) { - dlog_f("0x%x L:%d [Conflict]\n", DL_MAIN, wa.first, wa.second); - } else if (diff == plen) { - dlog_f("0x%x L:%d [Warning] PL:%d\n", DL_MAIN, wa.first, wa.second, plen); - } else { + unsigned long diff = (pa) ? (wa.first - pa) : -1; // length between two addresses + if (diff == 0) { + dlog_f("0x%x L:%d [Overwriting]\n", DL_MAIN, wa.first, wa.second); + } else if (diff < pl) { + dlog_f("0x%x L:%d [Conflict] with 0x%x L:%d\n", DL_MAIN, wa.first, wa.second, pa, pl); + } else if (level && diff == pl) { + dlog_f("0x%x L:%d [Warning] PL:%d\n", DL_MAIN, wa.first, wa.second, pl); + } else if (level) { dlog_f("0x%x L:%d\n", DL_MAIN, wa.first, wa.second); } - prev = wa.first; - plen = wa.second; + pa = wa.first; + pl = wa.second; } } void CheckConflict(DWORD addr, long len) { - writeAddress.begin()->first; if (writeAddress.find(addr) != writeAddress.cend()) { char buf[64]; sprintf_s(buf, "Memory writing conflict at address 0x%x.", addr); @@ -45,12 +47,9 @@ void CheckConflict(DWORD addr, long len) { void AddrAddToList(DWORD addr, long len) { writeAddress.emplace(addr, len); } - -#define _CheckConflict(addr, len) CheckConflict(addr, len); -#define _AddrAddToList(addr, len) AddrAddToList(addr, len); #else -#define _CheckConflict(a, b) -#define _AddrAddToList(a, b) +void CheckConflict(DWORD addr, long len) {} +void AddrAddToList(DWORD addr, long len) {} #endif static void _stdcall SafeWriteFunc(BYTE code, DWORD addr, void* func) { @@ -61,7 +60,7 @@ static void _stdcall SafeWriteFunc(BYTE code, DWORD addr, void* func) { *((DWORD*)(addr + 1)) = data; VirtualProtect((void *)addr, 5, oldProtect, &oldProtect); - _CheckConflict(addr, 5); + CheckConflict(addr, 5); } static __declspec(noinline) void _stdcall SafeWriteFunc(BYTE code, DWORD addr, void* func, DWORD len) { @@ -79,7 +78,7 @@ static __declspec(noinline) void _stdcall SafeWriteFunc(BYTE code, DWORD addr, v } VirtualProtect((void *)addr, protectLen, oldProtect, &oldProtect); - _CheckConflict(addr, protectLen); + CheckConflict(addr, protectLen); } void SafeWriteBytes(DWORD addr, BYTE* data, int count) { @@ -89,7 +88,7 @@ void SafeWriteBytes(DWORD addr, BYTE* data, int count) { memcpy((void*)addr, data, count); VirtualProtect((void *)addr, count, oldProtect, &oldProtect); - _AddrAddToList(addr, count) + AddrAddToList(addr, count); } void _stdcall SafeWrite8(DWORD addr, BYTE data) { @@ -99,7 +98,7 @@ void _stdcall SafeWrite8(DWORD addr, BYTE data) { *((BYTE*)addr) = data; VirtualProtect((void *)addr, 1, oldProtect, &oldProtect); - _AddrAddToList(addr, 1) + AddrAddToList(addr, 1); } void _stdcall SafeWrite16(DWORD addr, WORD data) { @@ -109,7 +108,7 @@ void _stdcall SafeWrite16(DWORD addr, WORD data) { *((WORD*)addr) = data; VirtualProtect((void *)addr, 2, oldProtect, &oldProtect); - _AddrAddToList(addr, 2) + AddrAddToList(addr, 2); } void _stdcall SafeWrite32(DWORD addr, DWORD data) { @@ -119,7 +118,7 @@ void _stdcall SafeWrite32(DWORD addr, DWORD data) { *((DWORD*)addr) = data; VirtualProtect((void *)addr, 4, oldProtect, &oldProtect); - _AddrAddToList(addr, 4) + AddrAddToList(addr, 4); } void _stdcall SafeWriteStr(DWORD addr, const char* data) { @@ -130,13 +129,13 @@ void _stdcall SafeWriteStr(DWORD addr, const char* data) { strcpy((char *)addr, data); VirtualProtect((void *)addr, len, oldProtect, &oldProtect); - _AddrAddToList(addr, len) + AddrAddToList(addr, len); } void HookCall(DWORD addr, void* func) { SafeWrite32(addr + 1, (DWORD)func - (addr + 5)); - _CheckConflict(addr, 1) + CheckConflict(addr, 1); } void MakeCall(DWORD addr, void* func) { @@ -174,7 +173,7 @@ void SafeMemSet(DWORD addr, BYTE val, int len) { memset((void*)addr, val, len); VirtualProtect((void *)addr, len, oldProtect, &oldProtect); - _AddrAddToList(addr, len) + AddrAddToList(addr, len); } void BlockCall(DWORD addr) { @@ -185,7 +184,7 @@ void BlockCall(DWORD addr) { *((BYTE*)(addr + 4)) = 0x00; VirtualProtect((void *)addr, 5, oldProtect, &oldProtect); - _CheckConflict(addr, 5) + CheckConflict(addr, 5); } } From e55a4f95d23062123bca7711e3a839fa741c21b4 Mon Sep 17 00:00:00 2001 From: NovaRain Date: Tue, 10 Sep 2019 09:37:46 +0800 Subject: [PATCH 28/34] Fixed AllowLargeTiles option (broken in 4.1.8) (due to commit d2cf728) --- sfall/Modules/Scripting/Handlers/Misc.cpp | 6 ++++-- sfall/Modules/Tiles.cpp | 7 ++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/sfall/Modules/Scripting/Handlers/Misc.cpp b/sfall/Modules/Scripting/Handlers/Misc.cpp index 75d8faef..fedfa24c 100644 --- a/sfall/Modules/Scripting/Handlers/Misc.cpp +++ b/sfall/Modules/Scripting/Handlers/Misc.cpp @@ -537,7 +537,9 @@ static int ParseIniSetting(const char* iniString, const char* &key, char section file[1] = '\\'; if (!ScriptExtender::iniConfigFolder.empty()) { const char* pos = strfind(iniString, &::sfall::ddrawIni[2]); - if (pos && pos < fileEnd) goto ddraw; + if (pos && pos < fileEnd) goto specialIni; + pos = strfind(iniString, "f2_res.ini"); + if (pos && pos < fileEnd) goto specialIni; 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 @@ -549,7 +551,7 @@ static int ParseIniSetting(const char* iniString, const char* &key, char section } strncpy_s(&file[2 + len], (128 - 2) - len, &iniString[n], filelen - n); // copy filename } else { -ddraw: +specialIni: memcpy(&file[2], iniString, filelen); file[filelen + 2] = 0; } diff --git a/sfall/Modules/Tiles.cpp b/sfall/Modules/Tiles.cpp index 15b86f47..26707387 100644 --- a/sfall/Modules/Tiles.cpp +++ b/sfall/Modules/Tiles.cpp @@ -53,7 +53,7 @@ static const DWORD Tiles_C0[] = { 0x4B247B, 0x4B2A77, 0x4B2BD5, }; -struct tilestruct { +struct TilesData { short tile[2]; }; @@ -196,7 +196,7 @@ static void __declspec(naked) iso_init_hook() { } } -static void __fastcall SquareLoadCheck(tilestruct* data) { +static long __fastcall SquareLoadCheck(TilesData* data) { for (DWORD y = 0; y < 100; y++) { for (DWORD x = 0; x < 100; x++) { for (DWORD z = 0; z < 2; z++) { @@ -214,6 +214,7 @@ static void __fastcall SquareLoadCheck(tilestruct* data) { } } } + return 0; // don't delete } static void __declspec(naked) square_load_hook() { @@ -222,7 +223,7 @@ static void __declspec(naked) square_load_hook() { call fo::funcoffs::db_freadIntCount_; test eax, eax; jnz end; - jmp SquareLoadCheck; + jmp SquareLoadCheck; // ecx - data end: retn; } From 693b78f9db0399dcbcc72ce195f28456043aff60 Mon Sep 17 00:00:00 2001 From: NovaRain Date: Wed, 11 Sep 2019 12:40:18 +0800 Subject: [PATCH 29/34] Added a new hook: HOOK_STDPROCEDURE Optimized execution for global/hook scripts. --- artifacts/scripting/headers/sfall.h | 1 + artifacts/scripting/hookscripts.txt | 19 +++++++---- sfall/Modules/Criticals.cpp | 5 +-- sfall/Modules/HookScripts.cpp | 1 + sfall/Modules/HookScripts.h | 1 + sfall/Modules/HookScripts/Common.cpp | 2 +- sfall/Modules/HookScripts/ObjectHs.cpp | 46 ++++++++++++++++++++++++++ sfall/Modules/HookScripts/ObjectHs.h | 1 + sfall/Modules/ScriptExtender.cpp | 36 +++++++++++--------- sfall/Modules/ScriptExtender.h | 2 ++ sfall/Modules/Scripting/Opcodes.cpp | 2 +- sfall/Modules/Worldmap.cpp | 6 ++-- 12 files changed, 94 insertions(+), 28 deletions(-) diff --git a/artifacts/scripting/headers/sfall.h b/artifacts/scripting/headers/sfall.h index 2f6e1927..b684946f 100644 --- a/artifacts/scripting/headers/sfall.h +++ b/artifacts/scripting/headers/sfall.h @@ -62,6 +62,7 @@ #define HOOK_SUBCOMBATDAMAGE (37) #define HOOK_SETLIGHTING (38) #define HOOK_SNEAK (39) +#define HOOK_STDPROCEDURE (40) //Valid arguments to list_begin #define LIST_CRITTERS (0) diff --git a/artifacts/scripting/hookscripts.txt b/artifacts/scripting/hookscripts.txt index 4c76758c..816fc29a 100644 --- a/artifacts/scripting/hookscripts.txt +++ b/artifacts/scripting/hookscripts.txt @@ -10,8 +10,6 @@ To aid in mods compatibility, avoid using hs_xxx .int scripts. Instead it is rec Example setup for a hook-script based mod: - - procedure tohit_hook_handler begin display_msg("Modifying hit_hook " + get_sfall_arg); set_hit_chance_max(100); @@ -25,8 +23,6 @@ procedure start begin end - - There are script functions available, specific to hook scripts: > int init_hook() @@ -48,7 +44,7 @@ Changes argument value. The argument number (argnum) is 0-indexed. This is usefu 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) -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"). 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!). +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!). 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. Example: let's say we have a Mod A which reduces all "to hit" chances by 50%. The code might look like this: @@ -336,7 +332,6 @@ int arg4 - Type of hook (0 - when subtracting ammo after single shot attack, int ret1 - new ammo cost value (set to 0 for unlimited ammo) - ------------------------------------------- HOOK_KEYPRESS (hs_keypress.int) @@ -633,3 +628,15 @@ Critter arg3 - the critter (usually dude_obj) int ret1 - overrides the result of the Sneak check int ret2 - overrides the duration time for the current result + +------------------------------------------- + +HOOK_STDPROCEDURE (hs_stdprocedure.int) + +Runs once every time before executing the standard procedure (handler) in the script of an object. + +int arg1 - number of the standard script handler (see define.h, except: start, critter_p_proc, and map_update_p_proc) +Obj arg2 - the object connected to the executed script (self_obj) +Obj arg3 - the object that called this handler (source_obj, can be 0) + +int ret1 - pass -1 to cancel the execution of the handler diff --git a/sfall/Modules/Criticals.cpp b/sfall/Modules/Criticals.cpp index 14579e3b..a11163dc 100644 --- a/sfall/Modules/Criticals.cpp +++ b/sfall/Modules/Criticals.cpp @@ -107,7 +107,7 @@ static void CritTableLoad() { memcpy(&baseCritTable[38], fo::var::pc_crit_succ_eff, 6 * 9 * sizeof(fo::CritInfo)); // PC crit table if (mode == 3) { - dlog(" and CriticalOverrides.ini (new fmt)", DL_CRITICALS); + dlogr(" and CriticalOverrides.ini (new fmt)", DL_CRITICALS); char buf[32], buf2[32], buf3[32]; for (int critter = 0; critter < Criticals::critTableCount; critter++) { sprintf_s(buf, "c_%02d", critter); @@ -129,8 +129,9 @@ static void CritTableLoad() { } } } + } else { + dlog("\n", DL_CRITICALS); } - dlog("\n", DL_CRITICALS); } } diff --git a/sfall/Modules/HookScripts.cpp b/sfall/Modules/HookScripts.cpp index 37ee7605..84db08dc 100644 --- a/sfall/Modules/HookScripts.cpp +++ b/sfall/Modules/HookScripts.cpp @@ -84,6 +84,7 @@ static HooksInjectInfo injectHooks[] = { {HOOK_SUBCOMBATDAMAGE, Inject_SubCombatDamageHook, false}, // replace the code logic {HOOK_SETLIGHTING, Inject_SetLightingHook, false}, {HOOK_SNEAK, Inject_SneakCheckHook, false}, + {HOOK_STDPROCEDURE, Inject_ScriptProcedureHook, false}, }; bool HookScripts::injectAllHooks; diff --git a/sfall/Modules/HookScripts.h b/sfall/Modules/HookScripts.h index 3db160ac..9836c37d 100644 --- a/sfall/Modules/HookScripts.h +++ b/sfall/Modules/HookScripts.h @@ -66,6 +66,7 @@ enum HookType HOOK_SUBCOMBATDAMAGE = 37, HOOK_SETLIGHTING = 38, HOOK_SNEAK = 39, + HOOK_STDPROCEDURE = 40, HOOK_COUNT }; diff --git a/sfall/Modules/HookScripts/Common.cpp b/sfall/Modules/HookScripts/Common.cpp index 91b06f65..102f1979 100644 --- a/sfall/Modules/HookScripts/Common.cpp +++ b/sfall/Modules/HookScripts/Common.cpp @@ -102,7 +102,7 @@ static void _stdcall RunSpecificHookScript(HookScript *hook) { if (hook->callback != -1) { fo::func::executeProcedure(hook->prog.ptr, hook->callback); } else { - RunScriptProc(&hook->prog, fo::ScriptProc::start); + hook->callback = RunScriptStartProc(&hook->prog); // run start } } diff --git a/sfall/Modules/HookScripts/ObjectHs.cpp b/sfall/Modules/HookScripts/ObjectHs.cpp index 5e3727c0..a54a8d17 100644 --- a/sfall/Modules/HookScripts/ObjectHs.cpp +++ b/sfall/Modules/HookScripts/ObjectHs.cpp @@ -235,6 +235,47 @@ skip: } } +static DWORD __fastcall StdProcedureHook_Script(long numHandler, fo::ScriptInstance* script, DWORD procTable) { + BeginHook(); + argCount = 3; + + 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; + } + EndHook(); + return procTable; +} + +static void __declspec(naked) ScriptStdProcedureHook() { + using namespace fo::ScriptProc; + __asm { + mov eax, [eax + 0x54]; // Script.procedure_table + test eax, eax; + jle end; + cmp ecx, critter_p_proc; + je skip; + cmp ecx, map_update_p_proc; + je skip; + cmp ecx, start; + jle skip; + push ecx; + push eax; // procTable + mov edx, esi; // script + call StdProcedureHook_Script; // ecx - numHandler + pop ecx; +skip: + test eax, eax; +end: + retn; + } +} + void Inject_UseObjOnHook() { HookCalls(UseObjOnHook, { 0x49C606, 0x473619 }); @@ -264,6 +305,10 @@ void Inject_SetLightingHook() { MakeCall(0x47A934, SetMapLightHook, 1); } +void Inject_ScriptProcedureHook() { + MakeCall(0x4A491F, ScriptStdProcedureHook); +} + void InitObjectHookScripts() { LoadHookScript("hs_useobjon", HOOK_USEOBJON); @@ -271,6 +316,7 @@ void InitObjectHookScripts() { LoadHookScript("hs_useanimobj", HOOK_USEANIMOBJ); LoadHookScript("hs_descriptionobj", HOOK_DESCRIPTIONOBJ); LoadHookScript("hs_setlighting", HOOK_SETLIGHTING); + LoadHookScript("hs_stdprocedure", HOOK_STDPROCEDURE); } } diff --git a/sfall/Modules/HookScripts/ObjectHs.h b/sfall/Modules/HookScripts/ObjectHs.h index 020cc066..f14084be 100644 --- a/sfall/Modules/HookScripts/ObjectHs.h +++ b/sfall/Modules/HookScripts/ObjectHs.h @@ -9,4 +9,5 @@ namespace sfall void Inject_UseAnimateObjHook(); void Inject_DescriptionObjHook(); void Inject_SetLightingHook(); + void Inject_ScriptProcedureHook(); } diff --git a/sfall/Modules/ScriptExtender.cpp b/sfall/Modules/ScriptExtender.cpp index c6bb5b66..ee1b76b0 100644 --- a/sfall/Modules/ScriptExtender.cpp +++ b/sfall/Modules/ScriptExtender.cpp @@ -57,17 +57,13 @@ std::string ScriptExtender::iniConfigFolder; struct GlobalScript { ScriptProgram prog; + int startProc; // position of the 'start' procedure in the script int count; int repeat; - int mode; //0 - local map loop, 1 - input loop, 2 - world map loop, 3 - local and world map loops + int mode; // 0 - local map loop, 1 - input loop, 2 - world map loop, 3 - local and world map loops - GlobalScript() {} - GlobalScript(ScriptProgram script) { - prog = script; - count = 0; - repeat = 0; - mode = 0; - } + //GlobalScript() {} + GlobalScript(ScriptProgram script) : prog(script), startProc(-1), count(0), repeat(0), mode(0) {} }; struct ExportedVar { @@ -433,10 +429,8 @@ void AddProgramToMap(ScriptProgram &prog) { } ScriptProgram* GetGlobalScriptProgram(fo::Program* scriptPtr) { - for (std::vector::iterator it = globalScripts.begin(); it != globalScripts.end(); it++) { - if (it->prog.ptr == scriptPtr) return &it->prog; - } - return nullptr; + SfallProgsMap::iterator it = sfallProgsMap.find(scriptPtr); + return (it == sfallProgsMap.end()) ? nullptr : &it->second ; // prog } bool _stdcall IsGameScript(const char* filename) { @@ -458,6 +452,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 globalScripts.push_back(gscript); AddProgramToMap(prog); // initialize script (start proc will be executed for the first time) -- this needs to be after script is added to "globalScripts" array @@ -553,9 +548,20 @@ void RunScriptProc(ScriptProgram* prog, long procId) { } } +int RunScriptStartProc(ScriptProgram* prog) { + fo::Program* sptr = prog->ptr; + int procNum = prog->procLookup[fo::ScriptProc::start]; + if (procNum != -1) { + fo::func::executeProcedure(sptr, procNum); + } + return procNum; +} + static void RunScript(GlobalScript* script) { script->count = 0; - RunScriptProc(&script->prog, fo::ScriptProc::start); // run "start" + if (script->startProc != -1) { + fo::func::executeProcedure(script->prog.ptr, script->startProc); // run "start" + } } /** @@ -599,8 +605,8 @@ static void RunGlobalScriptsOnWorldMap() { static DWORD _stdcall HandleMapUpdateForScripts(const DWORD procId) { if (procId == fo::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 (SfallProgsMap::iterator it = sfallProgsMap.begin(); it != sfallProgsMap.end(); it++) { - fo::func::runProgram(it->second.ptr); + 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(); diff --git a/sfall/Modules/ScriptExtender.h b/sfall/Modules/ScriptExtender.h index c7473730..96bc8b0f 100644 --- a/sfall/Modules/ScriptExtender.h +++ b/sfall/Modules/ScriptExtender.h @@ -90,6 +90,8 @@ void RunScriptProc(ScriptProgram* prog, const char* procName); // execute script proc by procId from define.h void RunScriptProc(ScriptProgram* prog, long procId); +int RunScriptStartProc(ScriptProgram* prog); + void AddProgramToMap(ScriptProgram &prog); ScriptProgram* GetGlobalScriptProgram(fo::Program* scriptPtr); diff --git a/sfall/Modules/Scripting/Opcodes.cpp b/sfall/Modules/Scripting/Opcodes.cpp index dd3b6d73..d9490aa7 100644 --- a/sfall/Modules/Scripting/Opcodes.cpp +++ b/sfall/Modules/Scripting/Opcodes.cpp @@ -184,7 +184,7 @@ static SfallOpcodeInfo opcodeInfoArray[] = { {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_ANY}}, + {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}}, diff --git a/sfall/Modules/Worldmap.cpp b/sfall/Modules/Worldmap.cpp index 16223792..788d4f96 100644 --- a/sfall/Modules/Worldmap.cpp +++ b/sfall/Modules/Worldmap.cpp @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -#include +#include #include #include "..\main.h" @@ -40,7 +40,7 @@ static DWORD ViewportY; struct levelRest { char level[4]; }; -std::map mapRestInfo; +std::unordered_map mapRestInfo; static bool restMap; static bool restMode; @@ -490,7 +490,7 @@ void PipBoyAutomapsPatch() { void Worldmap::SaveData(HANDLE file) { DWORD sizeWrite, count = mapRestInfo.size(); WriteFile(file, &count, 4, &sizeWrite, 0); - std::map::iterator it; + std::unordered_map::iterator it; for (it = mapRestInfo.begin(); it != mapRestInfo.end(); it++) { WriteFile(file, &it->first, 4, &sizeWrite, 0); WriteFile(file, &it->second, sizeof(levelRest), &sizeWrite, 0); From e23c2245352958c5bce719fd22634708aaffeec9 Mon Sep 17 00:00:00 2001 From: NovaRain Date: Thu, 12 Sep 2019 19:38:30 +0800 Subject: [PATCH 30/34] Excluded timed_event_p_proc from HOOK_STDPROCEDURE --- artifacts/scripting/hookscripts.txt | 7 ++++--- sfall/Modules/HookScripts/ObjectHs.cpp | 2 ++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/artifacts/scripting/hookscripts.txt b/artifacts/scripting/hookscripts.txt index 816fc29a..1da5b112 100644 --- a/artifacts/scripting/hookscripts.txt +++ b/artifacts/scripting/hookscripts.txt @@ -633,10 +633,11 @@ int ret2 - overrides the duration time for the current result HOOK_STDPROCEDURE (hs_stdprocedure.int) -Runs once every time before executing the standard procedure (handler) in the script of an object. +Runs before Fallout 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 - number of the standard script handler (see define.h, except: start, critter_p_proc, and map_update_p_proc) -Obj arg2 - the object connected to the executed script (self_obj) +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 ret1 - pass -1 to cancel the execution of the handler diff --git a/sfall/Modules/HookScripts/ObjectHs.cpp b/sfall/Modules/HookScripts/ObjectHs.cpp index a54a8d17..edc05be0 100644 --- a/sfall/Modules/HookScripts/ObjectHs.cpp +++ b/sfall/Modules/HookScripts/ObjectHs.cpp @@ -260,6 +260,8 @@ static void __declspec(naked) ScriptStdProcedureHook() { jle end; 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; From 2bdd4699192d0969c29d044890f1c73498652e79 Mon Sep 17 00:00:00 2001 From: NovaRain Date: Thu, 12 Sep 2019 22:16:17 +0800 Subject: [PATCH 31/34] Added "register_hook_proc_spec" script function Updated documents to address the new function. --- artifacts/scripting/hookscripts.txt | 3 +++ artifacts/scripting/sfall function notes.txt | 4 ++++ artifacts/scripting/sfall opcode list.txt | 2 ++ sfall/FalloutEngine/EngineUtils.cpp | 8 ------- sfall/FalloutEngine/EngineUtils.h | 2 -- sfall/Modules/HookScripts.cpp | 25 ++++++++++++++++---- sfall/Modules/HookScripts.h | 4 ++-- sfall/Modules/ScriptExtender.cpp | 2 +- sfall/Modules/ScriptExtender.h | 2 +- sfall/Modules/Scripting/Handlers/Core.cpp | 19 ++++++++++----- sfall/Modules/Scripting/Opcodes.cpp | 2 ++ 11 files changed, 48 insertions(+), 25 deletions(-) diff --git a/artifacts/scripting/hookscripts.txt b/artifacts/scripting/hookscripts.txt index 1da5b112..551a816e 100644 --- a/artifacts/scripting/hookscripts.txt +++ b/artifacts/scripting/hookscripts.txt @@ -46,6 +46,9 @@ Used from a normal global script if you want to run it at the same point a full > void register_hook_proc(int hook, 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) +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. Example: let's say we have a Mod A which reduces all "to hit" chances by 50%. The code might look like this: diff --git a/artifacts/scripting/sfall function notes.txt b/artifacts/scripting/sfall function notes.txt index 6bb2e18b..707f3c29 100644 --- a/artifacts/scripting/sfall function notes.txt +++ b/artifacts/scripting/sfall function notes.txt @@ -316,6 +316,10 @@ Some utility/math functions are available: - second argument should be passed just like you pass procedures to functions like gsay_option, giq_option, etc (name without quotes) - see "hookscripts.txt" for more details +> void register_hook_proc_spec(int hook, procedure proc) +- works just like "register_hook_proc", but allows to register a 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) +- to unregister hook script from current global script, use the register_hook_proc function + > string message_str_game(int fileId, int messageId) - works exactly the same as message_str, except you get messages from files in "text\\game\" directory - use GAME_MSG_* defines or mstr_* macros from sfall.h to use specific msg file diff --git a/artifacts/scripting/sfall opcode list.txt b/artifacts/scripting/sfall opcode list.txt index 4fa5563a..79f0026f 100644 --- a/artifacts/scripting/sfall opcode list.txt +++ b/artifacts/scripting/sfall opcode list.txt @@ -357,6 +357,8 @@ 0x827b - any sfall_func5(string funcName, arg1, arg2, arg3, arg4, arg5) 0x827c - any sfall_func6(string funcName, arg1, arg2, arg3, arg4, arg5, arg6) +0x827d - void register_hook_proc_spec(int hook, procedure proc) + * These functions require AllowUnsafeScripting to be enabled in ddraw.ini diff --git a/sfall/FalloutEngine/EngineUtils.cpp b/sfall/FalloutEngine/EngineUtils.cpp index a3904f18..dea4340f 100644 --- a/sfall/FalloutEngine/EngineUtils.cpp +++ b/sfall/FalloutEngine/EngineUtils.cpp @@ -181,14 +181,6 @@ long GetScriptLocalVars(long sid) { return (script) ? script->numLocalVars : 0; } -fo::GameObject* __fastcall LineOfSight(fo::GameObject* obj) { - long objTile = obj->tile; - fo::GameObject* object = fo::func::obj_blocking_at_wrapper(obj, objTile, obj->elevation, (void*)fo::funcoffs::obj_sight_blocking_at_); - if (object) objTile = fo::func::tile_num_in_direction(objTile, fo::func::tile_dir(objTile, fo::var::obj_dude->tile), 1); - fo::func::make_straight_path_func(fo::var::obj_dude, fo::var::obj_dude->tile, objTile, 0, (DWORD*)&object, 4, (void*)fo::funcoffs::obj_sight_blocking_at_); - return object; -} - //--------------------------------------------------------- //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 35835cc1..8832ac18 100644 --- a/sfall/FalloutEngine/EngineUtils.h +++ b/sfall/FalloutEngine/EngineUtils.h @@ -76,8 +76,6 @@ bool IsPartyMember(fo::GameObject* critter); // Returns the number of local variables of the object script long GetScriptLocalVars(long sid); -fo::GameObject* __fastcall LineOfSight(fo::GameObject* obj); - // 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/HookScripts.cpp b/sfall/Modules/HookScripts.cpp index 84db08dc..2b9b09bc 100644 --- a/sfall/Modules/HookScripts.cpp +++ b/sfall/Modules/HookScripts.cpp @@ -43,6 +43,12 @@ struct HooksInjectInfo { bool isInject; }; +static struct HooksPositionInfo { + long hsPosition = 0; // index of the hs_* script, or the beginning of the position for registering scripts using register_hook +// long positionShift = 0; // offset to the last script registered by register_hook + bool hasHsScript = false; +} hooksInfo[HOOK_COUNT]; + static HooksInjectInfo injectHooks[] = { {HOOK_TOHIT, Inject_ToHitHook, false}, {HOOK_AFTERHITROLL, Inject_AfterHitRollHook, false}, @@ -167,7 +173,7 @@ bool HookScripts::HookHasScript(int hookId) { return (hooks[hookId].empty() == false); } -void _stdcall RegisterHook(fo::Program* script, int id, int procNum) { +void RegisterHook(fo::Program* script, int id, int procNum, bool specReg) { if (id >= numHooks) return; for (std::vector::iterator it = hooks[id].begin(); it != hooks[id].end(); ++it) { if (it->prog.ptr == script) { @@ -184,7 +190,14 @@ void _stdcall RegisterHook(fo::Program* script, int id, int procNum) { hook.prog = *prog; hook.callback = procNum; hook.isGlobalScript = true; - hooks[id].push_back(hook); + + auto c_it = hooks[id].cend(); + if (specReg) { + c_it = hooks[id].cbegin(); + hooksInfo[id].hsPosition++; + } + hooks[id].insert(c_it, hook); + switch (id) { case HOOK_KEYPRESS: case HOOK_MOUSECLICK: @@ -218,6 +231,7 @@ void HookScriptClear() { for(int i = 0; i < numHooks; i++) { hooks[i].clear(); } + memset(hooksInfo, 0, HOOK_COUNT * sizeof(HooksPositionInfo)); } void LoadHookScripts() { @@ -226,6 +240,7 @@ void LoadHookScripts() { initingHookScripts = 1; for (int i = 0; i < numHooks; i++) { if (!hooks[i].empty()) { + hooksInfo[i].hasHsScript = true; InitScriptProgram(hooks[i][0].prog); // zero hook is always hs_*.int script because Hook scripts are loaded BEFORE global scripts } } @@ -234,10 +249,10 @@ void LoadHookScripts() { } // run specific event procedure for all hook scripts -void _stdcall RunHookScriptsAtProc(DWORD procId) { +void RunHookScriptsAtProc(DWORD procId) { for (int i = 0; i < numHooks; i++) { - if (!hooks[i].empty() && !hooks[i][0].isGlobalScript) { - RunScriptProc(&hooks[i][0].prog, procId); // run hs_*.int + if (hooksInfo[i].hasHsScript /*&& !hooks[i][hooksInfo[i].hsPosition].isGlobalScript*/) { + RunScriptProc(&hooks[i][hooksInfo[i].hsPosition].prog, procId); // run hs_*.int } } } diff --git a/sfall/Modules/HookScripts.h b/sfall/Modules/HookScripts.h index 9836c37d..0a81b86b 100644 --- a/sfall/Modules/HookScripts.h +++ b/sfall/Modules/HookScripts.h @@ -93,7 +93,7 @@ void _stdcall SetHSArg(DWORD id, DWORD value); void _stdcall SetHSReturn(DWORD d); // register hook by proc num (special values: -1 - use default (start) procedure, 0 - unregister) -void _stdcall RegisterHook(fo::Program* script, int id, int procNum); +void RegisterHook(fo::Program* script, int id, int procNum, bool specReg); // TODO: move void HookScriptClear(); @@ -101,6 +101,6 @@ void LoadHookScripts(); extern DWORD initingHookScripts; extern int __fastcall AmmoCostHook_Script(DWORD hookType, fo::GameObject* weapon, DWORD &rounds); -void _stdcall RunHookScriptsAtProc(DWORD procId); +void RunHookScriptsAtProc(DWORD procId); } diff --git a/sfall/Modules/ScriptExtender.cpp b/sfall/Modules/ScriptExtender.cpp index ee1b76b0..65c61786 100644 --- a/sfall/Modules/ScriptExtender.cpp +++ b/sfall/Modules/ScriptExtender.cpp @@ -617,7 +617,7 @@ static DWORD _stdcall HandleMapUpdateForScripts(const DWORD procId) { } // run all global scripts of types 0 and 3 at specific procedure (if exist) -void _stdcall RunGlobalScriptsAtProc(DWORD procId) { +void RunGlobalScriptsAtProc(DWORD procId) { for (DWORD d = 0; d < globalScripts.size(); d++) { if (globalScripts[d].mode != 0 && globalScripts[d].mode != 3) continue; RunScriptProc(&globalScripts[d].prog, procId); diff --git a/sfall/Modules/ScriptExtender.h b/sfall/Modules/ScriptExtender.h index 96bc8b0f..c491b08c 100644 --- a/sfall/Modules/ScriptExtender.h +++ b/sfall/Modules/ScriptExtender.h @@ -56,7 +56,7 @@ void __fastcall SetGlobalScriptRepeat(fo::Program* script, int frames); void __fastcall SetGlobalScriptType(fo::Program* script, int type); bool _stdcall IsGameScript(const char* filename); -void _stdcall RunGlobalScriptsAtProc(DWORD procId); +void RunGlobalScriptsAtProc(DWORD procId); bool LoadGlobals(HANDLE h); void SaveGlobals(HANDLE h); diff --git a/sfall/Modules/Scripting/Handlers/Core.cpp b/sfall/Modules/Scripting/Handlers/Core.cpp index 671e7c82..b086ab59 100644 --- a/sfall/Modules/Scripting/Handlers/Core.cpp +++ b/sfall/Modules/Scripting/Handlers/Core.cpp @@ -221,12 +221,19 @@ end: // used for both register_hook and register_hook_proc void sf_register_hook(OpcodeContext& ctx) { - int id = ctx.arg(0).asInt(); - int proc = (ctx.numArgs() > 1) - ? ctx.arg(1).asInt() - : -1; - - RegisterHook(ctx.program(), id, proc); + bool specReg = false; + int proc; + switch (ctx.opcode()) { + case 0x27d: + specReg = true; + case 0x262: + proc = ctx.arg(1).rawValue(); + if (proc < 0 || (specReg && proc == 0)) return; + break; + default: + proc = -1; + } + RegisterHook(ctx.program(), ctx.arg(0).rawValue(), proc, specReg); } void sf_sfall_ver_major(OpcodeContext& ctx) { diff --git a/sfall/Modules/Scripting/Opcodes.cpp b/sfall/Modules/Scripting/Opcodes.cpp index d9490aa7..b2da066c 100644 --- a/sfall/Modules/Scripting/Opcodes.cpp +++ b/sfall/Modules/Scripting/Opcodes.cpp @@ -210,6 +210,8 @@ static SfallOpcodeInfo opcodeInfoArray[] = { {0x27a, "sfall_func4", HandleMetarule, 5, true}, {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}}, }; // A hash-table for opcode info, indexed by opcode. From affb7c40205f1492d76370b817b121e78359bc55 Mon Sep 17 00:00:00 2001 From: NovaRain Date: Thu, 12 Sep 2019 22:59:51 +0800 Subject: [PATCH 32/34] Updated UI hotkeys example mod. --- .../example_mods/UIHotkeys/gl_uihotkeys.int | Bin 552 -> 602 bytes .../example_mods/UIHotkeys/gl_uihotkeys.ssl | 3 ++- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/artifacts/example_mods/UIHotkeys/gl_uihotkeys.int b/artifacts/example_mods/UIHotkeys/gl_uihotkeys.int index 4fab3d8a14f14a89cc1551195588d09f7ac5d91a..f00771f4a86447db8fc47ff45cadcd5482e1f19e 100644 GIT binary patch delta 190 zcmZ3%a*JicL0P8;mc}Os7#SEC7#a+rEXD>ykO-60#Cww3G9V@cQ-jd~Mg{=}h6aZQ zeXs~a18;+5gI0rP15X234P)5EABLPx4J;sO#tD;+7!kZvlXDmi6`^uWP9PNwj9?=f j7%Ur%Ax1JuP2R_-kERM@F%ts=SPx5sWrOi#HYOziJ>o3; delta 124 zcmcb`vVvv8L0N$Ymc}Os7#SEC7#a+rEXD>ykO-5&#CwvAI+K+c4LJoGSU^IIX_I4+ vcnc;^L-3dcCSPC_V`BU;`30lCBvb>F07x4HQ-jd~Mg}GZh6abp7EDS2?^GSy diff --git a/artifacts/example_mods/UIHotkeys/gl_uihotkeys.ssl b/artifacts/example_mods/UIHotkeys/gl_uihotkeys.ssl index 0288b17b..60336c17 100644 --- a/artifacts/example_mods/UIHotkeys/gl_uihotkeys.ssl +++ b/artifacts/example_mods/UIHotkeys/gl_uihotkeys.ssl @@ -32,6 +32,7 @@ procedure start begin if (event) then begin mode := get_game_mode; + if (mode bwand COUNTERWIN) then return; // new mode from sfall 4.2/3.8.20 if (keyDX == DIK_I and (mode bwand INVENTORY)) then begin tap_key(DIK_ESCAPE); end else if (keyDX == DIK_S and (mode bwand SKILLDEX)) then begin @@ -43,7 +44,7 @@ procedure start begin tap_key(DIK_CAPITAL); tap_key(DIK_A); tap_key(DIK_CAPITAL); - end else if (mode bwand BARTER) then begin + end else if (mode == (BARTER + DIALOG)) then begin tap_key(DIK_M); end end From ffe9d4c8cf43c0e2cdb753a818638300a872d0c9 Mon Sep 17 00:00:00 2001 From: NovaRain Date: Sat, 14 Sep 2019 08:50:54 +0800 Subject: [PATCH 33/34] Removed duplicate code from intface_redraw_ engine function Minor edits to code/documents. --- .../example_mods/UIHotkeys/gl_uihotkeys.int | Bin 602 -> 594 bytes .../example_mods/UIHotkeys/gl_uihotkeys.ssl | 2 +- artifacts/scripting/hookscripts.txt | 2 +- sfall/InputFuncs.cpp | 32 +++++++++--------- sfall/Modules/BugFixes.cpp | 3 ++ sfall/Modules/ExtraSaveSlots.cpp | 2 +- 6 files changed, 22 insertions(+), 19 deletions(-) diff --git a/artifacts/example_mods/UIHotkeys/gl_uihotkeys.int b/artifacts/example_mods/UIHotkeys/gl_uihotkeys.int index f00771f4a86447db8fc47ff45cadcd5482e1f19e..492d08a23057c4aad666e6c78105d7f9d8b3dd9b 100644 GIT binary patch delta 95 zcmcb`a*1WaL0PK?mc}Os7#SEC7#a+rEXD>ykO-62#CxTTR+B>*Eg7vQ_c2;>S~ajh fBqm>AbeDwlKw=C`4MqnT8JHLt8XP8DFew25F{T)e delta 103 zcmcb_a*JicL0P8;mc}Os7#SEC7#a+rEXD>ykO-60#CxTTPLo3zEg79A_c2;>IyJCB jBqm>AbXSD)Kw=C`4MqnT8JHLtz&w@)%Le1g224r-2aFm% diff --git a/artifacts/example_mods/UIHotkeys/gl_uihotkeys.ssl b/artifacts/example_mods/UIHotkeys/gl_uihotkeys.ssl index 60336c17..8600570f 100644 --- a/artifacts/example_mods/UIHotkeys/gl_uihotkeys.ssl +++ b/artifacts/example_mods/UIHotkeys/gl_uihotkeys.ssl @@ -44,7 +44,7 @@ procedure start begin tap_key(DIK_CAPITAL); tap_key(DIK_A); tap_key(DIK_CAPITAL); - end else if (mode == (BARTER + DIALOG)) then begin + end else if (mode bwand BARTER) then begin tap_key(DIK_M); end end diff --git a/artifacts/scripting/hookscripts.txt b/artifacts/scripting/hookscripts.txt index 551a816e..c932355b 100644 --- a/artifacts/scripting/hookscripts.txt +++ b/artifacts/scripting/hookscripts.txt @@ -47,7 +47,7 @@ Used from a normal global script if you want to run it at the same point a full 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) -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. +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. Example: let's say we have a Mod A which reduces all "to hit" chances by 50%. The code might look like this: diff --git a/sfall/InputFuncs.cpp b/sfall/InputFuncs.cpp index f2087fd8..1774ef23 100644 --- a/sfall/InputFuncs.cpp +++ b/sfall/InputFuncs.cpp @@ -268,29 +268,29 @@ public: return 0; } - // Only called for the keyboard - HRESULT _stdcall GetDeviceData(DWORD a, DIDEVICEOBJECTDATA* b, DWORD* c, DWORD d) { + // Only called for the keyboard (dxinput_read_keyboard_buffer_ called at 0x4E06AB) + HRESULT _stdcall GetDeviceData(DWORD a, DIDEVICEOBJECTDATA* buf, DWORD* count, DWORD d) { // buf - DirectInputKeyboardBuffer (0x6B2560) if (DeviceType != kDeviceType_KEYBOARD) { - return RealDevice->GetDeviceData(a, b, c, d); + return RealDevice->GetDeviceData(a, buf, count, d); } onInputLoop.invoke(); - if (!b || bufferedPresses.empty() || (d & DIGDD_PEEK)) { - HRESULT hr = RealDevice->GetDeviceData(a, b, c, d); - if (FAILED(hr) || !b || !(*c)) return hr; - for (DWORD i = 0; i < *c; i++) { - DWORD dxKey = b[i].dwOfs; - DWORD state = b[i].dwData & 0x80; + if (!buf || bufferedPresses.empty() || (d & DIGDD_PEEK)) { + HRESULT hr = RealDevice->GetDeviceData(a, buf, count, d); + if (FAILED(hr) || !buf || !(*count)) return hr; + for (DWORD i = 0; i < *count; i++) { + DWORD dxKey = buf[i].dwOfs; + DWORD state = buf[i].dwData & 0x80; DWORD oldState = keysDown[dxKey]; keysDown[dxKey] = state; HookScripts::KeyPressHook(&dxKey, (state > 0), MapVirtualKeyEx(dxKey, MAPVK_VSC_TO_VK, keyboardLayout)); - if (dxKey > 0 && dxKey != b[i].dwOfs) { - keysDown[b[i].dwOfs] = oldState; - b[i].dwOfs = dxKey; // Override key - keysDown[b[i].dwOfs] = state; + if (dxKey > 0 && dxKey != buf[i].dwOfs) { + keysDown[buf[i].dwOfs] = oldState; + buf[i].dwOfs = dxKey; // Override key + keysDown[buf[i].dwOfs] = state; } - onKeyPressed.invoke(b[i].dwOfs, (state > 0)); + onKeyPressed.invoke(buf[i].dwOfs, (state > 0)); } return hr; } @@ -298,9 +298,9 @@ public: //TODO: Fallouts behaviour when passing multiple keypresses makes it appear like it's expecting the DIDEVICEOBJECTDATA struct to be // something other than 16 bytes. afaik, fallout uses DX3 for input, which is before the appData field was added, but it could // be worth checking anyway. - *b = bufferedPresses.front(); + *buf = bufferedPresses.front(); bufferedPresses.pop(); - *c = 1; + *count = 1; return DI_OK; } diff --git a/sfall/Modules/BugFixes.cpp b/sfall/Modules/BugFixes.cpp index 0f4bf061..70d5c3b0 100644 --- a/sfall/Modules/BugFixes.cpp +++ b/sfall/Modules/BugFixes.cpp @@ -2892,6 +2892,9 @@ void BugFixes::init() // Place the player on a nearby empty tile if the entrance tile is blocked by another object when entering a map HookCall(0x4836F8, map_check_state_hook); + + // Remove duplicate code from intface_redraw_ engine function + BlockCall(0x45EBBF); } } diff --git a/sfall/Modules/ExtraSaveSlots.cpp b/sfall/Modules/ExtraSaveSlots.cpp index 3a648842..068738fa 100644 --- a/sfall/Modules/ExtraSaveSlots.cpp +++ b/sfall/Modules/ExtraSaveSlots.cpp @@ -423,7 +423,7 @@ static long quickSavePage = 0; static FILETIME ftPrevSlot; static DWORD __stdcall QuickSaveGame(fo::DbFile* file, char* filename) { - int currSlot = fo::var::slot_cursor; + long currSlot = fo::var::slot_cursor; if (file) { // This slot is not empty fo::func::db_fclose(file); From 91ac51d3ff6db236c7397f06ebb3462ac121cf42 Mon Sep 17 00:00:00 2001 From: NovaRain Date: Sun, 15 Sep 2019 07:16:38 +0800 Subject: [PATCH 34/34] Changed CorpseLineOfFireFix to be enabled by default --- artifacts/ddraw.ini | 2 +- sfall/Modules/BugFixes.cpp | 6 +++--- sfall/Modules/MiscPatches.cpp | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/artifacts/ddraw.ini b/artifacts/ddraw.ini index 0719c356..ca0b6827 100644 --- a/artifacts/ddraw.ini +++ b/artifacts/ddraw.ini @@ -385,7 +385,7 @@ AlwaysReloadMsgs=0 PlayIdleAnimOnReload=0 ;Set to 1 to prevent corpses from blocking line of fire -CorpseLineOfFireFix=0 +CorpseLineOfFireFix=1 ;Changes the timer (in days) for deleting corpses on a map after you leave (valid range: 0..13) ;The corpses of critters with 'Ages' flag or on maps with 'dead_bodies_age=No' set in maps.txt will not disappear diff --git a/sfall/Modules/BugFixes.cpp b/sfall/Modules/BugFixes.cpp index 70d5c3b0..2fd96a3c 100644 --- a/sfall/Modules/BugFixes.cpp +++ b/sfall/Modules/BugFixes.cpp @@ -817,7 +817,7 @@ static void __declspec(naked) op_wield_obj_critter_adjust_ac_hook() { } } -// Haenlomal +// Haenlomal: Check path to critter for attack static void __declspec(naked) MultiHexFix() { __asm { xor ecx, ecx; // argument value for make_path_func: ecx=0 (rotation data arg) @@ -2524,8 +2524,8 @@ void BugFixes::init() //} // Fix for multiple knockout events being added to the queue HookCall(0x424F9A, set_new_results_hack); - // Fix for knocked down critters not playing stand up animation when the combat ends - // and prevent dead NPCs from reloading their weapons + // Fix for knocked down critters not playing stand up animation when the combat ends (when DAM_LOSE_TURN and DAM_KNOCKED_DOWN + // flags are set) and prevent dead NPCs from reloading their weapons HookCall(0x421F30, combat_over_hook); dlog("Applying fix for explosives bugs.", DL_INIT); diff --git a/sfall/Modules/MiscPatches.cpp b/sfall/Modules/MiscPatches.cpp index 80c0a54a..b5535f77 100644 --- a/sfall/Modules/MiscPatches.cpp +++ b/sfall/Modules/MiscPatches.cpp @@ -504,7 +504,7 @@ void PlayIdleAnimOnReloadPatch() { } void CorpseLineOfFireFix() { - if (GetConfigInt("Misc", "CorpseLineOfFireFix", 0)) { + if (GetConfigInt("Misc", "CorpseLineOfFireFix", 1)) { dlog("Applying corpse line of fire patch.", DL_INIT); MakeJump(0x48B994, CorpseHitFix2); MakeJump(0x48BA04, CorpseHitFix2b);