Merge branch 'space-wizards:master' into elk-new

This commit is contained in:
Deerstop
2025-04-05 16:37:06 +11:00
committed by GitHub
98 changed files with 1168 additions and 466 deletions

View File

@@ -103,7 +103,7 @@ public sealed partial class AirAlarmWindow : FancyWindow
_temperature.SetMarkup(Loc.GetString("air-alarm-ui-window-temperature", ("tempC", $"{TemperatureHelpers.KelvinToCelsius(state.TemperatureAverage):0.#}"), ("temperature", $"{state.TemperatureAverage:0.##}")));
_alarmState.SetMarkup(Loc.GetString("air-alarm-ui-window-alarm-state",
("color", ColorForAlarm(state.AlarmType)),
("state", $"{state.AlarmType}")));
("state", state.AlarmType)));
UpdateModeSelector(state.Mode);
UpdateAutoMode(state.AutoMode);
foreach (var (addr, dev) in state.DeviceData)

View File

@@ -27,11 +27,11 @@ public sealed partial class SensorInfo : BoxContainer
_address = address;
SensorAddress.Title = $"{address} : {data.AlarmState}";
SensorAddress.Title = Loc.GetString("air-alarm-ui-window-listing-title", ("address", _address), ("state", data.AlarmState));
AlarmStateLabel.SetMarkup(Loc.GetString("air-alarm-ui-window-alarm-state-indicator",
("color", AirAlarmWindow.ColorForAlarm(data.AlarmState)),
("state", $"{data.AlarmState}")));
("state", data.AlarmState)));
PressureLabel.SetMarkup(Loc.GetString("air-alarm-ui-window-pressure-indicator",
("color", AirAlarmWindow.ColorForThreshold(data.Pressure, data.PressureThreshold)),
("pressure", $"{data.Pressure:0.##}")));
@@ -90,11 +90,11 @@ public sealed partial class SensorInfo : BoxContainer
public void ChangeData(AtmosSensorData data)
{
SensorAddress.Title = $"{_address} : {data.AlarmState}";
SensorAddress.Title = Loc.GetString("air-alarm-ui-window-listing-title", ("address", _address), ("state", data.AlarmState));
AlarmStateLabel.SetMarkup(Loc.GetString("air-alarm-ui-window-alarm-state-indicator",
("color", AirAlarmWindow.ColorForAlarm(data.AlarmState)),
("state", $"{data.AlarmState}")));
("state", data.AlarmState)));
PressureLabel.SetMarkup(Loc.GetString("air-alarm-ui-window-pressure-indicator",
("color", AirAlarmWindow.ColorForThreshold(data.Pressure, data.PressureThreshold)),

View File

@@ -1,6 +1,7 @@
using Content.Client.Rotation;
using Content.Shared.Buckle;
using Content.Shared.Buckle.Components;
using Content.Shared.Movement.Systems;
using Content.Shared.Rotation;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
@@ -21,6 +22,15 @@ internal sealed class BuckleSystem : SharedBuckleSystem
SubscribeLocalEvent<StrapComponent, MoveEvent>(OnStrapMoveEvent);
SubscribeLocalEvent<BuckleComponent, BuckledEvent>(OnBuckledEvent);
SubscribeLocalEvent<BuckleComponent, UnbuckledEvent>(OnUnbuckledEvent);
SubscribeLocalEvent<BuckleComponent, AttemptMobCollideEvent>(OnMobCollide);
}
private void OnMobCollide(Entity<BuckleComponent> ent, ref AttemptMobCollideEvent args)
{
if (ent.Comp.Buckled)
{
args.Cancelled = true;
}
}
private void OnStrapMoveEvent(EntityUid uid, StrapComponent component, ref MoveEvent args)

View File

@@ -1,4 +1,5 @@
using System.Numerics;
using Content.Client.Sprite;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Client.Utility;
@@ -17,12 +18,14 @@ public sealed class ClickableSystem : EntitySystem
private EntityQuery<ClickableComponent> _clickableQuery;
private EntityQuery<TransformComponent> _xformQuery;
private EntityQuery<FadingSpriteComponent> _fadingSpriteQuery;
public override void Initialize()
{
base.Initialize();
_clickableQuery = GetEntityQuery<ClickableComponent>();
_xformQuery = GetEntityQuery<TransformComponent>();
_fadingSpriteQuery = GetEntityQuery<FadingSpriteComponent>();
}
/// <summary>
@@ -34,7 +37,7 @@ public sealed class ClickableSystem : EntitySystem
/// The draw depth for the sprite that captured the click.
/// </param>
/// <returns>True if the click worked, false otherwise.</returns>
public bool CheckClick(Entity<ClickableComponent?, SpriteComponent, TransformComponent?> entity, Vector2 worldPos, IEye eye, out int drawDepth, out uint renderOrder, out float bottom)
public bool CheckClick(Entity<ClickableComponent?, SpriteComponent, TransformComponent?, FadingSpriteComponent?> entity, Vector2 worldPos, IEye eye, bool excludeFaded, out int drawDepth, out uint renderOrder, out float bottom)
{
if (!_clickableQuery.Resolve(entity.Owner, ref entity.Comp1, false))
{
@@ -52,6 +55,14 @@ public sealed class ClickableSystem : EntitySystem
return false;
}
if (excludeFaded && _fadingSpriteQuery.Resolve(entity.Owner, ref entity.Comp4, false))
{
drawDepth = default;
renderOrder = default;
bottom = default;
return false;
}
var sprite = entity.Comp2;
var transform = entity.Comp3;

View File

@@ -113,18 +113,18 @@ namespace Content.Client.Gameplay
return first.IsValid() ? first : null;
}
public IEnumerable<EntityUid> GetClickableEntities(EntityCoordinates coordinates)
public IEnumerable<EntityUid> GetClickableEntities(EntityCoordinates coordinates, bool excludeFaded = true)
{
var transformSystem = _entitySystemManager.GetEntitySystem<SharedTransformSystem>();
return GetClickableEntities(transformSystem.ToMapCoordinates(coordinates));
return GetClickableEntities(transformSystem.ToMapCoordinates(coordinates), excludeFaded);
}
public IEnumerable<EntityUid> GetClickableEntities(MapCoordinates coordinates)
public IEnumerable<EntityUid> GetClickableEntities(MapCoordinates coordinates, bool excludeFaded = true)
{
return GetClickableEntities(coordinates, _eyeManager.CurrentEye);
return GetClickableEntities(coordinates, _eyeManager.CurrentEye, excludeFaded);
}
public IEnumerable<EntityUid> GetClickableEntities(MapCoordinates coordinates, IEye? eye)
public IEnumerable<EntityUid> GetClickableEntities(MapCoordinates coordinates, IEye? eye, bool excludeFaded = true)
{
/*
* TODO:
@@ -147,7 +147,7 @@ namespace Content.Client.Gameplay
foreach (var entity in entities)
{
if (clickQuery.TryGetComponent(entity.Uid, out var component) &&
clickables.CheckClick((entity.Uid, component, entity.Component, entity.Transform), coordinates.Position, eye, out var drawDepthClicked, out var renderOrder, out var bottom))
clickables.CheckClick((entity.Uid, component, entity.Component, entity.Transform), coordinates.Position, eye, excludeFaded, out var drawDepthClicked, out var renderOrder, out var bottom))
{
foundEntities.Add((entity.Uid, drawDepthClicked, renderOrder, bottom));
}

View File

@@ -0,0 +1,42 @@
using System.Numerics;
using Content.Shared.CCVar;
using Content.Shared.Movement.Components;
using Content.Shared.Movement.Systems;
using Robust.Client.Player;
using Robust.Shared.Physics.Components;
using Robust.Shared.Timing;
namespace Content.Client.Movement.Systems;
public sealed class MobCollisionSystem : SharedMobCollisionSystem
{
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly IPlayerManager _player = default!;
public override void Update(float frameTime)
{
if (!CfgManager.GetCVar(CCVars.MovementMobPushing))
return;
if (_timing.IsFirstTimePredicted)
{
var player = _player.LocalEntity;
if (MobQuery.TryComp(player, out var comp) && PhysicsQuery.TryComp(player, out var physics))
{
HandleCollisions((player.Value, comp, physics), frameTime);
}
}
base.Update(frameTime);
}
protected override void RaiseCollisionEvent(EntityUid uid, Vector2 direction, float speedMod)
{
RaisePredictiveEvent(new MobCollisionMessage()
{
Direction = direction,
SpeedModifier = speedMod,
});
}
}

View File

@@ -62,16 +62,16 @@ public sealed class MoverController : SharedMoverController
private void OnRelayPlayerAttached(Entity<RelayInputMoverComponent> entity, ref LocalPlayerAttachedEvent args)
{
Physics.UpdateIsPredicted(entity.Owner);
Physics.UpdateIsPredicted(entity.Comp.RelayEntity);
PhysicsSystem.UpdateIsPredicted(entity.Owner);
PhysicsSystem.UpdateIsPredicted(entity.Comp.RelayEntity);
if (MoverQuery.TryGetComponent(entity.Comp.RelayEntity, out var inputMover))
SetMoveInput((entity.Comp.RelayEntity, inputMover), MoveButtons.None);
}
private void OnRelayPlayerDetached(Entity<RelayInputMoverComponent> entity, ref LocalPlayerDetachedEvent args)
{
Physics.UpdateIsPredicted(entity.Owner);
Physics.UpdateIsPredicted(entity.Comp.RelayEntity);
PhysicsSystem.UpdateIsPredicted(entity.Owner);
PhysicsSystem.UpdateIsPredicted(entity.Comp.RelayEntity);
if (MoverQuery.TryGetComponent(entity.Comp.RelayEntity, out var inputMover))
SetMoveInput((entity.Comp.RelayEntity, inputMover), MoveButtons.None);
}

View File

@@ -1,8 +1,14 @@
using System.Numerics;
using Content.Client.Gameplay;
using Content.Shared.Sprite;
using Robust.Client.GameObjects;
using Robust.Client.Input;
using Robust.Client.Player;
using Robust.Client.State;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface;
using Robust.Shared.Map;
using Robust.Shared.Physics.Systems;
using Robust.Shared.Physics;
namespace Content.Client.Sprite;
@@ -17,6 +23,9 @@ public sealed class SpriteFadeSystem : EntitySystem
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IStateManager _stateManager = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
[Dependency] private readonly IUserInterfaceManager _uiManager = default!;
[Dependency] private readonly IInputManager _inputManager = default!;
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
private readonly HashSet<FadingSpriteComponent> _comps = new();
@@ -24,6 +33,11 @@ public sealed class SpriteFadeSystem : EntitySystem
private EntityQuery<SpriteFadeComponent> _fadeQuery;
private EntityQuery<FadingSpriteComponent> _fadingQuery;
/// <summary>
/// Radius of the mouse point for the intersection test
/// </summary>
private static Vector2 MouseRadius = new Vector2(10f * float.Epsilon, 10f * float.Epsilon);
private const float TargetAlpha = 0.4f;
private const float ChangeRate = 1f;
@@ -46,46 +60,82 @@ public sealed class SpriteFadeSystem : EntitySystem
sprite.Color = sprite.Color.WithAlpha(component.OriginalAlpha);
}
public override void FrameUpdate(float frameTime)
/// <summary>
/// Adds sprites to the fade set, and brings their alpha downwards
/// </summary>
private void FadeIn(float change)
{
base.FrameUpdate(frameTime);
var player = _playerManager.LocalEntity;
var change = ChangeRate * frameTime;
// ExcludeBoundingBox is set if we don't want to fade this sprite within the collision bounding boxes for the given POI
var pointsOfInterest = new List<(MapCoordinates Point, bool ExcludeBoundingBox)>();
if (TryComp(player, out TransformComponent? playerXform) &&
_stateManager.CurrentState is GameplayState state &&
_spriteQuery.TryGetComponent(player, out var playerSprite))
if (_uiManager.CurrentlyHovered is IViewportControl vp
&& _inputManager.MouseScreenPosition.IsValid)
{
var mapPos = _transform.GetMapCoordinates(_playerManager.LocalEntity!.Value, xform: playerXform);
pointsOfInterest.Add((vp.PixelToMap(_inputManager.MouseScreenPosition.Position), true));
}
// Also want to handle large entities even if they may not be clickable.
foreach (var ent in state.GetClickableEntities(mapPos))
if (TryComp(player, out TransformComponent? playerXform))
{
pointsOfInterest.Add((_transform.GetMapCoordinates(_playerManager.LocalEntity!.Value, xform: playerXform), false));
}
if (_stateManager.CurrentState is GameplayState state && _spriteQuery.TryGetComponent(player, out var playerSprite))
{
foreach (var (mapPos, excludeBB) in pointsOfInterest)
{
if (ent == player ||
!_fadeQuery.HasComponent(ent) ||
!_spriteQuery.TryGetComponent(ent, out var sprite) ||
sprite.DrawDepth < playerSprite.DrawDepth)
// Also want to handle large entities even if they may not be clickable.
foreach (var ent in state.GetClickableEntities(mapPos, excludeFaded: false))
{
continue;
}
if (ent == player ||
!_fadeQuery.HasComponent(ent) ||
!_spriteQuery.TryGetComponent(ent, out var sprite) ||
sprite.DrawDepth < playerSprite.DrawDepth)
{
continue;
}
if (!_fadingQuery.TryComp(ent, out var fading))
{
fading = AddComp<FadingSpriteComponent>(ent);
fading.OriginalAlpha = sprite.Color.A;
}
if (excludeBB)
{
var test = new Box2Rotated(mapPos.Position - MouseRadius, mapPos.Position + MouseRadius);
var collided = false;
foreach (var fixture in _physics.GetCollidingEntities(mapPos.MapId, test))
{
if (fixture.Owner == ent)
{
collided = true;
break;
}
}
if (collided)
{
break;
}
}
_comps.Add(fading);
var newColor = Math.Max(sprite.Color.A - change, TargetAlpha);
if (!_fadingQuery.TryComp(ent, out var fading))
{
fading = AddComp<FadingSpriteComponent>(ent);
fading.OriginalAlpha = sprite.Color.A;
}
if (!sprite.Color.A.Equals(newColor))
{
sprite.Color = sprite.Color.WithAlpha(newColor);
_comps.Add(fading);
var newColor = Math.Max(sprite.Color.A - change, TargetAlpha);
if (!sprite.Color.A.Equals(newColor))
{
sprite.Color = sprite.Color.WithAlpha(newColor);
}
}
}
}
}
/// <summary>
/// Bring sprites back up to their original alpha if they aren't in the fade set, and removes their fade component when done
/// </summary>
private void FadeOut(float change)
{
var query = AllEntityQuery<FadingSpriteComponent>();
while (query.MoveNext(out var uid, out var comp))
{
@@ -106,6 +156,16 @@ public sealed class SpriteFadeSystem : EntitySystem
RemCompDeferred<FadingSpriteComponent>(uid);
}
}
}
public override void FrameUpdate(float frameTime)
{
base.FrameUpdate(frameTime);
var change = ChangeRate * frameTime;
FadeIn(change);
FadeOut(change);
_comps.Clear();
}

View File

@@ -16,6 +16,7 @@ using Robust.Client.State;
using Robust.Shared.Configuration;
using Robust.Shared.Containers;
using Robust.Shared.Map;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
namespace Content.Client.Verbs
@@ -36,6 +37,8 @@ namespace Content.Client.Verbs
private float _lookupSize;
private static readonly ProtoId<TagPrototype> HideContextMenuTag = "HideContextMenu";
/// <summary>
/// These flags determine what entities the user can see on the context menu.
/// </summary>
@@ -147,7 +150,7 @@ namespace Content.Client.Verbs
for (var i = entities.Count - 1; i >= 0; i--)
{
if (_tagSystem.HasTag(entities[i], "HideContextMenu"))
if (_tagSystem.HasTag(entities[i], HideContextMenuTag))
entities.RemoveSwap(i);
}

View File

@@ -39,6 +39,7 @@ public static partial class PoolManager
(CVars.NetBufferSize.Name, "0"),
(CCVars.InteractionRateLimitCount.Name, "9999999"),
(CCVars.InteractionRateLimitPeriod.Name, "0.1"),
(CCVars.MovementMobPushing.Name, "false"),
};
public static async Task SetupCVars(RobustIntegrationTest.IntegrationInstance instance, PoolSettings settings)

View File

@@ -80,7 +80,7 @@ namespace Content.IntegrationTests.Tests
var pos = clientEntManager.System<SharedTransformSystem>().GetWorldPosition(clientEnt);
hit = clientEntManager.System<ClickableSystem>().CheckClick((clientEnt, null, sprite, null), new Vector2(clickPosX, clickPosY) + pos, eye, out _, out _, out _);
hit = clientEntManager.System<ClickableSystem>().CheckClick((clientEnt, null, sprite, null), new Vector2(clickPosX, clickPosY) + pos, eye, false, out _, out _, out _);
});
await server.WaitPost(() =>

View File

@@ -1,4 +1,4 @@
using System.Linq;
using System.Linq;
using Content.Shared.Damage;
using Content.Shared.Damage.Prototypes;
using Content.Shared.Execution;
@@ -52,7 +52,7 @@ public sealed class SuicideCommandTests
name: test version of the material reclaimer
components:
- type: MaterialReclaimer";
private static readonly ProtoId<TagPrototype> CannotSuicideTag = "CannotSuicide";
/// <summary>
/// Run the suicide command in the console
/// Should successfully kill the player and ghost them
@@ -201,7 +201,7 @@ public sealed class SuicideCommandTests
mobStateComp = entManager.GetComponent<MobStateComponent>(player);
});
tagSystem.AddTag(player, "CannotSuicide");
tagSystem.AddTag(player, CannotSuicideTag);
// Check that running the suicide command kills the player
// and properly ghosts them without them being able to return to their body

View File

@@ -42,21 +42,9 @@ namespace Content.Server.Access.Systems
access.Tags.UnionWith(targetAccess.Tags);
var addedLength = access.Tags.Count - beforeLength;
if (addedLength == 0)
{
_popupSystem.PopupEntity(Loc.GetString("agent-id-no-new", ("card", args.Target)), args.Target.Value, args.User);
return;
}
Dirty(uid, access);
if (addedLength == 1)
{
_popupSystem.PopupEntity(Loc.GetString("agent-id-new-1", ("card", args.Target)), args.Target.Value, args.User);
return;
}
_popupSystem.PopupEntity(Loc.GetString("agent-id-new", ("number", addedLength), ("card", args.Target)), args.Target.Value, args.User);
if (addedLength > 0)
Dirty(uid, access);
}
private void AfterUIOpen(EntityUid uid, AgentIDCardComponent component, AfterActivatableUIOpenEvent args)

View File

@@ -168,7 +168,7 @@ public sealed class IdCardConsoleSystem : SharedIdCardConsoleSystem
/*TODO: ECS SharedIdCardConsoleComponent and then log on card ejection, together with the save.
This current implementation is pretty shit as it logs 27 entries (27 lines) if someone decides to give themselves AA*/
_adminLogger.Add(LogType.Action, LogImpact.High,
_adminLogger.Add(LogType.Action, LogImpact.Medium,
$"{ToPrettyString(player):player} has modified {ToPrettyString(targetId):entity} with the following accesses: [{string.Join(", ", addedTags.Union(removedTags))}] [{string.Join(", ", newAccessList)}]");
}

View File

@@ -43,5 +43,13 @@ namespace Content.Server.Administration.Commands
_entities.System<MindSystem>().ControlMob(player.UserId, target.Value);
}
public CompletionResult GetCompletion(IConsoleShell shell, string[] args)
{
if (args.Length != 1)
return CompletionResult.Empty;
return CompletionResult.FromOptions(CompletionHelper.NetEntities(args[0], entManager: _entities));
}
}
}

