Merge remote-tracking branch 'space-wizards/master'
# Conflicts: # Content.Client/Administration/UI/Tabs/PlayerTab/PlayerTab.xaml.cs # Content.Client/Administration/UI/Tabs/PlayerTab/PlayerTabEntry.xaml # Content.Client/Administration/UI/Tabs/PlayerTab/PlayerTabEntry.xaml.cs # Content.Client/Administration/UI/Tabs/PlayerTab/PlayerTabHeader.xaml # Content.Client/Administration/UI/Tabs/PlayerTab/PlayerTabHeader.xaml.cs # Content.Server/Administration/Systems/AdminSystem.cs # Content.Server/Administration/Systems/AdminVerbSystem.Antags.cs # Content.Server/Ghost/GhostSystem.cs # Resources/Locale/en-US/_strings/administration/antag.ftl # Resources/Locale/en-US/_strings/administration/commands/adminnotes.ftl # Resources/Locale/en-US/_strings/construction/components/block-anchor-component.ftl # Resources/Locale/en-US/_strings/power/commands.ftl # Resources/Prototypes/Catalog/VendingMachines/Inventories/clothesmate.yml # Resources/Textures/Interface/Misc/job_icons.rsi/meta.json # Resources/Textures/Objects/Specific/Hydroponics/aloe.rsi/produce.png # Resources/Textures/Objects/Specific/Hydroponics/apple.rsi/produce.png # Resources/Textures/Objects/Specific/Hydroponics/chili.rsi/produce.png # Resources/Textures/Objects/Specific/Hydroponics/corn.rsi/meta.json # Resources/Textures/Objects/Specific/Hydroponics/corn.rsi/produce.png # Resources/Textures/Objects/Specific/Hydroponics/eggplant.rsi/produce.png # Resources/Textures/Objects/Specific/Hydroponics/eggy.rsi/meta.json # Resources/Textures/Objects/Specific/Hydroponics/lemon.rsi/produce.png # Resources/Textures/Objects/Specific/Hydroponics/onion_red.rsi/produce.png # Resources/Textures/Objects/Specific/Hydroponics/potato.rsi/produce.png # Resources/Textures/Objects/Specific/Hydroponics/sugarcane.rsi/produce.png # Resources/Textures/Objects/Specific/Hydroponics/tomato.rsi/produce.png
|
|
@ -1,7 +1,10 @@
|
|||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Content.Client.Administration.Systems;
|
||||
using Content.Client.Stylesheets;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Ghost;
|
||||
using Content.Shared.Mind;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.ResourceManagement;
|
||||
|
|
@ -14,32 +17,54 @@ namespace Content.Client.Administration;
|
|||
|
||||
internal sealed class AdminNameOverlay : Overlay
|
||||
{
|
||||
[Dependency] private readonly IConfigurationManager _config = default!;
|
||||
|
||||
private readonly AdminSystem _system;
|
||||
private readonly IEntityManager _entityManager;
|
||||
private readonly IEyeManager _eyeManager;
|
||||
private readonly EntityLookupSystem _entityLookup;
|
||||
private readonly IUserInterfaceManager _userInterfaceManager;
|
||||
private readonly Font _font;
|
||||
private readonly Font _fontBold;
|
||||
private bool _overlayClassic;
|
||||
private bool _overlaySymbols;
|
||||
private bool _overlayPlaytime;
|
||||
private bool _overlayStartingJob;
|
||||
private float _ghostFadeDistance;
|
||||
private float _ghostHideDistance;
|
||||
private int _overlayStackMax;
|
||||
private float _overlayMergeDistance;
|
||||
|
||||
//TODO make this adjustable via GUI
|
||||
private readonly ProtoId<RoleTypePrototype>[] _filter =
|
||||
["SoloAntagonist", "TeamAntagonist", "SiliconAntagonist", "FreeAgent"];
|
||||
private readonly string _antagLabelClassic = Loc.GetString("admin-overlay-antag-classic");
|
||||
private readonly Color _antagColorClassic = Color.OrangeRed;
|
||||
|
||||
public AdminNameOverlay(AdminSystem system, IEntityManager entityManager, IEyeManager eyeManager, IResourceCache resourceCache, EntityLookupSystem entityLookup, IUserInterfaceManager userInterfaceManager)
|
||||
public AdminNameOverlay(
|
||||
AdminSystem system,
|
||||
IEntityManager entityManager,
|
||||
IEyeManager eyeManager,
|
||||
IResourceCache resourceCache,
|
||||
EntityLookupSystem entityLookup,
|
||||
IUserInterfaceManager userInterfaceManager,
|
||||
IConfigurationManager config)
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
_system = system;
|
||||
_entityManager = entityManager;
|
||||
_eyeManager = eyeManager;
|
||||
_entityLookup = entityLookup;
|
||||
_userInterfaceManager = userInterfaceManager;
|
||||
ZIndex = 200;
|
||||
_font = new VectorFont(resourceCache.GetResource<FontResource>("/Fonts/NotoSans/NotoSans-Regular.ttf"), 10);
|
||||
// Setting these to a specific ttf would break the antag symbols
|
||||
_font = resourceCache.NotoStack();
|
||||
_fontBold = resourceCache.NotoStack(variation: "Bold");
|
||||
|
||||
config.OnValueChanged(CCVars.AdminOverlayClassic, (show) => { _overlayClassic = show; }, true);
|
||||
config.OnValueChanged(CCVars.AdminOverlaySymbols, (show) => { _overlaySymbols = show; }, true);
|
||||
config.OnValueChanged(CCVars.AdminOverlayPlaytime, (show) => { _overlayPlaytime = show; }, true);
|
||||
config.OnValueChanged(CCVars.AdminOverlayStartingJob, (show) => { _overlayStartingJob = show; }, true);
|
||||
config.OnValueChanged(CCVars.AdminOverlayGhostHideDistance, (f) => { _ghostHideDistance = f; }, true);
|
||||
config.OnValueChanged(CCVars.AdminOverlayGhostFadeDistance, (f) => { _ghostFadeDistance = f; }, true);
|
||||
config.OnValueChanged(CCVars.AdminOverlayStackMax, (i) => { _overlayStackMax = i; }, true);
|
||||
config.OnValueChanged(CCVars.AdminOverlayMergeDistance, (f) => { _overlayMergeDistance = f; }, true);
|
||||
}
|
||||
|
||||
public override OverlaySpace Space => OverlaySpace.ScreenSpace;
|
||||
|
|
@ -47,75 +72,147 @@ internal sealed class AdminNameOverlay : Overlay
|
|||
protected override void Draw(in OverlayDrawArgs args)
|
||||
{
|
||||
var viewport = args.WorldAABB;
|
||||
var colorDisconnected = Color.White;
|
||||
var uiScale = _userInterfaceManager.RootControl.UIScale;
|
||||
var lineoffset = new Vector2(0f, 14f) * uiScale;
|
||||
var drawnOverlays = new List<(Vector2,Vector2)>() ; // A saved list of the overlays already drawn
|
||||
|
||||
//TODO make this adjustable via GUI
|
||||
var classic = _config.GetCVar(CCVars.AdminOverlayClassic);
|
||||
var playTime = _config.GetCVar(CCVars.AdminOverlayPlaytime);
|
||||
var startingJob = _config.GetCVar(CCVars.AdminOverlayStartingJob);
|
||||
|
||||
foreach (var playerInfo in _system.PlayerList)
|
||||
// Get all player positions before drawing overlays, so they can be sorted before iteration
|
||||
var sortable = new List<(PlayerInfo, Box2, EntityUid, Vector2)>();
|
||||
foreach (var info in _system.PlayerList)
|
||||
{
|
||||
var entity = _entityManager.GetEntity(playerInfo.NetEntity);
|
||||
var entity = _entityManager.GetEntity(info.NetEntity);
|
||||
|
||||
// Otherwise the entity can not exist yet
|
||||
if (entity == null || !_entityManager.EntityExists(entity))
|
||||
{
|
||||
// If entity does not exist or is on a different map, skip
|
||||
if (entity == null
|
||||
|| !_entityManager.EntityExists(entity)
|
||||
|| _entityManager.GetComponent<TransformComponent>(entity.Value).MapID != args.MapId)
|
||||
continue;
|
||||
}
|
||||
|
||||
// if not on the same map, continue
|
||||
if (_entityManager.GetComponent<TransformComponent>(entity.Value).MapID != args.MapId)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var aabb = _entityLookup.GetWorldAABB(entity.Value);
|
||||
|
||||
// if not on screen, continue
|
||||
// if not on screen, skip
|
||||
if (!aabb.Intersects(in viewport))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var uiScale = _userInterfaceManager.RootControl.UIScale;
|
||||
var lineoffset = new Vector2(0f, 14f) * uiScale;
|
||||
var screenCoordinates = _eyeManager.WorldToScreen(aabb.Center +
|
||||
new Angle(-_eyeManager.CurrentEye.Rotation).RotateVec(
|
||||
aabb.TopRight - aabb.Center)) + new Vector2(1f, 7f);
|
||||
// Get on-screen coordinates of player
|
||||
var screenCoordinates = _eyeManager.WorldToScreen(aabb.Center).Rounded();
|
||||
|
||||
sortable.Add((info, aabb, entity.Value, screenCoordinates));
|
||||
}
|
||||
|
||||
// Draw overlays for visible players, starting from the top of the screen
|
||||
foreach (var info in sortable.OrderBy(s => s.Item4.Y).ToList())
|
||||
{
|
||||
var playerInfo = info.Item1;
|
||||
var aabb = info.Item2;
|
||||
var entity = info.Item3;
|
||||
var screenCoordinatesCenter = info.Item4;
|
||||
//the center position is kept separately, for simpler position comparison later
|
||||
var centerOffset = new Vector2(28f, -18f) * uiScale;
|
||||
var screenCoordinates = screenCoordinatesCenter + centerOffset;
|
||||
var alpha = 1f;
|
||||
|
||||
//TODO make a smarter system where the starting offset can be modified by the predicted position and size of already-drawn overlays/stacks?
|
||||
var currentOffset = Vector2.Zero;
|
||||
|
||||
args.ScreenHandle.DrawString(_font, screenCoordinates + currentOffset, playerInfo.CharacterName, uiScale, playerInfo.Connected ? Color.Aquamarine : Color.White);
|
||||
// Ghosts near the cursor are made transparent/invisible
|
||||
// TODO would be "cheaper" if playerinfo already contained a ghost bool, this gets called every frame for every onscreen player!
|
||||
if (_entityManager.HasComponent<GhostComponent>(entity))
|
||||
{
|
||||
// We want the map positions here, so we don't have to worry about resolution and such shenanigans
|
||||
var mobPosition = aabb.Center;
|
||||
var mousePosition = _eyeManager
|
||||
.ScreenToMap(_userInterfaceManager.MousePositionScaled.Position * uiScale)
|
||||
.Position;
|
||||
var dist = Vector2.Distance(mobPosition, mousePosition);
|
||||
if (dist < _ghostHideDistance)
|
||||
continue;
|
||||
|
||||
alpha = Math.Clamp((dist - _ghostHideDistance) / (_ghostFadeDistance - _ghostHideDistance), 0f, 1f);
|
||||
colorDisconnected.A = alpha;
|
||||
}
|
||||
|
||||
// If the new overlay text block is within merge distance of any previous ones
|
||||
// merge them into a stack so they don't hide each other
|
||||
var stack = drawnOverlays.FindAll(x =>
|
||||
Vector2.Distance(_eyeManager.ScreenToMap(x.Item1).Position, aabb.Center) <= _overlayMergeDistance);
|
||||
if (stack.Count > 0)
|
||||
{
|
||||
screenCoordinates = stack.First().Item1 + centerOffset;
|
||||
// Replacing this overlay's coordinates for the later save with the stack root's coordinates
|
||||
// so that other overlays don't try to stack to these coordinates
|
||||
screenCoordinatesCenter = stack.First().Item1;
|
||||
|
||||
var i = 1;
|
||||
foreach (var s in stack)
|
||||
{
|
||||
// additional entries after maximum stack size is reached will be drawn over the last entry
|
||||
if (i <= _overlayStackMax - 1)
|
||||
currentOffset = lineoffset + s.Item2 ;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
// Character name
|
||||
var color = Color.Aquamarine;
|
||||
color.A = alpha;
|
||||
args.ScreenHandle.DrawString(_font, screenCoordinates + currentOffset, playerInfo.CharacterName, uiScale, playerInfo.Connected ? color : colorDisconnected);
|
||||
currentOffset += lineoffset;
|
||||
|
||||
args.ScreenHandle.DrawString(_font, screenCoordinates + currentOffset, playerInfo.Username, uiScale, playerInfo.Connected ? Color.Yellow : Color.White);
|
||||
// Username
|
||||
color = Color.Yellow;
|
||||
color.A = alpha;
|
||||
args.ScreenHandle.DrawString(_font, screenCoordinates + currentOffset, playerInfo.Username, uiScale, playerInfo.Connected ? color : colorDisconnected);
|
||||
currentOffset += lineoffset;
|
||||
|
||||
if (!string.IsNullOrEmpty(playerInfo.PlaytimeString) && playTime)
|
||||
// Playtime
|
||||
if (!string.IsNullOrEmpty(playerInfo.PlaytimeString) && _overlayPlaytime)
|
||||
{
|
||||
args.ScreenHandle.DrawString(_font, screenCoordinates + currentOffset, playerInfo.PlaytimeString, uiScale, playerInfo.Connected ? Color.Orange : Color.White);
|
||||
color = Color.Orange;
|
||||
color.A = alpha;
|
||||
args.ScreenHandle.DrawString(_font, screenCoordinates + currentOffset, playerInfo.PlaytimeString, uiScale, playerInfo.Connected ? color : colorDisconnected);
|
||||
currentOffset += lineoffset;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(playerInfo.StartingJob) && startingJob)
|
||||
// Job
|
||||
if (!string.IsNullOrEmpty(playerInfo.StartingJob) && _overlayStartingJob)
|
||||
{
|
||||
args.ScreenHandle.DrawString(_font, screenCoordinates + currentOffset, Loc.GetString(playerInfo.StartingJob), uiScale, playerInfo.Connected ? Color.GreenYellow : Color.White);
|
||||
color = Color.GreenYellow;
|
||||
color.A = alpha;
|
||||
args.ScreenHandle.DrawString(_font, screenCoordinates + currentOffset, Loc.GetString(playerInfo.StartingJob), uiScale, playerInfo.Connected ? color : colorDisconnected);
|
||||
currentOffset += lineoffset;
|
||||
}
|
||||
|
||||
if (classic && playerInfo.Antag)
|
||||
// Classic Antag Label
|
||||
if (_overlayClassic && playerInfo.Antag)
|
||||
{
|
||||
args.ScreenHandle.DrawString(_font, screenCoordinates + currentOffset, _antagLabelClassic, uiScale, Color.OrangeRed);
|
||||
var symbol = _overlaySymbols ? Loc.GetString("player-tab-antag-prefix") : string.Empty;
|
||||
var label = _overlaySymbols
|
||||
? Loc.GetString("player-tab-character-name-antag-symbol",
|
||||
("symbol", symbol),
|
||||
("name", _antagLabelClassic))
|
||||
: _antagLabelClassic;
|
||||
color = Color.OrangeRed;
|
||||
color.A = alpha;
|
||||
args.ScreenHandle.DrawString(_fontBold, screenCoordinates + currentOffset, label, uiScale, color);
|
||||
currentOffset += lineoffset;
|
||||
}
|
||||
else if (!classic && _filter.Contains(playerInfo.RoleProto))
|
||||
// Role Type
|
||||
else if (!_overlayClassic && _filter.Contains(playerInfo.RoleProto))
|
||||
{
|
||||
var label = Loc.GetString(playerInfo.RoleProto.Name).ToUpper();
|
||||
var color = playerInfo.RoleProto.Color;
|
||||
var symbol = _overlaySymbols && playerInfo.Antag ? playerInfo.RoleProto.Symbol : string.Empty;
|
||||
var role = Loc.GetString(playerInfo.RoleProto.Name).ToUpper();
|
||||
var label = _overlaySymbols
|
||||
? Loc.GetString("player-tab-character-name-antag-symbol", ("symbol", symbol), ("name", role))
|
||||
: role;
|
||||
color = playerInfo.RoleProto.Color;
|
||||
color.A = alpha;
|
||||
args.ScreenHandle.DrawString(_fontBold, screenCoordinates + currentOffset, label, uiScale, color);
|
||||
currentOffset += lineoffset;
|
||||
}
|
||||
|
||||
args.ScreenHandle.DrawString(_font, screenCoordinates + currentOffset, label, uiScale, color);
|
||||
currentOffset += lineoffset;
|
||||
}
|
||||
//Save the coordinates and size of the text block, for stack merge check
|
||||
drawnOverlays.Add((screenCoordinatesCenter, currentOffset));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ namespace Content.Client.Administration.Systems
|
|||
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
||||
[Dependency] private readonly EntityLookupSystem _entityLookup = default!;
|
||||
[Dependency] private readonly IUserInterfaceManager _userInterfaceManager = default!;
|
||||
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
|
||||
|
||||
private AdminNameOverlay _adminNameOverlay = default!;
|
||||
|
||||
|
|
@ -22,7 +23,14 @@ namespace Content.Client.Administration.Systems
|
|||
|
||||
private void InitializeOverlay()
|
||||
{
|
||||
_adminNameOverlay = new AdminNameOverlay(this, EntityManager, _eyeManager, _resourceCache, _entityLookup, _userInterfaceManager);
|
||||
_adminNameOverlay = new AdminNameOverlay(
|
||||
this,
|
||||
EntityManager,
|
||||
_eyeManager,
|
||||
_resourceCache,
|
||||
_entityLookup,
|
||||
_userInterfaceManager,
|
||||
_configurationManager);
|
||||
_adminManager.AdminStatusUpdated += OnAdminStatusUpdated;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,11 +4,13 @@ using Content.Client.Administration.Systems;
|
|||
using Content.Client.Administration.UI.AntagObjectives;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.CCVar;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Configuration;
|
||||
using static Content.Client.Administration.UI.Tabs.PlayerTab.PlayerTabHeader;
|
||||
using static Robust.Client.UserInterface.Controls.BaseButton;
|
||||
|
||||
|
|
@ -18,6 +20,7 @@ namespace Content.Client.Administration.UI.Tabs.PlayerTab;
|
|||
public sealed partial class PlayerTab : Control
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||
[Dependency] private readonly IConfigurationManager _config = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerMan = default!;
|
||||
|
||||
private const string ArrowUp = "↑";
|
||||
|
|
@ -45,6 +48,10 @@ public sealed partial class PlayerTab : Control
|
|||
_adminSystem.OverlayEnabled += OverlayEnabled;
|
||||
_adminSystem.OverlayDisabled += OverlayDisabled;
|
||||
|
||||
_config.OnValueChanged(CCVars.AdminPlayerlistSeparateSymbols, PlayerListSettingsChanged);
|
||||
_config.OnValueChanged(CCVars.AdminPlayerlistHighlightedCharacterColor, PlayerListSettingsChanged);
|
||||
_config.OnValueChanged(CCVars.AdminPlayerlistRoleTypeColor, PlayerListSettingsChanged);
|
||||
|
||||
OverlayButton.OnPressed += OverlayButtonPressed;
|
||||
ShowDisconnectedButton.OnPressed += ShowDisconnectedPressed;
|
||||
|
||||
|
|
@ -111,6 +118,11 @@ public sealed partial class PlayerTab : Control
|
|||
|
||||
#region ListContainer
|
||||
|
||||
private void PlayerListSettingsChanged(bool _)
|
||||
{
|
||||
RefreshPlayerList(_adminSystem.PlayerList);
|
||||
}
|
||||
|
||||
private void RefreshPlayerList(IReadOnlyList<PlayerInfo> players)
|
||||
{
|
||||
_players = players;
|
||||
|
|
@ -233,8 +245,7 @@ public sealed partial class PlayerTab : Control
|
|||
Header.Character => Compare(x.CharacterName, y.CharacterName),
|
||||
Header.Job => Compare(x.StartingJob, y.StartingJob),
|
||||
Header.Sponsor => string.Compare(x.SponsorTitle!, y.SponsorTitle, StringComparison.Ordinal), // Sunrise-Sponsors
|
||||
Header.Antagonist => x.Antag.CompareTo(y.Antag),
|
||||
Header.RoleType => Compare(x.RoleProto.Name , y.RoleProto.Name),
|
||||
Header.RoleType => y.SortWeight - x.SortWeight,
|
||||
Header.Playtime => TimeSpan.Compare(x.OverallPlaytime ?? default, y.OverallPlaytime ?? default),
|
||||
_ => 1
|
||||
};
|
||||
|
|
|
|||
|
|
@ -26,11 +26,6 @@
|
|||
ClipText="True"/>
|
||||
<customControls:VSeparator/>
|
||||
<!-- Sunrise-Sponsors-End -->
|
||||
<Label Name="AntagonistLabel"
|
||||
SizeFlagsStretchRatio="1"
|
||||
HorizontalExpand="True"
|
||||
ClipText="True"/>
|
||||
<customControls:VSeparator/>
|
||||
<Label Name="RoleTypeLabel"
|
||||
SizeFlagsStretchRatio="2"
|
||||
HorizontalExpand="True"
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
using Content.Shared.Administration;
|
||||
using Content.Shared.CCVar;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Configuration;
|
||||
|
||||
namespace Content.Client.Administration.UI.Tabs.PlayerTab;
|
||||
|
||||
|
|
@ -16,18 +18,26 @@ public sealed partial class PlayerTabEntry : PanelContainer
|
|||
public PlayerTabEntry(PlayerInfo player, StyleBoxFlat styleBoxFlat)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
var config = IoCManager.Resolve<IConfigurationManager>();
|
||||
|
||||
UsernameLabel.Text = player.Username;
|
||||
if (!player.Connected)
|
||||
UsernameLabel.StyleClasses.Add("Disabled");
|
||||
JobLabel.Text = player.StartingJob;
|
||||
CharacterLabel.Text = player.CharacterName;
|
||||
var separateAntagSymbols = config.GetCVar(CCVars.AdminPlayerlistSeparateSymbols);
|
||||
var genericAntagSymbol = player.Antag ? Loc.GetString("player-tab-antag-prefix") : string.Empty;
|
||||
var roleSymbol = player.Antag ? player.RoleProto.Symbol : string.Empty;
|
||||
var symbol = separateAntagSymbols ? roleSymbol : genericAntagSymbol;
|
||||
CharacterLabel.Text = Loc.GetString("player-tab-character-name-antag-symbol", ("symbol", symbol), ("name", player.CharacterName));
|
||||
|
||||
if (player.Antag && config.GetCVar(CCVars.AdminPlayerlistHighlightedCharacterColor))
|
||||
CharacterLabel.FontColorOverride = player.RoleProto.Color;
|
||||
if (player.IdentityName != player.CharacterName)
|
||||
CharacterLabel.Text += $" [{player.IdentityName}]";
|
||||
SponsorLabel.Text = player.IsSponsor ? player.SponsorTitle : ""; // Sunrise-Sponsors
|
||||
AntagonistLabel.Text = Loc.GetString(player.Antag ? "player-tab-is-antag-yes" : "player-tab-is-antag-no");
|
||||
RoleTypeLabel.Text = Loc.GetString(player.RoleProto.Name);
|
||||
RoleTypeLabel.FontColorOverride = player.RoleProto.Color;
|
||||
if (config.GetCVar(CCVars.AdminPlayerlistRoleTypeColor))
|
||||
RoleTypeLabel.FontColorOverride = player.RoleProto.Color;
|
||||
BackgroundColorPanel.PanelOverride = styleBoxFlat;
|
||||
OverallPlaytimeLabel.Text = player.PlaytimeString;
|
||||
PlayerEntity = player.NetEntity;
|
||||
|
|
|
|||
|
|
@ -34,13 +34,6 @@
|
|||
MouseFilter="Pass"/>
|
||||
<cc:VSeparator/>
|
||||
<!-- Sunrise-Sponsors-End -->
|
||||
<Label Name="AntagonistLabel"
|
||||
SizeFlagsStretchRatio="1"
|
||||
HorizontalExpand="True"
|
||||
ClipText="True"
|
||||
Text="{Loc player-tab-antagonist}"
|
||||
MouseFilter="Pass"/>
|
||||
<cc:VSeparator/>
|
||||
<Label Name="RoleTypeLabel"
|
||||
SizeFlagsStretchRatio="2"
|
||||
HorizontalExpand="True"
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ public sealed partial class PlayerTabHeader : Control
|
|||
CharacterLabel.OnKeyBindDown += CharacterClicked;
|
||||
JobLabel.OnKeyBindDown += JobClicked;
|
||||
SponsorLabel.OnKeyBindDown += SponsorClicked; // Sunrise-Sponsors
|
||||
AntagonistLabel.OnKeyBindDown += AntagonistClicked;
|
||||
RoleTypeLabel.OnKeyBindDown += RoleTypeClicked;
|
||||
PlaytimeLabel.OnKeyBindDown += PlaytimeClicked;
|
||||
}
|
||||
|
|
@ -32,7 +31,6 @@ public sealed partial class PlayerTabHeader : Control
|
|||
Header.Character => CharacterLabel,
|
||||
Header.Job => JobLabel,
|
||||
Header.Sponsor => SponsorLabel, // Sunrise-Sponsors
|
||||
Header.Antagonist => AntagonistLabel,
|
||||
Header.RoleType => RoleTypeLabel,
|
||||
Header.Playtime => PlaytimeLabel,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(header), header, null)
|
||||
|
|
@ -45,7 +43,6 @@ public sealed partial class PlayerTabHeader : Control
|
|||
CharacterLabel.Text = Loc.GetString("player-tab-character");
|
||||
JobLabel.Text = Loc.GetString("player-tab-job");
|
||||
SponsorLabel.Text = Loc.GetString("player-tab-sponsor"); // Sunrise-Sponsors
|
||||
AntagonistLabel.Text = Loc.GetString("player-tab-antagonist");
|
||||
RoleTypeLabel.Text = Loc.GetString("player-tab-roletype");
|
||||
PlaytimeLabel.Text = Loc.GetString("player-tab-playtime");
|
||||
}
|
||||
|
|
@ -76,11 +73,6 @@ public sealed partial class PlayerTabHeader : Control
|
|||
HeaderClicked(args, Header.Job);
|
||||
}
|
||||
|
||||
private void AntagonistClicked(GUIBoundKeyEventArgs args)
|
||||
{
|
||||
HeaderClicked(args, Header.Antagonist);
|
||||
}
|
||||
|
||||
private void RoleTypeClicked(GUIBoundKeyEventArgs args)
|
||||
{
|
||||
HeaderClicked(args, Header.RoleType);
|
||||
|
|
@ -107,7 +99,6 @@ public sealed partial class PlayerTabHeader : Control
|
|||
UsernameLabel.OnKeyBindDown -= UsernameClicked;
|
||||
CharacterLabel.OnKeyBindDown -= CharacterClicked;
|
||||
JobLabel.OnKeyBindDown -= JobClicked;
|
||||
AntagonistLabel.OnKeyBindDown -= AntagonistClicked;
|
||||
RoleTypeLabel.OnKeyBindDown -= RoleTypeClicked;
|
||||
PlaytimeLabel.OnKeyBindDown -= PlaytimeClicked;
|
||||
}
|
||||
|
|
@ -119,7 +110,6 @@ public sealed partial class PlayerTabHeader : Control
|
|||
Character,
|
||||
Job,
|
||||
Sponsor, // Sunrise-Sponsors
|
||||
Antagonist,
|
||||
RoleType,
|
||||
Playtime
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,20 @@
|
|||
<BoxContainer Orientation="Vertical">
|
||||
<ScrollContainer VerticalExpand="True" HScrollEnabled="False">
|
||||
<BoxContainer Orientation="Vertical" Margin="8">
|
||||
<Label Text="{Loc 'ui-options-admin-player-panel'}"
|
||||
StyleClasses="LabelKeyText"/>
|
||||
<CheckBox Name="PlayerlistSeparateSymbolsCheckBox" Text="{Loc 'ui-options-admin-playerlist-separate-symbols'}" />
|
||||
<CheckBox Name="PlayerlistCharacterColorCheckBox" Text="{Loc 'ui-options-admin-playerlist-character-color'}" />
|
||||
<CheckBox Name="PlayerlistRoleTypeColorCheckBox" Text="{Loc 'ui-options-admin-playerlist-roletype-color'}" />
|
||||
<Label Text="{Loc 'ui-options-admin-overlay-title'}"
|
||||
StyleClasses="LabelKeyText"/>
|
||||
<CheckBox Name="EnableClassicOverlayCheckBox" Text="{Loc 'ui-options-enable-classic-overlay'}" />
|
||||
<CheckBox Name="EnableOverlaySymbolsCheckBox" Text="{Loc 'ui-options-enable-overlay-symbols'}" />
|
||||
<CheckBox Name="EnableOverlayPlaytimeCheckBox" Text="{Loc 'ui-options-enable-overlay-playtime'}" />
|
||||
<CheckBox Name="EnableOverlayStartingJobCheckBox" Text="{Loc 'ui-options-enable-overlay-starting-job'}" />
|
||||
<ui:OptionSlider Name="OverlayMergeDistanceSlider" Title="{Loc 'ui-options-overlay-merge-distance'}"/>
|
||||
<ui:OptionSlider Name="OverlayGhostFadeSlider" Title="{Loc 'ui-options-overlay-ghost-fade-distance'}"/>
|
||||
<ui:OptionSlider Name="OverlayGhostHideSlider" Title="{Loc 'ui-options-overlay-ghost-hide-distance'}"/>
|
||||
</BoxContainer>
|
||||
</ScrollContainer>
|
||||
<ui:OptionsTabControlRow Name="Control" Access="Public" />
|
||||
|
|
|
|||
|
|
@ -8,13 +8,45 @@ namespace Content.Client.Options.UI.Tabs;
|
|||
[GenerateTypedNameReferences]
|
||||
public sealed partial class AdminOptionsTab : Control
|
||||
{
|
||||
private const float OverlayMergeMin = 0.05f;
|
||||
private const float OverlayMergeMax = 0.95f;
|
||||
private const int OverlayGhostFadeMin = 0;
|
||||
private const int OverlayGhostFadeMax = 10;
|
||||
private const int OverlayGhostHideMin = 0;
|
||||
private const int OverlayGhostHideMax = 5;
|
||||
|
||||
public AdminOptionsTab()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
|
||||
Control.AddOptionCheckBox(CCVars.AdminPlayerlistSeparateSymbols, PlayerlistSeparateSymbolsCheckBox);
|
||||
Control.AddOptionCheckBox(CCVars.AdminPlayerlistHighlightedCharacterColor, PlayerlistCharacterColorCheckBox);
|
||||
Control.AddOptionCheckBox(CCVars.AdminPlayerlistRoleTypeColor, PlayerlistRoleTypeColorCheckBox);
|
||||
|
||||
Control.AddOptionCheckBox(CCVars.AdminOverlayClassic, EnableClassicOverlayCheckBox);
|
||||
Control.AddOptionCheckBox(CCVars.AdminOverlaySymbols, EnableOverlaySymbolsCheckBox);
|
||||
Control.AddOptionCheckBox(CCVars.AdminOverlayPlaytime, EnableOverlayPlaytimeCheckBox);
|
||||
Control.AddOptionCheckBox(CCVars.AdminOverlayStartingJob, EnableOverlayStartingJobCheckBox);
|
||||
|
||||
Control.Initialize();
|
||||
|
||||
Control.AddOptionPercentSlider(
|
||||
CCVars.AdminOverlayMergeDistance,
|
||||
OverlayMergeDistanceSlider,
|
||||
OverlayMergeMin,
|
||||
OverlayMergeMax);
|
||||
|
||||
Control.AddOptionSlider(
|
||||
CCVars.AdminOverlayGhostFadeDistance,
|
||||
OverlayGhostFadeSlider,
|
||||
OverlayGhostFadeMin,
|
||||
OverlayGhostFadeMax);
|
||||
|
||||
Control.AddOptionSlider(
|
||||
CCVars.AdminOverlayGhostHideDistance,
|
||||
OverlayGhostHideSlider,
|
||||
OverlayGhostHideMin,
|
||||
OverlayGhostHideMax);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,13 +11,21 @@ public sealed class PaperVisualizerSystem : VisualizerSystem<PaperVisualsCompone
|
|||
if (args.Sprite == null)
|
||||
return;
|
||||
|
||||
if (AppearanceSystem.TryGetData<PaperStatus>(uid, PaperVisuals.Status , out var writingStatus, args.Component))
|
||||
if (AppearanceSystem.TryGetData<PaperStatus>(uid, PaperVisuals.Status, out var writingStatus, args.Component))
|
||||
args.Sprite.LayerSetVisible(PaperVisualLayers.Writing, writingStatus == PaperStatus.Written);
|
||||
|
||||
if (AppearanceSystem.TryGetData<string>(uid, PaperVisuals.Stamp, out var stampState, args.Component))
|
||||
{
|
||||
args.Sprite.LayerSetState(PaperVisualLayers.Stamp, stampState);
|
||||
args.Sprite.LayerSetVisible(PaperVisualLayers.Stamp, true);
|
||||
if (stampState != string.Empty)
|
||||
{
|
||||
args.Sprite.LayerSetState(PaperVisualLayers.Stamp, stampState);
|
||||
args.Sprite.LayerSetVisible(PaperVisualLayers.Stamp, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
args.Sprite.LayerSetVisible(PaperVisualLayers.Stamp, false);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -244,6 +244,12 @@ namespace Content.Client.Popups
|
|||
PopupEntity(message, uid, recipient.Value, type);
|
||||
}
|
||||
|
||||
public override void PopupPredicted(string? message, EntityUid uid, EntityUid? recipient, Filter filter, bool recordReplay, PopupType type = PopupType.Small)
|
||||
{
|
||||
if (recipient != null && _timing.IsFirstTimePredicted)
|
||||
PopupEntity(message, uid, recipient.Value, type);
|
||||
}
|
||||
|
||||
public override void PopupPredicted(string? recipientMessage, string? othersMessage, EntityUid uid, EntityUid? recipient, PopupType type = PopupType.Small)
|
||||
{
|
||||
if (recipient != null && _timing.IsFirstTimePredicted)
|
||||
|
|
|
|||
|
|
@ -1,19 +1,19 @@
|
|||
using Content.Server.Administration.Notes;
|
||||
using System.Linq;
|
||||
using Content.Server.Administration.Notes;
|
||||
using Content.Shared.Administration;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Console;
|
||||
|
||||
namespace Content.Server.Administration.Commands;
|
||||
|
||||
[AdminCommand(AdminFlags.ViewNotes)]
|
||||
public sealed class OpenAdminNotesCommand : IConsoleCommand
|
||||
public sealed class OpenAdminNotesCommand : LocalizedCommands
|
||||
{
|
||||
public const string CommandName = "adminnotes";
|
||||
|
||||
public string Command => CommandName;
|
||||
public string Description => "Opens the admin notes panel.";
|
||||
public string Help => $"Usage: {Command} <notedPlayerUserId OR notedPlayerUsername>";
|
||||
public override string Command => CommandName;
|
||||
|
||||
public async void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
public override async void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
if (shell.Player is not { } player)
|
||||
{
|
||||
|
|
@ -33,17 +33,27 @@ public sealed class OpenAdminNotesCommand : IConsoleCommand
|
|||
|
||||
if (dbGuid == null)
|
||||
{
|
||||
shell.WriteError($"Unable to find {args[0]} netuserid");
|
||||
shell.WriteError(Loc.GetString("cmd-adminnotes-wrong-target", ("user", args[0])));
|
||||
return;
|
||||
}
|
||||
|
||||
notedPlayer = dbGuid.UserId;
|
||||
break;
|
||||
default:
|
||||
shell.WriteError($"Invalid arguments.\n{Help}");
|
||||
shell.WriteError(Loc.GetString("cmd-adminnotes-args-error"));
|
||||
return;
|
||||
}
|
||||
|
||||
await IoCManager.Resolve<IAdminNotesManager>().OpenEui(player, notedPlayer);
|
||||
}
|
||||
|
||||
public override CompletionResult GetCompletion(IConsoleShell shell, string[] args)
|
||||
{
|
||||
if (args.Length != 1)
|
||||
return CompletionResult.Empty;
|
||||
|
||||
var playerMgr = IoCManager.Resolve<IPlayerManager>();
|
||||
var options = playerMgr.Sessions.Select(c => c.Name).OrderBy(c => c).ToArray();
|
||||
return CompletionResult.FromHintOptions(options, Loc.GetString("cmd-adminnotes-hint"));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ namespace Content.Server.Administration.Commands
|
|||
}
|
||||
else
|
||||
{
|
||||
if (player.Status != SessionStatus.InGame || player.AttachedEntity is not {Valid: true} playerEntity)
|
||||
if (player.Status != SessionStatus.InGame || player.AttachedEntity is not { Valid: true } playerEntity)
|
||||
{
|
||||
shell.WriteLine("You are not in-game!");
|
||||
return;
|
||||
|
|
@ -57,14 +57,16 @@ namespace Content.Server.Administration.Commands
|
|||
var currentMap = _entManager.GetComponent<TransformComponent>(playerEntity).MapID;
|
||||
var currentGrid = _entManager.GetComponent<TransformComponent>(playerEntity).GridUid;
|
||||
|
||||
var xformSystem = _entManager.System<SharedTransformSystem>();
|
||||
|
||||
var found = GetWarpPointByName(location)
|
||||
.OrderBy(p => p.Item1, Comparer<EntityCoordinates>.Create((a, b) =>
|
||||
{
|
||||
// Sort so that warp points on the same grid/map are first.
|
||||
// So if you have two maps loaded with the same warp points,
|
||||
// it will prefer the warp points on the map you're currently on.
|
||||
var aGrid = a.GetGridUid(_entManager);
|
||||
var bGrid = b.GetGridUid(_entManager);
|
||||
var aGrid = xformSystem.GetGrid(a);
|
||||
var bGrid = xformSystem.GetGrid(b);
|
||||
|
||||
if (aGrid == bGrid)
|
||||
{
|
||||
|
|
@ -81,8 +83,8 @@ namespace Content.Server.Administration.Commands
|
|||
return 1;
|
||||
}
|
||||
|
||||
var mapA = a.GetMapId(_entManager);
|
||||
var mapB = a.GetMapId(_entManager);
|
||||
var mapA = xformSystem.GetMapId(a);
|
||||
var mapB = xformSystem.GetMapId(b);
|
||||
|
||||
if (mapA == mapB)
|
||||
{
|
||||
|
|
@ -117,10 +119,8 @@ namespace Content.Server.Administration.Commands
|
|||
return;
|
||||
}
|
||||
|
||||
var xform = _entManager.GetComponent<TransformComponent>(playerEntity);
|
||||
var xformSystem = _entManager.System<SharedTransformSystem>();
|
||||
xform.Coordinates = coords;
|
||||
xformSystem.AttachToGridOrMap(playerEntity, xform);
|
||||
xformSystem.SetCoordinates(playerEntity, coords);
|
||||
xformSystem.AttachToGridOrMap(playerEntity);
|
||||
if (_entManager.TryGetComponent(playerEntity, out PhysicsComponent? physics))
|
||||
{
|
||||
_entManager.System<SharedPhysicsSystem>().SetLinearVelocity(playerEntity, Vector2.Zero, body: physics);
|
||||
|
|
|
|||
|
|
@ -226,6 +226,7 @@ public sealed class AdminSystem : EntitySystem
|
|||
var name = data.UserName;
|
||||
var entityName = string.Empty;
|
||||
var identityName = string.Empty;
|
||||
var sortWeight = 0;
|
||||
|
||||
// Visible (identity) name can be different from real name
|
||||
if (session?.AttachedEntity != null)
|
||||
|
|
@ -239,8 +240,10 @@ public sealed class AdminSystem : EntitySystem
|
|||
// Starting role, antagonist status and role type
|
||||
RoleTypePrototype roleType = new();
|
||||
var startingRole = string.Empty;
|
||||
if (_minds.TryGetMind(session, out var mindId, out var mindComp))
|
||||
if (_minds.TryGetMind(session, out var mindId, out var mindComp) && mindComp is not null)
|
||||
{
|
||||
sortWeight = _role.GetRoleCompByTime(mindComp)?.Comp.SortWeight ?? 0;
|
||||
|
||||
if (_proto.TryIndex(mindComp.RoleType, out var role))
|
||||
roleType = role;
|
||||
else
|
||||
|
|
@ -274,8 +277,21 @@ public sealed class AdminSystem : EntitySystem
|
|||
}
|
||||
// Sunrise-Sponsors-End
|
||||
|
||||
return new PlayerInfo(name, entityName, identityName, startingRole, antag, roleType, GetNetEntity(session?.AttachedEntity), data.UserId,
|
||||
connected, _roundActivePlayers.Contains(data.UserId), overallPlaytime, isSponsor, sponsorTitle); // Sunrise-Sponsors
|
||||
return new PlayerInfo(
|
||||
name,
|
||||
entityName,
|
||||
identityName,
|
||||
startingRole,
|
||||
antag,
|
||||
roleType,
|
||||
sortWeight,
|
||||
GetNetEntity(session?.AttachedEntity),
|
||||
data.UserId,
|
||||
connected,
|
||||
_roundActivePlayers.Contains(data.UserId),
|
||||
overallPlaytime,
|
||||
isSponsor,
|
||||
sponsorTitle);
|
||||
}
|
||||
|
||||
private void OnPanicBunkerChanged(bool enabled)
|
||||
|
|
|
|||
|
|
@ -2,10 +2,12 @@ using Content.Server._Sunrise.AssaultOps;
|
|||
using Content.Server._Sunrise.FleshCult.GameRule;
|
||||
using Content.Server.Administration.Commands;
|
||||
using Content.Server.Antag;
|
||||
using Content.Server.GameTicking;
|
||||
using Content.Server.GameTicking.Rules.Components;
|
||||
using Content.Server.Zombies;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Humanoid;
|
||||
using Content.Shared.Mind.Components;
|
||||
using Content.Shared.Roles;
|
||||
using Content.Shared.Verbs;
|
||||
|
|
@ -19,6 +21,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
[Dependency] private readonly AntagSelectionSystem _antag = default!;
|
||||
[Dependency] private readonly ZombieSystem _zombie = default!;
|
||||
[Dependency] private readonly GameTicker _gameTicker = default!;
|
||||
|
||||
[ValidatePrototypeId<EntityPrototype>]
|
||||
private const string DefaultTraitorRule = "Traitor";
|
||||
|
|
@ -50,6 +53,8 @@ public sealed partial class AdminVerbSystem
|
|||
[ValidatePrototypeId<StartingGearPrototype>]
|
||||
private const string PirateGearId = "PirateGear";
|
||||
|
||||
private readonly EntProtoId _paradoxCloneRuleId = "ParadoxCloneSpawn";
|
||||
|
||||
// All antag verbs have names so invokeverb works.
|
||||
private void AddAntagVerbs(GetVerbsEvent<Verb> args)
|
||||
{
|
||||
|
|
@ -172,6 +177,30 @@ public sealed partial class AdminVerbSystem
|
|||
};
|
||||
args.Verbs.Add(thief);
|
||||
|
||||
var paradoxCloneName = Loc.GetString("admin-verb-text-make-paradox-clone");
|
||||
Verb paradox = new()
|
||||
{
|
||||
Text = paradoxCloneName,
|
||||
Category = VerbCategory.Antag,
|
||||
Icon = new SpriteSpecifier.Rsi(new("/Textures/Interface/Misc/job_icons.rsi"), "ParadoxClone"),
|
||||
Act = () =>
|
||||
{
|
||||
var ruleEnt = _gameTicker.AddGameRule(_paradoxCloneRuleId);
|
||||
|
||||
if (!TryComp<ParadoxCloneRuleComponent>(ruleEnt, out var paradoxCloneRuleComp))
|
||||
return;
|
||||
|
||||
paradoxCloneRuleComp.OriginalBody = args.Target; // override the target player
|
||||
|
||||
_gameTicker.StartGameRule(ruleEnt);
|
||||
},
|
||||
Impact = LogImpact.High,
|
||||
Message = string.Join(": ", paradoxCloneName, Loc.GetString("admin-verb-make-paradox-clone")),
|
||||
};
|
||||
|
||||
if (HasComp<HumanoidAppearanceComponent>(args.Target)) // only humanoids can be cloned
|
||||
args.Verbs.Add(paradox);
|
||||
|
||||
Verb ling = new()
|
||||
{
|
||||
Text = Loc.GetString("admin-verb-text-make-changeling"),
|
||||
|
|
|
|||
|
|
@ -153,7 +153,7 @@ public sealed partial class AdminVerbSystem
|
|||
PopupType.MediumCaution);
|
||||
var board = Spawn("ChessBoard", xform.Coordinates);
|
||||
var session = _tabletopSystem.EnsureSession(Comp<TabletopGameComponent>(board));
|
||||
xform.Coordinates = _transformSystem.ToCoordinates(session.Position);
|
||||
_transformSystem.SetMapCoordinates(args.Target, session.Position);
|
||||
_transformSystem.SetWorldRotationNoLerp((args.Target, xform), Angle.Zero);
|
||||
},
|
||||
Impact = LogImpact.Extreme,
|
||||
|
|
@ -481,7 +481,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
var xform = Transform(args.Target);
|
||||
var fixtures = Comp<FixturesComponent>(args.Target);
|
||||
xform.Anchored = false; // Just in case.
|
||||
_transformSystem.Unanchor(args.Target); // Just in case.
|
||||
_physics.SetBodyType(args.Target, BodyType.Dynamic, manager: fixtures, body: physics);
|
||||
_physics.SetBodyStatus(args.Target, physics, BodyStatus.InAir);
|
||||
_physics.WakeBody(args.Target, manager: fixtures, body: physics);
|
||||
|
|
@ -519,7 +519,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
var xform = Transform(args.Target);
|
||||
var fixtures = Comp<FixturesComponent>(args.Target);
|
||||
xform.Anchored = false; // Just in case.
|
||||
_transformSystem.Unanchor(args.Target); // Just in case.
|
||||
|
||||
_physics.SetBodyType(args.Target, BodyType.Dynamic, body: physics);
|
||||
_physics.SetBodyStatus(args.Target, physics, BodyStatus.InAir);
|
||||
|
|
|
|||
|
|
@ -637,7 +637,7 @@ public sealed partial class AdminVerbSystem
|
|||
{
|
||||
if (_adminManager.HasAdminFlag(player, AdminFlags.Mapping))
|
||||
{
|
||||
if (_mapManager.IsMapPaused(map.MapId))
|
||||
if (_map.IsPaused(map.MapId))
|
||||
{
|
||||
Verb unpauseMap = new()
|
||||
{
|
||||
|
|
@ -646,7 +646,7 @@ public sealed partial class AdminVerbSystem
|
|||
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/play.png")),
|
||||
Act = () =>
|
||||
{
|
||||
_mapManager.SetMapPaused(map.MapId, false);
|
||||
_map.SetPaused(map.MapId, false);
|
||||
},
|
||||
Impact = LogImpact.Extreme,
|
||||
Message = Loc.GetString("admin-trick-unpause-map-description"),
|
||||
|
|
@ -663,7 +663,7 @@ public sealed partial class AdminVerbSystem
|
|||
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/pause.png")),
|
||||
Act = () =>
|
||||
{
|
||||
_mapManager.SetMapPaused(map.MapId, true);
|
||||
_map.SetPaused(map.MapId, true);
|
||||
},
|
||||
Impact = LogImpact.Extreme,
|
||||
Message = Loc.GetString("admin-trick-pause-map-description"),
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ namespace Content.Server.Administration.Systems
|
|||
[Dependency] private readonly IConsoleHost _console = default!;
|
||||
[Dependency] private readonly IAdminManager _adminManager = default!;
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] private readonly SharedMapSystem _map = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly AdminSystem _adminSystem = default!;
|
||||
[Dependency] private readonly DisposalTubeSystem _disposalTubes = default!;
|
||||
|
|
@ -153,12 +153,10 @@ namespace Content.Server.Administration.Systems
|
|||
|
||||
var profile = _ticker.GetPlayerProfile(targetActor.PlayerSession);
|
||||
var mobUid = _spawning.SpawnPlayerMob(coords.Value, null, profile, stationUid);
|
||||
var targetMind = _mindSystem.GetMind(args.Target);
|
||||
|
||||
if (targetMind != null)
|
||||
{
|
||||
_mindSystem.TransferTo(targetMind.Value, mobUid, true);
|
||||
}
|
||||
if (_mindSystem.TryGetMind(args.Target, out var mindId, out var mindComp))
|
||||
_mindSystem.TransferTo(mindId, mobUid, true, mind: mindComp);
|
||||
|
||||
},
|
||||
ConfirmationPopup = true,
|
||||
Impact = LogImpact.High,
|
||||
|
|
|
|||
83
Content.Server/Cloning/CloningSystem.Subscriptions.cs
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
using Content.Server.Forensics;
|
||||
using Content.Shared.Cloning.Events;
|
||||
using Content.Shared.Clothing.Components;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Content.Shared.Labels.Components;
|
||||
using Content.Shared.Labels.EntitySystems;
|
||||
using Content.Shared.Paper;
|
||||
using Content.Shared.Stacks;
|
||||
using Content.Shared.Store;
|
||||
using Content.Shared.Store.Components;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server.Cloning;
|
||||
|
||||
/// <summary>
|
||||
/// The part of item cloning responsible for copying over important components.
|
||||
/// This is used for <see cref="CopyItem"/>.
|
||||
/// Anything not copied over here gets reverted to the values the item had in its prototype.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method of copying items is of course not perfect as we cannot clone every single component, which would be pretty much impossible with our ECS.
|
||||
/// We only consider the most important components so the paradox clone gets similar equipment.
|
||||
/// This method of using subscriptions was chosen to make it easy for forks to add their own custom components that need to be copied.
|
||||
/// </remarks>
|
||||
public sealed partial class CloningSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedStackSystem _stack = default!;
|
||||
[Dependency] private readonly SharedLabelSystem _label = default!;
|
||||
[Dependency] private readonly ForensicsSystem _forensics = default!;
|
||||
[Dependency] private readonly PaperSystem _paper = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<StackComponent, CloningItemEvent>(OnCloneStack);
|
||||
SubscribeLocalEvent<LabelComponent, CloningItemEvent>(OnCloneLabel);
|
||||
SubscribeLocalEvent<PaperComponent, CloningItemEvent>(OnClonePaper);
|
||||
SubscribeLocalEvent<ForensicsComponent, CloningItemEvent>(OnCloneForensics);
|
||||
SubscribeLocalEvent<StoreComponent, CloningItemEvent>(OnCloneStore);
|
||||
}
|
||||
|
||||
private void OnCloneStack(Entity<StackComponent> ent, ref CloningItemEvent args)
|
||||
{
|
||||
// if the clone is a stack as well, adjust the count of the copy
|
||||
if (TryComp<StackComponent>(args.CloneUid, out var cloneStackComp))
|
||||
_stack.SetCount(args.CloneUid, ent.Comp.Count, cloneStackComp);
|
||||
}
|
||||
|
||||
private void OnCloneLabel(Entity<LabelComponent> ent, ref CloningItemEvent args)
|
||||
{
|
||||
// copy the label
|
||||
_label.Label(args.CloneUid, ent.Comp.CurrentLabel);
|
||||
}
|
||||
|
||||
private void OnClonePaper(Entity<PaperComponent> ent, ref CloningItemEvent args)
|
||||
{
|
||||
// copy the text and any stamps
|
||||
if (TryComp<PaperComponent>(args.CloneUid, out var clonePaperComp))
|
||||
{
|
||||
_paper.SetContent((args.CloneUid, clonePaperComp), ent.Comp.Content);
|
||||
_paper.CopyStamps(ent.AsNullable(), (args.CloneUid, clonePaperComp));
|
||||
}
|
||||
}
|
||||
|
||||
private void OnCloneForensics(Entity<ForensicsComponent> ent, ref CloningItemEvent args)
|
||||
{
|
||||
// copy any forensics to the cloned item
|
||||
_forensics.CopyForensicsFrom(ent.Comp, args.CloneUid);
|
||||
}
|
||||
|
||||
private void OnCloneStore(Entity<StoreComponent> ent, ref CloningItemEvent args)
|
||||
{
|
||||
// copy the current amount of currency in the store
|
||||
// at the moment this takes care of uplink implants and the portable nukie uplinks
|
||||
// turning a copied pda into an uplink will need some refactoring first
|
||||
if (TryComp<StoreComponent>(args.CloneUid, out var cloneStoreComp))
|
||||
{
|
||||
cloneStoreComp.Balance = new Dictionary<ProtoId<CurrencyPrototype>, FixedPoint2>(ent.Comp.Balance);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -9,7 +9,6 @@ using Content.Shared.Implants;
|
|||
using Content.Shared.Implants.Components;
|
||||
using Content.Shared.NameModifier.Components;
|
||||
using Content.Shared.StatusEffect;
|
||||
using Content.Shared.Stacks;
|
||||
using Content.Shared.Storage;
|
||||
using Content.Shared.Storage.EntitySystems;
|
||||
using Content.Shared.Whitelist;
|
||||
|
|
@ -25,7 +24,7 @@ namespace Content.Server.Cloning;
|
|||
/// System responsible for making a copy of a humanoid's body.
|
||||
/// For the cloning machines themselves look at CloningPodSystem, CloningConsoleSystem and MedicalScannerSystem instead.
|
||||
/// </summary>
|
||||
public sealed class CloningSystem : EntitySystem
|
||||
public sealed partial class CloningSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IComponentFactory _componentFactory = default!;
|
||||
[Dependency] private readonly HumanoidAppearanceSystem _humanoidSystem = default!;
|
||||
|
|
@ -36,7 +35,6 @@ public sealed class CloningSystem : EntitySystem
|
|||
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
|
||||
[Dependency] private readonly SharedContainerSystem _container = default!;
|
||||
[Dependency] private readonly SharedStorageSystem _storage = default!;
|
||||
[Dependency] private readonly SharedStackSystem _stack = default!;
|
||||
[Dependency] private readonly SharedSubdermalImplantSystem _subdermalImplant = default!;
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -157,9 +155,9 @@ public sealed class CloningSystem : EntitySystem
|
|||
|
||||
var spawned = EntityManager.SpawnAtPosition(prototype, coords);
|
||||
|
||||
// if the original is a stack, adjust the count of the copy
|
||||
if (TryComp<StackComponent>(original, out var originalStack) && TryComp<StackComponent>(spawned, out var spawnedStack))
|
||||
_stack.SetCount(spawned, originalStack.Count, spawnedStack);
|
||||
// copy over important component data
|
||||
var ev = new CloningItemEvent(spawned);
|
||||
RaiseLocalEvent(original, ref ev);
|
||||
|
||||
// if the original has items inside its storage, copy those as well
|
||||
if (TryComp<StorageComponent>(original, out var originalStorage) && TryComp<StorageComponent>(spawned, out var spawnedStorage))
|
||||
|
|
@ -232,7 +230,14 @@ public sealed class CloningSystem : EntitySystem
|
|||
|
||||
var targetImplant = _subdermalImplant.AddImplant(target, implantId);
|
||||
|
||||
if (copyStorage && targetImplant != null)
|
||||
if (targetImplant == null)
|
||||
continue;
|
||||
|
||||
// copy over important component data
|
||||
var ev = new CloningItemEvent(targetImplant.Value);
|
||||
RaiseLocalEvent(originalImplant, ref ev);
|
||||
|
||||
if (copyStorage)
|
||||
CopyStorage(originalImplant, targetImplant.Value, whitelist, blacklist); // only needed for storage implants
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -138,6 +138,11 @@ namespace Content.Server.Forensics
|
|||
{
|
||||
dest.Fingerprints.Add(print);
|
||||
}
|
||||
|
||||
foreach (var residue in src.Residues)
|
||||
{
|
||||
dest.Residues.Add(residue);
|
||||
}
|
||||
}
|
||||
|
||||
public List<string> GetSolutionsDNA(EntityUid uid)
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ public sealed partial class ParadoxCloneRuleComponent : Component
|
|||
/// Cloning settings to be used.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public ProtoId<CloningSettingsPrototype> Settings = "BaseClone";
|
||||
public ProtoId<CloningSettingsPrototype> Settings = "Antag";
|
||||
|
||||
/// <summary>
|
||||
/// Visual effect spawned when gibbing at round end.
|
||||
|
|
@ -22,12 +22,19 @@ public sealed partial class ParadoxCloneRuleComponent : Component
|
|||
[DataField]
|
||||
public EntProtoId GibProto = "MobParadoxTimed";
|
||||
|
||||
/// <summary>
|
||||
/// Entity of the original player.
|
||||
/// Gets randomly chosen from all alive players if not specified.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public EntityUid? OriginalBody;
|
||||
|
||||
/// <summary>
|
||||
/// Mind entity of the original player.
|
||||
/// Gets assigned when cloning.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public EntityUid? Original;
|
||||
public EntityUid? OriginalMind;
|
||||
|
||||
/// <summary>
|
||||
/// Whitelist for Objectives to be copied to the clone.
|
||||
|
|
|
|||
|
|
@ -47,28 +47,42 @@ public sealed class ParadoxCloneRuleSystem : GameRuleSystem<ParadoxCloneRuleComp
|
|||
if (args.Session?.AttachedEntity is not { } spawner)
|
||||
return;
|
||||
|
||||
// get possible targets
|
||||
var allHumans = _mind.GetAliveHumans();
|
||||
|
||||
// we already checked when starting the gamerule, but someone might have died since then.
|
||||
if (allHumans.Count == 0)
|
||||
if (ent.Comp.OriginalBody != null) // target was overridden, for example by admin antag control
|
||||
{
|
||||
Log.Warning("Could not find any alive players to create a paradox clone from!");
|
||||
return;
|
||||
if (Deleted(ent.Comp.OriginalBody.Value) || !_mind.TryGetMind(ent.Comp.OriginalBody.Value, out var originalMindId, out var _))
|
||||
{
|
||||
Log.Warning("Could not find mind of target player to paradox clone!");
|
||||
return;
|
||||
}
|
||||
ent.Comp.OriginalMind = originalMindId;
|
||||
}
|
||||
else
|
||||
{
|
||||
// get possible targets
|
||||
var allAliveHumanoids = _mind.GetAliveHumans();
|
||||
|
||||
// we already checked when starting the gamerule, but someone might have died since then.
|
||||
if (allAliveHumanoids.Count == 0)
|
||||
{
|
||||
Log.Warning("Could not find any alive players to create a paradox clone from!");
|
||||
return;
|
||||
}
|
||||
|
||||
// pick a random player
|
||||
var randomHumanoidMind = _random.Pick(allAliveHumanoids);
|
||||
ent.Comp.OriginalMind = randomHumanoidMind;
|
||||
ent.Comp.OriginalBody = randomHumanoidMind.Comp.OwnedEntity;
|
||||
|
||||
}
|
||||
|
||||
// pick a random player
|
||||
var playerToClone = _random.Pick(allHumans);
|
||||
var bodyToClone = playerToClone.Comp.OwnedEntity;
|
||||
|
||||
if (bodyToClone == null || !_cloning.TryCloning(bodyToClone.Value, _transform.GetMapCoordinates(spawner), ent.Comp.Settings, out var clone))
|
||||
if (ent.Comp.OriginalBody == null || !_cloning.TryCloning(ent.Comp.OriginalBody.Value, _transform.GetMapCoordinates(spawner), ent.Comp.Settings, out var clone))
|
||||
{
|
||||
Log.Error($"Unable to make a paradox clone of entity {ToPrettyString(bodyToClone)}");
|
||||
Log.Error($"Unable to make a paradox clone of entity {ToPrettyString(ent.Comp.OriginalBody)}");
|
||||
return;
|
||||
}
|
||||
|
||||
var targetComp = EnsureComp<TargetOverrideComponent>(clone.Value);
|
||||
targetComp.Target = playerToClone.Owner; // set the kill target
|
||||
targetComp.Target = ent.Comp.OriginalMind; // set the kill target
|
||||
|
||||
var gibComp = EnsureComp<GibOnRoundEndComponent>(clone.Value);
|
||||
gibComp.SpawnProto = ent.Comp.GibProto;
|
||||
|
|
@ -78,17 +92,16 @@ public sealed class ParadoxCloneRuleSystem : GameRuleSystem<ParadoxCloneRuleComp
|
|||
_sensor.SetAllSensors(clone.Value, SuitSensorMode.SensorOff);
|
||||
|
||||
args.Entity = clone;
|
||||
ent.Comp.Original = playerToClone.Owner;
|
||||
}
|
||||
|
||||
private void AfterAntagEntitySelected(Entity<ParadoxCloneRuleComponent> ent, ref AfterAntagEntitySelectedEvent args)
|
||||
{
|
||||
if (ent.Comp.Original == null)
|
||||
if (ent.Comp.OriginalMind == null)
|
||||
return;
|
||||
|
||||
if (!_mind.TryGetMind(args.EntityUid, out var cloneMindId, out var cloneMindComp))
|
||||
return;
|
||||
|
||||
_mind.CopyObjectives(ent.Comp.Original.Value, (cloneMindId, cloneMindComp), ent.Comp.ObjectiveWhitelist, ent.Comp.ObjectiveBlacklist);
|
||||
_mind.CopyObjectives(ent.Comp.OriginalMind.Value, (cloneMindId, cloneMindComp), ent.Comp.ObjectiveWhitelist, ent.Comp.ObjectiveBlacklist);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ using Content.Shared.Mobs.Components;
|
|||
using Content.Shared.Mobs.Systems;
|
||||
using Content.Shared.Movement.Events;
|
||||
using Content.Shared.Movement.Systems;
|
||||
using Content.Shared.NameModifier.EntitySystems;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Storage.Components;
|
||||
using Content.Shared.Tag;
|
||||
|
|
@ -69,6 +70,7 @@ namespace Content.Server.Ghost
|
|||
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly TagSystem _tag = default!;
|
||||
[Dependency] private readonly NameModifierSystem _nameMod = default!;
|
||||
[Dependency] private readonly NewLifeSystem _newLifeSystem = default!;
|
||||
[Dependency] private readonly EuiManager _euiManager = default!;
|
||||
|
||||
|
|
@ -532,6 +534,10 @@ namespace Content.Server.Ghost
|
|||
else
|
||||
_minds.TransferTo(mind.Owner, ghost, mind: mind.Comp);
|
||||
Log.Debug($"Spawned ghost \"{ToPrettyString(ghost)}\" for {mind.Comp.CharacterName}.");
|
||||
|
||||
// we changed the entity name above
|
||||
// we have to call this after the mind has been transferred since some mind roles modify the ghost's name
|
||||
_nameMod.RefreshNameModifiers(ghost);
|
||||
return ghost;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -188,8 +188,8 @@ namespace Content.Server.NPC.Pathfinding
|
|||
/// </summary>
|
||||
public bool TryCreatePortal(EntityCoordinates coordsA, EntityCoordinates coordsB, out int handle)
|
||||
{
|
||||
var mapUidA = coordsA.GetMapUid(EntityManager);
|
||||
var mapUidB = coordsB.GetMapUid(EntityManager);
|
||||
var mapUidA = _transform.GetMap(coordsA);
|
||||
var mapUidB = _transform.GetMap(coordsB);
|
||||
handle = -1;
|
||||
|
||||
if (mapUidA != mapUidB || mapUidA == null)
|
||||
|
|
@ -197,8 +197,8 @@ namespace Content.Server.NPC.Pathfinding
|
|||
return false;
|
||||
}
|
||||
|
||||
var gridUidA = coordsA.GetGridUid(EntityManager);
|
||||
var gridUidB = coordsB.GetGridUid(EntityManager);
|
||||
var gridUidA = _transform.GetGrid(coordsA);
|
||||
var gridUidB = _transform.GetGrid(coordsB);
|
||||
|
||||
if (!TryComp<GridPathfindingComponent>(gridUidA, out var gridA) ||
|
||||
!TryComp<GridPathfindingComponent>(gridUidB, out var gridB))
|
||||
|
|
@ -236,8 +236,8 @@ namespace Content.Server.NPC.Pathfinding
|
|||
|
||||
_portals.Remove(handle);
|
||||
|
||||
var gridUidA = portal.CoordinatesA.GetGridUid(EntityManager);
|
||||
var gridUidB = portal.CoordinatesB.GetGridUid(EntityManager);
|
||||
var gridUidA = _transform.GetGrid(portal.CoordinatesA);
|
||||
var gridUidB = _transform.GetGrid(portal.CoordinatesB);
|
||||
|
||||
if (!TryComp<GridPathfindingComponent>(gridUidA, out var gridA) ||
|
||||
!TryComp<GridPathfindingComponent>(gridUidB, out var gridB))
|
||||
|
|
@ -397,7 +397,7 @@ namespace Content.Server.NPC.Pathfinding
|
|||
/// </summary>
|
||||
public PathPoly? GetPoly(EntityCoordinates coordinates)
|
||||
{
|
||||
var gridUid = coordinates.GetGridUid(EntityManager);
|
||||
var gridUid = _transform.GetGrid(coordinates);
|
||||
|
||||
if (!TryComp<GridPathfindingComponent>(gridUid, out var comp) ||
|
||||
!TryComp(gridUid, out TransformComponent? xform))
|
||||
|
|
@ -405,14 +405,14 @@ namespace Content.Server.NPC.Pathfinding
|
|||
return null;
|
||||
}
|
||||
|
||||
var localPos = Vector2.Transform(coordinates.ToMapPos(EntityManager, _transform), _transform.GetInvWorldMatrix(xform));
|
||||
var localPos = Vector2.Transform(_transform.ToMapCoordinates(coordinates).Position, _transform.GetInvWorldMatrix(xform));
|
||||
var origin = GetOrigin(localPos);
|
||||
|
||||
if (!TryGetChunk(origin, comp, out var chunk))
|
||||
return null;
|
||||
|
||||
var chunkPos = new Vector2(MathHelper.Mod(localPos.X, ChunkSize), MathHelper.Mod(localPos.Y, ChunkSize));
|
||||
var polys = chunk.Polygons[(int) chunkPos.X * ChunkSize + (int) chunkPos.Y];
|
||||
var polys = chunk.Polygons[(int)chunkPos.X * ChunkSize + (int)chunkPos.Y];
|
||||
|
||||
foreach (var poly in polys)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
using System.Linq;
|
||||
using Content.Server.Administration;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.Parallax.Biomes;
|
||||
|
|
@ -29,9 +28,9 @@ public sealed partial class BiomeSystem
|
|||
|
||||
int.TryParse(args[0], out var mapInt);
|
||||
var mapId = new MapId(mapInt);
|
||||
var mapUid = _mapManager.GetMapEntityId(mapId);
|
||||
var mapUid = _mapSystem.GetMapOrInvalid(mapId);
|
||||
|
||||
if (_mapManager.MapExists(mapId) ||
|
||||
if (_mapSystem.MapExists(mapId) ||
|
||||
!TryComp<BiomeComponent>(mapUid, out var biome))
|
||||
{
|
||||
return;
|
||||
|
|
@ -64,9 +63,9 @@ public sealed partial class BiomeSystem
|
|||
}
|
||||
|
||||
var mapId = new MapId(mapInt);
|
||||
var mapUid = _mapManager.GetMapEntityId(mapId);
|
||||
var mapUid = _mapSystem.GetMapOrInvalid(mapId);
|
||||
|
||||
if (!_mapManager.MapExists(mapId) || !TryComp<BiomeComponent>(mapUid, out var biome))
|
||||
if (!_mapSystem.MapExists(mapId) || !TryComp<BiomeComponent>(mapUid, out var biome))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
@ -105,7 +104,7 @@ public sealed partial class BiomeSystem
|
|||
{
|
||||
var mapId = new MapId(mapInt);
|
||||
|
||||
if (TryComp<BiomeComponent>(_mapManager.GetMapEntityId(mapId), out var biome))
|
||||
if (TryComp<BiomeComponent>(_mapSystem.GetMapOrInvalid(mapId), out var biome))
|
||||
{
|
||||
var results = new List<string>();
|
||||
|
||||
|
|
@ -145,7 +144,7 @@ public sealed partial class BiomeSystem
|
|||
|
||||
var mapId = new MapId(mapInt);
|
||||
|
||||
if (!_mapManager.MapExists(mapId) || !TryComp<BiomeComponent>(_mapManager.GetMapEntityId(mapId), out var biome))
|
||||
if (!_mapSystem.MapExists(mapId) || !TryComp<BiomeComponent>(_mapSystem.GetMapOrInvalid(mapId), out var biome))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -149,6 +149,20 @@ namespace Content.Server.Popups
|
|||
}
|
||||
}
|
||||
|
||||
public override void PopupPredicted(string? message, EntityUid uid, EntityUid? recipient, Filter filter, bool recordReplay, PopupType type = PopupType.Small)
|
||||
{
|
||||
if (message == null)
|
||||
return;
|
||||
|
||||
if (recipient != null)
|
||||
{
|
||||
// Don't send to recipient, since they predicted it locally
|
||||
filter = filter.RemovePlayerByAttachedEntity(recipient.Value);
|
||||
}
|
||||
|
||||
RaiseNetworkEvent(new PopupEntityEvent(message, type, GetNetEntity(uid)), filter, recordReplay);
|
||||
}
|
||||
|
||||
public override void PopupPredicted(string? recipientMessage, string? othersMessage, EntityUid uid, EntityUid? recipient, PopupType type = PopupType.Small)
|
||||
{
|
||||
PopupPredicted(othersMessage, uid, recipient, type);
|
||||
|
|
|
|||
29
Content.Server/Power/Commands/PowerValidateCommand.cs
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
using Content.Server.Administration;
|
||||
using Content.Server.Power.EntitySystems;
|
||||
using Content.Shared.Administration;
|
||||
using Robust.Shared.Console;
|
||||
|
||||
namespace Content.Server.Power.Commands;
|
||||
|
||||
[AdminCommand(AdminFlags.Debug)]
|
||||
public sealed class PowerValidateCommand : LocalizedEntityCommands
|
||||
{
|
||||
[Dependency] private readonly PowerNetSystem _powerNet = null!;
|
||||
|
||||
public override string Command => "power_validate";
|
||||
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
_powerNet.Validate();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
shell.WriteLine(LocalizationManager.GetString("cmd-power_validate-error", ("err", e.ToString())));
|
||||
return;
|
||||
}
|
||||
|
||||
shell.WriteLine(LocalizationManager.GetString("cmd-power_validate-success"));
|
||||
}
|
||||
}
|
||||
|
|
@ -519,6 +519,14 @@ namespace Content.Server.Power.EntitySystems
|
|||
supplier.NetworkSupply.LinkedNetwork = netNode.Id;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validate integrity of the power state data. Throws if an error is found.
|
||||
/// </summary>
|
||||
public void Validate()
|
||||
{
|
||||
_solver.Validate(_powerState);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Robust.Shared.Utility;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Threading;
|
||||
using static Content.Server.Power.Pow3r.PowerState;
|
||||
|
||||
|
|
@ -40,7 +42,9 @@ namespace Content.Server.Power.Pow3r
|
|||
DebugTools.Assert(state.GroupedNets.Select(x => x.Count).Sum() == state.Networks.Count);
|
||||
_networkJob.State = state;
|
||||
_networkJob.FrameTime = frameTime;
|
||||
#if DEBUG
|
||||
ValidateNetworkGroups(state, state.GroupedNets);
|
||||
#endif
|
||||
|
||||
// Each network height layer can be run in parallel without issues.
|
||||
foreach (var group in state.GroupedNets)
|
||||
|
|
@ -328,16 +332,22 @@ namespace Content.Server.Power.Pow3r
|
|||
RecursivelyEstimateNetworkDepth(state, network, groupedNetworks);
|
||||
}
|
||||
|
||||
ValidateNetworkGroups(state, groupedNetworks);
|
||||
return groupedNetworks;
|
||||
}
|
||||
|
||||
public void Validate(PowerState state)
|
||||
{
|
||||
if (state.GroupedNets == null)
|
||||
throw new InvalidOperationException("We don't have grouped networks cached??");
|
||||
|
||||
ValidateNetworkGroups(state, state.GroupedNets);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validate that network grouping is up to date. I.e., that it is safe to solve each networking in a given
|
||||
/// group in parallel. This assumes that batteries are the only device that connects to multiple networks, and
|
||||
/// is thus the only obstacle to solving everything in parallel.
|
||||
/// </summary>
|
||||
[Conditional("DEBUG")]
|
||||
private void ValidateNetworkGroups(PowerState state, List<List<Network>> groupedNetworks)
|
||||
{
|
||||
HashSet<Network> nets = new();
|
||||
|
|
@ -362,9 +372,9 @@ namespace Content.Server.Power.Pow3r
|
|||
continue;
|
||||
}
|
||||
|
||||
DebugTools.Assert(!nets.Contains(subNet));
|
||||
DebugTools.Assert(!netIds.Contains(subNet.Id));
|
||||
DebugTools.Assert(subNet.Height < net.Height);
|
||||
Check(!nets.Contains(subNet), $"Net {net.Id}, battery {batteryId}");
|
||||
Check(!netIds.Contains(subNet.Id), $"Net {net.Id}, battery {batteryId}");
|
||||
Check(subNet.Height < net.Height, $"Net {net.Id}, battery {batteryId}");
|
||||
}
|
||||
|
||||
foreach (var batteryId in net.BatterySupplies)
|
||||
|
|
@ -380,15 +390,32 @@ namespace Content.Server.Power.Pow3r
|
|||
continue;
|
||||
}
|
||||
|
||||
DebugTools.Assert(!nets.Contains(parentNet));
|
||||
DebugTools.Assert(!netIds.Contains(parentNet.Id));
|
||||
DebugTools.Assert(parentNet.Height > net.Height);
|
||||
Check(!nets.Contains(parentNet), $"Net {net.Id}, battery {batteryId}");
|
||||
Check(!netIds.Contains(parentNet.Id), $"Net {net.Id}, battery {batteryId}");
|
||||
Check(parentNet.Height > net.Height, $"Net {net.Id}, battery {batteryId}");
|
||||
}
|
||||
|
||||
DebugTools.Assert(nets.Add(net));
|
||||
DebugTools.Assert(netIds.Add(net.Id));
|
||||
Check(nets.Add(net), $"Net {net.Id}");
|
||||
Check(netIds.Add(net.Id), $"Net {net.Id}");
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
// Most readable C# function def.
|
||||
[AssertionMethod]
|
||||
static void Check(
|
||||
[AssertionCondition(AssertionConditionType.IS_TRUE)]
|
||||
[DoesNotReturnIf(false)]
|
||||
bool condition,
|
||||
[InterpolatedStringHandlerArgument("condition")]
|
||||
ref DebugTools.AssertInterpolatedStringHandler handler,
|
||||
[CallerArgumentExpression(nameof(condition))]
|
||||
string check = "")
|
||||
{
|
||||
if (!condition)
|
||||
throw new DebugAssertException($"{handler.ToStringAndClear()}: failed check: {check}");
|
||||
}
|
||||
}
|
||||
|
||||
private static void RecursivelyEstimateNetworkDepth(PowerState state, Network network, List<List<Network>> groupedNetworks)
|
||||
|
|
|
|||
|
|
@ -5,5 +5,6 @@ namespace Content.Server.Power.Pow3r
|
|||
public interface IPowerSolver
|
||||
{
|
||||
void Tick(float frameTime, PowerState state, IParallelManager parallel);
|
||||
void Validate(PowerState state);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,5 +8,10 @@ namespace Content.Server.Power.Pow3r
|
|||
{
|
||||
// Literally nothing.
|
||||
}
|
||||
|
||||
public void Validate(PowerState state)
|
||||
{
|
||||
// Literally nothing.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@ public sealed class SpecialRespawnSystem : SharedSpecialRespawnSystem
|
|||
if (tile.IsSpace(_tileDefinitionManager)
|
||||
|| _turf.IsTileBlocked(tile, CollisionGroup.MobMask)
|
||||
|| !_atmosphere.IsTileMixtureProbablySafe(entityGridUid, entityMapUid.Value,
|
||||
grid.TileIndicesFor(mapPos)))
|
||||
_map.TileIndicesFor((entityGridUid.Value, grid), mapPos)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
|
@ -136,7 +136,7 @@ public sealed class SpecialRespawnSystem : SharedSpecialRespawnSystem
|
|||
private void Respawn(EntityUid oldEntity, string prototype, EntityCoordinates coords)
|
||||
{
|
||||
var entity = Spawn(prototype, coords);
|
||||
_adminLog.Add(LogType.Respawn, LogImpact.Extreme, $"{ToPrettyString(oldEntity)} was deleted and was respawned at {coords.ToMap(EntityManager, _transform)} as {ToPrettyString(entity)}");
|
||||
_adminLog.Add(LogType.Respawn, LogImpact.Extreme, $"{ToPrettyString(oldEntity)} was deleted and was respawned at {_transform.ToMapCoordinates(coords)} as {ToPrettyString(entity)}");
|
||||
_chat.SendAdminAlert($"{MetaData(oldEntity).EntityName} was deleted and was respawned as {ToPrettyString(entity)}");
|
||||
}
|
||||
|
||||
|
|
@ -157,7 +157,7 @@ public sealed class SpecialRespawnSystem : SharedSpecialRespawnSystem
|
|||
|
||||
var xform = Transform(targetGrid);
|
||||
|
||||
if (!grid.TryGetTileRef(xform.Coordinates, out var tileRef))
|
||||
if (!_map.TryGetTileRef(targetGrid, grid, xform.Coordinates, out var tileRef))
|
||||
return false;
|
||||
|
||||
var tile = tileRef.GridIndices;
|
||||
|
|
@ -169,12 +169,12 @@ public sealed class SpecialRespawnSystem : SharedSpecialRespawnSystem
|
|||
//Obviously don't put anything ridiculous in here
|
||||
for (var i = 0; i < maxAttempts; i++)
|
||||
{
|
||||
var randomX = _random.Next((int) gridBounds.Left, (int) gridBounds.Right);
|
||||
var randomY = _random.Next((int) gridBounds.Bottom, (int) gridBounds.Top);
|
||||
var randomX = _random.Next((int)gridBounds.Left, (int)gridBounds.Right);
|
||||
var randomY = _random.Next((int)gridBounds.Bottom, (int)gridBounds.Top);
|
||||
|
||||
tile = new Vector2i(randomX - (int) gridPos.X, randomY - (int) gridPos.Y);
|
||||
var mapPos = grid.GridTileToWorldPos(tile);
|
||||
var mapTarget = grid.WorldToTile(mapPos);
|
||||
tile = new Vector2i(randomX - (int)gridPos.X, randomY - (int)gridPos.Y);
|
||||
var mapPos = _map.GridTileToWorldPos(targetGrid, grid, tile);
|
||||
var mapTarget = _map.WorldToTile(targetGrid, grid, mapPos);
|
||||
var circle = new Circle(mapPos, 2);
|
||||
|
||||
foreach (var newTileRef in _map.GetTilesIntersecting(targetGrid, grid, circle))
|
||||
|
|
@ -183,7 +183,7 @@ public sealed class SpecialRespawnSystem : SharedSpecialRespawnSystem
|
|||
continue;
|
||||
|
||||
found = true;
|
||||
targetCoords = grid.GridTileToLocal(tile);
|
||||
targetCoords = _map.GridTileToLocal(targetGrid, grid, tile);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,4 +6,12 @@ namespace Content.Server.Roles;
|
|||
/// Added to mind role entities to tag that they are a paradox clone.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class ParadoxCloneRoleComponent : BaseMindRoleComponent;
|
||||
public sealed partial class ParadoxCloneRoleComponent : BaseMindRoleComponent
|
||||
{
|
||||
/// <summary>
|
||||
/// Name modifer applied to the player when they turn into a ghost.
|
||||
/// Needed to be able to keep the original and the clone apart in dead chat.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public LocId? NameModifier = "paradox-clone-ghost-name-modifier";
|
||||
}
|
||||
|
|
|
|||
32
Content.Server/Roles/ParadoxCloneRoleSystem.cs
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
using Content.Shared.Ghost;
|
||||
using Content.Shared.Mind;
|
||||
using Content.Shared.NameModifier.EntitySystems;
|
||||
using Content.Shared.Roles;
|
||||
|
||||
namespace Content.Server.Roles;
|
||||
|
||||
/// <summary>
|
||||
/// System responsible for giving a ghost of a paradox clone a name modifier.
|
||||
/// </summary>
|
||||
public sealed class ParadoxCloneRoleSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<ParadoxCloneRoleComponent, MindRelayedEvent<RefreshNameModifiersEvent>>(OnRefreshNameModifiers);
|
||||
}
|
||||
|
||||
private void OnRefreshNameModifiers(Entity<ParadoxCloneRoleComponent> ent, ref MindRelayedEvent<RefreshNameModifiersEvent> args)
|
||||
{
|
||||
if (!TryComp<MindRoleComponent>(ent.Owner, out var roleComp))
|
||||
return;
|
||||
|
||||
// only show for ghosts
|
||||
if (!HasComp<GhostComponent>(roleComp.Mind.Comp.OwnedEntity))
|
||||
return;
|
||||
|
||||
if (ent.Comp.NameModifier != null)
|
||||
args.Args.AddModifier(ent.Comp.NameModifier.Value, 50);
|
||||
}
|
||||
}
|
||||
|
|
@ -45,12 +45,12 @@ namespace Content.Server.Tabletop
|
|||
/// </summary>
|
||||
private void EnsureTabletopMap()
|
||||
{
|
||||
if (TabletopMap != MapId.Nullspace && _mapManager.MapExists(TabletopMap))
|
||||
if (TabletopMap != MapId.Nullspace && _map.MapExists(TabletopMap))
|
||||
return;
|
||||
|
||||
TabletopMap = _mapManager.CreateMap();
|
||||
var mapUid = _map.CreateMap(out var mapId);
|
||||
TabletopMap = mapId;
|
||||
_tabletops = 0;
|
||||
var mapUid = _mapManager.GetMapEntityId(TabletopMap);
|
||||
|
||||
var mapComp = EntityManager.GetComponent<MapComponent>(mapUid);
|
||||
|
||||
|
|
@ -89,11 +89,11 @@ namespace Content.Server.Tabletop
|
|||
|
||||
private void OnRoundRestart(RoundRestartCleanupEvent _)
|
||||
{
|
||||
if (TabletopMap == MapId.Nullspace || !_mapManager.MapExists(TabletopMap))
|
||||
if (TabletopMap == MapId.Nullspace || !_map.MapExists(TabletopMap))
|
||||
return;
|
||||
|
||||
// This will usually *not* be the case, but better make sure.
|
||||
_mapManager.DeleteMap(TabletopMap);
|
||||
_map.DeleteMap(TabletopMap);
|
||||
|
||||
// Reset tabletop count.
|
||||
_tabletops = 0;
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ using JetBrains.Annotations;
|
|||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
|
|
@ -21,7 +20,7 @@ namespace Content.Server.Tabletop
|
|||
[UsedImplicitly]
|
||||
public sealed partial class TabletopSystem : SharedTabletopSystem
|
||||
{
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] private readonly SharedMapSystem _map = default!;
|
||||
[Dependency] private readonly EyeSystem _eye = default!;
|
||||
[Dependency] private readonly ViewSubscriberSystem _viewSubscriberSystem = default!;
|
||||
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ public sealed record PlayerInfo(
|
|||
string StartingJob,
|
||||
bool Antag,
|
||||
RoleTypePrototype RoleProto,
|
||||
int SortWeight,
|
||||
NetEntity? NetEntity,
|
||||
NetUserId SessionId,
|
||||
bool Connected,
|
||||
|
|
|
|||
|
|
@ -49,11 +49,60 @@ public sealed partial class CCVars
|
|||
/// If true, the admin overlay will display the total time of the players
|
||||
/// </summary>
|
||||
public static readonly CVarDef<bool> AdminOverlayPlaytime =
|
||||
CVarDef.Create("ui.admin_overlay_playtime", false, CVar.CLIENTONLY | CVar.ARCHIVE);
|
||||
CVarDef.Create("ui.admin_overlay_playtime", true, CVar.CLIENTONLY | CVar.ARCHIVE);
|
||||
|
||||
/// <summary>
|
||||
/// If true, the admin overlay will display the players starting position.
|
||||
/// </summary>
|
||||
public static readonly CVarDef<bool> AdminOverlayStartingJob =
|
||||
CVarDef.Create("ui.admin_overlay_starting_job", false, CVar.CLIENTONLY | CVar.ARCHIVE);
|
||||
CVarDef.Create("ui.admin_overlay_starting_job", true, CVar.CLIENTONLY | CVar.ARCHIVE);
|
||||
|
||||
/// <summary>
|
||||
/// If true, the admin window player tab will show different antag symbols for each role type
|
||||
/// </summary>
|
||||
public static readonly CVarDef<bool> AdminPlayerlistSeparateSymbols =
|
||||
CVarDef.Create("ui.admin_playerlist_separate_symbols", false, CVar.CLIENTONLY | CVar.ARCHIVE);
|
||||
|
||||
/// <summary>
|
||||
/// If true, characters with antag role types will have their names colored by their role type
|
||||
/// </summary>
|
||||
public static readonly CVarDef<bool> AdminPlayerlistHighlightedCharacterColor =
|
||||
CVarDef.Create("ui.admin_playerlist_highlighted_character_color", true, CVar.CLIENTONLY | CVar.ARCHIVE);
|
||||
|
||||
/// <summary>
|
||||
/// If true, the Role Types column will be colored
|
||||
/// </summary>
|
||||
public static readonly CVarDef<bool> AdminPlayerlistRoleTypeColor =
|
||||
CVarDef.Create("ui.admin_playerlist_role_type_color", true, CVar.CLIENTONLY | CVar.ARCHIVE);
|
||||
|
||||
/// <summary>
|
||||
/// If true, the admin overlay will show antag symbols
|
||||
/// </summary>
|
||||
public static readonly CVarDef<bool> AdminOverlaySymbols =
|
||||
CVarDef.Create("ui.admin_overlay_symbols", true, CVar.CLIENTONLY | CVar.ARCHIVE);
|
||||
|
||||
/// <summary>
|
||||
/// The range (in tiles) around the cursor within which the admin overlays of ghosts start to fade out
|
||||
/// </summary>
|
||||
public static readonly CVarDef<int> AdminOverlayGhostFadeDistance =
|
||||
CVarDef.Create("ui.admin_overlay_ghost_fade_distance", 6, CVar.CLIENTONLY | CVar.ARCHIVE);
|
||||
|
||||
/// <summary>
|
||||
/// The range (in tiles) around the cursor within which the admin overlays of ghosts disappear
|
||||
/// </summary>
|
||||
public static readonly CVarDef<int> AdminOverlayGhostHideDistance =
|
||||
CVarDef.Create("ui.admin_overlay_ghost_hide_distance", 2, CVar.CLIENTONLY | CVar.ARCHIVE);
|
||||
|
||||
/// <summary>
|
||||
/// The maximum range (in tiles) at which admin overlay entries still merge to form a stack
|
||||
/// Recommended to keep under 1, otherwise the overlays of people sitting next to each other will stack
|
||||
/// </summary>
|
||||
public static readonly CVarDef<float> AdminOverlayMergeDistance =
|
||||
CVarDef.Create("ui.admin_overlay_merge_distance", 0.33f, CVar.CLIENTONLY | CVar.ARCHIVE);
|
||||
|
||||
/// <summary>
|
||||
/// The maximum size that an overlay stack can reach. Additional overlays will be superimposed over the last one.
|
||||
/// </summary>
|
||||
public static readonly CVarDef<int> AdminOverlayStackMax =
|
||||
CVarDef.Create("ui.admin_overlay_stack_max", 3, CVar.CLIENTONLY | CVar.ARCHIVE);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,12 +2,21 @@ namespace Content.Shared.Cloning.Events;
|
|||
|
||||
/// <summary>
|
||||
/// Raised before a mob is cloned. Cancel to prevent cloning.
|
||||
/// This is raised on the original mob.
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
public record struct CloningAttemptEvent(CloningSettingsPrototype Settings, bool Cancelled = false);
|
||||
|
||||
/// <summary>
|
||||
/// Raised after a new mob got spawned when cloning a humanoid.
|
||||
/// Raised after a new mob was spawned when cloning a humanoid.
|
||||
/// This is raised on the original mob.
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
public record struct CloningEvent(CloningSettingsPrototype Settings, EntityUid CloneUid);
|
||||
|
||||
/// <summary>
|
||||
/// Raised after a new item was spawned when cloning an item.
|
||||
/// This is raised on the original item.
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
public record struct CloningItemEvent(EntityUid CloneUid);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
using Content.Shared.Construction.EntitySystems;
|
||||
using Content.Shared.Whitelist;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared.Construction.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Will not allow anchoring if there is an anchored item in the same tile that fails the <see cref="EntityWhitelist"/>.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent, Access(typeof(BlockAnchorOnSystem))]
|
||||
public sealed partial class BlockAnchorOnComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// If not null, entities that match this whitelist are allowed.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public EntityWhitelist? Whitelist;
|
||||
|
||||
/// <summary>
|
||||
/// If not null, entities that match this blacklist are not allowed.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public EntityWhitelist? Blacklist;
|
||||
}
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
using Content.Shared.Construction.Components;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Whitelist;
|
||||
using Robust.Shared.Map.Components;
|
||||
|
||||
namespace Content.Shared.Construction.EntitySystems;
|
||||
|
||||
/// <summary>
|
||||
/// Prevents anchoring an item in the same tile as an item matching the <see cref="EntityWhitelist"/>.
|
||||
/// <seealso cref="BlockAnchorOnComponent"/>
|
||||
/// </summary>
|
||||
public sealed class BlockAnchorOnSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly EntityWhitelistSystem _whitelist = default!;
|
||||
[Dependency] private readonly SharedMapSystem _map = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _xform = default!;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<BlockAnchorOnComponent, AnchorStateChangedEvent>(OnAnchorStateChanged);
|
||||
SubscribeLocalEvent<BlockAnchorOnComponent, AnchorAttemptEvent>(OnAnchorAttempt);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles the <see cref="AnchorStateChangedEvent"/>.
|
||||
/// </summary>
|
||||
private void OnAnchorStateChanged(Entity<BlockAnchorOnComponent> ent, ref AnchorStateChangedEvent args)
|
||||
{
|
||||
if (!args.Anchored)
|
||||
return;
|
||||
|
||||
if (!HasOverlap((ent, ent.Comp, Transform(ent))))
|
||||
return;
|
||||
|
||||
_popup.PopupPredicted(Loc.GetString("anchored-already-present"), ent, null);
|
||||
_xform.Unanchor(ent, Transform(ent));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles the <see cref="AnchorAttemptEvent"/>.
|
||||
/// </summary>
|
||||
private void OnAnchorAttempt(Entity<BlockAnchorOnComponent> ent, ref AnchorAttemptEvent args)
|
||||
{
|
||||
if (args.Cancelled)
|
||||
return;
|
||||
|
||||
if (!HasOverlap((ent, ent.Comp, Transform(ent))))
|
||||
return;
|
||||
|
||||
_popup.PopupPredicted(Loc.GetString("anchored-already-present"), ent, args.User);
|
||||
args.Cancel();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if there is any anchored overlap with non whitelisted or blacklisted entities.
|
||||
/// </summary>
|
||||
/// <returns>True if there is, false if there isn't</returns>
|
||||
private bool HasOverlap(Entity<BlockAnchorOnComponent, TransformComponent> ent)
|
||||
{
|
||||
if (ent.Comp2.GridUid is not { } grid || !TryComp<MapGridComponent>(grid, out var gridComp))
|
||||
return false;
|
||||
|
||||
var indices = _map.TileIndicesFor(grid, gridComp, ent.Comp2.Coordinates);
|
||||
var enumerator = _map.GetAnchoredEntitiesEnumerator(grid, gridComp, indices);
|
||||
|
||||
while (enumerator.MoveNext(out var otherEnt))
|
||||
{
|
||||
// Don't match yourself.
|
||||
if (otherEnt == ent)
|
||||
continue;
|
||||
|
||||
if (!_whitelist.CheckBoth(otherEnt, ent.Comp1.Blacklist, ent.Comp1.Whitelist))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -22,4 +22,10 @@ public sealed partial class RoleTypePrototype : IPrototype
|
|||
/// </summary>
|
||||
[DataField]
|
||||
public Color Color { get; private set; } = Color.FromHex("#eeeeee");
|
||||
|
||||
/// <summary>
|
||||
/// A symbol used to represent the role type.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public string Symbol = string.Empty;
|
||||
}
|
||||
|
|
|
|||
48
Content.Shared/Mind/SharedMindSystem.Relay.cs
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
using Content.Shared.NameModifier.EntitySystems;
|
||||
using Content.Shared.Mind.Components;
|
||||
|
||||
namespace Content.Shared.Mind;
|
||||
|
||||
/// <summary>
|
||||
/// Relays events raised on a mobs body to its mind and mind role entities.
|
||||
/// Useful for events that should be raised both on the body and the mind.
|
||||
/// </summary>
|
||||
public abstract partial class SharedMindSystem : EntitySystem
|
||||
{
|
||||
public void InitializeRelay()
|
||||
{
|
||||
// for name modifiers that depend on certain mind roles
|
||||
SubscribeLocalEvent<MindContainerComponent, RefreshNameModifiersEvent>(RelayRefToMind);
|
||||
}
|
||||
|
||||
protected void RelayToMind<T>(EntityUid uid, MindContainerComponent component, T args) where T : class
|
||||
{
|
||||
var ev = new MindRelayedEvent<T>(args);
|
||||
|
||||
if (TryGetMind(uid, out var mindId, out var mindComp, component))
|
||||
{
|
||||
RaiseLocalEvent(mindId, ref ev);
|
||||
|
||||
foreach (var role in mindComp.MindRoles)
|
||||
RaiseLocalEvent(role, ref ev);
|
||||
}
|
||||
}
|
||||
|
||||
protected void RelayRefToMind<T>(EntityUid uid, MindContainerComponent component, ref T args) where T : class
|
||||
{
|
||||
var ev = new MindRelayedEvent<T>(args);
|
||||
|
||||
if (TryGetMind(uid, out var mindId, out var mindComp, component))
|
||||
{
|
||||
RaiseLocalEvent(mindId, ref ev);
|
||||
|
||||
foreach (var role in mindComp.MindRoles)
|
||||
RaiseLocalEvent(role, ref ev);
|
||||
}
|
||||
|
||||
args = ev.Args;
|
||||
}
|
||||
}
|
||||
|
||||
[ByRefEvent]
|
||||
public record struct MindRelayedEvent<TEvent>(TEvent Args);
|
||||
|
|
@ -19,7 +19,7 @@ using Robust.Shared.Utility;
|
|||
|
||||
namespace Content.Shared.Mind;
|
||||
|
||||
public abstract class SharedMindSystem : EntitySystem
|
||||
public abstract partial class SharedMindSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
|
||||
[Dependency] private readonly INetManager _net = default!;
|
||||
|
|
@ -42,6 +42,8 @@ public abstract class SharedMindSystem : EntitySystem
|
|||
SubscribeLocalEvent<RoundRestartCleanupEvent>(OnReset);
|
||||
SubscribeLocalEvent<MindComponent, ComponentStartup>(OnMindStartup);
|
||||
SubscribeLocalEvent<MindComponent, EntityRenamedEvent>(OnRenamed);
|
||||
|
||||
InitializeRelay();
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
|
|
|
|||
|
|
@ -224,6 +224,26 @@ public sealed class PaperSystem : EntitySystem
|
|||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copy any stamp information from one piece of paper to another.
|
||||
/// </summary>
|
||||
public void CopyStamps(Entity<PaperComponent?> source, Entity<PaperComponent?> target)
|
||||
{
|
||||
if (!Resolve(source, ref source.Comp) || !Resolve(target, ref target.Comp))
|
||||
return;
|
||||
|
||||
target.Comp.StampedBy = new List<StampDisplayInfo>(source.Comp.StampedBy);
|
||||
target.Comp.StampState = source.Comp.StampState;
|
||||
Dirty(target);
|
||||
|
||||
if (TryComp<AppearanceComponent>(target, out var appearance))
|
||||
{
|
||||
// delete any stamps if the stamp state is null
|
||||
_appearance.SetData(target, PaperVisuals.Stamp, target.Comp.StampState ?? "", appearance);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void SetContent(Entity<PaperComponent> entity, string content)
|
||||
{
|
||||
entity.Comp.Content = content;
|
||||
|
|
|
|||
|
|
@ -114,6 +114,19 @@ namespace Content.Shared.Popups
|
|||
/// </summary>
|
||||
public abstract void PopupPredicted(string? message, EntityUid uid, EntityUid? recipient, PopupType type = PopupType.Small);
|
||||
|
||||
/// <summary>
|
||||
/// Variant of <see cref="PopupEntity(string, EntityUid, Filter, bool, PopupType)"/> for use with prediction.
|
||||
/// The local client will show the popup to the recipient, and the server will show it to players in the filter.
|
||||
/// If recipient is null, the local client will do nothing and the server will show the message to players in the filter.
|
||||
/// </summary>
|
||||
/// <param name="message">The message to display.</param>
|
||||
/// <param name="uid">The entity to display the popup above.</param>
|
||||
/// <param name="recipient">The client that will see this popup locally during prediction.</param>
|
||||
/// <param name="filter">Filter for players that will see the popup from the server.</param>
|
||||
/// <param name="recordReplay">If true, this pop-up will be considered as a globally visible pop-up that gets shown during replays.</param>
|
||||
/// <param name="type">Used to customize how this popup should appear visually. See: <see cref="PopupType"/>.</param>
|
||||
public abstract void PopupPredicted(string? message, EntityUid uid, EntityUid? recipient, Filter filter, bool recordReplay, PopupType type = PopupType.Small);
|
||||
|
||||
/// <summary>
|
||||
/// Variant of <see cref="PopupPredicted(string?, EntityUid, EntityUid?, PopupType)"/> that displays <paramref name="recipientMessage"/>
|
||||
/// to the recipient and <paramref name="othersMessage"/> to everyone else in PVS range.
|
||||
|
|
|
|||
|
|
@ -45,6 +45,12 @@ public sealed partial class MindRoleComponent : BaseMindRoleComponent
|
|||
/// </summary>
|
||||
[DataField]
|
||||
public ProtoId<JobPrototype>? JobPrototype { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Used to order the characters on by role/antag status. Highest numbers are shown first.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public int SortWeight;
|
||||
}
|
||||
|
||||
// Why does this base component actually exist? It does make auto-categorization easy, but before that it was useless?
|
||||
|
|
|
|||
|
|
@ -211,20 +211,30 @@ public abstract class SharedRoleSystem : EntitySystem
|
|||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the most recently specified role type, or Neutral
|
||||
/// </summary>
|
||||
private ProtoId<RoleTypePrototype> GetRoleTypeByTime(MindComponent mind)
|
||||
{
|
||||
// If any Mind Roles specify a Role Type, return the most recent. Otherwise return Neutral
|
||||
var role = GetRoleCompByTime(mind);
|
||||
return role?.Comp?.RoleType ?? "Neutral";
|
||||
}
|
||||
|
||||
var roles = new List<ProtoId<RoleTypePrototype>>();
|
||||
/// <summary>
|
||||
/// Return the most recently specified role type's mind role entity, or null
|
||||
/// </summary>
|
||||
public Entity<MindRoleComponent>? GetRoleCompByTime(MindComponent mind)
|
||||
{
|
||||
var roles = new List<Entity<MindRoleComponent>>();
|
||||
|
||||
foreach (var role in mind.MindRoles)
|
||||
{
|
||||
var comp = Comp<MindRoleComponent>(role);
|
||||
if (comp.RoleType is not null)
|
||||
roles.Add(comp.RoleType.Value);
|
||||
roles.Add((role, comp));
|
||||
}
|
||||
|
||||
ProtoId<RoleTypePrototype> result = (roles.Count > 0) ? roles.LastOrDefault() : "Neutral";
|
||||
Entity<MindRoleComponent>? result = roles.Count > 0 ? roles.LastOrDefault() : null;
|
||||
return (result);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
using Content.Shared.Storage.EntitySystems;
|
||||
using Content.Shared.Whitelist;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared.Storage.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Entities with this component will eject all items that match the whitelist / blacklist when anchored.
|
||||
/// It also doesn't allow any items to be inserted that fit the whitelist / blacklist while anchored.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// If you have a smuggler stash that has a player inside of it, you want to eject the player before its anchored so they don't get stuck
|
||||
/// </example>
|
||||
[RegisterComponent, NetworkedComponent, Access(typeof(AnchoredStorageFilterSystem))]
|
||||
public sealed partial class AnchoredStorageFilterComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// If not null, entities that do not match this whitelist will be ejected.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public EntityWhitelist? Whitelist;
|
||||
|
||||
/// <summary>
|
||||
/// If not null, entities that match this blacklist will be ejected..
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public EntityWhitelist? Blacklist;
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
using Content.Shared.Storage.Components;
|
||||
using Content.Shared.Whitelist;
|
||||
using Robust.Shared.Containers;
|
||||
|
||||
namespace Content.Shared.Storage.EntitySystems;
|
||||
|
||||
/// <summary>
|
||||
/// Ejects items that do not match a <see cref="EntityWhitelist"/> from a storage when it is anchored.
|
||||
/// <seealso cref="AnchoredStorageFilterComponent"/>
|
||||
/// </summary>
|
||||
public sealed class AnchoredStorageFilterSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly EntityWhitelistSystem _whitelist = default!;
|
||||
[Dependency] private readonly SharedContainerSystem _container = default!;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<AnchoredStorageFilterComponent, AnchorStateChangedEvent>(OnAnchorStateChanged);
|
||||
SubscribeLocalEvent<AnchoredStorageFilterComponent, ContainerIsInsertingAttemptEvent>(OnInsertAttempt);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles the <see cref="AnchorStateChangedEvent"/>.
|
||||
/// </summary>
|
||||
private void OnAnchorStateChanged(Entity<AnchoredStorageFilterComponent> ent, ref AnchorStateChangedEvent args)
|
||||
{
|
||||
if (!args.Anchored)
|
||||
return;
|
||||
|
||||
if (!TryComp<StorageComponent>(ent, out var storage))
|
||||
return;
|
||||
|
||||
foreach (var item in storage.StoredItems.Keys)
|
||||
{
|
||||
if (!_whitelist.CheckBoth(item, ent.Comp.Blacklist, ent.Comp.Whitelist))
|
||||
_container.RemoveEntity(ent, item);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles the <see cref="ContainerIsInsertingAttemptEvent"/>.
|
||||
/// </summary>
|
||||
private void OnInsertAttempt(Entity<AnchoredStorageFilterComponent> ent, ref ContainerIsInsertingAttemptEvent args)
|
||||
{
|
||||
if (args.Cancelled)
|
||||
return;
|
||||
|
||||
if (Transform(ent).Anchored && !_whitelist.CheckBoth(args.EntityUid, ent.Comp.Blacklist, ent.Comp.Whitelist))
|
||||
args.Cancel();
|
||||
}
|
||||
}
|
||||
|
|
@ -918,5 +918,56 @@ Entries:
|
|||
id: 113
|
||||
time: '2025-03-21T23:21:32.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/35994
|
||||
- author: Errant
|
||||
changes:
|
||||
- message: The Antag column on the player list has been removed, and it's functionality
|
||||
folded into the Character and Role Type columns.
|
||||
type: Remove
|
||||
- message: The Role Type column now sorts entries in a revised order. All antagonist
|
||||
types are grouped at the end.
|
||||
type: Tweak
|
||||
- message: On the player list, the names of antagonist players are now prefixed
|
||||
with an antag symbol, and (optionally) shown in the color of their current role
|
||||
type. The default setting is to show the "standard antag dagger" symbol for
|
||||
any type of antag, same as the ahelp relay. Optionally a distinct icon can be
|
||||
shown for each role type for easier identification.
|
||||
type: Tweak
|
||||
- message: More playerlist/overlay settings can now be changed in Admin Options.
|
||||
type: Tweak
|
||||
id: 114
|
||||
time: '2025-03-25T16:03:59.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/35538
|
||||
- author: ScarKy0
|
||||
changes:
|
||||
- message: The "Spawn here" verb now reliably transfers mind of the target to their
|
||||
new body.
|
||||
type: Fix
|
||||
id: 115
|
||||
time: '2025-03-25T18:40:54.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/36080
|
||||
- author: Errant
|
||||
changes:
|
||||
- message: Admin overlays of mobs/players in very close proximity no longer overlap
|
||||
each other. They now form stacks of up to 3. If more than 3 overlay entries
|
||||
are stacked, any additional entries will be drawn over the 3rd element of the
|
||||
stack. These names are ordered by vertical position, but when the coordinates
|
||||
match exactly (such as someone carrying a mothroach/pAI, or ghosts orbiting),
|
||||
the top member of the stack is NOT guaranteed to be the most visible/interesting
|
||||
element in the stack.
|
||||
type: Add
|
||||
- message: Ghosts near the mouse cursor no longer show up on the admin overlay,
|
||||
including any ghosts in stacks near the mouse cursor.
|
||||
type: Add
|
||||
id: 116
|
||||
time: '2025-03-25T20:15:22.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/35622
|
||||
- author: slarticodefast
|
||||
changes:
|
||||
- message: Added an antag control verb to create a paradox clone ghost role of a
|
||||
selected player.
|
||||
type: Add
|
||||
id: 117
|
||||
time: '2025-03-27T17:04:25.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/36105
|
||||
Name: Admin
|
||||
Order: 1
|
||||
|
|
|
|||
|
|
@ -1,58 +1,4 @@
|
|||
Entries:
|
||||
- author: keronshb
|
||||
changes:
|
||||
- message: Added Ethereal Jaunt! A Wizard Spell that turns you invisible and into
|
||||
a ghost-like creature for escaping!
|
||||
type: Add
|
||||
id: 7607
|
||||
time: '2024-11-13T23:36:37.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/33201
|
||||
- author: CheddaCheez
|
||||
changes:
|
||||
- message: Fixed mimes being unable to break their vow more than once. Despicable!
|
||||
type: Fix
|
||||
id: 7608
|
||||
time: '2024-11-14T16:56:22.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/33303
|
||||
- author: PJB3005
|
||||
changes:
|
||||
- message: Borgs can now select their chassis type upon creation (construction or
|
||||
job spawn), immediately giving borgs access to all chassis types.
|
||||
type: Add
|
||||
- message: Borg chassis types now come with built-in modules depending on the type,
|
||||
so you can immediately do your job without help from science. Some upgrade modules
|
||||
must still be installed later however.
|
||||
type: Add
|
||||
- message: Specialized chassis types have been removed from construction, as they
|
||||
are no longer necessary.
|
||||
type: Remove
|
||||
- message: Borg unlock access is no longer determined by their chassis, it's always
|
||||
science or command. This means the janitor can't unlock jani borgs anymore.
|
||||
type: Remove
|
||||
id: 7609
|
||||
time: '2024-11-14T17:08:35.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/32586
|
||||
- author: thetolbean
|
||||
changes:
|
||||
- message: The Quartermaster's requisition digi-board can no longer be recycled.
|
||||
type: Fix
|
||||
id: 7610
|
||||
time: '2024-11-15T06:54:53.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/33315
|
||||
- author: RedBookcase
|
||||
changes:
|
||||
- message: Mixing up a Snow White no longer creates extra liquid out of thin air.
|
||||
type: Fix
|
||||
id: 7611
|
||||
time: '2024-11-15T20:52:19.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/33331
|
||||
- author: lzk228
|
||||
changes:
|
||||
- message: Added 10 seconds delay to Succumb action
|
||||
type: Add
|
||||
id: 7612
|
||||
time: '2024-11-15T21:21:08.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/32985
|
||||
- author: Beck Thompson
|
||||
changes:
|
||||
- message: Minor tweaks to clumsiness. Some of the timings and or noises have been
|
||||
|
|
@ -3880,3 +3826,52 @@
|
|||
id: 8106
|
||||
time: '2025-03-24T23:55:17.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/36062
|
||||
- author: Velcroboy, ErhardSteinhauer, whatston3, MilonPL, Beck, ArtisticRoomba, ScarKy0
|
||||
changes:
|
||||
- message: Smuggler stashes! Will get spawned as a round start event and can also
|
||||
be found in a hacked ClothesMate. Will have loot from a large random pool.
|
||||
type: Add
|
||||
- message: Smuggler stashes can now be bought from the syndicate uplink for 2TC.
|
||||
type: Add
|
||||
id: 8107
|
||||
time: '2025-03-26T15:20:15.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/19460
|
||||
- author: slarticodefast
|
||||
changes:
|
||||
- message: Ghosts of paradox clones now have a name modifier so they can be distinguished
|
||||
by other ghosts.
|
||||
type: Tweak
|
||||
id: 8108
|
||||
time: '2025-03-26T15:30:14.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/35940
|
||||
- author: slarticodefast
|
||||
changes:
|
||||
- message: Paradox clones of nuclear operatives and head revolutionaries now have
|
||||
the corresponding faction icon and need to be killed for a crew major victory.
|
||||
type: Fix
|
||||
id: 8109
|
||||
time: '2025-03-26T15:34:19.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/35910
|
||||
- author: slarticodefast
|
||||
changes:
|
||||
- message: Further improved item copying for paradox clones.
|
||||
type: Tweak
|
||||
id: 8110
|
||||
time: '2025-03-26T16:13:03.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/35993
|
||||
- author: Centronias
|
||||
changes:
|
||||
- message: Raw meatballs, like steaks, can now be cooked in the microwave or on
|
||||
a grill.
|
||||
type: Add
|
||||
id: 8111
|
||||
time: '2025-03-27T06:26:13.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/36003
|
||||
- author: SlamBamActionman
|
||||
changes:
|
||||
- message: Liltenhead! Thank you for over 2 years of update videos, and congrats
|
||||
on the 100th video!
|
||||
type: Add
|
||||
id: 8112
|
||||
time: '2025-03-27T17:19:36.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/36104
|
||||
|
|
|
|||
|
|
@ -14,3 +14,6 @@ force_client_hud_version_watermark = true
|
|||
|
||||
[chat]
|
||||
motd = "\n########################################################\n\n[font size=17]This is a test server. You can play with the newest changes to the game, but these [color=red]changes may not be final or stable[/color], and may be reverted. Please report bugs via our GitHub, forum, or community Discord.[/font]\n\n########################################################\n"
|
||||
|
||||
[debug]
|
||||
pow3r_disable_parallel = false
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ admin-verb-make-nuclear-operative = Make target into a lone Nuclear Operative.
|
|||
admin-verb-make-pirate = Make the target into a pirate. Note this doesn't configure the game rule.
|
||||
admin-verb-make-head-rev = Make the target into a Head Revolutionary.
|
||||
admin-verb-make-thief = Make the target into a thief.
|
||||
admin-verb-make-paradox-clone = Create a Paradox Clone ghost role of the target.
|
||||
admin-verb-make-changeling = Make the target into a changeling.
|
||||
|
||||
admin-verb-text-make-traitor = Make Traitor
|
||||
|
|
@ -15,6 +16,7 @@ admin-verb-text-make-nuclear-operative = Make Nuclear Operative
|
|||
admin-verb-text-make-pirate = Make Pirate
|
||||
admin-verb-text-make-head-rev = Make Head Rev
|
||||
admin-verb-text-make-thief = Make Thief
|
||||
admin-verb-text-make-paradox-clone = Create Paradox Clone
|
||||
admin-verb-text-make-changeling = Make Changeling
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
cmd-adminnotes-desc = Opens the admin notes panel of target player.
|
||||
cmd-adminnotes-help = Usage: adminnotes <UserId OR Username>
|
||||
|
||||
cmd-adminnotes-wrong-target = Unable to find user '{$user}'.
|
||||
cmd-adminnotes-args-error = Invalid arguments.
|
||||
Usage: adminnotes <UserId OR Username>
|
||||
|
||||
cmd-adminnotes-hint = UserId OR Username
|
||||
|
|
@ -2,7 +2,6 @@
|
|||
player-tab-username = Username
|
||||
player-tab-character = Character
|
||||
player-tab-job = Job
|
||||
player-tab-antagonist = Antagonist
|
||||
player-tab-roletype = Role Type
|
||||
player-tab-playtime = Playtime
|
||||
player-tab-show-disconnected = Show Disconnected
|
||||
|
|
@ -11,3 +10,7 @@ player-tab-entry-tooltip = Playtime is displayed in days:hours:minutes.
|
|||
player-tab-filter-line-edit-placeholder = Filter
|
||||
player-tab-is-antag-yes = YES
|
||||
player-tab-is-antag-no = NO
|
||||
|
||||
player-tab-character-name-antag-symbol = {$symbol} {$name}
|
||||
|
||||
player-tab-antag-prefix = 🗡
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
anchored-already-present = There's already something anchored here!
|
||||
|
|
@ -1,66 +1,118 @@
|
|||
figurines-hop-1 = Papers, please.
|
||||
figurines-hop-2 = You are fired.
|
||||
figurines-hop-3 = BRB.
|
||||
figurines-hop-4 = You can get AA if you fill out the form.
|
||||
figurines-hop-5 = I was gone for two seconds...
|
||||
|
||||
figurines-passenger-1 = Insuls please.
|
||||
figurines-passenger-2 = Call evac.
|
||||
figurines-passenger-3 = HELP MAINTS!!
|
||||
figurines-passenger-4 = I'm no tider.
|
||||
figurines-passenger-5 = How much for a toolbelt?
|
||||
|
||||
figurines-greytider-1 = Man, this party stinks. I fucking hate these people.
|
||||
figurines-greytider-2 = Uh-oh, who's lost their stunbaton?
|
||||
figurines-greytider-3 = Robust.
|
||||
figurines-greytider-4 = I'm not me without a toolbox.
|
||||
figurines-greytider-5 = Grey tide station wide!
|
||||
figurines-greytider-6 = Viva la revolution.
|
||||
|
||||
figurines-clown-1 = Honk!
|
||||
figurines-clown-2 = Banana!
|
||||
figurines-clown-3 = Soap!
|
||||
figurines-clown-4 = HoP has one clown, HoS has the whole department.
|
||||
figurines-clown-5 = Do I annoy you?
|
||||
figurines-clown-6 = Can I have AA? Please?
|
||||
figurines-clown-7 = I'm a clown, but you're the whole circus!
|
||||
|
||||
figurines-holoclown-1 = I'm helping my older brother.
|
||||
figurines-holoclown-2 = Hello, officer!
|
||||
figurines-holoclown-3 = Who are you calling blue?
|
||||
figurines-holoclown-4 = Bleeding on the ground is a good look for you.
|
||||
figurines-holoclown-5 = Pathetic.
|
||||
figurines-holoclown-6 = It's not them you need to worry about; it's me.
|
||||
figurines-holoclown-7 = What's so funny?
|
||||
|
||||
figurines-mime-1 = ...
|
||||
figurines-mime-2 = ...
|
||||
figurines-mime-3 = ....
|
||||
figurines-mime-4 = .......
|
||||
figurines-mime-5 = ................
|
||||
figurines-mime-6 = ...........?
|
||||
figurines-mime-7 = !!!
|
||||
figurines-mime-8 = ....!
|
||||
figurines-mime-9 = ???
|
||||
|
||||
figurines-musician-1 = Never gonna give you up!
|
||||
figurines-musician-2 = Never gonna let you down!
|
||||
figurines-musician-3 = Music is an art.
|
||||
figurines-musician-4 = Thank you, I'll be here all night.
|
||||
figurines-musician-5 = I'm a one man band.
|
||||
|
||||
figurines-boxer-1 = The first rule of Fight Club is...
|
||||
figurines-boxer-2 = We settle this in the ring, alright?
|
||||
figurines-boxer-3 = I. AM. THE. CHAMPION!!
|
||||
figurines-boxer-4 = Don't look at me, he was shot, not punched.
|
||||
figurines-boxer-4 = Don't look at me; he was shot, not punched.
|
||||
figurines-boxer-5 = 1v1 me, captain.
|
||||
|
||||
figurines-captain-1 = Glory to NT!
|
||||
figurines-captain-2 = How did I get hired? Yes.
|
||||
figurines-captain-3 = The nuclear disk is secure. Where? Somewhere.
|
||||
figurines-captain-4 = Where did my ID go?
|
||||
figurines-captain-5 = Everything is under control.
|
||||
figurines-captain-6 = The disk was in my bag last I checked.
|
||||
figurines-captain-7 = The chain of command starts and ends with me.
|
||||
figurines-captain-8 = It's hard being at the top.
|
||||
|
||||
figurines-hos-1 = Space law? What?
|
||||
figurines-hos-2 = Shoot the clown.
|
||||
figurines-hos-3 = Yes, I shot the clown. No, I don't regret it.
|
||||
figurines-hos-4 = Clown is now KOS.
|
||||
figurines-hos-5 = Armory is now open to the public!
|
||||
|
||||
figurines-warden-1 = Execute him for breaking in!
|
||||
figurines-warden-2 = Perma the fucker for insulting me!
|
||||
figurines-warden-3 = We totally treat everyone fairly and do NOT mistreat our prisoners.
|
||||
figurines-warden-4 = Brig is my home. My home is brig. My brig is home. Stop, what?
|
||||
figurines-warden-5 = Soap is now contraband.
|
||||
figurines-warden-6 = You're going away for a long time, buddy.
|
||||
|
||||
figurines-detective-1 = The butler did it.
|
||||
figurines-detective-2 = I need some whiskey after this.
|
||||
figurines-detective-3 = Chameleon fibers? How did a chameleon get in here?
|
||||
figurines-detective-4 = Go go gadget!
|
||||
figurines-detective-5 = Of course I checked the door logs!
|
||||
|
||||
figurines-security-1 = I am the law!
|
||||
figurines-security-2 = You have violated article 1984.
|
||||
figurines-security-3 = Whenever I get bored I use the clown as target practice.
|
||||
figurines-security-4 = You have two rights: to remain silent and to cry about it.
|
||||
figurines-security-5 = Harmbaton? It sure as hell harms!
|
||||
figurines-security-6 = Space law? Never heard of it.
|
||||
figurines-security-7 = Random search! Hand it over.
|
||||
figurines-security-8 = I love donuts.
|
||||
figurines-security-9 = Greytide this, motherfucker.
|
||||
figurines-security-10 = Do not resist.
|
||||
|
||||
figurines-lawyer-1 = Better Call Saul!
|
||||
figurines-lawyer-2 = Objection!
|
||||
figurines-lawyer-3 = Did you know that you have rights?
|
||||
figurines-lawyer-4 = Space law says!
|
||||
figurines-lawyer-5 = Sign the contract first.
|
||||
|
||||
figurines-cargotech-1 = DRAGON ON ATS!
|
||||
figurines-cargotech-2 = I sold the station!
|
||||
figurines-cargotech-3 = Brain bounty? I don't have a brain.
|
||||
figurines-cargotech-4 = You're worth 3000 spesos. Congrats.
|
||||
figurines-cargotech-5 = Vegetable bounty? Nobody eats those anyways.
|
||||
figurines-cargotech-6 = WE ARE SECEDING!! ALL HAIL CARGONIA!!
|
||||
|
||||
figurines-salvage-1 = Megafauna? It was mega easy.
|
||||
figurines-salvage-2 = We're lost. Anyone bring a GPS?
|
||||
figurines-salvage-3 = Anyone have oxygen?
|
||||
figurines-salvage-4 = I found a blood-red and e-sword!
|
||||
figurines-salvage-5 = There's bears in space?
|
||||
figurines-salvage-6 = Crusher? I barely know her!
|
||||
|
||||
figurines-qm-1 = Who stole the shuttle?
|
||||
figurines-qm-2 = I won't approve the guns.
|
||||
|
|
@ -69,91 +121,179 @@ figurines-qm-4 = One toys crate for ma fellow clown!
|
|||
figurines-qm-5 = Time to spent all money on gambling.
|
||||
figurines-qm-6 = Viva La Cargonia!
|
||||
figurines-qm-7 = Fill the form.
|
||||
figurines-qm-8 = Where'd all our money go?
|
||||
figurines-qm-9 = 99% of gamblers quit right before they hit it big!
|
||||
|
||||
figurines-ce-1 = Everyone to the briefing!
|
||||
figurines-ce-2 = Wire the solars!
|
||||
figurines-ce-3 = How to setup the TEG?
|
||||
figurines-ce-4 = SINGULOOSE!
|
||||
figurines-ce-5 = TESLOOSE!
|
||||
figurines-ce-6 = Power's out again.
|
||||
|
||||
figurines-engineer-1 = SINGULOOSE!
|
||||
figurines-engineer-2 = TESLOOSE!
|
||||
figurines-engineer-3 = What is AME?
|
||||
figurines-engineer-4 = Free insuls at engineering
|
||||
figurines-engineer-5 = Where'd the power go?
|
||||
figurines-engineer-6 = Someone bombed medbay... again...
|
||||
figurines-engineer-7 = Well, why don't you come and fix it?
|
||||
|
||||
figurines-atmostech-1 = I put plasma in distro.
|
||||
figurines-atmostech-2 = I will burn you in a burn chamber.
|
||||
figurines-atmostech-3 = Frezon...
|
||||
figurines-atmostech-4 = Tritium...
|
||||
figurines-atmostech-5 = Glory to Atmosia!
|
||||
figurines-atmostech-6 = Distro? That's short for disposal.
|
||||
figurines-atmostech-7 = TEG: Thermal Energy? Gone!
|
||||
|
||||
figurines-rd-1 = Blowing up all of the borgs!
|
||||
figurines-rd-2 = Tier 3 arsenal? No way.
|
||||
figurines-rd-3 = Now where did I leave my hardsuit...?
|
||||
figurines-rd-4 = Now you're thinking with portals!
|
||||
figurines-rd-5 = The cake is a lie!
|
||||
figurines-rd-6 = The trait I look for in a scientist is expendability.
|
||||
|
||||
figurines-scientist-1 = Someone else must have made those bombs!
|
||||
figurines-scientist-2 = He asked to be borged!
|
||||
figurines-scientist-3 = Carp at sci!
|
||||
figurines-scientist-4 = Explosion at sci!
|
||||
figurines-scientist-5 = The anomaly has exploded!
|
||||
figurines-scientist-5 = Anyone seen an anomaly?
|
||||
figurines-scientist-6 = The anomaly exploded!
|
||||
|
||||
figurines-cmo-1 = Suit sensors!
|
||||
figurines-cmo-2 = Why do we have meth?
|
||||
figurines-cmo-3 = Who drank all the chems?
|
||||
figurines-cmo-4 = Desoxyephedrine? Sounds healthy.
|
||||
figurines-cmo-5 = No, you're not getting my hypospray.
|
||||
|
||||
figurines-chemist-1 = Get your pills!
|
||||
figurines-chemist-2 = We need to cook.
|
||||
figurines-chemist-3 = I am the one who knocks!
|
||||
figurines-chemist-4 = Say my name.
|
||||
figurines-chemist-5 = 99.8% purity.
|
||||
figurines-chemist-6 = Epinephrine? Didn't you say methamphetamine?
|
||||
|
||||
figurines-paramedic-1 = Insuls and tools!
|
||||
figurines-paramedic-2 = I need AA for saving people!
|
||||
figurines-paramedic-3 = SUIT SENSORS!!
|
||||
figurines-paramedic-4 = I need the hypospray for saving people!
|
||||
figurines-paramedic-5 = 14 dead in the clown's room.
|
||||
|
||||
figurines-doctor-1 = The patient is already dead!
|
||||
figurines-doctor-2 = CLEAR!
|
||||
figurines-doctor-3 = Saw makes BRRR.
|
||||
figurines-doctor-4 = Just a week away...
|
||||
figurines-doctor-5 = I knew it...
|
||||
|
||||
figurines-librarian-1 = One day while...
|
||||
figurines-librarian-2 = Silence!
|
||||
figurines-librarian-1 = Silence!
|
||||
figurines-librarian-2 = One day while...
|
||||
figurines-librarian-3 = Once upon a time...
|
||||
figurines-librarian-4 = In a world where...
|
||||
figurines-librarian-5 = It was a dark and stormy night...
|
||||
figurines-librarian-6 = Long, long ago...
|
||||
figurines-librarian-7 = As the story goes...
|
||||
figurines-librarian-8 = Imagine, if you will...
|
||||
figurines-librarian-9 = Long before time had a name...
|
||||
figurines-librarian-10 = In a galaxy far, far away...
|
||||
figurines-librarian-11 = As the old saying goes...
|
||||
figurines-librarian-12 = Gather round...
|
||||
figurines-librarian-13 = ...It's a tale as old as time...
|
||||
figurines-librarian-14 = ...That's all she wrote.
|
||||
|
||||
figurines-chaplain-1 = Would you like to join my cul- I mean religion.
|
||||
figurines-chaplain-2 = Gods make me a killing machine please!
|
||||
figurines-chaplain-3 = God exists!
|
||||
figurines-chaplain-4 = Those aren't blood runes, I drew them in crayon.
|
||||
figurines-chaplain-5 = Anyone want to be sacrificed?
|
||||
figurines-chaplain-6 = Vampires aren't real.
|
||||
|
||||
figurines-chef-1 = I swear it's not human meat.
|
||||
figurines-chef-2 = More banana cream pies?
|
||||
figurines-chef-3 = How does rotary sushi sound?
|
||||
figurines-chef-4 = That'll be 1000 spesos
|
||||
figurines-chef-5 = For here or to go?
|
||||
figurines-chef-6 = Where'd Pun Pun go? No idea...
|
||||
|
||||
figurines-bartender-1 = Where's my monkey?
|
||||
figurines-bartender-2 = Sec won't drink.
|
||||
figurines-bartender-3 = I mixed a little something in there...
|
||||
figurines-bartender-4 = The recipe? Plasma and vomit. Why?
|
||||
figurines-bartender-5 = I need those toxins for my drinks, officer!
|
||||
figurines-bartender-6 = Read the room.
|
||||
figurines-bartender-7 = I've got a shotgun.
|
||||
|
||||
figurines-botanist-1 = I don't have any weed, officer!
|
||||
figurines-botanist-2 = Dude, I see colors...
|
||||
figurines-botanist-3 = Is it just me, or is that weed glowing?
|
||||
figurines-botanist-4 = 50 more units of mutagen. That should be enough.
|
||||
figurines-botanist-5 = More bananas for my favorite clown!
|
||||
|
||||
figurines-janitor-1 = Clown stole my soap. Again.
|
||||
figurines-janitor-2 = Look at the signs, you idiot.
|
||||
figurines-janitor-3 = I've never seen this much lube in my life.
|
||||
figurines-janitor-4 = Another day, another spill.
|
||||
figurines-janitor-5 = I'm not even paid for this.
|
||||
figurines-janitor-6 = This blood wasn't evidence, right?
|
||||
figurines-janitor-7 = My only friend is my mop.
|
||||
figurines-janitor-8 = That better not be what I think it is...
|
||||
figurines-janitor-9 = Another day, another body.
|
||||
|
||||
figurines-nukie-1 = I got the disk!
|
||||
figurines-nukie-2 = Whiskey, Echo, Whiskey.
|
||||
figurines-nukie-3 = The nuke makes boom.
|
||||
figurines-nukie-4 = What's the code?
|
||||
figurines-nukie-5 = Commander...? ...That's a balloon...
|
||||
|
||||
figurines-nukie-elite-1 = Not a word in nanotrasen.
|
||||
figurines-nukie-elite-2 = THAT'S A KEG!
|
||||
figurines-nukie-elite-3 = Guys are you alive?
|
||||
figurines-nukie-elite-4 = Breach and clear!
|
||||
figurines-nukie-elite-5 = Leave no survivors.
|
||||
figurines-nukie-elite-6 = Good work, team.
|
||||
|
||||
figurines-nukie-commander-1 = GET DAT FUKKEN DISK!
|
||||
figurines-nukie-commander-2 = Fuckin' flukies.
|
||||
figurines-nukie-commander-3 = The syndicate sends its regards.
|
||||
figurines-nukie-commander-4 = Failure is not an option.
|
||||
figurines-nukie-commander-5 = Whoops.
|
||||
|
||||
figurines-footsoldier-1 = I'm an evil boy. Less boy every day, more evil every day.
|
||||
figurines-footsoldier-2 = Who will you choose? Them or us? Us or them?
|
||||
figurines-footsoldier-3 = Glory to the syndicate!
|
||||
figurines-footsoldier-4 = Down with Nanotrasen!
|
||||
figurines-footsoldier-5 = I'd rather die than join Nanotrasen.
|
||||
|
||||
figurines-wizard-1 = Ei Nath!
|
||||
figurines-wizard-1 = Ei Nath!!
|
||||
figurines-wizard-2 = Wehgardium Leviosa!
|
||||
figurines-wizard-3 = Skidaddle skadoodle!
|
||||
figurines-wizard-4 = FIREBALL!
|
||||
|
||||
figurines-space-dragon-1 = Fish will consume the station.
|
||||
figurines-space-dragon-2 = Dragon de- Actually, nevermind.
|
||||
figurines-space-dragon-3 = Crew is delicious.
|
||||
figurines-space-dragon-4 = Don't you dare make sushi.
|
||||
figurines-space-dragon-5 = This station ain't big enough for the two of us.
|
||||
|
||||
# figurines-queen
|
||||
figurines-queen-1 = Our domain must grow.
|
||||
figurines-queen-2 = The hive hungers.
|
||||
figurines-queen-3 = We consume all.
|
||||
figurines-queen-4 = We are the apex.
|
||||
figurines-queen-5 = You're just biomass.
|
||||
figurines-queen-6 = We must evolve.
|
||||
|
||||
figurines-rat-king-1 = Gimme some food, capiche?
|
||||
figurines-rat-king-2 = Fugeddaboutit.
|
||||
figurines-rat-king-3 = Whack 'em!
|
||||
figurines-rat-king-4 = Let me give you an offer you can't refuse, capiche?
|
||||
figurines-rat-king-5 = Nothing personal, capiche?
|
||||
figurines-rat-king-6 = I run this station now, see? Nyeh!
|
||||
|
||||
figurines-rat-servant-1 = Capiche!
|
||||
figurines-rat-servant-2 = Boss says!
|
||||
figurines-rat-servant-3 = The boss wants a word with youse.
|
||||
figurines-rat-servant-4 = Ay, I'm walkin' here!
|
||||
figurines-rat-servant-5 = You get the chedda', then we talk.
|
||||
|
||||
figurines-mouse-1 = Piep!
|
||||
figurines-mouse-2 = Squeak!
|
||||
|
|
@ -166,6 +306,7 @@ figurines-mouse-7 = Heep!
|
|||
figurines-slime-1 = Blyump.
|
||||
figurines-slime-2 = Blimpuf?
|
||||
figurines-slime-3 = Blump!
|
||||
figurines-slime-4 = Squish!
|
||||
|
||||
figurines-hamlet-1 = Piep!
|
||||
figurines-hamlet-2 = Squeak!
|
||||
|
|
|
|||
|
|
@ -337,4 +337,17 @@ ui-options-censor-nudity = Censor character nudity
|
|||
|
||||
## Admin menu
|
||||
|
||||
ui-options-enable-classic-overlay = Revert antag overlay to classic mode
|
||||
ui-options-admin-player-panel = Admin Menu Players List
|
||||
|
||||
ui-options-admin-playerlist-separate-symbols = Show separate symbols for each antag role type
|
||||
ui-options-admin-playerlist-character-color = Color names of antagonist characters
|
||||
ui-options-admin-playerlist-roletype-color = Color role types
|
||||
|
||||
ui-options-admin-overlay-title = Admin Overlay
|
||||
ui-options-enable-classic-overlay = Revert overlay to classic mode
|
||||
ui-options-enable-overlay-symbols = Add antag symbol to text
|
||||
ui-options-enable-overlay-playtime = Show playtime
|
||||
ui-options-enable-overlay-starting-job = Show starting job
|
||||
ui-options-overlay-merge-distance = Stack merge distance
|
||||
ui-options-overlay-ghost-fade-distance = Ghost overlay fade range from mouse
|
||||
ui-options-overlay-ghost-hide-distance = Ghost overlay hide range from mouse
|
||||
|
|
|
|||
4
Resources/Locale/en-US/_strings/power/commands.ftl
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
cmd-power_validate-desc = Validate power network state integrity
|
||||
cmd-power_validate-help = Usage: power_validate
|
||||
cmd-power_validate-error = Error while validating: { $err }
|
||||
cmd-power_validate-success = Validation succeeded without error
|
||||
|
|
@ -466,3 +466,6 @@ uplink-business-card-desc = A business card that you can give to someone to demo
|
|||
|
||||
uplink-fake-mindshield-name = Fake Mindshield
|
||||
uplink-fake-mindshield-desc = A togglable implant capable of mimicking the same transmissions a real mindshield puts out when on, tricking capable Heads-up displays into thinking you have a mindshield (Nanotrasen brand implanter not provided.)
|
||||
|
||||
uplink-smuggler-satchel-name = Smuggler's Satchel
|
||||
uplink-smuggler-satchel-desc = A handy, suspicious looking satchel. Just flat enough to fit underneath floor tiles.
|
||||
|
|
|
|||
|
|
@ -3,3 +3,5 @@ paradox-clone-round-end-agent-name = paradox clone
|
|||
objective-issuer-paradox = [color=lightblue]Paradox[/color]
|
||||
|
||||
paradox-clone-role-greeting = A freak space-time anomaly has teleported you into another reality! Now you have to find your counterpart and kill and replace them. Only one of you two can survive.
|
||||
|
||||
paradox-clone-ghost-name-modifier = {$baseName} (clone)
|
||||
|
|
|
|||
|
|
@ -119,4 +119,5 @@
|
|||
ClothingUniformJumpskirtTacticool: 1
|
||||
ToyFigurinePassenger: 1
|
||||
ToyFigurineGreytider: 1
|
||||
# DO NOT ADD MORE, USE UNIFORM DYING
|
||||
ClothingBackpackSatchelSmugglerUnanchored: 1
|
||||
# DO NOT ADD MORE, USE UNIFORM DYING
|
||||
|
|
|
|||
|
|
@ -2331,3 +2331,16 @@
|
|||
whitelist:
|
||||
- Chef
|
||||
- Mime
|
||||
|
||||
- type: listing
|
||||
id: UplinkSmugglerSatchel
|
||||
name: uplink-smuggler-satchel-name
|
||||
description: uplink-smuggler-satchel-desc
|
||||
productEntity: ClothingBackpackSatchelSmugglerUnanchored
|
||||
discountCategory: usualDiscounts
|
||||
discountDownTo:
|
||||
Telecrystal: 1
|
||||
cost:
|
||||
Telecrystal: 2
|
||||
categories:
|
||||
- UplinkDeception
|
||||
|
|
|
|||
|
|
@ -2,211 +2,211 @@
|
|||
id: FigurinesHoP
|
||||
values:
|
||||
prefix: figurines-hop-
|
||||
count: 2
|
||||
count: 5
|
||||
|
||||
- type: localizedDataset
|
||||
id: FigurinesPassenger
|
||||
values:
|
||||
prefix: figurines-passenger-
|
||||
count: 2
|
||||
count: 5
|
||||
|
||||
- type: localizedDataset
|
||||
id: FigurinesGreytider
|
||||
values:
|
||||
prefix: figurines-greytider-
|
||||
count: 5
|
||||
count: 6
|
||||
|
||||
- type: localizedDataset
|
||||
id: FigurinesClown
|
||||
values:
|
||||
prefix: figurines-clown-
|
||||
count: 5
|
||||
count: 7
|
||||
|
||||
- type: localizedDataset
|
||||
id: FigurinesHoloClown
|
||||
values:
|
||||
prefix: figurines-holoclown-
|
||||
count: 1
|
||||
count: 7
|
||||
|
||||
- type: localizedDataset
|
||||
id: FigurinesMime
|
||||
values:
|
||||
prefix: figurines-mime-
|
||||
count: 5
|
||||
count: 9
|
||||
|
||||
- type: localizedDataset
|
||||
id: FigurinesMusician
|
||||
values:
|
||||
prefix: figurines-musician-
|
||||
count: 2
|
||||
count: 5
|
||||
|
||||
- type: localizedDataset
|
||||
id: FigurinesBoxer
|
||||
values:
|
||||
prefix: figurines-boxer-
|
||||
count: 4
|
||||
count: 5
|
||||
|
||||
- type: localizedDataset
|
||||
id: FigurinesCaptain
|
||||
values:
|
||||
prefix: figurines-captain-
|
||||
count: 3
|
||||
count: 8
|
||||
|
||||
- type: localizedDataset
|
||||
id: FigurinesHoS
|
||||
values:
|
||||
prefix: figurines-hos-
|
||||
count: 3
|
||||
count: 5
|
||||
|
||||
- type: localizedDataset
|
||||
id: FigurinesWarden
|
||||
values:
|
||||
prefix: figurines-warden-
|
||||
count: 4
|
||||
count: 6
|
||||
|
||||
- type: localizedDataset
|
||||
id: FigurinesDetective
|
||||
values:
|
||||
prefix: figurines-detective-
|
||||
count: 2
|
||||
count: 5
|
||||
|
||||
- type: localizedDataset
|
||||
id: FigurinesSecurity
|
||||
values:
|
||||
prefix: figurines-security-
|
||||
count: 4
|
||||
count: 10
|
||||
|
||||
- type: localizedDataset
|
||||
id: FigurinesLawyer
|
||||
values:
|
||||
prefix: figurines-lawyer-
|
||||
count: 2
|
||||
count: 5
|
||||
|
||||
- type: localizedDataset
|
||||
id: FigurinesCargoTech
|
||||
values:
|
||||
prefix: figurines-cargotech-
|
||||
count: 3
|
||||
count: 6
|
||||
|
||||
- type: localizedDataset
|
||||
id: FigurinesSalvage
|
||||
values:
|
||||
prefix: figurines-salvage-
|
||||
count: 1
|
||||
count: 6
|
||||
|
||||
- type: localizedDataset
|
||||
id: FigurinesQM
|
||||
values:
|
||||
prefix: figurines-qm-
|
||||
count: 7
|
||||
count: 9
|
||||
|
||||
- type: localizedDataset
|
||||
id: FigurinesCE
|
||||
values:
|
||||
prefix: figurines-ce-
|
||||
count: 5
|
||||
count: 6
|
||||
|
||||
- type: localizedDataset
|
||||
id: FigurinesEngineer
|
||||
values:
|
||||
prefix: figurines-engineer-
|
||||
count: 3
|
||||
count: 7
|
||||
|
||||
- type: localizedDataset
|
||||
id: FigurinesAtmosTech
|
||||
values:
|
||||
prefix: figurines-atmostech-
|
||||
count: 5
|
||||
count: 7
|
||||
|
||||
- type: localizedDataset
|
||||
id: FigurinesRD
|
||||
values:
|
||||
prefix: figurines-rd-
|
||||
count: 2
|
||||
count: 6
|
||||
|
||||
- type: localizedDataset
|
||||
id: FigurinesScientist
|
||||
values:
|
||||
prefix: figurines-scientist-
|
||||
count: 5
|
||||
count: 6
|
||||
|
||||
- type: localizedDataset
|
||||
id: FigurinesCMO
|
||||
values:
|
||||
prefix: figurines-cmo-
|
||||
count: 2
|
||||
count: 5
|
||||
|
||||
- type: localizedDataset
|
||||
id: FigurinesChemist
|
||||
values:
|
||||
prefix: figurines-chemist-
|
||||
count: 1
|
||||
count: 6
|
||||
|
||||
- type: localizedDataset
|
||||
id: FigurinesParamedic
|
||||
values:
|
||||
prefix: figurines-paramedic-
|
||||
count: 2
|
||||
count: 5
|
||||
|
||||
- type: localizedDataset
|
||||
id: FigurinesDoctor
|
||||
values:
|
||||
prefix: figurines-doctor-
|
||||
count: 3
|
||||
count: 5
|
||||
|
||||
- type: localizedDataset
|
||||
id: FigurinesLibrarian
|
||||
values:
|
||||
prefix: figurines-librarian-
|
||||
count: 2
|
||||
count: 14
|
||||
|
||||
- type: localizedDataset
|
||||
id: FigurinesChaplain
|
||||
values:
|
||||
prefix: figurines-chaplain-
|
||||
count: 3
|
||||
count: 6
|
||||
|
||||
- type: localizedDataset
|
||||
id: FigurinesChef
|
||||
values:
|
||||
prefix: figurines-chef-
|
||||
count: 1
|
||||
count: 6
|
||||
|
||||
- type: localizedDataset
|
||||
id: FigurinesBartender
|
||||
values:
|
||||
prefix: figurines-bartender-
|
||||
count: 2
|
||||
count: 7
|
||||
|
||||
- type: localizedDataset
|
||||
id: FigurinesBotanist
|
||||
values:
|
||||
prefix: figurines-botanist-
|
||||
count: 2
|
||||
count: 5
|
||||
|
||||
- type: localizedDataset
|
||||
id: FigurinesJanitor
|
||||
values:
|
||||
prefix: figurines-janitor-
|
||||
count: 2
|
||||
count: 9
|
||||
|
||||
- type: localizedDataset
|
||||
id: FigurinesNukie
|
||||
values:
|
||||
prefix: figurines-nukie-
|
||||
count: 4
|
||||
count: 5
|
||||
|
||||
- type: localizedDataset
|
||||
id: FigurinesNukieElite
|
||||
values:
|
||||
prefix: figurines-nukie-elite-
|
||||
count: 3
|
||||
count: 6
|
||||
|
||||
- type: localizedDataset
|
||||
id: FigurinesNukieCommander
|
||||
values:
|
||||
prefix: figurines-nukie-commander-
|
||||
count: 1
|
||||
count: 5
|
||||
|
||||
- type: localizedDataset
|
||||
id: FigurinesFootsoldier
|
||||
|
|
@ -218,31 +218,31 @@
|
|||
id: FigurinesWizard
|
||||
values:
|
||||
prefix: figurines-wizard-
|
||||
count: 1
|
||||
count: 4
|
||||
|
||||
- type: localizedDataset
|
||||
id: FigurinesSpaceDragon
|
||||
values:
|
||||
prefix: figurines-space-dragon-
|
||||
count: 1
|
||||
count: 5
|
||||
|
||||
# - type: localizedDataset # TODO add something
|
||||
# id: FigurinesQueen
|
||||
# values:
|
||||
# prefix: figurines-queen-
|
||||
# count: 0
|
||||
- type: localizedDataset
|
||||
id: FigurinesQueen
|
||||
values:
|
||||
prefix: figurines-queen-
|
||||
count: 6
|
||||
|
||||
- type: localizedDataset
|
||||
id: FigurinesRatKing
|
||||
values:
|
||||
prefix: figurines-rat-king-
|
||||
count: 3
|
||||
count: 6
|
||||
|
||||
- type: localizedDataset
|
||||
id: FigurinesRatServant
|
||||
values:
|
||||
prefix: figurines-rat-servant-
|
||||
count: 2
|
||||
count: 5
|
||||
|
||||
- type: localizedDataset
|
||||
id: FigurinesMouse
|
||||
|
|
@ -254,7 +254,7 @@
|
|||
id: FigurinesSlime
|
||||
values:
|
||||
prefix: figurines-slime-
|
||||
count: 3
|
||||
count: 4
|
||||
|
||||
- type: localizedDataset
|
||||
id: FigurinesHamlet
|
||||
|
|
|
|||
120
Resources/Prototypes/Entities/Clothing/Back/smuggler.yml
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
- type: entity
|
||||
abstract: true
|
||||
id: BaseSubfloorAnchorStorage
|
||||
components:
|
||||
- type: Appearance
|
||||
- type: SubFloorHide
|
||||
- type: Anchorable
|
||||
- type: CollideOnAnchor
|
||||
- type: Transform
|
||||
anchored: false
|
||||
- type: AnchoredStorageFilter
|
||||
blacklist:
|
||||
components:
|
||||
- HumanoidAppearance # for forks with felines
|
||||
- type: BlockAnchorOn
|
||||
blacklist:
|
||||
components:
|
||||
- AnchoredStorageFilter
|
||||
- type: Visibility
|
||||
layer: 1
|
||||
|
||||
- type: entity
|
||||
abstract: true
|
||||
parent: BaseSubfloorAnchorStorage
|
||||
id: BaseSubfloorAnchorStorageAnchored
|
||||
placement:
|
||||
mode: SnapgridCenter
|
||||
components:
|
||||
- type: Transform
|
||||
anchored: true
|
||||
- type: Physics
|
||||
canCollide: false
|
||||
bodyType: Static
|
||||
|
||||
- type: entity
|
||||
parent: [ BaseSubfloorAnchorStorageAnchored, ClothingBackpackSatchel, BaseMinorContraband ]
|
||||
id: ClothingBackpackSatchelSmuggler
|
||||
name: smuggler's satchel
|
||||
suffix: Empty
|
||||
description: A dingy, suspicious looking satchel.
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: Clothing/Back/Satchels/smuggler.rsi
|
||||
state: icon
|
||||
|
||||
- type: entity
|
||||
parent: [ BaseSubfloorAnchorStorage, ClothingBackpackSatchel, BaseMinorContraband ]
|
||||
id: ClothingBackpackSatchelSmugglerUnanchored
|
||||
name: smuggler's satchel
|
||||
suffix: Empty, Unanchored
|
||||
description: A dingy, suspicious looking satchel.
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: Clothing/Back/Satchels/smuggler.rsi
|
||||
state: icon
|
||||
|
||||
- type: entity
|
||||
parent: [ BaseSubfloorAnchorStorageAnchored, BriefcaseSyndie, BaseMinorContraband ]
|
||||
id: BriefcaseSmugglerCash
|
||||
name: smuggler's briefcase
|
||||
suffix: Smuggler, Do Not Map
|
||||
components:
|
||||
- type: EntityTableContainerFill
|
||||
containers:
|
||||
storagebase: !type:AllSelector
|
||||
children:
|
||||
- id: SpaceCash5000
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 11
|
||||
|
||||
- type: entity
|
||||
parent: [ BaseSubfloorAnchorStorageAnchored, ClothingBackpackDuffelClown, BaseMinorContraband ]
|
||||
id: ClothingBackpackDuffelClownSmuggler
|
||||
name: smuggler's clown duffel bag
|
||||
suffix: Smuggler, Do Not Map
|
||||
components:
|
||||
- type: EntityTableContainerFill
|
||||
containers:
|
||||
storagebase: !type:AllSelector
|
||||
children:
|
||||
- id: SpeedLoaderCap
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 4, 8
|
||||
- !type:GroupSelector
|
||||
children:
|
||||
- id: RevolverCapGun
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 4, 8
|
||||
weight: 95
|
||||
- id: RevolverCapGunFake
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 8
|
||||
weight: 5
|
||||
|
||||
- type: entity
|
||||
parent: ClothingBackpackSatchelSmuggler
|
||||
id: ClothingBackpackSatchelSmugglerFilled
|
||||
suffix: Smuggler, Do Not Map
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: Clothing/Back/Satchels/smuggler.rsi
|
||||
state: icon
|
||||
- type: EntityTableContainerFill
|
||||
containers:
|
||||
storagebase: !type:NestedSelector
|
||||
tableId: FillSmugglerBackpack
|
||||
|
||||
- type: entity
|
||||
parent: MarkerBase
|
||||
id: RandomSatchelSpawner
|
||||
name: random smuggler's satchel spawner
|
||||
suffix: Do Not Map
|
||||
components:
|
||||
- type: Sprite
|
||||
layers:
|
||||
- sprite: Clothing/Back/Satchels/smuggler.rsi
|
||||
state: icon
|
||||
- type: EntityTableSpawner
|
||||
table: !type:NestedSelector
|
||||
tableId: RandomSatchelTable
|
||||
671
Resources/Prototypes/Entities/Clothing/Back/smuggler_tables.yml
Normal file
|
|
@ -0,0 +1,671 @@
|
|||
#Table
|
||||
- type: entityTable
|
||||
id: RandomSatchelTable
|
||||
table: !type:GroupSelector
|
||||
children:
|
||||
- id: ClothingBackpackSatchelSmugglerFilled
|
||||
weight: 85
|
||||
- id: BriefcaseSmugglerCash
|
||||
weight: 10
|
||||
- id: ClothingBackpackDuffelClownSmuggler
|
||||
weight: 5
|
||||
|
||||
#Table
|
||||
- type: entityTable
|
||||
id: FillSmugglerBackpack
|
||||
table: !type:GroupSelector
|
||||
children:
|
||||
- !type:NestedSelector
|
||||
tableId: RandomSatchelTable1
|
||||
- !type:NestedSelector
|
||||
tableId: RandomSatchelTable2
|
||||
- !type:NestedSelector
|
||||
tableId: RandomSatchelTable3
|
||||
- !type:NestedSelector
|
||||
tableId: RandomSatchelTable4
|
||||
- !type:NestedSelector
|
||||
tableId: RandomSatchelTable5
|
||||
- !type:NestedSelector
|
||||
tableId: RandomSatchelTable6
|
||||
|
||||
- type: entityTable
|
||||
id: RandomSatchelTable1
|
||||
table: !type:AllSelector
|
||||
children:
|
||||
- !type:NestedSelector
|
||||
tableId: RandomSatchelGenericTable
|
||||
- !type:NestedSelector
|
||||
tableId: RandomSatchelFunnyTable
|
||||
- !type:NestedSelector
|
||||
tableId: RandomSatchelClothingTable
|
||||
- !type:NestedSelector
|
||||
tableId: RandomSatchelCannabisTable
|
||||
- !type:NestedSelector
|
||||
tableId: RandomSatchelGizmosTable
|
||||
- !type:NestedSelector
|
||||
tableId: RandomSatchelChemsTable
|
||||
|
||||
- type: entityTable
|
||||
id: RandomSatchelFunnyTable
|
||||
table: !type:GroupSelector
|
||||
children:
|
||||
- id: SpaceCash100
|
||||
weight: 20
|
||||
- id: WhoopieCushion
|
||||
- id: RubberChicken
|
||||
- id: PlasticBanana
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 5
|
||||
- id: PillSpaceDrugs
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 2
|
||||
- id: StrangePill
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 5
|
||||
|
||||
- type: entityTable
|
||||
id: RandomSatchelCannabisTable
|
||||
table: !type:GroupSelector
|
||||
children:
|
||||
- id: SpaceCash100
|
||||
weight: 4
|
||||
- id: Joint
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 5
|
||||
- id: Blunt
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 5
|
||||
- id: SmokingPipeFilledCannabis
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 5
|
||||
- id: GroundCannabis
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 15
|
||||
|
||||
- type: entityTable
|
||||
id: RandomSatchelGizmosTable
|
||||
table: !type:GroupSelector
|
||||
children:
|
||||
- id: SpaceCash100
|
||||
weight: 40
|
||||
- id: TimerTrigger
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 2
|
||||
- id: SignalTrigger
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 2
|
||||
- id: VoiceTrigger
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 2
|
||||
- id: ProximitySensor
|
||||
|
||||
- type: entityTable
|
||||
id: RandomSatchelChemsTable
|
||||
table: !type:GroupSelector
|
||||
children:
|
||||
- id: SpaceCash100
|
||||
weight: 20
|
||||
- id: ChemistryBottleUnstableMutagen
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 5
|
||||
- id: ChemistryBottleLeft4Zed
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 5
|
||||
- id: ChemistryBottleEZNutrient
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 5
|
||||
- id: ChemistryBottleRobustHarvest
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 5
|
||||
- id: ChemistryBottleEpinephrine
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 5
|
||||
- id: ChemistryBottleEphedrine
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 5
|
||||
- id: ChemistryBottleOmnizine
|
||||
- id: ChemistryBottleCognizine
|
||||
- id: ChemistryBottleToxin
|
||||
- id: ChemistryBottleNocturine
|
||||
- id: VestineChemistryVial
|
||||
|
||||
- type: entityTable
|
||||
id: RandomSatchelTable2
|
||||
table: !type:AllSelector
|
||||
children:
|
||||
- !type:NestedSelector
|
||||
tableId: RandomSatchelTobaccoTable
|
||||
- !type:NestedSelector
|
||||
tableId: RandomSatchelPartyTable
|
||||
- !type:NestedSelector
|
||||
tableId: RandomSatchelClothingTable
|
||||
- !type:NestedSelector
|
||||
tableId: RandomSatchelPayloadTable
|
||||
- !type:NestedSelector
|
||||
tableId: RandomSatchelCircuitboardsTable
|
||||
|
||||
- type: entityTable
|
||||
id: RandomSatchelTobaccoTable
|
||||
table: !type:GroupSelector
|
||||
children:
|
||||
- id: SpaceCash100
|
||||
weight: 8
|
||||
- id: CigPackSyndicate
|
||||
weight: 0.5
|
||||
- id: CigCartonGreen
|
||||
- id: CigCartonRed
|
||||
- id: CigCartonGreen
|
||||
- id: CigCartonBlack
|
||||
- id: CigarCase
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 2
|
||||
- id: CigarGoldCase
|
||||
weight: 0.25
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 2
|
||||
|
||||
- type: entityTable
|
||||
id: RandomSatchelPartyTable
|
||||
table: !type:GroupSelector
|
||||
children:
|
||||
- id: SpaceCash100
|
||||
weight: 2
|
||||
- id: GlowstickBase
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 5
|
||||
- id: GlowstickRed
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 5
|
||||
- id: GlowstickPurple
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 5
|
||||
- id: GlowstickYellow
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 5
|
||||
- id: GlowstickBlue
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 5
|
||||
|
||||
- type: entityTable
|
||||
id: RandomSatchelClothingTable
|
||||
table: !type:GroupSelector
|
||||
children:
|
||||
- id: SpaceCash100
|
||||
weight: 3
|
||||
- id: ClothingEyesGlassesOutlawGlasses
|
||||
- id: ClothingEyesEyepatch
|
||||
- id: ClothingHandsGlovesNitrile
|
||||
- id: ClothingHeadHatOutlawHat
|
||||
- id: ClothingMaskItalianMoustache
|
||||
- id: ClothingHandsGlovesCombat
|
||||
- id: ClothingNeckScarfStripedSyndieRed
|
||||
|
||||
- type: entityTable
|
||||
id: RandomSatchelPayloadTable
|
||||
table: !type:GroupSelector
|
||||
children:
|
||||
- id: SpaceCash100
|
||||
weight: 45
|
||||
- id: FlashPayload
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 2
|
||||
- id: ChemicalPayload
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 2
|
||||
- id: ExplosivePayload
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 2
|
||||
|
||||
- type: entityTable
|
||||
id: RandomSatchelCircuitboardsTable
|
||||
table: !type:GroupSelector
|
||||
children:
|
||||
- id: SpaceCash100
|
||||
weight: 15
|
||||
- id: ChemDispenserMachineCircuitboard
|
||||
- id: SyndicateMicrowaveMachineCircuitboard
|
||||
- id: HydroponicsTrayMachineCircuitboard
|
||||
- id: DawInstrumentMachineCircuitboard
|
||||
- id: PortableGeneratorPacmanMachineCircuitboard
|
||||
- id: PortableGeneratorSuperPacmanMachineCircuitboard
|
||||
- id: HellfireFreezerMachineCircuitBoard
|
||||
- id: HellfireHeaterMachineCircuitBoard
|
||||
- id: ReagentGrinderMachineCircuitboard
|
||||
- id: ReagentGrinderIndustrialMachineCircuitboard
|
||||
- id: BoozeDispenserMachineCircuitboard
|
||||
- id: MiniGravityGeneratorCircuitboard
|
||||
- id: AmmoTechFabCircuitboard
|
||||
- id: CryoPodMachineCircuitboard
|
||||
- id: PowerCageRechargerCircuitboard
|
||||
- id: ShuttleConsoleCircuitboard
|
||||
- id: TurboItemRechargerCircuitboard
|
||||
|
||||
- type: entityTable
|
||||
id: RandomSatchelTable3
|
||||
table: !type:AllSelector
|
||||
children:
|
||||
- !type:NestedSelector
|
||||
tableId: RandomSatchelPresentsOrToysTable
|
||||
- !type:NestedSelector
|
||||
tableId: RandomSatchelCashTable
|
||||
- !type:NestedSelector
|
||||
tableId: RandomSatchelWeaponTable
|
||||
- !type:NestedSelector
|
||||
tableId: RandomSatchelBurgerTable
|
||||
- !type:NestedSelector
|
||||
tableId: RandomSatchelGenericTable
|
||||
- !type:NestedSelector
|
||||
tableId: RandomSatchelKeysTable
|
||||
|
||||
- type: entityTable
|
||||
id: RandomSatchelPresentsOrToysTable
|
||||
table: !type:GroupSelector
|
||||
children:
|
||||
- id: SpaceCash100
|
||||
weight: 5
|
||||
- id: PresentRandom
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 3
|
||||
- id: ToyFigurineHamlet
|
||||
- id: ToyFigurineSpaceDragon
|
||||
- id: ToyFigurineQueen
|
||||
- id: ToyFigurineRatKing
|
||||
|
||||
- type: entityTable
|
||||
id: RandomSatchelCashTable
|
||||
table: !type:GroupSelector
|
||||
children:
|
||||
- id: SpaceCash100
|
||||
weight: 2
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 4
|
||||
- id: SpaceCash1000
|
||||
weight: 2
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 4
|
||||
- id: SpaceCash2500
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 4
|
||||
- id: SpaceCash5000
|
||||
weight: 0.25
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 4
|
||||
- id: SpaceCash10000
|
||||
weight: 0.005
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 4
|
||||
- id: SpaceCash1000000
|
||||
prob: 0.0001
|
||||
- id: SpaceCash
|
||||
weight: 0.01
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 10
|
||||
|
||||
- type: entityTable
|
||||
id: RandomSatchelWeaponTable
|
||||
table: !type:GroupSelector
|
||||
children:
|
||||
- id: SpaceCash100
|
||||
weight: 100
|
||||
- id: Katana
|
||||
- id: ThrowingStar
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 5
|
||||
|
||||
- type: entityTable
|
||||
id: RandomSatchelBurgerTable
|
||||
table: !type:GroupSelector
|
||||
children:
|
||||
- id: SpaceCash100
|
||||
weight: 10
|
||||
- id: FoodBurgerAppendix
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 5
|
||||
- id: FoodBurgerEmpowered
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 5
|
||||
- id: FoodBurgerClown
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 5
|
||||
- id: FoodBurgerGhost
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 5
|
||||
|
||||
- type: entityTable
|
||||
id: RandomSatchelGenericTable
|
||||
table: !type:GroupSelector
|
||||
children:
|
||||
- id: SpaceCash100
|
||||
weight: 15
|
||||
- id: RemoteSignaller
|
||||
- id: PersonalAI
|
||||
- id: WeaponFlareGun
|
||||
- id: ModularReceiver
|
||||
- id: RifleStock
|
||||
- id: DrinkSpaceGlue
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 2
|
||||
- id: DrinkSpaceLube
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 2
|
||||
- id: CrazyGlue
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 2
|
||||
|
||||
- type: entityTable
|
||||
id: RandomSatchelKeysTable
|
||||
table: !type:GroupSelector
|
||||
children:
|
||||
- id: SpaceCash100
|
||||
weight: 50
|
||||
- id: EncryptionKeyCommon
|
||||
- id: EncryptionKeyCargo
|
||||
- id: EncryptionKeyService
|
||||
- id: EncryptionKeyRobo
|
||||
- id: EncryptionKeyScience
|
||||
- id: EncryptionKeyMedical
|
||||
- id: EncryptionKeyEngineering
|
||||
- id: EncryptionKeySecurity
|
||||
weight: 0.5
|
||||
- id: EncryptionKeyCommand
|
||||
weight: 0.25
|
||||
- id: EncryptionKeyStationMaster
|
||||
weight: 0.01
|
||||
|
||||
- type: entityTable
|
||||
id: RandomSatchelTable4
|
||||
table: !type:AllSelector
|
||||
children:
|
||||
- !type:NestedSelector
|
||||
tableId: RandomSatchelMaterialsTable
|
||||
- !type:NestedSelector
|
||||
tableId: RandomSatchelImplantersTable
|
||||
- !type:NestedSelector
|
||||
tableId: RandomSatchelCellsTable
|
||||
- !type:NestedSelector
|
||||
tableId: RandomSatchelSyndicateTable
|
||||
- !type:NestedSelector
|
||||
tableId: RandomSatchelToolsTable
|
||||
|
||||
- type: entityTable
|
||||
id: RandomSatchelMaterialsTable
|
||||
table: !type:GroupSelector
|
||||
children:
|
||||
- id: SpaceCash100
|
||||
weight: 6
|
||||
- !type:GroupSelector
|
||||
children:
|
||||
- id: MaterialDiamond1
|
||||
- !type:GroupSelector
|
||||
children:
|
||||
- id: MaterialBananium1
|
||||
weight: 2
|
||||
- id: MaterialBananium
|
||||
- !type:GroupSelector
|
||||
children:
|
||||
- id: IngotGold1
|
||||
weight: 2
|
||||
- id: IngotGold
|
||||
- !type:GroupSelector
|
||||
children:
|
||||
- id: IngotSilver1
|
||||
weight: 2
|
||||
- id: IngotSilver
|
||||
- !type:GroupSelector
|
||||
children:
|
||||
- id: SheetPlasma1
|
||||
weight: 2
|
||||
- id: SheetPlasma10
|
||||
- id: SheetPlasma
|
||||
weight: 0.50
|
||||
- !type:GroupSelector
|
||||
children:
|
||||
- id: SheetUranium1
|
||||
weight: 2
|
||||
- id: SheetUranium
|
||||
|
||||
- type: entityTable
|
||||
id: RandomSatchelImplantersTable
|
||||
table: !type:GroupSelector
|
||||
children:
|
||||
- id: SpaceCash100
|
||||
weight: 3
|
||||
- id: LightImplanter
|
||||
- id: BikeHornImplanter
|
||||
- id: SadTromboneImplanter
|
||||
|
||||
- type: entityTable
|
||||
id: RandomSatchelCellsTable
|
||||
table: !type:GroupSelector
|
||||
children:
|
||||
- id: SpaceCash100
|
||||
weight: 15
|
||||
- id: PowerCellHyper
|
||||
- id: PowerCellMicroreactor
|
||||
- id: PowerCellHigh
|
||||
|
||||
- type: entityTable
|
||||
id: RandomSatchelSyndicateTable
|
||||
table: !type:GroupSelector
|
||||
children:
|
||||
- id: SpaceCash100
|
||||
weight: 50
|
||||
- id: Telecrystal1
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 3
|
||||
- id: GatfruitSeeds
|
||||
- id: ToySword
|
||||
- id: NukeDiskFake
|
||||
- id: RadioJammer
|
||||
- id: SoapSyndie
|
||||
- id: SingularityToy
|
||||
|
||||
- type: entityTable
|
||||
id: RandomSatchelToolsTable
|
||||
table: !type:GroupSelector
|
||||
children:
|
||||
- id: Crowbar
|
||||
- id: Multitool
|
||||
- id: ClothingHandsGlovesColorYellow
|
||||
- id: Screwdriver
|
||||
- id: ClothingHeadHatWeldingMaskFlame
|
||||
- id: WelderExperimental
|
||||
weight: 0.50
|
||||
|
||||
- type: entityTable
|
||||
id: RandomSatchelTable5
|
||||
table: !type:AllSelector
|
||||
children:
|
||||
- !type:NestedSelector
|
||||
tableId: RandomSatchelAlcoholTable
|
||||
- !type:NestedSelector
|
||||
tableId: RandomSatchelInstrumentTable
|
||||
- !type:NestedSelector
|
||||
tableId: RandomSatchelMedsTable
|
||||
- !type:NestedSelector
|
||||
tableId: RandomSatchelMysteriesTable
|
||||
|
||||
- type: entityTable
|
||||
id: RandomSatchelAlcoholTable
|
||||
table: !type:GroupSelector
|
||||
children:
|
||||
- id: SpaceCash100
|
||||
weight: 5
|
||||
- id: DrinkCognacBottleFull
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 4
|
||||
- id: DrinkGildlagerBottleFull
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 4
|
||||
- id: DrinkPatronBottleFull
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 4
|
||||
- id: DrinkRumBottleFull
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 4
|
||||
|
||||
- type: entityTable
|
||||
id: RandomSatchelInstrumentTable
|
||||
table: !type:GroupSelector
|
||||
children:
|
||||
- id: SpaceCash100
|
||||
weight: 15
|
||||
- id: SeashellInstrument
|
||||
- id: MusicalLungInstrument
|
||||
- id: HelicopterInstrument
|
||||
- id: GunpetInstrument
|
||||
- id: RockGuitarInstrument
|
||||
- id: BassGuitarInstrument
|
||||
|
||||
- type: entityTable
|
||||
id: RandomSatchelMedsTable
|
||||
table: !type:GroupSelector
|
||||
children:
|
||||
- id: SpaceCash100
|
||||
weight: 10
|
||||
- id: SyringeAmbuzol
|
||||
- id: SyringeHyronalin
|
||||
- id: SyringeDermaline
|
||||
- id: SyringeBicaridine
|
||||
- id: SyringeTranexamicAcid
|
||||
- id: SyringeInaprovaline
|
||||
- id: SyringeEphedrine
|
||||
- id: Gauze
|
||||
- id: Bloodpack
|
||||
- id: RegenerativeMesh
|
||||
- id: MedicatedSuture
|
||||
- id: EmergencyMedipen
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 5
|
||||
- id: AntiPoisonMedipen
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 5
|
||||
- id: BruteAutoInjector
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 5
|
||||
- id: BurnAutoInjector
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 5
|
||||
- id: SpaceMedipen
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 5
|
||||
- id: Stimpack
|
||||
- id: CombatMedipen
|
||||
|
||||
- type: entityTable
|
||||
id: RandomSatchelMysteriesTable
|
||||
table: !type:GroupSelector
|
||||
children:
|
||||
- id: SpaceCash100
|
||||
weight: 15
|
||||
- id: EggSpider
|
||||
weight: 5
|
||||
- id: ArtifactFragment1
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 2
|
||||
weight: 10
|
||||
- id: AnomalyCorePyroclastic
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 3
|
||||
- id: AnomalyCoreGravity
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 3
|
||||
- id: AnomalyCoreIce
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 3
|
||||
- id: AnomalyCoreFlesh
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 3
|
||||
- id: AnomalyCoreLiquid
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 3
|
||||
- id: AnomalyCoreBluespace
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 3
|
||||
- id: AnomalyCoreElectricity
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 3
|
||||
- id: AnomalyCoreFlora
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 3
|
||||
- id: AnomalyCoreShadow
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 3
|
||||
|
||||
- type: entityTable
|
||||
id: RandomSatchelTable6
|
||||
table: !type:AllSelector
|
||||
children:
|
||||
- !type:NestedSelector
|
||||
tableId: RandomSatchelGearTable
|
||||
- !type:NestedSelector
|
||||
tableId: RandomSatchelGadgetsTable
|
||||
- !type:NestedSelector
|
||||
tableId: CubeTable
|
||||
|
||||
- type: entityTable
|
||||
id: RandomSatchelGearTable
|
||||
table: !type:GroupSelector
|
||||
children:
|
||||
- id: SpaceCash100
|
||||
weight: 8
|
||||
- id: JetpackMiniFilled
|
||||
- id: HandheldGPSBasic
|
||||
- id: WelderIndustrialAdvanced
|
||||
- id: HandheldStationMap
|
||||
- id: PinpointerStation
|
||||
|
||||
- type: entityTable
|
||||
id: RandomSatchelGadgetsTable
|
||||
table: !type:GroupSelector
|
||||
children:
|
||||
- id: SpaceCash100
|
||||
weight: 30
|
||||
- id: HolofanProjector
|
||||
- id: HoloprojectorField
|
||||
- id: HoloprojectorSecurity
|
||||
- id: DeviceQuantumSpinInverter
|
||||
amount: !type:ConstantNumberSelector
|
||||
value: 2
|
||||
- id: SpectralLocator
|
||||
- id: ArabianLamp
|
||||
weight: 0.50
|
||||
- id: ChameleonProjector
|
||||
weight: 0.25
|
||||
|
||||
- type: entityTable
|
||||
id: CubeTable
|
||||
table: !type:GroupSelector
|
||||
children:
|
||||
- id: SpaceCash100
|
||||
weight: 8
|
||||
- id: MonkeyCube
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 10
|
||||
- id: KoboldCube
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 10
|
||||
- id: CowCube
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 10
|
||||
- id: GoatCube
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 10
|
||||
- id: MothroachCube
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 10
|
||||
- id: MouseCube
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 10
|
||||
- id: CockroachCube
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 10
|
||||
- id: SpaceCarpCube
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 10
|
||||
- id: SpaceTickCube
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 10
|
||||
- id: AbominationCube
|
||||
amount: !type:RangeNumberSelector
|
||||
range: 1, 10
|
||||
|
|
@ -160,4 +160,5 @@
|
|||
- PosterLegitSafetyMothHardhat
|
||||
- PosterLegitSafetyMothSSD
|
||||
- PosterLegitOppenhopper
|
||||
- PosterLegitTyrone
|
||||
chance: 1
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@
|
|||
# The datafields of the components are only shallow copied using CopyComp.
|
||||
# Subscribe to CloningEvent instead if that is not enough.
|
||||
|
||||
# for basic traits etc.
|
||||
# used by the random clone spawner
|
||||
- type: cloningSettings
|
||||
id: BaseClone
|
||||
components:
|
||||
|
|
@ -59,15 +61,19 @@
|
|||
components:
|
||||
- AttachedClothing # helmets, which are part of the suit
|
||||
- HumanoidAppearance # will cause problems for downstream felinids getting cloned as Urists
|
||||
- Implanter # they will spawn full again, but you already get the implant. And we can't do item slot copying yet
|
||||
- VirtualItem
|
||||
|
||||
# all antagonist roles
|
||||
- type: cloningSettings
|
||||
id: Antag
|
||||
parent: BaseClone
|
||||
components:
|
||||
- HeadRevolutionary
|
||||
- Revolutionary
|
||||
- NukeOperative
|
||||
|
||||
# for cloning pods
|
||||
- type: cloningSettings
|
||||
id: CloningPod
|
||||
parent: Antag
|
||||
|
|
|
|||
|
|
@ -664,6 +664,9 @@
|
|||
- Meat
|
||||
- type: Sprite
|
||||
state: meatball
|
||||
- type: Construction
|
||||
graph: MeatMeatballCooked
|
||||
node: start
|
||||
|
||||
- type: entity
|
||||
name: slimeball
|
||||
|
|
@ -1178,6 +1181,9 @@
|
|||
entries:
|
||||
Burger: MeatBall
|
||||
Taco: MeatBall
|
||||
- type: Construction
|
||||
graph: MeatMeatballCooked
|
||||
node: meatball cooked
|
||||
|
||||
- type: entity
|
||||
name: boiled snail
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
tags:
|
||||
- Figurine
|
||||
- type: UseDelay
|
||||
delay: 10
|
||||
delay: 5
|
||||
- type: TriggerOnActivate
|
||||
- type: TriggerOnSignal
|
||||
- type: Speech
|
||||
|
|
|
|||
|
|
@ -1078,6 +1078,14 @@
|
|||
- type: Sprite
|
||||
state: poster53_legit
|
||||
|
||||
- type: entity
|
||||
parent: PosterBase
|
||||
id: PosterLegitTyrone
|
||||
name: "Tyrone's Guide to Space"
|
||||
description: "A poster advertising online schooling about space. The classes listed seem to cover things from the basic usage of station equipment to complicated subjects like creating pipebombs or covering entire hallways in spacelube. A disclaimer reads \"It's never THAT bad, and at the end you might even get a tortilla.\""
|
||||
components:
|
||||
- type: Sprite
|
||||
state: poster54_legit
|
||||
|
||||
#maps
|
||||
|
||||
|
|
|
|||
|
|
@ -34,8 +34,6 @@
|
|||
- type: Damageable
|
||||
damageContainer: StructuralInorganic
|
||||
damageModifierSet: PerforatedMetallic
|
||||
- type: PowerConsumer
|
||||
showInMonitor: false
|
||||
- type: Electrified
|
||||
requirePower: true
|
||||
noWindowInTile: true
|
||||
|
|
|
|||
|
|
@ -655,6 +655,19 @@
|
|||
blacklist:
|
||||
- External # don't space everything
|
||||
|
||||
- type: entity
|
||||
parent: BaseGameRule
|
||||
id: SmugglerStashVariationPass
|
||||
components:
|
||||
- type: StationEvent
|
||||
earliestStart: 0
|
||||
duration: 1
|
||||
minimumPlayers: 1
|
||||
maxOccurrences: 2
|
||||
weight: 10
|
||||
- type: RandomSpawnRule
|
||||
prototype: RandomSatchelSpawner
|
||||
|
||||
- type: entity
|
||||
parent: BaseGameRule
|
||||
id: DerelictCyborgSpawn
|
||||
|
|
|
|||
|
|
@ -478,3 +478,5 @@
|
|||
- id: BloodbathPuddleMessVariationPass
|
||||
prob: 0.01
|
||||
orGroup: puddleMess
|
||||
- id: SmugglerStashVariationPass
|
||||
prob: 0.90
|
||||
|
|
|
|||
|
|
@ -667,6 +667,7 @@
|
|||
production: 6
|
||||
yield: 2
|
||||
potency: 20
|
||||
growthStages: 3
|
||||
idealLight: 9
|
||||
idealHeat: 298
|
||||
chemicals:
|
||||
|
|
|
|||
|
|
@ -539,6 +539,9 @@
|
|||
metamorphicFillBaseName: fill-
|
||||
metamorphicChangeColor: false
|
||||
|
||||
#Antifreeze as a drink is a bright blue color while it is an orangish red otherwise.
|
||||
#As a result this sprite doesn't need changing.
|
||||
|
||||
- type: reagent
|
||||
id: Antifreeze
|
||||
name: reagent-name-antifreeze
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
- type: constructionGraph
|
||||
id: MeatMeatballCooked
|
||||
start: start
|
||||
graph:
|
||||
|
||||
- node: start
|
||||
edges:
|
||||
- to: meatball cooked
|
||||
completed:
|
||||
- !type:PlaySound
|
||||
sound: /Audio/Effects/sizzle.ogg
|
||||
steps:
|
||||
- minTemperature: 335
|
||||
|
||||
- node: meatball cooked
|
||||
entity: FoodMeatMeatballCooked
|
||||
|
|
@ -15,6 +15,7 @@
|
|||
antag: true
|
||||
antagPrototype: GenericAntagonist
|
||||
roleType: SoloAntagonist
|
||||
sortWeight: 50
|
||||
|
||||
#Observer
|
||||
- type: entity
|
||||
|
|
@ -23,6 +24,8 @@
|
|||
name: Observer Role
|
||||
components:
|
||||
- type: ObserverRole
|
||||
- type: MindRole
|
||||
sortWeight: -10
|
||||
|
||||
#Ghost Roles
|
||||
- type: entity
|
||||
|
|
@ -48,6 +51,7 @@
|
|||
- type: MindRole
|
||||
roleType: FreeAgent
|
||||
antagPrototype: GenericFreeAgent
|
||||
sortWeight: 30
|
||||
|
||||
- type: entity
|
||||
parent: MindRoleGhostRoleNeutral
|
||||
|
|
@ -56,6 +60,7 @@
|
|||
components:
|
||||
- type: MindRole
|
||||
roleType: FreeAgent
|
||||
sortWeight: 0 # Maybe 10?
|
||||
|
||||
- type: entity
|
||||
parent: MindRoleGhostRoleNeutral
|
||||
|
|
@ -73,6 +78,7 @@
|
|||
- type: MindRole
|
||||
roleType: SiliconAntagonist
|
||||
antagPrototype: GenericSiliconAntagonist
|
||||
sortWeight: 30
|
||||
|
||||
- type: entity
|
||||
parent: [ BaseMindRoleAntag, MindRoleGhostRoleNeutral ]
|
||||
|
|
@ -94,6 +100,9 @@
|
|||
parent: MindRoleGhostRoleTeamAntagonist
|
||||
id: MindRoleGhostRoleTeamAntagonistFlock
|
||||
name: Ghost Role (Team Antagonist)
|
||||
components:
|
||||
- type: MindRole
|
||||
sortWeight: 40
|
||||
|
||||
# The Job MindRole holds the mob's Job prototype
|
||||
- type: entity
|
||||
|
|
|
|||
|
|
@ -5,34 +5,40 @@
|
|||
id: Neutral
|
||||
name: role-type-crew-aligned-name
|
||||
color: '#eeeeee'
|
||||
symbol: "🗡" # Should never be antag, but just in case.
|
||||
|
||||
- type: roleType
|
||||
id: SoloAntagonist
|
||||
name: role-type-solo-antagonist-name
|
||||
color: '#d82000'
|
||||
symbol: "🗡"
|
||||
|
||||
- type: roleType
|
||||
id: TeamAntagonist
|
||||
name: role-type-team-antagonist-name
|
||||
color: '#d82000'
|
||||
symbol: "⚔"
|
||||
|
||||
- type: roleType
|
||||
id: FreeAgent
|
||||
name: role-type-free-agent-name
|
||||
color: '#ffff00'
|
||||
symbol: "☯"
|
||||
|
||||
- type: roleType
|
||||
id: Familiar
|
||||
name: role-type-familiar-name
|
||||
color: '#6495ed'
|
||||
symbol: "🗡" # Should never be antag, but just in case.
|
||||
|
||||
- type: roleType
|
||||
id: Silicon
|
||||
name: role-type-silicon-name
|
||||
color: '#6495ed'
|
||||
symbol: "🗡" # Should never be antag, but just in case.
|
||||
|
||||
- type: roleType
|
||||
id: SiliconAntagonist
|
||||
name: role-type-silicon-antagonist-name
|
||||
color: '#c832e6'
|
||||
|
||||
symbol: "⛞"
|
||||
|
|
|
|||
|
After Width: | Height: | Size: 308 B |
BIN
Resources/Textures/Clothing/Back/Satchels/smuggler.rsi/icon.png
Normal file
|
After Width: | Height: | Size: 348 B |
|
After Width: | Height: | Size: 577 B |
|
After Width: | Height: | Size: 489 B |
|
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"version": 1,
|
||||
"license": "CC-BY-SA-3.0",
|
||||
"copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/a8056c6ba7f5367934ef829116e57d743226e1f0",
|
||||
"size": {
|
||||
"x": 32,
|
||||
"y": 32
|
||||
},
|
||||
"states": [
|
||||
{
|
||||
"name": "icon"
|
||||
},
|
||||
{
|
||||
"name": "equipped-BACKPACK",
|
||||
"directions": 4
|
||||
},
|
||||
{
|
||||
"name": "inhand-left",
|
||||
"directions": 4
|
||||
},
|
||||
{
|
||||
"name": "inhand-right",
|
||||
"directions": 4
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
Resources/Textures/Interface/Misc/job_icons.rsi/ParadoxClone.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
|
|
@ -1,7 +1,8 @@
|
|||
{
|
||||
"version": 1,
|
||||
"license": "CC-BY-SA-3.0",
|
||||
"copyright": "Taken from https://github.com/vgstation-coders/vgstation13/blob/e71d6c4fba5a51f99b81c295dcaec4fc2f58fb19/icons/mob/screen1.dmi | Brigmedic icon made by PuroSlavKing (Github) | Zombie icon made by RamZ | Zookeper by netwy (discort) | Rev and Head Rev icon taken from https://tgstation13.org/wiki/HUD and edited by coolmankid12345 (Discord) | Mindshield icon taken from https://github.com/tgstation/tgstation/blob/master/icons/mob/huds/hud.dmi | Pilot icon made by poemota (Discord)",
|
||||
"copyright": "Taken from https://github.com/vgstation-coders/vgstation13/blob/e71d6c4fba5a51f99b81c295dcaec4fc2f58fb19/icons/mob/screen1.dmi | Brigmedic icon made by PuroSlavKing (Github) | Zombie icon made by RamZ | Zookeper by netwy (discort) | Rev and Head Rev icon taken from https://tgstation13.org/wiki/HUD and edited by coolmankid12345 (Discord) | Mindshield icon taken from https://github.com/tgstation/tgstation/blob/ce6beb8a4d61235d9a597a7126c407160ed674ea/icons/mob/huds/hud.dmi | Admin recolored from MedicalIntern by TsjipTsjip | StationAi resprite to 8x8 size by lunarcomets | Service Worker resprite by anno_midi (Discord) and spanky-spanky (Github) | service icons darkened by frobnic8 (Discord and Github)",
|
||||
|
||||
"size": {
|
||||
"x": 8,
|
||||
"y": 8
|
||||
|
|
@ -203,6 +204,9 @@
|
|||
{
|
||||
"name": "Admin"
|
||||
},
|
||||
{
|
||||
"name": "ParadoxClone"
|
||||
},
|
||||
{
|
||||
"name": "Changeling"
|
||||
},
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 738 B After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 988 B After Width: | Height: | Size: 20 KiB |
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"version": 1,
|
||||
"license": "CC-BY-SA-3.0",
|
||||
"copyright": "Taken from https://github.com/tgstation/tgstation/commit/40d89d11ea4a5cb81d61dc1018b46f4e7d32c62a, inhands by mubururu_ (github)",
|
||||
"copyright": "Taken from https://github.com/tgstation/tgstation/commit/40d89d11ea4a5cb81d61dc1018b46f4e7d32c62a, inhands by mubururu_ (github), Growth stages, harvest, dead, and produce sprites created by Chaoticaa (GitHub)",
|
||||
"size": {
|
||||
"x": 32,
|
||||
"y": 32
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 461 B After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 623 B After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 691 B After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 749 B After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 824 B After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 980 B After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 390 B After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 668 B After Width: | Height: | Size: 20 KiB |
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"version": 1,
|
||||
"license": "CC-BY-SA-3.0",
|
||||
"copyright": "Taken from https://github.com/vgstation-coders/vgstation13/commit/1dbcf389b0ec6b2c51b002df5fef8dd1519f8068, inhands by mubururu_ (github)",
|
||||
"copyright": "Taken from https://github.com/vgstation-coders/vgstation13/commit/1dbcf389b0ec6b2c51b002df5fef8dd1519f8068, inhands by mubururu_ (github), Growth stages, harvest, dead, and produce sprites created by Chaoticaa (GitHub)",
|
||||
"size": {
|
||||
"x": 32,
|
||||
"y": 32
|
||||
|
|
|
|||