39 Commits

Author SHA1 Message Date
Arthur
e3784bc237 Complete spawn beam command 2022-07-11 16:41:35 +03:00
Arthur
acc2fd5a55 Start creating spawn beam command 2022-07-11 02:15:30 +03:00
Arthur
179116cc22 Update UI button & add timer and cooldown 2022-07-10 01:41:20 +03:00
Arthur
d07d294b91 Remove fire verb from terminal 2022-07-09 22:32:46 +03:00
Arthur
f6e950cbf7 Update locale keys 2022-07-09 22:32:09 +03:00
Arthur
4410380abc Fix locale 2022-07-09 22:30:05 +03:00
Arthur
ddd080f5f8 Add simple terminal UI for firing 2022-07-09 22:29:10 +03:00
Arthur
925ec94daf Dont call Ignite with visual update at every tick 2022-07-09 18:03:32 +03:00
Arthur
9e868145ff Correct beam spawning directed in center of station 2022-07-09 17:55:31 +03:00
Arthur
1e9966e946 Start work on beam spawn & launch direction 2022-07-08 20:57:19 +03:00
Arthur
a3dab14c6c Rename stuff & add move separate launch method 2022-07-08 20:57:09 +03:00
Arthur
586cf26f0f Add arson radius for beam 2022-07-08 05:16:36 +03:00
Arthur
9b76fc7d74 Use AllMask for fixture beam 2022-07-08 03:02:12 +03:00
Arthur
24b3223403 Use ambient component for loop sound 2022-07-08 02:55:35 +03:00
Arthur
a477e2f64f Combine sun ray sprites in one 2022-07-08 02:54:57 +03:00
Arthur
dc2f707f12 Add states & preparing phase 2022-07-06 04:09:42 +03:00
Arthur
888f5714dc Tweak beam component 2022-07-06 00:35:05 +03:00
Arthur
4793eaa494 Copy singularity destroy logic to beam 2022-07-06 00:34:51 +03:00
Arthur
9f0a8ca631 Fix terminal parent 2022-07-04 17:37:49 +03:00
Arthur
bdc027444f No rotation for beam 2022-07-04 17:37:34 +03:00
Arthur
54834dbb07 Remove IcarusBeam comments 2022-07-04 17:18:21 +03:00
Arthur
8941193a0f Terminal spawn IcarusBeam 2022-07-04 17:16:03 +03:00
Arthur
993b4315bb Revert "Concatenate images to one"
This reverts commit acfe951a0c.
2022-07-04 17:12:58 +03:00
Arthur
c3d4e811db Add loop sound & destroying all 2022-07-04 17:12:49 +03:00
Arthur
acfe951a0c Concatenate images to one 2022-07-03 16:47:21 +03:00
Arthur
c0794feada Basic Icarus beam 2022-07-03 16:40:03 +03:00
Arthur
f6598c255b Merge branch 'master' into icarus 2022-06-29 16:50:41 +03:00
Arthur
f570a9de6c Basic fire system 2022-06-26 16:52:33 +03:00
Arthur
4591833d81 Add announcements & activation verb 2022-06-26 07:16:52 +03:00
Arthur
a99c5c3488 Move system to server and add basic fire logic 2022-06-26 06:39:24 +03:00
Arthur
6df2a1a571 Use ItemSlots component for simplicity 2022-06-26 04:39:42 +03:00
Arthur
ab44d02630 Add basic key insert to slot 2022-06-26 03:35:02 +03:00
Arthur
dafe921ac4 Add sounds 2022-06-25 17:11:41 +03:00
Arthur
445a0d544f Merge branch 'master' into icarus 2022-06-24 06:33:45 +03:00
Arthur
9864bae192 Add basic server icarus components 2022-06-24 04:33:24 +03:00
Arthur
184bd103b0 Add to machine meta 2022-06-24 03:39:24 +03:00
Arthur
a88eb38484 Update terminal sprites 2022-06-24 03:39:13 +03:00
Arthur
d88fe1c1c4 Add computer goldeneye sprites to machines 2022-06-24 03:28:55 +03:00
Arthur
81fb2c02a2 Add sprites 2022-06-23 16:04:49 +03:00
35 changed files with 918 additions and 1 deletions

View File

