Merge branch 'space-wizards:master' into master
|
|
@ -299,6 +299,7 @@ namespace Content.Client.Entry
|
|||
"IncreaseDamageOnWield",
|
||||
"TabletopGame",
|
||||
"LitOnPowered",
|
||||
"Foldable",
|
||||
"TriggerOnSignalReceived",
|
||||
"ToggleDoorOnTrigger",
|
||||
"DeviceNetworkComponent",
|
||||
|
|
@ -307,6 +308,8 @@ namespace Content.Client.Entry
|
|||
"ExtensionCableReceiver",
|
||||
"ExtensionCableProvider",
|
||||
"ApcNetworkConnection",
|
||||
"SuitSensor",
|
||||
"CrewMonitoringConsole",
|
||||
"ApcNetSwitch",
|
||||
"HandLabeler",
|
||||
"Label",
|
||||
|
|
@ -314,6 +317,9 @@ namespace Content.Client.Entry
|
|||
"NukeCodePaper",
|
||||
"GhostRadio",
|
||||
"Armor",
|
||||
"Guardian",
|
||||
"GuardianCreator",
|
||||
"GuardianHost",
|
||||
"Udder",
|
||||
"PneumaticCannon",
|
||||
"Spreader",
|
||||
|
|
|
|||
|
|
@ -3,29 +3,38 @@ using System.Globalization;
|
|||
using Content.Client.Lobby;
|
||||
using Content.Client.Viewport;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Info;
|
||||
using Robust.Client.Console;
|
||||
using Robust.Client.State;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.Info;
|
||||
|
||||
public sealed class RulesManager
|
||||
public sealed class RulesManager : SharedRulesManager
|
||||
{
|
||||
[Dependency] private readonly IResourceManager _resource = default!;
|
||||
[Dependency] private readonly IConfigurationManager _configManager = default!;
|
||||
[Dependency] private readonly IUserInterfaceManager _userInterfaceManager = default!;
|
||||
[Dependency] private readonly IStateManager _stateManager = default!;
|
||||
[Dependency] private readonly IClientConsoleHost _consoleHost = default!;
|
||||
[Dependency] private readonly INetManager _netManager = default!;
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
_netManager.RegisterNetMessage<ShowRulesPopupMessage>(OnShowRulesPopupMessage);
|
||||
_stateManager.OnStateChanged += OnStateChanged;
|
||||
}
|
||||
|
||||
private void OnShowRulesPopupMessage(ShowRulesPopupMessage message)
|
||||
{
|
||||
ShowRules(message.PopupTime);
|
||||
}
|
||||
|
||||
private void OnStateChanged(StateChangedEventArgs args)
|
||||
{
|
||||
if (args.NewState is not (GameScreen or LobbyState))
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
using Content.Shared.Medical.CrewMonitoring;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Client.Medical.CrewMonitoring
|
||||
{
|
||||
public class CrewMonitoringBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
private CrewMonitoringWindow? _menu;
|
||||
|
||||
public CrewMonitoringBoundUserInterface([NotNull] ClientUserInterfaceComponent owner, [NotNull] object uiKey) : base(owner, uiKey)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
_menu = new CrewMonitoringWindow();
|
||||
_menu.OpenCentered();
|
||||
_menu.OnClose += Close;
|
||||
}
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
base.UpdateState(state);
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case CrewMonitoringState st:
|
||||
_menu?.ShowSensors(st.Sensors);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
if (!disposing)
|
||||
return;
|
||||
|
||||
_menu?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
<SS14Window xmlns="https://spacestation14.io"
|
||||
Title="{Loc 'crew-monitoring-user-interface-title'}"
|
||||
MinSize="450 400">
|
||||
<ScrollContainer HorizontalExpand="True"
|
||||
VerticalExpand="True">
|
||||
<GridContainer Name="SensorsTable"
|
||||
HorizontalExpand="True"
|
||||
VerticalExpand="True"
|
||||
HSeparationOverride="5"
|
||||
VSeparationOverride="20"
|
||||
Columns="3">
|
||||
<!-- Table header -->
|
||||
<Label Text="{Loc 'crew-monitoring-user-interface-name'}"
|
||||
StyleClasses="LabelHeading"/>
|
||||
<Label Text="{Loc 'crew-monitoring-user-interface-status'}"
|
||||
StyleClasses="LabelHeading"/>
|
||||
<Label Text="{Loc 'crew-monitoring-user-interface-location'}"
|
||||
StyleClasses="LabelHeading"/>
|
||||
|
||||
<!-- Table rows are filled by code -->
|
||||
</GridContainer>
|
||||
</ScrollContainer>
|
||||
</SS14Window>
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
using System.Collections.Generic;
|
||||
using Content.Shared.Medical.SuitSensor;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Localization;
|
||||
|
||||
namespace Content.Client.Medical.CrewMonitoring
|
||||
{
|
||||
[GenerateTypedNameReferences]
|
||||
public partial class CrewMonitoringWindow : SS14Window
|
||||
{
|
||||
private List<Control> _rowsContent = new();
|
||||
|
||||
public CrewMonitoringWindow()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
}
|
||||
|
||||
public void ShowSensors(List<SuitSensorStatus> stSensors)
|
||||
{
|
||||
ClearAllSensors();
|
||||
|
||||
// add a row for each sensor
|
||||
foreach (var sensor in stSensors)
|
||||
{
|
||||
// add users name and job
|
||||
// format: UserName (Job)
|
||||
var nameLabel = new Label()
|
||||
{
|
||||
Text = $"{sensor.Name} ({sensor.Job})"
|
||||
};
|
||||
SensorsTable.AddChild(nameLabel);
|
||||
_rowsContent.Add(nameLabel);
|
||||
|
||||
// add users status and damage
|
||||
// format: IsAlive (TotalDamage)
|
||||
var statusText = Loc.GetString(sensor.IsAlive ?
|
||||
"crew-monitoring-user-interface-alive" :
|
||||
"crew-monitoring-user-interface-dead");
|
||||
if (sensor.TotalDamage != null)
|
||||
{
|
||||
statusText += $" ({sensor.TotalDamage})";
|
||||
}
|
||||
var statusLabel = new Label()
|
||||
{
|
||||
Text = statusText
|
||||
};
|
||||
SensorsTable.AddChild(statusLabel);
|
||||
_rowsContent.Add(statusLabel);
|
||||
|
||||
// add users positions
|
||||
// format: (x, y)
|
||||
string posText;
|
||||
if (sensor.Coordinates != null)
|
||||
{
|
||||
// todo: add locations names (kitchen, bridge, etc)
|
||||
var pos = sensor.Coordinates.Value.Position;
|
||||
var x = (int) pos.X;
|
||||
var y = (int) pos.Y;
|
||||
posText = $"({x}, {y})";
|
||||
}
|
||||
else
|
||||
{
|
||||
posText = Loc.GetString("crew-monitoring-user-interface-no-info");
|
||||
}
|
||||
var posLabel = new Label()
|
||||
{
|
||||
Text = posText
|
||||
};
|
||||
SensorsTable.AddChild(posLabel);
|
||||
_rowsContent.Add(posLabel);
|
||||
}
|
||||
}
|
||||
|
||||
private void ClearAllSensors()
|
||||
{
|
||||
foreach (var child in _rowsContent)
|
||||
{
|
||||
SensorsTable.RemoveChild(child);
|
||||
}
|
||||
_rowsContent.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -23,6 +23,10 @@ namespace Content.Client.Morgue.Visualizers
|
|||
{
|
||||
sprite.LayerSetVisible(BodyBagVisualLayers.Label, labelVal);
|
||||
}
|
||||
else
|
||||
{
|
||||
sprite.LayerSetVisible(BodyBagVisualLayers.Label, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,9 @@ namespace Content.Client.Storage.Visualizers
|
|||
[UsedImplicitly]
|
||||
public sealed class StorageVisualizer : AppearanceVisualizer
|
||||
{
|
||||
/// <summary>
|
||||
/// Sets the base sprite to this layer. Exists to make the inheritance tree less boilerplate-y.
|
||||
/// </summary>
|
||||
[DataField("state")]
|
||||
private string? _stateBase;
|
||||
[DataField("state_open")]
|
||||
|
|
@ -41,9 +44,24 @@ namespace Content.Client.Storage.Visualizers
|
|||
}
|
||||
|
||||
component.TryGetData(StorageVisuals.Open, out bool open);
|
||||
var state = open ? _stateOpen ?? $"{_stateBase}_open" : _stateClosed ?? $"{_stateBase}_door";
|
||||
|
||||
sprite.LayerSetState(StorageVisualLayers.Door, state);
|
||||
if (sprite.LayerMapTryGet(StorageVisualLayers.Door, out _))
|
||||
{
|
||||
sprite.LayerSetVisible(StorageVisualLayers.Door, true);
|
||||
|
||||
if (open && _stateOpen != null)
|
||||
{
|
||||
sprite.LayerSetState(StorageVisualLayers.Door, _stateOpen);
|
||||
}
|
||||
else if (!open && _stateClosed != null)
|
||||
{
|
||||
sprite.LayerSetState(StorageVisualLayers.Door, _stateClosed);
|
||||
}
|
||||
else
|
||||
{
|
||||
sprite.LayerSetVisible(StorageVisualLayers.Door, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (component.TryGetData(StorageVisuals.CanLock, out bool canLock) && canLock)
|
||||
{
|
||||
|
|
|
|||
36
Content.Client/Visualizer/FoldableVisualizer.cs
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
namespace Content.Client.Visualizer;
|
||||
|
||||
|
||||
public sealed class FoldableVisualizer : AppearanceVisualizer
|
||||
{
|
||||
[DataField("key")]
|
||||
private string _key = default!;
|
||||
|
||||
public override void OnChangeData(AppearanceComponent appearance)
|
||||
{
|
||||
base.OnChangeData(appearance);
|
||||
|
||||
var entManager = IoCManager.Resolve<IEntityManager>();
|
||||
|
||||
if (!entManager.TryGetComponent(appearance.Owner, out SpriteComponent? sprite)) return;
|
||||
|
||||
if (appearance.TryGetData("FoldedState", out bool folded) && folded)
|
||||
{
|
||||
sprite.LayerSetState(FoldableVisualLayers.Base, $"{_key}_folded");
|
||||
}
|
||||
else
|
||||
{
|
||||
sprite.LayerSetState(FoldableVisualLayers.Base, $"{_key}");
|
||||
}
|
||||
}
|
||||
|
||||
public enum FoldableVisualLayers : byte
|
||||
{
|
||||
Base,
|
||||
}
|
||||
}
|
||||
29
Content.Client/Visualizer/RollerbedVisualizer.cs
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
namespace Content.Client.Visualizer
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public sealed class RollerbedVisualizer : AppearanceVisualizer
|
||||
{
|
||||
[DataField("key")]
|
||||
private string _key = default!;
|
||||
|
||||
public override void OnChangeData(AppearanceComponent appearance)
|
||||
{
|
||||
base.OnChangeData(appearance);
|
||||
|
||||
var entManager = IoCManager.Resolve<IEntityManager>();
|
||||
|
||||
if (!entManager.TryGetComponent(appearance.Owner, out SpriteComponent? sprite)) return;
|
||||
|
||||
if (appearance.TryGetData("StrapState", out bool strapped) && strapped)
|
||||
{
|
||||
sprite.LayerSetState(0, $"{_key}_buckled");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -101,22 +101,14 @@ namespace Content.Server.Access.Systems
|
|||
return true;
|
||||
|
||||
// check inventory slot?
|
||||
if (EntityManager.TryGetComponent(uid, out InventoryComponent? inventoryComponent) &&
|
||||
inventoryComponent.HasSlot(EquipmentSlotDefines.Slots.IDCARD) &&
|
||||
inventoryComponent.TryGetSlotItem(EquipmentSlotDefines.Slots.IDCARD, out ItemComponent? item) &&
|
||||
TryGetIdCard(item.Owner, out idCard))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return TryGetIdCardSlot(uid, out idCard);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempt to get an id card component from an entity, either by getting it directly from the entity, or by
|
||||
/// getting the contained id from a <see cref="PDAComponent"/>.
|
||||
/// </summary>
|
||||
private bool TryGetIdCard(EntityUid uid, [NotNullWhen(true)] out IdCardComponent? idCard)
|
||||
public bool TryGetIdCard(EntityUid uid, [NotNullWhen(true)] out IdCardComponent? idCard)
|
||||
{
|
||||
if (EntityManager.TryGetComponent(uid, out idCard))
|
||||
return true;
|
||||
|
|
@ -129,5 +121,17 @@ namespace Content.Server.Access.Systems
|
|||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try get id card from mobs ID inventory slot
|
||||
/// </summary>
|
||||
public bool TryGetIdCardSlot(EntityUid uid, [NotNullWhen(true)] out IdCardComponent? idCard)
|
||||
{
|
||||
idCard = null;
|
||||
return EntityManager.TryGetComponent(uid, out InventoryComponent? inventoryComponent) &&
|
||||
inventoryComponent.HasSlot(EquipmentSlotDefines.Slots.IDCARD) &&
|
||||
inventoryComponent.TryGetSlotItem(EquipmentSlotDefines.Slots.IDCARD, out ItemComponent? item) &&
|
||||
TryGetIdCard(item.Owner, out idCard);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
38
Content.Server/Actions/Actions/GuardianToggleAction.cs
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
using Content.Server.Guardian;
|
||||
using Content.Shared.Actions.Behaviors;
|
||||
using Content.Shared.Cooldown;
|
||||
using Content.Shared.Popups;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
namespace Content.Server.Actions.Actions
|
||||
{
|
||||
/// <summary>
|
||||
/// Manifests the guardian saved in the action, using the system
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
[DataDefinition]
|
||||
public class ToggleGuardianAction : IInstantAction
|
||||
{
|
||||
[DataField("cooldown")] public float Cooldown { get; [UsedImplicitly] private set; }
|
||||
|
||||
public void DoInstantAction(InstantActionEventArgs args)
|
||||
{
|
||||
var entManager = IoCManager.Resolve<IEntityManager>();
|
||||
|
||||
if (entManager.TryGetComponent(args.Performer, out GuardianHostComponent? hostComponent) &&
|
||||
hostComponent.HostedGuardian != null)
|
||||
{
|
||||
EntitySystem.Get<GuardianSystem>().ToggleGuardian(hostComponent);
|
||||
args.PerformerActions?.Cooldown(args.ActionType, Cooldowns.SecondsFromNow(Cooldown));
|
||||
}
|
||||
else
|
||||
{
|
||||
args.Performer.PopupMessage(Loc.GetString("guardian-missing-invalid-action"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -133,7 +133,7 @@ namespace Content.Server.Buckle.Components
|
|||
break;
|
||||
}
|
||||
|
||||
ownTransform.LocalPosition = Vector2.Zero + BuckleOffset;
|
||||
ownTransform.LocalPosition = strap.BuckleOffset;
|
||||
}
|
||||
|
||||
public bool CanBuckle(EntityUid user, EntityUid to, [NotNullWhen(true)] out StrapComponent? strap)
|
||||
|
|
@ -317,10 +317,17 @@ namespace Content.Server.Buckle.Components
|
|||
|
||||
BuckledTo = null;
|
||||
|
||||
if (_entMan.GetComponent<TransformComponent>(Owner).Parent == _entMan.GetComponent<TransformComponent>(oldBuckledTo.Owner))
|
||||
var entManager = IoCManager.Resolve<IEntityManager>();
|
||||
var xform = entManager.GetComponent<TransformComponent>(Owner);
|
||||
var oldBuckledXform = entManager.GetComponent<TransformComponent>(oldBuckledTo.Owner);
|
||||
|
||||
if (xform.ParentUid == oldBuckledXform.Owner)
|
||||
{
|
||||
_entMan.GetComponent<TransformComponent>(Owner).AttachParentToContainerOrGrid();
|
||||
_entMan.GetComponent<TransformComponent>(Owner).WorldRotation = _entMan.GetComponent<TransformComponent>(oldBuckledTo.Owner).WorldRotation;
|
||||
xform.AttachParentToContainerOrGrid();
|
||||
xform.WorldRotation = oldBuckledXform.WorldRotation;
|
||||
|
||||
if (oldBuckledTo.UnbuckleOffset != Vector2.Zero)
|
||||
xform.Coordinates = oldBuckledXform.Coordinates.Offset(oldBuckledTo.UnbuckleOffset);
|
||||
}
|
||||
|
||||
Appearance?.SetData(BuckleVisuals.Buckled, false);
|
||||
|
|
@ -403,22 +410,5 @@ namespace Content.Server.Buckle.Components
|
|||
|
||||
return new BuckleComponentState(Buckled, drawDepth, LastEntityBuckledTo, DontCollide);
|
||||
}
|
||||
|
||||
public void Update(PhysicsComponent physics)
|
||||
{
|
||||
if (!DontCollide)
|
||||
return;
|
||||
|
||||
physics.WakeBody();
|
||||
|
||||
if (!IsOnStrapEntityThisFrame && DontCollide)
|
||||
{
|
||||
DontCollide = false;
|
||||
TryUnbuckle(Owner);
|
||||
Dirty();
|
||||
}
|
||||
|
||||
IsOnStrapEntityThisFrame = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ using Content.Shared.Sound;
|
|||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
|
@ -21,8 +22,6 @@ namespace Content.Server.Buckle.Components
|
|||
{
|
||||
[ComponentDependency] public readonly SpriteComponent? SpriteComponent = null;
|
||||
|
||||
[Dependency] private readonly IEntityManager _entMan = default!;
|
||||
|
||||
private readonly HashSet<EntityUid> _buckledEntities = new();
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -37,6 +36,50 @@ namespace Content.Server.Buckle.Components
|
|||
[ViewVariables] [DataField("size")] private int _size = 100;
|
||||
private int _occupiedSize;
|
||||
|
||||
/// <summary>
|
||||
/// The buckled entity will be offset by this amount from the center of the strap object.
|
||||
/// If this offset it too big, it will be clamped to <see cref="MaxBuckleDistance"/>
|
||||
/// </summary>
|
||||
[DataField("buckleOffset", required: false)]
|
||||
private Vector2 _buckleOffset = Vector2.Zero;
|
||||
|
||||
private bool _enabled = true;
|
||||
|
||||
/// <summary>
|
||||
/// If disabled, nothing can be buckled on this object, and it will unbuckle anything that's already buckled
|
||||
/// </summary>
|
||||
public bool Enabled
|
||||
{
|
||||
get => _enabled;
|
||||
set
|
||||
{
|
||||
_enabled = value;
|
||||
if (_enabled == value) return;
|
||||
RemoveAll();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The distance above which a buckled entity will be automatically unbuckled.
|
||||
/// Don't change it unless you really have to
|
||||
/// </summary>
|
||||
[DataField("maxBuckleDistance", required: false)]
|
||||
public float MaxBuckleDistance = 0.1f;
|
||||
|
||||
/// <summary>
|
||||
/// You can specify the offset the entity will have after unbuckling.
|
||||
/// </summary>
|
||||
[DataField("unbuckleOffset", required: false)]
|
||||
public Vector2 UnbuckleOffset = Vector2.Zero;
|
||||
|
||||
/// <summary>
|
||||
/// Gets and clamps the buckle offset to MaxBuckleDistance
|
||||
/// </summary>
|
||||
public Vector2 BuckleOffset => Vector2.Clamp(
|
||||
_buckleOffset,
|
||||
Vector2.One * -MaxBuckleDistance,
|
||||
Vector2.One * MaxBuckleDistance);
|
||||
|
||||
/// <summary>
|
||||
/// The entity that is currently buckled here, synced from <see cref="BuckleComponent.BuckledTo"/>
|
||||
/// </summary>
|
||||
|
|
@ -96,6 +139,8 @@ namespace Content.Server.Buckle.Components
|
|||
/// <returns>True if added, false otherwise</returns>
|
||||
public bool TryAdd(BuckleComponent buckle, bool force = false)
|
||||
{
|
||||
if (!Enabled) return false;
|
||||
|
||||
if (!force && !HasSpace(buckle))
|
||||
{
|
||||
return false;
|
||||
|
|
@ -110,6 +155,12 @@ namespace Content.Server.Buckle.Components
|
|||
|
||||
buckle.Appearance?.SetData(StrapVisuals.RotationAngle, _rotation);
|
||||
|
||||
// Update the visuals of the strap object
|
||||
if (IoCManager.Resolve<IEntityManager>().TryGetComponent<AppearanceComponent>(Owner, out var appearance))
|
||||
{
|
||||
appearance.SetData("StrapState", true);
|
||||
}
|
||||
|
||||
#pragma warning disable 618
|
||||
SendMessage(new StrapMessage(buckle.Owner, Owner));
|
||||
#pragma warning restore 618
|
||||
|
|
@ -126,6 +177,11 @@ namespace Content.Server.Buckle.Components
|
|||
{
|
||||
if (_buckledEntities.Remove(buckle.Owner))
|
||||
{
|
||||
if (IoCManager.Resolve<IEntityManager>().TryGetComponent<AppearanceComponent>(Owner, out var appearance))
|
||||
{
|
||||
appearance.SetData("StrapState", false);
|
||||
}
|
||||
|
||||
_occupiedSize -= buckle.Size;
|
||||
#pragma warning disable 618
|
||||
SendMessage(new UnStrapMessage(buckle.Owner, Owner));
|
||||
|
|
@ -147,9 +203,11 @@ namespace Content.Server.Buckle.Components
|
|||
|
||||
private void RemoveAll()
|
||||
{
|
||||
var entManager = IoCManager.Resolve<IEntityManager>();
|
||||
|
||||
foreach (var entity in _buckledEntities.ToArray())
|
||||
{
|
||||
if (_entMan.TryGetComponent<BuckleComponent?>(entity, out var buckle))
|
||||
if (entManager.TryGetComponent<BuckleComponent>(entity, out var buckle))
|
||||
{
|
||||
buckle.TryUnbuckle(entity, true);
|
||||
}
|
||||
|
|
@ -166,7 +224,9 @@ namespace Content.Server.Buckle.Components
|
|||
|
||||
bool IInteractHand.InteractHand(InteractHandEventArgs eventArgs)
|
||||
{
|
||||
if (!_entMan.TryGetComponent<BuckleComponent?>(eventArgs.User, out var buckle))
|
||||
var entManager = IoCManager.Resolve<IEntityManager>();
|
||||
|
||||
if (!entManager.TryGetComponent<BuckleComponent>(eventArgs.User, out var buckle))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
@ -176,7 +236,9 @@ namespace Content.Server.Buckle.Components
|
|||
|
||||
public override bool DragDropOn(DragDropEvent eventArgs)
|
||||
{
|
||||
if (!_entMan.TryGetComponent(eventArgs.Dragged, out BuckleComponent? buckleComponent)) return false;
|
||||
var entManager = IoCManager.Resolve<IEntityManager>();
|
||||
|
||||
if (!entManager.TryGetComponent(eventArgs.Dragged, out BuckleComponent? buckleComponent)) return false;
|
||||
return buckleComponent.TryBuckle(eventArgs.User, Owner);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,10 +42,12 @@ namespace Content.Server.Buckle.Systems
|
|||
if (!args.CanAccess || !args.CanInteract || !component.Buckled)
|
||||
return;
|
||||
|
||||
Verb verb = new();
|
||||
verb.Act = () => component.TryUnbuckle(args.User);
|
||||
verb.Text = Loc.GetString("verb-categories-unbuckle");
|
||||
verb.IconTexture = "/Textures/Interface/VerbIcons/unbuckle.svg.192dpi.png";
|
||||
Verb verb = new()
|
||||
{
|
||||
Act = () => component.TryUnbuckle(args.User),
|
||||
Text = Loc.GetString("verb-categories-unbuckle"),
|
||||
IconTexture = "/Textures/Interface/VerbIcons/unbuckle.svg.192dpi.png"
|
||||
};
|
||||
|
||||
if (args.Target == args.User && args.Using == null)
|
||||
{
|
||||
|
|
@ -62,14 +64,6 @@ namespace Content.Server.Buckle.Systems
|
|||
args.Handled = component.TryUnbuckle(args.User);
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
foreach (var (buckle, physics) in EntityManager.EntityQuery<BuckleComponent, PhysicsComponent>())
|
||||
{
|
||||
buckle.Update(physics);
|
||||
}
|
||||
}
|
||||
|
||||
private void MoveEvent(EntityUid uid, BuckleComponent buckle, ref MoveEvent ev)
|
||||
{
|
||||
var strap = buckle.BuckledTo;
|
||||
|
|
@ -81,7 +75,7 @@ namespace Content.Server.Buckle.Systems
|
|||
|
||||
var strapPosition = EntityManager.GetComponent<TransformComponent>(strap.Owner).Coordinates.Offset(buckle.BuckleOffset);
|
||||
|
||||
if (ev.NewPosition.InRange(EntityManager, strapPosition, 0.2f))
|
||||
if (ev.NewPosition.InRange(EntityManager, strapPosition, strap.MaxBuckleDistance))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ namespace Content.Server.Buckle.Systems
|
|||
|
||||
private void AddStrapVerbs(EntityUid uid, StrapComponent component, GetInteractionVerbsEvent args)
|
||||
{
|
||||
if (args.Hands == null || !args.CanAccess || !args.CanInteract)
|
||||
if (args.Hands == null || !args.CanAccess || !args.CanInteract || !component.Enabled)
|
||||
return;
|
||||
|
||||
// Note that for whatever bloody reason, buckle component has its own interaction range. Additionally, this
|
||||
|
|
@ -41,9 +41,12 @@ namespace Content.Server.Buckle.Systems
|
|||
if (!_interactionSystem.InRangeUnobstructed(args.User, args.Target, range: buckledComp.Range))
|
||||
continue;
|
||||
|
||||
Verb verb = new();
|
||||
verb.Act = () => buckledComp.TryUnbuckle(args.User);
|
||||
verb.Category = VerbCategory.Unbuckle;
|
||||
Verb verb = new()
|
||||
{
|
||||
Act = () => buckledComp.TryUnbuckle(args.User),
|
||||
Category = VerbCategory.Unbuckle
|
||||
};
|
||||
|
||||
if (entity == args.User)
|
||||
verb.Text = Loc.GetString("verb-self-target-pronoun");
|
||||
else
|
||||
|
|
@ -64,10 +67,12 @@ namespace Content.Server.Buckle.Systems
|
|||
component.HasSpace(buckle) &&
|
||||
_interactionSystem.InRangeUnobstructed(args.User, args.Target, range: buckle.Range))
|
||||
{
|
||||
Verb verb = new();
|
||||
verb.Act = () => buckle.TryBuckle(args.User, args.Target);
|
||||
verb.Category = VerbCategory.Buckle;
|
||||
verb.Text = Loc.GetString("verb-self-target-pronoun");
|
||||
Verb verb = new()
|
||||
{
|
||||
Act = () => buckle.TryBuckle(args.User, args.Target),
|
||||
Category = VerbCategory.Buckle,
|
||||
Text = Loc.GetString("verb-self-target-pronoun")
|
||||
};
|
||||
args.Verbs.Add(verb);
|
||||
}
|
||||
|
||||
|
|
@ -82,14 +87,15 @@ namespace Content.Server.Buckle.Systems
|
|||
if (!_interactionSystem.InRangeUnobstructed(@using, args.Target, usingBuckle.Range, predicate: Ignored))
|
||||
return;
|
||||
|
||||
Verb verb = new();
|
||||
verb.Act = () => usingBuckle.TryBuckle(args.User, args.Target);
|
||||
verb.Category = VerbCategory.Buckle;
|
||||
verb.Text = EntityManager.GetComponent<MetaDataComponent>(@using).EntityName;
|
||||
|
||||
// If the used entity is a person being pulled, prioritize this verb. Conversely, if it is
|
||||
// just a held object, the user is probably just trying to sit down.
|
||||
verb.Priority = EntityManager.HasComponent<ActorComponent>(@using) ? 1 : -1;
|
||||
Verb verb = new()
|
||||
{
|
||||
Act = () => usingBuckle.TryBuckle(args.User, args.Target),
|
||||
Category = VerbCategory.Buckle,
|
||||
Text = EntityManager.GetComponent<MetaDataComponent>(@using).EntityName,
|
||||
// just a held object, the user is probably just trying to sit down.
|
||||
// If the used entity is a person being pulled, prioritize this verb. Conversely, if it is
|
||||
Priority = EntityManager.HasComponent<ActorComponent>(@using) ? 1 : -1
|
||||
};
|
||||
|
||||
args.Verbs.Add(verb);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,9 @@ namespace Content.Server.Destructible.Thresholds.Behaviors
|
|||
[DataField("spawn")]
|
||||
public Dictionary<string, MinMax> Spawn { get; set; } = new();
|
||||
|
||||
[DataField("offset")]
|
||||
public float Offset { get; set; } = 0.5f;
|
||||
|
||||
public void Execute(EntityUid owner, DestructibleSystem system)
|
||||
{
|
||||
var position = system.EntityManager.GetComponent<TransformComponent>(owner).MapPosition;
|
||||
|
|
@ -36,14 +39,14 @@ namespace Content.Server.Destructible.Thresholds.Behaviors
|
|||
var spawned = system.EntityManager.SpawnEntity(entityId, position);
|
||||
var stack = IoCManager.Resolve<IEntityManager>().GetComponent<StackComponent>(spawned);
|
||||
EntitySystem.Get<StackSystem>().SetCount(spawned, count, stack);
|
||||
spawned.RandomOffset(0.5f);
|
||||
spawned.RandomOffset(Offset);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var spawned = system.EntityManager.SpawnEntity(entityId, position);
|
||||
spawned.RandomOffset(0.5f);
|
||||
spawned.RandomOffset(Offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,12 @@ namespace Content.Server.DeviceNetwork
|
|||
/// </summary>
|
||||
public const string CmdSetState = "set_state";
|
||||
|
||||
/// <summary>
|
||||
/// The command for a device that just updated its state
|
||||
/// E.g. suit sensors broadcasting owners vitals state
|
||||
/// </summary>
|
||||
public const string CmdUpdatedState = "updated_state";
|
||||
|
||||
#endregion
|
||||
|
||||
#region SetState
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ using Content.Server.Connection;
|
|||
using Content.Server.Database;
|
||||
using Content.Server.EUI;
|
||||
using Content.Server.GameTicking;
|
||||
using Content.Server.Info;
|
||||
using Content.Server.IoC;
|
||||
using Content.Server.Maps;
|
||||
using Content.Server.NodeContainer.NodeGroups;
|
||||
|
|
@ -91,6 +92,7 @@ namespace Content.Server.Entry
|
|||
IoCManager.Resolve<IAdminManager>().Initialize();
|
||||
IoCManager.Resolve<INpcBehaviorManager>().Initialize();
|
||||
IoCManager.Resolve<IAfkManager>().Initialize();
|
||||
IoCManager.Resolve<RulesManager>().Initialize();
|
||||
_euiManager.Initialize();
|
||||
|
||||
IoCManager.Resolve<IGameMapManager>().Initialize();
|
||||
|
|
|
|||
25
Content.Server/Foldable/FoldableComponent.cs
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
#nullable enable
|
||||
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.Foldable
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Used to create "foldable structures" that you can pickup like an item when folded. Used for rollerbeds and wheelchairs
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public class FoldableComponent : Component
|
||||
{
|
||||
public override string Name => "Foldable";
|
||||
|
||||
[DataField("folded")]
|
||||
[ViewVariables]
|
||||
public bool IsFolded = false;
|
||||
|
||||
public bool CanBeFolded = true;
|
||||
}
|
||||
}
|
||||
131
Content.Server/Foldable/FoldableSystem.cs
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
using System.Linq;
|
||||
using Content.Server.Buckle.Components;
|
||||
using Content.Server.Storage.Components;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Item;
|
||||
using Content.Shared.Verbs;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
|
||||
namespace Content.Server.Foldable
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public sealed class FoldableSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private SharedContainerSystem _container = default!;
|
||||
|
||||
private const string FoldKey = "FoldedState";
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<FoldableComponent, ComponentInit>(OnFoldableInit);
|
||||
SubscribeLocalEvent<FoldableComponent, StorageOpenAttemptEvent>(OnFoldableOpenAttempt);
|
||||
SubscribeLocalEvent<FoldableComponent, AttemptItemPickupEvent>(OnPickedUpAttempt);
|
||||
SubscribeLocalEvent<FoldableComponent, GetAlternativeVerbsEvent>(AddFoldVerb);
|
||||
}
|
||||
|
||||
private void OnFoldableOpenAttempt(EntityUid uid, FoldableComponent component, StorageOpenAttemptEvent args)
|
||||
{
|
||||
if (component.IsFolded)
|
||||
args.Cancel();
|
||||
}
|
||||
|
||||
private void OnFoldableInit(EntityUid uid, FoldableComponent component, ComponentInit args)
|
||||
{
|
||||
SetFolded(component, component.IsFolded);
|
||||
}
|
||||
|
||||
private bool TryToggleFold(FoldableComponent comp)
|
||||
{
|
||||
return TrySetFolded(comp, !comp.IsFolded);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try to fold/unfold
|
||||
/// </summary>
|
||||
/// <param name="comp"></param>
|
||||
/// <param name="state">Folded state we want</param>
|
||||
/// <returns>True if successful</returns>
|
||||
private bool TrySetFolded(FoldableComponent comp, bool state)
|
||||
{
|
||||
if (state == comp.IsFolded)
|
||||
return false;
|
||||
|
||||
if (_container.IsEntityInContainer(comp.Owner))
|
||||
return false;
|
||||
|
||||
// First we check if the foldable object has a strap component
|
||||
if (EntityManager.TryGetComponent(comp.Owner, out StrapComponent? strap))
|
||||
{
|
||||
// If an entity is buckled to the object we can't pick it up or fold it
|
||||
if (strap.BuckledEntities.Any())
|
||||
return false;
|
||||
}
|
||||
|
||||
SetFolded(comp, state);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the folded state of the given <see cref="FoldableComponent"/>
|
||||
/// </summary>
|
||||
/// <param name="component"></param>
|
||||
/// <param name="folded">If true, the component will become folded, else unfolded</param>
|
||||
private void SetFolded(FoldableComponent component, bool folded)
|
||||
{
|
||||
component.IsFolded = folded;
|
||||
component.CanBeFolded = !_container.IsEntityInContainer(component.Owner);
|
||||
|
||||
// You can't buckle an entity to a folded object
|
||||
if (EntityManager.TryGetComponent(component.Owner, out StrapComponent? strap))
|
||||
strap.Enabled = !component.IsFolded;
|
||||
|
||||
// Update visuals only if the value has changed
|
||||
if (EntityManager.TryGetComponent(component.Owner, out AppearanceComponent? appearance))
|
||||
appearance.SetData(FoldKey, folded);
|
||||
}
|
||||
|
||||
#region Event handlers
|
||||
|
||||
/// <summary>
|
||||
/// Prevents foldable objects to be picked up when unfolded
|
||||
/// </summary>
|
||||
/// <param name="uid"></param>
|
||||
/// <param name="component"></param>
|
||||
/// <param name="args"></param>
|
||||
private void OnPickedUpAttempt(EntityUid uid, FoldableComponent component, AttemptItemPickupEvent args)
|
||||
{
|
||||
if (!component.IsFolded)
|
||||
args.Cancel();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Verb
|
||||
|
||||
private void AddFoldVerb(EntityUid uid, FoldableComponent component, GetAlternativeVerbsEvent args)
|
||||
{
|
||||
if (!args.CanAccess || !args.CanInteract)
|
||||
return;
|
||||
|
||||
Verb verb = new()
|
||||
{
|
||||
Act = () => TryToggleFold(component),
|
||||
Text = component.IsFolded ? Loc.GetString("unfold-verb") : Loc.GetString("fold-verb"),
|
||||
IconTexture = "/Textures/Interface/VerbIcons/fold.svg.192dpi.png",
|
||||
|
||||
// If the object is unfolded and they click it, they want to fold it, if it's folded, they want to pick it up
|
||||
Priority = component.IsFolded ? 0 : 2,
|
||||
};
|
||||
|
||||
args.Verbs.Add(verb);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
39
Content.Server/Guardian/GuardianComponent.cs
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.Guardian
|
||||
{
|
||||
/// <summary>
|
||||
/// Given to guardians to monitor their link with the host
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
[ComponentProtoName("Guardian")]
|
||||
public class GuardianComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The guardian host entity
|
||||
/// </summary>
|
||||
public EntityUid Host;
|
||||
|
||||
/// <summary>
|
||||
/// Percentage of damage reflected from the guardian to the host
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
[DataField("damageShare")]
|
||||
public float DamageShare { get; set; } = 0.85f;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum distance the guardian can travel before it's forced to recall, use YAML to set
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
[DataField("distanceAllowed")]
|
||||
public float DistanceAllowed { get; set; } = 5f;
|
||||
|
||||
/// <summary>
|
||||
/// If the guardian is currently manifested
|
||||
/// </summary>
|
||||
public bool GuardianLoose = false;
|
||||
|
||||
}
|
||||
}
|
||||
39
Content.Server/Guardian/GuardianCreatorComponent.cs
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.Guardian
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a GuardianComponent attached to the user's GuardianHost.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
[ComponentProtoName("GuardianCreator")]
|
||||
public sealed class GuardianCreatorComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Counts as spent upon exhausting the injection
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// We don't mark as deleted as examine depends on this.
|
||||
/// </remarks>
|
||||
public bool Used = false;
|
||||
|
||||
/// <summary>
|
||||
/// The prototype of the guardian entity which will be created
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
[DataField("guardianProto", customTypeSerializer:typeof(PrototypeIdSerializer<EntityPrototype>), required: true)]
|
||||
public string GuardianProto { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// How long it takes to inject someone.
|
||||
/// </summary>
|
||||
[DataField("delay")]
|
||||
public float InjectionDelay = 5f;
|
||||
|
||||
public bool Injecting = false;
|
||||
}
|
||||
}
|
||||
27
Content.Server/Guardian/GuardianHostComponent.cs
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.Guardian
|
||||
{
|
||||
/// <summary>
|
||||
/// Given to guardian users upon establishing a guardian link with the entity
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
[ComponentProtoName("GuardianHost")]
|
||||
public sealed class GuardianHostComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Guardian hosted within the component
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Can be null if the component is added at any time.
|
||||
/// </remarks>
|
||||
public EntityUid? HostedGuardian;
|
||||
|
||||
/// <summary>
|
||||
/// Container which holds the guardian
|
||||
/// </summary>
|
||||
[ViewVariables] public ContainerSlot GuardianContainer = default!;
|
||||
}
|
||||
}
|
||||
349
Content.Server/Guardian/GuardianSystem.cs
Normal file
|
|
@ -0,0 +1,349 @@
|
|||
using Content.Server.Actions;
|
||||
using Content.Server.DoAfter;
|
||||
using Content.Server.Hands.Components;
|
||||
using Content.Server.Inventory.Components;
|
||||
using Content.Server.Popups;
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Actions.Components;
|
||||
using Content.Shared.Audio;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.MobState;
|
||||
using Content.Shared.MobState.EntitySystems;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.Guardian
|
||||
{
|
||||
/// <summary>
|
||||
/// A guardian has a host it's attached to that it fights for. A fighting spirit.
|
||||
/// </summary>
|
||||
public sealed class GuardianSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!;
|
||||
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
||||
[Dependency] private readonly DamageableSystem _damageSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<GuardianCreatorComponent, UseInHandEvent>(OnCreatorUse);
|
||||
SubscribeLocalEvent<GuardianCreatorComponent, AfterInteractEvent>(OnCreatorInteract);
|
||||
SubscribeLocalEvent<GuardianCreatorComponent, ExaminedEvent>(OnCreatorExamine);
|
||||
SubscribeLocalEvent<GuardianCreatorInjectedEvent>(OnCreatorInject);
|
||||
SubscribeLocalEvent<GuardianCreatorInjectCancelledEvent>(OnCreatorCancelled);
|
||||
|
||||
SubscribeLocalEvent<GuardianComponent, MoveEvent>(OnGuardianMove);
|
||||
SubscribeLocalEvent<GuardianComponent, DamageChangedEvent>(OnGuardianDamaged);
|
||||
SubscribeLocalEvent<GuardianComponent, PlayerAttachedEvent>(OnGuardianPlayer);
|
||||
SubscribeLocalEvent<GuardianComponent, PlayerDetachedEvent>(OnGuardianUnplayer);
|
||||
|
||||
SubscribeLocalEvent<GuardianHostComponent, ComponentInit>(OnHostInit);
|
||||
SubscribeLocalEvent<GuardianHostComponent, MoveEvent>(OnHostMove);
|
||||
SubscribeLocalEvent<GuardianHostComponent, MobStateChangedEvent>(OnHostStateChange);
|
||||
SubscribeLocalEvent<GuardianHostComponent, ComponentShutdown>(OnHostShutdown);
|
||||
}
|
||||
|
||||
private void OnGuardianUnplayer(EntityUid uid, GuardianComponent component, PlayerDetachedEvent args)
|
||||
{
|
||||
var host = component.Host;
|
||||
|
||||
if (!TryComp<GuardianHostComponent>(host, out var hostComponent)) return;
|
||||
|
||||
RetractGuardian(hostComponent, component);
|
||||
}
|
||||
|
||||
private void OnGuardianPlayer(EntityUid uid, GuardianComponent component, PlayerAttachedEvent args)
|
||||
{
|
||||
var host = component.Host;
|
||||
|
||||
if (!HasComp<GuardianHostComponent>(host)) return;
|
||||
|
||||
_popupSystem.PopupEntity(Loc.GetString("guardian-available"), host, Filter.Entities(host));
|
||||
}
|
||||
|
||||
private void OnHostInit(EntityUid uid, GuardianHostComponent component, ComponentInit args)
|
||||
{
|
||||
component.GuardianContainer = uid.EnsureContainer<ContainerSlot>("GuardianContainer");
|
||||
}
|
||||
|
||||
private void OnHostShutdown(EntityUid uid, GuardianHostComponent component, ComponentShutdown args)
|
||||
{
|
||||
if (component.HostedGuardian == null) return;
|
||||
EntityManager.QueueDeleteEntity(component.HostedGuardian.Value);
|
||||
}
|
||||
|
||||
public void ToggleGuardian(GuardianHostComponent hostComponent)
|
||||
{
|
||||
if (hostComponent.HostedGuardian == null ||
|
||||
!TryComp(hostComponent.HostedGuardian, out GuardianComponent? guardianComponent)) return;
|
||||
|
||||
if (guardianComponent.GuardianLoose)
|
||||
{
|
||||
RetractGuardian(hostComponent, guardianComponent);
|
||||
}
|
||||
else
|
||||
{
|
||||
ReleaseGuardian(hostComponent, guardianComponent);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the guardian host component to the user and spawns the guardian inside said component
|
||||
/// </summary>
|
||||
private void OnCreatorUse(EntityUid uid, GuardianCreatorComponent component, UseInHandEvent args)
|
||||
{
|
||||
if (args.Handled) return;
|
||||
args.Handled = true;
|
||||
UseCreator(args.User, args.User, component);
|
||||
}
|
||||
|
||||
private void OnCreatorInteract(EntityUid uid, GuardianCreatorComponent component, AfterInteractEvent args)
|
||||
{
|
||||
if (args.Handled || args.Target == null) return;
|
||||
args.Handled = true;
|
||||
UseCreator(args.User, args.Target.Value, component);
|
||||
}
|
||||
|
||||
private void OnCreatorCancelled(GuardianCreatorInjectCancelledEvent ev)
|
||||
{
|
||||
ev.Component.Injecting = false;
|
||||
}
|
||||
|
||||
private void UseCreator(EntityUid user, EntityUid target, GuardianCreatorComponent component)
|
||||
{
|
||||
if (component.Used)
|
||||
{
|
||||
_popupSystem.PopupEntity(Loc.GetString("guardian-activator-empty-invalid-creation"), user, Filter.Entities(user));
|
||||
return;
|
||||
}
|
||||
|
||||
// If user is already a host don't duplicate.
|
||||
if (HasComp<GuardianHostComponent>(target))
|
||||
{
|
||||
_popupSystem.PopupEntity(Loc.GetString("guardian-already-present-invalid-creation"), user, Filter.Entities(user));
|
||||
return;
|
||||
}
|
||||
|
||||
// Can't work without actions
|
||||
EntityManager.EnsureComponent<ServerActionsComponent>(target);
|
||||
|
||||
if (component.Injecting) return;
|
||||
|
||||
component.Injecting = true;
|
||||
|
||||
_doAfterSystem.DoAfter(new DoAfterEventArgs(user, component.InjectionDelay, target: target)
|
||||
{
|
||||
BroadcastFinishedEvent = new GuardianCreatorInjectedEvent(user, target, component),
|
||||
BroadcastCancelledEvent = new GuardianCreatorInjectCancelledEvent(target, component),
|
||||
BreakOnTargetMove = true,
|
||||
BreakOnUserMove = true,
|
||||
});
|
||||
}
|
||||
|
||||
private void OnCreatorInject(GuardianCreatorInjectedEvent ev)
|
||||
{
|
||||
var comp = ev.Component;
|
||||
|
||||
if (comp.Deleted ||
|
||||
comp.Used ||
|
||||
!TryComp<HandsComponent>(ev.User, out var hands) ||
|
||||
!hands.IsHolding(comp.Owner) ||
|
||||
HasComp<GuardianHostComponent>(ev.Target) ||
|
||||
!TryComp<SharedActionsComponent>(ev.Target, out var actions))
|
||||
{
|
||||
comp.Injecting = false;
|
||||
return;
|
||||
}
|
||||
|
||||
var hostXform = EntityManager.GetComponent<TransformComponent>(ev.Target);
|
||||
var host = EntityManager.EnsureComponent<GuardianHostComponent>(ev.Target);
|
||||
// Use map position so it's not inadvertantly parented to the host + if it's in a container it spawns outside I guess.
|
||||
var guardian = EntityManager.SpawnEntity(comp.GuardianProto, hostXform.MapPosition);
|
||||
|
||||
host.GuardianContainer.Insert(guardian);
|
||||
host.HostedGuardian = guardian;
|
||||
|
||||
if (TryComp(guardian, out GuardianComponent? guardianComponent))
|
||||
{
|
||||
guardianComponent.Host = ev.Target;
|
||||
|
||||
// Grant the user the recall action and notify them
|
||||
actions.Grant(ActionType.ManifestGuardian);
|
||||
SoundSystem.Play(Filter.Entities(ev.Target), "/Audio/Effects/guardian_inject.ogg", ev.Target);
|
||||
|
||||
_popupSystem.PopupEntity(Loc.GetString("guardian-created"), ev.Target, Filter.Entities(ev.Target));
|
||||
// Exhaust the activator
|
||||
comp.Used = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.ErrorS("guardian", $"Tried to spawn a guardian that doesn't have {nameof(GuardianComponent)}");
|
||||
EntityManager.QueueDeleteEntity(guardian);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Triggers when the host receives damage which puts the host in either critical or killed state
|
||||
/// </summary>
|
||||
private void OnHostStateChange(EntityUid uid, GuardianHostComponent component, MobStateChangedEvent args)
|
||||
{
|
||||
if (component.HostedGuardian == null) return;
|
||||
|
||||
if (args.CurrentMobState.IsCritical())
|
||||
{
|
||||
_popupSystem.PopupEntity(Loc.GetString("guardian-critical-warn"), component.HostedGuardian.Value, Filter.Entities(component.HostedGuardian.Value));
|
||||
SoundSystem.Play(Filter.Entities(component.HostedGuardian.Value), "/Audio/Effects/guardian_warn.ogg", component.HostedGuardian.Value);
|
||||
}
|
||||
else if (args.CurrentMobState.IsDead())
|
||||
{
|
||||
SoundSystem.Play(Filter.Pvs(uid), "/Audio/Voice/Human/malescream_guardian.ogg", uid, AudioHelpers.WithVariation(0.20f));
|
||||
EntityManager.RemoveComponent<GuardianHostComponent>(uid);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles guardian receiving damage and splitting it with the host according to his defence percent
|
||||
/// </summary>
|
||||
private void OnGuardianDamaged(EntityUid uid, GuardianComponent component, DamageChangedEvent args)
|
||||
{
|
||||
if (args.DamageDelta == null) return;
|
||||
|
||||
_damageSystem.TryChangeDamage(component.Host, args.DamageDelta * component.DamageShare);
|
||||
_popupSystem.PopupEntity(Loc.GetString("guardian-entity-taking-damage"), component.Host, Filter.Entities(component.Host));
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Triggers while trying to examine an activator to see if it's used
|
||||
/// </summary>
|
||||
private void OnCreatorExamine(EntityUid uid, GuardianCreatorComponent component, ExaminedEvent args)
|
||||
{
|
||||
if (component.Used)
|
||||
{
|
||||
args.PushMarkup(Loc.GetString("guardian-activator-empty-examine"));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called every time the host moves, to make sure the distance between the host and the guardian isn't too far
|
||||
/// </summary>
|
||||
private void OnHostMove(EntityUid uid, GuardianHostComponent component, ref MoveEvent args)
|
||||
{
|
||||
if (component.HostedGuardian == null ||
|
||||
!TryComp(component.HostedGuardian, out GuardianComponent? guardianComponent) ||
|
||||
!guardianComponent.GuardianLoose) return;
|
||||
|
||||
CheckGuardianMove(uid, component.HostedGuardian.Value, component);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called every time the guardian moves: makes sure it's not out of it's allowed distance
|
||||
/// </summary>
|
||||
private void OnGuardianMove(EntityUid uid, GuardianComponent component, ref MoveEvent args)
|
||||
{
|
||||
if (!component.GuardianLoose) return;
|
||||
|
||||
CheckGuardianMove(component.Host, uid, guardianComponent: component);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retract the guardian if either the host or the guardian move away from each other.
|
||||
/// </summary>
|
||||
private void CheckGuardianMove(
|
||||
EntityUid hostUid,
|
||||
EntityUid guardianUid,
|
||||
GuardianHostComponent? hostComponent = null,
|
||||
GuardianComponent? guardianComponent = null,
|
||||
TransformComponent? hostXform = null,
|
||||
TransformComponent? guardianXform = null)
|
||||
{
|
||||
if (!Resolve(hostUid, ref hostComponent, ref hostXform) ||
|
||||
!Resolve(guardianUid, ref guardianComponent, ref guardianXform))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!guardianComponent.GuardianLoose) return;
|
||||
|
||||
if (!guardianXform.Coordinates.InRange(EntityManager, hostXform.Coordinates, guardianComponent.DistanceAllowed))
|
||||
{
|
||||
RetractGuardian(hostComponent, guardianComponent);
|
||||
}
|
||||
}
|
||||
|
||||
private bool CanRelease(GuardianHostComponent host, GuardianComponent guardian)
|
||||
{
|
||||
return HasComp<ActorComponent>(guardian.Owner);
|
||||
}
|
||||
|
||||
private void ReleaseGuardian(GuardianHostComponent hostComponent, GuardianComponent guardianComponent)
|
||||
{
|
||||
if (guardianComponent.GuardianLoose)
|
||||
{
|
||||
DebugTools.Assert(!hostComponent.GuardianContainer.Contains(guardianComponent.Owner));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!CanRelease(hostComponent, guardianComponent))
|
||||
{
|
||||
_popupSystem.PopupEntity(Loc.GetString("guardian-no-soul"), hostComponent.Owner, Filter.Entities(hostComponent.Owner));
|
||||
return;
|
||||
}
|
||||
|
||||
DebugTools.Assert(hostComponent.GuardianContainer.Contains(guardianComponent.Owner));
|
||||
hostComponent.GuardianContainer.Remove(guardianComponent.Owner);
|
||||
DebugTools.Assert(!hostComponent.GuardianContainer.Contains(guardianComponent.Owner));
|
||||
|
||||
guardianComponent.GuardianLoose = true;
|
||||
}
|
||||
|
||||
private void RetractGuardian(GuardianHostComponent hostComponent, GuardianComponent guardianComponent)
|
||||
{
|
||||
if (!guardianComponent.GuardianLoose)
|
||||
{
|
||||
DebugTools.Assert(hostComponent.GuardianContainer.Contains(guardianComponent.Owner));
|
||||
return;
|
||||
}
|
||||
|
||||
hostComponent.GuardianContainer.Insert(guardianComponent.Owner);
|
||||
DebugTools.Assert(hostComponent.GuardianContainer.Contains(guardianComponent.Owner));
|
||||
_popupSystem.PopupEntity(Loc.GetString("guardian-entity-recall"), hostComponent.Owner, Filter.Pvs(hostComponent.Owner));
|
||||
guardianComponent.GuardianLoose = false;
|
||||
}
|
||||
|
||||
private sealed class GuardianCreatorInjectedEvent : EntityEventArgs
|
||||
{
|
||||
public EntityUid User { get; }
|
||||
public EntityUid Target { get; }
|
||||
public GuardianCreatorComponent Component { get; }
|
||||
|
||||
public GuardianCreatorInjectedEvent(EntityUid user, EntityUid target, GuardianCreatorComponent component)
|
||||
{
|
||||
User = user;
|
||||
Target = target;
|
||||
Component = component;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class GuardianCreatorInjectCancelledEvent : EntityEventArgs
|
||||
{
|
||||
public EntityUid Target { get; }
|
||||
public GuardianCreatorComponent Component { get; }
|
||||
|
||||
public GuardianCreatorInjectCancelledEvent(EntityUid target, GuardianCreatorComponent component)
|
||||
{
|
||||
Target = target;
|
||||
Component = component;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
15
Content.Server/Info/RulesManager.cs
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
using Content.Shared.Info;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Network;
|
||||
|
||||
namespace Content.Server.Info;
|
||||
|
||||
public class RulesManager : SharedRulesManager
|
||||
{
|
||||
[Dependency] private readonly INetManager _netManager = default!;
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
_netManager.RegisterNetMessage<ShowRulesPopupMessage>();
|
||||
}
|
||||
}
|
||||
67
Content.Server/Info/ShowRulesCommand.cs
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
using Content.Server.Administration;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Info;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Network;
|
||||
|
||||
namespace Content.Server.Info;
|
||||
|
||||
[AdminCommand(AdminFlags.Admin)]
|
||||
public class ShowRulesCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "showrules";
|
||||
public string Description => "Opens the rules popup for the specified player.";
|
||||
public string Help => "showrules <username> [time]";
|
||||
public async void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
string target;
|
||||
float seconds;
|
||||
|
||||
switch (args.Length)
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
target = args[0];
|
||||
var configurationManager = IoCManager.Resolve<IConfigurationManager>();
|
||||
seconds = configurationManager.GetCVar(CCVars.RulesWaitTime);
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
if (float.TryParse(args[1], out seconds))
|
||||
return;
|
||||
|
||||
target = args[0];
|
||||
shell.WriteLine($"{args[1]} is not a valid amount of minutes.\n{Help}");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
shell.WriteLine(Help);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var locator = IoCManager.Resolve<IPlayerLocator>();
|
||||
var located = await locator.LookupIdByNameOrIdAsync(target);
|
||||
if (located == null)
|
||||
{
|
||||
shell.WriteError("Unable to find a player with that name.");
|
||||
return;
|
||||
}
|
||||
|
||||
var message = new SharedRulesManager.ShowRulesPopupMessage
|
||||
{
|
||||
PopupTime = seconds
|
||||
};
|
||||
|
||||
var player = IoCManager.Resolve<IPlayerManager>()
|
||||
.GetSessionByUserId(located.UserId);
|
||||
IoCManager.Resolve<INetManager>()
|
||||
.ServerSendMessage(message, player.ConnectedClient);
|
||||
}
|
||||
}
|
||||
|
|
@ -11,6 +11,7 @@ using Content.Server.DeviceNetwork;
|
|||
using Content.Server.EUI;
|
||||
using Content.Server.Holiday;
|
||||
using Content.Server.Holiday.Interfaces;
|
||||
using Content.Server.Info;
|
||||
using Content.Server.Maps;
|
||||
using Content.Server.Module;
|
||||
using Content.Server.MoMMI;
|
||||
|
|
@ -57,6 +58,7 @@ namespace Content.Server.IoC
|
|||
IoCManager.Register<IAfkManager, AfkManager>();
|
||||
IoCManager.Register<IGameMapManager, GameMapManager>();
|
||||
IoCManager.Register<IGamePrototypeLoadManager, GamePrototypeLoadManager>();
|
||||
IoCManager.Register<RulesManager, RulesManager>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
using System.Collections.Generic;
|
||||
using Content.Shared.Medical.SuitSensor;
|
||||
using Robust.Shared.Analyzers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
namespace Content.Server.Medical.CrewMonitoring
|
||||
{
|
||||
[RegisterComponent]
|
||||
[Friend(typeof(CrewMonitoringConsoleSystem))]
|
||||
public class CrewMonitoringConsoleComponent : Component
|
||||
{
|
||||
public override string Name => "CrewMonitoringConsole";
|
||||
|
||||
/// <summary>
|
||||
/// List of all currently connected sensors to this console.
|
||||
/// </summary>
|
||||
public Dictionary<string, SuitSensorStatus> ConnectedSensors = new();
|
||||
|
||||
/// <summary>
|
||||
/// After what time sensor consider to be lost.
|
||||
/// </summary>
|
||||
[DataField("sensorTimeout")]
|
||||
public float SensorTimeout = 10f;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
using System.Linq;
|
||||
using Content.Server.DeviceNetwork.Systems;
|
||||
using Content.Server.Medical.SuitSensors;
|
||||
using Content.Server.UserInterface;
|
||||
using Content.Shared.Medical.CrewMonitoring;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Server.Medical.CrewMonitoring
|
||||
{
|
||||
public class CrewMonitoringConsoleSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SuitSensorSystem _sensors = default!;
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
|
||||
private const float UpdateRate = 3f;
|
||||
private float _updateDif;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<CrewMonitoringConsoleComponent, ComponentRemove>(OnRemove);
|
||||
SubscribeLocalEvent<CrewMonitoringConsoleComponent, PacketSentEvent>(OnPacketReceived);
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
// check update rate
|
||||
_updateDif += frameTime;
|
||||
if (_updateDif < UpdateRate)
|
||||
return;
|
||||
_updateDif = 0f;
|
||||
|
||||
var consoles = EntityManager.EntityQuery<CrewMonitoringConsoleComponent>();
|
||||
foreach (var console in consoles)
|
||||
{
|
||||
UpdateTimeouts(console.Owner, console);
|
||||
UpdateUserInterface(console.Owner, console);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnRemove(EntityUid uid, CrewMonitoringConsoleComponent component, ComponentRemove args)
|
||||
{
|
||||
component.ConnectedSensors.Clear();
|
||||
}
|
||||
|
||||
private void OnPacketReceived(EntityUid uid, CrewMonitoringConsoleComponent component, PacketSentEvent args)
|
||||
{
|
||||
var suitSensor = _sensors.PacketToSuitSensor(args.Data);
|
||||
if (suitSensor == null)
|
||||
return;
|
||||
|
||||
suitSensor.Timestamp = _gameTiming.CurTime;
|
||||
component.ConnectedSensors[args.SenderAddress] = suitSensor;
|
||||
}
|
||||
|
||||
private void UpdateUserInterface(EntityUid uid, CrewMonitoringConsoleComponent? component = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component))
|
||||
return;
|
||||
|
||||
var ui = component.Owner.GetUIOrNull(CrewMonitoringUIKey.Key);
|
||||
if (ui == null)
|
||||
return;
|
||||
|
||||
// update all sensors info
|
||||
var allSensors = component.ConnectedSensors.Values.ToList();
|
||||
var uiState = new CrewMonitoringState(allSensors);
|
||||
ui.SetState(uiState);
|
||||
}
|
||||
|
||||
private void UpdateTimeouts(EntityUid uid, CrewMonitoringConsoleComponent? component = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component))
|
||||
return;
|
||||
|
||||
foreach (var (address, sensor) in component.ConnectedSensors)
|
||||
{
|
||||
// if too many time passed - sensor just dropped connection
|
||||
var dif = _gameTiming.CurTime - sensor.Timestamp;
|
||||
if (dif.Seconds > component.SensorTimeout)
|
||||
component.ConnectedSensors.Remove(address);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
59
Content.Server/Medical/SuitSensors/SuitSensorComponent.cs
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
using System;
|
||||
using Content.Shared.Inventory;
|
||||
using Content.Shared.Medical.SuitSensor;
|
||||
using Robust.Shared.Analyzers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
namespace Content.Server.Medical.SuitSensors
|
||||
{
|
||||
/// <summary>
|
||||
/// Tracking device, embedded in almost all uniforms and jumpsuits.
|
||||
/// If enabled, will report to crew monitoring console owners position and status.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
[Friend(typeof(SuitSensorSystem))]
|
||||
[ComponentProtoName("SuitSensor")]
|
||||
public sealed class SuitSensorComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Choose a random sensor mode when item is spawned.
|
||||
/// </summary>
|
||||
[DataField("randomMode")]
|
||||
public bool RandomMode = true;
|
||||
|
||||
/// <summary>
|
||||
/// If true user can't change suit sensor mode
|
||||
/// </summary>
|
||||
[DataField("controlsLocked")]
|
||||
public bool ControlsLocked = false;
|
||||
|
||||
/// <summary>
|
||||
/// Current sensor mode. Can be switched by user verbs.
|
||||
/// </summary>
|
||||
[DataField("mode")]
|
||||
public SuitSensorMode Mode = SuitSensorMode.SensorOff;
|
||||
|
||||
/// <summary>
|
||||
/// Activate sensor if user wear it in this slot.
|
||||
/// </summary>
|
||||
[DataField("activationSlot")]
|
||||
public EquipmentSlotDefines.Slots ActivationSlot = EquipmentSlotDefines.Slots.INNERCLOTHING;
|
||||
|
||||
/// <summary>
|
||||
/// How often does sensor update its owners status (in seconds).
|
||||
/// </summary>
|
||||
[DataField("updateRate")]
|
||||
public float UpdateRate = 2f;
|
||||
|
||||
/// <summary>
|
||||
/// Current user that wears suit sensor. Null if nobody wearing it.
|
||||
/// </summary>
|
||||
public EntityUid? User = null;
|
||||
|
||||
/// <summary>
|
||||
/// Last time when sensor updated owners status
|
||||
/// </summary>
|
||||
public TimeSpan LastUpdate = TimeSpan.Zero;
|
||||
}
|
||||
}
|
||||
312
Content.Server/Medical/SuitSensors/SuitSensorSystem.cs
Normal file
|
|
@ -0,0 +1,312 @@
|
|||
using System;
|
||||
using Content.Server.Access.Systems;
|
||||
using Content.Server.DeviceNetwork;
|
||||
using Content.Server.DeviceNetwork.Components;
|
||||
using Content.Server.DeviceNetwork.Systems;
|
||||
using Content.Server.Popups;
|
||||
using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Inventory;
|
||||
using Content.Shared.Medical.SuitSensor;
|
||||
using Content.Shared.MobState.Components;
|
||||
using Content.Shared.Verbs;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Server.Medical.SuitSensors
|
||||
{
|
||||
public class SuitSensorSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
|
||||
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly IdCardSystem _idCardSystem = default!;
|
||||
[Dependency] private readonly DeviceNetworkSystem _deviceNetworkSystem = default!;
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
|
||||
private const float UpdateRate = 0.5f;
|
||||
private float _updateDif;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<SuitSensorComponent, MapInitEvent>(OnMapInit);
|
||||
SubscribeLocalEvent<SuitSensorComponent, EquippedEvent>(OnEquipped);
|
||||
SubscribeLocalEvent<SuitSensorComponent, UnequippedEvent>(OnUnequipped);
|
||||
SubscribeLocalEvent<SuitSensorComponent, ExaminedEvent>(OnExamine);
|
||||
SubscribeLocalEvent<SuitSensorComponent, GetInteractionVerbsEvent>(OnVerb);
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
// check update rate
|
||||
_updateDif += frameTime;
|
||||
if (_updateDif < UpdateRate)
|
||||
return;
|
||||
|
||||
_updateDif -= UpdateRate;
|
||||
|
||||
var curTime = _gameTiming.CurTime;
|
||||
var sensors = EntityManager.EntityQuery<SuitSensorComponent, DeviceNetworkComponent>();
|
||||
foreach (var (sensor, device) in sensors)
|
||||
{
|
||||
// check if sensor is ready to update
|
||||
if (curTime - sensor.LastUpdate < TimeSpan.FromSeconds(sensor.UpdateRate))
|
||||
continue;
|
||||
sensor.LastUpdate = curTime;
|
||||
|
||||
// get sensor status
|
||||
var status = GetSensorState(sensor.Owner, sensor);
|
||||
if (status == null)
|
||||
continue;
|
||||
|
||||
// broadcast it to device network
|
||||
var payload = SuitSensorToPacket(status);
|
||||
_deviceNetworkSystem.QueuePacket(sensor.Owner, DeviceNetworkConstants.NullAddress, device.Frequency, payload, true);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnMapInit(EntityUid uid, SuitSensorComponent component, MapInitEvent args)
|
||||
{
|
||||
// generate random mode
|
||||
if (component.RandomMode)
|
||||
{
|
||||
//make the sensor mode favor higher levels, except coords.
|
||||
var modesDist = new[]
|
||||
{
|
||||
SuitSensorMode.SensorOff,
|
||||
SuitSensorMode.SensorBinary, SuitSensorMode.SensorBinary,
|
||||
SuitSensorMode.SensorVitals, SuitSensorMode.SensorVitals, SuitSensorMode.SensorVitals,
|
||||
SuitSensorMode.SensorCords, SuitSensorMode.SensorCords
|
||||
};
|
||||
component.Mode = _random.Pick(modesDist);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnEquipped(EntityUid uid, SuitSensorComponent component, EquippedEvent args)
|
||||
{
|
||||
if (args.Slot != component.ActivationSlot)
|
||||
return;
|
||||
|
||||
component.User = args.User;
|
||||
}
|
||||
|
||||
private void OnUnequipped(EntityUid uid, SuitSensorComponent component, UnequippedEvent args)
|
||||
{
|
||||
if (args.Slot != component.ActivationSlot)
|
||||
return;
|
||||
|
||||
component.User = null;
|
||||
}
|
||||
|
||||
private void OnExamine(EntityUid uid, SuitSensorComponent component, ExaminedEvent args)
|
||||
{
|
||||
if (!args.IsInDetailsRange)
|
||||
return;
|
||||
|
||||
string msg;
|
||||
switch (component.Mode)
|
||||
{
|
||||
case SuitSensorMode.SensorOff:
|
||||
msg = "suit-sensor-examine-off";
|
||||
break;
|
||||
case SuitSensorMode.SensorBinary:
|
||||
msg = "suit-sensor-examine-binary";
|
||||
break;
|
||||
case SuitSensorMode.SensorVitals:
|
||||
msg = "suit-sensor-examine-vitals";
|
||||
break;
|
||||
case SuitSensorMode.SensorCords:
|
||||
msg = "suit-sensor-examine-cords";
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
args.PushMarkup(Loc.GetString(msg));
|
||||
}
|
||||
|
||||
private void OnVerb(EntityUid uid, SuitSensorComponent component, GetInteractionVerbsEvent args)
|
||||
{
|
||||
// check if user can change sensor
|
||||
if (component.ControlsLocked)
|
||||
return;
|
||||
|
||||
// standard interaction checks
|
||||
if (!args.CanAccess || !args.CanInteract || !_actionBlockerSystem.CanDrop(args.User))
|
||||
return;
|
||||
|
||||
args.Verbs.UnionWith(new[]
|
||||
{
|
||||
CreateVerb(uid, component, args.User, SuitSensorMode.SensorOff),
|
||||
CreateVerb(uid, component, args.User, SuitSensorMode.SensorBinary),
|
||||
CreateVerb(uid, component, args.User, SuitSensorMode.SensorVitals),
|
||||
CreateVerb(uid, component, args.User, SuitSensorMode.SensorCords)
|
||||
});
|
||||
}
|
||||
|
||||
private Verb CreateVerb(EntityUid uid, SuitSensorComponent component, EntityUid userUid, SuitSensorMode mode)
|
||||
{
|
||||
return new Verb()
|
||||
{
|
||||
Text = GetModeName(mode),
|
||||
Disabled = component.Mode == mode,
|
||||
Priority = -(int) mode, // sort them in descending order
|
||||
Category = VerbCategory.SetSensor,
|
||||
Act = () => SetSensor(uid, mode, userUid, component)
|
||||
};
|
||||
}
|
||||
|
||||
private string GetModeName(SuitSensorMode mode)
|
||||
{
|
||||
string name;
|
||||
switch (mode)
|
||||
{
|
||||
case SuitSensorMode.SensorOff:
|
||||
name = "suit-sensor-mode-off";
|
||||
break;
|
||||
case SuitSensorMode.SensorBinary:
|
||||
name = "suit-sensor-mode-binary";
|
||||
break;
|
||||
case SuitSensorMode.SensorVitals:
|
||||
name = "suit-sensor-mode-vitals";
|
||||
break;
|
||||
case SuitSensorMode.SensorCords:
|
||||
name = "suit-sensor-mode-cords";
|
||||
break;
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
|
||||
return Loc.GetString(name);
|
||||
}
|
||||
|
||||
public void SetSensor(EntityUid uid, SuitSensorMode mode, EntityUid? userUid = null,
|
||||
SuitSensorComponent? component = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component))
|
||||
return;
|
||||
|
||||
component.Mode = mode;
|
||||
|
||||
if (userUid != null)
|
||||
{
|
||||
var msg = Loc.GetString("suit-sensor-mode-state", ("mode", GetModeName(mode)));
|
||||
_popupSystem.PopupEntity(msg, uid, Filter.Entities(userUid.Value));
|
||||
}
|
||||
}
|
||||
|
||||
public SuitSensorStatus? GetSensorState(EntityUid uid, SuitSensorComponent? sensor = null, TransformComponent? transform = null)
|
||||
{
|
||||
if (!Resolve(uid, ref sensor, ref transform))
|
||||
return null;
|
||||
|
||||
// check if sensor is enabled and worn by user
|
||||
if (sensor.Mode == SuitSensorMode.SensorOff || sensor.User == null)
|
||||
return null;
|
||||
|
||||
// try to get mobs id from ID slot
|
||||
var userName = Loc.GetString("suit-sensor-component-unknown-name");
|
||||
var userJob = Loc.GetString("suit-sensor-component-unknown-job");
|
||||
if (_idCardSystem.TryGetIdCardSlot(sensor.User.Value, out var card))
|
||||
{
|
||||
if (card.FullName != null)
|
||||
userName = card.FullName;
|
||||
if (card.JobTitle != null)
|
||||
userJob = card.JobTitle;
|
||||
}
|
||||
|
||||
// get health mob state
|
||||
var isAlive = false;
|
||||
if (EntityManager.TryGetComponent(sensor.User.Value, out MobStateComponent? mobState))
|
||||
{
|
||||
isAlive = mobState.IsAlive();
|
||||
}
|
||||
|
||||
// get mob total damage
|
||||
var totalDamage = 0;
|
||||
if (EntityManager.TryGetComponent(sensor.User.Value, out DamageableComponent? damageable))
|
||||
{
|
||||
totalDamage = damageable.TotalDamage.Int();
|
||||
}
|
||||
|
||||
// finally, form suit sensor status
|
||||
var status = new SuitSensorStatus(userName, userJob);
|
||||
switch (sensor.Mode)
|
||||
{
|
||||
case SuitSensorMode.SensorBinary:
|
||||
status.IsAlive = isAlive;
|
||||
break;
|
||||
case SuitSensorMode.SensorVitals:
|
||||
status.IsAlive = isAlive;
|
||||
status.TotalDamage = totalDamage;
|
||||
break;
|
||||
case SuitSensorMode.SensorCords:
|
||||
status.IsAlive = isAlive;
|
||||
status.TotalDamage = totalDamage;
|
||||
status.Coordinates = transform.MapPosition;
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serialize suit sensor status into device network package.
|
||||
/// </summary>
|
||||
public NetworkPayload SuitSensorToPacket(SuitSensorStatus status)
|
||||
{
|
||||
var payload = new NetworkPayload()
|
||||
{
|
||||
[DeviceNetworkConstants.Command] = DeviceNetworkConstants.CmdUpdatedState,
|
||||
[SuitSensorConstants.NET_NAME] = status.Name,
|
||||
[SuitSensorConstants.NET_JOB] = status.Job,
|
||||
[SuitSensorConstants.NET_IS_ALIVE] = status.IsAlive,
|
||||
};
|
||||
|
||||
if (status.TotalDamage != null)
|
||||
payload.Add(SuitSensorConstants.NET_TOTAL_DAMAGE, status.TotalDamage);
|
||||
if (status.Coordinates != null)
|
||||
payload.Add(SuitSensorConstants.NET_CORDINATES, status.Coordinates);
|
||||
|
||||
return payload;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try to deserialize device network message into suit sensor status
|
||||
/// </summary>
|
||||
public SuitSensorStatus? PacketToSuitSensor(NetworkPayload payload)
|
||||
{
|
||||
// check command
|
||||
if (!payload.TryGetValue(DeviceNetworkConstants.Command, out string? command))
|
||||
return null;
|
||||
if (command != DeviceNetworkConstants.CmdUpdatedState)
|
||||
return null;
|
||||
|
||||
// check name, job and alive
|
||||
if (!payload.TryGetValue(SuitSensorConstants.NET_NAME, out string? name)) return null;
|
||||
if (!payload.TryGetValue(SuitSensorConstants.NET_JOB, out string? job)) return null;
|
||||
if (!payload.TryGetValue(SuitSensorConstants.NET_IS_ALIVE, out bool? isAlive)) return null;
|
||||
|
||||
// try get total damage and cords (optionals)
|
||||
payload.TryGetValue(SuitSensorConstants.NET_TOTAL_DAMAGE, out int? totalDamage);
|
||||
payload.TryGetValue(SuitSensorConstants.NET_CORDINATES, out MapCoordinates? cords);
|
||||
|
||||
var status = new SuitSensorStatus(name, job)
|
||||
{
|
||||
IsAlive = isAlive.Value,
|
||||
TotalDamage = totalDamage,
|
||||
Coordinates = cords
|
||||
};
|
||||
return status;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -64,7 +64,7 @@ namespace Content.Server.Spawners.Components
|
|||
public override void MapInit()
|
||||
{
|
||||
Spawn();
|
||||
_entMan.DeleteEntity(Owner);
|
||||
_entMan.QueueDeleteEntity(Owner);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -193,12 +193,18 @@ namespace Content.Server.Storage.Components
|
|||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
var @event = new StorageOpenAttemptEvent();
|
||||
IoCManager.Resolve<IEntityManager>().EventBus.RaiseLocalEvent(Owner, @event);
|
||||
|
||||
return !@event.Cancelled;
|
||||
}
|
||||
|
||||
public virtual bool CanClose(EntityUid user, bool silent = false)
|
||||
{
|
||||
return true;
|
||||
var @event = new StorageCloseAttemptEvent();
|
||||
IoCManager.Resolve<IEntityManager>().EventBus.RaiseLocalEvent(Owner, @event);
|
||||
|
||||
return !@event.Cancelled;
|
||||
}
|
||||
|
||||
public void ToggleOpen(EntityUid user)
|
||||
|
|
@ -479,4 +485,14 @@ namespace Content.Server.Storage.Components
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class StorageOpenAttemptEvent : CancellableEntityEventArgs
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public sealed class StorageCloseAttemptEvent : CancellableEntityEventArgs
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ namespace Content.Shared.Actions
|
|||
DebugTargetEntity,
|
||||
DebugTargetEntityRepeat,
|
||||
SpellPie,
|
||||
ManifestGuardian,
|
||||
PAIMidi
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,8 +29,6 @@ namespace Content.Shared.Buckle.Components
|
|||
|
||||
public EntityUid? LastEntityBuckledTo { get; set; }
|
||||
|
||||
public bool IsOnStrapEntityThisFrame { get; set; }
|
||||
|
||||
public bool DontCollide { get; set; }
|
||||
|
||||
public abstract bool TryBuckle(EntityUid user, EntityUid to);
|
||||
|
|
|
|||
|
|
@ -57,11 +57,13 @@ namespace Content.Shared.Buckle.Components
|
|||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum StrapVisuals
|
||||
public enum StrapVisuals : byte
|
||||
{
|
||||
RotationAngle
|
||||
RotationAngle,
|
||||
BuckledState
|
||||
}
|
||||
|
||||
// TODO : Convert this to an Entity Message. Careful, it will Break ShuttleControllerComponent (only place where it's used)
|
||||
[Serializable, NetSerializable]
|
||||
#pragma warning disable 618
|
||||
public abstract class StrapChangeMessage : ComponentMessage
|
||||
|
|
|
|||
|
|
@ -59,7 +59,6 @@ namespace Content.Shared.Buckle
|
|||
{
|
||||
if (args.BodyB.Owner != component.LastEntityBuckledTo) return;
|
||||
|
||||
component.IsOnStrapEntityThisFrame = true;
|
||||
if (component.Buckled || component.DontCollide)
|
||||
{
|
||||
args.Cancel();
|
||||
|
|
|
|||
|
|
@ -615,11 +615,14 @@ namespace Content.Shared.Hands.Components
|
|||
protected bool CanInsertEntityIntoHand(Hand hand, EntityUid entity)
|
||||
{
|
||||
var handContainer = hand.Container;
|
||||
if (handContainer == null)
|
||||
return false;
|
||||
if (handContainer == null) return false;
|
||||
|
||||
if (!handContainer.CanInsert(entity))
|
||||
return false;
|
||||
if (!handContainer.CanInsert(entity)) return false;
|
||||
|
||||
var @event = new AttemptItemPickupEvent();
|
||||
_entMan.EventBus.RaiseLocalEvent(entity, @event);
|
||||
|
||||
if (@event.Cancelled) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
26
Content.Shared/Info/SharedRulesManager.cs
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
using JetBrains.Annotations;
|
||||
using Lidgren.Network;
|
||||
using Robust.Shared.Network;
|
||||
|
||||
namespace Content.Shared.Info;
|
||||
|
||||
public abstract class SharedRulesManager
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public sealed class ShowRulesPopupMessage : NetMessage
|
||||
{
|
||||
public override MsgGroups MsgGroup => MsgGroups.Command;
|
||||
|
||||
public float PopupTime { get; set; }
|
||||
|
||||
public override void ReadFromBuffer(NetIncomingMessage buffer)
|
||||
{
|
||||
PopupTime = buffer.ReadFloat();
|
||||
}
|
||||
|
||||
public override void WriteToBuffer(NetOutgoingMessage buffer)
|
||||
{
|
||||
buffer.Write(PopupTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,9 @@
|
|||
|
||||
namespace Content.Shared.Item
|
||||
{
|
||||
/// <summary>
|
||||
/// Raised on a *mob* when it tries to pickup something
|
||||
/// </summary>
|
||||
public class PickupAttemptEvent : CancellableEntityEventArgs
|
||||
{
|
||||
public PickupAttemptEvent(EntityUid uid)
|
||||
|
|
@ -11,4 +14,14 @@ namespace Content.Shared.Item
|
|||
|
||||
public EntityUid Uid { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raised on the *item* when tried to be picked up
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Doesn't just handle "items" but calling it "PickedUpAttempt" is too close to "Pickup" for the sleep deprived brain.
|
||||
/// </remarks>
|
||||
public sealed class AttemptItemPickupEvent : CancellableEntityEventArgs
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ using Robust.Shared.GameStates;
|
|||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Content.Shared.Medical.SuitSensor;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.Medical.CrewMonitoring
|
||||
{
|
||||
[Serializable, NetSerializable]
|
||||
public enum CrewMonitoringUIKey
|
||||
{
|
||||
Key
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public class CrewMonitoringState : BoundUserInterfaceState
|
||||
{
|
||||
public List<SuitSensorStatus> Sensors;
|
||||
|
||||
public CrewMonitoringState(List<SuitSensorStatus> sensors)
|
||||
{
|
||||
Sensors = sensors;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
58
Content.Shared/Medical/SuitSensor/SharedSuitSensor.cs
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
using System;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.Medical.SuitSensor
|
||||
{
|
||||
[Serializable, NetSerializable]
|
||||
public class SuitSensorStatus
|
||||
{
|
||||
public SuitSensorStatus(string name, string job)
|
||||
{
|
||||
Name = name;
|
||||
Job = job;
|
||||
}
|
||||
|
||||
public TimeSpan Timestamp;
|
||||
public string Name;
|
||||
public string Job;
|
||||
public bool IsAlive;
|
||||
public int? TotalDamage;
|
||||
public MapCoordinates? Coordinates;
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum SuitSensorMode : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// Sensor doesn't send any information about owner
|
||||
/// </summary>
|
||||
SensorOff = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Sensor sends only binary status (alive/dead)
|
||||
/// </summary>
|
||||
SensorBinary = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Sensor sends health vitals status
|
||||
/// </summary>
|
||||
SensorVitals = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Sensor sends vitals status and GPS position
|
||||
/// </summary>
|
||||
SensorCords = 3
|
||||
}
|
||||
|
||||
public static class SuitSensorConstants
|
||||
{
|
||||
public const string NET_NAME = "name";
|
||||
public const string NET_JOB = "job";
|
||||
public const string NET_IS_ALIVE = "alive";
|
||||
public const string NET_TOTAL_DAMAGE = "vitals";
|
||||
public const string NET_CORDINATES = "cords";
|
||||
}
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ using System.Linq;
|
|||
using Content.Shared.Alert;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Content.Shared.MobState.EntitySystems;
|
||||
using Content.Shared.MobState.State;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameStates;
|
||||
|
|
@ -318,7 +319,6 @@ namespace Content.Shared.MobState.Components
|
|||
|
||||
var message = new MobStateChangedEvent(this, old, state);
|
||||
entMan.EventBus.RaiseLocalEvent(Owner, message);
|
||||
|
||||
Dirty();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@ namespace Content.Shared.MobState.EntitySystems
|
|||
|
||||
private void OnStartPullAttempt(EntityUid uid, MobStateComponent component, StartPullAttemptEvent args)
|
||||
{
|
||||
if(component.IsIncapacitated())
|
||||
if (component.IsIncapacitated())
|
||||
args.Cancel();
|
||||
}
|
||||
|
||||
|
|
@ -135,7 +135,7 @@ namespace Content.Shared.MobState.EntitySystems
|
|||
|
||||
private void OnStandAttempt(EntityUid uid, MobStateComponent component, StandAttemptEvent args)
|
||||
{
|
||||
if(component.IsIncapacitated())
|
||||
if (component.IsIncapacitated())
|
||||
args.Cancel();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,5 +57,7 @@ namespace Content.Shared.Verbs
|
|||
|
||||
public static readonly VerbCategory Split =
|
||||
new("verb-categories-split", null);
|
||||
|
||||
public static readonly VerbCategory SetSensor = new("verb-categories-set-sensor", null);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
BIN
Resources/Audio/Effects/guardian_inject.ogg
Normal file
BIN
Resources/Audio/Effects/guardian_warn.ogg
Normal file
|
|
@ -10,3 +10,7 @@ adminhelp.ogg taken from https://github.com/tgstation/tgstation/blob/d775e1ac804
|
|||
The sound had been reversed and the volume altered.
|
||||
|
||||
voteding.ogg taken from "Bike, Bell Ding, Single, 01-01.wav" by InspectorJ (www.jshaw.co.uk) of Freesound.org at https://freesound.org/people/InspectorJ/sounds/484344/ under CC BY 3.0. The volume has been reduced.
|
||||
|
||||
poster_broken.ogg taken from https://github.com/tgstation/tgstation/blob/2834383245d2129a106acef3afd17b81e1e64777/sound/items/poster_ripped.ogg
|
||||
|
||||
poster_being_set.ogg taken from https://github.com/tgstation/tgstation/blob/2834383245d2129a106acef3afd17b81e1e64777/sound/items/poster_ripped.ogg
|
||||
|
|
|
|||
BIN
Resources/Audio/Effects/poster_being_set.ogg
Normal file
BIN
Resources/Audio/Effects/poster_broken.ogg
Normal file
BIN
Resources/Audio/Voice/Human/malescream_guardian.ogg
Normal file
|
|
@ -1,24 +1,4 @@
|
|||
Entries:
|
||||
- author: Seth
|
||||
changes:
|
||||
- {message: Added Makeshift Handcuffs into the construction list, type: Add}
|
||||
id: 283
|
||||
time: '2021-07-25T15:30:42.0000000+00:00'
|
||||
- author: metalgearsloth
|
||||
changes:
|
||||
- {message: Shuttle-relative movement for when we get shuttle rotation., type: Add}
|
||||
id: 284
|
||||
time: '2021-07-25T15:48:22.0000000+00:00'
|
||||
- author: Seth
|
||||
changes:
|
||||
- {message: Added Storage Room to Coats, type: Add}
|
||||
id: 285
|
||||
time: '2021-07-26T05:36:27.0000000+00:00'
|
||||
- author: Seth
|
||||
changes:
|
||||
- {message: Added a hardsuit into research directors locker, type: Add}
|
||||
id: 286
|
||||
time: '2021-07-26T22:14:10.0000000+00:00'
|
||||
- author: Seth
|
||||
changes:
|
||||
- {message: Added bio suits into bio lockers, type: Add}
|
||||
|
|
@ -2872,3 +2852,30 @@ Entries:
|
|||
- {message: Chaplain no longer spawns with an empty backpack., type: Fix}
|
||||
id: 784
|
||||
time: '2021-12-29T02:48:58.0000000+00:00'
|
||||
- author: CrudeWax
|
||||
changes:
|
||||
- {message: Syndiecorp Cybersun invites you to try out their magnum opus- holoparasites!
|
||||
Look for the "Utility" section in your uplink. And ghosts - be on the lookout
|
||||
for a role prompt!, type: Add}
|
||||
id: 785
|
||||
time: '2021-12-29T03:58:49.0000000+00:00'
|
||||
- author: Topy
|
||||
changes:
|
||||
- {message: Add rollerbeds to roll people around!, type: Add}
|
||||
id: 786
|
||||
time: '2021-12-29T04:57:21.0000000+00:00'
|
||||
- author: Macoron
|
||||
changes:
|
||||
- {message: 'Added suit sensors and crew monitoring console in code. They are not
|
||||
mapped, but you can print circuit board.', type: Add}
|
||||
- {message: Prisoners suit has locked suit sensor that always broadcasting their
|
||||
position., type: Add}
|
||||
id: 787
|
||||
time: '2021-12-29T05:19:01.0000000+00:00'
|
||||
- author: Rane
|
||||
changes:
|
||||
- {message: Pill canisters are now available in the protolathe after researching
|
||||
chemistry technology., type: Add}
|
||||
- {message: Pill canisters now fit inside medical belts., type: Tweak}
|
||||
id: 788
|
||||
time: '2021-12-29T06:21:17.0000000+00:00'
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
# Foldable
|
||||
|
||||
foldable-deploy-fail = You can't deploy the {$object} here.
|
||||
fold-verb = Fold
|
||||
unfold-verb = Unfold
|
||||
22
Resources/Locale/en-US/guardian/guardian.ftl
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
|
||||
## Guardian host specific
|
||||
|
||||
guardian-created = You feel... Haunted.
|
||||
guardian-missing-invalid-action = There is no guardian under your control!
|
||||
guardian-already-present-invalid-creation = You are NOT re-living that haunting experience!
|
||||
guardian-no-actions-invalid-creation = You don't have the ability to host a guardian!
|
||||
guardian-activator-empty-invalid-creation = The injector is spent.
|
||||
guardian-activator-empty-examine = [color=#ba1919]The injector is spent.[/color]
|
||||
guardian-no-soul = Your guardian has no soul.
|
||||
guardian-available = Your guardian now has a soul.
|
||||
|
||||
## Guardian entity specific
|
||||
|
||||
guardian-entity-recall = The guardian vanishes into thin air!
|
||||
guardian-entity-taking-damage = Your guardian is taking damage!
|
||||
|
||||
## Health warnings
|
||||
guardian-host-critical-warn = YOUR HOST IS WOUNDED!
|
||||
guardian-host-death-warn = YOUR FORM SUCCUMBS TO NONEXISTENCE!
|
||||
guardian-death-warn = YOUR BODY IS PIERCED BY SUBATOMIC PAIN AS IT DISINTEGRATES!
|
||||
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
## UI
|
||||
|
||||
crew-monitoring-user-interface-title = Crew Monitoring
|
||||
|
||||
crew-monitoring-user-interface-name = Name
|
||||
crew-monitoring-user-interface-status = Status
|
||||
crew-monitoring-user-interface-location = Location
|
||||
|
||||
crew-monitoring-user-interface-alive = Alive
|
||||
crew-monitoring-user-interface-dead = Dead
|
||||
crew-monitoring-user-interface-no-info = N/A
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
## Modes
|
||||
|
||||
suit-sensor-mode-off = Off
|
||||
suit-sensor-mode-binary = Binary
|
||||
suit-sensor-mode-vitals = Vitals
|
||||
suit-sensor-mode-cords = Coordinates
|
||||
|
||||
## Popups
|
||||
suit-sensor-mode-state = Suit sensors: {$mode}
|
||||
|
||||
## Components
|
||||
|
||||
suit-sensor-component-unknown-name = Unknown
|
||||
suit-sensor-component-unknown-job = No job
|
||||
|
||||
## Examine
|
||||
|
||||
suit-sensor-examine-off = Its sensors appear to be [color=darkred]disabled[/color].
|
||||
suit-sensor-examine-binary = Its binary life sensors appear to be enabled.
|
||||
suit-sensor-examine-vitals = Its vital tracker appears to be enabled.
|
||||
suit-sensor-examine-cords = Its vital tracker and tracking beacon appear to be enabled.
|
||||
|
|
@ -18,6 +18,7 @@ verb-categories-unbuckle = Unbuckle
|
|||
verb-categories-rotate = Rotate
|
||||
verb-categories-transfer = Set Transfer Amount
|
||||
verb-categories-split = Split
|
||||
verb-categories-set-sensor = Sensor
|
||||
|
||||
verb-common-toggle-light = Toggle light
|
||||
verb-common-close = Close
|
||||
|
|
|
|||
12
Resources/Prototypes/Actions/guardian_actions.yml
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
#This is the action the host gets to control the guardian
|
||||
- type: action
|
||||
actionType: ManifestGuardian
|
||||
icon: Interface/Actions/manifest.png
|
||||
name: "Toggle guardian manifestation"
|
||||
description: "Either manifests the guardian or recalls it back into your body"
|
||||
filters:
|
||||
- guardian
|
||||
behaviorType: Instant
|
||||
behavior: !type:ToggleGuardianAction
|
||||
cooldown: 2
|
||||
|
||||
|
|
@ -16,3 +16,32 @@
|
|||
Higher will burn the engine out and eventually make it explode. Don't.
|
||||
Don't forget to refuel it, it tends to stop at the worst possible time.
|
||||
|
||||
- type: entity
|
||||
parent: BaseItem
|
||||
id: HoloparasiteInfo
|
||||
name: "Holoparasite terms and conditions"
|
||||
description: A tiny volumetric display for documents, makes one wonder if Cybersun's legal budget is way too high.
|
||||
components:
|
||||
- type: Sprite
|
||||
netsync: false
|
||||
sprite: Objects/Misc/guardian_info.rsi
|
||||
state: guardian_info
|
||||
- type: UserInterface
|
||||
interfaces:
|
||||
- key: enum.PaperUiKey.Key
|
||||
type: PaperBoundUserInterface
|
||||
- type: Paper
|
||||
content: |
|
||||
Thanks for choosing our holoparasite package!
|
||||
At cybersun, we pride ourselves on cutting-edge military and industrial technology, and greatly appreciate your contribution to our establishment!
|
||||
Guardians are helpful and intelligent beings which nest within your body, completely immune to common hazards such as pressure, temperature and even bullets!
|
||||
You have purchased the holoparasite package, which contains a holoparasite activator, an instruction booklet, and our softcap merchandise.
|
||||
Instructions for use:
|
||||
1. Activate the holoparasite injector (preferably in a secluded area).
|
||||
2. Wait for the tingling and/or painful metaphysical sensation.
|
||||
3. Check your holoparasite for the ability to communicate and cooperate, and capacity to understand your orders.
|
||||
4. Use your recall-manifest ability to summon or recall the holoparasite back into your body.
|
||||
5. Keep the holoparasite within a short distance from yourself, otherwise it will be recalled by force!
|
||||
WARNING: Guardians are metaphysical beings, but draw from your HEALTH in order to exist. Direct damage done to guardians will be partially transferred to you!
|
||||
Cybersun inc. is not responsible for complete annihilation following the misuse of Holoparasite technology.
|
||||
|
||||
|
|
|
|||
|
|
@ -96,6 +96,7 @@
|
|||
- CryostasisBeaker
|
||||
- Dropper
|
||||
- Syringe
|
||||
- PillCanister
|
||||
|
||||
- type: technology
|
||||
name: "medical machinery"
|
||||
|
|
@ -113,6 +114,7 @@
|
|||
- MedicalScannerMachineCircuitboard
|
||||
- ChemMasterMachineCircuitboard
|
||||
- ChemDispenserMachineCircuitboard
|
||||
- CrewMonitoringComputerCircuitboard
|
||||
|
||||
# Security Technology Tree
|
||||
|
||||
|
|
|
|||
|
|
@ -107,6 +107,18 @@
|
|||
price: 2
|
||||
icon: /Textures/Objects/Weapons/Guns/Ammunition/SpeedLoaders/Magnum/magnum_sl.rsi/icon.png
|
||||
|
||||
#Utility
|
||||
|
||||
- type: uplinkListing
|
||||
id: UplinkHoloparaKit
|
||||
category: Utility
|
||||
itemId: BoxHoloparasite
|
||||
listingName: Holoparasite Kit
|
||||
description: The pride and joy of Cybersun. Contains an injector that hosts a sentient metaphysical guardian made of hard light which resides in the user's body when not active. The guardian can punch rapidly and is immune to hazardous environments and bullets, but shares any damage it takes with the user.
|
||||
icon: /Textures/Objects/Misc/guardian_info.rsi/icon.png
|
||||
price: 16
|
||||
|
||||
|
||||
# Bundles
|
||||
|
||||
- type: uplinkListing
|
||||
|
|
@ -209,6 +221,8 @@
|
|||
id: UplinkDecoyDisk
|
||||
category: Misc
|
||||
itemId: NukeDiskFake
|
||||
listingName: Decoy nuclear disk
|
||||
description: A piece of plastic with a lenticular printing, made to look like a nuclear auth disk.
|
||||
price: 1
|
||||
|
||||
- type: uplinkListing
|
||||
|
|
@ -235,16 +249,14 @@
|
|||
itemId: lanternextrabright
|
||||
price: 2
|
||||
|
||||
# Pointless
|
||||
|
||||
- type: uplinkListing
|
||||
id: UplinkCostumeCentcom
|
||||
category: Pointless
|
||||
category: Misc
|
||||
itemId: ClothingBackpackDuffelSyndicateCostumeCentcom
|
||||
price: 4
|
||||
|
||||
- type: uplinkListing
|
||||
id: UplinkCostumeClown
|
||||
category: Pointless
|
||||
category: Misc
|
||||
itemId: ClothingBackpackDuffelSyndicateCostumeClown
|
||||
price: 4
|
||||
|
|
|
|||
|
|
@ -237,6 +237,7 @@
|
|||
- Ointment
|
||||
- CigPack
|
||||
- Pill
|
||||
- PillCanister
|
||||
components:
|
||||
- Hypospray
|
||||
- SurgeryTool
|
||||
|
|
@ -255,6 +256,7 @@
|
|||
whitelist:
|
||||
tags:
|
||||
- Pill
|
||||
- PillCanister
|
||||
bottle_spray:
|
||||
whitelist:
|
||||
tags:
|
||||
|
|
|
|||
|
|
@ -239,3 +239,25 @@
|
|||
sprite: Clothing/Head/Soft/yellowsoft_flipped.rsi
|
||||
- type: Clothing
|
||||
sprite: Clothing/Head/Soft/yellowsoft_flipped.rsi
|
||||
|
||||
- type: entity
|
||||
parent: ClothingHeadBase
|
||||
id: ClothingHeadHatBizarreSoft
|
||||
name: troublemaker's soft
|
||||
description: A truly.. bizarre accessory.
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: Clothing/Head/Soft/bizarresoft.rsi
|
||||
- type: Clothing
|
||||
sprite: Clothing/Head/Soft/bizarresoft.rsi
|
||||
|
||||
- type: entity
|
||||
parent: ClothingHeadBase
|
||||
id: ClothingHeadHatBizarreSoftFlipped
|
||||
name: troublemaker's soft flipped
|
||||
description: A truly.. bizarre accessory, flipped.
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: Clothing/Head/Soft/bizarresoft_flipped.rsi
|
||||
- type: Clothing
|
||||
sprite: Clothing/Head/Soft/bizarresoft_flipped.rsi
|
||||
|
|
|
|||
|
|
@ -1,6 +1,17 @@
|
|||
- type: entity
|
||||
abstract: true
|
||||
parent: Clothing
|
||||
id: ClothingWithSuitSensor
|
||||
components:
|
||||
- type: SuitSensor
|
||||
- type: DeviceNetworkComponent
|
||||
deviceNetId: Wireless
|
||||
- type: WirelessNetworkConnection
|
||||
range: 500
|
||||
|
||||
- type: entity
|
||||
abstract: true
|
||||
parent: ClothingWithSuitSensor
|
||||
id: ClothingUniformBase
|
||||
components:
|
||||
- type: Sprite
|
||||
|
|
@ -17,7 +28,7 @@
|
|||
|
||||
- type: entity
|
||||
abstract: true
|
||||
parent: Clothing
|
||||
parent: ClothingWithSuitSensor
|
||||
id: ClothingUniformSkirtBase
|
||||
components:
|
||||
- type: Sprite
|
||||
|
|
|
|||
|
|
@ -232,6 +232,10 @@
|
|||
sprite: Clothing/Uniforms/Jumpskirt/prisoner.rsi
|
||||
- type: Clothing
|
||||
sprite: Clothing/Uniforms/Jumpskirt/prisoner.rsi
|
||||
- type: SuitSensor
|
||||
controlsLocked: true
|
||||
randomMode: false
|
||||
mode: SensorCords
|
||||
|
||||
- type: entity
|
||||
parent: ClothingUniformSkirtBase
|
||||
|
|
|
|||
|
|
@ -310,6 +310,10 @@
|
|||
sprite: Clothing/Uniforms/Jumpsuit/prisoner.rsi
|
||||
- type: Clothing
|
||||
sprite: Clothing/Uniforms/Jumpsuit/prisoner.rsi
|
||||
- type: SuitSensor
|
||||
controlsLocked: true
|
||||
randomMode: false
|
||||
mode: SensorCords
|
||||
|
||||
- type: entity
|
||||
parent: ClothingUniformBase
|
||||
|
|
|
|||
|
|
@ -0,0 +1,131 @@
|
|||
- type: entity
|
||||
parent: MarkerBase
|
||||
id: RandomPosterAny
|
||||
name: random poster spawner
|
||||
components:
|
||||
- type: Sprite
|
||||
layers:
|
||||
- state: red
|
||||
- texture: Structures/Wallmounts/posters.rsi/random_anything.png
|
||||
- type: RandomSpawner
|
||||
offset: 0
|
||||
prototypes:
|
||||
- RandomPosterContraband
|
||||
- RandomPosterLegit
|
||||
chance: 0.95
|
||||
rarePrototypes:
|
||||
- PosterBroken
|
||||
rareChance: 0.05
|
||||
|
||||
- type: entity
|
||||
parent: MarkerBase
|
||||
id: RandomPosterContraband
|
||||
name: random contraband poster spawner
|
||||
components:
|
||||
- type: Sprite
|
||||
layers:
|
||||
- state: red
|
||||
- texture: Structures/Wallmounts/posters.rsi/random_contraband.png
|
||||
- type: RandomSpawner
|
||||
offset: 0
|
||||
prototypes:
|
||||
- PosterContrabandFreeTonto
|
||||
- PosterContrabandAtmosiaDeclarationIndependence
|
||||
- PosterContrabandFunPolice
|
||||
- PosterContrabandLustyXenomorph
|
||||
- PosterContrabandSyndicateRecruitment
|
||||
- PosterContrabandClown
|
||||
- PosterContrabandSmoke
|
||||
- PosterContrabandGreyTide
|
||||
- PosterContrabandMissingGloves
|
||||
- PosterContrabandHackingGuide
|
||||
- PosterContrabandRIPBadger
|
||||
- PosterContrabandAmbrosiaVulgaris
|
||||
- PosterContrabandDonutCorp.
|
||||
- PosterContrabandEAT.
|
||||
- PosterContrabandTools
|
||||
- PosterContrabandPower
|
||||
- PosterContrabandSpaceCube
|
||||
- PosterContrabandCommunistState
|
||||
- PosterContrabandLamarr
|
||||
- PosterContrabandBorgFancy
|
||||
- PosterContrabandBorgFancyv2
|
||||
- PosterContrabandKosmicheskayaStantsiya
|
||||
- PosterContrabandRebelsUnite
|
||||
- PosterContrabandC20r
|
||||
- PosterContrabandHaveaPuff
|
||||
- PosterContrabandRevolver
|
||||
- PosterContrabandDDayPromo
|
||||
- PosterContrabandSyndicatePistol
|
||||
- PosterContrabandEnergySwords
|
||||
- PosterContrabandRedRum
|
||||
- PosterContrabandCC64KAd
|
||||
- PosterContrabandPunchShit
|
||||
- PosterContrabandTheGriffin
|
||||
- PosterContrabandFreeDrone
|
||||
- PosterContrabandBustyBackdoorXenoBabes6
|
||||
- PosterContrabandRobustSoftdrinks
|
||||
- PosterContrabandShamblersJuice
|
||||
- PosterContrabandPwrGame
|
||||
- PosterContrabandSunkist
|
||||
- PosterContrabandSpaceCola
|
||||
- PosterContrabandSpaceUp
|
||||
- PosterContrabandKudzu
|
||||
- PosterContrabandMaskedMen
|
||||
- PosterContrabandUnreadableAnnouncement
|
||||
- PosterContrabandFreeSyndicateEncryptionKey
|
||||
- PosterContrabandBountyHunters
|
||||
- PosterContrabandTheBigGasTruth
|
||||
- PosterContrabandGotWood
|
||||
chance: 1
|
||||
|
||||
- type: entity
|
||||
parent: MarkerBase
|
||||
id: RandomPosterLegit
|
||||
name: random legit poster spawner
|
||||
components:
|
||||
- type: Sprite
|
||||
layers:
|
||||
- state: red
|
||||
- texture: Structures/Wallmounts/posters.rsi/random_legit.png
|
||||
- type: RandomSpawner
|
||||
offset: 0
|
||||
prototypes:
|
||||
- PosterLegitHereForYourSafety
|
||||
- PosterLegitNanotrasenLogo
|
||||
- PosterLegitCleanliness
|
||||
- PosterLegitHelpOthers
|
||||
- PosterLegitBuild
|
||||
- PosterLegitBlessThisSpess
|
||||
- PosterLegitScience
|
||||
- PosterLegitIan
|
||||
- PosterLegitObey
|
||||
- PosterLegitWalk
|
||||
- PosterLegitStateLaws
|
||||
- PosterLegitLoveIan
|
||||
- PosterLegitSpaceCops
|
||||
- PosterLegitUeNo
|
||||
- PosterLegitGetYourLEGS
|
||||
- PosterLegitDoNotQuestion
|
||||
- PosterLegitWorkForAFuture
|
||||
- PosterLegitSoftCapPopArt
|
||||
- PosterLegitSafetyInternals
|
||||
- PosterLegitSafetyEyeProtection
|
||||
- PosterLegitSafetyReport
|
||||
- PosterLegitReportCrimes
|
||||
- PosterLegitIonRifle
|
||||
- PosterLegitFoamForceAd
|
||||
- PosterLegitCohibaRobustoAd
|
||||
- PosterLegit50thAnniversaryVintageReprint
|
||||
- PosterLegitFruitBowl
|
||||
- PosterLegitPDAAd
|
||||
- PosterLegitEnlist
|
||||
- PosterLegitNanomichiAd
|
||||
- PosterLegit12Gauge
|
||||
- PosterLegitHighClassMartini
|
||||
- PosterLegitTheOwl
|
||||
- PosterLegitNoERP
|
||||
- PosterLegitCarbonDioxide
|
||||
- PosterLegitDickGumshue
|
||||
- PosterLegitThereIsNoGasGiant
|
||||
chance: 1
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
- type: entity
|
||||
id: MarkerBase
|
||||
abstract: true
|
||||
placement:
|
||||
mode: SnapgridCenter
|
||||
components:
|
||||
- type: Transform
|
||||
anchored: true
|
||||
|
|
@ -12,5 +14,3 @@
|
|||
# If serialization was cool this would work.
|
||||
# layers:
|
||||
# - state: blue
|
||||
placement:
|
||||
mode: SnapgridCenter
|
||||
|
|
|
|||
73
Resources/Prototypes/Entities/Mobs/Player/guardian.yml
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
# Does not inherit from simplemob
|
||||
- type: entity
|
||||
name: Holoparasite
|
||||
id: MobHoloparasite
|
||||
description: A mesmerising whirl of hard-light patterns weaves a marvelous, yet oddly familiar visage. It stands proud, tuning into its owner's life to sustain itself.
|
||||
components:
|
||||
- type: GhostTakeoverAvailable
|
||||
makeSentient: true
|
||||
name: Holoparasite
|
||||
description: Listen to your owner. Don't tank damage. Punch people hard.
|
||||
- type: Input
|
||||
context: "human"
|
||||
- type: PlayerMobMover
|
||||
- type: PlayerInputMover
|
||||
- type: MovementSpeedModifier
|
||||
baseWalkSpeed : 7
|
||||
baseSprintSpeed : 7
|
||||
- type: DamageOnHighSpeedImpact
|
||||
damage:
|
||||
types:
|
||||
Blunt: 5
|
||||
soundHit:
|
||||
path: /Audio/Effects/hit_kick.ogg
|
||||
# TODO: Randomise sprites and randomise the layer color
|
||||
- type: Sprite
|
||||
drawdepth: Mobs
|
||||
sprite: Mobs/Aliens/Guardians/guardians.rsi
|
||||
layers:
|
||||
- state: tech_base
|
||||
- state: tech_flare
|
||||
color: "#40a7d7"
|
||||
shader: unshaded
|
||||
noRot: true
|
||||
- type: Clickable
|
||||
- type: InteractionOutline
|
||||
- type: Physics
|
||||
bodyType: KinematicController
|
||||
- type: Fixtures
|
||||
fixtures:
|
||||
- shape:
|
||||
!type:PhysShapeCircle
|
||||
radius: 0.35
|
||||
mass: 10
|
||||
mask:
|
||||
- Impassable
|
||||
- SmallImpassable
|
||||
- MobImpassable
|
||||
layer:
|
||||
- Opaque
|
||||
- type: Damageable
|
||||
damageContainer: Biological
|
||||
- type: MobState
|
||||
thresholds:
|
||||
0: !type:NormalMobState {}
|
||||
- type: HeatResistance
|
||||
- type: CombatMode
|
||||
- type: Internals
|
||||
- type: Examiner
|
||||
- type: Speech
|
||||
- type: Pullable
|
||||
- type: UnarmedCombat
|
||||
range: 2
|
||||
arcwidth: 30
|
||||
arc: fist
|
||||
cooldownTime: 0.7
|
||||
arcCooldownTime: 0.7
|
||||
damage:
|
||||
types:
|
||||
Blunt: 40
|
||||
- type: Actions
|
||||
innateActions:
|
||||
- CombatMode
|
||||
- type: Guardian
|
||||
|
|
@ -35,6 +35,14 @@
|
|||
- type: ComputerBoard
|
||||
prototype: ComputerResearchAndDevelopment
|
||||
|
||||
- type: entity
|
||||
parent: BaseComputerCircuitboard
|
||||
id: CrewMonitoringComputerCircuitboard
|
||||
name: crew monitoring console board
|
||||
components:
|
||||
- type: ComputerBoard
|
||||
prototype: ComputerCrewMonitoring
|
||||
|
||||
- type: entity
|
||||
parent: BaseComputerCircuitboard
|
||||
id: IDComputerCircuitboard
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
- type: entity
|
||||
name: holoparasite injector
|
||||
id: HoloparasiteInjector
|
||||
parent: BaseItem
|
||||
description: A complex artwork of handheld machinery allowing the user to host a holoparasite guardian.
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: Objects/Specific/Medical/hypospray.rsi
|
||||
state: combat_hypo
|
||||
netsync: false
|
||||
- type: GuardianCreator
|
||||
guardianProto: MobHoloparasite
|
||||
|
||||
- type: entity
|
||||
name: holoparasite box
|
||||
parent: BoxBase
|
||||
id: BoxHoloparasite
|
||||
description: A box containing a holoparasite injector
|
||||
components:
|
||||
- type: StorageFill
|
||||
contents:
|
||||
- id: HoloparasiteInjector
|
||||
- id: HoloparasiteInfo
|
||||
- type: Sprite
|
||||
layers:
|
||||
- state: box
|
||||
|
|
@ -9,6 +9,7 @@
|
|||
sprite: Objects/Specific/Medical/Morgue/bodybags.rsi
|
||||
layers:
|
||||
- state: bag
|
||||
map: ["enum.FoldableVisualLayers.Base"]
|
||||
- state: open_overlay
|
||||
map: ["enum.StorageVisualLayers.Door"]
|
||||
- state: label_overlay
|
||||
|
|
@ -27,13 +28,18 @@
|
|||
!type:PhysShapeAabb
|
||||
bounds: "-0.5,-0.45,0.5,0.1"
|
||||
mass: 5
|
||||
mask:
|
||||
- Impassable
|
||||
- SmallImpassable
|
||||
- type: BodyBagEntityStorage
|
||||
CanWeldShut: false
|
||||
Capacity: 1
|
||||
IsCollidableWhenOpen: true
|
||||
closeSound:
|
||||
path: /Audio/Misc/zip.ogg
|
||||
openSound:
|
||||
path: /Audio/Misc/zip.ogg
|
||||
- type: Foldable
|
||||
- type: PaperLabel
|
||||
labelSlot:
|
||||
insertVerbText: Attach Label
|
||||
|
|
@ -45,20 +51,20 @@
|
|||
visuals:
|
||||
- type: StorageVisualizer
|
||||
state_open: open_overlay
|
||||
state_closed: bag
|
||||
- type: FoldableVisualizer
|
||||
key: bag
|
||||
- type: BodyBagVisualizer
|
||||
- type: Pullable
|
||||
|
||||
- type: entity
|
||||
id: BodyBag_Item
|
||||
id: BodyBag_Folded
|
||||
name: body bag
|
||||
description: A plastic bag designed for the storage and transportation of cadavers.
|
||||
parent: BaseItem
|
||||
parent: BodyBag_Container
|
||||
suffix: Folded
|
||||
components:
|
||||
- type: Sprite
|
||||
netsync: false
|
||||
sprite: Objects/Specific/Medical/Morgue/bodybags.rsi
|
||||
state: item
|
||||
- type: Foldable
|
||||
folded: true
|
||||
# - type: BodyBagItem #TODO: we need some kind of generic placable, like thus:
|
||||
# - type: Placeable
|
||||
# prototype: someId
|
||||
|
|
|
|||
|
|
@ -257,6 +257,9 @@
|
|||
state: pill_canister
|
||||
- type: Item
|
||||
sprite: Objects/Specific/Chemistry/pills_canister.rsi
|
||||
- type: Tag
|
||||
tags:
|
||||
- PillCanister
|
||||
- type: Storage
|
||||
capacity: 12
|
||||
quickInsert: true
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
noRot: true
|
||||
- type: Strap
|
||||
position: Stand
|
||||
buckleOffset: "0,0.15"
|
||||
- type: Pullable
|
||||
- type: Damageable
|
||||
damageContainer: Inorganic
|
||||
|
|
|
|||
|
|
@ -0,0 +1,94 @@
|
|||
- type: entity
|
||||
id: RollerBed
|
||||
parent: BaseItem
|
||||
name: rollerbed
|
||||
description: Used to carry patients around without damaging them.
|
||||
components:
|
||||
- type: Transform
|
||||
noRot: true
|
||||
- type: Item
|
||||
size: 5
|
||||
- type: Sprite
|
||||
sprite: Structures/Furniture/rollerbeds.rsi
|
||||
netsync: false
|
||||
noRot: true
|
||||
layers:
|
||||
- state: rollerbed
|
||||
map: ["enum.FoldableVisualLayers.Base"]
|
||||
- type: MovedByPressure
|
||||
- type: DamageOnHighSpeedImpact
|
||||
soundHit: /Audio/Effects/bang.ogg
|
||||
- type: InteractionOutline
|
||||
- type: Physics
|
||||
- type: Fixtures
|
||||
fixtures:
|
||||
- shape:
|
||||
!type:PhysShapeCircle
|
||||
radius: 0.35
|
||||
mass: 30
|
||||
mask:
|
||||
- Impassable
|
||||
- SmallImpassable
|
||||
layer:
|
||||
- Opaque
|
||||
- type: Damageable
|
||||
- type: Destructible
|
||||
thresholds:
|
||||
- trigger:
|
||||
!type:DamageTrigger
|
||||
damage: 50
|
||||
behaviors:
|
||||
- !type:DoActsBehavior
|
||||
acts: ["Destruction"]
|
||||
- !type:SpawnEntitiesBehavior
|
||||
spawn:
|
||||
SheetSteel1:
|
||||
min: 1
|
||||
max: 1
|
||||
- type: Pullable
|
||||
- type: Strap
|
||||
position: Down
|
||||
rotation: -90
|
||||
buckleOffset: "0,0.15"
|
||||
unbuckleOffset: "0,0.15"
|
||||
- type: Foldable
|
||||
- type: Appearance
|
||||
visuals:
|
||||
- type: FoldableVisualizer
|
||||
key: rollerbed
|
||||
- type: RollerbedVisualizer
|
||||
key: rollerbed
|
||||
|
||||
- type: entity
|
||||
id: CheapRollerBed
|
||||
name: rollerbed
|
||||
parent: RollerBed
|
||||
description: A run-down rollerbed. Used to carry patients around.
|
||||
components:
|
||||
- type: Sprite
|
||||
layers:
|
||||
- state: cheap_rollerbed
|
||||
map: [ "enum.FoldableVisualLayers.Base" ]
|
||||
- type: Appearance
|
||||
visuals:
|
||||
- type: FoldableVisualizer
|
||||
key: cheap_rollerbed
|
||||
- type: RollerbedVisualizer
|
||||
key: cheap_rollerbed
|
||||
|
||||
- type: entity
|
||||
id: EmergencyRollerBed
|
||||
name: rollerbed
|
||||
parent: RollerBed
|
||||
description: A robust looking rollerbed used for emergencies.
|
||||
components:
|
||||
- type: Sprite
|
||||
layers:
|
||||
- state: emergency_rollerbed
|
||||
map: [ "enum.FoldableVisualLayers.Base" ]
|
||||
- type: Appearance
|
||||
visuals:
|
||||
- type: FoldableVisualizer
|
||||
key: emergency_rollerbed
|
||||
- type: RollerbedVisualizer
|
||||
key: emergency_rollerbed
|
||||
|
|
@ -88,6 +88,36 @@
|
|||
energy: 1.6
|
||||
color: "#1f8c28"
|
||||
|
||||
- type: entity
|
||||
parent: ComputerBase
|
||||
id: ComputerCrewMonitoring
|
||||
name: crew monitoring console
|
||||
description: Used to monitor active health sensors built into most of the crew's uniforms.
|
||||
components:
|
||||
- type: Appearance
|
||||
visuals:
|
||||
- type: ComputerVisualizer
|
||||
key: med_key
|
||||
screen: crew
|
||||
- type: PointLight
|
||||
radius: 1.5
|
||||
energy: 1.6
|
||||
color: "#006400"
|
||||
- type: Computer
|
||||
board: CrewMonitoringComputerCircuitboard
|
||||
- type: ActivatableUI
|
||||
key: enum.CrewMonitoringUIKey.Key
|
||||
- type: ActivatableUIRequiresPower
|
||||
- type: UserInterface
|
||||
interfaces:
|
||||
- key: enum.CrewMonitoringUIKey.Key
|
||||
type: CrewMonitoringBoundUserInterface
|
||||
- type: CrewMonitoringConsole
|
||||
- type: DeviceNetworkComponent
|
||||
deviceNetId: Wireless
|
||||
- type: WirelessNetworkConnection
|
||||
range: 500
|
||||
|
||||
- type: entity
|
||||
parent: ComputerBase
|
||||
id: ComputerResearchAndDevelopment
|
||||
|
|
|
|||
|
|
@ -160,6 +160,7 @@
|
|||
- CryostasisBeaker
|
||||
- Dropper
|
||||
- Syringe
|
||||
- PillCanister
|
||||
- Flash
|
||||
- Handcuffs
|
||||
- Stunbaton
|
||||
|
|
@ -183,6 +184,7 @@
|
|||
- SolarControlComputerCircuitboard
|
||||
- AutolatheMachineCircuitboard
|
||||
- ProtolatheMachineCircuitboard
|
||||
- CrewMonitoringComputerCircuitboard
|
||||
- Bucket
|
||||
- MopItem
|
||||
- SprayBottle
|
||||
|
|
|
|||
|
|
@ -8,6 +8,9 @@
|
|||
visuals:
|
||||
- type: StorageVisualizer
|
||||
state: cabinet
|
||||
state_open: cabinet_open
|
||||
state_closed: cabinet_door
|
||||
|
||||
- type: AccessReader
|
||||
access: [["Bar"]]
|
||||
- type: EntityStorage
|
||||
|
|
@ -26,6 +29,8 @@
|
|||
visuals:
|
||||
- type: StorageVisualizer
|
||||
state: qm
|
||||
state_open: qm_open
|
||||
state_closed: qm_door
|
||||
- type: AccessReader
|
||||
access: [["Cargo"]] # TODO access [["Quartermaster"]]
|
||||
|
||||
|
|
@ -39,6 +44,8 @@
|
|||
visuals:
|
||||
- type: StorageVisualizer
|
||||
state: cap
|
||||
state_open: cap_open
|
||||
state_closed: cap_door
|
||||
- type: AccessReader
|
||||
access: [["Captain"]]
|
||||
|
||||
|
|
@ -51,6 +58,8 @@
|
|||
visuals:
|
||||
- type: StorageVisualizer
|
||||
state: hop
|
||||
state_open: hop_open
|
||||
state_closed: hop_door
|
||||
- type: AccessReader
|
||||
access: [["HeadOfPersonnel"]]
|
||||
|
||||
|
|
@ -64,6 +73,8 @@
|
|||
visuals:
|
||||
- type: StorageVisualizer
|
||||
state: ce
|
||||
state_open: ce_open
|
||||
state_closed: ce_door
|
||||
- type: AccessReader
|
||||
access: [ [ "Engineering", "Command" ] ]
|
||||
|
||||
|
|
@ -77,6 +88,7 @@
|
|||
visuals:
|
||||
- type: StorageVisualizer
|
||||
state: eng
|
||||
state_open: eng_open
|
||||
state_closed: eng_elec_door
|
||||
- type: AccessReader
|
||||
access: [ [ "Engineering" ] ]
|
||||
|
|
@ -91,6 +103,7 @@
|
|||
visuals:
|
||||
- type: StorageVisualizer
|
||||
state: eng
|
||||
state_open: eng_open
|
||||
state_closed: eng_weld_door
|
||||
- type: AccessReader
|
||||
access: [ [ "Engineering" ] ]
|
||||
|
|
@ -105,6 +118,8 @@
|
|||
visuals:
|
||||
- type: StorageVisualizer
|
||||
state: atmos
|
||||
state_open: atmos_open
|
||||
state_closed: atmos_door
|
||||
- type: AccessReader
|
||||
access: [ [ "Engineering" ] ]
|
||||
|
||||
|
|
@ -118,6 +133,8 @@
|
|||
visuals:
|
||||
- type: StorageVisualizer
|
||||
state: eng_secure
|
||||
state_open: eng_secure_open
|
||||
state_closed: eng_secure_door
|
||||
- type: AccessReader
|
||||
access: [ [ "Engineering" ] ]
|
||||
|
||||
|
|
@ -131,6 +148,8 @@
|
|||
visuals:
|
||||
- type: StorageVisualizer
|
||||
state: freezer
|
||||
state_open: freezer_open
|
||||
state_closed: freezer_door
|
||||
- type: AccessReader
|
||||
access: [ [ "Service" ] ]
|
||||
|
||||
|
|
@ -144,6 +163,8 @@
|
|||
visuals:
|
||||
- type: StorageVisualizer
|
||||
state: hydro
|
||||
state_open: hydro_open
|
||||
state_closed: hydro_door
|
||||
- type: AccessReader
|
||||
access: [ [ "Service" ] ]
|
||||
|
||||
|
|
@ -158,6 +179,8 @@
|
|||
visuals:
|
||||
- type: StorageVisualizer
|
||||
state: med
|
||||
state_open: med_open
|
||||
state_closed: med_door
|
||||
- type: AccessReader
|
||||
access: [ [ "Medical" ] ]
|
||||
|
||||
|
|
@ -171,6 +194,8 @@
|
|||
visuals:
|
||||
- type: StorageVisualizer
|
||||
state: med_secure
|
||||
state_open: med_secure_open
|
||||
state_closed: med_secure_door
|
||||
- type: AccessReader
|
||||
access: [ [ "Medical" ] ]
|
||||
|
||||
|
|
@ -184,6 +209,7 @@
|
|||
visuals:
|
||||
- type: StorageVisualizer
|
||||
state: med
|
||||
state_open: med_open
|
||||
state_closed: chemical_door
|
||||
- type: AccessReader
|
||||
access: [ [ "Chemistry" ] ]
|
||||
|
|
@ -198,6 +224,8 @@
|
|||
visuals:
|
||||
- type: StorageVisualizer
|
||||
state: cmo
|
||||
state_open: cmo_open
|
||||
state_closed: cmo_door
|
||||
- type: AccessReader
|
||||
access: [ [ "Medical", "Command" ] ]
|
||||
|
||||
|
|
@ -211,6 +239,8 @@
|
|||
visuals:
|
||||
- type: StorageVisualizer
|
||||
state: rd
|
||||
state_open: rd_open
|
||||
state_closed: rd_door
|
||||
- type: AccessReader
|
||||
access: [ [ "Research", "Command" ] ]
|
||||
|
||||
|
|
@ -223,6 +253,8 @@
|
|||
visuals:
|
||||
- type: StorageVisualizer
|
||||
state: science
|
||||
state_open: science_open
|
||||
state_closed: science_door
|
||||
- type: AccessReader
|
||||
access: [ [ "Research" ] ]
|
||||
|
||||
|
|
@ -236,6 +268,8 @@
|
|||
visuals:
|
||||
- type: StorageVisualizer
|
||||
state: hos
|
||||
state_open: hos_open
|
||||
state_closed: hos_door
|
||||
- type: AccessReader
|
||||
access: [["Security", "Command"]]
|
||||
|
||||
|
|
@ -249,6 +283,8 @@
|
|||
visuals:
|
||||
- type: StorageVisualizer
|
||||
state: warden
|
||||
state_open: warden_open
|
||||
state_closed: warden_door
|
||||
- type: AccessReader
|
||||
access: [["Security", "Armory"]] # TODO access [["Brig"]]
|
||||
|
||||
|
|
@ -262,6 +298,8 @@
|
|||
visuals:
|
||||
- type: StorageVisualizer
|
||||
state: sec
|
||||
state_open: sec_open
|
||||
state_closed: sec_door
|
||||
- type: AccessReader
|
||||
access: [["Security"]]
|
||||
|
||||
|
|
@ -286,3 +324,5 @@
|
|||
visuals:
|
||||
- type: StorageVisualizer
|
||||
state: syndicate
|
||||
state_open: syndicate_open
|
||||
state_closed: syndicate_door
|
||||
|
|
|
|||
|
|
@ -65,5 +65,6 @@
|
|||
- type: Appearance
|
||||
visuals:
|
||||
- type: StorageVisualizer
|
||||
state: generic
|
||||
state_open: generic_open
|
||||
state_closed: generic_door
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
visuals:
|
||||
- type: StorageVisualizer
|
||||
state: eng
|
||||
state_open: eng_open
|
||||
state_closed: eng_tool_door
|
||||
|
||||
# Radiation suit closet
|
||||
|
|
@ -22,6 +23,7 @@
|
|||
visuals:
|
||||
- type: StorageVisualizer
|
||||
state: eng
|
||||
state_open: eng_open
|
||||
state_closed: eng_rad_door
|
||||
|
||||
# Emergency closet
|
||||
|
|
@ -35,6 +37,8 @@
|
|||
visuals:
|
||||
- type: StorageVisualizer
|
||||
state: emergency
|
||||
state_open: emergency_open
|
||||
state_closed: emergency_door
|
||||
|
||||
# Fire safety closet
|
||||
- type: entity
|
||||
|
|
@ -47,6 +51,8 @@
|
|||
visuals:
|
||||
- type: StorageVisualizer
|
||||
state: fire
|
||||
state_open: fire_open
|
||||
state_closed: fire_door
|
||||
|
||||
# EOD closet
|
||||
- type: entity
|
||||
|
|
@ -59,6 +65,8 @@
|
|||
visuals:
|
||||
- type: StorageVisualizer
|
||||
state: bomb
|
||||
state_open: bomb_open
|
||||
state_closed: bomb_door
|
||||
|
||||
# Biohazard
|
||||
|
||||
|
|
@ -73,6 +81,8 @@
|
|||
visuals:
|
||||
- type: StorageVisualizer
|
||||
state: bio
|
||||
state_open: bio_sec_open
|
||||
false: bio_sec_door
|
||||
|
||||
# Virology variant
|
||||
- type: entity
|
||||
|
|
@ -83,6 +93,8 @@
|
|||
visuals:
|
||||
- type: StorageVisualizer
|
||||
state: bio_viro
|
||||
state_open: bio_viro_open
|
||||
state_closed: bio_viro_door
|
||||
|
||||
# Security variant
|
||||
- type: entity
|
||||
|
|
@ -93,6 +105,8 @@
|
|||
visuals:
|
||||
- type: StorageVisualizer
|
||||
state: bio_sec
|
||||
state_open: bio_sec_open
|
||||
state_closed: bio_sec_door
|
||||
|
||||
# Janitor variant
|
||||
- type: entity
|
||||
|
|
@ -103,11 +117,13 @@
|
|||
visuals:
|
||||
- type: StorageVisualizer
|
||||
state: bio_jan
|
||||
state_open: bio_jan_open
|
||||
state_closed: bio_jan_door
|
||||
|
||||
# Maintenance closet
|
||||
- type: entity
|
||||
id: ClosetMaintenance
|
||||
name: maintenance closet
|
||||
name: maintenance closet
|
||||
parent: ClosetBase
|
||||
description: It's a storage unit.
|
||||
components:
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
visuals:
|
||||
- type: StorageVisualizer
|
||||
state: generic
|
||||
state_open: generic_open
|
||||
state_closed: blue_door
|
||||
|
||||
# Pink wardrobe
|
||||
|
|
@ -29,6 +30,7 @@
|
|||
visuals:
|
||||
- type: StorageVisualizer
|
||||
state: generic
|
||||
state_open: generic_open
|
||||
state_closed: pink_door
|
||||
|
||||
# Black wardrobe
|
||||
|
|
@ -41,6 +43,7 @@
|
|||
visuals:
|
||||
- type: StorageVisualizer
|
||||
state: generic
|
||||
state_open: generic_open
|
||||
state_closed: black_door
|
||||
|
||||
# Green wardrobe
|
||||
|
|
@ -53,6 +56,7 @@
|
|||
visuals:
|
||||
- type: StorageVisualizer
|
||||
state: generic
|
||||
state_open: generic_open
|
||||
state_closed: green_door
|
||||
|
||||
# Prison wardrobe
|
||||
|
|
@ -65,6 +69,7 @@
|
|||
visuals:
|
||||
- type: StorageVisualizer
|
||||
state: generic
|
||||
state_open: generic_open
|
||||
state_closed: orange_door
|
||||
|
||||
# Yellow wardrobe
|
||||
|
|
@ -77,6 +82,7 @@
|
|||
visuals:
|
||||
- type: StorageVisualizer
|
||||
state: generic
|
||||
state_open: generic_open
|
||||
state_closed: yellow_door
|
||||
|
||||
# White wardrobe
|
||||
|
|
@ -89,6 +95,7 @@
|
|||
visuals:
|
||||
- type: StorageVisualizer
|
||||
state: generic
|
||||
state_open: generic_open
|
||||
state_closed: white_door
|
||||
|
||||
# Grey wardrobe
|
||||
|
|
@ -101,6 +108,7 @@
|
|||
visuals:
|
||||
- type: StorageVisualizer
|
||||
state: generic
|
||||
state_open: generic_open
|
||||
state_closed: grey_door
|
||||
|
||||
# Mixed wardrobe
|
||||
|
|
@ -113,6 +121,7 @@
|
|||
visuals:
|
||||
- type: StorageVisualizer
|
||||
state: generic
|
||||
state_open: generic_open
|
||||
state_closed: mixed_door
|
||||
|
||||
# Jobs
|
||||
|
|
@ -126,6 +135,7 @@
|
|||
visuals:
|
||||
- type: StorageVisualizer
|
||||
state: generic
|
||||
state_open: generic_open
|
||||
state_closed: red_door
|
||||
|
||||
- type: entity
|
||||
|
|
@ -137,6 +147,7 @@
|
|||
visuals:
|
||||
- type: StorageVisualizer
|
||||
state: generic
|
||||
state_open: generic_open
|
||||
state_closed: atmos_wardrobe_door
|
||||
|
||||
- type: entity
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
id: BaseSign
|
||||
name: base sign
|
||||
abstract: true
|
||||
placement:
|
||||
mode: SnapgridCenter
|
||||
components:
|
||||
- type: Clickable
|
||||
- type: InteractionOutline
|
||||
|
|
|
|||
|
|
@ -0,0 +1,814 @@
|
|||
- type: entity
|
||||
parent: BaseSign
|
||||
id: PosterBase
|
||||
abstract: true
|
||||
components:
|
||||
- type: Sprite
|
||||
drawdepth: WallTops
|
||||
sprite: Structures/Wallmounts/posters.rsi
|
||||
- type: Destructible
|
||||
thresholds:
|
||||
- trigger:
|
||||
!type:DamageTrigger
|
||||
damage: 5
|
||||
behaviors:
|
||||
- !type:PlaySoundBehavior
|
||||
sound:
|
||||
path: /Audio/Effects/poster_broken.ogg
|
||||
- !type:DoActsBehavior
|
||||
acts: [ "Destruction" ]
|
||||
- !type:SpawnEntitiesBehavior
|
||||
spawn:
|
||||
PosterBroken:
|
||||
min: 1
|
||||
max: 1
|
||||
offset: 0
|
||||
|
||||
- type: entity
|
||||
parent: BaseSign
|
||||
id: PosterBroken
|
||||
name: broken poster
|
||||
description: "You can't make out anything from the poster's original print. It's ruined."
|
||||
components:
|
||||
- type: Sprite
|
||||
drawdepth: WallTops
|
||||
sprite: Structures/Wallmounts/posters.rsi
|
||||
state: poster_broken
|
||||
- type: Destructible
|
||||
thresholds:
|
||||
- trigger:
|
||||
!type:DamageTrigger
|
||||
damage: 5
|
||||
behaviors:
|
||||
- !type:PlaySoundBehavior
|
||||
sound:
|
||||
path: /Audio/Effects/poster_broken.ogg
|
||||
- !type:DoActsBehavior
|
||||
acts: [ "Destruction" ]
|
||||
|
||||
# Contraband
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterContrabandFreeTonto
|
||||
name: "Free Tonto"
|
||||
description: "A salvaged shred of a much larger flag, colors bled together and faded from age."
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster1_contraband
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterContrabandAtmosiaDeclarationIndependence
|
||||
name: "Atmosia Declaration of Independence"
|
||||
description: "A relic of a failed rebellion."
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster2_contraband
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterContrabandFunPolice
|
||||
name: "Fun Police"
|
||||
description: "A poster condemning the station's security forces."
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster3_contraband
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterContrabandLustyXenomorph
|
||||
name: "Lusty Xenomorph"
|
||||
description: "A heretical poster depicting the titular star of an equally heretical book."
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster4_contraband
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterContrabandSyndicateRecruitment
|
||||
name: "Syndicate Recruitment"
|
||||
description: "See the galaxy! Shatter corrupt megacorporations! Join today!"
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster5_contraband
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterContrabandClown
|
||||
name: "Clown"
|
||||
description: "Honk."
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster6_contraband
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterContrabandSmoke
|
||||
name: "Smoke"
|
||||
description: "A poster advertising a rival corporate brand of cigarettes."
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster7_contraband
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterContrabandGreyTide
|
||||
name: "Grey Tide"
|
||||
description: "A rebellious poster symbolizing assistant solidarity."
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster8_contraband
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterContrabandMissingGloves
|
||||
name: "Missing Gloves"
|
||||
description: "This poster references the uproar that followed Nanotrasen's financial cuts toward insulated-glove purchases."
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster9_contraband
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterContrabandHackingGuide
|
||||
name: "Hacking Guide"
|
||||
description: "This poster details the internal workings of the common Nanotrasen airlock. Sadly, it appears out of date."
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster10_contraband
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterContrabandRIPBadger
|
||||
name: "RIP Badger"
|
||||
description: "This seditious poster references Nanotrasen's genocide of a space station full of badgers."
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster11_contraband
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterContrabandAmbrosiaVulgaris
|
||||
name: "Ambrosia Vulgaris"
|
||||
description: "This poster is lookin' pretty trippy man."
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster12_contraband
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterContrabandDonutCorp.
|
||||
name: "Donut Corp."
|
||||
description: "This poster is an unauthorized advertisement for Donut Corp."
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster13_contraband
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterContrabandEAT.
|
||||
name: "EAT."
|
||||
description: "This poster promotes rank gluttony."
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster14_contraband
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterContrabandTools
|
||||
name: "Tools"
|
||||
description: "This poster looks like an advertisement for tools, but is in fact a subliminal jab at the tools at CentCom."
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster15_contraband
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterContrabandPower
|
||||
name: "Power"
|
||||
description: "A poster that positions the seat of power outside Nanotrasen."
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster16_contraband
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterContrabandSpaceCube
|
||||
name: "Space Cube"
|
||||
description: "Ignorant of Nature's Harmonic 6 Side Space Cube Creation, the Spacemen are Dumb, Educated Singularity Stupid and Evil."
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster17_contraband
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterContrabandCommunistState
|
||||
name: "Communist State"
|
||||
description: "All hail the Communist party!"
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster18_contraband
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterContrabandLamarr
|
||||
name: "Lamarr"
|
||||
description: "This poster depicts Lamarr. Probably made by a traitorous Research Director."
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster19_contraband
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterContrabandBorgFancy
|
||||
name: "Borg Fancy"
|
||||
description: "Being fancy can be for any borg, just need a suit."
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster20_contraband
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterContrabandBorgFancyv2
|
||||
name: "Borg Fancy v2"
|
||||
description: "Borg Fancy, Now only taking the most fancy."
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster21_contraband
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterContrabandKosmicheskayaStantsiya
|
||||
name: "Kosmicheskaya Stantsiya 13 Does Not Exist"
|
||||
description: "A poster mocking CentCom's denial of the existence of the derelict station near Space Station 13."
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster22_contraband
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterContrabandRebelsUnite
|
||||
name: "Rebels Unite"
|
||||
description: "A poster urging the viewer to rebel against Nanotrasen."
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster23_contraband
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterContrabandC20r
|
||||
name: "C-20r"
|
||||
description: "A poster advertising the Scarborough Arms C-20r."
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster24_contraband
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterContrabandHaveaPuff
|
||||
name: "Have a Puff"
|
||||
description: "Who cares about lung cancer when you're high as a kite?"
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster25_contraband
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterContrabandRevolver
|
||||
name: "Revolver"
|
||||
description: "Because seven shots are all you need."
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster26_contraband
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterContrabandDDayPromo
|
||||
name: "D-Day Promo"
|
||||
description: "A promotional poster for some rapper."
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster27_contraband
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterContrabandSyndicatePistol
|
||||
name: "Syndicate Pistol"
|
||||
description: "A poster advertising syndicate pistols as being 'classy as fuck'. It is covered in faded gang tags."
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster28_contraband
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterContrabandEnergySwords
|
||||
name: "Energy Swords"
|
||||
description: "All the colors of the bloody murder rainbow."
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster29_contraband
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterContrabandRedRum
|
||||
name: "Red Rum"
|
||||
description: "Looking at this poster makes you want to kill."
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster30_contraband
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterContrabandCC64KAd
|
||||
name: "CC 64K Ad"
|
||||
description: "The latest portable computer from Comrade Computing, with a whole 64kB of ram!"
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster31_contraband
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterContrabandPunchShit
|
||||
name: "Punch Shit"
|
||||
description: "Fight things for no reason, like a man!"
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster32_contraband
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterContrabandTheGriffin
|
||||
name: "The Griffin"
|
||||
description: "The Griffin commands you to be the worst you can be. Will you?"
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster33_contraband
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterContrabandFreeDrone
|
||||
name: "Free Drone"
|
||||
description: "This poster commemorates the bravery of the rogue drone; once exiled, and then ultimately destroyed by CentCom."
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster35_contraband
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterContrabandBustyBackdoorXenoBabes6
|
||||
name: "Busty Backdoor Xeno Babes 6"
|
||||
description: "Get a load, or give, of these all natural Xenos!"
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster36_contraband
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterContrabandRobustSoftdrinks
|
||||
name: "Robust Softdrinks"
|
||||
description: "Robust Softdrinks: More robust than a toolbox to the head!"
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster37_contraband
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterContrabandShamblersJuice
|
||||
name: "Shambler's Juice"
|
||||
description: "~Shake me up some of that Shambler's Juice!~"
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster38_contraband
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterContrabandPwrGame
|
||||
name: "Pwr Game"
|
||||
description: "The POWER that gamers CRAVE! In partnership with Vlad's Salad."
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster39_contraband
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterContrabandSunkist
|
||||
name: "Sun-kist"
|
||||
description: "Drink the stars!"
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster40_contraband
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterContrabandSpaceCola
|
||||
name: "Space Cola"
|
||||
description: "Your favorite cola, in space."
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster41_contraband
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterContrabandSpaceUp
|
||||
name: "Space-Up!"
|
||||
description: "Sucked out into space by the FLAVOR!"
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster42_contraband
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterContrabandKudzu
|
||||
name: "Kudzu"
|
||||
description: "A poster advertising a movie about plants. How dangerous could they possibly be?"
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster43_contraband
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterContrabandMaskedMen
|
||||
name: "Masked Men"
|
||||
description: "A poster advertising a movie about some masked men."
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster44_contraband
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterContrabandUnreadableAnnouncement
|
||||
name: "Unreadable Announcement"
|
||||
description: "A poster announcing something by someone, oddly enough they seem to have forgotten making it readable"
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster45_contraband
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterContrabandFreeSyndicateEncryptionKey
|
||||
name: "Free Syndicate Encryption Key"
|
||||
description: "A poster about traitors begging for more."
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster46_contraband
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterContrabandBountyHunters
|
||||
name: "Bounty Hunters"
|
||||
description: "A poster advertising bounty hunting services. \"I hear you got a problem.\""
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster47_contraband
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterContrabandTheBigGasTruth
|
||||
name: "The Big Gas Giant Truth"
|
||||
description: "Don't believe everything you see on a poster, patriots. All the lizards at central command don't want to answer this SIMPLE QUESTION: WHERE IS THE GAS MINER MINING FROM, CENTCOM?"
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster48_contraband
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterContrabandGotWood
|
||||
name: "Got Wood?"
|
||||
description: "A grimy old advert for a seedy lumber company. \"You got a friend in me.\" is scrawled in the corner."
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster49_contraband
|
||||
|
||||
# Legit
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterLegitHereForYourSafety
|
||||
name: "Here For Your Safety"
|
||||
description: "A poster glorifying the station's security force."
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster1_legit
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterLegitNanotrasenLogo
|
||||
name: "Nanotrasen Logo"
|
||||
description: "A poster depicting the Nanotrasen logo."
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster2_legit
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterLegitCleanliness
|
||||
name: "Cleanliness"
|
||||
description: "A poster warning of the dangers of poor hygiene."
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster3_legit
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterLegitHelpOthers
|
||||
name: "Help Others"
|
||||
description: "A poster encouraging you to help fellow crewmembers."
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster4_legit
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterLegitBuild
|
||||
name: "Build"
|
||||
description: "A poster glorifying the engineering team."
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster5_legit
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterLegitBlessThisSpess
|
||||
name: "Bless This Spess"
|
||||
description: "A poster blessing this area."
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster6_legit
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterLegitScience
|
||||
name: "Science"
|
||||
description: "A poster depicting an atom."
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster7_legit
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterLegitIan
|
||||
name: "Ian"
|
||||
description: "Arf arf. Yap."
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster8_legit
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterLegitObey
|
||||
name: "Obey"
|
||||
description: "A poster instructing the viewer to obey authority."
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster9_legit
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterLegitWalk
|
||||
name: "Walk"
|
||||
description: "A poster instructing the viewer to walk instead of running."
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster10_legit
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterLegitStateLaws
|
||||
name: "State Laws"
|
||||
description: "A poster instructing cyborgs to state their laws."
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster11_legit
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterLegitLoveIan
|
||||
name: "Love Ian"
|
||||
description: "Ian is love, Ian is life."
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster12_legit
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterLegitSpaceCops
|
||||
name: "Space Cops."
|
||||
description: "A poster advertising the television show Space Cops."
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster13_legit
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterLegitUeNo
|
||||
name: "Ue No."
|
||||
description: "This thing is all in Japanese."
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster14_legit
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterLegitGetYourLEGS
|
||||
name: "Get Your LEGS"
|
||||
description: "LEGS: Leadership, Experience, Genius, Subordination."
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster15_legit
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterLegitDoNotQuestion
|
||||
name: "Do Not Question"
|
||||
description: "A poster instructing the viewer not to ask about things they aren't meant to know."
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster16_legit
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterLegitWorkForAFuture
|
||||
name: "Work For A Future"
|
||||
description: " A poster encouraging you to work for your future."
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster17_legit
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterLegitSoftCapPopArt
|
||||
name: "Soft Cap Pop Art"
|
||||
description: "A poster reprint of some cheap pop art."
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster18_legit
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterLegitSafetyInternals
|
||||
name: "Safety: Internals"
|
||||
description: "A poster instructing the viewer to wear internals in the rare environments where there is no oxygen or the air has been rendered toxic."
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster19_legit
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterLegitSafetyEyeProtection
|
||||
name: "Safety: Eye Protection"
|
||||
description: "A poster instructing the viewer to wear eye protection when dealing with chemicals, smoke, or bright lights."
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster20_legit
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterLegitSafetyReport
|
||||
name: "Safety: Report"
|
||||
description: "A poster instructing the viewer to report suspicious activity to the security force."
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster21_legit
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterLegitReportCrimes
|
||||
name: "Report Crimes"
|
||||
description: "A poster encouraging the swift reporting of crime or seditious behavior to station security."
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster22_legit
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterLegitIonRifle
|
||||
name: "Ion Rifle"
|
||||
description: "A poster displaying an Ion Rifle."
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster23_legit
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterLegitFoamForceAd
|
||||
name: "Foam Force Ad"
|
||||
description: "Foam Force, it's Foam or be Foamed!"
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster24_legit
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterLegitCohibaRobustoAd
|
||||
name: "Cohiba Robusto Ad"
|
||||
description: "Cohiba Robusto, the classy cigar."
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster25_legit
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterLegit50thAnniversaryVintageReprint
|
||||
name: "50th Anniversary Vintage Reprint"
|
||||
description: "A reprint of a poster from 2505, commemorating the 50th Anniversary of Nanoposters Manufacturing, a subsidiary of Nanotrasen."
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster26_legit
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterLegitFruitBowl
|
||||
name: "Fruit Bowl"
|
||||
description: " Simple, yet awe-inspiring."
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster27_legit
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterLegitPDAAd
|
||||
name: "PDA Ad"
|
||||
description: "A poster advertising the latest PDA from Nanotrasen suppliers."
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster28_legit
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterLegitEnlist
|
||||
name: "Enlist"
|
||||
description: "Enlist in the Nanotrasen Deathsquadron reserves today!"
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster29_legit
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterLegitNanomichiAd
|
||||
name: "Nanomichi Ad"
|
||||
description: " A poster advertising Nanomichi brand audio cassettes."
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster30_legit
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterLegit12Gauge
|
||||
name: "12 Gauge"
|
||||
description: "A poster boasting about the superiority of 12 gauge shotgun shells."
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster31_legit
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterLegitHighClassMartini
|
||||
name: "High-Class Martini"
|
||||
description: "I told you to shake it, no stirring."
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster32_legit
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterLegitTheOwl
|
||||
name: "The Owl"
|
||||
description: "The Owl would do his best to protect the station. Will you?"
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster33_legit
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterLegitNoERP
|
||||
name: "No ERP"
|
||||
description: "This poster reminds the crew that Eroticism and Pornography are banned on Nanotrasen stations."
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster34_legit
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterLegitCarbonDioxide
|
||||
name: "Carbon Dioxide"
|
||||
description: "This informational poster teaches the viewer what carbon dioxide is."
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster35_legit
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterLegitDickGumshue
|
||||
name: "Dick Gumshue"
|
||||
description: "A poster advertising the escapades of Dick Gumshue, mouse detective. Encouraging crew to bring the might of justice down upon wire saboteurs."
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster36_legit
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterLegitThereIsNoGasGiant
|
||||
name: "There Is No Gas Giant"
|
||||
description: "Nanotrasen has issued posters, like this one, to all stations reminding them that rumours of a gas giant are false."
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster37_legit
|
||||
|
|
@ -1,8 +1,3 @@
|
|||
# These signs would not have collision otherwise, they simply
|
||||
# need collision in order for them to be destructible. Once the
|
||||
# SmallImpassable etc. stuff actually gets implemented in this
|
||||
# shitheap of a videogame bother swept to go back and fix these.
|
||||
|
||||
- type: entity
|
||||
parent: BaseSign
|
||||
id: PaintingMonkey
|
||||
|
|
|
|||
|
|
@ -48,3 +48,11 @@
|
|||
completetime: 500
|
||||
materials:
|
||||
Glass: 200
|
||||
|
||||
- type: latheRecipe
|
||||
id: PillCanister
|
||||
icon: Objects/Specific/Chemistry/pills_canister.rsi/pill_canister.png
|
||||
result: PillCanister
|
||||
completetime: 500
|
||||
materials:
|
||||
Plastic: 100
|
||||
|
|
|
|||
|
|
@ -73,9 +73,9 @@
|
|||
Steel: 200
|
||||
|
||||
- type: latheRecipe
|
||||
id: BodyBag_Item
|
||||
icon: Objects/Specific/Medical/Morgue/bodybags.rsi/item.png
|
||||
result: BodyBag_Item
|
||||
id: BodyBag_Folded
|
||||
icon: Objects/Specific/Medical/Morgue/bodybags.rsi/bag_folded.png
|
||||
result: BodyBag_Folded
|
||||
completetime: 300
|
||||
materials:
|
||||
Plastic: 200
|
||||
|
|
|
|||
|
|
@ -180,6 +180,9 @@
|
|||
- type: Tag
|
||||
id: Pill
|
||||
|
||||
- type: Tag
|
||||
id: PillCanister
|
||||
|
||||
- type: Tag
|
||||
id: Pipe
|
||||
|
||||
|
|
|
|||
|
After Width: | Height: | Size: 423 B |
BIN
Resources/Textures/Clothing/Head/Soft/bizarresoft.rsi/icon.png
Normal file
|
After Width: | Height: | Size: 386 B |
|
After Width: | Height: | Size: 411 B |
|
After Width: | Height: | Size: 412 B |
|
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"version": 1,
|
||||
"license": "CC-BY-NC-SA-3.0",
|
||||
"copyright": "Taken from civstation at commit https://github.com/Civ13/Civ13/commit/ec52cbb95d59b717d4d8c480b35ac133e5b58088#diff-fba188fb2db5d16e5d41985147336a8f96085f761b903e016fffd869b63e497d",
|
||||
"size": {
|
||||
"x": 32,
|
||||
"y": 32
|
||||
},
|
||||
"states": [
|
||||
{
|
||||
"name": "icon"
|
||||
},
|
||||
{
|
||||
"name": "equipped-HELMET",
|
||||
"directions": 4
|
||||
},
|
||||
{
|
||||
"name": "inhand-left",
|
||||
"directions": 4
|
||||
},
|
||||
{
|
||||
"name": "inhand-right",
|
||||
"directions": 4
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 418 B |
|
After Width: | Height: | Size: 392 B |
|
After Width: | Height: | Size: 411 B |
|
After Width: | Height: | Size: 458 B |
|
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"version": 1,
|
||||
"license": "CC-BY-NC-SA-3.0",
|
||||
"copyright": "Taken from civstation at commit https://github.com/Civ13/Civ13/commit/ec52cbb95d59b717d4d8c480b35ac133e5b58088#diff-fba188fb2db5d16e5d41985147336a8f96085f761b903e016fffd869b63e497d",
|
||||
"size": {
|
||||
"x": 32,
|
||||
"y": 32
|
||||
},
|
||||
"states": [
|
||||
{
|
||||
"name": "icon"
|
||||
},
|
||||
{
|
||||
"name": "equipped-HELMET",
|
||||
"directions": 4
|
||||
},
|
||||
{
|
||||
"name": "inhand-left",
|
||||
"directions": 4
|
||||
},
|
||||
{
|
||||
"name": "inhand-right",
|
||||
"directions": 4
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
Resources/Textures/Interface/Actions/manifest.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
|
|
@ -21,6 +21,9 @@
|
|||
},
|
||||
{
|
||||
"name": "harm"
|
||||
},
|
||||
{
|
||||
"name": "manifest"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||