+++ /dev/null
-using Content.Shared.Alert;
-using Robust.Shared.Prototypes;
-using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
-using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
-
-namespace Content.Server.Abilities.Mime
-{
- /// <summary>
- /// Lets its owner entity use mime powers, like placing invisible walls.
- /// </summary>
- [RegisterComponent]
- public sealed partial class MimePowersComponent : Component
- {
- /// <summary>
- /// Whether this component is active or not.
- /// </summarY>
- [DataField("enabled")]
- public bool Enabled = true;
-
- /// <summary>
- /// The wall prototype to use.
- /// </summary>
- [DataField("wallPrototype", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
- public string WallPrototype = "WallInvisible";
-
- [DataField("invisibleWallAction", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
- public string? InvisibleWallAction = "ActionMimeInvisibleWall";
-
- [DataField("invisibleWallActionEntity")] public EntityUid? InvisibleWallActionEntity;
-
- // The vow zone lies below
- public bool VowBroken = false;
-
- /// <summary>
- /// Whether this mime is ready to take the vow again.
- /// Note that if they already have the vow, this is also false.
- /// </summary>
- public bool ReadyToRepent = false;
-
- /// <summary>
- /// Time when the mime can repent their vow
- /// </summary>
- [DataField("vowRepentTime", customTypeSerializer: typeof(TimeOffsetSerializer))]
- public TimeSpan VowRepentTime = TimeSpan.Zero;
-
- /// <summary>
- /// How long it takes the mime to get their powers back
- /// </summary>
- [DataField("vowCooldown")]
- public TimeSpan VowCooldown = TimeSpan.FromMinutes(5);
-
- [DataField]
- public ProtoId<AlertPrototype> VowAlert = "VowOfSilence";
-
- [DataField]
- public ProtoId<AlertPrototype> VowBrokenAlert = "VowBroken";
-
- /// <summary>
- /// Does this component prevent the mime from writing on paper while their vow is active?
- /// </summary>
- [DataField]
- public bool PreventWriting = false;
-
- /// <summary>
- /// What message is displayed when the mime fails to write?
- /// </summary>
- [DataField]
- public LocId FailWriteMessage = "paper-component-illiterate-mime";
- }
-}
+++ /dev/null
-using Content.Server.Popups;
-using Content.Shared.Abilities.Mime;
-using Content.Shared.Actions;
-using Content.Shared.Actions.Events;
-using Content.Shared.Alert;
-using Content.Shared.Coordinates.Helpers;
-using Content.Shared.Maps;
-using Content.Shared.Paper;
-using Content.Shared.Physics;
-using Robust.Shared.Containers;
-using Robust.Shared.Map;
-using Robust.Shared.Timing;
-using Content.Shared.Speech.Muting;
-
-namespace Content.Server.Abilities.Mime
-{
- public sealed class MimePowersSystem : EntitySystem
- {
- [Dependency] private readonly PopupSystem _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<MimePowersComponent, ComponentInit>(OnComponentInit);
- SubscribeLocalEvent<MimePowersComponent, InvisibleWallActionEvent>(OnInvisibleWall);
-
- SubscribeLocalEvent<MimePowersComponent, BreakVowAlertEvent>(OnBreakVowAlert);
- SubscribeLocalEvent<MimePowersComponent, RetakeVowAlertEvent>(OnRetakeVowAlert);
- }
-
- public override void Update(float frameTime)
- {
- base.Update(frameTime);
- // Queue to track whether mimes can retake vows yet
-
- var query = EntityQueryEnumerator<MimePowersComponent>();
- while (query.MoveNext(out var uid, out var mime))
- {
- if (!mime.VowBroken || mime.ReadyToRepent)
- continue;
-
- if (_timing.CurTime < mime.VowRepentTime)
- continue;
-
- mime.ReadyToRepent = true;
- _popupSystem.PopupEntity(Loc.GetString("mime-ready-to-repent"), uid, uid);
- }
- }
-
- private void OnComponentInit(EntityUid uid, MimePowersComponent component, ComponentInit args)
- {
- EnsureComp<MutedComponent>(uid);
- if (component.PreventWriting)
- {
- EnsureComp<BlockWritingComponent>(uid, out var illiterateComponent);
- illiterateComponent.FailWriteMessage = component.FailWriteMessage;
- Dirty(uid, illiterateComponent);
- }
-
- _alertsSystem.ShowAlert(uid, component.VowAlert);
- _actionsSystem.AddAction(uid, ref component.InvisibleWallActionEntity, component.InvisibleWallAction, uid);
- }
-
- /// <summary>
- /// Creates an invisible wall in a free space after some checks.
- /// </summary>
- private void OnInvisibleWall(EntityUid uid, MimePowersComponent component, InvisibleWallActionEvent args)
- {
- if (!component.Enabled)
- return;
-
- if (_container.IsEntityOrParentInContainer(uid))
- return;
-
- var xform = Transform(uid);
- // 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.PopupEntity(Loc.GetString("mime-invisible-wall-failed"), uid, uid);
- return;
- }
-
- _popupSystem.PopupEntity(Loc.GetString("mime-invisible-wall-popup", ("mime", uid)), uid);
- // Make sure we set the invisible wall to despawn properly
- Spawn(component.WallPrototype, _turf.GetTileCenter(tile.Value));
- // Handle args so cooldown works
- args.Handled = true;
- }
-
- private void OnBreakVowAlert(Entity<MimePowersComponent> ent, ref BreakVowAlertEvent args)
- {
- if (args.Handled)
- return;
- BreakVow(ent, ent);
- args.Handled = true;
- }
-
- private void OnRetakeVowAlert(Entity<MimePowersComponent> ent, ref RetakeVowAlertEvent args)
- {
- if (args.Handled)
- return;
- RetakeVow(ent, ent);
- args.Handled = true;
- }
-
- /// <summary>
- /// Break this mime's vow to not speak.
- /// </summary>
- 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;
- RemComp<MutedComponent>(uid);
- if (mimePowers.PreventWriting)
- RemComp<BlockWritingComponent>(uid);
- _alertsSystem.ClearAlert(uid, mimePowers.VowAlert);
- _alertsSystem.ShowAlert(uid, mimePowers.VowBrokenAlert);
- _actionsSystem.RemoveAction(uid, mimePowers.InvisibleWallActionEntity);
- }
-
- /// <summary>
- /// Retake this mime's vow to not speak.
- /// </summary>
- public void RetakeVow(EntityUid uid, MimePowersComponent? mimePowers = null)
- {
- if (!Resolve(uid, ref mimePowers))
- return;
-
- if (!mimePowers.ReadyToRepent)
- {
- _popupSystem.PopupEntity(Loc.GetString("mime-not-ready-repent"), uid, uid);
- return;
- }
-
- mimePowers.Enabled = true;
- mimePowers.ReadyToRepent = false;
- mimePowers.VowBroken = false;
- AddComp<MutedComponent>(uid);
- if (mimePowers.PreventWriting)
- {
- EnsureComp<BlockWritingComponent>(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);
- }
- }
-}
-using Content.Server.Abilities.Mime;
+using Content.Shared.Abilities.Mime;
using Content.Server.Chat.Systems;
using Content.Server.Popups;
using Content.Server.Speech.Components;
--- /dev/null
+using Content.Shared.Alert;
+using Robust.Shared.Prototypes;
+using Robust.Shared.GameStates;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
+
+namespace Content.Shared.Abilities.Mime;
+
+/// <summary>
+/// Lets its owner entity use mime powers, like placing invisible walls.
+/// </summary>
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
+[AutoGenerateComponentPause]
+public sealed partial class MimePowersComponent : Component
+{
+ /// <summary>
+ /// Whether this component is active or not.
+ /// </summarY>
+ [DataField, AutoNetworkedField]
+ public bool Enabled = true;
+
+ /// <summary>
+ /// The wall prototype to use.
+ /// </summary>
+ [DataField, AutoNetworkedField]
+ public EntProtoId WallPrototype = "WallInvisible";
+
+ [DataField]
+ public EntProtoId? InvisibleWallAction = "ActionMimeInvisibleWall";
+
+ [DataField, AutoNetworkedField]
+ public EntityUid? InvisibleWallActionEntity;
+
+ // The vow zone lies below
+ [DataField, AutoNetworkedField]
+ public bool VowBroken = false;
+
+ /// <summary>
+ /// Whether this mime is ready to take the vow again.
+ /// Note that if they already have the vow, this is also false.
+ /// </summary>
+ [DataField, AutoNetworkedField]
+ public bool ReadyToRepent = false;
+
+ /// <summary>
+ /// Time when the mime can repent their vow
+ /// </summary>
+ [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
+ [AutoNetworkedField, AutoPausedField]
+ public TimeSpan VowRepentTime = TimeSpan.Zero;
+
+ /// <summary>
+ /// How long it takes the mime to get their powers back
+ /// </summary>
+ [DataField, AutoNetworkedField]
+ public TimeSpan VowCooldown = TimeSpan.FromMinutes(5);
+
+ [DataField]
+ public ProtoId<AlertPrototype> VowAlert = "VowOfSilence";
+
+ [DataField]
+ public ProtoId<AlertPrototype> VowBrokenAlert = "VowBroken";
+
+ /// <summary>
+ /// Does this component prevent the mime from writing on paper while their vow is active?
+ /// </summary>
+ [DataField, AutoNetworkedField]
+ public bool PreventWriting = false;
+
+ /// <summary>
+ /// What message is displayed when the mime fails to write?
+ /// </summary>
+ [DataField]
+ public LocId FailWriteMessage = "paper-component-illiterate-mime";
+
+ public override bool SendOnlyToOwner => true;
+}
--- /dev/null
+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<MimePowersComponent, ComponentInit>(OnComponentInit);
+ SubscribeLocalEvent<MimePowersComponent, ComponentShutdown>(OnComponentShutdown);
+ SubscribeLocalEvent<MimePowersComponent, InvisibleWallActionEvent>(OnInvisibleWall);
+
+ SubscribeLocalEvent<MimePowersComponent, BreakVowAlertEvent>(OnBreakVowAlert);
+ SubscribeLocalEvent<MimePowersComponent, RetakeVowAlertEvent>(OnRetakeVowAlert);
+ }
+
+ public override void Update(float frameTime)
+ {
+ base.Update(frameTime);
+ // Queue to track whether mimes can retake vows yet
+
+ var query = EntityQueryEnumerator<MimePowersComponent>();
+ 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<MimePowersComponent> ent, ref ComponentInit args)
+ {
+ EnsureComp<MutedComponent>(ent);
+
+ if (ent.Comp.PreventWriting)
+ {
+ EnsureComp<BlockWritingComponent>(ent, out var illiterateComponent);
+ illiterateComponent.FailWriteMessage = ent.Comp.FailWriteMessage;
+ Dirty(ent, illiterateComponent);
+ }
+
+ _alertsSystem.ShowAlert(ent, ent.Comp.VowAlert);
+ _actionsSystem.AddAction(ent, ref ent.Comp.InvisibleWallActionEntity, ent.Comp.InvisibleWallAction);
+ }
+
+ private void OnComponentShutdown(Entity<MimePowersComponent> ent, ref ComponentShutdown args)
+ {
+ _actionsSystem.RemoveAction(ent.Owner, ent.Comp.InvisibleWallActionEntity);
+ }
+
+ /// <summary>
+ /// Creates an invisible wall in a free space after some checks.
+ /// </summary>
+ private void OnInvisibleWall(Entity<MimePowersComponent> 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<MimePowersComponent> ent, ref BreakVowAlertEvent args)
+ {
+ if (args.Handled)
+ return;
+
+ BreakVow(ent, ent);
+ args.Handled = true;
+ }
+
+ private void OnRetakeVowAlert(Entity<MimePowersComponent> ent, ref RetakeVowAlertEvent args)
+ {
+ if (args.Handled)
+ return;
+
+ RetakeVow(ent, ent);
+ args.Handled = true;
+ }
+
+ /// <summary>
+ /// Break this mime's vow to not speak.
+ /// </summary>
+ 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<MutedComponent>(uid);
+ if (mimePowers.PreventWriting)
+ RemComp<BlockWritingComponent>(uid);
+
+ _alertsSystem.ClearAlert(uid, mimePowers.VowAlert);
+ _alertsSystem.ShowAlert(uid, mimePowers.VowBrokenAlert);
+ _actionsSystem.RemoveAction(uid, mimePowers.InvisibleWallActionEntity);
+ }
+
+ /// <summary>
+ /// Retake this mime's vow to not speak.
+ /// </summary>
+ 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<MutedComponent>(uid);
+ if (mimePowers.PreventWriting)
+ {
+ EnsureComp<BlockWritingComponent>(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);
+ }
+}
mime-cant-speak = Your vow of silence prevents you from speaking.
-mime-invisible-wall-popup = {CAPITALIZE(THE($mime))} brushes up against an invisible wall!
+mime-invisible-wall-popup-self = You brush up against an invisible wall!
+mime-invisible-wall-popup-others = {CAPITALIZE(THE($mime))} brushes up against an invisible wall!
mime-invisible-wall-failed = You can't create an invisible wall there.
mime-not-ready-repent = You aren't ready to repent for your broken vow yet.
mime-ready-to-repent = You feel ready to take your vows again.