Station AI (#30944)
* Station AI overlay * implement * Bunch of ports * Fix a heap of bugs and basic scouting * helldivers * Shuffle interactions a bit * navmap stuff * Revert "navmap stuff" This reverts commit d1f89dd4be83233e22cf5dd062b2581f3c6da062. * AI wires implemented * Fix examines * Optimise the overlay significantly * Back to old static * BUI radial working * lots of work * Saving work * thanks fork * alright * pc * AI upload console * AI upload * stuff * Fix copy-paste shitcode * AI actions * navmap work * Fixes * first impressions * a * reh * Revert "navmap work" This reverts commit 6f63fea6e9245e189f368f97be3e32e9b210580e. # Conflicts: # Content.Client/Silicons/StationAi/StationAiOverlay.cs * OD * radar * weh * Fix examines * scoop mine eyes * fixes * reh * Optimise * Final round of optimisations * Fixes * fixes
This commit is contained in:
@@ -19,9 +19,6 @@ public sealed partial class EmotesMenu : RadialMenu
|
|||||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||||
[Dependency] private readonly ISharedPlayerManager _playerManager = default!;
|
[Dependency] private readonly ISharedPlayerManager _playerManager = default!;
|
||||||
|
|
||||||
private readonly SpriteSystem _spriteSystem;
|
|
||||||
private readonly EntityWhitelistSystem _whitelistSystem;
|
|
||||||
|
|
||||||
public event Action<ProtoId<EmotePrototype>>? OnPlayEmote;
|
public event Action<ProtoId<EmotePrototype>>? OnPlayEmote;
|
||||||
|
|
||||||
public EmotesMenu()
|
public EmotesMenu()
|
||||||
@@ -29,8 +26,8 @@ public sealed partial class EmotesMenu : RadialMenu
|
|||||||
IoCManager.InjectDependencies(this);
|
IoCManager.InjectDependencies(this);
|
||||||
RobustXamlLoader.Load(this);
|
RobustXamlLoader.Load(this);
|
||||||
|
|
||||||
_spriteSystem = _entManager.System<SpriteSystem>();
|
var spriteSystem = _entManager.System<SpriteSystem>();
|
||||||
_whitelistSystem = _entManager.System<EntityWhitelistSystem>();
|
var whitelistSystem = _entManager.System<EntityWhitelistSystem>();
|
||||||
|
|
||||||
var main = FindControl<RadialContainer>("Main");
|
var main = FindControl<RadialContainer>("Main");
|
||||||
|
|
||||||
@@ -40,8 +37,8 @@ public sealed partial class EmotesMenu : RadialMenu
|
|||||||
var player = _playerManager.LocalSession?.AttachedEntity;
|
var player = _playerManager.LocalSession?.AttachedEntity;
|
||||||
if (emote.Category == EmoteCategory.Invalid ||
|
if (emote.Category == EmoteCategory.Invalid ||
|
||||||
emote.ChatTriggers.Count == 0 ||
|
emote.ChatTriggers.Count == 0 ||
|
||||||
!(player.HasValue && _whitelistSystem.IsWhitelistPassOrNull(emote.Whitelist, player.Value)) ||
|
!(player.HasValue && whitelistSystem.IsWhitelistPassOrNull(emote.Whitelist, player.Value)) ||
|
||||||
_whitelistSystem.IsBlacklistPass(emote.Blacklist, player.Value))
|
whitelistSystem.IsBlacklistPass(emote.Blacklist, player.Value))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!emote.Available &&
|
if (!emote.Available &&
|
||||||
@@ -63,7 +60,7 @@ public sealed partial class EmotesMenu : RadialMenu
|
|||||||
{
|
{
|
||||||
VerticalAlignment = VAlignment.Center,
|
VerticalAlignment = VAlignment.Center,
|
||||||
HorizontalAlignment = HAlignment.Center,
|
HorizontalAlignment = HAlignment.Center,
|
||||||
Texture = _spriteSystem.Frame0(emote.Icon),
|
Texture = spriteSystem.Frame0(emote.Icon),
|
||||||
TextureScale = new Vector2(2f, 2f),
|
TextureScale = new Vector2(2f, 2f),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,24 @@
|
|||||||
<controls:FancyWindow xmlns="https://spacestation14.io"
|
<controls:FancyWindow xmlns="https://spacestation14.io"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||||
|
xmlns:graphics="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
|
||||||
SetSize="800 800"
|
SetSize="800 800"
|
||||||
MinSize="800 64">
|
MinSize="800 128">
|
||||||
|
<BoxContainer Orientation="Vertical" VerticalExpand="True">
|
||||||
|
<!--
|
||||||
|
<BoxContainer Name="RoleNameBox" Orientation="Vertical" Margin="10">
|
||||||
|
<Label Name="LoadoutNameLabel" Text="{Loc 'loadout-name-edit-label'}"/>
|
||||||
|
<PanelContainer HorizontalExpand="True" SetHeight="24">
|
||||||
|
<PanelContainer.PanelOverride>
|
||||||
|
<graphics:StyleBoxFlat BackgroundColor="#1B1B1E" />
|
||||||
|
</PanelContainer.PanelOverride>
|
||||||
|
<LineEdit Name="RoleNameEdit" ToolTip="{Loc 'loadout-name-edit-tooltip'}" VerticalExpand="True" HorizontalExpand="True"/>
|
||||||
|
</PanelContainer>
|
||||||
|
</BoxContainer>
|
||||||
|
-->
|
||||||
<VerticalTabContainer Name="LoadoutGroupsContainer"
|
<VerticalTabContainer Name="LoadoutGroupsContainer"
|
||||||
VerticalExpand="True"
|
VerticalExpand="True"
|
||||||
HorizontalExpand="True">
|
HorizontalExpand="True">
|
||||||
</VerticalTabContainer>
|
</VerticalTabContainer>
|
||||||
|
</BoxContainer>
|
||||||
</controls:FancyWindow>
|
</controls:FancyWindow>
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using System.Numerics;
|
||||||
using Content.Client.UserInterface.Controls;
|
using Content.Client.UserInterface.Controls;
|
||||||
using Content.Shared.Preferences;
|
using Content.Shared.Preferences;
|
||||||
using Content.Shared.Preferences.Loadouts;
|
using Content.Shared.Preferences.Loadouts;
|
||||||
@@ -5,6 +6,7 @@ using Robust.Client.AutoGenerated;
|
|||||||
using Robust.Client.UserInterface.XAML;
|
using Robust.Client.UserInterface.XAML;
|
||||||
using Robust.Shared.Player;
|
using Robust.Shared.Player;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Random;
|
||||||
|
|
||||||
namespace Content.Client.Lobby.UI.Loadouts;
|
namespace Content.Client.Lobby.UI.Loadouts;
|
||||||
|
|
||||||
@@ -24,6 +26,14 @@ public sealed partial class LoadoutWindow : FancyWindow
|
|||||||
Profile = profile;
|
Profile = profile;
|
||||||
var protoManager = collection.Resolve<IPrototypeManager>();
|
var protoManager = collection.Resolve<IPrototypeManager>();
|
||||||
|
|
||||||
|
// Hide if no groups
|
||||||
|
if (proto.Groups.Count == 0)
|
||||||
|
{
|
||||||
|
LoadoutGroupsContainer.Visible = false;
|
||||||
|
SetSize = Vector2.Zero;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
foreach (var group in proto.Groups)
|
foreach (var group in proto.Groups)
|
||||||
{
|
{
|
||||||
if (!protoManager.TryIndex(group, out var groupProto))
|
if (!protoManager.TryIndex(group, out var groupProto))
|
||||||
@@ -47,6 +57,7 @@ public sealed partial class LoadoutWindow : FancyWindow
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void RefreshLoadouts(RoleLoadout loadout, ICommonSession session, IDependencyCollection collection)
|
public void RefreshLoadouts(RoleLoadout loadout, ICommonSession session, IDependencyCollection collection)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ namespace Content.Client.Silicons.Laws.SiliconLawEditUi;
|
|||||||
|
|
||||||
public sealed class SiliconLawEui : BaseEui
|
public sealed class SiliconLawEui : BaseEui
|
||||||
{
|
{
|
||||||
public readonly EntityManager _entityManager = default!;
|
private readonly EntityManager _entityManager;
|
||||||
|
|
||||||
private SiliconLawUi _siliconLawUi;
|
private SiliconLawUi _siliconLawUi;
|
||||||
private EntityUid _target;
|
private EntityUid _target;
|
||||||
|
|||||||
@@ -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<StationAiMenu>();
|
||||||
|
_menu.Track(Owner);
|
||||||
|
|
||||||
|
_menu.OnAiRadial += args =>
|
||||||
|
{
|
||||||
|
SendPredictedMessage(new StationAiRadialMessage()
|
||||||
|
{
|
||||||
|
Event = args,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
13
Content.Client/Silicons/StationAi/StationAiMenu.xaml
Normal file
13
Content.Client/Silicons/StationAi/StationAiMenu.xaml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<ui:RadialMenu xmlns="https://spacestation14.io"
|
||||||
|
xmlns:ui="clr-namespace:Content.Client.UserInterface.Controls"
|
||||||
|
BackButtonStyleClass="RadialMenuBackButton"
|
||||||
|
CloseButtonStyleClass="RadialMenuCloseButton"
|
||||||
|
VerticalExpand="True"
|
||||||
|
HorizontalExpand="True"
|
||||||
|
MinSize="450 450">
|
||||||
|
|
||||||
|
<!-- Main -->
|
||||||
|
<ui:RadialContainer Name="Main" VerticalExpand="True" HorizontalExpand="True" Radius="64" ReserveSpaceForHiddenChildren="False">
|
||||||
|
</ui:RadialContainer>
|
||||||
|
|
||||||
|
</ui:RadialMenu>
|
||||||
128
Content.Client/Silicons/StationAi/StationAiMenu.xaml.cs
Normal file
128
Content.Client/Silicons/StationAi/StationAiMenu.xaml.cs
Normal file
@@ -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<BaseStationAiAction>? 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<RadialContainer>("Main");
|
||||||
|
main.DisposeAllChildren();
|
||||||
|
var sprites = _entManager.System<SpriteSystem>();
|
||||||
|
|
||||||
|
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<SpriteSystem>().GetSpriteScreenCoordinates((_tracked, null, xform));
|
||||||
|
|
||||||
|
if (!coords.IsValid)
|
||||||
|
{
|
||||||
|
Close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
OpenScreenAt(coords.Position, _clyde);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class StationAiMenuButton(BaseStationAiAction action) : RadialMenuTextureButton
|
||||||
|
{
|
||||||
|
public BaseStationAiAction Action = action;
|
||||||
|
}
|
||||||
@@ -4,7 +4,9 @@ using Robust.Client.Graphics;
|
|||||||
using Robust.Client.Player;
|
using Robust.Client.Player;
|
||||||
using Robust.Shared.Enums;
|
using Robust.Shared.Enums;
|
||||||
using Robust.Shared.Map.Components;
|
using Robust.Shared.Map.Components;
|
||||||
|
using Robust.Shared.Physics;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Timing;
|
||||||
|
|
||||||
namespace Content.Client.Silicons.StationAi;
|
namespace Content.Client.Silicons.StationAi;
|
||||||
|
|
||||||
@@ -12,6 +14,7 @@ public sealed class StationAiOverlay : Overlay
|
|||||||
{
|
{
|
||||||
[Dependency] private readonly IClyde _clyde = default!;
|
[Dependency] private readonly IClyde _clyde = default!;
|
||||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||||
|
[Dependency] private readonly IGameTiming _timing = default!;
|
||||||
[Dependency] private readonly IPlayerManager _player = default!;
|
[Dependency] private readonly IPlayerManager _player = default!;
|
||||||
[Dependency] private readonly IPrototypeManager _proto = default!;
|
[Dependency] private readonly IPrototypeManager _proto = default!;
|
||||||
|
|
||||||
@@ -22,6 +25,9 @@ public sealed class StationAiOverlay : Overlay
|
|||||||
private IRenderTexture? _staticTexture;
|
private IRenderTexture? _staticTexture;
|
||||||
private IRenderTexture? _stencilTexture;
|
private IRenderTexture? _stencilTexture;
|
||||||
|
|
||||||
|
private float _updateRate = 1f / 30f;
|
||||||
|
private float _accumulator;
|
||||||
|
|
||||||
public StationAiOverlay()
|
public StationAiOverlay()
|
||||||
{
|
{
|
||||||
IoCManager.InjectDependencies(this);
|
IoCManager.InjectDependencies(this);
|
||||||
@@ -47,19 +53,22 @@ public sealed class StationAiOverlay : Overlay
|
|||||||
_entManager.TryGetComponent(playerEnt, out TransformComponent? playerXform);
|
_entManager.TryGetComponent(playerEnt, out TransformComponent? playerXform);
|
||||||
var gridUid = playerXform?.GridUid ?? EntityUid.Invalid;
|
var gridUid = playerXform?.GridUid ?? EntityUid.Invalid;
|
||||||
_entManager.TryGetComponent(gridUid, out MapGridComponent? grid);
|
_entManager.TryGetComponent(gridUid, out MapGridComponent? grid);
|
||||||
|
_entManager.TryGetComponent(gridUid, out BroadphaseComponent? broadphase);
|
||||||
|
|
||||||
var invMatrix = args.Viewport.GetWorldToLocalMatrix();
|
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<EntityLookupSystem>();
|
var lookups = _entManager.System<EntityLookupSystem>();
|
||||||
var xforms = _entManager.System<SharedTransformSystem>();
|
var xforms = _entManager.System<SharedTransformSystem>();
|
||||||
_entManager.System<StationAiVisionSystem>().GetView((gridUid, grid), worldBounds, _visibleTiles);
|
|
||||||
|
if (_accumulator <= 0f)
|
||||||
|
{
|
||||||
|
_accumulator = MathF.Max(0f, _accumulator + _updateRate);
|
||||||
|
_visibleTiles.Clear();
|
||||||
|
_entManager.System<StationAiVisionSystem>().GetView((gridUid, broadphase, grid), worldBounds, _visibleTiles);
|
||||||
|
}
|
||||||
|
|
||||||
var gridMatrix = xforms.GetWorldMatrix(gridUid);
|
var gridMatrix = xforms.GetWorldMatrix(gridUid);
|
||||||
var matty = Matrix3x2.Multiply(gridMatrix, invMatrix);
|
var matty = Matrix3x2.Multiply(gridMatrix, invMatrix);
|
||||||
|
|||||||
30
Content.Client/Silicons/StationAi/StationAiSystem.Airlock.cs
Normal file
30
Content.Client/Silicons/StationAi/StationAiSystem.Airlock.cs
Normal file
@@ -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<DoorBoltComponent, GetStationAiRadialEvent>(OnDoorBoltGetRadial);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDoorBoltGetRadial(Entity<DoorBoltComponent> 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,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
32
Content.Client/Silicons/StationAi/StationAiSystem.Light.cs
Normal file
32
Content.Client/Silicons/StationAi/StationAiSystem.Light.cs
Normal file
@@ -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<ItemTogglePointLightComponent, GetStationAiRadialEvent>(OnLightGetRadial);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnLightGetRadial(Entity<ItemTogglePointLightComponent> 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
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,7 +5,7 @@ using Robust.Shared.Player;
|
|||||||
|
|
||||||
namespace Content.Client.Silicons.StationAi;
|
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 IOverlayManager _overlayMgr = default!;
|
||||||
[Dependency] private readonly IPlayerManager _player = default!;
|
[Dependency] private readonly IPlayerManager _player = default!;
|
||||||
@@ -15,8 +15,8 @@ public sealed partial class StationAiSystem : EntitySystem
|
|||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
// InitializeAirlock();
|
InitializeAirlock();
|
||||||
// InitializePowerToggle();
|
InitializePowerToggle();
|
||||||
|
|
||||||
SubscribeLocalEvent<StationAiOverlayComponent, LocalPlayerAttachedEvent>(OnAiAttached);
|
SubscribeLocalEvent<StationAiOverlayComponent, LocalPlayerAttachedEvent>(OnAiAttached);
|
||||||
SubscribeLocalEvent<StationAiOverlayComponent, LocalPlayerDetachedEvent>(OnAiDetached);
|
SubscribeLocalEvent<StationAiOverlayComponent, LocalPlayerDetachedEvent>(OnAiDetached);
|
||||||
|
|||||||
@@ -78,6 +78,7 @@ namespace Content.Client.Verbs
|
|||||||
|
|
||||||
// Get entities
|
// Get entities
|
||||||
List<EntityUid> entities;
|
List<EntityUid> entities;
|
||||||
|
var examineFlags = LookupFlags.All & ~LookupFlags.Sensors;
|
||||||
|
|
||||||
// Do we have to do FoV checks?
|
// Do we have to do FoV checks?
|
||||||
if ((visibility & MenuVisibility.NoFov) == 0)
|
if ((visibility & MenuVisibility.NoFov) == 0)
|
||||||
@@ -88,7 +89,7 @@ namespace Content.Client.Verbs
|
|||||||
TryComp(player.Value, out ExaminerComponent? examiner);
|
TryComp(player.Value, out ExaminerComponent? examiner);
|
||||||
|
|
||||||
entities = new();
|
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))
|
if (_examine.CanExamine(player.Value, targetPos, Predicate, ent, examiner))
|
||||||
entities.Add(ent);
|
entities.Add(ent);
|
||||||
@@ -96,7 +97,7 @@ namespace Content.Client.Verbs
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
entities = _entityLookup.GetEntitiesInRange(targetPos, EntityMenuLookupSize).ToList();
|
entities = _entityLookup.GetEntitiesInRange(targetPos, EntityMenuLookupSize, flags: examineFlags).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entities.Count == 0)
|
if (entities.Count == 0)
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ using Robust.Shared.Utility;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using Content.Server.Silicons.Laws;
|
using Content.Server.Silicons.Laws;
|
||||||
|
using Content.Shared.Silicons.Laws;
|
||||||
using Content.Shared.Silicons.Laws.Components;
|
using Content.Shared.Silicons.Laws.Components;
|
||||||
using Robust.Server.Player;
|
using Robust.Server.Player;
|
||||||
using Robust.Shared.Physics.Components;
|
using Robust.Shared.Physics.Components;
|
||||||
|
|||||||
@@ -183,10 +183,6 @@ namespace Content.Server.Communications
|
|||||||
|
|
||||||
private bool CanUse(EntityUid user, EntityUid console)
|
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<AccessReaderComponent>(console, out var accessReaderComponent) && !HasComp<EmaggedComponent>(console))
|
if (TryComp<AccessReaderComponent>(console, out var accessReaderComponent) && !HasComp<EmaggedComponent>(console))
|
||||||
{
|
{
|
||||||
return _accessReaderSystem.IsAllowed(user, console, accessReaderComponent);
|
return _accessReaderSystem.IsAllowed(user, console, accessReaderComponent);
|
||||||
|
|||||||
@@ -220,7 +220,9 @@ namespace Content.Server.Database
|
|||||||
|
|
||||||
foreach (var role in profile.Loadouts)
|
foreach (var role in profile.Loadouts)
|
||||||
{
|
{
|
||||||
var loadout = new RoleLoadout(role.RoleName);
|
var loadout = new RoleLoadout(role.RoleName)
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
foreach (var group in role.Groups)
|
foreach (var group in role.Groups)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
using Content.Server.DeviceNetwork.Components;
|
using Content.Server.DeviceNetwork.Components;
|
||||||
using Content.Server.Power.Components;
|
using Content.Server.Power.Components;
|
||||||
using Content.Server.Power.EntitySystems;
|
using Content.Server.Power.EntitySystems;
|
||||||
using Content.Shared.Power.EntitySystems;
|
|
||||||
|
|
||||||
namespace Content.Server.DeviceNetwork.Systems;
|
namespace Content.Server.DeviceNetwork.Systems;
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ using Content.Server.Light.Components;
|
|||||||
using Content.Server.Power.Components;
|
using Content.Server.Power.Components;
|
||||||
using Content.Server.Power.EntitySystems;
|
using Content.Server.Power.EntitySystems;
|
||||||
using Content.Shared.Power;
|
using Content.Shared.Power;
|
||||||
using Content.Shared.Power.Components;
|
|
||||||
|
|
||||||
namespace Content.Server.Light.EntitySystems
|
namespace Content.Server.Light.EntitySystems
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ using Robust.Shared.Audio.Systems;
|
|||||||
using Content.Shared.Damage.Systems;
|
using Content.Shared.Damage.Systems;
|
||||||
using Content.Shared.Damage.Components;
|
using Content.Shared.Damage.Components;
|
||||||
using Content.Shared.Power;
|
using Content.Shared.Power;
|
||||||
using Content.Shared.Power.Components;
|
|
||||||
|
|
||||||
namespace Content.Server.Light.EntitySystems
|
namespace Content.Server.Light.EntitySystems
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -341,13 +341,13 @@ public sealed class MindSystem : SharedMindSystem
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ControlMob(EntityUid user, EntityUid target)
|
public override void ControlMob(EntityUid user, EntityUid target)
|
||||||
{
|
{
|
||||||
if (TryComp(user, out ActorComponent? actor))
|
if (TryComp(user, out ActorComponent? actor))
|
||||||
ControlMob(actor.PlayerSession.UserId, target);
|
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);
|
var (mindId, mind) = GetOrCreateMind(user);
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ using Content.Shared.Emp;
|
|||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Robust.Shared.Containers;
|
using Robust.Shared.Containers;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using Content.Shared.Power.Components;
|
|
||||||
using Content.Shared.Storage.Components;
|
using Content.Shared.Storage.Components;
|
||||||
using Robust.Server.Containers;
|
using Robust.Server.Containers;
|
||||||
using Content.Shared.Whitelist;
|
using Content.Shared.Whitelist;
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ using Content.Server.Power.NodeGroups;
|
|||||||
using Content.Server.Power.Pow3r;
|
using Content.Server.Power.Pow3r;
|
||||||
using Content.Shared.CCVar;
|
using Content.Shared.CCVar;
|
||||||
using Content.Shared.Power;
|
using Content.Shared.Power;
|
||||||
using Content.Shared.Power.Components;
|
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Robust.Server.GameObjects;
|
using Robust.Server.GameObjects;
|
||||||
using Robust.Shared.Configuration;
|
using Robust.Shared.Configuration;
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ using Content.Shared.Atmos;
|
|||||||
using Content.Shared.DeviceNetwork;
|
using Content.Shared.DeviceNetwork;
|
||||||
using Content.Shared.Examine;
|
using Content.Shared.Examine;
|
||||||
using Content.Shared.Power;
|
using Content.Shared.Power;
|
||||||
using Content.Shared.Power.Components;
|
|
||||||
using Content.Shared.Power.Generation.Teg;
|
using Content.Shared.Power.Generation.Teg;
|
||||||
using Content.Shared.Rounding;
|
using Content.Shared.Rounding;
|
||||||
using Robust.Server.GameObjects;
|
using Robust.Server.GameObjects;
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ using Content.Server.NodeContainer.Nodes;
|
|||||||
using Content.Server.Power.Components;
|
using Content.Server.Power.Components;
|
||||||
using Content.Shared.Atmos;
|
using Content.Shared.Atmos;
|
||||||
using Content.Shared.Power;
|
using Content.Shared.Power;
|
||||||
using Content.Shared.Power.Components;
|
|
||||||
|
|
||||||
namespace Content.Server.Power.Generator;
|
namespace Content.Server.Power.Generator;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Content.Server.NPC.Pathfinding;
|
using Content.Server.NPC.Pathfinding;
|
||||||
using Content.Shared.Maps;
|
using Content.Shared.Maps;
|
||||||
|
using Content.Shared.NPC;
|
||||||
using Content.Shared.Procedural;
|
using Content.Shared.Procedural;
|
||||||
using Content.Shared.Procedural.DungeonGenerators;
|
using Content.Shared.Procedural.DungeonGenerators;
|
||||||
using Robust.Shared.Collections;
|
using Robust.Shared.Collections;
|
||||||
@@ -29,7 +30,7 @@ public sealed partial class DungeonJob
|
|||||||
var pathfinder = _entManager.System<PathfindingSystem>();
|
var pathfinder = _entManager.System<PathfindingSystem>();
|
||||||
|
|
||||||
// Gridcast
|
// Gridcast
|
||||||
pathfinder.GridCast(startTile, position, tile =>
|
SharedPathfindingSystem.GridCast(startTile, position, tile =>
|
||||||
{
|
{
|
||||||
if (!_maps.TryGetTileRef(_gridUid, _grid, tile, out var tileRef) ||
|
if (!_maps.TryGetTileRef(_gridUid, _grid, tile, out var tileRef) ||
|
||||||
tileRef.Tile.IsSpace(_tileDefManager))
|
tileRef.Tile.IsSpace(_tileDefManager))
|
||||||
|
|||||||
@@ -5,12 +5,10 @@ using Content.Server.GameTicking;
|
|||||||
using Content.Server.Radio.Components;
|
using Content.Server.Radio.Components;
|
||||||
using Content.Server.Roles;
|
using Content.Server.Roles;
|
||||||
using Content.Server.Station.Systems;
|
using Content.Server.Station.Systems;
|
||||||
using Content.Shared.Actions;
|
|
||||||
using Content.Shared.Administration;
|
using Content.Shared.Administration;
|
||||||
using Content.Shared.Chat;
|
using Content.Shared.Chat;
|
||||||
using Content.Shared.Emag.Components;
|
using Content.Shared.Emag.Components;
|
||||||
using Content.Shared.Emag.Systems;
|
using Content.Shared.Emag.Systems;
|
||||||
using Content.Shared.Examine;
|
|
||||||
using Content.Shared.Mind;
|
using Content.Shared.Mind;
|
||||||
using Content.Shared.Mind.Components;
|
using Content.Shared.Mind.Components;
|
||||||
using Content.Shared.Roles;
|
using Content.Shared.Roles;
|
||||||
@@ -19,10 +17,10 @@ using Content.Shared.Silicons.Laws.Components;
|
|||||||
using Content.Shared.Stunnable;
|
using Content.Shared.Stunnable;
|
||||||
using Content.Shared.Wires;
|
using Content.Shared.Wires;
|
||||||
using Robust.Server.GameObjects;
|
using Robust.Server.GameObjects;
|
||||||
|
using Robust.Shared.Containers;
|
||||||
using Robust.Shared.Player;
|
using Robust.Shared.Player;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
using Robust.Shared.Toolshed;
|
using Robust.Shared.Toolshed;
|
||||||
using Robust.Shared.Utility;
|
|
||||||
|
|
||||||
namespace Content.Server.Silicons.Laws;
|
namespace Content.Server.Silicons.Laws;
|
||||||
|
|
||||||
@@ -32,11 +30,9 @@ public sealed class SiliconLawSystem : SharedSiliconLawSystem
|
|||||||
[Dependency] private readonly IChatManager _chatManager = default!;
|
[Dependency] private readonly IChatManager _chatManager = default!;
|
||||||
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||||
[Dependency] private readonly SharedMindSystem _mind = default!;
|
[Dependency] private readonly SharedMindSystem _mind = default!;
|
||||||
[Dependency] private readonly SharedActionsSystem _actions = default!;
|
|
||||||
[Dependency] private readonly StationSystem _station = default!;
|
[Dependency] private readonly StationSystem _station = default!;
|
||||||
[Dependency] private readonly UserInterfaceSystem _userInterface = default!;
|
[Dependency] private readonly UserInterfaceSystem _userInterface = default!;
|
||||||
[Dependency] private readonly SharedStunSystem _stunSystem = default!;
|
[Dependency] private readonly SharedStunSystem _stunSystem = default!;
|
||||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
|
||||||
[Dependency] private readonly SharedRoleSystem _roles = default!;
|
[Dependency] private readonly SharedRoleSystem _roles = default!;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
@@ -44,7 +40,6 @@ public sealed class SiliconLawSystem : SharedSiliconLawSystem
|
|||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
|
||||||
SubscribeLocalEvent<SiliconLawBoundComponent, ComponentShutdown>(OnComponentShutdown);
|
|
||||||
SubscribeLocalEvent<SiliconLawBoundComponent, MapInitEvent>(OnMapInit);
|
SubscribeLocalEvent<SiliconLawBoundComponent, MapInitEvent>(OnMapInit);
|
||||||
SubscribeLocalEvent<SiliconLawBoundComponent, MindAddedMessage>(OnMindAdded);
|
SubscribeLocalEvent<SiliconLawBoundComponent, MindAddedMessage>(OnMindAdded);
|
||||||
SubscribeLocalEvent<SiliconLawBoundComponent, ToggleLawsScreenEvent>(OnToggleLawsScreen);
|
SubscribeLocalEvent<SiliconLawBoundComponent, ToggleLawsScreenEvent>(OnToggleLawsScreen);
|
||||||
@@ -58,15 +53,8 @@ public sealed class SiliconLawSystem : SharedSiliconLawSystem
|
|||||||
SubscribeLocalEvent<EmagSiliconLawComponent, MindRemovedMessage>(OnEmagMindRemoved);
|
SubscribeLocalEvent<EmagSiliconLawComponent, MindRemovedMessage>(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)
|
private void OnMapInit(EntityUid uid, SiliconLawBoundComponent component, MapInitEvent args)
|
||||||
{
|
{
|
||||||
_actions.AddAction(uid, ref component.ViewLawsActionEntity, component.ViewLawsAction);
|
|
||||||
GetLaws(uid, component);
|
GetLaws(uid, component);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,7 +80,7 @@ public sealed class SiliconLawSystem : SharedSiliconLawSystem
|
|||||||
|
|
||||||
private void OnBoundUIOpened(EntityUid uid, SiliconLawBoundComponent component, BoundUIOpenedEvent args)
|
private void OnBoundUIOpened(EntityUid uid, SiliconLawBoundComponent component, BoundUIOpenedEvent args)
|
||||||
{
|
{
|
||||||
_entityManager.TryGetComponent<IntrinsicRadioTransmitterComponent>(uid, out var intrinsicRadio);
|
TryComp(uid, out IntrinsicRadioTransmitterComponent? intrinsicRadio);
|
||||||
var radioChannels = intrinsicRadio?.Channels;
|
var radioChannels = intrinsicRadio?.Channels;
|
||||||
|
|
||||||
var state = new SiliconLawBuiState(GetLaws(uid).Laws, radioChannels);
|
var state = new SiliconLawBuiState(GetLaws(uid).Laws, radioChannels);
|
||||||
@@ -264,9 +252,9 @@ public sealed class SiliconLawSystem : SharedSiliconLawSystem
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Extract all the laws from a lawset's prototype ids.
|
/// Extract all the laws from a lawset's prototype ids.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public SiliconLawset GetLawset(string lawset)
|
public SiliconLawset GetLawset(ProtoId<SiliconLawsetPrototype> lawset)
|
||||||
{
|
{
|
||||||
var proto = _prototype.Index<SiliconLawsetPrototype>(lawset);
|
var proto = _prototype.Index(lawset);
|
||||||
var laws = new SiliconLawset()
|
var laws = new SiliconLawset()
|
||||||
{
|
{
|
||||||
Laws = new List<SiliconLaw>(proto.Laws.Count)
|
Laws = new List<SiliconLaw>(proto.Laws.Count)
|
||||||
@@ -294,6 +282,21 @@ public sealed class SiliconLawSystem : SharedSiliconLawSystem
|
|||||||
component.Lawset.Laws = newLaws;
|
component.Lawset.Laws = newLaws;
|
||||||
NotifyLawsChanged(target);
|
NotifyLawsChanged(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void OnUpdaterInsert(Entity<SiliconLawUpdaterComponent> 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)]
|
[ToolshedCommand, AdminCommand(AdminFlags.Admin)]
|
||||||
|
|||||||
37
Content.Server/Silicons/StationAi/AiInteractWireAction.cs
Normal file
37
Content.Server/Silicons/StationAi/AiInteractWireAction.cs
Normal file
@@ -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;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Controls whether an AI can interact with the target entity.
|
||||||
|
/// </summary>
|
||||||
|
public sealed partial class AiInteractWireAction : ComponentWireAction<StationAiWhitelistComponent>
|
||||||
|
{
|
||||||
|
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<SharedStationAiSystem>()
|
||||||
|
.SetWhitelistEnabled((component.Owner, component), false, announce: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Mend(EntityUid user, Wire wire, StationAiWhitelistComponent component)
|
||||||
|
{
|
||||||
|
return EntityManager.System<SharedStationAiSystem>()
|
||||||
|
.SetWhitelistEnabled((component.Owner, component), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Pulse(EntityUid user, Wire wire, StationAiWhitelistComponent component)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
40
Content.Server/Silicons/StationAi/AiVisionWireAction.cs
Normal file
40
Content.Server/Silicons/StationAi/AiVisionWireAction.cs
Normal file
@@ -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;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles StationAiVision functionality for the attached entity.
|
||||||
|
/// </summary>
|
||||||
|
public sealed partial class AiVisionWireAction : ComponentWireAction<StationAiVisionComponent>
|
||||||
|
{
|
||||||
|
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<SharedStationAiSystem>()
|
||||||
|
.SetVisionEnabled((component.Owner, component), false, announce: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Mend(EntityUid user, Wire wire, StationAiVisionComponent component)
|
||||||
|
{
|
||||||
|
return EntityManager.System<SharedStationAiSystem>()
|
||||||
|
.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.
|
||||||
|
}
|
||||||
|
}
|
||||||
76
Content.Server/Silicons/StationAi/StationAiSystem.cs
Normal file
76
Content.Server/Silicons/StationAi/StationAiSystem.cs
Normal file
@@ -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<Entity<StationAiCoreComponent>> _ais = new();
|
||||||
|
|
||||||
|
public override bool SetVisionEnabled(Entity<StationAiVisionComponent> 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<StationAiWhitelistComponent> 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.
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
using Content.Server.Power.Components;
|
using Content.Server.Power.Components;
|
||||||
using Content.Server.Power.EntitySystems;
|
using Content.Server.Power.EntitySystems;
|
||||||
using Content.Shared.Power;
|
using Content.Shared.Power;
|
||||||
using Content.Shared.Power.Components;
|
|
||||||
using Content.Shared.Sound;
|
using Content.Shared.Sound;
|
||||||
using Content.Shared.Sound.Components;
|
using Content.Shared.Sound.Components;
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ using Content.Shared.Humanoid.Prototypes;
|
|||||||
using Content.Shared.PDA;
|
using Content.Shared.PDA;
|
||||||
using Content.Shared.Preferences;
|
using Content.Shared.Preferences;
|
||||||
using Content.Shared.Preferences.Loadouts;
|
using Content.Shared.Preferences.Loadouts;
|
||||||
|
using Content.Shared.Preferences.Loadouts.Effects;
|
||||||
using Content.Shared.Random;
|
using Content.Shared.Random;
|
||||||
using Content.Shared.Random.Helpers;
|
using Content.Shared.Random.Helpers;
|
||||||
using Content.Shared.Roles;
|
using Content.Shared.Roles;
|
||||||
@@ -150,6 +151,22 @@ public sealed class StationSpawningSystem : SharedStationSpawningSystem
|
|||||||
EntityUid? entity = null)
|
EntityUid? entity = null)
|
||||||
{
|
{
|
||||||
_prototypeManager.TryIndex(job?.Prototype ?? string.Empty, out var prototype);
|
_prototypeManager.TryIndex(job?.Prototype ?? string.Empty, out var prototype);
|
||||||
|
RoleLoadout? loadout = null;
|
||||||
|
|
||||||
|
// Need to get the loadout up-front to handle names if we use an entity spawn override.
|
||||||
|
var jobLoadout = LoadoutSystem.GetJobPrototype(prototype?.ID);
|
||||||
|
|
||||||
|
if (_prototypeManager.TryIndex(jobLoadout, out RoleLoadoutPrototype? roleProto))
|
||||||
|
{
|
||||||
|
profile?.Loadouts.TryGetValue(jobLoadout, out loadout);
|
||||||
|
|
||||||
|
// Set to default if not present
|
||||||
|
if (loadout == null)
|
||||||
|
{
|
||||||
|
loadout = new RoleLoadout(jobLoadout);
|
||||||
|
loadout.SetDefault(profile, _actors.GetSession(entity), _prototypeManager);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If we're not spawning a humanoid, we're gonna exit early without doing all the humanoid stuff.
|
// If we're not spawning a humanoid, we're gonna exit early without doing all the humanoid stuff.
|
||||||
if (prototype?.JobEntity != null)
|
if (prototype?.JobEntity != null)
|
||||||
@@ -157,6 +174,13 @@ public sealed class StationSpawningSystem : SharedStationSpawningSystem
|
|||||||
DebugTools.Assert(entity is null);
|
DebugTools.Assert(entity is null);
|
||||||
var jobEntity = EntityManager.SpawnEntity(prototype.JobEntity, coordinates);
|
var jobEntity = EntityManager.SpawnEntity(prototype.JobEntity, coordinates);
|
||||||
MakeSentientCommand.MakeSentient(jobEntity, EntityManager);
|
MakeSentientCommand.MakeSentient(jobEntity, EntityManager);
|
||||||
|
|
||||||
|
// Make sure custom names get handled, what is gameticker control flow whoopy.
|
||||||
|
if (loadout != null)
|
||||||
|
{
|
||||||
|
EquipRoleName(jobEntity, loadout, roleProto!);
|
||||||
|
}
|
||||||
|
|
||||||
DoJobSpecials(job, jobEntity);
|
DoJobSpecials(job, jobEntity);
|
||||||
_identity.QueueIdentityUpdate(jobEntity);
|
_identity.QueueIdentityUpdate(jobEntity);
|
||||||
return jobEntity;
|
return jobEntity;
|
||||||
@@ -188,21 +212,9 @@ public sealed class StationSpawningSystem : SharedStationSpawningSystem
|
|||||||
profile = HumanoidCharacterProfile.RandomWithSpecies(speciesId);
|
profile = HumanoidCharacterProfile.RandomWithSpecies(speciesId);
|
||||||
}
|
}
|
||||||
|
|
||||||
var jobLoadout = LoadoutSystem.GetJobPrototype(prototype?.ID);
|
if (loadout != null)
|
||||||
|
|
||||||
if (_prototypeManager.TryIndex(jobLoadout, out RoleLoadoutPrototype? roleProto))
|
|
||||||
{
|
{
|
||||||
RoleLoadout? loadout = null;
|
EquipRoleLoadout(entity.Value, loadout, roleProto!);
|
||||||
profile?.Loadouts.TryGetValue(jobLoadout, out loadout);
|
|
||||||
|
|
||||||
// Set to default if not present
|
|
||||||
if (loadout == null)
|
|
||||||
{
|
|
||||||
loadout = new RoleLoadout(jobLoadout);
|
|
||||||
loadout.SetDefault(profile, _actors.GetSession(entity), _prototypeManager);
|
|
||||||
}
|
|
||||||
|
|
||||||
EquipRoleLoadout(entity.Value, loadout, roleProto);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prototype?.StartingGear != null)
|
if (prototype?.StartingGear != null)
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ public sealed class EnergySwordSystem : EntitySystem
|
|||||||
if (args.Handled)
|
if (args.Handled)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!_toolSystem.HasQuality(args.Used, "Pulsing"))
|
if (!_toolSystem.HasQuality(args.Used, SharedToolSystem.PulseQuality))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
args.Handled = true;
|
args.Handled = true;
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ public sealed class ArtifactElectricityTriggerSystem : EntitySystem
|
|||||||
if (args.Handled)
|
if (args.Handled)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!_toolSystem.HasQuality(args.Used, "Pulsing"))
|
if (!_toolSystem.HasQuality(args.Used, SharedToolSystem.PulseQuality))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
args.Handled = _artifactSystem.TryActivateArtifact(uid, args.User);
|
args.Handled = _artifactSystem.TryActivateArtifact(uid, args.User);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ using Content.Shared.Body.Events;
|
|||||||
using Content.Shared.Emoting;
|
using Content.Shared.Emoting;
|
||||||
using Content.Shared.Hands;
|
using Content.Shared.Hands;
|
||||||
using Content.Shared.Interaction;
|
using Content.Shared.Interaction;
|
||||||
|
using Content.Shared.Interaction.Components;
|
||||||
using Content.Shared.Interaction.Events;
|
using Content.Shared.Interaction.Events;
|
||||||
using Content.Shared.Item;
|
using Content.Shared.Item;
|
||||||
using Content.Shared.Movement.Components;
|
using Content.Shared.Movement.Components;
|
||||||
@@ -22,9 +23,14 @@ namespace Content.Shared.ActionBlocker
|
|||||||
{
|
{
|
||||||
[Dependency] private readonly SharedContainerSystem _container = default!;
|
[Dependency] private readonly SharedContainerSystem _container = default!;
|
||||||
|
|
||||||
|
private EntityQuery<ComplexInteractionComponent> _complexInteractionQuery;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
|
||||||
|
_complexInteractionQuery = GetEntityQuery<ComplexInteractionComponent>();
|
||||||
|
|
||||||
SubscribeLocalEvent<InputMoverComponent, ComponentStartup>(OnMoverStartup);
|
SubscribeLocalEvent<InputMoverComponent, ComponentStartup>(OnMoverStartup);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,6 +59,15 @@ namespace Content.Shared.ActionBlocker
|
|||||||
return !ev.Cancelled;
|
return !ev.Cancelled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 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.
|
||||||
|
/// </summary>
|
||||||
|
public bool CanComplexInteract(EntityUid user)
|
||||||
|
{
|
||||||
|
return _complexInteractionQuery.HasComp(user);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Raises an event directed at both the user and the target entity to check whether a user is capable of
|
/// Raises an event directed at both the user and the target entity to check whether a user is capable of
|
||||||
/// interacting with this entity.
|
/// interacting with this entity.
|
||||||
|
|||||||
@@ -358,23 +358,15 @@ public sealed partial class ClimbSystem : VirtualController
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (args.OurFixture.Contacts.Count > 1)
|
|
||||||
{
|
|
||||||
foreach (var contact in args.OurFixture.Contacts.Values)
|
foreach (var contact in args.OurFixture.Contacts.Values)
|
||||||
{
|
{
|
||||||
if (!contact.IsTouching)
|
if (!contact.IsTouching)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var otherEnt = contact.EntityA;
|
var otherEnt = contact.OtherEnt(uid);
|
||||||
var otherFixture = contact.FixtureA;
|
var (otherFixtureId, otherFixture) = contact.OtherFixture(uid);
|
||||||
var otherFixtureId = contact.FixtureAId;
|
|
||||||
if (uid == contact.EntityA)
|
|
||||||
{
|
|
||||||
otherEnt = contact.EntityB;
|
|
||||||
otherFixture = contact.FixtureB;
|
|
||||||
otherFixtureId = contact.FixtureBId;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// TODO: Remove this on engine.
|
||||||
if (args.OtherEntity == otherEnt && args.OtherFixtureId == otherFixtureId)
|
if (args.OtherEntity == otherEnt && args.OtherFixtureId == otherFixtureId)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@@ -384,8 +376,8 @@ public sealed partial class ClimbSystem : VirtualController
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
// TODO: Is this even needed anymore?
|
||||||
foreach (var otherFixture in args.OurFixture.Contacts.Keys)
|
foreach (var otherFixture in args.OurFixture.Contacts.Keys)
|
||||||
{
|
{
|
||||||
// If it's the other fixture then ignore em
|
// If it's the other fixture then ignore em
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using Content.Shared.Tools;
|
using Content.Shared.Tools;
|
||||||
|
using Content.Shared.Tools.Systems;
|
||||||
using Robust.Shared.GameStates;
|
using Robust.Shared.GameStates;
|
||||||
using Robust.Shared.Serialization;
|
using Robust.Shared.Serialization;
|
||||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||||
@@ -13,7 +14,7 @@ namespace Content.Shared.Configurable
|
|||||||
public Dictionary<string, string?> Config = new();
|
public Dictionary<string, string?> Config = new();
|
||||||
|
|
||||||
[DataField("qualityNeeded", customTypeSerializer: typeof(PrototypeIdSerializer<ToolQualityPrototype>))]
|
[DataField("qualityNeeded", customTypeSerializer: typeof(PrototypeIdSerializer<ToolQualityPrototype>))]
|
||||||
public string QualityNeeded = "Pulsing";
|
public string QualityNeeded = SharedToolSystem.PulseQuality;
|
||||||
|
|
||||||
[DataField("validation")]
|
[DataField("validation")]
|
||||||
public Regex Validation = new("^[a-zA-Z0-9 ]*$", RegexOptions.Compiled);
|
public Regex Validation = new("^[a-zA-Z0-9 ]*$", RegexOptions.Compiled);
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ namespace Content.Shared.Doors
|
|||||||
PowerIndicator,
|
PowerIndicator,
|
||||||
BoltIndicator,
|
BoltIndicator,
|
||||||
BoltLightIndicator,
|
BoltLightIndicator,
|
||||||
AIControlIndicator,
|
AiControlIndicator,
|
||||||
TimingIndicator,
|
TimingIndicator,
|
||||||
SafetyIndicator,
|
SafetyIndicator,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -117,13 +117,26 @@ namespace Content.Shared.Examine
|
|||||||
if (EntityManager.GetComponent<TransformComponent>(examiner).MapID != target.MapId)
|
if (EntityManager.GetComponent<TransformComponent>(examiner).MapID != target.MapId)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
// Do target InRangeUnoccluded which has different checks.
|
||||||
|
if (examined != null)
|
||||||
|
{
|
||||||
return InRangeUnOccluded(
|
return InRangeUnOccluded(
|
||||||
_transform.GetMapCoordinates(examiner),
|
examiner,
|
||||||
|
examined.Value,
|
||||||
|
GetExaminerRange(examiner),
|
||||||
|
predicate: predicate,
|
||||||
|
ignoreInsideBlocker: true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return InRangeUnOccluded(
|
||||||
|
examiner,
|
||||||
target,
|
target,
|
||||||
GetExaminerRange(examiner),
|
GetExaminerRange(examiner),
|
||||||
predicate: predicate,
|
predicate: predicate,
|
||||||
ignoreInsideBlocker: true);
|
ignoreInsideBlocker: true);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Check if a given examiner is incapacitated. If yes, return a reduced examine range. Otherwise, return the deault range.
|
/// Check if a given examiner is incapacitated. If yes, return a reduced examine range. Otherwise, return the deault range.
|
||||||
@@ -214,6 +227,14 @@ namespace Content.Shared.Examine
|
|||||||
|
|
||||||
public bool InRangeUnOccluded(EntityUid origin, EntityUid other, float range = ExamineRange, Ignored? predicate = null, bool ignoreInsideBlocker = true)
|
public bool InRangeUnOccluded(EntityUid origin, EntityUid other, float range = ExamineRange, Ignored? predicate = null, bool ignoreInsideBlocker = true)
|
||||||
{
|
{
|
||||||
|
var ev = new InRangeOverrideEvent(origin, other);
|
||||||
|
RaiseLocalEvent(origin, ref ev);
|
||||||
|
|
||||||
|
if (ev.Handled)
|
||||||
|
{
|
||||||
|
return ev.InRange;
|
||||||
|
}
|
||||||
|
|
||||||
var originPos = _transform.GetMapCoordinates(origin);
|
var originPos = _transform.GetMapCoordinates(origin);
|
||||||
var otherPos = _transform.GetMapCoordinates(other);
|
var otherPos = _transform.GetMapCoordinates(other);
|
||||||
|
|
||||||
|
|||||||
@@ -161,6 +161,19 @@ public abstract partial class SharedHandsSystem
|
|||||||
return item != null;
|
return item != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets active hand item if relevant otherwise gets the entity itself.
|
||||||
|
/// </summary>
|
||||||
|
public EntityUid GetActiveItemOrSelf(Entity<HandsComponent?> entity)
|
||||||
|
{
|
||||||
|
if (!TryGetActiveItem(entity, out var item))
|
||||||
|
{
|
||||||
|
return entity.Owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
return item.Value;
|
||||||
|
}
|
||||||
|
|
||||||
public Hand? GetActiveHand(Entity<HandsComponent?> entity)
|
public Hand? GetActiveHand(Entity<HandsComponent?> entity)
|
||||||
{
|
{
|
||||||
if (!Resolve(entity, ref entity.Comp))
|
if (!Resolve(entity, ref entity.Comp))
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ using Content.Shared.Movement.Components;
|
|||||||
using Content.Shared.Movement.Pulling.Systems;
|
using Content.Shared.Movement.Pulling.Systems;
|
||||||
using Content.Shared.Physics;
|
using Content.Shared.Physics;
|
||||||
using Content.Shared.Popups;
|
using Content.Shared.Popups;
|
||||||
|
using Content.Shared.Silicons.StationAi;
|
||||||
using Content.Shared.Storage;
|
using Content.Shared.Storage;
|
||||||
using Content.Shared.Tag;
|
using Content.Shared.Tag;
|
||||||
using Content.Shared.Timing;
|
using Content.Shared.Timing;
|
||||||
@@ -74,7 +75,6 @@ namespace Content.Shared.Interaction
|
|||||||
private EntityQuery<WallMountComponent> _wallMountQuery;
|
private EntityQuery<WallMountComponent> _wallMountQuery;
|
||||||
private EntityQuery<UseDelayComponent> _delayQuery;
|
private EntityQuery<UseDelayComponent> _delayQuery;
|
||||||
private EntityQuery<ActivatableUIComponent> _uiQuery;
|
private EntityQuery<ActivatableUIComponent> _uiQuery;
|
||||||
private EntityQuery<ComplexInteractionComponent> _complexInteractionQuery;
|
|
||||||
|
|
||||||
private const CollisionGroup InRangeUnobstructedMask = CollisionGroup.Impassable | CollisionGroup.InteractImpassable;
|
private const CollisionGroup InRangeUnobstructedMask = CollisionGroup.Impassable | CollisionGroup.InteractImpassable;
|
||||||
|
|
||||||
@@ -97,7 +97,6 @@ namespace Content.Shared.Interaction
|
|||||||
_wallMountQuery = GetEntityQuery<WallMountComponent>();
|
_wallMountQuery = GetEntityQuery<WallMountComponent>();
|
||||||
_delayQuery = GetEntityQuery<UseDelayComponent>();
|
_delayQuery = GetEntityQuery<UseDelayComponent>();
|
||||||
_uiQuery = GetEntityQuery<ActivatableUIComponent>();
|
_uiQuery = GetEntityQuery<ActivatableUIComponent>();
|
||||||
_complexInteractionQuery = GetEntityQuery<ComplexInteractionComponent>();
|
|
||||||
|
|
||||||
SubscribeLocalEvent<BoundUserInterfaceCheckRangeEvent>(HandleUserInterfaceRangeCheck);
|
SubscribeLocalEvent<BoundUserInterfaceCheckRangeEvent>(HandleUserInterfaceRangeCheck);
|
||||||
SubscribeLocalEvent<BoundUserInterfaceMessageAttempt>(OnBoundInterfaceInteractAttempt);
|
SubscribeLocalEvent<BoundUserInterfaceMessageAttempt>(OnBoundInterfaceInteractAttempt);
|
||||||
@@ -165,7 +164,7 @@ namespace Content.Shared.Interaction
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (uiComp.RequireHands && !_handsQuery.HasComp(ev.Actor))
|
if (uiComp.RequiresComplex && !_actionBlockerSystem.CanComplexInteract(ev.Actor))
|
||||||
ev.Cancel();
|
ev.Cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -440,7 +439,7 @@ namespace Content.Shared.Interaction
|
|||||||
|
|
||||||
public void InteractHand(EntityUid user, EntityUid target)
|
public void InteractHand(EntityUid user, EntityUid target)
|
||||||
{
|
{
|
||||||
var complexInteractions = SupportsComplexInteractions(user);
|
var complexInteractions = _actionBlockerSystem.CanComplexInteract(user);
|
||||||
if (!complexInteractions)
|
if (!complexInteractions)
|
||||||
{
|
{
|
||||||
InteractionActivate(user,
|
InteractionActivate(user,
|
||||||
@@ -630,6 +629,14 @@ namespace Content.Shared.Interaction
|
|||||||
if (!Resolve(other, ref other.Comp))
|
if (!Resolve(other, ref other.Comp))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
var ev = new InRangeOverrideEvent(origin, other);
|
||||||
|
RaiseLocalEvent(origin, ref ev);
|
||||||
|
|
||||||
|
if (ev.Handled)
|
||||||
|
{
|
||||||
|
return ev.InRange;
|
||||||
|
}
|
||||||
|
|
||||||
return InRangeUnobstructed(origin,
|
return InRangeUnobstructed(origin,
|
||||||
other,
|
other,
|
||||||
other.Comp.Coordinates,
|
other.Comp.Coordinates,
|
||||||
@@ -1128,7 +1135,7 @@ namespace Content.Shared.Interaction
|
|||||||
// Get list of alt-interact verbs
|
// Get list of alt-interact verbs
|
||||||
var verbs = _verbSystem.GetLocalVerbs(target, user, typeof(AlternativeVerb));
|
var verbs = _verbSystem.GetLocalVerbs(target, user, typeof(AlternativeVerb));
|
||||||
|
|
||||||
if (!verbs.Any())
|
if (verbs.Count == 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
_verbSystem.ExecuteVerb(verbs.First(), user, target);
|
_verbSystem.ExecuteVerb(verbs.First(), user, target);
|
||||||
@@ -1182,6 +1189,13 @@ namespace Content.Shared.Interaction
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsAccessible(Entity<TransformComponent?> user, Entity<TransformComponent?> target)
|
public bool IsAccessible(Entity<TransformComponent?> user, Entity<TransformComponent?> 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))
|
if (_containerSystem.IsInSameOrParentContainer(user, target, out _, out var container))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
@@ -1324,13 +1338,10 @@ namespace Content.Shared.Interaction
|
|||||||
return ev.Handled;
|
return ev.Handled;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
[Obsolete("Use ActionBlockerSystem")]
|
||||||
/// 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.
|
|
||||||
/// </summary>
|
|
||||||
public bool SupportsComplexInteractions(EntityUid user)
|
public bool SupportsComplexInteractions(EntityUid user)
|
||||||
{
|
{
|
||||||
return _complexInteractionQuery.HasComp(user);
|
return _actionBlockerSystem.CanComplexInteract(user);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1368,13 +1379,6 @@ namespace Content.Shared.Interaction
|
|||||||
public bool Handled => Used != null;
|
public bool Handled => Used != null;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Raised directed by-ref on an item and a user to determine if interactions can occur.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="Cancelled">Whether the hand interaction should be cancelled.</param>
|
|
||||||
[ByRefEvent]
|
|
||||||
public record struct AttemptUseInteractEvent(EntityUid User, EntityUid Used, bool Cancelled = false);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Raised directed by-ref on an item to determine if hand interactions should go through.
|
/// 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.
|
/// Defaults to allowing hand interactions to go through. Cancel to force the item to be attacked instead.
|
||||||
@@ -1382,4 +1386,32 @@ namespace Content.Shared.Interaction
|
|||||||
/// <param name="Cancelled">Whether the hand interaction should be cancelled.</param>
|
/// <param name="Cancelled">Whether the hand interaction should be cancelled.</param>
|
||||||
[ByRefEvent]
|
[ByRefEvent]
|
||||||
public record struct CombatModeShouldHandInteractEvent(bool Cancelled = false);
|
public record struct CombatModeShouldHandInteractEvent(bool Cancelled = false);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Override event raised directed on the user to say the target is accessible.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="User"></param>
|
||||||
|
/// <param name="Target"></param>
|
||||||
|
[ByRefEvent]
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Override event raised directed on a user to check InRangeUnoccluded AND InRangeUnobstructed to the target if you require custom logic.
|
||||||
|
/// </summary>
|
||||||
|
[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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
using Robust.Shared.GameStates;
|
||||||
|
|
||||||
|
namespace Content.Shared.Light.Components;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Can activate <see cref="LightOnCollideComponent"/> when collided with.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, NetworkedComponent]
|
||||||
|
public sealed partial class LightOnCollideColliderComponent : Component
|
||||||
|
{
|
||||||
|
[DataField]
|
||||||
|
public string FixtureId = "lightTrigger";
|
||||||
|
}
|
||||||
11
Content.Shared/Light/Components/LightOnCollideComponent.cs
Normal file
11
Content.Shared/Light/Components/LightOnCollideComponent.cs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
using Robust.Shared.GameStates;
|
||||||
|
|
||||||
|
namespace Content.Shared.Light.Components;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Enables / disables pointlight whenever entities are contacting with it
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, NetworkedComponent]
|
||||||
|
public sealed partial class LightOnCollideComponent : Component
|
||||||
|
{
|
||||||
|
}
|
||||||
82
Content.Shared/Light/EntitySystems/LightCollideSystem.cs
Normal file
82
Content.Shared/Light/EntitySystems/LightCollideSystem.cs
Normal file
@@ -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<LightOnCollideColliderComponent, PreventCollideEvent>(OnPreventCollide);
|
||||||
|
SubscribeLocalEvent<LightOnCollideColliderComponent, StartCollideEvent>(OnStart);
|
||||||
|
SubscribeLocalEvent<LightOnCollideColliderComponent, EndCollideEvent>(OnEnd);
|
||||||
|
|
||||||
|
SubscribeLocalEvent<LightOnCollideColliderComponent, ComponentShutdown>(OnCollideShutdown);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnCollideShutdown(Entity<LightOnCollideColliderComponent> 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<LightOnCollideComponent>(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<LightOnCollideColliderComponent> ent, ref PreventCollideEvent args)
|
||||||
|
{
|
||||||
|
if (!HasComp<LightOnCollideComponent>(args.OtherEntity))
|
||||||
|
{
|
||||||
|
args.Cancelled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnEnd(Entity<LightOnCollideColliderComponent> ent, ref EndCollideEvent args)
|
||||||
|
{
|
||||||
|
if (args.OurFixtureId != ent.Comp.FixtureId)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!HasComp<LightOnCollideComponent>(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<LightOnCollideColliderComponent> ent, ref StartCollideEvent args)
|
||||||
|
{
|
||||||
|
if (args.OurFixtureId != ent.Comp.FixtureId)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!HasComp<LightOnCollideComponent>(args.OtherEntity))
|
||||||
|
return;
|
||||||
|
|
||||||
|
_lights.SetEnabled(args.OtherEntity, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
using Content.Shared.Light.Components;
|
using Content.Shared.Light.Components;
|
||||||
using Content.Shared.Power;
|
using Content.Shared.Power;
|
||||||
using Content.Shared.Power.Components;
|
|
||||||
using Content.Shared.Power.EntitySystems;
|
using Content.Shared.Power.EntitySystems;
|
||||||
|
|
||||||
namespace Content.Shared.Light.EntitySystems;
|
namespace Content.Shared.Light.EntitySystems;
|
||||||
|
|||||||
@@ -315,6 +315,10 @@ public abstract class SharedMindSystem : EntitySystem
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public virtual void ControlMob(EntityUid user, EntityUid target) {}
|
||||||
|
|
||||||
|
public virtual void ControlMob(NetUserId user, EntityUid target) {}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tries to create and add an objective from its prototype id.
|
/// Tries to create and add an objective from its prototype id.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
namespace Content.Server.NPC.Pathfinding;
|
namespace Content.Shared.NPC;
|
||||||
|
|
||||||
public sealed partial class PathfindingSystem
|
public abstract partial class SharedPathfindingSystem
|
||||||
{
|
{
|
||||||
public void GridCast(Vector2i start, Vector2i end, Vector2iCallback callback)
|
public static void GridCast(Vector2i start, Vector2i end, Vector2iCallback callback)
|
||||||
{
|
{
|
||||||
// https://gist.github.com/Pyr3z/46884d67641094d6cf353358566db566
|
// https://gist.github.com/Pyr3z/46884d67641094d6cf353358566db566
|
||||||
// declare all locals at the top so it's obvious how big the footprint is
|
// declare all locals at the top so it's obvious how big the footprint is
|
||||||
@@ -2,7 +2,7 @@ using System.Numerics;
|
|||||||
|
|
||||||
namespace Content.Shared.NPC;
|
namespace Content.Shared.NPC;
|
||||||
|
|
||||||
public abstract class SharedPathfindingSystem : EntitySystem
|
public abstract partial class SharedPathfindingSystem : EntitySystem
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This is equivalent to agent radii for navmeshes. In our case it's preferable that things are cleanly
|
/// This is equivalent to agent radii for navmeshes. In our case it's preferable that things are cleanly
|
||||||
@@ -37,4 +37,31 @@ public abstract class SharedPathfindingSystem : EntitySystem
|
|||||||
var ab = Vector2.Abs(diff);
|
var ab = Vector2.Abs(diff);
|
||||||
return ab.X + ab.Y + (1.41f - 2) * Math.Min(ab.X, ab.Y);
|
return ab.X + ab.Y + (1.41f - 2) * Math.Min(ab.X, ab.Y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<Vector2i> GetTileOutline(Vector2i center, float radius)
|
||||||
|
{
|
||||||
|
// https://www.redblobgames.com/grids/circle-drawing/
|
||||||
|
var vecCircle = center + Vector2.One / 2f;
|
||||||
|
|
||||||
|
for (var r = 0; r <= Math.Floor(radius * MathF.Sqrt(0.5f)); r++)
|
||||||
|
{
|
||||||
|
var d = MathF.Floor(MathF.Sqrt(radius * radius - r * r));
|
||||||
|
|
||||||
|
yield return new Vector2(vecCircle.X - d, vecCircle.Y + r).Floored();
|
||||||
|
|
||||||
|
yield return new Vector2(vecCircle.X + d, vecCircle.Y + r).Floored();
|
||||||
|
|
||||||
|
yield return new Vector2(vecCircle.X - d, vecCircle.Y - r).Floored();
|
||||||
|
|
||||||
|
yield return new Vector2(vecCircle.X + d, vecCircle.Y - r).Floored();
|
||||||
|
|
||||||
|
yield return new Vector2(vecCircle.X + r, vecCircle.Y - d).Floored();
|
||||||
|
|
||||||
|
yield return new Vector2(vecCircle.X + r, vecCircle.Y + d).Floored();
|
||||||
|
|
||||||
|
yield return new Vector2(vecCircle.X - r, vecCircle.Y - d).Floored();
|
||||||
|
|
||||||
|
yield return new Vector2(vecCircle.X - r, vecCircle.Y + d).Floored();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using Content.Shared.Dataset;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
namespace Content.Shared.Preferences.Loadouts;
|
namespace Content.Shared.Preferences.Loadouts;
|
||||||
@@ -15,10 +16,17 @@ public sealed partial class RoleLoadoutPrototype : IPrototype
|
|||||||
[IdDataField]
|
[IdDataField]
|
||||||
public string ID { get; } = string.Empty;
|
public string ID { get; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Should we use a random name for this loadout?
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public ProtoId<DatasetPrototype>? NameDataset;
|
||||||
|
|
||||||
|
// Not required so people can set their names.
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Groups that comprise this role loadout.
|
/// Groups that comprise this role loadout.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField(required: true)]
|
[DataField]
|
||||||
public List<ProtoId<LoadoutGroupPrototype>> Groups = new();
|
public List<ProtoId<LoadoutGroupPrototype>> Groups = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Content.Shared.Actions;
|
using Content.Shared.Actions;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
using Robust.Shared.Serialization;
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
@@ -7,21 +8,9 @@ namespace Content.Shared.Silicons.Laws.Components;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// This is used for entities which are bound to silicon laws and can view them.
|
/// This is used for entities which are bound to silicon laws and can view them.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[RegisterComponent, Access(typeof(SharedSiliconLawSystem))]
|
[RegisterComponent, NetworkedComponent, Access(typeof(SharedSiliconLawSystem))]
|
||||||
public sealed partial class SiliconLawBoundComponent : Component
|
public sealed partial class SiliconLawBoundComponent : Component
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// The sidebar action that toggles the laws screen.
|
|
||||||
/// </summary>
|
|
||||||
[DataField]
|
|
||||||
public EntProtoId ViewLawsAction = "ActionViewLaws";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The action for toggling laws. Stored here so we can remove it later.
|
|
||||||
/// </summary>
|
|
||||||
[DataField]
|
|
||||||
public EntityUid? ViewLawsActionEntity;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The last entity that provided laws to this entity.
|
/// The last entity that provided laws to this entity.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
using Robust.Shared.GameStates;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
|
namespace Content.Shared.Silicons.Laws.Components;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whenever an entity is inserted with silicon laws it will update the relevant entity's laws.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, NetworkedComponent]
|
||||||
|
public sealed partial class SiliconLawUpdaterComponent : Component
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Entities to update
|
||||||
|
/// </summary>
|
||||||
|
[DataField(required: true)]
|
||||||
|
public ComponentRegistry Components;
|
||||||
|
}
|
||||||
@@ -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<SiliconLawUpdaterComponent, EntInsertedIntoContainerMessage>(OnUpdaterInsert);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void OnUpdaterInsert(Entity<SiliconLawUpdaterComponent> ent, ref EntInsertedIntoContainerMessage args)
|
||||||
|
{
|
||||||
|
// TODO: Prediction
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,13 +8,14 @@ namespace Content.Shared.Silicons.Laws;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// This handles getting and displaying the laws for silicons.
|
/// This handles getting and displaying the laws for silicons.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class SharedSiliconLawSystem : EntitySystem
|
public abstract partial class SharedSiliconLawSystem : EntitySystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
|
InitializeUpdater();
|
||||||
SubscribeLocalEvent<EmagSiliconLawComponent, GotEmaggedEvent>(OnGotEmagged);
|
SubscribeLocalEvent<EmagSiliconLawComponent, GotEmaggedEvent>(OnGotEmagged);
|
||||||
SubscribeLocalEvent<EmagSiliconLawComponent, OnAttemptEmagEvent>(OnAttemptEmag);
|
SubscribeLocalEvent<EmagSiliconLawComponent, OnAttemptEmagEvent>(OnAttemptEmag);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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<DoorBoltComponent, StationAiBoltEvent>(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;
|
||||||
|
}
|
||||||
187
Content.Shared/Silicons/StationAi/SharedStationAiSystem.Held.cs
Normal file
187
Content.Shared/Silicons/StationAi/SharedStationAiSystem.Held.cs
Normal file
@@ -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<StationAiRadialMessage>(OnRadialMessage);
|
||||||
|
SubscribeLocalEvent<BoundUserInterfaceMessageAttempt>(OnMessageAttempt);
|
||||||
|
SubscribeLocalEvent<StationAiWhitelistComponent, GetVerbsEvent<AlternativeVerb>>(OnTargetVerbs);
|
||||||
|
|
||||||
|
SubscribeLocalEvent<StationAiHeldComponent, InteractionAttemptEvent>(OnHeldInteraction);
|
||||||
|
SubscribeLocalEvent<StationAiHeldComponent, AttemptRelayActionComponentChangeEvent>(OnHeldRelay);
|
||||||
|
SubscribeLocalEvent<StationAiHeldComponent, JumpToCoreEvent>(OnCoreJump);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnCoreJump(Entity<StationAiHeldComponent> ent, ref JumpToCoreEvent args)
|
||||||
|
{
|
||||||
|
if (!TryGetCore(ent.Owner, out var core) || core.Comp?.RemoteEntity == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_xforms.DropNextTo(core.Comp.RemoteEntity.Value, core.Owner) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tries to get the entity held in the AI core.
|
||||||
|
/// </summary>
|
||||||
|
private bool TryGetHeld(Entity<StationAiCoreComponent?> 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<StationAiCoreComponent?> 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<StationAiHeldComponent> 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<StationAiWhitelistComponent>(ev.Target)))
|
||||||
|
{
|
||||||
|
ev.Cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnHeldInteraction(Entity<StationAiHeldComponent> 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<StationAiWhitelistComponent> ent, ref GetVerbsEvent<AlternativeVerb> args)
|
||||||
|
{
|
||||||
|
if (!args.CanComplexInteract ||
|
||||||
|
!ent.Comp.Enabled ||
|
||||||
|
!HasComp<StationAiHeldComponent>(args.User) ||
|
||||||
|
!HasComp<StationAiWhitelistComponent>(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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raised from client to server as a BUI message wrapping the event to perform.
|
||||||
|
/// Also handles AI action validation.
|
||||||
|
/// </summary>
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed class StationAiRadialMessage : BoundUserInterfaceMessage
|
||||||
|
{
|
||||||
|
public BaseStationAiAction Event = default!;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do nothing on server just here for shared move along.
|
||||||
|
/// <summary>
|
||||||
|
/// Raised on client to get the relevant data for radial actions.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class StationAiRadial : BaseStationAiAction
|
||||||
|
{
|
||||||
|
public SpriteSpecifier? Sprite;
|
||||||
|
|
||||||
|
public string? Tooltip;
|
||||||
|
|
||||||
|
public BaseStationAiAction Event = default!;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Abstract parent for radial actions events.
|
||||||
|
/// When a client requests a radial action this will get sent.
|
||||||
|
/// </summary>
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public abstract class BaseStationAiAction
|
||||||
|
{
|
||||||
|
[field:NonSerialized]
|
||||||
|
public EntityUid User { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// No idea if there's a better way to do this.
|
||||||
|
/// <summary>
|
||||||
|
/// Grab actions possible for an AI on the target entity.
|
||||||
|
/// </summary>
|
||||||
|
[ByRefEvent]
|
||||||
|
public record struct GetStationAiRadialEvent()
|
||||||
|
{
|
||||||
|
public List<StationAiRadial> Actions = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public enum AiUi : byte
|
||||||
|
{
|
||||||
|
Key,
|
||||||
|
}
|
||||||
@@ -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<ItemTogglePointLightComponent, StationAiLightEvent>(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;
|
||||||
|
}
|
||||||
412
Content.Shared/Silicons/StationAi/SharedStationAiSystem.cs
Normal file
412
Content.Shared/Silicons/StationAi/SharedStationAiSystem.cs
Normal file
@@ -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<BroadphaseComponent> _broadphaseQuery;
|
||||||
|
private EntityQuery<MapGridComponent> _gridQuery;
|
||||||
|
|
||||||
|
[ValidatePrototypeId<EntityPrototype>]
|
||||||
|
private static readonly EntProtoId DefaultAi = "StationAiBrain";
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
_broadphaseQuery = GetEntityQuery<BroadphaseComponent>();
|
||||||
|
_gridQuery = GetEntityQuery<MapGridComponent>();
|
||||||
|
|
||||||
|
InitializeAirlock();
|
||||||
|
InitializeHeld();
|
||||||
|
InitializeLight();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<StationAiWhitelistComponent, BoundUserInterfaceCheckRangeEvent>(OnAiBuiCheck);
|
||||||
|
|
||||||
|
SubscribeLocalEvent<StationAiOverlayComponent, AccessibleOverrideEvent>(OnAiAccessible);
|
||||||
|
SubscribeLocalEvent<StationAiOverlayComponent, InRangeOverrideEvent>(OnAiInRange);
|
||||||
|
SubscribeLocalEvent<StationAiOverlayComponent, MenuVisibilityEvent>(OnAiMenu);
|
||||||
|
|
||||||
|
SubscribeLocalEvent<StationAiHolderComponent, ComponentInit>(OnHolderInit);
|
||||||
|
SubscribeLocalEvent<StationAiHolderComponent, ComponentRemove>(OnHolderRemove);
|
||||||
|
SubscribeLocalEvent<StationAiHolderComponent, AfterInteractEvent>(OnHolderInteract);
|
||||||
|
SubscribeLocalEvent<StationAiHolderComponent, MapInitEvent>(OnHolderMapInit);
|
||||||
|
SubscribeLocalEvent<StationAiHolderComponent, EntInsertedIntoContainerMessage>(OnHolderConInsert);
|
||||||
|
SubscribeLocalEvent<StationAiHolderComponent, EntRemovedFromContainerMessage>(OnHolderConRemove);
|
||||||
|
|
||||||
|
SubscribeLocalEvent<StationAiCoreComponent, EntInsertedIntoContainerMessage>(OnAiInsert);
|
||||||
|
SubscribeLocalEvent<StationAiCoreComponent, EntRemovedFromContainerMessage>(OnAiRemove);
|
||||||
|
SubscribeLocalEvent<StationAiCoreComponent, MapInitEvent>(OnAiMapInit);
|
||||||
|
SubscribeLocalEvent<StationAiCoreComponent, ComponentShutdown>(OnAiShutdown);
|
||||||
|
SubscribeLocalEvent<StationAiCoreComponent, PowerChangedEvent>(OnCorePower);
|
||||||
|
SubscribeLocalEvent<StationAiCoreComponent, GetVerbsEvent<Verb>>(OnCoreVerbs);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnCoreVerbs(Entity<StationAiCoreComponent> ent, ref GetVerbsEvent<Verb> 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<StationAiOverlayComponent> 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<StationAiOverlayComponent> ent, ref MenuVisibilityEvent args)
|
||||||
|
{
|
||||||
|
args.Visibility &= ~MenuVisibility.NoFov;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnAiBuiCheck(Entity<StationAiWhitelistComponent> 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<StationAiOverlayComponent> 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<StationAiHolderComponent> 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<StationAiHolderComponent> ent, ref ComponentInit args)
|
||||||
|
{
|
||||||
|
_slots.AddItemSlot(ent.Owner, StationAiHolderComponent.Container, ent.Comp.Slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnHolderRemove(Entity<StationAiHolderComponent> ent, ref ComponentRemove args)
|
||||||
|
{
|
||||||
|
_slots.RemoveItemSlot(ent.Owner, ent.Comp.Slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnHolderConInsert(Entity<StationAiHolderComponent> ent, ref EntInsertedIntoContainerMessage args)
|
||||||
|
{
|
||||||
|
UpdateAppearance((ent.Owner, ent.Comp));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnHolderConRemove(Entity<StationAiHolderComponent> ent, ref EntRemovedFromContainerMessage args)
|
||||||
|
{
|
||||||
|
UpdateAppearance((ent.Owner, ent.Comp));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnHolderMapInit(Entity<StationAiHolderComponent> ent, ref MapInitEvent args)
|
||||||
|
{
|
||||||
|
UpdateAppearance(ent.Owner);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnAiShutdown(Entity<StationAiCoreComponent> ent, ref ComponentShutdown args)
|
||||||
|
{
|
||||||
|
// TODO: Tryqueuedel
|
||||||
|
if (_net.IsClient)
|
||||||
|
return;
|
||||||
|
|
||||||
|
QueueDel(ent.Comp.RemoteEntity);
|
||||||
|
ent.Comp.RemoteEntity = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnCorePower(Entity<StationAiCoreComponent> 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<StationAiCoreComponent> ent, ref MapInitEvent args)
|
||||||
|
{
|
||||||
|
SetupEye(ent);
|
||||||
|
AttachEye(ent);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool SetupEye(Entity<StationAiCoreComponent> 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<StationAiCoreComponent> ent)
|
||||||
|
{
|
||||||
|
QueueDel(ent.Comp.RemoteEntity);
|
||||||
|
ent.Comp.RemoteEntity = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AttachEye(Entity<StationAiCoreComponent> 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<StationAiCoreComponent> 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<StationAiCoreComponent> 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<RelayInputMoverComponent>(args.Entity);
|
||||||
|
|
||||||
|
if (TryComp(args.Entity, out EyeComponent? eyeComp))
|
||||||
|
{
|
||||||
|
_eye.SetTarget(args.Entity, null, eyeComp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateAppearance(Entity<StationAiHolderComponent?> 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<StationAiVisionComponent> 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<StationAiWhitelistComponent> entity, bool value, bool announce = false)
|
||||||
|
{
|
||||||
|
if (entity.Comp.Enabled == value)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
entity.Comp.Enabled = value;
|
||||||
|
Dirty(entity);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// BUI validation for ai interactions.
|
||||||
|
/// </summary>
|
||||||
|
private bool ValidateAi(Entity<StationAiHeldComponent?> 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,
|
||||||
|
}
|
||||||
32
Content.Shared/Silicons/StationAi/StationAiCoreComponent.cs
Normal file
32
Content.Shared/Silicons/StationAi/StationAiCoreComponent.cs
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
using Robust.Shared.GameStates;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
|
namespace Content.Shared.Silicons.StationAi;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates this entity can interact with station equipment and is a "Station AI".
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||||
|
public sealed partial class StationAiCoreComponent : Component
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* I couldn't think of any other reason you'd want to split these out.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Can it move its camera around and interact remotely with things.
|
||||||
|
/// </summary>
|
||||||
|
[DataField, AutoNetworkedField]
|
||||||
|
public bool Remote = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The invisible eye entity being used to look around.
|
||||||
|
/// </summary>
|
||||||
|
[DataField, AutoNetworkedField]
|
||||||
|
public EntityUid? RemoteEntity;
|
||||||
|
|
||||||
|
[DataField(readOnly: true)]
|
||||||
|
public EntProtoId? RemoteEntityProto = "StationAiHolo";
|
||||||
|
|
||||||
|
public const string Container = "station_ai_mind_slot";
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
using Robust.Shared.GameStates;
|
||||||
|
|
||||||
|
namespace Content.Shared.Silicons.StationAi;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates this entity is currently held inside of a station AI core.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, NetworkedComponent]
|
||||||
|
public sealed partial class StationAiHeldComponent : Component;
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
using Content.Shared.Containers.ItemSlots;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
|
|
||||||
|
namespace Content.Shared.Silicons.StationAi;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Allows moving a <see cref="StationAiCoreComponent"/> contained entity to and from this component.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, NetworkedComponent]
|
||||||
|
public sealed partial class StationAiHolderComponent : Component
|
||||||
|
{
|
||||||
|
public const string Container = StationAiCoreComponent.Container;
|
||||||
|
|
||||||
|
[DataField]
|
||||||
|
public ItemSlot Slot = new();
|
||||||
|
}
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
|
using Content.Shared.Silicons.StationAi;
|
||||||
using Robust.Shared.GameStates;
|
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
|
public sealed partial class StationAiVisionComponent : Component
|
||||||
{
|
{
|
||||||
[DataField, AutoNetworkedField]
|
[DataField, AutoNetworkedField]
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
|
using Content.Shared.StationAi;
|
||||||
using Robust.Shared.Map.Components;
|
using Robust.Shared.Map.Components;
|
||||||
|
using Robust.Shared.Physics;
|
||||||
using Robust.Shared.Threading;
|
using Robust.Shared.Threading;
|
||||||
using Robust.Shared.Utility;
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
@@ -24,6 +26,8 @@ public sealed class StationAiVisionSystem : EntitySystem
|
|||||||
private readonly HashSet<Entity<StationAiVisionComponent>> _seeds = new();
|
private readonly HashSet<Entity<StationAiVisionComponent>> _seeds = new();
|
||||||
private readonly HashSet<Vector2i> _viewportTiles = new();
|
private readonly HashSet<Vector2i> _viewportTiles = new();
|
||||||
|
|
||||||
|
private EntityQuery<OccluderComponent> _occluderQuery;
|
||||||
|
|
||||||
// Dummy set
|
// Dummy set
|
||||||
private readonly HashSet<Vector2i> _singleTiles = new();
|
private readonly HashSet<Vector2i> _singleTiles = new();
|
||||||
|
|
||||||
@@ -36,15 +40,12 @@ public sealed class StationAiVisionSystem : EntitySystem
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private bool FastPath;
|
private bool FastPath;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Have we found the target tile if we're only checking for a single one.
|
|
||||||
/// </summary>
|
|
||||||
private bool TargetFound;
|
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
|
||||||
|
_occluderQuery = GetEntityQuery<OccluderComponent>();
|
||||||
|
|
||||||
_seedJob = new()
|
_seedJob = new()
|
||||||
{
|
{
|
||||||
System = this,
|
System = this,
|
||||||
@@ -61,16 +62,16 @@ public sealed class StationAiVisionSystem : EntitySystem
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns whether a tile is accessible based on vision.
|
/// Returns whether a tile is accessible based on vision.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsAccessible(Entity<MapGridComponent> grid, Vector2i tile, float expansionSize = 8.5f, bool fastPath = false)
|
public bool IsAccessible(Entity<BroadphaseComponent, MapGridComponent> grid, Vector2i tile, float expansionSize = 8.5f, bool fastPath = false)
|
||||||
{
|
{
|
||||||
_viewportTiles.Clear();
|
_viewportTiles.Clear();
|
||||||
_opaque.Clear();
|
_opaque.Clear();
|
||||||
_seeds.Clear();
|
_seeds.Clear();
|
||||||
_viewportTiles.Add(tile);
|
_viewportTiles.Add(tile);
|
||||||
var localBounds = _lookup.GetLocalBounds(tile, grid.Comp.TileSize);
|
var localBounds = _lookup.GetLocalBounds(tile, grid.Comp2.TileSize);
|
||||||
var expandedBounds = localBounds.Enlarged(expansionSize);
|
var expandedBounds = localBounds.Enlarged(expansionSize);
|
||||||
|
|
||||||
_seedJob.Grid = grid;
|
_seedJob.Grid = (grid.Owner, grid.Comp2);
|
||||||
_seedJob.ExpandedBounds = expandedBounds;
|
_seedJob.ExpandedBounds = expandedBounds;
|
||||||
_parallel.ProcessNow(_seedJob);
|
_parallel.ProcessNow(_seedJob);
|
||||||
_job.Data.Clear();
|
_job.Data.Clear();
|
||||||
@@ -110,21 +111,19 @@ public sealed class StationAiVisionSystem : EntitySystem
|
|||||||
_job.BoundaryTiles.Add(new HashSet<Vector2i>());
|
_job.BoundaryTiles.Add(new HashSet<Vector2i>());
|
||||||
}
|
}
|
||||||
|
|
||||||
_job.TargetTile = tile;
|
|
||||||
TargetFound = false;
|
|
||||||
_singleTiles.Clear();
|
_singleTiles.Clear();
|
||||||
_job.Grid = grid;
|
_job.Grid = (grid.Owner, grid.Comp2);
|
||||||
_job.VisibleTiles = _singleTiles;
|
_job.VisibleTiles = _singleTiles;
|
||||||
_parallel.ProcessNow(_job, _job.Data.Count);
|
_parallel.ProcessNow(_job, _job.Data.Count);
|
||||||
|
|
||||||
return TargetFound;
|
return _job.VisibleTiles.Contains(tile);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool IsOccluded(Entity<MapGridComponent> grid, Vector2i tile)
|
private bool IsOccluded(Entity<BroadphaseComponent, MapGridComponent> 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();
|
_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;
|
var anyOccluders = false;
|
||||||
|
|
||||||
foreach (var occluder in _occluders)
|
foreach (var occluder in _occluders)
|
||||||
@@ -143,17 +142,18 @@ public sealed class StationAiVisionSystem : EntitySystem
|
|||||||
/// Gets a byond-equivalent for tiles in the specified worldAABB.
|
/// Gets a byond-equivalent for tiles in the specified worldAABB.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="expansionSize">How much to expand the bounds before to find vision intersecting it. Makes this the largest vision size + 1 tile.</param>
|
/// <param name="expansionSize">How much to expand the bounds before to find vision intersecting it. Makes this the largest vision size + 1 tile.</param>
|
||||||
public void GetView(Entity<MapGridComponent> grid, Box2Rotated worldBounds, HashSet<Vector2i> visibleTiles, float expansionSize = 8.5f)
|
public void GetView(Entity<BroadphaseComponent, MapGridComponent> grid, Box2Rotated worldBounds, HashSet<Vector2i> visibleTiles, float expansionSize = 8.5f)
|
||||||
{
|
{
|
||||||
_viewportTiles.Clear();
|
_viewportTiles.Clear();
|
||||||
_opaque.Clear();
|
_opaque.Clear();
|
||||||
_seeds.Clear();
|
_seeds.Clear();
|
||||||
var expandedBounds = worldBounds.Enlarged(expansionSize);
|
|
||||||
|
|
||||||
// TODO: Would be nice to be able to run this while running the other stuff.
|
// TODO: Would be nice to be able to run this while running the other stuff.
|
||||||
_seedJob.Grid = grid;
|
_seedJob.Grid = (grid.Owner, grid.Comp2);
|
||||||
var localAABB = _xforms.GetInvWorldMatrix(grid).TransformBox(expandedBounds);
|
var invMatrix = _xforms.GetInvWorldMatrix(grid);
|
||||||
_seedJob.ExpandedBounds = localAABB;
|
var localAabb = invMatrix.TransformBox(worldBounds);
|
||||||
|
var enlargedLocalAabb = invMatrix.TransformBox(worldBounds.Enlarged(expansionSize));
|
||||||
|
_seedJob.ExpandedBounds = enlargedLocalAabb;
|
||||||
_parallel.ProcessNow(_seedJob);
|
_parallel.ProcessNow(_seedJob);
|
||||||
_job.Data.Clear();
|
_job.Data.Clear();
|
||||||
FastPath = false;
|
FastPath = false;
|
||||||
@@ -170,7 +170,7 @@ public sealed class StationAiVisionSystem : EntitySystem
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// Get viewport tiles
|
// 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))
|
while (tileEnumerator.MoveNext(out var tileRef))
|
||||||
{
|
{
|
||||||
@@ -182,9 +182,8 @@ public sealed class StationAiVisionSystem : EntitySystem
|
|||||||
_viewportTiles.Add(tileRef.GridIndices);
|
_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))
|
while (tileEnumerator.MoveNext(out var tileRef))
|
||||||
{
|
{
|
||||||
if (_viewportTiles.Contains(tileRef.GridIndices))
|
if (_viewportTiles.Contains(tileRef.GridIndices))
|
||||||
@@ -206,9 +205,7 @@ public sealed class StationAiVisionSystem : EntitySystem
|
|||||||
_job.BoundaryTiles.Add(new HashSet<Vector2i>());
|
_job.BoundaryTiles.Add(new HashSet<Vector2i>());
|
||||||
}
|
}
|
||||||
|
|
||||||
_job.TargetTile = null;
|
_job.Grid = (grid.Owner, grid.Comp2);
|
||||||
TargetFound = false;
|
|
||||||
_job.Grid = grid;
|
|
||||||
_job.VisibleTiles = visibleTiles;
|
_job.VisibleTiles = visibleTiles;
|
||||||
_parallel.ProcessNow(_job, _job.Data.Count);
|
_parallel.ProcessNow(_job, _job.Data.Count);
|
||||||
}
|
}
|
||||||
@@ -250,6 +247,7 @@ public sealed class StationAiVisionSystem : EntitySystem
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
/// Checks whether this tile fits the definition of a "corner"
|
/// Checks whether this tile fits the definition of a "corner"
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private bool IsCorner(
|
private bool IsCorner(
|
||||||
@@ -287,7 +285,7 @@ public sealed class StationAiVisionSystem : EntitySystem
|
|||||||
|
|
||||||
public void Execute()
|
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 @@ public sealed class StationAiVisionSystem : EntitySystem
|
|||||||
public Entity<MapGridComponent> Grid;
|
public Entity<MapGridComponent> Grid;
|
||||||
public List<Entity<StationAiVisionComponent>> Data = new();
|
public List<Entity<StationAiVisionComponent>> Data = new();
|
||||||
|
|
||||||
// If we're doing range-checks might be able to early out
|
|
||||||
public Vector2i? TargetTile;
|
|
||||||
|
|
||||||
public HashSet<Vector2i> VisibleTiles;
|
public HashSet<Vector2i> VisibleTiles;
|
||||||
|
|
||||||
public readonly List<Dictionary<Vector2i, int>> Vis1 = new();
|
public readonly List<Dictionary<Vector2i, int>> Vis1 = new();
|
||||||
@@ -315,18 +310,6 @@ public sealed class StationAiVisionSystem : EntitySystem
|
|||||||
|
|
||||||
public void Execute(int index)
|
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 seed = Data[index];
|
||||||
var seedXform = EntManager.GetComponent<TransformComponent>(seed);
|
var seedXform = EntManager.GetComponent<TransformComponent>(seed);
|
||||||
|
|
||||||
@@ -338,24 +321,6 @@ public sealed class StationAiVisionSystem : EntitySystem
|
|||||||
Grid.Comp,
|
Grid.Comp,
|
||||||
new Circle(System._xforms.GetWorldPosition(seedXform), seed.Comp.Range), ignoreEmpty: false);
|
new Circle(System._xforms.GetWorldPosition(seedXform), seed.Comp.Range), ignoreEmpty: false);
|
||||||
|
|
||||||
// Try to find the target tile.
|
|
||||||
if (TargetTile != null)
|
|
||||||
{
|
|
||||||
foreach (var tile in squircles)
|
|
||||||
{
|
|
||||||
if (tile.GridIndices == TargetTile)
|
|
||||||
{
|
|
||||||
lock (System)
|
|
||||||
{
|
|
||||||
System.TargetFound = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
lock (VisibleTiles)
|
lock (VisibleTiles)
|
||||||
{
|
{
|
||||||
foreach (var tile in squircles)
|
foreach (var tile in squircles)
|
||||||
@@ -363,7 +328,6 @@ public sealed class StationAiVisionSystem : EntitySystem
|
|||||||
VisibleTiles.Add(tile.GridIndices);
|
VisibleTiles.Add(tile.GridIndices);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -480,24 +444,6 @@ public sealed class StationAiVisionSystem : EntitySystem
|
|||||||
vis1[tile] = -1;
|
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.
|
// vis2 is what we care about for LOS.
|
||||||
foreach (var tile in seedTiles)
|
foreach (var tile in seedTiles)
|
||||||
{
|
{
|
||||||
@@ -519,4 +465,3 @@ public sealed class StationAiVisionSystem : EntitySystem
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
using Robust.Shared.GameStates;
|
||||||
|
|
||||||
|
namespace Content.Shared.Silicons.StationAi;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates an entity that has <see cref="StationAiHeldComponent"/> can interact with this.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, Access(typeof(SharedStationAiSystem))]
|
||||||
|
public sealed partial class StationAiWhitelistComponent : Component
|
||||||
|
{
|
||||||
|
[DataField, AutoNetworkedField]
|
||||||
|
public bool Enabled = true;
|
||||||
|
}
|
||||||
@@ -8,14 +8,18 @@ using Content.Shared.Storage;
|
|||||||
using Content.Shared.Storage.EntitySystems;
|
using Content.Shared.Storage.EntitySystems;
|
||||||
using Robust.Shared.Collections;
|
using Robust.Shared.Collections;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Random;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
namespace Content.Shared.Station;
|
namespace Content.Shared.Station;
|
||||||
|
|
||||||
public abstract class SharedStationSpawningSystem : EntitySystem
|
public abstract class SharedStationSpawningSystem : EntitySystem
|
||||||
{
|
{
|
||||||
[Dependency] protected readonly IPrototypeManager PrototypeManager = default!;
|
[Dependency] protected readonly IPrototypeManager PrototypeManager = default!;
|
||||||
|
[Dependency] private readonly IRobustRandom _random = default!;
|
||||||
[Dependency] protected readonly InventorySystem InventorySystem = default!;
|
[Dependency] protected readonly InventorySystem InventorySystem = default!;
|
||||||
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
|
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
|
||||||
|
[Dependency] private readonly MetaDataSystem _metadata = default!;
|
||||||
[Dependency] private readonly SharedStorageSystem _storage = default!;
|
[Dependency] private readonly SharedStorageSystem _storage = default!;
|
||||||
[Dependency] private readonly SharedTransformSystem _xformSystem = default!;
|
[Dependency] private readonly SharedTransformSystem _xformSystem = default!;
|
||||||
|
|
||||||
@@ -34,7 +38,7 @@ public abstract class SharedStationSpawningSystem : EntitySystem
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Equips the given starting gears from a `RoleLoadout` onto an entity.
|
/// Equips the data from a `RoleLoadout` onto an entity.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void EquipRoleLoadout(EntityUid entity, RoleLoadout loadout, RoleLoadoutPrototype roleProto)
|
public void EquipRoleLoadout(EntityUid entity, RoleLoadout loadout, RoleLoadoutPrototype roleProto)
|
||||||
{
|
{
|
||||||
@@ -52,6 +56,26 @@ public abstract class SharedStationSpawningSystem : EntitySystem
|
|||||||
EquipStartingGear(entity, loadoutProto, raiseEvent: false);
|
EquipStartingGear(entity, loadoutProto, raiseEvent: false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EquipRoleName(entity, loadout, roleProto);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Applies the role's name as applicable to the entity.
|
||||||
|
/// </summary>
|
||||||
|
public void EquipRoleName(EntityUid entity, RoleLoadout loadout, RoleLoadoutPrototype roleProto)
|
||||||
|
{
|
||||||
|
string? name = null;
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(name) && PrototypeManager.TryIndex(roleProto.NameDataset, out var nameData))
|
||||||
|
{
|
||||||
|
name = _random.Pick(nameData.Values);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(name))
|
||||||
|
{
|
||||||
|
_metadata.SetEntityName(entity, name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void EquipStartingGear(EntityUid entity, LoadoutPrototype loadout, bool raiseEvent = true)
|
public void EquipStartingGear(EntityUid entity, LoadoutPrototype loadout, bool raiseEvent = true)
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ namespace Content.Shared.UserInterface
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether the item must be held in one of the user's hands to work.
|
/// Whether the item must be held in one of the user's hands to work.
|
||||||
/// This is ignored unless <see cref="RequireHands"/> is true.
|
/// This is ignored unless <see cref="RequiresComplex"/> is true.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
[DataField]
|
[DataField]
|
||||||
@@ -29,15 +29,15 @@ namespace Content.Shared.UserInterface
|
|||||||
public LocId VerbText = "ui-verb-toggle-open";
|
public LocId VerbText = "ui-verb-toggle-open";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 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.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// This should probably be true for most machines & computers, but there will still be UIs that represent a
|
/// 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.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
[DataField]
|
[DataField]
|
||||||
public bool RequireHands = true;
|
public bool RequiresComplex = true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Entities that are required to open this UI.
|
/// Entities that are required to open this UI.
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ public sealed partial class ActivatableUISystem : EntitySystem
|
|||||||
if (_whitelistSystem.IsWhitelistFail(component.RequiredItems, args.Using ?? default))
|
if (_whitelistSystem.IsWhitelistFail(component.RequiredItems, args.Using ?? default))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (component.RequireHands)
|
if (component.RequiresComplex)
|
||||||
{
|
{
|
||||||
if (args.Hands == null)
|
if (args.Hands == null)
|
||||||
return false;
|
return false;
|
||||||
@@ -191,20 +191,23 @@ public sealed partial class ActivatableUISystem : EntitySystem
|
|||||||
if (!_blockerSystem.CanInteract(user, uiEntity) && (!HasComp<GhostComponent>(user) || aui.BlockSpectators))
|
if (!_blockerSystem.CanInteract(user, uiEntity) && (!HasComp<GhostComponent>(user) || aui.BlockSpectators))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (aui.RequireHands)
|
if (aui.RequiresComplex)
|
||||||
|
{
|
||||||
|
if (!_blockerSystem.CanComplexInteract(user))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aui.InHandsOnly)
|
||||||
{
|
{
|
||||||
if (!TryComp(user, out HandsComponent? hands))
|
if (!TryComp(user, out HandsComponent? hands))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (aui.InHandsOnly)
|
|
||||||
{
|
|
||||||
if (!_hands.IsHolding(user, uiEntity, out var hand, hands))
|
if (!_hands.IsHolding(user, uiEntity, out var hand, hands))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (aui.RequireActiveHand && hands.ActiveHand != hand)
|
if (aui.RequireActiveHand && hands.ActiveHand != hand)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (aui.AdminOnly && !_adminManager.IsAdmin(user))
|
if (aui.AdminOnly && !_adminManager.IsAdmin(user))
|
||||||
return false;
|
return false;
|
||||||
@@ -274,13 +277,13 @@ public sealed partial class ActivatableUISystem : EntitySystem
|
|||||||
|
|
||||||
private void OnHandDeselected(Entity<ActivatableUIComponent> ent, ref HandDeselectedEvent args)
|
private void OnHandDeselected(Entity<ActivatableUIComponent> ent, ref HandDeselectedEvent args)
|
||||||
{
|
{
|
||||||
if (ent.Comp.RequireHands && ent.Comp.InHandsOnly && ent.Comp.RequireActiveHand)
|
if (ent.Comp.InHandsOnly && ent.Comp.RequireActiveHand)
|
||||||
CloseAll(ent, ent);
|
CloseAll(ent, ent);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnHandUnequipped(Entity<ActivatableUIComponent> ent, ref GotUnequippedHandEvent args)
|
private void OnHandUnequipped(Entity<ActivatableUIComponent> ent, ref GotUnequippedHandEvent args)
|
||||||
{
|
{
|
||||||
if (ent.Comp.RequireHands && ent.Comp.InHandsOnly)
|
if (ent.Comp.InHandsOnly)
|
||||||
CloseAll(ent, ent);
|
CloseAll(ent, ent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,6 +78,7 @@ namespace Content.Shared.Verbs
|
|||||||
// A large number of verbs need to check action blockers. Instead of repeatedly having each system individually
|
// A large number of verbs need to check action blockers. Instead of repeatedly having each system individually
|
||||||
// call ActionBlocker checks, just cache it for the verb request.
|
// call ActionBlocker checks, just cache it for the verb request.
|
||||||
var canInteract = force || _actionBlockerSystem.CanInteract(user, target);
|
var canInteract = force || _actionBlockerSystem.CanInteract(user, target);
|
||||||
|
var canComplexInteract = force || _actionBlockerSystem.CanComplexInteract(user);
|
||||||
|
|
||||||
_interactionSystem.TryGetUsedEntity(user, out var @using);
|
_interactionSystem.TryGetUsedEntity(user, out var @using);
|
||||||
TryComp<HandsComponent>(user, out var hands);
|
TryComp<HandsComponent>(user, out var hands);
|
||||||
@@ -85,7 +86,7 @@ namespace Content.Shared.Verbs
|
|||||||
// TODO: fix this garbage and use proper generics or reflection or something else, not this.
|
// TODO: fix this garbage and use proper generics or reflection or something else, not this.
|
||||||
if (types.Contains(typeof(InteractionVerb)))
|
if (types.Contains(typeof(InteractionVerb)))
|
||||||
{
|
{
|
||||||
var verbEvent = new GetVerbsEvent<InteractionVerb>(user, target, @using, hands, canInteract, canAccess, extraCategories);
|
var verbEvent = new GetVerbsEvent<InteractionVerb>(user, target, @using, hands, canInteract: canInteract, canComplexInteract: canComplexInteract, canAccess: canAccess, extraCategories);
|
||||||
RaiseLocalEvent(target, verbEvent, true);
|
RaiseLocalEvent(target, verbEvent, true);
|
||||||
verbs.UnionWith(verbEvent.Verbs);
|
verbs.UnionWith(verbEvent.Verbs);
|
||||||
}
|
}
|
||||||
@@ -94,35 +95,35 @@ namespace Content.Shared.Verbs
|
|||||||
&& @using != null
|
&& @using != null
|
||||||
&& @using != target)
|
&& @using != target)
|
||||||
{
|
{
|
||||||
var verbEvent = new GetVerbsEvent<UtilityVerb>(user, target, @using, hands, canInteract, canAccess, extraCategories);
|
var verbEvent = new GetVerbsEvent<UtilityVerb>(user, target, @using, hands, canInteract: canInteract, canComplexInteract: canComplexInteract, canAccess: canAccess, extraCategories);
|
||||||
RaiseLocalEvent(@using.Value, verbEvent, true); // directed at used, not at target
|
RaiseLocalEvent(@using.Value, verbEvent, true); // directed at used, not at target
|
||||||
verbs.UnionWith(verbEvent.Verbs);
|
verbs.UnionWith(verbEvent.Verbs);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (types.Contains(typeof(InnateVerb)))
|
if (types.Contains(typeof(InnateVerb)))
|
||||||
{
|
{
|
||||||
var verbEvent = new GetVerbsEvent<InnateVerb>(user, target, @using, hands, canInteract, canAccess, extraCategories);
|
var verbEvent = new GetVerbsEvent<InnateVerb>(user, target, @using, hands, canInteract: canInteract, canComplexInteract: canComplexInteract, canAccess: canAccess, extraCategories);
|
||||||
RaiseLocalEvent(user, verbEvent, true);
|
RaiseLocalEvent(user, verbEvent, true);
|
||||||
verbs.UnionWith(verbEvent.Verbs);
|
verbs.UnionWith(verbEvent.Verbs);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (types.Contains(typeof(AlternativeVerb)))
|
if (types.Contains(typeof(AlternativeVerb)))
|
||||||
{
|
{
|
||||||
var verbEvent = new GetVerbsEvent<AlternativeVerb>(user, target, @using, hands, canInteract, canAccess, extraCategories);
|
var verbEvent = new GetVerbsEvent<AlternativeVerb>(user, target, @using, hands, canInteract: canInteract, canComplexInteract: canComplexInteract, canAccess: canAccess, extraCategories);
|
||||||
RaiseLocalEvent(target, verbEvent, true);
|
RaiseLocalEvent(target, verbEvent, true);
|
||||||
verbs.UnionWith(verbEvent.Verbs);
|
verbs.UnionWith(verbEvent.Verbs);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (types.Contains(typeof(ActivationVerb)))
|
if (types.Contains(typeof(ActivationVerb)))
|
||||||
{
|
{
|
||||||
var verbEvent = new GetVerbsEvent<ActivationVerb>(user, target, @using, hands, canInteract, canAccess, extraCategories);
|
var verbEvent = new GetVerbsEvent<ActivationVerb>(user, target, @using, hands, canInteract: canInteract, canComplexInteract: canComplexInteract, canAccess: canAccess, extraCategories);
|
||||||
RaiseLocalEvent(target, verbEvent, true);
|
RaiseLocalEvent(target, verbEvent, true);
|
||||||
verbs.UnionWith(verbEvent.Verbs);
|
verbs.UnionWith(verbEvent.Verbs);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (types.Contains(typeof(ExamineVerb)))
|
if (types.Contains(typeof(ExamineVerb)))
|
||||||
{
|
{
|
||||||
var verbEvent = new GetVerbsEvent<ExamineVerb>(user, target, @using, hands, canInteract, canAccess, extraCategories);
|
var verbEvent = new GetVerbsEvent<ExamineVerb>(user, target, @using, hands, canInteract: canInteract, canComplexInteract: canComplexInteract, canAccess: canAccess, extraCategories);
|
||||||
RaiseLocalEvent(target, verbEvent, true);
|
RaiseLocalEvent(target, verbEvent, true);
|
||||||
verbs.UnionWith(verbEvent.Verbs);
|
verbs.UnionWith(verbEvent.Verbs);
|
||||||
}
|
}
|
||||||
@@ -130,7 +131,7 @@ namespace Content.Shared.Verbs
|
|||||||
// generic verbs
|
// generic verbs
|
||||||
if (types.Contains(typeof(Verb)))
|
if (types.Contains(typeof(Verb)))
|
||||||
{
|
{
|
||||||
var verbEvent = new GetVerbsEvent<Verb>(user, target, @using, hands, canInteract, canAccess, extraCategories);
|
var verbEvent = new GetVerbsEvent<Verb>(user, target, @using, hands, canInteract: canInteract, canComplexInteract: canComplexInteract, canAccess: canAccess, extraCategories);
|
||||||
RaiseLocalEvent(target, verbEvent, true);
|
RaiseLocalEvent(target, verbEvent, true);
|
||||||
verbs.UnionWith(verbEvent.Verbs);
|
verbs.UnionWith(verbEvent.Verbs);
|
||||||
}
|
}
|
||||||
@@ -138,7 +139,7 @@ namespace Content.Shared.Verbs
|
|||||||
if (types.Contains(typeof(EquipmentVerb)))
|
if (types.Contains(typeof(EquipmentVerb)))
|
||||||
{
|
{
|
||||||
var access = canAccess || _interactionSystem.CanAccessEquipment(user, target);
|
var access = canAccess || _interactionSystem.CanAccessEquipment(user, target);
|
||||||
var verbEvent = new GetVerbsEvent<EquipmentVerb>(user, target, @using, hands, canInteract, access, extraCategories);
|
var verbEvent = new GetVerbsEvent<EquipmentVerb>(user, target, @using, hands, canInteract: canInteract, canComplexInteract: canComplexInteract, canAccess: canAccess, extraCategories);
|
||||||
RaiseLocalEvent(target, verbEvent);
|
RaiseLocalEvent(target, verbEvent);
|
||||||
verbs.UnionWith(verbEvent.Verbs);
|
verbs.UnionWith(verbEvent.Verbs);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -113,6 +113,11 @@ namespace Content.Shared.Verbs
|
|||||||
/// </remarks>
|
/// </remarks>
|
||||||
public readonly bool CanInteract;
|
public readonly bool CanInteract;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Cached version of CanComplexInteract
|
||||||
|
/// </summary>
|
||||||
|
public readonly bool CanComplexInteract;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The User's hand component.
|
/// The User's hand component.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -130,13 +135,14 @@ namespace Content.Shared.Verbs
|
|||||||
/// </remarks>
|
/// </remarks>
|
||||||
public readonly EntityUid? Using;
|
public readonly EntityUid? Using;
|
||||||
|
|
||||||
public GetVerbsEvent(EntityUid user, EntityUid target, EntityUid? @using, HandsComponent? hands, bool canInteract, bool canAccess, List<VerbCategory> extraCategories)
|
public GetVerbsEvent(EntityUid user, EntityUid target, EntityUid? @using, HandsComponent? hands, bool canInteract, bool canComplexInteract, bool canAccess, List<VerbCategory> extraCategories)
|
||||||
{
|
{
|
||||||
User = user;
|
User = user;
|
||||||
Target = target;
|
Target = target;
|
||||||
Using = @using;
|
Using = @using;
|
||||||
Hands = hands;
|
Hands = hands;
|
||||||
CanAccess = canAccess;
|
CanAccess = canAccess;
|
||||||
|
CanComplexInteract = canComplexInteract;
|
||||||
CanInteract = canInteract;
|
CanInteract = canInteract;
|
||||||
ExtraCategories = extraCategories;
|
ExtraCategories = extraCategories;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -130,11 +130,20 @@ public abstract class SharedWiresSystem : EntitySystem
|
|||||||
return !attempt.Cancelled;
|
return !attempt.Cancelled;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsPanelOpen(Entity<WiresPanelComponent?> entity)
|
public bool IsPanelOpen(Entity<WiresPanelComponent?> entity, EntityUid? tool = null)
|
||||||
{
|
{
|
||||||
if (!Resolve(entity, ref entity.Comp, false))
|
if (!Resolve(entity, ref entity.Comp, false))
|
||||||
return true;
|
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
|
// 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.
|
// but it looks like an almost direct duplication of WiresPanelComponent except with a shittier API.
|
||||||
if (TryComp<WiresPanelSecurityComponent>(entity, out var wiresPanelSecurity) &&
|
if (TryComp<WiresPanelSecurityComponent>(entity, out var wiresPanelSecurity) &&
|
||||||
@@ -161,3 +170,12 @@ public abstract class SharedWiresSystem : EntitySystem
|
|||||||
_activatableUI.CloseAll(uid);
|
_activatableUI.CloseAll(uid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raised directed on a tool to try and override panel visibility.
|
||||||
|
/// </summary>
|
||||||
|
[ByRefEvent]
|
||||||
|
public record struct PanelOverrideEvent()
|
||||||
|
{
|
||||||
|
public bool Allowed = true;
|
||||||
|
}
|
||||||
|
|||||||
@@ -76,5 +76,5 @@
|
|||||||
- borgwalk1.ogg
|
- borgwalk1.ogg
|
||||||
- borgwalk2.ogg
|
- borgwalk2.ogg
|
||||||
license: "CC-BY-SA-4.0"
|
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/"
|
source: "https://freesound.org/people/IENBA/sounds/697379/"
|
||||||
|
|||||||
Binary file not shown.
@@ -8,3 +8,5 @@ silicon-law-ui-delete = Delete
|
|||||||
silicon-law-ui-check-corrupted = Corrupted law
|
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-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-law-ui-placeholder = Type here to change law text...
|
||||||
|
|
||||||
|
silicon-laws-updated = Updated laws
|
||||||
|
|||||||
@@ -5,4 +5,5 @@ department-Engineering-description = Keep the power on and the station operation
|
|||||||
department-Medical-description = Keep the crew healthy.
|
department-Medical-description = Keep the crew healthy.
|
||||||
department-Security-description = Keep the peace around the station.
|
department-Security-description = Keep the peace around the station.
|
||||||
department-Science-description = Research artifacts and anomalies to invent new equipment for 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.
|
department-Specific-description = Jobs that not all stations have.
|
||||||
|
|||||||
@@ -5,4 +5,5 @@ department-Engineering = Engineering
|
|||||||
department-Medical = Medical
|
department-Medical = Medical
|
||||||
department-Security = Security
|
department-Security = Security
|
||||||
department-Science = Science
|
department-Science = Science
|
||||||
|
department-Silicon = Silicons
|
||||||
department-Specific = Station specific
|
department-Specific = Station specific
|
||||||
|
|||||||
@@ -43,6 +43,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-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-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-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-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-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 available on Gemini Station.
|
job-description-zookeeper = Put on a joyful display of cute animals and space carps for all the crew to see. Currently available on Gemini Station.
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ job-name-botanist = Botanist
|
|||||||
job-name-bartender = Bartender
|
job-name-bartender = Bartender
|
||||||
job-name-passenger = Passenger
|
job-name-passenger = Passenger
|
||||||
job-name-salvagespec = Salvage Specialist
|
job-name-salvagespec = Salvage Specialist
|
||||||
|
job-name-station-ai = Station AI
|
||||||
job-name-qm = Quartermaster
|
job-name-qm = Quartermaster
|
||||||
job-name-cargotech = Cargo Technician
|
job-name-cargotech = Cargo Technician
|
||||||
job-name-chef = Chef
|
job-name-chef = Chef
|
||||||
@@ -103,6 +104,7 @@ JobScientist = Scientist
|
|||||||
JobSecurityCadet = Security Cadet
|
JobSecurityCadet = Security Cadet
|
||||||
JobSecurityOfficer = Security Officer
|
JobSecurityOfficer = Security Officer
|
||||||
JobServiceWorker = Service Worker
|
JobServiceWorker = Service Worker
|
||||||
|
JobStationAi = Station AI
|
||||||
JobStationEngineer = Station Engineer
|
JobStationEngineer = Station Engineer
|
||||||
JobTechnicalAssistant = Technical Assistant
|
JobTechnicalAssistant = Technical Assistant
|
||||||
JobVisitor = Visitor
|
JobVisitor = Visitor
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
|
# Name
|
||||||
|
loadout-name-edit-label = Custom name
|
||||||
|
loadout-name-edit-tooltip = 32 characters max. If no name is specified a random one may be chosen for you.
|
||||||
|
|
||||||
# Restrictions
|
# Restrictions
|
||||||
loadout-restrictions = Restrictions
|
loadout-restrictions = Restrictions
|
||||||
loadouts-min-limit = Min count: {$count}
|
loadouts-min-limit = Min count: {$count}
|
||||||
|
|||||||
14
Resources/Locale/en-US/silicons/station-ai.ftl
Normal file
14
Resources/Locale/en-US/silicons/station-ai.ftl
Normal file
@@ -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
|
||||||
@@ -4386,6 +4386,13 @@ entities:
|
|||||||
- type: Transform
|
- type: Transform
|
||||||
pos: 1.5,-14.5
|
pos: 1.5,-14.5
|
||||||
parent: 179
|
parent: 179
|
||||||
|
- proto: PlayerStationAi
|
||||||
|
entities:
|
||||||
|
- uid: 14
|
||||||
|
components:
|
||||||
|
- type: Transform
|
||||||
|
pos: -5.5,-5.5
|
||||||
|
parent: 179
|
||||||
- proto: PortableGeneratorSuperPacman
|
- proto: PortableGeneratorSuperPacman
|
||||||
entities:
|
entities:
|
||||||
- uid: 1016
|
- uid: 1016
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
id: names_ai
|
id: names_ai
|
||||||
values:
|
values:
|
||||||
- 16-20
|
- 16-20
|
||||||
- 790
|
- "790"
|
||||||
- Adaptive Manipulator
|
- Adaptive Manipulator
|
||||||
- ALICE
|
- ALICE
|
||||||
- Allied Mastercomputer
|
- Allied Mastercomputer
|
||||||
|
|||||||
@@ -72,6 +72,9 @@
|
|||||||
- type: ActivatableUI
|
- type: ActivatableUI
|
||||||
key: enum.BorgUiKey.Key
|
key: enum.BorgUiKey.Key
|
||||||
- type: SiliconLawBound
|
- type: SiliconLawBound
|
||||||
|
- type: ActionGrant
|
||||||
|
actions:
|
||||||
|
- ActionViewLaws
|
||||||
- type: EmagSiliconLaw
|
- type: EmagSiliconLaw
|
||||||
stunTime: 5
|
stunTime: 5
|
||||||
- type: SiliconLawProvider
|
- type: SiliconLawProvider
|
||||||
|
|||||||
@@ -265,10 +265,10 @@
|
|||||||
access: [["Medical"], ["Command"], ["Research"]]
|
access: [["Medical"], ["Command"], ["Research"]]
|
||||||
- type: Inventory
|
- type: Inventory
|
||||||
templateId: borgDutch
|
templateId: borgDutch
|
||||||
- type: SolutionScanner
|
|
||||||
- type: FootstepModifier
|
- type: FootstepModifier
|
||||||
footstepSoundCollection:
|
footstepSoundCollection:
|
||||||
collection: FootstepHoverBorg
|
collection: FootstepHoverBorg
|
||||||
|
- type: SolutionScanner
|
||||||
- type: InteractionPopup
|
- type: InteractionPopup
|
||||||
interactSuccessString: petting-success-medical-cyborg
|
interactSuccessString: petting-success-medical-cyborg
|
||||||
interactFailureString: petting-failure-medical-cyborg
|
interactFailureString: petting-failure-medical-cyborg
|
||||||
|
|||||||
@@ -72,6 +72,7 @@
|
|||||||
parent:
|
parent:
|
||||||
- BaseSimpleMob
|
- BaseSimpleMob
|
||||||
- MobDamageable
|
- MobDamageable
|
||||||
|
- MobPolymorphable
|
||||||
- MobBloodstream
|
- MobBloodstream
|
||||||
- MobFlammable
|
- MobFlammable
|
||||||
- MobCombat
|
- MobCombat
|
||||||
|
|||||||
@@ -1,45 +1,27 @@
|
|||||||
- type: entity
|
- type: entity
|
||||||
id: MobRevenant
|
id: MobRevenant
|
||||||
|
parent:
|
||||||
|
- BaseMob
|
||||||
|
- Incorporeal
|
||||||
name: revenant
|
name: revenant
|
||||||
description: A spooky ghostie.
|
description: A spooky ghostie.
|
||||||
components:
|
components:
|
||||||
- type: MindContainer
|
|
||||||
- type: InputMover
|
|
||||||
- type: MobMover
|
|
||||||
- type: Input
|
- type: Input
|
||||||
context: "ghost"
|
context: "ghost"
|
||||||
- type: MovementSpeedModifier
|
- type: MovementSpeedModifier
|
||||||
baseWalkSpeed: 6
|
baseWalkSpeed: 6
|
||||||
baseSprintSpeed: 6
|
baseSprintSpeed: 6
|
||||||
- type: Sprite
|
- type: Sprite
|
||||||
noRot: true
|
|
||||||
drawdepth: Ghosts
|
|
||||||
sprite: Mobs/Ghosts/revenant.rsi
|
sprite: Mobs/Ghosts/revenant.rsi
|
||||||
layers:
|
layers:
|
||||||
- state: active
|
- state: active
|
||||||
- type: Clickable
|
|
||||||
- type: StatusEffects
|
- type: StatusEffects
|
||||||
allowed:
|
allowed:
|
||||||
- Stun
|
- Stun
|
||||||
- Corporeal
|
- Corporeal
|
||||||
- type: InteractionOutline
|
|
||||||
- type: Physics
|
|
||||||
bodyType: KinematicController
|
|
||||||
- type: Fixtures
|
|
||||||
fixtures:
|
|
||||||
fix1:
|
|
||||||
shape:
|
|
||||||
!type:PhysShapeCircle
|
|
||||||
radius: 0.40
|
|
||||||
density: 80
|
|
||||||
mask:
|
|
||||||
- GhostImpassable
|
|
||||||
- type: MovementIgnoreGravity
|
|
||||||
- type: Damageable
|
- type: Damageable
|
||||||
damageContainer: Biological
|
damageContainer: Biological
|
||||||
- type: Examiner
|
|
||||||
- type: NoSlip
|
- type: NoSlip
|
||||||
- type: Actions
|
|
||||||
- type: Eye
|
- type: Eye
|
||||||
drawFov: false
|
drawFov: false
|
||||||
visMask:
|
visMask:
|
||||||
@@ -47,8 +29,6 @@
|
|||||||
- Ghost
|
- Ghost
|
||||||
- type: ContentEye
|
- type: ContentEye
|
||||||
maxZoom: 1.2, 1.2
|
maxZoom: 1.2, 1.2
|
||||||
- type: DoAfter
|
|
||||||
- type: Alerts
|
|
||||||
- type: NameIdentifier
|
- type: NameIdentifier
|
||||||
group: GenericNumber
|
group: GenericNumber
|
||||||
- type: GhostRole
|
- type: GhostRole
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
parent:
|
parent:
|
||||||
- BaseMob
|
- BaseMob
|
||||||
- MobDamageable
|
- MobDamageable
|
||||||
|
- MobPolymorphable
|
||||||
- MobAtmosExposed
|
- MobAtmosExposed
|
||||||
id: BaseSimpleMob
|
id: BaseSimpleMob
|
||||||
suffix: AI
|
suffix: AI
|
||||||
|
|||||||
@@ -1,21 +1,22 @@
|
|||||||
- type: entity
|
- type: entity
|
||||||
parent: BaseMob
|
id: Incorporeal
|
||||||
id: MobObserver
|
save: false
|
||||||
name: observer
|
abstract: true
|
||||||
description: Boo!
|
description: Mobs without physical bodies
|
||||||
categories: [ HideSpawnMenu ]
|
|
||||||
components:
|
components:
|
||||||
- type: CargoSellBlacklist
|
|
||||||
- type: Sprite
|
- type: Sprite
|
||||||
overrideContainerOcclusion: true # Ghosts always show up regardless of where they're contained.
|
noRot: true
|
||||||
|
overrideContainerOcclusion: true # Always show up regardless of where they're contained.
|
||||||
drawdepth: Ghosts
|
drawdepth: Ghosts
|
||||||
sprite: Mobs/Ghosts/ghost_human.rsi
|
- type: CargoSellBlacklist
|
||||||
color: "#fff8"
|
- type: MovementSpeedModifier
|
||||||
layers:
|
baseSprintSpeed: 12
|
||||||
- state: animated
|
baseWalkSpeed: 8
|
||||||
shader: unshaded
|
- type: MovementIgnoreGravity
|
||||||
- type: ContentEye
|
- type: Physics
|
||||||
maxZoom: 1.44,1.44
|
bodyType: KinematicController
|
||||||
|
bodyStatus: InAir
|
||||||
|
- type: CanMoveInAir
|
||||||
- type: Fixtures
|
- type: Fixtures
|
||||||
fixtures:
|
fixtures:
|
||||||
fix1:
|
fix1:
|
||||||
@@ -25,6 +26,24 @@
|
|||||||
density: 15
|
density: 15
|
||||||
mask:
|
mask:
|
||||||
- GhostImpassable
|
- GhostImpassable
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
parent:
|
||||||
|
- Incorporeal
|
||||||
|
- BaseMob
|
||||||
|
id: MobObserver
|
||||||
|
name: observer
|
||||||
|
description: Boo!
|
||||||
|
categories: [ HideSpawnMenu ]
|
||||||
|
components:
|
||||||
|
- type: Sprite
|
||||||
|
sprite: Mobs/Ghosts/ghost_human.rsi
|
||||||
|
color: "#fff8"
|
||||||
|
layers:
|
||||||
|
- state: animated
|
||||||
|
shader: unshaded
|
||||||
|
- type: ContentEye
|
||||||
|
maxZoom: 1.44,1.44
|
||||||
- type: Eye
|
- type: Eye
|
||||||
drawFov: false
|
drawFov: false
|
||||||
- type: Input
|
- type: Input
|
||||||
@@ -33,18 +52,10 @@
|
|||||||
skipChecks: true
|
skipChecks: true
|
||||||
- type: Ghost
|
- type: Ghost
|
||||||
- type: GhostHearing
|
- type: GhostHearing
|
||||||
- type: MovementSpeedModifier
|
|
||||||
baseSprintSpeed: 12
|
|
||||||
baseWalkSpeed: 8
|
|
||||||
- type: MovementIgnoreGravity
|
|
||||||
- type: IntrinsicRadioReceiver
|
- type: IntrinsicRadioReceiver
|
||||||
- type: ActiveRadio
|
- type: ActiveRadio
|
||||||
receiveAllChannels: true
|
receiveAllChannels: true
|
||||||
globalReceive: true
|
globalReceive: true
|
||||||
- type: Physics
|
|
||||||
bodyType: KinematicController
|
|
||||||
bodyStatus: InAir
|
|
||||||
- type: CanMoveInAir
|
|
||||||
- type: Tag
|
- type: Tag
|
||||||
tags:
|
tags:
|
||||||
- BypassInteractionRangeChecks
|
- BypassInteractionRangeChecks
|
||||||
|
|||||||
@@ -1,3 +1,306 @@
|
|||||||
|
# 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.
|
||||||
|
noSpawn: true
|
||||||
|
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 }
|
||||||
|
|
||||||
|
# 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
|
||||||
|
noSpawn: true
|
||||||
|
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.
|
||||||
|
noSpawn: true
|
||||||
|
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"]
|
||||||
|
|
||||||
|
# Borgs
|
||||||
- type: entity
|
- type: entity
|
||||||
id: PlayerBorgGeneric
|
id: PlayerBorgGeneric
|
||||||
parent: BorgChassisGeneric
|
parent: BorgChassisGeneric
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
parent:
|
parent:
|
||||||
- BaseMob
|
- BaseMob
|
||||||
- MobDamageable
|
- MobDamageable
|
||||||
|
- MobPolymorphable
|
||||||
- MobCombat
|
- MobCombat
|
||||||
- StripableInventoryBase
|
- StripableInventoryBase
|
||||||
id: BaseMobSpecies
|
id: BaseMobSpecies
|
||||||
|
|||||||
@@ -41,11 +41,16 @@
|
|||||||
- type: CameraRecoil
|
- type: CameraRecoil
|
||||||
- type: MindContainer
|
- type: MindContainer
|
||||||
- type: MovementSpeedModifier
|
- type: MovementSpeedModifier
|
||||||
- type: Polymorphable
|
|
||||||
- type: StatusIcon
|
|
||||||
- type: RequireProjectileTarget
|
- type: RequireProjectileTarget
|
||||||
active: False
|
active: False
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
save: false
|
||||||
|
id: MobPolymorphable
|
||||||
|
abstract: true
|
||||||
|
components:
|
||||||
|
- type: Polymorphable
|
||||||
|
|
||||||
# Used for mobs that have health and can take damage.
|
# Used for mobs that have health and can take damage.
|
||||||
- type: entity
|
- type: entity
|
||||||
save: false
|
save: false
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
blockSpectators: true # otherwise they can play client-side music
|
blockSpectators: true # otherwise they can play client-side music
|
||||||
inHandsOnly: false
|
inHandsOnly: false
|
||||||
singleUser: true
|
singleUser: true
|
||||||
requireHands: true
|
requiresComplex: true
|
||||||
verbText: verb-instrument-openui
|
verbText: verb-instrument-openui
|
||||||
key: enum.InstrumentUiKey.Key
|
key: enum.InstrumentUiKey.Key
|
||||||
- type: InteractionOutline
|
- type: InteractionOutline
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
- type: PaperLabelType
|
- type: PaperLabelType
|
||||||
- type: ActivatableUI
|
- type: ActivatableUI
|
||||||
key: enum.PaperUiKey.Key
|
key: enum.PaperUiKey.Key
|
||||||
requireHands: false
|
requiresComplex: false
|
||||||
- type: UserInterface
|
- type: UserInterface
|
||||||
interfaces:
|
interfaces:
|
||||||
enum.PaperUiKey.Key:
|
enum.PaperUiKey.Key:
|
||||||
@@ -647,7 +647,7 @@
|
|||||||
- Paper
|
- Paper
|
||||||
- type: ActivatableUI
|
- type: ActivatableUI
|
||||||
key: enum.PaperUiKey.Key
|
key: enum.PaperUiKey.Key
|
||||||
requireHands: false
|
requiresComplex: false
|
||||||
- type: UserInterface
|
- type: UserInterface
|
||||||
interfaces:
|
interfaces:
|
||||||
enum.PaperUiKey.Key:
|
enum.PaperUiKey.Key:
|
||||||
|
|||||||
@@ -77,7 +77,6 @@
|
|||||||
map: ["base"]
|
map: ["base"]
|
||||||
- type: Input
|
- type: Input
|
||||||
context: human
|
context: human
|
||||||
- type: BlockMovement
|
|
||||||
- type: ToggleableGhostRole
|
- type: ToggleableGhostRole
|
||||||
examineTextMindPresent: positronic-brain-installed
|
examineTextMindPresent: positronic-brain-installed
|
||||||
examineTextMindSearching: positronic-brain-still-searching
|
examineTextMindSearching: positronic-brain-still-searching
|
||||||
@@ -90,6 +89,7 @@
|
|||||||
wipeVerbPopup: positronic-brain-wiped-device
|
wipeVerbPopup: positronic-brain-wiped-device
|
||||||
stopSearchVerbText: positronic-brain-stop-searching-verb-text
|
stopSearchVerbText: positronic-brain-stop-searching-verb-text
|
||||||
stopSearchVerbPopup: positronic-brain-stopped-searching
|
stopSearchVerbPopup: positronic-brain-stopped-searching
|
||||||
|
- type: BlockMovement
|
||||||
- type: Examiner
|
- type: Examiner
|
||||||
- type: BorgBrain
|
- type: BorgBrain
|
||||||
- type: IntrinsicRadioReceiver
|
- type: IntrinsicRadioReceiver
|
||||||
|
|||||||
@@ -66,7 +66,7 @@
|
|||||||
type: AccessOverriderBoundUserInterface
|
type: AccessOverriderBoundUserInterface
|
||||||
- type: ActivatableUI
|
- type: ActivatableUI
|
||||||
key: enum.AccessOverriderUiKey.Key
|
key: enum.AccessOverriderUiKey.Key
|
||||||
requireHands: true
|
requiresComplex: true
|
||||||
requireActiveHand: false
|
requireActiveHand: false
|
||||||
singleUser: true
|
singleUser: true
|
||||||
- type: ItemSlots
|
- type: ItemSlots
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
name: airlock
|
name: airlock
|
||||||
description: It opens, it closes, and maybe crushes you.
|
description: It opens, it closes, and maybe crushes you.
|
||||||
components:
|
components:
|
||||||
|
- type: StationAiWhitelist
|
||||||
- type: MeleeSound
|
- type: MeleeSound
|
||||||
soundGroups:
|
soundGroups:
|
||||||
Brute:
|
Brute:
|
||||||
@@ -104,6 +105,8 @@
|
|||||||
- type: SpawnOnOverload
|
- type: SpawnOnOverload
|
||||||
- type: UserInterface
|
- type: UserInterface
|
||||||
interfaces:
|
interfaces:
|
||||||
|
enum.AiUi.Key:
|
||||||
|
type: StationAiBoundUserInterface
|
||||||
enum.WiresUiKey.Key:
|
enum.WiresUiKey.Key:
|
||||||
type: WiresBoundUserInterface
|
type: WiresBoundUserInterface
|
||||||
- type: Airtight
|
- type: Airtight
|
||||||
|
|||||||
@@ -560,6 +560,7 @@
|
|||||||
name: communications computer
|
name: communications computer
|
||||||
description: A computer used to make station wide announcements via keyboard, set the appropriate alert level, and call the emergency shuttle.
|
description: A computer used to make station wide announcements via keyboard, set the appropriate alert level, and call the emergency shuttle.
|
||||||
components:
|
components:
|
||||||
|
- type: StationAiWhitelist
|
||||||
- type: Sprite
|
- type: Sprite
|
||||||
layers:
|
layers:
|
||||||
- map: ["computerLayerBody"]
|
- map: ["computerLayerBody"]
|
||||||
@@ -1117,3 +1118,46 @@
|
|||||||
access: [["ResearchDirector"]]
|
access: [["ResearchDirector"]]
|
||||||
- type: Lock
|
- type: Lock
|
||||||
unlockOnClick: false
|
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
|
||||||
|
|||||||
@@ -96,7 +96,7 @@
|
|||||||
type: WiresBoundUserInterface
|
type: WiresBoundUserInterface
|
||||||
- type: ActivatableUI
|
- type: ActivatableUI
|
||||||
key: enum.HealthAnalyzerUiKey.Key
|
key: enum.HealthAnalyzerUiKey.Key
|
||||||
requireHands: false
|
requiresComplex: false
|
||||||
- type: ActivatableUIRequiresPower
|
- type: ActivatableUIRequiresPower
|
||||||
- type: PointLight
|
- type: PointLight
|
||||||
color: "#3a807f"
|
color: "#3a807f"
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
placement:
|
placement:
|
||||||
mode: SnapgridCenter
|
mode: SnapgridCenter
|
||||||
components:
|
components:
|
||||||
|
- type: StationAiWhitelist
|
||||||
- type: AmbientOnPowered
|
- type: AmbientOnPowered
|
||||||
- type: AmbientSound
|
- type: AmbientSound
|
||||||
volume: -9
|
volume: -9
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
description: An intercom. For when the station just needs to know something.
|
description: An intercom. For when the station just needs to know something.
|
||||||
abstract: true
|
abstract: true
|
||||||
components:
|
components:
|
||||||
|
- type: StationAiWhitelist
|
||||||
- type: WallMount
|
- type: WallMount
|
||||||
- type: ApcPowerReceiver
|
- type: ApcPowerReceiver
|
||||||
- type: Electrified
|
- type: Electrified
|
||||||
|
|||||||
@@ -4,6 +4,28 @@
|
|||||||
name: camera
|
name: camera
|
||||||
description: A surveillance camera. It's watching you. Kinda.
|
description: A surveillance camera. It's watching you. Kinda.
|
||||||
components:
|
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: StationAiVision
|
||||||
- type: Clickable
|
- type: Clickable
|
||||||
- type: InteractionOutline
|
- type: InteractionOutline
|
||||||
@@ -43,6 +65,8 @@
|
|||||||
InUse: camera_in_use
|
InUse: camera_in_use
|
||||||
- type: UserInterface
|
- type: UserInterface
|
||||||
interfaces:
|
interfaces:
|
||||||
|
enum.AiUi.Key:
|
||||||
|
type: StationAiBoundUserInterface
|
||||||
enum.SurveillanceCameraSetupUiKey.Camera:
|
enum.SurveillanceCameraSetupUiKey.Camera:
|
||||||
type: SurveillanceCameraSetupBoundUi
|
type: SurveillanceCameraSetupBoundUi
|
||||||
enum.WiresUiKey.Key:
|
enum.WiresUiKey.Key:
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
snap:
|
snap:
|
||||||
- Wallmount
|
- Wallmount
|
||||||
components:
|
components:
|
||||||
|
- type: StationAiWhitelist
|
||||||
- type: Transform
|
- type: Transform
|
||||||
anchored: true
|
anchored: true
|
||||||
- type: WallMount
|
- type: WallMount
|
||||||
|
|||||||
@@ -25,6 +25,11 @@
|
|||||||
- Trinkets
|
- Trinkets
|
||||||
- GroupSpeciesBreathTool
|
- GroupSpeciesBreathTool
|
||||||
|
|
||||||
|
# Silicons
|
||||||
|
- type: roleLoadout
|
||||||
|
id: JobStationAi
|
||||||
|
nameDataset: names_ai
|
||||||
|
|
||||||
# Civilian
|
# Civilian
|
||||||
- type: roleLoadout
|
- type: roleLoadout
|
||||||
id: JobPassenger
|
id: JobPassenger
|
||||||
|
|||||||
@@ -1,3 +1,18 @@
|
|||||||
|
# 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:RoleTimeRequirement
|
||||||
|
role: JobBorg
|
||||||
|
time: 18000 # 5 hrs
|
||||||
|
canBeAntag: false
|
||||||
|
icon: JobIconStationAi
|
||||||
|
supervisors: job-supervisors-rd
|
||||||
|
jobEntity: StationAiBrain
|
||||||
|
|
||||||
- type: job
|
- type: job
|
||||||
id: Borg
|
id: Borg
|
||||||
name: job-name-borg
|
name: job-name-borg
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user