From 29608bb51c13a5a9d8e0b5dd385ad6fbf81afc0e Mon Sep 17 00:00:00 2001 From: NovaRain Date: Mon, 30 Mar 2020 21:06:11 +0800 Subject: [PATCH] Added ActiveGeigerMsgs option and radiation fixes Changed "Radiated" to be highlighted in red color when the player is under the influence of negative effects of radiation. --- artifacts/ddraw.ini | 3 + sfall/FalloutEngine/EngineUtils.cpp | 10 +++ sfall/FalloutEngine/EngineUtils.h | 4 + sfall/FalloutEngine/Structs.h | 11 ++- sfall/FalloutEngine/VariableOffsets.h | 1 + sfall/Modules/BugFixes.cpp | 107 +++++++++++++++++--------- sfall/Modules/MainLoopHook.cpp | 2 +- sfall/Modules/MiscPatches.cpp | 23 ++++++ 8 files changed, 119 insertions(+), 42 deletions(-) diff --git a/artifacts/ddraw.ini b/artifacts/ddraw.ini index 0ddc2a59..73834109 100644 --- a/artifacts/ddraw.ini +++ b/artifacts/ddraw.ini @@ -662,6 +662,9 @@ DontTurnOffSneakIfYouRun=0 ;Set to 0 to disable switching. (Default is 3) UseWalkDistance=3 +;Set to 1 to display messages about radiation for the active geiger counter +ActiveGeigerMsgs=1 + ;Set to 1 to fix the bug of being unable to sell used geiger counters or stealth boys CanSellUsedGeiger=1 diff --git a/sfall/FalloutEngine/EngineUtils.cpp b/sfall/FalloutEngine/EngineUtils.cpp index 72c66a08..a1baa472 100644 --- a/sfall/FalloutEngine/EngineUtils.cpp +++ b/sfall/FalloutEngine/EngineUtils.cpp @@ -144,6 +144,16 @@ long CheckAddictByPid(fo::GameObject* critter, long pid) { /* keyword 'return' is not needed, the compiler will do everything correctly */ } +// Checks whether the player is under the influence of negative effects of radiation +long __fastcall IsRadInfluence() { + fo::QueueRadiation* queue = (fo::QueueRadiation*)fo::func::queue_find_first(fo::var::obj_dude, fo::radiation_event); + while (queue) { + if (queue->init && queue->level >= 2) return 1; + queue = (fo::QueueRadiation*)fo::func::queue_find_next(fo::var::obj_dude, fo::radiation_event); + } + return 0; +} + void ToggleNpcFlag(fo::GameObject* npc, long flag, bool set) { Proto* protoPtr; if (fo::func::proto_ptr(npc->protoId, &protoPtr) != -1) { diff --git a/sfall/FalloutEngine/EngineUtils.h b/sfall/FalloutEngine/EngineUtils.h index cd6fad40..2230347c 100644 --- a/sfall/FalloutEngine/EngineUtils.h +++ b/sfall/FalloutEngine/EngineUtils.h @@ -18,6 +18,7 @@ #pragma once #include +#include #include "Functions.h" @@ -66,6 +67,9 @@ bool HeroIsFemale(); long CheckAddictByPid(fo::GameObject* critter, long pid); +// Checks whether the player is under the influence of negative effects of radiation +long __fastcall IsRadInfluence(); + void ToggleNpcFlag(fo::GameObject* npc, long flag, bool set); // Returns the number of party members in the existing table (begins from 1) diff --git a/sfall/FalloutEngine/Structs.h b/sfall/FalloutEngine/Structs.h index e7153248..bc6cf96e 100644 --- a/sfall/FalloutEngine/Structs.h +++ b/sfall/FalloutEngine/Structs.h @@ -117,7 +117,7 @@ struct GameObject { long damageLastTurn; long aiPacket; long teamNum; - long whoHitMe; + GameObject* whoHitMe; long health; long rads; long poison; @@ -791,10 +791,15 @@ struct Queue { DWORD time; long type; GameObject* object; - long data; + DWORD* data; Queue* next; }; +struct QueueRadiation { + long level; + long init; // 1 - for removing effect +}; + struct QueueDrug { DWORD pid; fo::Stat stat0; @@ -806,7 +811,7 @@ struct QueueDrug { }; struct QueueAddict { - long init; // 1 = perk is not active yet + long init; // 1 - perk is not active yet DWORD drugPid; fo::Perk perkId; // effect of addiction }; diff --git a/sfall/FalloutEngine/VariableOffsets.h b/sfall/FalloutEngine/VariableOffsets.h index 012592ef..257440e0 100644 --- a/sfall/FalloutEngine/VariableOffsets.h +++ b/sfall/FalloutEngine/VariableOffsets.h @@ -281,6 +281,7 @@ #define FO_VAR_GoodColor 0x6AB4EF #define FO_VAR_GreenColor 0x6A3CB0 #define FO_VAR_LIGHT_GREY_Color 0x6A76BF +#define FO_VAR_LIGHT_RED_Color 0x6AB61A #define FO_VAR_PeanutButter 0x6A82F3 #define FO_VAR_RedColor 0x6AB4D0 #define FO_VAR_WhiteColor 0x6AB8CF diff --git a/sfall/Modules/BugFixes.cpp b/sfall/Modules/BugFixes.cpp index 30e64a31..04af3905 100644 --- a/sfall/Modules/BugFixes.cpp +++ b/sfall/Modules/BugFixes.cpp @@ -1098,37 +1098,31 @@ skip: } static void __declspec(naked) combat_ctd_init_hack() { + static const DWORD combat_ctd_init_Ret = 0x422F11; __asm { - mov [esi+0x24], eax // ctd.targetTile - mov eax, [ebx + whoHitMe] // pobj.who_hit_me - inc eax - jnz end - mov [ebx + whoHitMe], eax // pobj.who_hit_me + mov [esi + 0x24], eax; // ctd.targetTile + mov eax, [ebx + whoHitMe]; // pobj.who_hit_me + inc eax; + jnz end; // jump if whoHitMe != -1 + mov [ebx + whoHitMe], eax; // pobj.who_hit_me = 0 end: - push 0x422F11 - retn + jmp combat_ctd_init_Ret; } } static void __declspec(naked) obj_save_hack() { - __asm { - inc eax - jz end - dec eax - mov edx, [esp+0x1C] // combat_data - mov eax, [eax + cid] // pobj.who_hit_me.cid - test byte ptr ds:[FO_VAR_combat_state], 1 // in combat? - jz clear // No - cmp dword ptr [edx], 0 // in combat? - jne skip // Yes + __asm { // edx - combat_data + mov eax, [eax + cid]; // pobj.who_hit_me.cid + test byte ptr ds:[FO_VAR_combat_state], 1; // in combat? + jz clear; // No + cmp dword ptr [edx], 0; // in combat? + jne skip; // Yes clear: - xor eax, eax - dec eax + xor eax, eax; + dec eax; // -1 skip: - mov [edx+0x18], eax // combat_data.who_hit_me -end: - push 0x489422 - retn + mov [edx + 0x18], eax; // combat_data.who_hit_me + retn; } } @@ -1511,11 +1505,14 @@ static void __declspec(naked) ResetPlayer_hook() { } } -static const DWORD obj_move_to_tile_Ret = 0x48A74E; static void __declspec(naked) obj_move_to_tile_hack() { + static const DWORD obj_move_to_tile_Ret = 0x48A74E; __asm { - cmp dword ptr ds:[FO_VAR_map_state], 0; // map number, -1 exit to worldmap + cmp ds:[FO_VAR_loadingGame], 0; + jnz skip; + cmp dword ptr ds:[FO_VAR_map_state], 0; // map number, -1 exit to worldmap (probably redundant) jz mapLeave; +skip: add esp, 4; jmp obj_move_to_tile_Ret; mapLeave: @@ -1868,7 +1865,32 @@ noRange: } } +static void __declspec(naked) process_rads_hack() { + static const DWORD process_rads_Ret = 0x42D708; + __asm { + test ebp, ebp; + jl fix; + test byte ptr [ecx + damageFlags], DAM_DEAD; + jnz fix; + retn; +fix: + add esp, 4; + jmp process_rads_Ret; + } +} + static void __declspec(naked) process_rads_hook() { + __asm { + test ebp, ebp; + jg skip; + mov esi, 999; // msg number + mov [esp + 0x20 - 0x20 + 4], esi; +skip: + jmp fo::funcoffs::message_search_; + } +} + +static void __declspec(naked) process_rads_hook_msg() { __asm { push eax; // death message for DialogOut call fo::funcoffs::display_print_; @@ -2789,6 +2811,16 @@ void BugFixes::init() dlogr(" Done", DL_INIT); //} + // Enable party members with level 6 protos to reach level 6 + //if (GetConfigInt("Misc", "NPCStage6Fix", 1)) { + dlog("Applying NPC Stage 6 Fix.", DL_INIT); + MakeJump(0x493CE9, NPCStage6Fix1); // partyMember_init_ + MakeJump(0x494224, NPCStage6Fix2); // partyMemberGetAIOptions_ + SafeWrite8(0x494063, 6); // loop should look for a potential 6th stage (partyMember_init_) + SafeWrite8(0x4940BB, 204); // move pointer by 204 bytes instead of 200 + dlogr(" Done", DL_INIT); + //} + //if (GetConfigInt("Misc", "NPCLevelFix", 1)) { dlog("Applying NPC level fix.", DL_INIT); HookCall(0x495BC9, (void*)0x495E51); // jz 0x495E7F > jz 0x495E51 @@ -2849,16 +2881,6 @@ void BugFixes::init() dlogr(" Done", DL_INIT); //} - // Enable party members with level 6 protos to reach level 6 - //if (GetConfigInt("Misc", "NPCStage6Fix", 1)) { - dlog("Applying NPC Stage 6 Fix.", DL_INIT); - MakeJump(0x493CE9, NPCStage6Fix1); // partyMember_init_ - MakeJump(0x494224, NPCStage6Fix2); // partyMemberGetAIOptions_ - SafeWrite8(0x494063, 6); // loop should look for a potential 6th stage (partyMember_init_) - SafeWrite8(0x4940BB, 204); // move pointer by 204 bytes instead of 200 - dlogr(" Done", DL_INIT); - //} - //if (GetConfigInt("Misc", "MultiHexPathingFix", 1)) { dlog("Applying MultiHex Pathing Fix.", DL_INIT); MakeCalls(MultiHexFix, {0x42901F, 0x429170}); @@ -2893,7 +2915,7 @@ void BugFixes::init() dlog("Applying fix for explosives bugs.", DL_INIT); // Fix crashes when killing critters with explosives MakeJump(0x422F05, combat_ctd_init_hack); - MakeJump(0x489413, obj_save_hack); + MakeCall(0x48941C, obj_save_hack, 1); // Fix for destroy_p_proc not being called if the critter is killed by explosives when you leave the map MakeCall(0x4130C3, action_explode_hack); MakeCall(0x4130E5, action_explode_hack1); @@ -3105,8 +3127,17 @@ void BugFixes::init() // Fix for determine_to_hit_func_ engine function taking distance into account when called from determine_to_hit_no_range_ HookCall(0x4244C3, determine_to_hit_func_hook); - // Display a pop-up messages box about death from radiation - HookCall(0x42D733, process_rads_hook); + // Radiation fixes + MakeCall(0x42D6C3, process_rads_hack, 1); // prevents player's death if a stat is less than 1 when removing radiation effects + HookCall(0x42D67A, process_rads_hook); // fix for the same message being displayed when removing radiation effects + // Display messages about radiation for the active geiger counter + if (GetConfigInt("Misc", "ActiveGeigerMsgs", 1)) { + dlog("Applying active geiger counter messages patch.", DL_INIT); + SafeWriteBatch(0x74, {0x42D424, 0x42D444}); // jnz > jz + dlogr(" Done", DL_INIT); + } + // Display a pop-up message box about death from radiation + HookCall(0x42D733, process_rads_hook_msg); // Fix for AI not taking chem_primary_desire in AI.txt as drug use preference when using drugs in their inventory if (GetConfigInt("Misc", "AIDrugUsePerfFix", 0)) { diff --git a/sfall/Modules/MainLoopHook.cpp b/sfall/Modules/MainLoopHook.cpp index cf28c887..41cd1095 100644 --- a/sfall/Modules/MainLoopHook.cpp +++ b/sfall/Modules/MainLoopHook.cpp @@ -45,7 +45,7 @@ static void __declspec(naked) CombatLoopHook() { //pop eax; pop edx; call fo::funcoffs::get_input_; - pop ecx; // prevent the combat turn from being skipped after pressing Alt-Tab + pop ecx; // fix to prevent the combat turn from being skipped after using Alt+Tab retn; } } diff --git a/sfall/Modules/MiscPatches.cpp b/sfall/Modules/MiscPatches.cpp index 027f0047..3e5a1c78 100644 --- a/sfall/Modules/MiscPatches.cpp +++ b/sfall/Modules/MiscPatches.cpp @@ -264,6 +264,26 @@ playWalkMovie: } } +static void __declspec(naked) ListDrvdStats_hook() { + static const DWORD ListDrvdStats_Ret = 0x4354D9; + __asm { + call fo::IsRadInfluence; + test eax, eax; + jnz influence; + mov eax, ds:[FO_VAR_obj_dude]; + jmp fo::funcoffs::critter_get_rads_; +influence: + xor ecx, ecx; + mov cl, ds:[FO_VAR_RedColor]; + cmp dword ptr [esp], 0x4354BE + 5; + jne skip; + mov cl, 131; // color index for selected +skip: + add esp, 4; + jmp ListDrvdStats_Ret; + } +} + void AdditionalWeaponAnimsPatch() { if (GetConfigInt("Misc", "AdditionalWeaponAnims", 0)) { dlog("Applying additional weapon animations patch.", DL_INIT); @@ -689,6 +709,9 @@ void MiscPatches::init() { dlogr(" Done", DL_INIT); } + // Highlight "Radiated" in red color when the player is under the influence of negative effects of radiation + HookCalls(ListDrvdStats_hook, { 0x43549C, 0x4354BE }); + // Increase the max text width of the information card in the character screen SafeWriteBatch(145, {0x43ACD5, 0x43DD37}); // 136, 133