View File

@@ -50,6 +50,9 @@ public sealed class PlantHolderSystem : EntitySystem
public const float HydroponicsSpeedMultiplier = 1f;
public const float HydroponicsConsumptionMultiplier = 2f;
private static readonly ProtoId<TagPrototype> HoeTag = "Hoe";
private static readonly ProtoId<TagPrototype> PlantSampleTakerTag = "PlantSampleTaker";
public override void Initialize()
{
base.Initialize();
@@ -203,7 +206,7 @@ public sealed class PlantHolderSystem : EntitySystem
return;
}
if (_tagSystem.HasTag(args.Used, "Hoe"))
if (_tagSystem.HasTag(args.Used, HoeTag))
{
args.Handled = true;
if (component.WeedLevel > 0)
@@ -243,7 +246,7 @@ public sealed class PlantHolderSystem : EntitySystem
return;
}
if (_tagSystem.HasTag(args.Used, "PlantSampleTaker"))
if (_tagSystem.HasTag(args.Used, PlantSampleTakerTag))
{
args.Handled = true;
if (component.Seed == null)

View File

@@ -4,6 +4,7 @@ using Content.Shared.Chat;
using Content.Shared.Damage;
using Content.Shared.Database;
using Content.Shared.Hands.Components;
using Content.Shared.IdentityManagement;
using Content.Shared.Interaction.Events;
using Content.Shared.Item;
using Content.Shared.Mind;
@@ -13,6 +14,7 @@ using Content.Shared.Mobs.Systems;
using Content.Shared.Popups;
using Content.Shared.Tag;
using Robust.Shared.Player;
using Robust.Shared.Prototypes;
namespace Content.Server.Chat;
@@ -26,6 +28,8 @@ public sealed class SuicideSystem : EntitySystem
[Dependency] private readonly GhostSystem _ghostSystem = default!;
[Dependency] private readonly SharedSuicideSystem _suicide = default!;
private static readonly ProtoId<TagPrototype> CannotSuicideTag = "CannotSuicide";
public override void Initialize()
{
base.Initialize();
@@ -59,7 +63,7 @@ public sealed class SuicideSystem : EntitySystem
// Suicide is considered a fail if the user wasn't able to ghost
// Suiciding with the CannotSuicide tag will ghost the player but not kill the body
if (!suicideGhostEvent.Handled || _tagSystem.HasTag(victim, "CannotSuicide"))
if (!suicideGhostEvent.Handled || _tagSystem.HasTag(victim, CannotSuicideTag))
return false;
var suicideEvent = new SuicideEvent(victim);
@@ -94,7 +98,7 @@ public sealed class SuicideSystem : EntitySystem
// CannotSuicide tag will allow the user to ghost, but also return to their mind
// This is kind of weird, not sure what it applies to?
if (_tagSystem.HasTag(victim, "CannotSuicide"))
if (_tagSystem.HasTag(victim, CannotSuicideTag))
args.CanReturnToBody = true;
if (_ghostSystem.OnGhostAttempt(victim.Comp.Mind.Value, args.CanReturnToBody, mind: mindComponent))
@@ -149,7 +153,7 @@ public sealed class SuicideSystem : EntitySystem
if (args.Handled)
return;
var othersMessage = Loc.GetString("suicide-command-default-text-others", ("name", victim));
var othersMessage = Loc.GetString("suicide-command-default-text-others", ("name", Identity.Entity(victim, EntityManager)));
_popup.PopupEntity(othersMessage, victim, Filter.PvsExcept(victim), true);
var selfMessage = Loc.GetString("suicide-command-default-text-self");

View File

@@ -9,6 +9,7 @@ using Content.Shared.Projectiles;
using Content.Shared.Tag;
using Content.Shared.Weapons.Melee.Events;
using Robust.Shared.Collections;
using Robust.Shared.Prototypes;
namespace Content.Server.Chemistry.EntitySystems;
@@ -24,6 +25,8 @@ public sealed class SolutionInjectOnCollideSystem : EntitySystem
[Dependency] private readonly SharedSolutionContainerSystem _solutionContainer = default!;
[Dependency] private readonly TagSystem _tag = default!;
private static readonly ProtoId<TagPrototype> HardsuitTag = "Hardsuit";
public override void Initialize()
{
base.Initialize();
@@ -93,7 +96,7 @@ public sealed class SolutionInjectOnCollideSystem : EntitySystem
// Yuck, this is way to hardcodey for my tastes
// TODO blocking injection with a hardsuit should probably done with a cancellable event or something
if (!injector.Comp.PierceArmor && _inventory.TryGetSlotEntity(target, "outerClothing", out var suit) && _tag.HasTag(suit.Value, "Hardsuit"))
if (!injector.Comp.PierceArmor && _inventory.TryGetSlotEntity(target, "outerClothing", out var suit) && _tag.HasTag(suit.Value, HardsuitTag))
{
// Only show popup to attacker
if (source != null)

View File

@@ -259,7 +259,7 @@ public sealed class IPIntel
{
_chatManager.SendAdminAlert(Loc.GetString("admin-alert-ipintel-warning",
("player", username),
("percent", Math.Round(score))));
("percent", score)));
}
if (!decisionIsReject)
@@ -269,7 +269,7 @@ public sealed class IPIntel
{
_chatManager.SendAdminAlert(Loc.GetString("admin-alert-ipintel-blocked",
("player", username),
("percent", Math.Round(score))));
("percent", score)));
}
return _rejectBad ? (true, Loc.GetString("ipintel-suspicious")) : (false, string.Empty);

View File

@@ -5,6 +5,7 @@ using Content.Shared.Construction;
using Content.Shared.Tag;
using Robust.Shared.Console;
using Robust.Shared.Map.Components;
using Robust.Shared.Prototypes;
namespace Content.Server.Construction.Commands;
@@ -13,6 +14,10 @@ public sealed class FixRotationsCommand : IConsoleCommand
{
[Dependency] private readonly IEntityManager _entManager = default!;
private static readonly ProtoId<TagPrototype> ForceFixRotationsTag = "ForceFixRotations";
private static readonly ProtoId<TagPrototype> ForceNoFixRotationsTag = "ForceNoFixRotations";
private static readonly ProtoId<TagPrototype> DiagonalTag = "Diagonal";
// ReSharper disable once StringLiteralTypo
public string Command => "fixrotations";
public string Description => "Sets the rotation of all occluders, low walls and windows to south.";
@@ -86,11 +91,11 @@ public sealed class FixRotationsCommand : IConsoleCommand
// cables
valid |= _entManager.HasComponent<CableComponent>(child);
// anything else that might need this forced
valid |= tagSystem.HasTag(child, "ForceFixRotations");
valid |= tagSystem.HasTag(child, ForceFixRotationsTag);
// override
valid &= !tagSystem.HasTag(child, "ForceNoFixRotations");
valid &= !tagSystem.HasTag(child, ForceNoFixRotationsTag);
// remove diagonal entities as well
valid &= !tagSystem.HasTag(child, "Diagonal");
valid &= !tagSystem.HasTag(child, DiagonalTag);
if (!valid)
continue;

View File

@@ -4,7 +4,7 @@ using Content.Server.StationRecords.Systems;
using Content.Shared.CriminalRecords;
using Content.Shared.CriminalRecords.Components;
using Content.Shared.CriminalRecords.Systems;
using Content.Shared.Dataset;
using Content.Shared.Random.Helpers;
using Content.Shared.Security;
using Content.Shared.StationRecords;
using Robust.Shared.Prototypes;
@@ -36,10 +36,10 @@ public sealed class CriminalRecordsHackerSystem : SharedCriminalRecordsHackerSys
if (_station.GetOwningStation(ent) is not {} station)
return;
var reasons = _proto.Index<DatasetPrototype>(ent.Comp.Reasons);
var reasons = _proto.Index(ent.Comp.Reasons);
foreach (var (key, record) in _records.GetRecordsOfType<CriminalRecord>(station))
{
var reason = _random.Pick(reasons.Values);
var reason = _random.Pick(reasons);
_criminalRecords.OverwriteStatus(new StationRecordKey(key, station), record, SecurityStatus.Wanted, reason);
// no radio message since spam
// no history since lazy and its easy to remove anyway

View File

@@ -1,4 +1,5 @@
using Content.Shared.Body.Components;
using Content.Shared.IdentityManagement;
using Content.Shared.Inventory;
using Content.Shared.Popups;
using JetBrains.Annotations;
@@ -25,7 +26,8 @@ public sealed partial class BurnBodyBehavior : IThresholdBehavior
}
}
sharedPopupSystem.PopupCoordinates(Loc.GetString("bodyburn-text-others", ("name", bodyId)), transformSystem.GetMoverCoordinates(bodyId), PopupType.LargeCaution);
var bodyIdentity = Identity.Entity(bodyId, system.EntityManager);
sharedPopupSystem.PopupCoordinates(Loc.GetString("bodyburn-text-others", ("name", bodyIdentity)), transformSystem.GetMoverCoordinates(bodyId), PopupType.LargeCaution);
system.EntityManager.QueueDeleteEntity(bodyId);
}

View File

