diff --git a/artifacts/ddraw.ini b/artifacts/ddraw.ini index 74bd4988..0826687a 100644 --- a/artifacts/ddraw.ini +++ b/artifacts/ddraw.ini @@ -178,6 +178,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/Combat.cpp b/sfall/Combat.cpp index a13c7858..0bd54201 100644 --- a/sfall/Combat.cpp +++ b/sfall/Combat.cpp @@ -451,6 +451,26 @@ static void BodypartHitReadConfig() { bodypartHit.Uncalled = static_cast(GetPrivateProfileIntA("Misc", "BodyHit_Torso_Uncalled", 0, ini)); } +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); +} + void Combat_OnGameLoad() { baseHitChance.maximum = 95; baseHitChance.mod = 0; @@ -466,6 +486,8 @@ void Combat_OnGameLoad() { } void CombatInit() { + CombatProcFix(); + MakeCall(0x424B76, compute_damage_hack, 2); // KnockbackMod MakeJump(0x4136D3, compute_dmg_damage_hack); // for op_critter_dmg @@ -476,12 +498,14 @@ void CombatInit() { MakeCall(0x429E44, ai_pick_hit_mode_hack, 1); // NoBurst if(GetPrivateProfileInt("Misc", "CheckWeaponAmmoCost", 0, ini)) { + 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); } + SimplePatch(0x424FA7, "Misc", "KnockoutTime", 35, 35, 100); + BodypartHitReadConfig(); // Remove the dependency of Body_Torso from Body_Uncalled diff --git a/sfall/Criticals.cpp b/sfall/Criticals.cpp index f06a9f7c..339b2415 100644 --- a/sfall/Criticals.cpp +++ b/sfall/Criticals.cpp @@ -270,9 +270,9 @@ static void CriticalTableOverride() { static void RemoveCriticalTimeLimitsPatch() { if (GetPrivateProfileIntA("Misc", "RemoveCriticalTimelimits", 0, ini)) { 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/Perks.cpp b/sfall/Perks.cpp index ca53be71..ba51fafa 100644 --- a/sfall/Perks.cpp +++ b/sfall/Perks.cpp @@ -1015,6 +1015,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 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 (GetPrivateProfileIntA("Misc", "FastShotFix", 1, ini)) { + 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) { @@ -1051,10 +1093,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); @@ -1167,6 +1209,8 @@ void PerksAcceptCharScreen() { } void PerksInit() { + FastShotTraitFix(); + for (int i = STAT_st; i <= STAT_lu; i++) SafeWrite8(GainStatPerks[i][0], (BYTE)GainStatPerks[i][1]); PerkEngineInit(); diff --git a/sfall/SafeWrite.cpp b/sfall/SafeWrite.cpp index c497da27..52a59318 100644 --- a/sfall/SafeWrite.cpp +++ b/sfall/SafeWrite.cpp @@ -65,10 +65,11 @@ void _stdcall SafeWrite32(DWORD addr, DWORD data) { 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); } void HookCall(DWORD addr, void* func) { diff --git a/sfall/SafeWrite.h b/sfall/SafeWrite.h index 059fc5d7..816107aa 100644 --- a/sfall/SafeWrite.h +++ b/sfall/SafeWrite.h @@ -1,20 +1,23 @@ #pragma once template void _stdcall SafeWrite(DWORD addr, T data) { - DWORD oldProtect; - VirtualProtect((void *)addr, 1, PAGE_EXECUTE_READWRITE, &oldProtect); + DWORD oldProtect; + VirtualProtect((void*)addr, sizeof(T), PAGE_EXECUTE_READWRITE, &oldProtect); *((T*)addr) = data; - VirtualProtect((void *)addr, 1, oldProtect, &oldProtect); + VirtualProtect((void*)addr, sizeof(T), oldProtect, &oldProtect); } + void _stdcall SafeWrite8(DWORD addr, BYTE data); void _stdcall SafeWrite16(DWORD addr, WORD data); void _stdcall SafeWrite32(DWORD addr, DWORD data); void _stdcall SafeWriteStr(DWORD addr, const char* data); + +void SafeMemSet(DWORD addr, BYTE val, int len); +void SafeWriteBytes(DWORD addr, BYTE* data, int count); + void HookCall(DWORD addr, void* func); void MakeCall(DWORD addr, void* func); void MakeCall(DWORD addr, void* func, int len); void MakeJump(DWORD addr, void* func); void MakeJump(DWORD addr, void* func, int len); void BlockCall(DWORD addr); -void SafeMemSet(DWORD addr, BYTE val, int len); -void SafeWriteBytes(DWORD addr, BYTE* data, int count); diff --git a/sfall/main.cpp b/sfall/main.cpp index 32ae86b1..eefc8561 100644 --- a/sfall/main.cpp +++ b/sfall/main.cpp @@ -97,10 +97,6 @@ static const char* musicOverridePath = "data\\sound\\music\\"; static int* scriptDialog = nullptr; -static const DWORD FastShotFixF1[] = { - 0x478BB8, 0x478BC7, 0x478BD6, 0x478BEA, 0x478BF9, 0x478C08, 0x478C2F, -}; - static const DWORD script_dialog_msgs[] = { 0x4A50C2, 0x4A5169, 0x4A52FA, 0x4A5302, 0x4A6B86, 0x4A6BE0, 0x4A6C37, }; @@ -335,18 +331,6 @@ void ClearSavPrototypes() { //////////////////////////////////////////////////////////////////////////////// -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; @@ -544,26 +528,6 @@ static void __declspec(naked) NPCStage6Fix2() { } } -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 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 ScannerHookRet = 0x41BC1D; static const DWORD ScannerHookFail = 0x41BC65; static void __declspec(naked) ScannerAutomapHook() { @@ -791,12 +755,6 @@ static void DllMain2() { SkillsInit(); dlog(".", DL_INIT); dlogr(" Done", DL_INIT); - - //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); //} dlogr("Running FileSystemInit().", DL_INIT); @@ -815,7 +773,7 @@ static void DllMain2() { if (GetPrivateProfileIntA("Misc", "OverrideArtCacheSize", 0, ini)) { dlog("Applying override art cache size patch.", DL_INIT); - SafeWrite8(0x41886A, 0x0); + SafeWrite32(0x418867, 0x90909090); SafeWrite32(0x418872, 256); dlogr(" Done", DL_INIT); } @@ -1002,22 +960,6 @@ static void DllMain2() { dlogr(" Done", DL_INIT); } - switch (GetPrivateProfileIntA("Misc", "FastShotFix", 1, ini)) { - 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; - } - if (GetPrivateProfileIntA("Misc", "BoostScriptDialogLimit", 0, ini)) { const int scriptDialogCount = 10000; dlog("Applying script dialog limit patch.", DL_INIT); @@ -1039,7 +981,7 @@ static void DllMain2() { if (tmp & 1) MakeJump(0x41BBE9, ScannerAutomapHook); if (tmp & 2) { // automap_ - SafeWrite8(0x41BC25, 0x0); + SafeWrite16(0x41BC24, 0x9090); BlockCall(0x41BC3C); // item_m_use_charged_item_ SafeWrite8(0x4794B3, 0x5E); // jbe short 0x479512 @@ -1176,12 +1118,17 @@ static void DllMain2() { dlogr(" Done", DL_INIT); } - SimplePatch(0x424FA7, "Misc", "KnockoutTime", 35, 35, 100); - // Increase the max text width of the information card in the character screen SafeWrite8(0x43ACD5, 144); // 136 SafeWrite8(0x43DD37, 144); // 133 + if (GetPrivateProfileIntA("Misc", "F1EngineBehavior", 0, ini)) { + 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); + } + dlogr("Leave DllMain2", DL_MAIN); }