From 24c9499021e856ec8d0499b0f25feb9fbd77100d Mon Sep 17 00:00:00 2001 From: NovaRain Date: Fri, 1 May 2020 07:54:53 +0800 Subject: [PATCH] Added combat rating check to "Snipe" distance preference fix Added a fix to reduce friendly fire in burst attacks. Updated the info of TGameObj struct. --- artifacts/ddraw.ini | 2 +- sfall/AI.cpp | 55 ++++++++++++++++++++++++++++++++ sfall/AI.h | 4 +++ sfall/FalloutEngine.cpp | 5 +++ sfall/FalloutEngine.h | 3 ++ sfall/FalloutStructs.h | 11 +++++-- sfall/ScriptOps/InterfaceOps.hpp | 2 +- 7 files changed, 77 insertions(+), 5 deletions(-) diff --git a/artifacts/ddraw.ini b/artifacts/ddraw.ini index eba277ef..35aec7f9 100644 --- a/artifacts/ddraw.ini +++ b/artifacts/ddraw.ini @@ -106,7 +106,7 @@ AllowDShowSound=0 ;Set to 1 to override the music path used by default (i.e. data\sound\music\) if not present in the cfg ;Set to 2 to overwrite all occurances of the music path -OverrideMusicDir=2 +OverrideMusicDir=1 ;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX [Input] diff --git a/sfall/AI.cpp b/sfall/AI.cpp index 60264367..43e5458e 100644 --- a/sfall/AI.cpp +++ b/sfall/AI.cpp @@ -28,6 +28,23 @@ typedef std::tr1::unordered_map::const_iterator iter; static std::tr1::unordered_map targets; static std::tr1::unordered_map sources; +TGameObj* __stdcall sf_check_critters_in_lof(TGameObj* object, DWORD checkTile, DWORD team) { + if (object && object->pid >> 24 == OBJ_TYPE_CRITTER && object->teamNum != team) { // not friendly fire + if (object->tile == checkTile) return nullptr; + TGameObj* obj = nullptr; // continue checking the line of fire from object to checkTile + MakeStraightPathFunc(object, object->tile, checkTile, 0, (DWORD*)&obj, 32, (void*)obj_shoot_blocking_at_); + if (!sf_check_critters_in_lof(obj, checkTile, team)) return nullptr; + } + return object; +} + +// Returns the friendly critter that is in the line of fire +TGameObj* __stdcall CheckFriendlyFire(TGameObj* target, TGameObj* attacker) { + TGameObj* object = nullptr; + MakeStraightPathFunc(attacker, attacker->tile, target->tile, 0, (DWORD*)&object, 32, (void*)obj_shoot_blocking_at_); + return sf_check_critters_in_lof(object, target->tile, attacker->teamNum); // 0 if there are no friendly critters +} + static void __declspec(naked) ai_try_attack_hook_FleeFix() { __asm { or byte ptr [esi + 0x3C], 8; // set new 'ReTarget' flag @@ -272,6 +289,14 @@ static void __declspec(naked) cai_perform_distance_prefs_hack() { lea edx, [edx + ecx - 1]; cmp edx, 5; // minimum threshold distance jge skipMove; // distance >= 5? + // check combat rating + mov eax, esi; + call combatai_rating_; + mov edx, eax; // source rating + mov eax, edi; + call combatai_rating_; + cmp eax, edx; // target vs source rating + jl skipMove; // target rating is low moveAway: mov ebx, 10; // move away max distance retn; @@ -283,6 +308,32 @@ skipMove: //////////////////////////////////////////////////////////////////////////////// +static long __fastcall RollFriendlyFire(TGameObj* target, TGameObj* attacker) { + if (CheckFriendlyFire(target, attacker)) { + long dice = RollRandom(1, 10); + return (StatLevel(attacker, STAT_iq) >= dice); // 1 - is friendly + } + return 0; +} + +static void __declspec(naked) combat_safety_invalidate_weapon_func_hook_check() { + static DWORD safety_invalidate_weapon_burst_friendly = 0x4216C9; + __asm { + pushadc; + mov ecx, esi; // target + call RollFriendlyFire; + test eax, eax; + jnz friendly; + popadc; + jmp combat_ctd_init_; +friendly: + lea esp, [esp + 8 + 3 * 4]; + jmp safety_invalidate_weapon_burst_friendly; // "Friendly was in the way!" + } +} + +//////////////////////////////////////////////////////////////////////////////// + static void __fastcall CombatAttackHook(TGameObj* source, TGameObj* target) { sources[target] = source; // who attacked the 'target' from the last time targets[source] = target; // who was attacked by the 'source' from the last time @@ -328,6 +379,10 @@ void AIInit() { /////////////////////// Combat AI behavior fixes /////////////////////// + // Fix to reduce friendly fire in burst attacks + // Adds a check/roll for friendly critters in the line of fire when AI uses burst attacks + HookCall(0x421666, combat_safety_invalidate_weapon_func_hook_check); + // Fix for duplicate critters being added to the list of potential targets for AI MakeCall(0x428E75, ai_find_attackers_hack_target2, 2); MakeCall(0x428EB5, ai_find_attackers_hack_target3); diff --git a/sfall/AI.h b/sfall/AI.h index c7c3d744..dd4f6781 100644 --- a/sfall/AI.h +++ b/sfall/AI.h @@ -21,6 +21,10 @@ #include "FalloutEngine.h" void AIInit(); + +TGameObj* __stdcall sf_check_critters_in_lof(TGameObj* object, DWORD checkTile, DWORD team); +TGameObj* __stdcall CheckFriendlyFire(TGameObj* target, TGameObj* attacker); + void __stdcall AICombatStart(); void __stdcall AICombatEnd(); diff --git a/sfall/FalloutEngine.cpp b/sfall/FalloutEngine.cpp index 57ce983d..7b513733 100644 --- a/sfall/FalloutEngine.cpp +++ b/sfall/FalloutEngine.cpp @@ -303,6 +303,7 @@ const DWORD combat_input_ = 0x4227F4; const DWORD combat_should_end_ = 0x422C60; const DWORD combat_turn_ = 0x42299C; const DWORD combat_turn_run_ = 0x4227DC; +const DWORD combatai_rating_ = 0x42B90C; const DWORD compute_damage_ = 0x4247B8; const DWORD compute_spray_ = 0x423488; const DWORD config_get_string_ = 0x42BF48; @@ -994,6 +995,10 @@ TGameObj* __stdcall ScrFindObjFromProgram(TProgram* program) { WRAP_WATCOM_CALL1(scr_find_obj_from_program_, program) } +long __stdcall RollRandom(long minValue, long maxValue) { + WRAP_WATCOM_CALL2(roll_random_, minValue, maxValue) +} + // Saves pointer to script object into scriptPtr using scriptID. // Returns 0 on success, -1 on failure. long __stdcall ScrPtr(long scriptId, TScript** scriptPtr) { diff --git a/sfall/FalloutEngine.h b/sfall/FalloutEngine.h index e6c97cc2..a30eef62 100644 --- a/sfall/FalloutEngine.h +++ b/sfall/FalloutEngine.h @@ -564,6 +564,7 @@ extern const DWORD combat_input_; extern const DWORD combat_should_end_; extern const DWORD combat_turn_; extern const DWORD combat_turn_run_; +extern const DWORD combatai_rating_; extern const DWORD compute_damage_; extern const DWORD compute_spray_; extern const DWORD config_get_string_; @@ -1144,6 +1145,8 @@ long __stdcall XFSeek(DbFile* file, long fOffset, long origin); TGameObj* __stdcall ScrFindObjFromProgram(TProgram* program); +long __stdcall RollRandom(long minValue, long maxValue); + // Saves pointer to script object into scriptPtr using scriptID. // Returns 0 on success, -1 on failure. long __stdcall ScrPtr(long scriptId, TScript** scriptPtr); diff --git a/sfall/FalloutStructs.h b/sfall/FalloutStructs.h index 5a01a765..2f6923d9 100644 --- a/sfall/FalloutStructs.h +++ b/sfall/FalloutStructs.h @@ -76,9 +76,14 @@ struct TGameObj { char gap_38[4]; long itemCharges; long critterAP_weaponAmmoPid; - char gap_44[16]; - TGameObj* whoHitMe; - char gap_58[12]; + long damageFlags; // critter + long damageLastTurn; // critter + long aiPacket; // critter + long teamNum; // critter + TGameObj* whoHitMe; // critter + long health; // critter + long rads; // critter + long poison; // critter DWORD pid; long cid; long lightDistance; diff --git a/sfall/ScriptOps/InterfaceOps.hpp b/sfall/ScriptOps/InterfaceOps.hpp index ed192443..e5fec60d 100644 --- a/sfall/ScriptOps/InterfaceOps.hpp +++ b/sfall/ScriptOps/InterfaceOps.hpp @@ -737,7 +737,7 @@ static void sf_unwield_slot() { if (update) IntfaceUpdateItems(0, -1, -1); } -void sf_get_window_attribute() { +static void sf_get_window_attribute() { long attr = 0; if (opHandler.numArgs() > 1) attr = opHandler.arg(1).rawValue();