Merge remote-tracking branch 'upstream/master' into upstream-sync

# Conflicts:
#	Resources/Prototypes/Datasets/Names/skeleton_first.yml
This commit is contained in:
Morb0 2023-08-22 08:22:56 +03:00
commit 4516e1ceef
70 changed files with 514 additions and 343 deletions

View file

@ -34,7 +34,7 @@ public sealed class BackgroundAudioSystem : EntitySystem
_client.PlayerLeaveServer += OnLeave;
_gameTicker.LobbyStatusUpdated += LobbySongReceived;
_gameTicker.LobbySongUpdated += LobbySongUpdated;
}
public override void Shutdown()
@ -48,7 +48,7 @@ public sealed class BackgroundAudioSystem : EntitySystem
_client.PlayerLeaveServer -= OnLeave;
_gameTicker.LobbyStatusUpdated -= LobbySongReceived;
_gameTicker.LobbySongUpdated -= LobbySongUpdated;
EndLobbyMusic();
}
@ -95,17 +95,9 @@ public sealed class BackgroundAudioSystem : EntitySystem
}
}
private void LobbySongReceived()
private void LobbySongUpdated()
{
if (_lobbyStream != null) //Toggling Ready status fires this method. This check ensures we only start the lobby music if it's not playing.
{
return;
}
if (_stateManager.CurrentState is LobbyState)
{
StartLobbyMusic();
}
RestartLobbyMusic();
}
public void RestartLobbyMusic()

View file