@@ -13,6 +13,7 @@ using Robust.Shared.Serialization.Manager;
using System.Numerics;
using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems;
using Robust.Shared.GameStates;
using Robust.Shared.Utility;
namespace Content.Server.Dragon;
@@ -33,11 +34,20 @@ public sealed class DragonRiftSystem : EntitySystem
{
base.Initialize();
SubscribeLocalEvent<DragonRiftComponent, ComponentGetState>(OnGetState);
SubscribeLocalEvent<DragonRiftComponent, ExaminedEvent>(OnExamined);
SubscribeLocalEvent<DragonRiftComponent, AnchorStateChangedEvent>(OnAnchorChange);
SubscribeLocalEvent<DragonRiftComponent, ComponentShutdown>(OnShutdown);
}
private void OnGetState(Entity<DragonRiftComponent> ent, ref ComponentGetState args)
{
args.State = new DragonRiftComponentState
{
State = ent.Comp.State,
};
}
public override void Update(float frameTime)
{
base.Update(frameTime);

View File

@@ -62,6 +62,8 @@ public sealed class ElectrocutionSystem : SharedElectrocutionSystem
[ValidatePrototypeId<DamageTypePrototype>]
private const string DamageType = "Shock";
private static readonly ProtoId<TagPrototype> WindowTag = "Window";
// Multiply and shift the log scale for shock damage.
private const float RecursiveDamageMultiplier = 0.75f;
private const float RecursiveTimeMultiplier = 0.8f;
@@ -139,7 +141,7 @@ public sealed class ElectrocutionSystem : SharedElectrocutionSystem
{
foreach (var entity in _entityLookup.GetLocalEntitiesIntersecting(tileRef.Value, flags: LookupFlags.StaticSundries))
{
if (_tag.HasTag(entity, "Window"))
if (_tag.HasTag(entity, WindowTag))
return false;
}
}

View File

@@ -21,6 +21,7 @@ using Robust.Server.GameObjects;
using Robust.Shared.Audio;
using Robust.Shared.Random;
using InventoryComponent = Content.Shared.Inventory.InventoryComponent;
using Robust.Shared.Prototypes;
namespace Content.Server.Flash
{
@@ -39,6 +40,8 @@ namespace Content.Server.Flash
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly StatusEffectsSystem _statusEffectsSystem = default!;
private static readonly ProtoId<TagPrototype> TrashTag = "Trash";
public override void Initialize()
{
base.Initialize();
@@ -94,7 +97,7 @@ namespace Content.Server.Flash
if (_charges.IsEmpty(uid, charges))
{
_appearance.SetData(uid, FlashVisuals.Burnt, true);
_tag.AddTag(uid, "Trash");
_tag.AddTag(uid, TrashTag);
_popup.PopupEntity(Loc.GetString("flash-component-becomes-empty"), user);
}

View File

@@ -16,6 +16,7 @@ using Robust.Shared.Audio;
using Robust.Shared.Player;
using Robust.Shared.Timing;
using Content.Server.Chemistry.Containers.EntitySystems;
using Robust.Shared.Prototypes;
// todo: remove this stinky LINQy
namespace Content.Server.Forensics
@@ -33,6 +34,8 @@ namespace Content.Server.Forensics
[Dependency] private readonly ForensicsSystem _forensicsSystem = default!;
[Dependency] private readonly TagSystem _tag = default!;
private static readonly ProtoId<TagPrototype> DNASolutionScannableTag = "DNASolutionScannable";
public override void Initialize()
{
base.Initialize();
@@ -86,7 +89,7 @@ namespace Content.Server.Forensics
scanner.Residues = forensics.Residues.ToList();
}
if (_tag.HasTag(args.Args.Target.Value, "DNASolutionScannable"))
if (_tag.HasTag(args.Args.Target.Value, DNASolutionScannableTag))
{
scanner.SolutionDNAs = _forensicsSystem.GetSolutionsDNA(args.Args.Target.Value);
} else

View File

@@ -9,6 +9,7 @@ using Content.Shared.Mobs.Systems;
using Content.Shared.Survivor.Components;
using Content.Shared.Tag;
using Robust.Server.GameObjects;
using Robust.Shared.Prototypes;
namespace Content.Server.GameTicking.Rules;
@@ -22,6 +23,8 @@ public sealed class SurvivorRuleSystem : GameRuleSystem<SurvivorRuleComponent>
[Dependency] private readonly TagSystem _tag = default!;
[Dependency] private readonly MobStateSystem _mobState = default!;
private static readonly ProtoId<TagPrototype> InvalidForSurvivorAntagTag = "InvalidForSurvivorAntag";
public override void Initialize()
{
base.Initialize();
@@ -44,7 +47,7 @@ public sealed class SurvivorRuleSystem : GameRuleSystem<SurvivorRuleComponent>
var mind = humanMind.Owner;
var ent = humanMind.Comp.OwnedEntity.Value;
if (HasComp<SurvivorComponent>(mind) || _tag.HasTag(mind, "InvalidForSurvivorAntag"))
if (HasComp<SurvivorComponent>(mind) || _tag.HasTag(mind, InvalidForSurvivorAntagTag))
continue;
EnsureComp<SurvivorComponent>(mind);

View File

@@ -72,6 +72,8 @@ namespace Content.Server.Ghost
private EntityQuery<GhostComponent> _ghostQuery;
private EntityQuery<PhysicsComponent> _physicsQuery;
private static readonly ProtoId<TagPrototype> AllowGhostShownByEventTag = "AllowGhostShownByEvent";
public override void Initialize()
{
base.Initialize();
@@ -403,7 +405,7 @@ namespace Content.Server.Ghost
var entityQuery = EntityQueryEnumerator<GhostComponent, VisibilityComponent>();
while (entityQuery.MoveNext(out var uid, out var _, out var vis))
{
if (!_tag.HasTag(uid, "AllowGhostShownByEvent"))
if (!_tag.HasTag(uid, AllowGhostShownByEventTag))
continue;
if (visible)

View File

@@ -73,6 +73,9 @@ namespace Content.Server.Kitchen.EntitySystems
[ValidatePrototypeId<EntityPrototype>]
private const string MalfunctionSpark = "Spark";
private static readonly ProtoId<TagPrototype> MetalTag = "Metal";
private static readonly ProtoId<TagPrototype> PlasticTag = "Plastic";
public override void Initialize()
{
base.Initialize();
@@ -550,12 +553,12 @@ namespace Content.Server.Kitchen.EntitySystems
return;
}
if (_tag.HasTag(item, "Metal"))
if (_tag.HasTag(item, MetalTag))
{
malfunctioning = true;
}
if (_tag.HasTag(item, "Plastic"))
if (_tag.HasTag(item, PlasticTag))
{
var junk = Spawn(component.BadRecipeEntityId, Transform(uid).Coordinates);
_container.Insert(junk, component.Storage);

View File

@@ -10,6 +10,7 @@ using Content.Shared.Verbs;
using JetBrains.Annotations;
using Robust.Server.GameObjects;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
namespace Content.Server.Light.EntitySystems
@@ -24,6 +25,8 @@ namespace Content.Server.Light.EntitySystems
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly MetaDataSystem _metaData = default!;
private static readonly ProtoId<TagPrototype> TrashTag = "Trash";
public override void Initialize()
{
base.Initialize();
@@ -69,7 +72,7 @@ namespace Content.Server.Light.EntitySystems
_metaData.SetEntityName(ent, Loc.GetString(component.SpentName), meta);
_metaData.SetEntityDescription(ent, Loc.GetString(component.SpentDesc), meta);
_tagSystem.AddTag(ent, "Trash");
_tagSystem.AddTag(ent, TrashTag);
UpdateSounds(ent);
UpdateVisualizer(ent);

View File

@@ -16,6 +16,8 @@ public sealed class MagicSystem : SharedMagicSystem
[Dependency] private readonly TagSystem _tag = default!;
[Dependency] private readonly SharedMindSystem _mind = default!;
private static readonly ProtoId<TagPrototype> InvalidForSurvivorAntagTag = "InvalidForSurvivorAntag";
public override void Initialize()
{
base.Initialize();
@@ -48,8 +50,8 @@ public sealed class MagicSystem : SharedMagicSystem
if (!ev.MakeSurvivorAntagonist)
return;
if (_mind.TryGetMind(ev.Performer, out var mind, out _) && !_tag.HasTag(mind, "InvalidForSurvivorAntag"))
_tag.AddTag(mind, "InvalidForSurvivorAntag");
if (_mind.TryGetMind(ev.Performer, out var mind, out _) && !_tag.HasTag(mind, InvalidForSurvivorAntagTag))
_tag.AddTag(mind, InvalidForSurvivorAntagTag);
EntProtoId survivorRule = "Survivor";

View File

@@ -11,6 +11,7 @@ using Content.Shared.MagicMirror;
using Content.Shared.Popups;
using Content.Shared.Tag;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Prototypes;
namespace Content.Server.MagicMirror;
@@ -27,6 +28,8 @@ public sealed class MagicMirrorSystem : SharedMagicMirrorSystem
[Dependency] private readonly InventorySystem _inventory = default!;
[Dependency] private readonly TagSystem _tagSystem = default!;
private static readonly ProtoId<TagPrototype> HidesHairTag = "HidesHair";
public override void Initialize()
{
base.Initialize();
@@ -59,7 +62,7 @@ public sealed class MagicMirrorSystem : SharedMagicMirrorSystem
_popup.PopupEntity(
component.Target == message.Actor
? Loc.GetString("magic-mirror-blocked-by-hat-self")
: Loc.GetString("magic-mirror-blocked-by-hat-self-target"),
: Loc.GetString("magic-mirror-blocked-by-hat-self-target", ("target", Identity.Entity(message.Actor, EntityManager))),
message.Actor,
message.Actor,
PopupType.Medium);
@@ -95,7 +98,7 @@ public sealed class MagicMirrorSystem : SharedMagicMirrorSystem
}
else
{
_popup.PopupEntity(Loc.GetString("magic-mirror-change-slot-target", ("user", Identity.Name(message.Actor, EntityManager))), component.Target.Value, component.Target.Value, PopupType.Medium);
_popup.PopupEntity(Loc.GetString("magic-mirror-change-slot-target", ("user", Identity.Entity(message.Actor, EntityManager))), component.Target.Value, component.Target.Value, PopupType.Medium);
}
component.DoAfter = doAfterId;
@@ -175,7 +178,7 @@ public sealed class MagicMirrorSystem : SharedMagicMirrorSystem
}
else
{
_popup.PopupEntity(Loc.GetString("magic-mirror-change-color-target", ("user", Identity.Name(message.Actor, EntityManager))), component.Target.Value, component.Target.Value, PopupType.Medium);
_popup.PopupEntity(Loc.GetString("magic-mirror-change-color-target", ("user", Identity.Entity(message.Actor, EntityManager))), component.Target.Value, component.Target.Value, PopupType.Medium);
}
component.DoAfter = doAfterId;
@@ -253,7 +256,7 @@ public sealed class MagicMirrorSystem : SharedMagicMirrorSystem
}
else
{
_popup.PopupEntity(Loc.GetString("magic-mirror-remove-slot-target", ("user", Identity.Name(message.Actor, EntityManager))), component.Target.Value, component.Target.Value, PopupType.Medium);
_popup.PopupEntity(Loc.GetString("magic-mirror-remove-slot-target", ("user", Identity.Entity(message.Actor, EntityManager))), component.Target.Value, component.Target.Value, PopupType.Medium);
}
component.DoAfter = doAfterId;
@@ -331,7 +334,7 @@ public sealed class MagicMirrorSystem : SharedMagicMirrorSystem
}
else
{
_popup.PopupEntity(Loc.GetString("magic-mirror-add-slot-target", ("user", Identity.Name(message.Actor, EntityManager))), component.Target.Value, component.Target.Value, PopupType.Medium);
_popup.PopupEntity(Loc.GetString("magic-mirror-add-slot-target", ("user", Identity.Entity(message.Actor, EntityManager))), component.Target.Value, component.Target.Value, PopupType.Medium);
}
component.DoAfter = doAfterId;
@@ -391,7 +394,7 @@ public sealed class MagicMirrorSystem : SharedMagicMirrorSystem
var slots = _inventory.GetSlotEnumerator((target, inventoryComp), SlotFlags.WITHOUT_POCKET);
while (slots.MoveNext(out var slot))
{
if (slot.ContainedEntity != null && _tagSystem.HasTag(slot.ContainedEntity.Value, "HidesHair"))
if (slot.ContainedEntity != null && _tagSystem.HasTag(slot.ContainedEntity.Value, HidesHairTag))
{
return true;
}

View File

@@ -0,0 +1,51 @@
using System.Numerics;
using Content.Shared.CCVar;
using Content.Shared.Movement.Components;
using Content.Shared.Movement.Systems;
using Robust.Shared.Player;
namespace Content.Server.Movement.Systems;
public sealed class MobCollisionSystem : SharedMobCollisionSystem
{
private EntityQuery<ActorComponent> _actorQuery;
public override void Initialize()
{
base.Initialize();
_actorQuery = GetEntityQuery<ActorComponent>();
SubscribeLocalEvent<MobCollisionComponent, MobCollisionMessage>(OnServerMobCollision);
}
private void OnServerMobCollision(Entity<MobCollisionComponent> ent, ref MobCollisionMessage args)
{
MoveMob((ent.Owner, ent.Comp, Transform(ent.Owner)), args.Direction, args.SpeedModifier);
}
public override void Update(float frameTime)
{
if (!CfgManager.GetCVar(CCVars.MovementMobPushing))
return;
var query = EntityQueryEnumerator<MobCollisionComponent>();
while (query.MoveNext(out var uid, out var comp))
{
if (_actorQuery.HasComp(uid) || !PhysicsQuery.TryComp(uid, out var physics))
continue;
HandleCollisions((uid, comp, physics), frameTime);
}
base.Update(frameTime);
}
protected override void RaiseCollisionEvent(EntityUid uid, Vector2 direction, float speedMod)
{
RaiseLocalEvent(uid, new MobCollisionMessage()
{
Direction = direction,
SpeedModifier = speedMod,
});
}
}

View File

@@ -86,7 +86,7 @@ public sealed partial class MoveToOperator : HTNOperator, IHtnConditionalShutdow
return (false, null);
if (!_entManager.TryGetComponent<MapGridComponent>(xform.GridUid, out var ownerGrid) ||
!_entManager.TryGetComponent<MapGridComponent>(targetCoordinates.GetGridUid(_entManager), out var targetGrid))
!_entManager.TryGetComponent<MapGridComponent>(_transform.GetGrid(targetCoordinates), out var targetGrid))
{
return (false, null);
}
@@ -155,8 +155,8 @@ public sealed partial class MoveToOperator : HTNOperator, IHtnConditionalShutdow
{
if (blackboard.TryGetValue<EntityCoordinates>(NPCBlackboard.OwnerCoordinates, out var coordinates, _entManager))
{
var mapCoords = coordinates.ToMap(_entManager, _transform);
_steering.PrunePath(uid, mapCoords, targetCoordinates.ToMapPos(_entManager, _transform) - mapCoords.Position, result.Path);
var mapCoords = _transform.ToMapCoordinates(coordinates);
_steering.PrunePath(uid, mapCoords, _transform.ToMapCoordinates(targetCoordinates).Position - mapCoords.Position, result.Path);
}
comp.CurrentPath = new Queue<PathPoly>(result.Path);

View File

@@ -2,10 +2,6 @@ using System.Diagnostics.CodeAnalysis;
using System.Numerics;
using System.Threading;
using System.Threading.Tasks;
using Content.Server.Destructible;
using Content.Shared.Access.Components;
using Content.Shared.Climbing.Components;
using Content.Shared.Doors.Components;
using Content.Shared.NPC;
using Content.Shared.Physics;
using Robust.Shared.Collections;
@@ -281,7 +277,7 @@ public sealed partial class PathfindingSystem
var gridUid = ev.Component.GridUid;
var oldGridUid = ev.OldPosition.EntityId == ev.NewPosition.EntityId
? gridUid
: ev.OldPosition.GetGridUid(EntityManager);
: _transform.GetGrid((ev.Entity.Owner, ev.Component));
if (oldGridUid != null && oldGridUid != gridUid)
{
@@ -395,7 +391,7 @@ public sealed partial class PathfindingSystem
private Vector2i GetOrigin(EntityCoordinates coordinates, EntityUid gridUid)
{
var localPos = Vector2.Transform(coordinates.ToMapPos(EntityManager, _transform), _transform.GetInvWorldMatrix(gridUid));
var localPos = Vector2.Transform(_transform.ToMapCoordinates(coordinates).Position, _transform.GetInvWorldMatrix(gridUid));
return new Vector2i((int) Math.Floor(localPos.X / ChunkSize), (int) Math.Floor(localPos.Y / ChunkSize));
}

View File

@@ -461,7 +461,7 @@ public sealed partial class NPCSteeringSystem : SharedNPCSteeringSystem
return;
}
var targetPos = steering.Coordinates.ToMap(EntityManager, _transform);
var targetPos = _transform.ToMapCoordinates(steering.Coordinates);
var ourPos = _transform.GetMapCoordinates(uid, xform: xform);
PrunePath(uid, ourPos, targetPos.Position - ourPos.Position, result.Path);

View File

@@ -3,6 +3,7 @@ using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Components.SolutionManager;
using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.Tag;
using Robust.Shared.Prototypes;
namespace Content.Server.Nutrition.EntitySystems
{
@@ -11,6 +12,8 @@ namespace Content.Server.Nutrition.EntitySystems
[Dependency] private readonly SharedSolutionContainerSystem _solutionContainerSystem = default!;
[Dependency] private readonly TagSystem _tagSystem = default!;
private static readonly ProtoId<TagPrototype> TrashTag = "Trash";
public override void Initialize()
{
base.Initialize();
@@ -41,11 +44,11 @@ namespace Content.Server.Nutrition.EntitySystems
{
if (solution.Volume <= 0)
{
_tagSystem.AddTag(entity.Owner, "Trash");
_tagSystem.AddTag(entity.Owner, TrashTag);
return;
}
if (_tagSystem.HasTag(entity.Owner, "Trash"))
_tagSystem.RemoveTag(entity.Owner, "Trash");
_tagSystem.RemoveTag(entity.Owner, TrashTag);
}
}
}

View File

@@ -11,6 +11,7 @@ using Robust.Shared.Serialization.Manager;
using Robust.Shared.Utility;
using System.Linq;
using Robust.Server.GameObjects;
using Robust.Shared.Prototypes;
namespace Content.Server.Payload.EntitySystems;
@@ -23,6 +24,8 @@ public sealed class PayloadSystem : EntitySystem
[Dependency] private readonly IComponentFactory _componentFactory = default!;
[Dependency] private readonly ISerializationManager _serializationManager = default!;
private static readonly ProtoId<TagPrototype> PayloadTag = "Payload";
public override void Initialize()
{
base.Initialize();
@@ -44,7 +47,7 @@ public sealed class PayloadSystem : EntitySystem
{
foreach (var entity in container.ContainedEntities)
{
if (_tagSystem.HasTag(entity, "Payload"))
if (_tagSystem.HasTag(entity, PayloadTag))
yield return entity;
}
}
@@ -71,7 +74,7 @@ public sealed class PayloadSystem : EntitySystem
return;
// Ensure we don't enter a trigger-loop
DebugTools.Assert(!_tagSystem.HasTag(uid, "Payload"));
DebugTools.Assert(!_tagSystem.HasTag(uid, PayloadTag));
RaiseLocalEvent(parent, args, false);
}

View File

@@ -1,8 +1,10 @@
using System.Numerics;
using Content.Shared.Procedural;
using Content.Shared.Tag;
using Robust.Shared.Collections;
using Robust.Shared.Map.Components;
using Robust.Shared.Physics.Components;
using Robust.Shared.Prototypes;
namespace Content.Server.Procedural.DungeonJob;
@@ -12,13 +14,15 @@ public sealed partial class DungeonJob
* Run after the main dungeon generation
*/
private static readonly ProtoId<TagPrototype> WallTag = "Wall";
private bool HasWall(Vector2i tile)
{
var anchored = _maps.GetAnchoredEntitiesEnumerator(_gridUid, _grid, tile);
while (anchored.MoveNext(out var uid))
{
if (_tags.HasTag(uid.Value, "Wall"))
if (_tags.HasTag(uid.Value, WallTag))
return true;
}

View File

@@ -29,6 +29,7 @@ using Robust.Shared.Physics.Components;
using Robust.Shared.Utility;
using Robust.Shared.Map.Components;
using Content.Shared.Whitelist;
using Robust.Shared.Prototypes;
namespace Content.Server.Revenant.EntitySystems;
@@ -44,6 +45,8 @@ public sealed partial class RevenantSystem
[Dependency] private readonly SharedTransformSystem _transformSystem = default!;
[Dependency] private readonly SharedMapSystem _mapSystem = default!;
private static readonly ProtoId<TagPrototype> WindowTag = "Window";
private void InitializeAbilities()
{
SubscribeLocalEvent<RevenantComponent, UserActivateInWorldEvent>(OnInteract);
@@ -253,7 +256,7 @@ public sealed partial class RevenantSystem
foreach (var ent in lookup)
{
//break windows
if (tags.HasComponent(ent) && _tag.HasTag(ent, "Window"))
if (tags.HasComponent(ent) && _tag.HasTag(ent, WindowTag))
{
//hardcoded damage specifiers til i die.
var dspec = new DamageSpecifier();

View File

@@ -21,6 +21,7 @@ using Robust.Shared.GameStates;
using Robust.Shared.Map;
using Robust.Shared.Utility;
using Content.Shared.UserInterface;
using Robust.Shared.Prototypes;
namespace Content.Server.Shuttles.Systems;
@@ -43,6 +44,8 @@ public sealed partial class ShuttleConsoleSystem : SharedShuttleConsoleSystem
private readonly HashSet<Entity<ShuttleConsoleComponent>> _consoles = new();
private static readonly ProtoId<TagPrototype> CanPilotTag = "CanPilot";
public override void Initialize()
{
base.Initialize();
@@ -168,7 +171,7 @@ public sealed partial class ShuttleConsoleSystem : SharedShuttleConsoleSystem
private bool TryPilot(EntityUid user, EntityUid uid)
{
if (!_tags.HasTag(user, "CanPilot") ||
if (!_tags.HasTag(user, CanPilotTag) ||
!TryComp<ShuttleConsoleComponent>(uid, out var component) ||
!this.IsPowered(uid, EntityManager) ||
!Transform(uid).Anchored ||

View File

@@ -14,6 +14,7 @@ using Robust.Shared.Map.Components;
using Robust.Shared.Physics.Components;
using Robust.Shared.Physics.Events;
using Robust.Shared.Physics.Systems;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
namespace Content.Server.Singularity.EntitySystems;
@@ -36,6 +37,8 @@ public sealed class EventHorizonSystem : SharedEventHorizonSystem
[Dependency] private readonly TagSystem _tagSystem = default!;
#endregion Dependencies
private static readonly ProtoId<TagPrototype> HighRiskItemTag = "HighRiskItem";
private EntityQuery<PhysicsComponent> _physicsQuery;
public override void Initialize()
@@ -127,7 +130,7 @@ public sealed class EventHorizonSystem : SharedEventHorizonSystem
return;
if (HasComp<MindContainerComponent>(morsel)
|| _tagSystem.HasTag(morsel, "HighRiskItem")
|| _tagSystem.HasTag(morsel, HighRiskItemTag)
|| HasComp<ContainmentFieldGeneratorComponent>(morsel))
{
_adminLogger.Add(LogType.EntityDelete, LogImpact.High, $"{ToPrettyString(morsel):player} entered the event horizon of {ToPrettyString(hungry)} and was deleted");

View File

@@ -8,6 +8,7 @@ using Content.Shared.Interaction.Components;
using Content.Shared.Storage;
using Content.Shared.Tag;
using Robust.Shared.Network;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
namespace Content.Server.Tools.Innate;
@@ -22,6 +23,8 @@ public sealed class InnateToolSystem : EntitySystem
[Dependency] private readonly SharedHandsSystem _sharedHandsSystem = default!;
[Dependency] private readonly TagSystem _tagSystem = default!;
private static readonly ProtoId<TagPrototype> InnateDontDeleteTag = "InnateDontDelete";
public override void Initialize()
{
base.Initialize();
@@ -76,7 +79,7 @@ public sealed class InnateToolSystem : EntitySystem
{
foreach (var tool in component.ToolUids)
{
if (_tagSystem.HasTag(tool, "InnateDontDelete"))
if (_tagSystem.HasTag(tool, InnateDontDeleteTag))
{
RemComp<UnremoveableComponent>(tool);
}

View File

@@ -36,6 +36,7 @@ using Content.Shared.Traits.Assorted;
using Robust.Shared.Audio.Systems;
using Content.Shared.Ghost.Roles.Components;
using Content.Shared.Tag;
using Robust.Shared.Prototypes;
namespace Content.Server.Zombies;
@@ -61,6 +62,8 @@ public sealed partial class ZombieSystem
[Dependency] private readonly TagSystem _tag = default!;
[Dependency] private readonly NameModifierSystem _nameMod = default!;
private static readonly ProtoId<TagPrototype> InvalidForGlobalSpawnSpellTag = "InvalidForGlobalSpawnSpell";
/// <summary>
/// Handles an entity turning into a zombie when they die or go into crit
/// </summary>
@@ -290,6 +293,6 @@ public sealed partial class ZombieSystem
//Need to prevent them from getting an item, they have no hands.
// Also prevents them from becoming a Survivor. They're undead.
_tag.AddTag(target, "InvalidForGlobalSpawnSpell");
_tag.AddTag(target, InvalidForGlobalSpawnSpellTag);
}
}

View File

@@ -12,6 +12,7 @@ using Robust.Shared.Containers;
using Robust.Shared.GameStates;
using Content.Shared.GameTicking;
using Content.Shared.IdentityManagement;
using Content.Shared.Tag;
using Robust.Shared.Collections;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
@@ -24,11 +25,14 @@ public sealed class AccessReaderSystem : EntitySystem
[Dependency] private readonly InventorySystem _inventorySystem = default!;
[Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly EmagSystem _emag = default!;
[Dependency] private readonly TagSystem _tag = default!;
[Dependency] private readonly SharedGameTicker _gameTicker = default!;
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
[Dependency] private readonly SharedContainerSystem _containerSystem = default!;
[Dependency] private readonly SharedStationRecordsSystem _recordsSystem = default!;
private static readonly ProtoId<TagPrototype> PreventAccessLoggingTag = "PreventAccessLogging";
public override void Initialize()
{
base.Initialize();
@@ -115,13 +119,13 @@ public sealed class AccessReaderSystem : EntitySystem
var access = FindAccessTags(user, accessSources);
FindStationRecordKeys(user, out var stationKeys, accessSources);
if (IsAllowed(access, stationKeys, target, reader))
{
LogAccess((target, reader), user);
return true;
}
if (!IsAllowed(access, stationKeys, target, reader))
return false;
return false;
if (!_tag.HasTag(user, PreventAccessLoggingTag))
LogAccess((target, reader), user);
return true;
}
public bool GetMainAccessReader(EntityUid uid, [NotNullWhen(true)] out Entity<AccessReaderComponent>? ent)

View File

@@ -0,0 +1,59 @@
using Content.Shared.Administration;
using Content.Shared.CCVar.CVarAccess;
using Robust.Shared.Configuration;
namespace Content.Shared.CCVar;
public sealed partial class CCVars
{
/// <summary>
/// Is mob pushing enabled.
/// </summary>
[CVarControl(AdminFlags.VarEdit)]
public static readonly CVarDef<bool> MovementMobPushing =
CVarDef.Create("movement.mob_pushing", false, CVar.SERVER | CVar.REPLICATED);
/// <summary>
/// Can we push mobs not moving.
/// </summary>
[CVarControl(AdminFlags.VarEdit)]
public static readonly CVarDef<bool> MovementPushingStatic =
CVarDef.Create("movement.pushing_static", true, CVar.SERVER | CVar.REPLICATED);
/// <summary>
/// Dot product for the pushed entity's velocity to a target entity's velocity before it gets moved.
/// </summary>
[CVarControl(AdminFlags.VarEdit)]
public static readonly CVarDef<float> MovementPushingVelocityProduct =
CVarDef.Create("movement.pushing_velocity_product", -1f, CVar.SERVER | CVar.REPLICATED);
/// <summary>
/// Cap for how much an entity can be pushed per second.
/// </summary>
[CVarControl(AdminFlags.VarEdit)]
public static readonly CVarDef<float> MovementPushingCap =
CVarDef.Create("movement.pushing_cap", 100f, CVar.SERVER | CVar.REPLICATED);
/// <summary>
/// Minimum pushing impulse per tick. If the value is below this it rounds to 0.
/// This is an optimisation to avoid pushing small values that won't actually move the mobs.
/// </summary>
[CVarControl(AdminFlags.VarEdit)]
public static readonly CVarDef<float> MovementMinimumPush =
CVarDef.Create("movement.minimum_push", 0.1f, CVar.SERVER | CVar.REPLICATED);
// Really this just exists because hot reloading is cooked on rider.
/// <summary>
/// Penetration depth cap for considering mob collisions.
/// </summary>
[CVarControl(AdminFlags.VarEdit)]
public static readonly CVarDef<float> MovementPenetrationCap =
CVarDef.Create("movement.penetration_cap", 0.3f, CVar.SERVER | CVar.REPLICATED);
/// <summary>
/// Based on the mass difference multiplies the push amount by this proportionally.
/// </summary>
[CVarControl(AdminFlags.VarEdit)]
public static readonly CVarDef<float> MovementPushMassCap =
CVarDef.Create("movement.push_mass_cap", 1.75f, CVar.SERVER | CVar.REPLICATED);
}

View File

@@ -15,13 +15,4 @@ public sealed partial class CCVars
public static readonly CVarDef<float> StopSpeed =
CVarDef.Create("physics.stop_speed", 0.1f, CVar.ARCHIVE | CVar.REPLICATED | CVar.SERVER);
/// <summary>
/// Whether mobs can push objects like lockers.
/// </summary>
/// <remarks>
/// Technically client doesn't need to know about it but this may prevent a bug in the distant future so it stays.
/// </remarks>
public static readonly CVarDef<bool> MobPushing =
CVarDef.Create("physics.mob_pushing", false, CVar.REPLICATED | CVar.SERVER);
}

View File

@@ -23,6 +23,8 @@ public abstract class SharedChameleonClothingSystem : EntitySystem
[Dependency] private readonly TagSystem _tag = default!;
[Dependency] protected readonly SharedUserInterfaceSystem UI = default!;
private static readonly ProtoId<TagPrototype> WhitelistChameleonTag = "WhitelistChameleon";
public override void Initialize()
{
base.Initialize();
@@ -124,7 +126,7 @@ public abstract class SharedChameleonClothingSystem : EntitySystem
return false;
// check if it is marked as valid chameleon target
if (!proto.TryGetComponent(out TagComponent? tag, _factory) || !_tag.HasTag(tag, "WhitelistChameleon"))
if (!proto.TryGetComponent(out TagComponent? tag, _factory) || !_tag.HasTag(tag, WhitelistChameleonTag))
return false;
if (requiredTag != null && !_tag.HasTag(tag, requiredTag))

View File

@@ -1,7 +1,8 @@
using Content.Shared.Maps;
using Content.Shared.Maps;
using Content.Shared.Tag;
using JetBrains.Annotations;
using Robust.Shared.Map;
using Robust.Shared.Prototypes;
namespace Content.Shared.Construction.Conditions
{
@@ -9,6 +10,8 @@ namespace Content.Shared.Construction.Conditions
[DataDefinition]
public sealed partial class NoWindowsInTile : IConstructionCondition
{
private static readonly ProtoId<TagPrototype> WindowTag = "Window";
public bool Condition(EntityUid user, EntityCoordinates location, Direction direction)
{
var entManager = IoCManager.Resolve<IEntityManager>();
@@ -17,7 +20,7 @@ namespace Content.Shared.Construction.Conditions
foreach (var entity in location.GetEntitiesInTile(LookupFlags.Static))
{
if (tagSystem.HasTag(entity, "Window"))
if (tagSystem.HasTag(entity, WindowTag))
return false;
}

View File

@@ -6,6 +6,7 @@ using JetBrains.Annotations;
using Robust.Shared.Map;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Systems;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
namespace Content.Shared.Construction.Conditions
@@ -14,6 +15,8 @@ namespace Content.Shared.Construction.Conditions
[DataDefinition]
public sealed partial class WallmountCondition : IConstructionCondition
{
private static readonly ProtoId<TagPrototype> WallTag = "Wall";
public bool Condition(EntityUid user, EntityCoordinates location, Direction direction)
{
var entManager = IoCManager.Resolve<IEntityManager>();
@@ -42,7 +45,7 @@ namespace Content.Shared.Construction.Conditions
var tagSystem = entManager.System<TagSystem>();
var userToObjRaycastResults = physics.IntersectRayWithPredicate(entManager.GetComponent<TransformComponent>(user).MapID, rUserToObj, maxLength: length,
predicate: (e) => !tagSystem.HasTag(e, "Wall"));
predicate: (e) => !tagSystem.HasTag(e, WallTag));
var targetWall = userToObjRaycastResults.FirstOrNull();
@@ -53,7 +56,7 @@ namespace Content.Shared.Construction.Conditions
// check that we didn't try to build wallmount that facing another adjacent wall
var rAdjWall = new CollisionRay(objWorldPosition, directionWithOffset.Normalized(), (int) CollisionGroup.Impassable);
var adjWallRaycastResults = physics.IntersectRayWithPredicate(entManager.GetComponent<TransformComponent>(user).MapID, rAdjWall, maxLength: 0.5f,
predicate: e => e == targetWall.Value.HitEntity || !tagSystem.HasTag(e, "Wall"));
predicate: e => e == targetWall.Value.HitEntity || !tagSystem.HasTag(e, WallTag));
return !adjWallRaycastResults.Any();
}

View File

@@ -12,6 +12,7 @@ using Content.Shared.Tag;
using Content.Shared.Verbs;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Containers;
using Robust.Shared.Prototypes;
namespace Content.Shared.Delivery;
@@ -30,6 +31,9 @@ public abstract class SharedDeliverySystem : EntitySystem
[Dependency] private readonly SharedHandsSystem _hands = default!;
[Dependency] private readonly NameModifierSystem _nameModifier = default!;
private static readonly ProtoId<TagPrototype> TrashTag = "Trash";
private static readonly ProtoId<TagPrototype> RecyclableTag = "Recyclable";
public override void Initialize()
{
base.Initialize();
@@ -129,7 +133,7 @@ public abstract class SharedDeliverySystem : EntitySystem
ent.Comp.IsOpened = true;
_appearance.SetData(ent, DeliveryVisuals.IsTrash, ent.Comp.IsOpened);
_tag.AddTags(ent, "Trash", "Recyclable");
_tag.AddTags(ent, TrashTag, RecyclableTag);
EnsureComp<SpaceGarbageComponent>(ent);
RemComp<StealTargetComponent>(ent); // opened mail should not count for the objective

View File

@@ -5,6 +5,7 @@ using Content.Shared.Damage;
using Content.Shared.Hands.Components;
using Content.Shared.Tag;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
@@ -23,6 +24,8 @@ public abstract partial class SharedDoAfterSystem : EntitySystem
/// </summary>
private static readonly TimeSpan ExcessTime = TimeSpan.FromSeconds(0.5f);
private static readonly ProtoId<TagPrototype> InstantDoAftersTag = "InstantDoAfters";
public override void Initialize()
{
base.Initialize();
@@ -233,7 +236,7 @@ public abstract partial class SharedDoAfterSystem : EntitySystem
// TODO DO AFTER
// Why does this tag exist? Just make this a bool on the component?
if (args.Delay <= TimeSpan.Zero || _tag.HasTag(args.User, "InstantDoAfters"))
if (args.Delay <= TimeSpan.Zero || _tag.HasTag(args.User, InstantDoAftersTag))
{
RaiseDoAfterEvents(doAfter, comp);
// We don't store instant do-afters. This is just a lazy way of hiding them from client-side visuals.

View File

@@ -18,6 +18,7 @@ using Robust.Shared.Network;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Systems;
using Robust.Shared.Player;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
namespace Content.Shared.Follower;
@@ -32,6 +33,8 @@ public sealed class FollowerSystem : EntitySystem
[Dependency] private readonly INetManager _netMan = default!;
[Dependency] private readonly ISharedAdminManager _adminManager = default!;
private static readonly ProtoId<TagPrototype> ForceableFollowTag = "ForceableFollow";
public override void Initialize()
{
base.Initialize();
@@ -106,7 +109,7 @@ public sealed class FollowerSystem : EntitySystem
ev.Verbs.Add(verb);
}
if (_tagSystem.HasTag(ev.Target, "ForceableFollow"))
if (_tagSystem.HasTag(ev.Target, ForceableFollowTag))
{
if (!ev.CanAccess || !ev.CanInteract)
return;

View File

@@ -6,12 +6,16 @@ using Content.Shared.Inventory.VirtualItem;
using Content.Shared.Tag;
using Robust.Shared.Containers;
using Robust.Shared.Map;
using Robust.Shared.Prototypes;
namespace Content.Shared.Hands.EntitySystems;
public abstract partial class SharedHandsSystem
{
[Dependency] private readonly TagSystem _tagSystem = default!;
private static readonly ProtoId<TagPrototype> BypassDropChecksTag = "BypassDropChecks";
private void InitializeDrop()
{
SubscribeLocalEvent<HandsComponent, EntRemovedFromContainerMessage>(HandleEntityRemoved);
@@ -37,7 +41,7 @@ public abstract partial class SharedHandsSystem
private bool ShouldIgnoreRestrictions(EntityUid user)
{
//Checks if the Entity is something that shouldn't care about drop distance or walls ie Aghost
return !_tagSystem.HasTag(user, "BypassDropChecks");
return !_tagSystem.HasTag(user, BypassDropChecksTag);
}
/// <summary>

View File

@@ -7,6 +7,7 @@ using Content.Shared.Tag;
using JetBrains.Annotations;
using Robust.Shared.Containers;
using Robust.Shared.Network;
using Robust.Shared.Prototypes;
using System.Linq;
namespace Content.Shared.Implants;
@@ -21,6 +22,9 @@ public abstract class SharedSubdermalImplantSystem : EntitySystem
public const string BaseStorageId = "storagebase";
private static readonly ProtoId<TagPrototype> MicroBombTag = "MicroBomb";
private static readonly ProtoId<TagPrototype> MacroBombTag = "MacroBomb";
public override void Initialize()
{
SubscribeLocalEvent<SubdermalImplantComponent, EntGotInsertedIntoContainerMessage>(OnInsert);
@@ -43,11 +47,11 @@ public abstract class SharedSubdermalImplantSystem : EntitySystem
}
//replace micro bomb with macro bomb
if (_container.TryGetContainer(component.ImplantedEntity.Value, ImplanterComponent.ImplantSlotId, out var implantContainer) && _tag.HasTag(uid, "MacroBomb"))
if (_container.TryGetContainer(component.ImplantedEntity.Value, ImplanterComponent.ImplantSlotId, out var implantContainer) && _tag.HasTag(uid, MacroBombTag))
{
foreach (var implant in implantContainer.ContainedEntities)
{
if (_tag.HasTag(implant, "MicroBomb"))
if (_tag.HasTag(implant, MicroBombTag))
{
_container.Remove(implant, implantContainer);
QueueDel(implant);

View File

@@ -37,6 +37,7 @@ using Robust.Shared.Physics;
using Robust.Shared.Physics.Components;
using Robust.Shared.Physics.Systems;
using Robust.Shared.Player;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
@@ -87,6 +88,8 @@ namespace Content.Shared.Interaction
public const float MaxRaycastRange = 100f;
public const string RateLimitKey = "Interaction";
private static readonly ProtoId<TagPrototype> BypassInteractionRangeChecksTag = "BypassInteractionRangeChecks";
public delegate bool Ignored(EntityUid entity);
public override void Initialize()
@@ -318,7 +321,7 @@ namespace Content.Shared.Interaction
{
// This is for Admin/mapping convenience. If ever there are other ghosts that can still interact, this check
// might need to be more selective.
return !_tagSystem.HasTag(user, "BypassInteractionRangeChecks");
return !_tagSystem.HasTag(user, BypassInteractionRangeChecksTag);
}
/// <summary>

View File

@@ -65,6 +65,8 @@ public abstract class SharedMagicSystem : EntitySystem
[Dependency] private readonly SharedMindSystem _mind = default!;
[Dependency] private readonly SharedStunSystem _stun = default!;
private static readonly ProtoId<TagPrototype> InvalidForGlobalSpawnSpellTag = "InvalidForGlobalSpawnSpell";
public override void Initialize()
{
base.Initialize();
@@ -484,7 +486,7 @@ public abstract class SharedMagicSystem : EntitySystem
var ent = human.Comp.OwnedEntity.Value;
if (_tag.HasTag(ent, "InvalidForGlobalSpawnSpell"))
if (_tag.HasTag(ent, InvalidForGlobalSpawnSpellTag))
continue;
var mapCoords = _transform.GetMapCoordinates(ent);

View File

@@ -0,0 +1,60 @@
using System.Numerics;
using Content.Shared.Movement.Systems;
using Robust.Shared.GameStates;
namespace Content.Shared.Movement.Components;
/// <summary>
/// Handles mobs pushing against each other.
/// </summary>
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(fieldDeltas: true)]
public sealed partial class MobCollisionComponent : Component
{
// If you want to tweak the feel of the pushing use SpeedModifier and Strength.
// Strength goes both ways and affects how much the other mob is pushed by so controls static pushing a lot.
// Speed mod affects your own mob primarily.
/// <summary>
/// Is this mob currently colliding? Used for SpeedModifier.
/// </summary>
[DataField, AutoNetworkedField]
public bool Colliding;
// TODO: I hate this but also I couldn't quite figure out a way to avoid having to dirty it every tick.
// The issue is it's a time target that changes constantly so we can't just use a timespan.
// However that doesn't mean it should be modified every tick if we're still colliding.
/// <summary>
/// Buffer time for <see cref="SpeedModifier"/> to keep applying after the entities are no longer colliding.
/// Without this you will get jittering unless you are very specific with your values.
/// </summary>
[DataField, AutoNetworkedField]
public float BufferAccumulator = SharedMobCollisionSystem.BufferTime;
/// <summary>
/// The speed modifier for mobs currently pushing.
/// By setting this low you can ensure you don't have to set the push-strength too high if you can push static entities.
/// </summary>
[DataField, AutoNetworkedField]
public float SpeedModifier = 1f;
[DataField, AutoNetworkedField]
public float MinimumSpeedModifier = 0.35f;
/// <summary>
/// Strength of the pushback for entities. This is combined between the 2 entities being pushed.
/// </summary>
[DataField, AutoNetworkedField]
public float Strength = 50f;
// Yes I know, I will deal with it if I ever refactor collision layers due to misuse.
// If anything it probably needs some assurance on mobcollisionsystem for it.
/// <summary>
/// Fixture to listen to for mob collisions.
/// </summary>
[DataField, AutoNetworkedField]
public string FixtureId = "flammable";
[DataField, AutoNetworkedField]
public Vector2 Direction;
}

View File

@@ -0,0 +1,338 @@
using System.Numerics;
using Content.Shared.CCVar;
using Content.Shared.Movement.Components;
using Robust.Shared;
using Robust.Shared.Configuration;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Components;
using Robust.Shared.Physics.Systems;
using Robust.Shared.Random;
using Robust.Shared.Serialization;
using Robust.Shared.Utility;
namespace Content.Shared.Movement.Systems;
public abstract class SharedMobCollisionSystem : EntitySystem
{
[Dependency] protected readonly IConfigurationManager CfgManager = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly MovementSpeedModifierSystem _moveMod = default!;
[Dependency] protected readonly SharedPhysicsSystem Physics = default!;
[Dependency] private readonly SharedTransformSystem _xformSystem = default!;
protected EntityQuery<MobCollisionComponent> MobQuery;
protected EntityQuery<PhysicsComponent> PhysicsQuery;
/// <summary>
/// <see cref="CCVars.MovementPushingCap"/>
/// </summary>
private float _pushingCap;
/// <summary>
/// <see cref="CCVars.MovementPushingVelocityProduct"/>
/// </summary>
private float _pushingDotProduct;
/// <summary>
/// <see cref="CCVars.MovementMinimumPush"/>
/// </summary>
private float _minimumPushSquared = 0.01f;
private float _penCap;
/// <summary>
/// Time after we stop colliding with another mob before adjusting the movespeedmodifier.
/// This is required so if we stop colliding for a frame we don't fully reset and get jerky movement.
/// </summary>
public const float BufferTime = 0.2f;
private float _massDiffCap;
public override void Initialize()
{
base.Initialize();
UpdatePushCap();
Subs.CVar(CfgManager, CVars.NetTickrate, _ => UpdatePushCap());
Subs.CVar(CfgManager, CCVars.MovementMinimumPush, val => _minimumPushSquared = val * val, true);
Subs.CVar(CfgManager, CCVars.MovementPenetrationCap, val => _penCap = val, true);
Subs.CVar(CfgManager, CCVars.MovementPushingCap, _ => UpdatePushCap());
Subs.CVar(CfgManager, CCVars.MovementPushingVelocityProduct,
value =>
{
_pushingDotProduct = value;
}, true);
Subs.CVar(CfgManager, CCVars.MovementPushMassCap, val => _massDiffCap = val, true);
MobQuery = GetEntityQuery<MobCollisionComponent>();
PhysicsQuery = GetEntityQuery<PhysicsComponent>();
SubscribeAllEvent<MobCollisionMessage>(OnCollision);
SubscribeLocalEvent<MobCollisionComponent, RefreshMovementSpeedModifiersEvent>(OnMoveModifier);
UpdatesBefore.Add(typeof(SharedPhysicsSystem));
}
private void UpdatePushCap()
{
_pushingCap = (1f / CfgManager.GetCVar(CVars.NetTickrate)) * CfgManager.GetCVar(CCVars.MovementPushingCap);
}
public override void Update(float frameTime)
{
base.Update(frameTime);
var query = AllEntityQuery<MobCollisionComponent>();
while (query.MoveNext(out var uid, out var comp))
{
if (!comp.Colliding)
continue;
comp.BufferAccumulator -= frameTime;
DirtyField(uid, comp, nameof(MobCollisionComponent.BufferAccumulator));
var direction = comp.Direction;
if (comp.BufferAccumulator <= 0f)
{
SetColliding((uid, comp), false, 1f);
}
// Apply the mob collision; if it's too low ignore it (e.g. if mob friction would overcome it).
// This is so we don't spam velocity changes every tick. It's not that expensive for physics but
// avoids the networking side.
else if (direction != Vector2.Zero && PhysicsQuery.TryComp(uid, out var physics))
{
DebugTools.Assert(direction.LengthSquared() >= _minimumPushSquared);
if (direction.Length() > _pushingCap)
{
direction = direction.Normalized() * _pushingCap;
}
Physics.ApplyLinearImpulse(uid, direction * physics.Mass, body: physics);
comp.Direction = Vector2.Zero;
DirtyField(uid, comp, nameof(MobCollisionComponent.Direction));
}
}
}
private void OnMoveModifier(Entity<MobCollisionComponent> ent, ref RefreshMovementSpeedModifiersEvent args)
{
if (!ent.Comp.Colliding)
return;
args.ModifySpeed(ent.Comp.SpeedModifier);
}
private void SetColliding(Entity<MobCollisionComponent> entity, bool value, float speedMod)
{
if (value)
{
entity.Comp.BufferAccumulator = BufferTime;
DirtyField(entity.Owner, entity.Comp, nameof(MobCollisionComponent.BufferAccumulator));
}
else
{
DebugTools.Assert(speedMod.Equals(1f));
}
if (entity.Comp.Colliding != value)
{
entity.Comp.Colliding = value;
DirtyField(entity.Owner, entity.Comp, nameof(MobCollisionComponent.Colliding));
}
if (!entity.Comp.SpeedModifier.Equals(speedMod))
{
entity.Comp.SpeedModifier = speedMod;
_moveMod.RefreshMovementSpeedModifiers(entity.Owner);
DirtyField(entity.Owner, entity.Comp, nameof(MobCollisionComponent.SpeedModifier));
}
}
private void OnCollision(MobCollisionMessage msg, EntitySessionEventArgs args)
{
var player = args.SenderSession.AttachedEntity;
if (!MobQuery.TryComp(player, out var comp))
return;
var xform = Transform(player.Value);
// If not parented directly to a grid then fail it.
if (xform.ParentUid != xform.GridUid && xform.ParentUid != xform.MapUid)
return;
var direction = msg.Direction;
MoveMob((player.Value, comp, xform), direction, msg.SpeedModifier);
}
protected void MoveMob(Entity<MobCollisionComponent, TransformComponent> entity, Vector2 direction, float speedMod)
{
// Length too short to do anything.
var pushing = true;
if (direction.LengthSquared() < _minimumPushSquared)
{
pushing = false;
direction = Vector2.Zero;
speedMod = 1f;
}
else if (float.IsNaN(direction.X) || float.IsNaN(direction.Y))
{
direction = Vector2.Zero;
}
speedMod = Math.Clamp(speedMod, 0f, 1f);
SetColliding(entity, pushing, speedMod);
if (direction == entity.Comp1.Direction)
return;
entity.Comp1.Direction = direction;
DirtyField(entity.Owner, entity.Comp1, nameof(MobCollisionComponent.Direction));
}
protected bool HandleCollisions(Entity<MobCollisionComponent, PhysicsComponent> entity, float frameTime)
{
var physics = entity.Comp2;
if (physics.ContactCount == 0)
return false;
var ourVelocity = entity.Comp2.LinearVelocity;
if (ourVelocity == Vector2.Zero && !CfgManager.GetCVar(CCVars.MovementPushingStatic))
return false;
var xform = Transform(entity.Owner);
if (xform.ParentUid != xform.GridUid && xform.ParentUid != xform.MapUid)
return false;
var ev = new AttemptMobCollideEvent();
RaiseLocalEvent(entity.Owner, ref ev);
if (ev.Cancelled)
return false;
var (worldPos, worldRot) = _xformSystem.GetWorldPositionRotation(xform);
var ourTransform = new Transform(worldPos, worldRot);
var contacts = Physics.GetContacts(entity.Owner);
var direction = Vector2.Zero;
var contactCount = 0;
var ourMass = physics.FixturesMass;
var speedMod = 1f;
while (contacts.MoveNext(out var contact))
{
if (!contact.IsTouching)
continue;
var ourFixture = contact.OurFixture(entity.Owner);
if (ourFixture.Id != entity.Comp1.FixtureId)
continue;
var other = contact.OtherEnt(entity.Owner);
if (!MobQuery.TryComp(other, out var otherComp) || !PhysicsQuery.TryComp(other, out var otherPhysics))
continue;
var velocityProduct = Vector2.Dot(ourVelocity, otherPhysics.LinearVelocity);
// If we're moving opposite directions for example then ignore (based on cvar).
if (velocityProduct < _pushingDotProduct)
{
continue;
}
var targetEv = new AttemptMobTargetCollideEvent();
RaiseLocalEvent(other, ref targetEv);
if (targetEv.Cancelled)
continue;
// TODO: More robust overlap detection.
var otherTransform = Physics.GetPhysicsTransform(other);
var diff = ourTransform.Position - otherTransform.Position;
if (diff == Vector2.Zero)
{
diff = _random.NextVector2(0.01f);
}
// 0.7 for 0.35 + 0.35 for mob bounds (see TODO above).
// Clamp so we don't get a heap of penetration depth and suddenly lurch other mobs.
// This is also so we don't have to trigger the speed-cap above.
// Maybe we just do speedcap and dump this? Though it's less configurable and the cap is just there for cheaters.
var penDepth = Math.Clamp(0.7f - diff.Length(), 0f, _penCap);
// Sum the strengths so we get pushes back the same amount (impulse-wise, ignoring prediction).
var mobMovement = penDepth * diff.Normalized() * (entity.Comp1.Strength + otherComp.Strength);
// Big mob push smaller mob, needs fine-tuning and potentially another co-efficient.
if (_massDiffCap > 0f)
{
var modifier = Math.Clamp(
otherPhysics.FixturesMass / ourMass,
1f / _massDiffCap,
_massDiffCap);
mobMovement *= modifier;
var speedReduction = 1f - entity.Comp1.MinimumSpeedModifier;
var speedModifier = Math.Clamp(
1f - speedReduction * modifier,
entity.Comp1.MinimumSpeedModifier, 1f);
speedMod = MathF.Min(speedModifier, 1f);
}
// Need the push strength proportional to penetration depth.
direction += mobMovement;
contactCount++;
}
if (direction == Vector2.Zero)
{
return contactCount > 0;
}
direction *= frameTime;
RaiseCollisionEvent(entity.Owner, direction, speedMod);
return true;
}
protected abstract void RaiseCollisionEvent(EntityUid uid, Vector2 direction, float speedmodifier);
/// <summary>
/// Raised from client -> server indicating mob push direction OR server -> server for NPC mob pushes.
/// </summary>
[Serializable, NetSerializable]
protected sealed class MobCollisionMessage : EntityEventArgs
{
public Vector2 Direction;
public float SpeedModifier;
}
}
/// <summary>
/// Raised on the entity itself when attempting to handle mob collisions.
/// </summary>
[ByRefEvent]
public record struct AttemptMobCollideEvent
{
public bool Cancelled;
}
/// <summary>
/// Raised on the other entity when attempting mob collisions.
/// </summary>
[ByRefEvent]
public record struct AttemptMobTargetCollideEvent
{
public bool Cancelled;
}

View File

@@ -14,12 +14,12 @@ public abstract partial class SharedMoverController
private void OnAfterRelayTargetState(Entity<MovementRelayTargetComponent> entity, ref AfterAutoHandleStateEvent args)
{
Physics.UpdateIsPredicted(entity.Owner);
PhysicsSystem.UpdateIsPredicted(entity.Owner);
}
private void OnAfterRelayState(Entity<RelayInputMoverComponent> entity, ref AfterAutoHandleStateEvent args)
{
Physics.UpdateIsPredicted(entity.Owner);
PhysicsSystem.UpdateIsPredicted(entity.Owner);
}
/// <summary>
@@ -42,7 +42,7 @@ public abstract partial class SharedMoverController
{
oldTarget.Source = EntityUid.Invalid;
RemComp(component.RelayEntity, oldTarget);
Physics.UpdateIsPredicted(component.RelayEntity);
PhysicsSystem.UpdateIsPredicted(component.RelayEntity);
}
var targetComp = EnsureComp<MovementRelayTargetComponent>(relayEntity);
@@ -50,11 +50,11 @@ public abstract partial class SharedMoverController
{
oldRelay.RelayEntity = EntityUid.Invalid;
RemComp(targetComp.Source, oldRelay);
Physics.UpdateIsPredicted(targetComp.Source);
PhysicsSystem.UpdateIsPredicted(targetComp.Source);
}
Physics.UpdateIsPredicted(uid);
Physics.UpdateIsPredicted(relayEntity);
PhysicsSystem.UpdateIsPredicted(uid);
PhysicsSystem.UpdateIsPredicted(relayEntity);
component.RelayEntity = relayEntity;
targetComp.Source = uid;
Dirty(uid, component);
@@ -63,8 +63,8 @@ public abstract partial class SharedMoverController
private void OnRelayShutdown(Entity<RelayInputMoverComponent> entity, ref ComponentShutdown args)
{
Physics.UpdateIsPredicted(entity.Owner);
Physics.UpdateIsPredicted(entity.Comp.RelayEntity);
PhysicsSystem.UpdateIsPredicted(entity.Owner);
PhysicsSystem.UpdateIsPredicted(entity.Comp.RelayEntity);
if (TryComp<InputMoverComponent>(entity.Comp.RelayEntity, out var inputMover))
SetMoveInput((entity.Comp.RelayEntity, inputMover), MoveButtons.None);
@@ -78,8 +78,8 @@ public abstract partial class SharedMoverController
private void OnTargetRelayShutdown(Entity<MovementRelayTargetComponent> entity, ref ComponentShutdown args)
{
Physics.UpdateIsPredicted(entity.Owner);
Physics.UpdateIsPredicted(entity.Comp.Source);
PhysicsSystem.UpdateIsPredicted(entity.Owner);
PhysicsSystem.UpdateIsPredicted(entity.Comp.Source);
if (Timing.ApplyingState)
return;

View File

@@ -20,6 +20,7 @@ using Robust.Shared.Physics;
using Robust.Shared.Physics.Components;
using Robust.Shared.Physics.Controllers;
using Robust.Shared.Physics.Systems;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
using PullableComponent = Content.Shared.Movement.Pulling.Components.PullableComponent;
@@ -43,7 +44,6 @@ public abstract partial class SharedMoverController : VirtualController
[Dependency] private readonly SharedContainerSystem _container = default!;
[Dependency] private readonly SharedMapSystem _mapSystem = default!;
[Dependency] private readonly SharedGravitySystem _gravity = default!;
[Dependency] protected readonly SharedPhysicsSystem Physics = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
[Dependency] private readonly TagSystem _tags = default!;
@@ -60,6 +60,8 @@ public abstract partial class SharedMoverController : VirtualController
protected EntityQuery<FootstepModifierComponent> FootstepModifierQuery;
protected EntityQuery<MapGridComponent> MapGridQuery;
private static readonly ProtoId<TagPrototype> FootstepSoundTag = "FootstepSound";
/// <summary>
/// <see cref="CCVars.StopSpeed"/>
/// </summary>
@@ -105,6 +107,14 @@ public abstract partial class SharedMoverController : VirtualController
public override void UpdateAfterSolve(bool prediction, float frameTime)
{
base.UpdateAfterSolve(prediction, frameTime);
var query = AllEntityQuery<InputMoverComponent, PhysicsComponent>();
while (query.MoveNext(out var uid, out var _, out var physics))
{
//PhysicsSystem.SetLinearVelocity(uid, Vector2.Zero, body: physics);
}
UsedMobMovement.Clear();
}
@@ -424,7 +434,7 @@ public abstract partial class SharedMoverController : VirtualController
{
sound = null;
if (!CanSound() || !_tags.HasTag(uid, "FootstepSound"))
if (!CanSound() || !_tags.HasTag(uid, FootstepSoundTag))
return false;
var coordinates = xform.Coordinates;

View File

@@ -9,6 +9,7 @@ using Content.Shared.Tag;
using Robust.Shared.Player;
using Robust.Shared.Audio.Systems;
using static Content.Shared.Paper.PaperComponent;
using Robust.Shared.Prototypes;
namespace Content.Shared.Paper;
@@ -23,6 +24,9 @@ public sealed class PaperSystem : EntitySystem
[Dependency] private readonly MetaDataSystem _metaSystem = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
private static readonly ProtoId<TagPrototype> WriteIgnoreStampsTag = "WriteIgnoreStamps";
private static readonly ProtoId<TagPrototype> WriteTag = "Write";
public override void Initialize()
{
base.Initialize();
@@ -100,8 +104,8 @@ public sealed class PaperSystem : EntitySystem
private void OnInteractUsing(Entity<PaperComponent> entity, ref InteractUsingEvent args)
{
// only allow editing if there are no stamps or when using a cyberpen
var editable = entity.Comp.StampedBy.Count == 0 || _tagSystem.HasTag(args.Used, "WriteIgnoreStamps");
if (_tagSystem.HasTag(args.Used, "Write"))
var editable = entity.Comp.StampedBy.Count == 0 || _tagSystem.HasTag(args.Used, WriteIgnoreStampsTag);
if (_tagSystem.HasTag(args.Used, WriteTag))
{
if (editable)
{

View File

@@ -1,7 +1,6 @@
using System.Numerics;
using Content.Shared.Conveyor;
using Content.Shared.Gravity;
using Content.Shared.Magic;
using Content.Shared.Movement.Components;
using Content.Shared.Movement.Events;
using Content.Shared.Movement.Systems;

View File

@@ -66,8 +66,7 @@ public abstract partial class SharedProjectileSystem : EntitySystem
private void OnEmbedRemove(Entity<EmbeddableProjectileComponent> embeddable, ref RemoveEmbeddedProjectileEvent args)
{
// Whacky prediction issues.
if (args.Cancelled || _net.IsClient)
if (args.Cancelled)
return;
EmbedDetach(embeddable, embeddable.Comp, args.User);

View File

@@ -51,6 +51,7 @@ public class RCDSystem : EntitySystem
private readonly EntProtoId _instantConstructionFx = "EffectRCDConstruct0";
private readonly ProtoId<RCDPrototype> _deconstructTileProto = "DeconstructTile";
private readonly ProtoId<RCDPrototype> _deconstructLatticeProto = "DeconstructLattice";
private static readonly ProtoId<TagPrototype> CatwalkTag = "Catwalk";
private HashSet<EntityUid> _intersectingEntities = new();
@@ -411,7 +412,7 @@ public class RCDSystem : EntitySystem
if (isWindow && HasComp<SharedCanBuildWindowOnTopComponent>(ent))
continue;
if (isCatwalk && _tags.HasTag(ent, "Catwalk"))
if (isCatwalk && _tags.HasTag(ent, CatwalkTag))
{
if (popMsgs)
_popup.PopupClient(Loc.GetString("rcd-component-cannot-build-on-occupied-tile-message"), uid, user);

View File

@@ -1,4 +1,5 @@
using Content.Shared.Hands.Components;
using Content.Shared.Movement.Systems;
using Content.Shared.Physics;
using Content.Shared.Rotation;
using Robust.Shared.Audio.Systems;
@@ -16,6 +17,29 @@ public sealed class StandingStateSystem : EntitySystem
// If StandingCollisionLayer value is ever changed to more than one layer, the logic needs to be edited.
private const int StandingCollisionLayer = (int) CollisionGroup.MidImpassable;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<StandingStateComponent, AttemptMobCollideEvent>(OnMobCollide);
SubscribeLocalEvent<StandingStateComponent, AttemptMobTargetCollideEvent>(OnMobTargetCollide);
}
private void OnMobTargetCollide(Entity<StandingStateComponent> ent, ref AttemptMobTargetCollideEvent args)
{
if (!ent.Comp.Standing)
{
args.Cancelled = true;
}
}
private void OnMobCollide(Entity<StandingStateComponent> ent, ref AttemptMobCollideEvent args)
{
if (!ent.Comp.Standing)
{
args.Cancelled = true;
}
}
public bool IsDown(EntityUid uid, StandingStateComponent? standingState = null)
{
if (!Resolve(uid, ref standingState, false))

View File

@@ -1,103 +1,4 @@
Entries:
- author: ArZarLordOfMango
changes:
- message: Most toggleable clothing must now be equipped to toggle their actions.
type: Fix
id: 7623
time: '2024-11-19T20:31:38.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/32826
- author: Plykiya
changes:
- message: The SWAT crate from cargo now requires armory access to open.
type: Fix
id: 7624
time: '2024-11-20T00:57:01.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/33415
- author: SlamBamActionman
changes:
- message: It's no longer possible to drag an item out of a container's UI to drop
it.
type: Tweak
id: 7625
time: '2024-11-20T01:00:38.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/32706
- author: Plykiya
changes:
- message: The crew monitoring crate now contains a flatpack of the server and computers,
and can be opened with science access instead of engineering access now.
type: Tweak
id: 7626
time: '2024-11-20T01:05:20.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/33417
- author: Beck Thompson
changes:
- message: Toggle verbs are no longer duplicated on magboots and fire extinguishers!
type: Fix
id: 7627
time: '2024-11-20T01:53:53.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/32138
- author: qwerltaz
changes:
- message: A new grid item view is available in the construction menu, togglable
with a button.
type: Add
- message: Construction menu default window size was tweaked.
type: Tweak
id: 7628
time: '2024-11-20T01:54:49.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/32577
- author: SpaceLizard24
changes:
- message: Reduced crafting costs of colored light tubes.
type: Tweak
id: 7629
time: '2024-11-20T01:59:31.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/33376
- author: thetolbean
changes:
- message: Items with a damage of 0 now have correct damage examination text.
type: Fix
id: 7630
time: '2024-11-20T02:05:15.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/33064
- author: SaphireLattice
changes:
- message: The Singularity/Tesla generator now requires being surrounded by containment
fields to activate. This can be disabled with an Emag.
type: Tweak
id: 7631
time: '2024-11-20T05:55:58.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/33358
- author: TheWaffleJesus
changes:
- message: You can now craft items with stacks of capacitors without it eating it
all!
type: Fix
id: 7632
time: '2024-11-20T07:18:38.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/31966
- author: ScarKy0, GoldenCan
changes:
- message: Ion stormed lawsets no longer persist between shifts.
type: Fix
- message: Cyborgs are now notified when inserted into a chassis with modified laws.
type: Tweak
id: 7633
time: '2024-11-20T07:55:12.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/33311
- author: ScarKy0, GoldenCan
changes:
- message: The Derelict Cyborg - a broken cyborg with altered laws due to year of
exposure to ion storms - can now appear as a ghost role through a new midround
event.
type: Add
- message: An ion storm affecting a Cyborg can no longer alter the law order of
the AI, cyborgs created later in the round or AI's and cyborgs in subsequent
rounds.
type: Fix
id: 7634
time: '2024-11-20T07:55:13.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/32499
- author: IProduceWidgets
changes:
- message: Presents no longer make non-items into items
@@ -3900,3 +3801,90 @@
id: 8122
time: '2025-04-01T23:26:53.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/35191
- author: Fildrance
changes:
- message: fixed missing deconstruct on RCD
type: Fix
id: 8123
time: '2025-04-02T16:11:35.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/36255
- author: qwerltaz
changes:
- message: Dragon rifts now shine a different color depending on charge progress.
type: Add
id: 8124
time: '2025-04-02T18:37:35.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/36216
- author: aada
changes:
- message: Diphenhydramine now causes light drowsiness.
type: Tweak
id: 8125
time: '2025-04-03T06:29:52.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/36212
- author: sowelipililimute
changes:
- message: You can now more easily interact with objects behind faded ones, and
you can look behind fadeable objects in your FOV by hovering them with your
mouse pointer
type: Add
id: 8126
time: '2025-04-03T06:58:05.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/35863
- author: whatston3
changes:
- message: Fancy tables and curtains now respect carpet stacks.
type: Fix
id: 8127
time: '2025-04-03T14:45:04.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/33721
- author: YuNii
changes:
- message: Stop ghosts from being logged to airlocks
type: Fix
id: 8128
time: '2025-04-03T22:24:30.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/36261
- author: K-Dynamic
changes:
- message: Proto-Kinetic Accelerators and PTK-800 Shuttle Gun recipes are now available
to the security techfab when researched by science.
type: Add
id: 8129
time: '2025-04-04T13:12:04.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/34566
- author: lzk228
changes:
- message: Fix ability to use implants on borgs and bots.
type: Fix
id: 8130
time: '2025-04-04T15:36:28.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/36218
- author: TheBlueYowie
changes:
- message: Aligned Adv. Mineral Scanner rotation in inventory
type: Tweak
id: 8131
time: '2025-04-04T15:56:30.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/36294
- author: Tayrtahn
changes:
- message: Fixed ninjas being unable to hack criminal records consoles.
type: Fix
id: 8132
time: '2025-04-04T17:46:59.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/36299
- author: ScarKy0
changes:
- message: Players now require 1h of playtime before selecting any antagonist role.
type: Tweak
id: 8133
time: '2025-04-04T20:24:05.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/36276
- author: aada
changes:
- message: SyndieJuice now comes with 10 units of liquid plasma.
type: Add
id: 8134
time: '2025-04-05T03:22:12.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/36280

View File

@@ -15,6 +15,9 @@ quick_lottery = true
[gateway]
generator_enabled = false
[movement]
mob_pushing = true
[physics]
# Makes mapping annoying
grid_splitting = false

View File

@@ -31,6 +31,9 @@ appeal = "https://appeal.ss14.io"
[server]
rules_file = "StandardRuleset"
[movement]
mob_pushing = true
[net]
max_connections = 1024

View File

@@ -1,6 +1,9 @@
agent-id-no-new = Didn't gain any new accesses from {THE($card)}.
agent-id-new-1 = Gained one new access from {THE($card)}.
agent-id-new = Gained {$number} new accesses from {THE($card)}.
agent-id-new = { $number ->
[0] Didn't gain any new accesses from {THE($card)}.
[one] Gained one new access from {THE($card)}.
*[other] Gained {$number} new accesses from {THE($card)}.
}
agent-id-card-current-name = Name:
agent-id-card-current-job = Job:
agent-id-card-job-icon-label = Job icon:

View File

@@ -1,3 +1,3 @@
admin-alert-shared-connection = {$player} is sharing a connection with {$otherCount} connected player(s): {$otherList}
admin-alert-ipintel-blocked = {$player} was rejected from joining due to their IP having a {TOSTRING($percent, "P0")} confidence of being a VPN/Datacenter.
admin-alert-ipintel-warning = {$player} IP has a {TOSTRING($percent, "P0")} confidence of being a VPN/Datacenter. Please watch them.
admin-alert-ipintel-blocked = {$player} was rejected from joining due to their IP having a {TOSTRING($percent, "P2")} confidence of being a VPN/Datacenter.
admin-alert-ipintel-warning = {$player} IP has a {TOSTRING($percent, "P2")} confidence of being a VPN/Datacenter. Please watch them.

View File

@@ -15,12 +15,21 @@ air-alarm-ui-window-resync-devices-label = Resync
air-alarm-ui-window-mode-label = Mode
air-alarm-ui-window-auto-mode-label = Auto mode
-air-alarm-state-name = { $state ->
[normal] Normal
[warning] Warning
[danger] Danger
[emagged] Emagged
*[invalid] Invalid
}
air-alarm-ui-window-listing-title = {$address} : {-air-alarm-state-name(state:$state)}
air-alarm-ui-window-pressure = {$pressure} kPa
air-alarm-ui-window-pressure-indicator = Pressure: [color={$color}]{$pressure} kPa[/color]
air-alarm-ui-window-temperature = {$tempC} C ({$temperature} K)
air-alarm-ui-window-temperature-indicator = Temperature: [color={$color}]{$tempC} C ({$temperature} K)[/color]
air-alarm-ui-window-alarm-state = [color={$color}]{$state}[/color]
air-alarm-ui-window-alarm-state-indicator = Status: [color={$color}]{$state}[/color]
air-alarm-ui-window-alarm-state = [color={$color}]{-air-alarm-state-name(state:$state)}[/color]
air-alarm-ui-window-alarm-state-indicator = Status: [color={$color}]{-air-alarm-state-name(state:$state)}[/color]
air-alarm-ui-window-tab-vents = Vents
air-alarm-ui-window-tab-scrubbers = Scrubbers

View File

@@ -1 +1 @@
bodyburn-text-others = {$name} burns to ash!
bodyburn-text-others = {CAPITALIZE(THE($name))} burns to ash!

View File

@@ -6,10 +6,10 @@ magic-mirror-remove-slot-self = You're removing some of your hair.
magic-mirror-change-slot-self = You're changing your hairstyle.
magic-mirror-change-color-self = You're changing your hair color.
magic-mirror-add-slot-target = Hair is being added to you by {$user}.
magic-mirror-remove-slot-target = Your hair is being cut off by {$user}.
magic-mirror-change-slot-target = Your hairstyle is being changed by {$user}.
magic-mirror-change-color-target = Your hair color is being changed by {$user}.
magic-mirror-add-slot-target = Hair is being added to you by {THE($user)}.
magic-mirror-remove-slot-target = Your hair is being cut off by {THE($user)}.
magic-mirror-change-slot-target = Your hairstyle is being changed by {THE($user)}.
magic-mirror-change-color-target = Your hair color is being changed by {THE($user)}.
magic-mirror-blocked-by-hat-self = You need to take off your hat before changing your hair.
magic-mirror-blocked-by-hat-self-target = You try to change their hair but their clothes gets in the way.
magic-mirror-blocked-by-hat-self-target = You try to change {POSS-ADJ($target)} hair but {POSS-ADJ($target)} clothes get in the way.

View File

@@ -3,7 +3,7 @@ suicide-command-help-text = The suicide command gives you a quick way out of a r
The method varies, first it will attempt to use the held item in your active hand.
If that fails, it will attempt to use an object in the environment.
Finally, if neither of the above worked, you will die by biting your tongue.
suicide-command-default-text-others = {$name} is attempting to bite their own tongue!
suicide-command-default-text-others = {CAPITALIZE(THE($name))} is attempting to bite {POSS-ADJ($name)} own tongue!
suicide-command-default-text-self = You attempt to bite your own tongue!
suicide-command-already-dead = You can't suicide. You're dead.
suicide-command-no-mind = You have no mind!

View File

@@ -10,14 +10,14 @@ injector-volume-label = Volume: [color=white]{$currentVolume}/{$totalVolume}[/co
injector-component-drawing-text = Now drawing
injector-component-injecting-text = Now injecting
injector-component-cannot-transfer-message = You aren't able to transfer to {$target}!
injector-component-cannot-draw-message = You aren't able to draw from {$target}!
injector-component-cannot-inject-message = You aren't able to inject to {$target}!
injector-component-inject-success-message = You inject {$amount}u into {$target}!
injector-component-transfer-success-message = You transfer {$amount}u into {$target}.
injector-component-draw-success-message = You draw {$amount}u from {$target}.
injector-component-target-already-full-message = {$target} is already full!
injector-component-target-is-empty-message = {$target} is empty!
injector-component-cannot-transfer-message = You aren't able to transfer to {THE($target)}!
injector-component-cannot-draw-message = You aren't able to draw from {THE($target)}!
injector-component-cannot-inject-message = You aren't able to inject to {THE($target)}!
injector-component-inject-success-message = You inject {$amount}u into {THE($target)}!
injector-component-transfer-success-message = You transfer {$amount}u into {THE($target)}.
injector-component-draw-success-message = You draw {$amount}u from {THE($target)}.
injector-component-target-already-full-message = {CAPITALIZE(THE($target))} is already full!
injector-component-target-is-empty-message = {CAPITALIZE(THE($target))} is empty!
injector-component-cannot-toggle-draw-message = Too full to draw!
injector-component-cannot-toggle-inject-message = Nothing to inject!

View File

@@ -5,7 +5,7 @@ reagent-name-dylovene = dylovene
reagent-desc-dylovene = A broad-spectrum anti-toxin, which treats toxin damage in organisms. Overdosing will cause vomiting, dizzyness and pain.
reagent-name-diphenhydramine = diphenhydramine
reagent-desc-diphenhydramine = Rapidly purges the body of histamine, reduces jitteriness, and treats poison damage.
reagent-desc-diphenhydramine = Rapidly purges the body of histamine, reduces jitteriness, causes drowsiness, and treats poison damage. Often included in sleep medication.
reagent-name-arithrazine = arithrazine
reagent-desc-arithrazine = A mildly unstable medication used for the most extreme case of radiation poisoning. Exerts minor stress on the body.

View File

@@ -53,6 +53,7 @@
JugSugar: 3
JugSulfur: 1
JugWeldingFuel: 1
PlasmaChemistryVial: 1
contrabandInventory:
DrinkLithiumFlask: 1
StrangePill: 3

View File

@@ -187,12 +187,12 @@
- HeadTop
- HeadSide
#Templar Helmet
#Knight Helmet
- type: entity
parent: ClothingHeadBase
id: ClothingHeadHelmetTemplar
name: templar helmet
description: DEUS VULT!
name: knight helmet
description: Decorative helmet fashioned to resemble the knights of old.
components:
- type: Sprite
sprite: Clothing/Head/Helmets/templar.rsi

View File

@@ -236,6 +236,7 @@
- DoorBumpOpener
- FootstepSound
- CanPilot
- Unimplantable
- type: Emoting
- type: GuideHelp
guides:

View File

@@ -66,6 +66,7 @@
tags:
- DoorBumpOpener
- Bot
- Unimplantable
- type: MobState
allowedStates:
- Alive
@@ -451,6 +452,7 @@
- DoorBumpOpener
- FootstepSound
- Bot
- Unimplantable
- type: ActiveRadio
channels:
- Common

View File

@@ -14,6 +14,7 @@
- BypassDropChecks
- NoConsoleSound
- SilentStorageUser
- PreventAccessLogging
- type: Input
context: "aghost"
- type: Ghost

View File

@@ -8,6 +8,7 @@
- type: Sprite
noRot: true
drawdepth: Mobs
- type: MobCollision
- type: Physics
bodyType: KinematicController
- type: Fixtures

View File

@@ -14,9 +14,6 @@
- Plating
- type: Stack
stackType: FloorCarpetRed
- type: Tag
tags:
- CarpetRed
- type: SpawnAfterInteract #Nuke after convert to FloorTile
prototype: Carpet
doAfter: 0.5
@@ -33,9 +30,6 @@
heldPrefix: carpet-black
- type: Stack
stackType: FloorCarpetBlack
- type: Tag
tags:
- CarpetBlack
- type: SpawnAfterInteract #Nuke after convert to FloorTile
prototype: CarpetBlack
doAfter: 0.5
@@ -52,9 +46,6 @@
heldPrefix: carpet-blue
- type: Stack
stackType: FloorCarpetBlue
- type: Tag
tags:
- CarpetBlue
- type: SpawnAfterInteract #Nuke after convert to FloorTile
prototype: CarpetBlue
doAfter: 0.5
@@ -71,9 +62,6 @@
heldPrefix: carpet-green
- type: Stack
stackType: FloorCarpetGreen
- type: Tag
tags:
- CarpetGreen
- type: SpawnAfterInteract #Nuke after convert to FloorTile
prototype: CarpetGreen
doAfter: 0.5
@@ -90,9 +78,6 @@
heldPrefix: carpet-orange
- type: Stack
stackType: FloorCarpetOrange
- type: Tag
tags:
- CarpetOrange
- type: SpawnAfterInteract #Nuke after convert to FloorTile
prototype: CarpetOrange
doAfter: 0.5
@@ -109,9 +94,6 @@
heldPrefix: carpet-skyblue
- type: Stack
stackType: FloorCarpetSkyBlue
- type: Tag
tags:
- CarpetSBlue
- type: SpawnAfterInteract #Nuke after convert to FloorTile
prototype: CarpetSBlue
doAfter: 0.5
@@ -128,9 +110,6 @@
heldPrefix: carpet-purple
- type: Stack
stackType: FloorCarpetPurple
- type: Tag
tags:
- CarpetPurple
- type: SpawnAfterInteract #Nuke after convert to FloorTile
prototype: CarpetPurple
doAfter: 0.5
@@ -147,9 +126,6 @@
heldPrefix: carpet-pink
- type: Stack
stackType: FloorCarpetPink
- type: Tag
tags:
- CarpetPink
- type: SpawnAfterInteract #Nuke after convert to FloorTile
prototype: CarpetPink
doAfter: 0.5
@@ -166,9 +142,6 @@
heldPrefix: carpet-cyan
- type: Stack
stackType: FloorCarpetCyan
- type: Tag
tags:
- CarpetCyan
- type: SpawnAfterInteract #Nuke after convert to FloorTile
prototype: CarpetCyan
doAfter: 0.5
@@ -185,9 +158,6 @@
heldPrefix: carpet-white
- type: Stack
stackType: FloorCarpetWhite
- type: Tag
tags:
- CarpetWhite
- type: SpawnAfterInteract #Nuke after convert to FloorTile
prototype: CarpetWhite
doAfter: 0.5

View File

@@ -67,6 +67,8 @@
shader: unshaded
visible: false
map: ["enum.ToggleVisuals.Layer"]
- type: Item
storedRotation: -90
- type: MiningScanner
range: 10

View File

@@ -2,7 +2,7 @@
parent: BaseItem
name: light replacer
id: LightReplacer
description: An item which uses magnets to easily replace broken lights. Refill By adding more lights into the replacer.
description: An item which uses magnets to easily replace broken lights. Refill by adding more lights into the replacer.
components:
- type: Sprite
sprite: Objects/Specific/Janitorial/light_replacer.rsi

View File

@@ -240,4 +240,4 @@
layer:
- HighImpassable
- type: Transform
noRot: true
noRot: false

View File

@@ -394,6 +394,8 @@
- SecurityAmmoStatic
- SecurityWeaponsStatic
dynamicPacks:
- SalvageSecurityBoards
- SalvageSecurityWeapons
- SecurityEquipment
- SecurityBoards
- SecurityExplosives

View File

@@ -2,25 +2,25 @@
- type: rcd
id: Invalid # Hidden prototype - do not add to RCDs
mode: Invalid
- type: rcd
id: Deconstruct
name: rcd-component-deconstruct
category: Main
category: WallsAndFlooring
sprite: /Textures/Interface/Radial/RCD/deconstruct.png
mode: Deconstruct
prototype: EffectRCDDeconstructPreview
rotation: Camera
- type: rcd
id: DeconstructLattice # Hidden prototype - do not add to RCDs
id: DeconstructLattice # Hidden prototype - do not add to RCDs
name: rcd-component-deconstruct
mode: Deconstruct
cost: 2
delay: 0
rotation: Camera
fx: EffectRCDConstruct0
- type: rcd
id: DeconstructTile # Hidden prototype - do not add to RCDs
name: rcd-component-deconstruct
@@ -30,7 +30,7 @@
rotation: Camera
fx: EffectRCDDeconstruct4
# Flooring
# Flooring
- type: rcd
id: Plating
name: rcd-component-plating
@@ -44,7 +44,7 @@
rules:
- CanBuildOnEmptyTile
fx: EffectRCDConstruct1
- type: rcd
id: FloorSteel
name: rcd-component-floor-steel
@@ -80,7 +80,7 @@
category: WallsAndFlooring
sprite: /Textures/Interface/Radial/RCD/solid_wall.png
mode: ConstructObject
prototype: WallSolid
prototype: WallSolid
cost: 4
delay: 2
collisionMask: FullTileMask
@@ -113,7 +113,7 @@
- IsWindow
rotation: Fixed
fx: EffectRCDConstruct2
- type: rcd
id: WindowDirectional
category: WindowsAndGrilles
@@ -128,7 +128,7 @@
- IsWindow
rotation: User
fx: EffectRCDConstruct1
- type: rcd
id: ReinforcedWindow
category: WindowsAndGrilles
@@ -142,7 +142,7 @@
- IsWindow
rotation: User
fx: EffectRCDConstruct3
- type: rcd
id: WindowReinforcedDirectional
category: WindowsAndGrilles
@@ -170,7 +170,7 @@
collisionMask: FullTileMask
rotation: Camera
fx: EffectRCDConstruct4
- type: rcd
id: AirlockGlass
category: Airlocks
@@ -182,7 +182,7 @@
collisionMask: FullTileMask
rotation: Camera
fx: EffectRCDConstruct4
- type: rcd
id: Firelock
category: Airlocks
@@ -208,7 +208,7 @@
collisionBounds: "-0.23,-0.49,0.23,-0.36"
rotation: User
fx: EffectRCDConstruct1
- type: rcd
id: BulbLight
category: Lighting
@@ -235,7 +235,7 @@
- MustBuildOnSubfloor
rotation: Fixed
fx: EffectRCDConstruct0
- type: rcd
id: MVCable
category: Electrical
@@ -248,7 +248,7 @@
- MustBuildOnSubfloor
rotation: Fixed
fx: EffectRCDConstruct0
- type: rcd
id: HVCable
category: Electrical
@@ -261,7 +261,7 @@
- MustBuildOnSubfloor
rotation: Fixed
fx: EffectRCDConstruct0
- type: rcd
id: CableTerminal
category: Electrical

View File

@@ -83,6 +83,12 @@
key: Jitter
time: 3.0
type: Remove
- !type:GenericStatusEffect
key: Drowsiness
component: Drowsiness
time: 1.5
type: Add
refresh: false
- !type:HealthChange
damage:
types:

View File

@@ -17,93 +17,66 @@
completed:
- !type:SnapToGrid { }
steps:
- tag: CarpetBlack
- material: FloorCarpetBlack
amount: 1
doAfter: 1
name: black carpet
icon:
sprite: Objects/Tiles/tile.rsi
state: carpet-black
- to: CurtainsBlue
completed:
- !type:SnapToGrid { }
steps:
- tag: CarpetBlue
- material: FloorCarpetBlue
amount: 1
doAfter: 1
name: blue carpet
icon:
sprite: Objects/Tiles/tile.rsi
state: carpet-blue
- to: CurtainsCyan
completed:
- !type:SnapToGrid { }
steps:
- tag: CarpetCyan
- material: FloorCarpetCyan
amount: 1
doAfter: 1
name: cyan carpet
icon:
sprite: Objects/Tiles/tile.rsi
state: carpet-cyan
- to: CurtainsGreen
completed:
- !type:SnapToGrid { }
steps:
- tag: CarpetGreen
- material: FloorCarpetGreen
amount: 1
doAfter: 1
name: green carpet
icon:
sprite: Objects/Tiles/tile.rsi
state: carpet-green
- to: CurtainsOrange
completed:
- !type:SnapToGrid { }
steps:
- tag: CarpetOrange
- material: FloorCarpetOrange
amount: 1
doAfter: 1
name: orange carpet
icon:
sprite: Objects/Tiles/tile.rsi
state: carpet-orange
- to: CurtainsPink
completed:
- !type:SnapToGrid { }
steps:
- tag: CarpetPink
- material: FloorCarpetPink
amount: 1
doAfter: 1
name: pink carpet
icon:
sprite: Objects/Tiles/tile.rsi
state: carpet-pink
- to: CurtainsPurple
completed:
- !type:SnapToGrid { }
steps:
- tag: CarpetPurple
- material: FloorCarpetPurple
amount: 1
doAfter: 1
name: purple carpet
icon:
sprite: Objects/Tiles/tile.rsi
state: carpet-purple
- to: CurtainsRed
completed:
- !type:SnapToGrid { }
steps:
- tag: CarpetRed
- material: FloorCarpetRed
amount: 1
doAfter: 1
name: red carpet
icon:
sprite: Objects/Tiles/tile.rsi
state: carpet-red
- to: CurtainsWhite
completed:
- !type:SnapToGrid { }
steps:
- tag: CarpetWhite
- material: FloorCarpetWhite
amount: 1
doAfter: 1
name: white carpet
icon:
sprite: Objects/Tiles/tile.rsi
state: carpet-white
- node: Curtains
entity: HospitalCurtains
edges:
@@ -126,7 +99,7 @@
steps:
- tool: Cutting
doAfter: 1
- node: CurtainsBlack
entity: CurtainsBlack
edges:
@@ -148,8 +121,8 @@
amount: 1
steps:
- tool: Cutting
doAfter: 1
doAfter: 1
- node: CurtainsBlue
entity: CurtainsBlue
edges:
@@ -172,7 +145,7 @@
steps:
- tool: Cutting
doAfter: 1
- node: CurtainsCyan
entity: CurtainsCyan
edges:
@@ -195,7 +168,7 @@
steps:
- tool: Cutting
doAfter: 1
- node: CurtainsGreen
entity: CurtainsGreen
edges:
@@ -218,7 +191,7 @@
steps:
- tool: Cutting
doAfter: 1
- node: CurtainsOrange
entity: CurtainsOrange
edges:
@@ -241,7 +214,7 @@
steps:
- tool: Cutting
doAfter: 1
- node: CurtainsPink
entity: CurtainsPink
edges:
@@ -264,7 +237,7 @@
steps:
- tool: Cutting
doAfter: 1
- node: CurtainsPurple
entity: CurtainsPurple
edges:
@@ -287,7 +260,7 @@
steps:
- tool: Cutting
doAfter: 1
- node: CurtainsRed
entity: CurtainsRed
edges:
@@ -310,7 +283,7 @@
steps:
- tool: Cutting
doAfter: 1
- node: CurtainsWhite
entity: CurtainsWhite
edges:

View File

@@ -218,75 +218,48 @@
- to: TableFancyBlack
steps:
- tag: CarpetBlack
name: black carpet
icon:
sprite: Objects/Tiles/tile.rsi
state: carpet-black
- material: FloorCarpetBlack
amount: 1
- to: TableFancyBlue
steps:
- tag: CarpetBlue
name: blue carpet
icon:
sprite: Objects/Tiles/tile.rsi
state: carpet-blue
- material: FloorCarpetBlue
amount: 1
- to: TableFancyCyan
steps:
- tag: CarpetCyan
name: cyan carpet
icon:
sprite: Objects/Tiles/tile.rsi
state: carpet-cyan
- material: FloorCarpetCyan
amount: 1
- to: TableFancyGreen
steps:
- tag: CarpetGreen
name: green carpet
icon:
sprite: Objects/Tiles/tile.rsi
state: carpet-green
- material: FloorCarpetGreen
amount: 1
- to: TableFancyOrange
steps:
- tag: CarpetOrange
name: orange carpet
icon:
sprite: Objects/Tiles/tile.rsi
state: carpet-orange
- material: FloorCarpetOrange
amount: 1
- to: TableFancyPurple
steps:
- tag: CarpetPurple
name: purple carpet
icon:
sprite: Objects/Tiles/tile.rsi
state: carpet-purple
- material: FloorCarpetPurple
amount: 1
- to: TableFancyPink
steps:
- tag: CarpetPink
name: pink carpet
icon:
sprite: Objects/Tiles/tile.rsi
state: carpet-pink
- material: FloorCarpetPink
amount: 1
- to: TableFancyRed
steps:
- tag: CarpetRed
name: red carpet
icon:
sprite: Objects/Tiles/tile.rsi
state: carpet-red
- material: FloorCarpetRed
amount: 1
- to: TableFancyWhite
steps:
- tag: CarpetWhite
name: white carpet
icon:
sprite: Objects/Tiles/tile.rsi
state: carpet-white
- material: FloorCarpetWhite
amount: 1
- node: TableCarpet
entity: TableCarpet

View File

@@ -49,3 +49,13 @@
- HandHeldMassScanner
- ClothingMaskWeldingGas
- SignallerAdvanced
- type: latheRecipePack
id: SalvageSecurityBoards
recipes:
- ShuttleGunKineticCircuitboard
- type: latheRecipePack
id: SalvageSecurityWeapons
recipes:
- WeaponProtoKineticAccelerator

View File

@@ -5,6 +5,9 @@
setPreference: true
objective: roles-antag-rev-head-objective
guides: [ Revolutionaries ]
requirements:
- !type:OverallPlaytimeRequirement
time: 3600 # 1h
- type: antag
id: Rev

View File

@@ -5,6 +5,9 @@
setPreference: true
objective: roles-antag-thief-objective
guides: [ Thieves ]
requirements:
- !type:OverallPlaytimeRequirement
time: 3600 # 1h
- type: startingGear
id: ThiefGear

View File

@@ -5,6 +5,9 @@
setPreference: true
objective: roles-antag-syndicate-agent-objective
guides: [ Traitors ]
requirements:
- !type:OverallPlaytimeRequirement
time: 3600 # 1h
- type: antag
id: TraitorSleeper
@@ -13,6 +16,9 @@
setPreference: true
objective: roles-antag-syndicate-agent-sleeper-objective
guides: [ Traitors ]
requirements:
- !type:OverallPlaytimeRequirement
time: 3600 # 1h
# Syndicate Operative Outfit - Monkey
- type: startingGear

View File

@@ -5,6 +5,9 @@
setPreference: true
objective: roles-antag-initial-infected-objective
guides: [ Zombies ]
requirements:
- !type:OverallPlaytimeRequirement
time: 3600 # 1h
- type: antag
id: Zombie

View File

@@ -222,36 +222,6 @@
- type: Tag
id: Carpet
- type: Tag
id: CarpetBlack
- type: Tag
id: CarpetBlue
- type: Tag
id: CarpetCyan
- type: Tag
id: CarpetGreen
- type: Tag
id: CarpetOrange
- type: Tag
id: CarpetPink
- type: Tag
id: CarpetPurple
- type: Tag
id: CarpetRed
- type: Tag
id: CarpetSBlue
- type: Tag
id: CarpetWhite
- type: Tag
id: Carrot
@@ -1056,6 +1026,9 @@
- type: Tag
id: Powerdrill
- type: Tag
id: PreventAccessLogging
- type: Tag
id: PrisonUniform
@@ -1317,10 +1290,10 @@
- type: Tag
id: Truncheon
- type: Tag
id: TurretCompatibleWeapon # Used in the construction of sentry turrets
- type: Tag
id: TurretControlElectronics # Used in the construction of sentry turret control panels