mirror of
https://github.com/space-syndicate/space-station-14.git
synced 2026-10-17 14:21:13 +03:00
Merge remote-tracking branch 'upstream/master' into master-syndie
# Conflicts: # .github/PULL_REQUEST_TEMPLATE.md # Resources/Prototypes/Datasets/Names/last.yml
This commit is contained in:
commit
e32c928ff8
53 changed files with 717 additions and 270 deletions
|
|
@ -379,7 +379,7 @@ namespace Content.Client.DragDrop
|
|||
// TODO: Duplicated in SpriteSystem and TargetOutlineSystem. Should probably be cached somewhere for a frame?
|
||||
var mousePos = _eyeManager.ScreenToMap(_inputManager.MouseScreenPosition).Position;
|
||||
var bounds = new Box2(mousePos - 1.5f, mousePos + 1.5f);
|
||||
var pvsEntities = _lookup.GetEntitiesIntersecting(_eyeManager.CurrentMap, bounds, LookupFlags.Approximate | LookupFlags.Anchored);
|
||||
var pvsEntities = _lookup.GetEntitiesIntersecting(_eyeManager.CurrentMap, bounds, LookupFlags.Approximate | LookupFlags.Static);
|
||||
foreach (var pvsEntity in pvsEntities)
|
||||
{
|
||||
if (!EntityManager.TryGetComponent(pvsEntity, out SpriteComponent? inRangeSprite) ||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
using Content.Shared.Light.Component;
|
||||
using Content.Shared.Light.Component;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Client.Light.Components
|
||||
{
|
||||
|
|
|
|||
18
Content.Client/Light/EntitySystems/ExpendableLightSystem.cs
Normal file
18
Content.Client/Light/EntitySystems/ExpendableLightSystem.cs
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
using Content.Client.Light.Components;
|
||||
|
||||
namespace Content.Client.Light.EntitySystems;
|
||||
|
||||
public sealed class ExpendableLightSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<ExpendableLightComponent, ComponentShutdown>(OnLightShutdown);
|
||||
}
|
||||
|
||||
private void OnLightShutdown(EntityUid uid, ExpendableLightComponent component, ComponentShutdown args)
|
||||
{
|
||||
component.PlayingStream?.Stop();
|
||||
}
|
||||
}
|
||||
|
|
@ -2,10 +2,6 @@ using Content.Client.Light.Components;
|
|||
using Content.Shared.Light.Component;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
namespace Content.Client.Light.Visualizers
|
||||
{
|
||||
|
|
@ -35,31 +31,21 @@ namespace Content.Client.Light.Visualizers
|
|||
}
|
||||
}
|
||||
|
||||
void TryStopStream(IPlayingAudioStream? stream)
|
||||
{
|
||||
stream?.Stop();
|
||||
}
|
||||
|
||||
if (component.TryGetData(ExpendableLightVisuals.State, out ExpendableLightState state)
|
||||
&& entities.TryGetComponent(component.Owner, out ExpendableLightComponent? expendableLight))
|
||||
&& entities.TryGetComponent(component.Owner, out ExpendableLightComponent? expendableLight))
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case ExpendableLightState.Lit:
|
||||
{
|
||||
TryStopStream(expendableLight.PlayingStream);
|
||||
if (expendableLight.LoopedSound != null)
|
||||
{
|
||||
expendableLight.PlayingStream = SoundSystem.Play(expendableLight.LoopedSound, Filter.Local(),
|
||||
expendableLight.Owner, SharedExpendableLightComponent.LoopedSoundParams.WithLoop(true));
|
||||
}
|
||||
expendableLight.PlayingStream?.Stop();
|
||||
expendableLight.PlayingStream = entities.EntitySysManager.GetEntitySystem<SharedAudioSystem>().PlayPvs(
|
||||
expendableLight.LoopedSound,
|
||||
expendableLight.Owner,
|
||||
SharedExpendableLightComponent.LoopedSoundParams);
|
||||
break;
|
||||
}
|
||||
case ExpendableLightState.Dead:
|
||||
{
|
||||
TryStopStream(expendableLight.PlayingStream);
|
||||
expendableLight.PlayingStream?.Stop();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -117,7 +117,7 @@ public sealed class TargetOutlineSystem : EntitySystem
|
|||
// TODO: Duplicated in SpriteSystem and DragDropSystem. Should probably be cached somewhere for a frame?
|
||||
var mousePos = _eyeManager.ScreenToMap(_inputManager.MouseScreenPosition).Position;
|
||||
var bounds = new Box2(mousePos - LookupSize / 2f, mousePos + LookupSize / 2f);
|
||||
var pvsEntities = _lookup.GetEntitiesIntersecting(_eyeManager.CurrentMap, bounds, LookupFlags.Approximate | LookupFlags.Anchored);
|
||||
var pvsEntities = _lookup.GetEntitiesIntersecting(_eyeManager.CurrentMap, bounds, LookupFlags.Approximate | LookupFlags.Static);
|
||||
var spriteQuery = GetEntityQuery<SpriteComponent>();
|
||||
|
||||
foreach (var entity in pvsEntities)
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ public sealed class TetherGunSystem : SharedTetherGunSystem
|
|||
var bodyQuery = GetEntityQuery<PhysicsComponent>();
|
||||
var lowest = new List<(int DrawDepth, uint RenderOrder, EntityUid Entity)>();
|
||||
|
||||
foreach (var ent in _lookup.GetEntitiesIntersecting(mousePos, LookupFlags.Approximate | LookupFlags.Anchored))
|
||||
foreach (var ent in _lookup.GetEntitiesIntersecting(mousePos, LookupFlags.Approximate | LookupFlags.Static))
|
||||
{
|
||||
if (!bodyQuery.HasComponent(ent) ||
|
||||
!TryComp<ClickableComponent>(ent, out var clickable) ||
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@ namespace Content.Server.Chat
|
|||
}
|
||||
|
||||
// Suicide by nearby entity (ex: Microwave)
|
||||
foreach (var entity in _entityLookupSystem.GetEntitiesInRange(victim, 1, LookupFlags.Approximate | LookupFlags.Anchored))
|
||||
foreach (var entity in _entityLookupSystem.GetEntitiesInRange(victim, 1, LookupFlags.Approximate | LookupFlags.Static))
|
||||
{
|
||||
// Skip any nearby items that can be picked up, we already checked the active held item above
|
||||
if (itemQuery.HasComponent(entity))
|
||||
|
|
|
|||
|
|
@ -43,34 +43,37 @@ public sealed partial class ChatSystem
|
|||
private (string, RadioChannelPrototype?) GetRadioPrefix(EntityUid source, string message)
|
||||
{
|
||||
// TODO: Turn common into a true frequency and support multiple aliases.
|
||||
var channelMessage = message.StartsWith(':') || message.StartsWith('.');
|
||||
var radioMessage = message.StartsWith(';') || channelMessage;
|
||||
if (!radioMessage) return (message, null);
|
||||
var isRadioMessage = false;
|
||||
RadioChannelPrototype? channel = null;
|
||||
// First check if this is a message to the base radio frequency
|
||||
if (message.StartsWith(';'))
|
||||
{
|
||||
// First Remove semicolon
|
||||
channel = _prototypeManager.Index<RadioChannelPrototype>("Common");
|
||||
message = message[1..].TrimStart();
|
||||
isRadioMessage = true;
|
||||
}
|
||||
|
||||
// Check now if the remaining message is a targeted radio message
|
||||
if ((message.StartsWith(':') || message.StartsWith('.')) && message.Length >= 2)
|
||||
{
|
||||
// Strip remaining message prefix.
|
||||
_keyCodes.TryGetValue(message[1], out channel);
|
||||
message = message[2..].TrimStart();
|
||||
isRadioMessage = true;
|
||||
}
|
||||
|
||||
// If not a radio message at all
|
||||
if (!isRadioMessage) return (message, null);
|
||||
|
||||
// Special case for empty messages
|
||||
if (message.Length <= 1)
|
||||
return (string.Empty, null);
|
||||
|
||||
// Look for a prefix indicating a destination radio channel.
|
||||
RadioChannelPrototype? chan;
|
||||
if (channelMessage && message.Length >= 2)
|
||||
if (channel == null)
|
||||
{
|
||||
_keyCodes.TryGetValue(message[1], out chan);
|
||||
|
||||
if (chan == null)
|
||||
{
|
||||
_popup.PopupEntity(Loc.GetString("chat-manager-no-such-channel"), source, Filter.Entities(source));
|
||||
chan = null;
|
||||
}
|
||||
|
||||
// Strip message prefix.
|
||||
message = message[2..].TrimStart();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Remove semicolon
|
||||
message = message[1..].TrimStart();
|
||||
chan = _prototypeManager.Index<RadioChannelPrototype>("Common");
|
||||
_popup.PopupEntity(Loc.GetString("chat-manager-no-such-channel"), source, Filter.Entities(source));
|
||||
channel = null;
|
||||
}
|
||||
|
||||
// Re-capitalize message since we removed the prefix.
|
||||
|
|
@ -83,6 +86,6 @@ public sealed partial class ChatSystem
|
|||
_popup.PopupEntity(Loc.GetString("chat-manager-no-headset-on-message"), source, Filter.Entities(source));
|
||||
}
|
||||
|
||||
return (message, chan);
|
||||
return (message, channel);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -151,11 +151,22 @@ public sealed partial class ChatSystem : SharedChatSystem
|
|||
if (string.IsNullOrEmpty(message))
|
||||
return;
|
||||
|
||||
// This message may have a radio prefix, and should then be whispered to the resolved radio channel
|
||||
if (checkRadioPrefix)
|
||||
{
|
||||
var (radioMessage, channel) = GetRadioPrefix(source, message);
|
||||
if (channel != null)
|
||||
{
|
||||
SendEntityWhisper(source, radioMessage, hideChat, hideGlobalGhostChat, channel, nameOverride);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, send whatever type.
|
||||
switch (desiredType)
|
||||
{
|
||||
case InGameICChatType.Speak:
|
||||
SendEntitySpeak(source, message, hideChat, hideGlobalGhostChat, nameOverride, checkRadioPrefix);
|
||||
SendEntitySpeak(source, message, hideChat, hideGlobalGhostChat, nameOverride);
|
||||
break;
|
||||
case InGameICChatType.Whisper:
|
||||
SendEntityWhisper(source, message, hideChat, hideGlobalGhostChat, null, nameOverride);
|
||||
|
|
@ -255,26 +266,12 @@ public sealed partial class ChatSystem : SharedChatSystem
|
|||
|
||||
#region Private API
|
||||
|
||||
private void SendEntitySpeak(EntityUid source, string originalMessage, bool hideChat, bool hideGlobalGhostChat, string? nameOverride, bool checkRadioPrefix)
|
||||
private void SendEntitySpeak(EntityUid source, string originalMessage, bool hideChat, bool hideGlobalGhostChat, string? nameOverride)
|
||||
{
|
||||
if (!_actionBlocker.CanSpeak(source))
|
||||
return;
|
||||
|
||||
RadioChannelPrototype? channel = null;
|
||||
string message;
|
||||
|
||||
if (checkRadioPrefix)
|
||||
(message, channel) = GetRadioPrefix(source, originalMessage);
|
||||
else
|
||||
message = originalMessage;
|
||||
|
||||
if (channel != null)
|
||||
{
|
||||
SendEntityWhisper(source, message, hideChat, hideGlobalGhostChat, channel, nameOverride);
|
||||
return;
|
||||
}
|
||||
|
||||
message = TransformSpeech(source, message);
|
||||
var message = TransformSpeech(source, originalMessage);
|
||||
if (message.Length == 0)
|
||||
return;
|
||||
|
||||
|
|
@ -297,7 +294,7 @@ public sealed partial class ChatSystem : SharedChatSystem
|
|||
|
||||
SendInVoiceRange(ChatChannel.Local, message, wrappedMessage, source, hideChat, hideGlobalGhostChat);
|
||||
|
||||
var ev = new EntitySpokeEvent(source, message, channel, null);
|
||||
var ev = new EntitySpokeEvent(source, message, null, null);
|
||||
RaiseLocalEvent(source, ev, true);
|
||||
|
||||
// To avoid logging any messages sent by entities that are not players, like vendors, cloning, etc.
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ namespace Content.Server.Construction.Conditions
|
|||
|
||||
var indices = transform.Coordinates.ToVector2i(entityManager, IoCManager.Resolve<IMapManager>());
|
||||
var lookup = entityManager.EntitySysManager.GetEntitySystem<EntityLookupSystem>();
|
||||
var entities = indices.GetEntitiesInTile(transform.GridUid.Value, LookupFlags.Approximate | LookupFlags.Anchored, lookup);
|
||||
var entities = indices.GetEntitiesInTile(transform.GridUid.Value, LookupFlags.Approximate | LookupFlags.Static, lookup);
|
||||
|
||||
foreach (var ent in entities)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -168,7 +168,7 @@ namespace Content.Server.Electrocution
|
|||
if (electrified.NoWindowInTile)
|
||||
{
|
||||
foreach (var entity in transform.Coordinates.GetEntitiesInTile(
|
||||
LookupFlags.Approximate | LookupFlags.Anchored, _entityLookup))
|
||||
LookupFlags.Approximate | LookupFlags.Static, _entityLookup))
|
||||
{
|
||||
if (_tagSystem.HasTag(entity, "Window"))
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -10,7 +10,9 @@ using Content.Server.Power.Components;
|
|||
using Content.Server.Power.EntitySystems;
|
||||
using Content.Server.Tools;
|
||||
using Content.Server.UserInterface;
|
||||
using Content.Shared.Administration.Logs;
|
||||
using Content.Shared.Containers.ItemSlots;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Emag.Systems;
|
||||
using Content.Shared.Fax;
|
||||
using Content.Shared.Interaction;
|
||||
|
|
@ -33,6 +35,7 @@ public sealed class FaxSystem : EntitySystem
|
|||
[Dependency] private readonly ToolSystem _toolSystem = default!;
|
||||
[Dependency] private readonly QuickDialogSystem _quickDialog = default!;
|
||||
[Dependency] private readonly UserInterfaceSystem _userInterface = default!;
|
||||
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
|
||||
|
||||
public const string PaperSlotId = "Paper";
|
||||
|
||||
|
|
@ -213,6 +216,8 @@ public sealed class FaxSystem : EntitySystem
|
|||
return;
|
||||
}
|
||||
|
||||
_adminLogger.Add(LogType.Action, LogImpact.Low,
|
||||
$"{ToPrettyString(args.User):user} renamed {ToPrettyString(uid)} from \"{component.FaxName}\" to \"{newName}\"");
|
||||
component.FaxName = newName;
|
||||
_popupSystem.PopupEntity(Loc.GetString("fax-machine-popup-name-set"), uid, Filter.Pvs(uid));
|
||||
UpdateUserInterface(uid, component);
|
||||
|
|
@ -283,7 +288,7 @@ public sealed class FaxSystem : EntitySystem
|
|||
|
||||
private void OnSendButtonPressed(EntityUid uid, FaxMachineComponent component, FaxSendMessage args)
|
||||
{
|
||||
Send(uid, component);
|
||||
Send(uid, component, args.Session.AttachedEntity);
|
||||
}
|
||||
|
||||
private void OnRefreshButtonPressed(EntityUid uid, FaxMachineComponent component, FaxRefreshMessage args)
|
||||
|
|
@ -363,7 +368,7 @@ public sealed class FaxSystem : EntitySystem
|
|||
/// Sends message to addressee if paper is set and a known fax is selected
|
||||
/// A timeout is set after sending
|
||||
/// </summary>
|
||||
public void Send(EntityUid uid, FaxMachineComponent? component = null)
|
||||
public void Send(EntityUid uid, FaxMachineComponent? component = null, EntityUid? sender = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component))
|
||||
return;
|
||||
|
|
@ -390,6 +395,8 @@ public sealed class FaxSystem : EntitySystem
|
|||
};
|
||||
_deviceNetworkSystem.QueuePacket(uid, component.DestinationFaxAddress, payload);
|
||||
|
||||
_adminLogger.Add(LogType.Action, LogImpact.Low, $"{(sender != null ? ToPrettyString(sender.Value) : "Unknown"):user} sent fax from \"{component.FaxName}\" {ToPrettyString(uid)} to {faxName} ({component.DestinationFaxAddress}): {paper.Content}");
|
||||
|
||||
component.SendTimeoutRemaining += component.SendTimeout;
|
||||
|
||||
_audioSystem.PlayPvs(component.SendSound, uid);
|
||||
|
|
@ -432,6 +439,8 @@ public sealed class FaxSystem : EntitySystem
|
|||
|
||||
if (TryComp<MetaDataComponent>(printed, out var metadata))
|
||||
metadata.EntityName = printout.Name;
|
||||
|
||||
_adminLogger.Add(LogType.Action, LogImpact.Low, $"\"{component.FaxName}\" {ToPrettyString(uid)} printed {ToPrettyString(printed)}: {printout.Content}");
|
||||
}
|
||||
|
||||
private void NotifyAdmins(string faxName)
|
||||
|
|
|
|||
|
|
@ -116,8 +116,12 @@ namespace Content.Server.Ghost
|
|||
eye.VisibilityMask |= (uint) VisibilityFlags.Ghost;
|
||||
}
|
||||
|
||||
component.TimeOfDeath = _gameTiming.RealTime;
|
||||
var time = _gameTiming.CurTime;
|
||||
component.TimeOfDeath = time;
|
||||
|
||||
// TODO ghost: remove once ghosts are persistent and aren't deleted when returning to body
|
||||
if (component.Action.UseDelay != null)
|
||||
component.Action.Cooldown = (time, time + component.Action.UseDelay.Value);
|
||||
_actions.AddAction(uid, component.Action, null);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ namespace Content.Server.Light.EntitySystems
|
|||
[Dependency] private readonly SharedItemSystem _item = default!;
|
||||
[Dependency] private readonly ClothingSystem _clothing = default!;
|
||||
[Dependency] private readonly TagSystem _tagSystem = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
|
|
@ -130,9 +131,7 @@ namespace Content.Server.Light.EntitySystems
|
|||
switch (component.CurrentState)
|
||||
{
|
||||
case ExpendableLightState.Lit:
|
||||
{
|
||||
SoundSystem.Play(component.LitSound.GetSound(), Filter.Pvs(component.Owner), component.Owner);
|
||||
|
||||
_audio.PlayPvs(component.LitSound, component.Owner);
|
||||
if (component.IconStateLit != string.Empty)
|
||||
{
|
||||
sprite.LayerSetState(2, component.IconStateLit);
|
||||
|
|
@ -141,22 +140,21 @@ namespace Content.Server.Light.EntitySystems
|
|||
|
||||
sprite.LayerSetVisible(1, true);
|
||||
break;
|
||||
}
|
||||
case ExpendableLightState.Fading:
|
||||
{
|
||||
break;
|
||||
}
|
||||
default:
|
||||
case ExpendableLightState.Dead:
|
||||
{
|
||||
if (component.DieSound != null)
|
||||
SoundSystem.Play(component.DieSound.GetSound(), Filter.Pvs(component.Owner), component.Owner);
|
||||
_audio.PlayPvs(component.DieSound, component.Owner);
|
||||
if (!string.IsNullOrEmpty(component.IconStateSpent))
|
||||
{
|
||||
sprite.LayerSetState(0, component.IconStateSpent);
|
||||
sprite.LayerSetShader(0, "shaded");
|
||||
}
|
||||
|
||||
sprite.LayerSetState(0, component.IconStateSpent);
|
||||
sprite.LayerSetShader(0, "shaded");
|
||||
sprite.LayerSetVisible(1, false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,38 +0,0 @@
|
|||
using Content.Server.NPC.Systems;
|
||||
|
||||
namespace Content.Server.NPC.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Stores data for RVO collision avoidance
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed class NPCRVOComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Maximum number of dynamic neighbors to consider for collision avoidance.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("maxNeighbors")]
|
||||
public int MaxNeighbors = 5;
|
||||
|
||||
/// <summary>
|
||||
/// Time horizon to consider for dynamic neighbor collision
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)] public float TimeHorizon = 3f;
|
||||
|
||||
/// <summary>
|
||||
/// Time horizon to consider for static neighbor collision.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)] public float ObstacleTimeHorizon = 3f;
|
||||
|
||||
/// <summary>
|
||||
/// Range considered for neighbor agents
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("neighborRange")]
|
||||
public float NeighborRange = 3f;
|
||||
|
||||
[ViewVariables]
|
||||
public readonly HashSet<EntityUid> ObstacleNeighbors = new();
|
||||
|
||||
[ViewVariables]
|
||||
public readonly HashSet<EntityUid> AgentNeighbors = new();
|
||||
}
|
||||
|
|
@ -42,6 +42,11 @@ public sealed class NPCBlackboard : IEnumerable<KeyValuePair<string, object>>
|
|||
/// </summary>
|
||||
public bool ReadOnly = false;
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
_blackboard.Clear();
|
||||
}
|
||||
|
||||
public NPCBlackboard ShallowClone()
|
||||
{
|
||||
var dict = new NPCBlackboard();
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ using Robust.Shared.Utility;
|
|||
|
||||
namespace Content.Server.NPC;
|
||||
|
||||
public sealed class NPCBlackboardSerializer : ITypeReader<NPCBlackboard, MappingDataNode>
|
||||
public sealed class NPCBlackboardSerializer : ITypeReader<NPCBlackboard, MappingDataNode>, ITypeCopier<NPCBlackboard>
|
||||
{
|
||||
public ValidationNode Validate(ISerializationManager serializationManager, MappingDataNode node,
|
||||
IDependencyCollection dependencies, ISerializationContext? context = null)
|
||||
|
|
@ -79,4 +79,17 @@ public sealed class NPCBlackboardSerializer : ITypeReader<NPCBlackboard, Mapping
|
|||
|
||||
return value;
|
||||
}
|
||||
|
||||
public void CopyTo(ISerializationManager serializationManager, NPCBlackboard source, ref NPCBlackboard target, bool skipHook,
|
||||
ISerializationContext? context = null)
|
||||
{
|
||||
target.Clear();
|
||||
using var enumerator = source.GetEnumerator();
|
||||
|
||||
while (enumerator.MoveNext())
|
||||
{
|
||||
var current = enumerator.Current;
|
||||
target.SetValue(current.Key, current.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,18 +45,16 @@ public sealed partial class PathfindingSystem
|
|||
{
|
||||
SubscribeLocalEvent<GridInitializeEvent>(OnGridInit);
|
||||
SubscribeLocalEvent<GridRemovalEvent>(OnGridRemoved);
|
||||
SubscribeLocalEvent<GridPathfindingComponent, EntityPausedEvent>(OnGridPathPause);
|
||||
SubscribeLocalEvent<GridPathfindingComponent, EntityUnpausedEvent>(OnGridPathPause);
|
||||
SubscribeLocalEvent<GridPathfindingComponent, ComponentShutdown>(OnGridPathShutdown);
|
||||
SubscribeLocalEvent<CollisionChangeEvent>(OnCollisionChange);
|
||||
SubscribeLocalEvent<PhysicsBodyTypeChangedEvent>(OnBodyTypeChange);
|
||||
SubscribeLocalEvent<MoveEvent>(OnMoveEvent);
|
||||
}
|
||||
|
||||
private void OnGridPathPause(EntityUid uid, GridPathfindingComponent component, EntityPausedEvent args)
|
||||
private void OnGridPathPause(EntityUid uid, GridPathfindingComponent component, ref EntityUnpausedEvent args)
|
||||
{
|
||||
// TODO: Need the offsets + time serializer. Mainly just need this here to ensure it gets update after load
|
||||
if (!args.Paused && component.NextUpdate < _timing.CurTime)
|
||||
component.NextUpdate = _timing.CurTime;
|
||||
component.NextUpdate += args.PausedTime;
|
||||
}
|
||||
|
||||
private void OnGridPathShutdown(EntityUid uid, GridPathfindingComponent component, ComponentShutdown args)
|
||||
|
|
|
|||
|
|
@ -1,38 +0,0 @@
|
|||
using Content.Shared.CCVar;
|
||||
using Robust.Shared.Configuration;
|
||||
|
||||
namespace Content.Server.NPC.Systems;
|
||||
|
||||
public sealed partial class NPCSteeringSystem
|
||||
{
|
||||
// TODO
|
||||
|
||||
// Derived from RVO2 library which uses ORCA (optimal reciprocal collision avoidance).
|
||||
// Could also potentially use something force based or RVO or detour crowd.
|
||||
|
||||
public bool CollisionAvoidanceEnabled { get; set; } = true;
|
||||
|
||||
public bool ObstacleAvoidanceEnabled { get; set; } = true;
|
||||
|
||||
private const float Radius = 0.35f;
|
||||
private const float RVO_EPSILON = 0.00001f;
|
||||
|
||||
private void InitializeAvoidance()
|
||||
{
|
||||
var configManager = IoCManager.Resolve<IConfigurationManager>();
|
||||
configManager.OnValueChanged(CCVars.NPCCollisionAvoidance, SetCollisionAvoidance);
|
||||
}
|
||||
|
||||
private void ShutdownAvoidance()
|
||||
{
|
||||
var configManager = IoCManager.Resolve<IConfigurationManager>();
|
||||
configManager.UnsubValueChanged(CCVars.NPCCollisionAvoidance, SetCollisionAvoidance);
|
||||
}
|
||||
|
||||
// I deleted all of my relevant code for now as I only had dynamic body avoidance working and not static
|
||||
// but it will be added back real soon.
|
||||
private void SetCollisionAvoidance(bool obj)
|
||||
{
|
||||
CollisionAvoidanceEnabled = obj;
|
||||
}
|
||||
}
|
||||
|
|
@ -86,7 +86,7 @@ public sealed partial class NPCSteeringSystem
|
|||
}
|
||||
else
|
||||
{
|
||||
arrivalDistance = SharedInteractionSystem.InteractionRange - 0.8f;
|
||||
arrivalDistance = SharedInteractionSystem.InteractionRange - 0.65f;
|
||||
}
|
||||
|
||||
// Check if mapids match.
|
||||
|
|
@ -105,9 +105,15 @@ public sealed partial class NPCSteeringSystem
|
|||
if (direction.Length <= arrivalDistance)
|
||||
{
|
||||
// Node needs some kind of special handling like access or smashing.
|
||||
if (steering.CurrentPath.TryPeek(out var node))
|
||||
if (steering.CurrentPath.TryPeek(out var node) && !node.Data.IsFreeSpace)
|
||||
{
|
||||
var status = TryHandleFlags(steering, node, bodyQuery);
|
||||
SteeringObstacleStatus status;
|
||||
|
||||
// Breaking behaviours and the likes.
|
||||
lock (_obstacles)
|
||||
{
|
||||
status = TryHandleFlags(steering, node, bodyQuery);
|
||||
}
|
||||
|
||||
// TODO: Need to handle re-pathing in case the target moves around.
|
||||
switch (status)
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
using Content.Server.CombatMode;
|
||||
using Content.Server.Destructible;
|
||||
using Content.Server.NPC.Components;
|
||||
using Content.Server.NPC.Pathfinding;
|
||||
using Content.Shared.Doors.Components;
|
||||
using Content.Shared.NPC;
|
||||
using Content.Shared.Weapons.Melee;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.NPC.Systems;
|
||||
|
||||
|
|
@ -33,9 +34,7 @@ public sealed partial class NPCSteeringSystem
|
|||
|
||||
private SteeringObstacleStatus TryHandleFlags(NPCSteeringComponent component, PathPoly poly, EntityQuery<PhysicsComponent> bodyQuery)
|
||||
{
|
||||
if (poly.Data.IsFreeSpace)
|
||||
return SteeringObstacleStatus.Completed;
|
||||
|
||||
DebugTools.Assert(!poly.Data.IsFreeSpace);
|
||||
// TODO: Store PathFlags on the steering comp
|
||||
// and be able to re-check it.
|
||||
|
||||
|
|
@ -111,26 +110,35 @@ public sealed partial class NPCSteeringSystem
|
|||
return SteeringObstacleStatus.Completed;
|
||||
}
|
||||
// Try smashing obstacles.
|
||||
else if ((component.Flags & PathFlags.Smashing) != 0x0 && TryComp<NPCMeleeCombatComponent>(component.Owner, out var melee) &&
|
||||
TryComp<MeleeWeaponComponent>(melee.Weapon, out var meleeWeapon))
|
||||
else if ((component.Flags & PathFlags.Smashing) != 0x0)
|
||||
{
|
||||
var destructibleQuery = GetEntityQuery<DestructibleComponent>();
|
||||
var meleeWeapon = _melee.GetWeapon(component.Owner);
|
||||
|
||||
// TODO: This is a hack around grilles and windows.
|
||||
_random.Shuffle(obstacleEnts);
|
||||
|
||||
foreach (var ent in obstacleEnts)
|
||||
if (meleeWeapon != null && meleeWeapon.NextAttack <= _timing.CurTime && TryComp<CombatModeComponent>(component.Owner, out var combatMode))
|
||||
{
|
||||
// TODO: Validate we can damage it
|
||||
if (destructibleQuery.HasComponent(ent))
|
||||
{
|
||||
_melee.AttemptLightAttack(component.Owner, meleeWeapon, ent);
|
||||
return SteeringObstacleStatus.Continuing;
|
||||
}
|
||||
}
|
||||
combatMode.IsInCombatMode = true;
|
||||
var destructibleQuery = GetEntityQuery<DestructibleComponent>();
|
||||
|
||||
if (obstacleEnts.Count == 0)
|
||||
return SteeringObstacleStatus.Completed;
|
||||
// TODO: This is a hack around grilles and windows.
|
||||
_random.Shuffle(obstacleEnts);
|
||||
|
||||
foreach (var ent in obstacleEnts)
|
||||
{
|
||||
// TODO: Validate we can damage it
|
||||
if (destructibleQuery.HasComponent(ent))
|
||||
{
|
||||
_melee.AttemptLightAttack(component.Owner, meleeWeapon, ent);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
combatMode.IsInCombatMode = false;
|
||||
|
||||
if (obstacleEnts.Count == 0)
|
||||
return SteeringObstacleStatus.Completed;
|
||||
|
||||
return SteeringObstacleStatus.Continuing;
|
||||
}
|
||||
}
|
||||
|
||||
return SteeringObstacleStatus.Failed;
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ namespace Content.Server.NPC.Systems
|
|||
|
||||
[Dependency] private readonly IAdminManager _admin = default!;
|
||||
[Dependency] private readonly IConfigurationManager _configManager = default!;
|
||||
[Dependency] private readonly IDependencyCollection _dependencies = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] private readonly IParallelManager _parallel = default!;
|
||||
|
|
@ -66,6 +67,8 @@ namespace Content.Server.NPC.Systems
|
|||
|
||||
private readonly HashSet<ICommonSession> _subscribedSessions = new();
|
||||
|
||||
private object _obstacles = new();
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
|
@ -76,7 +79,6 @@ namespace Content.Server.NPC.Systems
|
|||
}
|
||||
|
||||
UpdatesBefore.Add(typeof(SharedPhysicsSystem));
|
||||
InitializeAvoidance();
|
||||
_configManager.OnValueChanged(CCVars.NPCEnabled, SetNPCEnabled);
|
||||
_configManager.OnValueChanged(CCVars.NPCPathfinding, SetNPCPathfinding);
|
||||
|
||||
|
|
@ -114,7 +116,6 @@ namespace Content.Server.NPC.Systems
|
|||
public override void Shutdown()
|
||||
{
|
||||
base.Shutdown();
|
||||
ShutdownAvoidance();
|
||||
_configManager.UnsubValueChanged(CCVars.NPCEnabled, SetNPCEnabled);
|
||||
}
|
||||
|
||||
|
|
@ -131,6 +132,7 @@ namespace Content.Server.NPC.Systems
|
|||
|
||||
private void OnSteeringShutdown(EntityUid uid, NPCSteeringComponent component, ComponentShutdown args)
|
||||
{
|
||||
// Cancel any active pathfinding jobs as they're irrelevant.
|
||||
component.PathfindToken?.Cancel();
|
||||
}
|
||||
|
||||
|
|
@ -151,7 +153,6 @@ namespace Content.Server.NPC.Systems
|
|||
component.Flags = _pathfindingSystem.GetFlags(uid);
|
||||
}
|
||||
|
||||
EnsureComp<NPCRVOComponent>(uid);
|
||||
component.Coordinates = coordinates;
|
||||
return component;
|
||||
}
|
||||
|
|
@ -185,7 +186,6 @@ namespace Content.Server.NPC.Systems
|
|||
|
||||
component.PathfindToken?.Cancel();
|
||||
component.PathfindToken = null;
|
||||
RemComp<NPCRVOComponent>(uid);
|
||||
RemComp<NPCSteeringComponent>(uid);
|
||||
}
|
||||
|
||||
|
|
@ -203,7 +203,7 @@ namespace Content.Server.NPC.Systems
|
|||
|
||||
var npcs = EntityQuery<NPCSteeringComponent, ActiveNPCComponent, InputMoverComponent, TransformComponent>()
|
||||
.ToArray();
|
||||
var options = new ParallelOptions()
|
||||
var options = new ParallelOptions
|
||||
{
|
||||
MaxDegreeOfParallelism = _parallel.ParallelProcessCount,
|
||||
};
|
||||
|
|
@ -261,6 +261,8 @@ namespace Content.Server.NPC.Systems
|
|||
EntityQuery<TransformComponent> xformQuery,
|
||||
float frameTime)
|
||||
{
|
||||
IoCManager.InitThread(_dependencies, replaceExisting: true);
|
||||
|
||||
if (Deleted(steering.Coordinates.EntityId))
|
||||
{
|
||||
SetDirection(mover, steering, Vector2.Zero);
|
||||
|
|
|
|||
|
|
@ -34,15 +34,22 @@ namespace Content.Server.Power.EntitySystems
|
|||
SubscribeLocalEvent<ApcPowerReceiverComponent, ComponentInit>(ApcPowerReceiverInit);
|
||||
SubscribeLocalEvent<ApcPowerReceiverComponent, ComponentShutdown>(ApcPowerReceiverShutdown);
|
||||
SubscribeLocalEvent<ApcPowerReceiverComponent, EntityPausedEvent>(ApcPowerReceiverPaused);
|
||||
SubscribeLocalEvent<ApcPowerReceiverComponent, EntityUnpausedEvent>(ApcPowerReceiverUnpaused);
|
||||
|
||||
SubscribeLocalEvent<PowerNetworkBatteryComponent, ComponentInit>(BatteryInit);
|
||||
SubscribeLocalEvent<PowerNetworkBatteryComponent, ComponentShutdown>(BatteryShutdown);
|
||||
SubscribeLocalEvent<PowerNetworkBatteryComponent, EntityPausedEvent>(BatteryPaused);
|
||||
SubscribeLocalEvent<PowerNetworkBatteryComponent, EntityUnpausedEvent>(BatteryUnpaused);
|
||||
|
||||
SubscribeLocalEvent<PowerConsumerComponent, ComponentInit>(PowerConsumerInit);
|
||||
SubscribeLocalEvent<PowerConsumerComponent, ComponentShutdown>(PowerConsumerShutdown);
|
||||
SubscribeLocalEvent<PowerConsumerComponent, EntityPausedEvent>(PowerConsumerPaused);
|
||||
SubscribeLocalEvent<PowerConsumerComponent, EntityUnpausedEvent>(PowerConsumerUnpaused);
|
||||
|
||||
SubscribeLocalEvent<PowerSupplierComponent, ComponentInit>(PowerSupplierInit);
|
||||
SubscribeLocalEvent<PowerSupplierComponent, ComponentShutdown>(PowerSupplierShutdown);
|
||||
SubscribeLocalEvent<PowerSupplierComponent, EntityPausedEvent>(PowerSupplierPaused);
|
||||
SubscribeLocalEvent<PowerSupplierComponent, EntityUnpausedEvent>(PowerSupplierUnpaused);
|
||||
}
|
||||
|
||||
private void ApcPowerReceiverInit(EntityUid uid, ApcPowerReceiverComponent component, ComponentInit args)
|
||||
|
|
@ -59,9 +66,17 @@ namespace Content.Server.Power.EntitySystems
|
|||
private static void ApcPowerReceiverPaused(
|
||||
EntityUid uid,
|
||||
ApcPowerReceiverComponent component,
|
||||
EntityPausedEvent args)
|
||||
ref EntityPausedEvent args)
|
||||
{
|
||||
component.NetworkLoad.Paused = args.Paused;
|
||||
component.NetworkLoad.Paused = true;
|
||||
}
|
||||
|
||||
private static void ApcPowerReceiverUnpaused(
|
||||
EntityUid uid,
|
||||
ApcPowerReceiverComponent component,
|
||||
ref EntityUnpausedEvent args)
|
||||
{
|
||||
component.NetworkLoad.Paused = false;
|
||||
}
|
||||
|
||||
private void BatteryInit(EntityUid uid, PowerNetworkBatteryComponent component, ComponentInit args)
|
||||
|
|
@ -74,9 +89,14 @@ namespace Content.Server.Power.EntitySystems
|
|||
_powerState.Batteries.Free(component.NetworkBattery.Id);
|
||||
}
|
||||
|
||||
private static void BatteryPaused(EntityUid uid, PowerNetworkBatteryComponent component, EntityPausedEvent args)
|
||||
private static void BatteryPaused(EntityUid uid, PowerNetworkBatteryComponent component, ref EntityPausedEvent args)
|
||||
{
|
||||
component.NetworkBattery.Paused = args.Paused;
|
||||
component.NetworkBattery.Paused = true;
|
||||
}
|
||||
|
||||
private static void BatteryUnpaused(EntityUid uid, PowerNetworkBatteryComponent component, ref EntityUnpausedEvent args)
|
||||
{
|
||||
component.NetworkBattery.Paused = false;
|
||||
}
|
||||
|
||||
private void PowerConsumerInit(EntityUid uid, PowerConsumerComponent component, ComponentInit args)
|
||||
|
|
@ -89,9 +109,14 @@ namespace Content.Server.Power.EntitySystems
|
|||
_powerState.Loads.Free(component.NetworkLoad.Id);
|
||||
}
|
||||
|
||||
private static void PowerConsumerPaused(EntityUid uid, PowerConsumerComponent component, EntityPausedEvent args)
|
||||
private static void PowerConsumerPaused(EntityUid uid, PowerConsumerComponent component, ref EntityPausedEvent args)
|
||||
{
|
||||
component.NetworkLoad.Paused = args.Paused;
|
||||
component.NetworkLoad.Paused = true;
|
||||
}
|
||||
|
||||
private static void PowerConsumerUnpaused(EntityUid uid, PowerConsumerComponent component, ref EntityUnpausedEvent args)
|
||||
{
|
||||
component.NetworkLoad.Paused = false;
|
||||
}
|
||||
|
||||
private void PowerSupplierInit(EntityUid uid, PowerSupplierComponent component, ComponentInit args)
|
||||
|
|
@ -104,9 +129,14 @@ namespace Content.Server.Power.EntitySystems
|
|||
_powerState.Supplies.Free(component.NetworkSupply.Id);
|
||||
}
|
||||
|
||||
private static void PowerSupplierPaused(EntityUid uid, PowerSupplierComponent component, EntityPausedEvent args)
|
||||
private static void PowerSupplierPaused(EntityUid uid, PowerSupplierComponent component, ref EntityPausedEvent args)
|
||||
{
|
||||
component.NetworkSupply.Paused = args.Paused;
|
||||
component.NetworkSupply.Paused = true;
|
||||
}
|
||||
|
||||
private static void PowerSupplierUnpaused(EntityUid uid, PowerSupplierComponent component, ref EntityUnpausedEvent args)
|
||||
{
|
||||
component.NetworkSupply.Paused = false;
|
||||
}
|
||||
|
||||
public void InitPowerNet(PowerNet powerNet)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using Content.Server.Radio.EntitySystems;
|
||||
using Content.Shared.Radio;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
|
||||
|
||||
namespace Content.Server.Radio.Components;
|
||||
|
||||
|
|
@ -15,10 +16,23 @@ public sealed class RadioMicrophoneComponent : Component
|
|||
[DataField("broadcastChannel", customTypeSerializer: typeof(PrototypeIdSerializer<RadioChannelPrototype>))]
|
||||
public string BroadcastChannel = "Common";
|
||||
|
||||
[ViewVariables, DataField("supportedChannels", customTypeSerializer: typeof(PrototypeIdListSerializer<RadioChannelPrototype>))]
|
||||
public List<string>? SupportedChannels;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("listenRange")]
|
||||
public int ListenRange = 4;
|
||||
|
||||
[DataField("enabled")]
|
||||
public bool Enabled = false;
|
||||
|
||||
[DataField("powerRequired")]
|
||||
public bool PowerRequired = false;
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not the speaker must have an
|
||||
/// unobstructed path to the radio to speak
|
||||
/// </summary>
|
||||
[DataField("unobstructedRequired")]
|
||||
public bool UnobstructedRequired = false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,16 @@
|
|||
using Content.Server.Chat.Systems;
|
||||
using Content.Server.Interaction;
|
||||
using Content.Server.Popups;
|
||||
using Content.Server.Power.Components;
|
||||
using Content.Server.Power.EntitySystems;
|
||||
using Content.Server.Radio.Components;
|
||||
using Content.Server.Speech;
|
||||
using Content.Server.Speech.Components;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Radio;
|
||||
using Content.Shared.Verbs;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
|
|
@ -20,6 +25,8 @@ public sealed class RadioDeviceSystem : EntitySystem
|
|||
[Dependency] private readonly PopupSystem _popup = default!;
|
||||
[Dependency] private readonly ChatSystem _chat = default!;
|
||||
[Dependency] private readonly RadioSystem _radio = default!;
|
||||
[Dependency] private readonly InteractionSystem _interaction = default!;
|
||||
[Dependency] private readonly AppearanceSystem _appearance = default!;
|
||||
|
||||
// Used to prevent a shitter from using a bunch of radios to spam chat.
|
||||
private HashSet<(string, EntityUid)> _recentlySent = new();
|
||||
|
|
@ -31,6 +38,9 @@ public sealed class RadioDeviceSystem : EntitySystem
|
|||
SubscribeLocalEvent<RadioMicrophoneComponent, ExaminedEvent>(OnExamine);
|
||||
SubscribeLocalEvent<RadioMicrophoneComponent, ActivateInWorldEvent>(OnActivateMicrophone);
|
||||
SubscribeLocalEvent<RadioMicrophoneComponent, ListenEvent>(OnListen);
|
||||
SubscribeLocalEvent<RadioMicrophoneComponent, ListenAttemptEvent>(OnAttemptListen);
|
||||
SubscribeLocalEvent<RadioMicrophoneComponent, GetVerbsEvent<Verb>>(OnGetVerbs);
|
||||
SubscribeLocalEvent<RadioMicrophoneComponent, PowerChangedEvent>(OnPowerChanged);
|
||||
|
||||
SubscribeLocalEvent<RadioSpeakerComponent, ComponentInit>(OnSpeakerInit);
|
||||
SubscribeLocalEvent<RadioSpeakerComponent, ActivateInWorldEvent>(OnActivateSpeaker);
|
||||
|
|
@ -80,7 +90,10 @@ public sealed class RadioDeviceSystem : EntitySystem
|
|||
if (!Resolve(uid, ref component))
|
||||
return;
|
||||
|
||||
component.Enabled = !component.Enabled;
|
||||
if (component.PowerRequired && !this.IsPowered(uid, EntityManager))
|
||||
return;
|
||||
|
||||
SetMicrophoneEnabled(uid, !component.Enabled, component);
|
||||
|
||||
if (!quiet)
|
||||
{
|
||||
|
|
@ -95,6 +108,55 @@ public sealed class RadioDeviceSystem : EntitySystem
|
|||
RemCompDeferred<ActiveListenerComponent>(uid);
|
||||
}
|
||||
|
||||
private void OnGetVerbs(EntityUid uid, RadioMicrophoneComponent component, GetVerbsEvent<Verb> args)
|
||||
{
|
||||
if (!args.CanAccess || !args.CanInteract || args.Hands == null)
|
||||
return;
|
||||
|
||||
if (component.SupportedChannels == null || component.SupportedChannels.Count <= 1)
|
||||
return;
|
||||
|
||||
if (component.PowerRequired && !this.IsPowered(uid, EntityManager))
|
||||
return;
|
||||
|
||||
foreach (var channel in component.SupportedChannels)
|
||||
{
|
||||
var proto = _protoMan.Index<RadioChannelPrototype>(channel);
|
||||
|
||||
var v = new Verb
|
||||
{
|
||||
Text = proto.LocalizedName,
|
||||
Priority = 1,
|
||||
Category = VerbCategory.ChannelSelect,
|
||||
Disabled = component.BroadcastChannel == channel,
|
||||
DoContactInteraction = true,
|
||||
Act = () =>
|
||||
{
|
||||
component.BroadcastChannel = channel;
|
||||
_popup.PopupEntity(Loc.GetString("handheld-radio-component-channel-set",
|
||||
("channel", channel)), uid, Filter.Entities(args.User));
|
||||
}
|
||||
};
|
||||
args.Verbs.Add(v);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPowerChanged(EntityUid uid, RadioMicrophoneComponent component, ref PowerChangedEvent args)
|
||||
{
|
||||
if (args.Powered)
|
||||
return;
|
||||
SetMicrophoneEnabled(uid, false, component);
|
||||
}
|
||||
|
||||
public void SetMicrophoneEnabled(EntityUid uid, bool enabled, RadioMicrophoneComponent? component = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component, false))
|
||||
return;
|
||||
|
||||
component.Enabled = enabled;
|
||||
_appearance.SetData(uid, RadioDeviceVisuals.Broadcasting, component.Enabled);
|
||||
}
|
||||
|
||||
public void ToggleRadioSpeaker(EntityUid uid, EntityUid user, bool quiet = false, RadioSpeakerComponent? component = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component))
|
||||
|
|
@ -121,8 +183,9 @@ public sealed class RadioDeviceSystem : EntitySystem
|
|||
if (!args.IsInDetailsRange)
|
||||
return;
|
||||
|
||||
var freq = _protoMan.Index<RadioChannelPrototype>(component.BroadcastChannel).Frequency;
|
||||
args.PushMarkup(Loc.GetString("handheld-radio-component-on-examine", ("frequency", freq)));
|
||||
var proto = _protoMan.Index<RadioChannelPrototype>(component.BroadcastChannel);
|
||||
args.PushMarkup(Loc.GetString("handheld-radio-component-on-examine", ("frequency", proto.Frequency)));
|
||||
args.PushMarkup(Loc.GetString("handheld-radio-component-chennel-examine", ("channel", proto.LocalizedName)));
|
||||
}
|
||||
|
||||
private void OnListen(EntityUid uid, RadioMicrophoneComponent component, ListenEvent args)
|
||||
|
|
@ -134,6 +197,15 @@ public sealed class RadioDeviceSystem : EntitySystem
|
|||
_radio.SendRadioMessage(args.Source, args.Message, _protoMan.Index<RadioChannelPrototype>(component.BroadcastChannel));
|
||||
}
|
||||
|
||||
private void OnAttemptListen(EntityUid uid, RadioMicrophoneComponent component, ListenAttemptEvent args)
|
||||
{
|
||||
if (component.PowerRequired && !this.IsPowered(uid, EntityManager)
|
||||
|| component.UnobstructedRequired && !_interaction.InRangeUnobstructed(args.Source, uid, 0))
|
||||
{
|
||||
args.Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnReceiveRadio(EntityUid uid, RadioSpeakerComponent component, RadioReceiveEvent args)
|
||||
{
|
||||
var nameEv = new TransformSpeakerNameEvent(args.Source, Name(args.Source));
|
||||
|
|
@ -143,6 +215,6 @@ public sealed class RadioDeviceSystem : EntitySystem
|
|||
("originalName", nameEv.Name));
|
||||
|
||||
var hideGlobalGhostChat = true; // log to chat so people can identity the speaker/source, but avoid clogging ghost chat if there are many radios
|
||||
_chat.TrySendInGameICMessage(uid, args.Message, InGameICChatType.Speak, false, nameOverride: name, hideGlobalGhostChat:hideGlobalGhostChat, checkRadioPrefix: false);
|
||||
_chat.TrySendInGameICMessage(uid, args.Message, InGameICChatType.Speak, false, nameOverride: name, hideGlobalGhostChat:hideGlobalGhostChat, checkRadioPrefix: false);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -235,7 +235,7 @@ public sealed partial class RevenantSystem
|
|||
value.PryTile();
|
||||
}
|
||||
|
||||
var lookup = _lookup.GetEntitiesInRange(uid, component.DefileRadius, LookupFlags.Approximate | LookupFlags.Anchored);
|
||||
var lookup = _lookup.GetEntitiesInRange(uid, component.DefileRadius, LookupFlags.Approximate | LookupFlags.Static);
|
||||
var tags = GetEntityQuery<TagComponent>();
|
||||
var entityStorage = GetEntityQuery<EntityStorageComponent>();
|
||||
var items = GetEntityQuery<ItemComponent>();
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ public sealed partial class ShuttleSystem
|
|||
SubscribeLocalEvent<FTLDestinationComponent, EntityPausedEvent>(OnDestinationPause);
|
||||
}
|
||||
|
||||
private void OnDestinationPause(EntityUid uid, FTLDestinationComponent component, EntityPausedEvent args)
|
||||
private void OnDestinationPause(EntityUid uid, FTLDestinationComponent component, ref EntityPausedEvent args)
|
||||
{
|
||||
_console.RefreshShuttleConsoles();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -542,8 +542,6 @@ namespace Content.Shared.CCVar
|
|||
/// </summary>
|
||||
public static readonly CVarDef<bool> NPCPathfinding = CVarDef.Create("npc.pathfinding", true);
|
||||
|
||||
public static readonly CVarDef<bool> NPCCollisionAvoidance = CVarDef.Create("npc.collision_avoidance", true);
|
||||
|
||||
/*
|
||||
* Net
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ namespace Content.Shared.Construction.Conditions
|
|||
{
|
||||
var result = false;
|
||||
|
||||
foreach (var entity in location.GetEntitiesInTile(LookupFlags.Approximate | LookupFlags.Anchored))
|
||||
foreach (var entity in location.GetEntitiesInTile(LookupFlags.Approximate | LookupFlags.Static))
|
||||
{
|
||||
if (IoCManager.Resolve<IEntityManager>().HasComponent<SharedCanBuildWindowOnTopComponent>(entity))
|
||||
result = true;
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ namespace Content.Shared.Construction.Conditions
|
|||
{
|
||||
var tagSystem = IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<TagSystem>();
|
||||
|
||||
foreach (var entity in location.GetEntitiesInTile(LookupFlags.Approximate | LookupFlags.Anchored))
|
||||
foreach (var entity in location.GetEntitiesInTile(LookupFlags.Approximate | LookupFlags.Static))
|
||||
{
|
||||
if (tagSystem.HasTag(entity, "Window"))
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -52,11 +52,11 @@ namespace Content.Shared.Light.Component
|
|||
[DataField("iconStateOn")]
|
||||
public string IconStateLit { get; set; } = string.Empty;
|
||||
|
||||
[DataField("litSound", required: true)]
|
||||
public SoundSpecifier LitSound { get; set; } = default!;
|
||||
[DataField("litSound")]
|
||||
public SoundSpecifier? LitSound { get; set; }
|
||||
|
||||
[DataField("loopedSound")]
|
||||
public string? LoopedSound { get; set; } = null;
|
||||
public SoundSpecifier? LoopedSound { get; set; }
|
||||
|
||||
[DataField("dieSound")]
|
||||
public SoundSpecifier? DieSound { get; set; } = null;
|
||||
|
|
|
|||
|
|
@ -175,7 +175,7 @@ namespace Content.Shared.Maps
|
|||
/// Helper that returns all entities in a turf.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static IEnumerable<EntityUid> GetEntitiesInTile(this TileRef turf, LookupFlags flags = LookupFlags.Anchored, EntityLookupSystem? lookupSystem = null)
|
||||
public static IEnumerable<EntityUid> GetEntitiesInTile(this TileRef turf, LookupFlags flags = LookupFlags.Static, EntityLookupSystem? lookupSystem = null)
|
||||
{
|
||||
lookupSystem ??= EntitySystem.Get<EntityLookupSystem>();
|
||||
|
||||
|
|
@ -188,7 +188,7 @@ namespace Content.Shared.Maps
|
|||
/// <summary>
|
||||
/// Helper that returns all entities in a turf.
|
||||
/// </summary>
|
||||
public static IEnumerable<EntityUid> GetEntitiesInTile(this EntityCoordinates coordinates, LookupFlags flags = LookupFlags.Anchored, EntityLookupSystem? lookupSystem = null)
|
||||
public static IEnumerable<EntityUid> GetEntitiesInTile(this EntityCoordinates coordinates, LookupFlags flags = LookupFlags.Static, EntityLookupSystem? lookupSystem = null)
|
||||
{
|
||||
var turf = coordinates.GetTileRef();
|
||||
|
||||
|
|
@ -201,7 +201,7 @@ namespace Content.Shared.Maps
|
|||
/// <summary>
|
||||
/// Helper that returns all entities in a turf.
|
||||
/// </summary>
|
||||
public static IEnumerable<EntityUid> GetEntitiesInTile(this Vector2i indices, EntityUid gridId, LookupFlags flags = LookupFlags.Anchored, EntityLookupSystem? lookupSystem = null)
|
||||
public static IEnumerable<EntityUid> GetEntitiesInTile(this Vector2i indices, EntityUid gridId, LookupFlags flags = LookupFlags.Static, EntityLookupSystem? lookupSystem = null)
|
||||
{
|
||||
return GetEntitiesInTile(indices.GetTileRef(gridId), flags, lookupSystem);
|
||||
}
|
||||
|
|
|
|||
15
Content.Shared/Radio/RadioVisuals.cs
Normal file
15
Content.Shared/Radio/RadioVisuals.cs
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.Radio;
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum RadioDeviceVisuals : byte
|
||||
{
|
||||
Broadcasting
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum RadioDeviceVisualLayers : byte
|
||||
{
|
||||
Broadcasting
|
||||
}
|
||||
|
|
@ -18,6 +18,7 @@ public abstract class SharedStealthSystem : EntitySystem
|
|||
SubscribeLocalEvent<StealthOnMoveComponent, MoveEvent>(OnMove);
|
||||
SubscribeLocalEvent<StealthOnMoveComponent, GetVisibilityModifiersEvent>(OnGetVisibilityModifiers);
|
||||
SubscribeLocalEvent<StealthComponent, EntityPausedEvent>(OnPaused);
|
||||
SubscribeLocalEvent<StealthComponent, EntityUnpausedEvent>(OnUnpaused);
|
||||
SubscribeLocalEvent<StealthComponent, ComponentInit>(OnInit);
|
||||
SubscribeLocalEvent<StealthComponent, ExamineAttemptEvent>(OnExamineAttempt);
|
||||
SubscribeLocalEvent<StealthComponent, ExaminedEvent>(OnExamined);
|
||||
|
|
@ -57,18 +58,16 @@ public abstract class SharedStealthSystem : EntitySystem
|
|||
Dirty(component);
|
||||
}
|
||||
|
||||
private void OnPaused(EntityUid uid, StealthComponent component, EntityPausedEvent args)
|
||||
private void OnPaused(EntityUid uid, StealthComponent component, ref EntityPausedEvent args)
|
||||
{
|
||||
if (args.Paused)
|
||||
{
|
||||
component.LastVisibility = GetVisibility(uid, component);
|
||||
component.LastUpdated = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
component.LastUpdated = _timing.CurTime;
|
||||
}
|
||||
component.LastVisibility = GetVisibility(uid, component);
|
||||
component.LastUpdated = null;
|
||||
Dirty(component);
|
||||
}
|
||||
|
||||
private void OnUnpaused(EntityUid uid, StealthComponent component, ref EntityUnpausedEvent args)
|
||||
{
|
||||
component.LastUpdated = _timing.CurTime;
|
||||
Dirty(component);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,23 +20,23 @@ public sealed class UseDelaySystem : EntitySystem
|
|||
SubscribeLocalEvent<UseDelayComponent, ComponentHandleState>(OnHandleState);
|
||||
|
||||
SubscribeLocalEvent<UseDelayComponent, EntityPausedEvent>(OnPaused);
|
||||
SubscribeLocalEvent<UseDelayComponent, EntityUnpausedEvent>(OnUnpaused);
|
||||
}
|
||||
|
||||
private void OnPaused(EntityUid uid, UseDelayComponent component, EntityPausedEvent args)
|
||||
private void OnPaused(EntityUid uid, UseDelayComponent component, ref EntityPausedEvent args)
|
||||
{
|
||||
if (args.Paused)
|
||||
{
|
||||
// This entity just got paused, but wasn't before
|
||||
if (component.DelayEndTime != null)
|
||||
component.RemainingDelay = _gameTiming.CurTime - component.DelayEndTime;
|
||||
// This entity just got paused, but wasn't before
|
||||
if (component.DelayEndTime != null)
|
||||
component.RemainingDelay = _gameTiming.CurTime - component.DelayEndTime;
|
||||
|
||||
_activeDelays.Remove(component);
|
||||
}
|
||||
else if (component.RemainingDelay == null)
|
||||
{
|
||||
// Got unpaused, but had no active delay
|
||||
_activeDelays.Remove(component);
|
||||
Dirty(component);
|
||||
}
|
||||
|
||||
private void OnUnpaused(EntityUid uid, UseDelayComponent component, ref EntityUnpausedEvent args)
|
||||
{
|
||||
if (component.RemainingDelay == null)
|
||||
return;
|
||||
}
|
||||
|
||||
// We got unpaused, resume the delay/cooldown. Currently this takes for granted that ItemCooldownComponent
|
||||
// handles the pausing on its own. I'm not even gonna check, because I CBF fixing it if it doesn't.
|
||||
|
|
|
|||
|
|
@ -76,8 +76,10 @@ namespace Content.Shared.Verbs
|
|||
public static readonly VerbCategory InstrumentStyle =
|
||||
new("verb-categories-instrument-style", null);
|
||||
|
||||
public static readonly VerbCategory ChannelSelect = new("verb-categories-channel-select", null);
|
||||
|
||||
public static readonly VerbCategory SetSensor = new("verb-categories-set-sensor", null);
|
||||
|
||||
|
||||
public static readonly VerbCategory Lever = new("verb-categories-lever", null);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,32 +1,4 @@
|
|||
Entries:
|
||||
- author: EmoGarbage404
|
||||
changes:
|
||||
- {message: Zombie move speed should no longer mispredict., type: Fix}
|
||||
id: 2255
|
||||
time: '2022-09-07T01:59:27.0000000+00:00'
|
||||
- author: Rane
|
||||
changes:
|
||||
- {message: Sleeping should behave more as expected with critical and dead mobs.,
|
||||
type: Fix}
|
||||
id: 2256
|
||||
time: '2022-09-07T04:37:33.0000000+00:00'
|
||||
- author: rolfero
|
||||
changes:
|
||||
- {message: Armors shows their armor values when examined!, type: Add}
|
||||
id: 2257
|
||||
time: '2022-09-08T04:11:22.0000000+00:00'
|
||||
- author: rolfero
|
||||
changes:
|
||||
- {message: 'You can examine hardsuits, magboots, etc to find out how much your
|
||||
speed is decreased from them.', type: Add}
|
||||
id: 2258
|
||||
time: '2022-09-08T11:09:34.0000000+00:00'
|
||||
- author: ashtronaut
|
||||
changes:
|
||||
- {message: Nanotrasen has unlocked the gas analyzer's full potential and it can
|
||||
now be used to analyze atmospheric equipment, type: Tweak}
|
||||
id: 2259
|
||||
time: '2022-09-08T14:22:14.0000000+00:00'
|
||||
- author: metalgearsloth
|
||||
changes:
|
||||
- {message: Default camera rotations have been inverted., type: Tweak}
|
||||
|
|
@ -2932,3 +2904,31 @@ Entries:
|
|||
type: Remove}
|
||||
id: 2754
|
||||
time: '2022-12-12T10:19:38.0000000+00:00'
|
||||
- author: tgrkzus
|
||||
changes:
|
||||
- {message: Radio chat now supports department specific radio prefixes, type: Add}
|
||||
id: 2755
|
||||
time: '2022-12-14T01:24:49.0000000+00:00'
|
||||
- author: EmoGarbage404
|
||||
changes:
|
||||
- {message: 'Added wallmounted intercoms, useful for radioing a department.', type: Add}
|
||||
id: 2756
|
||||
time: '2022-12-14T01:39:55.0000000+00:00'
|
||||
- author: EmoGarbage404
|
||||
changes:
|
||||
- {message: Intercoms now have a light indicating they are broadcasting., type: Add}
|
||||
- {message: Intercoms no longer stay on when the power shuts off., type: Fix}
|
||||
- {message: Intercoms can now be rotated properly., type: Fix}
|
||||
id: 2757
|
||||
time: '2022-12-15T04:13:02.0000000+00:00'
|
||||
- author: ElectroJr
|
||||
changes:
|
||||
- {message: Fixed electrified grilles electrocuting players despite being blocked
|
||||
by windows., type: Fix}
|
||||
id: 2758
|
||||
time: '2022-12-15T04:29:47.0000000+00:00'
|
||||
- author: metalgearsloth
|
||||
changes:
|
||||
- {message: NPCs will now smash and pry correctly., type: Fix}
|
||||
id: 2759
|
||||
time: '2022-12-15T04:30:28.0000000+00:00'
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
handheld-radio-component-on-use = The radio is now {$radioState}.
|
||||
handheld-radio-component-on-examine = It's set to broadcast over the {$frequency} frequency.
|
||||
handheld-radio-component-on-state = on
|
||||
handheld-radio-component-off-state = off
|
||||
handheld-radio-component-off-state = off
|
||||
handheld-radio-component-channel-set = Channel set to {$channel}
|
||||
handheld-radio-component-chennel-examine = The current channel is {$channel}.
|
||||
|
|
@ -22,6 +22,7 @@ verb-categories-tricks = Tricks
|
|||
verb-categories-transfer = Set Transfer Amount
|
||||
verb-categories-split = Split
|
||||
verb-categories-instrument-style = Instrument Style
|
||||
verb-categories-channel-select = Channels
|
||||
verb-categories-set-sensor = Sensor
|
||||
verb-categories-timer = Set Delay
|
||||
verb-categories-lever = Lever
|
||||
|
|
|
|||
|
|
@ -292,6 +292,7 @@
|
|||
requiredTechnologies:
|
||||
- BasicResearch
|
||||
unlockedRecipes:
|
||||
- IntercomElectronics
|
||||
- ConveyorBeltAssembly
|
||||
- FlashlightLantern
|
||||
- FireExtinguisher
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
- type: entity
|
||||
id: IntercomElectronics
|
||||
parent: BaseElectronics
|
||||
name: intercom electronics
|
||||
description: An electronics board used in intercoms
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: Objects/Misc/module.rsi
|
||||
state: id_mod
|
||||
- type: Tag
|
||||
tags:
|
||||
- DroneUsable
|
||||
- IntercomElectronics
|
||||
|
||||
|
|
@ -205,6 +205,7 @@
|
|||
- APCElectronics
|
||||
- AirAlarmElectronics
|
||||
- FireAlarmElectronics
|
||||
- IntercomElectronics
|
||||
- MailingUnitElectronics
|
||||
- Bucket
|
||||
- MopItem
|
||||
|
|
@ -256,6 +257,7 @@
|
|||
- AirAlarmElectronics
|
||||
- FireAlarmElectronics
|
||||
- MailingUnitElectronics
|
||||
- IntercomElectronics
|
||||
- SMESMachineCircuitboard
|
||||
- SubstationMachineCircuitboard
|
||||
- ThermomachineFreezerMachineCircuitBoard
|
||||
|
|
|
|||
197
Resources/Prototypes/Entities/Structures/Wallmounts/intercom.yml
Normal file
197
Resources/Prototypes/Entities/Structures/Wallmounts/intercom.yml
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
- type: entity
|
||||
id: Intercom
|
||||
name: intercom
|
||||
description: An intercom. For when the station just needs to know something.
|
||||
components:
|
||||
- type: WallMount
|
||||
- type: ApcPowerReceiver
|
||||
- type: Electrified
|
||||
enabled: false
|
||||
usesApcPower: true
|
||||
- type: RadioMicrophone
|
||||
powerRequired: true
|
||||
unobstructedRequired: true
|
||||
listenRange: 2
|
||||
- type: ExtensionCableReceiver
|
||||
- type: Clickable
|
||||
- type: InteractionOutline
|
||||
- type: Appearance
|
||||
- type: Sprite
|
||||
noRot: false
|
||||
sprite: Structures/Wallmounts/intercom.rsi
|
||||
layers:
|
||||
- state: base
|
||||
- state: unshaded
|
||||
map: ["enum.PowerDeviceVisualLayers.Powered"]
|
||||
- state: broadcasting
|
||||
map: ["enum.RadioDeviceVisualLayers.Broadcasting"]
|
||||
shader: unshaded
|
||||
visible: false
|
||||
- type: Transform
|
||||
noRot: false
|
||||
anchored: true
|
||||
- type: Wires
|
||||
BoardName: "Intercom"
|
||||
LayoutId: Intercom
|
||||
- type: Construction
|
||||
graph: Intercom
|
||||
node: intercom
|
||||
- type: Damageable
|
||||
damageContainer: Inorganic
|
||||
damageModifierSet: Metallic
|
||||
- type: Destructible
|
||||
thresholds:
|
||||
- trigger:
|
||||
!type:DamageTrigger
|
||||
damage: 100
|
||||
behaviors:
|
||||
- !type:DoActsBehavior
|
||||
acts: [ "Destruction" ]
|
||||
- !type:PlaySoundBehavior
|
||||
sound:
|
||||
path: /Audio/Effects/metalbreak.ogg
|
||||
- type: GenericVisualizer
|
||||
visuals:
|
||||
enum.PowerDeviceVisuals.Powered:
|
||||
enum.PowerDeviceVisualLayers.Powered:
|
||||
True: { visible: true }
|
||||
False: { visible: false }
|
||||
enum.RadioDeviceVisuals.Broadcasting:
|
||||
enum.RadioDeviceVisualLayers.Broadcasting:
|
||||
True: { visible: true }
|
||||
False: { visible: false }
|
||||
placement:
|
||||
mode: SnapgridCenter
|
||||
snap:
|
||||
- Wallmount
|
||||
|
||||
- type: entity
|
||||
id: IntercomAssesmbly
|
||||
name: intercom assembly
|
||||
description: An intercom. It doesn't seem very helpful right now.
|
||||
components:
|
||||
- type: WallMount
|
||||
- type: Clickable
|
||||
- type: InteractionOutline
|
||||
- type: Sprite
|
||||
sprite: Structures/Wallmounts/intercom.rsi
|
||||
state: build
|
||||
- type: Construction
|
||||
graph: Intercom
|
||||
node: assembly
|
||||
- type: Transform
|
||||
anchored: true
|
||||
placement:
|
||||
mode: SnapgridCenter
|
||||
snap:
|
||||
- Wallmount
|
||||
|
||||
- type: entity
|
||||
id: IntercomCommand
|
||||
parent: Intercom
|
||||
suffix: Command
|
||||
components:
|
||||
- type: RadioMicrophone
|
||||
powerRequired: true
|
||||
unobstructedRequired: true
|
||||
listenRange: 2
|
||||
supportedChannels:
|
||||
- Common
|
||||
- Command
|
||||
|
||||
- type: entity
|
||||
id: IntercomEngineering
|
||||
parent: Intercom
|
||||
suffix: Engineering
|
||||
components:
|
||||
- type: RadioMicrophone
|
||||
powerRequired: true
|
||||
unobstructedRequired: true
|
||||
listenRange: 2
|
||||
supportedChannels:
|
||||
- Common
|
||||
- Engineering
|
||||
|
||||
- type: entity
|
||||
id: IntercomMedical
|
||||
parent: Intercom
|
||||
suffix: Medical
|
||||
components:
|
||||
- type: RadioMicrophone
|
||||
powerRequired: true
|
||||
unobstructedRequired: true
|
||||
listenRange: 2
|
||||
supportedChannels:
|
||||
- Common
|
||||
- Medical
|
||||
|
||||
- type: entity
|
||||
id: IntercomScience
|
||||
parent: Intercom
|
||||
suffix: Science
|
||||
components:
|
||||
- type: RadioMicrophone
|
||||
powerRequired: true
|
||||
unobstructedRequired: true
|
||||
listenRange: 2
|
||||
supportedChannels:
|
||||
- Common
|
||||
- Science
|
||||
|
||||
- type: entity
|
||||
id: IntercomSecurity
|
||||
parent: Intercom
|
||||
suffix: Security
|
||||
components:
|
||||
- type: RadioMicrophone
|
||||
powerRequired: true
|
||||
unobstructedRequired: true
|
||||
listenRange: 2
|
||||
supportedChannels:
|
||||
- Common
|
||||
- Security
|
||||
|
||||
- type: entity
|
||||
id: IntercomService
|
||||
parent: Intercom
|
||||
suffix: Service
|
||||
components:
|
||||
- type: RadioMicrophone
|
||||
powerRequired: true
|
||||
unobstructedRequired: true
|
||||
listenRange: 2
|
||||
supportedChannels:
|
||||
- Common
|
||||
- Service
|
||||
|
||||
- type: entity
|
||||
id: IntercomSupply
|
||||
parent: Intercom
|
||||
suffix: Supply
|
||||
components:
|
||||
- type: RadioMicrophone
|
||||
powerRequired: true
|
||||
unobstructedRequired: true
|
||||
listenRange: 2
|
||||
supportedChannels:
|
||||
- Common
|
||||
- Supply
|
||||
|
||||
- type: entity
|
||||
id: IntercomAll
|
||||
parent: Intercom
|
||||
suffix: All
|
||||
components:
|
||||
- type: RadioMicrophone
|
||||
powerRequired: true
|
||||
unobstructedRequired: true
|
||||
listenRange: 2
|
||||
supportedChannels:
|
||||
- Common
|
||||
- Command
|
||||
- Engineering
|
||||
- Medical
|
||||
- Science
|
||||
- Security
|
||||
- Service
|
||||
- Supply
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
- type: constructionGraph
|
||||
id: Intercom
|
||||
start: start
|
||||
graph:
|
||||
- node: start
|
||||
edges:
|
||||
- to: assembly
|
||||
steps:
|
||||
- material: Steel
|
||||
amount: 2
|
||||
doAfter: 2.0
|
||||
|
||||
- node: assembly
|
||||
entity: IntercomAssesmbly
|
||||
edges:
|
||||
- to: wired
|
||||
steps:
|
||||
- material: Cable
|
||||
amount: 2
|
||||
doAfter: 1
|
||||
- to: start
|
||||
completed:
|
||||
- !type:GivePrototype
|
||||
prototype: SheetSteel1
|
||||
amount: 2
|
||||
- !type:DeleteEntity {}
|
||||
steps:
|
||||
- tool: Welding
|
||||
doAfter: 2
|
||||
|
||||
- node: wired
|
||||
entity: IntercomAssesmbly
|
||||
edges:
|
||||
- to: electronics
|
||||
steps:
|
||||
- tag: IntercomElectronics
|
||||
store: board
|
||||
name: "intercom electronics"
|
||||
icon:
|
||||
sprite: "Objects/Misc/module.rsi"
|
||||
state: "id_mod"
|
||||
doAfter: 1
|
||||
- to: assembly
|
||||
completed:
|
||||
- !type:GivePrototype
|
||||
prototype: CableApcStack1
|
||||
amount: 2
|
||||
steps:
|
||||
- tool: Cutting
|
||||
doAfter: 1
|
||||
|
||||
- node: electronics
|
||||
edges:
|
||||
- to: intercom
|
||||
steps:
|
||||
- tool: Screwing
|
||||
doAfter: 2
|
||||
|
||||
- node: intercom
|
||||
entity: Intercom #TODO: make this work with encryption keys
|
||||
edges:
|
||||
- to: wired
|
||||
conditions:
|
||||
- !type:AllWiresCut {}
|
||||
- !type:WirePanel {}
|
||||
- !type:ContainerNotEmpty
|
||||
container: board
|
||||
completed:
|
||||
- !type:EmptyAllContainers {}
|
||||
steps:
|
||||
- tool: Prying
|
||||
doAfter: 1
|
||||
|
|
@ -580,3 +580,22 @@
|
|||
state: off
|
||||
conditions:
|
||||
- !type:TileNotBlocked {}
|
||||
|
||||
# INTERCOM
|
||||
- type: construction
|
||||
name: intercom
|
||||
id: IntercomAssesmbly
|
||||
graph: Intercom
|
||||
startNode: start
|
||||
targetNode: intercom
|
||||
category: construction-category-structures
|
||||
description: An intercom. For when the station just needs to know something.
|
||||
icon:
|
||||
sprite: Structures/Wallmounts/intercom.rsi
|
||||
state: base
|
||||
placementMode: SnapgridCenter
|
||||
objectType: Structure
|
||||
canRotate: true
|
||||
canBuildInImpassable: true
|
||||
conditions:
|
||||
- !type:WallmountCondition {}
|
||||
|
|
@ -34,6 +34,15 @@
|
|||
Steel: 50
|
||||
Plastic: 50
|
||||
|
||||
- type: latheRecipe
|
||||
id: IntercomElectronics
|
||||
icon: { sprite: Objects/Misc/module.rsi, state: id_mod }
|
||||
result: IntercomElectronics
|
||||
completetime: 2
|
||||
materials:
|
||||
Steel: 50
|
||||
Plastic: 50
|
||||
|
||||
- type: latheRecipe
|
||||
id: FireAlarmElectronics
|
||||
icon: { sprite: Objects/Misc/module.rsi, state: door_electronics }
|
||||
|
|
|
|||
|
|
@ -291,6 +291,9 @@
|
|||
- type: Tag
|
||||
id: Ingot
|
||||
|
||||
- type: Tag
|
||||
id: IntercomElectronics
|
||||
|
||||
- type: Tag
|
||||
id: JawsOfLife
|
||||
|
||||
|
|
|
|||
BIN
Resources/Textures/Structures/Wallmounts/intercom.rsi/base.png
Normal file
BIN
Resources/Textures/Structures/Wallmounts/intercom.rsi/base.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 171 B |
BIN
Resources/Textures/Structures/Wallmounts/intercom.rsi/build.png
Normal file
BIN
Resources/Textures/Structures/Wallmounts/intercom.rsi/build.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.3 KiB |
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"version": 1,
|
||||
"size": {
|
||||
"x": 32,
|
||||
"y": 32
|
||||
},
|
||||
"license": "CC-BY-SA-3.0",
|
||||
"copyright": "Taken from /tg/station at https://github.com/tgstation/tgstation/commit/6bfe3b2e4fcbcdac9159dc4f0327a82ddf05ba7bi",
|
||||
"states": [
|
||||
{
|
||||
"name": "base",
|
||||
"directions": 4
|
||||
},
|
||||
{
|
||||
"name": "broadcasting",
|
||||
"directions": 4
|
||||
},
|
||||
{
|
||||
"name": "build",
|
||||
"directions": 4
|
||||
},
|
||||
{
|
||||
"name": "unshaded",
|
||||
"directions": 4
|
||||
}
|
||||
]
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 369 B |
|
|
@ -1 +1 @@
|
|||
Subproject commit fef963e77a4168cbaaf5400c0233a6701cc56e9d
|
||||
Subproject commit e64d499000f61792a092c21c8444c643055ac754
|
||||
Loading…
Add table
Reference in a new issue