From 781c16798a3b81bfa58131637bbb636ea06db64c Mon Sep 17 00:00:00 2001 From: fedora737 Date: Tue, 20 Aug 2024 16:06:42 -0500 Subject: [PATCH 1/3] Improvements to medical system --- .../SCR_CharacterControllerComponent.c | 69 ++++++++++----- .../SCR_CharacterDamageManagerComponent.c | 86 ++++++++++++------- 2 files changed, 100 insertions(+), 55 deletions(-) diff --git a/addons/medical/scripts/Game/ACE_Medical/Character/SCR_CharacterControllerComponent.c b/addons/medical/scripts/Game/ACE_Medical/Character/SCR_CharacterControllerComponent.c index 93dcc098..51b73f43 100644 --- a/addons/medical/scripts/Game/ACE_Medical/Character/SCR_CharacterControllerComponent.c +++ b/addons/medical/scripts/Game/ACE_Medical/Character/SCR_CharacterControllerComponent.c @@ -5,54 +5,77 @@ modded class SCR_CharacterControllerComponent : CharacterControllerComponent { protected SCR_CharacterDamageManagerComponent m_pACE_Medical_DamageManager; protected const float ACE_MEDICAL_SECOND_CHANCE_DEACTIVATION_TIMEOUT_MS = 1000; - + //------------------------------------------------------------------------------------------------ //! Initialize member variables override void OnInit(IEntity owner) { super.OnInit(owner); m_pACE_Medical_DamageManager = SCR_CharacterDamageManagerComponent.Cast(owner.FindComponent(SCR_CharacterDamageManagerComponent)); + + // Add null check to avoid potential null reference errors + if (!m_pACE_Medical_DamageManager) + { + Print("ACE Medical Damage Manager not found!", LogLevel.ERROR); + return; + } } - + //------------------------------------------------------------------------------------------------ //! Add/remove second chance when life state changes override void OnLifeStateChanged(ECharacterLifeState previousLifeState, ECharacterLifeState newLifeState) { super.OnLifeStateChanged(previousLifeState, newLifeState); - - // Only run if ACE Medical has been initialized for this character + + // Check if ACE Medical has been initialized if (!m_pACE_Medical_DamageManager.ACE_Medical_IsInitialized()) return; - + // OnLifeStateChanged sometimes gets triggered without a change in state if (previousLifeState == newLifeState) return; - + switch (newLifeState) { - // Add second chance when revived case ECharacterLifeState.ALIVE: - { - GetGame().GetCallqueue().Remove(m_pACE_Medical_DamageManager.ACE_Medical_EnableSecondChance); - m_pACE_Medical_DamageManager.ACE_Medical_EnableSecondChance(true); - m_pACE_Medical_DamageManager.ACE_Medical_SetSecondChanceTrigged(false); + HandleAliveState(); break; - } - - // Schedule removal of second chance when falling unconscious + case ECharacterLifeState.INCAPACITATED: - { - GetGame().GetCallqueue().CallLater(m_pACE_Medical_DamageManager.ACE_Medical_EnableSecondChance, ACE_MEDICAL_SECOND_CHANCE_DEACTIVATION_TIMEOUT_MS, false, false); + HandleIncapacitatedState(); break; - } - - // Remove second chance when dead + case ECharacterLifeState.DEAD: - { - GetGame().GetCallqueue().Remove(m_pACE_Medical_DamageManager.ACE_Medical_EnableSecondChance); - m_pACE_Medical_DamageManager.ACE_Medical_EnableSecondChance(false); + HandleDeadState(); break; - } } } + + //------------------------------------------------------------------------------------------------ + //! Handle logic when character is revived (ALIVE state) + void HandleAliveState() + { + auto callQueue = GetGame().GetCallqueue(); + callQueue.Remove(m_pACE_Medical_DamageManager.ACE_Medical_EnableSecondChance); + m_pACE_Medical_DamageManager.ACE_Medical_EnableSecondChance(true); + m_pACE_Medical_DamageManager.ACE_Medical_SetSecondChanceTriggered(false); + } + + //------------------------------------------------------------------------------------------------ + //! Handle logic when character is incapacitated (INCAPACITATED state) + void HandleIncapacitatedState() + { + auto callQueue = GetGame().GetCallqueue(); + callQueue.CallLater(m_pACE_Medical_DamageManager.ACE_Medical_EnableSecondChance, ACE_MEDICAL_SECOND_CHANCE_DEACTIVATION_TIMEOUT_MS, false, false); + } + + //------------------------------------------------------------------------------------------------ + //! Handle logic when character is dead (DEAD state) + void HandleDeadState() + { + auto callQueue = GetGame().GetCallqueue(); + callQueue.Remove(m_pACE_Medical_DamageManager.ACE_Medical_EnableSecondChance); + m_pACE_Medical_DamageManager.ACE_Medical_EnableSecondChance(false); + } } + diff --git a/addons/medical/scripts/Game/ACE_Medical/Components/Damage/SCR_CharacterDamageManagerComponent.c b/addons/medical/scripts/Game/ACE_Medical/Components/Damage/SCR_CharacterDamageManagerComponent.c index 5a4c3954..96247688 100644 --- a/addons/medical/scripts/Game/ACE_Medical/Components/Damage/SCR_CharacterDamageManagerComponent.c +++ b/addons/medical/scripts/Game/ACE_Medical/Components/Damage/SCR_CharacterDamageManagerComponent.c @@ -15,9 +15,6 @@ modded class SCR_CharacterDamageManagerComponent : SCR_DamageManagerComponent protected float m_fACE_Medical_ModeratePainThreshold; protected float m_fACE_Medical_SeriousPainThreshold; - // We only notify the replication system about changes of these members on initialization - // After init, each proxy is itself responsible for updating these members - // Having them as RplProp also ensures that JIPs receive the current state from the server [RplProp()] protected bool m_bACE_Medical_Initialized = false; [RplProp()] @@ -26,41 +23,50 @@ modded class SCR_CharacterDamageManagerComponent : SCR_DamageManagerComponent protected bool m_bACE_Medical_SecondChanceOnHeadEnabled = false; [RplProp()] protected bool m_bACE_Medical_SecondChanceTriggered = false; - + //----------------------------------------------------------------------------------------------------------- //! Initialize member variables override void OnInit(IEntity owner) { super.OnInit(owner); - + + // Initialize Health Hit Zone m_pACE_Medical_HealthHitZone = GetHitZoneByName("Health"); if (!m_pACE_Medical_HealthHitZone) + { + Print("ACE Medical: Health hit zone not found!", LogLevel.ERROR); return; - + } + m_fACE_Medical_CriticalHealth = m_pACE_Medical_HealthHitZone.GetDamageStateThreshold(ECharacterHealthState.CRITICAL); GetPhysicalHitZones(m_aACE_Medical_PhysicalHitZones); + + // Log initialization success + Print("ACE Medical: Character Damage Manager initialized.", LogLevel.INFO); } - + //----------------------------------------------------------------------------------------------------------- //! Initialize ACE medical on a character damage manager (Called on the server) void ACE_Medical_Initialize() { if (m_bACE_Medical_Initialized) return; - + ACE_Medical_Settings settings = ACE_SettingsHelperT.GetModSettings(); if (settings) { m_bACE_Medical_SecondChanceOnHeadEnabled = settings.m_bSecondChanceOnHeadEnabled; m_fACE_Medical_SecondChanceRegenScale = settings.m_fSecondChanceRegenScale; } - + ACE_Medical_EnableSecondChance(true); - // Damage calculations are done on all machines, so we have to broadcast the init m_bACE_Medical_Initialized = true; Replication.BumpMe(); + + // Log successful initialization + Print("ACE Medical: Initialized with second chance enabled.", LogLevel.INFO); } - + //------------------------------------------------------------------------------------------------ // Reduce regeneration rate when second chance was triggered override float GetResilienceRegenScale() @@ -70,17 +76,14 @@ modded class SCR_CharacterDamageManagerComponent : SCR_DamageManagerComponent { return m_fACE_Medical_SecondChanceRegenScale; } - else - { - return scale; - } + return scale; } //------------------------------------------------------------------------------------------------ //! Returns true if health hit zone has critical health bool ACE_Medical_HasCriticalHealth() { - return m_pACE_Medical_HealthHitZone.GetHealthScaled() <= m_fACE_Medical_CriticalHealth; + return m_pACE_Medical_HealthHitZone && m_pACE_Medical_HealthHitZone.GetHealthScaled() <= m_fACE_Medical_CriticalHealth; } //------------------------------------------------------------------------------------------------ @@ -92,7 +95,7 @@ modded class SCR_CharacterDamageManagerComponent : SCR_DamageManagerComponent if (hitZone.GetHealthScaled() < 0.999) return true; } - + return false; } @@ -101,6 +104,14 @@ modded class SCR_CharacterDamageManagerComponent : SCR_DamageManagerComponent void ACE_Medical_SetPainHitZone(HitZone hitzone) { m_pACE_Medical_PainHitZone = ACE_Medical_PainHitZone.Cast(hitzone); + + // Null Check + if (!m_pACE_Medical_PainHitZone) + { + Print("ACE Medical: Pain hit zone not found!", LogLevel.ERROR); + return; + } + m_fACE_Medical_ModeratePainThreshold = m_pACE_Medical_PainHitZone.GetDamageStateThreshold(ECharacterHealthState.MODERATE); m_fACE_Medical_SeriousPainThreshold = m_pACE_Medical_PainHitZone.GetDamageStateThreshold(ECharacterHealthState.SERIOUS); } @@ -111,25 +122,30 @@ modded class SCR_CharacterDamageManagerComponent : SCR_DamageManagerComponent { return m_pACE_Medical_PainHitZone; } - + //----------------------------------------------------------------------------------------------------------- //! Returns true if pain hit zone is at least moderately damaged bool ACE_Medical_IsInPain() { - return m_pACE_Medical_PainHitZone.GetHealthScaled() <= m_fACE_Medical_ModeratePainThreshold; + return m_pACE_Medical_PainHitZone && m_pACE_Medical_PainHitZone.GetHealthScaled() <= m_fACE_Medical_ModeratePainThreshold; } - + //----------------------------------------------------------------------------------------------------------- //! Returns intensity of pain used for ACE_Medical_PainScreenEffect float ACE_Medical_GetPainIntensity() { + if (!m_pACE_Medical_PainHitZone) + { + Print("ACE Medical: Pain hit zone not initialized!", LogLevel.ERROR); + return 0; + } + // Clamp between serious damage and full health float scaledHealth = Math.Clamp(m_pACE_Medical_PainHitZone.GetHealthScaled(), m_fACE_Medical_SeriousPainThreshold, 1); - + if (scaledHealth > m_fACE_Medical_ModeratePainThreshold) { - // No effect when less than moderate damage - return 0; + return 0; // No effect when less than moderate damage } else { @@ -137,7 +153,7 @@ modded class SCR_CharacterDamageManagerComponent : SCR_DamageManagerComponent return Math.InverseLerp(1, m_fACE_Medical_SeriousPainThreshold, scaledHealth); } } - + //------------------------------------------------------------------------------------------------ //! Returns true if ACE Medical has been initialized bool ACE_Medical_IsInitialized() @@ -150,38 +166,44 @@ modded class SCR_CharacterDamageManagerComponent : SCR_DamageManagerComponent void ACE_Medical_EnableSecondChance(bool enable) { m_bACE_Medical_HasSecondChance = enable; + + // Log state change + Print("ACE Medical: Second chance " + (enable ? "enabled" : "disabled") + ".", LogLevel.INFO); } - + //------------------------------------------------------------------------------------------------ //! Check if second chance is enabled bool ACE_Medical_HasSecondChance() { return m_bACE_Medical_HasSecondChance; } - + //----------------------------------------------------------------------------------------------------------- //! Returns true if second chance is enabled for the given hit zone bool ACE_Medical_HasSecondChanceOnHitZone(HitZone hitZone) { if (!m_bACE_Medical_HasSecondChance) return false; - + if (m_bACE_Medical_SecondChanceOnHeadEnabled) return true; - + return (hitZone != m_pHeadHitZone); } - + //------------------------------------------------------------------------------------------------ //! To be set true when second chance was used - void ACE_Medical_SetSecondChanceTrigged(bool isTriggered) + void ACE_Medical_SetSecondChanceTriggered(bool isTriggered) { m_bACE_Medical_SecondChanceTriggered = isTriggered; + + // Log state change + Print("ACE Medical: Second chance triggered state set to " + isTriggered, LogLevel.INFO); } - + //------------------------------------------------------------------------------------------------ //! Check if second chance was used - bool ACE_Medical_WasSecondChanceTrigged() + bool ACE_Medical_WasSecondChanceTriggered() { return m_bACE_Medical_SecondChanceTriggered; } From 193cc2d19715295128e57d3cebc927a6234a20b8 Mon Sep 17 00:00:00 2001 From: fedora737 Date: Tue, 20 Aug 2024 16:10:15 -0500 Subject: [PATCH 2/3] Medical - additional error handling, refactor, and optimization --- .../ACE_Medical/Character/SCR_CharacterControllerComponent.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/medical/scripts/Game/ACE_Medical/Character/SCR_CharacterControllerComponent.c b/addons/medical/scripts/Game/ACE_Medical/Character/SCR_CharacterControllerComponent.c index 51b73f43..e815b665 100644 --- a/addons/medical/scripts/Game/ACE_Medical/Character/SCR_CharacterControllerComponent.c +++ b/addons/medical/scripts/Game/ACE_Medical/Character/SCR_CharacterControllerComponent.c @@ -7,7 +7,7 @@ modded class SCR_CharacterControllerComponent : CharacterControllerComponent protected const float ACE_MEDICAL_SECOND_CHANCE_DEACTIVATION_TIMEOUT_MS = 1000; //------------------------------------------------------------------------------------------------ - //! Initialize member variables + //! Initialize member variables. override void OnInit(IEntity owner) { super.OnInit(owner); From a4f389d0b6819f7995940b472dcd9ad7182dce73 Mon Sep 17 00:00:00 2001 From: fedora737 Date: Tue, 20 Aug 2024 16:10:58 -0500 Subject: [PATCH 3/3] Medical - additional error handling and optimization, framework for additional pain management --- .../Components/Damage/SCR_CharacterDamageManagerComponent.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/medical/scripts/Game/ACE_Medical/Components/Damage/SCR_CharacterDamageManagerComponent.c b/addons/medical/scripts/Game/ACE_Medical/Components/Damage/SCR_CharacterDamageManagerComponent.c index 96247688..a74ffd52 100644 --- a/addons/medical/scripts/Game/ACE_Medical/Components/Damage/SCR_CharacterDamageManagerComponent.c +++ b/addons/medical/scripts/Game/ACE_Medical/Components/Damage/SCR_CharacterDamageManagerComponent.c @@ -25,7 +25,7 @@ modded class SCR_CharacterDamageManagerComponent : SCR_DamageManagerComponent protected bool m_bACE_Medical_SecondChanceTriggered = false; //----------------------------------------------------------------------------------------------------------- - //! Initialize member variables + //! Initialize member variables. override void OnInit(IEntity owner) { super.OnInit(owner);