@@ -0,0 +1,58 @@
using Content.Shared.Icarus;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
namespace Content.Client.Icarus;
[UsedImplicitly]
public sealed class IcarusTerminalBoundUserInterface : BoundUserInterface
{
private IcarusTerminalWindow? _window;
public IcarusTerminalBoundUserInterface([NotNull] ClientUserInterfaceComponent owner, [NotNull] object uiKey) : base(owner, uiKey)
{
}
protected override void Open()
{
base.Open();
_window = new IcarusTerminalWindow();
_window.OnClose += Close;
_window.OpenCentered();
_window.FireButtonPressed += OnFireButtonPressed;
}
private void OnFireButtonPressed()
{
if (_window == null)
return;
SendMessage(new IcarusTerminalFireMessage());
}
protected override void UpdateState(BoundUserInterfaceState state)
{
base.UpdateState(state);
if (_window == null)
return;
if (state is not IcarusTerminalUiState cast)
return;
_window.UpdateState(cast);
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (!disposing)
return;
if (_window != null)
_window.OnClose -= Close;
_window?.Dispose();
}
}

View File

@@ -0,0 +1,21 @@
<DefaultWindow xmlns="https://spacestation14.io"
Title="{Loc 'icarus-ui-window-title'}"
MinSize="300 120">
<BoxContainer Orientation="Vertical">
<Button Name="FireButton"
Text="{Loc 'icarus-ui-fire-button'}"
StyleClasses="Caution"
MinHeight="50"
Disabled="True" />
<BoxContainer Name="TimerBox" Orientation="Horizontal" Visible="False">
<Label Text="{Loc 'icarus-ui-timer-label'}" />
<Label Text=" " />
<Label Name="TimerValue" Text="-" />
</BoxContainer>
<BoxContainer Name="CooldownBox" Orientation="Horizontal" Visible="False">
<Label Text="{Loc 'icarus-ui-cooldown-label'}" />
<Label Text=" " />
<Label Name="CooldownValue" Text="-" />
</BoxContainer>
</BoxContainer>
</DefaultWindow>

View File

@@ -0,0 +1,36 @@
using Content.Shared.Icarus;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;
namespace Content.Client.Icarus;
[GenerateTypedNameReferences]
public sealed partial class IcarusTerminalWindow : DefaultWindow
{
public event Action? FireButtonPressed;
public IcarusTerminalWindow()
{
RobustXamlLoader.Load(this);
FireButton.OnPressed += _ => FireButtonPressed?.Invoke();
}
public void UpdateState(IcarusTerminalUiState state)
{
FireButton.Disabled = state.Status != IcarusTerminalStatus.FIRE_READY;
TimerBox.Visible = state.Status == IcarusTerminalStatus.FIRE_PREPARING;
CooldownBox.Visible = state.Status == IcarusTerminalStatus.COOLDOWN;
switch (state.Status)
{
case IcarusTerminalStatus.FIRE_PREPARING:
TimerValue.Text = state.RemainingTime.ToString();
break;
case IcarusTerminalStatus.COOLDOWN:
CooldownValue.Text = state.CooldownTime.ToString();
break;
}
}
}

View File

@@ -0,0 +1,45 @@
using Content.Server.Administration;
using Content.Shared.Administration;
using JetBrains.Annotations;
using Robust.Shared.Console;
using Robust.Shared.Map;
namespace Content.Server.Icarus.Commands;
[UsedImplicitly]
[AdminCommand(AdminFlags.Fun)]
public sealed class SpawnIcarusCommand : IConsoleCommand
{
public string Command => "spawnicarus";
public string Description => "Spawn Icarus beam and direct to specified grid center.";
public string Help => "spawnicarus <gridId>";
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 1)
{
shell.WriteError("Incorrect number of arguments. " + Help);
return;
}
if (!int.TryParse(args[0], out var id))
{
shell.WriteLine($"{args[0]} is not a valid integer.");
return;
}
var gridId = new GridId(int.Parse(args[0]));
var mapManager = IoCManager.Resolve<IMapManager>();
if (mapManager.TryGetGrid(gridId, out var grid))
{
var icarusSystem = EntitySystem.Get<IcarusTerminalSystem>();
var coords = icarusSystem.FireBeam(grid.WorldAABB);
shell.WriteLine($"Icarus was spawned: {coords.ToString()}");
}
else
{
shell.WriteError($"No grid exists with id {id}");
}
}
}

View File

