242 lines
9.7 KiB
C#
242 lines
9.7 KiB
C#
using System.Linq;
|
|
using Content.Server.Popups;
|
|
using Content.Shared._DV.CosmicCult.Components.Examine;
|
|
using Content.Shared._DV.CosmicCult.Components;
|
|
using Content.Shared._DV.CosmicCult.Prototypes;
|
|
using Content.Shared._DV.CosmicCult;
|
|
using Content.Shared.Actions.Components;
|
|
using Content.Shared.Damage.Systems;
|
|
using Content.Shared.Humanoid;
|
|
using Content.Shared.Interaction;
|
|
using Content.Shared.UserInterface;
|
|
using Content.Shared.Mobs.Systems;
|
|
using Content.Shared.Verbs;
|
|
using Robust.Server.Audio;
|
|
using Robust.Shared.Audio;
|
|
using Robust.Shared.Containers;
|
|
using Robust.Shared.Prototypes;
|
|
using Robust.Shared.Timing;
|
|
|
|
namespace Content.Server._DV.CosmicCult;
|
|
|
|
public sealed class CosmicGlyphSystem : SharedCosmicGlyphSystem
|
|
{
|
|
[Dependency] private readonly DamageableSystem _damageable = default!;
|
|
[Dependency] private readonly EntityLookupSystem _lookup = default!;
|
|
[Dependency] private readonly PopupSystem _popup = default!;
|
|
[Dependency] private readonly MobStateSystem _mobState = default!;
|
|
[Dependency] private readonly AudioSystem _audio = default!;
|
|
[Dependency] private readonly SharedContainerSystem _container = default!;
|
|
[Dependency] private readonly CosmicCultRuleSystem _cultRule = default!;
|
|
[Dependency] private readonly SharedCosmicCultSystem _cosmicCult = default!;
|
|
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
|
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
|
[Dependency] private readonly SharedUserInterfaceSystem _ui = default!;
|
|
[Dependency] private readonly IGameTiming _timing = default!;
|
|
|
|
private readonly HashSet<Entity<CosmicCultComponent>> _cultists = [];
|
|
private readonly HashSet<Entity<HumanoidProfileComponent>> _humanoids = [];
|
|
|
|
public override void Initialize()
|
|
{
|
|
SubscribeLocalEvent<CosmicGlyphComponent, ActivateInWorldEvent>(OnUseGlyph);
|
|
SubscribeLocalEvent<CosmicGlyphComponent, ComponentStartup>(OnGlyphCreated);
|
|
SubscribeLocalEvent<CosmicGlyphComponent, GetVerbsEvent<AlternativeVerb>>(OnGetAlternativeVerbs);
|
|
SubscribeLocalEvent<CosmicCultComponent, EventCosmicDrawGlyph>(OnDrawGlyph);
|
|
SubscribeLocalEvent<CosmicGlyphDrawComponent, CosmicGlyphDrawSelectedMessage>(OnGlyphSelected);
|
|
base.Initialize();
|
|
}
|
|
|
|
private void OnGetAlternativeVerbs(Entity<CosmicGlyphComponent> ent, ref GetVerbsEvent<AlternativeVerb> args)
|
|
{
|
|
if (!args.CanInteract || !args.CanAccess || !_cosmicCult.EntityIsCultist(args.User) || ent.Comp.State == GlyphStatus.Despawning)
|
|
return;
|
|
|
|
args.Verbs.Add(new AlternativeVerb
|
|
{
|
|
Text = Loc.GetString("cult-glyph-verb-erase"),
|
|
Act = () => EraseGlyph(ent),
|
|
});
|
|
}
|
|
|
|
private void OnDrawGlyph(Entity<CosmicCultComponent> ent, ref EventCosmicDrawGlyph args)
|
|
{
|
|
if (_cultRule.AssociatedGamerule(ent) is not { } cult ||
|
|
!TryComp<CosmicGlyphDrawComponent>(args.Action, out var drawComp))
|
|
return;
|
|
|
|
args.Handled = true;
|
|
|
|
var glyphs = GetAvailableGlyphs(cult.Comp.CurrentTier);
|
|
if (glyphs.Count == 0)
|
|
{
|
|
drawComp.Target = null;
|
|
_ui.CloseUi(args.Action.Owner, CosmicGlyphDrawUiKey.Key, args.Performer);
|
|
return;
|
|
}
|
|
|
|
drawComp.Target = args.Target;
|
|
_ui.OpenUi(args.Action.Owner, CosmicGlyphDrawUiKey.Key, args.Performer);
|
|
_ui.SetUiState(args.Action.Owner, CosmicGlyphDrawUiKey.Key, new CosmicGlyphDrawBuiState(glyphs));
|
|
}
|
|
|
|
private void OnGlyphSelected(Entity<CosmicGlyphDrawComponent> ent, ref CosmicGlyphDrawSelectedMessage args)
|
|
{
|
|
if (ent.Comp.Target is not { } target || !TryComp<ActionComponent>(ent, out var action) || action.Container != args.Actor
|
|
|| !TryComp<CosmicCultComponent>(args.Actor, out _) || _cultRule.AssociatedGamerule(args.Actor) is not { } cult
|
|
|| !_prototype.TryIndex(args.GlyphProtoId, out var glyph) || glyph.Tier > cult.Comp.CurrentTier)
|
|
{
|
|
ent.Comp.Target = null;
|
|
_ui.CloseUi(ent.Owner, CosmicGlyphDrawUiKey.Key, args.Actor);
|
|
return;
|
|
}
|
|
|
|
var spawned = Spawn(glyph.Entity, target);
|
|
_cultRule.TransferCultAssociation(args.Actor, spawned);
|
|
|
|
ent.Comp.Target = null;
|
|
_ui.CloseUi(ent.Owner, CosmicGlyphDrawUiKey.Key, args.Actor);
|
|
}
|
|
|
|
private List<ProtoId<GlyphPrototype>> GetAvailableGlyphs(int currentTier)
|
|
{
|
|
return _prototype.EnumeratePrototypes<GlyphPrototype>()
|
|
.Where(glyph => glyph.Tier <= currentTier)
|
|
.OrderBy(glyph => Loc.GetString(glyph.Name))
|
|
.Select(glyph => new ProtoId<GlyphPrototype>(glyph.ID))
|
|
.ToList();
|
|
}
|
|
|
|
#region Base trigger
|
|
|
|
private void OnGlyphCreated(Entity<CosmicGlyphComponent> ent, ref ComponentStartup args)
|
|
{
|
|
ent.Comp.Timer = _timing.CurTime + ent.Comp.SpawnTime;
|
|
}
|
|
|
|
public override void Update(float frameTime)
|
|
{
|
|
base.Update(frameTime);
|
|
|
|
var glyphQuery = EntityQueryEnumerator<CosmicGlyphComponent>();
|
|
while (glyphQuery.MoveNext(out var uid, out var comp))
|
|
{
|
|
if (_timing.CurTime < comp.Timer) continue;
|
|
if (comp.State == GlyphStatus.Spawning || comp.State == GlyphStatus.Cooldown)
|
|
{
|
|
_appearance.SetData(uid, GlyphVisuals.Status, GlyphStatus.Ready);
|
|
comp.State = GlyphStatus.Ready;
|
|
return;
|
|
}
|
|
if (comp.State == GlyphStatus.Active)
|
|
{
|
|
ActivateGlyph(new Entity<CosmicGlyphComponent>(uid, comp));
|
|
}
|
|
if (comp.State == GlyphStatus.Despawning)
|
|
{
|
|
QueueDel(uid);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void OnUseGlyph(Entity<CosmicGlyphComponent> uid, ref ActivateInWorldEvent args)
|
|
{
|
|
var tgtpos = Transform(uid).Coordinates;
|
|
var userCoords = Transform(args.User).Coordinates;
|
|
if (args.Handled || !userCoords.TryDistance(EntityManager, tgtpos, out var distance) || distance > uid.Comp.ActivationRange || !_cosmicCult.EntityIsCultist(args.User) || uid.Comp.State != GlyphStatus.Ready)
|
|
return;
|
|
var cultists = GatherCultists(uid, uid.Comp.ActivationRange);
|
|
if (cultists.Count < uid.Comp.RequiredCultists)
|
|
{
|
|
_popup.PopupEntity(Loc.GetString("cult-glyph-not-enough-cultists"), uid, args.User);
|
|
return;
|
|
}
|
|
|
|
var ev = new CheckGlyphConditionsEvent(args.User, cultists);
|
|
RaiseLocalEvent(uid, ref ev);
|
|
if (ev.Cancelled) return;
|
|
|
|
args.Handled = true;
|
|
uid.Comp.User = args.User;
|
|
if (uid.Comp.ActivationTime > TimeSpan.FromSeconds(0))
|
|
{
|
|
_appearance.SetData(uid, GlyphVisuals.Status, GlyphStatus.Active);
|
|
uid.Comp.State = GlyphStatus.Active;
|
|
uid.Comp.Timer = _timing.CurTime + uid.Comp.ActivationTime;
|
|
_audio.PlayPvs(uid.Comp.ChargeSFX, Transform(uid).Coordinates);
|
|
}
|
|
else ActivateGlyph(uid);
|
|
}
|
|
|
|
private void ActivateGlyph(Entity<CosmicGlyphComponent> ent)
|
|
{
|
|
if (ent.Comp.EraseOnUse)
|
|
{
|
|
EraseGlyph(ent);
|
|
}
|
|
else if (ent.Comp.CooldownTime > TimeSpan.FromSeconds(0))
|
|
{
|
|
_appearance.SetData(ent, GlyphVisuals.Status, GlyphStatus.Cooldown);
|
|
ent.Comp.State = GlyphStatus.Cooldown;
|
|
ent.Comp.Timer = _timing.CurTime + ent.Comp.CooldownTime;
|
|
}
|
|
else
|
|
{
|
|
_appearance.SetData(ent, GlyphVisuals.Status, GlyphStatus.Ready);
|
|
ent.Comp.State = GlyphStatus.Ready;
|
|
}
|
|
|
|
if (ent.Comp.User is not { } user) return;
|
|
var cultists = GatherCultists(ent, ent.Comp.ActivationRange);
|
|
var tryInvokeEv = new TryActivateGlyphEvent(user, cultists);
|
|
RaiseLocalEvent(ent, ref tryInvokeEv);
|
|
var tgtpos = Transform(ent).Coordinates;
|
|
if (tryInvokeEv.Cancelled || cultists.Count < ent.Comp.RequiredCultists)
|
|
{
|
|
_audio.PlayPvs(ent.Comp.FailSFX, tgtpos);
|
|
return;
|
|
}
|
|
|
|
var damage = ent.Comp.ActivationDamage / cultists.Count;
|
|
foreach (var cultist in cultists)
|
|
{
|
|
_damageable.TryChangeDamage(cultist.Owner, damage, true);
|
|
}
|
|
|
|
_audio.PlayPvs(ent.Comp.TriggerSFX, tgtpos, AudioParams.Default.WithVolume(+1f));
|
|
Spawn(ent.Comp.GlyphVFX, tgtpos);
|
|
ent.Comp.User = null;
|
|
}
|
|
#endregion
|
|
|
|
#region Housekeeping
|
|
/// <summary>
|
|
/// Gets all cultists/constructs near a glyph.
|
|
/// </summary>
|
|
public HashSet<Entity<CosmicCultComponent>> GatherCultists(EntityUid uid, float range)
|
|
{
|
|
_cultists.Clear();
|
|
_lookup.GetEntitiesInRange<CosmicCultComponent>(Transform(uid).Coordinates, range, _cultists);
|
|
_cultists.RemoveWhere(entity => !_mobState.IsAlive(entity) || _container.IsEntityInContainer(entity));
|
|
return _cultists;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets all the humanoids near a glyph.
|
|
/// </summary>
|
|
/// <param name="uid">The glyph.</param>
|
|
/// <param name="range">Radius for a lookup.</param>
|
|
/// <param name="exclude">Filter to exclude from return.</param>
|
|
public HashSet<Entity<HumanoidProfileComponent>> GetTargetsNearGlyph(EntityUid uid, float range, Predicate<Entity<HumanoidProfileComponent>>? exclude = null)
|
|
{
|
|
_humanoids.Clear();
|
|
_lookup.GetEntitiesInRange<HumanoidProfileComponent>(Transform(uid).Coordinates, range, _humanoids);
|
|
if (exclude != null)
|
|
_humanoids.RemoveWhere(exclude);
|
|
_humanoids.RemoveWhere(target => HasComp<CosmicBlankComponent>(target) || HasComp<CosmicLapseComponent>(target)); // We never want these.
|
|
|
|
return _humanoids;
|
|
}
|
|
#endregion
|
|
}
|