fish-station/Content.Server/_Sunrise/Anomaly/Systems/AnomalyAutoInjectorSystem.cs
2025-12-26 17:44:36 +03:00

168 lines
6 KiB
C#

// Система для заражения цели случайной аномалией через двухэтапный процесс:
// 1. Заражение - визуальный эффект галлюцинаций и запуск таймера
// 2. Превращение - конец таймера, применение урона и добавление компонента аномалии
// дальше блокирует повторное использование, меняет спрайт и выводит попапы (звук инъекции воспроизводится напрямую)
using Content.Shared._Sunrise.Anomaly.Components;
using Content.Shared.Interaction;
using Content.Shared.Mobs.Components;
using Content.Shared.Popups;
using Robust.Shared.Audio;
using Robust.Shared.Prototypes;
using Robust.Shared.GameObjects;
using System.Collections.Generic;
using Robust.Shared.Random;
using System.Linq;
using System;
using Robust.Shared.Timing;
using Content.Shared.StatusEffectNew;
using Content.Shared.Damage;
using Content.Shared.Damage.Prototypes;
using Robust.Shared.Log;
using Robust.Shared.Audio.Systems;
using Content.Shared.Humanoid;
using System.Diagnostics.CodeAnalysis;
using Content.Shared._Sunrise.Drugs;
using Content.Shared.Anomaly.Components;
using Content.Shared.Damage.Systems;
namespace Content.Server._Sunrise.Anomaly.Systems;
public sealed partial class AnomalyAutoInjectorSystem : EntitySystem
{
private static readonly ProtoId<DamageTypePrototype> CellularDamageType = "Cellular";
[Dependency] private readonly SharedPopupSystem _popup = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly IPrototypeManager _proto = default!;
[Dependency] private readonly DamageableSystem _damageableSystem = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly StatusEffectsSystem _statusEffects = default!;
[Dependency] private readonly IGameTiming _timing = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<AnomalyAutoInjectorComponent, AfterInteractEvent>(OnAfterInteract);
}
private bool IsValidTargetForInjection(EntityUid target, EntityUid injector, AnomalyAutoInjectorComponent comp, [NotNullWhen(false)] out string? popup)
{
popup = null;
if (!HasComp<HumanoidAppearanceComponent>(target))
{
popup = comp.PopupNotApplicable;
return false;
}
if (HasComp<UsedAnomalyAutoInjectorComponent>(injector))
{
popup = comp.PopupNothingToInject;
return false;
}
if (HasComp<PendingAnomalyInfectionComponent>(target))
{
popup = comp.PopupPending;
return false;
}
if (HasComp<InnerBodyAnomalyComponent>(target))
{
popup = comp.PopupInfected;
return false;
}
return true;
}
private void OnAfterInteract(EntityUid uid, AnomalyAutoInjectorComponent comp, AfterInteractEvent args)
{
if (args.Handled || !args.CanReach || args.Target is not { } target)
return;
if (!IsValidTargetForInjection(target, uid, comp, out var popup))
{
if (popup != null)
_popup.PopupEntity(Loc.GetString(popup), target, args.User);
return;
}
if (comp.AnomalyTrapProtos.Count == 0)
{
args.Handled = true;
return;
}
EnsureComp<UsedAnomalyAutoInjectorComponent>(uid);
args.Handled = true;
_audio.PlayPvs(comp.HypospraySound, uid);
_statusEffects.TryAddStatusEffectDuration(target, comp.RainbowEffect, TimeSpan.FromSeconds(comp.RainbowDuration));
if (TryComp<SeeingRainbowsWeakStatusEffectComponent>(target, out var rainbowComp))
{
rainbowComp.Intensity = comp.RainbowEffectIntensity;
Dirty(target, rainbowComp);
}
var pending = EnsureComp<PendingAnomalyInfectionComponent>(target);
pending.EndAt = _timing.CurTime + TimeSpan.FromSeconds(comp.AnomalyDelay);
pending.CellularDamage = comp.CellularDamage;
pending.SelectedAnomalyTrapProtoId = _random.Pick(comp.AnomalyTrapProtos);
}
public override void Update(float frameTime)
{
base.Update(frameTime);
var now = _timing.CurTime;
var query = EntityQueryEnumerator<PendingAnomalyInfectionComponent>();
while (query.MoveNext(out var uid, out var pending))
{
if (pending.EndAt > now)
continue;
if (!Exists(uid))
continue;
var damage = new DamageSpecifier(_proto.Index<DamageTypePrototype>(CellularDamageType), pending.CellularDamage);
_damageableSystem.TryChangeDamage(uid, damage);
if (!HasComp<InnerBodyAnomalyComponent>(uid))
{
if (TryGetInjectionComponents(pending.SelectedAnomalyTrapProtoId!.Value, out var comps))
{
EntityManager.AddComponents(uid, comps);
}
}
RemCompDeferred<PendingAnomalyInfectionComponent>(uid);
}
}
private bool TryGetInjectionComponents(EntProtoId protoId, [NotNullWhen(true)] out ComponentRegistry? injectionComponents)
{
injectionComponents = null;
if (!_proto.TryIndex<EntityPrototype>(protoId, out var protoTrap))
return false;
InnerBodyAnomalyInjectorComponent? anomalyInjector = null;
foreach (var compData in protoTrap.Components.Values)
{
if (compData.Component is InnerBodyAnomalyInjectorComponent injector)
{
anomalyInjector = injector;
break;
}
}
if (anomalyInjector == null)
return false;
injectionComponents = anomalyInjector.InjectionComponents;
return true;
}
}