@ -20,6 +20,8 @@ using Content.Shared.Eye.Blinding.Components;
using Robust.Client;
using static Content.Shared.Interaction.SharedInteractionSystem;
using static Robust.Client.UserInterface.Controls.BoxContainer;
using Content.Shared.Interaction.Events;
using Content.Shared.Item;
namespace Content.Client.Examine
{
@ -50,6 +52,8 @@ namespace Content.Client.Examine
SubscribeNetworkEvent<ExamineSystemMessages.ExamineInfoResponseMessage>(OnExamineInfoResponse);
SubscribeLocalEvent<ItemComponent, DroppedEvent>(OnExaminedItemDropped);
CommandBinds.Builder
.Bind(ContentKeyFunctions.ExamineEntity, new PointerInputCmdHandler(HandleExamine, outsidePrediction: true))
.Register<ExamineSystem>();
@ -57,6 +61,19 @@ namespace Content.Client.Examine
_idCounter = 0;
}
private void OnExaminedItemDropped(EntityUid item, ItemComponent comp, DroppedEvent args)
{
if (!args.User.Valid)
return;
if (_playerManager.LocalPlayer == null)
return;
if (_examineTooltipOpen == null)
return;
if (item == _examinedEntity && args.User == _playerManager.LocalPlayer.ControlledEntity)
CloseTooltip();
}
public override void Update(float frameTime)
{
if (_examineTooltipOpen is not {Visible: true}) return;

View file

@ -1,4 +1,3 @@
using Content.Client.Audio;
using Content.Client.Gameplay;
using Content.Client.Lobby;
using Content.Client.RoundEnd;
@ -8,7 +7,6 @@ using Content.Shared.GameWindow;
using JetBrains.Annotations;
using Robust.Client.Graphics;
using Robust.Client.State;
using Robust.Shared.Audio;
using Robust.Shared.Configuration;
using Robust.Shared.Player;
using Robust.Shared.Utility;
@ -21,7 +19,6 @@ namespace Content.Client.GameTicking.Managers
[Dependency] private readonly IStateManager _stateManager = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IConfigurationManager _configManager = default!;
[Dependency] private readonly BackgroundAudioSystem _backgroundAudio = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[ViewVariables] private bool _initialized;
@ -49,6 +46,7 @@ namespace Content.Client.GameTicking.Managers
public event Action? InfoBlobUpdated;
public event Action? LobbyStatusUpdated;
public event Action? LobbySongUpdated;
public event Action? LobbyLateJoinStatusUpdated;
public event Action<IReadOnlyDictionary<EntityUid, Dictionary<string, uint?>>>? LobbyJobsAvailableUpdated;
@ -73,6 +71,16 @@ namespace Content.Client.GameTicking.Managers
_initialized = true;
}
public void SetLobbySong(string? song, bool forceUpdate = false)
{
var updated = song != LobbySong;
LobbySong = song;
if (updated || forceUpdate)
LobbySongUpdated?.Invoke();
}
private void LateJoinStatus(TickerLateJoinStatusEvent message)
{
DisallowedLateJoin = message.Disallowed;
@ -97,7 +105,7 @@ namespace Content.Client.GameTicking.Managers
RoundStartTimeSpan = message.RoundStartTimeSpan;
IsGameStarted = message.IsRoundStarted;
AreWeReady = message.YouAreReady;
LobbySong = message.LobbySong;
SetLobbySong(message.LobbySong);
LobbyBackground = message.LobbyBackground;
Paused = message.Paused;
@ -124,12 +132,8 @@ namespace Content.Client.GameTicking.Managers
private void RoundEnd(RoundEndMessageEvent message)
{
if (message.LobbySong != null)
{
LobbySong = message.LobbySong;
_backgroundAudio.StartLobbyMusic();
}
// Force an update in the event of this song being the same as the last.
SetLobbySong(message.LobbySong, true);
RestartSound = message.RestartSound;
// Don't open duplicate windows (mainly for replays).

View file

@ -44,7 +44,7 @@ public sealed class StealthSystem : SharedStealthSystem
if (!enabled)
{
if (component.HadOutline)
if (component.HadOutline && !TerminatingOrDeleted(uid))
EnsureComp<InteractionOutlineComponent>(uid);
return;
}
@ -63,7 +63,8 @@ public sealed class StealthSystem : SharedStealthSystem
private void OnShutdown(EntityUid uid, StealthComponent component, ComponentShutdown args)
{
SetShader(uid, false, component);
if (!Terminating(uid))
SetShader(uid, false, component);
}
private void OnShaderRender(EntityUid uid, StealthComponent component, BeforePostShaderRenderEvent args)

View file

@ -153,17 +153,10 @@ namespace Content.IntegrationTests.Tests
.Select(p => p.ID)
.ToList();
// for whatever reason, stealth boxes are breaking this test. Surplus crates have a chance of spawning them.
// TODO fix whatever is going wrong here.
HashSet<string> ignored = new() { "GhostBox", "StealthBox", "CrateSyndicateSurplusBundle", "CrateSyndicateSuperSurplusBundle" };
await server.WaitPost(() =>
{
foreach (var protoId in protoIds)
{
if (ignored.Contains(protoId))
continue;
var mapId = mapManager.CreateMap();
var grid = mapManager.CreateGrid(mapId);
var ent = sEntMan.SpawnEntity(protoId, new EntityCoordinates(grid.Owner, 0.5f, 0.5f));

View file

@ -71,7 +71,7 @@ public sealed partial class MindTests
[Test]
public async Task TestGhostOnDeleteMap()
{
await using var pairTracker = await SetupPair();
await using var pairTracker = await SetupPair(dirty: true);
var server = pairTracker.Pair.Server;
var testMap = await PoolManager.CreateTestMap(pairTracker);
var coordinates = testMap.GridCoords;

View file

@ -29,18 +29,9 @@ namespace Content.IntegrationTests.Tests;
[TestFixture]
public sealed class PrototypeSaveTest
{
private readonly HashSet<string> _ignoredPrototypes = new()
{
"Singularity", // physics collision uses "AllMask" (-1). The flag serializer currently fails to save this because this features un-named bits.
"constructionghost",
// Don't add to this list unless you have a good reason
// Or it is just temporary because tests stopped working and now master has too many broken entities.
};
[Test]
public async Task UninitializedSaveTest()
{
// Apparently SpawnTest fails to clean up properly. Due to the similarities, I'll assume this also fails.
await using var pairTracker = await PoolManager.GetServerClient();
var server = pairTracker.Pair.Server;
@ -92,9 +83,6 @@ public sealed class PrototypeSaveTest
if (!prototype.MapSavable)
continue;
if (_ignoredPrototypes.Contains(prototype.ID))
continue;
if (prototype.SetSuffix == "DEBUG")
continue;
@ -165,10 +153,7 @@ public sealed class PrototypeSaveTest
var diff = compMapping.Except(protoMapping);
if (diff != null && diff.Children.Count != 0)
{
var modComps = string.Join(",", diff.Keys.Select(x => x.ToString()));
Assert.Fail($"Prototype {prototype.ID} modifies component on spawn: {compName}. Modified fields: {modComps}");
}
Assert.Fail($"Prototype {prototype.ID} modifies component on spawn: {compName}. Modified yaml:\n{diff}");
}
else
{

View file

@ -1,7 +1,10 @@
using Content.Server.DeviceLinking.Components;
using Content.Shared.Atmos.Monitor;
using Content.Shared.Atmos.Monitor.Components;
using Content.Shared.Atmos.Piping.Unary.Components;
using Content.Shared.DeviceLinking;
using Robust.Shared.Network;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Server.Atmos.Monitor.Components;
@ -24,4 +27,28 @@ public sealed class AirAlarmComponent : Component
public HashSet<NetUserId> ActivePlayers = new();
public bool CanSync = true;
/// <summary>
/// Previous alarm state for use with output ports.
/// </summary>
[DataField("state")]
public AtmosAlarmType State = AtmosAlarmType.Normal;
/// <summary>
/// The port that gets set to high while the alarm is in the danger state, and low when not.
/// </summary>
[DataField("dangerPort", customTypeSerializer: typeof(PrototypeIdSerializer<SourcePortPrototype>))]
public string DangerPort = "AirDanger";
/// <summary>
/// The port that gets set to high while the alarm is in the warning state, and low when not.
/// </summary>
[DataField("warningPort", customTypeSerializer: typeof(PrototypeIdSerializer<SourcePortPrototype>))]
public string WarningPort = "AirWarning";
/// <summary>
/// The port that gets set to high while the alarm is in the normal state, and low when not.
/// </summary>
[DataField("normalPort", customTypeSerializer: typeof(PrototypeIdSerializer<SourcePortPrototype>))]
public string NormalPort = "AirNormal";
}

View file

@ -1,6 +1,7 @@
using System.Linq;
using Content.Server.Atmos.Monitor.Components;
using Content.Server.Atmos.Piping.Components;
using Content.Server.DeviceLinking.Systems;
using Content.Server.DeviceNetwork;
using Content.Server.DeviceNetwork.Components;
using Content.Server.DeviceNetwork.Systems;
@ -13,6 +14,7 @@ using Content.Shared.Atmos;
using Content.Shared.Atmos.Monitor;
using Content.Shared.Atmos.Monitor.Components;
using Content.Shared.Atmos.Piping.Unary.Components;
using Content.Shared.DeviceLinking;
using Content.Shared.DeviceNetwork;
using Content.Shared.DeviceNetwork.Systems;
using Content.Shared.Interaction;
@ -32,14 +34,14 @@ namespace Content.Server.Atmos.Monitor.Systems;
// response data in its data key.
public sealed class AirAlarmSystem : EntitySystem
{
[Dependency] private readonly DeviceNetworkSystem _deviceNet = default!;
[Dependency] private readonly DeviceListSystem _deviceListSystem = default!;
[Dependency] private readonly AtmosDeviceNetworkSystem _atmosDevNetSystem = default!;
[Dependency] private readonly AccessReaderSystem _access = default!;
[Dependency] private readonly AtmosAlarmableSystem _atmosAlarmable = default!;
[Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
[Dependency] private readonly AccessReaderSystem _accessSystem = default!;
[Dependency] private readonly AtmosDeviceNetworkSystem _atmosDevNet = default!;
[Dependency] private readonly DeviceNetworkSystem _deviceNet = default!;
[Dependency] private readonly DeviceLinkSystem _deviceLink = default!;
[Dependency] private readonly DeviceListSystem _deviceList = default!;
[Dependency] private readonly PopupSystem _popup = default!;
[Dependency] private readonly SharedInteractionSystem _interactionSystem = default!;
[Dependency] private readonly UserInterfaceSystem _ui = default!;
#region Device Network API
@ -57,8 +59,8 @@ public sealed class AirAlarmSystem : EntitySystem
/// <param name="data">The data to send to the device.</param>
public void SetData(EntityUid uid, string address, IAtmosDeviceData data)
{
_atmosDevNetSystem.SetDeviceState(uid, address, data);
_atmosDevNetSystem.Sync(uid, address);
_atmosDevNet.SetDeviceState(uid, address, data);
_atmosDevNet.Sync(uid, address);
}
/// <summary>
@ -66,7 +68,7 @@ public sealed class AirAlarmSystem : EntitySystem
/// </summary>
private void SyncAllDevices(EntityUid uid)
{
_atmosDevNetSystem.Sync(uid, null);
_atmosDevNet.Sync(uid, null);
}
/// <summary>
@ -75,7 +77,7 @@ public sealed class AirAlarmSystem : EntitySystem
/// <param name="address">The address of the device.</param>
private void SyncDevice(EntityUid uid, string address)
{
_atmosDevNetSystem.Sync(uid, address);
_atmosDevNet.Sync(uid, address);
}
/// <summary>
@ -85,8 +87,8 @@ public sealed class AirAlarmSystem : EntitySystem
/// <param name="uid"></param>
private void SyncRegisterAllDevices(EntityUid uid)
{
_atmosDevNetSystem.Register(uid, null);
_atmosDevNetSystem.Sync(uid, null);
_atmosDevNet.Register(uid, null);
_atmosDevNet.Sync(uid, null);
}
/// <summary>
@ -133,8 +135,7 @@ public sealed class AirAlarmSystem : EntitySystem
/// <param name="mode">The mode to sync with the rest of the network.</param>
private void SyncMode(EntityUid uid, AirAlarmMode mode)
{
if (EntityManager.TryGetComponent(uid, out AtmosMonitorComponent? monitor)
&& !monitor.NetEnabled)
if (TryComp<AtmosMonitorComponent>(uid, out var monitor) && !monitor.NetEnabled)
return;
var payload = new NetworkPayload
@ -165,6 +166,8 @@ public sealed class AirAlarmSystem : EntitySystem
SubscribeLocalEvent<AirAlarmComponent, AirAlarmTabSetMessage>(OnTabChange);
SubscribeLocalEvent<AirAlarmComponent, DeviceListUpdateEvent>(OnDeviceListUpdate);
SubscribeLocalEvent<AirAlarmComponent, BoundUIClosedEvent>(OnClose);
SubscribeLocalEvent<AirAlarmComponent, ComponentInit>(OnInit);
SubscribeLocalEvent<AirAlarmComponent, MapInitEvent>(OnMapInit);
SubscribeLocalEvent<AirAlarmComponent, ComponentShutdown>(OnShutdown);
SubscribeLocalEvent<AirAlarmComponent, ActivateInWorldEvent>(OnActivate);
}
@ -179,7 +182,7 @@ public sealed class AirAlarmSystem : EntitySystem
continue;
}
_atmosDevNetSystem.Deregister(uid, deviceNet.Address);
_atmosDevNet.Deregister(uid, deviceNet.Address);
}
component.ScrubberData.Clear();
@ -220,6 +223,18 @@ public sealed class AirAlarmSystem : EntitySystem
RemoveActiveInterface(uid);
}
private void OnInit(EntityUid uid, AirAlarmComponent comp, ComponentInit args)
{
_deviceLink.EnsureSourcePorts(uid, comp.DangerPort, comp.WarningPort, comp.NormalPort);
}
private void OnMapInit(EntityUid uid, AirAlarmComponent comp, MapInitEvent args)
{
// for mapped linked air alarms, start with high so when it changes for the first time it goes from high to low
// without this the output would suddenly get sent a low signal after nothing which is bad
_deviceLink.SendSignal(uid, GetPort(comp), true);
}
private void OnShutdown(EntityUid uid, AirAlarmComponent component, ComponentShutdown args)
{
_activeUserInterfaces.Remove(uid);
@ -227,9 +242,6 @@ public sealed class AirAlarmSystem : EntitySystem
private void OnActivate(EntityUid uid, AirAlarmComponent component, ActivateInWorldEvent args)
{
if (!_interactionSystem.InRangeUnobstructed(args.User, args.Target))
return;
if (!TryComp<ActorComponent>(args.User, out var actor))
return;
@ -242,9 +254,9 @@ public sealed class AirAlarmSystem : EntitySystem
if (!this.IsPowered(uid, EntityManager))
return;
var ui = _uiSystem.GetUiOrNull(uid, SharedAirAlarmInterfaceKey.Key);
var ui = _ui.GetUiOrNull(uid, SharedAirAlarmInterfaceKey.Key);
if (ui != null)
_uiSystem.OpenUi(ui, actor.PlayerSession);
_ui.OpenUi(ui, actor.PlayerSession);
component.ActivePlayers.Add(actor.PlayerSession.UserId);
AddActiveInterface(uid);
SyncAllDevices(uid);
@ -268,12 +280,20 @@ public sealed class AirAlarmSystem : EntitySystem
private void OnUpdateAlarmMode(EntityUid uid, AirAlarmComponent component, AirAlarmUpdateAlarmModeMessage args)
{
var addr = string.Empty;
if (EntityManager.TryGetComponent(uid, out DeviceNetworkComponent? netConn)) addr = netConn.Address;
if (AccessCheck(uid, args.Session.AttachedEntity, component))
{
var addr = string.Empty;
if (TryComp<DeviceNetworkComponent>(uid, out var netConn))
{
addr = netConn.Address;
}
SetMode(uid, addr, args.Mode, false);
}
else
{
UpdateUI(uid, component);
}
}
private void OnUpdateAutoMode(EntityUid uid, AirAlarmComponent component, AirAlarmUpdateAutoModeMessage args)
@ -293,7 +313,7 @@ public sealed class AirAlarmSystem : EntitySystem
private void OnUpdateDeviceData(EntityUid uid, AirAlarmComponent component, AirAlarmUpdateDeviceDataMessage args)
{
if (AccessCheck(uid, args.Session.AttachedEntity, component)
&& _deviceListSystem.ExistsInDeviceList(uid, args.Address))
&& _deviceList.ExistsInDeviceList(uid, args.Address))
{
SetDeviceData(uid, args.Address, args.Data);
}
@ -334,10 +354,14 @@ public sealed class AirAlarmSystem : EntitySystem
if (!Resolve(uid, ref component))
return false;
if (!EntityManager.TryGetComponent(uid, out AccessReaderComponent? reader) || user == null)
// if it has no access reader behave as if the user has AA
if (!TryComp<AccessReaderComponent>(uid, out var reader))
return true;
if (user == null)
return false;
if (!_accessSystem.IsAllowed(user.Value, reader))
if (!_access.IsAllowed(user.Value, reader))
{
_popup.PopupEntity(Loc.GetString("air-alarm-ui-access-denied"), user.Value, user.Value);
return false;
@ -354,8 +378,10 @@ public sealed class AirAlarmSystem : EntitySystem
}
var addr = string.Empty;
if (EntityManager.TryGetComponent(uid, out DeviceNetworkComponent? netConn))
if (TryComp<DeviceNetworkComponent>(uid, out var netConn))
{
addr = netConn.Address;
}
if (component.AutoMode)
{
@ -369,9 +395,32 @@ public sealed class AirAlarmSystem : EntitySystem
}
}
if (component.State != args.AlarmType)
{
TryComp<DeviceLinkSourceComponent>(uid, out var source);
// send low to old state's port
_deviceLink.SendSignal(uid, GetPort(component), false, source);
// send high to new state's port, along with updating the cached state
component.State = args.AlarmType;
_deviceLink.SendSignal(uid, GetPort(component), true, source);
}
UpdateUI(uid, component);
}
private string GetPort(AirAlarmComponent comp)
{
if (comp.State == AtmosAlarmType.Danger)
return comp.DangerPort;
if (comp.State == AtmosAlarmType.Warning)
return comp.WarningPort;
return comp.NormalPort;
}
#endregion
#region Air Alarm Settings
@ -517,7 +566,7 @@ public sealed class AirAlarmSystem : EntitySystem
/// </summary>
private void ForceCloseAllInterfaces(EntityUid uid)
{
_uiSystem.TryCloseAll(uid, SharedAirAlarmInterfaceKey.Key);
_ui.TryCloseAll(uid, SharedAirAlarmInterfaceKey.Key);
}
private void OnAtmosUpdate(EntityUid uid, AirAlarmComponent alarm, AtmosDeviceUpdateEvent args)
@ -585,7 +634,7 @@ public sealed class AirAlarmSystem : EntitySystem
highestAlarm = AtmosAlarmType.Normal;
}
_uiSystem.TrySetUiState(
_ui.TrySetUiState(
uid,
SharedAirAlarmInterfaceKey.Key,
new AirAlarmUIState(devNet.Address, deviceCount, pressure, temperature, dataToSend, alarm.CurrentMode, alarm.CurrentTab, highestAlarm.Value, alarm.AutoMode));

View file

@ -1,3 +1,4 @@
using System.Globalization;
using System.Linq;
using System.Text;
using Content.Server.Administration.Logs;
@ -201,8 +202,11 @@ public sealed partial class ChatSystem : SharedChatSystem
bool shouldCapitalize = (desiredType != InGameICChatType.Emote);
bool shouldPunctuate = _configurationManager.GetCVar(CCVars.ChatPunctuation);
// Capitalizing the word I only happens in English, so we check language here
bool shouldCapitalizeTheWordI = (!CultureInfo.CurrentCulture.IsNeutralCulture && CultureInfo.CurrentCulture.Parent.Name == "en")
|| (CultureInfo.CurrentCulture.IsNeutralCulture && CultureInfo.CurrentCulture.Name == "en");
message = SanitizeInGameICMessage(source, message, out var emoteStr, shouldCapitalize, shouldPunctuate);
message = SanitizeInGameICMessage(source, message, out var emoteStr, shouldCapitalize, shouldPunctuate, shouldCapitalizeTheWordI);
// Was there an emote in the message? If so, send it.
if (player != null && emoteStr != message && emoteStr != null)
@ -687,12 +691,14 @@ public sealed partial class ChatSystem : SharedChatSystem
}
// ReSharper disable once InconsistentNaming
private string SanitizeInGameICMessage(EntityUid source, string message, out string? emoteStr, bool capitalize = true, bool punctuate = false)
private string SanitizeInGameICMessage(EntityUid source, string message, out string? emoteStr, bool capitalize = true, bool punctuate = false, bool capitalizeTheWordI = true)
{
var newMessage = message.Trim();
newMessage = ReplaceWords(newMessage); // Corvax-ChatSanitize
if (capitalize)
newMessage = SanitizeMessageCapital(newMessage);
if (capitalizeTheWordI)
newMessage = SanitizeMessageCapitalizeTheWordI(newMessage, "i");
if (punctuate)
newMessage = SanitizeMessagePeriod(newMessage);

View file

@ -1,3 +1,4 @@
using Content.Server.DeviceLinking.Components;
using Content.Server.DeviceLinking.Events;
using Content.Server.DeviceNetwork;
using Content.Server.DeviceNetwork.Components;
@ -95,11 +96,25 @@ public sealed class DeviceLinkSystem : SharedDeviceLinkSystem
}
}
_deviceNetworkSystem.QueuePacket(uid, sinkNetworkComponent.Address, payload, sinkNetworkComponent.ReceiveFrequency);
// force using wireless network so things like atmos devices are able to send signals
var network = (int) DeviceNetworkComponent.DeviceNetIdDefaults.Wireless;
_deviceNetworkSystem.QueuePacket(uid, sinkNetworkComponent.Address, payload, sinkNetworkComponent.ReceiveFrequency, network);
}
}
}
/// <summary>
/// Helper function that invokes a port with a high/low binary logic signal.
/// </summary>
public void SendSignal(EntityUid uid, string port, bool signal, DeviceLinkSourceComponent? comp = null)
{
var data = new NetworkPayload
{
[DeviceNetworkConstants.LogicState] = signal ? SignalState.High : SignalState.Low
};
InvokePort(uid, port, data, comp);
}
/// <summary>
/// Checks if the payload has a port defined and if the port is present on the sink.
/// Raises a <see cref="SignalReceivedEvent"/> containing the payload when the check passes

View file

@ -140,12 +140,7 @@ public sealed class LogicGateSystem : EntitySystem
{
comp.LastOutput = output;
var data = new NetworkPayload
{
[DeviceNetworkConstants.LogicState] = output ? SignalState.High : SignalState.Low
};
_deviceLink.InvokePort(uid, comp.OutputPort, data);
_deviceLink.SendSignal(uid, comp.OutputPort, output);
}
}
}

