Give nukies the ability to declare war for a TC boost (#19291)
Co-authored-by: Kevin Zheng <kevinz5000@gmail.com>
This commit is contained in:
49
Content.Client/NukeOps/WarDeclaratorBoundUserInterface.cs
Normal file
49
Content.Client/NukeOps/WarDeclaratorBoundUserInterface.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
using Content.Shared.NukeOps;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Client.NukeOps;
|
||||
|
||||
[UsedImplicitly]
|
||||
public sealed class WarDeclaratorBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
[ViewVariables]
|
||||
private WarDeclaratorWindow? _window;
|
||||
|
||||
public WarDeclaratorBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) {}
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
|
||||
_window = new WarDeclaratorWindow();
|
||||
if (State != null)
|
||||
UpdateState(State);
|
||||
|
||||
_window.OpenCentered();
|
||||
|
||||
_window.OnClose += Close;
|
||||
_window.OnActivated += OnWarDeclaratorActivated;
|
||||
}
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
base.UpdateState(state);
|
||||
if (_window == null || state is not WarDeclaratorBoundUserInterfaceState cast)
|
||||
return;
|
||||
|
||||
_window?.UpdateState(cast);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
if (disposing) _window?.Dispose();
|
||||
}
|
||||
|
||||
private void OnWarDeclaratorActivated(string message)
|
||||
{
|
||||
SendMessage(new WarDeclaratorActivateMessage(message));
|
||||
}
|
||||
}
|
||||
18
Content.Client/NukeOps/WarDeclaratorWindow.xaml
Normal file
18
Content.Client/NukeOps/WarDeclaratorWindow.xaml
Normal file
@@ -0,0 +1,18 @@
|
||||
<DefaultWindow xmlns="https://spacestation14.io"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
Title="{Loc 'war-declarator-ui-header'}">
|
||||
<BoxContainer Orientation="Vertical" SeparationOverride="4" MinWidth="440">
|
||||
<TextEdit Name="MessageEdit"
|
||||
HorizontalExpand="True"
|
||||
MinHeight="200"
|
||||
Access="Public" />
|
||||
<Button Name="WarButton"
|
||||
Text="{Loc 'war-declarator-ui-war-button'}"
|
||||
StyleClasses="Caution"
|
||||
Access="Public"/>
|
||||
<Label Name="StatusLabel"
|
||||
Access="Public"/>
|
||||
<Label Name="InfoLabel"
|
||||
Access="Public"/>
|
||||
</BoxContainer>
|
||||
</DefaultWindow>
|
||||
138
Content.Client/NukeOps/WarDeclaratorWindow.xaml.cs
Normal file
138
Content.Client/NukeOps/WarDeclaratorWindow.xaml.cs
Normal file
@@ -0,0 +1,138 @@
|
||||
using Content.Client.Stylesheets;
|
||||
using Content.Shared.NukeOps;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.NukeOps;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class WarDeclaratorWindow : DefaultWindow
|
||||
{
|
||||
private readonly IGameTiming _gameTiming;
|
||||
|
||||
public event Action<string>? OnActivated;
|
||||
|
||||
private TimeSpan _endTime;
|
||||
private TimeSpan _timeStamp;
|
||||
private WarConditionStatus _status;
|
||||
|
||||
public WarDeclaratorWindow()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
|
||||
_gameTiming = IoCManager.Resolve<IGameTiming>();
|
||||
|
||||
WarButton.OnPressed += ActivateWarDeclarator;
|
||||
|
||||
var loc = IoCManager.Resolve<ILocalizationManager>();
|
||||
MessageEdit.Placeholder = new Rope.Leaf(loc.GetString("war-declarator-message-placeholder"));
|
||||
}
|
||||
|
||||
protected override void Draw(DrawingHandleScreen handle)
|
||||
{
|
||||
base.Draw(handle);
|
||||
UpdateTimer();
|
||||
}
|
||||
|
||||
public void UpdateState(WarDeclaratorBoundUserInterfaceState state)
|
||||
{
|
||||
WarButton.Disabled = state.Status != WarConditionStatus.YES_WAR;
|
||||
|
||||
_timeStamp = state.Delay;
|
||||
_endTime = state.EndTime;
|
||||
_status = state.Status;
|
||||
|
||||
switch(state.Status)
|
||||
{
|
||||
case WarConditionStatus.WAR_READY:
|
||||
StatusLabel.Text = Loc.GetString("war-declarator-boost-declared");
|
||||
InfoLabel.Text = Loc.GetString("war-declarator-conditions-ready");
|
||||
StatusLabel.SetOnlyStyleClass(StyleNano.StyleClassPowerStateLow);
|
||||
break;
|
||||
case WarConditionStatus.WAR_DELAY:
|
||||
StatusLabel.Text = Loc.GetString("war-declarator-boost-declared-delay");
|
||||
UpdateTimer();
|
||||
StatusLabel.SetOnlyStyleClass(StyleNano.StyleClassPowerStateLow);
|
||||
break;
|
||||
case WarConditionStatus.YES_WAR:
|
||||
StatusLabel.Text = Loc.GetString("war-declarator-boost-possible");
|
||||
UpdateTimer();
|
||||
StatusLabel.SetOnlyStyleClass(StyleNano.StyleClassPowerStateGood);
|
||||
break;
|
||||
case WarConditionStatus.NO_WAR_SMALL_CREW:
|
||||
StatusLabel.Text = Loc.GetString("war-declarator-boost-impossible");
|
||||
InfoLabel.Text = Loc.GetString("war-declarator-conditions-small-crew", ("min", state.MinCrew));
|
||||
StatusLabel.SetOnlyStyleClass(StyleNano.StyleClassPowerStateNone);
|
||||
break;
|
||||
case WarConditionStatus.NO_WAR_SHUTTLE_DEPARTED:
|
||||
StatusLabel.Text = Loc.GetString("war-declarator-boost-impossible");
|
||||
InfoLabel.Text = Loc.GetString("war-declarator-conditions-left-outpost");
|
||||
StatusLabel.SetOnlyStyleClass(StyleNano.StyleClassPowerStateNone);
|
||||
break;
|
||||
case WarConditionStatus.NO_WAR_TIMEOUT:
|
||||
StatusLabel.Text = Loc.GetString("war-declarator-boost-impossible");
|
||||
InfoLabel.Text = Loc.GetString("war-declarator-conditions-time-out");
|
||||
StatusLabel.SetOnlyStyleClass(StyleNano.StyleClassPowerStateNone);
|
||||
break;
|
||||
default:
|
||||
StatusLabel.Text = Loc.GetString("war-declarator-boost-impossible");
|
||||
InfoLabel.Text = Loc.GetString("war-declarator-conditions-unknown");
|
||||
StatusLabel.SetOnlyStyleClass(StyleNano.StyleClassPowerStateNone);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateTimer()
|
||||
{
|
||||
switch(_status)
|
||||
{
|
||||
case WarConditionStatus.YES_WAR:
|
||||
var gameruleTime = _gameTiming.CurTime.Subtract(_timeStamp);
|
||||
var timeLeft = _endTime.Subtract(gameruleTime);
|
||||
|
||||
if (timeLeft > TimeSpan.Zero)
|
||||
{
|
||||
InfoLabel.Text = Loc.GetString("war-declarator-boost-timer", ("minutes", timeLeft.Minutes), ("seconds", timeLeft.Seconds));
|
||||
}
|
||||
else
|
||||
{
|
||||
_status = WarConditionStatus.NO_WAR_TIMEOUT;
|
||||
StatusLabel.Text = Loc.GetString("war-declarator-boost-impossible");
|
||||
InfoLabel.Text = Loc.GetString("war-declarator-conditions-time-out");
|
||||
StatusLabel.SetOnlyStyleClass(StyleNano.StyleClassPowerStateNone);
|
||||
WarButton.Disabled = true;
|
||||
}
|
||||
break;
|
||||
case WarConditionStatus.WAR_DELAY:
|
||||
var timeAfterDeclaration = _gameTiming.CurTime.Subtract(_timeStamp);
|
||||
var timeRemain = _endTime.Subtract(timeAfterDeclaration);
|
||||
|
||||
if (timeRemain > TimeSpan.Zero)
|
||||
{
|
||||
InfoLabel.Text = Loc.GetString("war-declarator-boost-timer", ("minutes", timeRemain.Minutes), ("seconds", timeRemain.Seconds));
|
||||
}
|
||||
else
|
||||
{
|
||||
_status = WarConditionStatus.WAR_READY;
|
||||
StatusLabel.Text = Loc.GetString("war-declarator-boost-declared");
|
||||
InfoLabel.Text = Loc.GetString("war-declarator-conditions-ready");
|
||||
StatusLabel.SetOnlyStyleClass(StyleNano.StyleClassPowerStateLow);
|
||||
WarButton.Disabled = true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void ActivateWarDeclarator(BaseButton.ButtonEventArgs obj)
|
||||
{
|
||||
var message = Rope.Collapse(MessageEdit.TextRope);
|
||||
OnActivated?.Invoke(message);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,7 @@
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using Content.Server.Access.Systems;
|
||||
using Content.Server.Administration.Logs;
|
||||
using Content.Server.AlertLevel;
|
||||
using Content.Server.Chat;
|
||||
using Content.Server.Chat.Systems;
|
||||
using Content.Server.Interaction;
|
||||
using Content.Server.Popups;
|
||||
@@ -16,11 +14,9 @@ using Content.Shared.CCVar;
|
||||
using Content.Shared.Communications;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Emag.Components;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Popups;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
namespace Content.Server.Communications
|
||||
{
|
||||
@@ -262,6 +258,9 @@ namespace Content.Server.Communications
|
||||
comp.AnnouncementCooldownRemaining = comp.DelayBetweenAnnouncements;
|
||||
UpdateCommsConsoleInterface(uid, comp);
|
||||
|
||||
var ev = new CommunicationConsoleAnnouncementEvent(uid, comp, msg, message.Session.AttachedEntity);
|
||||
RaiseLocalEvent(ref ev);
|
||||
|
||||
// allow admemes with vv
|
||||
Loc.TryGetString(comp.AnnouncementDisplayName, out var title);
|
||||
title ??= comp.AnnouncementDisplayName;
|
||||
@@ -291,6 +290,15 @@ namespace Content.Server.Communications
|
||||
_popupSystem.PopupEntity(Loc.GetString("comms-console-permission-denied"), uid, message.Session);
|
||||
return;
|
||||
}
|
||||
|
||||
var ev = new CommunicationConsoleCallShuttleAttemptEvent(uid, comp, mob);
|
||||
RaiseLocalEvent(ref ev);
|
||||
if (ev.Cancelled)
|
||||
{
|
||||
_popupSystem.PopupEntity(ev.Reason ?? Loc.GetString("comms-console-shuttle-unavailable"), uid, message.Session);
|
||||
return;
|
||||
}
|
||||
|
||||
_roundEndSystem.RequestRoundEnd(uid);
|
||||
_adminLogger.Add(LogType.Action, LogImpact.Extreme, $"{ToPrettyString(mob):player} has called the shuttle.");
|
||||
}
|
||||
@@ -309,4 +317,29 @@ namespace Content.Server.Communications
|
||||
_adminLogger.Add(LogType.Action, LogImpact.Extreme, $"{ToPrettyString(mob):player} has recalled the shuttle.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raised on announcement
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
public record struct CommunicationConsoleAnnouncementEvent(EntityUid Uid, CommunicationsConsoleComponent Component, string Text, EntityUid? Sender)
|
||||
{
|
||||
public EntityUid Uid = Uid;
|
||||
public CommunicationsConsoleComponent Component = Component;
|
||||
public EntityUid? Sender = Sender;
|
||||
public string Text = Text;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raised on shuttle call attempt. Can be cancelled
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
public record struct CommunicationConsoleCallShuttleAttemptEvent(EntityUid Uid, CommunicationsConsoleComponent Component, EntityUid? Sender)
|
||||
{
|
||||
public bool Cancelled = false;
|
||||
public EntityUid Uid = Uid;
|
||||
public CommunicationsConsoleComponent Component = Component;
|
||||
public EntityUid? Sender = Sender;
|
||||
public string? Reason;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,6 +104,7 @@ public sealed partial class GameTicker
|
||||
_sawmill.Info($"Started game rule {ToPrettyString(ruleEntity)}");
|
||||
|
||||
ruleData.Active = true;
|
||||
ruleData.ActivatedAt = _gameTiming.CurTime;
|
||||
var ev = new GameRuleStartedEvent(ruleEntity, id);
|
||||
RaiseLocalEvent(ruleEntity, ref ev, true);
|
||||
return true;
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
namespace Content.Server.GameTicking.Rules.Components;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||
|
||||
namespace Content.Server.GameTicking.Rules.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Component attached to all gamerule entities.
|
||||
@@ -14,6 +16,12 @@ public sealed partial class GameRuleComponent : Component
|
||||
[DataField("active")]
|
||||
public bool Active;
|
||||
|
||||
/// <summary>
|
||||
/// Game time when game rule was activated
|
||||
/// </summary>
|
||||
[DataField("activatedAt", customTypeSerializer:typeof(TimeOffsetSerializer))]
|
||||
public TimeSpan ActivatedAt;
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not the gamerule finished.
|
||||
/// Used for tracking whether a non-active gamerule has been started before.
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace Content.Server.GameTicking.Rules.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Tags grid as nuke ops shuttle
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class NukeOpsShuttleComponent : Component
|
||||
{
|
||||
}
|
||||
@@ -1,15 +1,13 @@
|
||||
using Content.Server.NPC.Components;
|
||||
using Content.Server.StationEvents.Events;
|
||||
using Content.Shared.Dataset;
|
||||
using Content.Shared.Humanoid.Prototypes;
|
||||
using Content.Shared.Roles;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.GameTicking.Rules.Components;
|
||||
@@ -44,6 +42,48 @@ public sealed partial class NukeopsRuleComponent : Component
|
||||
[DataField("spawnOutpost")]
|
||||
public bool SpawnOutpost = true;
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not nukie left their outpost
|
||||
/// </summary>
|
||||
[DataField("leftOutpost")]
|
||||
public bool LeftOutpost = false;
|
||||
|
||||
/// <summary>
|
||||
/// Enables opportunity to get extra TC for war declaration
|
||||
/// </summary>
|
||||
[DataField("canEnableWarOps")]
|
||||
public bool CanEnableWarOps = true;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates time when war has been declared, null if not declared
|
||||
/// </summary>
|
||||
[DataField("warDeclaredTime", customTypeSerializer: typeof(TimeOffsetSerializer))]
|
||||
public TimeSpan? WarDeclaredTime;
|
||||
|
||||
/// <summary>
|
||||
/// This amount of TC will be given to each nukie
|
||||
/// </summary>
|
||||
[DataField("warTCAmountPerNukie")]
|
||||
public int WarTCAmountPerNukie = 40;
|
||||
|
||||
/// <summary>
|
||||
/// Time allowed for declaration of war
|
||||
/// </summary>
|
||||
[DataField("warDeclarationDelay")]
|
||||
public TimeSpan WarDeclarationDelay = TimeSpan.FromMinutes(6);
|
||||
|
||||
/// <summary>
|
||||
/// Delay between war declaration and nuke ops arrival on station map. Gives crew time to prepare
|
||||
/// </summary>
|
||||
[DataField("warNukieArriveDelay")]
|
||||
public TimeSpan? WarNukieArriveDelay = TimeSpan.FromMinutes(15);
|
||||
|
||||
/// <summary>
|
||||
/// Minimal operatives count for war declaration
|
||||
/// </summary>
|
||||
[DataField("warDeclarationMinOps")]
|
||||
public int WarDeclarationMinOps = 4;
|
||||
|
||||
[DataField("spawnPointProto", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
||||
public string SpawnPointPrototype = "SpawnPointNukies";
|
||||
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Content.Server.Administration.Commands;
|
||||
using Content.Server.Chat.Managers;
|
||||
using Content.Server.Chat.Systems;
|
||||
using Content.Server.Communications;
|
||||
using Content.Server.GameTicking.Rules.Components;
|
||||
using Content.Server.Ghost.Roles.Components;
|
||||
using Content.Server.Ghost.Roles.Events;
|
||||
@@ -11,30 +14,40 @@ using Content.Server.Mind.Components;
|
||||
using Content.Server.NPC.Components;
|
||||
using Content.Server.NPC.Systems;
|
||||
using Content.Server.Nuke;
|
||||
using Content.Server.NukeOps;
|
||||
using Content.Server.Popups;
|
||||
using Content.Server.Preferences.Managers;
|
||||
using Content.Server.Roles;
|
||||
using Content.Server.RoundEnd;
|
||||
using Content.Server.Shuttles.Components;
|
||||
using Content.Server.Shuttles.Events;
|
||||
using Content.Server.Shuttles.Systems;
|
||||
using Content.Server.Spawners.Components;
|
||||
using Content.Server.Station.Components;
|
||||
using Content.Server.Station.Systems;
|
||||
using Content.Server.Store.Components;
|
||||
using Content.Server.Store.Systems;
|
||||
using Content.Shared.Dataset;
|
||||
using Content.Shared.Humanoid;
|
||||
using Content.Shared.Humanoid.Prototypes;
|
||||
using Content.Shared.Mobs;
|
||||
using Content.Shared.Mobs.Components;
|
||||
using Content.Shared.Nuke;
|
||||
using Content.Shared.NukeOps;
|
||||
using Content.Shared.Preferences;
|
||||
using Content.Shared.Roles;
|
||||
using Content.Shared.Store;
|
||||
using Content.Shared.Tag;
|
||||
using Content.Shared.Zombies;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.Maps;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.GameTicking.Rules;
|
||||
@@ -58,6 +71,18 @@ public sealed class NukeopsRuleSystem : GameRuleSystem<NukeopsRuleComponent>
|
||||
[Dependency] private readonly MindSystem _mindSystem = default!;
|
||||
[Dependency] private readonly RoleSystem _roles = default!;
|
||||
[Dependency] private readonly MetaDataSystem _metaData = default!;
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
[Dependency] private readonly ChatSystem _chatSystem = default!;
|
||||
[Dependency] private readonly StoreSystem _storeSystem = default!;
|
||||
[Dependency] private readonly TagSystem _tag = default!;
|
||||
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
||||
[Dependency] private readonly WarDeclaratorSystem _warDeclaratorSystem = default!;
|
||||
|
||||
[ValidatePrototypeId<CurrencyPrototype>]
|
||||
private const string TelecrystalCurrencyPrototype = "Telecrystal";
|
||||
|
||||
[ValidatePrototypeId<TagPrototype>]
|
||||
private const string NukeOpsUplinkTagPrototype = "NukeOpsUplink";
|
||||
|
||||
[ValidatePrototypeId<AntagPrototype>]
|
||||
public const string NukeopsId = "Nukeops";
|
||||
@@ -78,6 +103,119 @@ public sealed class NukeopsRuleSystem : GameRuleSystem<NukeopsRuleComponent>
|
||||
SubscribeLocalEvent<NukeOperativeComponent, ComponentInit>(OnComponentInit);
|
||||
SubscribeLocalEvent<NukeOperativeComponent, ComponentRemove>(OnComponentRemove);
|
||||
SubscribeLocalEvent<NukeOperativeComponent, EntityZombifiedEvent>(OnOperativeZombified);
|
||||
SubscribeLocalEvent<CommunicationConsoleCallShuttleAttemptEvent>(OnShuttleCallAttempt);
|
||||
SubscribeLocalEvent<ShuttleConsoleFTLTravelStartEvent>(OnShuttleConsoleFTLStart);
|
||||
SubscribeLocalEvent<ConsoleFTLAttemptEvent>(OnShuttleFTLAttempt);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true when the player with UID opUid is a nuclear operative. Prevents random
|
||||
/// people from using the war declarator outside of the game mode.
|
||||
/// </summary>
|
||||
public bool TryGetRuleFromOperative(EntityUid opUid, [NotNullWhen(true)] out (NukeopsRuleComponent, GameRuleComponent)? comps)
|
||||
{
|
||||
comps = null;
|
||||
var query = EntityQueryEnumerator<NukeopsRuleComponent, GameRuleComponent>();
|
||||
while (query.MoveNext(out var ruleEnt, out var nukeops, out var gameRule))
|
||||
{
|
||||
if (!GameTicker.IsGameRuleAdded(ruleEnt, gameRule))
|
||||
continue;
|
||||
|
||||
var found = nukeops.OperativePlayers.Values.Any(v => v.AttachedEntity == opUid);
|
||||
if (found)
|
||||
{
|
||||
comps = (nukeops, gameRule);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Search rule components by grid uid
|
||||
/// </summary>
|
||||
public bool TryGetRuleFromGrid(EntityUid gridId, [NotNullWhen(true)] out (NukeopsRuleComponent, GameRuleComponent)? comps)
|
||||
{
|
||||
comps = null;
|
||||
var query = EntityQueryEnumerator<NukeopsRuleComponent, GameRuleComponent>();
|
||||
while (query.MoveNext(out var ruleEnt, out var nukeops, out var gameRule))
|
||||
{
|
||||
if (!GameTicker.IsGameRuleAdded(ruleEnt, gameRule))
|
||||
continue;
|
||||
|
||||
if (gridId == nukeops.NukieShuttle || gridId == nukeops.NukieOutpost)
|
||||
{
|
||||
comps = (nukeops, gameRule);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns conditions for war declaration
|
||||
/// </summary>
|
||||
public WarConditionStatus GetWarCondition(NukeopsRuleComponent nukieRule, GameRuleComponent gameRule)
|
||||
{
|
||||
if (!nukieRule.CanEnableWarOps)
|
||||
return WarConditionStatus.NO_WAR_UNKNOWN;
|
||||
|
||||
if (nukieRule.WarDeclaredTime != null && nukieRule.WarNukieArriveDelay != null)
|
||||
{
|
||||
// Nukies must wait some time after declaration of war to get on the station
|
||||
var warTime = _gameTiming.CurTime.Subtract(nukieRule.WarDeclaredTime.Value);
|
||||
if (warTime > nukieRule.WarNukieArriveDelay)
|
||||
{
|
||||
return WarConditionStatus.WAR_READY;
|
||||
}
|
||||
return WarConditionStatus.WAR_DELAY;
|
||||
}
|
||||
|
||||
if (nukieRule.OperativePlayers.Count < nukieRule.WarDeclarationMinOps)
|
||||
return WarConditionStatus.NO_WAR_SMALL_CREW;
|
||||
if (nukieRule.LeftOutpost)
|
||||
return WarConditionStatus.NO_WAR_SHUTTLE_DEPARTED;
|
||||
|
||||
var gameruleTime = _gameTiming.CurTime.Subtract(gameRule.ActivatedAt);
|
||||
if (gameruleTime > nukieRule.WarDeclarationDelay)
|
||||
return WarConditionStatus.NO_WAR_TIMEOUT;
|
||||
|
||||
return WarConditionStatus.YES_WAR;
|
||||
}
|
||||
|
||||
public void DeclareWar(EntityUid opsUid, string msg, string title, SoundSpecifier? announcementSound = null, Color? colorOverride = null)
|
||||
{
|
||||
if (!TryGetRuleFromOperative(opsUid, out var comps))
|
||||
return;
|
||||
|
||||
var nukieRule = comps.Value.Item1;
|
||||
nukieRule.WarDeclaredTime = _gameTiming.CurTime;
|
||||
_chatSystem.DispatchGlobalAnnouncement(msg, title, announcementSound: announcementSound, colorOverride: colorOverride);
|
||||
DistributeExtraTC(nukieRule);
|
||||
_warDeclaratorSystem.RefreshAllUI(comps.Value.Item1, comps.Value.Item2);
|
||||
}
|
||||
|
||||
private void DistributeExtraTC(NukeopsRuleComponent nukieRule)
|
||||
{
|
||||
var enumerator = EntityQueryEnumerator<StoreComponent>();
|
||||
while (enumerator.MoveNext(out var uid, out var component))
|
||||
{
|
||||
if (!_tag.HasTag(uid, NukeOpsUplinkTagPrototype))
|
||||
continue;
|
||||
|
||||
if (!nukieRule.NukieOutpost.HasValue)
|
||||
continue;
|
||||
|
||||
if (Transform(uid).MapID != Transform(nukieRule.NukieOutpost.Value).MapID) // Will receive bonus TC only on their start outpost
|
||||
continue;
|
||||
|
||||
_storeSystem.TryAddCurrency(new () { { TelecrystalCurrencyPrototype, nukieRule.WarTCAmountPerNukie } }, uid, component);
|
||||
|
||||
var msg = Loc.GetString("store-currency-war-boost-given", ("target", uid));
|
||||
_popupSystem.PopupEntity(msg, uid);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnComponentInit(EntityUid uid, NukeOperativeComponent component, ComponentInit args)
|
||||
@@ -597,7 +735,7 @@ public sealed class NukeopsRuleSystem : GameRuleSystem<NukeopsRuleComponent>
|
||||
if (!_mindSystem.TryGetMind(uid, out var mindId, out var mind))
|
||||
return;
|
||||
|
||||
foreach (var nukeops in EntityQuery<NukeopsRuleComponent>())
|
||||
foreach (var (nukeops, gameRule) in EntityQuery<NukeopsRuleComponent, GameRuleComponent>())
|
||||
{
|
||||
if (nukeops.OperativeMindPendingData.TryGetValue(uid, out var role) || !nukeops.SpawnOutpost || !nukeops.EndsRound)
|
||||
{
|
||||
@@ -615,6 +753,7 @@ public sealed class NukeopsRuleSystem : GameRuleSystem<NukeopsRuleComponent>
|
||||
var name = MetaData(uid).EntityName;
|
||||
|
||||
nukeops.OperativePlayers.Add(name, playerSession);
|
||||
_warDeclaratorSystem.RefreshAllUI(nukeops, gameRule);
|
||||
|
||||
if (GameTicker.RunLevel != GameRunLevel.InRound)
|
||||
return;
|
||||
@@ -680,6 +819,8 @@ public sealed class NukeopsRuleSystem : GameRuleSystem<NukeopsRuleComponent>
|
||||
_shuttle.TryFTLDock(shuttleId, shuttle, component.NukieOutpost.Value);
|
||||
}
|
||||
|
||||
AddComp<NukeOpsShuttleComponent>(shuttleId);
|
||||
|
||||
component.NukiePlanet = mapId;
|
||||
component.NukieShuttle = shuttleId;
|
||||
return true;
|
||||
@@ -856,6 +997,81 @@ public sealed class NukeopsRuleSystem : GameRuleSystem<NukeopsRuleComponent>
|
||||
}
|
||||
}
|
||||
|
||||
private void OnShuttleFTLAttempt(ref ConsoleFTLAttemptEvent ev)
|
||||
{
|
||||
var query = EntityQueryEnumerator<NukeopsRuleComponent, GameRuleComponent>();
|
||||
while (query.MoveNext(out var ruleUid, out var nukeops, out var gameRule))
|
||||
{
|
||||
if (!GameTicker.IsGameRuleAdded(ruleUid, gameRule))
|
||||
continue;
|
||||
|
||||
if (nukeops.NukieOutpost == null ||
|
||||
nukeops.WarDeclaredTime == null ||
|
||||
nukeops.WarNukieArriveDelay == null ||
|
||||
ev.Uid != nukeops.NukieShuttle)
|
||||
continue;
|
||||
|
||||
var mapOutpost = Transform(nukeops.NukieOutpost.Value).MapID;
|
||||
var mapShuttle = Transform(ev.Uid).MapID;
|
||||
|
||||
if (mapOutpost == mapShuttle)
|
||||
{
|
||||
var timeAfterDeclaration = _gameTiming.CurTime.Subtract(nukeops.WarDeclaredTime.Value);
|
||||
var timeRemain = nukeops.WarNukieArriveDelay.Value.Subtract(timeAfterDeclaration);
|
||||
if (timeRemain > TimeSpan.Zero)
|
||||
{
|
||||
ev.Cancelled = true;
|
||||
ev.Reason = Loc.GetString("war-ops-infiltrator-unavailable", ("minutes", timeRemain.Minutes), ("seconds", timeRemain.Seconds));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnShuttleConsoleFTLStart(ref ShuttleConsoleFTLTravelStartEvent ev)
|
||||
{
|
||||
var query = EntityQueryEnumerator<NukeopsRuleComponent, GameRuleComponent>();
|
||||
while (query.MoveNext(out var ruleUid, out var nukeops, out var gameRule))
|
||||
{
|
||||
if (!GameTicker.IsGameRuleAdded(ruleUid, gameRule))
|
||||
continue;
|
||||
|
||||
var gridUid = Transform(ev.Uid).GridUid;
|
||||
if (nukeops.NukieOutpost == null ||
|
||||
gridUid == null ||
|
||||
gridUid.Value != nukeops.NukieShuttle)
|
||||
continue;
|
||||
|
||||
var mapOutpost = Transform(nukeops.NukieOutpost.Value).MapID;
|
||||
var mapShuttle = Transform(ev.Uid).MapID;
|
||||
|
||||
if (mapOutpost == mapShuttle)
|
||||
{
|
||||
nukeops.LeftOutpost = true;
|
||||
|
||||
if (TryGetRuleFromGrid(gridUid.Value, out var comps))
|
||||
_warDeclaratorSystem.RefreshAllUI(comps.Value.Item1, comps.Value.Item2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnShuttleCallAttempt(ref CommunicationConsoleCallShuttleAttemptEvent ev)
|
||||
{
|
||||
var query = EntityQueryEnumerator<NukeopsRuleComponent, GameRuleComponent>();
|
||||
while (query.MoveNext(out var ruleUid, out var nukeops, out var gameRule))
|
||||
{
|
||||
if (!GameTicker.IsGameRuleAdded(ruleUid, gameRule))
|
||||
continue;
|
||||
|
||||
// Can't call while nukies are preparing to arrive
|
||||
if (GetWarCondition(nukeops, gameRule) == WarConditionStatus.WAR_DELAY)
|
||||
{
|
||||
ev.Cancelled = true;
|
||||
ev.Reason = Loc.GetString("war-ops-shuttle-call-unavailable");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Started(EntityUid uid, NukeopsRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args)
|
||||
{
|
||||
base.Started(uid, component, gameRule, args);
|
||||
|
||||
48
Content.Server/NukeOps/WarDeclaratorComponent.cs
Normal file
48
Content.Server/NukeOps/WarDeclaratorComponent.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
using Robust.Shared.Audio;
|
||||
|
||||
namespace Content.Server.NukeOps;
|
||||
|
||||
/// <summary>
|
||||
/// Used with NukeOps game rule to send war declaration announcement
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class WarDeclaratorComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Custom war declaration message. If empty, use default.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("message")]
|
||||
public string Message;
|
||||
|
||||
/// <summary>
|
||||
/// Permission to customize message text
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("allowEditingMessage")]
|
||||
public bool AllowEditingMessage = true;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("maxMessageLength")]
|
||||
public int MaxMessageLength = 512;
|
||||
|
||||
/// <summary>
|
||||
/// War declarement text color
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("color")]
|
||||
public Color DeclarementColor = Color.Red;
|
||||
|
||||
/// <summary>
|
||||
/// War declarement sound file path
|
||||
/// </summary>
|
||||
[DataField("sound")]
|
||||
public SoundSpecifier DeclarementSound = new SoundPathSpecifier("/Audio/Announcements/war.ogg");
|
||||
|
||||
/// <summary>
|
||||
/// Fluent ID for the declarement title
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("title")]
|
||||
public string DeclarementTitle = "comms-console-announcement-title-nukie";
|
||||
}
|
||||
127
Content.Server/NukeOps/WarDeclaratorSystem.cs
Normal file
127
Content.Server/NukeOps/WarDeclaratorSystem.cs
Normal file
@@ -0,0 +1,127 @@
|
||||
using Content.Server.Administration.Logs;
|
||||
using Content.Server.GameTicking.Rules;
|
||||
using Content.Server.GameTicking.Rules.Components;
|
||||
using Content.Server.Popups;
|
||||
using Content.Server.UserInterface;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.NukeOps;
|
||||
using Robust.Server.GameObjects;
|
||||
|
||||
namespace Content.Server.NukeOps;
|
||||
|
||||
/// <summary>
|
||||
/// This handles nukeops special war mode declaration device and directly using nukeops game rule
|
||||
/// </summary>
|
||||
public sealed class WarDeclaratorSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!;
|
||||
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
|
||||
[Dependency] private readonly NukeopsRuleSystem _nukeopsRuleSystem = default!;
|
||||
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<WarDeclaratorComponent, WarDeclaratorActivateMessage>(OnActivated);
|
||||
SubscribeLocalEvent<WarDeclaratorComponent, ActivatableUIOpenAttemptEvent>(OnAttemptOpenUI);
|
||||
}
|
||||
|
||||
private void OnAttemptOpenUI(EntityUid uid, WarDeclaratorComponent component, ActivatableUIOpenAttemptEvent args)
|
||||
{
|
||||
if (!_nukeopsRuleSystem.TryGetRuleFromOperative(args.User, out var comps))
|
||||
{
|
||||
var msg = Loc.GetString("war-declarator-not-nukeops");
|
||||
_popupSystem.PopupEntity(msg, uid);
|
||||
args.Cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateUI(uid, comps.Value.Item1, comps.Value.Item2);
|
||||
}
|
||||
|
||||
private void OnActivated(EntityUid uid, WarDeclaratorComponent component, WarDeclaratorActivateMessage args)
|
||||
{
|
||||
if (!args.Session.AttachedEntity.HasValue ||
|
||||
!_nukeopsRuleSystem.TryGetRuleFromOperative(args.Session.AttachedEntity.Value, out var comps))
|
||||
return;
|
||||
|
||||
var condition = _nukeopsRuleSystem.GetWarCondition(comps.Value.Item1, comps.Value.Item2);
|
||||
if (condition != WarConditionStatus.YES_WAR)
|
||||
{
|
||||
UpdateUI(uid, comps.Value.Item1, comps.Value.Item2);
|
||||
return;
|
||||
}
|
||||
|
||||
var text = (args.Message.Length <= component.MaxMessageLength ? args.Message.Trim() : $"{args.Message.Trim().Substring(0, 256)}...").ToCharArray();
|
||||
|
||||
// No more than 2 newlines, other replaced to spaces
|
||||
var newlines = 0;
|
||||
for (var i = 0; i < text.Length; i++)
|
||||
{
|
||||
if (text[i] != '\n')
|
||||
continue;
|
||||
|
||||
if (newlines >= 2)
|
||||
text[i] = ' ';
|
||||
|
||||
newlines++;
|
||||
}
|
||||
|
||||
string message = new string(text);
|
||||
if (component.AllowEditingMessage && message != string.Empty)
|
||||
{
|
||||
component.Message = message;
|
||||
}
|
||||
else
|
||||
{
|
||||
message = Loc.GetString("war-declarator-default-message");
|
||||
}
|
||||
var title = Loc.GetString(component.DeclarementTitle);
|
||||
|
||||
_nukeopsRuleSystem.DeclareWar(args.Session.AttachedEntity.Value, message, title, component.DeclarementSound, component.DeclarementColor);
|
||||
|
||||
if (args.Session.AttachedEntity != null)
|
||||
_adminLogger.Add(LogType.Chat, LogImpact.Low, $"{ToPrettyString(args.Session.AttachedEntity.Value):player} has declared war with this text: {message}");
|
||||
}
|
||||
|
||||
public void RefreshAllUI(NukeopsRuleComponent nukeops, GameRuleComponent gameRule)
|
||||
{
|
||||
var enumerator = EntityQueryEnumerator<WarDeclaratorComponent>();
|
||||
while (enumerator.MoveNext(out var uid, out _))
|
||||
{
|
||||
UpdateUI(uid, nukeops, gameRule);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateUI(EntityUid declaratorUid, NukeopsRuleComponent nukeops, GameRuleComponent gameRule)
|
||||
{
|
||||
var condition = _nukeopsRuleSystem.GetWarCondition(nukeops, gameRule);
|
||||
|
||||
TimeSpan startTime;
|
||||
TimeSpan delayTime;
|
||||
switch(condition)
|
||||
{
|
||||
case WarConditionStatus.YES_WAR:
|
||||
startTime = gameRule.ActivatedAt;
|
||||
delayTime = nukeops.WarDeclarationDelay;
|
||||
break;
|
||||
case WarConditionStatus.WAR_DELAY:
|
||||
startTime = nukeops.WarDeclaredTime!.Value;
|
||||
delayTime = nukeops.WarNukieArriveDelay!.Value;
|
||||
break;
|
||||
default:
|
||||
startTime = TimeSpan.Zero;
|
||||
delayTime = TimeSpan.Zero;
|
||||
break;
|
||||
}
|
||||
|
||||
_userInterfaceSystem.TrySetUiState(
|
||||
declaratorUid,
|
||||
WarDeclaratorUiKey.Key,
|
||||
new WarDeclaratorBoundUserInterfaceState(
|
||||
condition,
|
||||
nukeops.WarDeclarationMinOps,
|
||||
delayTime,
|
||||
startTime));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace Content.Server.Shuttles.Events;
|
||||
|
||||
/// <summary>
|
||||
/// Raised when shuttle console approved FTL
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
public record struct ShuttleConsoleFTLTravelStartEvent(EntityUid Uid)
|
||||
{
|
||||
public EntityUid Uid = Uid;
|
||||
}
|
||||
@@ -122,6 +122,9 @@ public sealed partial class ShuttleConsoleSystem : SharedShuttleConsoleSystem
|
||||
var tagEv = new FTLTagEvent();
|
||||
RaiseLocalEvent(xform.GridUid.Value, ref tagEv);
|
||||
|
||||
var ev = new ShuttleConsoleFTLTravelStartEvent(uid);
|
||||
RaiseLocalEvent(ref ev);
|
||||
|
||||
_shuttle.FTLTravel(xform.GridUid.Value, shuttle, args.Destination, dock: dock, priorityTag: tagEv.Tag);
|
||||
}
|
||||
|
||||
@@ -211,7 +214,7 @@ public sealed partial class ShuttleConsoleSystem : SharedShuttleConsoleSystem
|
||||
{
|
||||
RemovePilot(user, pilotComponent);
|
||||
|
||||
// This feels backwards; is this intended to be a toggle?
|
||||
// This feels backwards; is this intended to be a toggle?
|
||||
if (console == uid)
|
||||
return false;
|
||||
}
|
||||
|
||||
48
Content.Shared/NukeOps/WarDeclaratorEvents.cs
Normal file
48
Content.Shared/NukeOps/WarDeclaratorEvents.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.NukeOps;
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum WarDeclaratorUiKey
|
||||
{
|
||||
Key,
|
||||
}
|
||||
|
||||
public enum WarConditionStatus : byte
|
||||
{
|
||||
WAR_READY,
|
||||
WAR_DELAY,
|
||||
YES_WAR,
|
||||
NO_WAR_UNKNOWN,
|
||||
NO_WAR_TIMEOUT,
|
||||
NO_WAR_SMALL_CREW,
|
||||
NO_WAR_SHUTTLE_DEPARTED
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class WarDeclaratorBoundUserInterfaceState : BoundUserInterfaceState
|
||||
{
|
||||
public WarConditionStatus Status;
|
||||
public int MinCrew;
|
||||
public TimeSpan Delay;
|
||||
public TimeSpan EndTime;
|
||||
|
||||
public WarDeclaratorBoundUserInterfaceState(WarConditionStatus status, int minCrew, TimeSpan delay, TimeSpan endTime)
|
||||
{
|
||||
Status = status;
|
||||
MinCrew = minCrew;
|
||||
Delay = delay;
|
||||
EndTime = endTime;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class WarDeclaratorActivateMessage : BoundUserInterfaceMessage
|
||||
{
|
||||
public string Message { get; }
|
||||
|
||||
public WarDeclaratorActivateMessage(string msg)
|
||||
{
|
||||
Message = msg;
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ comms-console-menu-recall-shuttle = Recall emergency shuttle
|
||||
|
||||
# Popup
|
||||
comms-console-permission-denied = Permission denied
|
||||
comms-console-shuttle-unavailable = Shuttle is currently unavailable
|
||||
|
||||
# Placeholder values
|
||||
comms-console-announcement-sent-by = Sent by
|
||||
|
||||
16
Resources/Locale/en-US/nukeops/war-declarator.ftl
Normal file
16
Resources/Locale/en-US/nukeops/war-declarator.ftl
Normal file
@@ -0,0 +1,16 @@
|
||||
war-declarator-not-nukeops = The device makes beeping noises, but nothing happens...
|
||||
war-declarator-ui-header = Declaration of War
|
||||
war-declarator-ui-war-button = DECLARE WAR!
|
||||
war-declarator-conditions-small-crew = Less than { $min } operatives
|
||||
war-declarator-conditions-left-outpost = Shuttle left the syndicate outpost
|
||||
war-declarator-conditions-time-out = War declaration time passed
|
||||
war-declarator-conditions-delay = Shuttle departure temporarily unavailable
|
||||
war-declarator-conditions-ready = Shuttle can leave the outpost!
|
||||
war-declarator-conditions-unknown = Unknown
|
||||
war-declarator-boost-possible = Able to declare war
|
||||
war-declarator-boost-impossible = Unable to declare war
|
||||
war-declarator-boost-declared = War declared!
|
||||
war-declarator-boost-declared-delay = War declared! Shuttle departure temporarily disabled
|
||||
war-declarator-boost-timer = Time left: {$minutes} minutes and {$seconds} seconds
|
||||
war-declarator-default-message = A syndicate fringe group has declared their intent to utterly destroy station with a nuclear device, and dares the crew to try and stop them.
|
||||
war-declarator-message-placeholder = Write a custom declaration of war here...
|
||||
2
Resources/Locale/en-US/nukeops/war-ops.ftl
Normal file
2
Resources/Locale/en-US/nukeops/war-ops.ftl
Normal file
@@ -0,0 +1,2 @@
|
||||
war-ops-infiltrator-unavailable = ERROR: FTL Travel recalculation in progress. Estimated time: {$minutes} minutes and {$seconds} seconds
|
||||
war-ops-shuttle-call-unavailable = Evacuation shuttle is currently unavailable. Please wait
|
||||
@@ -1,4 +1,5 @@
|
||||
store-currency-inserted = {CAPITALIZE(THE($used))} is inserted into the {THE($target)}.
|
||||
store-currency-war-boost-given = { CAPITALIZE($target) } starts buzzing
|
||||
store-currency-inserted-implant = {CAPITALIZE(THE($used))} is inserted into your implant.
|
||||
|
||||
store-currency-free = Free
|
||||
|
||||
@@ -4737,14 +4737,6 @@ entities:
|
||||
- pos: -4.5,-17.5
|
||||
parent: 73
|
||||
type: Transform
|
||||
- proto: SyndicateComputerComms
|
||||
entities:
|
||||
- uid: 556
|
||||
components:
|
||||
- rot: -1.5707963267948966 rad
|
||||
pos: 1.5,-4.5
|
||||
parent: 73
|
||||
type: Transform
|
||||
- proto: SyndieMiniBomb
|
||||
entities:
|
||||
- uid: 723
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
- type: entity
|
||||
parent: BaseItem
|
||||
id: NukeOpsDeclarationOfWar
|
||||
name: the declaration of war
|
||||
description: Use to send a declaration of hostilities to the target, delaying your shuttle departure while they prepare for your assault. Such a brazen move will attract the attention of powerful benefactors within the Syndicate, who will supply your team with a massive amount of bonus telecrystals. Must be used at start of mission, or your benefactors will lose interest.
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: Objects/Devices/declaration_of_war.rsi
|
||||
layers:
|
||||
- state: declarator
|
||||
- type: Item
|
||||
- type: UseDelay
|
||||
delay: 0.5
|
||||
- type: ActivatableUI
|
||||
inHandsOnly: true
|
||||
singleUser: true
|
||||
closeOnHandDeselect: false
|
||||
key: enum.WarDeclaratorUiKey.Key
|
||||
- type: UserInterface
|
||||
interfaces:
|
||||
- key: enum.WarDeclaratorUiKey.Key
|
||||
type: WarDeclaratorBoundUserInterface
|
||||
- type: WarDeclarator
|
||||
message: war-declarator-default-message
|
||||
# - type: WarConditionOnExamine
|
||||
@@ -128,6 +128,8 @@
|
||||
pocket1: DoubleEmergencyOxygenTankFilled
|
||||
pocket2: BaseUplinkRadio40TC
|
||||
belt: ClothingBeltMilitaryWebbing
|
||||
inhand:
|
||||
right hand: NukeOpsDeclarationOfWar
|
||||
innerclothingskirt: ClothingUniformJumpskirtOperative
|
||||
satchel: ClothingBackpackDuffelSyndicateOperative
|
||||
duffelbag: ClothingBackpackDuffelSyndicateOperative
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 921 B |
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"version": 1,
|
||||
"license": "CC-BY-SA-3.0",
|
||||
"copyright": "Edit of the door remote sprite by Flareguy for https://github.com/space-wizards/space-station-14",
|
||||
"size": {
|
||||
"x": 32,
|
||||
"y": 32
|
||||
},
|
||||
"states": [
|
||||
{
|
||||
"name": "declarator",
|
||||
"delays": [
|
||||
[
|
||||
0.3,
|
||||
0.3,
|
||||
0.3,
|
||||
0.3,
|
||||
0.3,
|
||||
0.2,
|
||||
0.2,
|
||||
0.2
|
||||
]
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user