Files
EscapistsReintroduced/docs/evidence/epinephrine-eligibility-2026-11-05/proposal/without-consciousness-control.c
T
jakub 9caed3f12d
continuous-integration/drone/push Build is passing
Embed RHS 1.9 compatibility; record Workshop 0.0.141
2026-11-05 16:39:35 +01:00

107 lines
5.8 KiB
C

// 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<ref SCR_PersistentDamageEffect> 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<HitZone> 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<HitZone> 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);
}
}