View file

@ -30,15 +30,11 @@ public sealed class SignalSwitchSystem : EntitySystem
comp.State = !comp.State;
_deviceLink.InvokePort(uid, comp.State ? comp.OnPort : comp.OffPort);
var data = new NetworkPayload
{
[DeviceNetworkConstants.LogicState] = comp.State ? SignalState.High : SignalState.Low
};
// only send status if it's a toggle switch and not a button
if (comp.OnPort != comp.OffPort)
{
_deviceLink.InvokePort(uid, comp.StatusPort, data);
_deviceLink.SendSignal(uid, comp.StatusPort, comp.State);
}
_audio.PlayPvs(comp.ClickSound, uid, AudioParams.Default.WithVariation(0.125f).WithVolume(8f));

View file

@ -66,7 +66,7 @@ namespace Content.Server.DeviceNetwork.Systems
/// <param name="frequency">The frequency to send on</param>
/// <param name="data">The data to be sent</param>
/// <returns>Returns true when the packet was successfully enqueued.</returns>
public bool QueuePacket(EntityUid uid, string? address, NetworkPayload data, uint? frequency = null, DeviceNetworkComponent? device = null)
public bool QueuePacket(EntityUid uid, string? address, NetworkPayload data, uint? frequency = null, int? network = null, DeviceNetworkComponent? device = null)
{
if (!Resolve(uid, ref device, false))
return false;
@ -79,7 +79,9 @@ namespace Content.Server.DeviceNetwork.Systems
if (frequency == null)
return false;
_nextQueue.Enqueue(new DeviceNetworkPacketEvent(device.DeviceNetId, address, frequency.Value, device.Address, uid, data));
network ??= device.DeviceNetId;
_nextQueue.Enqueue(new DeviceNetworkPacketEvent(network.Value, address, frequency.Value, device.Address, uid, data));
return true;
}

