// Native injection animation/consumption; only resilience is restored over time. // This is not a blood replacement, bandage, full heal, or resurrection. [BaseContainerProps()] class ERR_ConsumableEpinephrine : SCR_ConsumableMorphine { override bool CanApplyEffect(notnull IEntity target, notnull IEntity user, out SCR_EConsumableFailReason failReason) { SCR_CharacterDamageManagerComponent damage = SCR_CharacterDamageManagerComponent.Cast(target.FindComponent(SCR_CharacterDamageManagerComponent)); CharacterControllerComponent controller = CharacterControllerComponent.Cast(target.FindComponent(CharacterControllerComponent)); if (!damage || damage.GetState() == EDamageState.DESTROYED || !controller || !controller.IsUnconscious()) { failReason = SCR_EConsumableFailReason.UNDAMAGED; return false; } array effects = damage.GetAllPersistentEffectsOfType(ERR_EpinephrineEffect); if (!effects.IsEmpty()) { failReason = SCR_EConsumableFailReason.ALREADY_APPLIED; return false; } return true; } } class ERR_EpinephrineEffect : SCR_DotDamageEffect { override bool HijackDamageEffect(SCR_ExtendedDamageManagerComponent dmgManager) { SCR_CharacterDamageManagerComponent damage = SCR_CharacterDamageManagerComponent.Cast(dmgManager); if (damage) SetAffectedHitZone(damage.GetResilienceHitZone()); return false; } override EDamageType GetDefaultDamageType() { return EDamageType.HEALING; } } // Separate stronger field injector. Standard epinephrine above keeps its // resilience-over-time behavior; neither item restores a dead character. class ERR_CombatEpinephrineRules { static float Heal(float current, bool leg) { float amount = 0.15; if (leg) amount = 0.5; return Math.Min(current + amount, 1.0); } static float Blood(float current, float threshold) { return Math.Min(1, current + threshold + 0.01); } } [BaseContainerProps()] class ERR_ConsumableCombatEpinephrine : SCR_ConsumableMorphine { override bool CanApplyEffect(notnull IEntity target, notnull IEntity user, out SCR_EConsumableFailReason failReason) { SCR_CharacterDamageManagerComponent damage = SCR_CharacterDamageManagerComponent.Cast(target.FindComponent(SCR_CharacterDamageManagerComponent)); if (!damage || damage.GetState() == EDamageState.DESTROYED) { failReason = SCR_EConsumableFailReason.UNDAMAGED; return false; } if (damage.IsBleeding()) return true; HitZone blood = damage.GetBloodHitZone(), resilience = damage.GetResilienceHitZone(); if ((blood && blood.GetHealthScaled() <= blood.GetDamageStateThreshold(ECharacterBloodState.FAINTING)) || (resilience && resilience.GetHealthScaled() < 1)) return true; array zones = {}; damage.GetPhysicalHitZones(zones); foreach (HitZone zone : zones) if (zone && zone.GetHealthScaled() < 1) return true; failReason = SCR_EConsumableFailReason.UNDAMAGED; return false; } } class ERR_CombatEpinephrineEffect : SCR_InstantDamageEffect { override EDamageType GetDefaultDamageType() { return EDamageType.HEALING; } override void OnEffectAdded(SCR_ExtendedDamageManagerComponent dmgManager) { super.OnEffectAdded(dmgManager); if (!Replication.IsServer()) return; SCR_CharacterDamageManagerComponent damage = SCR_CharacterDamageManagerComponent.Cast(dmgManager); if (!damage || damage.GetState() == EDamageState.DESTROYED) return; foreach (ECharacterHitZoneGroup group : {ECharacterHitZoneGroup.HEAD, ECharacterHitZoneGroup.UPPERTORSO, ECharacterHitZoneGroup.LOWERTORSO, ECharacterHitZoneGroup.LEFTARM, ECharacterHitZoneGroup.RIGHTARM, ECharacterHitZoneGroup.LEFTLEG, ECharacterHitZoneGroup.RIGHTLEG}) damage.RemoveGroupBleeding(group); SCR_CharacterBloodHitZone blood = damage.GetBloodHitZone(); if (blood && blood.GetHealthScaled() <= blood.GetDamageStateThreshold(ECharacterBloodState.FAINTING)) blood.SetHealthScaled(ERR_CombatEpinephrineRules.Blood(blood.GetHealthScaled(), blood.GetDamageStateThreshold(ECharacterBloodState.UNCONSCIOUS))); SCR_CharacterResilienceHitZone resilience = damage.GetResilienceHitZone(); if (resilience) resilience.SetHealthScaled(1); array zones = {}; damage.GetPhysicalHitZones(zones); foreach (HitZone zone : zones) { SCR_CharacterHitZone physical = SCR_CharacterHitZone.Cast(zone); if (!physical || zone.GetHealthScaled() >= 1) continue; int group = physical.GetHitZoneGroup(); zone.SetHealthScaled(ERR_CombatEpinephrineRules.Heal(zone.GetHealthScaled(), group == ECharacterHitZoneGroup.LEFTLEG || group == ECharacterHitZoneGroup.RIGHTLEG)); } CharacterControllerComponent controller = CharacterControllerComponent.Cast(damage.GetOwner().FindComponent(CharacterControllerComponent)); if (controller && controller.IsUnconscious()) controller.SetUnconscious(false); } } // The held injector uses the existing native injection interaction and animation. // Only its displayed name changes; ordinary morphine keeps its native behavior. modded class SCR_MorphineUserAction { protected bool m_ERRHoldingEpinephrine; protected bool m_ERRHoldingCombatEpinephrine; override bool CanBeShownScript(IEntity user) { m_ERRHoldingEpinephrine = false; m_ERRHoldingCombatEpinephrine = false; bool shown = super.CanBeShownScript(user); ChimeraCharacter character = ChimeraCharacter.Cast(user); if (shown && character) { SCR_ConsumableItemComponent item = GetConsumableComponent(character); m_ERRHoldingEpinephrine = item && ERR_ConsumableEpinephrine.Cast(item.GetConsumableEffect()); m_ERRHoldingCombatEpinephrine = item && ERR_ConsumableCombatEpinephrine.Cast(item.GetConsumableEffect()); } return shown; } override bool GetActionNameScript(out string outName) { if (m_ERRHoldingCombatEpinephrine) { outName = "Administer combat epinephrine"; return true; } if (m_ERRHoldingEpinephrine) { outName = "Administer epinephrine"; return true; } return super.GetActionNameScript(outName); } }