@@ -0,0 +1,32 @@
namespace Content.Server.Icarus;
[RegisterComponent]
public sealed class IcarusBeamComponent : Component
{
/// <summary>
/// Beam moving speed.
/// </summary>
[DataField("speed")]
public float Speed = 1f;
/// <summary>
/// The beam will be automatically cleaned up after this time.
/// </summary>
[DataField("lifetime")]
public TimeSpan Lifetime = TimeSpan.FromSeconds(240);
/// <summary>
/// With this set to true, beam will automatically set the tiles under them to space.
/// </summary>
[DataField("destroyTiles")]
public bool DestroyTiles = true;
[DataField("destroyRadius")]
public float DestroyRadius = 2f;
[DataField("flameRadius")]
public float FlameRadius = 4f;
[DataField("accumulator")]
public float Accumulator = 0f;
}

View File

@@ -0,0 +1,137 @@
using Content.Server.Atmos.Components;
using Content.Server.Atmos.EntitySystems;
using Content.Server.Ghost.Components;
using Robust.Shared.Audio;
using Robust.Shared.Map;
using Robust.Shared.Player;
namespace Content.Server.Icarus;
public sealed class IcarusBeamSystem : EntitySystem
{
[Dependency] private readonly IMapManager _map = default!;
[Dependency] private readonly EntityLookupSystem _lookup = default!;
[Dependency] private readonly FlammableSystem _flammable = default!;
public override void Update(float frameTime)
{
base.Update(frameTime);
var query = EntityQuery<IcarusBeamComponent, TransformComponent>(true);
foreach (var (beam, trans) in query)
{
AccumulateLifetime(beam, frameTime);
DestroyEntities(beam, trans);
BurnEntities(beam, trans);
if (!beam.DestroyTiles)
continue;
DestroyTiles(beam, trans);
}
}
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<IcarusBeamComponent, ComponentInit>(OnComponentInit);
}
private void OnComponentInit(EntityUid uid, IcarusBeamComponent component, ComponentInit args)
{
if (TryComp(uid, out PhysicsComponent? phys))
{
phys.LinearDamping = 0f;
phys.Friction = 0f;
phys.BodyStatus = BodyStatus.InAir;
}
}
public void LaunchInDirection(EntityUid uid, Vector2 dir, IcarusBeamComponent? beam = null)
{
if (!Resolve(uid, ref beam))
return;
if (TryComp(beam.Owner, out PhysicsComponent? phys))
phys.ApplyLinearImpulse(dir * beam.Speed);
}
private void AccumulateLifetime(IcarusBeamComponent beam, float frameTime)
{
beam.Accumulator += frameTime;
if (beam.Accumulator > beam.Lifetime.TotalSeconds)
{
QueueDel(beam.Owner);
}
}
/// <summary>
/// Destroy any grid tiles in beam radius.
/// </summary>
private void DestroyTiles(IcarusBeamComponent component, TransformComponent trans)
{
var radius = component.DestroyRadius;
var worldPos = trans.WorldPosition;
var circle = new Circle(worldPos, radius);
var box = new Box2(worldPos - radius, worldPos + radius);
foreach (var grid in _map.FindGridsIntersecting(trans.MapID, box))
{
// Bundle these together so we can use the faster helper to set tiles.
var toDestroy = new List<(Vector2i, Tile)>();
foreach (var tile in grid.GetTilesIntersecting(circle))
{
if (tile.Tile.IsEmpty)
continue;
toDestroy.Add((tile.GridIndices, Tile.Empty));
}
grid.SetTiles(toDestroy);
}
}
/// <summary>
/// Handle deleting entities in beam radius.
/// </summary>
private void DestroyEntities(IcarusBeamComponent component, TransformComponent trans)
{
var radius = component.DestroyRadius - 0.5f;
foreach (var entity in _lookup.GetEntitiesInRange(trans.MapID, trans.WorldPosition, radius))
{
if (!CanDestroy(component, entity))
continue;
QueueDel(entity);
}
}
/// <summary>
/// Handle igniting flammable entities in beam radius.
/// </summary>
private void BurnEntities(IcarusBeamComponent component, TransformComponent trans)
{
var radius = component.FlameRadius * 2;
foreach (var entity in _lookup.GetEntitiesInRange(trans.MapID, trans.WorldPosition, radius))
{
if (!CanDestroy(component, entity))
continue;
if (!TryComp<FlammableComponent>(entity, out var flammable))
continue;
flammable.FireStacks += 1;
if (!flammable.OnFire)
_flammable.Ignite(entity);
}
}
private bool CanDestroy(IcarusBeamComponent component, EntityUid entity)
{
return entity != component.Owner &&
!EntityManager.HasComponent<IMapGridComponent>(entity) &&
!EntityManager.HasComponent<GhostComponent>(entity);
}
}

