using Content.Shared.Popups; using Content.Shared.Actions; using Content.Shared.Actions.Events; using Content.Shared.Alert; using Content.Shared.Coordinates.Helpers; using Content.Shared.IdentityManagement; using Content.Shared.Maps; using Content.Shared.Paper; using Content.Shared.Physics; using Content.Shared.Speech.Muting; using Robust.Shared.Containers; using Robust.Shared.Map; using Robust.Shared.Timing; namespace Content.Shared.Abilities.Mime; public sealed class MimePowersSystem : EntitySystem { [Dependency] private readonly SharedPopupSystem _popupSystem = default!; [Dependency] private readonly SharedActionsSystem _actionsSystem = default!; [Dependency] private readonly AlertsSystem _alertsSystem = default!; [Dependency] private readonly TurfSystem _turf = default!; [Dependency] private readonly IMapManager _mapMan = default!; [Dependency] private readonly SharedContainerSystem _container = default!; [Dependency] private readonly IGameTiming _timing = default!; public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnComponentInit); SubscribeLocalEvent(OnComponentShutdown); SubscribeLocalEvent(OnInvisibleWall); SubscribeLocalEvent(OnBreakVowAlert); SubscribeLocalEvent(OnRetakeVowAlert); } public override void Update(float frameTime) { base.Update(frameTime); // Queue to track whether mimes can retake vows yet var query = EntityQueryEnumerator(); while (query.MoveNext(out var uid, out var mime)) { if (!mime.VowBroken || mime.ReadyToRepent) continue; if (_timing.CurTime < mime.VowRepentTime) continue; mime.ReadyToRepent = true; Dirty(uid, mime); _popupSystem.PopupClient(Loc.GetString("mime-ready-to-repent"), uid, uid); } } private void OnComponentInit(Entity ent, ref ComponentInit args) { EnsureComp(ent); if (ent.Comp.PreventWriting) { EnsureComp(ent, out var illiterateComponent); illiterateComponent.FailWriteMessage = ent.Comp.FailWriteMessage; Dirty(ent, illiterateComponent); } _alertsSystem.ShowAlert(ent.Owner, ent.Comp.VowAlert); _actionsSystem.AddAction(ent, ref ent.Comp.InvisibleWallActionEntity, ent.Comp.InvisibleWallAction); } private void OnComponentShutdown(Entity ent, ref ComponentShutdown args) { _actionsSystem.RemoveAction(ent.Owner, ent.Comp.InvisibleWallActionEntity); } /// /// Creates an invisible wall in a free space after some checks. /// private void OnInvisibleWall(Entity ent, ref InvisibleWallActionEvent args) { if (!ent.Comp.Enabled) return; if (_container.IsEntityOrParentInContainer(ent)) return; var xform = Transform(ent); // Get the tile in front of the mime var offsetValue = xform.LocalRotation.ToWorldVec(); var coords = xform.Coordinates.Offset(offsetValue).SnapToGrid(EntityManager, _mapMan); var tile = _turf.GetTileRef(coords); if (tile == null) return; // Check if the tile is blocked by a wall or mob, and don't create the wall if so if (_turf.IsTileBlocked(tile.Value, CollisionGroup.Impassable | CollisionGroup.Opaque)) { _popupSystem.PopupClient(Loc.GetString("mime-invisible-wall-failed"), ent, ent); return; } var messageSelf = Loc.GetString("mime-invisible-wall-popup-self", ("mime", Identity.Entity(ent.Owner, EntityManager))); var messageOthers = Loc.GetString("mime-invisible-wall-popup-others", ("mime", Identity.Entity(ent.Owner, EntityManager))); _popupSystem.PopupPredicted(messageSelf, messageOthers, ent, ent); // Make sure we set the invisible wall to despawn properly PredictedSpawnAtPosition(ent.Comp.WallPrototype, _turf.GetTileCenter(tile.Value)); // Handle args so cooldown works args.Handled = true; } private void OnBreakVowAlert(Entity ent, ref BreakVowAlertEvent args) { if (args.Handled) return; BreakVow(ent, ent); args.Handled = true; } private void OnRetakeVowAlert(Entity ent, ref RetakeVowAlertEvent args) { if (args.Handled) return; RetakeVow(ent, ent); args.Handled = true; } /// /// Break this mime's vow to not speak. /// public void BreakVow(EntityUid uid, MimePowersComponent? mimePowers = null) { if (!Resolve(uid, ref mimePowers)) return; if (mimePowers.VowBroken) return; mimePowers.Enabled = false; mimePowers.VowBroken = true; mimePowers.VowRepentTime = _timing.CurTime + mimePowers.VowCooldown; Dirty(uid, mimePowers); RemComp(uid); if (mimePowers.PreventWriting) RemComp(uid); _alertsSystem.ClearAlert(uid, mimePowers.VowAlert); _alertsSystem.ShowAlert(uid, mimePowers.VowBrokenAlert); _actionsSystem.RemoveAction(uid, mimePowers.InvisibleWallActionEntity); } /// /// Retake this mime's vow to not speak. /// public void RetakeVow(EntityUid uid, MimePowersComponent? mimePowers = null) { if (!Resolve(uid, ref mimePowers)) return; if (!mimePowers.ReadyToRepent) { _popupSystem.PopupClient(Loc.GetString("mime-not-ready-repent"), uid, uid); return; } mimePowers.Enabled = true; mimePowers.ReadyToRepent = false; mimePowers.VowBroken = false; Dirty(uid, mimePowers); AddComp(uid); if (mimePowers.PreventWriting) { EnsureComp(uid, out var illiterateComponent); illiterateComponent.FailWriteMessage = mimePowers.FailWriteMessage; Dirty(uid, illiterateComponent); } _alertsSystem.ClearAlert(uid, mimePowers.VowBrokenAlert); _alertsSystem.ShowAlert(uid, mimePowers.VowAlert); _actionsSystem.AddAction(uid, ref mimePowers.InvisibleWallActionEntity, mimePowers.InvisibleWallAction, uid); } }