diff --git a/Content.Server/_White/TimeBeacon/TimeBeaconAnchorComponent.cs b/Content.Server/_White/TimeBeacon/TimeBeaconAnchorComponent.cs new file mode 100644 index 0000000000..21de86fa2e --- /dev/null +++ b/Content.Server/_White/TimeBeacon/TimeBeaconAnchorComponent.cs @@ -0,0 +1,11 @@ +namespace Content.Server._White.TimeBeacon; + +[RegisterComponent] +public sealed partial class TimeBeaconAnchorComponent : Component +{ + [ViewVariables] + public EntityUid Entity = EntityUid.Invalid; + + [DataField] + public TimeSpan Duration = TimeSpan.FromSeconds(10); +} diff --git a/Content.Server/_White/TimeBeacon/TimeBeaconComponent.cs b/Content.Server/_White/TimeBeacon/TimeBeaconComponent.cs new file mode 100644 index 0000000000..9448346003 --- /dev/null +++ b/Content.Server/_White/TimeBeacon/TimeBeaconComponent.cs @@ -0,0 +1,20 @@ +using Robust.Shared.Audio; +using Robust.Shared.Prototypes; + +namespace Content.Server._White.TimeBeacon; + +[RegisterComponent] +public sealed partial class TimeBeaconComponent : Component +{ + [ViewVariables] + public TimeSpan NextUse = TimeSpan.Zero; + + [DataField, ViewVariables(VVAccess.ReadWrite)] + public TimeSpan Cooldown = TimeSpan.FromSeconds(20); + + [DataField] + public EntProtoId AnchorEntity = "TimeBeaconAnchor"; + + [DataField] + public SoundSpecifier Sound = new SoundPathSpecifier("/Audio/Machines/high_tech_confirm.ogg"); +} diff --git a/Content.Server/_White/TimeBeacon/TimeBeaconSystem.cs b/Content.Server/_White/TimeBeacon/TimeBeaconSystem.cs new file mode 100644 index 0000000000..9467353f89 --- /dev/null +++ b/Content.Server/_White/TimeBeacon/TimeBeaconSystem.cs @@ -0,0 +1,104 @@ +using Content.Server.Popups; +using Content.Server.Pulling; +using Content.Shared.Examine; +using Content.Shared.Interaction.Events; +using Content.Shared.Popups; +using Content.Shared.Pulling.Components; +using Robust.Server.Audio; +using Robust.Server.GameObjects; +using Robust.Shared.Timing; + +namespace Content.Server._White.TimeBeacon; + +public sealed class TimeBeaconSystem : EntitySystem +{ + [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly PopupSystem _popup = default!; + [Dependency] private readonly AudioSystem _audio = default!; + [Dependency] private readonly TransformSystem _transform = default!; + [Dependency] private readonly PullingSystem _pulling = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnUseInHand); + SubscribeLocalEvent(OnExamine); + SubscribeLocalEvent(OnMapInit); + } + + private void OnMapInit(Entity ent, ref MapInitEvent args) + { + Timer.Spawn(ent.Comp.Duration, () => + { + var entity = ent.Comp.Entity; + + if (entity == EntityUid.Invalid) + return; + + if (EntityManager.Deleted(ent) || EntityManager.Deleted(entity)) + return; + + if (!TryComp(ent, out TransformComponent? xform) || !TryComp(entity, out TransformComponent? entXform)) + return; + + if (xform.MapID != entXform.MapID) + return; + + // break pulls before portal enter so we dont break shit + if (TryComp(entity, out var pullable) && pullable.BeingPulled) + { + _pulling.TryStopPull(pullable); + } + + if (TryComp(entity, out var pulling) + && pulling.Pulling != null && + TryComp(pulling.Pulling.Value, out var subjectPulling)) + { + _pulling.TryStopPull(subjectPulling); + } + + _transform.SetCoordinates(entity, entXform, xform.Coordinates); + QueueDel(ent); + }); + } + + private void OnExamine(Entity ent, ref ExaminedEvent args) + { + if (ent.Comp.NextUse <= _timing.CurTime) + { + args.PushMarkup(Loc.GetString("time-beacon-component-charged")); + return; + } + + var message = Loc.GetString("time-beacon-component-charging", + ("cooldown", (int) (ent.Comp.NextUse - _timing.CurTime).TotalSeconds)); + args.PushMarkup(message); + } + + private void OnUseInHand(Entity ent, ref UseInHandEvent args) + { + var coords = CompOrNull(args.User)?.Coordinates; + + if (coords == null) + return; + + if (ent.Comp.NextUse > _timing.CurTime) + { + var message = Loc.GetString("time-beacon-component-cooldown", + ("cooldown", (int) (ent.Comp.NextUse - _timing.CurTime).TotalSeconds)); + _popup.PopupEntity(message, args.User, args.User); + return; + } + + var anchor = Spawn(ent.Comp.AnchorEntity, coords.Value); + _transform.AttachToGridOrMap(anchor); + var anchorComp = EnsureComp(anchor); + anchorComp.Entity = args.User; + + _popup.PopupEntity(Loc.GetString("time-beacon-component-anchor-set"), args.User, args.User, PopupType.Medium); + _audio.PlayEntity(ent.Comp.Sound, args.User, ent); + + ent.Comp.NextUse = _timing.CurTime + ent.Comp.Cooldown; + } +} diff --git a/Resources/Locale/ru-RU/_white/object/time-beacon.ftl b/Resources/Locale/ru-RU/_white/object/time-beacon.ftl new file mode 100644 index 0000000000..efb631fdf3 --- /dev/null +++ b/Resources/Locale/ru-RU/_white/object/time-beacon.ftl @@ -0,0 +1,10 @@ +ent-TimeBeacon = временной маяк + .desc = Перемотка! + +time-beacon-component-charged = Временной маяк [color=darkgreen]заряжен[/color] +time-beacon-component-charging = Временной маяк зарядится через [color=fuchsia]{ $cooldown }[/color] секунд. +time-beacon-component-cooldown = Временной маяк можно использовать через { $cooldown } секунд. +time-beacon-component-anchor-set = Маяк установлен! + +uplink-time-beacon = Временной маяк +uplink-time-beacon-desc = Устанавливает маяк при активации. Через 10 секунд телепортирует вас к точке маяка. Время перезарядки составляет 20 секунд. diff --git a/Resources/Prototypes/_White/Catalog/uplink.yml b/Resources/Prototypes/_White/Catalog/uplink.yml index 08c2a52f6a..09bdf2e136 100644 --- a/Resources/Prototypes/_White/Catalog/uplink.yml +++ b/Resources/Prototypes/_White/Catalog/uplink.yml @@ -63,6 +63,17 @@ categories: - UplinkAmmo +- type: listing + id: UplinkTimeBeacon + name: uplink-time-beacon + description: uplink-time-beacon-desc + productEntity: TimeBeacon + icon: { sprite: Objects/Devices/door_remote.rsi, state: door_remotebase_white } + cost: + Telecrystal: 2 + categories: + - UplinkUtility + - type: listing id: UplinkSmokeImplanter name: Имплант дыма diff --git a/Resources/Prototypes/_White/Entities/Objects/Misc/time_beacon.yml b/Resources/Prototypes/_White/Entities/Objects/Misc/time_beacon.yml new file mode 100644 index 0000000000..9ce5948401 --- /dev/null +++ b/Resources/Prototypes/_White/Entities/Objects/Misc/time_beacon.yml @@ -0,0 +1,24 @@ +- type: entity + parent: BaseItem + id: TimeBeacon + name: time beacon + description: Rewind! + components: + - type: Sprite + sprite: Objects/Devices/door_remote.rsi + layers: + - state: door_remotebase_white + - state: door_remotelightscolour + color: "#00FFF7" + - state: door_remotescreencolour + color: "#00FFF7" + - type: Item + storedRotation: -90 + - type: TimeBeacon + +- type: entity + id: TimeBeaconAnchor + name: time beacon anchor + description: Fuck! + components: + - type: TimeBeaconAnchor diff --git a/Resources/Textures/Objects/Devices/door_remote.rsi/door_remotebase_white.png b/Resources/Textures/Objects/Devices/door_remote.rsi/door_remotebase_white.png new file mode 100644 index 0000000000..463071528f Binary files /dev/null and b/Resources/Textures/Objects/Devices/door_remote.rsi/door_remotebase_white.png differ diff --git a/Resources/Textures/Objects/Devices/door_remote.rsi/meta.json b/Resources/Textures/Objects/Devices/door_remote.rsi/meta.json index ffb1be00d3..95de47cd86 100644 --- a/Resources/Textures/Objects/Devices/door_remote.rsi/meta.json +++ b/Resources/Textures/Objects/Devices/door_remote.rsi/meta.json @@ -10,6 +10,9 @@ { "name": "door_remotebase" }, + { + "name": "door_remotebase_white" + }, { "name": "door_remotelightscolour" },