View file

@ -17,7 +17,7 @@ namespace Content.Server.DeviceNetwork.Systems
/// </summary>
private void OnBeforePacketSent(EntityUid uid, WiredNetworkComponent component, BeforePacketSentEvent args)
{
if (EntityManager.GetComponent<TransformComponent>(uid).GridUid != args.SenderTransform.GridUid)
if (Transform(uid).GridUid != args.SenderTransform.GridUid)
{
args.Cancel();
}

View file

@ -20,8 +20,11 @@ namespace Content.Server.DeviceNetwork.Systems
var ownPosition = args.SenderPosition;
var xform = Transform(uid);
// not a wireless to wireless connection, just let it happen
if (!TryComp<WirelessNetworkComponent>(args.Sender, out var sendingComponent))
return;
if (xform.MapID != args.SenderTransform.MapID
|| !TryComp<WirelessNetworkComponent>(args.Sender, out var sendingComponent)
|| (ownPosition - xform.WorldPosition).Length() > sendingComponent.Range)
{
args.Cancel();

View file

@ -111,7 +111,7 @@ public sealed class MailingUnitSystem : EntitySystem
[NetTarget] = component.Target
};
_deviceNetworkSystem.QueuePacket(uid, null, payload, null, device);
_deviceNetworkSystem.QueuePacket(uid, null, payload, null, null, device);
}
/// <summary>
@ -129,7 +129,7 @@ public sealed class MailingUnitSystem : EntitySystem
};
component.TargetList.Clear();
_deviceNetworkSystem.QueuePacket(uid, null, payload, null, device);
_deviceNetworkSystem.QueuePacket(uid, null, payload, null, null, device);
}
/// <summary>

View file

@ -7,7 +7,6 @@ using Content.Shared.GameTicking;
using Content.Shared.Preferences;
using JetBrains.Annotations;
using Prometheus;
using Robust.Server.GameStates;
using Robust.Server.Maps;
using Robust.Server.Player;
using Robust.Shared.Audio;
@ -46,22 +45,6 @@ namespace Content.Server.GameTicking
[ViewVariables]
private bool _startingRound;
/// <summary>
/// This is a list of players that are going to appear in the round-end
/// crew manifest so their children entities can be collected each update
/// and sent to far-away players.
/// </summary>
[ViewVariables]
private HashSet<EntityUid> _expandPvsPlayers = new();
/// <summary>
/// This is the list of the children entities that should be sent to
/// all players at the end of the round, to keep distant characters from
/// looking naked in the crew manifest.
/// </summary>
[ViewVariables]
private HashSet<EntityUid> _expandPvsEntities = new();
[ViewVariables]
private GameRunLevel _runLevel;
@ -81,16 +64,6 @@ namespace Content.Server.GameTicking
}
}
private void InitializeRoundFlow()
{
SubscribeLocalEvent<ExpandPvsEvent>(OnExpandPvs);
}
private void OnEntityDeleted(EntityUid uid)
{
_expandPvsEntities.Remove(uid);
}
/// <summary>
/// Returns true if the round's map is eligible to be updated.
/// </summary>
@ -324,17 +297,15 @@ namespace Content.Server.GameTicking
RunLevel = GameRunLevel.PostRound;
// The lobby song is set here instead of in RestartRound,
// because ShowRoundEndScoreboard triggers the start of the music playing
// at the end of a round, and this needs to be set before RestartRound
// in order for the lobby song status display to be accurate.
LobbySong = _robustRandom.Pick(_lobbyMusicCollection.PickFiles).ToString();
ShowRoundEndScoreboard(text);
}
private void OnExpandPvs(ref ExpandPvsEvent args)
{
if (RunLevel != GameRunLevel.PostRound)
return;
args.Entities.AddRange(_expandPvsEntities);
}
public void ShowRoundEndScoreboard(string text = "")
{
// Log end of round
@ -352,11 +323,6 @@ namespace Content.Server.GameTicking
//Get the timespan of the round.
var roundDuration = RoundDuration();
// Should already be empty, but just in case.
_expandPvsEntities.Clear();
_expandPvsPlayers.Clear();
EntityManager.EntityDeleted += OnEntityDeleted;
//Generate a list of basic player info to display in the end round summary.
var listOfPlayerInfo = new List<RoundEndMessageEvent.RoundEndPlayerInfo>();
// Grab the great big book of all the Minds, we'll need them for this.
@ -390,8 +356,9 @@ namespace Content.Server.GameTicking
else if (mind.CurrentEntity != null && TryName(mind.CurrentEntity.Value, out var icName))
playerIcName = icName;
if (Exists(mind.OriginalOwnedEntity))
_expandPvsPlayers.Add(mind.OriginalOwnedEntity.Value);
var entity = mind.OriginalOwnedEntity;
if (Exists(entity))
_pvsOverride.AddGlobalOverride(entity.Value, recursive: true);
var playerEndRoundInfo = new RoundEndMessageEvent.RoundEndPlayerInfo()
{
@ -400,7 +367,7 @@ namespace Content.Server.GameTicking
PlayerOOCName = contentPlayerData?.Name ?? "(IMPOSSIBLE: REGISTERED MIND WITH NO OWNER)",
// Character name takes precedence over current entity name
PlayerICName = playerIcName,
PlayerEntityUid = mind.OriginalOwnedEntity,
PlayerEntityUid = entity,
Role = antag
? mind.AllRoles.First(role => role.Antagonist).Name
: mind.AllRoles.FirstOrDefault()?.Name ?? Loc.GetString("game-ticker-unknown-role"),
@ -411,20 +378,6 @@ namespace Content.Server.GameTicking
listOfPlayerInfo.Add(playerEndRoundInfo);
}
// Recursively collect entities for the crew manifest.
void RecursePvsEntities(IEnumerable<EntityUid> entities)
{
_expandPvsEntities.UnionWith(entities);
foreach (var entity in entities)
{
if (TryComp<TransformComponent>(entity, out var xform))
RecursePvsEntities(xform.ChildEntities);
}
}
RecursePvsEntities(_expandPvsPlayers);
// This ordering mechanism isn't great (no ordering of minds) but functions
var listOfPlayerInfoFinal = listOfPlayerInfo.OrderBy(pi => pi.PlayerOOCName).ToArray();
@ -455,7 +408,6 @@ namespace Content.Server.GameTicking
PlayersJoinedRoundNormally = 0;
RunLevel = GameRunLevel.PreRoundLobby;
LobbySong = _robustRandom.Pick(_lobbyMusicCollection.PickFiles).ToString();
RandomizeLobbyBackground();
ResettingCleanup();
IncrementRoundNumber();
@ -524,10 +476,6 @@ namespace Content.Server.GameTicking
_allPreviousGameRules.Clear();
EntityManager.EntityDeleted -= OnEntityDeleted;
_expandPvsPlayers.Clear();
_expandPvsEntities.Clear();
// Round restart cleanup event, so entity systems can reset.
var ev = new RoundRestartCleanupEvent();
RaiseLocalEvent(ev);

View file

@ -18,6 +18,7 @@ using Content.Shared.Mobs.Systems;
using Content.Shared.Roles;
using Robust.Server;
using Robust.Server.GameObjects;
using Robust.Server.GameStates;
using Robust.Shared.Configuration;
using Robust.Shared.Console;
#if EXCEPTION_TOLERANCE
@ -40,6 +41,7 @@ namespace Content.Server.GameTicking
[Dependency] private readonly MindSystem _mind = default!;
[Dependency] private readonly MindTrackerSystem _mindTracker = default!;
[Dependency] private readonly MobStateSystem _mobState = default!;
[Dependency] private readonly PvsOverrideSystem _pvsOverride = default!;
[Dependency] private readonly IAdminLogManager _adminLogs = default!;
[ViewVariables] private bool _initialized;
@ -70,7 +72,6 @@ namespace Content.Server.GameTicking
"Overflow role does not have the correct name!");
InitializeGameRules();
InitializeReplays();
InitializeRoundFlow();
_initialized = true;
}

