Mime Powers (Vow + Invisible Wall) (#7653)
This commit is contained in:
@@ -57,6 +57,7 @@ namespace Content.Client.Entry
|
||||
"ResearchPointSource",
|
||||
"ResearchClient",
|
||||
"IdCardConsole",
|
||||
"MimePowers",
|
||||
"ThermalRegulator",
|
||||
"DiseaseMachineRunning",
|
||||
"DiseaseMachine",
|
||||
|
||||
12
Content.Server/Abilities/Mime/InvisibleWallComponent.cs
Normal file
12
Content.Server/Abilities/Mime/InvisibleWallComponent.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
namespace Content.Server.Abilities.Mime
|
||||
{
|
||||
// Tracks invisible wall despawning
|
||||
[RegisterComponent]
|
||||
public sealed class InvisibleWallComponent : Component
|
||||
{
|
||||
[DataField("accumulator")]
|
||||
public float Accumulator = 0f;
|
||||
[DataField("despawnTime")]
|
||||
public TimeSpan DespawnTime = TimeSpan.FromSeconds(30);
|
||||
}
|
||||
}
|
||||
63
Content.Server/Abilities/Mime/MimePowersComponent.cs
Normal file
63
Content.Server/Abilities/Mime/MimePowersComponent.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Content.Shared.Actions.ActionTypes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.Abilities.Mime
|
||||
{
|
||||
/// <summary>
|
||||
/// Lets its owner entity use mime powers, like placing invisible walls.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed class MimePowersComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether this component is active or not.
|
||||
/// </summarY>
|
||||
[ViewVariables]
|
||||
[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")]
|
||||
public InstantAction InvisibleWallAction = new()
|
||||
{
|
||||
UseDelay = TimeSpan.FromSeconds(30),
|
||||
Icon = new SpriteSpecifier.Texture(new ResourcePath("Structures/Walls/solid.rsi/full.png")),
|
||||
Name = "mime-invisible-wall",
|
||||
Description = "mime-invisible-wall-desc",
|
||||
Priority = -1,
|
||||
Event = new InvisibleWallActionEvent(),
|
||||
};
|
||||
|
||||
|
||||
/// 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>
|
||||
/// Accumulator for when the mime breaks their vows
|
||||
/// </summary>
|
||||
|
||||
[DataField("accumulator")]
|
||||
public float Accumulator = 0f;
|
||||
|
||||
/// <summary>
|
||||
/// How long it takes the mime to get their powers back
|
||||
|
||||
[DataField("vowCooldown")]
|
||||
public TimeSpan VowCooldown = TimeSpan.FromMinutes(5);
|
||||
}
|
||||
}
|
||||
147
Content.Server/Abilities/Mime/MimePowersSystem.cs
Normal file
147
Content.Server/Abilities/Mime/MimePowersSystem.cs
Normal file
@@ -0,0 +1,147 @@
|
||||
using Content.Server.Popups;
|
||||
using Content.Server.Coordinates.Helpers;
|
||||
using Content.Shared.Speech;
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Alert;
|
||||
using Content.Shared.Physics;
|
||||
using Content.Shared.Doors.Components;
|
||||
using Content.Shared.Maps;
|
||||
using Content.Shared.MobState.Components;
|
||||
using Content.Shared.Tag;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Physics;
|
||||
|
||||
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 TagSystem _tagSystem = default!;
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<MimePowersComponent, ComponentInit>(OnComponentInit);
|
||||
SubscribeLocalEvent<MimePowersComponent, SpeakAttemptEvent>(OnSpeakAttempt);
|
||||
SubscribeLocalEvent<MimePowersComponent, InvisibleWallActionEvent>(OnInvisibleWall);
|
||||
}
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
/// Queue to despawn invis walls
|
||||
foreach (var invisWall in EntityQuery<InvisibleWallComponent>())
|
||||
{
|
||||
invisWall.Accumulator += frameTime;
|
||||
if (invisWall.Accumulator < invisWall.DespawnTime.TotalSeconds)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
EntityManager.QueueDeleteEntity(invisWall.Owner);
|
||||
}
|
||||
/// Queue to track whether mimes can retake vows yet
|
||||
foreach (var mime in EntityQuery<MimePowersComponent>())
|
||||
{
|
||||
if (!mime.VowBroken || mime.ReadyToRepent)
|
||||
return;
|
||||
|
||||
mime.Accumulator += frameTime;
|
||||
if (mime.Accumulator < mime.VowCooldown.TotalSeconds)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
mime.ReadyToRepent = true;
|
||||
_popupSystem.PopupEntity(Loc.GetString("mime-ready-to-repent"), mime.Owner, Filter.Entities(mime.Owner));
|
||||
}
|
||||
}
|
||||
|
||||
private void OnComponentInit(EntityUid uid, MimePowersComponent component, ComponentInit args)
|
||||
{
|
||||
_actionsSystem.AddAction(uid, component.InvisibleWallAction, uid);
|
||||
_alertsSystem.ShowAlert(uid, AlertType.VowOfSilence);
|
||||
}
|
||||
private void OnSpeakAttempt(EntityUid uid, MimePowersComponent component, SpeakAttemptEvent args)
|
||||
{
|
||||
if (!component.Enabled)
|
||||
return;
|
||||
|
||||
_popupSystem.PopupEntity(Loc.GetString("mime-cant-speak"), uid, Filter.Entities(uid));
|
||||
args.Cancel();
|
||||
}
|
||||
|
||||
/// <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;
|
||||
|
||||
var xform = Transform(uid);
|
||||
/// Get the tile in front of the mime
|
||||
var offsetValue = xform.LocalRotation.ToWorldVec().Normalized;
|
||||
var coords = xform.Coordinates.Offset(offsetValue).SnapToGrid();
|
||||
/// Check there are no walls or mobs there
|
||||
foreach (var entity in coords.GetEntitiesInTile())
|
||||
{
|
||||
IPhysBody? physics = null; /// We use this to check if it's impassable
|
||||
if ((HasComp<MobStateComponent>(entity) && entity != uid) || /// Is it a mob?
|
||||
((Resolve(entity, ref physics, false) && (physics.CollisionLayer & (int) CollisionGroup.Impassable) != 0) // Is it impassable?
|
||||
&& !(TryComp<DoorComponent>(entity, out var door) && door.State != DoorState.Closed))) // Is it a door that's open and so not actually impassable?
|
||||
{
|
||||
_popupSystem.PopupEntity(Loc.GetString("mime-invisible-wall-failed"), uid, Filter.Entities(uid));
|
||||
return;
|
||||
}
|
||||
}
|
||||
_popupSystem.PopupEntity(Loc.GetString("mime-invisible-wall-popup", ("mime", uid)), uid, Filter.Pvs(uid));
|
||||
/// Make sure we set the invisible wall to despawn properly
|
||||
var wall = EntityManager.SpawnEntity(component.WallPrototype, coords);
|
||||
EnsureComp<InvisibleWallComponent>(wall);
|
||||
/// Handle args so cooldown works
|
||||
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;
|
||||
_alertsSystem.ClearAlert(uid, AlertType.VowOfSilence);
|
||||
_alertsSystem.ShowAlert(uid, AlertType.VowBroken);
|
||||
_actionsSystem.RemoveAction(uid, mimePowers.InvisibleWallAction);
|
||||
}
|
||||
|
||||
/// <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, Filter.Entities(uid));
|
||||
return;
|
||||
}
|
||||
|
||||
mimePowers.Enabled = true;
|
||||
mimePowers.ReadyToRepent = false;
|
||||
mimePowers.VowBroken = false;
|
||||
mimePowers.Accumulator = 0f;
|
||||
_alertsSystem.ClearAlert(uid, AlertType.VowBroken);
|
||||
_alertsSystem.ShowAlert(uid, AlertType.VowOfSilence);
|
||||
_actionsSystem.AddAction(uid, mimePowers.InvisibleWallAction, uid);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class InvisibleWallActionEvent : InstantActionEvent {}
|
||||
}
|
||||
20
Content.Server/Alert/Click/BreakVow.cs
Normal file
20
Content.Server/Alert/Click/BreakVow.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using Content.Shared.Alert;
|
||||
using Content.Server.Abilities.Mime;
|
||||
|
||||
namespace Content.Server.Alert.Click
|
||||
{
|
||||
///<summary>
|
||||
/// Break your mime vows
|
||||
///</summary>
|
||||
[DataDefinition]
|
||||
public sealed class BreakVow : IAlertClick
|
||||
{
|
||||
public void AlertClicked(EntityUid player)
|
||||
{
|
||||
if (IoCManager.Resolve<IEntityManager>().TryGetComponent<MimePowersComponent?>(player, out var mimePowers))
|
||||
{
|
||||
EntitySystem.Get<MimePowersSystem>().BreakVow(player, mimePowers);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
20
Content.Server/Alert/Click/RetakeVow.cs
Normal file
20
Content.Server/Alert/Click/RetakeVow.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using Content.Shared.Alert;
|
||||
using Content.Server.Abilities.Mime;
|
||||
|
||||
namespace Content.Server.Alert.Click
|
||||
{
|
||||
///<summary>
|
||||
/// Retake your mime vows
|
||||
///</summary>
|
||||
[DataDefinition]
|
||||
public sealed class RetakeVow : IAlertClick
|
||||
{
|
||||
public void AlertClicked(EntityUid player)
|
||||
{
|
||||
if (IoCManager.Resolve<IEntityManager>().TryGetComponent<MimePowersComponent?>(player, out var mimePowers))
|
||||
{
|
||||
EntitySystem.Get<MimePowersSystem>().RetakeVow(player, mimePowers);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,8 @@
|
||||
Pulling,
|
||||
Magboots,
|
||||
Toxins,
|
||||
VowOfSilence,
|
||||
VowBroken,
|
||||
Debug1,
|
||||
Debug2,
|
||||
Debug3,
|
||||
|
||||
7
Resources/Locale/en-US/abilities/mime.ftl
Normal file
7
Resources/Locale/en-US/abilities/mime.ftl
Normal file
@@ -0,0 +1,7 @@
|
||||
mime-cant-speak = Your vow of silence prevents you from speaking.
|
||||
mime-invisible-wall = Create Invisible Wall
|
||||
mime-invisible-wall-desc = Create an invisible wall in front of you, if placeable there.
|
||||
mime-invisible-wall-popup = {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.
|
||||
@@ -191,6 +191,20 @@
|
||||
name: "[color=red]Parched[/color]"
|
||||
description: You're severely thirsty. The thirst makes moving around a chore.
|
||||
|
||||
- type: alert
|
||||
id: VowOfSilence
|
||||
icon: /Textures/Interface/Alerts/Abilities/silenced.png
|
||||
name: Vow of Silence
|
||||
onClick: !type:BreakVow { }
|
||||
description: You have taken a vow of silence as part of initiation into the Mystiko Tagma Mimon. Click to break your vow.
|
||||
|
||||
- type: alert
|
||||
id: VowBroken
|
||||
icon: /Textures/Interface/Actions/scream.png
|
||||
name: Vow Broken
|
||||
onClick: !type:RetakeVow { }
|
||||
description: You've broken your vows to Mimes everywhere. You can speak, but you've lost your powers for at least 5 entire minutes!!! Click to try and retake your vow.
|
||||
|
||||
- type: alert
|
||||
id: Pulled
|
||||
icon: /Textures/Interface/Alerts/Pull/pulled.png
|
||||
|
||||
@@ -649,3 +649,25 @@
|
||||
- type: IconSmooth
|
||||
key: walls
|
||||
base: wood
|
||||
|
||||
- type: entity
|
||||
id: WallInvisible
|
||||
name: Invisible Wall
|
||||
components:
|
||||
- type: Tag
|
||||
tags:
|
||||
- Wall
|
||||
- type: Physics
|
||||
bodyType: Static
|
||||
- type: Fixtures
|
||||
fixtures:
|
||||
- shape:
|
||||
!type:PhysShapeAabb
|
||||
bounds: "-0.5,-0.5,0.5,0.5"
|
||||
layer:
|
||||
- Impassable
|
||||
- VaultImpassable
|
||||
- SmallImpassable
|
||||
mask:
|
||||
- Impassable
|
||||
- type: Airtight
|
||||
|
||||
@@ -9,6 +9,10 @@
|
||||
access:
|
||||
- Theatre
|
||||
- Maintenance
|
||||
special:
|
||||
- !type:AddComponentSpecial
|
||||
components:
|
||||
- type: MimePowers
|
||||
|
||||
- type: startingGear
|
||||
id: MimeGear
|
||||
|
||||
BIN
Resources/Textures/Interface/Alerts/Abilities/silenced.png
Normal file
BIN
Resources/Textures/Interface/Alerts/Abilities/silenced.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.9 KiB |
Reference in New Issue
Block a user