diff --git a/artifacts/ddraw.ini b/artifacts/ddraw.ini
index bf0d919e..8da90558 100644
--- a/artifacts/ddraw.ini
+++ b/artifacts/ddraw.ini
@@ -710,7 +710,7 @@ SkipSizeCheck=0
;Does not require sfall debugging mode
;ExtraCRC=0x00000000,0x00000000
-;Set to 1 to stop Fallout from deleting non readonly protos at startup
+;Set to 1 to stop Fallout from deleting non read-only protos at startup
;Has pretty nasty side effects when saving/reloading, so don't use for regular gameplay
DontDeleteProtos=0
diff --git a/sfall/LoadGameHook.cpp b/sfall/LoadGameHook.cpp
index efe32808..cd51f0a7 100644
--- a/sfall/LoadGameHook.cpp
+++ b/sfall/LoadGameHook.cpp
@@ -44,6 +44,7 @@
#include "SuperSave.h"
#include "TalkingHeads.h"
#include "version.h"
+#include "Worldmap.h"
#define MAX_GLOBAL_SIZE (MaxGlobalVars * 12 + 4)
diff --git a/sfall/ScriptOps/WorldmapOps.hpp b/sfall/ScriptOps/WorldmapOps.hpp
index 3308f7af..a4a2dc3a 100644
--- a/sfall/ScriptOps/WorldmapOps.hpp
+++ b/sfall/ScriptOps/WorldmapOps.hpp
@@ -21,7 +21,7 @@
#include "main.h"
#include "Arrays.h"
#include "ScriptExtender.h"
-
+#include "Worldmap.h"
static DWORD EncounteredHorrigan;
static DWORD ForceEnconterMapID;
@@ -185,7 +185,6 @@ end:
}
}
-void _stdcall SetMapMulti(float d);
static void __declspec(naked) set_map_time_multi() {
__asm {
push ebx;
diff --git a/sfall/Worldmap.cpp b/sfall/Worldmap.cpp
new file mode 100644
index 00000000..703d9cbf
--- /dev/null
+++ b/sfall/Worldmap.cpp
@@ -0,0 +1,520 @@
+/*
+ * sfall
+ * Copyright (C) 2008-2019 The sfall team
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include
+
+#include "main.h"
+
+#include "Define.h"
+#include "FalloutEngine.h"
+#include "LoadGameHook.h"
+#include "ScriptExtender.h"
+#include "SpeedPatch.h"
+
+static DWORD AutomapPipboyList[AUTOMAP_MAX];
+
+static DWORD ViewportX;
+static DWORD ViewportY;
+
+static int mapSlotsScrollMax = 27 * (17 - 7);
+
+static DWORD worldMapDelay;
+static DWORD worldMapTicks;
+
+static DWORD WorldMapEncounterRate;
+
+static double tickFract = 0.0;
+static double mapMultiMod = 1.0;
+static float scriptMapMulti = 1.0;
+
+static bool addYear = false; // used as additional years indicator
+static DWORD addedYears = 0;
+
+static const DWORD ScrollCityListAddr[] = {
+ 0x4C04B9, 0x4C04C8, 0x4C4A34, 0x4C4A3D,
+};
+
+static __declspec(naked) void TimeDateFix() {
+ __asm {
+ test edi, edi; // year buf
+ jz end;
+ add esi, addedYears;
+ mov [edi], esi;
+end:
+ retn;
+ }
+}
+
+static void TimerReset() {
+ __asm push ecx;
+
+ const DWORD time = ONE_GAME_YEAR * 13;
+ *ptr_fallout_game_time -= time;
+ addedYears += 13;
+
+ // fix queue time
+ Queue* queue = (Queue*)(*ptr_queue);
+ while (queue) {
+ if (queue->time > time) {
+ queue->time -= time;
+ } else {
+ queue->time = 0;
+ }
+ queue = queue->next;
+ }
+ __asm pop ecx;
+}
+
+static __declspec(naked) void script_chk_timed_events_hack() {
+ __asm {
+ mov eax, dword ptr ds:[_fallout_game_time];
+ inc eax;
+ mov dword ptr ds:[_fallout_game_time], eax;
+ cmp eax, ONE_GAME_YEAR * 13;
+ jae reset;
+ retn;
+reset:
+ jmp TimerReset;
+ }
+}
+
+static __declspec(naked) void set_game_time_hack() {
+ __asm {
+ mov dword ptr ds:[_fallout_game_time], eax;
+ mov edx, eax;
+ call IsMapLoaded;
+ test al, al;
+ jz end;
+ cmp edx, ONE_GAME_YEAR * 13;
+ jb end;
+ call TimerReset;
+end:
+ xor edx, edx;
+ retn;
+ }
+}
+
+static __declspec(naked) void ScrollCityListFix() {
+ __asm {
+ push ebx;
+ mov ebx, ds:[0x672F10];
+ test eax, eax;
+ jl up;
+ cmp ebx, mapSlotsScrollMax;
+ pop ebx;
+ jne run;
+ retn;
+up:
+ test ebx, ebx;
+ pop ebx;
+ jnz run;
+ retn;
+run:
+ jmp wmInterfaceScrollTabsStart_;
+ }
+}
+
+static void __declspec(naked) WorldMapFpsPatch() {
+ __asm {
+ push dword ptr ds:[_last_buttons];
+ push dword ptr ds:[0x6AC7B0]; // _mouse_buttons
+ mov esi, worldMapTicks;
+ mov ebx, esi; // previous ticks
+loopDelay:
+ call RunGlobalScripts3;
+ call process_bk_;
+subLoop:
+ call ds:[sf_GetTickCount]; // current ticks
+ mov edx, eax;
+ sub eax, ebx; // get elapsed time (cur.ticks - prev.ticks)
+ cmp eax, 10; // delay - GetTickCount returns minimum difference of 15 units
+ jb subLoop; // elapsed < invoke delay
+ mov ebx, edx;
+ sub edx, esi; // get elapsed time (cur.ticks - worldMapTicks)
+ cmp edx, worldMapDelay;
+ jb loopDelay; // elapsed < worldMapDelay
+
+ pop dword ptr ds:[0x6AC7B0]; // _mouse_buttons
+ pop dword ptr ds:[_last_buttons];
+ call ds:[sf_GetTickCount];
+ mov worldMapTicks, eax;
+ jmp get_input_;
+ }
+}
+
+static void __declspec(naked) WorldMapFpsPatch2() {
+ __asm {
+ mov esi, worldMapTicks;
+ mov ebx, esi;
+loopDelay:
+ call RunGlobalScripts3;
+subLoop:
+ call ds:[sf_GetTickCount]; // current ticks
+ mov edx, eax;
+ sub eax, ebx; // get elapsed time
+ jz subLoop;
+ mov ebx, edx;
+ sub edx, esi; // get elapsed time
+ cmp edx, worldMapDelay;
+ jb loopDelay; // elapsed < worldMapDelay
+
+ call ds:[sf_GetTickCount];
+ mov worldMapTicks, eax;
+ jmp get_input_;
+ }
+}
+
+// Only used if the world map speed patch is disabled, so that world map scripts are still run
+static void __declspec(naked) wmWorldMap_hook() {
+ __asm {
+ pushadc;
+ call RunGlobalScripts3;
+ popadc;
+ jmp get_input_;
+ }
+}
+
+static void __declspec(naked) wmWorldMapFunc_hook() {
+ __asm {
+ inc dword ptr ds:[_wmLastRndTime];
+ jmp wmPartyWalkingStep_;
+ }
+}
+
+static void __declspec(naked) wmRndEncounterOccurred_hack() {
+ __asm {
+ xor ecx, ecx;
+ cmp edx, WorldMapEncounterRate;
+ retn;
+ }
+}
+
+static void __declspec(naked) ViewportHook() {
+ __asm {
+ call wmWorldMapLoadTempData_;
+ mov eax, ViewportX;
+ mov ds:[_wmWorldOffsetX], eax;
+ mov eax, ViewportY;
+ mov ds:[_wmWorldOffsetY], eax;
+ retn;
+ }
+}
+
+static void __declspec(naked) wmTownMapFunc_hack() {
+ __asm {
+ cmp dword ptr [edi][eax * 4 + 0], 0; // Visited
+ je end;
+ cmp dword ptr [edi][eax * 4 + 4], -1; // Xpos
+ je end;
+ cmp dword ptr [edi][eax * 4 + 8], -1; // Ypos
+ je end;
+ // engine code
+ mov edx, [edi][eax * 4 + 0xC];
+ mov [esi], edx
+ retn;
+end:
+ add esp, 4; // destroy the return address
+ mov eax, 0x4C4976;
+ jmp eax;
+ }
+}
+
+static DWORD _stdcall PathfinderCalc(DWORD perkLevel, DWORD ticks) {
+ double multi = mapMultiMod * scriptMapMulti;
+
+ switch (perkLevel) {
+ case 1:
+ multi *= 0.75;
+ break;
+ case 2:
+ multi *= 0.5;
+ break;
+ case 3:
+ multi *= 0.25;
+ break;
+ }
+ multi = ((double)ticks) * multi + tickFract;
+ tickFract = modf(multi, &multi);
+
+ return static_cast(multi);
+}
+
+static __declspec(naked) void PathfinderFix() {
+ __asm {
+ push eax; // ticks
+ mov eax, ds:[_obj_dude];
+ mov edx, PERK_pathfinder;
+ call perk_level_;
+ push eax;
+ call PathfinderCalc;
+ jmp inc_game_time_;
+ }
+}
+
+static void __declspec(naked) wmInterfaceInit_text_font_hook() {
+ __asm {
+ mov eax, 0x65; // normal text font
+ jmp text_font_;
+ }
+}
+
+static const char* automap = "automap"; // no/yes overrides the value in the table to display the automap in pipboy
+static void __declspec(naked) wmMapInit_hack() {
+ __asm {
+ mov esi, [esp + 0xA0 - 0x20 + 4]; // curent map number
+ cmp esi, AUTOMAP_MAX;
+ jge end;
+ lea eax, [esp + 4]; // file
+ lea edx, [esp + 0xA0 - 0x50 + 4]; // section
+ mov ebx, automap; // key
+ lea ecx, [esp + 0xA0 - 0x24 + 4]; // value buf
+ call config_get_string_;
+ test eax, eax;
+ jz end;
+ mov ecx, 2; // max index
+ mov ebx, _wmYesNoStrs;
+ lea eax, [esp + 0xA0 - 0x24 + 4]; // key value
+ sub esp, 4;
+ mov edx, esp; // index buf
+ call strParseStrFromList_;
+ cmp eax, -1;
+ jz skip;
+ mov edx, [esp]; // value index
+ dec edx;
+ mov [AutomapPipboyList][esi * 4], edx; // no = -1, yes = 0
+skip:
+ add esp, 4;
+end:
+ inc esi;
+ mov [esp + 0xA0 - 0x20 + 4], esi;
+ retn;
+ }
+}
+
+void WorldLimitsPatches() {
+ DWORD data = GetPrivateProfileIntA("Misc", "LocalMapXLimit", 0, ini);
+ if (data) {
+ dlog("Applying local map x limit patch.", DL_INIT);
+ SafeWrite32(0x4B13B9, data);
+ dlogr(" Done", DL_INIT);
+ }
+ data = GetPrivateProfileIntA("Misc", "LocalMapYLimit", 0, ini);
+ if (data) {
+ dlog("Applying local map y limit patch.", DL_INIT);
+ SafeWrite32(0x4B13C7, data);
+ dlogr(" Done", DL_INIT);
+ }
+
+ //if (GetPrivateProfileIntA("Misc", "WorldMapCitiesListFix", 0, ini)) {
+ dlog("Applying world map cities list patch.", DL_INIT);
+ for (int i = 0; i < sizeof(ScrollCityListAddr) / 4; i++) {
+ HookCall(ScrollCityListAddr[i], ScrollCityListFix);
+ }
+ dlogr(" Done", DL_INIT);
+ //}
+
+ //if (GetPrivateProfileIntA("Misc", "CitiesLimitFix", 0, ini)) {
+ dlog("Applying cities limit patch.", DL_INIT);
+ if (*((BYTE*)0x4BF3BB) != 0xEB) {
+ SafeWrite8(0x4BF3BB, 0xEB);
+ }
+ dlogr(" Done", DL_INIT);
+ //}
+
+ DWORD wmSlots = GetPrivateProfileIntA("Misc", "WorldMapSlots", 0, ini);
+ if (wmSlots && wmSlots < 128) {
+ dlog("Applying world map slots patch.", DL_INIT);
+ if (wmSlots < 7) wmSlots = 7;
+ mapSlotsScrollMax = (wmSlots - 7) * 27;
+ if (wmSlots < 25) {
+ SafeWrite32(0x4C21FD, 230 - (wmSlots - 17) * 27);
+ } else {
+ SafeWrite8(0x4C21FC, 0xC2); // sub > add
+ SafeWrite32(0x4C21FD, 2 + 27 * (wmSlots - 26));
+ }
+ dlogr(" Done", DL_INIT);
+ }
+}
+
+void TimeLimitPatch() {
+ int limit = GetPrivateProfileIntA("Misc", "TimeLimit", 13, ini);
+ if (limit == -2 || limit == -3) {
+ addYear = true;
+ MakeCall(0x4A33B8, TimeDateFix, 1); // game_time_date_
+ limit = -1; // also reset time
+ }
+ if (limit >= -1 && limit < 13) {
+ dlog("Applying time limit patch.", DL_INIT);
+ if (limit == -1) {
+ MakeCall(0x4A3DF5, script_chk_timed_events_hack, 1);
+ MakeCall(0x4A3488, set_game_time_hack);
+ MakeCall(0x4A34EF, TimerReset); // inc_game_time_
+ MakeCall(0x4A3547, TimerReset); // inc_game_time_in_seconds_
+ SafeMemSet(0x4A34F4, 0x90, 16);
+ SafeMemSet(0x4A354C, 0x90, 16);
+ } else {
+ SafeWrite8(0x4A34EC, limit);
+ SafeWrite8(0x4A3544, limit);
+ }
+ dlogr(" Done", DL_INIT);
+ }
+}
+
+void TownMapsHotkeyFix() {
+ if (GetPrivateProfileIntA("Misc", "TownMapHotkeysFix", 1, ini)) {
+ dlog("Applying town map hotkeys patch.", DL_INIT);
+ MakeCall(0x4C495A, wmTownMapFunc_hack, 1);
+ dlogr(" Done", DL_INIT);
+ }
+}
+
+void WorldmapFpsPatch() {
+ bool fpsPatchOK = (*(DWORD*)0x4BFE5E == 0x8D16);
+ if (GetPrivateProfileIntA("Misc", "WorldMapFPSPatch", 0, ini)) {
+ dlog("Applying world map fps patch.", DL_INIT);
+ if (!fpsPatchOK) {
+ dlogr(" Failed", DL_INIT);
+ } else {
+ int delay = GetPrivateProfileIntA("Misc", "WorldMapDelay2", 66, ini);
+ worldMapDelay = max(1, delay);
+ dlogr(" Done", DL_INIT);
+ }
+ }
+ if (fpsPatchOK) {
+ void* func;
+ if (worldMapDelay == 0) {
+ func = wmWorldMap_hook;
+ } else if (worldMapDelay > 25) {
+ func = WorldMapFpsPatch;
+ } else {
+ func = WorldMapFpsPatch2;
+ }
+ HookCall(0x4BFE5D, func);
+ AvailableGlobalScriptTypes |= 2;
+ }
+
+ if (GetPrivateProfileIntA("Misc", "WorldMapEncounterFix", 0, ini)) {
+ dlog("Applying world map encounter patch.", DL_INIT);
+ WorldMapEncounterRate = GetPrivateProfileIntA("Misc", "WorldMapEncounterRate", 5, ini);
+ SafeWrite32(0x4C232D, 0x01EBC031); // xor eax, eax; jmps 0x4C2332 (wmInterfaceInit_)
+ HookCall(0x4BFEE0, wmWorldMapFunc_hook);
+ MakeCall(0x4C0667, wmRndEncounterOccurred_hack);
+ dlogr(" Done", DL_INIT);
+ }
+}
+
+void PathfinderFixInit() {
+ //if (GetPrivateProfileIntA("Misc", "PathfinderFix", 0, ini)) {
+ dlog("Applying Pathfinder patch.", DL_INIT);
+ SafeWrite16(0x4C1FF6, 0x9090); // wmPartyWalkingStep_
+ HookCall(0x4C1C78, PathfinderFix); // wmGameTimeIncrement_
+ mapMultiMod = (double)GetPrivateProfileIntA("Misc", "WorldMapTimeMod", 100, ini) / 100.0;
+ dlogr(" Done", DL_INIT);
+ //}
+}
+
+void StartingStatePatches() {
+ int date = GetPrivateProfileIntA("Misc", "StartYear", -1, ini);
+ if (date >= 0) {
+ dlog("Applying starting year patch.", DL_INIT);
+ SafeWrite32(0x4A336C, date);
+ dlogr(" Done", DL_INIT);
+ }
+ date = GetPrivateProfileIntA("Misc", "StartMonth", -1, ini);
+ if (date >= 0 && date < 12) {
+ dlog("Applying starting month patch.", DL_INIT);
+ SafeWrite32(0x4A3382, date);
+ dlogr(" Done", DL_INIT);
+ }
+ date = GetPrivateProfileIntA("Misc", "StartDay", -1, ini);
+ if (date >= 0 && date < 31) {
+ dlog("Applying starting day patch.", DL_INIT);
+ SafeWrite8(0x4A3356, date);
+ dlogr(" Done", DL_INIT);
+ }
+
+ date = GetPrivateProfileIntA("Misc", "StartXPos", -1, ini);
+ if (date != -1) {
+ dlog("Applying starting x position patch.", DL_INIT);
+ SafeWrite32(0x4BC990, date);
+ SafeWrite32(0x4BCC08, date);
+ dlogr(" Done", DL_INIT);
+ }
+ date = GetPrivateProfileIntA("Misc", "StartYPos", -1, ini);
+ if (date != -1) {
+ dlog("Applying starting y position patch.", DL_INIT);
+ SafeWrite32(0x4BC995, date);
+ SafeWrite32(0x4BCC0D, date);
+ dlogr(" Done", DL_INIT);
+ }
+
+ ViewportX = GetPrivateProfileIntA("Misc", "ViewXPos", -1, ini);
+ if (ViewportX != -1) {
+ dlog("Applying starting x view patch.", DL_INIT);
+ SafeWrite32(_wmWorldOffsetX, ViewportX);
+ dlogr(" Done", DL_INIT);
+ }
+ ViewportY = GetPrivateProfileIntA("Misc", "ViewYPos", -1, ini);
+ if (ViewportY != -1) {
+ dlog("Applying starting y view patch.", DL_INIT);
+ SafeWrite32(_wmWorldOffsetY, ViewportY);
+ dlogr(" Done", DL_INIT);
+ }
+ if (ViewportX != -1 || ViewportY != -1) HookCall(0x4BCF07, ViewportHook); // game_reset_
+}
+
+void WorldMapFontPatch() {
+ if (GetPrivateProfileIntA("Misc", "WorldMapFontPatch", 0, ini)) {
+ dlog("Applying world map font patch.", DL_INIT);
+ HookCall(0x4C2343, wmInterfaceInit_text_font_hook);
+ dlogr(" Done", DL_INIT);
+ }
+}
+
+void PipBoyAutomapsPatch() {
+ //if (GetPrivateProfileIntA("Misc", "PipBoyAutomaps", 0, ini)) {
+ dlog("Applying Pip-Boy automaps patch.", DL_INIT);
+ MakeCall(0x4BF931, wmMapInit_hack, 2);
+ SafeWrite32(0x41B8B7, (DWORD)AutomapPipboyList);
+ memcpy(AutomapPipboyList, (void*)_displayMapList, sizeof(AutomapPipboyList)); // copy vanilla data
+ dlogr(" Done", DL_INIT);
+ //}
+}
+
+void _stdcall SetMapMulti(float value) {
+ scriptMapMulti = value;
+}
+
+void SetAddedYears(DWORD years) {
+ addedYears = years;
+}
+
+DWORD GetAddedYears(bool isCheck) {
+ return (isCheck && !addYear) ? 0 : addedYears;
+}
+
+void WorldmapInit() {
+ PathfinderFixInit();
+ StartingStatePatches();
+ TimeLimitPatch();
+ TownMapsHotkeyFix();
+ WorldLimitsPatches();
+ WorldmapFpsPatch();
+ WorldMapFontPatch();
+ PipBoyAutomapsPatch();
+}
diff --git a/sfall/Worldmap.h b/sfall/Worldmap.h
new file mode 100644
index 00000000..8409c02b
--- /dev/null
+++ b/sfall/Worldmap.h
@@ -0,0 +1,26 @@
+/*
+ * sfall
+ * Copyright (C) 2008-2019 The sfall team
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#pragma once
+
+void WorldmapInit();
+
+DWORD GetAddedYears(bool isCheck = true);
+void SetAddedYears(DWORD years);
+
+void _stdcall SetMapMulti(float value);
diff --git a/sfall/ddraw.vcxproj b/sfall/ddraw.vcxproj
index bfa93efa..938cb981 100644
--- a/sfall/ddraw.vcxproj
+++ b/sfall/ddraw.vcxproj
@@ -327,6 +327,7 @@
+
@@ -380,6 +381,7 @@
+
diff --git a/sfall/ddraw.vcxproj.filters b/sfall/ddraw.vcxproj.filters
index 6500632f..bacf96ea 100644
--- a/sfall/ddraw.vcxproj.filters
+++ b/sfall/ddraw.vcxproj.filters
@@ -196,6 +196,9 @@
Headers
+
+ Headers
+
Headers
@@ -337,6 +340,9 @@
Source
+
+ Source
+
Source
diff --git a/sfall/main.cpp b/sfall/main.cpp
index cdd3a62a..89402049 100644
--- a/sfall/main.cpp
+++ b/sfall/main.cpp
@@ -62,6 +62,7 @@
#include "TalkingHeads.h"
#include "Tiles.h"
#include "version.h"
+#include "Worldmap.h"
ddrawDll ddraw;
@@ -87,15 +88,6 @@ static const char* musicOverridePath = "data\\sound\\music\\";
static int* scriptDialog = nullptr;
-static DWORD AutomapPipboyList[AUTOMAP_MAX];
-
-static DWORD ViewportX;
-static DWORD ViewportY;
-
-static const DWORD ScrollCityListAddr[] = {
- 0x4C04B9, 0x4C04C8, 0x4C4A34, 0x4C4A3D,
-};
-
static const DWORD FastShotFixF1[] = {
0x478BB8, 0x478BC7, 0x478BD6, 0x478BEA, 0x478BF9, 0x478C08, 0x478C2F,
};
@@ -121,214 +113,6 @@ static const DWORD WalkDistanceAddr[] = {
0x411FF0, 0x4121C4, 0x412475, 0x412906,
};
-static bool addYear = false; // used as additional years indicator
-static DWORD addedYears = 0;
-void SetAddedYears(DWORD years) {
- addedYears = years;
-}
-
-DWORD GetAddedYears(bool isCheck) {
- return (isCheck && !addYear) ? 0 : addedYears;
-}
-
-static __declspec(naked) void TimeDateFix() {
- __asm {
- test edi, edi; // year buf
- jz end;
- add esi, addedYears;
- mov [edi], esi;
-end:
- retn;
- }
-}
-
-static void TimerReset() {
- __asm push ecx;
-
- const DWORD time = ONE_GAME_YEAR * 13;
- *ptr_fallout_game_time -= time;
- addedYears += 13;
-
- // fix queue time
- Queue* queue = (Queue*)(*ptr_queue);
- while (queue) {
- if (queue->time > time) {
- queue->time -= time;
- } else {
- queue->time = 0;
- }
- queue = queue->next;
- }
- __asm pop ecx;
-}
-
-static __declspec(naked) void script_chk_timed_events_hack() {
- __asm {
- mov eax, dword ptr ds:[_fallout_game_time];
- inc eax;
- mov dword ptr ds:[_fallout_game_time], eax;
- cmp eax, ONE_GAME_YEAR * 13;
- jae reset;
- retn;
-reset:
- jmp TimerReset;
- }
-}
-
-static __declspec(naked) void set_game_time_hack() {
- __asm {
- mov dword ptr ds:[_fallout_game_time], eax;
- mov edx, eax;
- call IsMapLoaded;
- test al, al;
- jz end;
- cmp edx, ONE_GAME_YEAR * 13;
- jb end;
- call TimerReset;
-end:
- xor edx, edx;
- retn;
- }
-}
-
-static double tickFract = 0.0;
-static double mapMultiMod = 1.0;
-static float scriptMapMulti = 1.0;
-void _stdcall SetMapMulti(float value) {
- scriptMapMulti = value;
-}
-
-static DWORD _stdcall PathfinderCalc(DWORD perkLevel, DWORD ticks) {
- double multi = mapMultiMod * scriptMapMulti;
-
- switch (perkLevel) {
- case 1:
- multi *= 0.75;
- break;
- case 2:
- multi *= 0.5;
- break;
- case 3:
- multi *= 0.25;
- break;
- }
- multi = ((double)ticks) * multi + tickFract;
- tickFract = modf(multi, &multi);
-
- return static_cast(multi);
-}
-
-static __declspec(naked) void PathfinderFix() {
- __asm {
- push eax; // ticks
- mov eax, ds:[_obj_dude];
- mov edx, PERK_pathfinder;
- call perk_level_;
- push eax;
- call PathfinderCalc;
- jmp inc_game_time_;
- }
-}
-
-static int mapSlotsScrollMax = 27 * (17 - 7);
-static __declspec(naked) void ScrollCityListFix() {
- __asm {
- push ebx;
- mov ebx, ds:[0x672F10];
- test eax, eax;
- jl up;
- cmp ebx, mapSlotsScrollMax;
- pop ebx;
- jne run;
- retn;
-up:
- test ebx, ebx;
- pop ebx;
- jnz run;
- retn;
-run:
- jmp wmInterfaceScrollTabsStart_;
- }
-}
-
-static DWORD worldMapDelay;
-static DWORD worldMapTicks;
-static void __declspec(naked) WorldMapFpsPatch() {
- __asm {
- push dword ptr ds:[_last_buttons];
- push dword ptr ds:[0x6AC7B0]; // _mouse_buttons
- mov esi, worldMapTicks;
- mov ebx, esi; // previous ticks
-loopDelay:
- call RunGlobalScripts3;
- call process_bk_;
-subLoop:
- call ds:[sf_GetTickCount]; // current ticks
- mov edx, eax;
- sub eax, ebx; // get elapsed time (cur.ticks - prev.ticks)
- cmp eax, 10; // delay - GetTickCount returns minimum difference of 15 units
- jb subLoop; // elapsed < invoke delay
- mov ebx, edx;
- sub edx, esi; // get elapsed time (cur.ticks - worldMapTicks)
- cmp edx, worldMapDelay;
- jb loopDelay; // elapsed < worldMapDelay
-
- pop dword ptr ds:[0x6AC7B0]; // _mouse_buttons
- pop dword ptr ds:[_last_buttons];
- call ds:[sf_GetTickCount];
- mov worldMapTicks, eax;
- jmp get_input_;
- }
-}
-
-static void __declspec(naked) WorldMapFpsPatch2() {
- __asm {
- mov esi, worldMapTicks;
- mov ebx, esi;
-loopDelay:
- call RunGlobalScripts3;
-subLoop:
- call ds:[sf_GetTickCount]; // current ticks
- mov edx, eax;
- sub eax, ebx; // get elapsed time
- jz subLoop;
- mov ebx, edx;
- sub edx, esi; // get elapsed time
- cmp edx, worldMapDelay;
- jb loopDelay; // elapsed < worldMapDelay
-
- call ds:[sf_GetTickCount];
- mov worldMapTicks, eax;
- jmp get_input_;
- }
-}
-
-// Only used if the world map speed patch is disabled, so that world map scripts are still run
-static void __declspec(naked) wmWorldMap_hook() {
- __asm {
- pushadc;
- call RunGlobalScripts3;
- popadc;
- jmp get_input_;
- }
-}
-
-static DWORD WorldMapEncounterRate;
-static void __declspec(naked) wmWorldMapFunc_hook() {
- __asm {
- inc dword ptr ds:[_wmLastRndTime];
- jmp wmPartyWalkingStep_;
- }
-}
-
-static void __declspec(naked) wmRndEncounterOccurred_hack() {
- __asm {
- xor ecx, ecx;
- cmp edx, WorldMapEncounterRate;
- retn;
- }
-}
-
static void __declspec(naked) Combat_p_procFix() {
__asm {
push eax;
@@ -377,17 +161,6 @@ end_cppf:
}
}
-static void __declspec(naked) ViewportHook() {
- __asm {
- call wmWorldMapLoadTempData_;
- mov eax, ViewportX;
- mov ds:[_wmWorldOffsetX], eax;
- mov eax, ViewportY;
- mov ds:[_wmWorldOffsetY], eax;
- retn;
- }
-}
-
static void __declspec(naked) WeaponAnimHook() {
__asm {
cmp edx, 11;
@@ -879,65 +652,6 @@ static void __declspec(naked) op_obj_can_see_obj_hook() {
}
}
-static void __declspec(naked) wmTownMapFunc_hack() {
- __asm {
- cmp dword ptr [edi][eax * 4 + 0], 0; // Visited
- je end;
- cmp dword ptr [edi][eax * 4 + 4], -1; // Xpos
- je end;
- cmp dword ptr [edi][eax * 4 + 8], -1; // Ypos
- je end;
- // engine code
- mov edx, [edi][eax * 4 + 0xC];
- mov [esi], edx
- retn;
-end:
- add esp, 4; // destroy the return address
- mov eax, 0x4C4976;
- jmp eax;
- }
-}
-
-static void __declspec(naked) wmInterfaceInit_text_font_hook() {
- __asm {
- mov eax, 0x65; // normal text font
- jmp text_font_;
- }
-}
-
-static const char* automap = "automap"; // no/yes overrides the value in the table to display the automap in pipboy
-static void __declspec(naked) wmMapInit_hack() {
- __asm {
- mov esi, [esp + 0xA0 - 0x20 + 4]; // curent map number
- cmp esi, AUTOMAP_MAX;
- jge end;
- lea eax, [esp + 4]; // file
- lea edx, [esp + 0xA0 - 0x50 + 4]; // section
- mov ebx, automap; // key
- lea ecx, [esp + 0xA0 - 0x24 + 4]; // value buf
- call config_get_string_;
- test eax, eax;
- jz end;
- mov ecx, 2; // max index
- mov ebx, _wmYesNoStrs;
- lea eax, [esp + 0xA0 - 0x24 + 4]; // key value
- sub esp, 4;
- mov edx, esp; // index buf
- call strParseStrFromList_;
- cmp eax, -1;
- jz skip;
- mov edx, [esp]; // value index
- dec edx;
- mov [AutomapPipboyList][esi * 4], edx; // no = -1, yes = 0
-skip:
- add esp, 4;
-end:
- inc esi;
- mov [esp + 0xA0 - 0x20 + 4], esi;
- retn;
- }
-}
-
static void __declspec(naked) register_object_take_out_hack() {
__asm {
push ecx
@@ -1084,110 +798,8 @@ static void DllMain2() {
SafeWrite32(0x418B6D, (DWORD)&dfModelName);
dlogr(" Done", DL_INIT);
- int date = GetPrivateProfileInt("Misc", "StartYear", -1, ini);
- if (date >= 0) {
- dlog("Applying starting year patch.", DL_INIT);
- SafeWrite32(0x4A336C, date);
- dlogr(" Done", DL_INIT);
- }
-
- date = GetPrivateProfileInt("Misc", "StartMonth", -1, ini);
- if (date >= 0 && date < 12) {
- dlog("Applying starting month patch.", DL_INIT);
- SafeWrite32(0x4A3382, date);
- dlogr(" Done", DL_INIT);
- }
-
- date = GetPrivateProfileInt("Misc", "StartDay", -1, ini);
- if (date >= 0 && date < 31) {
- dlog("Applying starting day patch.", DL_INIT);
- SafeWrite8(0x4A3356, date);
- dlogr(" Done", DL_INIT);
- }
-
- tmp = GetPrivateProfileInt("Misc", "LocalMapXLimit", 0, ini);
- if (tmp) {
- dlog("Applying local map x limit patch.", DL_INIT);
- SafeWrite32(0x4B13B9, tmp);
- dlogr(" Done", DL_INIT);
- }
- tmp = GetPrivateProfileInt("Misc", "LocalMapYLimit", 0, ini);
- if (tmp) {
- dlog("Applying local map y limit patch.", DL_INIT);
- SafeWrite32(0x4B13C7, tmp);
- dlogr(" Done", DL_INIT);
- }
-
- date = GetPrivateProfileInt("Misc", "StartXPos", -1, ini);
- if (date != -1) {
- dlog("Applying starting x position patch.", DL_INIT);
- SafeWrite32(0x4BC990, date);
- SafeWrite32(0x4BCC08, date);
- dlogr(" Done", DL_INIT);
- }
-
- date = GetPrivateProfileInt("Misc", "StartYPos", -1, ini);
- if (date != -1) {
- dlog("Applying starting y position patch.", DL_INIT);
- SafeWrite32(0x4BC995, date);
- SafeWrite32(0x4BCC0D, date);
- dlogr(" Done", DL_INIT);
- }
-
- ViewportX = GetPrivateProfileInt("Misc", "ViewXPos", -1, ini);
- if (ViewportX != -1) {
- dlog("Applying starting x view patch.", DL_INIT);
- SafeWrite32(_wmWorldOffsetX, ViewportX);
- dlogr(" Done", DL_INIT);
- }
- ViewportY = GetPrivateProfileInt("Misc", "ViewYPos", -1, ini);
- if (ViewportY != -1) {
- dlog("Applying starting y view patch.", DL_INIT);
- SafeWrite32(_wmWorldOffsetY, ViewportY);
- dlogr(" Done", DL_INIT);
- }
- if (ViewportX != -1 || ViewportY != -1) HookCall(0x4BCF07, ViewportHook); // game_reset_
-
- //if (GetPrivateProfileIntA("Misc", "PathfinderFix", 0, ini)) {
- dlog("Applying Pathfinder patch.", DL_INIT);
- SafeWrite16(0x4C1FF6, 0x9090); // wmPartyWalkingStep_
- HookCall(0x4C1C78, PathfinderFix); // wmGameTimeIncrement_
- mapMultiMod = (double)GetPrivateProfileIntA("Misc", "WorldMapTimeMod", 100, ini) / 100.0;
- dlogr(" Done", DL_INIT);
- //}
-
- bool fpsPatchOK = (*(DWORD*)0x4BFE5E == 0x8D16);
- if (GetPrivateProfileIntA("Misc", "WorldMapFPSPatch", 0, ini)) {
- dlog("Applying world map fps patch.", DL_INIT);
- if (!fpsPatchOK) {
- dlogr(" Failed", DL_INIT);
- } else {
- int delay = GetPrivateProfileIntA("Misc", "WorldMapDelay2", 66, ini);
- worldMapDelay = max(1, delay);
- dlogr(" Done", DL_INIT);
- }
- }
- if (fpsPatchOK) {
- void* func;
- if (worldMapDelay == 0) {
- func = wmWorldMap_hook;
- } else if (worldMapDelay > 25) {
- func = WorldMapFpsPatch;
- } else {
- func = WorldMapFpsPatch2;
- }
- HookCall(0x4BFE5D, func);
- AvailableGlobalScriptTypes |= 2;
- }
-
- if (GetPrivateProfileIntA("Misc", "WorldMapEncounterFix", 0, ini)) {
- dlog("Applying world map encounter patch.", DL_INIT);
- WorldMapEncounterRate = GetPrivateProfileIntA("Misc", "WorldMapEncounterRate", 5, ini);
- SafeWrite32(0x4C232D, 0x01EBC031); // xor eax, eax; jmps 0x4C2332 (wmInterfaceInit_)
- HookCall(0x4BFEE0, wmWorldMapFunc_hook);
- MakeCall(0x4C0667, wmRndEncounterOccurred_hack);
- dlogr(" Done", DL_INIT);
- }
+ dlogr("Running WorldmapInit().", DL_INIT);
+ WorldmapInit();
if (GetPrivateProfileIntA("Misc", "DialogueFix", 1, ini)) {
dlog("Applying dialogue patch.", DL_INIT);
@@ -1228,58 +840,6 @@ static void DllMain2() {
dlogr(" Done", DL_INIT);
//}
- //if (GetPrivateProfileIntA("Misc", "WorldMapCitiesListFix", 0, ini)) {
- dlog("Applying world map cities list patch.", DL_INIT);
- for (int i = 0; i < sizeof(ScrollCityListAddr) / 4; i++) {
- HookCall(ScrollCityListAddr[i], ScrollCityListFix);
- }
- dlogr(" Done", DL_INIT);
- //}
-
- //if (GetPrivateProfileIntA("Misc", "CitiesLimitFix", 0, ini)) {
- dlog("Applying cities limit patch.", DL_INIT);
- if (*((BYTE*)0x4BF3BB) != 0xEB) {
- SafeWrite8(0x4BF3BB, 0xEB);
- }
- dlogr(" Done", DL_INIT);
- //}
-
- tmp = GetPrivateProfileIntA("Misc", "WorldMapSlots", 0, ini);
- if (tmp && tmp < 128) {
- dlog("Applying world map slots patch.", DL_INIT);
- if (tmp < 7) tmp = 7;
- mapSlotsScrollMax = (tmp - 7) * 27;
- if (tmp < 25) {
- SafeWrite32(0x4C21FD, 230 - (tmp - 17) * 27);
- } else {
- SafeWrite8(0x4C21FC, 0xC2); // sub > add
- SafeWrite32(0x4C21FD, 2 + 27 * (tmp - 26));
- }
- dlogr(" Done", DL_INIT);
- }
-
- int limit = GetPrivateProfileIntA("Misc", "TimeLimit", 13, ini);
- if (limit == -2 || limit == -3) {
- addYear = true;
- MakeCall(0x4A33B8, TimeDateFix, 1); // game_time_date_
- limit = -1; // also reset time
- }
- if (limit >= -1 && limit < 13) {
- dlog("Applying time limit patch.", DL_INIT);
- if (limit == -1) {
- MakeCall(0x4A3DF5, script_chk_timed_events_hack, 1);
- MakeCall(0x4A3488, set_game_time_hack);
- MakeCall(0x4A34EF, TimerReset); // inc_game_time_
- MakeCall(0x4A3547, TimerReset); // inc_game_time_in_seconds_
- SafeMemSet(0x4A34F4, 0x90, 16);
- SafeMemSet(0x4A354C, 0x90, 16);
- } else {
- SafeWrite8(0x4A34EC, limit);
- SafeWrite8(0x4A3544, limit);
- }
- dlogr(" Done", DL_INIT);
- }
-
dlogr("Running FileSystemInit().", DL_INIT);
FileSystemInit();
@@ -1612,26 +1172,6 @@ static void DllMain2() {
dlogr(" Done", DL_INIT);
}
- if (GetPrivateProfileIntA("Misc", "TownMapHotkeysFix", 1, ini)) {
- dlog("Applying town map hotkeys patch.", DL_INIT);
- MakeCall(0x4C495A, wmTownMapFunc_hack, 1);
- dlogr(" Done", DL_INIT);
- }
-
- if (GetPrivateProfileIntA("Misc", "WorldMapFontPatch", 0, ini)) {
- dlog("Applying world map font patch.", DL_INIT);
- HookCall(0x4C2343, wmInterfaceInit_text_font_hook);
- dlogr(" Done", DL_INIT);
- }
-
- //if (GetPrivateProfileIntA("Misc", "PipBoyAutomaps", 0, ini)) {
- dlog("Applying Pip-Boy automaps patch.", DL_INIT);
- MakeCall(0x4BF931, wmMapInit_hack, 2);
- SafeWrite32(0x41B8B7, (DWORD)AutomapPipboyList);
- memcpy(AutomapPipboyList, (void*)_displayMapList, sizeof(AutomapPipboyList)); // copy vanilla data
- dlogr(" Done", DL_INIT);
- //}
-
if (GetPrivateProfileIntA("Misc", "DontTurnOffSneakIfYouRun", 0, ini)) {
dlog("Applying DontTurnOffSneakIfYouRun patch.", DL_INIT);
SafeWrite8(0x418135, 0xEB);
diff --git a/sfall/main.h b/sfall/main.h
index 30cc3a7e..e46d888c 100644
--- a/sfall/main.h
+++ b/sfall/main.h
@@ -70,9 +70,6 @@ extern DWORD modifiedIni;
extern char dmModelName[65];
extern char dfModelName[65];
-DWORD GetAddedYears(bool isCheck = true);
-void SetAddedYears(DWORD years);
-
void RemoveSavFiles();
void ClearSavPrototypes();