View file

@ -15,6 +15,7 @@ using Content.Shared.Popups;
using Content.Shared.Toilet;
using Content.Shared.Tools;
using Content.Shared.Tools.Components;
using Content.Shared.Verbs;
using Robust.Shared.Audio;
using Robust.Shared.Player;
using Robust.Shared.Random;
@ -37,10 +38,11 @@ namespace Content.Server.Toilet
SubscribeLocalEvent<ToiletComponent, ComponentInit>(OnInit);
SubscribeLocalEvent<ToiletComponent, MapInitEvent>(OnMapInit);
SubscribeLocalEvent<ToiletComponent, InteractUsingEvent>(OnInteractUsing);
SubscribeLocalEvent<ToiletComponent, InteractHandEvent>(OnInteractHand, new []{typeof(SharedBuckleSystem)});
SubscribeLocalEvent<ToiletComponent, InteractHandEvent>(OnInteractHand);
SubscribeLocalEvent<ToiletComponent, ExaminedEvent>(OnExamine);
SubscribeLocalEvent<ToiletComponent, SuicideEvent>(OnSuicide);
SubscribeLocalEvent<ToiletComponent, ToiletPryDoAfterEvent>(OnToiletPried);
SubscribeLocalEvent<ToiletComponent, GetVerbsEvent<AlternativeVerb>>(OnToggleSeatVerb);
}
private void OnSuicide(EntityUid uid, ToiletComponent component, SuicideEvent args)
@ -123,13 +125,26 @@ namespace Content.Server.Toilet
}
}
// just want to up/down seat?
// check that nobody seats on seat right now
if (TryComp<StrapComponent>(uid, out var strap) && strap.BuckledEntities.Count != 0)
args.Handled = true;
}
private void OnToggleSeatVerb(EntityUid uid, ToiletComponent component, GetVerbsEvent<AlternativeVerb> args)
{
if (!args.CanInteract || !args.CanAccess || !CanToggle(uid))
return;
ToggleToiletSeat(uid, component);
args.Handled = true;
var alterToiletSeatText = component.IsSeatUp ? Loc.GetString("toilet-seat-close") : Loc.GetString("toilet-seat-open");
var verb = new AlternativeVerb()
{
Act = () => {
if (CanToggle(uid))
ToggleToiletSeat(uid, component);
},
Text = alterToiletSeatText
};
args.Verbs.Add(verb);
}
private void OnExamine(EntityUid uid, ToiletComponent component, ExaminedEvent args)
@ -153,6 +168,11 @@ namespace Content.Server.Toilet
UpdateSprite(uid, toilet);
}
public bool CanToggle(EntityUid uid)
{
return TryComp<StrapComponent>(uid, out var strap) && strap.BuckledEntities.Count == 0;
}
public void ToggleToiletSeat(EntityUid uid, ToiletComponent? component = null)
{
if (!Resolve(uid, ref component))

View file

@ -162,4 +162,33 @@ public abstract class SharedChatSystem : EntitySystem
message = char.ToUpper(message[0]) + message.Remove(0, 1);
return message;
}
public string SanitizeMessageCapitalizeTheWordI(string message, string theWordI = "i")
{
if (string.IsNullOrEmpty(message))
return message;
for
(
var index = message.IndexOf(theWordI);
index != -1;
index = message.IndexOf(theWordI, index + 1)
)
{
// Stops the code If It's tryIng to capItalIze the letter I In the mIddle of words
// Repeating the code twice is the simplest option
if (index + 1 < message.Length && char.IsLetter(message[index + 1]))
continue;
if (index - 1 >= 0 && char.IsLetter(message[index - 1]))
continue;
var beforeTarget = message.Substring(0, index);
var target = message.Substring(index, theWordI.Length);
var afterTarget = message.Substring(index + theWordI.Length);
message = beforeTarget + target.ToUpper() + afterTarget;
}
return message;
}
}

View file

@ -39,6 +39,12 @@ public sealed class StaminaComponent : Component
[ViewVariables(VVAccess.ReadWrite), DataField("critThreshold")]
public float CritThreshold = 100f;
/// <summary>
/// How long will this mob be stunned for?
/// </summary>
[ViewVariables(VVAccess.ReadWrite), DataField("stunTime")]
public TimeSpan StunTime = TimeSpan.FromSeconds(6);
/// <summary>
/// To avoid continuously updating our data we track the last time we updated so we can extrapolate our current stamina.
/// </summary>

View file

@ -394,11 +394,10 @@ public sealed partial class StaminaSystem : EntitySystem
component.Critical = true;
component.StaminaDamage = component.CritThreshold;
var stunTime = TimeSpan.FromSeconds(6);
_stunSystem.TryParalyze(uid, stunTime, true);
_stunSystem.TryParalyze(uid, component.StunTime, true);
// Give them buffer before being able to be re-stunned
component.NextUpdate = _timing.CurTime + stunTime + StamCritBufferTime;
component.NextUpdate = _timing.CurTime + component.StunTime + StamCritBufferTime;
EnsureComp<ActiveStaminaComponent>(uid);
Dirty(component);
_adminLogger.Add(LogType.Stamina, LogImpact.Medium, $"{ToPrettyString(uid):user} entered stamina crit");

View file

@ -163,9 +163,9 @@ namespace Content.Shared.Humanoid
break;
case HumanoidSkinColor.Hues:
case HumanoidSkinColor.TintedHues:
var rbyte = random.Next(0, 255);
var gbyte = random.Next(0, 255);
var bbyte = random.Next(0, 255);
var rbyte = random.NextByte();
var gbyte = random.NextByte();
var bbyte = random.NextByte();
newSkinColor = new Color(rbyte, gbyte, bbyte);
break;
}

View file

@ -118,7 +118,7 @@ public static class SkinColor
public static Color TintedHues(Color color)
{
var newColor = Color.ToHsv(color);
newColor.Y = .1f;
newColor.Y *= 0.1f;
return Color.FromHsv(newColor);
}

View file

