diff --git a/Content.Client/Mapping/MappingState.cs b/Content.Client/Mapping/MappingState.cs index bcc739fe4fc..4aa3a4aa5dc 100644 --- a/Content.Client/Mapping/MappingState.cs +++ b/Content.Client/Mapping/MappingState.cs @@ -793,7 +793,7 @@ private bool HandlePick(ICommonSession? session, EntityCoordinates coords, Entit if (_mapMan.TryFindGridAt(mapPos, out var gridUid, out var grid) && _entityManager.System().TryGetTileRef(gridUid, grid, coords, out var tileRef) && - _allPrototypesDict.TryGetValue(tileRef.GetContentTileDefinition(), out button)) + _allPrototypesDict.TryGetValue(_entityManager.System().GetContentTileDefinition(tileRef), out button)) { OnSelected(button); return true; diff --git a/Content.Server/Abilities/Mime/MimePowersSystem.cs b/Content.Server/Abilities/Mime/MimePowersSystem.cs index 3de356f6082..98f5a562a19 100644 --- a/Content.Server/Abilities/Mime/MimePowersSystem.cs +++ b/Content.Server/Abilities/Mime/MimePowersSystem.cs @@ -7,10 +7,10 @@ using Content.Shared.Maps; using Content.Shared.Paper; using Content.Shared.Physics; +using Content.Shared.Speech.Muting; using Robust.Shared.Containers; using Robust.Shared.Map; using Robust.Shared.Timing; -using Content.Shared.Speech.Muting; namespace Content.Server.Abilities.Mime { @@ -82,7 +82,7 @@ private void OnInvisibleWall(EntityUid uid, MimePowersComponent component, Invis // Get the tile in front of the mime var offsetValue = xform.LocalRotation.ToWorldVec(); var coords = xform.Coordinates.Offset(offsetValue).SnapToGrid(EntityManager, _mapMan); - var tile = coords.GetTileRef(EntityManager, _mapMan); + var tile = _turf.GetTileRef(coords); if (tile == null) return; diff --git a/Content.Server/Administration/Commands/VariantizeCommand.cs b/Content.Server/Administration/Commands/VariantizeCommand.cs index 3f9b7efd07e..a75f72b1c7d 100644 --- a/Content.Server/Administration/Commands/VariantizeCommand.cs +++ b/Content.Server/Administration/Commands/VariantizeCommand.cs @@ -10,7 +10,6 @@ namespace Content.Server.Administration.Commands; public sealed class VariantizeCommand : IConsoleCommand { [Dependency] private readonly IEntityManager _entManager = default!; - [Dependency] private readonly ITileDefinitionManager _tileDefManager = default!; public string Command => "variantize"; @@ -40,10 +39,11 @@ public void Execute(IConsoleShell shell, string argStr, string[] args) var mapsSystem = _entManager.System(); var tileSystem = _entManager.System(); + var turfSystem = _entManager.System(); foreach (var tile in mapsSystem.GetAllTiles(euid.Value, gridComp)) { - var def = tile.GetContentTileDefinition(_tileDefManager); + var def = turfSystem.GetContentTileDefinition(tile); var newTile = new Tile(tile.Tile.TypeId, tile.Tile.Flags, tileSystem.PickVariant(def)); mapsSystem.SetTile(euid.Value, gridComp, tile.GridIndices, newTile); } diff --git a/Content.Server/Atmos/Monitor/Components/AtmosAlarmableComponent.cs b/Content.Server/Atmos/Monitor/Components/AtmosAlarmableComponent.cs index e291334ad0f..cc53df2ecde 100644 --- a/Content.Server/Atmos/Monitor/Components/AtmosAlarmableComponent.cs +++ b/Content.Server/Atmos/Monitor/Components/AtmosAlarmableComponent.cs @@ -48,7 +48,7 @@ public sealed partial class AtmosAlarmableComponent : Component public HashSet SyncWithTags { get; private set; } = new(); [DataField("monitorAlertTypes")] - public HashSet? MonitorAlertTypes { get; private set; } + public AtmosMonitorThresholdTypeFlags MonitorAlertTypes { get; private set; } /// /// If this device should receive only. If it can only diff --git a/Content.Server/Atmos/Monitor/Components/AtmosMonitorComponent.cs b/Content.Server/Atmos/Monitor/Components/AtmosMonitorComponent.cs index 830479561de..ffb1fe0d277 100644 --- a/Content.Server/Atmos/Monitor/Components/AtmosMonitorComponent.cs +++ b/Content.Server/Atmos/Monitor/Components/AtmosMonitorComponent.cs @@ -59,7 +59,7 @@ public sealed partial class AtmosMonitorComponent : Component public AtmosAlarmType LastAlarmState = AtmosAlarmType.Normal; [DataField("trippedThresholds")] - public HashSet TrippedThresholds = new(); + public AtmosMonitorThresholdTypeFlags TrippedThresholds; /// /// Registered devices in this atmos monitor. Alerts will be sent directly diff --git a/Content.Server/Atmos/Monitor/Systems/AtmosAlarmableSystem.cs b/Content.Server/Atmos/Monitor/Systems/AtmosAlarmableSystem.cs index 81a3968e6ff..7934f1587f7 100644 --- a/Content.Server/Atmos/Monitor/Systems/AtmosAlarmableSystem.cs +++ b/Content.Server/Atmos/Monitor/Systems/AtmosAlarmableSystem.cs @@ -4,7 +4,6 @@ using Content.Server.DeviceNetwork; using Content.Server.DeviceNetwork.Components; using Content.Server.DeviceNetwork.Systems; -using Content.Server.Power.Components; using Content.Shared.Atmos.Monitor; using Content.Shared.DeviceNetwork; using Content.Shared.Power; @@ -13,7 +12,6 @@ using Robust.Server.GameObjects; using Robust.Shared.Audio; using Robust.Shared.Prototypes; -using Robust.Shared.Utility; namespace Content.Server.Atmos.Monitor.Systems; @@ -110,9 +108,9 @@ private void OnPacketRecv(EntityUid uid, AtmosAlarmableComponent component, Devi break; } - if (args.Data.TryGetValue(AlertTypes, out HashSet? types) && component.MonitorAlertTypes != null) + if (args.Data.TryGetValue(AlertTypes, out AtmosMonitorThresholdTypeFlags types) && component.MonitorAlertTypes != AtmosMonitorThresholdTypeFlags.None) { - isValid = types.Any(type => component.MonitorAlertTypes.Contains(type)); + isValid = (types & component.MonitorAlertTypes) != 0; } if (!component.NetworkAlarmStates.ContainsKey(args.SenderAddress)) diff --git a/Content.Server/Atmos/Monitor/Systems/AtmosMonitoringSystem.cs b/Content.Server/Atmos/Monitor/Systems/AtmosMonitoringSystem.cs index 99cf0109bbb..d5647203c67 100644 --- a/Content.Server/Atmos/Monitor/Systems/AtmosMonitoringSystem.cs +++ b/Content.Server/Atmos/Monitor/Systems/AtmosMonitoringSystem.cs @@ -4,7 +4,6 @@ using Content.Server.Atmos.Piping.EntitySystems; using Content.Server.DeviceNetwork; using Content.Server.DeviceNetwork.Systems; -using Content.Server.NodeContainer; using Content.Server.NodeContainer.EntitySystems; using Content.Server.NodeContainer.Nodes; using Content.Server.Power.Components; @@ -206,7 +205,7 @@ private void OnFireEvent(EntityUid uid, AtmosMonitorComponent component, ref Til if (component.MonitorFire && component.LastAlarmState != AtmosAlarmType.Danger) { - component.TrippedThresholds.Add(AtmosMonitorThresholdType.Temperature); + component.TrippedThresholds |= AtmosMonitorThresholdTypeFlags.Temperature; Alert(uid, AtmosAlarmType.Danger, null, component); // technically??? } @@ -217,7 +216,7 @@ private void OnFireEvent(EntityUid uid, AtmosMonitorComponent component, ref Til && component.TemperatureThreshold.CheckThreshold(args.Temperature, out var temperatureState) && temperatureState > component.LastAlarmState) { - component.TrippedThresholds.Add(AtmosMonitorThresholdType.Temperature); + component.TrippedThresholds |= AtmosMonitorThresholdTypeFlags.Temperature; Alert(uid, AtmosAlarmType.Danger, null, component); } } @@ -258,7 +257,7 @@ private void UpdateState(EntityUid uid, GasMixture? air, AtmosMonitorComponent? if (!Resolve(uid, ref monitor)) return; var state = AtmosAlarmType.Normal; - HashSet alarmTypes = new(monitor.TrippedThresholds); + var alarmTypes = monitor.TrippedThresholds; if (monitor.TemperatureThreshold != null && monitor.TemperatureThreshold.CheckThreshold(air.Temperature, out var temperatureState)) @@ -266,11 +265,11 @@ private void UpdateState(EntityUid uid, GasMixture? air, AtmosMonitorComponent? if (temperatureState > state) { state = temperatureState; - alarmTypes.Add(AtmosMonitorThresholdType.Temperature); + alarmTypes |= AtmosMonitorThresholdTypeFlags.Temperature; } else if (temperatureState == AtmosAlarmType.Normal) { - alarmTypes.Remove(AtmosMonitorThresholdType.Temperature); + alarmTypes &= ~AtmosMonitorThresholdTypeFlags.Temperature; } } @@ -281,11 +280,11 @@ private void UpdateState(EntityUid uid, GasMixture? air, AtmosMonitorComponent? if (pressureState > state) { state = pressureState; - alarmTypes.Add(AtmosMonitorThresholdType.Pressure); + alarmTypes |= AtmosMonitorThresholdTypeFlags.Pressure; } else if (pressureState == AtmosAlarmType.Normal) { - alarmTypes.Remove(AtmosMonitorThresholdType.Pressure); + alarmTypes &= ~AtmosMonitorThresholdTypeFlags.Pressure; } } @@ -305,17 +304,17 @@ private void UpdateState(EntityUid uid, GasMixture? air, AtmosMonitorComponent? if (tripped) { - alarmTypes.Add(AtmosMonitorThresholdType.Gas); + alarmTypes |= AtmosMonitorThresholdTypeFlags.Gas; } else { - alarmTypes.Remove(AtmosMonitorThresholdType.Gas); + alarmTypes &= ~AtmosMonitorThresholdTypeFlags.Gas; } } // if the state of the current air doesn't match the last alarm state, // we update the state - if (state != monitor.LastAlarmState || !alarmTypes.SetEquals(monitor.TrippedThresholds)) + if (state != monitor.LastAlarmState || alarmTypes != monitor.TrippedThresholds) { Alert(uid, state, alarmTypes, monitor); } @@ -326,7 +325,7 @@ private void UpdateState(EntityUid uid, GasMixture? air, AtmosMonitorComponent? /// /// The alarm state to set this monitor to. /// The alarms that caused this alarm state. - public void Alert(EntityUid uid, AtmosAlarmType state, HashSet? alarms = null, AtmosMonitorComponent? monitor = null) + public void Alert(EntityUid uid, AtmosAlarmType state, AtmosMonitorThresholdTypeFlags? alarms = null, AtmosMonitorComponent? monitor = null) { if (!Resolve(uid, ref monitor)) return; diff --git a/Content.Server/Audio/ContentAudioSystem.cs b/Content.Server/Audio/ContentAudioSystem.cs index cb99d8d9086..6ac9d3d6761 100644 --- a/Content.Server/Audio/ContentAudioSystem.cs +++ b/Content.Server/Audio/ContentAudioSystem.cs @@ -14,7 +14,7 @@ namespace Content.Server.Audio; public sealed class ContentAudioSystem : SharedContentAudioSystem { [ValidatePrototypeId] - private const string LobbyMusicCollection = "MonoLobbyMusic"; // Mono + private const string LobbyMusicCollection = "NullLobbyMusic"; // Null Sector [Dependency] private readonly AudioSystem _serverAudio = default!; [Dependency] private readonly IRobustRandom _robustRandom = default!; diff --git a/Content.Server/Chat/Managers/ChatSanitizationManager.cs b/Content.Server/Chat/Managers/ChatSanitizationManager.cs index 0c78e45f86e..106e5313e6e 100644 --- a/Content.Server/Chat/Managers/ChatSanitizationManager.cs +++ b/Content.Server/Chat/Managers/ChatSanitizationManager.cs @@ -12,86 +12,86 @@ namespace Content.Server.Chat.Managers; /// public sealed class ChatSanitizationManager : IChatSanitizationManager { - private static readonly Dictionary ShorthandToEmote = new() - { - { ":)", "chatsan-smiles" }, - { ":]", "chatsan-smiles" }, - { "=)", "chatsan-smiles" }, - { "=]", "chatsan-smiles" }, - { "(:", "chatsan-smiles" }, - { "[:", "chatsan-smiles" }, - { "(=", "chatsan-smiles" }, - { "[=", "chatsan-smiles" }, - { "^^", "chatsan-smiles" }, - { "^-^", "chatsan-smiles" }, - { ":(", "chatsan-frowns" }, - { ":[", "chatsan-frowns" }, - { "=(", "chatsan-frowns" }, - { "=[", "chatsan-frowns" }, - { "):", "chatsan-frowns" }, - { ")=", "chatsan-frowns" }, - { "]:", "chatsan-frowns" }, - { "]=", "chatsan-frowns" }, - { ":D", "chatsan-smiles-widely" }, - { "D:", "chatsan-frowns-deeply" }, - { ":O", "chatsan-surprised" }, - { ":3", "chatsan-smiles" }, - { ":S", "chatsan-uncertain" }, - { ":>", "chatsan-grins" }, - { ":<", "chatsan-pouts" }, - { "xD", "chatsan-laughs" }, - { ":'(", "chatsan-cries" }, - { ":'[", "chatsan-cries" }, - { "='(", "chatsan-cries" }, - { "='[", "chatsan-cries" }, - { ")':", "chatsan-cries" }, - { "]':", "chatsan-cries" }, - { ")'=", "chatsan-cries" }, - { "]'=", "chatsan-cries" }, - { ";-;", "chatsan-cries" }, - { ";_;", "chatsan-cries" }, - { "qwq", "chatsan-cries" }, - { ":u", "chatsan-smiles-smugly" }, - { ":v", "chatsan-smiles-smugly" }, - { ">:i", "chatsan-annoyed" }, - { ":i", "chatsan-sighs" }, - { ":|", "chatsan-sighs" }, - { ":p", "chatsan-stick-out-tongue" }, - { ";p", "chatsan-stick-out-tongue" }, - { ":b", "chatsan-stick-out-tongue" }, - { "0-0", "chatsan-wide-eyed" }, - { "o-o", "chatsan-wide-eyed" }, - { "o.o", "chatsan-wide-eyed" }, - { "._.", "chatsan-surprised" }, - { ".-.", "chatsan-confused" }, - { "-_-", "chatsan-unimpressed" }, - { "smh", "chatsan-unimpressed" }, - { "o/", "chatsan-waves" }, - { "^^/", "chatsan-waves" }, - { ":/", "chatsan-uncertain" }, - { ":\\", "chatsan-uncertain" }, - { "lmao", "chatsan-laughs" }, - { "lmfao", "chatsan-laughs" }, - { "lol", "chatsan-laughs" }, - { "lel", "chatsan-laughs" }, - { "kek", "chatsan-laughs" }, - { "rofl", "chatsan-laughs" }, - { "o7", "chatsan-salutes" }, - { ";_;7", "chatsan-tearfully-salutes" }, - { "idk", "chatsan-shrugs" }, - { ";)", "chatsan-winks" }, - { ";]", "chatsan-winks" }, - { "(;", "chatsan-winks" }, - { "[;", "chatsan-winks" }, - { ":')", "chatsan-tearfully-smiles" }, - { ":']", "chatsan-tearfully-smiles" }, - { "=')", "chatsan-tearfully-smiles" }, - { "=']", "chatsan-tearfully-smiles" }, - { "(':", "chatsan-tearfully-smiles" }, - { "[':", "chatsan-tearfully-smiles" }, - { "('=", "chatsan-tearfully-smiles" }, - { "['=", "chatsan-tearfully-smiles" } - }; + private static readonly (Regex regex, string emoteKey)[] ShorthandToEmote = + [ + Entry(":)", "chatsan-smiles"), + Entry(":]", "chatsan-smiles"), + Entry("=)", "chatsan-smiles"), + Entry("=]", "chatsan-smiles"), + Entry("(:", "chatsan-smiles"), + Entry("[:", "chatsan-smiles"), + Entry("(=", "chatsan-smiles"), + Entry("[=", "chatsan-smiles"), + Entry("^^", "chatsan-smiles"), + Entry("^-^", "chatsan-smiles"), + Entry(":(", "chatsan-frowns"), + Entry(":[", "chatsan-frowns"), + Entry("=(", "chatsan-frowns"), + Entry("=[", "chatsan-frowns"), + Entry("):", "chatsan-frowns"), + Entry(")=", "chatsan-frowns"), + Entry("]:", "chatsan-frowns"), + Entry("]=", "chatsan-frowns"), + Entry(":D", "chatsan-smiles-widely"), + Entry("D:", "chatsan-frowns-deeply"), + Entry(":O", "chatsan-surprised"), + Entry(":3", "chatsan-smiles"), + Entry(":S", "chatsan-uncertain"), + Entry(":>", "chatsan-grins"), + Entry(":<", "chatsan-pouts"), + Entry("xD", "chatsan-laughs"), + Entry(":'(", "chatsan-cries"), + Entry(":'[", "chatsan-cries"), + Entry("='(", "chatsan-cries"), + Entry("='[", "chatsan-cries"), + Entry(")':", "chatsan-cries"), + Entry("]':", "chatsan-cries"), + Entry(")'=", "chatsan-cries"), + Entry("]'=", "chatsan-cries"), + Entry(";-;", "chatsan-cries"), + Entry(";_;", "chatsan-cries"), + Entry("qwq", "chatsan-cries"), + Entry(":u", "chatsan-smiles-smugly"), + Entry(":v", "chatsan-smiles-smugly"), + Entry(">:i", "chatsan-annoyed"), + Entry(":i", "chatsan-sighs"), + Entry(":|", "chatsan-sighs"), + Entry(":p", "chatsan-stick-out-tongue"), + Entry(";p", "chatsan-stick-out-tongue"), + Entry(":b", "chatsan-stick-out-tongue"), + Entry("0-0", "chatsan-wide-eyed"), + Entry("o-o", "chatsan-wide-eyed"), + Entry("o.o", "chatsan-wide-eyed"), + Entry("._.", "chatsan-surprised"), + Entry(".-.", "chatsan-confused"), + Entry("-_-", "chatsan-unimpressed"), + Entry("smh", "chatsan-unimpressed"), + Entry("o/", "chatsan-waves"), + Entry("^^/", "chatsan-waves"), + Entry(":/", "chatsan-uncertain"), + Entry(":\\", "chatsan-uncertain"), + Entry("lmao", "chatsan-laughs"), + Entry("lmfao", "chatsan-laughs"), + Entry("lol", "chatsan-laughs"), + Entry("lel", "chatsan-laughs"), + Entry("kek", "chatsan-laughs"), + Entry("rofl", "chatsan-laughs"), + Entry("o7", "chatsan-salutes"), + Entry(";_;7", "chatsan-tearfully-salutes"), + Entry("idk", "chatsan-shrugs"), + Entry(";)", "chatsan-winks"), + Entry(";]", "chatsan-winks"), + Entry("(;", "chatsan-winks"), + Entry("[;", "chatsan-winks"), + Entry(":')", "chatsan-tearfully-smiles"), + Entry(":']", "chatsan-tearfully-smiles"), + Entry("=')", "chatsan-tearfully-smiles"), + Entry("=']", "chatsan-tearfully-smiles"), + Entry("(':", "chatsan-tearfully-smiles"), + Entry("[':", "chatsan-tearfully-smiles"), + Entry("('=", "chatsan-tearfully-smiles"), + Entry("['=", "chatsan-tearfully-smiles"), + ]; [Dependency] private readonly IConfigurationManager _configurationManager = default!; [Dependency] private readonly ILocalizationManager _loc = default!; @@ -125,21 +125,8 @@ public bool TrySanitizeEmoteShorthands(string message, // -1 is just a canary for nothing found yet var lastEmoteIndex = -1; - foreach (var (shorthand, emoteKey) in ShorthandToEmote) + foreach (var (r, emoteKey) in ShorthandToEmote) { - // We have to escape it because shorthands like ":)" or "-_-" would break the regex otherwise. - var escaped = Regex.Escape(shorthand); - - // So there are 2 cases: - // - If there is whitespace before it and after it is either punctuation, whitespace, or the end of the line - // Delete the word and the whitespace before - // - If it is at the start of the string and is followed by punctuation, whitespace, or the end of the line - // Delete the word and the punctuation if it exists. - var pattern = - $@"\s{escaped}(?=\p{{P}}|\s|$)|^{escaped}(?:\p{{P}}|(?=\s|$))"; - - var r = new Regex(pattern, RegexOptions.RightToLeft | RegexOptions.IgnoreCase); - // We're using sanitized as the original message until the end so that we can make sure the indices of // the emotes are accurate. var lastMatch = r.Match(sanitized); @@ -159,4 +146,21 @@ public bool TrySanitizeEmoteShorthands(string message, sanitized = message.Trim(); return emote is not null; } + + private static (Regex regex, string emoteKey) Entry(string shorthand, string emoteKey) + { + // We have to escape it because shorthands like ":)" or "-_-" would break the regex otherwise. + var escaped = Regex.Escape(shorthand); + + // So there are 2 cases: + // - If there is whitespace before it and after it is either punctuation, whitespace, or the end of the line + // Delete the word and the whitespace before + // - If it is at the start of the string and is followed by punctuation, whitespace, or the end of the line + // Delete the word and the punctuation if it exists. + var pattern = new Regex( + $@"\s{escaped}(?=\p{{P}}|\s|$)|^{escaped}(?:\p{{P}}|(?=\s|$))", + RegexOptions.RightToLeft | RegexOptions.IgnoreCase | RegexOptions.Compiled); + + return (pattern, emoteKey); + } } diff --git a/Content.Server/Chemistry/TileReactions/CreateEntityTileReaction.cs b/Content.Server/Chemistry/TileReactions/CreateEntityTileReaction.cs index 0249b6255a2..36ce480fba6 100644 --- a/Content.Server/Chemistry/TileReactions/CreateEntityTileReaction.cs +++ b/Content.Server/Chemistry/TileReactions/CreateEntityTileReaction.cs @@ -1,3 +1,4 @@ +using System.Numerics; using Content.Shared.Chemistry.Reaction; using Content.Shared.Chemistry.Reagent; using Content.Shared.FixedPoint; @@ -7,7 +8,6 @@ using Robust.Shared.Prototypes; using Robust.Shared.Random; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; -using System.Numerics; namespace Content.Server.Chemistry.TileReactions; @@ -41,33 +41,33 @@ public FixedPoint2 TileReact(TileRef tile, IEntityManager entityManager, List? data) { - if (reactVolume >= Usage) + if (reactVolume < Usage) + return FixedPoint2.Zero; + + if (Whitelist != null) { - if (Whitelist != null) + var lookup = entityManager.System(); + + int acc = 0; + foreach (var ent in lookup.GetEntitiesInTile(tile, LookupFlags.Static)) { - int acc = 0; - foreach (var ent in tile.GetEntitiesInTile()) - { - var whitelistSystem = entityManager.System(); - if (whitelistSystem.IsWhitelistPass(Whitelist, ent)) - acc += 1; + var whitelistSystem = entityManager.System(); + if (whitelistSystem.IsWhitelistPass(Whitelist, ent)) + acc += 1; - if (acc >= MaxOnTile) - return FixedPoint2.Zero; - } + if (acc >= MaxOnTile) + return FixedPoint2.Zero; } + } - var random = IoCManager.Resolve(); - var xoffs = random.NextFloat(-RandomOffsetMax, RandomOffsetMax); - var yoffs = random.NextFloat(-RandomOffsetMax, RandomOffsetMax); - - var center = entityManager.System().GetTileCenter(tile); - var pos = center.Offset(new Vector2(xoffs, yoffs)); - entityManager.SpawnEntity(Entity, pos); + var random = IoCManager.Resolve(); + var xoffs = random.NextFloat(-RandomOffsetMax, RandomOffsetMax); + var yoffs = random.NextFloat(-RandomOffsetMax, RandomOffsetMax); - return Usage; - } + var center = entityManager.System().GetTileCenter(tile); + var pos = center.Offset(new Vector2(xoffs, yoffs)); + entityManager.SpawnEntity(Entity, pos); - return FixedPoint2.Zero; + return Usage; } } diff --git a/Content.Server/Construction/Conditions/ComponentInTile.cs b/Content.Server/Construction/Conditions/ComponentInTile.cs index 429c4fdabfc..6af8c36e241 100644 --- a/Content.Server/Construction/Conditions/ComponentInTile.cs +++ b/Content.Server/Construction/Conditions/ComponentInTile.cs @@ -58,9 +58,7 @@ public bool Condition(EntityUid uid, IEntityManager entityManager) if (!entityManager.System().TryGetTileRef(transform.GridUid.Value, grid, indices, out var tile)) return !HasEntity; - var entities = tile.GetEntitiesInTile(LookupFlags.Approximate | LookupFlags.Static, lookup); - - foreach (var ent in entities) + foreach (var ent in lookup.GetEntitiesInTile(tile, flags: LookupFlags.Approximate | LookupFlags.Static)) { if (entityManager.HasComponent(ent, type)) return HasEntity; diff --git a/Content.Server/Decals/Commands/AddDecalCommand.cs b/Content.Server/Decals/Commands/AddDecalCommand.cs index 2d9c5a1b254..e7463ff9cba 100644 --- a/Content.Server/Decals/Commands/AddDecalCommand.cs +++ b/Content.Server/Decals/Commands/AddDecalCommand.cs @@ -54,8 +54,9 @@ public void Execute(IConsoleShell shell, string argStr, string[] args) } var mapSystem = _entManager.System(); + var turfSystem = _entManager.System(); var coordinates = new EntityCoordinates(gridIdRaw.Value, new Vector2(x, y)); - if (mapSystem.GetTileRef(gridIdRaw.Value, grid, coordinates).IsSpace()) + if (turfSystem.IsSpace(mapSystem.GetTileRef(gridIdRaw.Value, grid, coordinates))) { shell.WriteError($"Cannot create decal on space tile at {coordinates}."); return; diff --git a/Content.Server/Decals/DecalSystem.cs b/Content.Server/Decals/DecalSystem.cs index fb21021e6c5..bb5bb2a8575 100644 --- a/Content.Server/Decals/DecalSystem.cs +++ b/Content.Server/Decals/DecalSystem.cs @@ -46,7 +46,6 @@ public sealed class DecalSystem : SharedDecalSystem { [Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly IAdminManager _adminManager = default!; - [Dependency] private readonly ITileDefinitionManager _tileDefMan = default!; [Dependency] private readonly IParallelManager _parMan = default!; [Dependency] private readonly ChunkingSystem _chunking = default!; [Dependency] private readonly IConfigurationManager _conf = default!; @@ -54,6 +53,7 @@ public sealed class DecalSystem : SharedDecalSystem [Dependency] private readonly IAdminLogManager _adminLogger = default!; [Dependency] private readonly SharedMapSystem _mapSystem = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly TurfSystem _turf = default!; private readonly Dictionary> _dirtyChunks = new(); private readonly Dictionary>> _previousSentChunks = new(); @@ -184,7 +184,7 @@ private void OnTileChanged(ref TileChangedEvent args) foreach (var change in args.Changes) { - if (!change.NewTile.IsSpace(_tileDefMan)) + if (!_turf.IsSpace(change.NewTile)) continue; var indices = GetChunkIndices(change.GridIndices); @@ -325,7 +325,7 @@ public bool TryAddDecal(Decal decal, EntityCoordinates coordinates, out uint dec if (!TryComp(gridId, out MapGridComponent? grid)) return false; - if (_mapSystem.GetTileRef(gridId.Value, grid, coordinates).IsSpace(_tileDefMan)) + if (_turf.IsSpace(_mapSystem.GetTileRef(gridId.Value, grid, coordinates))) return false; if (!TryComp(gridId, out DecalGridComponent? comp)) diff --git a/Content.Server/DeviceLinking/Components/SignalSwitchComponent.cs b/Content.Server/DeviceLinking/Components/SignalSwitchComponent.cs index 5dd3b9c4815..376fa1a6feb 100644 --- a/Content.Server/DeviceLinking/Components/SignalSwitchComponent.cs +++ b/Content.Server/DeviceLinking/Components/SignalSwitchComponent.cs @@ -6,7 +6,7 @@ namespace Content.Server.DeviceLinking.Components; /// -/// Simple switch that will fire ports when toggled on or off. A button is jsut a switch that signals on the +/// Simple switch that will fire ports when toggled on or off. A button is just a switch that signals on the /// same port regardless of its state. /// [RegisterComponent, Access(typeof(SignalSwitchSystem))] diff --git a/Content.Server/Dragon/DragonSystem.cs b/Content.Server/Dragon/DragonSystem.cs index efac049b354..5e84c8fa0bc 100644 --- a/Content.Server/Dragon/DragonSystem.cs +++ b/Content.Server/Dragon/DragonSystem.cs @@ -1,7 +1,6 @@ using Content.Server.Objectives.Components; using Content.Server.Objectives.Systems; using Content.Server.Popups; -using Content.Server.Roles; using Content.Shared.Actions; using Content.Shared.Dragon; using Content.Shared.Maps; @@ -13,7 +12,6 @@ using Content.Shared.NPC.Systems; using Content.Shared.Zombies; using Robust.Shared.Audio.Systems; -using Robust.Shared.Map; using Robust.Shared.Map.Components; namespace Content.Server.Dragon; @@ -21,7 +19,6 @@ namespace Content.Server.Dragon; public sealed partial class DragonSystem : EntitySystem { [Dependency] private readonly CarpRiftsConditionSystem _carpRifts = default!; - [Dependency] private readonly ITileDefinitionManager _tileDef = default!; [Dependency] private readonly MovementSpeedModifierSystem _movement = default!; [Dependency] private readonly NpcFactionSystem _faction = default!; [Dependency] private readonly PopupSystem _popup = default!; @@ -30,6 +27,7 @@ public sealed partial class DragonSystem : EntitySystem [Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly SharedMapSystem _map = default!; [Dependency] private readonly MobStateSystem _mobState = default!; + [Dependency] private readonly TurfSystem _turf = default!; private EntityQuery _objQuery; @@ -159,7 +157,7 @@ private void OnSpawnRift(EntityUid uid, DragonComponent component, DragonSpawnRi // cant put a rift on solars foreach (var tile in _map.GetTilesIntersecting(xform.GridUid.Value, grid, new Circle(_transform.GetWorldPosition(xform), RiftTileRadius), false)) { - if (!tile.IsSpace(_tileDef)) + if (!_turf.IsSpace(tile)) continue; _popup.PopupEntity(Loc.GetString("carp-rift-space-proximity", ("proximity", RiftTileRadius)), uid, uid); diff --git a/Content.Server/Electrocution/ElectrocutionSystem.cs b/Content.Server/Electrocution/ElectrocutionSystem.cs index eb10f8d2809..9eb7f660306 100644 --- a/Content.Server/Electrocution/ElectrocutionSystem.cs +++ b/Content.Server/Electrocution/ElectrocutionSystem.cs @@ -55,6 +55,7 @@ public sealed class ElectrocutionSystem : SharedElectrocutionSystem [Dependency] private readonly SharedStutteringSystem _stuttering = default!; [Dependency] private readonly TagSystem _tag = default!; [Dependency] private readonly MetaDataSystem _metaData = default!; + [Dependency] private readonly TurfSystem _turf = default!; [ValidatePrototypeId] private const string StatusEffectKey = "Electrocution"; @@ -133,7 +134,7 @@ private bool IsPowered(EntityUid uid, ElectrifiedComponent electrified, Transfor return false; if (electrified.NoWindowInTile) { - var tileRef = transform.Coordinates.GetTileRef(EntityManager, _mapManager); + var tileRef = _turf.GetTileRef(transform.Coordinates); if (tileRef != null) { diff --git a/Content.Server/Explosion/Components/TriggerOnCollideComponent.cs b/Content.Server/Explosion/Components/TriggerOnCollideComponent.cs index 950bd3e462b..51fca559da5 100644 --- a/Content.Server/Explosion/Components/TriggerOnCollideComponent.cs +++ b/Content.Server/Explosion/Components/TriggerOnCollideComponent.cs @@ -4,10 +4,10 @@ namespace Content.Server.Explosion.Components public sealed partial class TriggerOnCollideComponent : Component { [DataField("fixtureID", required: true)] - public string FixtureID = String.Empty; + public string FixtureID = string.Empty; /// - /// Doesn't trigger if the other colliding fixture is nonhard. + /// Doesn't trigger if the other colliding fixture is non-hard. /// [DataField("ignoreOtherNonHard")] public bool IgnoreOtherNonHard = true; diff --git a/Content.Server/Explosion/Components/TriggerSignalOnCollideComponent.cs b/Content.Server/Explosion/Components/TriggerSignalOnCollideComponent.cs new file mode 100644 index 00000000000..03039bbce15 --- /dev/null +++ b/Content.Server/Explosion/Components/TriggerSignalOnCollideComponent.cs @@ -0,0 +1,45 @@ +using Content.Server.DeviceLinking.Systems; +using Content.Server.Explosion.EntitySystems; +using Content.Shared.DeviceLinking; +using Robust.Shared.Audio; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; + +namespace Content.Server.Explosion.Components; + +[RegisterComponent, Access(typeof(SignalSwitchSystem), typeof(TriggerSystem))] +public sealed partial class TriggerSignalOnCollideComponent : Component +{ + [DataField("fixtureID", required: true)] + public string FixtureID = string.Empty; + + /// + /// Doesn't trigger if the other colliding fixture is non-hard. + /// + [DataField("ignoreOtherNonHard")] + public bool IgnoreOtherNonHard = true; + + /// + /// The port that gets signaled when the switch turns on. + /// + [DataField("onPort", customTypeSerializer: typeof(PrototypeIdSerializer))] + public string OnPort = "On"; + + /// + /// The port that gets signaled when the switch turns off. + /// + [DataField("offPort", customTypeSerializer: typeof(PrototypeIdSerializer))] + public string OffPort = "Off"; + + /// + /// The port that gets signaled with the switch's current status. + /// This is only used if OnPort is different from OffPort, not in the case of a toggle switch. + /// + [DataField("statusPort", customTypeSerializer: typeof(PrototypeIdSerializer))] + public string StatusPort = "Status"; + + [DataField("state")] + public bool State; + + [DataField("clickSound")] + public SoundSpecifier ClickSound = new SoundPathSpecifier("/Audio/Machines/lightswitch.ogg"); +} diff --git a/Content.Server/Explosion/EntitySystems/SmokeOnTriggerSystem.cs b/Content.Server/Explosion/EntitySystems/SmokeOnTriggerSystem.cs index 3d3c5d85630..26951d81667 100644 --- a/Content.Server/Explosion/EntitySystems/SmokeOnTriggerSystem.cs +++ b/Content.Server/Explosion/EntitySystems/SmokeOnTriggerSystem.cs @@ -1,9 +1,9 @@ -using Content.Shared.Explosion.Components; -using Content.Shared.Explosion.EntitySystems; using Content.Server.Fluids.EntitySystems; using Content.Server.Spreader; using Content.Shared.Chemistry.Components; using Content.Shared.Coordinates.Helpers; +using Content.Shared.Explosion.Components; +using Content.Shared.Explosion.EntitySystems; using Content.Shared.Maps; using Robust.Server.GameObjects; using Robust.Shared.Map; @@ -19,6 +19,7 @@ public sealed class SmokeOnTriggerSystem : SharedSmokeOnTriggerSystem [Dependency] private readonly SmokeSystem _smoke = default!; [Dependency] private readonly TransformSystem _transform = default!; [Dependency] private readonly SpreaderSystem _spreader = default!; + [Dependency] private readonly TurfSystem _turf = default!; public override void Initialize() { @@ -38,7 +39,7 @@ private void OnTrigger(EntityUid uid, SmokeOnTriggerComponent comp, TriggerEvent return; } - if (_spreader.RequiresFloorToSpread(comp.SmokePrototype.ToString()) && tileRef.Tile.IsSpace()) + if (_spreader.RequiresFloorToSpread(comp.SmokePrototype.ToString()) && _turf.IsSpace(tileRef)) return; var coords = grid.MapToGrid(mapCoords); diff --git a/Content.Server/Explosion/EntitySystems/TriggerSystem.cs b/Content.Server/Explosion/EntitySystems/TriggerSystem.cs index 96bec98ddb3..58ca62fd1bb 100644 --- a/Content.Server/Explosion/EntitySystems/TriggerSystem.cs +++ b/Content.Server/Explosion/EntitySystems/TriggerSystem.cs @@ -1,23 +1,28 @@ using Content.Server.Administration.Logs; using Content.Server.Body.Systems; +using Content.Server.DeviceLinking.Systems; +using Content.Server.Electrocution; using Content.Server.Explosion.Components; using Content.Server.Flash; -using Content.Server.Electrocution; using Content.Server.Pinpointer; -using Content.Shared.Chemistry.EntitySystems; -using Content.Shared.Flash.Components; using Content.Server.Radio.EntitySystems; +using Content.Server.Station.Systems; +using Content.Shared.Body.Components; using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Components.SolutionManager; +using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Database; using Content.Shared.Explosion.Components; using Content.Shared.Explosion.Components.OnTrigger; +using Content.Shared.Flash.Components; +using Content.Shared.Humanoid; using Content.Shared.Implants.Components; using Content.Shared.Interaction; using Content.Shared.Inventory; using Content.Shared.Mobs; using Content.Shared.Mobs.Components; using Content.Shared.Payload.Components; +using Content.Shared.Projectiles; using Content.Shared.Radio; using Content.Shared.Slippery; using Content.Shared.StepTrigger.Systems; @@ -29,12 +34,11 @@ using Robust.Shared.Containers; using Robust.Shared.Physics.Events; using Robust.Shared.Physics.Systems; -using Content.Server.Station.Systems; -using Content.Shared.Humanoid; using Robust.Shared.Prototypes; using Robust.Shared.Random; -using Content.Shared.Body.Components; // Frontier: Gib organs -using Content.Shared.Projectiles; // Frontier: embed triggers +// Frontier: Gib organs + +// Frontier: embed triggers namespace Content.Server.Explosion.EntitySystems { @@ -79,6 +83,7 @@ public sealed partial class TriggerSystem : EntitySystem [Dependency] private readonly InventorySystem _inventory = default!; [Dependency] private readonly ElectrocutionSystem _electrocution = default!; [Dependency] private readonly StationSystem _station = default!; // Frontier: medical insurance + [Dependency] private readonly DeviceLinkSystem _deviceLink = default!; public override void Initialize() { @@ -94,6 +99,7 @@ public override void Initialize() SubscribeLocalEvent(OnSpawnTriggered); SubscribeLocalEvent(OnTriggerCollide); + SubscribeLocalEvent(OnTriggerCollide); SubscribeLocalEvent(OnActivate); SubscribeLocalEvent(OnImplantTrigger); SubscribeLocalEvent(OnStepTriggered); @@ -284,6 +290,22 @@ private void OnTriggerCollide(EntityUid uid, TriggerOnCollideComponent component Trigger(uid, args.OtherEntity); } + private void OnTriggerCollide(EntityUid uid, TriggerSignalOnCollideComponent comp, ref StartCollideEvent args) + { + if (args.OurFixtureId != comp.FixtureID || (comp.IgnoreOtherNonHard && !args.OtherFixture.Hard)) + return; // Short-Circuit + comp.State = !comp.State; + _deviceLink.InvokePort(uid, comp.State ? comp.OnPort : comp.OffPort); + // only send status if it's a toggle switch and not a button + if (comp.OnPort != comp.OffPort) + { + _deviceLink.SendSignal(uid, comp.StatusPort, comp.State); + } + Trigger(uid, args.OtherEntity); + _audio.PlayPvs(comp.ClickSound, uid, AudioParams.Default.WithVariation(0.125f).WithVolume(8f)); + + } + private void OnSpawnTriggered(EntityUid uid, TriggerOnSpawnComponent component, MapInitEvent args) { Trigger(uid); diff --git a/Content.Server/Fluids/EntitySystems/PuddleSystem.cs b/Content.Server/Fluids/EntitySystems/PuddleSystem.cs index 188b41fa6fa..bc907c5b3db 100644 --- a/Content.Server/Fluids/EntitySystems/PuddleSystem.cs +++ b/Content.Server/Fluids/EntitySystems/PuddleSystem.cs @@ -72,7 +72,6 @@ public sealed partial class PuddleSystem : SharedPuddleSystem [Dependency] private readonly SharedMapSystem _map = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IRobustRandom _random = default!; - [Dependency] private readonly ITileDefinitionManager _tileDefMan = default!; [Dependency] private readonly AudioSystem _audio = default!; [Dependency] private readonly EntityLookupSystem _lookup = default!; [Dependency] private readonly ReactiveSystem _reactive = default!; @@ -84,6 +83,7 @@ public sealed partial class PuddleSystem : SharedPuddleSystem [Dependency] private readonly SpeedModifierContactsSystem _speedModContacts = default!; [Dependency] private readonly TileFrictionController _tile = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly TurfSystem _turf = default!; [ValidatePrototypeId] private const string Blood = "Blood"; @@ -689,7 +689,7 @@ public override bool TrySpillAt(TileRef tileRef, Solution solution, out EntityUi } // If space return early, let that spill go out into the void - if (tileRef.Tile.IsEmpty || tileRef.IsSpace(_tileDefMan)) + if (tileRef.Tile.IsEmpty || _turf.IsSpace(tileRef)) { puddleUid = EntityUid.Invalid; return false; diff --git a/Content.Server/NPC/Systems/NPCSteeringSystem.cs b/Content.Server/NPC/Systems/NPCSteeringSystem.cs index a8124c02493..48c056c43b9 100644 --- a/Content.Server/NPC/Systems/NPCSteeringSystem.cs +++ b/Content.Server/NPC/Systems/NPCSteeringSystem.cs @@ -15,10 +15,13 @@ using Content.Shared.Movement.Systems; using Content.Shared.NPC; using Content.Shared.NPC.Components; -using Content.Shared.NPC.Systems; using Content.Shared.NPC.Events; +using Content.Shared.NPC.Systems; using Content.Shared.Physics; +using Content.Shared.Prying.Systems; using Content.Shared.Weapons.Melee; +using Microsoft.Extensions.ObjectPool; +using Prometheus; using Robust.Shared.Configuration; using Robust.Shared.Map; using Robust.Shared.Physics; @@ -28,13 +31,15 @@ using Robust.Shared.Random; using Robust.Shared.Timing; using Robust.Shared.Utility; -using Content.Shared.Prying.Systems; -using Microsoft.Extensions.ObjectPool; namespace Content.Server.NPC.Systems; public sealed partial class NPCSteeringSystem : SharedNPCSteeringSystem { + private static readonly Gauge ActiveSteeringGauge = Metrics.CreateGauge( + "npc_steering_active_count", + "Amount of NPCs trying to actively do steering"); + /* * We use context steering to determine which way to move. * This involves creating an array of possible directions and assigning a value for the desireability of each direction. @@ -87,6 +92,8 @@ public sealed partial class NPCSteeringSystem : SharedNPCSteeringSystem private object _obstacles = new(); + private int _activeSteeringCount; + public override void Initialize() { base.Initialize(); @@ -244,12 +251,15 @@ public override void Update(float frameTime) }; var curTime = _timing.CurTime; + _activeSteeringCount = 0; + Parallel.For(0, index, options, i => { var (uid, steering, mover, xform) = npcs[i]; Steer(uid, steering, mover, xform, frameTime, curTime); }); + ActiveSteeringGauge.Set(_activeSteeringCount); if (_subscribedSessions.Count > 0) { @@ -324,6 +334,8 @@ private void Steer( return; } + Interlocked.Increment(ref _activeSteeringCount); + var agentRadius = steering.Radius; var worldPos = _transform.GetWorldPosition(xform); var (layer, mask) = _physics.GetHardCollision(uid); diff --git a/Content.Server/NPC/Systems/NPCSystem.cs b/Content.Server/NPC/Systems/NPCSystem.cs index bc21916976e..ba2aa32696b 100644 --- a/Content.Server/NPC/Systems/NPCSystem.cs +++ b/Content.Server/NPC/Systems/NPCSystem.cs @@ -2,13 +2,11 @@ using Content.Server.NPC.Components; using Content.Server.NPC.HTN; using Content.Shared.CCVar; -using Content.Shared.Mind; using Content.Shared.Mind.Components; using Content.Shared.Mobs; using Content.Shared.Mobs.Systems; using Content.Shared.NPC; -using Content.Shared.NPC.Systems; -using Robust.Server.GameObjects; +using Prometheus; using Robust.Shared.Configuration; using Robust.Shared.Player; @@ -19,6 +17,10 @@ namespace Content.Server.NPC.Systems /// public sealed partial class NPCSystem : EntitySystem { + private static readonly Gauge ActiveGauge = Metrics.CreateGauge( + "npc_active_count", + "Amount of NPCs that are actively processing"); + [Dependency] private readonly IConfigurationManager _configurationManager = default!; [Dependency] private readonly HTNSystem _htn = default!; [Dependency] private readonly MobStateSystem _mobState = default!; @@ -139,6 +141,8 @@ public override void Update(float frameTime) _count = 0; // Add your system here. _htn.UpdateNPC(ref _count, _maxUpdates, frameTime); + + ActiveGauge.Set(Count()); } public void OnMobStateChange(EntityUid uid, HTNComponent component, MobStateChangedEvent args) diff --git a/Content.Server/Nuke/NukeSystem.cs b/Content.Server/Nuke/NukeSystem.cs index aa1fe324010..9f40204500f 100644 --- a/Content.Server/Nuke/NukeSystem.cs +++ b/Content.Server/Nuke/NukeSystem.cs @@ -17,9 +17,7 @@ using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.Containers; -using Robust.Shared.Map; using Robust.Shared.Map.Components; -using Robust.Shared.Player; using Robust.Shared.Random; using Robust.Shared.Utility; @@ -31,7 +29,6 @@ public sealed class NukeSystem : EntitySystem [Dependency] private readonly ChatSystem _chatSystem = default!; [Dependency] private readonly ExplosionSystem _explosions = default!; [Dependency] private readonly IRobustRandom _random = default!; - [Dependency] private readonly ITileDefinitionManager _tileDefManager = default!; [Dependency] private readonly ItemSlotsSystem _itemSlots = default!; [Dependency] private readonly NavMapSystem _navMap = default!; [Dependency] private readonly PointLightSystem _pointLight = default!; @@ -44,6 +41,7 @@ public sealed class NukeSystem : EntitySystem [Dependency] private readonly StationSystem _station = default!; [Dependency] private readonly UserInterfaceSystem _ui = default!; [Dependency] private readonly AppearanceSystem _appearance = default!; + [Dependency] private readonly TurfSystem _turf = default!; /// /// Used to calculate when the nuke song should start playing for maximum kino with the nuke sfx @@ -193,7 +191,7 @@ private async void OnAnchorButtonPressed(EntityUid uid, NukeComponent component, foreach (var tile in _map.GetTilesIntersecting(xform.GridUid.Value, grid, new Circle(worldPos, component.RequiredFloorRadius), false)) { - if (!tile.IsSpace(_tileDefManager)) + if (!_turf.IsSpace(tile)) continue; var msg = Loc.GetString("nuke-component-cant-anchor-floor"); diff --git a/Content.Server/Physics/Controllers/ConveyorController.cs b/Content.Server/Physics/Controllers/ConveyorController.cs index 6ca57bdadee..209f57afcbf 100644 --- a/Content.Server/Physics/Controllers/ConveyorController.cs +++ b/Content.Server/Physics/Controllers/ConveyorController.cs @@ -40,6 +40,7 @@ public sealed class ConveyorController : SharedConveyorController [Dependency] private readonly DeviceLinkSystem _signalSystem = default!; [Dependency] private readonly MaterialReclaimerSystem _materialReclaimer = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + [Dependency] private readonly TurfSystem _turf = default!; public override void Initialize() { @@ -144,7 +145,7 @@ protected override void AwakenConveyor(Entity ent) var xform = ent.Comp; - var beltTileRef = xform.Coordinates.GetTileRef(EntityManager, MapManager); + var beltTileRef = _turf.GetTileRef(xform.Coordinates); if (beltTileRef != null) { diff --git a/Content.Server/Physics/Controllers/MoverController.cs b/Content.Server/Physics/Controllers/MoverController.cs index 471db343b91..d0134273543 100644 --- a/Content.Server/Physics/Controllers/MoverController.cs +++ b/Content.Server/Physics/Controllers/MoverController.cs @@ -7,6 +7,7 @@ using Content.Shared.Movement.Systems; using Content.Shared.Shuttles.Components; using Content.Shared.Shuttles.Systems; +using Prometheus; using Robust.Shared.Map.Components; using Robust.Shared.Physics.Components; using Robust.Shared.Player; @@ -18,6 +19,10 @@ namespace Content.Server.Physics.Controllers; public sealed class MoverController : SharedMoverController { + private static readonly Gauge ActiveMoverGauge = Metrics.CreateGauge( + "physics_active_mover_count", + "Active amount of InputMovers being processed by MoverController"); + [Dependency] private readonly ThrusterSystem _thruster = default!; [Dependency] private readonly SharedTransformSystem _xformSystem = default!; @@ -101,6 +106,8 @@ public override void UpdateBeforeSolve(bool prediction, float frameTime) HandleMobMovement(mover, frameTime); } + ActiveMoverGauge.Set(_movers.Count); + HandleShuttleMovement(frameTime); } diff --git a/Content.Server/Pinpointer/NavMapSystem.cs b/Content.Server/Pinpointer/NavMapSystem.cs index 4d48e1a9798..d21eff2dff4 100644 --- a/Content.Server/Pinpointer/NavMapSystem.cs +++ b/Content.Server/Pinpointer/NavMapSystem.cs @@ -37,7 +37,7 @@ public sealed partial class NavMapSystem : SharedNavMapSystem [Dependency] private readonly SharedTransformSystem _transformSystem = default!; [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly IGameTiming _gameTiming = default!; - [Dependency] private readonly ITileDefinitionManager _tileDefManager = default!; + [Dependency] private readonly TurfSystem _turfSystem = default!; public const float CloseDistance = 15f; public const float FarDistance = 30f; @@ -127,7 +127,7 @@ private void OnTileChanged(ref TileChangedEvent ev) var relative = SharedMapSystem.GetChunkRelative(tile, ChunkSize); ref var tileData = ref chunk.TileData[GetTileIndex(relative)]; - if (change.NewTile.IsSpace(_tileDefManager)) + if (_turfSystem.IsSpace(change.NewTile)) { tileData = 0; if (PruneEmpty((ev.Entity, navMap), chunk)) diff --git a/Content.Server/Procedural/DungeonJob/DungeonJob.cs b/Content.Server/Procedural/DungeonJob/DungeonJob.cs index 5973c11fe40..8cbe5b023e5 100644 --- a/Content.Server/Procedural/DungeonJob/DungeonJob.cs +++ b/Content.Server/Procedural/DungeonJob/DungeonJob.cs @@ -1,7 +1,6 @@ using System.Threading; using System.Threading.Tasks; using Content.Server.Decals; -using Content.Server.NPC.Components; using Content.Server.NPC.HTN; using Content.Server.NPC.Systems; using Content.Server.Shuttles.Systems; @@ -20,7 +19,6 @@ using Robust.Shared.Physics.Components; using Robust.Shared.Prototypes; using Robust.Shared.Random; -using Robust.Shared.Utility; using IDunGenLayer = Content.Shared.Procedural.IDunGenLayer; namespace Content.Server.Procedural.DungeonJob; @@ -39,6 +37,7 @@ public sealed partial class DungeonJob : Job> private readonly EntityLookupSystem _lookup; private readonly TagSystem _tags; private readonly TileSystem _tile; + private readonly TurfSystem _turf; private readonly SharedMapSystem _maps; private readonly SharedTransformSystem _transform; @@ -68,6 +67,7 @@ public DungeonJob( DungeonSystem dungeon, EntityLookupSystem lookup, TileSystem tile, + TurfSystem turf, SharedTransformSystem transform, DungeonConfig gen, MapGridComponent grid, @@ -88,6 +88,7 @@ public DungeonJob( _dungeon = dungeon; _lookup = lookup; _tile = tile; + _turf = turf; _tags = _entManager.System(); _maps = _entManager.System(); _transform = transform; diff --git a/Content.Server/Procedural/DungeonSystem.cs b/Content.Server/Procedural/DungeonSystem.cs index 509a62aaadf..727f2b6cbc1 100644 --- a/Content.Server/Procedural/DungeonSystem.cs +++ b/Content.Server/Procedural/DungeonSystem.cs @@ -1,7 +1,5 @@ using System.Threading; using System.Threading.Tasks; -using Content.Server.Construction; -using Robust.Shared.CPUJob.JobQueues.Queues; using Content.Server.Decals; using Content.Server.GameTicking.Events; using Content.Shared.CCVar; @@ -10,11 +8,9 @@ using Content.Shared.Maps; using Content.Shared.Physics; using Content.Shared.Procedural; -using Content.Shared.Tag; -using Robust.Server.GameObjects; -using Robust.Shared.Collections; using Robust.Shared.Configuration; using Robust.Shared.Console; +using Robust.Shared.CPUJob.JobQueues.Queues; using Robust.Shared.EntitySerialization; using Robust.Shared.EntitySerialization.Systems; using Robust.Shared.Map; @@ -37,6 +33,7 @@ public sealed partial class DungeonSystem : SharedDungeonSystem [Dependency] private readonly DecalSystem _decals = default!; [Dependency] private readonly EntityLookupSystem _lookup = default!; [Dependency] private readonly TileSystem _tile = default!; + [Dependency] private readonly TurfSystem _turf = default!; [Dependency] private readonly MapLoaderSystem _loader = default!; [Dependency] private readonly SharedMapSystem _maps = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; @@ -214,6 +211,7 @@ public void GenerateDungeon(DungeonConfig gen, this, _lookup, _tile, + _turf, _transform, gen, grid, @@ -248,6 +246,7 @@ public async Task> GenerateDungeonAsync( this, _lookup, _tile, + _turf, _transform, gen, grid, @@ -276,4 +275,4 @@ public Angle GetDungeonRotation(int seed) var dungeonRotationSeed = 3 & seed; return Math.PI / 2 * dungeonRotationSeed; } -} \ No newline at end of file +} diff --git a/Content.Server/Respawn/SpecialRespawnSystem.cs b/Content.Server/Respawn/SpecialRespawnSystem.cs index a403818e742..93087123acc 100644 --- a/Content.Server/Respawn/SpecialRespawnSystem.cs +++ b/Content.Server/Respawn/SpecialRespawnSystem.cs @@ -17,7 +17,6 @@ namespace Content.Server.Respawn; public sealed class SpecialRespawnSystem : SharedSpecialRespawnSystem { [Dependency] private readonly IAdminLogManager _adminLog = default!; - [Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!; [Dependency] private readonly AtmosphereSystem _atmosphere = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; @@ -108,7 +107,7 @@ private void OnTermination(EntityUid uid, SpecialRespawnComponent component, ref foreach (var tile in _map.GetTilesIntersecting(entityGridUid.Value, grid, circle)) { - if (tile.IsSpace(_tileDefinitionManager) + if (_turf.IsSpace(tile) || _turf.IsTileBlocked(tile, CollisionGroup.MobMask) || !_atmosphere.IsTileMixtureProbablySafe(entityGridUid, entityMapUid.Value, grid.TileIndicesFor(mapPos))) @@ -179,7 +178,7 @@ public bool TryFindRandomTile(EntityUid targetGrid, EntityUid targetMap, int max foreach (var newTileRef in _map.GetTilesIntersecting(targetGrid, grid, circle)) { - if (newTileRef.IsSpace(_tileDefinitionManager) || _turf.IsTileBlocked(newTileRef, CollisionGroup.MobMask) || !_atmosphere.IsTileMixtureProbablySafe(targetGrid, targetMap, mapTarget)) + if (_turf.IsSpace(newTileRef) || _turf.IsTileBlocked(newTileRef, CollisionGroup.MobMask) || !_atmosphere.IsTileMixtureProbablySafe(targetGrid, targetMap, mapTarget)) continue; found = true; diff --git a/Content.Server/Revenant/EntitySystems/RevenantSystem.cs b/Content.Server/Revenant/EntitySystems/RevenantSystem.cs index 096fc00b15c..a245e3d2b4e 100644 --- a/Content.Server/Revenant/EntitySystems/RevenantSystem.cs +++ b/Content.Server/Revenant/EntitySystems/RevenantSystem.cs @@ -44,6 +44,7 @@ public sealed partial class RevenantSystem : EntitySystem [Dependency] private readonly StoreSystem _store = default!; [Dependency] private readonly TagSystem _tag = default!; [Dependency] private readonly VisibilitySystem _visibility = default!; + [Dependency] private readonly TurfSystem _turf = default!; [ValidatePrototypeId] private const string RevenantShopId = "ActionRevenantShop"; @@ -163,7 +164,7 @@ private bool TryUseAbility(EntityUid uid, RevenantComponent component, FixedPoin return false; } - var tileref = Transform(uid).Coordinates.GetTileRef(); + var tileref = _turf.GetTileRef(Transform(uid).Coordinates); if (tileref != null) { if(_physics.GetEntitiesIntersectingBody(uid, (int) CollisionGroup.Impassable).Count > 0) diff --git a/Content.Server/Speech/EntitySystems/ReplacementAccentSystem.cs b/Content.Server/Speech/EntitySystems/ReplacementAccentSystem.cs index 656a72b3f97..53ded45c332 100644 --- a/Content.Server/Speech/EntitySystems/ReplacementAccentSystem.cs +++ b/Content.Server/Speech/EntitySystems/ReplacementAccentSystem.cs @@ -18,9 +18,21 @@ public sealed class ReplacementAccentSystem : EntitySystem [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly ILocalizationManager _loc = default!; + private readonly Dictionary, (Regex regex, string replacement)[]> + _cachedReplacements = new(); + public override void Initialize() { SubscribeLocalEvent(OnAccent); + + _proto.PrototypesReloaded += OnPrototypesReloaded; + } + + public override void Shutdown() + { + base.Shutdown(); + + _proto.PrototypesReloaded -= OnPrototypesReloaded; } private void OnAccent(EntityUid uid, ReplacementAccentComponent component, AccentGetEvent args) @@ -47,27 +59,22 @@ public string ApplyReplacements(string message, string accent) return prototype.FullReplacements.Length != 0 ? Loc.GetString(_random.Pick(prototype.FullReplacements)) : ""; } - if (prototype.WordReplacements == null) - return message; - // Prohibition of repeated word replacements. // All replaced words placed in the final message are placed here as dashes (___) with the same length. // The regex search goes through this buffer message, from which the already replaced words are crossed out, // ensuring that the replaced words cannot be replaced again. var maskMessage = message; - foreach (var (first, replace) in prototype.WordReplacements) + foreach (var (regex, replace) in GetCachedReplacements(prototype)) { - var f = _loc.GetString(first); - var r = _loc.GetString(replace); // this is kind of slow but its not that bad // essentially: go over all matches, try to match capitalization where possible, then replace // rather than using regex.replace - for (int i = Regex.Count(maskMessage, $@"(? 0; i--) + for (int i = regex.Count(maskMessage); i > 0; i--) { // fetch the match again as the character indices may have changed - Match match = Regex.Match(maskMessage, $@"(? + { + var (first, replace) = kv; + var firstLoc = _loc.GetString(first); + var replaceLoc = _loc.GetString(replace); + + var regex = new Regex($@"(? + /// A recycled hashset used to check turfs for spiderwebs. + /// + private readonly HashSet _webs = []; public override void Initialize() { @@ -17,6 +26,30 @@ public override void Initialize() SubscribeLocalEvent(OnSpawnNet); } + public override void Update(float frameTime) + { + base.Update(frameTime); + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var spider)) + { + spider.NextWebSpawn ??= _timing.CurTime + spider.WebSpawnCooldown; + + if (_timing.CurTime < spider.NextWebSpawn) + continue; + + spider.NextWebSpawn += spider.WebSpawnCooldown; + + if (HasComp(uid) + || _mobState.IsDead(uid) + || !spider.SpawnsWebsAsNonPlayer) + continue; + + var transform = Transform(uid); + SpawnWeb((uid, spider), transform.Coordinates); + } + } + private void OnSpawnNet(EntityUid uid, SpiderComponent component, SpiderWebActionEvent args) { if (args.Handled) @@ -30,43 +63,49 @@ private void OnSpawnNet(EntityUid uid, SpiderComponent component, SpiderWebActio return; } - var coords = transform.Coordinates; + var result = SpawnWeb((uid, component), transform.Coordinates); - // TODO generic way to get certain coordinates + if (result) + { + _popup.PopupEntity(Loc.GetString("spider-web-action-success"), args.Performer, args.Performer); + args.Handled = true; + } + else + _popup.PopupEntity(Loc.GetString("spider-web-action-fail"), args.Performer, args.Performer); + } + private bool SpawnWeb(Entity ent, EntityCoordinates coords) + { var result = false; + // Spawn web in center if (!IsTileBlockedByWeb(coords)) { - Spawn(component.WebPrototype, coords); + Spawn(ent.Comp.WebPrototype, coords); result = true; } // Spawn web in other directions for (var i = 0; i < 4; i++) { - var direction = (DirectionFlag) (1 << i); - coords = transform.Coordinates.Offset(direction.AsDir().ToVec()); - - if (!IsTileBlockedByWeb(coords)) - { - Spawn(component.WebPrototype, coords); - result = true; - } - } + var direction = (DirectionFlag)(1 << i); + var outerSpawnCoordinates = coords.Offset(direction.AsDir().ToVec()); - if (result) - { - _popup.PopupEntity(Loc.GetString("spider-web-action-success"), args.Performer, args.Performer); - args.Handled = true; + if (IsTileBlockedByWeb(outerSpawnCoordinates)) + continue; + + Spawn(ent.Comp.WebPrototype, outerSpawnCoordinates); + result = true; } - else - _popup.PopupEntity(Loc.GetString("spider-web-action-fail"), args.Performer, args.Performer); + + return result; } private bool IsTileBlockedByWeb(EntityCoordinates coords) { - foreach (var entity in coords.GetEntitiesInTile()) + _webs.Clear(); + _turf.GetEntitiesInTile(coords, _webs); + foreach (var entity in _webs) { if (HasComp(entity)) return true; diff --git a/Content.Server/Spreader/SpreaderSystem.cs b/Content.Server/Spreader/SpreaderSystem.cs index 50f5d81183b..328f4f0504c 100644 --- a/Content.Server/Spreader/SpreaderSystem.cs +++ b/Content.Server/Spreader/SpreaderSystem.cs @@ -23,6 +23,7 @@ public sealed class SpreaderSystem : EntitySystem [Dependency] private readonly IRobustRandom _robustRandom = default!; [Dependency] private readonly SharedMapSystem _map = default!; [Dependency] private readonly TagSystem _tag = default!; + [Dependency] private readonly TurfSystem _turf = default!; /// /// Cached maximum number of updates per spreader prototype. This is applied per-grid. @@ -246,7 +247,7 @@ public void GetNeighbors(EntityUid uid, TransformComponent comp, ProtoId public override void Initialize() @@ -30,7 +31,7 @@ private void OnFloorPlanBuilt(EntityUid uid, SimpleFloorPlanPopulatorComponent c while (enumerator.MoveNext(out var tile)) { var coords = grid.GridTileToLocal(tile.Value.GridIndices); - var selector = tile.Value.Tile.GetContentTileDefinition(_tileDefinition).ID; + var selector = _turf.GetContentTileDefinition(tile.Value).ID; if (!component.Caches.TryGetValue(selector, out var cache)) continue; diff --git a/Content.Server/Zombies/ZombieSystem.Transform.cs b/Content.Server/Zombies/ZombieSystem.Transform.cs index a65dff7c184..ce3e4e10adb 100644 --- a/Content.Server/Zombies/ZombieSystem.Transform.cs +++ b/Content.Server/Zombies/ZombieSystem.Transform.cs @@ -147,8 +147,15 @@ public void ZombifyEntity(EntityUid target, MobStateComponent? mobState = null) if (!Resolve(target, ref mobState, logMissing: false)) return; + // You're a real zombie now, son. var zombieComponent = AddComp(target); + + // If the zombie is designated as non-infectious in the prototype, then add non-spreader component. + // this could be done better with a boolean toggle, but who cares. + if (TryComp(target, out var pendingZombieComponent) && !pendingZombieComponent.Infectious) + AddComp(target); + zombieComponent.BeforeComponentData = [ (false, typeof(RespiratorComponent), null), @@ -288,8 +295,9 @@ public void ZombifyEntity(EntityUid target, MobStateComponent? mobState = null) #endregion - //This is specifically here to combat insulated gloves, because frying zombies on grilles is funny as shit. + //This is specifically here to combat insulated gloves, because frying zombies on grilles is funny. _inventory.TryUnequip(target, "gloves", true, true); + //Should prevent instances of zombies using comms for information they shouldn't be able to have. // Null Sector: Zombies will be permitted to hear comms, due to Semi-Intelligence vote. //_inventory.TryUnequip(target, "ears", true, true); @@ -303,6 +311,7 @@ public void ZombifyEntity(EntityUid target, MobStateComponent? mobState = null) //Make the zombie not die in the cold. Good for space zombies if (TryComp(target, out var tempComp)) { + // Store old cold resistance to restore later zombieComponent.BeforeColdDamage = _serializationManager.CreateCopy(tempComp.ColdDamage, notNullableOverride: true); tempComp.ColdDamage.ClampMax(0); } diff --git a/Content.Shared/Abilities/Goliath/GoliathTentacleSystem.cs b/Content.Shared/Abilities/Goliath/GoliathTentacleSystem.cs index 98dbc5e18a4..a1ccccdad95 100644 --- a/Content.Shared/Abilities/Goliath/GoliathTentacleSystem.cs +++ b/Content.Shared/Abilities/Goliath/GoliathTentacleSystem.cs @@ -54,7 +54,7 @@ private void OnSummonAction(GoliathSummonTentacleAction args) foreach (var pos in spawnPos) { if (!_map.TryGetTileRef(grid, gridComp, pos, out var tileRef) || - tileRef.IsSpace() || + _turf.IsSpace(tileRef) || _turf.IsTileBlocked(tileRef, CollisionGroup.Impassable)) { continue; diff --git a/Content.Shared/Atmos/Monitor/AtmosAlarmThreshold.cs b/Content.Shared/Atmos/Monitor/AtmosAlarmThreshold.cs index f80a887b4ff..ce1d9862e99 100644 --- a/Content.Shared/Atmos/Monitor/AtmosAlarmThreshold.cs +++ b/Content.Shared/Atmos/Monitor/AtmosAlarmThreshold.cs @@ -383,9 +383,21 @@ public enum AtmosMonitorLimitType // +/// Bitflags version of +/// +[Flags] +public enum AtmosMonitorThresholdTypeFlags +{ + None = 0, + Temperature = 1 << 0, + Pressure = 1 << 1, + Gas = 1 << 2, } [Serializable, NetSerializable] diff --git a/Content.Shared/CCVar/CCVars.cs b/Content.Shared/CCVar/CCVars.cs index 6c705808bb4..6e8cd0b92c7 100644 --- a/Content.Shared/CCVar/CCVars.cs +++ b/Content.Shared/CCVar/CCVars.cs @@ -36,7 +36,7 @@ public sealed partial class CCVars : CVars /// Set to true to disable parallel processing in the pow3r solver. /// public static readonly CVarDef DebugPow3rDisableParallel = - CVarDef.Create("debug.pow3r_disable_parallel", true, CVar.SERVERONLY); + CVarDef.Create("debug.pow3r_disable_parallel", false, CVar.SERVERONLY); #region Surgery diff --git a/Content.Shared/Construction/Conditions/TileNotBlocked.cs b/Content.Shared/Construction/Conditions/TileNotBlocked.cs index 02bc84f7362..c56dcb15838 100644 --- a/Content.Shared/Construction/Conditions/TileNotBlocked.cs +++ b/Content.Shared/Construction/Conditions/TileNotBlocked.cs @@ -1,4 +1,5 @@ using Content.Shared.Maps; +using Content.Shared.Physics; using JetBrains.Annotations; using Robust.Shared.Map; @@ -14,24 +15,25 @@ public sealed partial class TileNotBlocked : IConstructionCondition public bool Condition(EntityUid user, EntityCoordinates location, Direction direction) { - var tileRef = location.GetTileRef(); + if (!IoCManager.Resolve().TrySystem(out var turfSystem)) + return false; - if (tileRef == null) + if (!turfSystem.TryGetTileRef(location, out var tileRef)) { return false; } - if (tileRef.Value.IsSpace() && _failIfSpace) + if (turfSystem.IsSpace(tileRef.Value) && _failIfSpace) { return false; } - if (!tileRef.Value.GetContentTileDefinition().Sturdy && _failIfNotSturdy) + if (!turfSystem.GetContentTileDefinition(tileRef.Value).Sturdy && _failIfNotSturdy) { return false; } - return !tileRef.Value.IsBlockedTurf(_filterMobs); + return !turfSystem.IsTileBlocked(tileRef.Value, _filterMobs ? CollisionGroup.MobMask : CollisionGroup.Impassable); } public ConstructionGuideEntry GenerateGuideEntry() diff --git a/Content.Shared/Construction/Conditions/TileType.cs b/Content.Shared/Construction/Conditions/TileType.cs index 3ffdf660502..e066e6af2e0 100644 --- a/Content.Shared/Construction/Conditions/TileType.cs +++ b/Content.Shared/Construction/Conditions/TileType.cs @@ -20,12 +20,13 @@ public sealed partial class TileType : IConstructionCondition public bool Condition(EntityUid user, EntityCoordinates location, Direction direction) { - var tileFound = location.GetTileRef(); + if (!IoCManager.Resolve().TrySystem(out var turfSystem)) + return false; - if (tileFound == null) + if (!turfSystem.TryGetTileRef(location, out var tileFound)) return false; - var tile = tileFound.Value.Tile.GetContentTileDefinition(); + var tile = turfSystem.GetContentTileDefinition(tileFound.Value); foreach (var targetTile in TargetTiles) { if (tile.ID == targetTile) diff --git a/Content.Shared/Magic/SharedMagicSystem.cs b/Content.Shared/Magic/SharedMagicSystem.cs index 3570491b730..9fd39c45a7d 100644 --- a/Content.Shared/Magic/SharedMagicSystem.cs +++ b/Content.Shared/Magic/SharedMagicSystem.cs @@ -63,6 +63,7 @@ public abstract class SharedMagicSystem : EntitySystem [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedMindSystem _mind = default!; [Dependency] private readonly SharedStunSystem _stun = default!; + [Dependency] private readonly TurfSystem _turf = default!; public override void Initialize() { @@ -158,7 +159,7 @@ private List GetInstantSpawnPositions(TransformComponent cast if (!TryComp(casterXform.GridUid, out var mapGrid)) return new List(); - if (!directionPos.TryGetTileRef(out var tileReference, EntityManager, _mapManager)) + if (!_turf.TryGetTileRef(directionPos, out var tileReference)) return new List(); var tileIndex = tileReference.Value.GridIndices; @@ -171,7 +172,7 @@ private List GetInstantSpawnPositions(TransformComponent cast if (!TryComp(casterXform.GridUid, out var mapGrid)) return new List(); - if (!directionPos.TryGetTileRef(out var tileReference, EntityManager, _mapManager)) + if (!_turf.TryGetTileRef(directionPos, out var tileReference)) return new List(); var tileIndex = tileReference.Value.GridIndices; diff --git a/Content.Shared/Maps/TurfSystem.cs b/Content.Shared/Maps/TurfSystem.cs index 8a4bbf68bed..2d62b3e155a 100644 --- a/Content.Shared/Maps/TurfSystem.cs +++ b/Content.Shared/Maps/TurfSystem.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using System.Numerics; using Content.Shared.Physics; using Robust.Shared.Map; @@ -11,8 +12,43 @@ namespace Content.Shared.Maps; /// public sealed class TurfSystem : EntitySystem { + [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly EntityLookupSystem _entityLookup = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly SharedMapSystem _mapSystem = default!; + [Dependency] private readonly ITileDefinitionManager _tileDefinitions = default!; + + + /// + /// Attempts to get the turf at or under some given coordinates or null if no such turf exists. + /// + /// The coordinates to search for a turf. + /// A for the turf found at the given coordinates or null if no such turf exists. + public TileRef? GetTileRef(EntityCoordinates coordinates) + { + if (!coordinates.IsValid(EntityManager)) + return null; + + var pos = _transform.ToMapCoordinates(coordinates); + if (!_mapManager.TryFindGridAt(pos, out var gridUid, out var gridComp)) + return null; + + if (!_mapSystem.TryGetTileRef(gridUid, gridComp, coordinates, out var tile)) + return null; + + return tile; + } + + /// + /// Attempts to get the turf at or under some given coordinates. + /// + /// The coordinates to search for a turf. + /// Returns the turf found at the given coordinates if any. + /// True if a turf was found at the given coordinates, false otherwise. + public bool TryGetTileRef(EntityCoordinates coordinates, [NotNullWhen(true)] out TileRef? tile) + { + return (tile = GetTileRef(coordinates)) is not null; + } /// /// Returns true if a given tile is blocked by physics-enabled entities. @@ -63,14 +99,14 @@ public bool IsTileBlocked(EntityUid gridUid, rot -= gridRot; pos = (-gridRot).RotateVec(pos - gridPos); - var xform = new Transform(pos, (float) rot.Theta); + var xform = new Transform(pos, (float)rot.Theta); foreach (var fixture in fixtures.Fixtures.Values) { if (!fixture.Hard) continue; - if ((fixture.CollisionLayer & (int) mask) == 0) + if ((fixture.CollisionLayer & (int)mask) == 0) continue; for (var i = 0; i < fixture.Shape.ChildCount; i++) @@ -86,6 +122,26 @@ public bool IsTileBlocked(EntityUid gridUid, return false; } + /// + /// Returns whether a tile is considered to be space or directly exposed to space. + /// + /// The tile in question. + /// True if the tile is considered to be space, false otherwise. + public bool IsSpace(Tile tile) + { + return GetContentTileDefinition(tile).MapAtmosphere; + } + + /// + /// Returns whether a tile is considered to be space or directly exposed to space. + /// + /// The tile in question. + /// True if the tile is considered to be space, false otherwise. + public bool IsSpace(TileRef tile) + { + return IsSpace(tile.Tile); + } + /// /// Returns the location of the centre of the tile in grid coordinates. /// @@ -95,4 +151,78 @@ public EntityCoordinates GetTileCenter(TileRef turf) var center = (turf.GridIndices + new Vector2(0.5f, 0.5f)) * grid.TileSize; return new EntityCoordinates(turf.GridUid, center); } + + /// + /// Returns the content tile definition for a tile. + /// + public ContentTileDefinition GetContentTileDefinition(Tile tile) + { + return (ContentTileDefinition)_tileDefinitions[tile.TypeId]; + } + + /// + /// Returns the content tile definition for a tile ref. + /// + public ContentTileDefinition GetContentTileDefinition(TileRef tile) + { + return GetContentTileDefinition(tile.Tile); + } + + /// + /// Collects all of the entities intersecting with the turf at a given position into a provided + /// + /// The position of the turf to search for entities. + /// The hashset used to collect the relevant entities. + /// A set of lookup categories to search for relevant entities. + public void GetEntitiesInTile(EntityCoordinates coords, HashSet intersecting, LookupFlags flags = LookupFlags.Static) + { + if (!TryGetTileRef(coords, out var tileRef)) + return; + + _entityLookup.GetEntitiesInTile(tileRef.Value, intersecting, flags); + } + + /// + /// Returns a collection containing all of the entities overlapping with the turf at a given position. + /// + /// + /// A hashset containing all of the entities overlapping with the turf in question. + public HashSet GetEntitiesInTile(EntityCoordinates coords, LookupFlags flags = LookupFlags.Static) + { + if (!TryGetTileRef(coords, out var tileRef)) + return []; + + return _entityLookup.GetEntitiesInTile(tileRef.Value, flags); + } +} + +/// +/// Extension methods for looking up entities with respect to given turfs. +/// +public static partial class TurfLookupExtensions +{ + /// + /// Collects all of the entities overlapping with a given turf into a provided . + /// + /// The turf in question. + /// The hashset used to collect the relevant entities. + /// A set of lookup categories to search for relevant entities. + public static void GetEntitiesInTile(this EntityLookupSystem lookupSystem, TileRef turf, HashSet intersecting, LookupFlags flags = LookupFlags.Static) + { + var bounds = lookupSystem.GetWorldBounds(turf); + bounds.Box = bounds.Box.Scale(0.9f); // Otherwise the box can clip into neighboring tiles. + lookupSystem.GetEntitiesIntersecting(turf.GridUid, bounds, intersecting, flags); + } + + /// + /// Returns a collection containing all of the entities overlapping with a given turf. + /// + /// + /// A hashset containing all of the entities overlapping with the turf in question. + public static HashSet GetEntitiesInTile(this EntityLookupSystem lookupSystem, TileRef turf, LookupFlags flags = LookupFlags.Static) + { + var intersecting = new HashSet(); + lookupSystem.GetEntitiesInTile(turf, intersecting, flags); + return intersecting; + } } diff --git a/Content.Shared/Movement/Systems/SharedMoverController.cs b/Content.Shared/Movement/Systems/SharedMoverController.cs index 73eebd5fcd8..ab800f25ba2 100644 --- a/Content.Shared/Movement/Systems/SharedMoverController.cs +++ b/Content.Shared/Movement/Systems/SharedMoverController.cs @@ -77,6 +77,8 @@ public abstract partial class SharedMoverController : VirtualController /// public Dictionary UsedMobMovement = new(); + private readonly HashSet _aroundColliderSet = []; + public override void Initialize() { UpdatesBefore.Add(typeof(TileFrictionController)); @@ -471,7 +473,9 @@ private bool IsAroundCollider(EntityLookupSystem lookupSystem, Entity(OnInit); - SubscribeLocalEvent(OnWebStartup); } private void OnInit(EntityUid uid, SpiderComponent component, MapInitEvent args) { _action.AddAction(uid, ref component.Action, component.WebAction, uid); } - - private void OnWebStartup(EntityUid uid, SpiderWebObjectComponent component, ComponentStartup args) - { - // TODO dont use this. use some general random appearance system - _appearance.SetData(uid, SpiderWebVisuals.Variant, _robustRandom.Next(1, 3)); - } } diff --git a/Content.Shared/Spider/SpiderComponent.cs b/Content.Shared/Spider/SpiderComponent.cs index 42213adcb10..3a1612fed07 100644 --- a/Content.Shared/Spider/SpiderComponent.cs +++ b/Content.Shared/Spider/SpiderComponent.cs @@ -18,6 +18,24 @@ public sealed partial class SpiderComponent : Component public string WebAction = "ActionSpiderWeb"; [DataField] public EntityUid? Action; + + /// + /// Whether the spider will spawn webs when not controlled by a player. + /// + [DataField] + public bool SpawnsWebsAsNonPlayer = true; + + /// + /// The cooldown in seconds between web spawns when not controlled by a player. + /// + [DataField] + public TimeSpan WebSpawnCooldown = TimeSpan.FromSeconds(45f); + + /// + /// The next time the spider can spawn a web when not controlled by a player. + /// + [DataField] + public TimeSpan? NextWebSpawn; } public sealed partial class SpiderWebActionEvent : InstantActionEvent { } diff --git a/Content.Shared/Spider/SpiderWebVisualsComponent.cs b/Content.Shared/Spider/SpiderWebVisualsComponent.cs deleted file mode 100644 index 2d6fb588159..00000000000 --- a/Content.Shared/Spider/SpiderWebVisualsComponent.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Robust.Shared.Serialization; - -namespace Content.Shared.Spider; - -[Serializable, NetSerializable] -public enum SpiderWebVisuals -{ - Variant -} diff --git a/Content.Shared/Tiles/FloorTileSystem.cs b/Content.Shared/Tiles/FloorTileSystem.cs index 8a66662c2d9..1eb5213fdfa 100644 --- a/Content.Shared/Tiles/FloorTileSystem.cs +++ b/Content.Shared/Tiles/FloorTileSystem.cs @@ -35,9 +35,15 @@ public sealed class FloorTileSystem : EntitySystem [Dependency] private readonly TileSystem _tile = default!; [Dependency] private readonly SharedPhysicsSystem _physics = default!; [Dependency] private readonly SharedMapSystem _map = default!; + [Dependency] private readonly TurfSystem _turf = default!; private static readonly Vector2 CheckRange = new(1f, 1f); + /// + /// A recycled hashset used to check for walls when trying to place tiles on turfs. + /// + private readonly HashSet _turfCheck = []; + public override void Initialize() { base.Initialize(); @@ -102,14 +108,16 @@ private void OnAfterInteract(EntityUid uid, FloorTileComponent component, AfterI // if user can access tile center then they can place floor // otherwise check it isn't blocked by a wall - if (!canAccessCenter) + if (!canAccessCenter && _turf.TryGetTileRef(location, out var tileRef)) { - foreach (var ent in location.GetEntitiesInTile(lookupSystem: _lookup)) + _turfCheck.Clear(); + _lookup.GetEntitiesInTile(tileRef.Value, _turfCheck); + foreach (var ent in _turfCheck) { if (physicQuery.TryGetComponent(ent, out var phys) && phys.BodyType == BodyType.Static && phys.Hard && - (phys.CollisionLayer & (int) CollisionGroup.Impassable) != 0) + (phys.CollisionLayer & (int)CollisionGroup.Impassable) != 0) { return; } diff --git a/Content.Shared/Zombies/PendingZombieComponent.cs b/Content.Shared/Zombies/PendingZombieComponent.cs index 39838514a16..88b523bc8af 100644 --- a/Content.Shared/Zombies/PendingZombieComponent.cs +++ b/Content.Shared/Zombies/PendingZombieComponent.cs @@ -28,6 +28,12 @@ public sealed partial class PendingZombieComponent : Component [DataField("instantaneous")] public bool IsInstant = false; + /// + /// Determines whether this Zombie can infect other entities. If they are immune, this has no affect. + /// + [DataField("infectious")] + public bool Infectious = true; + /// /// A multiplier for applied when the entity is in critical condition. /// diff --git a/DEV_RESEARCH_GRAPH.pdn b/DEV_RESEARCH_GRAPH.pdn index 38434c9bcf2..764df36a19a 100644 Binary files a/DEV_RESEARCH_GRAPH.pdn and b/DEV_RESEARCH_GRAPH.pdn differ diff --git a/Resources/Audio/Expedition/attributions.yml b/Resources/Audio/Expedition/attributions.yml index 932a5c7cad0..bb13a4530f4 100644 --- a/Resources/Audio/Expedition/attributions.yml +++ b/Resources/Audio/Expedition/attributions.yml @@ -16,4 +16,14 @@ - files: ["scp_isd.ogg"] license: "CC-BY-SA-3.0" copyright: "Made by PurpleJester" - source: "https://www.youtube.com/watch?v=J_JXKZvLi3Q" \ No newline at end of file + source: "https://www.youtube.com/watch?v=J_JXKZvLi3Q" + +- files: ["planetxmarksthespot.ogg"] + license: "CC-BY-SA-3.0" + copyright: "Doctor Steel - Planet X Marks the Spot" + source: "https://www.youtube.com/watch?v=luoA-NevOGY" + +- files: ["the_devil_you_know.ogg"] + license: "CC-BY-SA-3.0" + copyright: "Blues Sacramento - The Devil You Know (Instrumental)" + source: "https://www.youtube.com/watch?v=vySg8DtnXYo" \ No newline at end of file diff --git a/Resources/Audio/Expedition/planetxmarksthespot.ogg b/Resources/Audio/Expedition/planetxmarksthespot.ogg new file mode 100644 index 00000000000..2209330b40b Binary files /dev/null and b/Resources/Audio/Expedition/planetxmarksthespot.ogg differ diff --git a/Resources/Audio/Expedition/the_devil_you_know.ogg b/Resources/Audio/Expedition/the_devil_you_know.ogg new file mode 100644 index 00000000000..f74a3e8a776 Binary files /dev/null and b/Resources/Audio/Expedition/the_devil_you_know.ogg differ diff --git a/Resources/Audio/_Null/Jukebox/attributions.yml b/Resources/Audio/_Null/Jukebox/attributions.yml index 611a851cc7b..b76367eb206 100644 --- a/Resources/Audio/_Null/Jukebox/attributions.yml +++ b/Resources/Audio/_Null/Jukebox/attributions.yml @@ -7,3 +7,13 @@ license: "CC-BY-NC-SA-3.0" copyright: "Noosphere (Pipe Organ Cut) - Guillaume David" source: "https://www.youtube.com/watch?v=KCR7ovEwtCA" + +- files: ["morsmordre.ogg"] + license: "CC-BY-NC-SA-3.0" + copyright: "Crazy Donkey - Morsmordre" + source: "https://www.youtube.com/watch?v=eYTpGDqsS0E" + +- files: ["front_hall_police_station.ogg"] + license: "CC-BY-NC-SA-3.0" + copyright: "Resident Evil 2 OST - The Front Hall" + source: "https://www.youtube.com/watch?v=D30IAZAhLmA" diff --git a/Resources/Audio/_Null/Jukebox/front_hall_police_station.ogg b/Resources/Audio/_Null/Jukebox/front_hall_police_station.ogg new file mode 100644 index 00000000000..2c2d47888b7 Binary files /dev/null and b/Resources/Audio/_Null/Jukebox/front_hall_police_station.ogg differ diff --git a/Resources/Audio/_Null/Jukebox/morsmordre.ogg b/Resources/Audio/_Null/Jukebox/morsmordre.ogg new file mode 100644 index 00000000000..b49e5933e31 Binary files /dev/null and b/Resources/Audio/_Null/Jukebox/morsmordre.ogg differ diff --git a/Resources/Locale/en-US/_Null/ship_weapons.ftl b/Resources/Locale/en-US/_Null/ship_weapons.ftl new file mode 100644 index 00000000000..b0d52ac59e0 --- /dev/null +++ b/Resources/Locale/en-US/_Null/ship_weapons.ftl @@ -0,0 +1 @@ +research-technology-mining-laser = PGI-3K Mining Advancements \ No newline at end of file diff --git a/Resources/Maps/_Null/POI/lark_relay.yml b/Resources/Maps/_Null/POI/lark_relay.yml index eb14fa60da4..ae38249b84b 100644 --- a/Resources/Maps/_Null/POI/lark_relay.yml +++ b/Resources/Maps/_Null/POI/lark_relay.yml @@ -1170,7 +1170,7 @@ entities: - type: Transform pos: -6.4079437,12.119837 parent: 1 -- proto: FrontierUplinkCoin +- proto: SplenduriumCoin entities: - uid: 98 components: diff --git a/Resources/Prototypes/Entities/Clothing/Eyes/glasses.yml b/Resources/Prototypes/Entities/Clothing/Eyes/glasses.yml index dc245970e09..045f983b68d 100644 --- a/Resources/Prototypes/Entities/Clothing/Eyes/glasses.yml +++ b/Resources/Prototypes/Entities/Clothing/Eyes/glasses.yml @@ -67,7 +67,10 @@ coverage: EYES - type: StaticPrice # Frontier price: 135 # Frontier - + - type: Tag + tags: + - HudMeson + - type: entity parent: ClothingEyesBase id: ClothingEyesGlasses diff --git a/Resources/Prototypes/Entities/Clothing/Eyes/hud.yml b/Resources/Prototypes/Entities/Clothing/Eyes/hud.yml index 41e09a4d747..0024d1114fa 100644 --- a/Resources/Prototypes/Entities/Clothing/Eyes/hud.yml +++ b/Resources/Prototypes/Entities/Clothing/Eyes/hud.yml @@ -163,6 +163,26 @@ graph: HudMedSec node: medsecHud +# Null Sector - Meson / Engineering Goggles + Medical Hud +- type: entity + parent: [ClothingEyesBase, ShowMedicalIcons, ClothingEyesGlassesMeson] + id: ClothingEyesHudMedicalMeson + name: medical-meson hud + description: Green-tinted heads-up display using a proprietary polymer that provides protection from eye damage of all types and scans the humanoids in view to provides accurate data about their health status. + components: + - type: Sprite + sprite: Clothing/Eyes/Hud/medmeson.rsi + - type: Clothing + sprite: Clothing/Eyes/Hud/medmeson.rsi + - type: Tag + tags: + - HudMedical + - type: StaticPrice # Null Sector + price: 165 # Null Sector + - type: Construction + graph: HudMedMeson + node: medmesonHud + - type: entity parent: [ClothingEyesBase, ShowSecurityIcons, ShowMedicalIcons] id: ClothingEyesHudMultiversal diff --git a/Resources/Prototypes/Entities/Markers/Spawners/Random/Salvage/tables_loot.yml b/Resources/Prototypes/Entities/Markers/Spawners/Random/Salvage/tables_loot.yml index 79636ab9561..92dffee5db0 100644 --- a/Resources/Prototypes/Entities/Markers/Spawners/Random/Salvage/tables_loot.yml +++ b/Resources/Prototypes/Entities/Markers/Spawners/Random/Salvage/tables_loot.yml @@ -102,6 +102,7 @@ tableId: RandomInstrumentTable - !type:NestedSelector tableId: RingTableCommon + - id: Binoculars - id: CassetteTapesSpawner weight: 0.3 - id: CassettePlayer diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/pets.yml b/Resources/Prototypes/Entities/Mobs/NPCs/pets.yml index 6a94647485b..c60150192ee 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/pets.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/pets.yml @@ -679,6 +679,8 @@ types: Piercing: 8 Poison: 8 + - type: Spider + spawnsWebsAsNonPlayer: false - type: Grammar attributes: proper: true diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/meat.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/meat.yml index 951810cb4fc..74adeaa38bb 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/meat.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/meat.yml @@ -459,6 +459,8 @@ Quantity: 4 - ReagentId: Fat Quantity: 4 + - ReagentId: Romerol # Null Sector + Quantity: 0.01 - type: entity name: raw spider meat diff --git a/Resources/Prototypes/Entities/Objects/Misc/books.yml b/Resources/Prototypes/Entities/Objects/Misc/books.yml index 77e20a15607..8b8f48614ba 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/books.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/books.yml @@ -329,6 +329,26 @@ guides: - Survival +- type: entity # Null Sector + id: BookNullAndYou + parent: BaseGuidebook + name: the null sector and you + description: A small pocket manual of the Null Sector's most critical knowledge, given for free for the aspiring wanderer, trader, miner, scientist, or mercenary. + components: + - type: Sprite + layers: + - state: paper + - state: cover_strong + color: "#FFB439" + - state: decor_wingette + color: "#404040" + - state: icon_glow + color: red + - state: icon_null + - type: GuideHelp + guides: + - NullSectorIntro + - type: entity id: BookChemicalCompendium parent: BaseGuidebook diff --git a/Resources/Prototypes/Entities/Objects/Misc/spider_web.yml b/Resources/Prototypes/Entities/Objects/Misc/spider_web.yml deleted file mode 100644 index 66273c6b6d1..00000000000 --- a/Resources/Prototypes/Entities/Objects/Misc/spider_web.yml +++ /dev/null @@ -1,166 +0,0 @@ -- type: entity - id: SpiderWeb - name: spider web - description: It's stringy and sticky. - placement: - mode: SnapgridCenter - snap: - - Wall - components: - - type: MeleeSound - soundGroups: - Brute: - path: - "/Audio/Weapons/slash.ogg" - - type: Sprite - sprite: Objects/Misc/spiderweb.rsi - layers: - - state: spider_web_1 - map: ["spiderWebLayer"] - drawdepth: WallMountedItems - - type: Appearance - - type: GenericVisualizer - visuals: - enum.SpiderWebVisuals.Variant: - spiderWebLayer: - 1: {state: spider_web_1} - 2: {state: spider_web_2} - - type: Clickable - - type: Transform - anchored: true - - type: Physics - - type: Fixtures - fixtures: - fix1: - hard: false - density: 7 - shape: - !type:PhysShapeAabb - bounds: "-0.5,-0.5,0.5,0.5" - layer: - - MidImpassable - - type: Damageable - damageModifierSet: Wood - - type: Destructible - thresholds: - - trigger: - !type:DamageTrigger - damage: 10 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:SpawnEntitiesBehavior - spawn: - MaterialWebSilk: - min: 0 - max: 1 - - type: Temperature - heatDamage: - types: - Heat: 5 - coldDamage: {} - coldDamageThreshold: 0 - - type: Flammable - fireSpread: true - damage: - types: - Heat: 5 - - type: Reactive - groups: - Flammable: [Touch] - Extinguish: [Touch] - - type: SpiderWebObject - - type: SpeedModifierContacts - walkSpeedModifier: 0.5 - sprintSpeedModifier: 0.5 - ignoreWhitelist: - components: - - IgnoreSpiderWeb - -- type: entity - id: SpiderWebClown - name: clown spider web - description: It's stringy and slippy. - placement: - mode: SnapgridCenter - snap: - - Wall - components: - - type: MeleeSound - soundGroups: - Brute: - path: - "/Audio/Weapons/slash.ogg" - - type: Sprite - sprite: Objects/Misc/spiderweb.rsi - layers: - - state: spider_web_clown_1 - map: ["spiderWebLayer"] - drawdepth: WallMountedItems - - type: Appearance - - type: GenericVisualizer - visuals: - enum.SpiderWebVisuals.Variant: - spiderWebLayer: - 1: {state: spider_web_clown_1} - 2: {state: spider_web_clown_2} - - type: Clickable - - type: Transform - anchored: true - - type: Slippery - - type: StepTrigger - intersectRatio: 0.2 - - type: Physics - - type: Fixtures - fixtures: - slips: - shape: - !type:PhysShapeAabb - bounds: "-0.4,-0.3,0.4,0.3" - hard: false - layer: - - SlipLayer - fix1: - shape: - !type:PhysShapeAabb - bounds: "-0.4,-0.3,0.4,0.3" - density: 1000 - mask: - - ItemMask - - type: Damageable - damageModifierSet: Wood - - type: Destructible - thresholds: - - trigger: - !type:DamageTrigger - damage: 10 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - type: Temperature - heatDamage: - types: - Heat: 5 - coldDamage: {} - coldDamageThreshold: 0 - - type: Flammable - fireSpread: true - damage: - types: - Heat: 5 - - type: Reactive - groups: - Flammable: [Touch] - Extinguish: [Touch] - - type: SpiderWebObject - - type: FlavorProfile - flavors: - - sweet - - type: Food - delay: 2 - - type: SolutionContainerManager - solutions: - food: - reagents: - - ReagentId: Sugar - Quantity: 2 \ No newline at end of file diff --git a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/special.yml b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/special.yml index eff831fa9ea..bdd05bb88ab 100644 --- a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/special.yml +++ b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/special.yml @@ -13,6 +13,24 @@ - type: Sprite sprite: Structures/Piping/Atmospherics/tinyfan.rsi state: icon + - type: InteractionOutline + - type: Destructible # Null Sector - 20251113 + thresholds: + - trigger: + !type:DamageTrigger + damage: 12 + behaviors: + - !type:SpillBehavior { } + - !type:DoActsBehavior + acts: [ "Destruction" ] + - type: Damageable # Null Sector - 20251113 + damageContainer: StructuralInorganic + damageModifierSet: Metallic + - type: Pullable + - type: Construction + graph: FanConstruction + node: tinyfan + - type: Anchorable - type: Fixtures fixtures: fix1: diff --git a/Resources/Prototypes/Entities/Structures/spider_web.yml b/Resources/Prototypes/Entities/Structures/spider_web.yml new file mode 100644 index 00000000000..962b5adbbdb --- /dev/null +++ b/Resources/Prototypes/Entities/Structures/spider_web.yml @@ -0,0 +1,137 @@ +- type: entity + id: SpiderWebBase + abstract: true + placement: + mode: SnapgridCenter + snap: + - Wall + components: + - type: PlacementReplacement + key: web + - type: MeleeSound + soundGroups: + Brute: + path: + "/Audio/Weapons/slash.ogg" + - type: Sprite + sprite: Structures/floor_web.rsi + drawdepth: WallMountedItems + - type: Appearance + - type: Clickable + - type: Transform + anchored: true + - type: Physics + - type: Damageable + damageModifierSet: Wood + - type: Temperature + heatDamage: + types: + Heat: 5 + coldDamage: {} + coldDamageThreshold: 0 + - type: Flammable + fireSpread: true + damage: + types: + Heat: 5 + - type: Reactive + groups: + Flammable: [Touch] + Extinguish: [Touch] + - type: SpiderWebObject + - type: IconSmooth + key: web + additionalKeys: + - walls + base: web_ + +- type: entity + id: SpiderWeb + parent: SpiderWebBase + name: spider web + description: It's stringy and sticky. + components: + - type: Sprite + color: "#ffffffdd" + - type: Fixtures + fixtures: + fix1: + hard: false + density: 7 + shape: + !type:PhysShapeAabb + bounds: "-0.5,-0.5,0.5,0.5" + layer: + - MidImpassable + - type: Destructible + thresholds: + - trigger: # Excess damage, don't spawn entities + !type:DamageTrigger + damage: 50 + behaviors: + - !type:DoActsBehavior + acts: ["Destruction"] + - trigger: + !type:DamageTrigger + damage: 10 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - !type:SpawnEntitiesBehavior + spawn: + MaterialWebSilk: + min: 0 + max: 1 + - type: SpeedModifierContacts + walkSpeedModifier: 0.5 + sprintSpeedModifier: 0.5 + ignoreWhitelist: + components: + - IgnoreSpiderWeb + +- type: entity + id: SpiderWebClown + parent: SpiderWebBase + name: clown spider web + description: It's stringy and slippy. + components: + - type: Sprite + color: "#ffa3fc" + - type: Slippery + - type: StepTrigger + intersectRatio: 0.2 + - type: Fixtures + fixtures: + slips: + shape: + !type:PhysShapeAabb + bounds: "-0.4,-0.4,0.4,0.4" + hard: false + layer: + - SlipLayer + fix1: + shape: + !type:PhysShapeAabb + bounds: "-0.4,-0.4,0.4,0.4" + density: 1000 + mask: + - ItemMask + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 10 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - type: FlavorProfile + flavors: + - sweet + - type: Food + delay: 2 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Sugar + Quantity: 2 diff --git a/Resources/Prototypes/Recipes/Construction/Graphs/clothing/medmeson_hud.yml b/Resources/Prototypes/Recipes/Construction/Graphs/clothing/medmeson_hud.yml new file mode 100644 index 00000000000..d4d851f0eaf --- /dev/null +++ b/Resources/Prototypes/Recipes/Construction/Graphs/clothing/medmeson_hud.yml @@ -0,0 +1,34 @@ +- type: constructionGraph + id: HudMedMeson + start: start + graph: + - node: start + edges: + - to: medmesonHud + steps: + - tag: HudMedical + name: medical hud + icon: + sprite: Clothing/Eyes/Hud/med.rsi + state: icon + doAfter: 5 + - tag: MedMeson + name: meson-engineering goggles + icon: + sprite: Clothing/Eyes/Glasses/meson.rsi + state: icon + doAfter: 5 + - material: Cable + amount: 5 + doAfter: 5 + - tag: Radio + name: handicomms # Frontier handicomms