View File

@@ -0,0 +1,219 @@
using System.Linq;
using Content.Server.Chat.Systems;
using Content.Server.GameTicking;
using Content.Server.Station.Components;
using Content.Server.Station.Systems;
using Content.Shared.Containers.ItemSlots;
using Content.Shared.Icarus;
using Robust.Server.GameObjects;
using Robust.Shared.Audio;
using Robust.Shared.Map;
using Robust.Shared.Player;
using Robust.Shared.Random;
namespace Content.Server.Icarus;
/// <summary>
/// Handle Icarus activation terminal
/// </summary>
public sealed class IcarusTerminalSystem : EntitySystem
{
private const string IcarusBeamPrototypeId = "IcarusBeam";
[Dependency] private readonly ChatSystem _chatSystem = default!;
[Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly IRobustRandom _robustRandom = default!;
[Dependency] private readonly GameTicker _gameTicker = default!;
[Dependency] private readonly StationSystem _stationSystem = default!;
[Dependency] private readonly IcarusBeamSystem _icarusSystem = default!;
[Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!;
public override void Update(float frameTime)
{
base.Update(frameTime);
var query = EntityQuery<IcarusTerminalComponent>();
foreach (var terminal in query)
{
switch (terminal.Status)
{
case IcarusTerminalStatus.FIRE_PREPARING:
TickTimer(terminal, frameTime);
break;
case IcarusTerminalStatus.COOLDOWN:
TickCooldown(terminal, frameTime);
break;
}
}
}
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<IcarusTerminalComponent, ComponentInit>(OnInit);
SubscribeLocalEvent<IcarusTerminalComponent, ItemSlotChangedEvent>(OnItemSlotChanged);
// UI events
SubscribeLocalEvent<IcarusTerminalComponent, IcarusTerminalFireMessage>(OnFireButtonPressed);
}
private void OnInit(EntityUid uid, IcarusTerminalComponent component, ComponentInit args)
{
component.RemainingTime = component.Timer;
UpdateStatus(component);
UpdateUserInterface(component);
}
private void OnItemSlotChanged(EntityUid uid, IcarusTerminalComponent component, ref ItemSlotChangedEvent args)
{
UpdateStatus(component);
UpdateUserInterface(component);
}
private void OnFireButtonPressed(EntityUid uid, IcarusTerminalComponent component, IcarusTerminalFireMessage args)
{
Fire(component);
}
private void Fire(IcarusTerminalComponent component)
{
if (component.Status == IcarusTerminalStatus.FIRE_PREPARING)
return;
component.RemainingTime = component.Timer;
component.Status = IcarusTerminalStatus.FIRE_PREPARING;
_chatSystem.DispatchStationAnnouncement(component.Owner,
Loc.GetString("icarus-fire-announcement", ("seconds", component.Timer)),
Loc.GetString("icarus-announce-sender"),
false,
Color.Red);
SoundSystem.Play(component.AlertSound.GetSound(), Filter.Broadcast());
}
private void UpdateStatus(IcarusTerminalComponent component)
{
switch (component.Status)
{
case IcarusTerminalStatus.AWAIT_DISKS:
if (IsAccessGranted(component.Owner))
Authorize(component);
break;
case IcarusTerminalStatus.FIRE_READY:
{
if (!IsAccessGranted(component.Owner))
{
component.Status = IcarusTerminalStatus.AWAIT_DISKS;
}
break;
}
}
}
private void UpdateUserInterface(IcarusTerminalComponent component)
{
_userInterfaceSystem.TrySetUiState(component.Owner, IcarusTerminalUiKey.Key, new IcarusTerminalUiState(
component.Status,
(int) component.RemainingTime,
(int) component.CooldownTime)
);
}
private bool IsAccessGranted(EntityUid uid)
{
return Comp<ItemSlotsComponent>(uid).Slots.Values.All(v => v.HasItem);
}
private bool CanFire(EntityUid uid, IcarusTerminalComponent component)
{
return IsAccessGranted(uid) &&
component.Status == IcarusTerminalStatus.FIRE_READY;
}
private void Authorize(IcarusTerminalComponent component)
{
component.Status = IcarusTerminalStatus.FIRE_READY;
SoundSystem.Play(component.AccessGrantedSound.GetSound(), Filter.Pvs(component.Owner), component.Owner);
if (!component.AuthorizationNotified)
{
_chatSystem.DispatchStationAnnouncement(component.Owner, Loc.GetString("icarus-authorized-announcement"),
playDefaultSound: false); // TODO: Just pass custom sound path after PR accepting
SoundSystem.Play("/Audio/Misc/notice1.ogg",
Filter.Broadcast());
component.AuthorizationNotified = true;
}
}
private void TickCooldown(IcarusTerminalComponent component, float frameTime)
{
component.CooldownTime -= frameTime;
if (component.CooldownTime <= 0)
{
component.CooldownTime = 0;
component.Status = IcarusTerminalStatus.AWAIT_DISKS;
UpdateStatus(component);
}
UpdateUserInterface(component);
}
private void TickTimer(IcarusTerminalComponent component, float frameTime)
{
component.RemainingTime -= frameTime;
if (component.RemainingTime <= 0)
{
component.RemainingTime = 0;
ActivateBeamOnStation(component);
}
UpdateUserInterface(component);
}
private void ActivateBeamOnStation(IcarusTerminalComponent component)
{
component.Status = IcarusTerminalStatus.COOLDOWN;
component.CooldownTime = component.Cooldown;
SoundSystem.Play(component.FireSound.GetSound(), Filter.Broadcast());
FireBeam(GetStationArea());
}
public MapCoordinates FireBeam(Box2 area)
{
TryGetBeamSpawnLocation(area, out var coords, out var offset);
Logger.DebugS("icarus", $"Try spawn beam on coords: {coords.ToString()}");
var entUid = Spawn(IcarusBeamPrototypeId, coords);
_icarusSystem.LaunchInDirection(entUid, -offset.Normalized);
return coords;
}
private void TryGetBeamSpawnLocation(Box2 area, out MapCoordinates coords,
out Vector2 offset)
{
coords = MapCoordinates.Nullspace;
offset = Vector2.Zero;
var center = area.Center;
var distance = (area.TopRight - center).Length;
var angle = new Angle(_robustRandom.NextFloat() * MathF.Tau);
offset = angle.RotateVec(new Vector2(distance, 0));
coords = new MapCoordinates(center + offset, _gameTicker.DefaultMap);
}
private Box2 GetStationArea()
{
var areas = _stationSystem.Stations.SelectMany(x =>
Comp<StationDataComponent>(x).Grids.Select(x => _mapManager.GetGridComp(x).Grid.WorldAABB)).ToArray();
var stationArea = areas[0];
for (var i = 1; i < areas.Length; i++)
{
stationArea.Union(areas[i]);
}
return stationArea;
}
}

View File

@@ -0,0 +1,7 @@
namespace Content.Shared.Icarus;
/// <summary>
/// Used for Icarus terminal activation
/// </summary>
[RegisterComponent]
public sealed class IcarusKeyComponent : Component {}

View File

@@ -0,0 +1,64 @@
using Content.Shared.Containers.ItemSlots;
using Content.Shared.Sound;
namespace Content.Shared.Icarus;
/// <summary>
/// Used for Icarus terminal activation
/// </summary>
[RegisterComponent]
public sealed class IcarusTerminalComponent : Component
{
/// <summary>
/// Default fire timer value in seconds.
/// </summary>
[DataField("timer")]
[ViewVariables(VVAccess.ReadWrite)]
public int Timer = 25;
/// <summary>
/// How long until the beam can arm again after fire.
/// </summary>
[DataField("cooldown")]
[ViewVariables(VVAccess.ReadWrite)]
public int Cooldown = 360;
/// <summary>
/// Current status of a terminal.
/// </summary>
[ViewVariables]
public IcarusTerminalStatus Status = IcarusTerminalStatus.AWAIT_DISKS;
/// <summary>
/// Time until beam will be spawned in seconds.
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
public float RemainingTime;
/// <summary>
/// Time until beam cooldown will expire in seconds.
/// </summary>
[ViewVariables]
public float CooldownTime;
[DataField("alertSound")]
public SoundSpecifier AlertSound = new SoundPathSpecifier("/Audio/Corvax/AssaultOperatives/icarus_alarm.ogg");
[DataField("accessGrantedSound")]
public SoundSpecifier AccessGrantedSound = new SoundPathSpecifier("/Audio/Machines/Nuke/confirm_beep.ogg");
[DataField("fireSound")]
public SoundSpecifier FireSound = new SoundPathSpecifier("/Audio/Corvax/AssaultOperatives/sunbeam_fire.ogg");
/// <summary>
/// Check if already notified about system authorization
/// </summary>
public bool AuthorizationNotified = false;
protected override void Initialize()
{
base.Initialize();
Owner.EnsureComponentWarn<ItemSlotsComponent>();
}
}

View File

@@ -0,0 +1,38 @@
using Content.Shared.Nuke;
using Robust.Shared.Serialization;
namespace Content.Shared.Icarus;
[Serializable, NetSerializable]
public enum IcarusTerminalUiKey
{
Key,
}
public enum IcarusTerminalStatus : byte
{
AWAIT_DISKS,
FIRE_READY,
FIRE_PREPARING,
COOLDOWN
}
[Serializable, NetSerializable]
public sealed class IcarusTerminalUiState : BoundUserInterfaceState
{
public IcarusTerminalStatus Status { get; }
public int RemainingTime { get; }
public int CooldownTime { get; }
public IcarusTerminalUiState(IcarusTerminalStatus status, int remainingTime, int cooldownTime)
{
Status = status;
RemainingTime = remainingTime;
CooldownTime = cooldownTime;
}
}
[Serializable, NetSerializable]
public sealed class IcarusTerminalFireMessage : BoundUserInterfaceMessage
{
}

View File

@@ -0,0 +1,16 @@
# Announce
icarus-announce-sender = Icarus Defence Network
icarus-authorized-announcement = UNAUTHORISED KEYCARD UPLOAD DETECTED. ALL KEYCARDS UPLOADED!
icarus-fire-announcement = /// ICARUS DEFENCE NETWORK BREACHED ///
Unauthorised Icarus Defence Network access detected.
ICARUS online.
Targeting system override detected...
New target: /NTSS14/
ICARUS firing protocols activated.
ETA to fire: { $seconds } seconds.
# UI
icarus-ui-window-title = Icarus Terminal
icarus-ui-fire-button = Fire
icarus-ui-timer-label = Time until the shot:
icarus-ui-cooldown-label = Cooldown:

View File

@@ -0,0 +1,20 @@
# Announce
icarus-announce-sender = Оборонная сеть "Икарус"
icarus-authorized-announcement = ОБНАРУЖЕНА НЕСАНКЦИОНИРОВАННАЯ ЗАГРУЗКА КЛЮЧ-КАРТ. ВСЕ КЛЮЧИ ЗАГРУЖЕНЫ!
icarus-fire-announcement = /// ВЗЛОМ ЗАЩИЩЕННОЙ СЕТИ "ИКАРУС" ///
Обнаружен несанкционированный доступ к оборонной сеть "Икарус"
ИКАРУС онлайн.
Обнаружено переопределение системы нацеливания...
Новая цель: /NTSS14/
Активированы протоколы стрельбы ИКАРУС.
РАСЧЕТНОЕ ВРЕМЯ выстера: { $seconds } { $seconds ->
[one] секунду
[few] секунды
*[other] секунд
}.
# UI
icarus-ui-window-title = Терминал Icarus
icarus-ui-fire-button = Огонь
icarus-ui-timer-label = Время до выстрела:
icarus-ui-cooldown-label = Перезарядка:

View File

@@ -0,0 +1,41 @@
- type: entity
id: IcarusBeam
name: icarus beam
description: A beam of light from the sun.
components:
- type: Clickable
- type: MovementIgnoreGravity
- type: Sprite
sprite: Corvax/AssaultOperatives/sunray.rsi
drawdepth: Effects
noRot: true
scale: 5, 5
layers:
- state: sunray_splash
- state: sunray
offset: 0, 1
- state: sunray_muzzle
offset: 0, 2
- type: IcarusBeam
- type: AmbientSound
range: 14
sound:
path: /Audio/Corvax/AssaultOperatives/sunbeam_loop.ogg
- type: Physics
bodyType: Dynamic
linearDamping: 0
- type: PointLight
radius: 12
color: yellow
energy: 10.0
- type: Fixtures
fixtures:
- shape:
!type:PhysShapeCircle
radius: 2
mass: 1
hard: false
mask:
- AllMask
layer:
- AllMask

View File

@@ -0,0 +1,11 @@
- type: entity
parent: BaseItem
id: IcarusKey
name: icarus authentication keycard
description: A high profile authentication keycard to Nanotrasen's Icarus secured network.
components:
- type: Sprite
sprite: Corvax/AssaultOperatives/goldeneye.rsi
layers:
- state: goldeneye_key
- type: IcarusKey

View File

@@ -0,0 +1,50 @@
- type: entity
parent: BaseComputer
id: ComputerIcarus
name: icarus terminal
description: An ominous terminal with some ports and keypads, the screen is scrolling with illegible nonsense. It has a strange marking on the side, a red ring with a gold circle within.
components:
- type: Appearance
visuals:
- type: ComputerVisualizer
key: goldeneye_key
screen: goldeneye
- type: Computer
board: CommsComputerCircuitboard # TODO: Create uniq item
- type: PointLight
radius: 1.5
energy: 1.6
color: "#b5a13c"
- type: ItemSlots
slots:
firstKeySlot:
ejectSound: /Audio/Machines/id_swipe.ogg
insertSound: /Audio/Machines/Nuke/general_beep.ogg
ejectOnBreak: true
swap: false
whitelist:
components:
- IcarusKey
secondKeySlot:
ejectSound: /Audio/Machines/id_swipe.ogg
insertSound: /Audio/Machines/Nuke/general_beep.ogg
ejectOnBreak: true
swap: false
whitelist:
components:
- IcarusKey
thirdKeySlot:
ejectSound: /Audio/Machines/id_swipe.ogg
insertSound: /Audio/Machines/Nuke/general_beep.ogg
ejectOnBreak: true
swap: false
whitelist:
components:
- IcarusKey
- type: IcarusTerminal
- type: UserInterface
interfaces:
- key: enum.IcarusTerminalUiKey.Key
type: IcarusTerminalBoundUserInterface
- type: ActivatableUI
key: enum.IcarusTerminalUiKey.Key

Binary file not shown.

After

Width:  |  Height:  |  Size: 795 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 705 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1,72 @@
{
"version": 1,
"license": "CC-BY-SA-3.0",
"copyright": "Taken from skyrat-tg at commit https://github.com/Skyrat-SS13/Skyrat-tg/commit/e2a68d3b15f0daff453ff14a1745f68eeb76b765",
"size": {
"x": 32,
"y": 32
},
"states": [
{
"name": "interrogator_off",
"delays": [
[
0.1,
0.1,
0.1,
0.1
]
]
},
{
"name": "interrogator_on",
"delays": [
[
0.1,
0.08,
0.08,
0.08,
0.08,
0.08,
0.08,
0.08,
0.08,
0.08,
0.08,
0.08,
0.08,
0.08,
0.08,
0.08,
0.08,
0.08,
0.08,
0.08,
0.08,
0.08,
0.08
]
]
},
{
"name": "interrogator_open"
},
{
"name": "interrogator_closed",
"delays": [
[
0.1,
0.1,
0.1,
0.1
]
]
},
{
"name": "goldeneye_key"
},
{
"name": "goldeneye_terminal"
}
]
}

View File

@@ -0,0 +1,41 @@
{
"version": 1,
"license": "CC-BY-SA-3.0",
"copyright": "Taken from skyrat-tg at commit https://github.com/Skyrat-SS13/Skyrat-tg/commit/e2a68d3b15f0daff453ff14a1745f68eeb76b765",
"size": {
"x": 32,
"y": 32
},
"states": [
{
"name": "sunray_splash",
"delays": [
[
0.15,
0.15,
0.15
]
]
},
{
"name": "sunray",
"delays": [
[
0.15,
0.15,
0.15
]
]
},
{
"name": "sunray_muzzle",
"delays": [
[
0.15,
0.15,
0.15
]
]
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 548 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 421 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 407 B

View File

@@ -1562,6 +1562,15 @@
},
{
"name": "detective_television"
}
},
{
"name": "goldeneye"
},
{
"name": "goldeneye_key"
},
{
"name": "goldeneye_key_off"
}
]
}