@ -1,4 +1,5 @@
using System.Diagnostics.CodeAnalysis;
using Content.Shared.ActionBlocker;
using Content.Shared.Actions;
using Content.Shared.Administration.Logs;
using Content.Shared.Audio;
@ -33,6 +34,7 @@ namespace Content.Shared.Weapons.Ranged.Systems;
public abstract partial class SharedGunSystem : EntitySystem
{
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
[Dependency] protected readonly IGameTiming Timing = default!;
[Dependency] protected readonly IMapManager MapManager = default!;
[Dependency] private readonly INetManager _netManager = default!;
@ -208,7 +210,8 @@ public abstract partial class SharedGunSystem : EntitySystem
private void AttemptShoot(EntityUid user, EntityUid gunUid, GunComponent gun)
{
if (gun.FireRate <= 0f)
if (gun.FireRate <= 0f ||
!_actionBlockerSystem.CanUseHeldEntity(user))
return;
var toCoordinates = gun.ShootCoordinates;

View file

@ -1,67 +1,4 @@
Entries:
- author: TemporalOroboros
changes:
- {message: The AME controller increases and decreases in steps of 2 again., type: Tweak}
id: 4146
time: '2023-06-30T23:24:23.0000000+00:00'
- author: Nairodian
changes:
- {message: MRP rules updated., type: Tweak}
id: 4147
time: '2023-07-01T00:15:04.0000000+00:00'
- author: Lank
changes:
- {message: 'The Cargo Telepad has been significantly improved, teleporting orders
over 4x faster.', type: Tweak}
id: 4148
time: '2023-07-01T19:00:33.0000000+00:00'
- author: Dygon
changes:
- {message: Windoors can now be bolted through the network system., type: Tweak}
id: 4149
time: '2023-07-01T19:00:53.0000000+00:00'
- author: Dygon
changes:
- {message: 'Shutters are able to be closed again on windoors, tables, flaps and
windows.', type: Fix}
id: 4150
time: '2023-07-01T19:19:04.0000000+00:00'
- author: Slava0135
changes:
- {message: added rare immovable rod event that can happen late in the game and
can literally cut through entire station, type: Add}
- {message: immovable rods now move on spawn by default, type: Fix}
- {message: you can now warp to immovable rod as ghost, type: Tweak}
id: 4151
time: '2023-07-02T13:40:07.0000000+00:00'
- author: brainfood1183
changes:
- {message: Fixed Hamlet sprite error on revival., type: Fix}
id: 4152
time: '2023-07-03T07:59:29.0000000+00:00'
- author: HerCoyote23
changes:
- {message: You can now speak on your department's radio channel using .X, type: Tweak}
id: 4153
time: '2023-07-03T08:55:08.0000000+00:00'
- author: EmoGarbage404
changes:
- {message: Artifacts now make noises when activated., type: Add}
id: 4154
time: '2023-07-03T11:14:27.0000000+00:00'
- author: noudoit
changes:
- {message: Attempting to uncuff yourself damages you slightly, type: Tweak}
id: 4155
time: '2023-07-03T21:30:09.0000000+00:00'
- author: dmnct
changes:
- {message: Added fun!, type: Add}
- {message: Removed fun!, type: Remove}
- {message: Changed fun!, type: Tweak}
- {message: Fixed fun!, type: Fix}
id: 4156
time: '2023-07-03T21:31:53.0000000+00:00'
- author: Geekyhobo
changes:
- {message: Nukie bundle prices have been adjusted for balance, type: Tweak}
@ -3029,3 +2966,66 @@ Entries:
- {message: Changed salvage and QM locker contents, type: Tweak}
id: 4645
time: '2023-08-21T01:04:43.0000000+00:00'
- author: EnDecc
changes:
- {message: 'Three "plastic crates" in cargo have now been given appropriate labels,
after Centcomm forgot to use tape on them. Disaster pizza crate, Softdrinks
crate, and Softdrinks bulk crate will now show up in orders appropriately.',
type: Fix}
id: 4646
time: '2023-08-21T18:16:21.0000000+00:00'
- author: Errant-4
changes:
- {message: 'Crit/Dead people that somehow end up holding a gun can no longer fire
them. The Gundead aren''t real, they can''t hurt you.', type: Fix}
id: 4647
time: '2023-08-21T18:27:08.0000000+00:00'
- author: Verslebas
changes:
- {message: Changed admin ghost's hands order, type: Tweak}
id: 4648
time: '2023-08-21T18:35:30.0000000+00:00'
- author: Potato1234_x
changes:
- {message: Vending machines have been polished. Again., type: Tweak}
id: 4649
time: '2023-08-21T18:45:54.0000000+00:00'
- author: Alekshhh
changes:
- {message: Changed medical locker contents, type: Tweak}
id: 4650
time: '2023-08-21T18:49:22.0000000+00:00'
- author: deltanedas
changes:
- {message: Air alarms now have output signal ports for each state., type: Tweak}
id: 4651
time: '2023-08-21T21:18:30.0000000+00:00'
- author: EmoGarbage404
changes:
- {message: Freshly spawned diona are no longer gray., type: Fix}
- {message: Skeletons are no longer extremely saturated in color., type: Fix}
id: 4652
time: '2023-08-22T02:05:24.0000000+00:00'
- author: func_kenobi
changes:
- {message: "CentComm allowed the station architectors to place Schr\xF6dinger's\
\ grilles.", type: Add}
id: 4653
time: '2023-08-22T02:21:45.0000000+00:00'
- author: astriloqua
changes:
- {message: NanoTransen has hired professionals to teach people how to use toilets.
Employees can now properly sit on toilets., type: Tweak}
id: 4654
time: '2023-08-22T04:21:51.0000000+00:00'
- author: kalane15
changes:
- {message: Dropping item stops its examination, type: Fix}
id: 4655
time: '2023-08-22T04:28:45.0000000+00:00'
- author: Interrobang01
changes:
- {message: The word "i" is now automatically capitalized in IC chat! Your pinky's
structural integrity is now safe!, type: Add}
id: 4656
time: '2023-08-22T04:43:37.0000000+00:00'

View file

@ -8,7 +8,7 @@ signal-port-name-off-transmitter = Off
signal-port-description-off-transmitter = This port is invoked whenever the transmitter is turned off.
signal-port-name-status-transmitter = Status
signal-port-description-logic-output = This port is invoked with HIGH or LOW depending on the transmitter status.
signal-port-description-status-transmitter = This port is invoked with HIGH or LOW depending on the transmitter status.
signal-port-name-left = Left
signal-port-description-left = This port is invoked whenever the lever is moved to the leftmost position.
@ -36,3 +36,12 @@ signal-port-description-logic-output-high = This port is invoked whenever the in
signal-port-name-logic-output-low = Low Output
signal-port-description-logic-output-low = This port is invoked whenever the input has a falling edge.
signal-port-name-air-danger = Danger
signal-port-description-air-danger = This port is invoked with HIGH when in danger mode and LOW when not.
signal-port-name-air-warning = Warning
signal-port-description-air-warning = This port is invoked with HIGH when in warning mode and LOW when not.
signal-port-name-air-normal = Normal
signal-port-description-air-normal = This port is invoked with HIGH when in normal mode and LOW when not.

View file

@ -1,6 +1,9 @@
ent-FoodPizza = { ent-CrateFoodPizza }
.desc = { ent-CrateFoodPizza.desc }
ent-FoodPizzaLarge = { ent-CrateFoodPizzaLarge }
.desc = { ent-CrateFoodPizzaLarge.desc }
ent-FoodMRE = { ent-CrateFoodMRE }
.desc = { ent-CrateFoodMRE.desc }
@ -12,3 +15,9 @@ ent-FoodDinnerware = { ent-CrateFoodDinnerware }
ent-FoodBarSupply = { ent-CrateFoodBarSupply }
.desc = { ent-CrateFoodBarSupply.desc }
ent-FoodSoftdrinks = { ent-CrateFoodSoftdrinks }
.desc = { ent-CrateFoodSoftdrinks.desc }
ent-FoodSoftdrinksLarge = { ent-CrateFoodSoftdrinksLarge }
.desc = { ent-CrateFoodSoftdrinksLarge.desc }

View file

@ -1,5 +1,8 @@
ent-CrateFoodPizza = Emergency pizza delivery
.desc = Help do your part to end station hunger by distributing pizza to underfunded departments!
.desc = Help do your part to end station hunger by distributing pizza to underfunded departments! Includes 4 pizzas.
ent-CrateFoodPizzaLarge = Disaster pizza delivery
.desc = In the ultimate event that all else has failed, Find comfort in that more pizza solves everything. Includes 16 pizzas.
ent-CrateFoodMRE = MRE crate
.desc = A military style meal fit to feed a whole department.
@ -12,3 +15,9 @@ ent-CrateFoodDinnerware = Kitchen dinnerware crate
ent-CrateFoodBarSupply = Bartending supplies crate
.desc = Extra Bar supplies, in case the clown was allowed in the bar unsupervised.
ent-CrateFoodSoftdrinks = Softdrinks crate
.desc = A variety of sodas to complement a small party, without having to empty the soda machines. Includes 14 sodas.
ent-CrateFoodSoftdrinksLarge = Softdrinks bulk crate
.desc = Lots of sodas taken straight out of Centcomm's own vending machines, because you just can't leave your department. Includes 28 sodas.

View file

@ -5,3 +5,5 @@ toilet-component-suicide-head-message-others = {CAPITALIZE(THE($victim))} sticks
toilet-component-suicide-head-message = You stick your head into {THE($owner)} and flush it!
toilet-component-suicide-message-others = {CAPITALIZE(THE($victim))} bashes themselves with {THE($owner)}!
toilet-component-suicide-message = You bash yourself with {THE($owner)}!
toilet-seat-close = Close Seat
toilet-seat-open = Open Seat

View file

@ -6,8 +6,8 @@
torso:
part: TorsoHuman
connections:
- right arm
- left arm
- right arm
right arm:
part: RightArmHuman
connections:

View file

@ -2,7 +2,7 @@
id: FoodPizza
icon:
sprite: Objects/Consumable/Food/Baked/pizza.rsi
state: margherita
state: margherita-slice
product: CrateFoodPizza
cost: 450
category: Food
@ -61,7 +61,7 @@
- type: cargoProduct
id: FoodSoftdrinks
icon:
sprite: Objects/Consumable/Drinks/beerglass.rsi
sprite: Objects/Consumable/Drinks/cola.rsi
state: icon
product: CrateFoodSoftdrinks
cost: 1200
@ -71,7 +71,7 @@
- type: cargoProduct
id: FoodSoftdrinksLarge
icon:
sprite: Objects/Consumable/Drinks/beerglass.rsi
sprite: Objects/Consumable/Drinks/colabottle.rsi
state: icon
product: CrateFoodSoftdrinksLarge
cost: 2400

View file

@ -14,14 +14,14 @@
id: CrateFoodPizzaLarge
parent: CratePlastic
components:
- type: StorageFill
contents:
- id: FoodBoxPizzaFilled
amount: 16
- id: KnifePlastic
amount: 4
- id: LidSalami
prob: 0.04
- type: StorageFill
contents:
- id: FoodBoxPizzaFilled
amount: 16
- id: KnifePlastic
amount: 4
- id: LidSalami
prob: 0.04
- type: entity
id: CrateFoodMRE
@ -109,37 +109,37 @@
id: CrateFoodSoftdrinks
parent: CratePlastic
components:
- type: StorageFill
contents:
- id: DrinkColaCan
amount: 4
- id: DrinkGrapeCan
amount: 2
- id: DrinkRootBeerCan
amount: 2
- id: DrinkIcedTeaCan
amount: 2
- id: DrinkLemonLimeCan
amount: 2
- id: DrinkFourteenLokoCan
amount: 2
- type: StorageFill
contents:
- id: DrinkColaCan
amount: 4
- id: DrinkGrapeCan
amount: 2
- id: DrinkRootBeerCan
amount: 2
- id: DrinkIcedTeaCan
amount: 2
- id: DrinkLemonLimeCan
amount: 2
- id: DrinkFourteenLokoCan
amount: 2
- type: entity
id: CrateFoodSoftdrinksLarge
parent: CratePlastic
components:
- type: StorageFill
contents:
- id: DrinkColaCan
amount: 8
- id: DrinkGrapeCan
amount: 4
- id: DrinkRootBeerCan
amount: 4
- id: DrinkIcedTeaCan
amount: 4
- id: DrinkLemonLimeCan
amount: 4
- id: DrinkFourteenLokoCan
amount: 4
- type: StorageFill
contents:
- id: DrinkColaCan
amount: 8
- id: DrinkGrapeCan
amount: 4
- id: DrinkRootBeerCan
amount: 4
- id: DrinkIcedTeaCan
amount: 4
- id: DrinkLemonLimeCan
amount: 4
- id: DrinkFourteenLokoCan
amount: 4

View file

@ -187,15 +187,11 @@
- id: ClothingMaskSterile
- id: ClothingHeadHatBeretCmo
- id: ClothingOuterHardsuitMedical
- id: DiagnoserMachineCircuitboard
- id: VaccinatorMachineCircuitboard
- id: Hypospray
- id: HandheldCrewMonitor
- id: DoorRemoteMedical
- id: RubberStampCMO
- id: MedicalTechFabCircuitboard
- id: MaterialBiomass
amount: 3
- id: BoxEncryptionKeyMedical
- type: entity
@ -214,15 +210,11 @@
- id: ClothingOuterCoatLabCmo
- id: ClothingMaskSterile
- id: ClothingHeadHatBeretCmo
- id: DiagnoserMachineCircuitboard
- id: VaccinatorMachineCircuitboard
- id: Hypospray
- id: HandheldCrewMonitor
- id: DoorRemoteMedical
- id: RubberStampCMO
- id: MedicalTechFabCircuitboard
- id: MaterialBiomass
amount: 3
- id: BoxEncryptionKeyMedical
- type: entity

View file

@ -5,16 +5,16 @@
components:
- type: StorageFill
contents:
- id: BoxPillCanister
- id: BoxSyringe
- id: EpinephrineChemistryBottle
amount: 3
amount: 1
- id: Brutepack
amount: 2
- id: Ointment
amount: 2
- id: Bloodpack
amount: 2
- id: Gauze
- type: entity
id: LockerWallMedicalFilled
@ -24,16 +24,16 @@
components:
- type: StorageFill
contents:
- id: BoxPillCanister
- id: BoxSyringe
- id: EpinephrineChemistryBottle
amount: 3
amount: 1
- id: Brutepack
amount: 2
- id: Ointment
amount: 2
- id: Bloodpack
amount: 2
- id: Gauze
- type: entity
@ -67,8 +67,6 @@
- id: UniformScrubsColorPurple
prob: 0.05
orGroup: Surgshrubs
- id: NitrousOxideTankFilled
prob: 0.3
- id: ClothingMaskSterile
- type: entity
@ -103,8 +101,6 @@
- id: UniformScrubsColorPurple
prob: 0.05
orGroup: Surgshrubs
- id: NitrousOxideTankFilled
prob: 0.3
- id: ClothingMaskSterile
- type: entity
@ -116,10 +112,17 @@
contents:
- id: BoxSyringe
- id: BoxBeaker
- id: BoxBeaker
prob: 0.3
- id: BoxPillCanister
- id: BoxBottle
amount: 2
- id: ChemBag
- id: ClothingHandsGlovesLatex
- id: ClothingHeadsetMedical
- id: ClothingMaskSterile
- id: HandLabeler
prob: 0.5
- type: entity
id: LockerParamedicFilled
suffix: Filled
@ -134,4 +137,8 @@
- id: ClothingUniformJumpsuitParamedic
- id: ClothingUniformJumpskirtParamedic
- id: ClothingEyesHudMedical
- id: ClothingHandsGlovesLatex
- id: ClothingHeadsetMedical
- id: ClothingMaskSterile
- id: MedkitFilled
prob: 0.3

View file

@ -22,3 +22,9 @@
- Пателла
- Тейлбоун
- Бонер
- Rib
- Hyoid
- Coccyx
- Tarsus
- Lacrimal
- Bone

View file

@ -96,3 +96,21 @@
name: signal-port-name-logic-output-low
description: signal-port-description-logic-output-low
defaultLinks: [ Off, Close ]
- type: sourcePort
id: AirDanger
name: signal-port-name-air-danger
description: signal-port-description-air-danger
defaultLinks: [ DoorBolt ]
- type: sourcePort
id: AirWarning
name: signal-port-name-air-warning
description: signal-port-description-air-warning
defaultLinks: [ DoorBolt ]
- type: sourcePort
id: AirNormal
name: signal-port-name-air-normal
description: signal-port-description-air-normal
defaultLinks: [ DoorBolt ]

View file

@ -0,0 +1,16 @@
- type: entity
name: Random Grille Spawner
id: GrilleSpawner
parent: MarkerBase
components:
- type: Sprite
layers:
- sprite: Structures/Walls/random.rsi
state: randomgrille
- type: RandomSpawner
prototypes:
- Grille
- GrilleBroken
chance: 1.0
placement:
mode: AlignTileAny

View file

@ -17,6 +17,8 @@
colliderFixtureId: EventHorizonCollider
consumerFixtureId: EventHorizonConsumer
- type: GravityWell # To make the singularity attract things.
baseRadialAcceleration: 10
maxRange: 4
- type: Fixtures
fixtures:
EventHorizonCollider:
@ -43,11 +45,16 @@
energy: 180
level: 1
radsPerLevel: 2
energyLoss: 1
- type: RandomWalk # To make the singularity move around.
maxSpeed: 2.5
minSpeed: 1.875
- type: SingularityDistortion
intensity: 20
falloffPower: 2.529822
intensity: 3645
- type: RadiationSource
slope: 0.2 # its emit really far away
intensity: 2
- type: PointLight
enabled: true
radius: 10

View file

@ -50,7 +50,6 @@
- type: Tag
tags:
- DoorBumpOpener
- type: InteractionOutline
- type: Construction
graph: BaseBigBox
node: basebigbox
@ -66,6 +65,7 @@
- type: Damageable
damageModifierSet: FlimsyMetallic #Syndicate boxes should have a bit of protection
- type: Stealth
hadOutline: true
- type: StealthOnMove
passiveVisibilityRate: -0.37
movementVisibilityRate: 0.20
@ -74,6 +74,7 @@
id: BigBox
parent: BaseBigBox
components:
- type: InteractionOutline
- type: Destructible
thresholds:
- trigger:

View file

@ -2,6 +2,10 @@
id: AirAlarm
name: air alarm
description: An air alarm. Alarms... air?
placement:
mode: SnapgridCenter
snap:
- Wallmount
components:
- type: WallMount
- type: ApcPowerReceiver
@ -16,14 +20,22 @@
prefix: device-address-prefix-air-alarm
sendBroadcastAttemptEvent: true
- type: WiredNetworkConnection
# for output status ports
- type: WirelessNetworkConnection
range: 200
- type: DeviceList
- type: DeviceNetworkRequiresPower
- type: DeviceLinkSource
ports:
- AirDanger
- AirWarning
- AirNormal
- type: AtmosAlarmable
syncWith:
- AirAlarm
- AirSensor
- GasVent
- GasScrubber
- AirAlarm
- AirSensor
- GasVent
- GasScrubber
- type: AtmosAlarmableVisuals
layerMap: "airAlarmBase"
alarmStates:
@ -85,15 +97,15 @@
- !type:PlaySoundBehavior
sound:
path: /Audio/Effects/metalbreak.ogg
placement:
mode: SnapgridCenter
snap:
- Wallmount
- type: entity
id: AirAlarmAssembly
name: air alarm assembly
description: An air alarm. Doesn't look like it'll be alarming air any time soon.
placement:
mode: SnapgridCenter
snap:
- Wallmount
components:
- type: WallMount
- type: Clickable
@ -115,7 +127,3 @@
node: assembly
- type: Transform
anchored: true
placement:
mode: SnapgridCenter
snap:
- Wallmount

View file

@ -4,6 +4,7 @@
roundStart: true
prototype: MobDiona
sprites: MobDionaSprites
defaultSkinTone: "#cdb369"
markingLimits: MobDionaMarkingLimits
dollPrototype: MobDionaDummy
skinColoration: Hues

View file

@ -4,6 +4,7 @@
roundStart: false
prototype: MobSkeletonPerson
sprites: MobSkeletonSprites
defaultSkinTone: "#fff9e2"
markingLimits: MobHumanMarkingLimits
maleFirstNames: skeletonNamesFirst
femaleFirstNames: skeletonNamesFirst

Binary file not shown.

Before

Width:  |  Height:  |  Size: 930 B

After

Width:  |  Height:  |  Size: 534 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1 KiB

After

Width:  |  Height:  |  Size: 452 B

View file

@ -1,7 +1,7 @@
{
"version": 1,
"license": "CC-BY-SA-3.0",
"copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/1516a728931b4985c1e86f0c5995a5aa1554a1ad and modified by Swept",
"copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/1516a728931b4985c1e86f0c5995a5aa1554a1ad and modified by potato1234x (Github) for ss14",
"size": {
"x": 32,
"y": 32
@ -14,22 +14,12 @@
"name": "eject-unshaded",
"delays": [
[
0.1,
0.4,
0.4,
0.4,
0.4,
0.4,
0.4,
0.4,
0.4,
0.4,
0.4,
0.4,
0.4,
0.4,
0.4,
0.1
0.2,
0.2,
0.2,
0.2,
0.2,
0.2
]
]
},

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 555 B

After

Width:  |  Height:  |  Size: 383 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 817 B

After

Width:  |  Height:  |  Size: 534 B

View file

@ -1,7 +1,7 @@
{
"version": 1,
"license": "CC-BY-SA-3.0",
"copyright": "Taken from baystation at https://github.com/Baystation12/Baystation12/commit/f200ae08d71ecbc91412ee650a334981892f5177",
"copyright": "Taken from baystation at https://github.com/Baystation12/Baystation12/commit/f200ae08d71ecbc91412ee650a334981892f5177 and modified by potato1234x",
"size": {
"x": 32,
"y": 32

Binary file not shown.

Before

Width:  |  Height:  |  Size: 498 B

After

Width:  |  Height:  |  Size: 415 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 930 B

After

Width:  |  Height:  |  Size: 476 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

View file

@ -1,7 +1,7 @@
{
"version": 1,
"license": "CC-BY-SA-3.0",
"copyright": "Taken from baystation at https://github.com/Baystation12/Baystation12/commit/f200ae08d71ecbc91412ee650a334981892f5177",
"copyright": "Taken from baystation at https://github.com/Baystation12/Baystation12/commit/f200ae08d71ecbc91412ee650a334981892f5177 and modified by potato1234x (github) for ss14",
"size": {
"x": 32,
"y": 32
@ -23,16 +23,6 @@
]
]
},
{
"name": "hellfire",
"delays": [
[
0.25,
0.25,
0.25
]
]
},
{
"name": "off"
},

Binary file not shown.

Before

Width:  |  Height:  |  Size: 519 B

After

Width:  |  Height:  |  Size: 390 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 882 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 779 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 855 B

View file

@ -1,7 +1,7 @@
{
"version": 1,
"license": "CC-BY-SA-4.0",
"copyright": "Created by Hyenh#6078 (313846233099927552)",
"copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/1516a728931b4985c1e86f0c5995a5aa1554a1ad and modified by potato1234x",
"size": {
"x": 32,
"y": 32

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 990 B

After

Width:  |  Height:  |  Size: 750 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 694 B

After

Width:  |  Height:  |  Size: 244 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 516 B

After

Width:  |  Height:  |  Size: 563 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 665 B

After

Width:  |  Height:  |  Size: 469 B

View file

@ -1,7 +1,7 @@
{
"version": 1,
"license": "CC-BY-SA-3.0",
"copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/1516a728931b4985c1e86f0c5995a5aa1554a1ad and modified by Swept",
"copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/1516a728931b4985c1e86f0c5995a5aa1554a1ad and modified by Swept and potato1234x",
"size": {
"x": 32,
"y": 32

Binary file not shown.

Before

Width:  |  Height:  |  Size: 447 B

After

Width:  |  Height:  |  Size: 365 B

View file

@ -0,0 +1,14 @@
{
"version": 1,
"license": "CC-BY-SA-3.0",
"copyright": "Grille sprite taken from https://github.com/tgstation/tgstation at commit 17c603f1d037f22081d446b9a3f3ae8d41f52e86",
"size": {
"x": 32,
"y": 32
},
"states": [
{
"name": "randomgrille"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3 KiB

@ -1 +1 @@
Subproject commit 8a440d705f753d4cec20e1a3d81b0c5fa77ef726
Subproject commit f6c55085fe9a798a45b531c056f5545db1bf4475