using Content.Server.Body.Systems; using Content.Server.DoAfter; using Content.Server.Popups; using Content.Shared.Actions; using Content.Shared.CharacterAppearance.Components; using Content.Shared.Chemistry.Components; using Content.Shared.MobState; using Content.Shared.MobState.Components; using Robust.Shared.Containers; using Robust.Shared.Player; using System.Threading; namespace Content.Server.Dragon { public sealed class DragonSystem : EntitySystem { [Dependency] private readonly SharedActionsSystem _actionsSystem = default!; [Dependency] private readonly DoAfterSystem _doAfterSystem = default!; [Dependency] private readonly PopupSystem _popupSystem = default!; [Dependency] private readonly BloodstreamSystem _bloodstreamSystem = default!; [Dependency] private readonly SharedContainerSystem _containerSystem = default!; [Dependency] private readonly SharedAudioSystem _audioSystem = default!; public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnStartup); SubscribeLocalEvent(OnDragonDevourComplete); SubscribeLocalEvent(OnDevourAction); SubscribeLocalEvent(OnDragonSpawnAction); SubscribeLocalEvent(OnDragonStructureDevourComplete); SubscribeLocalEvent(OnDragonDevourCancelled); SubscribeLocalEvent(OnMobStateChanged); } private void OnMobStateChanged(EntityUid uid, DragonComponent component, MobStateChangedEvent args) { //Empties the stomach upon death //TODO: Do this when the dragon gets butchered instead if (args.CurrentMobState == DamageState.Dead) { if (component.SoundDeath != null) _audioSystem.PlayPvs(component.SoundDeath, uid, component.SoundDeath.Params); component.DragonStomach.EmptyContainer(); } } private void OnDragonDevourCancelled(EntityUid uid, DragonComponent component, DragonDevourCancelledEvent args) { component.CancelToken = null; } private void OnDragonDevourComplete(EntityUid uid, DragonComponent component, DragonDevourComplete args) { component.CancelToken = null; var ichorInjection = new Solution(component.DevourChem, component.DevourHealRate); //Humanoid devours allow dragon to get eggs, corpses included if (EntityManager.HasComponent(args.Target)) { // Add a spawn for a consumed humanoid component.SpawnsLeft = Math.Min(component.SpawnsLeft + 1, component.MaxSpawns); } //Non-humanoid mobs can only heal dragon for half the normal amount, with no additional spawn tickets else { ichorInjection.ScaleSolution(0.5f); } _bloodstreamSystem.TryAddToChemicals(uid, ichorInjection); component.DragonStomach.Insert(args.Target); if (component.SoundDevour != null) _audioSystem.PlayPvs(component.SoundDevour, uid, component.SoundDevour.Params); } private void OnDragonStructureDevourComplete(EntityUid uid, DragonComponent component, DragonStructureDevourComplete args) { component.CancelToken = null; //TODO: Figure out a better way of removing structures via devour that still entails standing still and waiting for a DoAfter. Somehow. EntityManager.QueueDeleteEntity(args.Target); if (component.SoundDevour != null) _audioSystem.PlayPvs(component.SoundDevour, uid, component.SoundDevour.Params); } private void OnStartup(EntityUid uid, DragonComponent component, ComponentStartup args) { component.SpawnsLeft = Math.Min(component.SpawnsLeft, component.MaxSpawns); //Dragon doesn't actually chew, since he sends targets right into his stomach. //I did it mom, I added ERP content into upstream. Legally! component.DragonStomach = _containerSystem.EnsureContainer(uid, "dragon_stomach"); if (component.DevourAction != null) _actionsSystem.AddAction(uid, component.DevourAction, null); if (component.SpawnAction != null) _actionsSystem.AddAction(uid, component.SpawnAction, null); if (component.SoundRoar != null) _audioSystem.Play(component.SoundRoar, Filter.Pvs(uid, 4f, EntityManager), uid, component.SoundRoar.Params); } /// /// The devour action /// private void OnDevourAction(EntityUid uid, DragonComponent component, DragonDevourActionEvent args) { if (component.CancelToken != null || args.Handled || component.DevourWhitelist?.IsValid(args.Target, EntityManager) != true) { return; } args.Handled = true; var target = args.Target; // Structure and mob devours handled differently. if (EntityManager.TryGetComponent(target, out MobStateComponent? targetState)) { switch (targetState.CurrentState) { case DamageState.Critical: case DamageState.Dead: component.CancelToken = new CancellationTokenSource(); _doAfterSystem.DoAfter(new DoAfterEventArgs(uid, component.DevourTime, component.CancelToken.Token, target) { UserFinishedEvent = new DragonDevourComplete(uid, target), UserCancelledEvent = new DragonDevourCancelledEvent(), BreakOnTargetMove = true, BreakOnUserMove = true, BreakOnStun = true, }); break; default: _popupSystem.PopupEntity(Loc.GetString("devour-action-popup-message-fail-target-alive"), uid, Filter.Entities(uid)); break; } return; } _popupSystem.PopupEntity(Loc.GetString("devour-action-popup-message-structure"), uid, Filter.Entities(uid)); if (component.SoundStructureDevour != null) _audioSystem.PlayPvs(component.SoundStructureDevour, uid, component.SoundStructureDevour.Params); component.CancelToken = new CancellationTokenSource(); _doAfterSystem.DoAfter(new DoAfterEventArgs(uid, component.StructureDevourTime, component.CancelToken.Token, target) { UserFinishedEvent = new DragonStructureDevourComplete(uid, target), UserCancelledEvent = new DragonDevourCancelledEvent(), BreakOnTargetMove = true, BreakOnUserMove = true, BreakOnStun = true, }); } private void OnDragonSpawnAction(EntityUid dragonuid, DragonComponent component, DragonSpawnActionEvent args) { if (component.SpawnPrototype == null) return; // If dragon has spawns then add one. if (component.SpawnsLeft > 0) { Spawn(component.SpawnPrototype, Transform(dragonuid).Coordinates); component.SpawnsLeft--; return; } _popupSystem.PopupEntity(Loc.GetString("dragon-spawn-action-popup-message-fail-no-eggs"), dragonuid, Filter.Entities(dragonuid)); } private sealed class DragonDevourComplete : EntityEventArgs { public EntityUid User { get; } public EntityUid Target { get; } public DragonDevourComplete(EntityUid user, EntityUid target) { User = user; Target = target; } } private sealed class DragonStructureDevourComplete : EntityEventArgs { public EntityUid User { get; } public EntityUid Target { get; } public DragonStructureDevourComplete(EntityUid user, EntityUid target) { User = user; Target = target; } } private sealed class DragonDevourCancelledEvent : EntityEventArgs {} } }