Rewrote several fixes for attack_complex in C++ and merged them

Added a default fix for attack_complex to untie target_results from
attacker_results.
This commit is contained in:
NovaRain
2020-08-01 10:27:18 +08:00
parent 76c9d63d3e
commit 21cbcb4a03
6 changed files with 73 additions and 75 deletions
+1 -1
View File
@@ -573,7 +573,7 @@ StartGDialogFix=0
;Set to 1 to fix attacker_results/target_results arguments and repurpose the unused called_shot/num_attacks arguments of attack_complex script function ;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: ;New behavior of the arguments:
;called_shot - additional damage, when the damage received by the target is above the specified minimum damage (minDmg argument) ;called_shot - additional damage when hitting the target
;num_attacks - the number of free action points on the first turn only ;num_attacks - the number of free action points on the first turn only
AttackComplexFix=0 AttackComplexFix=0
+46 -67
View File
@@ -1989,8 +1989,8 @@ skip:
static void __declspec(naked) op_attack_hook() { static void __declspec(naked) op_attack_hook() {
__asm { __asm {
mov esi, dword ptr [esp + 0x3C + 4]; // free_move mov esi, dword ptr [esp + 0x3C + 4]; // free_move
mov ebx, dword ptr [esp + 0x40 + 4]; // add amount damage to target mov ebx, dword ptr [esp + 0x40 + 4]; // add amount damage to target
jmp gdialogActive_; jmp gdialogActive_;
} }
} }
@@ -1998,9 +1998,10 @@ static void __declspec(naked) op_attack_hook() {
static void __declspec(naked) op_attack_hook_flags() { static void __declspec(naked) op_attack_hook_flags() {
__asm { __asm {
and eax, 0xFF; and eax, 0xFF;
shl al, 1; // shift to bit 2
test ebp, ebp; test ebp, ebp;
jz skip; jz skip;
or al, 2; // set bit 2 or al, 1;
skip: skip:
// EAX: gcsd.changeFlags contains the attribute value for setting the flags // 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 // bit 1 - set result flags for target, bit 2 - set result flags for attacker
@@ -2009,65 +2010,44 @@ skip:
} }
} }
static void __declspec(naked) combat_attack_hack_gcsdFlags() { static void __stdcall combat_attack_gcsd() {
__asm { if ((*ptr_gcsd)->changeFlags & 2) { // only for AttackComplexFix
mov ebp, [eax + 0x24]; // gcsd.flagsTarget long flags = (*ptr_gcsd)->flagsSource;
mov ebx, [eax + 0x1C]; // gcsd.changeFlags flags |= (*ptr_main_ctd).attackerFlags & (DAM_HIT | DAM_DEAD); // don't unset DAM_HIT and DAM_DEAD flags
test bl, 2; (*ptr_main_ctd).attackerFlags = flags;
jz checkTarget;
// set source
mov eax, ds:[_main_ctd + 0x14]; // flagsSource
and eax, DAM_DEAD;
or edx, eax; // don't unset DAM_DEAD flag
mov ds:[_main_ctd + 0x14], edx; // flagsSource
checkTarget:
mov eax, ds:[_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;
} }
} if ((*ptr_gcsd)->changeFlags & 1) {
long flags = (*ptr_gcsd)->flagsTarget;
static void __declspec(naked) combat_attack_hack_gcsdMinDamage() { flags |= (*ptr_main_ctd).targetFlags & DAM_DEAD; // don't unset DAM_DEAD flag
__asm { (*ptr_main_ctd).targetFlags = flags;
mov ds:[_main_ctd + 0x2C], ecx; // amountTarget (min)
mov edx, ecx;
lea ebx, ds:[_main_ctd + 0x30]; // flagsTarget
mov eax, ds:[_main_ctd + 0x20]; // Target
jmp check_for_death_; // set DAM_DEAD
} }
}
static void __declspec(naked) combat_attack_hack_gcsdMaxDamage() { if ((*ptr_main_ctd).attackerFlags & DAM_HIT) {
__asm { long damage = (*ptr_main_ctd).targetDamage;
mov ds:[_main_ctd + 0x2C], ebp; // amountTarget (max) (*ptr_main_ctd).targetDamage += (*ptr_gcsd)->bonusDamage;
mov eax, ds:[_main_ctd + 0x20]; // Target if ((*ptr_main_ctd).targetDamage < (*ptr_gcsd)->minDamage) {
call critter_get_hits_; (*ptr_main_ctd).targetDamage = (*ptr_gcsd)->minDamage;
cmp eax, ebp; // curr.HP <= max.DMG }
jle skip; // check the hit points and set the DAM_DEAD flag
cmp eax, edx; // curr.HP > curr.DMG if (damage != (*ptr_main_ctd).targetDamage) {
jg skip; CheckForDeath((*ptr_main_ctd).target, (*ptr_main_ctd).targetDamage, &(*ptr_main_ctd).targetFlags);
and byte ptr ds:[_main_ctd + 0x30], ~DAM_DEAD; // flagsTarget (unset) }
skip: if ((*ptr_main_ctd).targetDamage > (*ptr_gcsd)->maxDamage) {
retn; (*ptr_main_ctd).targetDamage = (*ptr_gcsd)->maxDamage;
if ((*ptr_main_ctd).target->Type() == OBJ_TYPE_CRITTER) {
long cHP = (*ptr_main_ctd).target->critter.health;
if (cHP > (*ptr_gcsd)->maxDamage && cHP <= damage) {
(*ptr_main_ctd).targetFlags &= ~DAM_DEAD; // unset
}
}
}
} }
} }
static void __declspec(naked) combat_attack_hack() { static void __declspec(naked) combat_attack_hack() {
__asm { __asm {
mov ebx, ds:[_main_ctd + 0x2C]; // amountTarget push 0x423039; // return addr
test ebx, ebx; jmp combat_attack_gcsd;
jz end;
retn;
end:
add esp, 4;
mov ebx, 0x423039;
jmp ebx;
} }
} }
@@ -3268,31 +3248,30 @@ void BugFixesInit()
HookCall(0x4C6162, db_freadInt_hook); HookCall(0x4C6162, db_freadInt_hook);
// Fix and repurpose the unused called_shot/num_attack arguments of attack_complex function // Fix and repurpose the unused called_shot/num_attack arguments of attack_complex function
// called_shot - additional damage, when the damage received by the target is above the specified minimum // called_shot - additional damage when hitting the target
// num_attacks - the number of free action points on the first turn only // num_attacks - the number of free action points on the first turn only
if (GetConfigInt("Misc", "AttackComplexFix", 0)) { if (GetConfigInt("Misc", "AttackComplexFix", 0)) {
dlog("Applying attack_complex arguments fix.", DL_INIT); dlog("Applying attack_complex arguments fix.", DL_INIT);
HookCall(0x456D4A, op_attack_hook); HookCall(0x456D4A, op_attack_hook);
SafeWrite8(0x456D61, 0x74); // mov [esp+x], esi SafeWrite8(0x456D61, 0x74); // mov [gcsd.free_move], esi
SafeWrite8(0x456D92, 0x5C); // mov [esp+x], ebx SafeWrite8(0x456D92, 0x5C); // mov [gcsd.amount], ebx
// Fix setting result flags arguments for the attacker and the target (now work independently of each other) // Allow 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 SafeWrite16(0x456D95, 0xC085); // cmp eax, ebp > test eax, eax
MakeCall(0x456D9A, op_attack_hook_flags); MakeCall(0x456D9A, op_attack_hook_flags);
SafeWrite8(0x456D9F, CODETYPE_JumpNZ); // jz > jnz
SafeWrite16(0x456DA7, 0x8489); // mov [gcsd.changeFlags], 1 > mov [gcsd.changeFlags], eax SafeWrite16(0x456DA7, 0x8489); // mov [gcsd.changeFlags], 1 > mov [gcsd.changeFlags], eax
SafeWrite8(0x456DAB, 0); SafeWrite8(0x456DAB, 0);
SafeWrite8(0x456DAE, CODETYPE_Nop); SafeWrite8(0x456DAE, CODETYPE_Nop);
dlogr(" Done", DL_INIT); dlogr(" Done", DL_INIT);
} else {
// Fix setting result flags argument for the target
SafeWrite16(0x456D95, 0xED85); // cmp eax, ebp > test ebp, ebp
} }
SafeWrite8(0x456D9F, CODETYPE_JumpNZ); // jz > jnz
// Fix result flags for the attacker and the target when calling attack_complex function // Fix result flags for the attacker and the target when calling attack_complex function
MakeCall(0x42302B, combat_attack_hack_gcsdFlags, 4); // also set/unset the DAM_DEAD flag when changing the minimum/maximum damage to the target
// Set/Unset the DAM_DEAD flag when changing the minimum/maximum damage to the target // and fix minimum damage still being applied to the target when the attacker misses
MakeCall(0x422FFF, combat_attack_hack_gcsdMinDamage, 1); MakeJump(0x422FE5, combat_attack_hack, 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);
// Fix for critter_mod_skill taking a negative amount value as a positive // Fix for critter_mod_skill taking a negative amount value as a positive
dlog("Applying critter_mod_skill fix.", DL_INIT); dlog("Applying critter_mod_skill fix.", DL_INIT);
+7 -3
View File
@@ -78,7 +78,7 @@ DWORD* ptr_frame_time = reinterpret_cast<DWORD*>(_frame_time);
char* ptr_free_perk = reinterpret_cast<char*>(_free_perk); char* ptr_free_perk = reinterpret_cast<char*>(_free_perk);
long** ptr_game_global_vars = reinterpret_cast<long**>(_game_global_vars); // dynamic array of size == num_game_global_vars long** ptr_game_global_vars = reinterpret_cast<long**>(_game_global_vars); // dynamic array of size == num_game_global_vars
DWORD* ptr_game_user_wants_to_quit = reinterpret_cast<DWORD*>(_game_user_wants_to_quit); DWORD* ptr_game_user_wants_to_quit = reinterpret_cast<DWORD*>(_game_user_wants_to_quit);
DWORD* ptr_gcsd = reinterpret_cast<DWORD*>(_gcsd); CombatGcsd** ptr_gcsd = reinterpret_cast<CombatGcsd**>(_gcsd);
DWORD* ptr_gdBarterMod = reinterpret_cast<DWORD*>(_gdBarterMod); DWORD* ptr_gdBarterMod = reinterpret_cast<DWORD*>(_gdBarterMod);
DWORD* ptr_gdNumOptions = reinterpret_cast<DWORD*>(_gdNumOptions); DWORD* ptr_gdNumOptions = reinterpret_cast<DWORD*>(_gdNumOptions);
DWORD* ptr_gIsSteal = reinterpret_cast<DWORD*>(_gIsSteal); DWORD* ptr_gIsSteal = reinterpret_cast<DWORD*>(_gIsSteal);
@@ -123,7 +123,7 @@ DWORD* ptr_list_total = reinterpret_cast<DWORD*>(_list_total);
DWORD* ptr_loadingGame = reinterpret_cast<DWORD*>(_loadingGame); DWORD* ptr_loadingGame = reinterpret_cast<DWORD*>(_loadingGame);
DWORD* ptr_LSData = reinterpret_cast<DWORD*>(_LSData); DWORD* ptr_LSData = reinterpret_cast<DWORD*>(_LSData);
DWORD* ptr_lsgwin = reinterpret_cast<DWORD*>(_lsgwin); DWORD* ptr_lsgwin = reinterpret_cast<DWORD*>(_lsgwin);
DWORD* ptr_main_ctd = reinterpret_cast<DWORD*>(_main_ctd); TComputeAttack* ptr_main_ctd = reinterpret_cast<TComputeAttack*>(_main_ctd);
DWORD* ptr_main_death_voiceover_done = reinterpret_cast<DWORD*>(_main_death_voiceover_done); DWORD* ptr_main_death_voiceover_done = reinterpret_cast<DWORD*>(_main_death_voiceover_done);
DWORD* ptr_main_window = reinterpret_cast<DWORD*>(_main_window); DWORD* ptr_main_window = reinterpret_cast<DWORD*>(_main_window);
DWORD* ptr_map_elevation = reinterpret_cast<DWORD*>(_map_elevation); DWORD* ptr_map_elevation = reinterpret_cast<DWORD*>(_map_elevation);
@@ -180,7 +180,7 @@ DWORD* ptr_pipmesg = reinterpret_cast<DWORD*>(_pipmesg);
DWORD* ptr_preload_list_index = reinterpret_cast<DWORD*>(_preload_list_index); DWORD* ptr_preload_list_index = reinterpret_cast<DWORD*>(_preload_list_index);
const char** ptr_procTableStrs = reinterpret_cast<const char**>(_procTableStrs); // table of procId (from define.h) => procName map const char** ptr_procTableStrs = reinterpret_cast<const char**>(_procTableStrs); // table of procId (from define.h) => procName map
MSGList* ptr_proto_main_msg_file = reinterpret_cast<MSGList*>(_proto_main_msg_file); MSGList* ptr_proto_main_msg_file = reinterpret_cast<MSGList*>(_proto_main_msg_file);
DWORD* ptr_proto_msg_files = reinterpret_cast<DWORD*>(_proto_msg_files); // array of 6 MSGList elements MSGList* ptr_proto_msg_files = reinterpret_cast<MSGList*>(_proto_msg_files); // array of 6 elements
DWORD* ptr_ptable = reinterpret_cast<DWORD*>(_ptable); DWORD* ptr_ptable = reinterpret_cast<DWORD*>(_ptable);
DWORD* ptr_pud = reinterpret_cast<DWORD*>(_pud); DWORD* ptr_pud = reinterpret_cast<DWORD*>(_pud);
DWORD* ptr_queue = reinterpret_cast<DWORD*>(_queue); DWORD* ptr_queue = reinterpret_cast<DWORD*>(_queue);
@@ -1615,6 +1615,10 @@ long __stdcall BarterComputeValue(TGameObj* source, TGameObj* target) {
WRAP_WATCOM_CALL2(barter_compute_value_, source, target) WRAP_WATCOM_CALL2(barter_compute_value_, source, target)
} }
void __fastcall CheckForDeath(TGameObj* critter, long amountDamage, long* flags) {
WRAP_WATCOM_FCALL3(check_for_death_, critter, amountDamage, flags)
}
void __fastcall CorrectFidForRemovedItemFunc(TGameObj* critter, TGameObj* item, long slotFlag) { void __fastcall CorrectFidForRemovedItemFunc(TGameObj* critter, TGameObj* item, long slotFlag) {
WRAP_WATCOM_FCALL3(correctFidForRemovedItem_, critter, item, slotFlag) WRAP_WATCOM_FCALL3(correctFidForRemovedItem_, critter, item, slotFlag)
} }
+5 -3
View File
@@ -371,7 +371,7 @@ extern DWORD* ptr_frame_time;
extern char* ptr_free_perk; extern char* ptr_free_perk;
extern long** ptr_game_global_vars; // dynamic array of size == num_game_global_vars extern long** ptr_game_global_vars; // dynamic array of size == num_game_global_vars
extern DWORD* ptr_game_user_wants_to_quit; extern DWORD* ptr_game_user_wants_to_quit;
extern DWORD* ptr_gcsd; extern CombatGcsd** ptr_gcsd;
extern DWORD* ptr_gdBarterMod; extern DWORD* ptr_gdBarterMod;
extern DWORD* ptr_gdNumOptions; extern DWORD* ptr_gdNumOptions;
extern DWORD* ptr_gIsSteal; extern DWORD* ptr_gIsSteal;
@@ -416,7 +416,7 @@ extern DWORD* ptr_list_total;
extern DWORD* ptr_loadingGame; extern DWORD* ptr_loadingGame;
extern DWORD* ptr_LSData; extern DWORD* ptr_LSData;
extern DWORD* ptr_lsgwin; extern DWORD* ptr_lsgwin;
extern DWORD* ptr_main_ctd; extern TComputeAttack* ptr_main_ctd;
extern DWORD* ptr_main_death_voiceover_done; extern DWORD* ptr_main_death_voiceover_done;
extern DWORD* ptr_main_window; extern DWORD* ptr_main_window;
extern DWORD* ptr_map_elevation; extern DWORD* ptr_map_elevation;
@@ -473,7 +473,7 @@ extern DWORD* ptr_pipmesg;
extern DWORD* ptr_preload_list_index; extern DWORD* ptr_preload_list_index;
extern const char** ptr_procTableStrs; // table of procId (from define.h) => procName map extern const char** ptr_procTableStrs; // table of procId (from define.h) => procName map
extern MSGList* ptr_proto_main_msg_file; extern MSGList* ptr_proto_main_msg_file;
extern DWORD* ptr_proto_msg_files; extern MSGList* ptr_proto_msg_files; // array of 6 elements
extern DWORD* ptr_ptable; extern DWORD* ptr_ptable;
extern DWORD* ptr_pud; extern DWORD* ptr_pud;
extern DWORD* ptr_queue; extern DWORD* ptr_queue;
@@ -1406,6 +1406,8 @@ long __stdcall ItemWeight(TGameObj* item);
long __stdcall BarterComputeValue(TGameObj* source, TGameObj* target); long __stdcall BarterComputeValue(TGameObj* source, TGameObj* target);
void __fastcall CheckForDeath(TGameObj* critter, long amountDamage, long* flags);
void __fastcall CorrectFidForRemovedItemFunc(TGameObj* critter, TGameObj* item, long slotFlag); void __fastcall CorrectFidForRemovedItemFunc(TGameObj* critter, TGameObj* item, long slotFlag);
void __stdcall IntfaceToggleItemState(); void __stdcall IntfaceToggleItemState();
+13
View File
@@ -137,6 +137,19 @@ struct TComputeAttack {
long extraFlags[6]; long extraFlags[6];
long extraKnockbackValue[6]; long extraKnockbackValue[6];
}; };
struct CombatGcsd {
TGameObj* source;
TGameObj* target;
long freeAP;
long bonusToHit;
long bonusDamage;
long minDamage;
long maxDamage;
long changeFlags;
DWORD flagsSource;
DWORD flagsTarget;
};
#pragma pack(pop) #pragma pack(pop)
// Script instance attached to an object or tile (spatial script). // Script instance attached to an object or tile (spatial script).
+1 -1
View File
@@ -495,7 +495,7 @@ static void __stdcall op_message_str_game2() {
if (fileId < 20) { // main msg files if (fileId < 20) { // main msg files
msg = GetMessageStr(gameMsgFiles[fileId], msgId); msg = GetMessageStr(gameMsgFiles[fileId], msgId);
} else if (fileId >= 0x1000 && fileId <= 0x1005) { // proto msg files } else if (fileId >= 0x1000 && fileId <= 0x1005) { // proto msg files
msg = GetMessageStr((MSGList*)&ptr_proto_msg_files[2 * (fileId - 0x1000)], msgId); msg = GetMessageStr(&ptr_proto_msg_files[fileId - 0x1000], msgId);
} else if (fileId >= 0x2000) { // Extra game message files. } else if (fileId >= 0x2000) { // Extra game message files.
ExtraGameMessageListsMap::iterator it = gExtraGameMsgLists.find(fileId); ExtraGameMessageListsMap::iterator it = gExtraGameMsgLists.find(fileId);
if (it != gExtraGameMsgLists.end()) { if (it != gExtraGameMsgLists.end()) {