diff --git a/artifacts/ddraw.ini b/artifacts/ddraw.ini index c20662a1..55070953 100644 --- a/artifacts/ddraw.ini +++ b/artifacts/ddraw.ini @@ -587,11 +587,10 @@ ObjCanHearObjFix=0 ;If the argument value is -1, the mood will be determined by the local variable 0 of the script (vanilla behavior) StartGDialogFix=0 -;Set to 1 to fix and repurpose the unused called_shot/num_attacks arguments of attack_complex script function -;This also changes the behavior of the result flags arguments -;called_shot - additional damage, when the damage received by the target is above the specified minimum damage +;Set to 1 to fix attacker_results/target_results arguments and repurpose the unused called_shot/num_attacks arguments of attack_complex script function +;New behavior of the arguments: +;called_shot - additional damage, when the damage received by the target is above the specified minimum damage (minDmg argument) ;num_attacks - the number of free action points on the first turn only -;attacker_results - unused, must be 0 or not equal to the target_results argument when specifying result flags for the target AttackComplexFix=0 ;Set to 1 to fix the issue with the division operator treating negative integers as unsigned diff --git a/sfall/Modules/BugFixes.cpp b/sfall/Modules/BugFixes.cpp index 74ddc3a0..90262cab 100644 --- a/sfall/Modules/BugFixes.cpp +++ b/sfall/Modules/BugFixes.cpp @@ -2012,6 +2012,69 @@ static void __declspec(naked) op_attack_hook() { } } +static void __declspec(naked) op_attack_hook_flags() { + __asm { + and eax, 0xFF; + test ebp, ebp; + jz skip; + or al, 2; // set bit 2 +skip: + // EAX: gcsd.changeFlags contains the attribute value for setting the flags + // bit 1 - set result flags for target, bit 2 - set result flags for attacker + test eax, eax; + retn; + } +} + +static void __declspec(naked) combat_attack_hack_gcsdFlags() { + __asm { + mov ebp, [eax + 0x24]; // gcsd.flagsTarget + mov ebx, [eax + 0x1C]; // gcsd.changeFlags + test bl, 2; + jz checkTarget; + // set source + mov eax, ds:[FO_VAR_main_ctd + 0x14]; // flagsSource + and eax, DAM_DEAD; + or edx, eax; // don't unset DAM_DEAD flag + mov ds:[FO_VAR_main_ctd + 0x14], edx; // flagsSource +checkTarget: + mov eax, ds:[FO_VAR_main_ctd + 0x30]; // flagsTarget + test bl, 1; + jnz setTarget; + retn; +setTarget: + and eax, DAM_DEAD; + or ebp, eax; // don't unset DAM_DEAD flag + mov eax, ebp; + retn; + } +} + +static void __declspec(naked) combat_attack_hack_gcsdMinDamage() { + __asm { + mov ds:[FO_VAR_main_ctd + 0x2C], ecx; // amountTarget (min) + mov edx, ecx; + lea ebx, ds:[FO_VAR_main_ctd + 0x30]; // flagsTarget + mov eax, ds:[FO_VAR_main_ctd + 0x20]; // Target + jmp fo::funcoffs::check_for_death_; // set DAM_DEAD + } +} + +static void __declspec(naked) combat_attack_hack_gcsdMaxDamage() { + __asm { + mov ds:[FO_VAR_main_ctd + 0x2C], ebp; // amountTarget (max) + mov eax, ds:[FO_VAR_main_ctd + 0x20]; // Target + call fo::funcoffs::critter_get_hits_; + cmp eax, ebp; // curr.HP <= max.DMG + jle skip; + cmp eax, edx; // curr.HP > curr.DMG + jg skip; + and byte ptr ds:[FO_VAR_main_ctd + 0x30], ~DAM_DEAD; // flagsTarget (unset) +skip: + retn; + } +} + static void __declspec(naked) combat_attack_hack() { __asm { mov ebx, ds:[FO_VAR_main_ctd + 0x2C]; // amountTarget @@ -3148,7 +3211,7 @@ void BugFixes::init() // Fix for the encounter description being displayed in two lines instead of one SafeWrite32(0x4C1011, 0x9090C789); // mov edi, eax; - SafeWrite8(0x4C1015, 0x90); + SafeWrite8(0x4C1015, CodeType::Nop); HookCall(0x4C1042, wmSetupRandomEncounter_hook); // Fix for being unable to sell/give items in the barter screen when the player/party member is overloaded @@ -3217,18 +3280,28 @@ void BugFixes::init() HookCall(0x4C6162, db_freadInt_hook); // Fix and repurpose the unused called_shot/num_attack arguments of attack_complex function - // also change the behavior of the result flags arguments // called_shot - additional damage, when the damage received by the target is above the specified minimum // num_attacks - the number of free action points on the first turn only - // attacker_results - unused, must be 0 or not equal to the target_results argument when specifying result flags for the target if (GetConfigInt("Misc", "AttackComplexFix", 0)) { - dlog("Applying attack_complex fix.", DL_INIT); + dlog("Applying attack_complex arguments fix.", DL_INIT); HookCall(0x456D4A, op_attack_hook); SafeWrite8(0x456D61, 0x74); // mov [esp+x], esi SafeWrite8(0x456D92, 0x5C); // mov [esp+x], ebx - SafeWrite8(0x456D98, 0x94); // setnz > setz (fix setting result flags) + + // Fix setting result flags arguments for the attacker and the target (now work independently of each other) + SafeWrite16(0x456D95, 0xC085); // cmp eax, ebx > test eax, eax + MakeCall(0x456D9A, op_attack_hook_flags); + SafeWrite8(0x456D9F, CodeType::JumpNZ); // jz > jnz + SafeWrite16(0x456DA7, 0x8489); // mov [gcsd.changeFlags], 1 > mov [gcsd.changeFlags], eax + SafeWrite8(0x456DAB, 0); + SafeWrite8(0x456DAE, CodeType::Nop); dlogr(" Done", DL_INIT); } + // Fix result flags for the attacker and the target when calling attack_complex function + MakeCall(0x42302B, combat_attack_hack_gcsdFlags, 4); + // Set/Unset the DAM_DEAD flag when changing the minimum/maximum damage to the target + MakeCall(0x422FFF, combat_attack_hack_gcsdMinDamage, 1); + MakeCall(0x423017, combat_attack_hack_gcsdMaxDamage, 1); // Fix for attack_complex still causing minimum damage to the target when the attacker misses MakeCall(0x422FE5, combat_attack_hack, 1); diff --git a/sfall/Modules/Drugs.cpp b/sfall/Modules/Drugs.cpp index 72042f2c..36c93c7a 100644 --- a/sfall/Modules/Drugs.cpp +++ b/sfall/Modules/Drugs.cpp @@ -335,7 +335,7 @@ void Drugs::init() { MakeCall(0x47A5B8, pid_to_gvar_hack, 1); MakeCall(0x47A50C, perform_withdrawal_start_hack); SafeWrite32(0x47A523, 0x9090EBD1); // shr ebx, 1 (fix for trait drug addict) - SafeWrite8(0x47A527, 0x90); + SafeWrite8(0x47A527, CodeType::Nop); if (addictionGvarCount) { LoadGameHook::OnAfterGameInit() += CheckValidGvarNumber; diff --git a/sfall/Modules/HeroAppearance.cpp b/sfall/Modules/HeroAppearance.cpp index 3dfffb44..85c8eab0 100644 --- a/sfall/Modules/HeroAppearance.cpp +++ b/sfall/Modules/HeroAppearance.cpp @@ -1496,7 +1496,7 @@ static void EnableHeroAppearanceMod() { HookCall(0x42613A, FixPcCriticalHitMsg); // Force Criticals For Testing - //SafeMemSet(0x423A8F, 0x90, 8); + //SafeMemSet(0x423A8F, CodeType::Nop, 8); } static void HeroAppearanceModExit() { diff --git a/sfall/Modules/KillCounter.cpp b/sfall/Modules/KillCounter.cpp index 590cd4e4..725af797 100644 --- a/sfall/Modules/KillCounter.cpp +++ b/sfall/Modules/KillCounter.cpp @@ -50,8 +50,8 @@ static void KillCounterInit() { // Overwrite the critter_kill_count_inc_ function that increments the kill counter MakeCall(0x42D89C, IncKillCounter, 1); - SafeWrite8(0x42D88E, 0x45); // lea edx, [eax * 2] - SafeWrite8(0x42D899, 0x90); // inc ebx > nop + SafeWrite8(0x42D88E, 0x45); // lea edx, [eax * 2] + SafeWrite8(0x42D899, CodeType::Nop); // inc ebx > nop const DWORD extraKillTypesCountAddr[] = { 0x42D8AF, // critter_kill_count_ diff --git a/sfall/Modules/LoadOrder.cpp b/sfall/Modules/LoadOrder.cpp index 683cbc57..6b8a9683 100644 --- a/sfall/Modules/LoadOrder.cpp +++ b/sfall/Modules/LoadOrder.cpp @@ -245,8 +245,8 @@ static void GetExtraPatches() { static void MultiPatchesPatch() { //if (GetConfigInt("Misc", "MultiPatches", 0)) { dlog("Applying load multiple patches patch.", DL_INIT); - SafeWrite8(0x444354, 0x90); // Change step from 2 to 1 - SafeWrite8(0x44435C, 0xC4); // Disable check + SafeWrite8(0x444354, CodeType::Nop); // Change step from 2 to 1 + SafeWrite8(0x44435C, 0xC4); // Disable check dlogr(" Done", DL_INIT); //} } diff --git a/sfall/Modules/Objects.cpp b/sfall/Modules/Objects.cpp index b0eb7d74..965961f9 100644 --- a/sfall/Modules/Objects.cpp +++ b/sfall/Modules/Objects.cpp @@ -242,7 +242,7 @@ void Objects::init() { }; HookCall(0x4A38A5, new_obj_id_hook); - SafeWrite8(0x4A38B3, 0x90); // fix ID increment + SafeWrite8(0x4A38B3, CodeType::Nop); // fix ID increment MakeCall(0x477A0E, item_identical_hack); // don't put item with unique ID to items stack diff --git a/sfall/Modules/Scripting/Handlers/Misc.cpp b/sfall/Modules/Scripting/Handlers/Misc.cpp index a496077a..f0aea6b1 100644 --- a/sfall/Modules/Scripting/Handlers/Misc.cpp +++ b/sfall/Modules/Scripting/Handlers/Misc.cpp @@ -247,9 +247,9 @@ static void __cdecl IncNPCLevel(const char* fmt, const char* name) { SafeWrite32(0x495C50, 0x01FB840F); // Want to keep this check intact. (restore) - SafeMemSet(0x495C77, 0x90, 6); // Check that the player is high enough for the npc to consider this level - //SafeMemSet(0x495C8C, 0x90, 6); // Check that the npc isn't already at its maximum level - SafeMemSet(0x495CEC, 0x90, 6); // Check that the npc hasn't already levelled up recently + SafeMemSet(0x495C77, CodeType::Nop, 6); // Check that the player is high enough for the npc to consider this level + //SafeMemSet(0x495C8C, CodeType::Nop, 6); // Check that the npc isn't already at its maximum level + SafeMemSet(0x495CEC, CodeType::Nop, 6); // Check that the npc hasn't already levelled up recently if (!npcAutoLevelEnabled) { SafeWrite8(0x495CFB, CodeType::JumpShort); // Disable random element } diff --git a/sfall/Modules/Sound.cpp b/sfall/Modules/Sound.cpp index c81be971..4f7ce746 100644 --- a/sfall/Modules/Sound.cpp +++ b/sfall/Modules/Sound.cpp @@ -170,9 +170,9 @@ static void CreateSndWnd() { static DWORD GetSpeechDurationTime() { if (!speechSound || !speechSound->pSeek) return 0; speechSound->pSeek->SetTimeFormat(&TIME_FORMAT_MEDIA_TIME); - __int64 outVal; + __int64 outVal = -1; speechSound->pSeek->GetDuration(&outVal); - return static_cast(outVal / 10000000) + 1; + return (outVal != -1) ? static_cast(outVal / 10000000) + 1 : 0; } static DWORD GetSpeechPlayingPosition() { @@ -860,8 +860,8 @@ void Sound::init() { } int sBuff = GetConfigInt("Sound", "NumSoundBuffers", 0); - if (sBuff > 0 && sBuff <= 32) { - SafeWrite8(0x451129, (BYTE)sBuff); + if (sBuff > 0) { + SafeWrite8(0x451129, (sBuff > 32) ? (BYTE)32 : (BYTE)sBuff); } if (GetConfigInt("Sound", "AllowSoundForFloats", 0)) { diff --git a/sfall/Modules/Worldmap.cpp b/sfall/Modules/Worldmap.cpp index eb73773a..d656dd11 100644 --- a/sfall/Modules/Worldmap.cpp +++ b/sfall/Modules/Worldmap.cpp @@ -384,8 +384,8 @@ static void TimeLimitPatch() { 0x4A34EF, // inc_game_time_ 0x4A3547 // inc_game_time_in_seconds_ }); - SafeMemSet(0x4A34F4, 0x90, 16); - SafeMemSet(0x4A354C, 0x90, 16); + SafeMemSet(0x4A34F4, CodeType::Nop, 16); + SafeMemSet(0x4A354C, CodeType::Nop, 16); } else { SafeWrite8(0x4A34EC, limit); SafeWrite8(0x4A3544, limit);