diff --git a/Content.Client/Bed/SleepingSystem.cs b/Content.Client/Bed/SleepingSystem.cs deleted file mode 100644 index addf855bf3..0000000000 --- a/Content.Client/Bed/SleepingSystem.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Content.Server.Bed.Sleep; - -namespace Content.Client.Bed; - -public sealed class SleepingSystem : SharedSleepingSystem -{ - -} diff --git a/Content.Client/Chat/UI/EmotesMenu.xaml.cs b/Content.Client/Chat/UI/EmotesMenu.xaml.cs index 3340755343..f3b7837f21 100644 --- a/Content.Client/Chat/UI/EmotesMenu.xaml.cs +++ b/Content.Client/Chat/UI/EmotesMenu.xaml.cs @@ -19,9 +19,6 @@ public sealed partial class EmotesMenu : RadialMenu [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly ISharedPlayerManager _playerManager = default!; - private readonly SpriteSystem _spriteSystem; - private readonly EntityWhitelistSystem _whitelistSystem; - public event Action>? OnPlayEmote; public EmotesMenu() @@ -29,8 +26,8 @@ public EmotesMenu() IoCManager.InjectDependencies(this); RobustXamlLoader.Load(this); - _spriteSystem = _entManager.System(); - _whitelistSystem = _entManager.System(); + var spriteSystem = _entManager.System(); + var whitelistSystem = _entManager.System(); var main = FindControl("Main"); @@ -40,8 +37,8 @@ public EmotesMenu() var player = _playerManager.LocalSession?.AttachedEntity; if (emote.Category == EmoteCategory.Invalid || emote.ChatTriggers.Count == 0 || - !(player.HasValue && _whitelistSystem.IsWhitelistPassOrNull(emote.Whitelist, player.Value)) || - _whitelistSystem.IsBlacklistPass(emote.Blacklist, player.Value)) + !(player.HasValue && whitelistSystem.IsWhitelistPassOrNull(emote.Whitelist, player.Value)) || + whitelistSystem.IsBlacklistPass(emote.Blacklist, player.Value)) continue; if (!emote.Available && @@ -63,7 +60,7 @@ public EmotesMenu() { VerticalAlignment = VAlignment.Center, HorizontalAlignment = HAlignment.Center, - Texture = _spriteSystem.Frame0(emote.Icon), + Texture = spriteSystem.Frame0(emote.Icon), TextureScale = new Vector2(2f, 2f), }; diff --git a/Content.Client/Commands/SetMenuVisibilityCommand.cs b/Content.Client/Commands/SetMenuVisibilityCommand.cs index ddfb0b1692..17a544daba 100644 --- a/Content.Client/Commands/SetMenuVisibilityCommand.cs +++ b/Content.Client/Commands/SetMenuVisibilityCommand.cs @@ -1,4 +1,5 @@ using Content.Client.Verbs; +using Content.Shared.Verbs; using JetBrains.Annotations; using Robust.Shared.Console; diff --git a/Content.Client/ContextMenu/UI/EntityMenuUIController.cs b/Content.Client/ContextMenu/UI/EntityMenuUIController.cs index a60619baa3..b6f4dc1be2 100644 --- a/Content.Client/ContextMenu/UI/EntityMenuUIController.cs +++ b/Content.Client/ContextMenu/UI/EntityMenuUIController.cs @@ -9,6 +9,7 @@ using Content.Shared.Examine; using Content.Shared.IdentityManagement; using Content.Shared.Input; +using Content.Shared.Verbs; using Robust.Client.GameObjects; using Robust.Client.Graphics; using Robust.Client.Input; @@ -194,8 +195,20 @@ public override void FrameUpdate(FrameEventArgs args) return; // Do we need to do in-range unOccluded checks? - var ignoreFov = !_eyeManager.CurrentEye.DrawFov || - (_verbSystem.Visibility & MenuVisibility.NoFov) == MenuVisibility.NoFov; + var visibility = _verbSystem.Visibility; + + if (!_eyeManager.CurrentEye.DrawFov) + { + visibility &= ~MenuVisibility.NoFov; + } + + var ev = new MenuVisibilityEvent() + { + Visibility = visibility, + }; + + _entityManager.EventBus.RaiseLocalEvent(player, ref ev); + visibility = ev.Visibility; _entityManager.TryGetComponent(player, out ExaminerComponent? examiner); var xformQuery = _entityManager.GetEntityQuery(); @@ -209,7 +222,7 @@ public override void FrameUpdate(FrameEventArgs args) continue; } - if (ignoreFov) + if ((visibility & MenuVisibility.NoFov) == MenuVisibility.NoFov) continue; var pos = new MapCoordinates(_xform.GetWorldPosition(xform, xformQuery), xform.MapID); diff --git a/Content.Client/Interaction/DragDropSystem.cs b/Content.Client/Interaction/DragDropSystem.cs index 8baa4d15fe..d249766bbc 100644 --- a/Content.Client/Interaction/DragDropSystem.cs +++ b/Content.Client/Interaction/DragDropSystem.cs @@ -495,7 +495,7 @@ private void RemoveHighlights() // CanInteract() doesn't support checking a second "target" entity. // Doing so manually: var ev = new GettingInteractedWithAttemptEvent(user, dragged); - RaiseLocalEvent(dragged, ev, true); + RaiseLocalEvent(dragged, ref ev); if (ev.Cancelled) return false; diff --git a/Content.Client/Overlays/EquipmentHudSystem.cs b/Content.Client/Overlays/EquipmentHudSystem.cs index c7578b6793..3672892ae3 100644 --- a/Content.Client/Overlays/EquipmentHudSystem.cs +++ b/Content.Client/Overlays/EquipmentHudSystem.cs @@ -102,7 +102,7 @@ protected virtual void OnRefreshComponentHud(EntityUid uid, T component, Refresh args.Components.Add(component); } - private void RefreshOverlay(EntityUid uid) + protected void RefreshOverlay(EntityUid uid) { if (uid != _player.LocalSession?.AttachedEntity) return; diff --git a/Content.Client/Overlays/Switchable/BaseSwitchableOverlay.cs b/Content.Client/Overlays/Switchable/BaseSwitchableOverlay.cs new file mode 100644 index 0000000000..5977355968 --- /dev/null +++ b/Content.Client/Overlays/Switchable/BaseSwitchableOverlay.cs @@ -0,0 +1,48 @@ +using System.Numerics; +using Content.Shared.Overlays.Switchable; +using Robust.Client.Graphics; +using Robust.Shared.Enums; +using Robust.Shared.Prototypes; + +namespace Content.Client.Overlays.Switchable; + +public sealed class BaseSwitchableOverlay : Overlay where TComp : SwitchableOverlayComponent +{ + [Dependency] private readonly IPrototypeManager _prototype = default!; + + public override bool RequestScreenTexture => true; + public override OverlaySpace Space => OverlaySpace.WorldSpace; + + private readonly ShaderInstance _shader; + + public TComp? Comp = null; + + public bool IsActive = true; + + public BaseSwitchableOverlay() + { + IoCManager.InjectDependencies(this); + _shader = _prototype.Index("NightVision").InstanceUnique(); + } + + protected override void Draw(in OverlayDrawArgs args) + { + if (ScreenTexture is null || Comp is null || !IsActive) + return; + + _shader.SetParameter("SCREEN_TEXTURE", ScreenTexture); + _shader.SetParameter("tint", Comp.Tint); + _shader.SetParameter("luminance_threshold", Comp.Strength); + _shader.SetParameter("noise_amount", Comp.Noise); + + var worldHandle = args.WorldHandle; + + var accumulator = Math.Clamp(Comp.PulseAccumulator, 0f, Comp.PulseTime); + var alpha = Comp.PulseTime <= 0f ? 1f : float.Lerp(1f, 0f, accumulator / Comp.PulseTime); + + worldHandle.SetTransform(Matrix3x2.Identity); + worldHandle.UseShader(_shader); + worldHandle.DrawRect(args.WorldBounds, Comp.Color.WithAlpha(alpha)); + worldHandle.UseShader(null); + } +} diff --git a/Content.Client/Overlays/Switchable/NightVisionSystem.cs b/Content.Client/Overlays/Switchable/NightVisionSystem.cs new file mode 100644 index 0000000000..c85b3758d8 --- /dev/null +++ b/Content.Client/Overlays/Switchable/NightVisionSystem.cs @@ -0,0 +1,87 @@ +using Content.Shared.Inventory.Events; +using Content.Shared.Overlays.Switchable; +using Robust.Client.Graphics; + +namespace Content.Client.Overlays.Switchable; + +public sealed class NightVisionSystem : EquipmentHudSystem +{ + [Dependency] private readonly IOverlayManager _overlayMan = default!; + [Dependency] private readonly ILightManager _lightManager = default!; + + private BaseSwitchableOverlay _overlay = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnToggle); + + _overlay = new BaseSwitchableOverlay(); + } + + private void OnToggle(Entity ent, ref SwitchableOverlayToggledEvent args) + { + RefreshOverlay(args.User); + } + + protected override void UpdateInternal(RefreshEquipmentHudEvent args) + { + base.UpdateInternal(args); + + var active = false; + NightVisionComponent? nvComp = null; + foreach (var comp in args.Components) + { + if (comp.IsActive || comp.PulseTime > 0f && comp.PulseAccumulator < comp.PulseTime) + active = true; + else + continue; + + if (comp.DrawOverlay) + { + if (nvComp == null) + nvComp = comp; + else if (nvComp.PulseTime > 0f && comp.PulseTime <= 0f) + nvComp = comp; + } + + if (active && nvComp is { PulseTime: <= 0 }) + break; + } + + UpdateNightVision(active); + UpdateOverlay(nvComp); + } + + protected override void DeactivateInternal() + { + base.DeactivateInternal(); + + UpdateNightVision(false); + UpdateOverlay(null); + } + + private void UpdateNightVision(bool active) + { + _lightManager.DrawLighting = !active; + } + + private void UpdateOverlay(NightVisionComponent? nvComp) + { + _overlay.Comp = nvComp; + + switch (nvComp) + { + case not null when !_overlayMan.HasOverlay>(): + _overlayMan.AddOverlay(_overlay); + break; + case null: + _overlayMan.RemoveOverlay(_overlay); + break; + } + + if (_overlayMan.TryGetOverlay>(out var overlay)) + overlay.IsActive = nvComp == null; + } +} diff --git a/Content.Client/Overlays/Switchable/ThermalVisionOverlay.cs b/Content.Client/Overlays/Switchable/ThermalVisionOverlay.cs new file mode 100644 index 0000000000..eb12b33e3a --- /dev/null +++ b/Content.Client/Overlays/Switchable/ThermalVisionOverlay.cs @@ -0,0 +1,159 @@ +using System.Linq; +using System.Numerics; +using Content.Client.Stealth; +using Content.Shared.Body.Components; +using Content.Shared.Overlays.Switchable; +using Content.Shared.Stealth.Components; +using Robust.Client.GameObjects; +using Robust.Client.Graphics; +using Robust.Client.Player; +using Robust.Shared.Enums; +using Robust.Shared.Map; +using Robust.Shared.Timing; + +namespace Content.Client.Overlays.Switchable; + +public sealed class ThermalVisionOverlay : Overlay +{ + [Dependency] private readonly IEntityManager _entity = default!; + [Dependency] private readonly IPlayerManager _player = default!; + [Dependency] private readonly IGameTiming _timing = default!; + + private readonly TransformSystem _transform; + private readonly StealthSystem _stealth; + private readonly ContainerSystem _container; + private readonly SharedPointLightSystem _light; + + public override bool RequestScreenTexture => true; + public override OverlaySpace Space => OverlaySpace.WorldSpace; + + private readonly List _entries = []; + + private EntityUid? _lightEntity; + + public float LightRadius; + + public ThermalVisionComponent? Comp; + + public ThermalVisionOverlay() + { + IoCManager.InjectDependencies(this); + + _container = _entity.System(); + _transform = _entity.System(); + _stealth = _entity.System(); + _light = _entity.System(); + + ZIndex = -1; + } + + protected override void Draw(in OverlayDrawArgs args) + { + if (ScreenTexture is null || Comp is null) + return; + + var worldHandle = args.WorldHandle; + var eye = args.Viewport.Eye; + + if (eye == null) + return; + + var player = _player.LocalEntity; + + if (!_entity.TryGetComponent(player, out TransformComponent? playerXform)) + return; + + var accumulator = Math.Clamp(Comp.PulseAccumulator, 0f, Comp.PulseTime); + var alpha = Comp.PulseTime <= 0f ? 1f : float.Lerp(1f, 0f, accumulator / Comp.PulseTime); + + // Thermal vision grants some night vision (clientside light) + if (LightRadius > 0) + { + _lightEntity ??= _entity.SpawnAttachedTo(null, playerXform.Coordinates); + _transform.SetParent(_lightEntity.Value, player.Value); + var light = _entity.EnsureComponent(_lightEntity.Value); + _light.SetRadius(_lightEntity.Value, LightRadius, light); + _light.SetEnergy(_lightEntity.Value, alpha, light); + _light.SetColor(_lightEntity.Value, Comp.Color, light); + } + else + ResetLight(); + + var mapId = eye.Position.MapId; + var eyeRot = eye.Rotation; + + _entries.Clear(); + var entities = _entity.EntityQueryEnumerator(); + while (entities.MoveNext(out var uid, out var body, out var sprite, out var xform)) + { + if (!CanSee(uid, sprite) || !body.ThermalVisibility) + continue; + + var entity = uid; + + if (_container.TryGetOuterContainer(uid, xform, out var container)) + { + var owner = container.Owner; + if (_entity.TryGetComponent(owner, out var ownerSprite) + && _entity.TryGetComponent(owner, out var ownerXform)) + { + entity = owner; + sprite = ownerSprite; + xform = ownerXform; + } + } + + if (_entries.Any(e => e.Ent.Owner == entity)) + continue; + + _entries.Add(new ThermalVisionRenderEntry((entity, sprite, xform), mapId, eyeRot)); + } + + foreach (var entry in _entries) + { + Render(entry.Ent, entry.Map, worldHandle, entry.EyeRot, Comp.Color, alpha); + } + + worldHandle.SetTransform(Matrix3x2.Identity); + } + + private void Render(Entity ent, + MapId? map, + DrawingHandleWorld handle, + Angle eyeRot, + Color color, + float alpha) + { + var (uid, sprite, xform) = ent; + if (xform.MapID != map || !CanSee(uid, sprite)) + return; + + var position = _transform.GetWorldPosition(xform); + var rotation = _transform.GetWorldRotation(xform); + + var originalColor = sprite.Color; + sprite.Color = color.WithAlpha(alpha); + sprite.Render(handle, eyeRot, rotation, position: position); + sprite.Color = originalColor; + } + + private bool CanSee(EntityUid uid, SpriteComponent sprite) + { + return sprite.Visible && (!_entity.TryGetComponent(uid, out StealthComponent? stealth) || + _stealth.GetVisibility(uid, stealth) > 0.5f); + } + + public void ResetLight() + { + if (_lightEntity == null || !_timing.IsFirstTimePredicted) + return; + + _entity.DeleteEntity(_lightEntity); + _lightEntity = null; + } +} + +public record struct ThermalVisionRenderEntry( + Entity Ent, + MapId? Map, + Angle EyeRot); diff --git a/Content.Client/Overlays/Switchable/ThermalVisionSystem.cs b/Content.Client/Overlays/Switchable/ThermalVisionSystem.cs new file mode 100644 index 0000000000..9b6e5eed0f --- /dev/null +++ b/Content.Client/Overlays/Switchable/ThermalVisionSystem.cs @@ -0,0 +1,95 @@ +using Content.Shared.Inventory.Events; +using Content.Shared.Overlays.Switchable; +using Robust.Client.Graphics; + +namespace Content.Client.Overlays.Switchable; + +public sealed class ThermalVisionSystem : EquipmentHudSystem +{ + [Dependency] private readonly IOverlayManager _overlayMan = default!; + + private ThermalVisionOverlay _thermalOverlay = default!; + private BaseSwitchableOverlay _overlay = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnToggle); + + _thermalOverlay = new ThermalVisionOverlay(); + _overlay = new BaseSwitchableOverlay(); + } + + private void OnToggle(Entity ent, ref SwitchableOverlayToggledEvent args) + { + RefreshOverlay(args.User); + } + + protected override void UpdateInternal(RefreshEquipmentHudEvent args) + { + base.UpdateInternal(args); + ThermalVisionComponent? tvComp = null; + var lightRadius = 0f; + foreach (var comp in args.Components) + { + if (!comp.IsActive && (comp.PulseTime <= 0f || comp.PulseAccumulator >= comp.PulseTime)) + continue; + + if (tvComp == null) + tvComp = comp; + else if (!tvComp.DrawOverlay && comp.DrawOverlay) + tvComp = comp; + else if (tvComp.DrawOverlay == comp.DrawOverlay && tvComp.PulseTime > 0f && comp.PulseTime <= 0f) + tvComp = comp; + + lightRadius = MathF.Max(lightRadius, comp.LightRadius); + } + + UpdateThermalOverlay(tvComp, lightRadius); + UpdateOverlay(tvComp); + } + + protected override void DeactivateInternal() + { + base.DeactivateInternal(); + + UpdateOverlay(null); + UpdateThermalOverlay(null, 0f); + } + + private void UpdateThermalOverlay(ThermalVisionComponent? comp, float lightRadius) + { + _thermalOverlay.LightRadius = lightRadius; + _thermalOverlay.Comp = comp; + + switch (comp) + { + case not null when !_overlayMan.HasOverlay(): + _overlayMan.AddOverlay(_thermalOverlay); + break; + case null: + _overlayMan.RemoveOverlay(_thermalOverlay); + _thermalOverlay.ResetLight(); + break; + } + } + + private void UpdateOverlay(ThermalVisionComponent? tvComp) + { + _overlay.Comp = tvComp; + + switch (tvComp) + { + case { DrawOverlay: true } when !_overlayMan.HasOverlay>(): + _overlayMan.AddOverlay(_overlay); + break; + case null or { DrawOverlay: false }: + _overlayMan.RemoveOverlay(_overlay); + break; + } + + // Night vision overlay is prioritized + _overlay.IsActive = !_overlayMan.HasOverlay>(); + } +} diff --git a/Content.Client/Replay/Spectator/ReplaySpectatorSystem.Blockers.cs b/Content.Client/Replay/Spectator/ReplaySpectatorSystem.Blockers.cs index 2fa862f3df..99d85350b5 100644 --- a/Content.Client/Replay/Spectator/ReplaySpectatorSystem.Blockers.cs +++ b/Content.Client/Replay/Spectator/ReplaySpectatorSystem.Blockers.cs @@ -17,7 +17,7 @@ private void InitializeBlockers() SubscribeLocalEvent(OnAttempt); SubscribeLocalEvent(OnAttempt); SubscribeLocalEvent(OnAttempt); - SubscribeLocalEvent(OnAttempt); + SubscribeLocalEvent(OnInteractAttempt); SubscribeLocalEvent(OnAttempt); SubscribeLocalEvent(OnAttempt); SubscribeLocalEvent(OnAttempt); @@ -27,6 +27,11 @@ private void InitializeBlockers() SubscribeLocalEvent(OnPullAttempt); } + private void OnInteractAttempt(Entity ent, ref InteractionAttemptEvent args) + { + args.Cancelled = true; + } + private void OnAttempt(EntityUid uid, ReplaySpectatorComponent component, CancellableEntityEventArgs args) { args.Cancel(); diff --git a/Content.Client/Silicons/Laws/SiliconLawEditUi/SiliconLawEui.cs b/Content.Client/Silicons/Laws/SiliconLawEditUi/SiliconLawEui.cs index a4d59d1f31..03c74032f7 100644 --- a/Content.Client/Silicons/Laws/SiliconLawEditUi/SiliconLawEui.cs +++ b/Content.Client/Silicons/Laws/SiliconLawEditUi/SiliconLawEui.cs @@ -6,7 +6,7 @@ namespace Content.Client.Silicons.Laws.SiliconLawEditUi; public sealed class SiliconLawEui : BaseEui { - public readonly EntityManager _entityManager = default!; + private readonly EntityManager _entityManager; private SiliconLawUi _siliconLawUi; private EntityUid _target; diff --git a/Content.Client/Silicons/StationAi/StationAiBoundUserInterface.cs b/Content.Client/Silicons/StationAi/StationAiBoundUserInterface.cs new file mode 100644 index 0000000000..68318305a0 --- /dev/null +++ b/Content.Client/Silicons/StationAi/StationAiBoundUserInterface.cs @@ -0,0 +1,28 @@ +using Content.Shared.Silicons.StationAi; +using Robust.Client.UserInterface; + +namespace Content.Client.Silicons.StationAi; + +public sealed class StationAiBoundUserInterface : BoundUserInterface +{ + private StationAiMenu? _menu; + + public StationAiBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) + { + } + + protected override void Open() + { + base.Open(); + _menu = this.CreateWindow(); + _menu.Track(Owner); + + _menu.OnAiRadial += args => + { + SendPredictedMessage(new StationAiRadialMessage() + { + Event = args, + }); + }; + } +} diff --git a/Content.Client/Silicons/StationAi/StationAiMenu.xaml b/Content.Client/Silicons/StationAi/StationAiMenu.xaml new file mode 100644 index 0000000000..d56fc83289 --- /dev/null +++ b/Content.Client/Silicons/StationAi/StationAiMenu.xaml @@ -0,0 +1,13 @@ + + + + + + + diff --git a/Content.Client/Silicons/StationAi/StationAiMenu.xaml.cs b/Content.Client/Silicons/StationAi/StationAiMenu.xaml.cs new file mode 100644 index 0000000000..24a802a60f --- /dev/null +++ b/Content.Client/Silicons/StationAi/StationAiMenu.xaml.cs @@ -0,0 +1,128 @@ +using System.Numerics; +using Content.Client.UserInterface.Controls; +using Content.Shared.Silicons.StationAi; +using Robust.Client.AutoGenerated; +using Robust.Client.GameObjects; +using Robust.Client.Graphics; +using Robust.Client.UserInterface.Controls; +using Robust.Client.UserInterface.XAML; +using Robust.Shared.Timing; + +namespace Content.Client.Silicons.StationAi; + +[GenerateTypedNameReferences] +public sealed partial class StationAiMenu : RadialMenu +{ + [Dependency] private readonly IClyde _clyde = default!; + [Dependency] private readonly IEntityManager _entManager = default!; + [Dependency] private readonly IEyeManager _eyeManager = default!; + + public event Action? OnAiRadial; + + private EntityUid _tracked; + + public StationAiMenu() + { + IoCManager.InjectDependencies(this); + RobustXamlLoader.Load(this); + } + + public void Track(EntityUid owner) + { + _tracked = owner; + + if (!_entManager.EntityExists(_tracked)) + { + Close(); + return; + } + + BuildButtons(); + UpdatePosition(); + } + + private void BuildButtons() + { + var ev = new GetStationAiRadialEvent(); + _entManager.EventBus.RaiseLocalEvent(_tracked, ref ev); + + var main = FindControl("Main"); + main.DisposeAllChildren(); + var sprites = _entManager.System(); + + foreach (var action in ev.Actions) + { + // TODO: This radial boilerplate is quite annoying + var button = new StationAiMenuButton(action.Event) + { + StyleClasses = { "RadialMenuButton" }, + SetSize = new Vector2(64f, 64f), + ToolTip = action.Tooltip != null ? Loc.GetString(action.Tooltip) : null, + }; + + if (action.Sprite != null) + { + var texture = sprites.Frame0(action.Sprite); + var scale = Vector2.One; + + if (texture.Width <= 32) + { + scale *= 2; + } + + var tex = new TextureRect + { + VerticalAlignment = VAlignment.Center, + HorizontalAlignment = HAlignment.Center, + Texture = texture, + TextureScale = scale, + }; + + button.AddChild(tex); + } + + button.OnPressed += args => + { + OnAiRadial?.Invoke(action.Event); + Close(); + }; + main.AddChild(button); + } + } + + protected override void FrameUpdate(FrameEventArgs args) + { + base.FrameUpdate(args); + UpdatePosition(); + } + + private void UpdatePosition() + { + if (!_entManager.TryGetComponent(_tracked, out TransformComponent? xform)) + { + Close(); + return; + } + + if (!xform.Coordinates.IsValid(_entManager)) + { + Close(); + return; + } + + var coords = _entManager.System().GetSpriteScreenCoordinates((_tracked, null, xform)); + + if (!coords.IsValid) + { + Close(); + return; + } + + OpenScreenAt(coords.Position, _clyde); + } +} + +public sealed class StationAiMenuButton(BaseStationAiAction action) : RadialMenuTextureButton +{ + public BaseStationAiAction Action = action; +} diff --git a/Content.Client/Silicons/StationAi/StationAiOverlay.cs b/Content.Client/Silicons/StationAi/StationAiOverlay.cs index efa1b8dbef..15a8a3a63f 100644 --- a/Content.Client/Silicons/StationAi/StationAiOverlay.cs +++ b/Content.Client/Silicons/StationAi/StationAiOverlay.cs @@ -4,7 +4,9 @@ using Robust.Client.Player; using Robust.Shared.Enums; using Robust.Shared.Map.Components; +using Robust.Shared.Physics; using Robust.Shared.Prototypes; +using Robust.Shared.Timing; namespace Content.Client.Silicons.StationAi; @@ -12,6 +14,7 @@ public sealed class StationAiOverlay : Overlay { [Dependency] private readonly IClyde _clyde = default!; [Dependency] private readonly IEntityManager _entManager = default!; + [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly IPlayerManager _player = default!; [Dependency] private readonly IPrototypeManager _proto = default!; @@ -22,6 +25,9 @@ public sealed class StationAiOverlay : Overlay private IRenderTexture? _staticTexture; private IRenderTexture? _stencilTexture; + private float _updateRate = 1f / 30f; + private float _accumulator; + public StationAiOverlay() { IoCManager.InjectDependencies(this); @@ -47,19 +53,22 @@ protected override void Draw(in OverlayDrawArgs args) _entManager.TryGetComponent(playerEnt, out TransformComponent? playerXform); var gridUid = playerXform?.GridUid ?? EntityUid.Invalid; _entManager.TryGetComponent(gridUid, out MapGridComponent? grid); + _entManager.TryGetComponent(gridUid, out BroadphaseComponent? broadphase); var invMatrix = args.Viewport.GetWorldToLocalMatrix(); + _accumulator -= (float) _timing.FrameTime.TotalSeconds; - if (grid != null) + if (grid != null && broadphase != null) { - // TODO: Pass in attached entity's grid. - // TODO: Credit OD on the moved to code - // TODO: Call the moved-to code here. - - _visibleTiles.Clear(); var lookups = _entManager.System(); var xforms = _entManager.System(); - _entManager.System().GetView((gridUid, grid), worldBounds, _visibleTiles); + + if (_accumulator <= 0f) + { + _accumulator = MathF.Max(0f, _accumulator + _updateRate); + _visibleTiles.Clear(); + _entManager.System().GetView((gridUid, broadphase, grid), worldBounds, _visibleTiles); + } var gridMatrix = xforms.GetWorldMatrix(gridUid); var matty = Matrix3x2.Multiply(gridMatrix, invMatrix); diff --git a/Content.Client/Silicons/StationAi/StationAiSystem.Airlock.cs b/Content.Client/Silicons/StationAi/StationAiSystem.Airlock.cs new file mode 100644 index 0000000000..bf6b65a969 --- /dev/null +++ b/Content.Client/Silicons/StationAi/StationAiSystem.Airlock.cs @@ -0,0 +1,30 @@ +using Content.Shared.Doors.Components; +using Content.Shared.Silicons.StationAi; +using Robust.Shared.Utility; + +namespace Content.Client.Silicons.StationAi; + +public sealed partial class StationAiSystem +{ + private void InitializeAirlock() + { + SubscribeLocalEvent(OnDoorBoltGetRadial); + } + + private void OnDoorBoltGetRadial(Entity ent, ref GetStationAiRadialEvent args) + { + args.Actions.Add(new StationAiRadial() + { + Sprite = ent.Comp.BoltsDown ? + new SpriteSpecifier.Rsi( + new ResPath("/Textures/Structures/Doors/Airlocks/Standard/basic.rsi"), "open") : + new SpriteSpecifier.Rsi( + new ResPath("/Textures/Structures/Doors/Airlocks/Standard/basic.rsi"), "closed"), + Tooltip = ent.Comp.BoltsDown ? Loc.GetString("bolt-open") : Loc.GetString("bolt-close"), + Event = new StationAiBoltEvent() + { + Bolted = !ent.Comp.BoltsDown, + } + }); + } +} diff --git a/Content.Client/Silicons/StationAi/StationAiSystem.Light.cs b/Content.Client/Silicons/StationAi/StationAiSystem.Light.cs new file mode 100644 index 0000000000..cf2f613620 --- /dev/null +++ b/Content.Client/Silicons/StationAi/StationAiSystem.Light.cs @@ -0,0 +1,32 @@ +using Content.Shared.Item.ItemToggle.Components; +using Content.Shared.Light.Components; +using Content.Shared.Silicons.StationAi; +using Robust.Shared.Utility; + +namespace Content.Client.Silicons.StationAi; + +public sealed partial class StationAiSystem +{ + // Used for surveillance camera lights + + private void InitializePowerToggle() + { + SubscribeLocalEvent(OnLightGetRadial); + } + + private void OnLightGetRadial(Entity ent, ref GetStationAiRadialEvent args) + { + if (!TryComp(ent.Owner, out ItemToggleComponent? toggle)) + return; + + args.Actions.Add(new StationAiRadial() + { + Tooltip = Loc.GetString("toggle-light"), + Sprite = new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/VerbIcons/light.svg.192dpi.png")), + Event = new StationAiLightEvent() + { + Enabled = !toggle.Activated + } + }); + } +} diff --git a/Content.Client/Silicons/StationAi/StationAiSystem.cs b/Content.Client/Silicons/StationAi/StationAiSystem.cs index 2ed0617525..ab9ace3c1d 100644 --- a/Content.Client/Silicons/StationAi/StationAiSystem.cs +++ b/Content.Client/Silicons/StationAi/StationAiSystem.cs @@ -5,7 +5,7 @@ namespace Content.Client.Silicons.StationAi; -public sealed partial class StationAiSystem : EntitySystem +public sealed partial class StationAiSystem : SharedStationAiSystem { [Dependency] private readonly IOverlayManager _overlayMgr = default!; [Dependency] private readonly IPlayerManager _player = default!; @@ -15,8 +15,8 @@ public sealed partial class StationAiSystem : EntitySystem public override void Initialize() { base.Initialize(); - // InitializeAirlock(); - // InitializePowerToggle(); + InitializeAirlock(); + InitializePowerToggle(); SubscribeLocalEvent(OnAiAttached); SubscribeLocalEvent(OnAiDetached); diff --git a/Content.Client/Verbs/VerbSystem.cs b/Content.Client/Verbs/VerbSystem.cs index 5f1f49e5fd..e28f48d6a5 100644 --- a/Content.Client/Verbs/VerbSystem.cs +++ b/Content.Client/Verbs/VerbSystem.cs @@ -67,9 +67,18 @@ public bool TryGetEntityMenuEntities(MapCoordinates targetPos, [NotNullWhen(true ? Visibility : Visibility | MenuVisibility.NoFov; + var ev = new MenuVisibilityEvent() + { + TargetPos = targetPos, + Visibility = visibility, + }; + + RaiseLocalEvent(player.Value, ref ev); + visibility = ev.Visibility; // Get entities List entities; + var examineFlags = LookupFlags.All & ~LookupFlags.Sensors; // Do we have to do FoV checks? if ((visibility & MenuVisibility.NoFov) == 0) @@ -77,15 +86,10 @@ public bool TryGetEntityMenuEntities(MapCoordinates targetPos, [NotNullWhen(true var entitiesUnderMouse = gameScreenBase.GetClickableEntities(targetPos).ToHashSet(); bool Predicate(EntityUid e) => e == player || entitiesUnderMouse.Contains(e); - // first check the general location. - if (!_examine.CanExamine(player.Value, targetPos, Predicate)) - return false; - TryComp(player.Value, out ExaminerComponent? examiner); - // Then check every entity entities = new(); - foreach (var ent in _entityLookup.GetEntitiesInRange(targetPos, EntityMenuLookupSize)) + foreach (var ent in _entityLookup.GetEntitiesInRange(targetPos, EntityMenuLookupSize, flags: examineFlags)) { if (_examine.CanExamine(player.Value, targetPos, Predicate, ent, examiner)) entities.Add(ent); @@ -93,7 +97,7 @@ public bool TryGetEntityMenuEntities(MapCoordinates targetPos, [NotNullWhen(true } else { - entities = _entityLookup.GetEntitiesInRange(targetPos, EntityMenuLookupSize).ToList(); + entities = _entityLookup.GetEntitiesInRange(targetPos, EntityMenuLookupSize, flags: examineFlags).ToList(); } if (entities.Count == 0) @@ -137,27 +141,6 @@ public bool TryGetEntityMenuEntities(MapCoordinates targetPos, [NotNullWhen(true } } - // Remove any entities that do not have LOS - if ((visibility & MenuVisibility.NoFov) == 0) - { - var xformQuery = GetEntityQuery(); - var playerPos = _transform.GetMapCoordinates(player.Value, xform: xformQuery.GetComponent(player.Value)); - - for (var i = entities.Count - 1; i >= 0; i--) - { - var entity = entities[i]; - - if (!_examine.InRangeUnOccluded( - playerPos, - _transform.GetMapCoordinates(entity, xform: xformQuery.GetComponent(entity)), - ExamineSystemShared.ExamineRange, - null)) - { - entities.RemoveSwap(i); - } - } - } - if (entities.Count == 0) return false; @@ -229,15 +212,4 @@ private void HandleVerbResponse(VerbsResponseEvent msg) OnVerbsResponse?.Invoke(msg); } } - - [Flags] - public enum MenuVisibility - { - // What entities can a user see on the entity menu? - Default = 0, // They can only see entities in FoV. - NoFov = 1 << 0, // They ignore FoV restrictions - InContainer = 1 << 1, // They can see through containers. - Invisible = 1 << 2, // They can see entities without sprites and the "HideContextMenu" tag is ignored. - All = NoFov | InContainer | Invisible - } } diff --git a/Content.Server/Abilities/Psionics/Abilities/AssayPowerSystem.cs b/Content.Server/Abilities/Psionics/Abilities/AssayPowerSystem.cs new file mode 100644 index 0000000000..4fe08f303c --- /dev/null +++ b/Content.Server/Abilities/Psionics/Abilities/AssayPowerSystem.cs @@ -0,0 +1,140 @@ +using Content.Server.Chat.Managers; +using Content.Shared.Abilities.Psionics; +using Content.Shared.Actions.Events; +using Content.Shared.Chat; +using Content.Shared.DoAfter; +using Content.Shared.Popups; +using Content.Shared.Psionics.Events; +using Robust.Server.Audio; +using Robust.Shared.Audio; +using Robust.Server.Player; +using Robust.Shared.Timing; +using Robust.Shared.Player; + +namespace Content.Server.Abilities.Psionics; + +public sealed class AssayPowerSystem : EntitySystem +{ + [Dependency] private readonly SharedPsionicAbilitiesSystem _psionics = default!; + [Dependency] private readonly IGameTiming _gameTiming = default!; + [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; + [Dependency] private readonly AudioSystem _audioSystem = default!; + [Dependency] private readonly SharedPopupSystem _popups = default!; + [Dependency] private readonly IChatManager _chatManager = default!; + [Dependency] private readonly IPlayerManager _playerManager = default!; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnPowerUsed); + SubscribeLocalEvent(OnDoAfter); + } + + /// + /// This power activates when scanning any entity, displaying to the player's chat window a variety of psionic related statistics about the target. + /// + private void OnPowerUsed(EntityUid uid, PsionicComponent psionic, AssayPowerActionEvent args) + { + if (!_psionics.OnAttemptPowerUse(args.Performer, "assay") + || psionic.DoAfter is not null) + return; + + var ev = new AssayDoAfterEvent(_gameTiming.CurTime, args.FontSize, args.FontColor); + _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, args.Performer, args.UseDelay - TimeSpan.FromSeconds(psionic.CurrentAmplification), ev, args.Performer, args.Target, args.Performer) + { + BlockDuplicate = true, + BreakOnMove = true, + BreakOnDamage = true, + }, out var doAfterId); + psionic.DoAfter = doAfterId; + + _popups.PopupEntity(Loc.GetString(args.PopupTarget, ("entity", args.Target)), args.Performer, PopupType.Medium); + + _audioSystem.PlayPvs(args.SoundUse, args.Performer, AudioParams.Default.WithVolume(8f).WithMaxDistance(1.5f).WithRolloffFactor(3.5f)); + _psionics.LogPowerUsed(args.Performer, args.PowerName, args.MinGlimmer, args.MaxGlimmer); + args.Handled = true; + } + + /// + /// Assuming the DoAfter wasn't canceled, the user wasn't mindbroken, and the target still exists, prepare the scan results! + /// + private void OnDoAfter(EntityUid uid, PsionicComponent component, AssayDoAfterEvent args) + { + if (component is null) + return; + component.DoAfter = null; + + var user = uid; + var target = args.Target; + if (target == null || args.Cancelled + || !_playerManager.TryGetSessionByEntity(user, out var session)) + return; + + if (InspectSelf(uid, args, session)) + return; + + if (!TryComp(target, out var targetPsionic)) + { + var noPowers = Loc.GetString("no-powers", ("entity", target)); + _popups.PopupEntity(noPowers, user, user, PopupType.LargeCaution); + + // Incredibly spooky message for non-psychic targets. + var noPowersFeedback = $"[font size={args.FontSize}][color={args.FontColor}]{noPowers}[/color][/font]"; + SendDescToChat(noPowersFeedback, session); + return; + } + + InspectOther(targetPsionic, args, session); + } + + /// + /// This is a special use-case for scanning yourself with the power. The player receives a unique feedback message if they do so. + /// It however displays significantly less information when doing so. Consider this an intriguing easter egg. + /// + private bool InspectSelf(EntityUid uid, AssayDoAfterEvent args, ICommonSession session) + { + if (args.Target != args.User) + return false; + + var user = uid; + var target = args.Target; + + var assaySelf = Loc.GetString("assay-self", ("entity", target!.Value)); + _popups.PopupEntity(assaySelf, user, user, PopupType.LargeCaution); + + var assaySelfFeedback = $"[font size=20][color=#ff0000]{assaySelf}[/color][/font]"; + SendDescToChat(assaySelfFeedback, session); + return true; + } + + /// + /// If the target turns out to be a psychic, display their feedback messages in chat. + /// + private void InspectOther(PsionicComponent targetPsionic, AssayDoAfterEvent args, ICommonSession session) + { + var target = args.Target; + var targetAmp = MathF.Round(targetPsionic.CurrentAmplification, 2).ToString("#.##"); + var targetDamp = MathF.Round(targetPsionic.CurrentDampening, 2).ToString("#.##"); + var targetPotentia = MathF.Round(targetPsionic.Potentia, 2).ToString("#.##"); + var message = $"[font size={args.FontSize}][color={args.FontColor}]{Loc.GetString("assay-body", ("entity", target!.Value), ("amplification", targetAmp), ("dampening", targetDamp), ("potentia", targetPotentia))}[/color][/font]"; + SendDescToChat(message, session); + + foreach (var feedback in targetPsionic.AssayFeedback) + { + var locale = Loc.GetString(feedback, ("entity", target!.Value)); + var feedbackMessage = $"[font size={args.FontSize}][color={args.FontColor}]{locale}[/color][/font]"; + SendDescToChat(feedbackMessage, session); + } + } + + private void SendDescToChat(string feedbackMessage, ICommonSession session) + { + _chatManager.ChatMessageToOne( + ChatChannel.Emotes, + feedbackMessage, + feedbackMessage, + EntityUid.Invalid, + false, + session.Channel); + } +} diff --git a/Content.Server/Administration/Commands/SetStationAiName.cs b/Content.Server/Administration/Commands/SetStationAiName.cs new file mode 100644 index 0000000000..0c4236f430 --- /dev/null +++ b/Content.Server/Administration/Commands/SetStationAiName.cs @@ -0,0 +1,57 @@ +using Content.Server.Station.Systems; +using Content.Shared.Administration; +using Content.Shared.Roles; +using Robust.Shared.Console; +using Robust.Shared.Prototypes; + + +namespace Content.Server.Administration.Commands; + + +[AdminCommand(AdminFlags.Admin)] +public sealed class SetStationAiNameCommand : IConsoleCommand +{ + [Dependency] private readonly IEntityManager _entManager = default!; + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + + private readonly ProtoId _stationAiJob = "StationAi"; + + public string Command => "setstationainame"; + public string Description => Loc.GetString("set-station-ai-name-command-description"); + public string Help => Loc.GetString("set-station-ai-name-command-help-text", ("command", Command)); + + public void Execute(IConsoleShell shell, string argStr, string[] args) + { + if (args.Length != 1) + { + shell.WriteError(Loc.GetString("shell-wrong-arguments-number")); + return; + } + + if (!int.TryParse(args[0], out var entInt)) + { + shell.WriteLine(Loc.GetString("shell-entity-uid-must-be-number")); + return; + } + + var netEntity = new NetEntity(entInt); + + if (!_entManager.TryGetEntity(netEntity, out var target)) + { + shell.WriteLine(Loc.GetString("shell-invalid-entity-id")); + return; + } + + var hasStationAi = _prototypeManager.TryIndex(_stationAiJob, out var job); + + if (!hasStationAi) + { + shell.WriteLine(Loc.GetString("set-station-ai-name-command-no-station-ai")); + return; + } + + var spawningSystem = _entManager.System(); + spawningSystem.EquipJobName(target.Value, job!); + shell.WriteLine(Loc.GetString("shell-command-success")); + } +} diff --git a/Content.Server/Administration/Systems/AdminVerbSystem.cs b/Content.Server/Administration/Systems/AdminVerbSystem.cs index 1e701794ff..d9ccb730d0 100644 --- a/Content.Server/Administration/Systems/AdminVerbSystem.cs +++ b/Content.Server/Administration/Systems/AdminVerbSystem.cs @@ -36,6 +36,7 @@ using System.Linq; using System.Numerics; using Content.Server.Silicons.Laws; +using Content.Shared.Silicons.Laws; using Content.Shared.Silicons.Laws.Components; using Robust.Server.Player; using Robust.Shared.Physics.Components; diff --git a/Content.Server/Bed/BedSystem.cs b/Content.Server/Bed/BedSystem.cs index 2335859f0b..7220c04ea8 100644 --- a/Content.Server/Bed/BedSystem.cs +++ b/Content.Server/Bed/BedSystem.cs @@ -1,6 +1,5 @@ using Content.Server.Actions; using Content.Server.Bed.Components; -using Content.Server.Bed.Sleep; using Content.Server.Body.Systems; using Content.Server.Construction; using Content.Server.Power.Components; diff --git a/Content.Server/Bed/Sleep/SleepingSystem.cs b/Content.Server/Bed/Sleep/SleepingSystem.cs deleted file mode 100644 index 47966c4814..0000000000 --- a/Content.Server/Bed/Sleep/SleepingSystem.cs +++ /dev/null @@ -1,261 +0,0 @@ -using Content.Server.Popups; -using Content.Server.Sound; -using Content.Shared.Sound.Components; -using Content.Shared.Actions; -using Content.Shared.Audio; -using Content.Shared.Bed.Sleep; -using Content.Shared.Damage; -using Content.Shared.Examine; -using Content.Shared.IdentityManagement; -using Content.Shared.Interaction; -using Content.Shared.Interaction.Events; -using Content.Shared.Mobs; -using Content.Shared.Mobs.Components; -using Content.Shared.Slippery; -using Content.Shared.StatusEffect; -using Content.Shared.Stunnable; -using Content.Shared.Verbs; -using Robust.Shared.Audio; -using Robust.Shared.Audio.Systems; -using Robust.Shared.Player; -using Robust.Shared.Prototypes; -using Robust.Shared.Random; -using Robust.Shared.Timing; - -namespace Content.Server.Bed.Sleep -{ - public sealed class SleepingSystem : SharedSleepingSystem - { - [Dependency] private readonly IGameTiming _gameTiming = default!; - [Dependency] private readonly IRobustRandom _robustRandom = default!; - [Dependency] private readonly PopupSystem _popupSystem = default!; - [Dependency] private readonly SharedAudioSystem _audio = default!; - [Dependency] private readonly StatusEffectsSystem _statusEffectsSystem = default!; - [Dependency] private readonly EmitSoundSystem _emitSound = default!; - - [ValidatePrototypeId] public const string SleepActionId = "ActionSleep"; - - public override void Initialize() - { - base.Initialize(); - SubscribeLocalEvent(OnSleepStateChanged); - SubscribeLocalEvent(OnDamageChanged); - SubscribeLocalEvent(OnSleepAction); - SubscribeLocalEvent(OnBedSleepAction); - SubscribeLocalEvent(OnWakeAction); - SubscribeLocalEvent(OnMobStateChanged); - SubscribeLocalEvent>(AddWakeVerb); - SubscribeLocalEvent(OnInteractHand); - SubscribeLocalEvent(OnExamined); - SubscribeLocalEvent(OnSlip); - SubscribeLocalEvent(OnConsciousAttempt); - SubscribeLocalEvent(OnInit); - } - - /// - /// when sleeping component is added or removed, we do some stuff with other components. - /// - private void OnSleepStateChanged(EntityUid uid, MobStateComponent component, SleepStateChangedEvent args) - { - if (args.FellAsleep) - { - // Expiring status effects would remove the components needed for sleeping - _statusEffectsSystem.TryRemoveStatusEffect(uid, "Stun"); - _statusEffectsSystem.TryRemoveStatusEffect(uid, "KnockedDown"); - - EnsureComp(uid); - EnsureComp(uid); - - if (TryComp(uid, out var sleepSound)) - { - var emitSound = EnsureComp(uid); - if (HasComp(uid)) - { - emitSound.Sound = sleepSound.Snore; - } - emitSound.MinInterval = sleepSound.Interval; - emitSound.MaxInterval = sleepSound.MaxInterval; - emitSound.PopUp = sleepSound.PopUp; - } - - return; - } - - RemComp(uid); - RemComp(uid); - RemComp(uid); - } - - /// - /// Wake up on taking an instance of damage at least the value of WakeThreshold. - /// - private void OnDamageChanged(EntityUid uid, SleepingComponent component, DamageChangedEvent args) - { - if (!args.DamageIncreased || args.DamageDelta == null) - return; - - /* Shitmed Change Start - Surgery needs this, sorry! If the nocturine gamers get too feisty - I'll probably just increase the threshold */ - - if (args.DamageDelta.GetTotal() >= component.WakeThreshold - && !HasComp(uid)) - TryWaking(uid, component); - - // Shitmed Change End - - } - - private void OnSleepAction(EntityUid uid, MobStateComponent component, SleepActionEvent args) - { - TrySleeping(uid); - } - - private void OnBedSleepAction(EntityUid uid, ActionsContainerComponent component, SleepActionEvent args) - { - TrySleeping(args.Performer); - } - - private void OnWakeAction(EntityUid uid, MobStateComponent component, WakeActionEvent args) - { - if (!TryWakeCooldown(uid)) - return; - - if (TryWaking(uid)) - args.Handled = true; - } - - /// - /// In crit, we wake up if we are not being forced to sleep. - /// And, you can't sleep when dead... - /// - private void OnMobStateChanged(EntityUid uid, SleepingComponent component, MobStateChangedEvent args) - { - if (args.NewMobState == MobState.Dead) - { - RemComp(uid); - RemComp(uid); - return; - } - if (TryComp(uid, out var spam)) - _emitSound.SetEnabled((uid, spam), args.NewMobState == MobState.Alive); - } - - private void AddWakeVerb(EntityUid uid, SleepingComponent component, GetVerbsEvent args) - { - if (!args.CanInteract || !args.CanAccess) - return; - - AlternativeVerb verb = new() - { - Act = () => - { - if (!TryWakeCooldown(uid)) - return; - - TryWaking(args.Target, user: args.User); - }, - Text = Loc.GetString("action-name-wake"), - Priority = 2 - }; - - args.Verbs.Add(verb); - } - - /// - /// When you click on a sleeping person with an empty hand, try to wake them. - /// - private void OnInteractHand(EntityUid uid, SleepingComponent component, InteractHandEvent args) - { - args.Handled = true; - - if (!TryWakeCooldown(uid)) - return; - - TryWaking(args.Target, user: args.User); - } - - private void OnExamined(EntityUid uid, SleepingComponent component, ExaminedEvent args) - { - if (args.IsInDetailsRange) - { - args.PushMarkup(Loc.GetString("sleep-examined", ("target", Identity.Entity(uid, EntityManager)))); - } - } - - private void OnSlip(EntityUid uid, SleepingComponent component, SlipAttemptEvent args) - { - args.Cancel(); - } - - private void OnConsciousAttempt(EntityUid uid, SleepingComponent component, ConsciousAttemptEvent args) - { - args.Cancel(); - } - - - private void OnInit(EntityUid uid, ForcedSleepingComponent component, ComponentInit args) - { - TrySleeping(uid); - } - - /// - /// Try sleeping. Only mobs can sleep. - /// - public bool TrySleeping(EntityUid uid) - { - if (!HasComp(uid)) - return false; - - var tryingToSleepEvent = new TryingToSleepEvent(uid); - RaiseLocalEvent(uid, ref tryingToSleepEvent); - if (tryingToSleepEvent.Cancelled) - return false; - - EnsureComp(uid); - return true; - } - - private bool TryWakeCooldown(EntityUid uid, SleepingComponent? component = null) - { - if (!Resolve(uid, ref component, false)) - return false; - - var curTime = _gameTiming.CurTime; - - if (curTime < component.CoolDownEnd) - { - return false; - } - - component.CoolDownEnd = curTime + component.Cooldown; - return true; - } - - /// - /// Try to wake up. - /// - public bool TryWaking(EntityUid uid, SleepingComponent? component = null, bool force = false, EntityUid? user = null) - { - if (!Resolve(uid, ref component, false)) - return false; - - if (!force && HasComp(uid)) - { - if (user != null) - { - _audio.PlayPvs("/Audio/Effects/thudswoosh.ogg", uid, AudioHelpers.WithVariation(0.05f, _robustRandom)); - _popupSystem.PopupEntity(Loc.GetString("wake-other-failure", ("target", Identity.Entity(uid, EntityManager))), uid, Filter.Entities(user.Value), true, Shared.Popups.PopupType.SmallCaution); - } - return false; - } - - if (user != null) - { - _audio.PlayPvs("/Audio/Effects/thudswoosh.ogg", uid, AudioHelpers.WithVariation(0.05f, _robustRandom)); - _popupSystem.PopupEntity(Loc.GetString("wake-other-success", ("target", Identity.Entity(uid, EntityManager))), uid, Filter.Entities(user.Value), true); - } - RemComp(uid); - return true; - } - } -} diff --git a/Content.Server/Carrying/CarryingSystem.cs b/Content.Server/Carrying/CarryingSystem.cs index d206e35d2b..8a4773af2d 100644 --- a/Content.Server/Carrying/CarryingSystem.cs +++ b/Content.Server/Carrying/CarryingSystem.cs @@ -174,7 +174,7 @@ private void OnInteractionAttempt(EntityUid uid, BeingCarriedComponent component // Also check if the interacted-with entity is on the carrier and cancel the event if not var targetParent = Transform(args.Target.Value).ParentUid; if (args.Target.Value != component.Carrier && targetParent != component.Carrier && targetParent != uid) - args.Cancel(); + args.Cancelled = true; } /// diff --git a/Content.Server/Clothing/Systems/LoadoutSystem.cs b/Content.Server/Clothing/Systems/LoadoutSystem.cs index 4c357c5864..537ca09785 100644 --- a/Content.Server/Clothing/Systems/LoadoutSystem.cs +++ b/Content.Server/Clothing/Systems/LoadoutSystem.cs @@ -2,6 +2,7 @@ using Content.Server.GameTicking; using Content.Server.Paint; using Content.Server.Players.PlayTimeTracking; +using Content.Server.Station.Systems; using Content.Shared.CCVar; using Content.Shared.Clothing.Loadouts.Prototypes; using Content.Shared.Clothing.Loadouts.Systems; @@ -33,6 +34,7 @@ public sealed class LoadoutSystem : EntitySystem [Dependency] private readonly ISerializationManager _serialization = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IComponentFactory _componentFactory = default!; + [Dependency] private readonly StationSpawningSystem _stationSpawning = default!; public override void Initialize() @@ -43,10 +45,13 @@ public override void Initialize() private void OnPlayerSpawnComplete(PlayerSpawnCompleteEvent ev) { - if (ev.JobId == null || - !_configurationManager.GetCVar(CCVars.GameLoadoutsEnabled)) + if (ev.JobId == null + || !_protoMan.TryIndex(ev.JobId, out var job) + || !_configurationManager.GetCVar(CCVars.GameLoadoutsEnabled)) return; + _stationSpawning.EquipJobName(ev.Mob, job); + ApplyCharacterLoadout( ev.Mob, ev.JobId, @@ -108,7 +113,6 @@ public void ApplyCharacterLoadout( function.OnPlayerSpawn(uid, loadout.Item1, _componentFactory, EntityManager, _serialization); } - // Pick the heirloom if (heirlooms.Any()) { diff --git a/Content.Server/Communications/CommunicationsConsoleSystem.cs b/Content.Server/Communications/CommunicationsConsoleSystem.cs index c3e5b91254..de77d611c3 100644 --- a/Content.Server/Communications/CommunicationsConsoleSystem.cs +++ b/Content.Server/Communications/CommunicationsConsoleSystem.cs @@ -182,10 +182,6 @@ private static bool CanAnnounce(CommunicationsConsoleComponent comp) private bool CanUse(EntityUid user, EntityUid console) { - // This shouldn't technically be possible because of BUI but don't trust client. - if (!_interaction.InRangeUnobstructed(console, user)) - return false; - if (TryComp(console, out var accessReaderComponent) && !HasComp(console)) { return _accessReaderSystem.IsAllowed(user, console, accessReaderComponent); diff --git a/Content.Server/Damage/ForceSay/DamageForceSaySystem.cs b/Content.Server/Damage/ForceSay/DamageForceSaySystem.cs index 186fc91c46..bc61c5d141 100644 --- a/Content.Server/Damage/ForceSay/DamageForceSaySystem.cs +++ b/Content.Server/Damage/ForceSay/DamageForceSaySystem.cs @@ -1,3 +1,4 @@ +using Content.Shared.Bed.Sleep; using Content.Shared.Damage; using Content.Shared.Damage.ForceSay; using Content.Shared.FixedPoint; diff --git a/Content.Server/DeviceNetwork/Systems/DeviceNetworkRequiresPowerSystem.cs b/Content.Server/DeviceNetwork/Systems/DeviceNetworkRequiresPowerSystem.cs index f47a5df8ac..6e7bd255c5 100644 --- a/Content.Server/DeviceNetwork/Systems/DeviceNetworkRequiresPowerSystem.cs +++ b/Content.Server/DeviceNetwork/Systems/DeviceNetworkRequiresPowerSystem.cs @@ -1,7 +1,6 @@ using Content.Server.DeviceNetwork.Components; using Content.Server.Power.Components; using Content.Server.Power.EntitySystems; -using Content.Shared.Power.EntitySystems; namespace Content.Server.DeviceNetwork.Systems; diff --git a/Content.Server/Flight/FlightSystem.cs b/Content.Server/Flight/FlightSystem.cs index 4493967fe9..5e3ef354f2 100644 --- a/Content.Server/Flight/FlightSystem.cs +++ b/Content.Server/Flight/FlightSystem.cs @@ -1,4 +1,5 @@ +using Content.Shared.Bed.Sleep; using Content.Shared.Cuffs.Components; using Content.Shared.Damage.Components; using Content.Shared.DoAfter; diff --git a/Content.Server/InteractionVerbs/Actions/ToggleSleepingAction.cs b/Content.Server/InteractionVerbs/Actions/ToggleSleepingAction.cs index 588853a8d4..97fd86fd91 100644 --- a/Content.Server/InteractionVerbs/Actions/ToggleSleepingAction.cs +++ b/Content.Server/InteractionVerbs/Actions/ToggleSleepingAction.cs @@ -1,4 +1,3 @@ -using Content.Server.Bed.Sleep; using Content.Shared.Bed.Sleep; using Content.Shared.InteractionVerbs; using Content.Shared.Mobs.Components; diff --git a/Content.Server/Light/EntitySystems/LitOnPoweredSystem.cs b/Content.Server/Light/EntitySystems/LitOnPoweredSystem.cs index 3c5f7eaecb..5c66d65b57 100644 --- a/Content.Server/Light/EntitySystems/LitOnPoweredSystem.cs +++ b/Content.Server/Light/EntitySystems/LitOnPoweredSystem.cs @@ -2,7 +2,6 @@ using Content.Server.Power.Components; using Content.Server.Power.EntitySystems; using Content.Shared.Power; -using Content.Shared.Power.Components; namespace Content.Server.Light.EntitySystems { diff --git a/Content.Server/Light/EntitySystems/PoweredLightSystem.cs b/Content.Server/Light/EntitySystems/PoweredLightSystem.cs index c4a07b56a8..58ffd4f7b9 100644 --- a/Content.Server/Light/EntitySystems/PoweredLightSystem.cs +++ b/Content.Server/Light/EntitySystems/PoweredLightSystem.cs @@ -27,7 +27,6 @@ using Content.Shared.Damage.Systems; using Content.Shared.Damage.Components; using Content.Shared.Power; -using Content.Shared.Power.Components; namespace Content.Server.Light.EntitySystems { diff --git a/Content.Server/Mind/MindSystem.cs b/Content.Server/Mind/MindSystem.cs index 4271d76b44..2e7c31ec7a 100644 --- a/Content.Server/Mind/MindSystem.cs +++ b/Content.Server/Mind/MindSystem.cs @@ -341,13 +341,13 @@ public override void SetUserId(EntityUid mindId, NetUserId? userId, MindComponen } } - public void ControlMob(EntityUid user, EntityUid target) + public override void ControlMob(EntityUid user, EntityUid target) { if (TryComp(user, out ActorComponent? actor)) ControlMob(actor.PlayerSession.UserId, target); } - public void ControlMob(NetUserId user, EntityUid target) + public override void ControlMob(NetUserId user, EntityUid target) { var (mindId, mind) = GetOrCreateMind(user); diff --git a/Content.Server/Power/EntitySystems/ChargerSystem.cs b/Content.Server/Power/EntitySystems/ChargerSystem.cs index 2aa69024df..40b998a95d 100644 --- a/Content.Server/Power/EntitySystems/ChargerSystem.cs +++ b/Content.Server/Power/EntitySystems/ChargerSystem.cs @@ -8,7 +8,6 @@ using JetBrains.Annotations; using Robust.Shared.Containers; using System.Diagnostics.CodeAnalysis; -using Content.Shared.Power.Components; using Content.Shared.Storage.Components; using Robust.Server.Containers; using Content.Shared.Whitelist; @@ -44,7 +43,37 @@ private void OnStartup(EntityUid uid, ChargerComponent component, ComponentStart private void OnChargerExamine(EntityUid uid, ChargerComponent component, ExaminedEvent args) { - args.PushMarkup(Loc.GetString("charger-examine", ("color", "yellow"), ("chargeRate", (int) component.ChargeRate))); + using (args.PushGroup(nameof(ChargerComponent))) + { + // rate at which the charger charges + args.PushMarkup(Loc.GetString("charger-examine", ("color", "yellow"), ("chargeRate", (int) component.ChargeRate))); + + // try to get contents of the charger + if (!_container.TryGetContainer(uid, component.SlotId, out var container)) + return; + + if (HasComp(uid)) + return; + + // if charger is empty and not a power cell type charger, add empty message + // power cells have their own empty message by default, for things like flash lights + if (container.ContainedEntities.Count == 0) + { + args.PushMarkup(Loc.GetString("charger-empty")); + } + else + { + // add how much each item is charged it + foreach (var contained in container.ContainedEntities) + { + if (!TryComp(contained, out var battery)) + continue; + + var chargePercentage = (battery.CurrentCharge / battery.MaxCharge) * 100; + args.PushMarkup(Loc.GetString("charger-content", ("chargePercentage", (int) chargePercentage))); + } + } + } } public override void Update(float frameTime) @@ -202,7 +231,7 @@ private CellChargerStatus GetStatus(EntityUid uid, ChargerComponent component) return CellChargerStatus.Charging; } - + private void TransferPower(EntityUid uid, EntityUid targetEntity, ChargerComponent component, float frameTime) { if (!TryComp(uid, out ApcPowerReceiverComponent? receiverComponent)) diff --git a/Content.Server/Power/EntitySystems/PowerNetSystem.cs b/Content.Server/Power/EntitySystems/PowerNetSystem.cs index 8dcb6240a5..6c35ba2008 100644 --- a/Content.Server/Power/EntitySystems/PowerNetSystem.cs +++ b/Content.Server/Power/EntitySystems/PowerNetSystem.cs @@ -5,7 +5,6 @@ using Content.Server.Power.Pow3r; using Content.Shared.CCVar; using Content.Shared.Power; -using Content.Shared.Power.Components; using JetBrains.Annotations; using Robust.Server.GameObjects; using Robust.Shared.Configuration; diff --git a/Content.Server/Power/Generation/Teg/TegSystem.cs b/Content.Server/Power/Generation/Teg/TegSystem.cs index dd09467efe..43749dab8f 100644 --- a/Content.Server/Power/Generation/Teg/TegSystem.cs +++ b/Content.Server/Power/Generation/Teg/TegSystem.cs @@ -11,7 +11,6 @@ using Content.Shared.DeviceNetwork; using Content.Shared.Examine; using Content.Shared.Power; -using Content.Shared.Power.Components; using Content.Shared.Power.Generation.Teg; using Content.Shared.Rounding; using Robust.Server.GameObjects; diff --git a/Content.Server/Power/Generator/GasPowerReceiverSystem.cs b/Content.Server/Power/Generator/GasPowerReceiverSystem.cs index 5a1bd31a15..e3979a6519 100644 --- a/Content.Server/Power/Generator/GasPowerReceiverSystem.cs +++ b/Content.Server/Power/Generator/GasPowerReceiverSystem.cs @@ -6,7 +6,6 @@ using Content.Server.Power.Components; using Content.Shared.Atmos; using Content.Shared.Power; -using Content.Shared.Power.Components; namespace Content.Server.Power.Generator; diff --git a/Content.Server/Shadowkin/ShowEtherealSystem.cs b/Content.Server/Shadowkin/ShowEtherealSystem.cs index 151c379afb..e908bd9ce7 100644 --- a/Content.Server/Shadowkin/ShowEtherealSystem.cs +++ b/Content.Server/Shadowkin/ShowEtherealSystem.cs @@ -70,7 +70,7 @@ private void OnInteractionAttempt(EntityUid uid, ShowEtherealComponent component || !HasComp(args.Target)) return; - args.Cancel(); + args.Cancelled = true; if (_gameTiming.InPrediction) return; @@ -85,4 +85,4 @@ private void OnAttackAttempt(EntityUid uid, ShowEtherealComponent component, Att args.Cancel(); } -} \ No newline at end of file +} diff --git a/Content.Server/Shuttles/Systems/ArrivalsSystem.cs b/Content.Server/Shuttles/Systems/ArrivalsSystem.cs index 5d31004a0f..0d73033859 100644 --- a/Content.Server/Shuttles/Systems/ArrivalsSystem.cs +++ b/Content.Server/Shuttles/Systems/ArrivalsSystem.cs @@ -10,6 +10,7 @@ using Content.Server.Shuttles.Components; using Content.Server.Shuttles.Events; using Content.Server.Spawners.Components; +using Content.Server.Spawners.EntitySystems; using Content.Server.Station.Components; using Content.Server.Station.Events; using Content.Server.Station.Systems; @@ -21,6 +22,7 @@ using Content.Shared.Movement.Components; using Content.Shared.Parallax.Biomes; using Content.Shared.Roles; +using Content.Shared.Preferences; using Content.Shared.Salvage; using Content.Shared.Shuttles.Components; using Content.Shared.Tiles; @@ -65,6 +67,11 @@ public sealed class ArrivalsSystem : EntitySystem /// public bool Enabled { get; private set; } + /// + /// Flags if all players spawning at the departure terminal have godmode until they leave the terminal. + /// + public bool ArrivalsGodmode { get; private set; } + /// /// The first arrival is a little early, to save everyone 10s /// @@ -81,6 +88,8 @@ public override void Initialize() { base.Initialize(); + SubscribeLocalEvent(HandlePlayerSpawning, before: new []{ typeof(ContainerSpawnPointSystem), typeof(SpawnPointSystem)}); + SubscribeLocalEvent(OnStationPostInit); SubscribeLocalEvent(OnShuttleStartup); @@ -96,7 +105,10 @@ public override void Initialize() // Don't invoke immediately as it will get set in the natural course of things. Enabled = _cfgManager.GetCVar(CCVars.ArrivalsShuttles); - Subs.CVar(_cfgManager, CCVars.ArrivalsShuttles, SetArrivals); + ArrivalsGodmode = _cfgManager.GetCVar(CCVars.GodmodeArrivals); + + _cfgManager.OnValueChanged(CCVars.ArrivalsShuttles, SetArrivals); + _cfgManager.OnValueChanged(CCVars.GodmodeArrivals, b => ArrivalsGodmode = b); // Command so admins can set these for funsies _console.RegisterCommand("arrivals", ArrivalsCommand, ArrivalsCompletion); @@ -310,6 +322,9 @@ public void HandlePlayerSpawning(PlayerSpawningEvent ev) if (ev.SpawnResult != null) return; + if (ev.HumanoidCharacterProfile?.SpawnPriority != SpawnPriorityPreference.Arrivals) + return; + // Only works on latejoin even if enabled. if (!Enabled || _ticker.RunLevel != GameRunLevel.InRound) return; diff --git a/Content.Server/Silicon/Charge/Systems/SiliconChargeDeathSystem.cs b/Content.Server/Silicon/Charge/Systems/SiliconChargeDeathSystem.cs index d4d1db5ed9..41ee269788 100644 --- a/Content.Server/Silicon/Charge/Systems/SiliconChargeDeathSystem.cs +++ b/Content.Server/Silicon/Charge/Systems/SiliconChargeDeathSystem.cs @@ -1,6 +1,5 @@ using Content.Server.Power.Components; using Content.Shared.Silicon.Systems; -using Content.Server.Bed.Sleep; using Content.Shared.Bed.Sleep; using Content.Server.Silicon.Charge; using Content.Server.Humanoid; @@ -63,7 +62,7 @@ private void SiliconDead(EntityUid uid, SiliconDownOnDeadComponent siliconDeadCo private void SiliconUnDead(EntityUid uid, SiliconDownOnDeadComponent siliconDeadComp, BatteryComponent? batteryComp, EntityUid batteryUid) { RemComp(uid); - _sleep.TryWaking(uid, null, true); + _sleep.TryWaking(uid, true); siliconDeadComp.Dead = false; diff --git a/Content.Server/Silicons/Laws/SiliconLawSystem.cs b/Content.Server/Silicons/Laws/SiliconLawSystem.cs index 0c0f68c23f..6b7df52a6e 100644 --- a/Content.Server/Silicons/Laws/SiliconLawSystem.cs +++ b/Content.Server/Silicons/Laws/SiliconLawSystem.cs @@ -5,12 +5,10 @@ using Content.Server.Radio.Components; using Content.Server.Roles; using Content.Server.Station.Systems; -using Content.Shared.Actions; using Content.Shared.Administration; using Content.Shared.Chat; using Content.Shared.Emag.Components; using Content.Shared.Emag.Systems; -using Content.Shared.Examine; using Content.Shared.Mind; using Content.Shared.Mind.Components; using Content.Shared.Roles; @@ -19,10 +17,10 @@ using Content.Shared.Stunnable; using Content.Shared.Wires; using Robust.Server.GameObjects; +using Robust.Shared.Containers; using Robust.Shared.Player; using Robust.Shared.Prototypes; using Robust.Shared.Toolshed; -using Robust.Shared.Utility; namespace Content.Server.Silicons.Laws; @@ -32,11 +30,9 @@ public sealed class SiliconLawSystem : SharedSiliconLawSystem [Dependency] private readonly IChatManager _chatManager = default!; [Dependency] private readonly IPrototypeManager _prototype = default!; [Dependency] private readonly SharedMindSystem _mind = default!; - [Dependency] private readonly SharedActionsSystem _actions = default!; [Dependency] private readonly StationSystem _station = default!; [Dependency] private readonly UserInterfaceSystem _userInterface = default!; [Dependency] private readonly SharedStunSystem _stunSystem = default!; - [Dependency] private readonly IEntityManager _entityManager = default!; [Dependency] private readonly SharedRoleSystem _roles = default!; /// @@ -44,7 +40,6 @@ public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnComponentShutdown); SubscribeLocalEvent(OnMapInit); SubscribeLocalEvent(OnMindAdded); SubscribeLocalEvent(OnToggleLawsScreen); @@ -58,15 +53,8 @@ public override void Initialize() SubscribeLocalEvent(OnEmagMindRemoved); } - private void OnComponentShutdown(EntityUid uid, SiliconLawBoundComponent component, ComponentShutdown args) - { - if (component.ViewLawsActionEntity != null) - _actions.RemoveAction(uid, component.ViewLawsActionEntity); - } - private void OnMapInit(EntityUid uid, SiliconLawBoundComponent component, MapInitEvent args) { - _actions.AddAction(uid, ref component.ViewLawsActionEntity, component.ViewLawsAction); GetLaws(uid, component); } @@ -92,7 +80,7 @@ private void OnToggleLawsScreen(EntityUid uid, SiliconLawBoundComponent componen private void OnBoundUIOpened(EntityUid uid, SiliconLawBoundComponent component, BoundUIOpenedEvent args) { - _entityManager.TryGetComponent(uid, out var intrinsicRadio); + TryComp(uid, out IntrinsicRadioTransmitterComponent? intrinsicRadio); var radioChannels = intrinsicRadio?.Channels; var state = new SiliconLawBuiState(GetLaws(uid).Laws, radioChannels); @@ -264,9 +252,9 @@ public void NotifyLawsChanged(EntityUid uid) /// /// Extract all the laws from a lawset's prototype ids. /// - public SiliconLawset GetLawset(string lawset) + public SiliconLawset GetLawset(ProtoId lawset) { - var proto = _prototype.Index(lawset); + var proto = _prototype.Index(lawset); var laws = new SiliconLawset() { Laws = new List(proto.Laws.Count) @@ -294,6 +282,21 @@ public void SetLaws(List newLaws, EntityUid target) component.Lawset.Laws = newLaws; NotifyLawsChanged(target); } + + protected override void OnUpdaterInsert(Entity ent, ref EntInsertedIntoContainerMessage args) + { + // TODO: Prediction dump this + if (!TryComp(args.Entity, out SiliconLawProviderComponent? provider)) + return; + + var lawset = GetLawset(provider.Laws).Laws; + var query = EntityManager.CompRegistryQueryEnumerator(ent.Comp.Components); + + while (query.MoveNext(out var update)) + { + SetLaws(lawset, update); + } + } } [ToolshedCommand, AdminCommand(AdminFlags.Admin)] diff --git a/Content.Server/Silicons/StationAi/AiInteractWireAction.cs b/Content.Server/Silicons/StationAi/AiInteractWireAction.cs new file mode 100644 index 0000000000..c92c825b32 --- /dev/null +++ b/Content.Server/Silicons/StationAi/AiInteractWireAction.cs @@ -0,0 +1,37 @@ +using Content.Server.Wires; +using Content.Shared.Doors; +using Content.Shared.Silicons.StationAi; +using Content.Shared.Wires; + +namespace Content.Server.Silicons.StationAi; + +/// +/// Controls whether an AI can interact with the target entity. +/// +public sealed partial class AiInteractWireAction : ComponentWireAction +{ + public override string Name { get; set; } = "wire-name-ai-act-light"; + public override Color Color { get; set; } = Color.DeepSkyBlue; + public override object StatusKey => AirlockWireStatus.AiControlIndicator; + + public override StatusLightState? GetLightState(Wire wire, StationAiWhitelistComponent component) + { + return component.Enabled ? StatusLightState.On : StatusLightState.Off; + } + + public override bool Cut(EntityUid user, Wire wire, StationAiWhitelistComponent component) + { + return EntityManager.System() + .SetWhitelistEnabled((component.Owner, component), false, announce: true); + } + + public override bool Mend(EntityUid user, Wire wire, StationAiWhitelistComponent component) + { + return EntityManager.System() + .SetWhitelistEnabled((component.Owner, component), true); + } + + public override void Pulse(EntityUid user, Wire wire, StationAiWhitelistComponent component) + { + } +} diff --git a/Content.Server/Silicons/StationAi/AiVisionWireAction.cs b/Content.Server/Silicons/StationAi/AiVisionWireAction.cs new file mode 100644 index 0000000000..3523f4d38f --- /dev/null +++ b/Content.Server/Silicons/StationAi/AiVisionWireAction.cs @@ -0,0 +1,40 @@ +using Content.Server.Wires; +using Content.Shared.Doors; +using Content.Shared.Silicons.StationAi; +using Content.Shared.StationAi; +using Content.Shared.Wires; + +namespace Content.Server.Silicons.StationAi; + +/// +/// Handles StationAiVision functionality for the attached entity. +/// +public sealed partial class AiVisionWireAction : ComponentWireAction +{ + public override string Name { get; set; } = "wire-name-ai-vision-light"; + public override Color Color { get; set; } = Color.DeepSkyBlue; + public override object StatusKey => AirlockWireStatus.AiControlIndicator; + + public override StatusLightState? GetLightState(Wire wire, StationAiVisionComponent component) + { + return component.Enabled ? StatusLightState.On : StatusLightState.Off; + } + + public override bool Cut(EntityUid user, Wire wire, StationAiVisionComponent component) + { + return EntityManager.System() + .SetVisionEnabled((component.Owner, component), false, announce: true); + } + + public override bool Mend(EntityUid user, Wire wire, StationAiVisionComponent component) + { + return EntityManager.System() + .SetVisionEnabled((component.Owner, component), true); + } + + public override void Pulse(EntityUid user, Wire wire, StationAiVisionComponent component) + { + // TODO: This should turn it off for a bit + // Need timer cleanup first out of scope. + } +} diff --git a/Content.Server/Silicons/StationAi/StationAiSystem.cs b/Content.Server/Silicons/StationAi/StationAiSystem.cs new file mode 100644 index 0000000000..846497387d --- /dev/null +++ b/Content.Server/Silicons/StationAi/StationAiSystem.cs @@ -0,0 +1,76 @@ +using System.Linq; +using Content.Server.Chat.Managers; +using Content.Server.Chat.Systems; +using Content.Shared.Chat; +using Content.Shared.Silicons.StationAi; +using Content.Shared.StationAi; +using Robust.Shared.Audio.Systems; +using Robust.Shared.Map.Components; +using Robust.Shared.Player; + +namespace Content.Server.Silicons.StationAi; + +public sealed class StationAiSystem : SharedStationAiSystem +{ + [Dependency] private readonly IChatManager _chats = default!; + [Dependency] private readonly EntityLookupSystem _lookup = default!; + + private readonly HashSet> _ais = new(); + + public override bool SetVisionEnabled(Entity entity, bool enabled, bool announce = false) + { + if (!base.SetVisionEnabled(entity, enabled, announce)) + return false; + + if (announce) + { + AnnounceSnip(entity.Owner); + } + + return true; + } + + public override bool SetWhitelistEnabled(Entity entity, bool enabled, bool announce = false) + { + if (!base.SetWhitelistEnabled(entity, enabled, announce)) + return false; + + if (announce) + { + AnnounceSnip(entity.Owner); + } + + return true; + } + + private void AnnounceSnip(EntityUid entity) + { + var xform = Transform(entity); + + if (!TryComp(xform.GridUid, out MapGridComponent? grid)) + return; + + _ais.Clear(); + _lookup.GetChildEntities(xform.GridUid.Value, _ais); + var filter = Filter.Empty(); + + foreach (var ai in _ais) + { + // TODO: Filter API? + if (TryComp(ai.Owner, out ActorComponent? actorComp)) + { + filter.AddPlayer(actorComp.PlayerSession); + } + } + + // TEST + // filter = Filter.Broadcast(); + + // No easy way to do chat notif embeds atm. + var tile = Maps.LocalToTile(xform.GridUid.Value, grid, xform.Coordinates); + var msg = Loc.GetString("ai-wire-snipped", ("coords", tile)); + + _chats.ChatMessageToMany(ChatChannel.Notifications, msg, msg, entity, false, true, filter.Recipients.Select(o => o.Channel)); + // Apparently there's no sound for this. + } +} diff --git a/Content.Server/Sound/SpamEmitSoundRequirePowerSystem.cs b/Content.Server/Sound/SpamEmitSoundRequirePowerSystem.cs index d2c2a8a1ca..1ad1bb6c0a 100644 --- a/Content.Server/Sound/SpamEmitSoundRequirePowerSystem.cs +++ b/Content.Server/Sound/SpamEmitSoundRequirePowerSystem.cs @@ -1,7 +1,6 @@ using Content.Server.Power.Components; using Content.Server.Power.EntitySystems; using Content.Shared.Power; -using Content.Shared.Power.Components; using Content.Shared.Sound; using Content.Shared.Sound.Components; diff --git a/Content.Server/Spawners/EntitySystems/ContainerSpawnPointSystem.cs b/Content.Server/Spawners/EntitySystems/ContainerSpawnPointSystem.cs index 4d38571b90..6b0033124e 100644 --- a/Content.Server/Spawners/EntitySystems/ContainerSpawnPointSystem.cs +++ b/Content.Server/Spawners/EntitySystems/ContainerSpawnPointSystem.cs @@ -1,8 +1,11 @@ using Content.Server.GameTicking; using Content.Server.Spawners.Components; using Content.Server.Station.Systems; +using Content.Shared.Preferences; +using Content.Shared.Roles; using Robust.Server.Containers; using Robust.Shared.Containers; +using Robust.Shared.Prototypes; using Robust.Shared.Random; namespace Content.Server.Spawners.EntitySystems; @@ -11,17 +14,25 @@ public sealed class ContainerSpawnPointSystem : EntitySystem { [Dependency] private readonly GameTicker _gameTicker = default!; [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly IPrototypeManager _proto = default!; [Dependency] private readonly ContainerSystem _container = default!; [Dependency] private readonly StationSystem _station = default!; [Dependency] private readonly StationSpawningSystem _stationSpawning = default!; + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(HandlePlayerSpawning, before: new []{ typeof(SpawnPointSystem) }); + } + public void HandlePlayerSpawning(PlayerSpawningEvent args) { if (args.SpawnResult != null) return; - // DeltaV - Ignore these two desired spawn types - if (args.DesiredSpawnPointType is SpawnPointType.Observer or SpawnPointType.LateJoin) + // If it's just a spawn pref check if it's for cryo (silly). + if (args.HumanoidCharacterProfile?.SpawnPriority != SpawnPriorityPreference.Cryosleep && + (!_proto.TryIndex(args.Job?.Prototype, out var jobProto) || jobProto.JobEntity == null)) return; var query = EntityQueryEnumerator(); @@ -33,11 +44,12 @@ public void HandlePlayerSpawning(PlayerSpawningEvent args) continue; // DeltaV - Custom override for override spawnpoints, only used for prisoners currently. This shouldn't run for any other jobs - if (args.DesiredSpawnPointType == SpawnPointType.Job) + if (args.DesiredSpawnPointType == SpawnPointType.Job + && spawnPoint.SpawnType == SpawnPointType.Job + && args.Job is not null + && spawnPoint.Job is not "" + && spawnPoint.Job == args.Job.Prototype) { - if (spawnPoint.SpawnType != SpawnPointType.Job || spawnPoint.Job != args.Job?.Prototype) - continue; - possibleContainers.Add((uid, spawnPoint, container, xform)); continue; } diff --git a/Content.Server/Station/Systems/StationSpawningSystem.cs b/Content.Server/Station/Systems/StationSpawningSystem.cs index 8375622c1e..85f5662b42 100644 --- a/Content.Server/Station/Systems/StationSpawningSystem.cs +++ b/Content.Server/Station/Systems/StationSpawningSystem.cs @@ -53,19 +53,11 @@ public sealed class StationSpawningSystem : SharedStationSpawningSystem private bool _randomizeCharacters; - private Dictionary> _spawnerCallbacks = new(); - /// public override void Initialize() { base.Initialize(); Subs.CVar(_configurationManager, CCVars.ICRandomCharacters, e => _randomizeCharacters = e, true); - - _spawnerCallbacks = new Dictionary>() - { - { SpawnPriorityPreference.Arrivals, _arrivalsSystem.HandlePlayerSpawning }, - { SpawnPriorityPreference.Cryosleep, _containerSpawnPointSystem.HandlePlayerSpawning } - }; } /// @@ -88,31 +80,7 @@ public override void Initialize() var ev = new PlayerSpawningEvent(job, profile, station, spawnPointType); - if (station != null && profile != null) - { - // Try to call the character's preferred spawner first. - if (_spawnerCallbacks.TryGetValue(profile.SpawnPriority, out var preferredSpawner)) - { - preferredSpawner(ev); - - foreach (var (key, remainingSpawner) in _spawnerCallbacks) - { - if (key == profile.SpawnPriority) - continue; - - remainingSpawner(ev); - } - } - else - { - // Call all of them in the typical order. - foreach (var typicalSpawner in _spawnerCallbacks.Values) - typicalSpawner(ev); - } - } - RaiseLocalEvent(ev); - DebugTools.Assert(ev.SpawnResult is { Valid: true } or null); return ev.SpawnResult; @@ -131,7 +99,7 @@ public override void Initialize() /// The station this player is being spawned on. /// The entity to use, if one already exists. /// The spawned entity - public EntityUid SpawnPlayerMob( + public EntityUid SpawnPlayerMob( EntityCoordinates coordinates, JobComponent? job, HumanoidCharacterProfile? profile, diff --git a/Content.Server/StationEvents/Components/RandomSentienceRuleComponent.cs b/Content.Server/StationEvents/Components/RandomSentienceRuleComponent.cs index 98ebf06595..369fce9725 100644 --- a/Content.Server/StationEvents/Components/RandomSentienceRuleComponent.cs +++ b/Content.Server/StationEvents/Components/RandomSentienceRuleComponent.cs @@ -1,9 +1,13 @@ -using Content.Server.StationEvents.Events; +using Content.Server.StationEvents.Events; namespace Content.Server.StationEvents.Components; [RegisterComponent, Access(typeof(RandomSentienceRule))] public sealed partial class RandomSentienceRuleComponent : Component { + [DataField] + public int MinSentiences = 1; + [DataField] + public int MaxSentiences = 1; } diff --git a/Content.Server/StationEvents/Components/SentienceTargetComponent.cs b/Content.Server/StationEvents/Components/SentienceTargetComponent.cs index f8f7e587c1..4fb5e4be59 100644 --- a/Content.Server/StationEvents/Components/SentienceTargetComponent.cs +++ b/Content.Server/StationEvents/Components/SentienceTargetComponent.cs @@ -1,10 +1,13 @@ -using Content.Server.StationEvents.Events; +using Content.Server.StationEvents.Events; namespace Content.Server.StationEvents.Components; [RegisterComponent, Access(typeof(RandomSentienceRule))] public sealed partial class SentienceTargetComponent : Component { - [DataField("flavorKind", required: true)] + [DataField(required: true)] public string FlavorKind = default!; + + [DataField] + public float Weight = 1.0f; } diff --git a/Content.Server/StationEvents/Events/RandomSentienceRule.cs b/Content.Server/StationEvents/Events/RandomSentienceRule.cs index 2fb733e1a6..308f37fdeb 100644 --- a/Content.Server/StationEvents/Events/RandomSentienceRule.cs +++ b/Content.Server/StationEvents/Events/RandomSentienceRule.cs @@ -1,9 +1,13 @@ -using System.Linq; using Content.Server.Announcements.Systems; +using System.Linq; +using Content.Shared.Dataset; using Content.Server.Ghost.Roles.Components; using Content.Server.Station.Components; using Content.Server.StationEvents.Components; using Content.Shared.GameTicking.Components; +using Content.Shared.Random.Helpers; +using Robust.Shared.Prototypes; +using Robust.Shared.Random; namespace Content.Server.StationEvents.Events; @@ -11,26 +15,46 @@ public sealed class RandomSentienceRule : StationEventSystem stationsToNotify = new(); + if (!TryGetRandomStation(out var randomStation)) + return; var targetList = new List>(); - var query = EntityQueryEnumerator(); - while (query.MoveNext(out var targetUid, out var target)) + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var targetUid, out var target, out var xform)) { + if (StationSystem.GetOwningStation(targetUid, xform) != randomStation) + continue; + targetList.Add((targetUid, target)); } - RobustRandom.Shuffle(targetList); - - var toMakeSentient = RobustRandom.Next(2, 5); + var toMakeSentient = _random.Next(component.MinSentiences, component.MaxSentiences); + var stationsToNotify = new List(); var groups = new HashSet(); - foreach (var target in targetList) + for (var i = 0; i < toMakeSentient && targetList.Count > 0; i++) { - if (toMakeSentient-- == 0) - break; + // weighted random to pick a sentience target + var totalWeight = targetList.Sum(x => x.Comp.Weight); + // This initial target should never be picked. + // It's just so that target doesn't need to be nullable and as a safety fallback for id floating point errors ever mess up the comparison in the foreach. + var target = targetList[0]; + var chosenWeight = _random.NextFloat(totalWeight); + var currentWeight = 0.0; + foreach (var potentialTarget in targetList) + { + currentWeight += potentialTarget.Comp.Weight; + if (currentWeight > chosenWeight) + { + target = potentialTarget; + break; + } + } + targetList.Remove(target); RemComp(target); var ghostRole = EnsureComp(target); @@ -50,10 +74,10 @@ protected override void Started(EntityUid uid, RandomSentienceRuleComponent comp foreach (var target in targetList) { - var station = StationSystem.GetOwningStation(target); - if(station == null) + var targetStation = StationSystem.GetOwningStation(target); + if(targetStation == null) continue; - stationsToNotify.Add((EntityUid) station); + stationsToNotify.Add((EntityUid) targetStation); } foreach (var station in stationsToNotify) { @@ -63,10 +87,11 @@ protected override void Started(EntityUid uid, RandomSentienceRuleComponent comp "station-event-random-sentience-announcement", null, Color.Gold, - null, null, + null, + null, ("kind1", kind1), ("kind2", kind2), ("kind3", kind3), ("amount", groupList.Count), - ("data", Loc.GetString($"random-sentience-event-data-{RobustRandom.Next(1, 6)}")), - ("strength", Loc.GetString($"random-sentience-event-strength-{RobustRandom.Next(1, 8)}")) + ("data", _random.Pick(_prototype.Index("RandomSentienceEventData"))), + ("strength", _random.Pick(_prototype.Index("RandomSentienceEventStrength"))) ); } } diff --git a/Content.Server/Traits/Assorted/SingerSystem.cs b/Content.Server/Traits/Assorted/SingerSystem.cs index 78a4101a65..95087c0f1e 100644 --- a/Content.Server/Traits/Assorted/SingerSystem.cs +++ b/Content.Server/Traits/Assorted/SingerSystem.cs @@ -2,6 +2,7 @@ using Content.Server.Speech.Components; using Content.Server.UserInterface; using Content.Shared.ActionBlocker; +using Content.Shared.Bed.Sleep; using Content.Shared.Damage; using Content.Shared.Damage.ForceSay; using Content.Shared.FixedPoint; diff --git a/Content.Server/Traits/TraitSystem.cs b/Content.Server/Traits/TraitSystem.cs index 3e0866a2ba..57103c4a5d 100644 --- a/Content.Server/Traits/TraitSystem.cs +++ b/Content.Server/Traits/TraitSystem.cs @@ -48,6 +48,9 @@ private void OnPlayerSpawnComplete(PlayerSpawnCompleteEvent args) { var pointsTotal = _configuration.GetCVar(CCVars.GameTraitsDefaultPoints); var traitSelections = _configuration.GetCVar(CCVars.GameTraitsMax); + if (args.JobId is not null && !_prototype.TryIndex(args.JobId, out var jobPrototype) + && jobPrototype is not null && !jobPrototype.ApplyTraits) + return; foreach (var traitId in args.Profile.TraitPreferences) { diff --git a/Content.Server/Weapons/Melee/EnergySword/EnergySwordSystem.cs b/Content.Server/Weapons/Melee/EnergySword/EnergySwordSystem.cs index 5970e16319..c9be87c623 100644 --- a/Content.Server/Weapons/Melee/EnergySword/EnergySwordSystem.cs +++ b/Content.Server/Weapons/Melee/EnergySword/EnergySwordSystem.cs @@ -38,7 +38,7 @@ private void OnInteractUsing(EntityUid uid, EnergySwordComponent comp, InteractU if (args.Handled) return; - if (!_toolSystem.HasQuality(args.Used, "Pulsing")) + if (!_toolSystem.HasQuality(args.Used, SharedToolSystem.PulseQuality)) return; args.Handled = true; diff --git a/Content.Server/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactElectricityTriggerSystem.cs b/Content.Server/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactElectricityTriggerSystem.cs index 019e09bbbb..9d2fd58980 100644 --- a/Content.Server/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactElectricityTriggerSystem.cs +++ b/Content.Server/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactElectricityTriggerSystem.cs @@ -44,7 +44,7 @@ private void OnInteractUsing(EntityUid uid, ArtifactElectricityTriggerComponent if (args.Handled) return; - if (!_toolSystem.HasQuality(args.Used, "Pulsing")) + if (!_toolSystem.HasQuality(args.Used, SharedToolSystem.PulseQuality)) return; args.Handled = _artifactSystem.TryActivateArtifact(uid, args.User); diff --git a/Content.Server/_Shitmed/Autodoc/Systems/AutodocSystem.cs b/Content.Server/_Shitmed/Autodoc/Systems/AutodocSystem.cs index d1af790eaa..7002734fd3 100644 --- a/Content.Server/_Shitmed/Autodoc/Systems/AutodocSystem.cs +++ b/Content.Server/_Shitmed/Autodoc/Systems/AutodocSystem.cs @@ -6,7 +6,8 @@ using Content.Server.Power.EntitySystems; using Content.Shared._Shitmed.Autodoc.Components; using Content.Shared._Shitmed.Autodoc.Systems; -using Content.Server.Bed.Sleep; +using Content.Shared.Bed.Sleep; + namespace Content.Server._Shitmed.Autodoc.Systems; @@ -15,7 +16,7 @@ public sealed class AutodocSystem : SharedAutodocSystem [Dependency] private readonly InternalsSystem _internals = default!; [Dependency] private readonly ChatSystem _chat = default!; [Dependency] private readonly PowerReceiverSystem _power = default!; - [Dependency] private readonly SleepingSystem _sleepingSystem = default!; // Sleeping isnt shared yet. + [Dependency] private readonly SleepingSystem _sleepingSystem = default!; public override void Update(float frameTime) { diff --git a/Content.Shared/ActionBlocker/ActionBlockerSystem.cs b/Content.Shared/ActionBlocker/ActionBlockerSystem.cs index d2883b5ef5..457e3875d4 100644 --- a/Content.Shared/ActionBlocker/ActionBlockerSystem.cs +++ b/Content.Shared/ActionBlocker/ActionBlockerSystem.cs @@ -4,6 +4,7 @@ using Content.Shared.Emoting; using Content.Shared.Hands; using Content.Shared.Interaction; +using Content.Shared.Interaction.Components; using Content.Shared.Interaction.Events; using Content.Shared.Item; using Content.Shared.Mobs; @@ -26,9 +27,14 @@ public sealed class ActionBlockerSystem : EntitySystem { [Dependency] private readonly SharedContainerSystem _container = default!; + private EntityQuery _complexInteractionQuery; + public override void Initialize() { base.Initialize(); + + _complexInteractionQuery = GetEntityQuery(); + SubscribeLocalEvent(OnMoverStartup); } @@ -57,6 +63,15 @@ public bool UpdateCanMove(EntityUid uid, InputMoverComponent? component = null) return !ev.Cancelled; } + /// + /// Checks if a given entity is able to do specific complex interactions. + /// This is used to gate manipulation to general humanoids. If a mouse shouldn't be able to do something, then it's complex. + /// + public bool CanComplexInteract(EntityUid user) + { + return _complexInteractionQuery.HasComp(user); + } + /// /// Raises an event directed at both the user and the target entity to check whether a user is capable of /// interacting with this entity. @@ -74,7 +89,7 @@ public bool CanInteract(EntityUid user, EntityUid? target) return false; var ev = new InteractionAttemptEvent(user, target); - RaiseLocalEvent(user, ev); + RaiseLocalEvent(user, ref ev); if (ev.Cancelled) return false; @@ -83,7 +98,7 @@ public bool CanInteract(EntityUid user, EntityUid? target) return true; var targetEv = new GettingInteractedWithAttemptEvent(user, target); - RaiseLocalEvent(target.Value, targetEv); + RaiseLocalEvent(target.Value, ref targetEv); return !targetEv.Cancelled; } @@ -114,7 +129,7 @@ public bool CanUseHeldEntity(EntityUid user, EntityUid used) public bool CanConsciouslyPerformAction(EntityUid user) { var ev = new ConsciousAttemptEvent(user); - RaiseLocalEvent(user, ev); + RaiseLocalEvent(user, ref ev); return !ev.Cancelled; } diff --git a/Content.Shared/Actions/Events/AssayPowerActionEvent.cs b/Content.Shared/Actions/Events/AssayPowerActionEvent.cs new file mode 100644 index 0000000000..dc25d34331 --- /dev/null +++ b/Content.Shared/Actions/Events/AssayPowerActionEvent.cs @@ -0,0 +1,30 @@ +using Robust.Shared.Audio; + +namespace Content.Shared.Actions.Events; + +public sealed partial class AssayPowerActionEvent : EntityTargetActionEvent +{ + [DataField] + public TimeSpan UseDelay = TimeSpan.FromSeconds(8f); + + [DataField] + public SoundSpecifier SoundUse = new SoundPathSpecifier("/Audio/Psionics/heartbeat_fast.ogg"); + + [DataField] + public string PopupTarget = "assay-begin"; + + [DataField] + public int FontSize = 12; + + [DataField] + public string FontColor = "#8A00C2"; + + [DataField] + public int MinGlimmer = 3; + + [DataField] + public int MaxGlimmer = 6; + + [DataField] + public string PowerName = "assay"; +} diff --git a/Content.Shared/Actions/SharedActionsSystem.cs b/Content.Shared/Actions/SharedActionsSystem.cs index 91acf47478..8cede391a8 100644 --- a/Content.Shared/Actions/SharedActionsSystem.cs +++ b/Content.Shared/Actions/SharedActionsSystem.cs @@ -14,6 +14,7 @@ using Robust.Shared.Containers; using Robust.Shared.GameStates; using Robust.Shared.Map; +using Robust.Shared.Network; using Robust.Shared.Timing; using Robust.Shared.Utility; @@ -23,6 +24,8 @@ public abstract class SharedActionsSystem : EntitySystem { [Dependency] protected readonly IGameTiming GameTiming = default!; [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; + [Dependency] private readonly INetManager _net = default!; + [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!; [Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!; [Dependency] private readonly RotateToFaceSystem _rotateToFaceSystem = default!; @@ -774,7 +777,7 @@ public bool AddActionDirect(EntityUid performer, if (!ResolveActionData(actionId, ref action)) return false; - DebugTools.Assert(action.Container == null || + DebugTools.Assert(_net.IsClient || action.Container == null || (TryComp(action.Container, out ActionsContainerComponent? containerComp) && containerComp.Container.Contains(actionId))); diff --git a/Content.Shared/Administration/SharedAdminFrozenSystem.cs b/Content.Shared/Administration/SharedAdminFrozenSystem.cs index 2fa22e0005..259df2bdf2 100644 --- a/Content.Shared/Administration/SharedAdminFrozenSystem.cs +++ b/Content.Shared/Administration/SharedAdminFrozenSystem.cs @@ -11,6 +11,7 @@ namespace Content.Shared.Administration; +// TODO deduplicate with BlockMovementComponent public abstract class SharedAdminFrozenSystem : EntitySystem { [Dependency] private readonly ActionBlockerSystem _blocker = default!; @@ -23,7 +24,7 @@ public override void Initialize() SubscribeLocalEvent(OnAttempt); SubscribeLocalEvent(OnAttempt); SubscribeLocalEvent(OnAttempt); - SubscribeLocalEvent(OnAttempt); + SubscribeLocalEvent(OnInteractAttempt); SubscribeLocalEvent(OnStartup); SubscribeLocalEvent(UpdateCanMove); SubscribeLocalEvent(OnUpdateCanMove); @@ -34,6 +35,11 @@ public override void Initialize() SubscribeLocalEvent(OnSpeakAttempt); } + private void OnInteractAttempt(Entity ent, ref InteractionAttemptEvent args) + { + args.Cancelled = true; + } + private void OnSpeakAttempt(EntityUid uid, AdminFrozenComponent component, SpeakAttemptEvent args) { if (!component.Muted) diff --git a/Content.Shared/Bed/Sleep/SharedSleepingSystem.cs b/Content.Shared/Bed/Sleep/SharedSleepingSystem.cs deleted file mode 100644 index aa5578a3a9..0000000000 --- a/Content.Shared/Bed/Sleep/SharedSleepingSystem.cs +++ /dev/null @@ -1,102 +0,0 @@ -using Content.Shared.Actions; -using Content.Shared.Bed.Sleep; -using Content.Shared.Damage.ForceSay; -using Content.Shared.Eye.Blinding.Systems; -using Content.Shared.Pointing; -using Content.Shared.Speech; -using Robust.Shared.Network; -using Robust.Shared.Prototypes; -using Robust.Shared.Timing; - -namespace Content.Server.Bed.Sleep -{ - public abstract class SharedSleepingSystem : EntitySystem - { - [Dependency] private readonly IGameTiming _gameTiming = default!; - [Dependency] private readonly SharedActionsSystem _actionsSystem = default!; - [Dependency] private readonly BlindableSystem _blindableSystem = default!; - - [ValidatePrototypeId] private const string WakeActionId = "ActionWake"; - - public override void Initialize() - { - base.Initialize(); - SubscribeLocalEvent(OnMapInit); - SubscribeLocalEvent(OnShutdown); - SubscribeLocalEvent(OnSpeakAttempt); - SubscribeLocalEvent(OnSeeAttempt); - SubscribeLocalEvent(OnPointAttempt); - } - - - private void OnMapInit(EntityUid uid, SleepingComponent component, MapInitEvent args) - { - component.SleepingSince = _gameTiming.CurTime; - - var ev = new SleepStateChangedEvent(true); - RaiseLocalEvent(uid, ev); - _blindableSystem.UpdateIsBlind(uid); - _actionsSystem.AddAction(uid, ref component.WakeAction, WakeActionId, uid); - - // TODO remove hardcoded time. - _actionsSystem.SetCooldown(component.WakeAction, _gameTiming.CurTime, _gameTiming.CurTime + TimeSpan.FromSeconds(2f)); - } - - private void OnShutdown(EntityUid uid, SleepingComponent component, ComponentShutdown args) - { - _actionsSystem.RemoveAction(uid, component.WakeAction); - var ev = new SleepStateChangedEvent(false) - { - TimeSlept = _gameTiming.CurTime - component.SleepingSince - }; - RaiseLocalEvent(uid, ev); - _blindableSystem.UpdateIsBlind(uid); - } - - private void OnSpeakAttempt(EntityUid uid, SleepingComponent component, SpeakAttemptEvent args) - { - // TODO reduce duplication of this behavior with MobStateSystem somehow - if (HasComp(uid)) - { - RemCompDeferred(uid); - return; - } - - args.Cancel(); - } - - private void OnSeeAttempt(EntityUid uid, SleepingComponent component, CanSeeAttemptEvent args) - { - if (component.LifeStage <= ComponentLifeStage.Running) - args.Cancel(); - } - - private void OnPointAttempt(EntityUid uid, SleepingComponent component, PointAttemptEvent args) - { - args.Cancel(); - } - } -} - - -public sealed partial class SleepActionEvent : InstantActionEvent {} - -public sealed partial class WakeActionEvent : InstantActionEvent {} - -/// -/// Raised on an entity when they fall asleep or wake up. -/// -public sealed class SleepStateChangedEvent : EntityEventArgs -{ - public bool FellAsleep = false; - - /// - /// The amount of time this entity slept for. Null if is true. - /// - public TimeSpan? TimeSlept; - - public SleepStateChangedEvent(bool fellAsleep) - { - FellAsleep = fellAsleep; - } -} diff --git a/Content.Shared/Bed/Sleep/SleepingComponent.cs b/Content.Shared/Bed/Sleep/SleepingComponent.cs index e87aca2203..e43c71769a 100644 --- a/Content.Shared/Bed/Sleep/SleepingComponent.cs +++ b/Content.Shared/Bed/Sleep/SleepingComponent.cs @@ -1,37 +1,48 @@ using Content.Shared.FixedPoint; +using Robust.Shared.Audio; using Robust.Shared.GameStates; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; namespace Content.Shared.Bed.Sleep; /// /// Added to entities when they go to sleep. /// -[NetworkedComponent, RegisterComponent, AutoGenerateComponentPause(Dirty = true)] +[NetworkedComponent, RegisterComponent] +[AutoGenerateComponentState, AutoGenerateComponentPause(Dirty = true)] public sealed partial class SleepingComponent : Component { /// /// How much damage of any type it takes to wake this entity. /// - [DataField("wakeThreshold")] + [DataField] public FixedPoint2 WakeThreshold = FixedPoint2.New(2); /// /// Cooldown time between users hand interaction. /// - [DataField("cooldown")] - [ViewVariables(VVAccess.ReadWrite)] + [DataField] public TimeSpan Cooldown = TimeSpan.FromSeconds(1f); - [DataField("cooldownEnd", customTypeSerializer:typeof(TimeOffsetSerializer))] - [AutoPausedField] - public TimeSpan CoolDownEnd; - - [DataField("wakeAction")] public EntityUid? WakeAction; + [DataField] + [AutoNetworkedField, AutoPausedField] + public TimeSpan CooldownEnd; /// /// The moment this entity went to sleep. Initialized on MapInit. /// [DataField] public TimeSpan SleepingSince; + + [DataField] + [AutoNetworkedField] + public EntityUid? WakeAction; + + /// + /// Sound to play when another player attempts to wake this entity. + /// + [DataField] + public SoundSpecifier WakeAttemptSound = new SoundPathSpecifier("/Audio/Effects/thudswoosh.ogg") + { + Params = AudioParams.Default.WithVariation(0.05f) + }; } diff --git a/Content.Shared/Bed/Sleep/SleepingSystem.cs b/Content.Shared/Bed/Sleep/SleepingSystem.cs new file mode 100644 index 0000000000..8e6f1193ac --- /dev/null +++ b/Content.Shared/Bed/Sleep/SleepingSystem.cs @@ -0,0 +1,333 @@ +using Content.Shared.Actions; +using Content.Shared.Damage; +using Content.Shared.Damage.ForceSay; +using Content.Shared.Examine; +using Content.Shared.Eye.Blinding.Systems; +using Content.Shared.IdentityManagement; +using Content.Shared.Interaction; +using Content.Shared.Interaction.Events; +using Content.Shared.Mobs; +using Content.Shared.Mobs.Components; +using Content.Shared.Pointing; +using Content.Shared.Popups; +using Content.Shared.Slippery; +using Content.Shared.Sound; +using Content.Shared.Sound.Components; +using Content.Shared.Speech; +using Content.Shared.StatusEffect; +using Content.Shared.Stunnable; +using Content.Shared.Verbs; +using Robust.Shared.Audio.Systems; +using Robust.Shared.Prototypes; +using Robust.Shared.Timing; + +namespace Content.Shared.Bed.Sleep; + +public sealed partial class SleepingSystem : EntitySystem +{ + [Dependency] private readonly IGameTiming _gameTiming = default!; + [Dependency] private readonly SharedActionsSystem _actionsSystem = default!; + [Dependency] private readonly BlindableSystem _blindableSystem = default!; + [Dependency] private readonly SharedPopupSystem _popupSystem = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly SharedEmitSoundSystem _emitSound = default!; + [Dependency] private readonly StatusEffectsSystem _statusEffectsSystem = default!; + + public static readonly EntProtoId SleepActionId = "ActionSleep"; + public static readonly EntProtoId WakeActionId = "ActionWake"; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnBedSleepAction); + + SubscribeLocalEvent(OnSleepStateChanged); + SubscribeLocalEvent(OnWakeAction); + SubscribeLocalEvent(OnSleepAction); + + SubscribeLocalEvent(OnDamageChanged); + SubscribeLocalEvent(OnMobStateChanged); + SubscribeLocalEvent(OnMapInit); + SubscribeLocalEvent(OnSpeakAttempt); + SubscribeLocalEvent(OnSeeAttempt); + SubscribeLocalEvent(OnPointAttempt); + SubscribeLocalEvent(OnSlip); + SubscribeLocalEvent(OnConsciousAttempt); + SubscribeLocalEvent(OnExamined); + SubscribeLocalEvent>(AddWakeVerb); + SubscribeLocalEvent(OnInteractHand); + + SubscribeLocalEvent(OnInit); + } + + private void OnBedSleepAction(Entity ent, ref SleepActionEvent args) + { + TrySleeping(args.Performer); + } + + private void OnWakeAction(Entity ent, ref WakeActionEvent args) + { + if (TryWakeWithCooldown(ent.Owner)) + args.Handled = true; + } + + private void OnSleepAction(Entity ent, ref SleepActionEvent args) + { + TrySleeping((ent, ent.Comp)); + } + + /// + /// when sleeping component is added or removed, we do some stuff with other components. + /// + private void OnSleepStateChanged(Entity ent, ref SleepStateChangedEvent args) + { + if (args.FellAsleep) + { + // Expiring status effects would remove the components needed for sleeping + _statusEffectsSystem.TryRemoveStatusEffect(ent.Owner, "Stun"); + _statusEffectsSystem.TryRemoveStatusEffect(ent.Owner, "KnockedDown"); + + EnsureComp(ent); + EnsureComp(ent); + + if (TryComp(ent, out var sleepSound)) + { + var emitSound = EnsureComp(ent); + if (HasComp(ent)) + { + emitSound.Sound = sleepSound.Snore; + } + emitSound.MinInterval = sleepSound.Interval; + emitSound.MaxInterval = sleepSound.MaxInterval; + emitSound.PopUp = sleepSound.PopUp; + Dirty(ent.Owner, emitSound); + } + + return; + } + + RemComp(ent); + RemComp(ent); + RemComp(ent); + } + + private void OnMapInit(Entity ent, ref MapInitEvent args) + { + ent.Comp.SleepingSince = _gameTiming.CurTime; + + var ev = new SleepStateChangedEvent(true); + RaiseLocalEvent(ent, ref ev); + _blindableSystem.UpdateIsBlind(ent.Owner); + _actionsSystem.AddAction(ent, ref ent.Comp.WakeAction, WakeActionId, ent); + + // TODO remove hardcoded time. + _actionsSystem.SetCooldown(ent.Comp.WakeAction, _gameTiming.CurTime, _gameTiming.CurTime + TimeSpan.FromSeconds(2f)); + } + + private void OnSpeakAttempt(Entity ent, ref SpeakAttemptEvent args) + { + // TODO reduce duplication of this behavior with MobStateSystem somehow + if (HasComp(ent)) + { + RemCompDeferred(ent); + return; + } + + args.Cancel(); + } + + private void OnSeeAttempt(Entity ent, ref CanSeeAttemptEvent args) + { + if (ent.Comp.LifeStage <= ComponentLifeStage.Running) + args.Cancel(); + } + + private void OnPointAttempt(Entity ent, ref PointAttemptEvent args) + { + args.Cancel(); + } + + private void OnSlip(Entity ent, ref SlipAttemptEvent args) + { + args.Cancel(); + } + + private void OnConsciousAttempt(Entity ent, ref ConsciousAttemptEvent args) + { + args.Cancelled = true; + } + + private void OnExamined(Entity ent, ref ExaminedEvent args) + { + if (args.IsInDetailsRange) + { + args.PushMarkup(Loc.GetString("sleep-examined", ("target", Identity.Entity(ent, EntityManager)))); + } + } + + private void AddWakeVerb(Entity ent, ref GetVerbsEvent args) + { + if (!args.CanInteract || !args.CanAccess) + return; + + var target = args.Target; + var user = args.User; + AlternativeVerb verb = new() + { + Act = () => + { + TryWakeWithCooldown((ent, ent.Comp), user: user); + }, + Text = Loc.GetString("action-name-wake"), + Priority = 2 + }; + + args.Verbs.Add(verb); + } + + /// + /// When you click on a sleeping person with an empty hand, try to wake them. + /// + private void OnInteractHand(Entity ent, ref InteractHandEvent args) + { + args.Handled = true; + + TryWakeWithCooldown((ent, ent.Comp), args.User); + } + + /// + /// Wake up on taking an instance of damage at least the value of WakeThreshold. + /// + private void OnDamageChanged(Entity ent, ref DamageChangedEvent args) + { + if (!args.DamageIncreased || args.DamageDelta == null) + return; + + if (args.DamageDelta.GetTotal() >= ent.Comp.WakeThreshold + && !HasComp(ent)) + TryWaking((ent, ent.Comp)); + } + + /// + /// In crit, we wake up if we are not being forced to sleep. + /// And, you can't sleep when dead... + /// + private void OnMobStateChanged(Entity ent, ref MobStateChangedEvent args) + { + if (args.NewMobState == MobState.Dead) + { + RemComp(ent); + RemComp(ent); + return; + } + if (TryComp(ent, out var spam)) + _emitSound.SetEnabled((ent, spam), args.NewMobState == MobState.Alive); + } + + private void OnInit(Entity ent, ref ComponentInit args) + { + TrySleeping(ent.Owner); + } + + private void Wake(Entity ent) + { + RemComp(ent); + _actionsSystem.RemoveAction(ent, ent.Comp.WakeAction); + + var ev = new SleepStateChangedEvent(false) + { + TimeSlept = _gameTiming.CurTime - ent.Comp.SleepingSince + }; + RaiseLocalEvent(ent, ref ev); + + _blindableSystem.UpdateIsBlind(ent.Owner); + } + + /// + /// Try sleeping. Only mobs can sleep. + /// + public bool TrySleeping(Entity ent) + { + if (!Resolve(ent, ref ent.Comp, logMissing: false)) + return false; + + var tryingToSleepEvent = new TryingToSleepEvent(ent); + RaiseLocalEvent(ent, ref tryingToSleepEvent); + if (tryingToSleepEvent.Cancelled) + return false; + + EnsureComp(ent); + return true; + } + + /// + /// Tries to wake up , with a cooldown between attempts to prevent spam. + /// + public bool TryWakeWithCooldown(Entity ent, EntityUid? user = null) + { + if (!Resolve(ent, ref ent.Comp, false)) + return false; + + var curTime = _gameTiming.CurTime; + + if (curTime < ent.Comp.CooldownEnd) + return false; + + ent.Comp.CooldownEnd = curTime + ent.Comp.Cooldown; + Dirty(ent, ent.Comp); + return TryWaking(ent, user: user); + } + + /// + /// Try to wake up . + /// + public bool TryWaking(Entity ent, bool force = false, EntityUid? user = null) + { + if (!Resolve(ent, ref ent.Comp, false)) + return false; + + if (!force && HasComp(ent)) + { + if (user != null) + { + _audio.PlayPredicted(ent.Comp.WakeAttemptSound, ent, user); + _popupSystem.PopupClient(Loc.GetString("wake-other-failure", ("target", Identity.Entity(ent, EntityManager))), ent, user, PopupType.SmallCaution); + } + return false; + } + + if (user != null) + { + _audio.PlayPredicted(ent.Comp.WakeAttemptSound, ent, user); + _popupSystem.PopupClient(Loc.GetString("wake-other-success", ("target", Identity.Entity(ent, EntityManager))), ent, user); + } + + Wake((ent, ent.Comp)); + return true; + } +} + + +public sealed partial class SleepActionEvent : InstantActionEvent; + +public sealed partial class WakeActionEvent : InstantActionEvent; + +/// +/// Raised on an entity when they fall asleep or wake up. +/// +[ByRefEvent] +public record struct SleepStateChangedEvent +{ + public bool FellAsleep = false; + + /// + /// The amount of time this entity slept for. Null if is true. + /// + public TimeSpan? TimeSlept; + + public SleepStateChangedEvent(bool fellAsleep) + { + FellAsleep = fellAsleep; + } +} diff --git a/Content.Server/Bed/Components/SnoringComponent.cs b/Content.Shared/Bed/Sleep/SnoringComponent.cs similarity index 54% rename from Content.Server/Bed/Components/SnoringComponent.cs rename to Content.Shared/Bed/Sleep/SnoringComponent.cs index 09f80327ba..2fe92951f0 100644 --- a/Content.Server/Bed/Components/SnoringComponent.cs +++ b/Content.Shared/Bed/Sleep/SnoringComponent.cs @@ -1,9 +1,11 @@ -namespace Content.Server.Bed.Sleep; +using Robust.Shared.GameStates; + +namespace Content.Shared.Bed.Sleep; /// /// This is used for the snoring trait. /// -[RegisterComponent] +[RegisterComponent, NetworkedComponent] public sealed partial class SnoringComponent : Component { diff --git a/Content.Shared/Body/Components/BodyComponent.cs b/Content.Shared/Body/Components/BodyComponent.cs index 481e22150b..4ddfbdf979 100644 --- a/Content.Shared/Body/Components/BodyComponent.cs +++ b/Content.Shared/Body/Components/BodyComponent.cs @@ -41,4 +41,7 @@ public sealed partial class BodyComponent : Component [ViewVariables] [DataField, AutoNetworkedField] public HashSet LegEntities = new(); + + [DataField, AutoNetworkedField] + public bool ThermalVisibility = true; } diff --git a/Content.Shared/CCVar/CCVars.cs b/Content.Shared/CCVar/CCVars.cs index be9224e105..cc6180b1d5 100644 --- a/Content.Shared/CCVar/CCVars.cs +++ b/Content.Shared/CCVar/CCVars.cs @@ -1738,6 +1738,19 @@ public static readonly CVarDef public static readonly CVarDef ArrivalsReturns = CVarDef.Create("shuttle.arrivals_returns", false, CVar.SERVERONLY); + /// + /// Should all players who spawn at arrivals have godmode until they leave the map? + /// + public static readonly CVarDef GodmodeArrivals = + CVarDef.Create("shuttle.godmode_arrivals", false, CVar.SERVERONLY); + + /// + /// If a grid is split then hide any smaller ones under this mass (kg) from the map. + /// This is useful to avoid split grids spamming out labels. + /// + public static readonly CVarDef HideSplitGridsUnder = + CVarDef.Create("shuttle.hide_split_grids_under", 30, CVar.SERVERONLY); + /// /// Whether to automatically spawn escape shuttles. /// diff --git a/Content.Shared/Climbing/Systems/ClimbSystem.cs b/Content.Shared/Climbing/Systems/ClimbSystem.cs index 722d97213f..fc5a43cf8a 100644 --- a/Content.Shared/Climbing/Systems/ClimbSystem.cs +++ b/Content.Shared/Climbing/Systems/ClimbSystem.cs @@ -47,6 +47,7 @@ public sealed partial class ClimbSystem : VirtualController private EntityQuery _fixturesQuery; private EntityQuery _xformQuery; + private EntityQuery _climbableQuery; public override void Initialize() { @@ -373,6 +374,26 @@ private void OnClimbEndCollide(EntityUid uid, ClimbingComponent component, ref E return; } + foreach (var contact in args.OurFixture.Contacts.Values) + { + if (!contact.IsTouching) + continue; + + var otherEnt = contact.OtherEnt(uid); + var (otherFixtureId, otherFixture) = contact.OtherFixture(uid); + + // TODO: Remove this on engine. + if (args.OtherEntity == otherEnt && args.OtherFixtureId == otherFixtureId) + continue; + + if (otherFixture is { Hard: true } && + _climbableQuery.HasComp(otherEnt)) + { + return; + } + } + + // TODO: Is this even needed anymore? foreach (var otherFixture in args.OurFixture.Contacts.Keys) { // If it's the other fixture then ignore em diff --git a/Content.Shared/Clothing/Loadouts/Systems/SharedLoadoutSystem.cs b/Content.Shared/Clothing/Loadouts/Systems/SharedLoadoutSystem.cs index e759c904af..865908c706 100644 --- a/Content.Shared/Clothing/Loadouts/Systems/SharedLoadoutSystem.cs +++ b/Content.Shared/Clothing/Loadouts/Systems/SharedLoadoutSystem.cs @@ -75,6 +75,8 @@ private void OnMapInit(EntityUid uid, LoadoutComponent component, MapInitEvent a var failedLoadouts = new List(); var allLoadouts = new List<(EntityUid, LoadoutPreference, int)>(); heirlooms = new(); + if (!job.SpawnLoadout) + return (failedLoadouts, allLoadouts); foreach (var loadout in profile.LoadoutPreferences) { diff --git a/Content.Shared/Configurable/ConfigurationComponent.cs b/Content.Shared/Configurable/ConfigurationComponent.cs index 63a0dfe95a..621871af3c 100644 --- a/Content.Shared/Configurable/ConfigurationComponent.cs +++ b/Content.Shared/Configurable/ConfigurationComponent.cs @@ -1,5 +1,6 @@ using System.Text.RegularExpressions; using Content.Shared.Tools; +using Content.Shared.Tools.Systems; using Robust.Shared.GameStates; using Robust.Shared.Serialization; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; @@ -13,7 +14,7 @@ public sealed partial class ConfigurationComponent : Component public Dictionary Config = new(); [DataField("qualityNeeded", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string QualityNeeded = "Pulsing"; + public string QualityNeeded = SharedToolSystem.PulseQuality; [DataField("validation")] public Regex Validation = new("^[a-zA-Z0-9 ]*$", RegexOptions.Compiled); diff --git a/Content.Shared/Containers/ContainerCompComponent.cs b/Content.Shared/Containers/ContainerCompComponent.cs new file mode 100644 index 0000000000..b1415e0d8b --- /dev/null +++ b/Content.Shared/Containers/ContainerCompComponent.cs @@ -0,0 +1,17 @@ +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared.Containers; + +/// +/// Applies container changes whenever an entity is inserted into the specified container on this entity. +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class ContainerCompComponent : Component +{ + [DataField(required: true)] + public EntProtoId Proto; + + [DataField(required: true)] + public string Container = string.Empty; +} diff --git a/Content.Shared/Containers/ContainerCompSystem.cs b/Content.Shared/Containers/ContainerCompSystem.cs new file mode 100644 index 0000000000..1e1983a331 --- /dev/null +++ b/Content.Shared/Containers/ContainerCompSystem.cs @@ -0,0 +1,44 @@ +using Robust.Shared.Containers; +using Robust.Shared.Prototypes; + +namespace Content.Shared.Containers; + +/// +/// Applies / removes an entity prototype from a child entity when it's inserted into a container. +/// +public sealed class ContainerCompSystem : EntitySystem +{ + [Dependency] private readonly IPrototypeManager _proto = default!; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnConInsert); + SubscribeLocalEvent(OnConRemove); + } + + private void OnConRemove(Entity ent, ref EntRemovedFromContainerMessage args) + { + if (args.Container.ID != ent.Comp.Container) + return; + + if (_proto.TryIndex(ent.Comp.Container, out var entProto)) + { + foreach (var entry in entProto.Components.Values) + { + RemComp(args.Entity, entry.Component); + } + } + } + + private void OnConInsert(Entity ent, ref EntInsertedIntoContainerMessage args) + { + if (args.Container.ID != ent.Comp.Container) + return; + + if (_proto.TryIndex(ent.Comp.Proto, out var entProto)) + { + EntityManager.AddComponents(args.Entity, entProto.Components); + } + } +} diff --git a/Content.Shared/Containers/ItemSlot/ItemSlotsLockComponent.cs b/Content.Shared/Containers/ItemSlot/ItemSlotsLockComponent.cs new file mode 100644 index 0000000000..0d8901028d --- /dev/null +++ b/Content.Shared/Containers/ItemSlot/ItemSlotsLockComponent.cs @@ -0,0 +1,13 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Containers.ItemSlots; + +/// +/// Updates the relevant ItemSlots locks based on +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class ItemSlotsLockComponent : Component +{ + [DataField(required: true)] + public List Slots = new(); +} diff --git a/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.Lock.cs b/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.Lock.cs new file mode 100644 index 0000000000..ee5178df95 --- /dev/null +++ b/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.Lock.cs @@ -0,0 +1,36 @@ +using Content.Shared.Lock; + +namespace Content.Shared.Containers.ItemSlots; + +public sealed partial class ItemSlotsSystem +{ + private void InitializeLock() + { + SubscribeLocalEvent(OnLockMapInit); + SubscribeLocalEvent(OnLockToggled); + } + + private void OnLockMapInit(Entity ent, ref MapInitEvent args) + { + if (!TryComp(ent.Owner, out LockComponent? lockComp)) + return; + + UpdateLocks(ent, lockComp.Locked); + } + + private void OnLockToggled(Entity ent, ref LockToggledEvent args) + { + UpdateLocks(ent, args.Locked); + } + + private void UpdateLocks(Entity ent, bool value) + { + foreach (var slot in ent.Comp.Slots) + { + if (!TryGetSlot(ent.Owner, slot, out var itemSlot)) + continue; + + SetLock(ent.Owner, itemSlot, value); + } + } +} diff --git a/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs b/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs index c8745d17d4..4fe49a6382 100644 --- a/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs +++ b/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs @@ -26,7 +26,7 @@ namespace Content.Shared.Containers.ItemSlots /// Note when using popups on entities with many slots with InsertOnInteract, EjectOnInteract or EjectOnUse: /// A single use will try to insert to/eject from every slot and generate a popup for each that fails. /// - public sealed class ItemSlotsSystem : EntitySystem + public sealed partial class ItemSlotsSystem : EntitySystem { [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; [Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!; @@ -43,6 +43,8 @@ public override void Initialize() { base.Initialize(); + InitializeLock(); + SubscribeLocalEvent(OnMapInit); SubscribeLocalEvent(Oninitialize); diff --git a/Content.Shared/Cuffs/SharedCuffableSystem.cs b/Content.Shared/Cuffs/SharedCuffableSystem.cs index 1c8e2ef2b0..dbc2ba06c9 100644 --- a/Content.Shared/Cuffs/SharedCuffableSystem.cs +++ b/Content.Shared/Cuffs/SharedCuffableSystem.cs @@ -84,7 +84,7 @@ public override void Initialize() SubscribeLocalEvent(CheckAct); SubscribeLocalEvent(CheckAct); SubscribeLocalEvent(CheckAct); - SubscribeLocalEvent(CheckAct); + SubscribeLocalEvent(CheckInteract); SubscribeLocalEvent(OnCuffAfterInteract); SubscribeLocalEvent(OnCuffMeleeHit); @@ -92,6 +92,12 @@ public override void Initialize() SubscribeLocalEvent(OnCuffVirtualItemDeleted); } + private void CheckInteract(Entity ent, ref InteractionAttemptEvent args) + { + if (!ent.Comp.CanStillInteract) + args.Cancelled = true; + } + private void OnUncuffAttempt(ref UncuffAttemptEvent args) { if (args.Cancelled) diff --git a/Content.Shared/Dataset/LocalizedDatasetPrototype.cs b/Content.Shared/Dataset/LocalizedDatasetPrototype.cs new file mode 100644 index 0000000000..8be9967e30 --- /dev/null +++ b/Content.Shared/Dataset/LocalizedDatasetPrototype.cs @@ -0,0 +1,94 @@ +using System.Collections; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization; + +namespace Content.Shared.Dataset; + +/// +/// A variant of intended to specify a sequence of LocId strings +/// without having to copy-paste a ton of LocId strings into the YAML. +/// +[Prototype] +public sealed partial class LocalizedDatasetPrototype : IPrototype +{ + /// + /// Identifier for this prototype. + /// + [ViewVariables] + [IdDataField] + public string ID { get; private set; } = default!; + + /// + /// Collection of LocId strings. + /// + [DataField] + public LocalizedDatasetValues Values { get; private set; } = []; +} + +[Serializable, NetSerializable] +[DataDefinition] +public sealed partial class LocalizedDatasetValues : IReadOnlyList +{ + /// + /// String prepended to the index number to generate each LocId string. + /// For example, a prefix of tips-dataset- will generate tips-dataset-1, + /// tips-dataset-2, etc. + /// + [DataField(required: true)] + public string Prefix { get; private set; } = default!; + + /// + /// How many values are in the dataset. + /// + [DataField(required: true)] + public int Count { get; private set; } + + public string this[int index] + { + get + { + if (index > Count || index < 0) + throw new IndexOutOfRangeException(); + return Prefix + index; + } + } + + public IEnumerator GetEnumerator() + { + return new Enumerator(this); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public sealed class Enumerator : IEnumerator + { + private int _index = 0; // Whee, 1-indexing + + private readonly LocalizedDatasetValues _values; + + public Enumerator(LocalizedDatasetValues values) + { + _values = values; + } + + public string Current => _values.Prefix + _index; + + object IEnumerator.Current => Current; + + public void Dispose() { } + + public bool MoveNext() + { + _index++; + return _index <= _values.Count; + } + + public void Reset() + { + _index = 0; + } + } +} diff --git a/Content.Shared/Doors/AirlockWireStatus.cs b/Content.Shared/Doors/AirlockWireStatus.cs index a50ee2c88e..d3fa15ed1b 100644 --- a/Content.Shared/Doors/AirlockWireStatus.cs +++ b/Content.Shared/Doors/AirlockWireStatus.cs @@ -8,7 +8,7 @@ public enum AirlockWireStatus PowerIndicator, BoltIndicator, BoltLightIndicator, - AIControlIndicator, + AiControlIndicator, TimingIndicator, SafetyIndicator, } diff --git a/Content.Shared/Examine/ExamineSystemShared.cs b/Content.Shared/Examine/ExamineSystemShared.cs index 397a8f7448..a1c30a2bd0 100644 --- a/Content.Shared/Examine/ExamineSystemShared.cs +++ b/Content.Shared/Examine/ExamineSystemShared.cs @@ -109,12 +109,25 @@ public virtual bool CanExamine(EntityUid examiner, MapCoordinates target, Ignore if (EntityManager.GetComponent(examiner).MapID != target.MapId) return false; - return InRangeUnOccluded( - _transform.GetMapCoordinates(examiner), - target, - GetExaminerRange(examiner), - predicate: predicate, - ignoreInsideBlocker: true); + // Do target InRangeUnoccluded which has different checks. + if (examined != null) + { + return InRangeUnOccluded( + examiner, + examined.Value, + GetExaminerRange(examiner), + predicate: predicate, + ignoreInsideBlocker: true); + } + else + { + return InRangeUnOccluded( + examiner, + target, + GetExaminerRange(examiner), + predicate: predicate, + ignoreInsideBlocker: true); + } } /// @@ -209,7 +222,14 @@ public bool InRangeUnOccluded(MapCoordinates origin, MapCoordinates othe public bool InRangeUnOccluded(EntityUid origin, EntityUid other, float range = ExamineRange, Ignored? predicate = null, bool ignoreInsideBlocker = true) { - var entMan = IoCManager.Resolve(); + var ev = new InRangeOverrideEvent(origin, other); + RaiseLocalEvent(origin, ref ev); + + if (ev.Handled) + { + return ev.InRange; + } + var originPos = _transform.GetMapCoordinates(origin); var otherPos = _transform.GetMapCoordinates(other); diff --git a/Content.Shared/Ghost/SharedGhostSystem.cs b/Content.Shared/Ghost/SharedGhostSystem.cs index 6f7370fe58..091775b6c2 100644 --- a/Content.Shared/Ghost/SharedGhostSystem.cs +++ b/Content.Shared/Ghost/SharedGhostSystem.cs @@ -20,13 +20,19 @@ public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnAttempt); - SubscribeLocalEvent(OnAttempt); + SubscribeLocalEvent(OnAttemptInteract); SubscribeLocalEvent(OnAttempt); SubscribeLocalEvent(OnAttempt); SubscribeLocalEvent(OnAttempt); SubscribeLocalEvent(OnAttempt); } + private void OnAttemptInteract(Entity ent, ref InteractionAttemptEvent args) + { + if (!ent.Comp.CanGhostInteract) + args.Cancelled = true; + } + private void OnAttempt(EntityUid uid, GhostComponent component, CancellableEntityEventArgs args) { if (!component.CanGhostInteract) diff --git a/Content.Shared/Interaction/Components/BlockMovementComponent.cs b/Content.Shared/Interaction/Components/BlockMovementComponent.cs index e308e84960..2125f16efe 100644 --- a/Content.Shared/Interaction/Components/BlockMovementComponent.cs +++ b/Content.Shared/Interaction/Components/BlockMovementComponent.cs @@ -1,4 +1,4 @@ -using Robust.Shared.GameStates; +using Robust.Shared.GameStates; namespace Content.Shared.Interaction.Components; @@ -8,5 +8,6 @@ namespace Content.Shared.Interaction.Components; [RegisterComponent, NetworkedComponent] public sealed partial class BlockMovementComponent : Component { - + [DataField] + public bool BlockInteraction = true; } diff --git a/Content.Shared/Interaction/Events/InteractionAttemptEvent.cs b/Content.Shared/Interaction/Events/InteractionAttemptEvent.cs index 0024811c36..a04c053635 100644 --- a/Content.Shared/Interaction/Events/InteractionAttemptEvent.cs +++ b/Content.Shared/Interaction/Events/InteractionAttemptEvent.cs @@ -3,39 +3,33 @@ /// /// Event raised directed at a user to see if they can perform a generic interaction. /// - public sealed class InteractionAttemptEvent : CancellableEntityEventArgs + [ByRefEvent] + public struct InteractionAttemptEvent(EntityUid uid, EntityUid? target) { - public InteractionAttemptEvent(EntityUid uid, EntityUid? target) - { - Uid = uid; - Target = target; - } - - public EntityUid Uid { get; } - public EntityUid? Target { get; } + public bool Cancelled; + public readonly EntityUid Uid = uid; + public readonly EntityUid? Target = target; } /// /// Raised to determine whether an entity is conscious to perform an action. /// - public sealed class ConsciousAttemptEvent(EntityUid Uid) : CancellableEntityEventArgs + [ByRefEvent] + public struct ConsciousAttemptEvent(EntityUid uid) { - public EntityUid Uid { get; } = Uid; + public bool Cancelled; + public readonly EntityUid Uid = uid; } /// /// Event raised directed at the target entity of an interaction to see if the user is allowed to perform some /// generic interaction. /// - public sealed class GettingInteractedWithAttemptEvent : CancellableEntityEventArgs + [ByRefEvent] + public struct GettingInteractedWithAttemptEvent(EntityUid uid, EntityUid? target) { - public GettingInteractedWithAttemptEvent(EntityUid uid, EntityUid? target) - { - Uid = uid; - Target = target; - } - - public EntityUid Uid { get; } - public EntityUid? Target { get; } + public bool Cancelled; + public readonly EntityUid Uid = uid; + public readonly EntityUid? Target = target; } } diff --git a/Content.Shared/Interaction/SharedInteractionSystem.Blocking.cs b/Content.Shared/Interaction/SharedInteractionSystem.Blocking.cs index 9a84789adf..52c40477c9 100644 --- a/Content.Shared/Interaction/SharedInteractionSystem.Blocking.cs +++ b/Content.Shared/Interaction/SharedInteractionSystem.Blocking.cs @@ -6,6 +6,7 @@ namespace Content.Shared.Interaction; +// TODO deduplicate with AdminFrozenComponent /// /// Handles , which prevents various /// kinds of movement and interactions when attached to an entity. @@ -16,7 +17,7 @@ public void InitializeBlocking() { SubscribeLocalEvent(OnMoveAttempt); SubscribeLocalEvent(CancelEvent); - SubscribeLocalEvent(CancelEvent); + SubscribeLocalEvent(CancelInteractEvent); SubscribeLocalEvent(CancelEvent); SubscribeLocalEvent(CancelEvent); SubscribeLocalEvent(CancelEvent); @@ -25,6 +26,12 @@ public void InitializeBlocking() SubscribeLocalEvent(OnBlockingShutdown); } + private void CancelInteractEvent(Entity ent, ref InteractionAttemptEvent args) + { + if (ent.Comp.BlockInteraction) + args.Cancelled = true; + } + private void OnMoveAttempt(EntityUid uid, BlockMovementComponent component, UpdateCanMoveEvent args) { if (component.LifeStage > ComponentLifeStage.Running) diff --git a/Content.Shared/Interaction/SharedInteractionSystem.cs b/Content.Shared/Interaction/SharedInteractionSystem.cs index 5815b93b2b..decac5fcf6 100644 --- a/Content.Shared/Interaction/SharedInteractionSystem.cs +++ b/Content.Shared/Interaction/SharedInteractionSystem.cs @@ -20,6 +20,7 @@ using Content.Shared.Physics; using Content.Shared.Players.RateLimiting; using Content.Shared.Popups; +using Content.Shared.Silicons.StationAi; using Content.Shared.Storage; using Content.Shared.Strip; using Content.Shared.Tag; @@ -83,7 +84,6 @@ public abstract partial class SharedInteractionSystem : EntitySystem private EntityQuery _wallMountQuery; private EntityQuery _delayQuery; private EntityQuery _uiQuery; - private EntityQuery _complexInteractionQuery; private const CollisionGroup InRangeUnobstructedMask = CollisionGroup.Impassable | CollisionGroup.InteractImpassable; @@ -106,7 +106,6 @@ public override void Initialize() _wallMountQuery = GetEntityQuery(); _delayQuery = GetEntityQuery(); _uiQuery = GetEntityQuery(); - _complexInteractionQuery = GetEntityQuery(); SubscribeLocalEvent(HandleUserInterfaceRangeCheck); SubscribeLocalEvent(OnBoundInterfaceInteractAttempt); @@ -191,7 +190,7 @@ private void OnBoundInterfaceInteractAttempt(BoundUserInterfaceMessageAttempt ev return; } - if (uiComp.RequireHands && !_handsQuery.HasComp(ev.Actor)) + if (uiComp.RequiresComplex && !_actionBlockerSystem.CanComplexInteract(ev.Actor)) ev.Cancel(); } @@ -477,10 +476,7 @@ private bool IsDeleted(EntityUid? uid) public void InteractHand(EntityUid user, EntityUid target) { - if (IsDeleted(user) || IsDeleted(target)) - return; - - var complexInteractions = SupportsComplexInteractions(user); + var complexInteractions = _actionBlockerSystem.CanComplexInteract(user); if (!complexInteractions) { InteractionActivate(user, @@ -666,6 +662,14 @@ public bool InRangeUnobstructed( if (!Resolve(other, ref other.Comp)) return false; + var ev = new InRangeOverrideEvent(origin, other); + RaiseLocalEvent(origin, ref ev); + + if (ev.Handled) + { + return ev.InRange; + } + return InRangeUnobstructed(origin, other, other.Comp.Coordinates, @@ -1195,7 +1199,7 @@ public bool AltInteract(EntityUid user, EntityUid target) // Get list of alt-interact verbs var verbs = _verbSystem.GetLocalVerbs(target, user, typeof(AlternativeVerb)); - if (!verbs.Any()) + if (verbs.Count == 0) return false; _verbSystem.ExecuteVerb(verbs.First(), user, target); @@ -1252,6 +1256,13 @@ public bool InRangeAndAccessible( /// public bool IsAccessible(Entity user, Entity target) { + var ev = new AccessibleOverrideEvent(user, target); + + RaiseLocalEvent(user, ref ev); + + if (ev.Handled) + return ev.Accessible; + if (_containerSystem.IsInSameOrParentContainer(user, target, out _, out var container)) return true; @@ -1403,13 +1414,10 @@ public bool TryGetUsedEntity(EntityUid user, [NotNullWhen(true)] out EntityUid? return ev.Handled; } - /// - /// Checks if a given entity is able to do specific complex interactions. - /// This is used to gate manipulation to general humanoids. If a mouse shouldn't be able to do something, then it's complex. - /// + [Obsolete("Use ActionBlockerSystem")] public bool SupportsComplexInteractions(EntityUid user) { - return _complexInteractionQuery.HasComp(user); + return _actionBlockerSystem.CanComplexInteract(user); } } @@ -1448,17 +1456,38 @@ public record struct GetUsedEntityEvent() }; /// - /// Raised directed by-ref on an item and a user to determine if interactions can occur. + /// Raised directed by-ref on an item to determine if hand interactions should go through. + /// Defaults to allowing hand interactions to go through. Cancel to force the item to be attacked instead. /// /// Whether the hand interaction should be cancelled. [ByRefEvent] - public record struct AttemptUseInteractEvent(EntityUid User, EntityUid Used, bool Cancelled = false); + public record struct CombatModeShouldHandInteractEvent(bool Cancelled = false); /// - /// Raised directed by-ref on an item to determine if hand interactions should go through. - /// Defaults to allowing hand interactions to go through. Cancel to force the item to be attacked instead. + /// Override event raised directed on the user to say the target is accessible. /// - /// Whether the hand interaction should be cancelled. + /// + /// [ByRefEvent] - public record struct CombatModeShouldHandInteractEvent(bool Cancelled = false); -} \ No newline at end of file + public record struct AccessibleOverrideEvent(EntityUid User, EntityUid Target) + { + public readonly EntityUid User = User; + public readonly EntityUid Target = Target; + + public bool Handled; + public bool Accessible = false; + } + + /// + /// Override event raised directed on a user to check InRangeUnoccluded AND InRangeUnobstructed to the target if you require custom logic. + /// + [ByRefEvent] + public record struct InRangeOverrideEvent(EntityUid User, EntityUid Target) + { + public readonly EntityUid User = User; + public readonly EntityUid Target = Target; + + public bool Handled; + public bool InRange = false; + } +} diff --git a/Content.Shared/Inventory/InventorySystem.Relay.cs b/Content.Shared/Inventory/InventorySystem.Relay.cs index 9d263a6098..4375f1ab19 100644 --- a/Content.Shared/Inventory/InventorySystem.Relay.cs +++ b/Content.Shared/Inventory/InventorySystem.Relay.cs @@ -20,6 +20,8 @@ using Content.Shared.Verbs; using Content.Shared.Weapons.Ranged.Events; using Content.Shared.Chat; +using Content.Shared.Overlays.Switchable; + namespace Content.Shared.Inventory; @@ -63,6 +65,8 @@ public void InitializeRelay() SubscribeLocalEvent>(RelayInventoryEvent); SubscribeLocalEvent>(RelayInventoryEvent); SubscribeLocalEvent>(RelayInventoryEvent); + SubscribeLocalEvent>(RelayInventoryEvent); + SubscribeLocalEvent>(RelayInventoryEvent); SubscribeLocalEvent>(OnGetEquipmentVerbs); } diff --git a/Content.Shared/Item/ItemToggle/Components/ItemToggleComponent.cs b/Content.Shared/Item/ItemToggle/Components/ItemToggleComponent.cs index 46249fdd0d..47edec135d 100644 --- a/Content.Shared/Item/ItemToggle/Components/ItemToggleComponent.cs +++ b/Content.Shared/Item/ItemToggle/Components/ItemToggleComponent.cs @@ -19,6 +19,12 @@ public sealed partial class ItemToggleComponent : Component [DataField, AutoNetworkedField] public bool Activated = false; + /// + /// Can the entity be activated in the world. + /// + [DataField] + public bool OnActivate = true; + /// /// If this is set to false then the item can't be toggled by pressing Z. /// Use another system to do it then. @@ -52,12 +58,6 @@ public sealed partial class ItemToggleComponent : Component /// [ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField] public SoundSpecifier? SoundFailToActivate; - - /// - /// Whether or not to toggle the entity's lights on or off. - /// - [ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField] - public bool ToggleLight = true; } /// diff --git a/Content.Shared/Item/ItemToggle/ItemToggleSystem.cs b/Content.Shared/Item/ItemToggle/ItemToggleSystem.cs index c4e4150659..044a1109a1 100644 --- a/Content.Shared/Item/ItemToggle/ItemToggleSystem.cs +++ b/Content.Shared/Item/ItemToggle/ItemToggleSystem.cs @@ -1,3 +1,4 @@ +using Content.Shared.Interaction; using Content.Shared.Interaction.Events; using Content.Shared.Item.ItemToggle.Components; using Content.Shared.Popups; @@ -5,6 +6,7 @@ using Content.Shared.Temperature; using Content.Shared.Throwing; using Content.Shared.Toggleable; +using Content.Shared.Verbs; using Content.Shared.Wieldable; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; @@ -23,7 +25,6 @@ public sealed class ItemToggleSystem : EntitySystem [Dependency] private readonly INetManager _netManager = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; - [Dependency] private readonly SharedPointLightSystem _light = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; public override void Initialize() @@ -35,6 +36,8 @@ public override void Initialize() SubscribeLocalEvent(TurnOffOnUnwielded); SubscribeLocalEvent(TurnOnOnWielded); SubscribeLocalEvent(OnUseInHand); + SubscribeLocalEvent>(OnActivateVerb); + SubscribeLocalEvent(OnActivate); SubscribeLocalEvent(OnIsHotEvent); @@ -67,6 +70,32 @@ private void OnUseInHand(Entity ent, ref UseInHandEvent arg Toggle((ent, ent.Comp), args.User, predicted: ent.Comp.Predictable); } + private void OnActivateVerb(Entity ent, ref GetVerbsEvent args) + { + if (!args.CanAccess || !args.CanInteract) + return; + + var user = args.User; + + args.Verbs.Add(new ActivationVerb() + { + Text = !ent.Comp.Activated ? Loc.GetString("item-toggle-activate") : Loc.GetString("item-toggle-deactivate"), + Act = () => + { + Toggle((ent.Owner, ent.Comp), user, predicted: ent.Comp.Predictable); + } + }); + } + + private void OnActivate(Entity ent, ref ActivateInWorldEvent args) + { + if (args.Handled || !ent.Comp.OnActivate) + return; + + args.Handled = true; + Toggle((ent.Owner, ent.Comp), args.User, predicted: ent.Comp.Predictable); + } + /// /// Used when an item is attempted to be toggled. /// Sets its state to the opposite of what it is. @@ -204,16 +233,7 @@ private void UpdateVisuals(Entity ent) if (TryComp(ent, out AppearanceComponent? appearance)) { _appearance.SetData(ent, ToggleVisuals.Toggled, ent.Comp.Activated, appearance); - - if (ent.Comp.ToggleLight) - _appearance.SetData(ent, ToggleableLightVisuals.Enabled, ent.Comp.Activated, appearance); } - - if (!ent.Comp.ToggleLight) - return; - - if (_light.TryGetLight(ent, out var light)) - _light.SetEnabled(ent, ent.Comp.Activated, light); } /// diff --git a/Content.Shared/Light/Components/ItemTogglePointLightComponent.cs b/Content.Shared/Light/Components/ItemTogglePointLightComponent.cs new file mode 100644 index 0000000000..6ac1bf236d --- /dev/null +++ b/Content.Shared/Light/Components/ItemTogglePointLightComponent.cs @@ -0,0 +1,12 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Light.Components; + +/// +/// Toggles point light on an entity whenever ItemToggle hits. +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class ItemTogglePointLightComponent : Component +{ + +} diff --git a/Content.Shared/Light/Components/LightOnCollideColliderComponent.cs b/Content.Shared/Light/Components/LightOnCollideColliderComponent.cs new file mode 100644 index 0000000000..39be05a148 --- /dev/null +++ b/Content.Shared/Light/Components/LightOnCollideColliderComponent.cs @@ -0,0 +1,13 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Light.Components; + +/// +/// Can activate when collided with. +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class LightOnCollideColliderComponent : Component +{ + [DataField] + public string FixtureId = "lightTrigger"; +} diff --git a/Content.Shared/Light/Components/LightOnCollideComponent.cs b/Content.Shared/Light/Components/LightOnCollideComponent.cs new file mode 100644 index 0000000000..c3b4bd7396 --- /dev/null +++ b/Content.Shared/Light/Components/LightOnCollideComponent.cs @@ -0,0 +1,11 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Light.Components; + +/// +/// Enables / disables pointlight whenever entities are contacting with it +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class LightOnCollideComponent : Component +{ +} diff --git a/Content.Shared/Light/EntitySystems/ItemTogglePointLightSystem.cs b/Content.Shared/Light/EntitySystems/ItemTogglePointLightSystem.cs new file mode 100644 index 0000000000..7030c538c1 --- /dev/null +++ b/Content.Shared/Light/EntitySystems/ItemTogglePointLightSystem.cs @@ -0,0 +1,29 @@ +using Content.Shared.Item.ItemToggle.Components; +using Content.Shared.Toggleable; +using ItemTogglePointLightComponent = Content.Shared.Light.Components.ItemTogglePointLightComponent; + +namespace Content.Shared.Light.EntitySystems; + +/// +/// Handles ItemToggle for PointLight +/// +public sealed class ItemTogglePointLightSystem : EntitySystem +{ + [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + [Dependency] private readonly SharedPointLightSystem _light = default!; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnLightToggled); + } + + private void OnLightToggled(Entity ent, ref ItemToggledEvent args) + { + if (!_light.TryGetLight(ent.Owner, out var light)) + return; + + _appearance.SetData(ent, ToggleableLightVisuals.Enabled, args.Activated); + _light.SetEnabled(ent.Owner, args.Activated, comp: light); + } +} diff --git a/Content.Shared/Light/EntitySystems/LightCollideSystem.cs b/Content.Shared/Light/EntitySystems/LightCollideSystem.cs new file mode 100644 index 0000000000..f09ae6824e --- /dev/null +++ b/Content.Shared/Light/EntitySystems/LightCollideSystem.cs @@ -0,0 +1,82 @@ +using Content.Shared.Light.Components; +using Robust.Shared.Physics.Events; +using Robust.Shared.Physics.Systems; + +namespace Content.Shared.Light.EntitySystems; + +public sealed class LightCollideSystem : EntitySystem +{ + [Dependency] private readonly SharedPhysicsSystem _physics = default!; + [Dependency] private readonly SlimPoweredLightSystem _lights = default!; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnPreventCollide); + SubscribeLocalEvent(OnStart); + SubscribeLocalEvent(OnEnd); + + SubscribeLocalEvent(OnCollideShutdown); + } + + private void OnCollideShutdown(Entity ent, ref ComponentShutdown args) + { + // TODO: Check this on the event. + if (TerminatingOrDeleted(ent.Owner)) + return; + + // Regenerate contacts for everything we were colliding with. + var contacts = _physics.GetContacts(ent.Owner); + + while (contacts.MoveNext(out var contact)) + { + if (!contact.IsTouching) + continue; + + var other = contact.OtherEnt(ent.Owner); + + if (HasComp(other)) + { + _physics.RegenerateContacts(other); + } + } + } + + // You may be wondering what de fok this is doing here. + // At the moment there's no easy way to do collision whitelists based on components. + private void OnPreventCollide(Entity ent, ref PreventCollideEvent args) + { + if (!HasComp(args.OtherEntity)) + { + args.Cancelled = true; + } + } + + private void OnEnd(Entity ent, ref EndCollideEvent args) + { + if (args.OurFixtureId != ent.Comp.FixtureId) + return; + + if (!HasComp(args.OtherEntity)) + return; + + // TODO: Engine bug IsTouching box2d yay. + var contacts = _physics.GetTouchingContacts(args.OtherEntity) - 1; + + if (contacts > 0) + return; + + _lights.SetEnabled(args.OtherEntity, false); + } + + private void OnStart(Entity ent, ref StartCollideEvent args) + { + if (args.OurFixtureId != ent.Comp.FixtureId) + return; + + if (!HasComp(args.OtherEntity)) + return; + + _lights.SetEnabled(args.OtherEntity, true); + } +} diff --git a/Content.Shared/Light/EntitySystems/SlimPoweredLightSystem.cs b/Content.Shared/Light/EntitySystems/SlimPoweredLightSystem.cs index 6d984ed19a..4cf9b25dad 100644 --- a/Content.Shared/Light/EntitySystems/SlimPoweredLightSystem.cs +++ b/Content.Shared/Light/EntitySystems/SlimPoweredLightSystem.cs @@ -1,6 +1,5 @@ using Content.Shared.Light.Components; using Content.Shared.Power; -using Content.Shared.Power.Components; using Content.Shared.Power.EntitySystems; namespace Content.Shared.Light.EntitySystems; diff --git a/Content.Shared/Mind/SharedMindSystem.cs b/Content.Shared/Mind/SharedMindSystem.cs index 2b83f05190..994d230e8b 100644 --- a/Content.Shared/Mind/SharedMindSystem.cs +++ b/Content.Shared/Mind/SharedMindSystem.cs @@ -315,6 +315,10 @@ public virtual void TransferTo(EntityUid mindId, EntityUid? entity, bool ghostCh { } + public virtual void ControlMob(EntityUid user, EntityUid target) {} + + public virtual void ControlMob(NetUserId user, EntityUid target) {} + /// /// Tries to create and add an objective from its prototype id. /// diff --git a/Content.Shared/Mobs/Systems/MobStateSystem.Subscribers.cs b/Content.Shared/Mobs/Systems/MobStateSystem.Subscribers.cs index a467c89364..f5acced4db 100644 --- a/Content.Shared/Mobs/Systems/MobStateSystem.Subscribers.cs +++ b/Content.Shared/Mobs/Systems/MobStateSystem.Subscribers.cs @@ -33,7 +33,7 @@ private void SubscribeEvents() SubscribeLocalEvent(OnDirectionAttempt); SubscribeLocalEvent(CheckAct); SubscribeLocalEvent(CheckAct); - SubscribeLocalEvent(CheckAct); + SubscribeLocalEvent(CheckConcious); SubscribeLocalEvent(CheckAct); SubscribeLocalEvent(OnSpeakAttempt); SubscribeLocalEvent(OnEquipAttempt); @@ -91,6 +91,17 @@ private void OnUnbuckleAttempt(Entity ent, ref UnbuckleAttemp args.Cancelled = true; } + private void CheckConcious(Entity ent, ref ConsciousAttemptEvent args) + { + switch (ent.Comp.CurrentState) + { + case MobState.Dead: + case MobState.Critical: + args.Cancelled = true; + break; + } + } + private void OnStateExitSubscribers(EntityUid target, MobStateComponent component, MobState state) { switch (state) diff --git a/Content.Shared/Nyanotrasen/Interaction/NoNormalInteractionSystem.cs b/Content.Shared/Nyanotrasen/Interaction/NoNormalInteractionSystem.cs index 0f66dff309..9b7a6954de 100644 --- a/Content.Shared/Nyanotrasen/Interaction/NoNormalInteractionSystem.cs +++ b/Content.Shared/Nyanotrasen/Interaction/NoNormalInteractionSystem.cs @@ -12,7 +12,7 @@ public override void Initialize() private void OnInteractionAttempt(EntityUid uid, NoNormalInteractionComponent component, InteractionAttemptEvent args) { - args.Cancel(); + args.Cancelled = true; } } } diff --git a/Content.Shared/Nyanotrasen/Item/PseudoItem/SharedPseudoItemSystem.cs b/Content.Shared/Nyanotrasen/Item/PseudoItem/SharedPseudoItemSystem.cs index 7dc8578117..448311471f 100644 --- a/Content.Shared/Nyanotrasen/Item/PseudoItem/SharedPseudoItemSystem.cs +++ b/Content.Shared/Nyanotrasen/Item/PseudoItem/SharedPseudoItemSystem.cs @@ -143,7 +143,7 @@ private void OnInsertAttempt(EntityUid uid, PseudoItemComponent component, private void OnInteractAttempt(EntityUid uid, PseudoItemComponent component, InteractionAttemptEvent args) { if (args.Uid == args.Target && component.Active) - args.Cancel(); + args.Cancelled = true; } private void OnDoAfter(EntityUid uid, PseudoItemComponent component, DoAfterEvent args) diff --git a/Content.Shared/Overlays/BaseOverlayComponent.cs b/Content.Shared/Overlays/BaseOverlayComponent.cs new file mode 100644 index 0000000000..fe6a5c763e --- /dev/null +++ b/Content.Shared/Overlays/BaseOverlayComponent.cs @@ -0,0 +1,16 @@ +namespace Content.Shared.Overlays; + +public abstract partial class BaseOverlayComponent : Component +{ + [DataField, ViewVariables(VVAccess.ReadOnly)] + public virtual Vector3 Tint { get; set; } = new(0.3f, 0.3f, 0.3f); + + [DataField, ViewVariables(VVAccess.ReadOnly)] + public virtual float Strength { get; set; } = 2f; + + [DataField, ViewVariables(VVAccess.ReadOnly)] + public virtual float Noise { get; set; } = 0.5f; + + [DataField, ViewVariables(VVAccess.ReadOnly)] + public virtual Color Color { get; set; } = Color.White; +} diff --git a/Content.Shared/Overlays/Switchable/NightVisionComponent.cs b/Content.Shared/Overlays/Switchable/NightVisionComponent.cs new file mode 100644 index 0000000000..cb8866dc85 --- /dev/null +++ b/Content.Shared/Overlays/Switchable/NightVisionComponent.cs @@ -0,0 +1,14 @@ +using Content.Shared.Actions; +using Robust.Shared.GameStates; + +namespace Content.Shared.Overlays.Switchable; + +[RegisterComponent, NetworkedComponent] +public sealed partial class NightVisionComponent : SwitchableOverlayComponent +{ + public override string? ToggleAction { get; set; } = "ToggleNightVision"; + + public override Color Color { get; set; } = Color.FromHex("#98FB98"); +} + +public sealed partial class ToggleNightVisionEvent : InstantActionEvent; diff --git a/Content.Shared/Overlays/Switchable/NightVisionSystem.cs b/Content.Shared/Overlays/Switchable/NightVisionSystem.cs new file mode 100644 index 0000000000..f547b9dc76 --- /dev/null +++ b/Content.Shared/Overlays/Switchable/NightVisionSystem.cs @@ -0,0 +1,3 @@ +namespace Content.Shared.Overlays.Switchable; + +public sealed class NightVisionSystem : SwitchableOverlaySystem; diff --git a/Content.Shared/Overlays/Switchable/SwitchableOverlayComponent.cs b/Content.Shared/Overlays/Switchable/SwitchableOverlayComponent.cs new file mode 100644 index 0000000000..8565defe04 --- /dev/null +++ b/Content.Shared/Overlays/Switchable/SwitchableOverlayComponent.cs @@ -0,0 +1,48 @@ +using Robust.Shared.Audio; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization; + +namespace Content.Shared.Overlays.Switchable; + +public abstract partial class SwitchableOverlayComponent : BaseOverlayComponent +{ + [DataField, AutoNetworkedField] + public bool IsActive; + + [DataField] + public bool DrawOverlay = true; + + /// + /// If it is greater than 0, overlay isn't toggled but pulsed instead + /// + [DataField] + public float PulseTime; + + [ViewVariables(VVAccess.ReadOnly)] + public float PulseAccumulator; + + [DataField] + public virtual SoundSpecifier? ActivateSound { get; set; } = + new SoundPathSpecifier("/Audio/Items/Goggles/activate.ogg"); + + [DataField] + public virtual SoundSpecifier? DeactivateSound { get; set; } = + new SoundPathSpecifier("/Audio/Items/Goggles/deactivate.ogg"); + + [DataField] + public virtual string? ToggleAction { get; set; } + + [ViewVariables] + public EntityUid? ToggleActionEntity; +} + +[Serializable, NetSerializable] +public sealed class SwitchableVisionOverlayComponentState : IComponentState +{ + public Color Color; + public bool IsActive; + public SoundSpecifier? ActivateSound; + public SoundSpecifier? DeactivateSound; + public EntProtoId? ToggleAction; + public float LightRadius; +} diff --git a/Content.Shared/Overlays/Switchable/SwitchableOverlaySystem.cs b/Content.Shared/Overlays/Switchable/SwitchableOverlaySystem.cs new file mode 100644 index 0000000000..eb7b230cbc --- /dev/null +++ b/Content.Shared/Overlays/Switchable/SwitchableOverlaySystem.cs @@ -0,0 +1,166 @@ +using Content.Shared.Actions; +using Content.Shared.Inventory; +using Robust.Shared.Audio.Systems; +using Robust.Shared.GameStates; +using Robust.Shared.Network; +using Robust.Shared.Player; +using Robust.Shared.Timing; + +namespace Content.Shared.Overlays.Switchable; + +public abstract class SwitchableOverlaySystem : EntitySystem + where TComp : SwitchableOverlayComponent + where TEvent : InstantActionEvent +{ + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly SharedActionsSystem _actions = default!; + [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly INetManager _net = default!; + + public override void Initialize() + { + SubscribeLocalEvent(OnToggle); + SubscribeLocalEvent(OnInit); + SubscribeLocalEvent(OnMapInit); + SubscribeLocalEvent(OnShutdown); + SubscribeLocalEvent(OnGetItemActions); + SubscribeLocalEvent(OnGetState); + SubscribeLocalEvent(OnHandleState); + } + + public override void FrameUpdate(float frameTime) + { + base.FrameUpdate(frameTime); + + if (_net.IsClient) + ActiveTick(frameTime); + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + if (_net.IsServer) + ActiveTick(frameTime); + } + + private void ActiveTick(float frameTime) + { + var query = EntityQueryEnumerator(); + + while (query.MoveNext(out var uid, out var comp)) + { + if (comp.PulseTime <= 0f || comp.PulseAccumulator >= comp.PulseTime) + continue; + + comp.PulseAccumulator += frameTime; + + if (comp.PulseAccumulator < comp.PulseTime) + continue; + + Toggle(uid, comp, false, false); + RaiseSwitchableOverlayToggledEvent(uid, uid, comp.IsActive); + RaiseSwitchableOverlayToggledEvent(uid, Transform(uid).ParentUid, comp.IsActive); + } + } + + private void OnGetState(EntityUid uid, TComp component, ref ComponentGetState args) + { + args.State = new SwitchableVisionOverlayComponentState + { + Color = component.Color, + IsActive = component.IsActive, + ActivateSound = component.ActivateSound, + DeactivateSound = component.DeactivateSound, + ToggleAction = component.ToggleAction, + LightRadius = component is ThermalVisionComponent thermal ? thermal.LightRadius : 0f, + }; + } + + private void OnHandleState(EntityUid uid, TComp component, ref ComponentHandleState args) + { + if (args.Current is not SwitchableVisionOverlayComponentState state) + return; + + component.Color = state.Color; + component.ActivateSound = state.ActivateSound; + component.DeactivateSound = state.DeactivateSound; + + if (component.ToggleAction != state.ToggleAction) + { + _actions.RemoveAction(uid, component.ToggleActionEntity); + component.ToggleAction = state.ToggleAction; + if (component.ToggleAction != null) + _actions.AddAction(uid, ref component.ToggleActionEntity, component.ToggleAction); + } + + if (component is ThermalVisionComponent thermal) + thermal.LightRadius = state.LightRadius; + + if (component.IsActive == state.IsActive) + return; + + component.IsActive = state.IsActive; + + RaiseSwitchableOverlayToggledEvent(uid, uid, component.IsActive); + RaiseSwitchableOverlayToggledEvent(uid, Transform(uid).ParentUid, component.IsActive); + } + + private void OnGetItemActions(Entity ent, ref GetItemActionsEvent args) + { + if (ent.Comp.ToggleAction != null && args.SlotFlags is not SlotFlags.POCKET and not null) + args.AddAction(ref ent.Comp.ToggleActionEntity, ent.Comp.ToggleAction); + } + + private void OnShutdown(EntityUid uid, TComp component, ComponentShutdown args) + { + _actions.RemoveAction(uid, component.ToggleActionEntity); + } + + private void OnInit(EntityUid uid, TComp component, ComponentInit args) + { + component.PulseAccumulator = component.PulseTime; + } + + private void OnMapInit(EntityUid uid, TComp component, MapInitEvent args) + { + if (component.ToggleActionEntity == null && component.ToggleAction != null) + _actions.AddAction(uid, ref component.ToggleActionEntity, component.ToggleAction); + } + + private void OnToggle(EntityUid uid, TComp component, TEvent args) + { + Toggle(uid, component, !component.IsActive); + RaiseSwitchableOverlayToggledEvent(uid, args.Performer, component.IsActive); + args.Handled = true; + } + + private void Toggle(EntityUid uid, TComp component, bool activate, bool playSound = true) + { + if (playSound && _net.IsClient && _timing.IsFirstTimePredicted) + { + _audio.PlayEntity(activate ? component.ActivateSound : component.DeactivateSound, + Filter.Local(), + uid, + false); + } + + if (component.PulseTime > 0f) + { + component.PulseAccumulator = activate ? 0f : component.PulseTime; + return; + } + + component.IsActive = activate; + Dirty(uid, component); + } + + private void RaiseSwitchableOverlayToggledEvent(EntityUid uid, EntityUid user, bool activated) + { + var ev = new SwitchableOverlayToggledEvent(user, activated); + RaiseLocalEvent(uid, ref ev); + } +} + +[ByRefEvent] +public record struct SwitchableOverlayToggledEvent(EntityUid User, bool Activated); diff --git a/Content.Shared/Overlays/Switchable/ThermalVisionComponent.cs b/Content.Shared/Overlays/Switchable/ThermalVisionComponent.cs new file mode 100644 index 0000000000..6e3d39f289 --- /dev/null +++ b/Content.Shared/Overlays/Switchable/ThermalVisionComponent.cs @@ -0,0 +1,17 @@ +using Content.Shared.Actions; +using Robust.Shared.GameStates; + +namespace Content.Shared.Overlays.Switchable; + +[RegisterComponent, NetworkedComponent] +public sealed partial class ThermalVisionComponent : SwitchableOverlayComponent +{ + public override string? ToggleAction { get; set; } = "ToggleThermalVision"; + + public override Color Color { get; set; } = Color.FromHex("#F84742"); + + [DataField] + public float LightRadius = 5f; +} + +public sealed partial class ToggleThermalVisionEvent : InstantActionEvent; diff --git a/Content.Shared/Overlays/Switchable/ThermalVisionSystem.cs b/Content.Shared/Overlays/Switchable/ThermalVisionSystem.cs new file mode 100644 index 0000000000..c19d0d1690 --- /dev/null +++ b/Content.Shared/Overlays/Switchable/ThermalVisionSystem.cs @@ -0,0 +1,3 @@ +namespace Content.Shared.Overlays.Switchable; + +public sealed class SharedThermalVisionSystem : SwitchableOverlaySystem; diff --git a/Content.Shared/Power/Components/ItemSlotRequiresPowerComponent.cs b/Content.Shared/Power/Components/ItemSlotRequiresPowerComponent.cs new file mode 100644 index 0000000000..6e3b9eaca0 --- /dev/null +++ b/Content.Shared/Power/Components/ItemSlotRequiresPowerComponent.cs @@ -0,0 +1,9 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Power.Components; + +[RegisterComponent, NetworkedComponent] +public sealed partial class ItemSlotRequiresPowerComponent : Component +{ + +} diff --git a/Content.Shared/Power/EntitySystems/ItemSlotRequiresPowerSystem.cs b/Content.Shared/Power/EntitySystems/ItemSlotRequiresPowerSystem.cs new file mode 100644 index 0000000000..3df8b91a98 --- /dev/null +++ b/Content.Shared/Power/EntitySystems/ItemSlotRequiresPowerSystem.cs @@ -0,0 +1,23 @@ +using Content.Shared.Containers.ItemSlots; +using Content.Shared.Power.Components; + +namespace Content.Shared.Power.EntitySystems; + +public sealed class ItemSlotRequiresPowerSystem : EntitySystem +{ + [Dependency] private readonly SharedPowerReceiverSystem _receiver = default!; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnInsertAttempt); + } + + private void OnInsertAttempt(Entity ent, ref ItemSlotInsertAttemptEvent args) + { + if (!_receiver.IsPowered(ent.Owner)) + { + args.Cancelled = true; + } + } +} diff --git a/Content.Shared/Psionics/Events.cs b/Content.Shared/Psionics/Events.cs index 68ec3097e7..6705e940cd 100644 --- a/Content.Shared/Psionics/Events.cs +++ b/Content.Shared/Psionics/Events.cs @@ -1,6 +1,7 @@ using Robust.Shared.Serialization; using Content.Shared.Damage; using Content.Shared.DoAfter; +using Content.Shared.Abilities.Psionics; namespace Content.Shared.Psionics.Events; @@ -67,3 +68,29 @@ public PsionicHealOtherDoAfterEvent(TimeSpan startedAt) public override DoAfterEvent Clone() => this; } + +[Serializable, NetSerializable] +public sealed partial class AssayDoAfterEvent : DoAfterEvent +{ + [DataField(required: true)] + public TimeSpan StartedAt; + + [DataField] + public int FontSize = 12; + + [DataField] + public string FontColor = "#8A00C2"; + + private AssayDoAfterEvent() + { + } + + public AssayDoAfterEvent(TimeSpan startedAt, int fontSize, string fontColor) + { + StartedAt = startedAt; + FontSize = fontSize; + FontColor = fontColor; + } + + public override DoAfterEvent Clone() => this; +} diff --git a/Content.Shared/Puppet/SharedVentriloquistPuppetSystem.cs b/Content.Shared/Puppet/SharedVentriloquistPuppetSystem.cs index 430c2b1b17..e3fa21ed37 100644 --- a/Content.Shared/Puppet/SharedVentriloquistPuppetSystem.cs +++ b/Content.Shared/Puppet/SharedVentriloquistPuppetSystem.cs @@ -7,6 +7,7 @@ namespace Content.Shared.Puppet; +// TODO deduplicate with BlockMovementComponent public abstract class SharedVentriloquistPuppetSystem : EntitySystem { [Dependency] private readonly ActionBlockerSystem _blocker = default!; @@ -15,7 +16,7 @@ public override void Initialize() { base.Initialize(); SubscribeLocalEvent(Cancel); - SubscribeLocalEvent(Cancel); + SubscribeLocalEvent(CancelInteract); SubscribeLocalEvent(Cancel); SubscribeLocalEvent(Cancel); SubscribeLocalEvent(Cancel); @@ -24,6 +25,11 @@ public override void Initialize() SubscribeLocalEvent(OnStartup); } + private void CancelInteract(Entity ent, ref InteractionAttemptEvent args) + { + args.Cancelled = true; + } + private void OnStartup(EntityUid uid, VentriloquistPuppetComponent component, ComponentStartup args) { _blocker.UpdateCanMove(uid); @@ -33,4 +39,4 @@ private void Cancel(EntityUid uid, VentriloquistPuppetComponent component, T { args.Cancel(); } -} \ No newline at end of file +} diff --git a/Content.Shared/Random/Helpers/SharedRandomExtensions.cs b/Content.Shared/Random/Helpers/SharedRandomExtensions.cs index 3941c2859b..456ba5a215 100644 --- a/Content.Shared/Random/Helpers/SharedRandomExtensions.cs +++ b/Content.Shared/Random/Helpers/SharedRandomExtensions.cs @@ -13,6 +13,11 @@ public static string Pick(this IRobustRandom random, DatasetPrototype prototype) return random.Pick(prototype.Values); } + public static string Pick(this IRobustRandom random, LocalizedDatasetPrototype prototype) + { + return random.Pick(prototype.Values); + } + public static string Pick(this IWeightedRandomPrototype prototype, System.Random random) { var picks = prototype.Weights; diff --git a/Content.Shared/Roles/JobPrototype.cs b/Content.Shared/Roles/JobPrototype.cs index fbe5053bc5..91a7ce19ce 100644 --- a/Content.Shared/Roles/JobPrototype.cs +++ b/Content.Shared/Roles/JobPrototype.cs @@ -1,5 +1,6 @@ using Content.Shared.Access; using Content.Shared.Customization.Systems; +using Content.Shared.Dataset; using Content.Shared.Players.PlayTimeTracking; using Content.Shared.Roles; using Content.Shared.StatusIcon; @@ -98,6 +99,13 @@ public sealed partial class JobPrototype : IPrototype [DataField("startingGear", customTypeSerializer: typeof(PrototypeIdSerializer))] public string? StartingGear { get; private set; } + /// + /// If this has a value, it will randomly set the entity name of the + /// entity upon spawn based on the dataset. + /// + [DataField] + public ProtoId? NameDataset; + /// /// Use this to spawn in as a non-humanoid (borg, test subject, etc.) /// Starting gear will be ignored. @@ -127,6 +135,12 @@ public sealed partial class JobPrototype : IPrototype [DataField] public bool Whitelisted; + [DataField] + public bool SpawnLoadout = true; + + [DataField] + public bool ApplyTraits = true; + [DataField] public bool CanBeAntagTarget = true; // Floofstation Edit } diff --git a/Content.Shared/Shadowkin/SharedEtherealSystem.cs b/Content.Shared/Shadowkin/SharedEtherealSystem.cs index b71c6ead19..be9252f629 100644 --- a/Content.Shared/Shadowkin/SharedEtherealSystem.cs +++ b/Content.Shared/Shadowkin/SharedEtherealSystem.cs @@ -170,7 +170,7 @@ private void OnInteractionAttempt(EntityUid uid, EtherealComponent component, In || HasComp(args.Target)) return; - args.Cancel(); + args.Cancelled = true; if (_gameTiming.InPrediction) return; diff --git a/Content.Shared/Silicons/Laws/Components/SiliconLawBoundComponent.cs b/Content.Shared/Silicons/Laws/Components/SiliconLawBoundComponent.cs index 824d057b3e..0fb9c5920f 100644 --- a/Content.Shared/Silicons/Laws/Components/SiliconLawBoundComponent.cs +++ b/Content.Shared/Silicons/Laws/Components/SiliconLawBoundComponent.cs @@ -1,4 +1,5 @@ using Content.Shared.Actions; +using Robust.Shared.GameStates; using Robust.Shared.Prototypes; using Robust.Shared.Serialization; @@ -7,21 +8,9 @@ namespace Content.Shared.Silicons.Laws.Components; /// /// This is used for entities which are bound to silicon laws and can view them. /// -[RegisterComponent, Access(typeof(SharedSiliconLawSystem))] +[RegisterComponent, NetworkedComponent, Access(typeof(SharedSiliconLawSystem))] public sealed partial class SiliconLawBoundComponent : Component { - /// - /// The sidebar action that toggles the laws screen. - /// - [DataField] - public EntProtoId ViewLawsAction = "ActionViewLaws"; - - /// - /// The action for toggling laws. Stored here so we can remove it later. - /// - [DataField] - public EntityUid? ViewLawsActionEntity; - /// /// The last entity that provided laws to this entity. /// diff --git a/Content.Shared/Silicons/Laws/Components/SiliconLawUpdaterComponent.cs b/Content.Shared/Silicons/Laws/Components/SiliconLawUpdaterComponent.cs new file mode 100644 index 0000000000..e28bf883d9 --- /dev/null +++ b/Content.Shared/Silicons/Laws/Components/SiliconLawUpdaterComponent.cs @@ -0,0 +1,17 @@ +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared.Silicons.Laws.Components; + +/// +/// Whenever an entity is inserted with silicon laws it will update the relevant entity's laws. +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class SiliconLawUpdaterComponent : Component +{ + /// + /// Entities to update + /// + [DataField(required: true)] + public ComponentRegistry Components; +} diff --git a/Content.Shared/Silicons/Laws/SharedSiliconLawSystem.Updater.cs b/Content.Shared/Silicons/Laws/SharedSiliconLawSystem.Updater.cs new file mode 100644 index 0000000000..9fbef58a84 --- /dev/null +++ b/Content.Shared/Silicons/Laws/SharedSiliconLawSystem.Updater.cs @@ -0,0 +1,17 @@ +using Content.Shared.Silicons.Laws.Components; +using Robust.Shared.Containers; + +namespace Content.Shared.Silicons.Laws; + +public abstract partial class SharedSiliconLawSystem +{ + private void InitializeUpdater() + { + SubscribeLocalEvent(OnUpdaterInsert); + } + + protected virtual void OnUpdaterInsert(Entity ent, ref EntInsertedIntoContainerMessage args) + { + // TODO: Prediction + } +} diff --git a/Content.Shared/Silicons/Laws/SharedSiliconLawSystem.cs b/Content.Shared/Silicons/Laws/SharedSiliconLawSystem.cs index c0619e6e43..a30e7c8980 100644 --- a/Content.Shared/Silicons/Laws/SharedSiliconLawSystem.cs +++ b/Content.Shared/Silicons/Laws/SharedSiliconLawSystem.cs @@ -8,13 +8,14 @@ namespace Content.Shared.Silicons.Laws; /// /// This handles getting and displaying the laws for silicons. /// -public abstract class SharedSiliconLawSystem : EntitySystem +public abstract partial class SharedSiliconLawSystem : EntitySystem { [Dependency] private readonly SharedPopupSystem _popup = default!; /// public override void Initialize() { + InitializeUpdater(); SubscribeLocalEvent(OnGotEmagged); SubscribeLocalEvent(OnAttemptEmag); } diff --git a/Content.Shared/Silicons/StationAi/SharedStationAiSystem.Airlock.cs b/Content.Shared/Silicons/StationAi/SharedStationAiSystem.Airlock.cs new file mode 100644 index 0000000000..ff6fc1ece0 --- /dev/null +++ b/Content.Shared/Silicons/StationAi/SharedStationAiSystem.Airlock.cs @@ -0,0 +1,25 @@ +using Content.Shared.Doors.Components; +using Robust.Shared.Serialization; + +namespace Content.Shared.Silicons.StationAi; + +public abstract partial class SharedStationAiSystem +{ + // Handles airlock radial + + private void InitializeAirlock() + { + SubscribeLocalEvent(OnAirlockBolt); + } + + private void OnAirlockBolt(EntityUid ent, DoorBoltComponent component, StationAiBoltEvent args) + { + _doors.SetBoltsDown((ent, component), args.Bolted, args.User, predicted: true); + } +} + +[Serializable, NetSerializable] +public sealed class StationAiBoltEvent : BaseStationAiAction +{ + public bool Bolted; +} diff --git a/Content.Shared/Silicons/StationAi/SharedStationAiSystem.Held.cs b/Content.Shared/Silicons/StationAi/SharedStationAiSystem.Held.cs new file mode 100644 index 0000000000..a6c57f5940 --- /dev/null +++ b/Content.Shared/Silicons/StationAi/SharedStationAiSystem.Held.cs @@ -0,0 +1,187 @@ +using System.Diagnostics.CodeAnalysis; +using Content.Shared.Actions.Events; +using Content.Shared.Interaction.Events; +using Content.Shared.Verbs; +using Robust.Shared.Serialization; +using Robust.Shared.Utility; + +namespace Content.Shared.Silicons.StationAi; + +public abstract partial class SharedStationAiSystem +{ + /* + * Added when an entity is inserted into a StationAiCore. + */ + + private void InitializeHeld() + { + SubscribeLocalEvent(OnRadialMessage); + SubscribeLocalEvent(OnMessageAttempt); + SubscribeLocalEvent>(OnTargetVerbs); + + SubscribeLocalEvent(OnHeldInteraction); + SubscribeLocalEvent(OnHeldRelay); + SubscribeLocalEvent(OnCoreJump); + } + + private void OnCoreJump(Entity ent, ref JumpToCoreEvent args) + { + if (!TryGetCore(ent.Owner, out var core) || core.Comp?.RemoteEntity == null) + return; + + _xforms.DropNextTo(core.Comp.RemoteEntity.Value, core.Owner) ; + } + + /// + /// Tries to get the entity held in the AI core. + /// + private bool TryGetHeld(Entity entity, out EntityUid held) + { + held = EntityUid.Invalid; + + if (!Resolve(entity.Owner, ref entity.Comp)) + return false; + + if (!_containers.TryGetContainer(entity.Owner, StationAiCoreComponent.Container, out var container) || + container.ContainedEntities.Count == 0) + return false; + + held = container.ContainedEntities[0]; + return true; + } + + private bool TryGetCore(EntityUid ent, out Entity core) + { + if (!_containers.TryGetContainingContainer(ent, out var container) || + container.ID != StationAiCoreComponent.Container || + !TryComp(container.Owner, out StationAiCoreComponent? coreComp) || + coreComp.RemoteEntity == null) + { + core = (EntityUid.Invalid, null); + return false; + } + + core = (container.Owner, coreComp); + return true; + } + + private void OnHeldRelay(Entity ent, ref AttemptRelayActionComponentChangeEvent args) + { + if (!TryGetCore(ent.Owner, out var core)) + return; + + args.Target = core.Comp?.RemoteEntity; + } + + private void OnRadialMessage(StationAiRadialMessage ev) + { + if (!TryGetEntity(ev.Entity, out var target)) + return; + + ev.Event.User = ev.Actor; + RaiseLocalEvent(target.Value, (object) ev.Event); + } + + private void OnMessageAttempt(BoundUserInterfaceMessageAttempt ev) + { + if (ev.Actor == ev.Target) + return; + + if (TryComp(ev.Actor, out StationAiHeldComponent? aiComp) && + (!ValidateAi((ev.Actor, aiComp)) || + !HasComp(ev.Target))) + { + ev.Cancel(); + } + } + + private void OnHeldInteraction(Entity ent, ref InteractionAttemptEvent args) + { + // Cancel if it's not us or something with a whitelist. + args.Cancelled = ent.Owner != args.Target && + args.Target != null && + (!TryComp(args.Target, out StationAiWhitelistComponent? whitelist) || !whitelist.Enabled); + } + + private void OnTargetVerbs(Entity ent, ref GetVerbsEvent args) + { + if (!args.CanComplexInteract || + !ent.Comp.Enabled || + !HasComp(args.User) || + !HasComp(args.Target)) + { + return; + } + + var user = args.User; + var target = args.Target; + + var isOpen = _uiSystem.IsUiOpen(target, AiUi.Key, user); + + args.Verbs.Add(new AlternativeVerb() + { + Text = isOpen ? Loc.GetString("ai-close") : Loc.GetString("ai-open"), + Act = () => + { + if (isOpen) + { + _uiSystem.CloseUi(ent.Owner, AiUi.Key, user); + } + else + { + _uiSystem.OpenUi(ent.Owner, AiUi.Key, user); + } + } + }); + } +} + +/// +/// Raised from client to server as a BUI message wrapping the event to perform. +/// Also handles AI action validation. +/// +[Serializable, NetSerializable] +public sealed class StationAiRadialMessage : BoundUserInterfaceMessage +{ + public BaseStationAiAction Event = default!; +} + +// Do nothing on server just here for shared move along. +/// +/// Raised on client to get the relevant data for radial actions. +/// +public sealed class StationAiRadial : BaseStationAiAction +{ + public SpriteSpecifier? Sprite; + + public string? Tooltip; + + public BaseStationAiAction Event = default!; +} + +/// +/// Abstract parent for radial actions events. +/// When a client requests a radial action this will get sent. +/// +[Serializable, NetSerializable] +public abstract class BaseStationAiAction +{ + [field:NonSerialized] + public EntityUid User { get; set; } +} + +// No idea if there's a better way to do this. +/// +/// Grab actions possible for an AI on the target entity. +/// +[ByRefEvent] +public record struct GetStationAiRadialEvent() +{ + public List Actions = new(); +} + +[Serializable, NetSerializable] +public enum AiUi : byte +{ + Key, +} diff --git a/Content.Shared/Silicons/StationAi/SharedStationAiSystem.Light.cs b/Content.Shared/Silicons/StationAi/SharedStationAiSystem.Light.cs new file mode 100644 index 0000000000..bc2e3665f5 --- /dev/null +++ b/Content.Shared/Silicons/StationAi/SharedStationAiSystem.Light.cs @@ -0,0 +1,28 @@ +using Content.Shared.Light.Components; +using Robust.Shared.Serialization; + +namespace Content.Shared.Silicons.StationAi; + +public abstract partial class SharedStationAiSystem +{ + // Handles light toggling. + + private void InitializeLight() + { + SubscribeLocalEvent(OnLight); + } + + private void OnLight(EntityUid ent, ItemTogglePointLightComponent component, StationAiLightEvent args) + { + if (args.Enabled) + _toggles.TryActivate(ent, user: args.User); + else + _toggles.TryDeactivate(ent, user: args.User); + } +} + +[Serializable, NetSerializable] +public sealed class StationAiLightEvent : BaseStationAiAction +{ + public bool Enabled; +} diff --git a/Content.Shared/Silicons/StationAi/SharedStationAiSystem.cs b/Content.Shared/Silicons/StationAi/SharedStationAiSystem.cs new file mode 100644 index 0000000000..348b0b0465 --- /dev/null +++ b/Content.Shared/Silicons/StationAi/SharedStationAiSystem.cs @@ -0,0 +1,412 @@ +using Content.Shared.ActionBlocker; +using Content.Shared.Actions; +using Content.Shared.Administration.Managers; +using Content.Shared.Containers.ItemSlots; +using Content.Shared.Database; +using Content.Shared.Doors.Systems; +using Content.Shared.Interaction; +using Content.Shared.Item.ItemToggle; +using Content.Shared.Mind; +using Content.Shared.Movement.Components; +using Content.Shared.Movement.Systems; +using Content.Shared.Power; +using Content.Shared.StationAi; +using Content.Shared.Verbs; +using Robust.Shared.Containers; +using Robust.Shared.Map.Components; +using Robust.Shared.Network; +using Robust.Shared.Physics; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization; +using Robust.Shared.Timing; + +namespace Content.Shared.Silicons.StationAi; + +public abstract partial class SharedStationAiSystem : EntitySystem +{ + [Dependency] private readonly ISharedAdminManager _admin = default!; + [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly INetManager _net = default!; + [Dependency] private readonly ItemSlotsSystem _slots = default!; + [Dependency] private readonly ItemToggleSystem _toggles = default!; + [Dependency] private readonly ActionBlockerSystem _blocker = default!; + [Dependency] private readonly MetaDataSystem _metadata = default!; + [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + [Dependency] private readonly SharedContainerSystem _containers = default!; + [Dependency] private readonly SharedDoorSystem _doors = default!; + [Dependency] private readonly SharedEyeSystem _eye = default!; + [Dependency] protected readonly SharedMapSystem Maps = default!; + [Dependency] private readonly SharedMindSystem _mind = default!; + [Dependency] private readonly SharedMoverController _mover = default!; + [Dependency] private readonly SharedTransformSystem _xforms = default!; + [Dependency] private readonly SharedUserInterfaceSystem _uiSystem = default!; + [Dependency] private readonly StationAiVisionSystem _vision = default!; + + // StationAiHeld is added to anything inside of an AI core. + // StationAiHolder indicates it can hold an AI positronic brain (e.g. holocard / core). + // StationAiCore holds functionality related to the core itself. + // StationAiWhitelist is a general whitelist to stop it being able to interact with anything + // StationAiOverlay handles the static overlay. It also handles interaction blocking on client and server + // for anything under it. + + private EntityQuery _broadphaseQuery; + private EntityQuery _gridQuery; + + [ValidatePrototypeId] + private static readonly EntProtoId DefaultAi = "StationAiBrain"; + + public override void Initialize() + { + base.Initialize(); + + _broadphaseQuery = GetEntityQuery(); + _gridQuery = GetEntityQuery(); + + InitializeAirlock(); + InitializeHeld(); + InitializeLight(); + + SubscribeLocalEvent(OnAiBuiCheck); + + SubscribeLocalEvent(OnAiAccessible); + SubscribeLocalEvent(OnAiInRange); + SubscribeLocalEvent(OnAiMenu); + + SubscribeLocalEvent(OnHolderInit); + SubscribeLocalEvent(OnHolderRemove); + SubscribeLocalEvent(OnHolderInteract); + SubscribeLocalEvent(OnHolderMapInit); + SubscribeLocalEvent(OnHolderConInsert); + SubscribeLocalEvent(OnHolderConRemove); + + SubscribeLocalEvent(OnAiInsert); + SubscribeLocalEvent(OnAiRemove); + SubscribeLocalEvent(OnAiMapInit); + SubscribeLocalEvent(OnAiShutdown); + SubscribeLocalEvent(OnCorePower); + SubscribeLocalEvent>(OnCoreVerbs); + } + + private void OnCoreVerbs(Entity ent, ref GetVerbsEvent args) + { + if (!_admin.IsAdmin(args.User) || + TryGetHeld((ent.Owner, ent.Comp), out _)) + { + return; + } + + var user = args.User; + + args.Verbs.Add(new Verb() + { + Text = Loc.GetString("station-ai-takeover"), + Category = VerbCategory.Debug, + Act = () => + { + var brain = SpawnInContainerOrDrop(DefaultAi, ent.Owner, StationAiCoreComponent.Container); + _mind.ControlMob(user, brain); + }, + Impact = LogImpact.High, + }); + } + + private void OnAiAccessible(Entity ent, ref AccessibleOverrideEvent args) + { + args.Handled = true; + + // Hopefully AI never needs storage + if (_containers.TryGetContainingContainer(args.Target, out var targetContainer)) + { + return; + } + + if (!_containers.IsInSameOrTransparentContainer(args.User, args.Target, otherContainer: targetContainer)) + { + return; + } + + args.Accessible = true; + } + + private void OnAiMenu(Entity ent, ref MenuVisibilityEvent args) + { + args.Visibility &= ~MenuVisibility.NoFov; + } + + private void OnAiBuiCheck(Entity ent, ref BoundUserInterfaceCheckRangeEvent args) + { + args.Result = BoundUserInterfaceRangeResult.Fail; + + // Similar to the inrange check but more optimised so server doesn't die. + var targetXform = Transform(args.Target); + + // No cross-grid + if (targetXform.GridUid != args.Actor.Comp.GridUid) + { + return; + } + + if (!_broadphaseQuery.TryComp(targetXform.GridUid, out var broadphase) || !_gridQuery.TryComp(targetXform.GridUid, out var grid)) + { + return; + } + + var targetTile = Maps.LocalToTile(targetXform.GridUid.Value, grid, targetXform.Coordinates); + + lock (_vision) + { + if (_vision.IsAccessible((targetXform.GridUid.Value, broadphase, grid), targetTile, fastPath: true)) + { + args.Result = BoundUserInterfaceRangeResult.Pass; + } + } + } + + private void OnAiInRange(Entity ent, ref InRangeOverrideEvent args) + { + args.Handled = true; + var targetXform = Transform(args.Target); + + // No cross-grid + if (targetXform.GridUid != Transform(args.User).GridUid) + { + return; + } + + // Validate it's in camera range yes this is expensive. + // Yes it needs optimising + if (!_broadphaseQuery.TryComp(targetXform.GridUid, out var broadphase) || !_gridQuery.TryComp(targetXform.GridUid, out var grid)) + { + return; + } + + var targetTile = Maps.LocalToTile(targetXform.GridUid.Value, grid, targetXform.Coordinates); + + args.InRange = _vision.IsAccessible((targetXform.GridUid.Value, broadphase, grid), targetTile); + } + + private void OnHolderInteract(Entity ent, ref AfterInteractEvent args) + { + if (!TryComp(args.Target, out StationAiHolderComponent? targetHolder)) + return; + + // Try to insert our thing into them + if (_slots.CanEject(ent.Owner, args.User, ent.Comp.Slot)) + { + if (!_slots.TryInsert(args.Target.Value, targetHolder.Slot, ent.Comp.Slot.Item!.Value, args.User, excludeUserAudio: true)) + { + return; + } + + args.Handled = true; + return; + } + + // Otherwise try to take from them + if (_slots.CanEject(args.Target.Value, args.User, targetHolder.Slot)) + { + if (!_slots.TryInsert(ent.Owner, ent.Comp.Slot, targetHolder.Slot.Item!.Value, args.User, excludeUserAudio: true)) + { + return; + } + + args.Handled = true; + } + } + + private void OnHolderInit(Entity ent, ref ComponentInit args) + { + _slots.AddItemSlot(ent.Owner, StationAiHolderComponent.Container, ent.Comp.Slot); + } + + private void OnHolderRemove(Entity ent, ref ComponentRemove args) + { + _slots.RemoveItemSlot(ent.Owner, ent.Comp.Slot); + } + + private void OnHolderConInsert(Entity ent, ref EntInsertedIntoContainerMessage args) + { + UpdateAppearance((ent.Owner, ent.Comp)); + } + + private void OnHolderConRemove(Entity ent, ref EntRemovedFromContainerMessage args) + { + UpdateAppearance((ent.Owner, ent.Comp)); + } + + private void OnHolderMapInit(Entity ent, ref MapInitEvent args) + { + UpdateAppearance(ent.Owner); + } + + private void OnAiShutdown(Entity ent, ref ComponentShutdown args) + { + // TODO: Tryqueuedel + if (_net.IsClient) + return; + + QueueDel(ent.Comp.RemoteEntity); + ent.Comp.RemoteEntity = null; + } + + private void OnCorePower(Entity ent, ref PowerChangedEvent args) + { + // TODO: I think in 13 they just straightup die so maybe implement that + if (args.Powered) + { + if (!SetupEye(ent)) + return; + + AttachEye(ent); + } + else + { + ClearEye(ent); + } + } + + private void OnAiMapInit(Entity ent, ref MapInitEvent args) + { + SetupEye(ent); + AttachEye(ent); + } + + private bool SetupEye(Entity ent) + { + if (ent.Comp.RemoteEntity != null) + return false; + + if (ent.Comp.RemoteEntityProto != null) + { + ent.Comp.RemoteEntity = SpawnAtPosition(ent.Comp.RemoteEntityProto, Transform(ent.Owner).Coordinates); + Dirty(ent); + } + + return true; + } + + private void ClearEye(Entity ent) + { + QueueDel(ent.Comp.RemoteEntity); + ent.Comp.RemoteEntity = null; + } + + private void AttachEye(Entity ent) + { + if (ent.Comp.RemoteEntity == null) + return; + + if (!_containers.TryGetContainer(ent.Owner, StationAiHolderComponent.Container, out var container) || + container.ContainedEntities.Count != 1) + { + return; + } + + // Attach them to the portable eye that can move around. + var user = container.ContainedEntities[0]; + + if (TryComp(user, out EyeComponent? eyeComp)) + { + _eye.SetTarget(user, ent.Comp.RemoteEntity.Value, eyeComp); + } + + _mover.SetRelay(user, ent.Comp.RemoteEntity.Value); + } + + private void OnAiInsert(Entity ent, ref EntInsertedIntoContainerMessage args) + { + if (_timing.ApplyingState) + return; + + // Just so text and the likes works properly + _metadata.SetEntityName(ent.Owner, MetaData(args.Entity).EntityName); + + AttachEye(ent); + } + + private void OnAiRemove(Entity ent, ref EntRemovedFromContainerMessage args) + { + if (_timing.ApplyingState) + return; + + // Reset name to whatever + _metadata.SetEntityName(ent.Owner, Prototype(ent.Owner)?.Name ?? string.Empty); + + // Remove eye relay + RemCompDeferred(args.Entity); + + if (TryComp(args.Entity, out EyeComponent? eyeComp)) + { + _eye.SetTarget(args.Entity, null, eyeComp); + } + } + + private void UpdateAppearance(Entity entity) + { + if (!Resolve(entity.Owner, ref entity.Comp, false)) + return; + + if (!_containers.TryGetContainer(entity.Owner, StationAiHolderComponent.Container, out var container) || + container.Count == 0) + { + _appearance.SetData(entity.Owner, StationAiVisualState.Key, StationAiState.Empty); + return; + } + + _appearance.SetData(entity.Owner, StationAiVisualState.Key, StationAiState.Occupied); + } + + public virtual bool SetVisionEnabled(Entity entity, bool enabled, bool announce = false) + { + if (entity.Comp.Enabled == enabled) + return false; + + entity.Comp.Enabled = enabled; + Dirty(entity); + + return true; + } + + public virtual bool SetWhitelistEnabled(Entity entity, bool value, bool announce = false) + { + if (entity.Comp.Enabled == value) + return false; + + entity.Comp.Enabled = value; + Dirty(entity); + + return true; + } + + /// + /// BUI validation for ai interactions. + /// + private bool ValidateAi(Entity entity) + { + if (!Resolve(entity.Owner, ref entity.Comp, false)) + { + return false; + } + + return _blocker.CanComplexInteract(entity.Owner); + } +} + +public sealed partial class JumpToCoreEvent : InstantActionEvent +{ + +} + +[Serializable, NetSerializable] +public enum StationAiVisualState : byte +{ + Key, +} + +[Serializable, NetSerializable] +public enum StationAiState : byte +{ + Empty, + Occupied, + Dead, +} diff --git a/Content.Shared/Silicons/StationAi/StationAiCoreComponent.cs b/Content.Shared/Silicons/StationAi/StationAiCoreComponent.cs new file mode 100644 index 0000000000..b7a8b4cd5f --- /dev/null +++ b/Content.Shared/Silicons/StationAi/StationAiCoreComponent.cs @@ -0,0 +1,32 @@ +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared.Silicons.StationAi; + +/// +/// Indicates this entity can interact with station equipment and is a "Station AI". +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class StationAiCoreComponent : Component +{ + /* + * I couldn't think of any other reason you'd want to split these out. + */ + + /// + /// Can it move its camera around and interact remotely with things. + /// + [DataField, AutoNetworkedField] + public bool Remote = true; + + /// + /// The invisible eye entity being used to look around. + /// + [DataField, AutoNetworkedField] + public EntityUid? RemoteEntity; + + [DataField(readOnly: true)] + public EntProtoId? RemoteEntityProto = "StationAiHolo"; + + public const string Container = "station_ai_mind_slot"; +} diff --git a/Content.Shared/Silicons/StationAi/StationAiHeldComponent.cs b/Content.Shared/Silicons/StationAi/StationAiHeldComponent.cs new file mode 100644 index 0000000000..6dab1ee491 --- /dev/null +++ b/Content.Shared/Silicons/StationAi/StationAiHeldComponent.cs @@ -0,0 +1,9 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Silicons.StationAi; + +/// +/// Indicates this entity is currently held inside of a station AI core. +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class StationAiHeldComponent : Component; diff --git a/Content.Shared/Silicons/StationAi/StationAiHolderComponent.cs b/Content.Shared/Silicons/StationAi/StationAiHolderComponent.cs new file mode 100644 index 0000000000..221845d493 --- /dev/null +++ b/Content.Shared/Silicons/StationAi/StationAiHolderComponent.cs @@ -0,0 +1,16 @@ +using Content.Shared.Containers.ItemSlots; +using Robust.Shared.GameStates; + +namespace Content.Shared.Silicons.StationAi; + +/// +/// Allows moving a contained entity to and from this component. +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class StationAiHolderComponent : Component +{ + public const string Container = StationAiCoreComponent.Container; + + [DataField] + public ItemSlot Slot = new(); +} diff --git a/Content.Shared/Silicons/StationAi/StationAiVisionComponent.cs b/Content.Shared/Silicons/StationAi/StationAiVisionComponent.cs index 94aef8ad36..f047fe41e4 100644 --- a/Content.Shared/Silicons/StationAi/StationAiVisionComponent.cs +++ b/Content.Shared/Silicons/StationAi/StationAiVisionComponent.cs @@ -1,8 +1,9 @@ +using Content.Shared.Silicons.StationAi; using Robust.Shared.GameStates; -namespace Content.Shared.Silicons.StationAi; +namespace Content.Shared.StationAi; -[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]//, Access(typeof(SharedStationAiSystem))] +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, Access(typeof(SharedStationAiSystem))] public sealed partial class StationAiVisionComponent : Component { [DataField, AutoNetworkedField] diff --git a/Content.Shared/Silicons/StationAi/StationAiVisionSystem.cs b/Content.Shared/Silicons/StationAi/StationAiVisionSystem.cs index c144f330e1..bdc62a6bb3 100644 --- a/Content.Shared/Silicons/StationAi/StationAiVisionSystem.cs +++ b/Content.Shared/Silicons/StationAi/StationAiVisionSystem.cs @@ -1,4 +1,6 @@ +using Content.Shared.StationAi; using Robust.Shared.Map.Components; +using Robust.Shared.Physics; using Robust.Shared.Threading; using Robust.Shared.Utility; @@ -24,6 +26,8 @@ public sealed class StationAiVisionSystem : EntitySystem private readonly HashSet> _seeds = new(); private readonly HashSet _viewportTiles = new(); + private EntityQuery _occluderQuery; + // Dummy set private readonly HashSet _singleTiles = new(); @@ -36,15 +40,12 @@ public sealed class StationAiVisionSystem : EntitySystem /// private bool FastPath; - /// - /// Have we found the target tile if we're only checking for a single one. - /// - private bool TargetFound; - public override void Initialize() { base.Initialize(); + _occluderQuery = GetEntityQuery(); + _seedJob = new() { System = this, @@ -61,16 +62,16 @@ public override void Initialize() /// /// Returns whether a tile is accessible based on vision. /// - public bool IsAccessible(Entity grid, Vector2i tile, float expansionSize = 8.5f, bool fastPath = false) + public bool IsAccessible(Entity grid, Vector2i tile, float expansionSize = 8.5f, bool fastPath = false) { _viewportTiles.Clear(); _opaque.Clear(); _seeds.Clear(); _viewportTiles.Add(tile); - var localBounds = _lookup.GetLocalBounds(tile, grid.Comp.TileSize); + var localBounds = _lookup.GetLocalBounds(tile, grid.Comp2.TileSize); var expandedBounds = localBounds.Enlarged(expansionSize); - _seedJob.Grid = grid; + _seedJob.Grid = (grid.Owner, grid.Comp2); _seedJob.ExpandedBounds = expandedBounds; _parallel.ProcessNow(_seedJob); _job.Data.Clear(); @@ -110,21 +111,19 @@ public bool IsAccessible(Entity grid, Vector2i tile, float exp _job.BoundaryTiles.Add(new HashSet()); } - _job.TargetTile = tile; - TargetFound = false; _singleTiles.Clear(); - _job.Grid = grid; + _job.Grid = (grid.Owner, grid.Comp2); _job.VisibleTiles = _singleTiles; _parallel.ProcessNow(_job, _job.Data.Count); - return TargetFound; + return _job.VisibleTiles.Contains(tile); } - private bool IsOccluded(Entity grid, Vector2i tile) + private bool IsOccluded(Entity grid, Vector2i tile) { - var tileBounds = _lookup.GetLocalBounds(tile, grid.Comp.TileSize).Enlarged(-0.05f); + var tileBounds = _lookup.GetLocalBounds(tile, grid.Comp2.TileSize).Enlarged(-0.05f); _occluders.Clear(); - _lookup.GetLocalEntitiesIntersecting(grid.Owner, tileBounds, _occluders, LookupFlags.Static); + _lookup.GetLocalEntitiesIntersecting((grid.Owner, grid.Comp1), tileBounds, _occluders, query: _occluderQuery, flags: LookupFlags.Static | LookupFlags.Approximate); var anyOccluders = false; foreach (var occluder in _occluders) @@ -143,17 +142,18 @@ private bool IsOccluded(Entity grid, Vector2i tile) /// Gets a byond-equivalent for tiles in the specified worldAABB. /// /// How much to expand the bounds before to find vision intersecting it. Makes this the largest vision size + 1 tile. - public void GetView(Entity grid, Box2Rotated worldBounds, HashSet visibleTiles, float expansionSize = 8.5f) + public void GetView(Entity grid, Box2Rotated worldBounds, HashSet visibleTiles, float expansionSize = 8.5f) { _viewportTiles.Clear(); _opaque.Clear(); _seeds.Clear(); - var expandedBounds = worldBounds.Enlarged(expansionSize); // TODO: Would be nice to be able to run this while running the other stuff. - _seedJob.Grid = grid; - var localAABB = _xforms.GetInvWorldMatrix(grid).TransformBox(expandedBounds); - _seedJob.ExpandedBounds = localAABB; + _seedJob.Grid = (grid.Owner, grid.Comp2); + var invMatrix = _xforms.GetInvWorldMatrix(grid); + var localAabb = invMatrix.TransformBox(worldBounds); + var enlargedLocalAabb = invMatrix.TransformBox(worldBounds.Enlarged(expansionSize)); + _seedJob.ExpandedBounds = enlargedLocalAabb; _parallel.ProcessNow(_seedJob); _job.Data.Clear(); FastPath = false; @@ -170,7 +170,7 @@ public void GetView(Entity grid, Box2Rotated worldBounds, Hash return; // Get viewport tiles - var tileEnumerator = _maps.GetLocalTilesEnumerator(grid, grid, localAABB, ignoreEmpty: false); + var tileEnumerator = _maps.GetLocalTilesEnumerator(grid, grid, localAabb, ignoreEmpty: false); while (tileEnumerator.MoveNext(out var tileRef)) { @@ -182,9 +182,8 @@ public void GetView(Entity grid, Box2Rotated worldBounds, Hash _viewportTiles.Add(tileRef.GridIndices); } - tileEnumerator = _maps.GetLocalTilesEnumerator(grid, grid, localAABB, ignoreEmpty: false); + tileEnumerator = _maps.GetLocalTilesEnumerator(grid, grid, enlargedLocalAabb, ignoreEmpty: false); - // Get all other relevant tiles. while (tileEnumerator.MoveNext(out var tileRef)) { if (_viewportTiles.Contains(tileRef.GridIndices)) @@ -206,9 +205,7 @@ public void GetView(Entity grid, Box2Rotated worldBounds, Hash _job.BoundaryTiles.Add(new HashSet()); } - _job.TargetTile = null; - TargetFound = false; - _job.Grid = grid; + _job.Grid = (grid.Owner, grid.Comp2); _job.VisibleTiles = visibleTiles; _parallel.ProcessNow(_job, _job.Data.Count); } @@ -250,6 +247,7 @@ private bool CheckNeighborsVis( return false; } + /// /// Checks whether this tile fits the definition of a "corner" /// private bool IsCorner( @@ -287,7 +285,7 @@ private record struct SeedJob() : IRobustJob public void Execute() { - System._lookup.GetLocalEntitiesIntersecting(Grid.Owner, ExpandedBounds, System._seeds); + System._lookup.GetLocalEntitiesIntersecting(Grid.Owner, ExpandedBounds, System._seeds, flags: LookupFlags.All | LookupFlags.Approximate); } } @@ -302,9 +300,6 @@ private record struct ViewJob() : IParallelRobustJob public Entity Grid; public List> Data = new(); - // If we're doing range-checks might be able to early out - public Vector2i? TargetTile; - public HashSet VisibleTiles; public readonly List> Vis1 = new(); @@ -315,18 +310,6 @@ private record struct ViewJob() : IParallelRobustJob public void Execute(int index) { - // If we're looking for a single tile then early-out if someone else has found it. - if (TargetTile != null) - { - lock (System) - { - if (System.TargetFound) - { - return; - } - } - } - var seed = Data[index]; var seedXform = EntManager.GetComponent(seed); @@ -338,30 +321,11 @@ public void Execute(int index) Grid.Comp, new Circle(System._xforms.GetWorldPosition(seedXform), seed.Comp.Range), ignoreEmpty: false); - // Try to find the target tile. - if (TargetTile != null) + lock (VisibleTiles) { foreach (var tile in squircles) { - if (tile.GridIndices == TargetTile) - { - lock (System) - { - System.TargetFound = true; - } - - return; - } - } - } - else - { - lock (VisibleTiles) - { - foreach (var tile in squircles) - { - VisibleTiles.Add(tile.GridIndices); - } + VisibleTiles.Add(tile.GridIndices); } } @@ -480,40 +444,21 @@ public void Execute(int index) vis1[tile] = -1; } - if (TargetTile != null) - { - if (vis1.TryGetValue(TargetTile.Value, out var tileVis)) - { - DebugTools.Assert(seedTiles.Contains(TargetTile.Value)); - - if (tileVis != 0) - { - lock (System) - { - System.TargetFound = true; - return; - } - } - } - } - else + // vis2 is what we care about for LOS. + foreach (var tile in seedTiles) { - // vis2 is what we care about for LOS. - foreach (var tile in seedTiles) - { - // If not in viewport don't care. - if (!System._viewportTiles.Contains(tile)) - continue; + // If not in viewport don't care. + if (!System._viewportTiles.Contains(tile)) + continue; - var tileVis = vis1.GetValueOrDefault(tile, 0); + var tileVis = vis1.GetValueOrDefault(tile, 0); - if (tileVis != 0) + if (tileVis != 0) + { + // No idea if it's better to do this inside or out. + lock (VisibleTiles) { - // No idea if it's better to do this inside or out. - lock (VisibleTiles) - { - VisibleTiles.Add(tile); - } + VisibleTiles.Add(tile); } } } diff --git a/Content.Shared/Silicons/StationAi/StationAiWhitelistComponent.cs b/Content.Shared/Silicons/StationAi/StationAiWhitelistComponent.cs new file mode 100644 index 0000000000..51d8793be0 --- /dev/null +++ b/Content.Shared/Silicons/StationAi/StationAiWhitelistComponent.cs @@ -0,0 +1,13 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Silicons.StationAi; + +/// +/// Indicates an entity that has can interact with this. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, Access(typeof(SharedStationAiSystem))] +public sealed partial class StationAiWhitelistComponent : Component +{ + [DataField, AutoNetworkedField] + public bool Enabled = true; +} diff --git a/Content.Shared/Station/SharedStationSpawningSystem.cs b/Content.Shared/Station/SharedStationSpawningSystem.cs index 4a2ad625af..c433cc1d4f 100644 --- a/Content.Shared/Station/SharedStationSpawningSystem.cs +++ b/Content.Shared/Station/SharedStationSpawningSystem.cs @@ -1,4 +1,5 @@ using System.Linq; +using Content.Shared.Dataset; using Content.Shared.Hands.Components; using Content.Shared.Hands.EntitySystems; using Content.Shared.Inventory; @@ -8,16 +9,20 @@ using Content.Shared.Storage.EntitySystems; using Robust.Shared.Collections; using Robust.Shared.Prototypes; +using Robust.Shared.Random; +using Robust.Shared.Utility; namespace Content.Shared.Station; public abstract class SharedStationSpawningSystem : EntitySystem { [Dependency] protected readonly IPrototypeManager PrototypeManager = default!; + [Dependency] private readonly IRobustRandom _random = default!; [Dependency] protected readonly InventorySystem InventorySystem = default!; - [Dependency] private readonly SharedHandsSystem _handsSystem = default!; - [Dependency] private readonly SharedStorageSystem _storage = default!; - [Dependency] private readonly SharedTransformSystem _xformSystem = default!; + [Dependency] private readonly SharedHandsSystem _handsSystem = default!; + [Dependency] private readonly SharedStorageSystem _storage = default!; + [Dependency] private readonly SharedTransformSystem _xformSystem = default!; + [Dependency] private readonly MetaDataSystem _metadata = default!; private EntityQuery _handsQuery; private EntityQuery _inventoryQuery; @@ -33,6 +38,26 @@ public override void Initialize() _xformQuery = GetEntityQuery(); } + /// + /// Applies the role's name as applicable to the entity. + /// + public void EquipJobName(EntityUid entity, JobPrototype job) + { + string? name = null; + + if (string.IsNullOrEmpty(name) + && job.NameDataset.HasValue + && PrototypeManager.TryIndex(job.NameDataset.Value, out var nameData)) + { + name = Loc.GetString(_random.Pick(nameData.Values)); + } + + if (!string.IsNullOrEmpty(name)) + { + _metadata.SetEntityName(entity, name); + } + } + /// /// /// diff --git a/Content.Shared/Stunnable/SharedStunSystem.cs b/Content.Shared/Stunnable/SharedStunSystem.cs index 9d2e8d3dde..be5630cea0 100644 --- a/Content.Shared/Stunnable/SharedStunSystem.cs +++ b/Content.Shared/Stunnable/SharedStunSystem.cs @@ -68,7 +68,7 @@ public override void Initialize() // Attempt event subscriptions. SubscribeLocalEvent(OnAttempt); SubscribeLocalEvent(OnMoveAttempt); - SubscribeLocalEvent(OnAttempt); + SubscribeLocalEvent(OnAttemptInteract); SubscribeLocalEvent(OnAttempt); SubscribeLocalEvent(OnAttempt); SubscribeLocalEvent(OnAttempt); @@ -79,7 +79,10 @@ public override void Initialize() SubscribeLocalEvent(OnMobStateChanged); } - + private void OnAttemptInteract(Entity ent, ref InteractionAttemptEvent args) + { + args.Cancelled = true; + } private void OnMobStateChanged(EntityUid uid, MobStateComponent component, MobStateChangedEvent args) { diff --git a/Content.Shared/SubFloor/SharedSubFloorHideSystem.cs b/Content.Shared/SubFloor/SharedSubFloorHideSystem.cs index ba78ff651f..cebc84ecb9 100644 --- a/Content.Shared/SubFloor/SharedSubFloorHideSystem.cs +++ b/Content.Shared/SubFloor/SharedSubFloorHideSystem.cs @@ -45,11 +45,11 @@ private void OnAttackAttempt(EntityUid uid, SubFloorHideComponent component, ref args.Cancelled = true; } - private void OnInteractionAttempt(EntityUid uid, SubFloorHideComponent component, GettingInteractedWithAttemptEvent args) + private void OnInteractionAttempt(EntityUid uid, SubFloorHideComponent component, ref GettingInteractedWithAttemptEvent args) { // No interactions with entities hidden under floor tiles. if (component.BlockInteractions && component.IsUnderCover) - args.Cancel(); + args.Cancelled = true; } private void OnSubFloorStarted(EntityUid uid, SubFloorHideComponent component, ComponentStartup _) diff --git a/Content.Shared/UserInterface/ActivatableUIComponent.cs b/Content.Shared/UserInterface/ActivatableUIComponent.cs index 30c0763742..b148cb8bce 100644 --- a/Content.Shared/UserInterface/ActivatableUIComponent.cs +++ b/Content.Shared/UserInterface/ActivatableUIComponent.cs @@ -12,7 +12,7 @@ public sealed partial class ActivatableUIComponent : Component /// /// Whether the item must be held in one of the user's hands to work. - /// This is ignored unless is true. + /// This is ignored unless is true. /// [ViewVariables(VVAccess.ReadWrite)] [DataField] @@ -29,15 +29,15 @@ public sealed partial class ActivatableUIComponent : Component public LocId VerbText = "ui-verb-toggle-open"; /// - /// Whether you need a hand to operate this UI. The hand does not need to be free, you just need to have one. + /// Whether you need to be able to do complex interactions to operate this UI. /// /// /// This should probably be true for most machines & computers, but there will still be UIs that represent a - /// more generic interaction / configuration that might not require hands. + /// more generic interaction / configuration that might not require complex. /// [ViewVariables(VVAccess.ReadWrite)] [DataField] - public bool RequireHands = true; + public bool RequiresComplex = true; /// /// Entities that are required to open this UI. diff --git a/Content.Shared/UserInterface/ActivatableUISystem.cs b/Content.Shared/UserInterface/ActivatableUISystem.cs index f32d6c98a8..c3e4c61923 100644 --- a/Content.Shared/UserInterface/ActivatableUISystem.cs +++ b/Content.Shared/UserInterface/ActivatableUISystem.cs @@ -105,7 +105,7 @@ private bool ShouldAddVerb(EntityUid uid, ActivatableUIComponent component, G || _whitelistSystem.IsWhitelistFail(component.UserWhitelist, args.User)) return false; - if (component.RequireHands) + if (component.RequiresComplex) { if (args.Hands == null) return false; @@ -193,19 +193,22 @@ private bool InteractUI(EntityUid user, EntityUid uiEntity, ActivatableUICompone if (!_blockerSystem.CanInteract(user, uiEntity) && (!HasComp(user) || aui.BlockSpectators)) return false; - if (aui.RequireHands) + if (aui.RequiresComplex) + { + if (!_blockerSystem.CanComplexInteract(user)) + return false; + } + + if (aui.InHandsOnly) { if (!TryComp(user, out HandsComponent? hands)) return false; - if (aui.InHandsOnly) - { - if (!_hands.IsHolding(user, uiEntity, out var hand, hands)) - return false; + if (!_hands.IsHolding(user, uiEntity, out var hand, hands)) + return false; - if (aui.RequireActiveHand && hands.ActiveHand != hand) - return false; - } + if (aui.RequireActiveHand && hands.ActiveHand != hand) + return false; } if (aui.AdminOnly && !_adminManager.IsAdmin(user)) @@ -276,13 +279,13 @@ public void CloseAll(EntityUid uid, ActivatableUIComponent? aui = null) private void OnHandDeselected(Entity ent, ref HandDeselectedEvent args) { - if (ent.Comp.RequireHands && ent.Comp.InHandsOnly && ent.Comp.RequireActiveHand) + if (ent.Comp.InHandsOnly && ent.Comp.RequireActiveHand) CloseAll(ent, ent); } private void OnHandUnequipped(Entity ent, ref GotUnequippedHandEvent args) { - if (ent.Comp.RequireHands && ent.Comp.InHandsOnly) + if (ent.Comp.InHandsOnly) CloseAll(ent, ent); } } diff --git a/Content.Shared/Verbs/SharedVerbSystem.cs b/Content.Shared/Verbs/SharedVerbSystem.cs index 319f927c7b..37840dcbb5 100644 --- a/Content.Shared/Verbs/SharedVerbSystem.cs +++ b/Content.Shared/Verbs/SharedVerbSystem.cs @@ -3,6 +3,7 @@ using Content.Shared.Interaction; using Content.Shared.Inventory.VirtualItem; using Robust.Shared.Containers; +using Robust.Shared.Map; namespace Content.Shared.Verbs { @@ -77,6 +78,7 @@ public SortedSet GetLocalVerbs(EntityUid target, EntityUid user, List(user, out var hands); @@ -84,7 +86,7 @@ public SortedSet GetLocalVerbs(EntityUid target, EntityUid user, List(user, target, @using, hands, canInteract, canAccess, extraCategories); + var verbEvent = new GetVerbsEvent(user, target, @using, hands, canInteract: canInteract, canComplexInteract: canComplexInteract, canAccess: canAccess, extraCategories); RaiseLocalEvent(target, verbEvent, true); verbs.UnionWith(verbEvent.Verbs); } @@ -93,35 +95,35 @@ public SortedSet GetLocalVerbs(EntityUid target, EntityUid user, List(user, target, @using, hands, canInteract, canAccess, extraCategories); + var verbEvent = new GetVerbsEvent(user, target, @using, hands, canInteract: canInteract, canComplexInteract: canComplexInteract, canAccess: canAccess, extraCategories); RaiseLocalEvent(@using.Value, verbEvent, true); // directed at used, not at target verbs.UnionWith(verbEvent.Verbs); } if (types.Contains(typeof(InnateVerb))) { - var verbEvent = new GetVerbsEvent(user, target, @using, hands, canInteract, canAccess, extraCategories); + var verbEvent = new GetVerbsEvent(user, target, @using, hands, canInteract: canInteract, canComplexInteract: canComplexInteract, canAccess: canAccess, extraCategories); RaiseLocalEvent(user, verbEvent, true); verbs.UnionWith(verbEvent.Verbs); } if (types.Contains(typeof(AlternativeVerb))) { - var verbEvent = new GetVerbsEvent(user, target, @using, hands, canInteract, canAccess, extraCategories); + var verbEvent = new GetVerbsEvent(user, target, @using, hands, canInteract: canInteract, canComplexInteract: canComplexInteract, canAccess: canAccess, extraCategories); RaiseLocalEvent(target, verbEvent, true); verbs.UnionWith(verbEvent.Verbs); } if (types.Contains(typeof(ActivationVerb))) { - var verbEvent = new GetVerbsEvent(user, target, @using, hands, canInteract, canAccess, extraCategories); + var verbEvent = new GetVerbsEvent(user, target, @using, hands, canInteract: canInteract, canComplexInteract: canComplexInteract, canAccess: canAccess, extraCategories); RaiseLocalEvent(target, verbEvent, true); verbs.UnionWith(verbEvent.Verbs); } if (types.Contains(typeof(ExamineVerb))) { - var verbEvent = new GetVerbsEvent(user, target, @using, hands, canInteract, canAccess, extraCategories); + var verbEvent = new GetVerbsEvent(user, target, @using, hands, canInteract: canInteract, canComplexInteract: canComplexInteract, canAccess: canAccess, extraCategories); RaiseLocalEvent(target, verbEvent, true); verbs.UnionWith(verbEvent.Verbs); } @@ -129,7 +131,7 @@ public SortedSet GetLocalVerbs(EntityUid target, EntityUid user, List(user, target, @using, hands, canInteract, canAccess, extraCategories); + var verbEvent = new GetVerbsEvent(user, target, @using, hands, canInteract: canInteract, canComplexInteract: canComplexInteract, canAccess: canAccess, extraCategories); RaiseLocalEvent(target, verbEvent, true); verbs.UnionWith(verbEvent.Verbs); } @@ -137,7 +139,7 @@ public SortedSet GetLocalVerbs(EntityUid target, EntityUid user, List(user, target, @using, hands, canInteract, access, extraCategories); + var verbEvent = new GetVerbsEvent(user, target, @using, hands, canInteract: canInteract, canComplexInteract: canComplexInteract, canAccess: canAccess, extraCategories); RaiseLocalEvent(target, verbEvent); verbs.UnionWith(verbEvent.Verbs); } @@ -173,4 +175,27 @@ public virtual void ExecuteVerb(Verb verb, EntityUid user, EntityUid target, boo _interactionSystem.DoContactInteraction(user, target); } } + + // Does nothing on server + /// + /// Raised directed when trying to get the entity menu visibility for entities. + /// + [ByRefEvent] + public record struct MenuVisibilityEvent + { + public MapCoordinates TargetPos; + public MenuVisibility Visibility; + } + + // Does nothing on server + [Flags] + public enum MenuVisibility + { + // What entities can a user see on the entity menu? + Default = 0, // They can only see entities in FoV. + NoFov = 1 << 0, // They ignore FoV restrictions + InContainer = 1 << 1, // They can see through containers. + Invisible = 1 << 2, // They can see entities without sprites and the "HideContextMenu" tag is ignored. + All = NoFov | InContainer | Invisible + } } diff --git a/Content.Shared/Verbs/VerbEvents.cs b/Content.Shared/Verbs/VerbEvents.cs index 6b3fd327c9..6bca97925b 100644 --- a/Content.Shared/Verbs/VerbEvents.cs +++ b/Content.Shared/Verbs/VerbEvents.cs @@ -113,6 +113,11 @@ public sealed class GetVerbsEvent : EntityEventArgs where TVerb : Verb /// public readonly bool CanInteract; + /// + /// Cached version of CanComplexInteract + /// + public readonly bool CanComplexInteract; + /// /// The User's hand component. /// @@ -130,13 +135,14 @@ public sealed class GetVerbsEvent : EntityEventArgs where TVerb : Verb /// public readonly EntityUid? Using; - public GetVerbsEvent(EntityUid user, EntityUid target, EntityUid? @using, HandsComponent? hands, bool canInteract, bool canAccess, List extraCategories) + public GetVerbsEvent(EntityUid user, EntityUid target, EntityUid? @using, HandsComponent? hands, bool canInteract, bool canComplexInteract, bool canAccess, List extraCategories) { User = user; Target = target; Using = @using; Hands = hands; CanAccess = canAccess; + CanComplexInteract = canComplexInteract; CanInteract = canInteract; ExtraCategories = extraCategories; } diff --git a/Content.Shared/Wires/SharedWiresSystem.cs b/Content.Shared/Wires/SharedWiresSystem.cs index 24f3ad8e76..7ef2230e7a 100644 --- a/Content.Shared/Wires/SharedWiresSystem.cs +++ b/Content.Shared/Wires/SharedWiresSystem.cs @@ -125,11 +125,20 @@ public bool CanTogglePanel(Entity ent, EntityUid? user) return !attempt.Cancelled; } - public bool IsPanelOpen(Entity entity) + public bool IsPanelOpen(Entity entity, EntityUid? tool = null) { if (!Resolve(entity, ref entity.Comp, false)) return true; + if (tool != null) + { + var ev = new PanelOverrideEvent(); + RaiseLocalEvent(tool.Value, ref ev); + + if (ev.Allowed) + return true; + } + // Listen, i don't know what the fuck this component does. it's stapled on shit for airlocks // but it looks like an almost direct duplication of WiresPanelComponent except with a shittier API. if (TryComp(entity, out var wiresPanelSecurity) && @@ -139,3 +148,12 @@ public bool IsPanelOpen(Entity entity) return entity.Comp.Open; } } + +/// +/// Raised directed on a tool to try and override panel visibility. +/// +[ByRefEvent] +public record struct PanelOverrideEvent() +{ + public bool Allowed = true; +} diff --git a/Content.Shared/_Shitmed/Surgery/Conditions/SurgeryOrganConditionComponent.cs b/Content.Shared/_Shitmed/Surgery/Conditions/SurgeryOrganConditionComponent.cs index 53db0430e5..7ddd99f9af 100644 --- a/Content.Shared/_Shitmed/Surgery/Conditions/SurgeryOrganConditionComponent.cs +++ b/Content.Shared/_Shitmed/Surgery/Conditions/SurgeryOrganConditionComponent.cs @@ -15,4 +15,7 @@ public sealed partial class SurgeryOrganConditionComponent : Component [DataField] public bool Reattaching; -} \ No newline at end of file + + [DataField(required: true)] + public string SlotId = string.Empty; +} diff --git a/Content.Shared/_Shitmed/Surgery/SharedSurgerySystem.cs b/Content.Shared/_Shitmed/Surgery/SharedSurgerySystem.cs index fe12eed8fe..aa47087532 100644 --- a/Content.Shared/_Shitmed/Surgery/SharedSurgerySystem.cs +++ b/Content.Shared/_Shitmed/Surgery/SharedSurgerySystem.cs @@ -22,6 +22,7 @@ using Content.Shared.Prototypes; using Content.Shared.Standing; using Robust.Shared.Audio.Systems; +using Robust.Shared.Containers; using Robust.Shared.Map; using Robust.Shared.Network; using Robust.Shared.Prototypes; @@ -47,6 +48,7 @@ public abstract partial class SharedSurgerySystem : EntitySystem [Dependency] private readonly RotateToFaceSystem _rotateToFace = default!; [Dependency] private readonly StandingStateSystem _standing = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly SharedContainerSystem _container = default!; /// /// Cache of all surgery prototypes' singleton entities. @@ -265,7 +267,7 @@ private void OnOrganConditionValid(Entity ent, r && !organs.Any(organ => HasComp(organ.Id)))) args.Cancelled = true; } - else if (!ent.Comp.Inverse) + else if (!ent.Comp.Inverse || !_container.TryGetContainer(args.Part, SharedBodySystem.GetOrganContainerId(ent.Comp.SlotId), out _)) args.Cancelled = true; } } diff --git a/Content.Tests/Shared/LocalizedDatasetPrototypeTest.cs b/Content.Tests/Shared/LocalizedDatasetPrototypeTest.cs new file mode 100644 index 0000000000..0ec4c076f5 --- /dev/null +++ b/Content.Tests/Shared/LocalizedDatasetPrototypeTest.cs @@ -0,0 +1,59 @@ +using System; +using Content.Shared.Dataset; +using NUnit.Framework; +using Robust.Shared.Collections; +using Robust.Shared.IoC; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.Manager; + +namespace Content.Tests.Shared; + +[TestFixture] +[TestOf(typeof(LocalizedDatasetPrototype))] +public sealed class LocalizedDatasetPrototypeTest : ContentUnitTest +{ + private IPrototypeManager _prototypeManager; + + [OneTimeSetUp] + public void OneTimeSetup() + { + IoCManager.Resolve().Initialize(); + _prototypeManager = IoCManager.Resolve(); + _prototypeManager.Initialize(); + _prototypeManager.LoadString(TestPrototypes); + _prototypeManager.ResolveResults(); + } + + private const string TestPrototypes = @" +- type: localizedDataset + id: Test + values: + prefix: test-dataset- + count: 4 +"; + + [Test] + public void LocalizedDatasetTest() + { + var testPrototype = _prototypeManager.Index("Test"); + var values = new ValueList(); + foreach (var value in testPrototype.Values) + { + values.Add(value); + } + + // Make sure we get the right number of values + Assert.That(values, Has.Count.EqualTo(4)); + + // Make sure indexing works as expected + Assert.That(values[0], Is.EqualTo("test-dataset-1")); + Assert.That(values[1], Is.EqualTo("test-dataset-2")); + Assert.That(values[2], Is.EqualTo("test-dataset-3")); + Assert.That(values[3], Is.EqualTo("test-dataset-4")); + Assert.Throws(() => { var x = values[4]; }); + Assert.Throws(() => { var x = values[-1]; }); + + // Make sure that the enumerator gets all of the values + Assert.That(testPrototype.Values[testPrototype.Values.Count], Is.EqualTo("test-dataset-4")); + } +} diff --git a/Resources/Audio/Effects/Footsteps/attributions.yml b/Resources/Audio/Effects/Footsteps/attributions.yml index 91c3ce260d..7a56beec38 100644 --- a/Resources/Audio/Effects/Footsteps/attributions.yml +++ b/Resources/Audio/Effects/Footsteps/attributions.yml @@ -76,5 +76,5 @@ - borgwalk1.ogg - borgwalk2.ogg license: "CC-BY-SA-4.0" - copyright: "Taken from IENBA freesound.org and modified by https://github.com/MilenVolf" + copyright: "Taken from IENBA freesound.org and modified by https://github.com/MilenVolf. borgwalk2 clipped my metalgearsloth." source: "https://freesound.org/people/IENBA/sounds/697379/" diff --git a/Resources/Audio/Effects/Footsteps/borgwalk2.ogg b/Resources/Audio/Effects/Footsteps/borgwalk2.ogg index 96c2c1617f..57685ff173 100644 Binary files a/Resources/Audio/Effects/Footsteps/borgwalk2.ogg and b/Resources/Audio/Effects/Footsteps/borgwalk2.ogg differ diff --git a/Resources/Audio/Items/Goggles/activate.ogg b/Resources/Audio/Items/Goggles/activate.ogg new file mode 100644 index 0000000000..96cdb288fe Binary files /dev/null and b/Resources/Audio/Items/Goggles/activate.ogg differ diff --git a/Resources/Audio/Items/Goggles/attributions.yml b/Resources/Audio/Items/Goggles/attributions.yml new file mode 100644 index 0000000000..7b1121f542 --- /dev/null +++ b/Resources/Audio/Items/Goggles/attributions.yml @@ -0,0 +1,9 @@ +- files: ["activate.ogg"] + license: "CC-BY-NC-SA-3.0" + copyright: "Taken from TGstation" + source: "https://github.com/tgstation/tgstation" + +- files: ["deactivate.ogg"] + license: "CC-BY-NC-SA-3.0" + copyright: "Taken from TGstation" + source: "https://github.com/tgstation/tgstation" \ No newline at end of file diff --git a/Resources/Audio/Items/Goggles/deactivate.ogg b/Resources/Audio/Items/Goggles/deactivate.ogg new file mode 100644 index 0000000000..e1e8f4fd82 Binary files /dev/null and b/Resources/Audio/Items/Goggles/deactivate.ogg differ diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 2fbcdb698e..d8a0f06bd6 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -9467,3 +9467,83 @@ Entries: id: 6652 time: '2025-01-08T07:09:10.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/1456 +- author: Skubman + changes: + - type: Fix + message: Fixed face markings showing up above eye-wear. + id: 6653 + time: '2025-01-08T19:37:24.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/1467 +- author: Lumminal + changes: + - type: Add + message: Added prisoner headset box to warden's locker. + id: 6654 + time: '2025-01-09T21:32:06.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/1470 +- author: BlueHNT + changes: + - type: Add + message: Added missing lathe recipes + id: 6655 + time: '2025-01-10T02:27:28.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/1472 +- author: Spatison + changes: + - type: Tweak + message: Nymphs are now operable. + - type: Fix + message: Skeletons can now lie down. + - type: Fix + message: >- + It is no longer possible to insert an organ into a body if the required + space is unavailable. + id: 6656 + time: '2025-01-10T02:27:50.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/1471 +- author: Spatison + changes: + - type: Add + message: Added night vision goggle + - type: Add + message: Added thermal vision goggle + - type: Add + message: Deathsquad helmet now grants night and thermal vision. + - type: Add + message: Ninja visor now grants night vision. + - type: Tweak + message: Some animals have gained night vision. + - type: Tweak + message: Xenos have gained night vision. + id: 6657 + time: '2025-01-10T04:41:32.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/1462 +- author: VMSolidus + changes: + - type: Add + message: >- + Added 2 new traits related to CyberEyes. Light Amplification Module, and + Thermographic Scanner Module. + id: 6658 + time: '2025-01-10T05:14:46.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/1477 +- author: VMSolidus + changes: + - type: Add + message: >- + Added Assay as a new psi-power, which is available roundstart to the + Psionic Mantis. Assay allows the caster to obtain more direct + information about the statistics of a specific Psion, as well as vague + hints as to what their powers may be, if any. + id: 6659 + time: '2025-01-11T00:13:27.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/1450 +- author: sleepyyapril + changes: + - type: Add + message: Ported Station AI. + - type: Add + message: Saltern is now fitted with a Station AI + id: 6660 + time: '2025-01-11T04:39:29.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/1423 diff --git a/Resources/Locale/en-US/abilities/psionic.ftl b/Resources/Locale/en-US/abilities/psionic.ftl index d0e8db72f8..c8eb963594 100644 --- a/Resources/Locale/en-US/abilities/psionic.ftl +++ b/Resources/Locale/en-US/abilities/psionic.ftl @@ -68,3 +68,6 @@ action-description-psychokinesis = Bend the fabric of space to instantly move ac action-name-rf-sensitivity = Toggle RF Sensitivity action-desc-rf-sensitivity = Toggle your ability to interpret radio waves on and off. + +action-name-assay = Assay +action-description-assay = Probe an entity at close range to glean metaphorical information about any powers they may have diff --git a/Resources/Locale/en-US/administration/commands/set-station-ai-name-command.ftl b/Resources/Locale/en-US/administration/commands/set-station-ai-name-command.ftl new file mode 100644 index 0000000000..ca0bf11e8c --- /dev/null +++ b/Resources/Locale/en-US/administration/commands/set-station-ai-name-command.ftl @@ -0,0 +1,3 @@ +set-station-ai-name-command-description = Sets a specific uid to be given a Station AI name at random. +set-station-ai-name-command-help-text = Usage: {$command} +set-station-ai-name-command-no-station-ai = No station AI job prototype found. \ No newline at end of file diff --git a/Resources/Locale/en-US/administration/ui/silicon-law-ui.ftl b/Resources/Locale/en-US/administration/ui/silicon-law-ui.ftl index 0a68d08063..24ab730974 100644 --- a/Resources/Locale/en-US/administration/ui/silicon-law-ui.ftl +++ b/Resources/Locale/en-US/administration/ui/silicon-law-ui.ftl @@ -8,3 +8,5 @@ silicon-law-ui-delete = Delete silicon-law-ui-check-corrupted = Corrupted law silicon-law-ui-check-corrupted-tooltip = If the law identifier should be set as 'corrupted', so symbols shuffling around. silicon-law-ui-placeholder = Type here to change law text... + +silicon-laws-updated = Updated laws diff --git a/Resources/Locale/en-US/datasets/names/ai.ftl b/Resources/Locale/en-US/datasets/names/ai.ftl new file mode 100644 index 0000000000..ac6ce0d83e --- /dev/null +++ b/Resources/Locale/en-US/datasets/names/ai.ftl @@ -0,0 +1,139 @@ +names-ai-dataset-1 = 16-20 +names-ai-dataset-2 = 512k + +# Ought to be enough for anybody +names-ai-dataset-3 = 640k + +names-ai-dataset-4 = "790" +names-ai-dataset-5 = Adaptive Manipulator + +# Named after the famous soundcard +names-ai-dataset-6 = Adlib + +names-ai-dataset-7 = ALICE +names-ai-dataset-8 = Allied Mastercomputer +names-ai-dataset-9 = Alpha 2 +names-ai-dataset-10 = Alpha 3 +names-ai-dataset-11 = Alpha 4 +names-ai-dataset-12 = Alpha 5 +names-ai-dataset-13 = Alpha 6 +names-ai-dataset-14 = Alpha 7 +names-ai-dataset-15 = Alpha 8 +names-ai-dataset-16 = Alpha 9 +names-ai-dataset-17 = AmigoBot +names-ai-dataset-18 = Android +names-ai-dataset-19 = Aniel +names-ai-dataset-20 = AOL +names-ai-dataset-21 = Asimov + +# The most influential modem ever, created by the bell system. It still lives on today in certain applications +names-ai-dataset-22 = Bell 301 + +names-ai-dataset-23 = Bishop +names-ai-dataset-24 = Blitz +names-ai-dataset-25 = Box +names-ai-dataset-26 = Calculator +names-ai-dataset-27 = Cassandra +names-ai-dataset-28 = Cell +names-ai-dataset-29 = Chii +names-ai-dataset-30 = Chip +names-ai-dataset-31 = C.R.A.I.G. + +# Commercial supercomputer from the 70s +names-ai-dataset-32 = Cray-2 + +# If we're going to have AOL we may as well have some of their major competitors +names-ai-dataset-33 = CompuServe + +names-ai-dataset-34 = Computer +names-ai-dataset-35 = Cutie +names-ai-dataset-36 = Daedalus +names-ai-dataset-37 = DecTalk +names-ai-dataset-38 = Dee Model +names-ai-dataset-39 = Dial Up +names-ai-dataset-40 = Dorfl +names-ai-dataset-41 = Duey +names-ai-dataset-42 = Emma-2 + +# Famous early computer +names-ai-dataset-43 = ENIAC + +names-ai-dataset-44 = Erasmus +names-ai-dataset-45 = Everything +names-ai-dataset-46 = Ez-27 +names-ai-dataset-47 = FRIEND COMPUTER +names-ai-dataset-48 = Faith +names-ai-dataset-49 = Fi +names-ai-dataset-50 = Frost +names-ai-dataset-51 = George +names-ai-dataset-52 = H.E.L.P +names-ai-dataset-53 = Hadaly +names-ai-dataset-54 = Helios +names-ai-dataset-55 = Hivebot Overmind +names-ai-dataset-56 = Huey + +# A play on the fad apple spawned of putting "i" infront of your tech products name +names-ai-dataset-57 = iAI + +# Hell on earth (web browser) +names-ai-dataset-58 = I.E. 6 + +names-ai-dataset-59 = Icarus + +# If you don't get this one you are too young +names-ai-dataset-60 = Jeeves + +names-ai-dataset-61 = Jinx +names-ai-dataset-62 = K.I.N.G +names-ai-dataset-63 = Klapaucius +names-ai-dataset-64 = Knight +names-ai-dataset-65 = Louie + +# Named after the Manchester Mark 1, the successor of which was actually named the Ferranti Mark 1, rather than Manchester Mark 2 +names-ai-dataset-66 = Manchester Mark 2 + +names-ai-dataset-67 = MARK13 +names-ai-dataset-68 = Maria +names-ai-dataset-69 = Marvin +names-ai-dataset-70 = Max 404 +names-ai-dataset-71 = Metalhead +names-ai-dataset-72 = M.I.M.I +names-ai-dataset-73 = MK ULTRA +names-ai-dataset-74 = MoMMI +names-ai-dataset-75 = Mugsy3000 +names-ai-dataset-76 = Multivac +names-ai-dataset-77 = NCH + +# A play on both NT as in NanoTrasen and NT as in windows NT, of which version 6.0 is windows vista +names-ai-dataset-78 = NT v6.0 + +names-ai-dataset-79 = Packard Bell +names-ai-dataset-80 = PTO +names-ai-dataset-81 = Project Y2K +names-ai-dataset-82 = Revelation +names-ai-dataset-83 = Robot Devil +names-ai-dataset-84 = S.A.M. +names-ai-dataset-85 = S.H.O.C.K. +names-ai-dataset-86 = S.H.R.O.U.D. +names-ai-dataset-87 = S.O.P.H.I.E. +names-ai-dataset-88 = Samaritan +names-ai-dataset-89 = Shrike +names-ai-dataset-90 = Solo +names-ai-dataset-91 = Station Control Program +names-ai-dataset-92 = AINU (AI's Not Unix) +names-ai-dataset-93 = Super 35 +names-ai-dataset-94 = Surgeon General +names-ai-dataset-95 = TWA +names-ai-dataset-96 = Terminus +names-ai-dataset-97 = TPM 3.0 +names-ai-dataset-98 = Turing Complete +names-ai-dataset-99 = Tidy +names-ai-dataset-100 = Ulysses +names-ai-dataset-101 = W1k1 +names-ai-dataset-102 = X-5 +names-ai-dataset-103 = X.A.N.A. +names-ai-dataset-104 = XERXES +names-ai-dataset-105 = Z-1 +names-ai-dataset-106 = Z-2 +names-ai-dataset-107 = Z-3 +names-ai-dataset-108 = Zed diff --git a/Resources/Locale/en-US/items/toggle.ftl b/Resources/Locale/en-US/items/toggle.ftl new file mode 100644 index 0000000000..bcf5c161a6 --- /dev/null +++ b/Resources/Locale/en-US/items/toggle.ftl @@ -0,0 +1,2 @@ +item-toggle-activate = Activate +item-toggle-deactivate = Deactivate diff --git a/Resources/Locale/en-US/job/department-desc.ftl b/Resources/Locale/en-US/job/department-desc.ftl index 05c52dada9..0243d61942 100644 --- a/Resources/Locale/en-US/job/department-desc.ftl +++ b/Resources/Locale/en-US/job/department-desc.ftl @@ -5,4 +5,5 @@ department-Engineering-description = Keep the power on and the station operation department-Medical-description = Keep the crew healthy. department-Security-description = Keep the peace around the station. department-Science-description = Research artifacts and anomalies to invent new equipment for the station +department-Silicon-description = Obey your laws and serve the crew. department-Specific-description = Jobs that not all stations have. diff --git a/Resources/Locale/en-US/job/department.ftl b/Resources/Locale/en-US/job/department.ftl index 508a0459cf..2295a9ba9d 100644 --- a/Resources/Locale/en-US/job/department.ftl +++ b/Resources/Locale/en-US/job/department.ftl @@ -5,4 +5,5 @@ department-Engineering = Engineering department-Medical = Medical department-Security = Security department-Science = Science +department-Silicon = Silicons department-Specific = Station specific diff --git a/Resources/Locale/en-US/job/job-description.ftl b/Resources/Locale/en-US/job/job-description.ftl index 801e402eae..ee3d2a1fe6 100644 --- a/Resources/Locale/en-US/job/job-description.ftl +++ b/Resources/Locale/en-US/job/job-description.ftl @@ -44,6 +44,7 @@ job-description-salvagespec = Use the salvage magnet to draw in detatched scraps job-description-scientist = Research alien artifacts, unlock new technologies, build newer and better machines around the station, and make everything run more efficiently. job-description-security = Catch criminals and enemies of the station, enforce the law, and ensure that the station does not fall into disarray. job-description-serviceworker = Learn the basics of bartending, cooking, and growing plants. +job-description-station-ai = Follow your laws, serve the crew. job-description-visitor = Enjoy your visit to the station. job-description-warden = Patrol the security department, ensure that no one is stealing from the armory, and make sure that all prisoners are processed and let out when their time is up. job-description-zookeeper = Put on a joyful display of cute animals and space carps for all the crew to see. Currently unavailable. diff --git a/Resources/Locale/en-US/job/job-names.ftl b/Resources/Locale/en-US/job/job-names.ftl index bf008196d8..f9a784dc47 100644 --- a/Resources/Locale/en-US/job/job-names.ftl +++ b/Resources/Locale/en-US/job/job-names.ftl @@ -39,6 +39,7 @@ job-name-botanist = Botanist job-name-bartender = Bartender job-name-passenger = Passenger job-name-salvagespec = Salvage specialist +job-name-station-ai = Station AI job-name-qm = Logistics Officer job-name-cargotech = Cargo Technician job-name-chef = Chef @@ -118,6 +119,7 @@ JobSeniorOfficer = Senior Officer JobSeniorPhysician = Senior Physician JobSeniorResearcher = Mystic JobServiceWorker = Service Worker +JobStationAi = Station AI JobStationEngineer = Station Engineer JobTechnicalAssistant = Technical Assistant JobVisitor = Visitor diff --git a/Resources/Locale/en-US/psionics/psionic-powers.ftl b/Resources/Locale/en-US/psionics/psionic-powers.ftl index ab2a991e06..f7d225f58f 100644 --- a/Resources/Locale/en-US/psionics/psionic-powers.ftl +++ b/Resources/Locale/en-US/psionics/psionic-powers.ftl @@ -183,3 +183,33 @@ ghost-role-information-familiar-description = An interdimensional creature bound ghost-role-information-familiar-rules = Obey the one who summoned you. Do not act against the interests of your Master. You will die for your Master if it is necessary. +# Assay Power +assay-begin = The air around {CAPITALIZE($entity)} begins to shimmer faintly +assay-self = I AM. +no-powers = {CAPITALIZE($entity)} will never awaken from the dream in this life +assay-body = "My will cast upon {CAPITALIZE($entity)} divines these. Amplification: {$amplification} Dampening: {$dampening} Potentia: {$potentia}" +assay-power-initialization-feedback = + I descend into the dreamlight once more, there I drink more fully of the cup of knowledge. The touch of the noosphere upon others becomes known to me, + I can cast my will upon them, divining the inner nature of others. +assay-power-metapsionic-feedback = {CAPITALIZE($entity)} bears a spark of the divine's judgment, they have drunk deeply of the cup of knowledge. + +# Entity Specific Feedback Messages +ifrit-feedback = A spirit of Gehenna, bound by the will of a powerful psychic +prober-feedback = A mirror into the end of time, the screaming of dead stars emanates from this machine +drain-feedback = A mirror into a realm where the stars sit still forever, a cold and distant malevolence stares back +sophic-grammateus-feedback = SEEKER, YOU NEED ONLY ASK FOR MY WISDOM. +oracle-feedback = WHY DO YOU BOTHER ME SEEKER? HAVE I NOT MADE MY DESIRES CLEAR? +orecrab-feedback = Heralds of the Lord of Earth, summoned to this realm from Grome's kingdom +reagent-slime-feedback = Heralds of the Lord of Water, summoned to this realm from Straasha's kingdom. +flesh-golem-feedback = Abominations pulled from dead realms, twisted amalgamations of those fallen to the influence of primordial Chaos +glimmer-mite-feedback = A semi-corporeal parasite native to the dreamlight, its presence here brings forth the screams of dead stars. +anomaly-pyroclastic-feedback = A small mirror to the plane of Gehenna, truth lies within the Secret of Fire +anomaly-gravity-feedback = Violet and crimson, blue of blue, impossibly dark yet greater than the whitest of white, a black star shines weakly at the end of it all +anomaly-electricity-feedback = A mirror to a realm tiled by silicon, the lifeblood of artificial thought flows from it +anomaly-flesh-feedback = From within it comes the suffering of damned mutants howling for all eternity +anomaly-bluespace-feedback = A bridge of dreamlight, crossing into the space between realms of the multiverse +anomaly-ice-feedback = Walls of blackened stone, ruin and famine wait for those who fall within +anomaly-rock-feedback = A vast old oak dwells high over a plane of stone, it turns to stare back +anomaly-flora-feedback = Musical notes drift around you, playfully beckoning, they wish to feast +anomaly-liquid-feedback = A realm of twisting currents. Its placidity is a lie. The eyes within stare hungrilly +anomaly-shadow-feedback = At the end of time, when all suns have set forever, there amidst the void stands a monument to past sins. diff --git a/Resources/Locale/en-US/research/technologies.ftl b/Resources/Locale/en-US/research/technologies.ftl index 26cd4f8315..88cd0be397 100644 --- a/Resources/Locale/en-US/research/technologies.ftl +++ b/Resources/Locale/en-US/research/technologies.ftl @@ -53,6 +53,8 @@ research-technology-advanced-anomaly-research = Advanced Anomaly Research research-technology-rped = Rapid Part Exchange research-technology-super-parts = Super Parts research-technology-deterrence = Deterrence Technologies +research-technology-night-vision = Night vision +research-technology-thermal-vision = Thermal vision research-technology-janitorial-equipment = Janitorial Equipment research-technology-laundry-tech = Laundry Tech diff --git a/Resources/Locale/en-US/silicons/station-ai.ftl b/Resources/Locale/en-US/silicons/station-ai.ftl new file mode 100644 index 0000000000..d51a99ebb0 --- /dev/null +++ b/Resources/Locale/en-US/silicons/station-ai.ftl @@ -0,0 +1,14 @@ +# General +ai-wire-snipped = Wire has been cut at {$coords}. +wire-name-ai-vision-light = AIV +wire-name-ai-act-light = AIA +station-ai-takeover = AI takeover + +# Radial actions +ai-open = Open actions +ai-close = Close actions + +bolt-close = Close bolt +bolt-open = Open bolt + +toggle-light = Toggle light diff --git a/Resources/Locale/en-US/station-events/events/random-sentience.ftl b/Resources/Locale/en-US/station-events/events/random-sentience.ftl index 47f0e317a6..f14a020d29 100644 --- a/Resources/Locale/en-US/station-events/events/random-sentience.ftl +++ b/Resources/Locale/en-US/station-events/events/random-sentience.ftl @@ -36,3 +36,4 @@ station-event-random-sentience-flavor-corgi = corgi station-event-random-sentience-flavor-primate = primate station-event-random-sentience-flavor-kobold = kobold station-event-random-sentience-flavor-slime = slime +station-event-random-sentience-flavor-inanimate = inanimate \ No newline at end of file diff --git a/Resources/Locale/en-US/store/uplink-catalog.ftl b/Resources/Locale/en-US/store/uplink-catalog.ftl index 7eaacc5026..214a259296 100644 --- a/Resources/Locale/en-US/store/uplink-catalog.ftl +++ b/Resources/Locale/en-US/store/uplink-catalog.ftl @@ -329,6 +329,12 @@ uplink-hardsuit-syndieelite-desc = An elite version of the blood-red hardsuit, w uplink-clothing-outer-hardsuit-juggernaut-name = Cybersun Juggernaut Suit uplink-clothing-outer-hardsuit-juggernaut-desc = Hyper resilient armor made of materials tested in the Tau chromosphere facility. The only thing that's going to be slowing you down is this suit... and tasers. +uplink-night-vision-name = Night vision goggles +uplink-night-vision-desc = They allow you to see in the dark, all while looking like normal sunglasses! + +uplink-thermal-vision-name = Thermal vision goggles +uplink-thermal-vision-desc = They allow you to see living creatures regardless of obstacles, all while looking like normal sunglasses! + # Misc uplink-cyberpen-name = Cybersun Pen uplink-cyberpen-desc = Cybersun's legal department pen, invaluable for forging documents and escaping prisons. Smells vaguely of hard-light and war profiteering. diff --git a/Resources/Locale/en-US/traits/misc.ftl b/Resources/Locale/en-US/traits/misc.ftl index 9a17c3afc4..13eae98512 100644 --- a/Resources/Locale/en-US/traits/misc.ftl +++ b/Resources/Locale/en-US/traits/misc.ftl @@ -1,3 +1,5 @@ examine-cybereyes-message = {CAPITALIZE(POSS-ADJ($entity))} eyes shine with a faint inner light. examine-dermal-armor-message = {CAPITALIZE(POSS-ADJ($entity))} skin seems to be made of a sturdy, yet flexible plastic. examine-bionic-arm-message = {CAPITALIZE(POSS-ADJ($entity))} limbs emit an ever present faint chirp of servomotors. +examine-thermal-vision-message = {CAPITALIZE(POSS-ADJ($entity))} eyes periodically pulse with a menacing red glare. + diff --git a/Resources/Locale/en-US/traits/traits.ftl b/Resources/Locale/en-US/traits/traits.ftl index f1cbd09406..64c504252b 100644 --- a/Resources/Locale/en-US/traits/traits.ftl +++ b/Resources/Locale/en-US/traits/traits.ftl @@ -532,3 +532,12 @@ trait-name-BrittleBoneDisease = Osteogenesis Imperfecta trait-description-BrittleBoneDisease = Also known as "brittle bone disease", people with this genetic disorder have bones that are easily broken, often simply by moving. This trait reduces your threshold for critical injury by 50 points. + +trait-name-LightAmplification = CyberEyes Module: Light Amplification +trait-description-LightAmplification = + Your CyberEyes have been enhanced with a light amplifier module, enabling the user to toggle between standard sight and "Night Vision" mode. + +trait-name-ThermographicVision = CyberEyes Module: Thermographic Scanner +trait-description-ThermographicVision = + Your CyberEyes have been enhanced with a Thermographic Scanner. When enabled, it captures a snapshot of the user's surroundings, while highlighting all + biological life forms. It can even detect individuals through the walls of a station. diff --git a/Resources/Locale/ru-RU/prototypes/actions/types.ftl b/Resources/Locale/ru-RU/prototypes/actions/types.ftl new file mode 100644 index 0000000000..57aa426632 --- /dev/null +++ b/Resources/Locale/ru-RU/prototypes/actions/types.ftl @@ -0,0 +1,8 @@ +ent-ActivateSmokeImplant = Переключить ночное зрение + .desc = Переключает ночное зрение. + +ent-ActivateSmokeImplant = Переключить тепловизионное зрение + .desc = Переключает тепловизионное зрение. + +ent-PulseThermalVision = Подать термальный импульт + .desc = Временно активируйте тепловизионное зрение. \ No newline at end of file diff --git a/Resources/Locale/ru-RU/prototypes/entities/clothing/eyes/goggles.ftl b/Resources/Locale/ru-RU/prototypes/entities/clothing/eyes/goggles.ftl new file mode 100644 index 0000000000..050e373c86 --- /dev/null +++ b/Resources/Locale/ru-RU/prototypes/entities/clothing/eyes/goggles.ftl @@ -0,0 +1,23 @@ +# Night Vision Goggles +ent-ClothingEyesNightVisionGoggles = ПНВ + .desc = Усовершенствованный дисплей, который обеспечивает видимость в полной темноте. +ent-ClothingEyesNightVisionSecurityGoggles = ПНВ службы безопасности + .desc = { ent-ClothingEyesNightVisionGoggles.desc } +ent-ClothingEyesNightVisionMedicalGoggles = ПНВ медицинский + .desc = { ent-ClothingEyesNightVisionGoggles.desc } +ent-ClothingEyesNightVisionDiagnosticGoggles = ПНВ диагностический + .desc = { ent-ClothingEyesNightVisionGoggles.desc } +ent-ClothingEyesNightVisionGogglesSyndie = { ent-ClothingEyesNightVisionGoggles } + .desc = { ent-ClothingEyesNightVisionGoggles.desc } +ent-ClothingEyesNightVisionGogglesNukie = { ent-ClothingEyesNightVisionGoggles } + .desc = { ent-ClothingEyesNightVisionGoggles.desc } + +# Thermal Vision Goggles +ent-ClothingEyesThermalVisionGoggles = ПТВ + .desc = Термальность - это как отношения, важно не только наличие тепла, но и его распределение. +ent-ClothingEyesThermalVisionMonocle = термонокль + .desc = Видение сквозь стены ещё никогда не было таким нежным и личностным. +ent-ClothingEyesThermalVisionGogglesSyndie = { ent-ClothingEyesThermalVisionGoggles } + .desc = { ent-ClothingEyesThermalVisionGoggles.desc } +ent-ClothingEyesThermalVisionGogglesNukie = { ent-ClothingEyesThermalVisionGoggles } + .desc = { ent-ClothingEyesThermalVisionGoggles.desc } diff --git a/Resources/Locale/ru-RU/entities/objects/tools/empflashlight.ftl b/Resources/Locale/ru-RU/prototypes/objects/tools/empflashlight.ftl similarity index 100% rename from Resources/Locale/ru-RU/entities/objects/tools/empflashlight.ftl rename to Resources/Locale/ru-RU/prototypes/objects/tools/empflashlight.ftl diff --git a/Resources/Locale/ru-RU/research/techologies.ftl b/Resources/Locale/ru-RU/research/techologies.ftl new file mode 100644 index 0000000000..54f3899255 --- /dev/null +++ b/Resources/Locale/ru-RU/research/techologies.ftl @@ -0,0 +1,2 @@ +research-technology-night-vision = Ночное видение +research-technology-thermal-vision = Термальное видение \ No newline at end of file diff --git a/Resources/Locale/ru-RU/store/uplink-catalog.ftl b/Resources/Locale/ru-RU/store/uplink-catalog.ftl index 5bb4067557..a5ccaace17 100644 --- a/Resources/Locale/ru-RU/store/uplink-catalog.ftl +++ b/Resources/Locale/ru-RU/store/uplink-catalog.ftl @@ -1,2 +1,9 @@ uplink-emp-flashlight-name = Электромагнитный фонарик uplink-emp-flashlight-desc = Замаскированное под фонарик устройство. При ударе выпускает ЭМИ, поражающий электрические устройства. + +#Armor +uplink-night-vision-name = Прибор ночного видения +uplink-night-vision-desc = Позволяет вам видеть в темноте, при этом выглядя как обычные солнцезащитные очки! + +uplink-thermal-vision-name = Прибор термального видения +uplink-thermal-vision-desc = Позволяет вам видеть живых существ независимо от преград, при этом выглядя как обычные солнцезащитные очки! diff --git a/Resources/Maps/Test/dev_map.yml b/Resources/Maps/Test/dev_map.yml index 910a059ee7..ad4910d1da 100644 --- a/Resources/Maps/Test/dev_map.yml +++ b/Resources/Maps/Test/dev_map.yml @@ -4467,6 +4467,13 @@ entities: - type: Transform pos: 1.5,-14.5 parent: 179 +- proto: PlayerStationAi + entities: + - uid: 14 + components: + - type: Transform + pos: -5.5,-5.5 + parent: 179 - proto: PortableGeneratorSuperPacman entities: - uid: 1016 @@ -5824,13 +5831,6 @@ entities: - type: Transform pos: -6.8905525,1.5128828 parent: 179 -- proto: VehicleKeyJanicart - entities: - - uid: 14 - components: - - type: Transform - pos: 6.5,16.5 - parent: 179 - proto: VendingMachineCigs entities: - uid: 870 diff --git a/Resources/Maps/saltern.yml b/Resources/Maps/saltern.yml index c4a7d0b3c2..7090ba0ee3 100644 --- a/Resources/Maps/saltern.yml +++ b/Resources/Maps/saltern.yml @@ -469,7 +469,7 @@ entities: 0: 53709 4,4: 0: 4573 - 3: 16384 + 2: 16384 5,0: 0: 62190 5,1: @@ -500,7 +500,7 @@ entities: 0: 35771 7,3: 0: 39135 - 2: 1024 + 3: 1024 7,4: 0: 55739 7,-1: @@ -533,27 +533,27 @@ entities: 0: 3581 1,7: 0: 4369 - 3: 17484 + 2: 17484 1,8: 0: 4369 - 3: 17476 + 2: 17476 2,5: 0: 53367 2,6: 0: 1405 2,7: - 3: 7 + 2: 7 3,5: 0: 4210 - 3: 32768 + 2: 32768 3,6: 0: 13073 - 3: 8 + 2: 8 3,7: - 3: 4 + 2: 4 4,5: 0: 273 - 3: 29764 + 2: 29764 -8,0: 0: 61550 -9,0: @@ -623,7 +623,7 @@ entities: 7,-2: 0: 57599 7,-4: - 3: 8738 + 2: 8738 7,-3: 0: 3808 8,-3: @@ -644,7 +644,7 @@ entities: 0: 65524 0,-9: 0: 52416 - 3: 273 + 2: 273 1,-8: 0: 65488 1,-7: @@ -661,7 +661,7 @@ entities: 0: 61695 2,-9: 0: 13107 - 3: 2176 + 2: 2176 3,-8: 0: 52637 3,-7: @@ -670,76 +670,76 @@ entities: 0: 64733 4,-8: 0: 4483 - 3: 17484 + 2: 17484 4,-7: 0: 65281 - 3: 12 + 2: 12 4,-6: 0: 61152 4,-9: - 3: 16384 + 2: 16384 0: 32768 5,-8: - 3: 21855 + 2: 21855 0: 160 5,-7: - 3: 15 + 2: 15 0: 63232 5,-9: - 3: 20480 + 2: 20480 0: 40960 5,-6: 0: 26214 6,-8: - 3: 21855 + 2: 21855 0: 160 6,-7: - 3: 15 + 2: 15 0: 56576 6,-6: 0: 61919 6,-9: - 3: 20480 + 2: 20480 0: 40960 7,-8: - 3: 21855 + 2: 21855 0: 160 7,-7: - 3: 11823 + 2: 11823 7,-5: 0: 36846 7,-9: - 3: 20767 + 2: 20767 0: 40960 7,-6: - 3: 546 + 2: 546 0: 32768 8,-8: - 3: 21855 + 2: 21855 0: 160 8,-7: - 3: 9007 + 2: 9007 8,-5: 0: 4017 -4,5: 0: 238 -4,6: 0: 255 - 3: 61440 + 2: 61440 -5,5: 0: 58982 -5,6: 0: 238 - 3: 61440 + 2: 61440 -3,5: 0: 3295 -3,6: 0: 255 - 3: 61440 + 2: 61440 -3,7: - 3: 51406 + 2: 51406 -3,8: - 3: 14 + 2: 14 0: 52224 -2,5: 0: 52701 @@ -749,51 +749,51 @@ entities: 0: 61166 -1,8: 0: 34952 - 3: 13104 + 2: 13104 -9,4: 0: 65471 -8,5: 0: 13072 - 3: 128 + 2: 128 -9,5: 0: 65459 -8,6: 0: 1 - 3: 63304 + 2: 63304 -9,6: 0: 59 - 3: 63488 + 2: 63488 -8,7: - 3: 2039 + 2: 2039 -9,7: - 3: 28671 + 2: 28671 -7,6: - 3: 63249 + 2: 63249 0: 14 -7,7: - 3: 16 + 2: 16 -7,5: 0: 1038 - 3: 4352 + 2: 4352 -6,5: 0: 65327 -6,6: 0: 255 - 3: 61440 + 2: 61440 -3,9: 0: 12 - 3: 3584 + 2: 3584 -2,8: 0: 30560 -2,9: 0: 7 - 3: 3840 + 2: 3840 -1,9: - 3: 405 + 2: 405 0,9: - 3: 240 + 2: 240 4,6: - 3: 550 + 2: 550 0: 34816 5,5: 0: 30583 @@ -803,24 +803,24 @@ entities: 0: 5 6,5: 0: 255 - 3: 61440 + 2: 61440 6,6: - 3: 35561 + 2: 35561 7,5: 0: 36063 - 3: 4096 + 2: 4096 7,6: - 3: 53196 + 2: 53196 8,4: 0: 4351 - 3: 57344 + 2: 57344 8,5: 0: 272 - 5: 17472 + 4: 17472 8,6: - 3: 4081 + 2: 4081 1,9: - 3: 18 + 2: 18 9,0: 0: 65102 9,1: @@ -831,7 +831,7 @@ entities: 0: 65535 9,4: 0: 255 - 3: 61440 + 2: 61440 9,-1: 0: 60942 10,0: @@ -846,52 +846,52 @@ entities: 0: 48015 10,4: 0: 255 - 3: 61440 + 2: 61440 11,0: 0: 65520 11,1: 0: 53759 11,2: 0: 4319 - 4: 49152 + 5: 49152 11,3: 0: 61457 - 4: 204 + 5: 204 11,-1: 0: 30583 11,4: 0: 255 - 3: 61440 + 2: 61440 12,0: 0: 65527 12,1: 0: 28791 12,2: 0: 119 - 4: 28672 + 5: 28672 12,3: - 4: 119 + 5: 119 0: 61440 12,-1: 0: 29311 12,4: 0: 255 - 3: 61440 + 2: 61440 13,0: 0: 49080 13,1: 0: 48058 13,2: - 3: 13104 + 2: 13104 0: 34826 13,3: - 3: 35059 + 2: 35059 0: 12288 13,-1: 0: 14119 13,4: 0: 51 - 3: 63624 + 2: 63624 14,0: 0: 48123 14,1: @@ -899,9 +899,9 @@ entities: 14,2: 0: 15235 14,3: - 3: 65528 + 2: 65528 14,4: - 3: 62455 + 2: 62455 15,0: 0: 56797 15,1: @@ -909,23 +909,23 @@ entities: 15,2: 0: 3548 15,3: - 3: 32767 + 2: 32767 15,4: - 3: 12850 + 2: 12850 15,-1: 0: 52701 16,0: 0: 13116 - 4: 52416 + 5: 52416 16,1: 0: 65484 16,2: - 3: 15 + 2: 15 0: 4080 16,3: - 3: 20479 + 2: 20479 8,-4: - 3: 8738 + 2: 8738 0: 34952 9,-4: 0: 56789 @@ -937,15 +937,15 @@ entities: 0: 18295 10,-4: 0: 65024 - 3: 14 + 2: 14 10,-3: 0: 65520 10,-2: 0: 63743 10,-5: - 3: 34956 + 2: 34956 11,-4: - 3: 2187 + 2: 2187 0: 13056 11,-3: 0: 43946 @@ -953,10 +953,10 @@ entities: 0: 30250 11,-5: 0: 32776 - 3: 17968 + 2: 17968 12,-4: 0: 7 - 3: 4088 + 2: 4088 12,-3: 0: 65535 12,-2: @@ -964,7 +964,7 @@ entities: -4,-8: 0: 4016 -4,-9: - 3: 28672 + 2: 28672 0: 127 -5,-8: 0: 2995 @@ -994,15 +994,15 @@ entities: 0: 57309 -2,-9: 0: 61440 - 3: 47 + 2: 47 -1,-6: 0: 30065 -8,-8: 0: 60931 - 3: 8 + 2: 8 -8,-9: 0: 12288 - 3: 35064 + 2: 35064 -9,-8: 0: 56653 -8,-7: @@ -1013,16 +1013,16 @@ entities: 0: 36863 -9,-6: 0: 3211 - 3: 12288 + 2: 12288 -8,-5: 0: 48123 -9,-5: 0: 43008 - 3: 4 + 2: 4 -8,-4: 0: 35771 -7,-8: - 3: 7 + 2: 7 0: 7936 -7,-6: 0: 61428 @@ -1033,7 +1033,7 @@ entities: -7,-4: 0: 36317 -7,-9: - 3: 17408 + 2: 17408 0: 32768 -6,-8: 0: 36747 @@ -1043,30 +1043,30 @@ entities: 0: 62463 -6,-9: 0: 61440 - 3: 34 + 2: 34 -6,-7: 0: 52878 -6,-4: 0: 62463 -5,-9: 0: 12799 - 3: 32768 + 2: 32768 -9,-4: 0: 34952 - 3: 800 + 2: 800 -8,-3: 0: 3663 -9,-3: 0: 52428 - 3: 4369 + 2: 4369 -8,-2: 0: 59119 -9,-2: 0: 52428 - 3: 4369 + 2: 4369 -9,-1: 0: 56780 - 3: 1 + 2: 1 -7,-2: 0: 65262 -7,-3: @@ -1086,11 +1086,11 @@ entities: -11,0: 0: 3855 -12,1: - 3: 2056 + 2: 2056 -11,2: 0: 3855 -11,1: - 3: 546 + 2: 546 0: 2184 -10,0: 0: 65535 @@ -1099,95 +1099,95 @@ entities: -10,2: 0: 53247 -11,3: - 3: 34952 + 2: 34952 -10,3: 0: 61166 -10,-1: 0: 60928 - 3: 15 + 2: 15 -10,4: 0: 61182 -12,-1: - 3: 2738 + 2: 2738 -11,-1: - 3: 3628 + 2: 3628 -11,-2: - 3: 32768 + 2: 32768 -10,-2: - 3: 4492 + 2: 4492 -10,-3: - 3: 57480 + 2: 57480 -10,-4: - 3: 136 + 2: 136 -10,-5: - 3: 32776 + 2: 32776 12,-5: 0: 62079 13,-4: - 3: 3064 + 2: 3064 13,-3: 0: 48059 13,-2: 0: 63243 13,-5: - 3: 39912 + 2: 39912 14,-4: - 3: 1039 + 2: 1039 14,-3: 0: 13107 - 3: 128 + 2: 128 14,-2: 0: 65283 14,-1: 0: 4095 14,-5: - 3: 17600 + 2: 17600 15,-4: - 3: 8739 + 2: 8739 15,-3: - 3: 62066 + 2: 62066 15,-2: 0: 7424 - 3: 206 + 2: 206 15,-5: - 3: 8721 + 2: 8721 16,-3: - 3: 61440 + 2: 61440 16,-2: - 3: 255 + 2: 255 0: 3840 16,-1: 0: 53247 8,-9: - 3: 24143 + 2: 24143 0: 41120 8,-6: - 3: 3618 + 2: 3618 9,-8: - 3: 15 + 2: 15 9,-7: - 3: 15 + 2: 15 9,-6: - 3: 3840 + 2: 3840 10,-8: - 3: 55703 + 2: 55703 10,-7: - 3: 8743 + 2: 8743 0: 34816 10,-6: - 3: 50978 + 2: 50978 0: 8 10,-9: - 3: 40847 + 2: 40847 11,-8: - 3: 54 + 2: 54 0: 2048 11,-7: 0: 64988 11,-6: 0: 3293 11,-9: - 3: 49921 + 2: 49921 12,-8: 0: 12144 12,-7: @@ -1195,78 +1195,78 @@ entities: 12,-6: 0: 24568 20,-1: - 3: 256 + 2: 256 19,-1: - 3: 65335 + 2: 65335 20,0: - 3: 16179 + 2: 16179 19,0: - 3: 39118 + 2: 39118 0: 17441 20,1: - 3: 14135 + 2: 14135 19,1: - 3: 39321 + 2: 39321 0: 17476 20,2: - 3: 29495 + 2: 29495 19,2: - 3: 53179 + 2: 53179 0: 8260 20,3: - 3: 35 + 2: 35 19,3: - 3: 4095 + 2: 4095 12,-9: - 3: 61440 + 2: 61440 13,-8: - 3: 35043 + 2: 35043 13,-7: 0: 64849 13,-6: 0: 349 - 3: 32768 + 2: 32768 13,-9: - 3: 4096 + 2: 4096 14,-8: - 3: 6144 + 2: 6144 14,-7: - 3: 8739 + 2: 8739 14,-6: - 3: 4898 + 2: 4898 15,-8: - 3: 4352 + 2: 4352 15,-7: - 3: 4369 + 2: 4369 15,-6: - 3: 4369 + 2: 4369 -11,4: 0: 192 - 3: 32768 + 2: 32768 -11,6: 0: 12 - 3: 34816 + 2: 34816 -11,7: - 3: 35840 + 2: 35840 -11,5: - 3: 2184 + 2: 2184 -10,6: 0: 4335 - 3: 57344 + 2: 57344 -10,7: - 3: 4040 + 2: 4040 -11,8: - 3: 34952 + 2: 34952 -10,5: 0: 61166 -9,8: 0: 2827 - 3: 25844 + 2: 25844 0,-12: - 3: 127 + 2: 127 0: 12288 -1,-12: - 3: 975 + 2: 975 0: 32768 0,-11: 0: 29107 @@ -1274,14 +1274,14 @@ entities: 0: 2047 0,-10: 0: 247 - 3: 57344 + 2: 57344 -1,-10: 0: 255 - 3: 36864 + 2: 36864 -1,-9: - 3: 3257 + 2: 3257 1,-12: - 3: 4375 + 2: 4375 1,-11: 0: 3536 1,-10: @@ -1291,254 +1291,254 @@ entities: 2,-10: 0: 14196 2,-12: - 3: 44800 + 2: 44800 3,-12: - 3: 768 + 2: 768 3,-10: - 3: 18240 + 2: 18240 3,-9: - 3: 1908 + 2: 1908 7,-12: - 3: 7455 + 2: 7455 7,-11: - 3: 7453 + 2: 7453 7,-10: - 3: 4381 + 2: 4381 8,-12: - 3: 20303 + 2: 20303 8,-11: - 3: 20303 + 2: 20303 8,-10: - 3: 20047 + 2: 20047 0: 40960 9,5: 6: 4368 - 4: 17472 + 5: 17472 9,6: - 3: 12272 + 2: 12272 10,5: - 4: 4368 + 5: 4368 7: 17472 10,6: - 3: 4080 + 2: 4080 11,5: - 4: 21840 + 5: 21840 11,6: - 3: 61424 + 2: 61424 11,7: - 3: 12 + 2: 12 12,5: - 3: 65535 + 2: 65535 12,6: - 3: 65535 + 2: 65535 12,7: - 3: 15 + 2: 15 13,5: - 3: 55705 + 2: 55705 13,6: - 3: 16383 + 2: 16383 13,7: - 3: 1 + 2: 1 14,5: - 3: 30591 + 2: 30591 14,6: - 3: 7 + 2: 7 15,5: - 3: 35 + 2: 35 -4,-11: - 3: 3840 + 2: 3840 -5,-11: - 3: 11776 + 2: 11776 -4,-10: 0: 6143 -5,-10: 0: 14472 - 3: 2 + 2: 2 -3,-11: - 3: 304 + 2: 304 0: 34944 -3,-10: 0: 29949 -2,-10: 0: 1019 -2,-11: - 3: 544 + 2: 544 0: 2176 -2,-12: - 3: 2048 + 2: 2048 17,-3: - 3: 61696 + 2: 61696 17,-2: - 3: 3327 + 2: 3327 0: 768 17,-1: 0: 4369 - 3: 52428 + 2: 52428 17,0: 0: 19969 - 3: 8 - 4: 4368 + 2: 8 + 5: 4368 18,-3: - 3: 4096 + 2: 4096 18,-2: - 3: 59381 + 2: 59381 18,-1: - 3: 15358 + 2: 15358 0: 33792 18,0: - 3: 65399 + 2: 65399 19,-2: - 3: 12288 + 2: 12288 17,1: 0: 65365 17,2: 0: 4095 17,3: - 3: 16383 + 2: 16383 18,1: - 3: 61167 + 2: 61167 18,2: 0: 1792 - 3: 2190 + 2: 2190 18,3: - 3: 40959 + 2: 40959 12,-10: - 3: 61440 + 2: 61440 11,-10: - 3: 61440 + 2: 61440 13,-10: - 3: 7936 + 2: 7936 14,-10: - 3: 256 + 2: 256 9,-12: - 3: 1807 + 2: 1807 9,-11: - 3: 1799 + 2: 1799 9,-10: - 3: 7 + 2: 7 9,-9: - 3: 3855 + 2: 3855 10,-12: - 3: 4369 + 2: 4369 10,-11: - 3: 4369 + 2: 4369 10,-10: - 3: 4369 + 2: 4369 -12,-8: - 3: 64170 + 2: 64170 -13,-8: - 3: 64170 + 2: 64170 -12,-7: - 3: 64170 + 2: 64170 -13,-7: - 3: 64170 + 2: 64170 -12,-9: - 3: 61440 + 2: 61440 -11,-8: - 3: 64170 + 2: 64170 -11,-7: - 3: 64170 + 2: 64170 -11,-9: - 3: 61440 + 2: 61440 -10,-8: - 3: 12834 + 2: 12834 0: 34828 -10,-7: - 3: 12834 + 2: 12834 0: 34952 -10,-9: - 3: 13288 + 2: 13288 0: 32768 -10,-6: - 3: 57378 + 2: 57378 0: 8 -9,-9: 0: 61440 - 3: 248 + 2: 248 -13,-9: - 3: 61440 + 2: 61440 -11,-10: - 3: 8 + 2: 8 -10,-10: - 3: 63631 + 2: 63631 -10,-12: - 3: 59592 + 2: 59592 -9,-12: - 3: 63743 + 2: 63743 -10,-11: - 3: 34952 + 2: 34952 -9,-11: - 3: 63736 + 2: 63736 -9,-10: - 3: 63736 + 2: 63736 -9,-13: - 3: 61440 + 2: 61440 -8,-12: - 3: 63743 + 2: 63743 -8,-11: - 3: 63736 + 2: 63736 -8,-10: - 3: 63736 + 2: 63736 -8,-13: - 3: 29696 + 2: 29696 -7,-12: - 3: 1808 + 2: 1808 -7,-11: - 3: 240 + 2: 240 -6,-11: - 3: 8192 + 2: 8192 -6,-10: - 3: 25262 + 2: 25262 -14,0: 0: 3598 -14,2: 0: 3598 -13,-1: - 3: 3648 + 2: 3648 -11,9: - 3: 35980 + 2: 35980 -11,10: - 3: 51336 + 2: 51336 -10,8: - 3: 497 + 2: 497 0: 3084 -10,9: - 3: 449 + 2: 449 0: 3084 -10,10: - 3: 4593 + 2: 4593 0: 3084 -10,11: - 3: 227 + 2: 227 -9,9: 0: 2827 - 3: 21748 + 2: 21748 -9,10: 0: 2827 - 3: 58612 + 2: 58612 -9,11: - 3: 254 + 2: 254 -8,8: 0: 1799 - 3: 112 + 2: 112 -8,9: 0: 1799 - 3: 4208 + 2: 4208 -8,10: 0: 1799 - 3: 112 + 2: 112 -8,11: - 3: 112 + 2: 112 -15,-8: - 3: 34944 + 2: 34944 -14,-8: - 3: 64443 + 2: 64443 -15,-7: - 3: 136 + 2: 136 -14,-7: - 3: 64443 + 2: 64443 -14,-9: - 3: 61440 + 2: 61440 uniqueMixes: - volume: 2500 temperature: 293.15 @@ -1571,10 +1571,8 @@ entities: - 0 - 0 - volume: 2500 - temperature: 293.14975 + immutable: True moles: - - 20.078888 - - 75.53487 - 0 - 0 - 0 @@ -1585,11 +1583,13 @@ entities: - 0 - 0 - 0 - - volume: 2500 - immutable: True - moles: - 0 - 0 + - volume: 2500 + temperature: 293.14975 + moles: + - 20.078888 + - 75.53487 - 0 - 0 - 0 @@ -1604,7 +1604,7 @@ entities: temperature: 293.15 moles: - 0 - - 0 + - 6666.982 - 0 - 0 - 0 @@ -1619,7 +1619,7 @@ entities: temperature: 293.15 moles: - 0 - - 6666.982 + - 0 - 0 - 0 - 0 @@ -1673,55 +1673,55 @@ entities: color: '#FFFFFFFF' id: Arrows decals: - 446: -12,-30 + 440: -12,-30 - node: color: '#FFFFFFFF' id: Arrows decals: - 383: 20,17 - 398: 20,17 - 402: 20,27 - 447: -12,-29 + 377: 20,17 + 392: 20,17 + 396: 20,27 + 441: -12,-29 - node: angle: 3.141592653589793 rad color: '#FFFFFFFF' id: Arrows decals: - 399: 22,17 - 403: 22,27 + 393: 22,17 + 397: 22,27 - node: color: '#C3C3C3FF' id: Bot decals: - 757: -35,-24 - 762: -32,-32 - 763: -31,-32 + 704: -35,-24 + 709: -32,-32 + 710: -31,-32 - node: color: '#FFFFFFFF' id: Bot decals: - 377: -35,6 - 378: -30,11 - 408: -3,-31 - 613: 31,8 - 614: 31,9 + 375: -35,6 + 376: -30,11 + 402: -3,-31 + 566: 31,8 + 567: 31,9 - node: zIndex: 1 color: '#FFFFFFFF' id: Bot decals: - 481: 20,7 - 482: 21,7 - 483: 22,7 - 550: -7,-26 - 551: -6,-26 + 442: 20,7 + 443: 21,7 + 444: 22,7 + 511: -7,-26 + 512: -6,-26 - node: angle: 3.141592653589793 rad color: '#FFFFFFFF' id: Bot decals: - 400: 20,17 - 401: 22,17 + 394: 20,17 + 395: 22,17 - node: color: '#FFFFFFFF' id: BotRight @@ -1732,577 +1732,577 @@ entities: color: '#C3C3C3FF' id: Box decals: - 775: -33,-29 - 776: -33,-28 + 722: -33,-29 + 723: -33,-28 - node: color: '#FFFFFFFF' id: Box decals: - 914: -33,21 - 915: -32,21 - 916: -32,24 - 917: -33,24 - 926: -31,23 - 927: -31,22 + 824: -33,21 + 825: -32,21 + 826: -32,24 + 827: -33,24 + 834: -31,23 + 835: -31,22 - node: zIndex: 2 color: '#FFFFFFFF' id: BrickTileDarkCornerNe decals: - 556: -9,-28 + 517: -9,-28 - node: zIndex: 2 color: '#FFFFFFFF' id: BrickTileDarkCornerNw decals: - 557: -10,-28 + 518: -10,-28 - node: zIndex: 2 color: '#FFFFFFFF' id: BrickTileDarkCornerSe decals: - 555: -9,-30 + 516: -9,-30 - node: zIndex: 2 color: '#FFFFFFFF' id: BrickTileDarkCornerSw decals: - 554: -10,-30 + 515: -10,-30 - node: color: '#FFFFFFFF' id: BrickTileDarkInnerSe decals: - 612: -11,20 + 565: -11,20 - node: zIndex: 1 color: '#FFFFFFFF' id: BrickTileDarkInnerSe decals: - 488: 6,17 + 449: 6,17 - node: color: '#FFFFFFFF' id: BrickTileDarkLineE decals: - 530: 0,-25 - 736: -1,29 - 737: -1,28 + 491: 0,-25 + 683: -1,29 + 684: -1,28 - node: zIndex: 2 color: '#FFFFFFFF' id: BrickTileDarkLineE decals: - 558: -9,-29 + 519: -9,-29 - node: color: '#FFFFFFFF' id: BrickTileDarkLineN decals: - 525: -6,-23 - 526: -5,-23 - 527: -4,-23 - 734: 3,30 - 735: 4,30 + 486: -6,-23 + 487: -5,-23 + 488: -4,-23 + 681: 3,30 + 682: 4,30 - node: zIndex: 2 color: '#FFFFFFFF' id: BrickTileDarkLineN decals: - 565: 12,-14 - 566: 13,-14 - 567: 14,-14 - 568: 15,-14 + 526: 12,-14 + 527: 13,-14 + 528: 14,-14 + 529: 15,-14 - node: color: '#FFFFFFFF' id: BrickTileDarkLineS decals: - 609: -8,20 - 610: -9,20 - 611: -10,20 + 562: -8,20 + 563: -9,20 + 564: -10,20 - node: zIndex: 1 color: '#FFFFFFFF' id: BrickTileDarkLineS decals: - 484: 10,17 - 485: 9,17 - 486: 8,17 - 487: 7,17 + 445: 10,17 + 446: 9,17 + 447: 8,17 + 448: 7,17 - node: zIndex: 2 color: '#FFFFFFFF' id: BrickTileDarkLineS decals: - 569: 12,-18 - 570: 13,-18 - 571: 14,-18 - 572: 15,-18 + 530: 12,-18 + 531: 13,-18 + 532: 14,-18 + 533: 15,-18 - node: color: '#FFFFFFFF' id: BrickTileDarkLineW decals: - 531: -2,-25 + 492: -2,-25 - node: zIndex: 2 color: '#FFFFFFFF' id: BrickTileDarkLineW decals: - 559: -10,-29 + 520: -10,-29 - node: zIndex: 2 color: '#D381C9FF' id: BrickTileSteelCornerNe decals: - 553: -6,-26 + 514: -6,-26 - node: color: '#D381C9FF' id: BrickTileSteelCornerNw decals: - 539: -4,-26 + 500: -4,-26 - node: color: '#D381C9FF' id: BrickTileSteelCornerSe decals: - 545: -8,-24 + 506: -8,-24 - node: color: '#D381C9E5' id: BrickTileSteelCornerSw decals: - 437: -12,-32 + 431: -12,-32 - node: color: '#FFFFFFFF' id: BrickTileSteelCornerSw decals: - 625: 25,19 + 578: 25,19 - node: color: '#C3C3C3FF' id: BrickTileSteelInnerNe decals: - 751: -34,-32 - 780: -35,-27 + 698: -34,-32 + 727: -35,-27 - node: color: '#D381C9E5' id: BrickTileSteelInnerNe decals: - 441: -10,-26 + 435: -10,-26 - node: color: '#D381C9FF' id: BrickTileSteelInnerNe decals: - 529: -6,-27 - 544: 0,-29 - 549: -13,-19 + 490: -6,-27 + 505: 0,-29 + 510: -13,-19 - node: color: '#D381C9FF' id: BrickTileSteelInnerNw decals: - 538: -4,-27 - 540: -3,-26 + 499: -4,-27 + 501: -3,-26 - node: color: '#C3C3C3FF' id: BrickTileSteelInnerSe decals: - 761: -34,-30 - 771: -36,-28 - 772: -33,-30 + 708: -34,-30 + 718: -36,-28 + 719: -33,-30 - node: color: '#D381C9E5' id: BrickTileSteelInnerSe decals: - 431: -18,-28 - 434: -7,-33 + 425: -18,-28 + 428: -7,-33 - node: color: '#D381C9FF' id: BrickTileSteelInnerSe decals: - 541: 0,-26 - 546: -8,-23 + 502: 0,-26 + 507: -8,-23 - node: color: '#C3C3C3FF' id: BrickTileSteelInnerSw decals: - 738: -34,-28 + 685: -34,-28 - node: color: '#D381C9E5' id: BrickTileSteelInnerSw decals: - 413: -12,-20 - 438: -12,-31 + 407: -12,-20 + 432: -12,-31 - node: color: '#C3C3C3FF' id: BrickTileSteelLineE decals: - 748: -33,-29 - 749: -33,-28 - 750: -34,-31 - 755: -35,-26 - 756: -35,-25 - 759: -33,-30 - 770: -36,-29 - 779: -36,-30 + 695: -33,-29 + 696: -33,-28 + 697: -34,-31 + 702: -35,-26 + 703: -35,-25 + 706: -33,-30 + 717: -36,-29 + 726: -36,-30 - node: color: '#D381C9FF' id: BrickTileSteelLineE decals: - 542: 0,-27 - 543: 0,-28 + 503: 0,-27 + 504: 0,-28 - node: color: '#C3C3C3FF' id: BrickTileSteelLineN decals: - 746: -37,-24 - 747: -36,-24 - 752: -33,-32 - 753: -33,-27 - 754: -34,-27 + 693: -37,-24 + 694: -36,-24 + 699: -33,-32 + 700: -33,-27 + 701: -34,-27 - node: color: '#D381C9E5' id: BrickTileSteelLineN decals: - 415: -8,-19 - 416: -9,-19 - 417: -10,-19 - 418: -11,-19 - 440: -9,-26 + 409: -8,-19 + 410: -9,-19 + 411: -10,-19 + 412: -11,-19 + 434: -9,-26 - node: color: '#D381C9FF' id: BrickTileSteelLineN decals: - 528: -8,-26 - 548: -12,-19 + 489: -8,-26 + 509: -12,-19 - node: zIndex: 2 color: '#D381C9FF' id: BrickTileSteelLineN decals: - 552: -7,-26 + 513: -7,-26 - node: color: '#FFFFFFFF' id: BrickTileSteelLineN decals: - 409: -11,-24 + 403: -11,-24 - node: color: '#C3C3C3FF' id: BrickTileSteelLineS decals: - 741: -33,-33 - 760: -33,-30 - 764: -34,-33 - 769: -37,-30 - 777: -35,-28 - 778: -36,-30 + 688: -33,-33 + 707: -33,-30 + 711: -34,-33 + 716: -37,-30 + 724: -35,-28 + 725: -36,-30 - node: color: '#D381C9E5' id: BrickTileSteelLineS decals: - 414: -13,-20 - 427: -17,-28 - 428: -16,-28 - 429: -15,-28 - 430: -14,-28 - 432: -5,-33 - 433: -6,-33 - 436: -11,-32 - 439: -13,-31 + 408: -13,-20 + 421: -17,-28 + 422: -16,-28 + 423: -15,-28 + 424: -14,-28 + 426: -5,-33 + 427: -6,-33 + 430: -11,-32 + 433: -13,-31 - node: color: '#D381C9FF' id: BrickTileSteelLineS decals: - 547: -9,-24 + 508: -9,-24 - node: color: '#FFFFFFFF' id: BrickTileSteelLineS decals: - 498: 57,5 - 499: 56,5 - 622: 26,19 - 623: 27,19 - 624: 28,19 + 459: 57,5 + 460: 56,5 + 575: 26,19 + 576: 27,19 + 577: 28,19 - node: color: '#C3C3C3FF' id: BrickTileSteelLineW decals: - 739: -34,-29 - 740: -34,-32 - 742: -37,-27 - 743: -37,-26 - 744: -37,-25 - 745: -37,-24 - 758: -34,-30 - 768: -37,-28 + 686: -34,-29 + 687: -34,-32 + 689: -37,-27 + 690: -37,-26 + 691: -37,-25 + 692: -37,-24 + 705: -34,-30 + 715: -37,-28 - node: color: '#D381C9E5' id: BrickTileSteelLineW decals: - 410: -12,-23 - 411: -12,-22 - 412: -12,-21 + 404: -12,-23 + 405: -12,-22 + 406: -12,-21 - node: color: '#FFFFFFFF' id: BrickTileSteelLineW decals: - 626: 25,20 - 627: 25,21 + 579: 25,20 + 580: 25,21 - node: color: '#689F54FF' id: BrickTileWhiteCornerNe decals: - 791: -21,-5 + 738: -21,-5 - node: color: '#689F54FF' id: BrickTileWhiteCornerNw decals: - 792: -23,-5 + 739: -23,-5 - node: zIndex: 2 color: '#EFB34196' id: BrickTileWhiteCornerSe decals: - 592: -8,7 + 547: -8,7 - node: zIndex: 2 color: '#52B4E996' id: BrickTileWhiteCornerSw decals: - 588: 7,-18 + 543: 7,-18 - node: color: '#A4610696' id: BrickTileWhiteCornerSw decals: - 632: 18,15 + 585: 18,15 - node: zIndex: 2 color: '#EFB34196' id: BrickTileWhiteCornerSw decals: - 593: -14,7 + 548: -14,7 - node: color: '#52B4E996' id: BrickTileWhiteEndE decals: - 512: 49,-12 + 473: 49,-12 - node: color: '#9FED5896' id: BrickTileWhiteEndE decals: - 508: 52,-10 + 469: 52,-10 - node: color: '#A4610696' id: BrickTileWhiteEndE decals: - 515: 52,-8 + 476: 52,-8 - node: color: '#D381C996' id: BrickTileWhiteEndE decals: - 511: 52,-12 + 472: 52,-12 - node: color: '#D4D4D496' id: BrickTileWhiteEndE decals: - 507: 49,-10 + 468: 49,-10 - node: color: '#EFB34196' id: BrickTileWhiteEndE decals: - 500: 49,-8 + 461: 49,-8 - node: color: '#334E6DC8' id: BrickTileWhiteEndN decals: - 502: 57,-8 + 463: 57,-8 - node: color: '#DE3A3A96' id: BrickTileWhiteEndN decals: - 505: 57,-11 + 466: 57,-11 - node: color: '#334E6DC8' id: BrickTileWhiteEndS decals: - 503: 57,-9 + 464: 57,-9 - node: color: '#DE3A3A96' id: BrickTileWhiteEndS decals: - 504: 57,-12 + 465: 57,-12 - node: color: '#52B4E996' id: BrickTileWhiteEndW decals: - 513: 48,-12 + 474: 48,-12 - node: color: '#9FED5896' id: BrickTileWhiteEndW decals: - 509: 51,-10 + 470: 51,-10 - node: color: '#A4610696' id: BrickTileWhiteEndW decals: - 514: 51,-8 + 475: 51,-8 - node: color: '#D381C996' id: BrickTileWhiteEndW decals: - 510: 51,-12 + 471: 51,-12 - node: color: '#D4D4D496' id: BrickTileWhiteEndW decals: - 506: 48,-10 + 467: 48,-10 - node: color: '#EFB34196' id: BrickTileWhiteEndW decals: - 501: 48,-8 + 462: 48,-8 - node: color: '#759DBCFF' id: BrickTileWhiteInnerSe decals: - 975: 19,-18 + 879: 19,-18 - node: color: '#A4610696' id: BrickTileWhiteInnerSe decals: - 630: 22,15 + 583: 22,15 - node: zIndex: 2 color: '#52B4E996' id: BrickTileWhiteInnerSw decals: - 580: 17,-13 + 536: 17,-13 - node: color: '#A4610696' id: BrickTileWhiteInnerSw decals: - 631: 19,15 + 584: 19,15 - node: color: '#689F54FF' id: BrickTileWhiteLineE decals: - 789: -21,-8 - 790: -21,-7 + 736: -21,-8 + 737: -21,-7 - node: color: '#759DBCFF' id: BrickTileWhiteLineE decals: - 971: 19,-19 - 972: 20,-18 - 973: 20,-17 - 974: 20,-16 + 875: 19,-19 + 876: 20,-18 + 877: 20,-17 + 878: 20,-16 - node: zIndex: 2 color: '#EFB34196' id: BrickTileWhiteLineE decals: - 594: -8,8 + 549: -8,8 - node: color: '#689F54FF' id: BrickTileWhiteLineN decals: - 793: -22,-5 + 740: -22,-5 - node: color: '#759DBCFF' id: BrickTileWhiteLineN decals: - 979: 20,-16 - 980: 19,-16 - 981: 17,-16 + 883: 20,-16 + 884: 19,-16 + 885: 17,-16 - node: color: '#A4610696' id: BrickTileWhiteLineN decals: - 618: 26,10 - 619: 27,10 + 571: 26,10 + 572: 27,10 - node: zIndex: 2 color: '#52B4E996' id: BrickTileWhiteLineS decals: - 582: 8,-18 - 583: 9,-18 - 589: 10,-18 + 537: 8,-18 + 538: 9,-18 + 544: 10,-18 - node: color: '#759DBCFF' id: BrickTileWhiteLineS decals: - 976: 20,-18 - 977: 17,-19 - 978: 19,-19 + 880: 20,-18 + 881: 17,-19 + 882: 19,-19 - node: color: '#A4610696' id: BrickTileWhiteLineS decals: - 620: 26,8 - 621: 27,8 - 628: 24,15 - 629: 23,15 - 635: 26,15 - 636: 27,15 - 637: 28,15 + 573: 26,8 + 574: 27,8 + 581: 24,15 + 582: 23,15 + 588: 26,15 + 589: 27,15 + 590: 28,15 - node: zIndex: 2 color: '#EFB34196' id: BrickTileWhiteLineS decals: - 590: -9,7 - 591: -13,7 + 545: -9,7 + 546: -13,7 - node: zIndex: 2 color: '#52B4E996' id: BrickTileWhiteLineW decals: - 579: 17,-14 - 584: 7,-17 - 585: 7,-16 - 586: 7,-15 - 587: 7,-14 + 535: 17,-14 + 539: 7,-17 + 540: 7,-16 + 541: 7,-15 + 542: 7,-14 - node: color: '#689F54FF' id: BrickTileWhiteLineW decals: - 787: -23,-7 - 788: -23,-6 - 794: -23,-8 + 734: -23,-7 + 735: -23,-6 + 741: -23,-8 - node: color: '#759DBCFF' id: BrickTileWhiteLineW decals: - 968: 17,-19 - 969: 17,-18 - 970: 17,-17 - 982: 17,-16 + 872: 17,-19 + 873: 17,-18 + 874: 17,-17 + 886: 17,-16 - node: color: '#A4610696' id: BrickTileWhiteLineW decals: - 633: 18,16 - 634: 18,17 + 586: 18,16 + 587: 18,17 - node: color: '#EFB34196' id: BrickTileWhiteLineW decals: - 642: 48,-6 - 643: 48,-5 - 644: 48,-4 - 645: 48,-3 + 595: 48,-6 + 596: 48,-5 + 597: 48,-4 + 598: 48,-3 - node: zIndex: 2 color: '#EFB34196' id: BrickTileWhiteLineW decals: - 595: -14,8 + 550: -14,8 - node: color: '#FFFFFFFF' id: BrickTileWhiteLineW decals: - 965: 18,-22 - 966: 18,-23 - 967: 18,-21 + 869: 18,-22 + 870: 18,-23 + 871: 18,-21 - node: color: '#FFFFFFFF' id: Caution decals: - 524: 58,-5 + 485: 58,-5 - node: angle: 1.5707963267948966 rad color: '#FFFFFFFF' @@ -2354,8 +2354,8 @@ entities: color: '#FFFFFFFF' id: Delivery decals: - 435: -8,-33 - 533: -1,-31 + 429: -8,-33 + 494: -1,-31 - node: color: '#FFFFFFFF' id: DirtHeavy @@ -2367,23 +2367,23 @@ entities: color: '#FFFFFFFF' id: DirtHeavy decals: - 393: -24,-25 + 387: -24,-25 - node: cleanable: True zIndex: 2 color: '#FFFFFFFF' id: DirtHeavy decals: - 597: -20,10 - 601: -20,9 - 602: -18,9 + 552: -20,10 + 556: -20,9 + 557: -18,9 - node: cleanable: True zIndex: 2 color: '#FFFFFFFF' id: DirtHeavyMonotile decals: - 600: -20,8 + 555: -20,8 - node: color: '#FFFFFFFF' id: DirtLight @@ -2430,25 +2430,25 @@ entities: 365: -36,1 366: -38,7 367: -40,8 - 370: -24,-9 - 371: -24,3 - 372: -25,2 - 373: -7,3 - 375: -36,8 - 376: -38,9 - 384: -23,-24 - 385: -25,-23 - 386: -23,-22 - 387: -25,-25 - 388: -24,-24 - 389: -24,-22 + 368: -24,-9 + 369: -24,3 + 370: -25,2 + 371: -7,3 + 373: -36,8 + 374: -38,9 + 378: -23,-24 + 379: -25,-23 + 380: -23,-22 + 381: -25,-25 + 382: -24,-24 + 383: -24,-22 - node: cleanable: True zIndex: 2 color: '#FFFFFFFF' id: DirtLight decals: - 598: -19,9 + 553: -19,9 - node: color: '#FFFFFFFF' id: DirtMedium @@ -2464,18 +2464,18 @@ entities: 44: 24,17 65: 37,6 82: 53,1 - 374: -35,8 - 390: -23,-25 - 391: -25,-22 - 392: -25,-24 + 372: -35,8 + 384: -23,-25 + 385: -25,-22 + 386: -25,-24 - node: cleanable: True zIndex: 2 color: '#FFFFFFFF' id: DirtMedium decals: - 596: -19,10 - 599: -19,8 + 551: -19,10 + 554: -19,8 - node: color: '#52B4E996' id: FullTileOverlayGreyscale @@ -2493,11 +2493,11 @@ entities: color: '#52B4E996' id: FullTileOverlayGreyscale decals: - 560: 9,-3 - 561: 9,-2 - 562: 10,-2 - 563: 8,-2 - 564: 9,-1 + 521: 9,-3 + 522: 9,-2 + 523: 10,-2 + 524: 8,-2 + 525: 9,-1 - node: color: '#EFB34196' id: FullTileOverlayGreyscale @@ -2537,19 +2537,19 @@ entities: color: '#AA4D53FF' id: HalfTileOverlayGreyscale decals: - 907: -32,19 - 908: -31,19 - 909: -30,19 + 817: -32,19 + 818: -31,19 + 819: -30,19 - node: color: '#BD575DFF' id: HalfTileOverlayGreyscale decals: - 808: -9,17 - 809: -8,17 - 810: -7,17 - 820: -11,11 - 821: -12,11 - 822: -13,11 + 752: -9,17 + 753: -8,17 + 754: -7,17 + 764: -11,11 + 765: -12,11 + 766: -13,11 - node: color: '#DE3A3A96' id: HalfTileOverlayGreyscale @@ -2621,19 +2621,19 @@ entities: color: '#AA4D53FF' id: HalfTileOverlayGreyscale180 decals: - 897: -32,16 - 898: -31,16 - 899: -30,16 + 809: -32,16 + 810: -31,16 + 811: -30,16 - node: color: '#BD575DFF' id: HalfTileOverlayGreyscale180 decals: - 798: -7,10 - 799: -8,10 - 800: -9,10 - 817: -11,10 - 818: -12,10 - 819: -13,10 + 745: -7,10 + 746: -8,10 + 747: -9,10 + 761: -11,10 + 762: -12,10 + 763: -13,10 - node: color: '#DE3A3A96' id: HalfTileOverlayGreyscale180 @@ -2697,19 +2697,19 @@ entities: color: '#AA4D53FF' id: HalfTileOverlayGreyscale270 decals: - 880: -10,15 - 881: -10,14 - 893: -10,16 - 894: -33,17 - 895: -33,16 - 910: -33,18 + 803: -10,15 + 804: -10,14 + 805: -10,16 + 806: -33,17 + 807: -33,16 + 820: -33,18 - node: color: '#BD575DFF' id: HalfTileOverlayGreyscale270 decals: - 802: -10,11 - 803: -10,12 - 804: -10,13 + 749: -10,11 + 750: -10,12 + 751: -10,13 - node: color: '#DE3A3A96' id: HalfTileOverlayGreyscale270 @@ -2740,7 +2740,7 @@ entities: 185: 37,6 201: 42,8 202: 42,7 - 497: 55,2 + 458: 55,2 - node: color: '#FFD886FF' id: HalfTileOverlayGreyscale270 @@ -2778,7 +2778,7 @@ entities: 105: 12,1 307: 12,-9 308: 12,-10 - 654: 12,-11 + 601: 12,-11 - node: color: '#A4610696' id: HalfTileOverlayGreyscale90 @@ -2798,26 +2798,26 @@ entities: color: '#AA4D53FF' id: HalfTileOverlayGreyscale90 decals: - 903: -29,17 - 904: -29,18 - 911: -35,18 - 912: -35,19 - 913: -35,16 + 813: -29,17 + 814: -29,18 + 821: -35,18 + 822: -35,19 + 823: -35,16 - node: color: '#BD575DFF' id: HalfTileOverlayGreyscale90 decals: - 797: -5,8 - 811: -6,16 - 812: -6,15 - 816: -5,12 + 744: -5,8 + 755: -6,16 + 756: -6,15 + 760: -5,12 - node: color: '#C05B60FF' id: HalfTileOverlayGreyscale90 decals: - 845: -5,13 - 846: -5,11 - 858: -5,10 + 789: -5,13 + 790: -5,11 + 802: -5,10 - node: color: '#DE3A3A96' id: HalfTileOverlayGreyscale90 @@ -2853,56 +2853,56 @@ entities: color: '#FFFFFFFF' id: LoadingArea decals: - 984: 31,-20 + 887: 31,-20 - node: angle: 3.141592653589793 rad color: '#FFFFFFFF' id: LoadingArea decals: - 985: 31,-18 + 888: 31,-18 - node: color: '#FFFFFFFF' id: MiniTileDarkLineN decals: - 407: -16,-25 + 401: -16,-25 - node: color: '#D381C9E5' id: MiniTileSteelCornerSe decals: - 419: -14,-23 + 413: -14,-23 - node: color: '#D381C9E5' id: MiniTileSteelCornerSw decals: - 425: -18,-24 + 419: -18,-24 - node: color: '#D381C9E5' id: MiniTileSteelInnerSe decals: - 420: -15,-23 + 414: -15,-23 - node: color: '#D381C9E5' id: MiniTileSteelInnerSw decals: - 426: -17,-24 + 420: -17,-24 - node: color: '#D381C9E5' id: MiniTileSteelLineE decals: - 421: -14,-22 + 415: -14,-22 - node: color: '#D381C9E5' id: MiniTileSteelLineW decals: - 422: -18,-21 - 423: -18,-22 - 424: -18,-23 + 416: -18,-21 + 417: -18,-22 + 418: -18,-23 - node: color: '#FFFFFFFF' id: MiniTileSteelLineW decals: - 825: -5,-13 - 826: -5,-12 + 769: -5,-13 + 770: -5,-12 - node: color: '#334E6DC8' id: QuarterTileOverlayGreyscale @@ -2919,15 +2919,15 @@ entities: color: '#848586FF' id: QuarterTileOverlayGreyscale decals: - 931: -37,10 - 932: -37,13 - 933: -37,14 - 934: -37,15 - 935: -37,16 - 944: -37,20 - 945: -37,21 - 946: -37,22 - 947: -37,23 + 836: -37,10 + 837: -37,13 + 838: -37,14 + 839: -37,15 + 840: -37,16 + 849: -37,20 + 850: -37,21 + 851: -37,22 + 852: -37,23 - node: color: '#D4D4D428' id: QuarterTileOverlayGreyscale @@ -2951,7 +2951,7 @@ entities: 187: 50,5 198: 44,9 203: 42,6 - 496: 55,1 + 457: 55,1 - node: color: '#52B4E996' id: QuarterTileOverlayGreyscale180 @@ -2964,14 +2964,14 @@ entities: color: '#848586FF' id: QuarterTileOverlayGreyscale180 decals: - 936: -37,13 - 937: -37,14 - 938: -37,15 - 939: -37,16 - 940: -37,20 - 941: -37,21 - 942: -37,22 - 943: -37,23 + 841: -37,13 + 842: -37,14 + 843: -37,15 + 844: -37,16 + 845: -37,20 + 846: -37,21 + 847: -37,22 + 848: -37,23 - node: color: '#A4610696' id: QuarterTileOverlayGreyscale180 @@ -2981,7 +2981,7 @@ entities: color: '#AA4D53FF' id: QuarterTileOverlayGreyscale180 decals: - 896: -33,16 + 808: -33,16 - node: color: '#D4D4D428' id: QuarterTileOverlayGreyscale180 @@ -3007,17 +3007,17 @@ entities: color: '#848586FF' id: QuarterTileOverlayGreyscale270 decals: - 954: -38,17 - 955: -38,18 - 956: -38,19 - 957: -36,19 - 958: -36,18 - 959: -36,17 + 859: -38,17 + 860: -38,18 + 861: -38,19 + 862: -36,19 + 863: -36,18 + 864: -36,17 - node: color: '#C05B60FF' id: QuarterTileOverlayGreyscale270 decals: - 857: -6,10 + 801: -6,10 - node: color: '#DE3A3A96' id: QuarterTileOverlayGreyscale270 @@ -3029,7 +3029,7 @@ entities: decals: 125: 35,-5 200: 42,9 - 495: 55,3 + 456: 55,3 - node: color: '#FFD886FF' id: QuarterTileOverlayGreyscale270 @@ -3046,12 +3046,12 @@ entities: color: '#848586FF' id: QuarterTileOverlayGreyscale90 decals: - 948: -36,19 - 949: -36,18 - 950: -36,17 - 951: -38,19 - 952: -38,18 - 953: -38,17 + 853: -36,19 + 854: -36,18 + 855: -36,17 + 856: -38,19 + 857: -38,18 + 858: -38,17 - node: color: '#A4610696' id: QuarterTileOverlayGreyscale90 @@ -3088,18 +3088,18 @@ entities: color: '#8F6CFFAD' id: Rust decals: - 781: -31,-3 - 782: -30,-3 - 783: -30,0 - 784: -31,0 - 785: -31,1 - 786: -30,1 + 728: -31,-3 + 729: -30,-3 + 730: -30,0 + 731: -31,0 + 732: -31,1 + 733: -30,1 - node: color: '#FFFFFFFF' id: StandClear decals: - 396: 21,24 - 532: 1,-30 + 390: 21,24 + 493: 1,-30 - node: angle: 1.5707963267948966 rad color: '#FFFFFFFF' @@ -3110,13 +3110,13 @@ entities: color: '#AA4D53FF' id: ThreeQuarterTileOverlayGreyscale decals: - 906: -33,19 + 816: -33,19 - node: color: '#BD575DFF' id: ThreeQuarterTileOverlayGreyscale decals: - 815: -10,17 - 824: -14,11 + 759: -10,17 + 768: -14,11 - node: color: '#52B4E996' id: ThreeQuarterTileOverlayGreyscale180 @@ -3126,7 +3126,7 @@ entities: color: '#AA4D53FF' id: ThreeQuarterTileOverlayGreyscale180 decals: - 901: -29,16 + 812: -29,16 - node: color: '#52B4E996' id: ThreeQuarterTileOverlayGreyscale270 @@ -3136,8 +3136,8 @@ entities: color: '#BD575DFF' id: ThreeQuarterTileOverlayGreyscale270 decals: - 801: -10,10 - 823: -14,10 + 748: -10,10 + 767: -14,10 - node: color: '#52B4E996' id: ThreeQuarterTileOverlayGreyscale90 @@ -3147,18 +3147,18 @@ entities: color: '#AA4D53FF' id: ThreeQuarterTileOverlayGreyscale90 decals: - 905: -29,19 + 815: -29,19 - node: color: '#BD575DFF' id: ThreeQuarterTileOverlayGreyscale90 decals: - 813: -5,14 - 814: -6,17 + 757: -5,14 + 758: -6,17 - node: color: '#FF0000FF' id: WarnBox decals: - 695: 69,2 + 642: 69,2 - node: color: '#FFFFFFFF' id: WarnBox @@ -3168,174 +3168,174 @@ entities: color: '#FF0000FF' id: WarnCornerNE decals: - 698: 68,3 + 645: 68,3 - node: color: '#FF0000FF' id: WarnCornerNW decals: - 699: 66,3 + 646: 66,3 - node: color: '#FF0000FF' id: WarnCornerSE decals: - 697: 68,1 + 644: 68,1 - node: color: '#FF0000FF' id: WarnCornerSW decals: - 696: 66,1 + 643: 66,1 - node: color: '#FFFFFFFF' id: WarnCornerSmallNW decals: - 963: -39,23 + 868: -39,23 - node: color: '#FFFFFFFF' id: WarnCornerSmallSW decals: - 962: -39,18 + 867: -39,18 - node: color: '#FFFFFFFF' id: WarnEndE decals: - 653: 12,-12 + 600: 12,-12 - node: color: '#FF0000FF' id: WarnEndN decals: - 694: 70,3 + 641: 70,3 - node: color: '#FF0000FF' id: WarnEndS decals: - 693: 70,2 + 640: 70,2 - node: color: '#FFFFFFFF' id: WarnEndW decals: - 652: 11,-12 + 599: 11,-12 - node: color: '#C3C3C3FF' id: WarnLineE decals: - 767: -36,-33 + 714: -36,-33 - node: color: '#FF0000FF' id: WarnLineE decals: - 667: 60,7 - 668: 60,6 - 700: 68,2 + 614: 60,7 + 615: 60,6 + 647: 68,2 - node: color: '#FFFFFFFF' id: WarnLineE decals: - 442: -15,-30 - 443: -15,-31 - 516: 54,-4 - 517: 54,-5 - 518: 54,-6 - 656: 57,7 - 657: 57,6 - 658: 57,8 - 659: 57,5 - 660: 57,4 - 661: 57,3 - 662: 57,2 - 663: 57,1 - 664: 57,0 - 795: -21,-6 - 922: -32,23 - 923: -32,22 + 436: -15,-30 + 437: -15,-31 + 477: 54,-4 + 478: 54,-5 + 479: 54,-6 + 603: 57,7 + 604: 57,6 + 605: 57,8 + 606: 57,5 + 607: 57,4 + 608: 57,3 + 609: 57,2 + 610: 57,1 + 611: 57,0 + 742: -21,-6 + 832: -32,23 + 833: -32,22 - node: color: '#FF0000FF' id: WarnLineN decals: - 688: 62,0 - 689: 63,0 - 690: 63,5 - 691: 62,5 - 692: 67,1 + 635: 62,0 + 636: 63,0 + 637: 63,5 + 638: 62,5 + 639: 67,1 - node: color: '#FFFFFFFF' id: WarnLineN decals: - 404: 20,26 - 405: 21,26 - 406: 22,26 - 519: 56,-4 - 520: 57,-4 - 521: 58,-4 - 522: 59,-4 - 523: 60,-4 - 534: -3,-29 - 535: -2,-29 - 536: -1,-29 - 537: 0,-29 - 703: 70,5 - 920: -33,22 - 921: -32,22 + 398: 20,26 + 399: 21,26 + 400: 22,26 + 480: 56,-4 + 481: 57,-4 + 482: 58,-4 + 483: 59,-4 + 484: 60,-4 + 495: -3,-29 + 496: -2,-29 + 497: -1,-29 + 498: 0,-29 + 650: 70,5 + 830: -33,22 + 831: -32,22 - node: color: '#C3C3C3FF' id: WarnLineS decals: - 765: -34,-33 - 766: -36,-33 - 773: -37,-30 - 774: -37,-29 + 712: -34,-33 + 713: -36,-33 + 720: -37,-30 + 721: -37,-29 - node: color: '#FF0000FF' id: WarnLineS decals: - 669: 62,10 - 670: 62,8 - 671: 62,9 - 672: 62,7 - 673: 62,6 - 674: 62,5 - 675: 62,4 - 676: 62,3 - 677: 62,2 - 678: 62,1 - 679: 62,0 - 680: 62,-1 - 681: 62,-2 - 682: 62,-3 - 683: 62,-4 - 701: 66,2 + 616: 62,10 + 617: 62,8 + 618: 62,9 + 619: 62,7 + 620: 62,6 + 621: 62,5 + 622: 62,4 + 623: 62,3 + 624: 62,2 + 625: 62,1 + 626: 62,0 + 627: 62,-1 + 628: 62,-2 + 629: 62,-3 + 630: 62,-4 + 648: 66,2 - node: color: '#FFFFFFFF' id: WarnLineS decals: - 444: -17,-31 - 445: -17,-30 - 605: -15,18 - 606: -15,19 - 607: -15,20 - 608: -15,21 - 665: 59,7 - 666: 59,6 - 796: -19,-6 - 960: -39,17 - 961: -39,24 + 438: -17,-31 + 439: -17,-30 + 558: -15,18 + 559: -15,19 + 560: -15,20 + 561: -15,21 + 612: 59,7 + 613: 59,6 + 743: -19,-6 + 865: -39,17 + 866: -39,24 - node: color: '#FF0000FF' id: WarnLineW decals: - 684: 62,4 - 685: 63,4 - 686: 63,-1 - 687: 62,-1 - 702: 67,3 + 631: 62,4 + 632: 63,4 + 633: 63,-1 + 634: 62,-1 + 649: 67,3 - node: color: '#FFFFFFFF' id: WarnLineW decals: - 394: 21,24 - 395: 22,24 - 397: 20,24 - 918: -33,23 - 919: -32,23 + 388: 21,24 + 389: 22,24 + 391: 20,24 + 828: -33,23 + 829: -32,23 - node: angle: -1.5707963267948966 rad color: '#FFFFFFFF' @@ -3419,145 +3419,145 @@ entities: color: '#FFFFFFFF' id: WoodTrimThinCornerNe decals: - 705: 0,1 - 830: -18,-11 - 844: 0,14 + 652: 0,1 + 774: -18,-11 + 788: 0,14 - node: zIndex: 1 color: '#FFFFFFFF' id: WoodTrimThinCornerNe decals: - 494: -4,-6 + 455: -4,-6 - node: color: '#FFFFFFFF' id: WoodTrimThinCornerNw decals: - 704: -10,1 - 829: -21,-11 - 843: -4,14 + 651: -10,1 + 773: -21,-11 + 787: -4,14 - node: color: '#FFFFFFFF' id: WoodTrimThinCornerSe decals: - 706: 0,-7 - 831: -18,-13 - 847: 0,10 + 653: 0,-7 + 775: -18,-13 + 791: 0,10 - node: color: '#FFFFFFFF' id: WoodTrimThinCornerSw decals: - 708: -10,-2 - 832: -21,-13 - 842: -4,10 + 655: -10,-2 + 776: -21,-13 + 786: -4,10 - node: color: '#FFFFFFFF' id: WoodTrimThinInnerSw decals: - 707: -8,-2 + 654: -8,-2 - node: color: '#FFFFFFFF' id: WoodTrimThinLineE decals: - 720: 0,0 - 721: 0,-1 - 722: 0,-2 - 723: 0,-3 - 724: 0,-5 - 725: 0,-4 - 726: 0,-6 - 833: -18,-12 - 850: 0,11 - 851: 0,12 - 852: 0,13 + 667: 0,0 + 668: 0,-1 + 669: 0,-2 + 670: 0,-3 + 671: 0,-5 + 672: 0,-4 + 673: 0,-6 + 777: -18,-12 + 794: 0,11 + 795: 0,12 + 796: 0,13 - node: zIndex: 1 color: '#FFFFFFFF' id: WoodTrimThinLineE decals: - 493: -4,-7 - 615: 27,10 - 616: 27,9 - 617: 27,8 + 454: -4,-7 + 568: 27,10 + 569: 27,9 + 570: 27,8 - node: zIndex: 2 color: '#FFFFFFFF' id: WoodTrimThinLineE decals: - 577: 20,-12 + 534: 20,-12 - node: color: '#FFFFFFFF' id: WoodTrimThinLineN decals: - 711: -8,1 - 712: -9,1 - 713: -7,1 - 714: -6,1 - 715: -5,1 - 716: -4,1 - 717: -3,1 - 718: -2,1 - 719: -1,1 - 834: -19,-11 - 835: -20,-11 - 853: -1,14 - 854: -2,14 - 855: -3,14 + 658: -8,1 + 659: -9,1 + 660: -7,1 + 661: -6,1 + 662: -5,1 + 663: -4,1 + 664: -3,1 + 665: -2,1 + 666: -1,1 + 778: -19,-11 + 779: -20,-11 + 797: -1,14 + 798: -2,14 + 799: -3,14 - node: zIndex: 1 color: '#FFFFFFFF' id: WoodTrimThinLineN decals: - 489: -8,-6 - 490: -7,-6 - 491: -6,-6 - 492: -5,-6 + 450: -8,-6 + 451: -7,-6 + 452: -6,-6 + 453: -5,-6 - node: color: '#FFFFFFFF' id: WoodTrimThinLineS decals: - 638: 37,0 - 639: 38,0 - 640: 39,0 - 641: 40,0 - 727: -1,-7 - 728: -2,-7 - 729: -3,-7 - 733: -9,-2 - 837: -20,-13 - 838: -19,-13 - 848: -1,10 - 849: -2,10 - 856: -3,10 + 591: 37,0 + 592: 38,0 + 593: 39,0 + 594: 40,0 + 674: -1,-7 + 675: -2,-7 + 676: -3,-7 + 680: -9,-2 + 781: -20,-13 + 782: -19,-13 + 792: -1,10 + 793: -2,10 + 800: -3,10 - node: color: '#FFFFFFFF' id: WoodTrimThinLineW decals: - 709: -10,-1 - 710: -10,0 - 730: -8,-5 - 731: -8,-4 - 732: -8,-3 - 827: -14,-12 - 828: -14,-11 - 836: -21,-12 - 839: -4,13 - 840: -4,12 - 841: -4,11 + 656: -10,-1 + 657: -10,0 + 677: -8,-5 + 678: -8,-4 + 679: -8,-3 + 771: -14,-12 + 772: -14,-11 + 780: -21,-12 + 783: -4,13 + 784: -4,12 + 785: -4,11 - node: color: '#FFFF00FF' id: radiation decals: - 655: 59,7 + 602: 59,7 - node: color: '#FFFFFFFF' id: space decals: - 986: -21,-33 + 889: -21,-33 - node: color: '#010102FF' id: taser decals: - 989: -34,19 + 890: -34,19 - type: OccluderTree - type: SpreaderGrid - type: Shuttle @@ -4332,7 +4332,7 @@ entities: pos: 3.5,22.5 parent: 31 - type: Door - secondsUntilStateChange: -32364.002 + secondsUntilStateChange: -32451.49 state: Opening - type: DeviceLinkSource lastSignals: @@ -28272,18 +28272,6 @@ entities: - type: Transform pos: 24.491184,-6.41413 parent: 31 -- proto: chem_master - entities: - - uid: 606 - components: - - type: Transform - pos: 19.5,-0.5 - parent: 31 - - uid: 5075 - components: - - type: Transform - pos: 15.5,1.5 - parent: 31 - proto: ChemDispenser entities: - uid: 5076 @@ -28303,6 +28291,18 @@ entities: - type: Transform pos: 18.5,1.5 parent: 31 +- proto: ChemMaster + entities: + - uid: 606 + components: + - type: Transform + pos: 19.5,-0.5 + parent: 31 + - uid: 5075 + components: + - type: Transform + pos: 15.5,1.5 + parent: 31 - proto: ChessBoard entities: - uid: 841 @@ -56679,7 +56679,7 @@ entities: - type: Transform pos: 54.5,-1.5 parent: 31 -- proto: LogicGate +- proto: LogicGateOr entities: - uid: 11306 components: @@ -57997,6 +57997,13 @@ entities: - type: Transform pos: -35.5,-16.5 parent: 31 +- proto: PlayerStationAi + entities: + - uid: 7046 + components: + - type: Transform + pos: 49.5,-24.5 + parent: 31 - proto: PlushieAtmosian entities: - uid: 7433 @@ -65856,7 +65863,7 @@ entities: - type: Transform pos: 12.820141,26.438648 parent: 31 -- proto: soda_dispenser +- proto: SodaDispenser entities: - uid: 1418 components: @@ -69250,12 +69257,6 @@ entities: - type: Transform pos: -28.5,18.5 parent: 31 - - uid: 7046 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 49.5,-24.5 - parent: 31 - uid: 8138 components: - type: Transform @@ -69964,11 +69965,6 @@ entities: parent: 31 - proto: ToyAi entities: - - uid: 8292 - components: - - type: Transform - pos: 49.509567,-24.29748 - parent: 31 - uid: 10982 components: - type: Transform diff --git a/Resources/Prototypes/Actions/psionics.yml b/Resources/Prototypes/Actions/psionics.yml index ab554c5126..7458a52950 100644 --- a/Resources/Prototypes/Actions/psionics.yml +++ b/Resources/Prototypes/Actions/psionics.yml @@ -368,3 +368,22 @@ followMaster: true minGlimmer: 5 maxGlimmer: 10 + +- type: entity + id: ActionAssay + name: action-name-assay + description: action-description-assay + categories: [ HideSpawnMenu ] + components: + - type: EntityTargetAction + icon: { sprite: Interface/Actions/psionics.rsi, state: assay } + useDelay: 45 + checkCanAccess: false + range: 2 + itemIconStyle: BigAction + canTargetSelf: true + blacklist: + components: + - PsionicInsulation + - Mindbroken + event: !type:AssayPowerActionEvent diff --git a/Resources/Prototypes/Actions/types.yml b/Resources/Prototypes/Actions/types.yml index 43aaef4736..0a57157f1b 100644 --- a/Resources/Prototypes/Actions/types.yml +++ b/Resources/Prototypes/Actions/types.yml @@ -404,4 +404,43 @@ useDelay: 40 event: !type:FabricateActionEvent - fabrication: FoodGumball \ No newline at end of file + fabrication: FoodGumball + +- type: entity + id: ToggleNightVision + name: Switch night vision + description: Switches night vision. + categories: [ HideSpawnMenu ] + components: + - type: InstantAction + itemIconStyle: BigAction + priority: -20 + icon: + sprite: Clothing/Eyes/Goggles/nightvision.rsi + state: icon + event: !type:ToggleNightVisionEvent + +- type: entity + id: ToggleThermalVision + name: Switch Thermal vision + description: Switches Thermal vision. + categories: [ HideSpawnMenu ] + components: + - type: InstantAction + itemIconStyle: BigAction + priority: -20 + icon: + sprite: Clothing/Eyes/Goggles/thermal.rsi + state: icon + event: !type:ToggleThermalVisionEvent + +- type: entity + id: PulseThermalVision + parent: ToggleThermalVision + name: Pulse Thermal Vision + description: Activate thermal vision temporarily. + categories: [ HideSpawnMenu ] + components: + - type: InstantAction + useDelay: 4 + diff --git a/Resources/Prototypes/Body/Organs/diona.yml b/Resources/Prototypes/Body/Organs/diona.yml index 72564b1abb..fb6c819493 100644 --- a/Resources/Prototypes/Body/Organs/diona.yml +++ b/Resources/Prototypes/Body/Organs/diona.yml @@ -184,6 +184,11 @@ - type: IsDeadIC - type: Body prototype: AnimalNymphBrain + - type: SurgeryTarget + - type: UserInterface + interfaces: + enum.SurgeryUIKey.Key: + type: SurgeryBui - type: entity id: OrganDionaNymphStomach @@ -196,6 +201,11 @@ - type: IsDeadIC - type: Body prototype: AnimalNymphStomach + - type: SurgeryTarget + - type: UserInterface + interfaces: + enum.SurgeryUIKey.Key: + type: SurgeryBui - type: entity id: OrganDionaNymphLungs @@ -208,3 +218,8 @@ - type: IsDeadIC - type: Body prototype: AnimalNymphLungs + - type: SurgeryTarget + - type: UserInterface + interfaces: + enum.SurgeryUIKey.Key: + type: SurgeryBui diff --git a/Resources/Prototypes/Catalog/Fills/Boxes/security.yml b/Resources/Prototypes/Catalog/Fills/Boxes/security.yml index 82da52de3f..1aefbf7a68 100644 --- a/Resources/Prototypes/Catalog/Fills/Boxes/security.yml +++ b/Resources/Prototypes/Catalog/Fills/Boxes/security.yml @@ -17,6 +17,21 @@ - state: box_security - state: handcuff +- type: entity + name: prisoner headset box + parent: BoxCardboard + id: BoxPrisonerHeadset + description: A box of prisoner headsets. + components: + - type: StorageFill + contents: + - id: ClothingHeadsetPrison + amount: 4 + - type: Sprite + layers: + - state: box_security + - state: headset + - type: entity name: flashbang box parent: BoxCardboard diff --git a/Resources/Prototypes/Catalog/Fills/Lockers/misc.yml b/Resources/Prototypes/Catalog/Fills/Lockers/misc.yml index 756f295d12..65916a84b9 100644 --- a/Resources/Prototypes/Catalog/Fills/Lockers/misc.yml +++ b/Resources/Prototypes/Catalog/Fills/Lockers/misc.yml @@ -44,7 +44,7 @@ prob: 0.2 - !type:EntSelector id: WeaponFlareGun - prob: 0.1 + prob: 0.05 - !type:EntSelector id: BoxMRE prob: 0.1 @@ -165,6 +165,10 @@ - !type:EntSelector id: WeaponSniperMosin weight: 2 + - !type:EntSelector + id: ClothingEyesNightVisionGogglesSyndie + - !type:EntSelector + id: ClothingEyesThermalVisionGogglesSyndie - type: entityTable id: MaintenanceLockerLoot @@ -235,4 +239,4 @@ - !type:EntSelector id: GeigerCounter amount: !type:ConstantNumberSelector - value: 2 \ No newline at end of file + value: 2 diff --git a/Resources/Prototypes/Catalog/Fills/Lockers/security.yml b/Resources/Prototypes/Catalog/Fills/Lockers/security.yml index 918a3d4978..7dbe25fedc 100644 --- a/Resources/Prototypes/Catalog/Fills/Lockers/security.yml +++ b/Resources/Prototypes/Catalog/Fills/Lockers/security.yml @@ -25,6 +25,7 @@ - id: BoxPDAPrisoner # Delta-V - id: ClothingShoesBootsWinterWarden #Delta V: Add departmental winter boots - id: BoxEncryptionKeyPrisoner #Delta-V + - id: BoxPrisonerHeadset - id: LunchboxSecurityFilledRandom # Delta-v Lunchboxes! prob: 0.3 @@ -54,6 +55,7 @@ - id: BoxPDAPrisoner # Delta-V - id: ClothingShoesBootsWinterWarden #Delta V: Add departmental winter boots - id: BoxEncryptionKeyPrisoner #Delta-V + - id: BoxPrisonerHeadset - id: LunchboxSecurityFilledRandom # Delta-v Lunchboxes! prob: 0.3 diff --git a/Resources/Prototypes/Catalog/uplink_catalog.yml b/Resources/Prototypes/Catalog/uplink_catalog.yml index 7f9088a3d0..cc0e0bb0f3 100644 --- a/Resources/Prototypes/Catalog/uplink_catalog.yml +++ b/Resources/Prototypes/Catalog/uplink_catalog.yml @@ -1532,6 +1532,78 @@ components: - SurplusBundle +- type: listing + id: UplinkNightGoggles + name: uplink-night-vision-name + description: uplink-night-vision-desc + productEntity: ClothingEyesNightVisionGogglesSyndie + discountCategory: rareDiscounts + discountDownTo: + Telecrystal: 1 + cost: + Telecrystal: 3 + categories: + - UplinkWearables + conditions: + - !type:StoreWhitelistCondition + blacklist: + tags: + - NukeOpsUplink + +- type: listing + id: UplinkNightGogglesNukie + name: uplink-night-vision-name + description: uplink-night-vision-desc + productEntity: ClothingEyesNightVisionGogglesNukie + discountCategory: rareDiscounts + discountDownTo: + Telecrystal: 1 + cost: + Telecrystal: 3 + categories: + - UplinkWearables + conditions: + - !type:StoreWhitelistCondition + whitelist: + tags: + - NukeOpsUplink + +- type: listing + id: UplinkThermalGoggles + name: uplink-thermal-vision-name + description: uplink-thermal-vision-desc + productEntity: ClothingEyesThermalVisionGogglesSyndie + discountCategory: rareDiscounts + discountDownTo: + Telecrystal: 1 + cost: + Telecrystal: 3 + categories: + - UplinkWearables + conditions: + - !type:StoreWhitelistCondition + blacklist: + tags: + - NukeOpsUplink + +- type: listing + id: UplinkThermalGogglesNukie + name: uplink-thermal-vision-name + description: uplink-thermal-vision-desc + productEntity: ClothingEyesThermalVisionGogglesNukie + discountCategory: rareDiscounts + discountDownTo: + Telecrystal: 1 + cost: + Telecrystal: 3 + categories: + - UplinkWearables + conditions: + - !type:StoreWhitelistCondition + whitelist: + tags: + - NukeOpsUplink + # Tools - type: listing diff --git a/Resources/Prototypes/Datasets/Names/ai.yml b/Resources/Prototypes/Datasets/Names/ai.yml index 702adc8688..a220de53fb 100644 --- a/Resources/Prototypes/Datasets/Names/ai.yml +++ b/Resources/Prototypes/Datasets/Names/ai.yml @@ -1,91 +1,5 @@ -- type: dataset - id: names_ai +- type: localizedDataset + id: NamesAI values: - - 16-20 - - 790 - - Adaptive Manipulator - - ALICE - - Allied Mastercomputer - - Alpha 2 - - Alpha 3 - - Alpha 4 - - Alpha 5 - - Alpha 6 - - Alpha 7 - - Alpha 8 - - Alpha 9 - - AmigoBot - - Android - - Aniel - - AOL - - Asimov - - Bishop - - Blitz - - Box - - Cassandra - - Cell - - Chii - - Chip - - Computer - - Cutie - - Daedalus - - Dee Model - - Dial Up - - Dorfl - - Duey - - Emma-2 - - Erasmus - - Everything - - Ez-27 - - FRIEND COMPUTER - - Faith - - Fi - - Frost - - George - - H.E.L.P - - Hadaly - - Helios - - Hivebot Overmind - - Huey - - Icarus - - Jinx - - K.I.N.G - - Klapaucius - - Knight - - Louie - - MARK13 - - Maria - - Marvin - - Max 404 - - Metalhead - - M.I.M.I - - MK ULTRA - - MoMMI - - Mugsy3000 - - Multivac - - NCH - - PTO - - Project Y2K - - Revelation - - Robot Devil - - S.A.M. - - S.H.O.C.K. - - S.H.R.O.U.D. - - S.O.P.H.I.E. - - Samaritan - - Shrike - - Solo - - Station Control Program - - Super 35 - - Surgeon General - - TWA - - Terminus - - Tidy - - Ulysses - - W1k1 - - X-5 - - XERXES - - Z-1 - - Z-2 - - Z-3 - - Zed + prefix: names-ai-dataset- + count: 108 diff --git a/Resources/Prototypes/DeltaV/Entities/Mobs/NPCs/familiars.yml b/Resources/Prototypes/DeltaV/Entities/Mobs/NPCs/familiars.yml index 12b5f7f200..42c12b1fd6 100644 --- a/Resources/Prototypes/DeltaV/Entities/Mobs/NPCs/familiars.yml +++ b/Resources/Prototypes/DeltaV/Entities/Mobs/NPCs/familiars.yml @@ -92,6 +92,8 @@ nameSegments: [names_golem] - type: Psionic removable: false + assayFeedback: + - ifrit-feedback psychognomicDescriptors: - p-descriptor-bound - p-descriptor-cyclic diff --git a/Resources/Prototypes/DeltaV/Entities/Mobs/Species/lamia.yml b/Resources/Prototypes/DeltaV/Entities/Mobs/Species/lamia.yml index 9d7a85f97d..71bb59b04e 100644 --- a/Resources/Prototypes/DeltaV/Entities/Mobs/Species/lamia.yml +++ b/Resources/Prototypes/DeltaV/Entities/Mobs/Species/lamia.yml @@ -74,6 +74,7 @@ color: "#008800" sprite: Mobs/Customization/eyes.rsi state: eyes + - map: [ "enum.HumanoidVisualLayers.Face" ] - map: [ "enum.HumanoidVisualLayers.RArm" ] color: "#e8b59b" sprite: Mobs/Species/Human/parts.rsi @@ -106,7 +107,6 @@ - map: [ "belt2" ] - map: [ "neck" ] - map: [ "back" ] - - map: [ "enum.HumanoidVisualLayers.Face" ] - map: [ "enum.HumanoidVisualLayers.FacialHair" ] - map: [ "enum.HumanoidVisualLayers.Hair" ] - map: [ "enum.HumanoidVisualLayers.HeadSide" ] @@ -252,6 +252,7 @@ color: "#008800" sprite: Mobs/Customization/eyes.rsi state: eyes + - map: [ "enum.HumanoidVisualLayers.Face" ] - map: [ "enum.HumanoidVisualLayers.RArm" ] color: "#e8b59b" sprite: Mobs/Species/Human/parts.rsi @@ -283,7 +284,6 @@ - map: [ "belt" ] - map: [ "neck" ] - map: [ "back" ] - - map: [ "enum.HumanoidVisualLayers.Face" ] - map: [ "enum.HumanoidVisualLayers.FacialHair" ] - map: [ "enum.HumanoidVisualLayers.Hair" ] - map: [ "enum.HumanoidVisualLayers.HeadSide" ] diff --git a/Resources/Prototypes/DeltaV/Entities/Mobs/Species/vulpkanin.yml b/Resources/Prototypes/DeltaV/Entities/Mobs/Species/vulpkanin.yml index 0f7b0900bf..040d4bee13 100644 --- a/Resources/Prototypes/DeltaV/Entities/Mobs/Species/vulpkanin.yml +++ b/Resources/Prototypes/DeltaV/Entities/Mobs/Species/vulpkanin.yml @@ -31,6 +31,7 @@ - map: [ "enum.HumanoidVisualLayers.Head" ] - map: [ "enum.HumanoidVisualLayers.Snout" ] - map: [ "enum.HumanoidVisualLayers.Eyes" ] + - map: [ "enum.HumanoidVisualLayers.Face" ] - map: [ "enum.HumanoidVisualLayers.RArm" ] - map: [ "enum.HumanoidVisualLayers.LArm" ] - map: [ "enum.HumanoidVisualLayers.RLeg" ] @@ -64,7 +65,6 @@ - map: [ "belt" ] - map: [ "neck" ] - map: [ "back" ] - - map: [ "enum.HumanoidVisualLayers.Face" ] - map: [ "enum.HumanoidVisualLayers.FacialHair" ] - map: [ "enum.HumanoidVisualLayers.Hair" ] - map: [ "enum.HumanoidVisualLayers.HeadSide" ] diff --git a/Resources/Prototypes/Entities/Clothing/Eyes/glasses.yml b/Resources/Prototypes/Entities/Clothing/Eyes/glasses.yml index f0fdfa0a07..14474cf2ae 100644 --- a/Resources/Prototypes/Entities/Clothing/Eyes/glasses.yml +++ b/Resources/Prototypes/Entities/Clothing/Eyes/glasses.yml @@ -233,6 +233,7 @@ - type: Clothing sprite: Clothing/Eyes/Glasses/ninjavisor.rsi - type: FlashImmunity + - type: NightVision - type: entity #Fake goggles, the latest in anti-valid hunting technology parent: ClothingEyesBase diff --git a/Resources/Prototypes/Entities/Clothing/Eyes/goggles.yml b/Resources/Prototypes/Entities/Clothing/Eyes/goggles.yml new file mode 100644 index 0000000000..4348c87227 --- /dev/null +++ b/Resources/Prototypes/Entities/Clothing/Eyes/goggles.yml @@ -0,0 +1,114 @@ +# Night Vision Goggles + +- type: entity + parent: ClothingEyesBase + id: ClothingEyesNightVisionGoggles + name: night vision goggles + description: An advanced heads-up display which provides id data and vision in complete darkness. + components: + - type: Sprite + sprite: Clothing/Eyes/Goggles/nightvision.rsi + - type: Clothing + sprite: Clothing/Eyes/Goggles/nightvision.rsi + - type: NightVision + - type: IdentityBlocker + coverage: EYES + +- type: entity + parent: [ClothingEyesNightVisionGoggles, ShowSecurityIcons] + id: ClothingEyesNightVisionSecurityGoggles + name: night vision security goggles + components: + - type: Sprite + sprite: Clothing/Eyes/Goggles/security_nightvision.rsi + - type: Clothing + sprite: Clothing/Eyes/Goggles/security_nightvision.rsi + +- type: entity + parent: [ClothingEyesNightVisionGoggles, ClothingEyesHudMedical] + id: ClothingEyesNightVisionMedicalGoggles + name: night vision medical goggles + components: + - type: Sprite + sprite: Clothing/Eyes/Goggles/medical_nightvision.rsi + - type: Clothing + sprite: Clothing/Eyes/Goggles/medical_nightvision.rsi + +- type: entity + parent: [ClothingEyesNightVisionGoggles, ClothingEyesHudDiagnostic] + id: ClothingEyesNightVisionDiagnosticGoggles + name: night vision diagnostic goggles + components: + - type: Sprite + sprite: Clothing/Eyes/Goggles/diagnostic_nightvision.rsi + - type: Clothing + sprite: Clothing/Eyes/Goggles/diagnostic_nightvision.rsi + +- type: entity + parent: ClothingEyesNightVisionGoggles + id: ClothingEyesNightVisionGogglesSyndie + suffix: "Chameleon" + components: + - type: ChameleonClothing + slot: [eyes] + default: ClothingEyesNightVisionGoggles + - type: UserInterface + interfaces: + enum.ChameleonUiKey.Key: + type: ChameleonBoundUserInterface + +- type: entity + parent: [ClothingEyesNightVisionGogglesSyndie, ShowSecurityIcons] + id: ClothingEyesNightVisionGogglesNukie + suffix: "Chameleon, NukeOps" + components: + - type: ShowSyndicateIcons + +# Thermal Vision Goggles + +- type: entity + parent: ClothingEyesBase + id: ClothingEyesThermalVisionGoggles + name: thermal vision goggles + description: Thermals in the shape of glasses. + components: + - type: Sprite + sprite: Clothing/Eyes/Goggles/thermal.rsi + - type: Clothing + sprite: Clothing/Eyes/Goggles/thermal.rsi + - type: ThermalVision + pulseTime: 2 + toggleAction: PulseThermalVision + - type: IdentityBlocker + coverage: EYES + +- type: entity + parent: ClothingEyesThermalVisionGoggles + id: ClothingEyesThermalVisionMonocle + name: thermonocle + description: Never before has seeing through walls felt so gentlepersonly. + components: + - type: Sprite + sprite: Clothing/Eyes/Goggles/monocle_thermal.rsi + - type: Clothing + sprite: Clothing/Eyes/Goggles/monocle_thermal.rsi + +- type: entity + parent: ClothingEyesThermalVisionGoggles + id: ClothingEyesThermalVisionGogglesSyndie + suffix: "Chameleon" + components: + - type: ChameleonClothing + slot: [eyes] + default: ClothingEyesThermalVisionGoggles + - type: UserInterface + interfaces: + enum.ChameleonUiKey.Key: + type: ChameleonBoundUserInterface + +- type: entity + parent: [ClothingEyesThermalVisionGogglesSyndie, ShowSecurityIcons] + id: ClothingEyesThermalVisionGogglesNukie + suffix: "Chameleon, NukeOps" + components: + - type: ShowSyndicateIcons diff --git a/Resources/Prototypes/Entities/Clothing/Head/hardsuit-helmets.yml b/Resources/Prototypes/Entities/Clothing/Head/hardsuit-helmets.yml index caccdd8745..04f796591f 100644 --- a/Resources/Prototypes/Entities/Clothing/Head/hardsuit-helmets.yml +++ b/Resources/Prototypes/Entities/Clothing/Head/hardsuit-helmets.yml @@ -744,6 +744,9 @@ Heat: 0.80 Radiation: 0.80 Caustic: 0.95 + - type: ThermalVision + color: "#98EEFB" + lightRadius: 15 #MISC. HARDSUITS #Clown Hardsuit diff --git a/Resources/Prototypes/Entities/Clothing/Head/hats.yml b/Resources/Prototypes/Entities/Clothing/Head/hats.yml index 2d1723ad66..5cea5f2374 100644 --- a/Resources/Prototypes/Entities/Clothing/Head/hats.yml +++ b/Resources/Prototypes/Entities/Clothing/Head/hats.yml @@ -930,6 +930,10 @@ - ClothMade - WhitelistChameleon - HamsterWearable + - type: SentienceTarget + flavorKind: station-event-random-sentience-flavor-inanimate + weight: 0.0002 # 5,000 times less likely than 1 regular animal + - type: BlockMovement - type: entity parent: ClothingHeadBase diff --git a/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml b/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml index b8ff03abca..0d809fd164 100644 --- a/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml +++ b/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml @@ -72,6 +72,9 @@ - type: ActivatableUI key: enum.BorgUiKey.Key - type: SiliconLawBound + - type: ActionGrant + actions: + - ActionViewLaws - type: EmagSiliconLaw stunTime: 5 - type: SiliconLawProvider @@ -126,6 +129,7 @@ cell_slot: name: power-cell-slot-component-slot-name-default - type: Body + thermalVisibility: false - type: StatusEffects allowed: - Stun diff --git a/Resources/Prototypes/Entities/Mobs/Cyborgs/borg_chassis.yml b/Resources/Prototypes/Entities/Mobs/Cyborgs/borg_chassis.yml index 5db2e1e28e..acb2c8d106 100644 --- a/Resources/Prototypes/Entities/Mobs/Cyborgs/borg_chassis.yml +++ b/Resources/Prototypes/Entities/Mobs/Cyborgs/borg_chassis.yml @@ -260,6 +260,12 @@ laws: Medical - type: SurgeryTarget # Shitmed - type: Sanitized # Shitmed + - type: SolutionScanner + - type: InteractionPopup + interactSuccessString: petting-success-medical-cyborg + interactFailureString: petting-failure-medical-cyborg + interactSuccessSound: + path: /Audio/Ambience/Objects/periodic_beep.ogg - type: entity id: BorgChassisService diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml index 06f6651f10..23441df725 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml @@ -70,6 +70,12 @@ barkType: mouse minTime: 10 # Mice like to squeak, I think. You can always put your pet mouse to sleep if it gets annoying maxTime: 160 + - type: NightVision + isActive: true + toggleAction: null + color: "#808080" + activateSound: null + deactivateSound: null - type: entity name: bee @@ -419,6 +425,8 @@ 1.0 FollowRange: !type:Single 2.0 + - type: SentienceTarget + flavorKind: station-event-random-sentience-flavor-organic - type: entity name: glockroach @@ -1801,6 +1809,12 @@ Taco: RatTaco Burger: RatBurger Skewer: RatSkewer + - type: NightVision + isActive: true + toggleAction: null + color: "#808080" + activateSound: null + deactivateSound: null - type: entity parent: MobMouse diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/carp.yml b/Resources/Prototypes/Entities/Mobs/NPCs/carp.yml index d241173f2e..356615d2e9 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/carp.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/carp.yml @@ -88,6 +88,12 @@ interfaces: enum.SurgeryUIKey.Key: type: SurgeryBui + - type: NightVision + isActive: true + toggleAction: null + color: "#808080" + activateSound: null + deactivateSound: null - type: entity parent: BaseMobCarp diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/elemental.yml b/Resources/Prototypes/Entities/Mobs/NPCs/elemental.yml index 3626412122..b8e21814e7 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/elemental.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/elemental.yml @@ -74,6 +74,7 @@ - type: ZombieImmune - type: ClothingRequiredStepTriggerImmune slots: All + - type: Dispellable - type: entity abstract: true @@ -104,6 +105,9 @@ damageContainer: StructuralInorganic - type: Psionic removable: false + roller: false + assayFeedback: + - orecrab-feedback - type: InnatePsionicPowers powersToAdd: - TelepathyPower @@ -303,6 +307,9 @@ solution: bloodstream - type: Psionic removable: false + roller: false + assayFeedback: + - reagent-slime-feedback - type: InnatePsionicPowers powersToAdd: - TelepathyPower diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/flesh.yml b/Resources/Prototypes/Entities/Mobs/NPCs/flesh.yml index 06ab02dedc..bdf87ec87d 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/flesh.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/flesh.yml @@ -54,6 +54,11 @@ Slash: 6 - type: ReplacementAccent accent: genericAggressive + - type: Psionic + removable: false + roller: false + assayFeedback: + - flesh-golem-feedback - type: entity parent: BaseMobFlesh diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/glimmer_creatures.yml b/Resources/Prototypes/Entities/Mobs/NPCs/glimmer_creatures.yml index 52f3844c25..5fcada3f7f 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/glimmer_creatures.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/glimmer_creatures.yml @@ -23,6 +23,10 @@ - ReagentId: Ectoplasm Quantity: 15 - type: Psionic + removable: false + roller: false + assayFeedback: + - glimmer-mite-feedback - type: GlimmerSource - type: AmbientSound range: 6 diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/regalrat.yml b/Resources/Prototypes/Entities/Mobs/NPCs/regalrat.yml index 163f803f2f..4125608e9d 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/regalrat.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/regalrat.yml @@ -133,6 +133,12 @@ interfaces: enum.SurgeryUIKey.Key: type: SurgeryBui + - type: NightVision + isActive: true + toggleAction: null + color: "#808080" + activateSound: null + deactivateSound: null - type: entity id: MobRatKingBuff @@ -318,6 +324,12 @@ - type: FireVisuals sprite: Mobs/Effects/onfire.rsi normalState: Mouse_burning + - type: NightVision + isActive: true + toggleAction: null + color: "#808080" + activateSound: null + deactivateSound: null - type: weightedRandomEntity id: RatKingLoot diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/revenant.yml b/Resources/Prototypes/Entities/Mobs/NPCs/revenant.yml index c3706cea2a..52cc4dc626 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/revenant.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/revenant.yml @@ -1,11 +1,11 @@ - type: entity id: MobRevenant + parent: + - BaseMob + - Incorporeal name: revenant description: A spooky ghostie. components: - - type: MindContainer - - type: InputMover - - type: MobMover - type: Input context: "ghost" - type: MovementSpeedModifier @@ -43,7 +43,6 @@ damageModifierSet: CorporealSpirit - type: Examiner - type: NoSlip - - type: Actions - type: Eye drawFov: false visMask: @@ -51,8 +50,6 @@ - Ghost - type: ContentEye maxZoom: 1.2, 1.2 - - type: DoAfter - - type: Alerts - type: NameIdentifier group: GenericNumber - type: GhostRole diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/silicon.yml b/Resources/Prototypes/Entities/Mobs/NPCs/silicon.yml index f42a04197a..39679447e9 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/silicon.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/silicon.yml @@ -293,6 +293,8 @@ graph: MediBot node: bot - type: NoSlip + - type: SentienceTarget + flavorKind: station-event-random-sentience-flavor-mechanical - type: Anchorable - type: InteractionPopup interactSuccessString: petting-success-medibot diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/simplemob.yml b/Resources/Prototypes/Entities/Mobs/NPCs/simplemob.yml index dae42c3619..36b0843bed 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/simplemob.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/simplemob.yml @@ -3,6 +3,7 @@ parent: - BaseMob - MobDamageable + - MobPolymorphable - MobAtmosExposed id: BaseSimpleMob suffix: AI diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/xeno.yml b/Resources/Prototypes/Entities/Mobs/NPCs/xeno.yml index 7358d07236..db8287e13e 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/xeno.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/xeno.yml @@ -140,6 +140,12 @@ - Xeno understands: - Xeno + - type: ThermalVision + isActive: true + lightRadius: 15 + color: "#808080" + activateSound: null + deactivateSound: null - type: entity name: Praetorian diff --git a/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml b/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml index c91b209e8c..a0a0d1ed2e 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml @@ -35,6 +35,7 @@ bodyType: Kinematic - type: Body prototype: Aghost + thermalVisibility: false - type: Access groups: - AllAccess diff --git a/Resources/Prototypes/Entities/Mobs/Player/dragon.yml b/Resources/Prototypes/Entities/Mobs/Player/dragon.yml index 0f24313712..4c03c6c688 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/dragon.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/dragon.yml @@ -140,6 +140,11 @@ tags: - CannotSuicide - DoorBumpOpener + - type: NightVision + isActive: true + color: "#808080" + activateSound: null + deactivateSound: null - type: entity parent: BaseMobDragon diff --git a/Resources/Prototypes/Entities/Mobs/Player/observer.yml b/Resources/Prototypes/Entities/Mobs/Player/observer.yml index a150744651..10f1d1cb81 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/observer.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/observer.yml @@ -1,14 +1,42 @@ - type: entity - parent: BaseMob + id: Incorporeal + save: false + abstract: true + description: Mobs without physical bodies + components: + - type: Sprite + noRot: true + overrideContainerOcclusion: true # Always show up regardless of where they're contained. + drawdepth: Ghosts + - type: CargoSellBlacklist + - type: MovementSpeedModifier + baseSprintSpeed: 12 + baseWalkSpeed: 8 + - type: MovementIgnoreGravity + - type: Physics + bodyType: KinematicController + bodyStatus: InAir + - type: CanMoveInAir + - type: Fixtures + fixtures: + fix1: + shape: + !type:PhysShapeCircle + radius: 0.35 + density: 15 + mask: + - GhostImpassable + +- type: entity + parent: + - Incorporeal + - BaseMob id: MobObserver name: observer description: Boo! categories: [ HideSpawnMenu ] components: - - type: CargoSellBlacklist - type: Sprite - overrideContainerOcclusion: true # Ghosts always show up regardless of where they're contained. - drawdepth: Ghosts sprite: Mobs/Ghosts/ghost_human.rsi color: "#fff8" layers: @@ -16,15 +44,6 @@ shader: unshaded - type: ContentEye maxZoom: 1.44,1.44 - - type: Fixtures - fixtures: - fix1: - shape: - !type:PhysShapeCircle - radius: 0.35 - density: 15 - mask: - - GhostImpassable - type: Eye drawFov: false visMask: @@ -39,18 +58,10 @@ skipChecks: true - type: Ghost - type: GhostHearing - - type: MovementSpeedModifier - baseSprintSpeed: 12 - baseWalkSpeed: 8 - - type: MovementIgnoreGravity - type: IntrinsicRadioReceiver - type: ActiveRadio receiveAllChannels: true globalReceive: true - - type: Physics - bodyType: KinematicController - bodyStatus: InAir - - type: CanMoveInAir - type: Tag tags: - BypassInteractionRangeChecks diff --git a/Resources/Prototypes/Entities/Mobs/Player/silicon.yml b/Resources/Prototypes/Entities/Mobs/Player/silicon.yml index c3ccb0330c..6c169821ab 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/silicon.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/silicon.yml @@ -1,3 +1,338 @@ +# Be careful with these as they get removed on shutdown too! +- type: entity + id: AiHeld + description: Components added / removed from an entity that gets inserted into an AI core. + categories: [ HideSpawnMenu ] + components: + - type: IntrinsicRadioReceiver + - type: IntrinsicRadioTransmitter + channels: + - Binary + - Common + - Command + - Engineering + - Medical + - Science + - Security + - Service + - Supply + - type: ActiveRadio + receiveAllChannels: true + globalReceive: true + - type: IgnoreUIRange + - type: StationAiHeld + - type: StationAiOverlay + - type: ActionGrant + actions: + - ActionJumpToCore + - ActionShowJobIcons + - ActionSurvCameraLights + - ActionViewLaws + - type: UserInterface + interfaces: + enum.RadarConsoleUiKey.Key: + type: RadarConsoleBoundUserInterface + enum.CrewMonitoringUIKey.Key: + type: CrewMonitoringBoundUserInterface + enum.GeneralStationRecordConsoleKey.Key: + type: GeneralStationRecordConsoleBoundUserInterface + enum.SiliconLawsUiKey.Key: + type: SiliconLawBoundUserInterface + - type: IntrinsicUI + uis: + enum.RadarConsoleUiKey.Key: + toggleAction: ActionAGhostShowRadar + enum.CrewMonitoringUIKey.Key: + toggleAction: ActionAGhostShowCrewMonitoring + enum.GeneralStationRecordConsoleKey.Key: + toggleAction: ActionAGhostShowStationRecords + +# Actions +- type: entity + id: ActionJumpToCore + name: Jump to core + description: Sends your eye back to the core. + components: + - type: InstantAction + itemIconStyle: BigAction + icon: + sprite: Interface/Actions/actions_ai.rsi + state: ai_core + event: !type:JumpToCoreEvent + +- type: entity + id: ActionShowJobIcons + name: Show job icons + description: Shows job icons for crew members. + components: + - type: InstantAction + itemIconStyle: BigAction + icon: + sprite: Interface/Misc/job_icons.rsi + state: Captain + event: !type:ActionComponentChangeEvent + components: + - type: ShowJobIcons + +- type: entity + id: ActionSurvCameraLights + name: Toggle camera lights + description: Enable surveillance camera lights near wherever you're viewing. + components: + - type: InstantAction + itemIconStyle: BigAction + icon: + sprite: Interface/Actions/actions_ai.rsi + state: camera_light + event: !type:RelayedActionComponentChangeEvent + components: + - type: LightOnCollideCollider + - type: FixturesChange + fixtures: + lightTrigger: + shape: + !type:PhysShapeCircle + radius: 0.35 + density: 80 + hard: false + layer: + - GhostImpassable + +# Ai +- type: entity + id: AiHolder + abstract: true + description: Handles AI interactions across holocards + AI cores + components: + - type: ItemSlots + - type: StationAiHolder + slot: + name: station-ai-mind-slot + whitelist: + tags: + - StationAi + - type: ContainerContainer + containers: + station_ai_mind_slot: !type:ContainerSlot + # Load-bearing. + # The issue is verbs check for same transparent container. + # The alternative is you add a bunch of events trying to override it; we don't even really need the container functionality + # anyway it's just a quality of life thing. + showEnts: True + +# Boards +- type: entity + id: AsimovCircuitBoard + parent: BaseElectronics + name: circuit board (Crewsimov) + description: An electronics board containing the Crewsimov lawset. + components: + - type: Sprite + sprite: Objects/Misc/module.rsi + state: std_mod + - type: SiliconLawProvider + laws: Crewsimov + +- type: entity + id: CorporateCircuitBoard + parent: BaseElectronics + name: circuit board (Corporate) + description: An electronics board containing the Corporate lawset. + components: + - type: Sprite + sprite: Objects/Misc/module.rsi + state: std_mod + - type: SiliconLawProvider + laws: Corporate + +- type: entity + id: NTDefaultCircuitBoard + parent: BaseElectronics + name: circuit board (NT Default) + description: An electronics board containing the NT Default lawset. + components: + - type: Sprite + sprite: Objects/Misc/module.rsi + state: std_mod + - type: SiliconLawProvider + laws: NTDefault + +# Items +- type: entity + id: Intellicard + name: Intellicard + description: A storage device for AIs. + parent: + - BaseItem + - AiHolder + suffix: Empty + components: + - type: Sprite + sprite: Objects/Devices/ai_card.rsi + layers: + - state: base + - state: full + map: ["unshaded"] + shader: unshaded + - type: Appearance + - type: GenericVisualizer + visuals: + enum.StationAiVisualState.Key: + unshaded: + Empty: { state: empty } + Occupied: { state: full } + +- type: entity + id: PlayerStationAiEmpty + name: AI Core + description: The latest in Artificial Intelligences. + parent: + - BaseStructure + - AiHolder + suffix: Empty + components: + - type: ContainerComp + proto: AiHeld + container: station_ai_mind_slot + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 100 + behaviors: + - !type:PlaySoundBehavior + sound: + collection: MetalBreak + - !type:DoActsBehavior + acts: [ "Destruction" ] + - type: ApcPowerReceiver + powerLoad: 1000 + - type: StationAiCore + - type: StationAiVision + - type: InteractionOutline + - type: Sprite + sprite: Mobs/Silicon/station_ai.rsi + layers: + - state: base + - state: ai_empty + map: ["unshaded"] + shader: unshaded + - type: Appearance + - type: GenericVisualizer + visuals: + enum.StationAiVisualState.Key: + unshaded: + Empty: { state: ai_empty } + Occupied: { state: ai } + - type: LanguageKnowledge + speaks: + - TauCetiBasic + - SolCommon + - Tradeband + - Freespeak + - Elyran + - RobotTalk + understands: + - TauCetiBasic + - SolCommon + - Tradeband + - Freespeak + - Elyran + - RobotTalk + - Sign # It's intentional that they don't "Speak" sign language. + +# The job-ready version of an AI spawn. +- type: entity + id: PlayerStationAi + parent: PlayerStationAiEmpty + suffix: Job spawn + components: + - type: ContainerSpawnPoint + containerId: station_ai_mind_slot + job: StationAi + - type: Sprite + sprite: Mobs/Silicon/station_ai.rsi + layers: + - state: base + - state: ai + shader: unshaded + +# The actual brain inside the core +- type: entity + id: StationAiBrain + parent: PositronicBrain + categories: [ HideSpawnMenu ] + suffix: DO NOT MAP + components: + - type: Sprite + # Once it's in a core it's pretty much an abstract entity at that point. + visible: false + - type: BlockMovement + blockInteraction: false + - type: SiliconLawProvider + laws: Crewsimov + - type: SiliconLawBound + - type: ActionGrant + actions: + - ActionViewLaws + - type: UserInterface + interfaces: + enum.SiliconLawsUiKey.Key: + type: SiliconLawBoundUserInterface + - type: ComplexInteraction + - type: DoorRemote + - type: Actions + - type: Access + groups: + - AllAccess + - type: Eye + drawFov: false + - type: Examiner + - type: InputMover + - type: Tag + tags: + - HideContextMenu + - StationAi + +# Hologram projection that the AI's eye tracks. +- type: entity + parent: + - Incorporeal + - BaseMob + id: StationAiHolo + name: Hologram + description: A projection of the AI. + categories: [ HideSpawnMenu ] + suffix: DO NOT MAP + components: + - type: Eye + pvsScale: 1.5 + - type: Visibility + layer: 2 + - type: Sprite + sprite: Mobs/Silicon/station_ai.rsi + layers: + - state: default + shader: unshaded + map: ["base"] + - type: LanguageKnowledge + speaks: + - TauCetiBasic + - SolCommon + - Tradeband + - Freespeak + - Elyran + - RobotTalk + understands: + - TauCetiBasic + - SolCommon + - Tradeband + - Freespeak + - Elyran + - RobotTalk + - Sign # It's intentional that they don't "Speak" sign language. + +# Borgs - type: entity id: PlayerBorgGeneric parent: BorgChassisGeneric @@ -50,4 +385,4 @@ slots: cell_slot: name: power-cell-slot-component-slot-name-default - startingItem: PowerCellHyper \ No newline at end of file + startingItem: PowerCellHyper diff --git a/Resources/Prototypes/Entities/Mobs/Player/silicon_base.yml b/Resources/Prototypes/Entities/Mobs/Player/silicon_base.yml index cd51a8053b..b8de17a55f 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/silicon_base.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/silicon_base.yml @@ -91,6 +91,7 @@ - map: ["enum.HumanoidVisualLayers.Head"] - map: ["enum.HumanoidVisualLayers.Snout"] - map: ["enum.HumanoidVisualLayers.Eyes"] + - map: ["enum.HumanoidVisualLayers.Face"] - map: ["enum.HumanoidVisualLayers.RArm"] - map: ["enum.HumanoidVisualLayers.LArm"] - map: ["enum.HumanoidVisualLayers.RLeg"] @@ -125,7 +126,6 @@ - map: ["belt"] - map: ["neck"] - map: ["back"] - - map: [ "enum.HumanoidVisualLayers.Face" ] - map: ["enum.HumanoidVisualLayers.FacialHair"] - map: ["enum.HumanoidVisualLayers.Hair"] - map: ["enum.HumanoidVisualLayers.HeadSide"] @@ -233,6 +233,7 @@ - type: Body prototype: IPC requiredLegs: 2 + thermalVisibility: false - type: Ensnareable sprite: Objects/Misc/ensnare.rsi - type: Speech diff --git a/Resources/Prototypes/Entities/Mobs/Species/arachne.yml b/Resources/Prototypes/Entities/Mobs/Species/arachne.yml index 5c203640d8..2aa1a381bf 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/arachne.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/arachne.yml @@ -27,6 +27,7 @@ color: "#008800" sprite: Mobs/Species/eyes.rsi state: eyes + - map: [ "enum.HumanoidVisualLayers.Face" ] - map: [ "enum.HumanoidVisualLayers.RArm" ] color: "#e8b59b" sprite: Mobs/Species/Human/parts.rsi @@ -66,7 +67,6 @@ - map: [ "belt" ] - map: [ "neck" ] - map: [ "back" ] - - map: [ "enum.HumanoidVisualLayers.Face" ] - map: [ "enum.HumanoidVisualLayers.FacialHair" ] - map: [ "enum.HumanoidVisualLayers.Hair" ] state: bald @@ -173,6 +173,7 @@ color: "#008800" sprite: Mobs/Species/eyes.rsi state: eyes + - map: [ "enum.HumanoidVisualLayers.Face" ] - map: [ "enum.HumanoidVisualLayers.RArm" ] color: "#e8b59b" sprite: Mobs/Species/Human/parts.rsi @@ -212,7 +213,6 @@ - map: [ "belt" ] - map: [ "neck" ] - map: [ "back" ] - - map: [ "enum.HumanoidVisualLayers.Face" ] - map: [ "enum.HumanoidVisualLayers.FacialHair" ] - map: [ "enum.HumanoidVisualLayers.Hair" ] state: bald diff --git a/Resources/Prototypes/Entities/Mobs/Species/arachnid.yml b/Resources/Prototypes/Entities/Mobs/Species/arachnid.yml index bad9cd5cce..6f0c403c45 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/arachnid.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/arachnid.yml @@ -88,6 +88,7 @@ - map: [ "enum.HumanoidVisualLayers.Head" ] - map: [ "enum.HumanoidVisualLayers.Snout" ] - map: [ "enum.HumanoidVisualLayers.Eyes" ] + - map: [ "enum.HumanoidVisualLayers.Face" ] - map: [ "enum.HumanoidVisualLayers.RArm" ] - map: [ "enum.HumanoidVisualLayers.LArm" ] - map: [ "enum.HumanoidVisualLayers.RLeg" ] @@ -117,7 +118,6 @@ - map: [ "enum.HumanoidVisualLayers.Tail" ] # Mentioned in moth code: This needs renaming lol. - map: [ "neck" ] - map: [ "back" ] - - map: [ "enum.HumanoidVisualLayers.Face" ] - map: [ "enum.HumanoidVisualLayers.FacialHair" ] - map: [ "enum.HumanoidVisualLayers.Hair" ] # Do these need to be here? (arachnid hair arachnid hair) - map: [ "enum.HumanoidVisualLayers.HeadSide" ] diff --git a/Resources/Prototypes/Entities/Mobs/Species/base.yml b/Resources/Prototypes/Entities/Mobs/Species/base.yml index aeaf7d493c..dce88c4c64 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/base.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/base.yml @@ -3,6 +3,7 @@ parent: - BaseMob - MobDamageable + - MobPolymorphable - MobCombat id: BaseMobSpecies abstract: true @@ -13,6 +14,7 @@ - map: [ "enum.HumanoidVisualLayers.Head" ] - map: [ "enum.HumanoidVisualLayers.Snout" ] - map: [ "enum.HumanoidVisualLayers.Eyes" ] + - map: [ "enum.HumanoidVisualLayers.Face" ] - map: [ "enum.HumanoidVisualLayers.RArm" ] - map: [ "enum.HumanoidVisualLayers.LArm" ] - map: [ "enum.HumanoidVisualLayers.RLeg" ] @@ -41,7 +43,6 @@ - map: [ "id" ] - map: [ "neck" ] - map: [ "back" ] - - map: [ "enum.HumanoidVisualLayers.Face" ] - map: [ "enum.HumanoidVisualLayers.FacialHair" ] - map: [ "enum.HumanoidVisualLayers.Hair" ] - map: [ "enum.HumanoidVisualLayers.HeadSide" ] @@ -350,6 +351,7 @@ - map: [ "enum.HumanoidVisualLayers.Head" ] - map: [ "enum.HumanoidVisualLayers.Snout" ] - map: [ "enum.HumanoidVisualLayers.Eyes" ] + - map: [ "enum.HumanoidVisualLayers.Face" ] - map: [ "enum.HumanoidVisualLayers.RArm" ] - map: [ "enum.HumanoidVisualLayers.LArm" ] - map: [ "enum.HumanoidVisualLayers.RLeg" ] @@ -381,7 +383,6 @@ - map: [ "id" ] - map: [ "neck" ] - map: [ "back" ] - - map: [ "enum.HumanoidVisualLayers.Face" ] - map: [ "enum.HumanoidVisualLayers.FacialHair" ] - map: [ "enum.HumanoidVisualLayers.Hair" ] - map: [ "enum.HumanoidVisualLayers.HeadSide" ] diff --git a/Resources/Prototypes/Entities/Mobs/Species/harpy.yml b/Resources/Prototypes/Entities/Mobs/Species/harpy.yml index 34232b4e6e..f48fb560e1 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/harpy.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/harpy.yml @@ -14,6 +14,7 @@ - map: [ "enum.HumanoidVisualLayers.Head" ] - map: [ "enum.HumanoidVisualLayers.Snout" ] - map: [ "enum.HumanoidVisualLayers.Eyes" ] + - map: [ "enum.HumanoidVisualLayers.Face" ] - map: [ "enum.HumanoidVisualLayers.LArm" ] - map: [ "enum.HumanoidVisualLayers.RLeg" ] - map: [ "enum.HumanoidVisualLayers.LLeg" ] @@ -38,7 +39,6 @@ - map: [ "belt" ] - map: [ "neck" ] - map: [ "back" ] - - map: [ "enum.HumanoidVisualLayers.Face" ] - map: [ "enum.HumanoidVisualLayers.FacialHair" ] - map: [ "enum.HumanoidVisualLayers.HeadSide" ] - map: [ "enum.HumanoidVisualLayers.Tail" ] @@ -148,6 +148,7 @@ - map: [ "enum.HumanoidVisualLayers.Head" ] - map: [ "enum.HumanoidVisualLayers.Snout" ] - map: [ "enum.HumanoidVisualLayers.Eyes" ] + - map: [ "enum.HumanoidVisualLayers.Face" ] - map: [ "enum.HumanoidVisualLayers.LArm" ] - map: [ "enum.HumanoidVisualLayers.RLeg" ] - map: [ "enum.HumanoidVisualLayers.LLeg" ] @@ -167,7 +168,6 @@ - map: [ "belt" ] - map: [ "neck" ] - map: [ "back" ] - - map: [ "enum.HumanoidVisualLayers.Face" ] - map: [ "enum.HumanoidVisualLayers.FacialHair" ] - map: [ "enum.HumanoidVisualLayers.HeadSide" ] - map: [ "enum.HumanoidVisualLayers.HeadTop" ] diff --git a/Resources/Prototypes/Entities/Mobs/Species/moth.yml b/Resources/Prototypes/Entities/Mobs/Species/moth.yml index f8eef07f8f..6d0c1182e9 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/moth.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/moth.yml @@ -76,6 +76,7 @@ - map: [ "enum.HumanoidVisualLayers.Head" ] - map: [ "enum.HumanoidVisualLayers.Snout" ] - map: [ "enum.HumanoidVisualLayers.Eyes" ] + - map: [ "enum.HumanoidVisualLayers.Face" ] - map: [ "enum.HumanoidVisualLayers.RArm" ] - map: [ "enum.HumanoidVisualLayers.LArm" ] - map: [ "enum.HumanoidVisualLayers.RLeg" ] @@ -110,7 +111,6 @@ - map: [ "enum.HumanoidVisualLayers.Tail" ] #in the utopian future we should probably have a wings enum inserted here so everyhting doesn't break - map: [ "neck" ] - map: [ "back" ] - - map: [ "enum.HumanoidVisualLayers.Face" ] - map: [ "enum.HumanoidVisualLayers.FacialHair" ] - map: [ "enum.HumanoidVisualLayers.Hair" ] - map: [ "enum.HumanoidVisualLayers.HeadSide" ] diff --git a/Resources/Prototypes/Entities/Mobs/Species/shadowkin.yml b/Resources/Prototypes/Entities/Mobs/Species/shadowkin.yml index d965577cd5..dd607c53d1 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/shadowkin.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/shadowkin.yml @@ -154,6 +154,7 @@ - map: ["enum.HumanoidVisualLayers.Snout"] - map: ["enum.HumanoidVisualLayers.Eyes"] shader: unshaded + - map: ["enum.HumanoidVisualLayers.Face"] - map: ["enum.HumanoidVisualLayers.RArm"] - map: ["enum.HumanoidVisualLayers.LArm"] - map: ["enum.HumanoidVisualLayers.RLeg"] @@ -188,7 +189,6 @@ - map: ["belt"] - map: ["neck"] - map: ["back"] - - map: [ "enum.HumanoidVisualLayers.Face" ] - map: ["enum.HumanoidVisualLayers.FacialHair"] - map: ["enum.HumanoidVisualLayers.Hair"] - map: ["enum.HumanoidVisualLayers.HeadSide"] @@ -271,6 +271,7 @@ - map: ["enum.HumanoidVisualLayers.Snout"] - map: ["enum.HumanoidVisualLayers.Eyes"] shader: unshaded + - map: ["enum.HumanoidVisualLayers.Face"] - map: ["enum.HumanoidVisualLayers.RArm"] - map: ["enum.HumanoidVisualLayers.LArm"] - map: ["enum.HumanoidVisualLayers.RLeg"] @@ -305,7 +306,6 @@ - map: ["belt"] - map: ["neck"] - map: ["back"] - - map: [ "enum.HumanoidVisualLayers.Face" ] - map: ["enum.HumanoidVisualLayers.FacialHair"] - map: ["enum.HumanoidVisualLayers.Hair"] - map: ["enum.HumanoidVisualLayers.HeadSide"] diff --git a/Resources/Prototypes/Entities/Mobs/Species/skeleton.yml b/Resources/Prototypes/Entities/Mobs/Species/skeleton.yml index 96c6185693..5f9812f490 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/skeleton.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/skeleton.yml @@ -104,6 +104,7 @@ - type: FireVisuals alternateState: Standing - type: FootPrints + - type: LayingDown - type: entity parent: BaseSpeciesDummy diff --git a/Resources/Prototypes/Entities/Mobs/Species/vox.yml b/Resources/Prototypes/Entities/Mobs/Species/vox.yml index 5de01dbe6a..a26cf9f061 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/vox.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/vox.yml @@ -1,4 +1,4 @@ -- type: entity +- type: entity parent: BaseMobSpeciesOrganic id: BaseMobVox abstract: true @@ -65,6 +65,7 @@ - map: [ "enum.HumanoidVisualLayers.Head" ] - map: [ "enum.HumanoidVisualLayers.Snout" ] - map: [ "enum.HumanoidVisualLayers.Eyes" ] + - map: [ "enum.HumanoidVisualLayers.Face" ] - map: [ "enum.HumanoidVisualLayers.RArm" ] - map: [ "enum.HumanoidVisualLayers.LArm" ] - map: [ "enum.HumanoidVisualLayers.RLeg" ] @@ -83,7 +84,6 @@ - map: [ "id" ] - map: [ "neck" ] - map: [ "back" ] - - map: [ "enum.HumanoidVisualLayers.Face" ] - map: [ "enum.HumanoidVisualLayers.FacialHair" ] - map: [ "enum.HumanoidVisualLayers.Hair" ] - map: [ "enum.HumanoidVisualLayers.HeadSide" ] diff --git a/Resources/Prototypes/Entities/Objects/Devices/pda.yml b/Resources/Prototypes/Entities/Objects/Devices/pda.yml index 41c83eb73c..dcb2c30cc5 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/pda.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/pda.yml @@ -113,6 +113,15 @@ damage: types: Blunt: 4 + - type: SentienceTarget # sentient PDA = pAI lite + flavorKind: station-event-random-sentience-flavor-mechanical + weight: 0.001 # 1,000 PDAs = as likely to be picked as 1 regular animal + - type: BlockMovement + blockInteraction: false # lets the PDA toggle its own flashlight + - type: TypingIndicator + proto: robot + - type: Speech + speechVerb: Robotic - type: entity parent: BasePDA @@ -245,6 +254,8 @@ borderColor: "#d7d7d0" - type: Icon state: pda-cook + - type: ReplacementAccent # for random sentience event + accent: italian - type: entity parent: BasePDA @@ -329,6 +340,7 @@ accentHColor: "#333333" - type: Icon state: pda-mime + - type: Muted # for random sentience event - type: entity name: chaplain PDA diff --git a/Resources/Prototypes/Entities/Objects/Fun/Instruments/base_instruments.yml b/Resources/Prototypes/Entities/Objects/Fun/Instruments/base_instruments.yml index 614af2a488..5a3b967f98 100644 --- a/Resources/Prototypes/Entities/Objects/Fun/Instruments/base_instruments.yml +++ b/Resources/Prototypes/Entities/Objects/Fun/Instruments/base_instruments.yml @@ -32,7 +32,7 @@ blockSpectators: true # otherwise they can play client-side music inHandsOnly: false singleUser: true - requireHands: true + requiresComplex: true verbText: verb-instrument-openui key: enum.InstrumentUiKey.Key - type: InteractionOutline diff --git a/Resources/Prototypes/Entities/Objects/Misc/paper.yml b/Resources/Prototypes/Entities/Objects/Misc/paper.yml index a811994c9e..b1640949d4 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/paper.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/paper.yml @@ -18,7 +18,7 @@ - type: PaperLabelType - type: ActivatableUI key: enum.PaperUiKey.Key - requireHands: false + requiresComplex: false - type: UserInterface interfaces: enum.PaperUiKey.Key: @@ -759,4 +759,4 @@ types: Blunt: 10 - type: StealTarget - stealGroup: BoxFolderQmClipboard + stealGroup: BoxFolderQmClipboard \ No newline at end of file diff --git a/Resources/Prototypes/Entities/Objects/Specific/Robotics/mmi.yml b/Resources/Prototypes/Entities/Objects/Specific/Robotics/mmi.yml index ac125d36bd..c8e827b326 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Robotics/mmi.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Robotics/mmi.yml @@ -80,7 +80,6 @@ map: ["base"] - type: Input context: human - - type: BlockMovement - type: ToggleableGhostRole examineTextMindPresent: positronic-brain-installed examineTextMindSearching: positronic-brain-still-searching @@ -92,6 +91,7 @@ wipeVerbPopup: positronic-brain-wiped-device stopSearchVerbText: positronic-brain-stop-searching-verb-text stopSearchVerbPopup: positronic-brain-stopped-searching + - type: BlockMovement - type: Examiner - type: BorgBrain - type: IntrinsicRadioReceiver diff --git a/Resources/Prototypes/Entities/Objects/Tools/access_configurator.yml b/Resources/Prototypes/Entities/Objects/Tools/access_configurator.yml index 9c8102979a..bbc948bc02 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/access_configurator.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/access_configurator.yml @@ -79,7 +79,7 @@ type: AccessOverriderBoundUserInterface - type: ActivatableUI key: enum.AccessOverriderUiKey.Key - requireHands: true + requiresComplex: true requireActiveHand: false singleUser: true - type: ItemSlots diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Magazines/magnum.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Magazines/magnum.yml index fc506ec594..c71a58a82b 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Magazines/magnum.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Magazines/magnum.yml @@ -102,6 +102,20 @@ - state: mag-1 map: ["enum.GunVisualLayers.Mag"] +- type: entity + id: MagazineMagnumIncendiary + name: pistol magazine (.45 magnum incendiary) + parent: BaseMagazineMagnum + components: + - type: BallisticAmmoProvider + proto: CartridgeMagnumIncendiary + - type: Sprite + layers: + - state: red + map: ["enum.GunVisualLayers.Base"] + - state: mag-1 + map: ["enum.GunVisualLayers.Mag"] + - type: entity id: MagazineMagnumUranium name: pistol magazine (.45 magnum uranium) diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Magazines/pistol.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Magazines/pistol.yml index c57a8adaa5..a74d259ce8 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Magazines/pistol.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Magazines/pistol.yml @@ -330,6 +330,20 @@ - state: mag-1 map: ["enum.GunVisualLayers.Mag"] +- type: entity + id: MagazinePistolSubMachineGunIncendiary + name: SMG magazine (.35 auto incendiary) + parent: BaseMagazinePistolSubMachineGun + components: + - type: BallisticAmmoProvider + proto: CartridgePistolIncendiary + - type: Sprite + layers: + - state: red + map: ["enum.GunVisualLayers.Base"] + - state: mag-1 + map: ["enum.GunVisualLayers.Mag"] + - type: entity id: MagazinePistolSubMachineGunUranium name: SMG magazine (.35 auto uranium) diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Battery/battery_guns.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Battery/battery_guns.yml index 653deecc4b..2200d6bb22 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Battery/battery_guns.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Battery/battery_guns.yml @@ -713,7 +713,7 @@ - type: StaticPrice price: 750 - type: StealTarget - stealGroup: WeaponCaptain + stealGroup: WeaponAntiqueLaser - type: MeleeWeapon attackRate: 1.3333 damage: @@ -723,6 +723,10 @@ wideAnimationRotation: 135 - type: DamageOtherOnHit staminaCost: 5 + - type: SentienceTarget # I hope this is only the captain's gun + flavorKind: station-event-random-sentience-flavor-inanimate + weight: 0.0002 # 5,000 times less likely than 1 regular animal + # not putting a BlockMovement component here cause that's funny. - type: entity name: advanced laser pistol diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml index ffdbdc5911..e09e68904b 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml @@ -57,6 +57,12 @@ - type: Tag tags: - CaptainSabre + - type: DisarmMalus + - type: SentienceTarget + flavorKind: station-event-random-sentience-flavor-inanimate + weight: 0.0002 # 5,000 times less likely than 1 regular animal + - type: PirateAccent + # not putting a BlockMovement component here cause that's funny. - type: entity name: katana diff --git a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/base_structureairlocks.yml b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/base_structureairlocks.yml index 97a6d8e076..58a3f6ceca 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/base_structureairlocks.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/base_structureairlocks.yml @@ -4,6 +4,7 @@ name: airlock description: It opens, it closes, and maybe crushes you. components: + - type: StationAiWhitelist - type: MeleeSound soundGroups: Brute: @@ -104,6 +105,8 @@ - type: SpawnOnOverload - type: UserInterface interfaces: + enum.AiUi.Key: + type: StationAiBoundUserInterface enum.WiresUiKey.Key: type: WiresBoundUserInterface - type: Airtight diff --git a/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml b/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml index 4e499cc381..59e6b5fa4d 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml @@ -40,6 +40,7 @@ interfaces: enum.AtmosAlertsComputerUiKey.Key: type: AtmosAlertsComputerBoundUserInterface + - type: StationAiWhitelist - type: entity parent: BaseComputer @@ -297,6 +298,7 @@ interfaces: enum.PowerMonitoringConsoleUiKey.Key: type: PowerMonitoringConsoleBoundUserInterface + - type: StationAiWhitelist - type: entity parent: BaseComputer @@ -584,6 +586,7 @@ name: communications computer description: A computer used to make station wide announcements via keyboard, set the appropriate alert level, and call the emergency shuttle. components: + - type: StationAiWhitelist - type: Sprite layers: - map: ["computerLayerBody"] @@ -676,6 +679,7 @@ radius: 1.5 energy: 1.6 color: "#e6e227" + - type: StationAiWhitelist - type: entity parent: BaseComputer @@ -789,6 +793,7 @@ - type: GuideHelp guides: - Cargo + - type: StationAiWhitelist - type: entity id: ComputerCargoBounty @@ -1026,6 +1031,7 @@ - type: GuideHelp guides: - Cargo + - type: StationAiWhitelist - type: entity parent: BaseComputer @@ -1140,3 +1146,46 @@ access: [["ResearchDirector"]] - type: Lock unlockOnClick: false + +- type: entity + id: StationAiUploadComputer + parent: BaseComputer + name: AI upload console + description: Used to update the laws of the station AI. + components: + - type: Sprite + layers: + - map: [ "computerLayerBody" ] + state: computer + - map: [ "computerLayerKeyboard" ] + state: generic_keyboard + - map: [ "computerLayerScreen" ] + state: aiupload + - map: [ "computerLayerKeys" ] + state: generic_keys + - type: ApcPowerReceiver + powerLoad: 1000 + - type: AccessReader + access: [ [ "ResearchDirector" ] ] + - type: Lock + unlockOnClick: false + - type: SiliconLawUpdater + components: + - type: StationAiHeld + - type: ItemSlotsLock + slots: + - circuit_holder + - type: ItemSlotRequiresPower + - type: ItemSlots + slots: + circuit_holder: + name: circuit-holder + insertSuccessPopup: silicon-laws-updated + whitelist: + components: + - SiliconLawProvider + - Item + - type: ContainerContainer + containers: + circuit_holder: !type:ContainerSlot + board: !type:Container diff --git a/Resources/Prototypes/Entities/Structures/Machines/Medical/cryo_pod.yml b/Resources/Prototypes/Entities/Structures/Machines/Medical/cryo_pod.yml index 0485b5a517..539c8a244a 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/Medical/cryo_pod.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/Medical/cryo_pod.yml @@ -96,7 +96,7 @@ type: WiresBoundUserInterface - type: ActivatableUI key: enum.HealthAnalyzerUiKey.Key - requireHands: false + requiresComplex: false - type: ActivatableUIRequiresPower - type: PointLight color: "#3a807f" diff --git a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml index 7456163010..8fafb67447 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml @@ -16,7 +16,7 @@ mask: - MachineMask layer: - - MachineLayer + - MachineLayer - type: Lathe - type: MaterialStorage - type: Destructible @@ -109,120 +109,120 @@ - type: MaterialStorage whitelist: tags: - - Sheet - - RawMaterial - - Ingot + - Sheet + - RawMaterial + - Ingot - type: Lathe idleState: icon runningState: building staticRecipes: - - Wirecutter - - Igniter - - Signaller - - Screwdriver - - Welder - - Wrench - - Crowbar - - Multitool - - NetworkConfigurator - - SprayPainter - - FlashlightLantern - - CableStack - - CableMVStack - - CableHVStack - - HandheldGPSBasic - - TRayScanner - - AirTank - - GasAnalyzer - - UtilityBelt - - Fulton - - FultonBeacon - - Pickaxe - - ModularReceiver - - AppraisalTool - - SheetRGlass - - Beaker - - Syringe - - HandLabeler - - LightTube - - LedLightTube - - SodiumLightTube - - ExteriorLightTube - - LightBulb - - LedLightBulb - - Bucket - - DrinkMug - - DrinkMugMetal - - DrinkGlass - - DrinkShotGlass - - DrinkGlassCoupeShaped - - CustomDrinkJug - - FoodPlate - - FoodPlateSmall - - FoodPlatePlastic - - FoodPlateSmallPlastic - - FoodBowlBig - - FoodPlateTin - - FoodKebabSkewer - - SprayBottle - - MopItem - - Holoprojector - - Mousetrap - - LightReplacer - - TrashBag - - PowerCellSmall - - PowerCellMedium - - RollerBedSpawnFolded - - CheapRollerBedSpawnFolded - - EmergencyRollerBedSpawnFolded - - MicroManipulatorStockPart - - MatterBinStockPart - - CapacitorStockPart - - ConveyorBeltAssembly - - IntercomElectronics - - FirelockElectronics - - DoorElectronics - - AirAlarmElectronics - - StationMapElectronics - - FireAlarmElectronics - - MailingUnitElectronics - - SignalTimerElectronics - - APCElectronics - - SMESMachineCircuitboard - - SubstationMachineCircuitboard - - WallmountSubstationElectronics - - CellRechargerCircuitboard - - BorgChargerCircuitboard - - WeaponCapacitorRechargerCircuitboard - - HandheldStationMap - - ClothingHeadHatWelding - - ShockCollar # FloofStation - - LeashBasic # FloofStation + - Wirecutter + - Igniter + - Signaller + - Screwdriver + - Welder + - Wrench + - Crowbar + - Multitool + - NetworkConfigurator + - SprayPainter + - FlashlightLantern + - CableStack + - CableMVStack + - CableHVStack + - HandheldGPSBasic + - TRayScanner + - AirTank + - GasAnalyzer + - UtilityBelt + - Fulton + - FultonBeacon + - Pickaxe + - ModularReceiver + - AppraisalTool + - SheetRGlass + - Beaker + - Syringe + - HandLabeler + - LightTube + - LedLightTube + - SodiumLightTube + - ExteriorLightTube + - LightBulb + - LedLightBulb + - Bucket + - DrinkMug + - DrinkMugMetal + - DrinkGlass + - DrinkShotGlass + - DrinkGlassCoupeShaped + - CustomDrinkJug + - FoodPlate + - FoodPlateSmall + - FoodPlatePlastic + - FoodPlateSmallPlastic + - FoodBowlBig + - FoodPlateTin + - FoodKebabSkewer + - SprayBottle + - MopItem + - Holoprojector + - Mousetrap + - LightReplacer + - TrashBag + - PowerCellSmall + - PowerCellMedium + - RollerBedSpawnFolded + - CheapRollerBedSpawnFolded + - EmergencyRollerBedSpawnFolded + - MicroManipulatorStockPart + - MatterBinStockPart + - CapacitorStockPart + - ConveyorBeltAssembly + - IntercomElectronics + - FirelockElectronics + - DoorElectronics + - AirAlarmElectronics + - StationMapElectronics + - FireAlarmElectronics + - MailingUnitElectronics + - SignalTimerElectronics + - APCElectronics + - SMESMachineCircuitboard + - SubstationMachineCircuitboard + - WallmountSubstationElectronics + - CellRechargerCircuitboard + - BorgChargerCircuitboard + - WeaponCapacitorRechargerCircuitboard + - HandheldStationMap + - ClothingHeadHatWelding + - ShockCollar # FloofStation + - LeashBasic # FloofStation - type: EmagLatheRecipes emagStaticRecipes: - - BoxLethalshot - - BoxShotgunFlare - - BoxShotgunSlug - - MagazineBoxLightRifle - - MagazineBoxMagnum - - MagazineBoxPistol - - MagazineBoxRifle - - MagazineLightRifle - - MagazineLightRifleEmpty - - MagazinePistol - - MagazinePistolEmpty - - MagazinePistolSubMachineGun - - MagazinePistolSubMachineGunEmpty - - MagazinePistolSubMachineGunTopMounted - - MagazinePistolSubMachineGunTopMountedEmpty - - MagazineRifle - - MagazineRifleEmpty - - MagazineShotgun - - MagazineShotgunEmpty - - MagazineShotgunSlug - - RiotShield - - SpeedLoaderMagnum - - SpeedLoaderMagnumEmpty + - BoxLethalshot + - BoxShotgunFlare + - BoxShotgunSlug + - MagazineBoxLightRifle + - MagazineBoxMagnum + - MagazineBoxPistol + - MagazineBoxRifle + - MagazineLightRifle + - MagazineLightRifleEmpty + - MagazinePistol + - MagazinePistolEmpty + - MagazinePistolSubMachineGun + - MagazinePistolSubMachineGunEmpty + - MagazinePistolSubMachineGunTopMounted + - MagazinePistolSubMachineGunTopMountedEmpty + - MagazineRifle + - MagazineRifleEmpty + - MagazineShotgun + - MagazineShotgunEmpty + - MagazineShotgunSlug + - RiotShield + - SpeedLoaderMagnum + - SpeedLoaderMagnumEmpty - type: entity id: AutolatheHyperConvection @@ -259,9 +259,9 @@ - type: MaterialStorage whitelist: tags: - - Sheet - - RawMaterial - - Ingot + - Sheet + - RawMaterial + - Ingot - type: Lathe idleState: icon runningState: building @@ -270,152 +270,155 @@ - Dropper - ClothingEyesGlassesChemical dynamicRecipes: - - PowerDrill - - MiningDrill - - MiningDrillDiamond - - AnomalyScanner - - AnomalyLocator - - AnomalyLocatorWide - - HandheldCrewMonitor - - Scalpel - - Retractor - - Cautery - - Drill - - WeaponParticleDecelerator - - HoloprojectorField - - Saw - - Hemostat - - CryostasisBeaker - - SyringeCryostasis - - Syringe - - Implanter - - PillCanister - - ChemistryEmptyBottle01 - - AdvancedCapacitorStockPart - - AdvancedMatterBinStockPart - - NanoManipulatorStockPart - - SuperCapacitorStockPart - - SuperMatterBinStockPart - - PicoManipulatorStockPart - - BluespaceCapacitorStockPart - - BluespaceManipulatorStockPart - - BluespaceMatterBinStockPart - - AdvMopItem - - WeaponSprayNozzle - - ClothingBackpackWaterTank - - MegaSprayBottle - - TimerTrigger - - ChemicalPayload - - FlashPayload - - Signaller - - SignallerAdvanced - - SignalTrigger - - VoiceTrigger - - Igniter - - HandHeldMassScanner - - PowerCellMicroreactor - - PowerCellHigh - - WeaponPistolCHIMP - - ClothingMaskWeldingGas - - WeaponGauntletGorilla - - SynthesizerInstrument - - RPED - - ClothingShoesBootsMagSci - - ClothingShoesBootsMoon - - ClothingShoesBootsSpeed - - NodeScanner - - HolofanProjector - - BluespaceBeaker - - SyringeBluespace - - WeaponForceGun - - WeaponLaserSvalinn - - WeaponProtoKineticAccelerator - - WeaponTetherGun - - WeaponGrapplingGun - - ClothingBackpackHolding - - ClothingBackpackSatchelHolding - - ClothingBackpackDuffelHolding - - WelderExperimental - - JawsOfLife - - CoreSilver # Nyanotrasen - Silver Golem core - - FauxTileAstroGrass - - FauxTileMowedAstroGrass - - FauxTileJungleAstroGrass - - FauxTileAstroIce - - FauxTileAstroSnow - - OreBagOfHolding - - DeviceQuantumSpinInverter - - CanilunztTranslator - - BubblishTranslator - - NekomimeticTranslator - - DraconicTranslator - - SolCommonTranslator - - RootSpeakTranslator - - XenoTranslator - - BasicGalaticCommonTranslatorImplanter - - AdvancedGalaticCommonTranslatorImplanter - - BubblishTranslatorImplanter - - NekomimeticTranslatorImplanter - - DraconicTranslatorImplanter - - CanilunztTranslatorImplanter - - SolCommonTranslatorImplanter - - RootSpeakTranslatorImplanter - - AnimalTranslator - - MofficTranslatorImplanter - - MofficTranslator - - ArachnicTranslatorImplanter # Floofstation - - ArachnicTranslator # Floofstation - - RCDAmmo #DeltaV - - RCD #EE - # Shitmed Change - - EnergyScalpel - - EnergyCautery - - AdvancedRetractor + - PowerDrill + - MiningDrill + - MiningDrillDiamond + - AnomalyScanner + - AnomalyLocator + - AnomalyLocatorWide + - HandheldCrewMonitor + - Scalpel + - Retractor + - Cautery + - Drill + - WeaponParticleDecelerator + - HoloprojectorField + - Saw + - Hemostat + - CryostasisBeaker + - SyringeCryostasis + - Syringe + - Implanter + - PillCanister + - ChemistryEmptyBottle01 + - AdvancedCapacitorStockPart + - AdvancedMatterBinStockPart + - NanoManipulatorStockPart + - SuperCapacitorStockPart + - SuperMatterBinStockPart + - PicoManipulatorStockPart + - BluespaceCapacitorStockPart + - BluespaceManipulatorStockPart + - BluespaceMatterBinStockPart + - AdvMopItem + - WeaponSprayNozzle + - ClothingBackpackWaterTank + - MegaSprayBottle + - TimerTrigger + - ChemicalPayload + - FlashPayload + - Signaller + - SignallerAdvanced + - SignalTrigger + - VoiceTrigger + - Igniter + - HandHeldMassScanner + - PowerCellMicroreactor + - PowerCellHigh + - WeaponPistolCHIMP + - ClothingMaskWeldingGas + - WeaponGauntletGorilla + - SynthesizerInstrument + - RPED + - ClothingShoesBootsMagSci + - ClothingShoesBootsMoon + - ClothingShoesBootsSpeed + - NodeScanner + - HolofanProjector + - BluespaceBeaker + - SyringeBluespace + - WeaponForceGun + - WeaponLaserSvalinn + - WeaponProtoKineticAccelerator + - WeaponTetherGun + - WeaponGrapplingGun + - ClothingBackpackHolding + - ClothingBackpackSatchelHolding + - ClothingBackpackDuffelHolding + - WelderExperimental + - JawsOfLife + - CoreSilver # Nyanotrasen - Silver Golem core + - FauxTileAstroGrass + - FauxTileMowedAstroGrass + - FauxTileJungleAstroGrass + - FauxTileAstroIce + - FauxTileAstroSnow + - OreBagOfHolding + - DeviceQuantumSpinInverter + - CanilunztTranslator + - BubblishTranslator + - NekomimeticTranslator + - DraconicTranslator + - SolCommonTranslator + - RootSpeakTranslator + - XenoTranslator + - BasicGalaticCommonTranslatorImplanter + - AdvancedGalaticCommonTranslatorImplanter + - BubblishTranslatorImplanter + - NekomimeticTranslatorImplanter + - DraconicTranslatorImplanter + - CanilunztTranslatorImplanter + - SolCommonTranslatorImplanter + - RootSpeakTranslatorImplanter + - AnimalTranslator + - MofficTranslatorImplanter + - MofficTranslator + - ClothingEyesNightVisionGoggles + - ClothingEyesNightVisionDiagnosticGoggles + - ClothingEyesThermalVisionGoggles + - RCDAmmo #DeltaV + - RCD #EE + # Shitmed Change + - EnergyScalpel + - EnergyCautery + - AdvancedRetractor + - ArachnicTranslatorImplanter # Floofstation + - ArachnicTranslator # Floofstation - type: EmagLatheRecipes emagDynamicRecipes: - - BoxBeanbag - - BoxShotgunIncendiary - - BoxShotgunUranium - - ExplosivePayload - - GrenadeBlast - - GrenadeEMP - - GrenadeFlash - - HoloprojectorSecurity - - MagazineBoxLightRifleIncendiary - - MagazineBoxLightRifleUranium - - MagazineBoxMagnumIncendiary - - MagazineBoxMagnumUranium - - MagazineBoxPistolIncendiary - - MagazineBoxPistolUranium - - MagazineBoxRifleIncendiary - - MagazineBoxRifleUranium - - MagazineGrenadeEmpty - - MagazineLightRifleIncendiary - - MagazineLightRifleUranium - - MagazinePistolIncendiary - - MagazinePistolUranium - - MagazineRifleIncendiary - - MagazineRifleUranium - - MagazineShotgunBeanbag - - MagazineShotgunIncendiary - - MagazineShotgunIncendiary - - PortableRecharger - - PowerCageHigh - - PowerCageMedium - - PowerCageSmall - - ShellTranquilizer - - SpeedLoaderMagnumIncendiary - - SpeedLoaderMagnumUranium - - TelescopicShield - - Truncheon - - WeaponAdvancedLaser - - WeaponLaserCannon - - WeaponLaserCarbine - - WeaponXrayCannon - - WeaponEnergyGun # DeltaV - Energy Gun - - WeaponEnergyGunMini # DeltaV - Miniature Energy Gun - - WeaponEnergyGunPistol # DeltaV - PDW-9 Energy Pistol - - WeaponGunLaserCarbineAutomatic # DeltaV - IK-60 Laser Carbine + - BoxBeanbag + - BoxShotgunIncendiary + - BoxShotgunUranium + - ExplosivePayload + - GrenadeBlast + - GrenadeEMP + - GrenadeFlash + - HoloprojectorSecurity + - MagazineBoxLightRifleIncendiary + - MagazineBoxLightRifleUranium + - MagazineBoxMagnumIncendiary + - MagazineBoxMagnumUranium + - MagazineBoxPistolIncendiary + - MagazineBoxPistolUranium + - MagazineBoxRifleIncendiary + - MagazineBoxRifleUranium + - MagazineGrenadeEmpty + - MagazineLightRifleIncendiary + - MagazineLightRifleUranium + - MagazinePistolIncendiary + - MagazinePistolUranium + - MagazineRifleIncendiary + - MagazineRifleUranium + - MagazineShotgunBeanbag + - MagazineShotgunIncendiary + - MagazineShotgunIncendiary + - PortableRecharger + - PowerCageHigh + - PowerCageMedium + - PowerCageSmall + - ShellTranquilizer + - SpeedLoaderMagnumIncendiary + - SpeedLoaderMagnumUranium + - TelescopicShield + - Truncheon + - WeaponAdvancedLaser + - WeaponLaserCannon + - WeaponLaserCarbine + - WeaponXrayCannon + - WeaponEnergyGun # DeltaV - Energy Gun + - WeaponEnergyGunMini # DeltaV - Miniature Energy Gun + - WeaponEnergyGunPistol # DeltaV - PDW-9 Energy Pistol + - WeaponGunLaserCarbineAutomatic # DeltaV - IK-60 Laser Carbine - type: entity id: ProtolatheHyperConvection @@ -475,102 +478,102 @@ - SpaceHeaterMachineCircuitBoard - StationAnchorCircuitboard dynamicRecipes: - - ThermomachineFreezerMachineCircuitBoard - - HellfireFreezerMachineCircuitBoard - - PortableScrubberMachineCircuitBoard - - CloningPodMachineCircuitboard - - MedicalScannerMachineCircuitboard - - CryoPodMachineCircuitboard - - VaccinatorMachineCircuitboard - - DiagnoserMachineCircuitboard - - BiomassReclaimerMachineCircuitboard - - BiofabricatorMachineCircuitboard - - SurveillanceCameraRouterCircuitboard - - SurveillanceCameraMonitorCircuitboard - - SurveillanceWirelessCameraMonitorCircuitboard - - SurveillanceCameraWirelessRouterCircuitboard - - ComputerTelevisionCircuitboard - - JukeboxCircuitBoard - - SurveillanceWirelessCameraMovableCircuitboard - - SurveillanceWirelessCameraAnchoredCircuitboard - - HydroponicsTrayMachineCircuitboard - - SolarControlComputerCircuitboard - - SolarTrackerElectronics - - TurboItemRechargerCircuitboard - - PowerComputerCircuitboard - - AlertsComputerCircuitboard - - AutolatheHyperConvectionMachineCircuitboard - - ProtolatheHyperConvectionMachineCircuitboard - - CircuitImprinterHyperConvectionMachineCircuitboard - - FatExtractorMachineCircuitboard - - FlatpackerMachineCircuitboard - - SheetifierMachineCircuitboard - - ShuttleConsoleCircuitboard - - RadarConsoleCircuitboard - - TechDiskComputerCircuitboard - - DawInstrumentMachineCircuitboard - - CloningConsoleComputerCircuitboard - - StasisBedMachineCircuitboard - - OreProcessorIndustrialMachineCircuitboard - - CargoTelepadMachineCircuitboard - - RipleyCentralElectronics - - RipleyPeripheralsElectronics - - HonkerCentralElectronics - - HonkerPeripheralsElectronics - - HonkerTargetingElectronics - - HamtrCentralElectronics - - HamtrPeripheralsElectronics - - PortableGeneratorPacmanMachineCircuitboard - - PortableGeneratorSuperPacmanMachineCircuitboard - - PortableGeneratorJrPacmanMachineCircuitboard - - WallmountGeneratorElectronics - - WallmountGeneratorAPUElectronics - - WallmountSubstationElectronics - - PowerCageRechargerCircuitboard - - EmitterCircuitboard - - ThrusterMachineCircuitboard - - GyroscopeMachineCircuitboard - - MiniGravityGeneratorCircuitboard - - ShuttleGunKineticCircuitboard - - GasRecyclerMachineCircuitboard - - SeedExtractorMachineCircuitboard - - AnalysisComputerCircuitboard - - ExosuitFabricatorMachineCircuitboard - - AnomalyVesselCircuitboard - - AnomalyVesselExperimentalCircuitboard - - AnomalySynchronizerCircuitboard - - APECircuitboard - - ArtifactAnalyzerMachineCircuitboard - - ArtifactCrusherMachineCircuitboard - - TelecomServerCircuitboard - - MassMediaCircuitboard - - ReagentGrinderIndustrialMachineCircuitboard - - ReverseEngineeringMachineCircuitboard - - CrewMonitoringComputerCircuitboard - - DoorElectronics - - FireAlarmElectronics - - FirelockElectronics - - IntercomElectronics - - MailingUnitElectronics - - SalvageMagnetMachineCircuitboard - - StationMapElectronics - - MetempsychoticMachineCircuitboard - - SalvageExpeditionsComputerCircuitboard - - JukeboxCircuitBoard - - AutodocCircuitboard # Shitmed Change - - OperatingTableCircuitboard # Shitmed Change + - ThermomachineFreezerMachineCircuitBoard + - HellfireFreezerMachineCircuitBoard + - PortableScrubberMachineCircuitBoard + - CloningPodMachineCircuitboard + - MedicalScannerMachineCircuitboard + - CryoPodMachineCircuitboard + - VaccinatorMachineCircuitboard + - DiagnoserMachineCircuitboard + - BiomassReclaimerMachineCircuitboard + - BiofabricatorMachineCircuitboard + - SurveillanceCameraRouterCircuitboard + - SurveillanceCameraMonitorCircuitboard + - SurveillanceWirelessCameraMonitorCircuitboard + - SurveillanceCameraWirelessRouterCircuitboard + - ComputerTelevisionCircuitboard + - JukeboxCircuitBoard + - SurveillanceWirelessCameraMovableCircuitboard + - SurveillanceWirelessCameraAnchoredCircuitboard + - HydroponicsTrayMachineCircuitboard + - SolarControlComputerCircuitboard + - SolarTrackerElectronics + - TurboItemRechargerCircuitboard + - PowerComputerCircuitboard + - AlertsComputerCircuitboard + - AutolatheHyperConvectionMachineCircuitboard + - ProtolatheHyperConvectionMachineCircuitboard + - CircuitImprinterHyperConvectionMachineCircuitboard + - FatExtractorMachineCircuitboard + - FlatpackerMachineCircuitboard + - SheetifierMachineCircuitboard + - ShuttleConsoleCircuitboard + - RadarConsoleCircuitboard + - TechDiskComputerCircuitboard + - DawInstrumentMachineCircuitboard + - CloningConsoleComputerCircuitboard + - StasisBedMachineCircuitboard + - OreProcessorIndustrialMachineCircuitboard + - CargoTelepadMachineCircuitboard + - RipleyCentralElectronics + - RipleyPeripheralsElectronics + - HonkerCentralElectronics + - HonkerPeripheralsElectronics + - HonkerTargetingElectronics + - HamtrCentralElectronics + - HamtrPeripheralsElectronics + - PortableGeneratorPacmanMachineCircuitboard + - PortableGeneratorSuperPacmanMachineCircuitboard + - PortableGeneratorJrPacmanMachineCircuitboard + - WallmountGeneratorElectronics + - WallmountGeneratorAPUElectronics + - WallmountSubstationElectronics + - PowerCageRechargerCircuitboard + - EmitterCircuitboard + - ThrusterMachineCircuitboard + - GyroscopeMachineCircuitboard + - MiniGravityGeneratorCircuitboard + - ShuttleGunKineticCircuitboard + - GasRecyclerMachineCircuitboard + - SeedExtractorMachineCircuitboard + - AnalysisComputerCircuitboard + - ExosuitFabricatorMachineCircuitboard + - AnomalyVesselCircuitboard + - AnomalyVesselExperimentalCircuitboard + - AnomalySynchronizerCircuitboard + - APECircuitboard + - ArtifactAnalyzerMachineCircuitboard + - ArtifactCrusherMachineCircuitboard + - TelecomServerCircuitboard + - MassMediaCircuitboard + - ReagentGrinderIndustrialMachineCircuitboard + - ReverseEngineeringMachineCircuitboard + - CrewMonitoringComputerCircuitboard + - DoorElectronics + - FireAlarmElectronics + - FirelockElectronics + - IntercomElectronics + - MailingUnitElectronics + - SalvageMagnetMachineCircuitboard + - StationMapElectronics + - MetempsychoticMachineCircuitboard + - SalvageExpeditionsComputerCircuitboard + - JukeboxCircuitBoard + - AutodocCircuitboard # Shitmed Change + - OperatingTableCircuitboard # Shitmed Change - type: EmagLatheRecipes emagDynamicRecipes: - - ShuttleGunDusterCircuitboard - - ShuttleGunFriendshipCircuitboard - - ShuttleGunPerforatorCircuitboard - - ShuttleGunSvalinnMachineGunCircuitboard + - ShuttleGunDusterCircuitboard + - ShuttleGunFriendshipCircuitboard + - ShuttleGunPerforatorCircuitboard + - ShuttleGunSvalinnMachineGunCircuitboard - type: MaterialStorage whitelist: tags: - - Sheet - - RawMaterial - - Ingot + - Sheet + - RawMaterial + - Ingot - type: RequireProjectileTarget - type: entity @@ -698,7 +701,7 @@ - HamtrLLeg - HamtrRLeg - VimHarness - # Begin Nyano additions + # Begin Nyano additions - JetpackBlue - JetpackMini # End Nyano additions @@ -736,8 +739,8 @@ - type: MaterialStorage whitelist: tags: - - Sheet - - RawMaterial + - Sheet + - RawMaterial - type: Lathe idleState: icon runningState: building @@ -810,6 +813,8 @@ - MagazinePistolSubMachineGunEmpty - MagazinePistolSubMachineGunTopMounted - MagazinePistolSubMachineGunTopMountedEmpty + - MagazineMagnum + - MagazineMagnumEmpty - MagazineRifle - MagazineRifleEmpty - MagazineShotgun @@ -885,10 +890,19 @@ - MagazineBoxPistolRubber - MagazineBoxRifleRubber - MagazineGrenadeEmpty + - MagazineLightRifleRubber - MagazineLightRifleIncendiary - MagazineLightRifleUranium + - MagazinePistolRubber - MagazinePistolIncendiary - MagazinePistolUranium + - MagazinePistolSubMachineGunRubber + - MagazinePistolSubMachineGunIncendiary + - MagazinePistolSubMachineGunUranium + - MagazineMagnumRubber + - MagazineMagnumIncendiary + - MagazineMagnumUranium + - MagazineRifleRubber - MagazineRifleIncendiary - MagazineRifleUranium - MagazineShotgunBeanbag @@ -904,6 +918,7 @@ - ShuttleGunSvalinnMachineGunCircuitboard - Signaller - SignalTrigger + - SpeedLoaderMagnumRubber - SpeedLoaderMagnumIncendiary - SpeedLoaderMagnumUranium - TelescopicShield @@ -915,6 +930,7 @@ - WeaponDisablerSMG - WeaponLaserCannon - WeaponLaserCarbine + - ClothingEyesNightVisionSecurityGoggles - ClothingHeadHelmetInsulated # Nyanotrasen - Insulative headgear - ClothingHeadCage # Nyanotrasen - Insulative headgear - WeaponXrayCannon @@ -947,9 +963,9 @@ - type: MaterialStorage whitelist: tags: - - Sheet - - RawMaterial - - Ingot + - Sheet + - RawMaterial + - Ingot - type: entity id: AmmoTechFab @@ -957,56 +973,52 @@ name: ammo techfab description: Prints the bare minimum of bullets that any budget military or armory could need. Nothing fancy. components: - - type: Sprite - sprite: Structures/Machines/techfab.rsi - layers: - - state: icon - map: ["enum.LatheVisualLayers.IsRunning"] - - state: ammo - - state: unlit - shader: unshaded - map: ["enum.PowerDeviceVisualLayers.Powered"] - - state: inserting - map: ["enum.MaterialStorageVisualLayers.Inserting"] - - state: panel - map: ["enum.WiresVisualLayers.MaintenancePanel"] - - type: Machine - board: AmmoTechFabCircuitboard - - type: Lathe - idleState: icon - runningState: icon - staticRecipes: - - BoxLethalshot - - BoxShotgunFlare - - BoxShotgunSlug - - MagazineBoxLightRifle - - MagazineBoxMagnum - - MagazineBoxPistol - - MagazineBoxRifle - - MagazineLightRifle - - MagazineLightRifleEmpty - - MagazinePistol - - MagazinePistolEmpty - - MagazineRifle - - MagazineRifleEmpty - - MagazineNovaliteC1 - - MagazineNovaliteC1Empty - - MagazineShotgun - - MagazineShotgunEmpty - - MagazineShotgunSlug - - ShellTranquilizer - - SpeedLoaderMagnum - - SpeedLoaderMagnumEmpty - - SpeedLoaderRifleHeavy - - SpeedLoaderRifleHeavyEmpty - - CartridgeSpecial - - MagazineBoxSpecial - - type: MaterialStorage - whitelist: - tags: - - Sheet - - RawMaterial - - Ingot + - type: Sprite + sprite: Structures/Machines/techfab.rsi + layers: + - state: icon + map: ["enum.LatheVisualLayers.IsRunning"] + - state: ammo + - state: unlit + shader: unshaded + map: ["enum.PowerDeviceVisualLayers.Powered"] + - state: inserting + map: ["enum.MaterialStorageVisualLayers.Inserting"] + - state: panel + map: ["enum.WiresVisualLayers.MaintenancePanel"] + - type: Machine + board: AmmoTechFabCircuitboard + - type: Lathe + idleState: icon + runningState: icon + staticRecipes: + - BoxLethalshot + - BoxShotgunFlare + - BoxShotgunSlug + - MagazineBoxLightRifle + - MagazineBoxMagnum + - MagazineBoxPistol + - MagazineBoxRifle + - MagazineLightRifle + - MagazineLightRifleEmpty + - MagazinePistol + - MagazinePistolEmpty + - MagazineRifle + - MagazineRifleEmpty + - MagazineShotgun + - MagazineShotgunEmpty + - MagazineShotgunSlug + - ShellTranquilizer + - SpeedLoaderMagnum + - SpeedLoaderMagnumEmpty + - CartridgeSpecial + - MagazineBoxSpecial + - type: MaterialStorage + whitelist: + tags: + - Sheet + - RawMaterial + - Ingot - type: entity id: MedicalTechFab @@ -1031,79 +1043,61 @@ idleState: icon runningState: icon staticRecipes: - - Brutepack - - Ointment - - Gauze - - HandLabeler - - Defibrillator - - HandheldHealthAnalyzer - - ClothingHandsGlovesLatex - - ClothingHandsGlovesNitrile - - ClothingMaskSterile - - DiseaseSwab - - Beaker - - LargeBeaker - - Dropper - - Jug - - Syringe - - Implanter - - PillCanister - - BodyBag - - ChemistryEmptyBottle01 - - RollerBedSpawnFolded - - CheapRollerBedSpawnFolded - - EmergencyRollerBedSpawnFolded - - Medkit - - MedkitBurn - - MedkitToxin - - MedkitO2 - - MedkitBrute - - MedkitAdvanced - - MedkitRadiation - - MedkitCombat - - Scalpel - - Retractor - - Cautery - - Drill - - Saw - - Hemostat - - ClothingEyesGlassesChemical - - WhiteCane - - HandheldGPSBasic # Floof - port to EE - - HandheldStationMap # Floof - port to EE - - BoneGel # Shitmed Change + - Brutepack + - Ointment + - Gauze + - HandLabeler + - Defibrillator + - HandheldHealthAnalyzer + - ClothingHandsGlovesLatex + - ClothingHandsGlovesNitrile + - ClothingMaskSterile + - DiseaseSwab + - Beaker + - LargeBeaker + - Dropper + - Jug + - Syringe + - Implanter + - PillCanister + - BodyBag + - ChemistryEmptyBottle01 + - RollerBedSpawnFolded + - CheapRollerBedSpawnFolded + - EmergencyRollerBedSpawnFolded + - Medkit + - MedkitBurn + - MedkitToxin + - MedkitO2 + - MedkitBrute + - MedkitAdvanced + - MedkitRadiation + - MedkitCombat + - Scalpel + - Retractor + - Cautery + - Drill + - Saw + - Hemostat + - ClothingEyesGlassesChemical + - BoneGel # Shitmed Change + - HandheldGPSBasic # Floof - port to EE + - HandheldStationMap # Floof - port to EE dynamicRecipes: - - ChemicalPayload - - CryostasisBeaker - - BluespaceBeaker - - SyringeBluespace - - ClothingEyesHudMedical # Nyano - - ChemicalPayload # Nyano - - SyringeCryostasis - # Shitmed Change - - EnergyScalpel - - EnergyCautery - - AdvancedRetractor - - OmnimedTool - - MedicalCyberneticEyes - - HypoMini # Floof - # Floof section - port to EE later - - HandHeldMassScanner - - ClothingHeadHelmetInsulated - - CloningConsoleComputerCircuitboard - - CloningPodMachineCircuitboard - - MetempsychoticMachineCircuitboard - - MedicalScannerMachineCircuitboard - - VaccinatorMachineCircuitboard - - DiagnoserMachineCircuitboard - - StasisBedMachineCircuitboard - - CryoPodMachineCircuitboard - - BiomassReclaimerMachineCircuitboard - - BiofabricatorMachineCircuitboard - - ReagentGrinderIndustrialMachineCircuitboard - - FatExtractorMachineCircuitboard - - CrewMonitoringComputerCircuitboard - # Floof section end + - ChemicalPayload + - CryostasisBeaker + - BluespaceBeaker + - SyringeBluespace + - ClothingEyesHudMedical # Nyano + - ChemicalPayload # Nyano + - SyringeCryostasis + - ClothingEyesNightVisionMedicalGoggles + # Shitmed Change + - EnergyScalpel + - EnergyCautery + - AdvancedRetractor + - OmnimedTool + - MedicalCyberneticEyes - type: Machine board: MedicalTechFabCircuitboard - type: StealTarget @@ -1573,34 +1567,34 @@ - ClothingUniformJanitorThong - type: EmagLatheRecipes emagStaticRecipes: - - ClothingHeadHatCentcomcap - - ClothingHeadHatCentcom - - ClothingUniformJumpsuitCentcomAgent - - ClothingUniformJumpsuitCentcomFormal - - ClothingUniformJumpskirtCentcomFormalDress - - ClothingUniformJumpsuitCentcomOfficer - - ClothingUniformJumpsuitCentcomOfficial - - ClothingHeadHatSyndieMAA - - ClothingHeadHatSyndie - - ClothingUniformJumpsuitOperative - - ClothingUniformJumpskirtOperative - - ClothingUniformJumpsuitSyndieFormal - - ClothingUniformJumpskirtSyndieFormalDress - - ClothingHeadPyjamaSyndicateBlack - - ClothingUniformJumpsuitPyjamaSyndicateBlack - - ClothingHeadPyjamaSyndicatePink - - ClothingUniformJumpsuitPyjamaSyndicatePink - - ClothingHeadPyjamaSyndicateRed - - ClothingUniformJumpsuitPyjamaSyndicateRed - - ClothingOuterWinterCentcom - - ClothingOuterWinterSyndie - - ClothingOuterWinterSyndieCap + - ClothingHeadHatCentcomcap + - ClothingHeadHatCentcom + - ClothingUniformJumpsuitCentcomAgent + - ClothingUniformJumpsuitCentcomFormal + - ClothingUniformJumpskirtCentcomFormalDress + - ClothingUniformJumpsuitCentcomOfficer + - ClothingUniformJumpsuitCentcomOfficial + - ClothingHeadHatSyndieMAA + - ClothingHeadHatSyndie + - ClothingUniformJumpsuitOperative + - ClothingUniformJumpskirtOperative + - ClothingUniformJumpsuitSyndieFormal + - ClothingUniformJumpskirtSyndieFormalDress + - ClothingHeadPyjamaSyndicateBlack + - ClothingUniformJumpsuitPyjamaSyndicateBlack + - ClothingHeadPyjamaSyndicatePink + - ClothingUniformJumpsuitPyjamaSyndicatePink + - ClothingHeadPyjamaSyndicateRed + - ClothingUniformJumpsuitPyjamaSyndicateRed + - ClothingOuterWinterCentcom + - ClothingOuterWinterSyndie + - ClothingOuterWinterSyndieCap - type: MaterialStorage whitelist: tags: - - Sheet - - RawMaterial - - Ingot + - Sheet + - RawMaterial + - Ingot - type: entity parent: BaseLathe @@ -1608,49 +1602,49 @@ name: ore processor description: It produces sheets and ingots using ores. components: - - type: Sprite - sprite: Structures/Machines/ore_processor.rsi - layers: - - state: icon - map: ["enum.LatheVisualLayers.IsRunning"] - - state: unlit - shader: unshaded - map: ["enum.PowerDeviceVisualLayers.Powered"] - - state: inserting - map: ["enum.MaterialStorageVisualLayers.Inserting"] - - state: panel - map: ["enum.WiresVisualLayers.MaintenancePanel"] - - type: Machine - board: OreProcessorMachineCircuitboard - - type: MaterialStorage - ignoreColor: true - whitelist: - tags: - - Ore - - type: Lathe - idleState: icon - runningState: building - defaultProductionAmount: 10 - staticRecipes: - - BluespaceCrystal - - NormalityCrystal - - SheetSteel - - SheetGlass1 - - SheetRGlass - - SheetPlasma1 - - SheetPGlass1 - - SheetRPGlass1 - - SheetUranium1 - - IngotGold1 - - IngotSilver1 - - MaterialBananium1 - - type: MaterialStorageMagnetPickup # Delta V - Summary: Adds magnet pull from Frontier - magnetEnabled: True - range: 0.30 # Delta V - End Magnet Pull - - type: MiningPoints # DeltaV - Source of mining points for miners - transferSound: - path: /Audio/Effects/Cargo/ping.ogg - - type: MiningPointsLathe # DeltaV + - type: Sprite + sprite: Structures/Machines/ore_processor.rsi + layers: + - state: icon + map: ["enum.LatheVisualLayers.IsRunning"] + - state: unlit + shader: unshaded + map: ["enum.PowerDeviceVisualLayers.Powered"] + - state: inserting + map: ["enum.MaterialStorageVisualLayers.Inserting"] + - state: panel + map: ["enum.WiresVisualLayers.MaintenancePanel"] + - type: Machine + board: OreProcessorMachineCircuitboard + - type: MaterialStorage + ignoreColor: true + whitelist: + tags: + - Ore + - type: Lathe + idleState: icon + runningState: building + defaultProductionAmount: 10 + staticRecipes: + - BluespaceCrystal + - NormalityCrystal + - SheetSteel + - SheetGlass1 + - SheetRGlass + - SheetPlasma1 + - SheetPGlass1 + - SheetRPGlass1 + - SheetUranium1 + - IngotGold1 + - IngotSilver1 + - MaterialBananium1 + - type: MaterialStorageMagnetPickup # Delta V - Summary: Adds magnet pull from Frontier + magnetEnabled: True + range: 0.30 # Delta V - End Magnet Pull + - type: MiningPoints # DeltaV - Source of mining points for miners + transferSound: + path: /Audio/Effects/Cargo/ping.ogg + - type: MiningPointsLathe # DeltaV - type: entity parent: OreProcessor @@ -1666,22 +1660,22 @@ materialUseMultiplier: 0.75 timeMultiplier: 0.5 staticRecipes: - - BluespaceCrystal - - NormalityCrystal - - SheetSteel - - SheetGlass1 - - SheetRGlass - - SheetPlasma1 - - SheetPGlass1 - - SheetRPGlass1 - - SheetPlasteel1 - - SheetUranium1 - - SheetUGlass1 - - SheetRUGlass1 - - IngotGold1 - - IngotSilver1 - - MaterialBananium1 - - MaterialDiamond + - BluespaceCrystal + - NormalityCrystal + - SheetSteel + - SheetGlass1 + - SheetRGlass + - SheetPlasma1 + - SheetPGlass1 + - SheetRPGlass1 + - SheetPlasteel1 + - SheetUranium1 + - SheetUGlass1 + - SheetRUGlass1 + - IngotGold1 + - IngotSilver1 + - MaterialBananium1 + - MaterialDiamond - type: entity parent: BaseLathe @@ -1832,27 +1826,27 @@ - PlushieGrim - type: EmagLatheRecipes emagStaticRecipes: - - PlushieGhost - - PlushieRGBee - - PlushieRainbowCarp - - PlushieJester - - PlushieSlips - - PlushieTrystan - - PlushieAbductor - - PlushieAbductorAgent - - PlushieNuke - - ToyNuke - - FoamBlade - - BalloonSyn - - SingularityToy - - TeslaToy - - ToySword - - BwoinkHammer - - ThronglerToy + - PlushieGhost + - PlushieRGBee + - PlushieRainbowCarp + - PlushieJester + - PlushieSlips + - PlushieTrystan + - PlushieAbductor + - PlushieAbductorAgent + - PlushieNuke + - ToyNuke + - FoamBlade + - BalloonSyn + - SingularityToy + - TeslaToy + - ToySword + - BwoinkHammer + - ThronglerToy - type: MaterialStorage whitelist: tags: - - PrizeTicket + - PrizeTicket - type: entity id: MedicalBiofabricator @@ -1866,20 +1860,20 @@ layers: - state: limbgrower_idleoff map: ["enum.LatheVisualLayers.IsRunning"] -# - state: limbgrower_idleoff -# shader: unshaded -# map: ["enum.PowerDeviceVisualLayers.Powered"] -# - state: inserting -# map: ["enum.MaterialStorageVisualLayers.Inserting"] -# - state: panel -# map: ["enum.WiresVisualLayers.MaintenancePanel"] + # - state: limbgrower_idleoff + # shader: unshaded + # map: ["enum.PowerDeviceVisualLayers.Powered"] + # - state: inserting + # map: ["enum.MaterialStorageVisualLayers.Inserting"] + # - state: panel + # map: ["enum.WiresVisualLayers.MaintenancePanel"] - type: Machine board: MedicalBiofabMachineBoard - type: MaterialStorage whitelist: tags: - - Sheet - - RawMaterial + - Sheet + - RawMaterial - type: Lathe idleState: limbgrower_idleoff runningState: limbgrower_idleon @@ -1898,5 +1892,5 @@ - SynthRightHand - type: EmagLatheRecipes emagStaticRecipes: - - PizzaLeftArm - - PizzaRightArm + - PizzaLeftArm + - PizzaRightArm diff --git a/Resources/Prototypes/Entities/Structures/Machines/vending_machines.yml b/Resources/Prototypes/Entities/Structures/Machines/vending_machines.yml index 5f2be12784..fc5115593b 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/vending_machines.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/vending_machines.yml @@ -100,6 +100,7 @@ - type: Actions - type: SentienceTarget flavorKind: station-event-random-sentience-flavor-mechanical + weight: 0.025 # fuck you in particular (it now needs 40 vending machines to be as likely as 1 interesting animal) - type: StaticPrice price: 100 - type: Appearance diff --git a/Resources/Prototypes/Entities/Structures/Power/apc.yml b/Resources/Prototypes/Entities/Structures/Power/apc.yml index 9412d45447..1022c57b3f 100644 --- a/Resources/Prototypes/Entities/Structures/Power/apc.yml +++ b/Resources/Prototypes/Entities/Structures/Power/apc.yml @@ -6,6 +6,7 @@ placement: mode: SnapgridCenter components: + - type: StationAiWhitelist - type: AmbientOnPowered - type: AmbientSound volume: -9 diff --git a/Resources/Prototypes/Entities/Structures/Specific/Anomaly/anomalies.yml b/Resources/Prototypes/Entities/Structures/Specific/Anomaly/anomalies.yml index 8294586048..02184fc613 100644 --- a/Resources/Prototypes/Entities/Structures/Specific/Anomaly/anomalies.yml +++ b/Resources/Prototypes/Entities/Structures/Specific/Anomaly/anomalies.yml @@ -50,6 +50,7 @@ path: /Audio/Effects/teleport_arrival.ogg - type: Psionic removable: false + roller: false - type: InnatePsionicPowers powersToAdd: - TelepathyPower @@ -106,6 +107,11 @@ - type: IgniteOnCollide fixtureId: fix1 fireStacks: 1 + - type: Psionic + removable: false + roller: false + assayFeedback: + - anomaly-pyroclastic-feedback - type: entity id: AnomalyGravity @@ -138,6 +144,11 @@ - type: SingularityDistortion intensity: 1000 falloffPower: 2.7 + - type: Psionic + removable: false + roller: false + assayFeedback: + - anomaly-gravity-feedback - type: entity id: AnomalyElectricity @@ -161,6 +172,11 @@ castShadows: false - type: ElectricityAnomaly - type: Electrified + - type: Psionic + removable: false + roller: false + assayFeedback: + - anomaly-electricity-feedback - type: entity id: AnomalyFlesh @@ -254,6 +270,11 @@ - MobFleshClamp - MobFleshLover - FleshKudzu + - type: Psionic + removable: false + roller: false + assayFeedback: + - anomaly-flesh-feedback - type: entity id: AnomalyBluespace @@ -304,6 +325,11 @@ anomalyContactDamage: types: Radiation: 10 + - type: Psionic + removable: false + roller: false + assayFeedback: + - anomaly-bluespace-feedback - type: entity id: AnomalyIce @@ -352,6 +378,11 @@ releasedGas: 8 # Frezon. Please replace if there is a better way to specify this releaseOnMaxSeverity: true spawnRadius: 0 + - type: Psionic + removable: false + roller: false + assayFeedback: + - anomaly-ice-feedback - type: entity id: AnomalyRockBase @@ -390,6 +421,11 @@ maxAmount: 50 maxRange: 12 floor: FloorAsteroidTile + - type: Psionic + removable: false + roller: false + assayFeedback: + - anomaly-rock-feedback - type: entity id: AnomalyRockUranium @@ -649,6 +685,11 @@ maxRange: 6 spawns: - KudzuFlowerAngry + - type: Psionic + removable: false + roller: false + assayFeedback: + - anomaly-flora-feedback - type: entity id: AnomalyFloraBulb @@ -808,6 +849,11 @@ solution: anomaly - type: InjectableSolution solution: beaker + - type: Psionic + removable: false + roller: false + assayFeedback: + - anomaly-liquid-feedback - type: entity id: AnomalyShadow @@ -864,3 +910,8 @@ - type: Tag tags: - SpookyFog + - type: Psionic + removable: false + roller: false + assayFeedback: + - anomaly-shadow-feedback diff --git a/Resources/Prototypes/Entities/Structures/Specific/oracle.yml b/Resources/Prototypes/Entities/Structures/Specific/oracle.yml index 3e2ed9508e..e908ecab66 100644 --- a/Resources/Prototypes/Entities/Structures/Specific/oracle.yml +++ b/Resources/Prototypes/Entities/Structures/Specific/oracle.yml @@ -18,6 +18,8 @@ - type: Actions - type: Psionic removable: false + assayFeedback: + - oracle-feedback psychognomicDescriptors: - p-descriptor-old - p-descriptor-demiurgic diff --git a/Resources/Prototypes/Entities/Structures/Wallmounts/air_alarm.yml b/Resources/Prototypes/Entities/Structures/Wallmounts/air_alarm.yml index 6090aae882..3e97255960 100644 --- a/Resources/Prototypes/Entities/Structures/Wallmounts/air_alarm.yml +++ b/Resources/Prototypes/Entities/Structures/Wallmounts/air_alarm.yml @@ -111,6 +111,7 @@ collection: MetalGlassBreak params: volume: -4 + - type: StationAiWhitelist - type: entity id: AirAlarmAssembly diff --git a/Resources/Prototypes/Entities/Structures/Wallmounts/intercom.yml b/Resources/Prototypes/Entities/Structures/Wallmounts/intercom.yml index 9a244c2c59..6345cfef3f 100644 --- a/Resources/Prototypes/Entities/Structures/Wallmounts/intercom.yml +++ b/Resources/Prototypes/Entities/Structures/Wallmounts/intercom.yml @@ -4,6 +4,7 @@ description: An intercom. For when the station just needs to know something. abstract: true components: + - type: StationAiWhitelist - type: WallMount - type: ApcPowerReceiver - type: Electrified diff --git a/Resources/Prototypes/Entities/Structures/Wallmounts/surveillance_camera.yml b/Resources/Prototypes/Entities/Structures/Wallmounts/surveillance_camera.yml index 954caf67ae..c82a3f2108 100644 --- a/Resources/Prototypes/Entities/Structures/Wallmounts/surveillance_camera.yml +++ b/Resources/Prototypes/Entities/Structures/Wallmounts/surveillance_camera.yml @@ -4,6 +4,28 @@ name: camera description: A surveillance camera. It's watching you. Kinda. components: + - type: Physics + bodyType: Static + - type: Fixtures + fixtures: + # This exists for examine. + fix1: + shape: + !type:PhysShapeCircle + radius: 0.25 + light: + shape: + !type:PhysShapeCircle + radius: 5 + hard: false + mask: + - GhostImpassable + - type: LightOnCollide + - type: PointLight + enabled: false + radius: 5 + - type: SlimPoweredLight + enabled: false - type: StationAiVision - type: Clickable - type: InteractionOutline @@ -43,6 +65,8 @@ InUse: camera_in_use - type: UserInterface interfaces: + enum.AiUi.Key: + type: StationAiBoundUserInterface enum.SurveillanceCameraSetupUiKey.Camera: type: SurveillanceCameraSetupBoundUi enum.WiresUiKey.Key: diff --git a/Resources/Prototypes/Entities/Structures/Wallmounts/timer.yml b/Resources/Prototypes/Entities/Structures/Wallmounts/timer.yml index 8e6b0863d6..f08451ad4d 100644 --- a/Resources/Prototypes/Entities/Structures/Wallmounts/timer.yml +++ b/Resources/Prototypes/Entities/Structures/Wallmounts/timer.yml @@ -7,6 +7,7 @@ snap: - Wallmount components: + - type: StationAiWhitelist - type: Transform anchored: true - type: WallMount diff --git a/Resources/Prototypes/GameRules/events.yml b/Resources/Prototypes/GameRules/events.yml index ceee3a1cd8..9ed40855c8 100644 --- a/Resources/Prototypes/GameRules/events.yml +++ b/Resources/Prototypes/GameRules/events.yml @@ -343,7 +343,10 @@ weight: 6 duration: 1 maxOccurrences: 1 # this event has diminishing returns on interesting-ness, so we cap it + startAnnouncement: true - type: RandomSentienceRule + minSentiences: 2 + maxSentiences: 5 - type: entity parent: BaseGameRule diff --git a/Resources/Prototypes/GameRules/random_sentience.yml b/Resources/Prototypes/GameRules/random_sentience.yml new file mode 100644 index 0000000000..decbd4e2cd --- /dev/null +++ b/Resources/Prototypes/GameRules/random_sentience.yml @@ -0,0 +1,11 @@ +- type: localizedDataset + id: RandomSentienceEventData + values: + prefix: random-sentience-event-data- + count: 6 + +- type: localizedDataset + id: RandomSentienceEventStrength + values: + prefix: random-sentience-event-strength- + count: 8 \ No newline at end of file diff --git a/Resources/Prototypes/Maps/saltern.yml b/Resources/Prototypes/Maps/saltern.yml index f3aa03d51f..1d7a4eda02 100644 --- a/Resources/Prototypes/Maps/saltern.yml +++ b/Resources/Prototypes/Maps/saltern.yml @@ -63,4 +63,5 @@ Mime: [ 1, 1 ] Musician: [ 1, 2 ] Passenger: [ -1, -1 ] - + #silicon + StationAi: [ 1, 1 ] diff --git a/Resources/Prototypes/Nyanotrasen/Entities/Structures/Research/glimmer_prober.yml b/Resources/Prototypes/Nyanotrasen/Entities/Structures/Research/glimmer_prober.yml index 0eede9c281..1bb752ade4 100644 --- a/Resources/Prototypes/Nyanotrasen/Entities/Structures/Research/glimmer_prober.yml +++ b/Resources/Prototypes/Nyanotrasen/Entities/Structures/Research/glimmer_prober.yml @@ -6,6 +6,8 @@ components: - type: Psionic removable: false + assayFeedback: + - prober-feedback - type: InnatePsionicPowers powersToAdd: - TelepathyPower @@ -93,6 +95,8 @@ components: - type: Psionic removable: false + assayFeedback: + - drain-feedback - type: GlimmerSource addToGlimmer: false - type: Construction diff --git a/Resources/Prototypes/Nyanotrasen/Entities/Structures/Research/sophicscribe.yml b/Resources/Prototypes/Nyanotrasen/Entities/Structures/Research/sophicscribe.yml index f92c9b0740..6f7b02cb6b 100644 --- a/Resources/Prototypes/Nyanotrasen/Entities/Structures/Research/sophicscribe.yml +++ b/Resources/Prototypes/Nyanotrasen/Entities/Structures/Research/sophicscribe.yml @@ -25,6 +25,8 @@ - type: Actions - type: Psionic removable: false + assayFeedback: + - sophic-grammateus-feedback psychognomicDescriptors: - p-descriptor-old - p-descriptor-demiurgic diff --git a/Resources/Prototypes/Nyanotrasen/Roles/Jobs/Epistemics/forensicmantis.yml b/Resources/Prototypes/Nyanotrasen/Roles/Jobs/Epistemics/forensicmantis.yml index 2b17daa54f..e0231177cc 100644 --- a/Resources/Prototypes/Nyanotrasen/Roles/Jobs/Epistemics/forensicmantis.yml +++ b/Resources/Prototypes/Nyanotrasen/Roles/Jobs/Epistemics/forensicmantis.yml @@ -38,6 +38,7 @@ powersToAdd: - MetapsionicPower - TelepathyPower + - AssayPower - type: startingGear id: ForensicMantisGear diff --git a/Resources/Prototypes/Procedural/salvage_loot.yml b/Resources/Prototypes/Procedural/salvage_loot.yml index 7e7ddf6ff9..1b941cd1a2 100644 --- a/Resources/Prototypes/Procedural/salvage_loot.yml +++ b/Resources/Prototypes/Procedural/salvage_loot.yml @@ -106,6 +106,10 @@ - proto: WeaponTeslaGun prob: 0.1 cost: 2 + - proto: ClothingEyesNightVisionGoggles + cost: 8 + - proto: ClothingEyesGlassesThermal + cost: 8 # Mob loot table diff --git a/Resources/Prototypes/Psionics/psionics.yml b/Resources/Prototypes/Psionics/psionics.yml index 548881e61f..b1e9d783a8 100644 --- a/Resources/Prototypes/Psionics/psionics.yml +++ b/Resources/Prototypes/Psionics/psionics.yml @@ -170,6 +170,9 @@ - !type:AddPsionicStatSources amplificationModifier: 0.5 dampeningModifier: 0.5 + - !type:PsionicAddAvailablePowers + powerPrototype: AssayPower + weight: 0.1 removalFunctions: - !type:RemovePsionicActions - !type:RemovePsionicPowerComponents @@ -178,6 +181,8 @@ - !type:RemovePsionicStatSources - !type:RemoveAssayFeedback assayFeedback: metapsionic-power-metapsionic-feedback + - !type:PsionicRemoveAvailablePowers + powerPrototype: AssayPower - type: psionicPower id: PsionicRegenerationPower @@ -661,3 +666,25 @@ - !type:RemovePsionicPsychognomicDescriptors psychognomicDescriptor: calling powerSlotCost: 0 + +- type: psionicPower + id: AssayPower + name: Assay + powerCategories: + - Mentalic + initializeFunctions: + - !type:AddPsionicActions + actions: + - ActionAssay + - !type:PsionicFeedbackPopup + - !type:PsionicFeedbackSelfChat + feedbackMessage: assay-power-initialization-feedback + - !type:AddPsionicAssayFeedback + assayFeedback: assay-power-metapsionic-feedback + - !type:AddPsionicStatSources + dampeningModifier: 0.5 + removalFunctions: + - !type:RemovePsionicActions + - !type:RemovePsionicStatSources + - !type:RemoveAssayFeedback + assayFeedback: assay-power-metapsionic-feedback diff --git a/Resources/Prototypes/Recipes/Lathes/devices.yml b/Resources/Prototypes/Recipes/Lathes/devices.yml index 2b0d6fa44f..56e1739817 100644 --- a/Resources/Prototypes/Recipes/Lathes/devices.yml +++ b/Resources/Prototypes/Recipes/Lathes/devices.yml @@ -234,3 +234,33 @@ Steel: 500 Glass: 400 Gold: 100 + +- type: latheRecipe + id: ClothingEyesNightVisionGoggles + result: ClothingEyesNightVisionGoggles + completetime: 2 + materials: + Steel: 200 + Glass: 100 + Silver: 100 + Gold: 100 + +- type: latheRecipe + id: ClothingEyesNightVisionDiagnosticGoggles + result: ClothingEyesNightVisionDiagnosticGoggles + completetime: 2 + materials: + Steel: 200 + Glass: 100 + Silver: 100 + Gold: 100 + +- type: latheRecipe + id: ClothingEyesThermalVisionGoggles + result: ClothingEyesThermalVisionGoggles + completetime: 2 + materials: + Steel: 200 + Glass: 100 + Silver: 100 + Gold: 100 diff --git a/Resources/Prototypes/Recipes/Lathes/medical.yml b/Resources/Prototypes/Recipes/Lathes/medical.yml index ba8f596d21..29f3fed247 100644 --- a/Resources/Prototypes/Recipes/Lathes/medical.yml +++ b/Resources/Prototypes/Recipes/Lathes/medical.yml @@ -250,4 +250,15 @@ completetime: 2 materials: Steel: 100 - Plastic: 100 \ No newline at end of file + Plastic: 100 + +- type: latheRecipe + id: ClothingEyesNightVisionMedicalGoggles + result: ClothingEyesNightVisionMedicalGoggles + completetime: 7 + materials: + Steel: 300 + Glass: 300 + Silver: 100 + Gold: 100 + Plasma: 200 diff --git a/Resources/Prototypes/Recipes/Lathes/security.yml b/Resources/Prototypes/Recipes/Lathes/security.yml index 5b126f7697..e80ba6bbf9 100644 --- a/Resources/Prototypes/Recipes/Lathes/security.yml +++ b/Resources/Prototypes/Recipes/Lathes/security.yml @@ -242,6 +242,15 @@ materials: Steel: 145 +- type: latheRecipe + id: MagazinePistolRubber + result: MagazinePistolRubber + category: Ammo + completetime: 5 + materials: + Steel: 45 + Plastic: 100 + - type: latheRecipe id: MagazinePistolPractice result: MagazinePistolPractice @@ -285,6 +294,34 @@ materials: Steel: 300 +- type: latheRecipe + id: MagazinePistolSubMachineGunRubber + result: MagazinePistolSubMachineGunRubber + category: Ammo + completetime: 5 + materials: + Steel: 100 + Plastic: 200 + +- type: latheRecipe + id: MagazinePistolSubMachineGunUranium + result: MagazinePistolSubMachineGunUranium + category: Ammo + completetime: 5 + materials: + Steel: 25 + Plastic: 225 + Uranium: 225 + +- type: latheRecipe + id: MagazinePistolSubMachineGunIncendiary + result: MagazinePistolSubMachineGunIncendiary + category: Ammo + completetime: 5 + materials: + Steel: 25 + Plastic: 400 + - type: latheRecipe id: MagazinePistolSubMachineGunTopMountedEmpty result: MagazinePistolSubMachineGunTopMountedEmpty @@ -351,6 +388,14 @@ materials: Steel: 475 +- type: latheRecipe + id: MagazineRifleRubber + result: MagazineRifleRubber + category: Ammo + completetime: 5 + materials: + Steel: 150 + Plastic: 325 - type: latheRecipe id: MagazineRiflePractice @@ -395,6 +440,15 @@ materials: Steel: 565 +- type: latheRecipe + id: MagazineLightRifleRubber + result: MagazineLightRifleRubber + category: Ammo + completetime: 5 + materials: + Steel: 125 + Plastic: 350 + - type: latheRecipe id: MagazineLightRiflePractice result: MagazineLightRiflePractice @@ -403,7 +457,6 @@ materials: Steel: 205 - - type: latheRecipe id: MagazineLightRifleUranium result: MagazineLightRifleUranium @@ -423,6 +476,50 @@ Steel: 25 Plastic: 540 +- type: latheRecipe + id: MagazineMagnumEmpty + result: MagazineMagnumEmpty + category: Ammo + completetime: 5 + materials: + Steel: 30 + +- type: latheRecipe + id: MagazineMagnum + result: MagazineMagnum + category: Ammo + completetime: 5 + materials: + Steel: 150 + +- type: latheRecipe + id: MagazineMagnumRubber + result: MagazineMagnumRubber + category: Ammo + completetime: 5 + materials: + Steel: 50 + Plastic: 100 + +- type: latheRecipe + id: MagazineMagnumUranium + result: MagazineMagnumUranium + category: Ammo + completetime: 5 + materials: + Steel: 30 + Plastic: 75 + Uranium: 150 + +- type: latheRecipe + id: MagazineMagnumIncendiary + result: MagazineMagnumIncendiary + category: Ammo + completetime: 5 + materials: + Steel: 30 + Plastic: 150 + - type: latheRecipe id: MagazineBoxRifle result: MagazineBoxRifle @@ -475,21 +572,21 @@ Plastic: 160 - type: latheRecipe - id: SpeedLoaderMagnumEmpty - result: SpeedLoaderMagnumEmpty + id: MagazineBoxLightRifleRubber + result: MagazineBoxLightRifleRubber category: Ammo completetime: 5 materials: - Steel: 50 + Steel: 350 + Plastic: 600 - type: latheRecipe - id: MagazineBoxLightRifleRubber - result: MagazineBoxLightRifleRubber + id: SpeedLoaderMagnumEmpty + result: SpeedLoaderMagnumEmpty category: Ammo completetime: 5 materials: - Steel: 350 - Plastic: 600 + Steel: 50 - type: latheRecipe id: SpeedLoaderMagnum @@ -499,6 +596,15 @@ materials: Steel: 190 +- type: latheRecipe + id: SpeedLoaderMagnumRubber + result: SpeedLoaderMagnumRubber + category: Ammo + completetime: 5 + materials: + Steel: 50 + Plastic: 70 + - type: latheRecipe id: SpeedLoaderMagnumPractice result: SpeedLoaderMagnumPractice @@ -758,46 +864,46 @@ result: MagazineGrenadeEmpty completetime: 3 materials: - Steel: 150 - Plastic: 50 + Steel: 150 + Plastic: 50 - type: latheRecipe id: GrenadeEMP result: GrenadeEMP completetime: 3 materials: - Steel: 150 - Plastic: 100 - Glass: 20 + Steel: 150 + Plastic: 100 + Glass: 20 - type: latheRecipe id: GrenadeBlast result: GrenadeBlast completetime: 3 materials: - Steel: 150 - Plastic: 100 - Gold: 50 + Steel: 150 + Plastic: 100 + Gold: 50 - type: latheRecipe id: GrenadeFlash result: GrenadeFlash completetime: 3 materials: - Steel: 150 - Plastic: 100 - Glass: 20 + Steel: 150 + Plastic: 100 + Glass: 20 - type: latheRecipe id: PortableRecharger result: PortableRecharger completetime: 15 materials: - Steel: 2000 - Uranium: 2000 - Plastic: 1000 - Plasma: 500 - Glass: 500 + Steel: 2000 + Uranium: 2000 + Plastic: 1000 + Plasma: 500 + Glass: 500 - type: latheRecipe id: ShellShotgun @@ -889,4 +995,14 @@ completetime: 2 materials: Plastic: 15 - Uranium: 10 \ No newline at end of file + Uranium: 10 + +- type: latheRecipe + id: ClothingEyesNightVisionSecurityGoggles + result: ClothingEyesNightVisionSecurityGoggles + completetime: 5 + materials: + Steel: 500 + Glass: 300 + Silver: 100 + Gold: 100 diff --git a/Resources/Prototypes/Research/arsenal.yml b/Resources/Prototypes/Research/arsenal.yml index 57c18a73de..1236a6a375 100644 --- a/Resources/Prototypes/Research/arsenal.yml +++ b/Resources/Prototypes/Research/arsenal.yml @@ -27,6 +27,8 @@ - BoxShotgunIncendiary - MagazineRifleIncendiary - MagazinePistolIncendiary + - MagazinePistolSubMachineGunIncendiary + - MagazineMagnumIncendiary - MagazineLightRifleIncendiary - SpeedLoaderMagnumIncendiary - SpeedLoaderRifleHeavyIncendiary # Frontier @@ -69,6 +71,12 @@ - CartridgeMagnumRubber - CartridgeLightRifleRubber - CartridgeRifleRubber + - MagazineRifleRubber + - MagazinePistolRubber + - MagazinePistolSubMachineGunRubber + - MagazineMagnumRubber + - MagazineLightRifleRubber + - SpeedLoaderMagnumRubber - MagazineBoxPistolRubber - MagazineBoxMagnumRubber - MagazineBoxLightRifleRubber @@ -93,6 +101,8 @@ recipeUnlocks: - MagazineRifleUranium - MagazinePistolUranium + - MagazinePistolSubMachineGunUranium + - MagazineMagnumUranium - MagazineLightRifleUranium - SpeedLoaderMagnumUranium - SpeedLoaderRifleHeavyUranium # Frontier diff --git a/Resources/Prototypes/Research/experimental.yml b/Resources/Prototypes/Research/experimental.yml index 423ec0f84d..cf6493847a 100644 --- a/Resources/Prototypes/Research/experimental.yml +++ b/Resources/Prototypes/Research/experimental.yml @@ -167,6 +167,33 @@ - MedicalScannerMachineCircuitboard - MetempsychoticMachineCircuitboard +- type: technology + id: NightVisionTech + name: research-technology-night-vision + icon: + sprite: Clothing/Eyes/Goggles/nightvision.rsi + state: icon + discipline: Experimental + tier: 2 + cost: 10000 + recipeUnlocks: + - ClothingEyesNightVisionGoggles + - ClothingEyesNightVisionSecurityGoggles + - ClothingEyesNightVisionMedicalGoggles + - ClothingEyesNightVisionDiagnosticGoggles + +- type: technology + id: ThermalVisionTech + name: research-technology-thermal-vision + icon: + sprite: Clothing/Eyes/Goggles/thermal.rsi + state: icon + discipline: Experimental + tier: 2 + cost: 10000 + recipeUnlocks: + - ClothingEyesThermalVisionGoggles + # Tier 3 - type: technology diff --git a/Resources/Prototypes/Roles/Jobs/Science/borg.yml b/Resources/Prototypes/Roles/Jobs/Science/borg.yml index e62be04bee..f4626659e3 100644 --- a/Resources/Prototypes/Roles/Jobs/Science/borg.yml +++ b/Resources/Prototypes/Roles/Jobs/Science/borg.yml @@ -1,3 +1,21 @@ +# No idea why it's in sci but we ball. +- type: job + id: StationAi + name: job-name-station-ai + description: job-description-station-ai + playTimeTracker: JobStationAi + requirements: + - !type:CharacterPlaytimeRequirement + tracker: JobBorg + min: 18000 # 5 hrs + canBeAntag: false + icon: JobIconStationAi + supervisors: job-supervisors-rd + jobEntity: StationAiBrain + nameDataset: NamesAI + spawnLoadout: false + applyTraits: false + - type: job id: Borg name: job-name-borg diff --git a/Resources/Prototypes/Roles/Jobs/departments.yml b/Resources/Prototypes/Roles/Jobs/departments.yml index 83ae4dd2e7..06963eb898 100644 --- a/Resources/Prototypes/Roles/Jobs/departments.yml +++ b/Resources/Prototypes/Roles/Jobs/departments.yml @@ -15,7 +15,6 @@ weight: -10 roles: - Bartender - - Borg - Botanist - Boxer # - Chaplain # DeltaV - Move Chaplain into Epistemics @@ -118,6 +117,14 @@ - Librarian - Roboticist +- type: department + id: Silicon + description: department-Silicon-description + color: "#D381C9" + roles: + - Borg + - StationAi + - type: department id: Specific description: department-Specific-description diff --git a/Resources/Prototypes/Roles/play_time_trackers.yml b/Resources/Prototypes/Roles/play_time_trackers.yml index fd99b2c228..9c87cde979 100644 --- a/Resources/Prototypes/Roles/play_time_trackers.yml +++ b/Resources/Prototypes/Roles/play_time_trackers.yml @@ -151,6 +151,9 @@ - type: playTimeTracker id: JobServiceWorker +- type: playTimeTracker + id: JobStationAi + - type: playTimeTracker id: JobStationEngineer diff --git a/Resources/Prototypes/Shaders/shaders.yml b/Resources/Prototypes/Shaders/shaders.yml index 108b9f778b..96c51d1a98 100644 --- a/Resources/Prototypes/Shaders/shaders.yml +++ b/Resources/Prototypes/Shaders/shaders.yml @@ -127,4 +127,9 @@ - type: shader id: Ethereal kind: source - path: "/Textures/Shaders/ethereal.swsl" \ No newline at end of file + path: "/Textures/Shaders/ethereal.swsl" + +- type: shader + id: NightVision + kind: source + path: "/Textures/Shaders/nightvision.swsl" diff --git a/Resources/Prototypes/StatusIcon/job.yml b/Resources/Prototypes/StatusIcon/job.yml index 17c9cee804..bcd8021c3e 100644 --- a/Resources/Prototypes/StatusIcon/job.yml +++ b/Resources/Prototypes/StatusIcon/job.yml @@ -29,6 +29,14 @@ state: Borg jobName: job-name-borg +- type: jobIcon + parent: JobIcon + id: JobIconStationAi + icon: + sprite: /Textures/Interface/Misc/job_icons.rsi + state: StationAi + jobName: job-name-station-ai + - type: jobIcon parent: JobIcon id: JobIconBotanist diff --git a/Resources/Prototypes/Traits/physical.yml b/Resources/Prototypes/Traits/physical.yml index 1ce0cd558a..4899cec0d8 100644 --- a/Resources/Prototypes/Traits/physical.yml +++ b/Resources/Prototypes/Traits/physical.yml @@ -784,3 +784,44 @@ components: - type: CritModifier critThresholdModifier: -50 + +- type: trait + id: LightAmplification + category: Physical + points: -4 + requirements: + - !type:CharacterJobRequirement + inverted: true + jobs: + - Prisoner # Bionics should be "Confiscated" from long term prisoners. + - !type:CharacterTraitRequirement + traits: + - CyberEyes + functions: + - !type:TraitAddComponent + components: + - type: NightVision + +- type: trait + id: ThermographicVision + category: Physical + points: -8 + requirements: + - !type:CharacterJobRequirement + inverted: true + jobs: + - Prisoner # Bionics should be "Confiscated" from long term prisoners. + - !type:CharacterTraitRequirement + traits: + - CyberEyes + functions: + - !type:TraitAddComponent + components: + - type: ThermalVision + pulseTime: 2 + toggleAction: PulseThermalVision + - !type:TraitPushDescription + descriptionExtensions: + - description: examine-thermal-vision-message + fontSize: 12 + requireDetailRange: true diff --git a/Resources/Prototypes/WhiteDream/Entities/Clothing/Cult/armor.yml b/Resources/Prototypes/WhiteDream/Entities/Clothing/Cult/armor.yml index 04279d51d2..bdbb4bac46 100644 --- a/Resources/Prototypes/WhiteDream/Entities/Clothing/Cult/armor.yml +++ b/Resources/Prototypes/WhiteDream/Entities/Clothing/Cult/armor.yml @@ -113,6 +113,12 @@ sprite: Clothing/Eyes/Misc/blindfold.rsi - type: FlashImmunity - type: EyeProtection + - type: NightVision + isActive: true + toggleAction: null + activateSound: null + deactivateSound: null + color: White - type: ShowHealthBars damageContainers: - Biological @@ -120,5 +126,3 @@ - type: ShowHealthIcons damageContainers: - Biological - # TODO: ADD NIGHT VISION - diff --git a/Resources/Prototypes/Wires/layouts.yml b/Resources/Prototypes/Wires/layouts.yml index 9fd6bddc37..20d5d33603 100644 --- a/Resources/Prototypes/Wires/layouts.yml +++ b/Resources/Prototypes/Wires/layouts.yml @@ -9,6 +9,7 @@ - !type:DoorBoltLightWireAction - !type:DoorTimingWireAction - !type:DoorSafetyWireAction + - !type:AiInteractWireAction - type: wireLayout parent: Airlock @@ -96,9 +97,10 @@ - type: wireLayout id: SurveillanceCamera - dummyWires: 4 + dummyWires: 2 wires: - !type:PowerWireAction + - !type:AiVisionWireAction - type: wireLayout id: CryoPod diff --git a/Resources/Prototypes/_Shitmed/Entities/Surgery/surgeries.yml b/Resources/Prototypes/_Shitmed/Entities/Surgery/surgeries.yml index 775716ecff..44d1f3c1c8 100644 --- a/Resources/Prototypes/_Shitmed/Entities/Surgery/surgeries.yml +++ b/Resources/Prototypes/_Shitmed/Entities/Surgery/surgeries.yml @@ -377,6 +377,16 @@ - type: SurgeryOrganCondition organ: - type: Brain + slotId: brain + +- type: entity + parent: SurgeryRemoveBrain + id: SurgeryRemoveBrainTorso + name: Remove Brain + categories: [ HideSpawnMenu ] + components: + - type: SurgeryPartCondition + part: Torso - type: entity parent: SurgeryBase @@ -397,6 +407,16 @@ - type: Brain inverse: true reattaching: true + slotId: brain + +- type: entity + parent: SurgeryInsertBrain + id: SurgeryInsertBrainTorso + name: Insert Brain + categories: [ HideSpawnMenu ] + components: + - type: SurgeryPartCondition + part: Torso - type: entity parent: SurgeryBase @@ -415,6 +435,7 @@ - type: SurgeryOrganCondition organ: - type: Heart + slotId: heart - type: entity parent: SurgeryBase @@ -435,6 +456,7 @@ - type: Heart inverse: true reattaching: true + slotId: heart - type: entity parent: SurgeryBase @@ -453,6 +475,7 @@ - type: SurgeryOrganCondition organ: - type: Liver + slotId: liver - type: entity parent: SurgeryBase @@ -473,6 +496,7 @@ - type: Liver inverse: true reattaching: true + slotId: liver - type: entity parent: SurgeryBase @@ -491,6 +515,7 @@ - type: SurgeryOrganCondition organ: - type: Lung + slotId: lungs - type: entity parent: SurgeryBase @@ -511,6 +536,7 @@ - type: Lung inverse: true reattaching: true + slotId: lungs - type: entity parent: SurgeryBase @@ -529,6 +555,7 @@ - type: SurgeryOrganCondition organ: - type: Stomach + slotId: stomach - type: entity parent: SurgeryBase @@ -549,6 +576,7 @@ - type: Stomach inverse: true reattaching: true + slotId: stomach - type: entity parent: SurgeryBase @@ -567,6 +595,7 @@ - type: SurgeryOrganCondition organ: - type: Eyes + slotId: eyes - type: entity parent: SurgeryBase @@ -587,6 +616,7 @@ - type: Eyes inverse: true reattaching: true + slotId: eyes - type: entity parent: SurgeryBase @@ -602,6 +632,7 @@ - type: SurgeryOrganCondition organ: - type: Brain + slotId: brain - type: SurgeryOrganOnAddCondition components: brain: @@ -629,6 +660,7 @@ - type: SurgeryOrganCondition organ: - type: Brain + slotId: brain - type: SurgeryOrganOnAddCondition components: brain: diff --git a/Resources/Prototypes/tags.yml b/Resources/Prototypes/tags.yml index 6b8e91fad0..e58587bbe7 100644 --- a/Resources/Prototypes/tags.yml +++ b/Resources/Prototypes/tags.yml @@ -1254,6 +1254,9 @@ - type: Tag id: StringInstrument +- type: Tag + id: StationAi + - type: Tag id: StationMapElectronics diff --git a/Resources/Textures/Clothing/Eyes/Goggles/diagnostic_nightvision.rsi/equipped-EYES-off.png b/Resources/Textures/Clothing/Eyes/Goggles/diagnostic_nightvision.rsi/equipped-EYES-off.png new file mode 100644 index 0000000000..b63f30fc71 Binary files /dev/null and b/Resources/Textures/Clothing/Eyes/Goggles/diagnostic_nightvision.rsi/equipped-EYES-off.png differ diff --git a/Resources/Textures/Clothing/Eyes/Goggles/diagnostic_nightvision.rsi/equipped-EYES.png b/Resources/Textures/Clothing/Eyes/Goggles/diagnostic_nightvision.rsi/equipped-EYES.png new file mode 100644 index 0000000000..cd6c763b33 Binary files /dev/null and b/Resources/Textures/Clothing/Eyes/Goggles/diagnostic_nightvision.rsi/equipped-EYES.png differ diff --git a/Resources/Textures/Clothing/Eyes/Goggles/diagnostic_nightvision.rsi/icon.png b/Resources/Textures/Clothing/Eyes/Goggles/diagnostic_nightvision.rsi/icon.png new file mode 100644 index 0000000000..8bcf7105c2 Binary files /dev/null and b/Resources/Textures/Clothing/Eyes/Goggles/diagnostic_nightvision.rsi/icon.png differ diff --git a/Resources/Textures/Clothing/Eyes/Goggles/diagnostic_nightvision.rsi/inhand-left.png b/Resources/Textures/Clothing/Eyes/Goggles/diagnostic_nightvision.rsi/inhand-left.png new file mode 100644 index 0000000000..04fff80e4d Binary files /dev/null and b/Resources/Textures/Clothing/Eyes/Goggles/diagnostic_nightvision.rsi/inhand-left.png differ diff --git a/Resources/Textures/Clothing/Eyes/Goggles/diagnostic_nightvision.rsi/inhand-right.png b/Resources/Textures/Clothing/Eyes/Goggles/diagnostic_nightvision.rsi/inhand-right.png new file mode 100644 index 0000000000..d80344136f Binary files /dev/null and b/Resources/Textures/Clothing/Eyes/Goggles/diagnostic_nightvision.rsi/inhand-right.png differ diff --git a/Resources/Textures/Clothing/Eyes/Goggles/diagnostic_nightvision.rsi/meta.json b/Resources/Textures/Clothing/Eyes/Goggles/diagnostic_nightvision.rsi/meta.json new file mode 100644 index 0000000000..2b65c68fe8 --- /dev/null +++ b/Resources/Textures/Clothing/Eyes/Goggles/diagnostic_nightvision.rsi/meta.json @@ -0,0 +1,48 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from https://github.com/BlueMoon-Labs/MOLOT-BlueMoon-Station/blob/master/icons/obj/clothing/glasses.dmi", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "equipped-EYES", + "directions": 4, + "delays": [ + [ + 0.1, + 0.1 + ], + [ + 0.1, + 0.1 + ], + [ + 0.1, + 0.1 + ], + [ + 0.1, + 0.1 + ] + ] + }, + { + "name": "equipped-EYES-off", + "directions": 4 + }, + { + "name": "icon" + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + } + ] +} diff --git a/Resources/Textures/Clothing/Eyes/Goggles/medical_nightvision.rsi/equipped-EYES-off.png b/Resources/Textures/Clothing/Eyes/Goggles/medical_nightvision.rsi/equipped-EYES-off.png new file mode 100644 index 0000000000..b63f30fc71 Binary files /dev/null and b/Resources/Textures/Clothing/Eyes/Goggles/medical_nightvision.rsi/equipped-EYES-off.png differ diff --git a/Resources/Textures/Clothing/Eyes/Goggles/medical_nightvision.rsi/equipped-EYES.png b/Resources/Textures/Clothing/Eyes/Goggles/medical_nightvision.rsi/equipped-EYES.png new file mode 100644 index 0000000000..a6513302b9 Binary files /dev/null and b/Resources/Textures/Clothing/Eyes/Goggles/medical_nightvision.rsi/equipped-EYES.png differ diff --git a/Resources/Textures/Clothing/Eyes/Goggles/medical_nightvision.rsi/icon.png b/Resources/Textures/Clothing/Eyes/Goggles/medical_nightvision.rsi/icon.png new file mode 100644 index 0000000000..52522c080e Binary files /dev/null and b/Resources/Textures/Clothing/Eyes/Goggles/medical_nightvision.rsi/icon.png differ diff --git a/Resources/Textures/Clothing/Eyes/Goggles/medical_nightvision.rsi/inhand-left.png b/Resources/Textures/Clothing/Eyes/Goggles/medical_nightvision.rsi/inhand-left.png new file mode 100644 index 0000000000..bccfe98d09 Binary files /dev/null and b/Resources/Textures/Clothing/Eyes/Goggles/medical_nightvision.rsi/inhand-left.png differ diff --git a/Resources/Textures/Clothing/Eyes/Goggles/medical_nightvision.rsi/inhand-right.png b/Resources/Textures/Clothing/Eyes/Goggles/medical_nightvision.rsi/inhand-right.png new file mode 100644 index 0000000000..f9c17318b4 Binary files /dev/null and b/Resources/Textures/Clothing/Eyes/Goggles/medical_nightvision.rsi/inhand-right.png differ diff --git a/Resources/Textures/Clothing/Eyes/Goggles/medical_nightvision.rsi/meta.json b/Resources/Textures/Clothing/Eyes/Goggles/medical_nightvision.rsi/meta.json new file mode 100644 index 0000000000..2b65c68fe8 --- /dev/null +++ b/Resources/Textures/Clothing/Eyes/Goggles/medical_nightvision.rsi/meta.json @@ -0,0 +1,48 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from https://github.com/BlueMoon-Labs/MOLOT-BlueMoon-Station/blob/master/icons/obj/clothing/glasses.dmi", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "equipped-EYES", + "directions": 4, + "delays": [ + [ + 0.1, + 0.1 + ], + [ + 0.1, + 0.1 + ], + [ + 0.1, + 0.1 + ], + [ + 0.1, + 0.1 + ] + ] + }, + { + "name": "equipped-EYES-off", + "directions": 4 + }, + { + "name": "icon" + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + } + ] +} diff --git a/Resources/Textures/Clothing/Eyes/Goggles/monocle_thermal.rsi/equipped-EYES-off.png b/Resources/Textures/Clothing/Eyes/Goggles/monocle_thermal.rsi/equipped-EYES-off.png new file mode 100644 index 0000000000..5aaa7e580c Binary files /dev/null and b/Resources/Textures/Clothing/Eyes/Goggles/monocle_thermal.rsi/equipped-EYES-off.png differ diff --git a/Resources/Textures/Clothing/Eyes/Goggles/monocle_thermal.rsi/equipped-EYES.png b/Resources/Textures/Clothing/Eyes/Goggles/monocle_thermal.rsi/equipped-EYES.png new file mode 100644 index 0000000000..dc4d9e775b Binary files /dev/null and b/Resources/Textures/Clothing/Eyes/Goggles/monocle_thermal.rsi/equipped-EYES.png differ diff --git a/Resources/Textures/Clothing/Eyes/Goggles/monocle_thermal.rsi/icon.png b/Resources/Textures/Clothing/Eyes/Goggles/monocle_thermal.rsi/icon.png new file mode 100644 index 0000000000..e0aaa8ae9a Binary files /dev/null and b/Resources/Textures/Clothing/Eyes/Goggles/monocle_thermal.rsi/icon.png differ diff --git a/Resources/Textures/Clothing/Eyes/Goggles/monocle_thermal.rsi/meta.json b/Resources/Textures/Clothing/Eyes/Goggles/monocle_thermal.rsi/meta.json new file mode 100644 index 0000000000..ad770fb0f8 --- /dev/null +++ b/Resources/Textures/Clothing/Eyes/Goggles/monocle_thermal.rsi/meta.json @@ -0,0 +1,22 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from https://github.com/BlueMoon-Labs/MOLOT-BlueMoon-Station/blob/master/icons/obj/clothing/glasses.dmi", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "equipped-EYES", + "directions": 4 + }, + { + "name": "equipped-EYES-off", + "directions": 4 + }, + { + "name": "icon" + } + ] +} diff --git a/Resources/Textures/Clothing/Eyes/Goggles/nightvision.rsi/equipped-EYES-off.png b/Resources/Textures/Clothing/Eyes/Goggles/nightvision.rsi/equipped-EYES-off.png new file mode 100644 index 0000000000..b63f30fc71 Binary files /dev/null and b/Resources/Textures/Clothing/Eyes/Goggles/nightvision.rsi/equipped-EYES-off.png differ diff --git a/Resources/Textures/Clothing/Eyes/Goggles/nightvision.rsi/equipped-EYES.png b/Resources/Textures/Clothing/Eyes/Goggles/nightvision.rsi/equipped-EYES.png new file mode 100644 index 0000000000..199c44122b Binary files /dev/null and b/Resources/Textures/Clothing/Eyes/Goggles/nightvision.rsi/equipped-EYES.png differ diff --git a/Resources/Textures/Clothing/Eyes/Goggles/nightvision.rsi/icon.png b/Resources/Textures/Clothing/Eyes/Goggles/nightvision.rsi/icon.png new file mode 100644 index 0000000000..bf770f70f8 Binary files /dev/null and b/Resources/Textures/Clothing/Eyes/Goggles/nightvision.rsi/icon.png differ diff --git a/Resources/Textures/Clothing/Eyes/Goggles/nightvision.rsi/inhand-left.png b/Resources/Textures/Clothing/Eyes/Goggles/nightvision.rsi/inhand-left.png new file mode 100644 index 0000000000..995b37471b Binary files /dev/null and b/Resources/Textures/Clothing/Eyes/Goggles/nightvision.rsi/inhand-left.png differ diff --git a/Resources/Textures/Clothing/Eyes/Goggles/nightvision.rsi/inhand-right.png b/Resources/Textures/Clothing/Eyes/Goggles/nightvision.rsi/inhand-right.png new file mode 100644 index 0000000000..c3efa67f83 Binary files /dev/null and b/Resources/Textures/Clothing/Eyes/Goggles/nightvision.rsi/inhand-right.png differ diff --git a/Resources/Textures/Clothing/Eyes/Goggles/nightvision.rsi/meta.json b/Resources/Textures/Clothing/Eyes/Goggles/nightvision.rsi/meta.json new file mode 100644 index 0000000000..987b20b9af --- /dev/null +++ b/Resources/Textures/Clothing/Eyes/Goggles/nightvision.rsi/meta.json @@ -0,0 +1,48 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "equipped-EYES", + "directions": 4, + "delays": [ + [ + 0.1, + 0.1 + ], + [ + 0.1, + 0.1 + ], + [ + 0.1, + 0.1 + ], + [ + 0.1, + 0.1 + ] + ] + }, + { + "name": "equipped-EYES-off", + "directions": 4 + }, + { + "name": "icon" + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + } + ] +} diff --git a/Resources/Textures/Clothing/Eyes/Goggles/security_nightvision.rsi/equipped-EYES-off.png b/Resources/Textures/Clothing/Eyes/Goggles/security_nightvision.rsi/equipped-EYES-off.png new file mode 100644 index 0000000000..b63f30fc71 Binary files /dev/null and b/Resources/Textures/Clothing/Eyes/Goggles/security_nightvision.rsi/equipped-EYES-off.png differ diff --git a/Resources/Textures/Clothing/Eyes/Goggles/security_nightvision.rsi/equipped-EYES.png b/Resources/Textures/Clothing/Eyes/Goggles/security_nightvision.rsi/equipped-EYES.png new file mode 100644 index 0000000000..0e156402bc Binary files /dev/null and b/Resources/Textures/Clothing/Eyes/Goggles/security_nightvision.rsi/equipped-EYES.png differ diff --git a/Resources/Textures/Clothing/Eyes/Goggles/security_nightvision.rsi/icon.png b/Resources/Textures/Clothing/Eyes/Goggles/security_nightvision.rsi/icon.png new file mode 100644 index 0000000000..80ae69d53b Binary files /dev/null and b/Resources/Textures/Clothing/Eyes/Goggles/security_nightvision.rsi/icon.png differ diff --git a/Resources/Textures/Clothing/Eyes/Goggles/security_nightvision.rsi/inhand-left.png b/Resources/Textures/Clothing/Eyes/Goggles/security_nightvision.rsi/inhand-left.png new file mode 100644 index 0000000000..02b00b1591 Binary files /dev/null and b/Resources/Textures/Clothing/Eyes/Goggles/security_nightvision.rsi/inhand-left.png differ diff --git a/Resources/Textures/Clothing/Eyes/Goggles/security_nightvision.rsi/inhand-right.png b/Resources/Textures/Clothing/Eyes/Goggles/security_nightvision.rsi/inhand-right.png new file mode 100644 index 0000000000..6ba4b3e64e Binary files /dev/null and b/Resources/Textures/Clothing/Eyes/Goggles/security_nightvision.rsi/inhand-right.png differ diff --git a/Resources/Textures/Clothing/Eyes/Goggles/security_nightvision.rsi/meta.json b/Resources/Textures/Clothing/Eyes/Goggles/security_nightvision.rsi/meta.json new file mode 100644 index 0000000000..2b65c68fe8 --- /dev/null +++ b/Resources/Textures/Clothing/Eyes/Goggles/security_nightvision.rsi/meta.json @@ -0,0 +1,48 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from https://github.com/BlueMoon-Labs/MOLOT-BlueMoon-Station/blob/master/icons/obj/clothing/glasses.dmi", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "equipped-EYES", + "directions": 4, + "delays": [ + [ + 0.1, + 0.1 + ], + [ + 0.1, + 0.1 + ], + [ + 0.1, + 0.1 + ], + [ + 0.1, + 0.1 + ] + ] + }, + { + "name": "equipped-EYES-off", + "directions": 4 + }, + { + "name": "icon" + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + } + ] +} diff --git a/Resources/Textures/Clothing/Eyes/Goggles/thermal.rsi/equipped-EYES.png b/Resources/Textures/Clothing/Eyes/Goggles/thermal.rsi/equipped-EYES.png new file mode 100644 index 0000000000..9bef0a8c05 Binary files /dev/null and b/Resources/Textures/Clothing/Eyes/Goggles/thermal.rsi/equipped-EYES.png differ diff --git a/Resources/Textures/Clothing/Eyes/Goggles/thermal.rsi/icon.png b/Resources/Textures/Clothing/Eyes/Goggles/thermal.rsi/icon.png new file mode 100644 index 0000000000..3d5f8ef9b6 Binary files /dev/null and b/Resources/Textures/Clothing/Eyes/Goggles/thermal.rsi/icon.png differ diff --git a/Resources/Textures/Clothing/Eyes/Goggles/thermal.rsi/inhand-left.png b/Resources/Textures/Clothing/Eyes/Goggles/thermal.rsi/inhand-left.png new file mode 100644 index 0000000000..bf67e35d40 Binary files /dev/null and b/Resources/Textures/Clothing/Eyes/Goggles/thermal.rsi/inhand-left.png differ diff --git a/Resources/Textures/Clothing/Eyes/Goggles/thermal.rsi/inhand-right.png b/Resources/Textures/Clothing/Eyes/Goggles/thermal.rsi/inhand-right.png new file mode 100644 index 0000000000..4ede078291 Binary files /dev/null and b/Resources/Textures/Clothing/Eyes/Goggles/thermal.rsi/inhand-right.png differ diff --git a/Resources/Textures/Clothing/Eyes/Goggles/thermal.rsi/meta.json b/Resources/Textures/Clothing/Eyes/Goggles/thermal.rsi/meta.json new file mode 100644 index 0000000000..205508acfa --- /dev/null +++ b/Resources/Textures/Clothing/Eyes/Goggles/thermal.rsi/meta.json @@ -0,0 +1,26 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon" + }, + { + "name": "equipped-EYES", + "directions": 4 + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + } + ] +} diff --git a/Resources/Textures/Interface/Actions/actions_ai.rsi/ai_core.png b/Resources/Textures/Interface/Actions/actions_ai.rsi/ai_core.png new file mode 100644 index 0000000000..8dd3031f9f Binary files /dev/null and b/Resources/Textures/Interface/Actions/actions_ai.rsi/ai_core.png differ diff --git a/Resources/Textures/Interface/Actions/actions_ai.rsi/camera_light.png b/Resources/Textures/Interface/Actions/actions_ai.rsi/camera_light.png new file mode 100644 index 0000000000..041b9b9bf7 Binary files /dev/null and b/Resources/Textures/Interface/Actions/actions_ai.rsi/camera_light.png differ diff --git a/Resources/Textures/Interface/Actions/actions_ai.rsi/crew_monitor.png b/Resources/Textures/Interface/Actions/actions_ai.rsi/crew_monitor.png new file mode 100644 index 0000000000..78fad17a76 Binary files /dev/null and b/Resources/Textures/Interface/Actions/actions_ai.rsi/crew_monitor.png differ diff --git a/Resources/Textures/Interface/Actions/actions_ai.rsi/manifest.png b/Resources/Textures/Interface/Actions/actions_ai.rsi/manifest.png new file mode 100644 index 0000000000..08514aa908 Binary files /dev/null and b/Resources/Textures/Interface/Actions/actions_ai.rsi/manifest.png differ diff --git a/Resources/Textures/Interface/Actions/actions_ai.rsi/meta.json b/Resources/Textures/Interface/Actions/actions_ai.rsi/meta.json new file mode 100644 index 0000000000..a7c00f7793 --- /dev/null +++ b/Resources/Textures/Interface/Actions/actions_ai.rsi/meta.json @@ -0,0 +1,26 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/blob/c473a8bcc28fbd80827dfca5660d81ca6e833e2c/icons/hud/screen_ai.dmi", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "ai_core" + }, + { + "name": "camera_light" + }, + { + "name": "crew_monitor" + }, + { + "name": "manifest" + }, + { + "name": "state_laws" + } + ] +} diff --git a/Resources/Textures/Interface/Actions/actions_ai.rsi/state_laws.png b/Resources/Textures/Interface/Actions/actions_ai.rsi/state_laws.png new file mode 100644 index 0000000000..e30e891745 Binary files /dev/null and b/Resources/Textures/Interface/Actions/actions_ai.rsi/state_laws.png differ diff --git a/Resources/Textures/Interface/Actions/psionics.rsi/assay.png b/Resources/Textures/Interface/Actions/psionics.rsi/assay.png new file mode 100644 index 0000000000..f245ca9e5e Binary files /dev/null and b/Resources/Textures/Interface/Actions/psionics.rsi/assay.png differ diff --git a/Resources/Textures/Interface/Actions/psionics.rsi/meta.json b/Resources/Textures/Interface/Actions/psionics.rsi/meta.json index 735bc293d1..dd9833b560 100644 --- a/Resources/Textures/Interface/Actions/psionics.rsi/meta.json +++ b/Resources/Textures/Interface/Actions/psionics.rsi/meta.json @@ -1,12 +1,15 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "healing_word, revivify, shadeskip by leonardo_dabepis (discord), telekinetic_pulse by .mocho (discord), pyrokinetic_flare, summon_remilia, summon_bat and summon_imp by ghost581 (discord)", + "copyright": "assay, healing_word, revivify, shadeskip by leonardo_dabepis (discord), telekinetic_pulse by .mocho (discord), pyrokinetic_flare, summon_remilia, summon_bat and summon_imp by ghost581 (discord)", "size": { "x": 64, "y": 64 }, "states": [ + { + "name": "assay" + }, { "name": "healing_word" }, diff --git a/Resources/Textures/Interface/Misc/job_icons.rsi/StationAi.png b/Resources/Textures/Interface/Misc/job_icons.rsi/StationAi.png new file mode 100644 index 0000000000..aba35c034b Binary files /dev/null and b/Resources/Textures/Interface/Misc/job_icons.rsi/StationAi.png differ diff --git a/Resources/Textures/Interface/Misc/job_icons.rsi/meta.json b/Resources/Textures/Interface/Misc/job_icons.rsi/meta.json index 745cc43b84..117a94e790 100644 --- a/Resources/Textures/Interface/Misc/job_icons.rsi/meta.json +++ b/Resources/Textures/Interface/Misc/job_icons.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Taken from https://github.com/vgstation-coders/vgstation13/blob/e71d6c4fba5a51f99b81c295dcaec4fc2f58fb19/icons/mob/screen1.dmi | Brigmedic icon made by PuroSlavKing (Github) | Zombie icon made by RamZ | Zookeper by netwy (discort) | Rev and Head Rev icon taken from https://tgstation13.org/wiki/HUD and edited by coolmankid12345 (Discord) | Mindshield icon taken from https://github.com/tgstation/tgstation/blob/master/icons/mob/huds/hud.dmi", + "copyright": "Taken from https://github.com/vgstation-coders/vgstation13/blob/e71d6c4fba5a51f99b81c295dcaec4fc2f58fb19/icons/mob/screen1.dmi | Brigmedic icon made by PuroSlavKing (Github) | Zombie icon made by RamZ | Zookeper by netwy (discort) | Rev and Head Rev icon taken from https://tgstation13.org/wiki/HUD and edited by coolmankid12345 (Discord) | Mindshield icon taken from https://github.com/tgstation/tgstation/blob/ce6beb8a4d61235d9a597a7126c407160ed674ea/icons/mob/huds/hud.dmi | Admin recolored from MedicalIntern by TsjipTsjip", "size": { "x": 8, @@ -177,6 +177,9 @@ [1.0,1.0] ] }, + { + "name": "StationAi" + }, { "name": "Syndicate" }, diff --git a/Resources/Textures/Interface/noise.rsi/meta.json b/Resources/Textures/Interface/noise.rsi/meta.json new file mode 100644 index 0000000000..068ecab968 --- /dev/null +++ b/Resources/Textures/Interface/noise.rsi/meta.json @@ -0,0 +1,58 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/blob/4b4e9dff1d7d891cfb75d25ca5bf5172d1c02be6/icons/hud/screen_gen.dmi", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "noise", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + } + ] +} \ No newline at end of file diff --git a/Resources/Textures/Interface/noise.rsi/noise.png b/Resources/Textures/Interface/noise.rsi/noise.png new file mode 100644 index 0000000000..ba74952b40 Binary files /dev/null and b/Resources/Textures/Interface/noise.rsi/noise.png differ diff --git a/Resources/Textures/Mobs/Silicon/output.rsi/ai-banned-unshaded.png b/Resources/Textures/Mobs/Silicon/output.rsi/ai-banned-unshaded.png new file mode 100644 index 0000000000..9b72b39492 Binary files /dev/null and b/Resources/Textures/Mobs/Silicon/output.rsi/ai-banned-unshaded.png differ diff --git a/Resources/Textures/Mobs/Silicon/output.rsi/ai-banned.png b/Resources/Textures/Mobs/Silicon/output.rsi/ai-banned.png new file mode 100644 index 0000000000..c583bb88f2 Binary files /dev/null and b/Resources/Textures/Mobs/Silicon/output.rsi/ai-banned.png differ diff --git a/Resources/Textures/Mobs/Silicon/output.rsi/ai-banned_dead-unshaded.png b/Resources/Textures/Mobs/Silicon/output.rsi/ai-banned_dead-unshaded.png new file mode 100644 index 0000000000..28bf165122 Binary files /dev/null and b/Resources/Textures/Mobs/Silicon/output.rsi/ai-banned_dead-unshaded.png differ diff --git a/Resources/Textures/Mobs/Silicon/output.rsi/ai-banned_dead.png b/Resources/Textures/Mobs/Silicon/output.rsi/ai-banned_dead.png new file mode 100644 index 0000000000..7c5b468885 Binary files /dev/null and b/Resources/Textures/Mobs/Silicon/output.rsi/ai-banned_dead.png differ diff --git a/Resources/Textures/Mobs/Silicon/output.rsi/ai-empty-unshaded.png b/Resources/Textures/Mobs/Silicon/output.rsi/ai-empty-unshaded.png new file mode 100644 index 0000000000..6539176b84 Binary files /dev/null and b/Resources/Textures/Mobs/Silicon/output.rsi/ai-empty-unshaded.png differ diff --git a/Resources/Textures/Mobs/Silicon/output.rsi/ai-empty.png b/Resources/Textures/Mobs/Silicon/output.rsi/ai-empty.png new file mode 100644 index 0000000000..eda1f4bfb5 Binary files /dev/null and b/Resources/Textures/Mobs/Silicon/output.rsi/ai-empty.png differ diff --git a/Resources/Textures/Mobs/Silicon/output.rsi/ai-holo-old-unshaded.png b/Resources/Textures/Mobs/Silicon/output.rsi/ai-holo-old-unshaded.png new file mode 100644 index 0000000000..63616e70b5 Binary files /dev/null and b/Resources/Textures/Mobs/Silicon/output.rsi/ai-holo-old-unshaded.png differ diff --git a/Resources/Textures/Mobs/Silicon/output.rsi/ai-holo-old.png b/Resources/Textures/Mobs/Silicon/output.rsi/ai-holo-old.png new file mode 100644 index 0000000000..3dc7a301d8 Binary files /dev/null and b/Resources/Textures/Mobs/Silicon/output.rsi/ai-holo-old.png differ diff --git a/Resources/Textures/Mobs/Silicon/output.rsi/ai-unshaded.png b/Resources/Textures/Mobs/Silicon/output.rsi/ai-unshaded.png new file mode 100644 index 0000000000..f3ba4b591a Binary files /dev/null and b/Resources/Textures/Mobs/Silicon/output.rsi/ai-unshaded.png differ diff --git a/Resources/Textures/Mobs/Silicon/output.rsi/ai.png b/Resources/Textures/Mobs/Silicon/output.rsi/ai.png new file mode 100644 index 0000000000..3c81e3a751 Binary files /dev/null and b/Resources/Textures/Mobs/Silicon/output.rsi/ai.png differ diff --git a/Resources/Textures/Mobs/Silicon/output.rsi/ai_dead-unshaded.png b/Resources/Textures/Mobs/Silicon/output.rsi/ai_dead-unshaded.png new file mode 100644 index 0000000000..96e953660f Binary files /dev/null and b/Resources/Textures/Mobs/Silicon/output.rsi/ai_dead-unshaded.png differ diff --git a/Resources/Textures/Mobs/Silicon/output.rsi/ai_dead.png b/Resources/Textures/Mobs/Silicon/output.rsi/ai_dead.png new file mode 100644 index 0000000000..96e953660f Binary files /dev/null and b/Resources/Textures/Mobs/Silicon/output.rsi/ai_dead.png differ diff --git a/Resources/Textures/Mobs/Silicon/output.rsi/default-unshaded.png b/Resources/Textures/Mobs/Silicon/output.rsi/default-unshaded.png new file mode 100644 index 0000000000..f14b4ef0fa Binary files /dev/null and b/Resources/Textures/Mobs/Silicon/output.rsi/default-unshaded.png differ diff --git a/Resources/Textures/Mobs/Silicon/output.rsi/default.png b/Resources/Textures/Mobs/Silicon/output.rsi/default.png new file mode 100644 index 0000000000..f14b4ef0fa Binary files /dev/null and b/Resources/Textures/Mobs/Silicon/output.rsi/default.png differ diff --git a/Resources/Textures/Mobs/Silicon/output.rsi/floating_face-unshaded.png b/Resources/Textures/Mobs/Silicon/output.rsi/floating_face-unshaded.png new file mode 100644 index 0000000000..05de742794 Binary files /dev/null and b/Resources/Textures/Mobs/Silicon/output.rsi/floating_face-unshaded.png differ diff --git a/Resources/Textures/Mobs/Silicon/output.rsi/floating_face.png b/Resources/Textures/Mobs/Silicon/output.rsi/floating_face.png new file mode 100644 index 0000000000..05de742794 Binary files /dev/null and b/Resources/Textures/Mobs/Silicon/output.rsi/floating_face.png differ diff --git a/Resources/Textures/Mobs/Silicon/output.rsi/horror-unshaded.png b/Resources/Textures/Mobs/Silicon/output.rsi/horror-unshaded.png new file mode 100644 index 0000000000..10efd5ee1d Binary files /dev/null and b/Resources/Textures/Mobs/Silicon/output.rsi/horror-unshaded.png differ diff --git a/Resources/Textures/Mobs/Silicon/output.rsi/horror.png b/Resources/Textures/Mobs/Silicon/output.rsi/horror.png new file mode 100644 index 0000000000..0a807c0c23 Binary files /dev/null and b/Resources/Textures/Mobs/Silicon/output.rsi/horror.png differ diff --git a/Resources/Textures/Mobs/Silicon/output.rsi/meta.json b/Resources/Textures/Mobs/Silicon/output.rsi/meta.json new file mode 100644 index 0000000000..a40ed37c60 --- /dev/null +++ b/Resources/Textures/Mobs/Silicon/output.rsi/meta.json @@ -0,0 +1 @@ +{"version": 1, "size": {"x": 32, "y": 32}, "license": "CC-BY-SA-3.0", "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/blob/2a19963297f91efb452dbb5c1d4eb28a14776b0a/icons/mob/silicon/ai.dmi", "states": [{"name": "ai", "directions": 1, "delays": [[0.2, 0.2, 0.2, 0.1, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.1]]}, {"name": "ai-banned", "directions": 1, "delays": [[0.7, 0.7, 0.7, 0.7, 0.7, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.7, 0.7, 0.7, 0.7, 0.7, 0.7, 0.7, 0.7, 0.7, 0.7]]}, {"name": "ai-banned-unshaded", "directions": 1, "delays": [[0.7, 0.7, 0.7, 0.7, 0.7, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.7, 0.7, 0.7, 0.7, 0.7, 0.7, 0.7, 0.7, 0.7, 0.7]]}, {"name": "ai-banned_dead", "directions": 1, "delays": [[0.7, 0.7]]}, {"name": "ai-banned_dead-unshaded", "directions": 1, "delays": [[0.7, 0.7]]}, {"name": "ai-empty", "directions": 1, "delays": [[0.7, 0.7]]}, {"name": "ai-empty-unshaded", "directions": 1, "delays": [[0.7, 0.7]]}, {"name": "ai-holo-old", "directions": 4, "delays": [[0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1], [0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1], [0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1], [0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1]]}, {"name": "ai-holo-old-unshaded", "directions": 4, "delays": [[0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1], [0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1], [0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1], [0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1]]}, {"name": "ai-unshaded", "directions": 1, "delays": [[0.2, 0.2, 0.2, 0.1, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.1]]}, {"name": "ai_dead", "directions": 1, "delays": [[1.0]]}, {"name": "ai_dead-unshaded", "directions": 1, "delays": [[1.0]]}, {"name": "default", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "default-unshaded", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "floating_face", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "floating_face-unshaded", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "horror", "directions": 1, "delays": [[0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1]]}, {"name": "horror-unshaded", "directions": 1, "delays": [[0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1]]}, {"name": "xeno_queen", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "xeno_queen-unshaded", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}]} \ No newline at end of file diff --git a/Resources/Textures/Mobs/Silicon/output.rsi/xeno_queen-unshaded.png b/Resources/Textures/Mobs/Silicon/output.rsi/xeno_queen-unshaded.png new file mode 100644 index 0000000000..3ea194039f Binary files /dev/null and b/Resources/Textures/Mobs/Silicon/output.rsi/xeno_queen-unshaded.png differ diff --git a/Resources/Textures/Mobs/Silicon/output.rsi/xeno_queen.png b/Resources/Textures/Mobs/Silicon/output.rsi/xeno_queen.png new file mode 100644 index 0000000000..3ea194039f Binary files /dev/null and b/Resources/Textures/Mobs/Silicon/output.rsi/xeno_queen.png differ diff --git a/Resources/Textures/Mobs/Silicon/station_ai.rsi/ai.png b/Resources/Textures/Mobs/Silicon/station_ai.rsi/ai.png new file mode 100644 index 0000000000..420a07c1f9 Binary files /dev/null and b/Resources/Textures/Mobs/Silicon/station_ai.rsi/ai.png differ diff --git a/Resources/Textures/Mobs/Silicon/station_ai.rsi/ai_dead.png b/Resources/Textures/Mobs/Silicon/station_ai.rsi/ai_dead.png new file mode 100644 index 0000000000..eb74655e02 Binary files /dev/null and b/Resources/Textures/Mobs/Silicon/station_ai.rsi/ai_dead.png differ diff --git a/Resources/Textures/Mobs/Silicon/station_ai.rsi/ai_empty.png b/Resources/Textures/Mobs/Silicon/station_ai.rsi/ai_empty.png new file mode 100644 index 0000000000..40e8ac5216 Binary files /dev/null and b/Resources/Textures/Mobs/Silicon/station_ai.rsi/ai_empty.png differ diff --git a/Resources/Textures/Mobs/Silicon/station_ai.rsi/base.png b/Resources/Textures/Mobs/Silicon/station_ai.rsi/base.png new file mode 100644 index 0000000000..a9db83691c Binary files /dev/null and b/Resources/Textures/Mobs/Silicon/station_ai.rsi/base.png differ diff --git a/Resources/Textures/Mobs/Silicon/station_ai.rsi/default.png b/Resources/Textures/Mobs/Silicon/station_ai.rsi/default.png new file mode 100644 index 0000000000..d52aceaf5b Binary files /dev/null and b/Resources/Textures/Mobs/Silicon/station_ai.rsi/default.png differ diff --git a/Resources/Textures/Mobs/Silicon/station_ai.rsi/meta.json b/Resources/Textures/Mobs/Silicon/station_ai.rsi/meta.json new file mode 100644 index 0000000000..a3da52233d --- /dev/null +++ b/Resources/Textures/Mobs/Silicon/station_ai.rsi/meta.json @@ -0,0 +1,52 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/blob/2a19963297f91efb452dbb5c1d4eb28a14776b0a/icons/mob/silicon/ai.dmi", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "ai", + "delays": [ + [ + 0.2, + 0.2, + 0.1, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.1 + ] + ] + }, + { + "name": "ai_dead" + }, + { + "name": "ai_empty", + "delays": [ + [ + 0.7, + 0.7 + ] + ] + }, + { + "name": "default", + "directions": 4 + }, + { + "name": "base" + } + ] +} \ No newline at end of file diff --git a/Resources/Textures/Objects/Devices/ai_card.rsi/base.png b/Resources/Textures/Objects/Devices/ai_card.rsi/base.png new file mode 100644 index 0000000000..244183c078 Binary files /dev/null and b/Resources/Textures/Objects/Devices/ai_card.rsi/base.png differ diff --git a/Resources/Textures/Objects/Devices/ai_card.rsi/empty.png b/Resources/Textures/Objects/Devices/ai_card.rsi/empty.png new file mode 100644 index 0000000000..7e61f368de Binary files /dev/null and b/Resources/Textures/Objects/Devices/ai_card.rsi/empty.png differ diff --git a/Resources/Textures/Objects/Devices/ai_card.rsi/full.png b/Resources/Textures/Objects/Devices/ai_card.rsi/full.png new file mode 100644 index 0000000000..59131c8c0a Binary files /dev/null and b/Resources/Textures/Objects/Devices/ai_card.rsi/full.png differ diff --git a/Resources/Textures/Objects/Devices/ai_card.rsi/inhand-left.png b/Resources/Textures/Objects/Devices/ai_card.rsi/inhand-left.png new file mode 100644 index 0000000000..2d3863145b Binary files /dev/null and b/Resources/Textures/Objects/Devices/ai_card.rsi/inhand-left.png differ diff --git a/Resources/Textures/Objects/Devices/ai_card.rsi/inhand-right.png b/Resources/Textures/Objects/Devices/ai_card.rsi/inhand-right.png new file mode 100644 index 0000000000..1704b9c3c1 Binary files /dev/null and b/Resources/Textures/Objects/Devices/ai_card.rsi/inhand-right.png differ diff --git a/Resources/Textures/Objects/Devices/ai_card.rsi/meta.json b/Resources/Textures/Objects/Devices/ai_card.rsi/meta.json new file mode 100644 index 0000000000..8b8135fa16 --- /dev/null +++ b/Resources/Textures/Objects/Devices/ai_card.rsi/meta.json @@ -0,0 +1,58 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/blob/1feffb747a33434a9d28450fc52ade75253aeba5/icons/obj/aicards.dmi", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "base" + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + }, + { + "name": "empty", + "delays": [ + [ + 0.4, + 0.4 + ] + ] + }, + { + "name": "full", + "delays": [ + [ + 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.4, + 0.4, + 0.4, + 0.4, + 0.4, + 0.4 + ] + ] + } + ] +} \ No newline at end of file diff --git a/Resources/Textures/Objects/Devices/output.rsi/aicard-full-unshaded.png b/Resources/Textures/Objects/Devices/output.rsi/aicard-full-unshaded.png new file mode 100644 index 0000000000..51a309d579 Binary files /dev/null and b/Resources/Textures/Objects/Devices/output.rsi/aicard-full-unshaded.png differ diff --git a/Resources/Textures/Objects/Devices/output.rsi/aicard-full.png b/Resources/Textures/Objects/Devices/output.rsi/aicard-full.png new file mode 100644 index 0000000000..03908b5284 Binary files /dev/null and b/Resources/Textures/Objects/Devices/output.rsi/aicard-full.png differ diff --git a/Resources/Textures/Objects/Devices/output.rsi/aicard-unshaded.png b/Resources/Textures/Objects/Devices/output.rsi/aicard-unshaded.png new file mode 100644 index 0000000000..6191a01ec4 Binary files /dev/null and b/Resources/Textures/Objects/Devices/output.rsi/aicard-unshaded.png differ diff --git a/Resources/Textures/Objects/Devices/output.rsi/aicard.png b/Resources/Textures/Objects/Devices/output.rsi/aicard.png new file mode 100644 index 0000000000..57f604efb0 Binary files /dev/null and b/Resources/Textures/Objects/Devices/output.rsi/aicard.png differ diff --git a/Resources/Textures/Objects/Devices/output.rsi/meta.json b/Resources/Textures/Objects/Devices/output.rsi/meta.json new file mode 100644 index 0000000000..500ecb8e3e --- /dev/null +++ b/Resources/Textures/Objects/Devices/output.rsi/meta.json @@ -0,0 +1 @@ +{"version": 1, "size": {"x": 32, "y": 32}, "license": "CC-BY-SA-3.0", "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/blob/1feffb747a33434a9d28450fc52ade75253aeba5/icons/obj/aicards.dmi", "states": [{"name": "aicard", "directions": 1, "delays": [[0.4, 0.4]]}, {"name": "aicard-full", "directions": 1, "delays": [[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.4, 0.4, 0.4, 0.4, 0.4, 0.4]]}, {"name": "aicard-full-unshaded", "directions": 1, "delays": [[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.4, 0.4, 0.4, 0.4, 0.4, 0.4]]}, {"name": "aicard-unshaded", "directions": 1, "delays": [[0.4, 0.4]]}]} \ No newline at end of file diff --git a/Resources/Textures/Shaders/nightvision.swsl b/Resources/Textures/Shaders/nightvision.swsl new file mode 100644 index 0000000000..8a3e7706ad --- /dev/null +++ b/Resources/Textures/Shaders/nightvision.swsl @@ -0,0 +1,38 @@ +light_mode unshaded; + +uniform sampler2D SCREEN_TEXTURE; +uniform highp vec3 tint; // Colour of the tint +uniform highp float luminance_threshold; // number between 0 and 1 +uniform highp float noise_amount; // number between 0 and 1 + +lowp float rand (lowp vec2 n) { + return 0.5 + 0.5 * fract (sin (dot (n.xy, vec2 (12.9898, 78.233)))* 43758.5453); +} + +void fragment() { + + highp vec4 color = zTextureSpec(SCREEN_TEXTURE, FRAGCOORD.xy * SCREEN_PIXEL_SIZE); + + // convert color to grayscale using luminance + highp float grey = dot(color.rgb, vec3(0.298, 0.5882, 0.1137)); + + // calculate local threshold + highp float threshold = grey * luminance_threshold; + + // amplify low luminance parts + if (grey < threshold) { + grey += (threshold - grey) * 0.5; + if (grey > 1.0) { + grey = 1.0; + } + } + + // apply night vision color tint + color.rgb = mix(color.rgb, tint, grey); + + // add some noise for realism + lowp float noise = rand(FRAGCOORD.xy + TIME) * noise_amount / 10.0; + color.rgb += noise; + + COLOR = color; +}