Added patch for healing time on worldmap and set_world_map_heal_time

* the time interval check is tied to real time clock, instead of game time.
This commit is contained in:
NovaRain
2024-05-27 21:56:54 +08:00
parent cfcfa21afe
commit bc882680e4
10 changed files with 71 additions and 5 deletions
+1
View File
@@ -392,6 +392,7 @@
#define set_unique_id(obj) sfall_func1("set_unique_id", obj) #define set_unique_id(obj) sfall_func1("set_unique_id", obj)
#define set_unjam_locks_time(time) sfall_func1("set_unjam_locks_time", time) #define set_unjam_locks_time(time) sfall_func1("set_unjam_locks_time", time)
#define set_window_flag(winID, flag, value) sfall_func3("set_window_flag", winID, flag, value) #define set_window_flag(winID, flag, value) sfall_func3("set_window_flag", winID, flag, value)
#define set_worldmap_heal_time(time) sfall_func1("set_worldmap_heal_time", time)
#define show_win sfall_func0("show_window") #define show_win sfall_func0("show_window")
#define show_window(winName) sfall_func1("show_window", winName) #define show_window(winName) sfall_func1("show_window", winName)
#define signal_close_game sfall_func0("signal_close_game") #define signal_close_game sfall_func0("signal_close_game")
+1 -1
View File
@@ -606,7 +606,7 @@ int ret0 - pass 1 at the start of turn to skip the turn, pass -1 at the end
#### `HOOK_CARTRAVEL (hs_cartravel.int)` #### `HOOK_CARTRAVEL (hs_cartravel.int)`
Runs continuously during worldmap travel on car. Runs continuously during world map travel by car.
``` ```
int arg0 - vanilla car speed (between 3 and 8 "steps") int arg0 - vanilla car speed (between 3 and 8 "steps")
@@ -1132,6 +1132,14 @@ sfall_funcX metarule functions
- `frame`: frame number, the first frame starts from zero - `frame`: frame number, the first frame starts from zero
- `rotation`: rotation to get the frame for, useful when reading FRM files by path - `rotation`: rotation to get the frame for, useful when reading FRM files by path
----
#### set_worldmap_heal_time
`void sfall_func1("set_worldmap_heal_time", int minutes)`
- Sets the time interval in minutes for healing during world map travel
- Passing 0 will revert to 1 second in real time (vanilla engine behavior)
- Passing -1 will disable healing during travel
- The time interval will be reset each time the player reloads the game
**** ****
_See other documentation files (arrays.md, hookscripts.md) for related functions reference._ _See other documentation files (arrays.md, hookscripts.md) for related functions reference._
@@ -225,6 +225,7 @@ FUNC(dude_stand_, 0x418378)
FUNC(dude_standup_, 0x418574) FUNC(dude_standup_, 0x418574)
FUNC(editor_design_, 0x431DF8) FUNC(editor_design_, 0x431DF8)
FUNC(elapsed_time_, 0x4C93E0) FUNC(elapsed_time_, 0x4C93E0)
FUNC(elapsed_tocks_, 0x4C9400)
FUNC(elevator_end_, 0x43F6D0) FUNC(elevator_end_, 0x43F6D0)
FUNC(elevator_start_, 0x43F324) FUNC(elevator_start_, 0x43F324)
FUNC(endgame_slideshow_, 0x43F788) FUNC(endgame_slideshow_, 0x43F788)
+1
View File
@@ -123,6 +123,7 @@ void ClearLoopFlag(LoopFlag flag) {
} }
static void __stdcall GameModeChange(DWORD state) { // OnGameModeChange static void __stdcall GameModeChange(DWORD state) { // OnGameModeChange
Worldmap::OnGameModeChange();
HookCommon::GameModeChangeHook(state); HookCommon::GameModeChangeHook(state);
} }
@@ -146,6 +146,7 @@ static const SfallMetarule metarules[] = {
{"set_outline", mf_set_outline, 2, 2, -1, {ARG_OBJECT, ARG_INT}}, {"set_outline", mf_set_outline, 2, 2, -1, {ARG_OBJECT, ARG_INT}},
{"set_quest_failure_value", mf_set_quest_failure_value, 2, 2, -1, {ARG_INT, ARG_INT}}, {"set_quest_failure_value", mf_set_quest_failure_value, 2, 2, -1, {ARG_INT, ARG_INT}},
{"set_rest_heal_time", mf_set_rest_heal_time, 1, 1, -1, {ARG_INT}}, {"set_rest_heal_time", mf_set_rest_heal_time, 1, 1, -1, {ARG_INT}},
{"set_worldmap_heal_time", mf_set_worldmap_heal_time, 1, 1, -1, {ARG_INT}},
{"set_rest_mode", mf_set_rest_mode, 1, 1, -1, {ARG_INT}}, {"set_rest_mode", mf_set_rest_mode, 1, 1, -1, {ARG_INT}},
{"set_scr_name", mf_set_scr_name, 0, 1, -1, {ARG_STRING}}, {"set_scr_name", mf_set_scr_name, 0, 1, -1, {ARG_STRING}},
{"set_terrain_name", mf_set_terrain_name, 3, 3, -1, {ARG_INT, ARG_INT, ARG_STRING}}, {"set_terrain_name", mf_set_terrain_name, 3, 3, -1, {ARG_INT, ARG_INT, ARG_STRING}},
@@ -202,6 +202,10 @@ void mf_set_rest_heal_time(OpcodeContext& ctx) {
Worldmap::SetRestHealTime(ctx.arg(0).rawValue()); Worldmap::SetRestHealTime(ctx.arg(0).rawValue());
} }
void mf_set_worldmap_heal_time(OpcodeContext& ctx) {
Worldmap::SetWorldMapHealTime(ctx.arg(0).rawValue());
}
void mf_set_rest_mode(OpcodeContext& ctx) { void mf_set_rest_mode(OpcodeContext& ctx) {
Worldmap::SetRestMode(ctx.arg(0).rawValue()); Worldmap::SetRestMode(ctx.arg(0).rawValue());
} }
@@ -50,6 +50,8 @@ void mf_get_map_enter_position(OpcodeContext&);
void mf_set_rest_heal_time(OpcodeContext&); void mf_set_rest_heal_time(OpcodeContext&);
void mf_set_worldmap_heal_time(OpcodeContext&);
void mf_set_rest_mode(OpcodeContext&); void mf_set_rest_mode(OpcodeContext&);
void mf_set_rest_on_map(OpcodeContext&); void mf_set_rest_on_map(OpcodeContext&);
+49 -3
View File
@@ -57,6 +57,11 @@ static DWORD worldMapTicks;
static DWORD WorldMapEncounterRate; static DWORD WorldMapEncounterRate;
static const long WorldMapHealingDefaultInterval = 0;
// Healing interval in game time
static long worldMapHealingInterval = WorldMapHealingDefaultInterval;
static DWORD worldMapLastHealTime;
static double tickFract = 0.0; static double tickFract = 0.0;
static double mapMultiMod = 1.0; static double mapMultiMod = 1.0;
static float scriptMapMulti = 1.0f; static float scriptMapMulti = 1.0f;
@@ -448,6 +453,34 @@ static void WorldmapFpsPatch() {
} }
} }
// Original function checks how many system Ticks (not game ticks) passed since last WM loop iteration and if more than 1000 ms,
// applies one healing to the whole party according to their healing rate.
// So we hijack it and return number bigger than 1000 to force the event, or 0 to skip it.
static DWORD __fastcall wmWorldMap_HealingTimeElapsed(DWORD elapsedTocks) {
if (worldMapHealingInterval == 0) return elapsedTocks;
if (worldMapHealingInterval > 0 && (*fo::ptr::fallout_game_time - worldMapLastHealTime > (DWORD)worldMapHealingInterval)) {
worldMapLastHealTime = *fo::ptr::fallout_game_time;
return 10000; // force healing
}
return 0; // skip healing
}
static void __declspec(naked) wmWorldMap_elapsed_tocks_hook() {
__asm {
push ecx;
call fo::funcoffs::elapsed_tocks_;
mov ecx, eax;
call wmWorldMap_HealingTimeElapsed;
pop ecx;
retn;
}
}
static void WorldmapHealingRatePatch() {
dlogr("Applying world map healing rate patch.", DL_INIT);
HookCall(0x4C0085, wmWorldMap_elapsed_tocks_hook);
}
static void PathfinderFixInit() { static void PathfinderFixInit() {
//if (IniReader::GetConfigInt("Misc", "PathfinderFix", 0)) { //if (IniReader::GetConfigInt("Misc", "PathfinderFix", 0)) {
dlogr("Applying Pathfinder patch.", DL_INIT); dlogr("Applying Pathfinder patch.", DL_INIT);
@@ -572,13 +605,20 @@ void Worldmap::SetCarInterfaceArt(DWORD artIndex) {
SafeWrite32(0x4C2D9B, artIndex); SafeWrite32(0x4C2D9B, artIndex);
} }
void Worldmap::SetRestHealTime(DWORD minutes) { void Worldmap::SetRestHealTime(long minutes) {
if (minutes > 0) { if (minutes > 0) {
SafeWrite32(0x499FDE, minutes); SafeWrite32(0x499FDE, minutes);
restTime = (minutes != 180); restTime = (minutes != 180);
} }
} }
void Worldmap::SetWorldMapHealTime(long minutes) {
worldMapHealingInterval = (minutes >= 0)
? minutes * 60 * 10
: -1;
worldMapLastHealTime = *fo::ptr::fallout_game_time;
}
void Worldmap::SetRestMode(DWORD mode) { void Worldmap::SetRestMode(DWORD mode) {
RestRestore(); // restore default RestRestore(); // restore default
@@ -595,8 +635,8 @@ void Worldmap::SetRestMode(DWORD mode) {
SafeWrite32(0x42E588, 0x94); // jmp 0x42E620 SafeWrite32(0x42E588, 0x94); // jmp 0x42E620
} }
if (mode & 4) { // bit3 - disable healing during resting if (mode & 4) { // bit3 - disable healing during resting
SafeWrite16(0x499FD4, 0x9090); SafeWrite16(0x499FD4, 0x9090); // Check4Health_
SafeWrite16(0x499E93, 0x8FEB); // jmp 0x499E24 SafeWrite16(0x499E93, 0x8FEB); // TimedRest_: jmp 0x499E24
} }
} }
@@ -678,6 +718,10 @@ const char* Worldmap::GetCustomAreaTitle(long areaID) {
return (it != wmAreaHotSpotTitle.cend()) ? it->second.c_str() : nullptr; return (it != wmAreaHotSpotTitle.cend()) ? it->second.c_str() : nullptr;
} }
void Worldmap::OnGameModeChange() {
if (InWorldMap()) worldMapLastHealTime = *fo::ptr::fallout_game_time;
}
void Worldmap::OnGameReset() { void Worldmap::OnGameReset() {
SetCarInterfaceArt(433); // set index SetCarInterfaceArt(433); // set index
if (restTime) SetRestHealTime(180); if (restTime) SetRestHealTime(180);
@@ -685,6 +729,7 @@ void Worldmap::OnGameReset() {
mapRestInfo.clear(); mapRestInfo.clear();
wmTerrainTypeNames.clear(); wmTerrainTypeNames.clear();
wmAreaHotSpotTitle.clear(); wmAreaHotSpotTitle.clear();
worldMapHealingInterval = WorldMapHealingDefaultInterval;
} }
void Worldmap::init() { void Worldmap::init() {
@@ -694,6 +739,7 @@ void Worldmap::init() {
MapLimitsPatches(); MapLimitsPatches();
WorldmapFpsPatch(); WorldmapFpsPatch();
PipBoyAutomapsPatch(); PipBoyAutomapsPatch();
WorldmapHealingRatePatch();
// Add a flashing icon to the Horrigan encounter // Add a flashing icon to the Horrigan encounter
HookCall(0x4C071C, wmRndEncounterOccurred_hook); HookCall(0x4C071C, wmRndEncounterOccurred_hook);
+3 -1
View File
@@ -29,12 +29,14 @@ public:
void init(); void init();
static void OnGameReset(); static void OnGameReset();
static void OnGameModeChange();
static void SaveData(HANDLE); static void SaveData(HANDLE);
static bool LoadData(HANDLE); static bool LoadData(HANDLE);
static void SetCarInterfaceArt(DWORD artIndex); static void SetCarInterfaceArt(DWORD artIndex);
static void SetRestHealTime(DWORD minutes); static void SetRestHealTime(long minutes);
static void SetWorldMapHealTime(long minutes);
static void SetRestMode(DWORD mode); static void SetRestMode(DWORD mode);
static void SetRestMapLevel(int mapId, long elev, bool canRest); static void SetRestMapLevel(int mapId, long elev, bool canRest);
static long __fastcall GetRestMapLevel(long elev, int mapId); static long __fastcall GetRestMapLevel(long elev, int mapId);