Merge remote-tracking branch 'space-wizards/master'
# Conflicts: # Content.Client/Weapons/Ranged/Systems/GunSystem.cs # Content.IntegrationTests/Tests/PostMapInitTest.cs # Content.Server/Entry/EntryPoint.cs # Content.Server/Ghost/GhostSystem.cs # Content.Server/IoC/ServerContentIoC.cs # Content.Server/Shuttles/Systems/ArrivalsSystem.cs # Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs # Content.Server/Storage/EntitySystems/SpawnItemsOnUseSystem.cs # Content.Server/Voting/Managers/VoteManager.DefaultVotes.cs # Content.Server/Weapons/Ranged/Systems/GunSystem.Battery.cs # Content.Server/Weapons/Ranged/Systems/GunSystem.Cartridges.cs # Content.Server/Weapons/Ranged/Systems/GunSystem.cs # Content.Shared/Damage/Systems/DamageableSystem.cs # Content.Shared/Follower/FollowerSystem.cs # Resources/Locale/en-US/_strings/actions/actions/dna-scrambler.ftl # Resources/Locale/en-US/_strings/administration/commands/change-cvar-command.ftl # Resources/Locale/en-US/discord/vote-notifications.ftl # Resources/Prototypes/Catalog/Fills/Crates/armory.yml # Resources/Prototypes/Catalog/Fills/Lockers/security.yml # Resources/Prototypes/Entities/Clothing/OuterClothing/misc.yml # Resources/Prototypes/Entities/Objects/Specific/Medical/handheld_crew_monitor.yml # Resources/Prototypes/Entities/Objects/Weapons/Guns/Rifles/rifles.yml # Resources/Prototypes/Entities/Objects/Weapons/Guns/SMGs/smgs.yml # Resources/Prototypes/Entities/Objects/Weapons/Guns/Shotguns/shotguns.yml # Resources/Prototypes/Voice/speech_emotes.yml
This commit is contained in:
commit
ed0a30bb64
273 changed files with 59076 additions and 37668 deletions
|
|
@ -9,13 +9,14 @@ using Content.IntegrationTests.Pair;
|
|||
using Content.Shared.Clothing.Components;
|
||||
using Content.Shared.Doors.Components;
|
||||
using Content.Shared.Item;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Analyzers;
|
||||
using Robust.Shared.EntitySerialization;
|
||||
using Robust.Shared.EntitySerialization.Systems;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Benchmarks;
|
||||
|
||||
|
|
@ -32,7 +33,6 @@ public class ComponentQueryBenchmark
|
|||
|
||||
private TestPair _pair = default!;
|
||||
private IEntityManager _entMan = default!;
|
||||
private MapId _mapId = new(10);
|
||||
private EntityQuery<ItemComponent> _itemQuery;
|
||||
private EntityQuery<ClothingComponent> _clothingQuery;
|
||||
private EntityQuery<MapComponent> _mapQuery;
|
||||
|
|
@ -54,10 +54,10 @@ public class ComponentQueryBenchmark
|
|||
_pair.Server.ResolveDependency<IRobustRandom>().SetSeed(42);
|
||||
_pair.Server.WaitPost(() =>
|
||||
{
|
||||
var success = _entMan.System<MapLoaderSystem>().TryLoad(_mapId, Map, out _);
|
||||
if (!success)
|
||||
var map = new ResPath(Map);
|
||||
var opts = DeserializationOptions.Default with {InitializeMaps = true};
|
||||
if (!_entMan.System<MapLoaderSystem>().TryLoadMap(map, out _, out _, opts))
|
||||
throw new Exception("Map load failed");
|
||||
_pair.Server.MapMan.DoMapInitialize(_mapId);
|
||||
}).GetAwaiter().GetResult();
|
||||
|
||||
_items = new EntityUid[_entMan.Count<ItemComponent>()];
|
||||
|
|
|
|||
|
|
@ -6,12 +6,13 @@ using BenchmarkDotNet.Attributes;
|
|||
using Content.IntegrationTests;
|
||||
using Content.IntegrationTests.Pair;
|
||||
using Content.Server.Maps;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Analyzers;
|
||||
using Robust.Shared.EntitySerialization.Systems;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Benchmarks;
|
||||
|
||||
|
|
@ -20,7 +21,7 @@ public class MapLoadBenchmark
|
|||
{
|
||||
private TestPair _pair = default!;
|
||||
private MapLoaderSystem _mapLoader = default!;
|
||||
private IMapManager _mapManager = default!;
|
||||
private SharedMapSystem _mapSys = default!;
|
||||
|
||||
[GlobalSetup]
|
||||
public void Setup()
|
||||
|
|
@ -36,7 +37,7 @@ public class MapLoadBenchmark
|
|||
.ToDictionary(x => x.ID, x => x.MapPath.ToString());
|
||||
|
||||
_mapLoader = server.ResolveDependency<IEntitySystemManager>().GetEntitySystem<MapLoaderSystem>();
|
||||
_mapManager = server.ResolveDependency<IMapManager>();
|
||||
_mapSys = server.ResolveDependency<IEntitySystemManager>().GetEntitySystem<SharedMapSystem>();
|
||||
}
|
||||
|
||||
[GlobalCleanup]
|
||||
|
|
@ -52,17 +53,19 @@ public class MapLoadBenchmark
|
|||
public string Map;
|
||||
|
||||
public Dictionary<string, string> Paths;
|
||||
private MapId _mapId;
|
||||
|
||||
[Benchmark]
|
||||
public async Task LoadMap()
|
||||
{
|
||||
var mapPath = Paths[Map];
|
||||
var mapPath = new ResPath(Paths[Map]);
|
||||
var server = _pair.Server;
|
||||
await server.WaitPost(() =>
|
||||
{
|
||||
var success = _mapLoader.TryLoad(new MapId(10), mapPath, out _);
|
||||
var success = _mapLoader.TryLoadMap(mapPath, out var map, out _);
|
||||
if (!success)
|
||||
throw new Exception("Map load failed");
|
||||
_mapId = map.Value.Comp.MapId;
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -70,9 +73,7 @@ public class MapLoadBenchmark
|
|||
public void IterationCleanup()
|
||||
{
|
||||
var server = _pair.Server;
|
||||
server.WaitPost(() =>
|
||||
{
|
||||
_mapManager.DeleteMap(new MapId(10));
|
||||
}).Wait();
|
||||
server.WaitPost(() => _mapSys.DeleteMap(_mapId))
|
||||
.Wait();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,13 +7,15 @@ using Content.IntegrationTests;
|
|||
using Content.IntegrationTests.Pair;
|
||||
using Content.Server.Mind;
|
||||
using Content.Server.Warps;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Analyzers;
|
||||
using Robust.Shared.EntitySerialization;
|
||||
using Robust.Shared.EntitySerialization.Systems;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Benchmarks;
|
||||
|
||||
|
|
@ -34,7 +36,6 @@ public class PvsBenchmark
|
|||
|
||||
private TestPair _pair = default!;
|
||||
private IEntityManager _entMan = default!;
|
||||
private MapId _mapId = new(10);
|
||||
private ICommonSession[] _players = default!;
|
||||
private EntityCoordinates[] _spawns = default!;
|
||||
public int _cycleOffset = 0;
|
||||
|
|
@ -65,10 +66,10 @@ public class PvsBenchmark
|
|||
_pair.Server.ResolveDependency<IRobustRandom>().SetSeed(42);
|
||||
await _pair.Server.WaitPost(() =>
|
||||
{
|
||||
var success = _entMan.System<MapLoaderSystem>().TryLoad(_mapId, Map, out _);
|
||||
if (!success)
|
||||
var path = new ResPath(Map);
|
||||
var opts = DeserializationOptions.Default with {InitializeMaps = true};
|
||||
if (!_entMan.System<MapLoaderSystem>().TryLoadMap(path, out _, out _, opts))
|
||||
throw new Exception("Map load failed");
|
||||
_pair.Server.MapMan.DoMapInitialize(_mapId);
|
||||
});
|
||||
|
||||
// Get list of ghost warp positions
|
||||
|
|
|
|||
|
|
@ -141,6 +141,11 @@ public sealed class ChemistryGuideDataSystem : SharedChemistryGuideDataSystem
|
|||
{
|
||||
return _reagentSources.GetValueOrDefault(id) ?? new List<ReagentSourceData>();
|
||||
}
|
||||
|
||||
// Is handled on server and updated on client via ReagentGuideRegistryChangedEvent
|
||||
public override void ReloadAllReagentPrototypes()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ namespace Content.Client.Construction
|
|||
[RegisterComponent]
|
||||
public sealed partial class ConstructionGhostComponent : Component
|
||||
{
|
||||
public int GhostId { get; set; }
|
||||
[ViewVariables] public ConstructionPrototype? Prototype { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,6 +55,12 @@ namespace Content.Client.Construction
|
|||
.Register<ConstructionSystem>();
|
||||
|
||||
SubscribeLocalEvent<ConstructionGhostComponent, ExaminedEvent>(HandleConstructionGhostExamined);
|
||||
SubscribeLocalEvent<ConstructionGhostComponent, ComponentShutdown>(HandleGhostComponentShutdown);
|
||||
}
|
||||
|
||||
private void HandleGhostComponentShutdown(EntityUid uid, ConstructionGhostComponent component, ComponentShutdown args)
|
||||
{
|
||||
ClearGhost(component.GhostId);
|
||||
}
|
||||
|
||||
private void OnConstructionGuideReceived(ResponseConstructionGuide ev)
|
||||
|
|
@ -205,8 +211,9 @@ namespace Content.Client.Construction
|
|||
ghost = EntityManager.SpawnEntity("constructionghost", loc);
|
||||
var comp = EntityManager.GetComponent<ConstructionGhostComponent>(ghost.Value);
|
||||
comp.Prototype = prototype;
|
||||
comp.GhostId = ghost.GetHashCode();
|
||||
EntityManager.GetComponent<TransformComponent>(ghost.Value).LocalRotation = dir.ToAngle();
|
||||
_ghosts.Add(ghost.GetHashCode(), ghost.Value);
|
||||
_ghosts.Add(comp.GhostId, ghost.Value);
|
||||
var sprite = EntityManager.GetComponent<SpriteComponent>(ghost.Value);
|
||||
sprite.Color = new Color(48, 255, 48, 128);
|
||||
|
||||
|
|
|
|||
|
|
@ -58,9 +58,7 @@
|
|||
StyleClasses="LabelBig" />
|
||||
<BoxContainer Orientation="Horizontal"
|
||||
Margin="0 0 0 5">
|
||||
<Label Text="{Loc 'crew-monitoring-user-interface-job'}:"
|
||||
FontColorOverride="DarkGray" />
|
||||
<Label Text=":"
|
||||
<Label Text="{Loc 'crew-monitoring-user-interface-job'}"
|
||||
FontColorOverride="DarkGray" />
|
||||
<TextureRect Name="PersonJobIcon"
|
||||
TextureScale="2 2"
|
||||
|
|
|
|||
59
Content.Client/Light/AfterLightTargetOverlay.cs
Normal file
59
Content.Client/Light/AfterLightTargetOverlay.cs
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
using System.Numerics;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Enums;
|
||||
|
||||
namespace Content.Client.Light;
|
||||
|
||||
/// <summary>
|
||||
/// This exists just to copy <see cref="BeforeLightTargetOverlay"/> to the light render target
|
||||
/// </summary>
|
||||
public sealed class AfterLightTargetOverlay : Overlay
|
||||
{
|
||||
public override OverlaySpace Space => OverlaySpace.BeforeLighting;
|
||||
|
||||
[Dependency] private readonly IOverlayManager _overlay = default!;
|
||||
|
||||
public const int ContentZIndex = LightBlurOverlay.ContentZIndex + 1;
|
||||
|
||||
public AfterLightTargetOverlay()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
ZIndex = ContentZIndex;
|
||||
}
|
||||
|
||||
protected override void Draw(in OverlayDrawArgs args)
|
||||
{
|
||||
var viewport = args.Viewport;
|
||||
var worldHandle = args.WorldHandle;
|
||||
|
||||
if (viewport.Eye == null)
|
||||
return;
|
||||
|
||||
var lightOverlay = _overlay.GetOverlay<BeforeLightTargetOverlay>();
|
||||
var bounds = args.WorldBounds;
|
||||
|
||||
// at 1-1 render scale it's mostly fine but at 4x4 it's way too fkn big
|
||||
var lightScale = viewport.LightRenderTarget.Size / (Vector2) viewport.Size;
|
||||
var newScale = viewport.RenderScale / (Vector2.One / lightScale);
|
||||
|
||||
var localMatrix =
|
||||
viewport.LightRenderTarget.GetWorldToLocalMatrix(viewport.Eye, newScale);
|
||||
var diff = (lightOverlay.EnlargedLightTarget.Size - viewport.LightRenderTarget.Size);
|
||||
var halfDiff = diff / 2;
|
||||
|
||||
// Pixels -> Metres -> Half distance.
|
||||
// If we're zoomed in need to enlarge the bounds further.
|
||||
args.WorldHandle.RenderInRenderTarget(viewport.LightRenderTarget,
|
||||
() =>
|
||||
{
|
||||
// We essentially need to draw the cropped version onto the lightrendertarget.
|
||||
var subRegion = new UIBox2i(halfDiff.X,
|
||||
halfDiff.Y,
|
||||
viewport.LightRenderTarget.Size.X + halfDiff.X,
|
||||
viewport.LightRenderTarget.Size.Y + halfDiff.Y);
|
||||
|
||||
worldHandle.SetTransform(localMatrix);
|
||||
worldHandle.DrawTextureRectRegion(lightOverlay.EnlargedLightTarget.Texture, bounds, subRegion: subRegion);
|
||||
}, null);
|
||||
}
|
||||
}
|
||||
51
Content.Client/Light/BeforeLightTargetOverlay.cs
Normal file
51
Content.Client/Light/BeforeLightTargetOverlay.cs
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
using System.Numerics;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Enums;
|
||||
|
||||
namespace Content.Client.Light;
|
||||
|
||||
/// <summary>
|
||||
/// Handles an enlarged lighting target so content can use large blur radii.
|
||||
/// </summary>
|
||||
public sealed class BeforeLightTargetOverlay : Overlay
|
||||
{
|
||||
public override OverlaySpace Space => OverlaySpace.BeforeLighting;
|
||||
|
||||
[Dependency] private readonly IClyde _clyde = default!;
|
||||
|
||||
public IRenderTexture EnlargedLightTarget = default!;
|
||||
public Box2Rotated EnlargedBounds;
|
||||
|
||||
/// <summary>
|
||||
/// In metres
|
||||
/// </summary>
|
||||
private float _skirting = 2f;
|
||||
|
||||
public const int ContentZIndex = -10;
|
||||
|
||||
public BeforeLightTargetOverlay()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
ZIndex = ContentZIndex;
|
||||
}
|
||||
|
||||
protected override void Draw(in OverlayDrawArgs args)
|
||||
{
|
||||
// Code is weird but I don't think engine should be enlarging the lighting render target arbitrarily either, maybe via cvar?
|
||||
// The problem is the blur has no knowledge of pixels outside the viewport so with a large enough blur radius you get sampling issues.
|
||||
var size = args.Viewport.LightRenderTarget.Size + (int) (_skirting * EyeManager.PixelsPerMeter);
|
||||
EnlargedBounds = args.WorldBounds.Enlarged(_skirting / 2f);
|
||||
|
||||
// This just exists to copy the lightrendertarget and write back to it.
|
||||
if (EnlargedLightTarget?.Size != size)
|
||||
{
|
||||
EnlargedLightTarget = _clyde
|
||||
.CreateRenderTarget(size, new RenderTargetFormatParameters(RenderTargetColorFormat.Rgba8Srgb), name: "enlarged-light-copy");
|
||||
}
|
||||
|
||||
args.WorldHandle.RenderInRenderTarget(EnlargedLightTarget,
|
||||
() =>
|
||||
{
|
||||
}, _clyde.GetClearColor(args.MapUid));
|
||||
}
|
||||
}
|
||||
36
Content.Client/Light/EntitySystems/PlanetLightSystem.cs
Normal file
36
Content.Client/Light/EntitySystems/PlanetLightSystem.cs
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
using Robust.Client.Graphics;
|
||||
|
||||
namespace Content.Client.Light.EntitySystems;
|
||||
|
||||
public sealed class PlanetLightSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IOverlayManager _overlayMan = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<GetClearColorEvent>(OnClearColor);
|
||||
|
||||
_overlayMan.AddOverlay(new BeforeLightTargetOverlay());
|
||||
_overlayMan.AddOverlay(new RoofOverlay(EntityManager));
|
||||
_overlayMan.AddOverlay(new TileEmissionOverlay(EntityManager));
|
||||
_overlayMan.AddOverlay(new LightBlurOverlay());
|
||||
_overlayMan.AddOverlay(new AfterLightTargetOverlay());
|
||||
}
|
||||
|
||||
private void OnClearColor(ref GetClearColorEvent ev)
|
||||
{
|
||||
ev.Color = Color.Transparent;
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
{
|
||||
base.Shutdown();
|
||||
_overlayMan.RemoveOverlay<BeforeLightTargetOverlay>();
|
||||
_overlayMan.RemoveOverlay<RoofOverlay>();
|
||||
_overlayMan.RemoveOverlay<TileEmissionOverlay>();
|
||||
_overlayMan.RemoveOverlay<LightBlurOverlay>();
|
||||
_overlayMan.RemoveOverlay<AfterLightTargetOverlay>();
|
||||
}
|
||||
}
|
||||
9
Content.Client/Light/EntitySystems/RoofSystem.cs
Normal file
9
Content.Client/Light/EntitySystems/RoofSystem.cs
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
using Content.Shared.Light.EntitySystems;
|
||||
|
||||
namespace Content.Client.Light.EntitySystems;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public sealed class RoofSystem : SharedRoofSystem
|
||||
{
|
||||
|
||||
}
|
||||
44
Content.Client/Light/LightBlurOverlay.cs
Normal file
44
Content.Client/Light/LightBlurOverlay.cs
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Enums;
|
||||
|
||||
namespace Content.Client.Light;
|
||||
|
||||
/// <summary>
|
||||
/// Essentially handles blurring for content-side light overlays.
|
||||
/// </summary>
|
||||
public sealed class LightBlurOverlay : Overlay
|
||||
{
|
||||
public override OverlaySpace Space => OverlaySpace.BeforeLighting;
|
||||
|
||||
[Dependency] private readonly IClyde _clyde = default!;
|
||||
[Dependency] private readonly IOverlayManager _overlay = default!;
|
||||
|
||||
public const int ContentZIndex = TileEmissionOverlay.ContentZIndex + 1;
|
||||
|
||||
private IRenderTarget? _blurTarget;
|
||||
|
||||
public LightBlurOverlay()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
ZIndex = ContentZIndex;
|
||||
}
|
||||
|
||||
protected override void Draw(in OverlayDrawArgs args)
|
||||
{
|
||||
if (args.Viewport.Eye == null)
|
||||
return;
|
||||
|
||||
var beforeOverlay = _overlay.GetOverlay<BeforeLightTargetOverlay>();
|
||||
var size = beforeOverlay.EnlargedLightTarget.Size;
|
||||
|
||||
if (_blurTarget?.Size != size)
|
||||
{
|
||||
_blurTarget = _clyde
|
||||
.CreateRenderTarget(size, new RenderTargetFormatParameters(RenderTargetColorFormat.Rgba8Srgb), name: "enlarged-light-blur");
|
||||
}
|
||||
|
||||
var target = beforeOverlay.EnlargedLightTarget;
|
||||
// Yeah that's all this does keep walkin.
|
||||
_clyde.BlurRenderTarget(args.Viewport, target, _blurTarget, args.Viewport.Eye, 14f * 5f);
|
||||
}
|
||||
}
|
||||
33
Content.Client/Light/LightCycleSystem.cs
Normal file
33
Content.Client/Light/LightCycleSystem.cs
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
using Content.Client.GameTicking.Managers;
|
||||
using Content.Shared;
|
||||
using Content.Shared.Light.Components;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Client.Light;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public sealed class LightCycleSystem : SharedLightCycleSystem
|
||||
{
|
||||
[Dependency] private readonly ClientGameTicker _ticker = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
var mapQuery = AllEntityQuery<LightCycleComponent, MapLightComponent>();
|
||||
while (mapQuery.MoveNext(out var uid, out var cycle, out var map))
|
||||
{
|
||||
if (!cycle.Running)
|
||||
continue;
|
||||
|
||||
var time = (float) _timing.CurTime
|
||||
.Add(cycle.Offset)
|
||||
.Subtract(_ticker.RoundStartTimeSpan)
|
||||
.TotalSeconds;
|
||||
|
||||
var color = GetColor((uid, cycle), cycle.OriginalColor, time);
|
||||
map.AmbientLightColor = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
122
Content.Client/Light/RoofOverlay.cs
Normal file
122
Content.Client/Light/RoofOverlay.cs
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
using System.Numerics;
|
||||
using Content.Shared.Light.Components;
|
||||
using Content.Shared.Maps;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Physics;
|
||||
|
||||
namespace Content.Client.Light;
|
||||
|
||||
public sealed class RoofOverlay : Overlay
|
||||
{
|
||||
private readonly IEntityManager _entManager;
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] private readonly IOverlayManager _overlay = default!;
|
||||
|
||||
private readonly EntityLookupSystem _lookup;
|
||||
private readonly SharedMapSystem _mapSystem;
|
||||
private readonly SharedTransformSystem _xformSystem;
|
||||
|
||||
private readonly HashSet<Entity<OccluderComponent>> _occluders = new();
|
||||
private List<Entity<MapGridComponent>> _grids = new();
|
||||
|
||||
public override OverlaySpace Space => OverlaySpace.BeforeLighting;
|
||||
|
||||
public const int ContentZIndex = BeforeLightTargetOverlay.ContentZIndex + 1;
|
||||
|
||||
public RoofOverlay(IEntityManager entManager)
|
||||
{
|
||||
_entManager = entManager;
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
_lookup = _entManager.System<EntityLookupSystem>();
|
||||
_mapSystem = _entManager.System<SharedMapSystem>();
|
||||
_xformSystem = _entManager.System<SharedTransformSystem>();
|
||||
|
||||
ZIndex = ContentZIndex;
|
||||
}
|
||||
|
||||
protected override void Draw(in OverlayDrawArgs args)
|
||||
{
|
||||
if (args.Viewport.Eye == null)
|
||||
return;
|
||||
|
||||
var viewport = args.Viewport;
|
||||
var eye = args.Viewport.Eye;
|
||||
|
||||
var worldHandle = args.WorldHandle;
|
||||
var lightoverlay = _overlay.GetOverlay<BeforeLightTargetOverlay>();
|
||||
var bounds = lightoverlay.EnlargedBounds;
|
||||
var target = lightoverlay.EnlargedLightTarget;
|
||||
|
||||
_grids.Clear();
|
||||
_mapManager.FindGridsIntersecting(args.MapId, bounds, ref _grids);
|
||||
|
||||
for (var i = _grids.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var grid = _grids[i];
|
||||
|
||||
if (_entManager.HasComponent<RoofComponent>(grid.Owner))
|
||||
continue;
|
||||
|
||||
_grids.RemoveAt(i);
|
||||
}
|
||||
|
||||
if (_grids.Count == 0)
|
||||
return;
|
||||
|
||||
var lightScale = viewport.LightRenderTarget.Size / (Vector2) viewport.Size;
|
||||
var scale = viewport.RenderScale / (Vector2.One / lightScale);
|
||||
|
||||
worldHandle.RenderInRenderTarget(target,
|
||||
() =>
|
||||
{
|
||||
foreach (var grid in _grids)
|
||||
{
|
||||
if (!_entManager.TryGetComponent(grid.Owner, out RoofComponent? roof))
|
||||
continue;
|
||||
|
||||
var invMatrix = target.GetWorldToLocalMatrix(eye, scale);
|
||||
|
||||
var gridMatrix = _xformSystem.GetWorldMatrix(grid.Owner);
|
||||
var matty = Matrix3x2.Multiply(gridMatrix, invMatrix);
|
||||
|
||||
worldHandle.SetTransform(matty);
|
||||
|
||||
var tileEnumerator = _mapSystem.GetTilesEnumerator(grid.Owner, grid, bounds);
|
||||
|
||||
// Due to stencilling we essentially draw on unrooved tiles
|
||||
while (tileEnumerator.MoveNext(out var tileRef))
|
||||
{
|
||||
if ((tileRef.Tile.Flags & (byte) TileFlag.Roof) == 0x0)
|
||||
{
|
||||
// Check if the tile is occluded in which case hide it anyway.
|
||||
// This is to avoid lit walls bleeding over to unlit tiles.
|
||||
_occluders.Clear();
|
||||
_lookup.GetLocalEntitiesIntersecting(grid.Owner, tileRef.GridIndices, _occluders);
|
||||
var found = false;
|
||||
|
||||
foreach (var occluder in _occluders)
|
||||
{
|
||||
if (!occluder.Comp.Enabled)
|
||||
continue;
|
||||
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!found)
|
||||
continue;
|
||||
}
|
||||
|
||||
var local = _lookup.GetLocalBounds(tileRef, grid.Comp.TileSize);
|
||||
worldHandle.DrawRect(local, roof.Color);
|
||||
}
|
||||
}
|
||||
}, null);
|
||||
|
||||
worldHandle.SetTransform(Matrix3x2.Identity);
|
||||
}
|
||||
}
|
||||
96
Content.Client/Light/TileEmissionOverlay.cs
Normal file
96
Content.Client/Light/TileEmissionOverlay.cs
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
using System.Numerics;
|
||||
using Content.Shared.Light.Components;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
|
||||
namespace Content.Client.Light;
|
||||
|
||||
public sealed class TileEmissionOverlay : Overlay
|
||||
{
|
||||
public override OverlaySpace Space => OverlaySpace.BeforeLighting;
|
||||
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] private readonly IOverlayManager _overlay = default!;
|
||||
|
||||
private SharedMapSystem _mapSystem;
|
||||
private SharedTransformSystem _xformSystem;
|
||||
|
||||
private readonly EntityLookupSystem _lookup;
|
||||
|
||||
private readonly EntityQuery<TransformComponent> _xformQuery;
|
||||
private readonly HashSet<Entity<TileEmissionComponent>> _entities = new();
|
||||
|
||||
private List<Entity<MapGridComponent>> _grids = new();
|
||||
|
||||
public const int ContentZIndex = RoofOverlay.ContentZIndex + 1;
|
||||
|
||||
public TileEmissionOverlay(IEntityManager entManager)
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
_lookup = entManager.System<EntityLookupSystem>();
|
||||
_mapSystem = entManager.System<SharedMapSystem>();
|
||||
_xformSystem = entManager.System<SharedTransformSystem>();
|
||||
|
||||
_xformQuery = entManager.GetEntityQuery<TransformComponent>();
|
||||
ZIndex = ContentZIndex;
|
||||
}
|
||||
|
||||
protected override void Draw(in OverlayDrawArgs args)
|
||||
{
|
||||
if (args.Viewport.Eye == null)
|
||||
return;
|
||||
|
||||
var mapId = args.MapId;
|
||||
var worldHandle = args.WorldHandle;
|
||||
var lightoverlay = _overlay.GetOverlay<BeforeLightTargetOverlay>();
|
||||
var bounds = lightoverlay.EnlargedBounds;
|
||||
var target = lightoverlay.EnlargedLightTarget;
|
||||
var viewport = args.Viewport;
|
||||
_grids.Clear();
|
||||
_mapManager.FindGridsIntersecting(mapId, bounds, ref _grids, approx: true);
|
||||
|
||||
if (_grids.Count == 0)
|
||||
return;
|
||||
|
||||
var lightScale = viewport.LightRenderTarget.Size / (Vector2) viewport.Size;
|
||||
var scale = viewport.RenderScale / (Vector2.One / lightScale);
|
||||
|
||||
args.WorldHandle.RenderInRenderTarget(target,
|
||||
() =>
|
||||
{
|
||||
var invMatrix = target.GetWorldToLocalMatrix(viewport.Eye, scale);
|
||||
|
||||
foreach (var grid in _grids)
|
||||
{
|
||||
var gridInvMatrix = _xformSystem.GetInvWorldMatrix(grid);
|
||||
var localBounds = gridInvMatrix.TransformBox(bounds);
|
||||
_entities.Clear();
|
||||
_lookup.GetLocalEntitiesIntersecting(grid.Owner, localBounds, _entities);
|
||||
|
||||
if (_entities.Count == 0)
|
||||
continue;
|
||||
|
||||
var gridMatrix = _xformSystem.GetWorldMatrix(grid.Owner);
|
||||
|
||||
foreach (var ent in _entities)
|
||||
{
|
||||
var xform = _xformQuery.Comp(ent);
|
||||
|
||||
var tile = _mapSystem.LocalToTile(grid.Owner, grid, xform.Coordinates);
|
||||
var matty = Matrix3x2.Multiply(gridMatrix, invMatrix);
|
||||
|
||||
worldHandle.SetTransform(matty);
|
||||
|
||||
// Yes I am fully aware this leads to overlap. If you really want to have alpha then you'll need
|
||||
// to turn the squares into polys.
|
||||
// Additionally no shadows so if you make it too big it's going to go through a 1x wall.
|
||||
var local = _lookup.GetLocalBounds(tile, grid.Comp.TileSize).Enlarged(ent.Comp.Range);
|
||||
worldHandle.DrawRect(local, ent.Comp.Color);
|
||||
}
|
||||
}
|
||||
}, null);
|
||||
}
|
||||
}
|
||||
|
|
@ -6,7 +6,7 @@
|
|||
MinSize="800 128">
|
||||
<BoxContainer Orientation="Vertical" VerticalExpand="True">
|
||||
<BoxContainer Name="RoleNameBox" Orientation="Vertical" Margin="10">
|
||||
<Label Name="LoadoutNameLabel" Text="{Loc 'loadout-name-edit-label'}"/>
|
||||
<Label Name="LoadoutNameLabel"/>
|
||||
<PanelContainer HorizontalExpand="True" SetHeight="24">
|
||||
<PanelContainer.PanelOverride>
|
||||
<graphics:StyleBoxFlat BackgroundColor="#1B1B1E" />
|
||||
|
|
|
|||
|
|
@ -40,6 +40,10 @@ public sealed partial class LoadoutWindow : FancyWindow
|
|||
{
|
||||
var name = loadout.EntityName;
|
||||
|
||||
LoadoutNameLabel.Text = proto.NameDataset == null ?
|
||||
Loc.GetString("loadout-name-edit-label") :
|
||||
Loc.GetString("loadout-name-edit-label-dataset");
|
||||
|
||||
RoleNameEdit.ToolTip = Loc.GetString(
|
||||
"loadout-name-edit-tooltip",
|
||||
("max", HumanoidCharacterProfile.MaxLoadoutNameLength));
|
||||
|
|
|
|||
|
|
@ -30,8 +30,7 @@ namespace Content.Client.PDA
|
|||
|
||||
private void CreateMenu()
|
||||
{
|
||||
_menu = this.CreateWindow<PdaMenu>();
|
||||
_menu.OpenCenteredLeft();
|
||||
_menu = this.CreateWindowCenteredLeft<PdaMenu>();
|
||||
|
||||
_menu.FlashLightToggleButton.OnToggled += _ =>
|
||||
{
|
||||
|
|
|
|||
|
|
@ -130,6 +130,12 @@ namespace Content.Client.Popups
|
|||
PopupMessage(message, type, coordinates, null);
|
||||
}
|
||||
|
||||
public override void PopupPredictedCoordinates(string? message, EntityCoordinates coordinates, EntityUid? recipient, PopupType type = PopupType.Small)
|
||||
{
|
||||
if (recipient != null && _timing.IsFirstTimePredicted)
|
||||
PopupCoordinates(message, coordinates, recipient.Value, type);
|
||||
}
|
||||
|
||||
private void PopupCursorInternal(string? message, PopupType type, bool recordReplay)
|
||||
{
|
||||
if (message == null)
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
using System.Numerics;
|
||||
using Content.Client.UserInterface.Systems.Storage;
|
||||
using Content.Client.UserInterface.Systems.Storage.Controls;
|
||||
using Content.Shared.Storage;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
|
||||
namespace Content.Client.Storage;
|
||||
|
||||
|
|
@ -11,6 +13,8 @@ public sealed class StorageBoundUserInterface : BoundUserInterface
|
|||
{
|
||||
private StorageWindow? _window;
|
||||
|
||||
public Vector2? Position => _window?.Position;
|
||||
|
||||
public StorageBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
|
||||
{
|
||||
}
|
||||
|
|
@ -21,7 +25,7 @@ public sealed class StorageBoundUserInterface : BoundUserInterface
|
|||
|
||||
_window = IoCManager.Resolve<IUserInterfaceManager>()
|
||||
.GetUIController<StorageUIController>()
|
||||
.CreateStorageWindow(Owner);
|
||||
.CreateStorageWindow(this);
|
||||
|
||||
if (EntMan.TryGetComponent(Owner, out StorageComponent? storage))
|
||||
{
|
||||
|
|
@ -50,10 +54,20 @@ public sealed class StorageBoundUserInterface : BoundUserInterface
|
|||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
|
||||
Reclaim();
|
||||
}
|
||||
|
||||
public void CloseWindow(Vector2 position)
|
||||
{
|
||||
if (_window == null)
|
||||
return;
|
||||
|
||||
// Update its position before potentially saving.
|
||||
// Listen it makes sense okay.
|
||||
LayoutContainer.SetPosition(_window, position);
|
||||
_window?.Close();
|
||||
}
|
||||
|
||||
public void Hide()
|
||||
{
|
||||
if (_window == null)
|
||||
|
|
@ -70,6 +84,15 @@ public sealed class StorageBoundUserInterface : BoundUserInterface
|
|||
_window.Visible = true;
|
||||
}
|
||||
|
||||
public void Show(Vector2 position)
|
||||
{
|
||||
if (_window == null)
|
||||
return;
|
||||
|
||||
Show();
|
||||
LayoutContainer.SetPosition(_window, position);
|
||||
}
|
||||
|
||||
public void ReOpen()
|
||||
{
|
||||
_window?.Orphan();
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@ public sealed class StorageSystem : SharedStorageSystem
|
|||
|
||||
private Dictionary<EntityUid, ItemStorageLocation> _oldStoredItems = new();
|
||||
|
||||
private List<(StorageBoundUserInterface Bui, bool Value)> _queuedBuis = new();
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
|
@ -72,7 +74,7 @@ public sealed class StorageSystem : SharedStorageSystem
|
|||
if (NestedStorage && player != null && ContainerSystem.TryGetContainingContainer((uid, null, null), out var container) &&
|
||||
UI.TryGetOpenUi<StorageBoundUserInterface>(container.Owner, StorageComponent.StorageUiKey.Key, out var containerBui))
|
||||
{
|
||||
containerBui.Hide();
|
||||
_queuedBuis.Add((containerBui, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -89,7 +91,7 @@ public sealed class StorageSystem : SharedStorageSystem
|
|||
{
|
||||
if (UI.TryGetOpenUi<StorageBoundUserInterface>(uid, StorageComponent.StorageUiKey.Key, out var storageBui))
|
||||
{
|
||||
storageBui.Hide();
|
||||
_queuedBuis.Add((storageBui, false));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -97,7 +99,7 @@ public sealed class StorageSystem : SharedStorageSystem
|
|||
{
|
||||
if (UI.TryGetOpenUi<StorageBoundUserInterface>(uid, StorageComponent.StorageUiKey.Key, out var storageBui))
|
||||
{
|
||||
storageBui.Show();
|
||||
_queuedBuis.Add((storageBui, true));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -152,4 +154,30 @@ public sealed class StorageSystem : SharedStorageSystem
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
if (!_timing.IsFirstTimePredicted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// This update loop exists just to synchronize with UISystem and avoid 1-tick delays.
|
||||
// If deferred opens / closes ever get removed you can dump this.
|
||||
foreach (var (bui, open) in _queuedBuis)
|
||||
{
|
||||
if (open)
|
||||
{
|
||||
bui.Show();
|
||||
}
|
||||
else
|
||||
{
|
||||
bui.Hide();
|
||||
}
|
||||
}
|
||||
|
||||
_queuedBuis.Clear();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ using Content.Shared.IdentityManagement;
|
|||
using Content.Shared.Input;
|
||||
using Content.Shared.Item;
|
||||
using Content.Shared.Storage;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
|
|
@ -190,6 +191,26 @@ public sealed class StorageWindow : BaseWindow
|
|||
BuildGridRepresentation();
|
||||
}
|
||||
|
||||
private void CloseParent()
|
||||
{
|
||||
if (StorageEntity == null)
|
||||
return;
|
||||
|
||||
var containerSystem = _entity.System<SharedContainerSystem>();
|
||||
var uiSystem = _entity.System<UserInterfaceSystem>();
|
||||
|
||||
if (containerSystem.TryGetContainingContainer(StorageEntity.Value, out var container) &&
|
||||
_entity.TryGetComponent(container.Owner, out StorageComponent? storage) &&
|
||||
storage.Container.Contains(StorageEntity.Value) &&
|
||||
uiSystem
|
||||
.TryGetOpenUi<StorageBoundUserInterface>(container.Owner,
|
||||
StorageComponent.StorageUiKey.Key,
|
||||
out var parentBui))
|
||||
{
|
||||
parentBui.CloseWindow(Position);
|
||||
}
|
||||
}
|
||||
|
||||
private void BuildGridRepresentation()
|
||||
{
|
||||
if (!_entity.TryGetComponent<StorageComponent>(StorageEntity, out var comp) || comp.Grid.Count == 0)
|
||||
|
|
@ -212,7 +233,9 @@ public sealed class StorageWindow : BaseWindow
|
|||
};
|
||||
exitButton.OnPressed += _ =>
|
||||
{
|
||||
// Close ourselves and all parent BUIs.
|
||||
Close();
|
||||
CloseParent();
|
||||
};
|
||||
exitButton.OnKeyBindDown += args =>
|
||||
{
|
||||
|
|
@ -220,6 +243,7 @@ public sealed class StorageWindow : BaseWindow
|
|||
if (!args.Handled && args.Function == ContentKeyFunctions.ActivateItemInWorld)
|
||||
{
|
||||
Close();
|
||||
CloseParent();
|
||||
args.Handle();
|
||||
}
|
||||
};
|
||||
|
|
@ -258,7 +282,8 @@ public sealed class StorageWindow : BaseWindow
|
|||
var containerSystem = _entity.System<SharedContainerSystem>();
|
||||
|
||||
if (containerSystem.TryGetContainingContainer(StorageEntity.Value, out var container) &&
|
||||
_entity.TryGetComponent(container.Owner, out StorageComponent? storage))
|
||||
_entity.TryGetComponent(container.Owner, out StorageComponent? storage) &&
|
||||
storage.Container.Contains(StorageEntity.Value))
|
||||
{
|
||||
Close();
|
||||
|
||||
|
|
@ -267,7 +292,7 @@ public sealed class StorageWindow : BaseWindow
|
|||
StorageComponent.StorageUiKey.Key,
|
||||
out var parentBui))
|
||||
{
|
||||
parentBui.Show();
|
||||
parentBui.Show(Position);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -412,6 +437,8 @@ public sealed class StorageWindow : BaseWindow
|
|||
{
|
||||
if (storageComp.StoredItems.TryGetValue(ent, out var updated))
|
||||
{
|
||||
data.Control.Marked = IsMarked(ent);
|
||||
|
||||
if (data.Loc.Equals(updated))
|
||||
{
|
||||
DebugTools.Assert(data.Control.Location == updated);
|
||||
|
|
@ -450,12 +477,7 @@ public sealed class StorageWindow : BaseWindow
|
|||
var gridPiece = new ItemGridPiece((ent, itemEntComponent), loc, _entity)
|
||||
{
|
||||
MinSize = size,
|
||||
Marked = _contained.IndexOf(ent) switch
|
||||
{
|
||||
0 => ItemGridPieceMarks.First,
|
||||
1 => ItemGridPieceMarks.Second,
|
||||
_ => null,
|
||||
}
|
||||
Marked = IsMarked(ent),
|
||||
};
|
||||
gridPiece.OnPiecePressed += OnPiecePressed;
|
||||
gridPiece.OnPieceUnpressed += OnPieceUnpressed;
|
||||
|
|
@ -467,6 +489,16 @@ public sealed class StorageWindow : BaseWindow
|
|||
}
|
||||
}
|
||||
|
||||
private ItemGridPieceMarks? IsMarked(EntityUid uid)
|
||||
{
|
||||
return _contained.IndexOf(uid) switch
|
||||
{
|
||||
0 => ItemGridPieceMarks.First,
|
||||
1 => ItemGridPieceMarks.Second,
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
{
|
||||
base.FrameUpdate(args);
|
||||
|
|
@ -486,8 +518,9 @@ public sealed class StorageWindow : BaseWindow
|
|||
{
|
||||
if (StorageEntity != null && _entity.System<StorageSystem>().NestedStorage)
|
||||
{
|
||||
// If parent container nests us then show back button
|
||||
if (containerSystem.TryGetContainingContainer(StorageEntity.Value, out var container) &&
|
||||
_entity.HasComponent<StorageComponent>(container.Owner))
|
||||
_entity.TryGetComponent(container.Owner, out StorageComponent? storageComp) && storageComp.Container.Contains(StorageEntity.Value))
|
||||
{
|
||||
_backButton.Visible = true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ using Content.Shared.CCVar;
|
|||
using Content.Shared.Input;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Storage;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Input;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Client.UserInterface;
|
||||
|
|
@ -37,6 +38,7 @@ public sealed class StorageUIController : UIController, IOnSystemChanged<Storage
|
|||
[Dependency] private readonly IInputManager _input = default!;
|
||||
[Dependency] private readonly IPlayerManager _player = default!;
|
||||
[UISystemDependency] private readonly StorageSystem _storage = default!;
|
||||
[UISystemDependency] private readonly UserInterfaceSystem _ui = default!;
|
||||
|
||||
private readonly DragDropHelper<ItemGridPiece> _menuDragHelper;
|
||||
|
||||
|
|
@ -107,7 +109,7 @@ public sealed class StorageUIController : UIController, IOnSystemChanged<Storage
|
|||
StaticStorageUIEnabled = obj;
|
||||
}
|
||||
|
||||
public StorageWindow CreateStorageWindow(EntityUid uid)
|
||||
public StorageWindow CreateStorageWindow(StorageBoundUserInterface sBui)
|
||||
{
|
||||
var window = new StorageWindow();
|
||||
window.MouseFilter = Control.MouseFilterMode.Pass;
|
||||
|
|
@ -127,9 +129,25 @@ public sealed class StorageUIController : UIController, IOnSystemChanged<Storage
|
|||
}
|
||||
else
|
||||
{
|
||||
window.OpenCenteredLeft();
|
||||
// Open at parent position if it's open.
|
||||
if (_ui.TryGetOpenUi<StorageBoundUserInterface>(EntityManager.GetComponent<TransformComponent>(sBui.Owner).ParentUid,
|
||||
StorageComponent.StorageUiKey.Key, out var bui) && bui.Position != null)
|
||||
{
|
||||
window.Open(bui.Position.Value);
|
||||
}
|
||||
// Open at the saved position if it exists.
|
||||
else if (_ui.TryGetPosition(sBui.Owner, StorageComponent.StorageUiKey.Key, out var pos))
|
||||
{
|
||||
window.Open(pos);
|
||||
}
|
||||
// Open at the default position.
|
||||
else
|
||||
{
|
||||
window.OpenCenteredLeft();
|
||||
}
|
||||
}
|
||||
|
||||
_ui.RegisterControl(sBui, window);
|
||||
return window;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ public sealed partial class GunSystem : SharedGunSystem
|
|||
[Dependency] private readonly InputSystem _inputSystem = default!;
|
||||
[Dependency] private readonly SharedCameraRecoilSystem _recoil = default!;
|
||||
[Dependency] private readonly SharedMapSystem _maps = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _xform = default!;
|
||||
|
||||
[ValidatePrototypeId<EntityPrototype>]
|
||||
public const string HitscanProto = "HitscanEffect";
|
||||
|
|
@ -117,6 +118,14 @@ public sealed partial class GunSystem : SharedGunSystem
|
|||
|
||||
private void OnHitscan(HitscanEvent ev)
|
||||
{
|
||||
// ALL I WANT IS AN ANIMATED EFFECT
|
||||
|
||||
// TODO EFFECTS
|
||||
// This is very jank
|
||||
// because the effect consists of three unrelatd entities, the hitscan beam can be split appart.
|
||||
// E.g., if a grid rotates while part of the beam is parented to the grid, and part of it is parented to the map.
|
||||
// Ideally, there should only be one entity, with one sprite that has multiple layers
|
||||
// Or at the very least, have the other entities parented to the same entity to make sure they stick together.
|
||||
const double tracerInterval = 0.01f;
|
||||
|
||||
foreach (var a in ev.Sprites)
|
||||
|
|
@ -126,7 +135,7 @@ public sealed partial class GunSystem : SharedGunSystem
|
|||
|
||||
var startCoords = GetCoordinates(a.coordinates);
|
||||
|
||||
if (Deleted(startCoords.EntityId))
|
||||
if (!TryComp(startCoords.EntityId, out TransformComponent? relativeXform))
|
||||
continue;
|
||||
|
||||
if (a.effectType == EffectType.Tracer)
|
||||
|
|
@ -142,7 +151,7 @@ public sealed partial class GunSystem : SharedGunSystem
|
|||
|
||||
Timer.Spawn(TimeSpan.FromSeconds(delay), () =>
|
||||
{
|
||||
CreateTracerEffect(stepCoords, a.angle, rsi);
|
||||
CreateTracerEffect(stepCoords, a.angle, rsi, relativeXform);
|
||||
});
|
||||
|
||||
stepIndex++;
|
||||
|
|
@ -150,17 +159,21 @@ public sealed partial class GunSystem : SharedGunSystem
|
|||
}
|
||||
else if (a.effectType == EffectType.Static)
|
||||
{
|
||||
CreateStaticEffect(startCoords, a.angle, rsi, a.distance);
|
||||
CreateStaticEffect(startCoords, a.angle, rsi, a.distance, relativeXform);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private EntityUid CreateTracerEffect(EntityCoordinates coords, Angle angle, SpriteSpecifier.Rsi rsi)
|
||||
private EntityUid CreateTracerEffect(EntityCoordinates coords, Angle angle, SpriteSpecifier.Rsi rsi, TransformComponent relativeXform)
|
||||
{
|
||||
var ent = Spawn(HitscanTracerProto, coords);
|
||||
var sprite = Comp<SpriteComponent>(ent);
|
||||
|
||||
var xform = Transform(ent);
|
||||
xform.LocalRotation = angle;
|
||||
var targetWorldRot = angle + _xform.GetWorldRotation(relativeXform);
|
||||
var delta = targetWorldRot - _xform.GetWorldRotation(xform);
|
||||
_xform.SetLocalRotationNoLerp(ent, xform.LocalRotation + delta, xform);
|
||||
|
||||
sprite[EffectLayers.Unshaded].AutoAnimated = false;
|
||||
sprite.LayerSetSprite(EffectLayers.Unshaded, rsi);
|
||||
sprite.LayerSetState(EffectLayers.Unshaded, rsi.RsiState);
|
||||
|
|
@ -188,7 +201,7 @@ public sealed partial class GunSystem : SharedGunSystem
|
|||
return ent;
|
||||
}
|
||||
|
||||
private void CreateStaticEffect(EntityCoordinates coords, Angle angle, SpriteSpecifier.Rsi rsi, float distance)
|
||||
private void CreateStaticEffect(EntityCoordinates coords, Angle angle, SpriteSpecifier.Rsi rsi, float distance, TransformComponent relativeXform)
|
||||
{
|
||||
var ent = Spawn(HitscanProto, coords);
|
||||
var sprite = Comp<SpriteComponent>(ent);
|
||||
|
|
|
|||
|
|
@ -26,11 +26,7 @@ public sealed partial class TestPair
|
|||
instance.ProtoMan.LoadString(file, changed: changed);
|
||||
}
|
||||
|
||||
await instance.WaitPost(() =>
|
||||
{
|
||||
instance.ProtoMan.ResolveResults();
|
||||
instance.ProtoMan.ReloadPrototypes(changed);
|
||||
});
|
||||
await instance.WaitPost(() => instance.ProtoMan.ReloadPrototypes(changed));
|
||||
|
||||
foreach (var (kind, ids) in changed)
|
||||
{
|
||||
|
|
@ -65,4 +61,4 @@ public sealed partial class TestPair
|
|||
{
|
||||
return _loadedPrototypes.TryGetValue(kind, out var ids) && ids.Contains(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ using Robust.Shared.Map;
|
|||
using Robust.Shared.Map.Components;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Robust.Shared.EntitySerialization.Systems;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.IntegrationTests.Tests.Body
|
||||
{
|
||||
|
|
@ -57,7 +59,6 @@ namespace Content.IntegrationTests.Tests.Body
|
|||
|
||||
await server.WaitIdleAsync();
|
||||
|
||||
var mapManager = server.ResolveDependency<IMapManager>();
|
||||
var entityManager = server.ResolveDependency<IEntityManager>();
|
||||
var mapLoader = entityManager.System<MapLoaderSystem>();
|
||||
var mapSys = entityManager.System<SharedMapSystem>();
|
||||
|
|
@ -69,17 +70,13 @@ namespace Content.IntegrationTests.Tests.Body
|
|||
GridAtmosphereComponent relevantAtmos = default;
|
||||
var startingMoles = 0.0f;
|
||||
|
||||
var testMapName = "Maps/Test/Breathing/3by3-20oxy-80nit.yml";
|
||||
var testMapName = new ResPath("Maps/Test/Breathing/3by3-20oxy-80nit.yml");
|
||||
|
||||
await server.WaitPost(() =>
|
||||
{
|
||||
mapSys.CreateMap(out var mapId);
|
||||
Assert.That(mapLoader.TryLoad(mapId, testMapName, out var roots));
|
||||
|
||||
var query = entityManager.GetEntityQuery<MapGridComponent>();
|
||||
var grids = roots.Where(x => query.HasComponent(x));
|
||||
Assert.That(grids, Is.Not.Empty);
|
||||
grid = grids.First();
|
||||
Assert.That(mapLoader.TryLoadGrid(mapId, testMapName, out var gridEnt));
|
||||
grid = gridEnt!.Value.Owner;
|
||||
});
|
||||
|
||||
Assert.That(grid, Is.Not.Null, $"Test blueprint {testMapName} not found.");
|
||||
|
|
@ -148,18 +145,13 @@ namespace Content.IntegrationTests.Tests.Body
|
|||
RespiratorComponent respirator = null;
|
||||
EntityUid human = default;
|
||||
|
||||
var testMapName = "Maps/Test/Breathing/3by3-20oxy-80nit.yml";
|
||||
var testMapName = new ResPath("Maps/Test/Breathing/3by3-20oxy-80nit.yml");
|
||||
|
||||
await server.WaitPost(() =>
|
||||
{
|
||||
mapSys.CreateMap(out var mapId);
|
||||
|
||||
Assert.That(mapLoader.TryLoad(mapId, testMapName, out var ents), Is.True);
|
||||
var query = entityManager.GetEntityQuery<MapGridComponent>();
|
||||
grid = ents
|
||||
.Select<EntityUid, EntityUid?>(x => x)
|
||||
.FirstOrDefault((uid) => uid.HasValue && query.HasComponent(uid.Value), null);
|
||||
Assert.That(grid, Is.Not.Null);
|
||||
Assert.That(mapLoader.TryLoadGrid(mapId, testMapName, out var gridEnt));
|
||||
grid = gridEnt!.Value.Owner;
|
||||
});
|
||||
|
||||
Assert.That(grid, Is.Not.Null, $"Test blueprint {testMapName} not found.");
|
||||
|
|
|
|||
|
|
@ -2,10 +2,11 @@
|
|||
using System.Linq;
|
||||
using Content.Shared.Body.Components;
|
||||
using Content.Shared.Body.Systems;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.EntitySerialization.Systems;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.IntegrationTests.Tests.Body;
|
||||
|
||||
|
|
@ -111,13 +112,12 @@ public sealed class SaveLoadReparentTest
|
|||
Is.Not.Empty
|
||||
);
|
||||
|
||||
const string mapPath = $"/{nameof(SaveLoadReparentTest)}{nameof(Test)}map.yml";
|
||||
var mapPath = new ResPath($"/{nameof(SaveLoadReparentTest)}{nameof(Test)}map.yml");
|
||||
|
||||
mapLoader.SaveMap(mapId, mapPath);
|
||||
maps.DeleteMap(mapId);
|
||||
Assert.That(mapLoader.TrySaveMap(mapId, mapPath));
|
||||
mapSys.DeleteMap(mapId);
|
||||
|
||||
mapSys.CreateMap(out mapId);
|
||||
Assert.That(mapLoader.TryLoad(mapId, mapPath, out _), Is.True);
|
||||
Assert.That(mapLoader.TryLoadMap(mapPath, out var map, out _), Is.True);
|
||||
|
||||
var query = EnumerateQueryEnumerator(
|
||||
entities.EntityQueryEnumerator<BodyComponent>()
|
||||
|
|
@ -173,7 +173,7 @@ public sealed class SaveLoadReparentTest
|
|||
});
|
||||
}
|
||||
|
||||
maps.DeleteMap(mapId);
|
||||
entities.DeleteEntity(map);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
70
Content.IntegrationTests/Tests/MagazineVisualsSpriteTest.cs
Normal file
70
Content.IntegrationTests/Tests/MagazineVisualsSpriteTest.cs
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
using System.Collections.Generic;
|
||||
using Content.Client.Weapons.Ranged.Components;
|
||||
using Content.Shared.Prototypes;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.IntegrationTests.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// Tests all entity prototypes with the MagazineVisualsComponent.
|
||||
/// </summary>
|
||||
[TestFixture]
|
||||
public sealed class MagazineVisualsSpriteTest
|
||||
{
|
||||
[Test]
|
||||
public async Task MagazineVisualsSpritesExist()
|
||||
{
|
||||
await using var pair = await PoolManager.GetServerClient();
|
||||
var client = pair.Client;
|
||||
var protoMan = client.ResolveDependency<IPrototypeManager>();
|
||||
var componentFactory = client.ResolveDependency<IComponentFactory>();
|
||||
|
||||
await client.WaitAssertion(() =>
|
||||
{
|
||||
foreach (var proto in protoMan.EnumeratePrototypes<EntityPrototype>())
|
||||
{
|
||||
if (proto.Abstract || pair.IsTestPrototype(proto))
|
||||
continue;
|
||||
|
||||
if (!proto.TryGetComponent<MagazineVisualsComponent>(out var visuals, componentFactory))
|
||||
continue;
|
||||
|
||||
Assert.That(proto.TryGetComponent<SpriteComponent>(out var sprite, componentFactory),
|
||||
@$"{proto.ID} has MagazineVisualsComponent but no SpriteComponent.");
|
||||
Assert.That(proto.HasComponent<AppearanceComponent>(componentFactory),
|
||||
@$"{proto.ID} has MagazineVisualsComponent but no AppearanceComponent.");
|
||||
|
||||
var toTest = new List<(int, string)>();
|
||||
if (sprite.LayerMapTryGet(GunVisualLayers.Mag, out var magLayerId))
|
||||
toTest.Add((magLayerId, ""));
|
||||
if (sprite.LayerMapTryGet(GunVisualLayers.MagUnshaded, out var magUnshadedLayerId))
|
||||
toTest.Add((magUnshadedLayerId, "-unshaded"));
|
||||
|
||||
Assert.That(toTest, Is.Not.Empty,
|
||||
@$"{proto.ID} has MagazineVisualsComponent but no Mag or MagUnshaded layer map.");
|
||||
|
||||
var start = visuals.ZeroVisible ? 0 : 1;
|
||||
foreach (var (id, midfix) in toTest)
|
||||
{
|
||||
Assert.That(sprite.TryGetLayer(id, out var layer));
|
||||
var rsi = layer.ActualRsi;
|
||||
for (var i = start; i < visuals.MagSteps; i++)
|
||||
{
|
||||
var state = $"{visuals.MagState}{midfix}-{i}";
|
||||
Assert.That(rsi.TryGetState(state, out _),
|
||||
@$"{proto.ID} has MagazineVisualsComponent with MagSteps = {visuals.MagSteps}, but {rsi.Path} doesn't have state {state}!");
|
||||
}
|
||||
|
||||
// MagSteps includes the 0th step, so sometimes people are off by one.
|
||||
var extraState = $"{visuals.MagState}{midfix}-{visuals.MagSteps}";
|
||||
Assert.That(rsi.TryGetState(extraState, out _), Is.False,
|
||||
@$"{proto.ID} has MagazineVisualsComponent with MagSteps = {visuals.MagSteps}, but more states exist!");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
await pair.CleanReturnAsync();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
#nullable enable
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
|
||||
namespace Content.IntegrationTests.Tests.Minds;
|
||||
|
|
@ -39,8 +40,8 @@ public sealed partial class MindTests
|
|||
Assert.That(pair.Client.EntMan.EntityCount, Is.EqualTo(0));
|
||||
|
||||
// Create a new map.
|
||||
int mapId = 1;
|
||||
await pair.Server.WaitPost(() => conHost.ExecuteCommand($"addmap {mapId}"));
|
||||
MapId mapId = default;
|
||||
await pair.Server.WaitPost(() => pair.Server.System<SharedMapSystem>().CreateMap(out mapId));
|
||||
await pair.RunTicksSync(5);
|
||||
|
||||
// Client is not attached to anything
|
||||
|
|
@ -56,7 +57,7 @@ public sealed partial class MindTests
|
|||
Assert.That(pair.Client.EntMan.EntityExists(pair.Client.AttachedEntity));
|
||||
Assert.That(pair.Server.EntMan.EntityExists(pair.PlayerData?.Mind));
|
||||
var xform = pair.Client.Transform(pair.Client.AttachedEntity!.Value);
|
||||
Assert.That(xform.MapID, Is.EqualTo(new MapId(mapId)));
|
||||
Assert.That(xform.MapID, Is.EqualTo(mapId));
|
||||
|
||||
await pair.CleanReturnAsync();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ using Content.Server.Spawners.Components;
|
|||
using Content.Server.Station.Components;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Roles;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
|
@ -17,6 +16,9 @@ using Robust.Shared.Map;
|
|||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Content.Shared.Station.Components;
|
||||
using Robust.Shared.EntitySerialization;
|
||||
using Robust.Shared.EntitySerialization.Systems;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Utility;
|
||||
using YamlDotNet.RepresentationModel;
|
||||
|
||||
|
|
@ -35,11 +37,21 @@ namespace Content.IntegrationTests.Tests
|
|||
};
|
||||
|
||||
private static readonly string[] Grids =
|
||||
{
|
||||
"/Maps/centcomm.yml"
|
||||
};
|
||||
|
||||
private static readonly string[] DoNotMapWhitelist =
|
||||
{
|
||||
"/Maps/centcomm.yml",
|
||||
"/Maps/Shuttles/cargo.yml",
|
||||
"/Maps/Shuttles/emergency.yml",
|
||||
"/Maps/Shuttles/infiltrator.yml",
|
||||
"/Maps/bagel.yml", // Contains mime's rubber stamp --> Either fix this, remove the category, or remove this comment if intentional.
|
||||
"/Maps/gate.yml", // Contains positronic brain and LSE-1200c "Perforator"
|
||||
"/Maps/meta.yml", // Contains warden's rubber stamp
|
||||
"/Maps/reach.yml", // Contains handheld crew monitor
|
||||
"/Maps/Shuttles/ShuttleEvent/cruiser.yml", // Contains LSE-1200c "Perforator"
|
||||
"/Maps/Shuttles/ShuttleEvent/honki.yml", // Contains golden honker, clown's rubber stamp
|
||||
"/Maps/Shuttles/ShuttleEvent/instigator.yml", // Contains EXP-320g "Friendship"
|
||||
"/Maps/Shuttles/ShuttleEvent/syndie_evacpod.yml", // Contains syndicate rubber stamp
|
||||
};
|
||||
|
||||
private static readonly string[] GameMaps =
|
||||
|
|
@ -117,33 +129,72 @@ namespace Content.IntegrationTests.Tests
|
|||
var entManager = server.ResolveDependency<IEntityManager>();
|
||||
var mapLoader = entManager.System<MapLoaderSystem>();
|
||||
var mapSystem = entManager.System<SharedMapSystem>();
|
||||
var mapManager = server.ResolveDependency<IMapManager>();
|
||||
var cfg = server.ResolveDependency<IConfigurationManager>();
|
||||
Assert.That(cfg.GetCVar(CCVars.GridFill), Is.False);
|
||||
var path = new ResPath(mapFile);
|
||||
|
||||
await server.WaitPost(() =>
|
||||
{
|
||||
mapSystem.CreateMap(out var mapId);
|
||||
try
|
||||
{
|
||||
#pragma warning disable NUnit2045
|
||||
Assert.That(mapLoader.TryLoad(mapId, mapFile, out var roots));
|
||||
Assert.That(roots.Where(uid => entManager.HasComponent<MapGridComponent>(uid)), Is.Not.Empty);
|
||||
#pragma warning restore NUnit2045
|
||||
Assert.That(mapLoader.TryLoadGrid(mapId, path, out var grid));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"Failed to load map {mapFile}, was it saved as a map instead of a grid?", ex);
|
||||
}
|
||||
|
||||
try
|
||||
mapSystem.DeleteMap(mapId);
|
||||
});
|
||||
await server.WaitRunTicks(1);
|
||||
|
||||
await pair.CleanReturnAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asserts that shuttles are loadable and have been saved as grids and not maps.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task ShuttlesLoadableTest()
|
||||
{
|
||||
await using var pair = await PoolManager.GetServerClient();
|
||||
var server = pair.Server;
|
||||
|
||||
var entManager = server.ResolveDependency<IEntityManager>();
|
||||
var resMan = server.ResolveDependency<IResourceManager>();
|
||||
var mapLoader = entManager.System<MapLoaderSystem>();
|
||||
var mapSystem = entManager.System<SharedMapSystem>();
|
||||
var cfg = server.ResolveDependency<IConfigurationManager>();
|
||||
Assert.That(cfg.GetCVar(CCVars.GridFill), Is.False);
|
||||
|
||||
var shuttleFolder = new ResPath("/Maps/Shuttles");
|
||||
var shuttles = resMan
|
||||
.ContentFindFiles(shuttleFolder)
|
||||
.Where(filePath =>
|
||||
filePath.Extension == "yml" && !filePath.Filename.StartsWith(".", StringComparison.Ordinal))
|
||||
.ToArray();
|
||||
|
||||
await server.WaitPost(() =>
|
||||
{
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
mapManager.DeleteMap(mapId);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"Failed to delete map {mapFile}", ex);
|
||||
}
|
||||
foreach (var path in shuttles)
|
||||
{
|
||||
mapSystem.CreateMap(out var mapId);
|
||||
try
|
||||
{
|
||||
Assert.That(mapLoader.TryLoadGrid(mapId, path, out _),
|
||||
$"Failed to load shuttle {path}, was it saved as a map instead of a grid?");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"Failed to load shuttle {path}, was it saved as a map instead of a grid?",
|
||||
ex);
|
||||
}
|
||||
mapSystem.DeleteMap(mapId);
|
||||
}
|
||||
});
|
||||
});
|
||||
await server.WaitRunTicks(1);
|
||||
|
||||
|
|
@ -157,12 +208,16 @@ namespace Content.IntegrationTests.Tests
|
|||
var server = pair.Server;
|
||||
|
||||
var resourceManager = server.ResolveDependency<IResourceManager>();
|
||||
var protoManager = server.ResolveDependency<IPrototypeManager>();
|
||||
var loader = server.System<MapLoaderSystem>();
|
||||
|
||||
var mapFolder = new ResPath("/Maps");
|
||||
var maps = resourceManager
|
||||
.ContentFindFiles(mapFolder)
|
||||
.Where(filePath => filePath.Extension == "yml" && !filePath.Filename.StartsWith(".", StringComparison.Ordinal))
|
||||
.ToArray();
|
||||
|
||||
var v7Maps = new List<ResPath>();
|
||||
foreach (var map in maps)
|
||||
{
|
||||
var rootedPath = map.ToRootedPath();
|
||||
|
|
@ -185,13 +240,101 @@ namespace Content.IntegrationTests.Tests
|
|||
|
||||
var root = yamlStream.Documents[0].RootNode;
|
||||
var meta = root["meta"];
|
||||
var postMapInit = meta["postmapinit"].AsBool();
|
||||
var version = meta["format"].AsInt();
|
||||
|
||||
// TODO MAP TESTS
|
||||
// Move this to some separate test?
|
||||
CheckDoNotMap(map, root, protoManager);
|
||||
|
||||
if (version >= 7)
|
||||
{
|
||||
v7Maps.Add(map);
|
||||
continue;
|
||||
}
|
||||
|
||||
var postMapInit = meta["postmapinit"].AsBool();
|
||||
Assert.That(postMapInit, Is.False, $"Map {map.Filename} was saved postmapinit");
|
||||
}
|
||||
|
||||
var deps = server.ResolveDependency<IEntitySystemManager>().DependencyCollection;
|
||||
foreach (var map in v7Maps)
|
||||
{
|
||||
Assert.That(IsPreInit(map, loader, deps));
|
||||
}
|
||||
|
||||
// Check that the test actually does manage to catch post-init maps and isn't just blindly passing everything.
|
||||
// To that end, create a new post-init map and try verify it.
|
||||
var mapSys = server.System<SharedMapSystem>();
|
||||
MapId id = default;
|
||||
await server.WaitPost(() => mapSys.CreateMap(out id, runMapInit: false));
|
||||
await server.WaitPost(() => server.EntMan.Spawn(null, new MapCoordinates(0, 0, id)));
|
||||
|
||||
// First check that a pre-init version passes
|
||||
var path = new ResPath($"{nameof(NoSavedPostMapInitTest)}.yml");
|
||||
Assert.That(loader.TrySaveMap(id, path));
|
||||
Assert.That(IsPreInit(path, loader, deps));
|
||||
|
||||
// and the post-init version fails.
|
||||
await server.WaitPost(() => mapSys.InitializeMap(id));
|
||||
Assert.That(loader.TrySaveMap(id, path));
|
||||
Assert.That(IsPreInit(path, loader, deps), Is.False);
|
||||
|
||||
await pair.CleanReturnAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check that maps do not have any entities that belong to the DoNotMap entity category
|
||||
/// </summary>
|
||||
private void CheckDoNotMap(ResPath map, YamlNode node, IPrototypeManager protoManager)
|
||||
{
|
||||
if (DoNotMapWhitelist.Contains(map.ToString()))
|
||||
return;
|
||||
|
||||
var yamlEntities = node["entities"];
|
||||
if (!protoManager.TryIndex<EntityCategoryPrototype>("DoNotMap", out var dnmCategory))
|
||||
return;
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
foreach (var yamlEntity in (YamlSequenceNode)yamlEntities)
|
||||
{
|
||||
var protoId = yamlEntity["proto"].AsString();
|
||||
|
||||
// This doesn't properly handle prototype migrations, but thats not a significant issue.
|
||||
if (!protoManager.TryIndex(protoId, out var proto, false))
|
||||
continue;
|
||||
|
||||
Assert.That(!proto.Categories.Contains(dnmCategory),
|
||||
$"\nMap {map} contains entities in the DO NOT MAP category ({proto.Name})");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private bool IsPreInit(ResPath map, MapLoaderSystem loader, IDependencyCollection deps)
|
||||
{
|
||||
if (!loader.TryReadFile(map, out var data))
|
||||
{
|
||||
Assert.Fail($"Failed to read {map}");
|
||||
return false;
|
||||
}
|
||||
|
||||
var reader = new EntityDeserializer(deps, data, DeserializationOptions.Default);
|
||||
if (!reader.TryProcessData())
|
||||
{
|
||||
Assert.Fail($"Failed to process {map}");
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (var mapId in reader.MapYamlIds)
|
||||
{
|
||||
var mapData = reader.YamlEntities[mapId];
|
||||
if (mapData.PostInit)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
[Test, TestCaseSource(nameof(TotalMaps))] // Sunrise-Edit
|
||||
public async Task GameMapsLoadableTest(string mapProto)
|
||||
{
|
||||
|
|
@ -208,16 +351,16 @@ namespace Content.IntegrationTests.Tests
|
|||
var protoManager = server.ResolveDependency<IPrototypeManager>();
|
||||
var ticker = entManager.EntitySysManager.GetEntitySystem<GameTicker>();
|
||||
var shuttleSystem = entManager.EntitySysManager.GetEntitySystem<ShuttleSystem>();
|
||||
var xformQuery = entManager.GetEntityQuery<TransformComponent>();
|
||||
var cfg = server.ResolveDependency<IConfigurationManager>();
|
||||
Assert.That(cfg.GetCVar(CCVars.GridFill), Is.False);
|
||||
|
||||
await server.WaitPost(() =>
|
||||
{
|
||||
mapSystem.CreateMap(out var mapId);
|
||||
MapId mapId;
|
||||
try
|
||||
{
|
||||
ticker.LoadGameMap(protoManager.Index<GameMapPrototype>(mapProto), mapId, null);
|
||||
var opts = DeserializationOptions.Default with {InitializeMaps = true};
|
||||
ticker.LoadGameMap(protoManager.Index<GameMapPrototype>(mapProto), out mapId, opts);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
|
@ -254,21 +397,17 @@ namespace Content.IntegrationTests.Tests
|
|||
if (entManager.TryGetComponent<StationEmergencyShuttleComponent>(station, out var stationEvac))
|
||||
{
|
||||
var shuttlePath = stationEvac.EmergencyShuttlePath;
|
||||
#pragma warning disable NUnit2045
|
||||
Assert.That(mapLoader.TryLoad(shuttleMap, shuttlePath.ToString(), out var roots));
|
||||
EntityUid shuttle = default!;
|
||||
Assert.DoesNotThrow(() =>
|
||||
{
|
||||
shuttle = roots.First(uid => entManager.HasComponent<MapGridComponent>(uid));
|
||||
}, $"Failed to load {shuttlePath}");
|
||||
Assert.That(mapLoader.TryLoadGrid(shuttleMap, shuttlePath, out var shuttle),
|
||||
$"Failed to load {shuttlePath}");
|
||||
|
||||
Assert.That(
|
||||
shuttleSystem.TryFTLDock(shuttle,
|
||||
entManager.GetComponent<ShuttleComponent>(shuttle), targetGrid.Value),
|
||||
shuttleSystem.TryFTLDock(shuttle!.Value.Owner,
|
||||
entManager.GetComponent<ShuttleComponent>(shuttle!.Value.Owner),
|
||||
targetGrid.Value),
|
||||
$"Unable to dock {shuttlePath} to {mapProto}");
|
||||
#pragma warning restore NUnit2045
|
||||
}
|
||||
|
||||
mapManager.DeleteMap(shuttleMap);
|
||||
mapSystem.DeleteMap(shuttleMap);
|
||||
|
||||
if (entManager.HasComponent<StationJobsComponent>(station))
|
||||
{
|
||||
|
|
@ -306,7 +445,7 @@ namespace Content.IntegrationTests.Tests
|
|||
|
||||
try
|
||||
{
|
||||
mapManager.DeleteMap(mapId);
|
||||
mapSystem.DeleteMap(mapId);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
|
@ -370,11 +509,9 @@ namespace Content.IntegrationTests.Tests
|
|||
var server = pair.Server;
|
||||
|
||||
var mapLoader = server.ResolveDependency<IEntitySystemManager>().GetEntitySystem<MapLoaderSystem>();
|
||||
var mapManager = server.ResolveDependency<IMapManager>();
|
||||
var resourceManager = server.ResolveDependency<IResourceManager>();
|
||||
var protoManager = server.ResolveDependency<IPrototypeManager>();
|
||||
var cfg = server.ResolveDependency<IConfigurationManager>();
|
||||
var mapSystem = server.System<SharedMapSystem>();
|
||||
Assert.That(cfg.GetCVar(CCVars.GridFill), Is.False);
|
||||
|
||||
var gameMaps = protoManager.EnumeratePrototypes<GameMapPrototype>().Select(o => o.MapPath).ToHashSet();
|
||||
|
|
@ -385,7 +522,7 @@ namespace Content.IntegrationTests.Tests
|
|||
.Where(filePath => filePath.Extension == "yml" && !filePath.Filename.StartsWith(".", StringComparison.Ordinal))
|
||||
.ToArray();
|
||||
|
||||
var mapNames = new List<string>();
|
||||
var mapPaths = new List<ResPath>();
|
||||
foreach (var map in maps)
|
||||
{
|
||||
if (gameMaps.Contains(map))
|
||||
|
|
@ -396,32 +533,46 @@ namespace Content.IntegrationTests.Tests
|
|||
{
|
||||
continue;
|
||||
}
|
||||
mapNames.Add(rootedPath.ToString());
|
||||
mapPaths.Add(rootedPath);
|
||||
}
|
||||
|
||||
await server.WaitPost(() =>
|
||||
{
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
foreach (var mapName in mapNames)
|
||||
// This bunch of files contains a random mixture of both map and grid files.
|
||||
// TODO MAPPING organize files
|
||||
var opts = MapLoadOptions.Default with
|
||||
{
|
||||
DeserializationOptions = DeserializationOptions.Default with
|
||||
{
|
||||
InitializeMaps = true,
|
||||
LogOrphanedGrids = false
|
||||
}
|
||||
};
|
||||
|
||||
HashSet<Entity<MapComponent>> maps;
|
||||
foreach (var path in mapPaths)
|
||||
{
|
||||
mapSystem.CreateMap(out var mapId);
|
||||
try
|
||||
{
|
||||
Assert.That(mapLoader.TryLoad(mapId, mapName, out _));
|
||||
Assert.That(mapLoader.TryLoadGeneric(path, out maps, out _, opts));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"Failed to load map {mapName}", ex);
|
||||
throw new Exception($"Failed to load map {path}", ex);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
mapManager.DeleteMap(mapId);
|
||||
foreach (var map in maps)
|
||||
{
|
||||
server.EntMan.DeleteEntity(map);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"Failed to delete map {mapName}", ex);
|
||||
throw new Exception($"Failed to delete map {path}", ex);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,11 +1,8 @@
|
|||
using System.Linq;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Salvage;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.EntitySerialization.Systems;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.IntegrationTests.Tests;
|
||||
|
|
@ -24,7 +21,6 @@ public sealed class SalvageTest
|
|||
|
||||
var entManager = server.ResolveDependency<IEntityManager>();
|
||||
var mapLoader = entManager.System<MapLoaderSystem>();
|
||||
var mapManager = server.ResolveDependency<IMapManager>();
|
||||
var prototypeManager = server.ResolveDependency<IPrototypeManager>();
|
||||
var cfg = server.ResolveDependency<IConfigurationManager>();
|
||||
var mapSystem = entManager.System<SharedMapSystem>();
|
||||
|
|
@ -34,13 +30,10 @@ public sealed class SalvageTest
|
|||
{
|
||||
foreach (var salvage in prototypeManager.EnumeratePrototypes<SalvageMapPrototype>())
|
||||
{
|
||||
var mapFile = salvage.MapPath;
|
||||
|
||||
mapSystem.CreateMap(out var mapId);
|
||||
try
|
||||
{
|
||||
Assert.That(mapLoader.TryLoad(mapId, mapFile.ToString(), out var roots));
|
||||
Assert.That(roots.Where(uid => entManager.HasComponent<MapGridComponent>(uid)), Is.Not.Empty);
|
||||
Assert.That(mapLoader.TryLoadGrid(mapId, salvage.MapPath, out var grid));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
|
@ -49,7 +42,7 @@ public sealed class SalvageTest
|
|||
|
||||
try
|
||||
{
|
||||
mapManager.DeleteMap(mapId);
|
||||
mapSystem.DeleteMap(mapId);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ using Content.Shared.CCVar;
|
|||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.EntitySerialization.Systems;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
|
|
@ -16,7 +17,7 @@ namespace Content.IntegrationTests.Tests
|
|||
[Test]
|
||||
public async Task SaveLoadMultiGridMap()
|
||||
{
|
||||
const string mapPath = @"/Maps/Test/TestMap.yml";
|
||||
var mapPath = new ResPath("/Maps/Test/TestMap.yml");
|
||||
|
||||
await using var pair = await PoolManager.GetServerClient();
|
||||
var server = pair.Server;
|
||||
|
|
@ -31,7 +32,7 @@ namespace Content.IntegrationTests.Tests
|
|||
|
||||
await server.WaitAssertion(() =>
|
||||
{
|
||||
var dir = new ResPath(mapPath).Directory;
|
||||
var dir = mapPath.Directory;
|
||||
resManager.UserData.CreateDir(dir);
|
||||
|
||||
mapSystem.CreateMap(out var mapId);
|
||||
|
|
@ -39,23 +40,25 @@ namespace Content.IntegrationTests.Tests
|
|||
{
|
||||
var mapGrid = mapManager.CreateGridEntity(mapId);
|
||||
xformSystem.SetWorldPosition(mapGrid, new Vector2(10, 10));
|
||||
mapSystem.SetTile(mapGrid, new Vector2i(0, 0), new Tile(1, (TileRenderFlag) 1, 255));
|
||||
mapSystem.SetTile(mapGrid, new Vector2i(0, 0), new Tile(typeId: 1, flags: 1, variant: 255));
|
||||
}
|
||||
{
|
||||
var mapGrid = mapManager.CreateGridEntity(mapId);
|
||||
xformSystem.SetWorldPosition(mapGrid, new Vector2(-8, -8));
|
||||
mapSystem.SetTile(mapGrid, new Vector2i(0, 0), new Tile(2, (TileRenderFlag) 1, 254));
|
||||
mapSystem.SetTile(mapGrid, new Vector2i(0, 0), new Tile(typeId: 2, flags: 1, variant: 254));
|
||||
}
|
||||
|
||||
Assert.Multiple(() => mapLoader.SaveMap(mapId, mapPath));
|
||||
Assert.Multiple(() => mapManager.DeleteMap(mapId));
|
||||
Assert.That(mapLoader.TrySaveMap(mapId, mapPath));
|
||||
mapSystem.DeleteMap(mapId);
|
||||
});
|
||||
|
||||
await server.WaitIdleAsync();
|
||||
|
||||
MapId newMap = default;
|
||||
await server.WaitAssertion(() =>
|
||||
{
|
||||
Assert.That(mapLoader.TryLoad(new MapId(10), mapPath, out _));
|
||||
Assert.That(mapLoader.TryLoadMap(mapPath, out var map, out _));
|
||||
newMap = map!.Value.Comp.MapId;
|
||||
});
|
||||
|
||||
await server.WaitIdleAsync();
|
||||
|
|
@ -63,7 +66,7 @@ namespace Content.IntegrationTests.Tests
|
|||
await server.WaitAssertion(() =>
|
||||
{
|
||||
{
|
||||
if (!mapManager.TryFindGridAt(new MapId(10), new Vector2(10, 10), out var gridUid, out var mapGrid) ||
|
||||
if (!mapManager.TryFindGridAt(newMap, new Vector2(10, 10), out var gridUid, out var mapGrid) ||
|
||||
!sEntities.TryGetComponent<TransformComponent>(gridUid, out var gridXform))
|
||||
{
|
||||
Assert.Fail();
|
||||
|
|
@ -73,11 +76,11 @@ namespace Content.IntegrationTests.Tests
|
|||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(xformSystem.GetWorldPosition(gridXform), Is.EqualTo(new Vector2(10, 10)));
|
||||
Assert.That(mapSystem.GetTileRef(gridUid, mapGrid, new Vector2i(0, 0)).Tile, Is.EqualTo(new Tile(1, (TileRenderFlag) 1, 255)));
|
||||
Assert.That(mapSystem.GetTileRef(gridUid, mapGrid, new Vector2i(0, 0)).Tile, Is.EqualTo(new Tile(typeId: 1, flags: 1, variant: 255)));
|
||||
});
|
||||
}
|
||||
{
|
||||
if (!mapManager.TryFindGridAt(new MapId(10), new Vector2(-8, -8), out var gridUid, out var mapGrid) ||
|
||||
if (!mapManager.TryFindGridAt(newMap, new Vector2(-8, -8), out var gridUid, out var mapGrid) ||
|
||||
!sEntities.TryGetComponent<TransformComponent>(gridUid, out var gridXform))
|
||||
{
|
||||
Assert.Fail();
|
||||
|
|
@ -87,7 +90,7 @@ namespace Content.IntegrationTests.Tests
|
|||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(xformSystem.GetWorldPosition(gridXform), Is.EqualTo(new Vector2(-8, -8)));
|
||||
Assert.That(mapSystem.GetTileRef(gridUid, mapGrid, new Vector2i(0, 0)).Tile, Is.EqualTo(new Tile(2, (TileRenderFlag) 1, 254)));
|
||||
Assert.That(mapSystem.GetTileRef(gridUid, mapGrid, new Vector2i(0, 0)).Tile, Is.EqualTo(new Tile(typeId: 2, flags: 1, variant: 254)));
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,25 +1,25 @@
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using Content.Shared.CCVar;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.Maps;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.EntitySerialization.Systems;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Map.Events;
|
||||
using Robust.Shared.Serialization.Markdown.Mapping;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.IntegrationTests.Tests
|
||||
{
|
||||
/// <summary>
|
||||
/// Tests that a map's yaml does not change when saved consecutively.
|
||||
/// Tests that a grid's yaml does not change when saved consecutively.
|
||||
/// </summary>
|
||||
[TestFixture]
|
||||
public sealed class SaveLoadSaveTest
|
||||
{
|
||||
[Test]
|
||||
public async Task SaveLoadSave()
|
||||
public async Task CreateSaveLoadSaveGrid()
|
||||
{
|
||||
await using var pair = await PoolManager.GetServerClient();
|
||||
var server = pair.Server;
|
||||
|
|
@ -30,22 +30,21 @@ namespace Content.IntegrationTests.Tests
|
|||
var cfg = server.ResolveDependency<IConfigurationManager>();
|
||||
Assert.That(cfg.GetCVar(CCVars.GridFill), Is.False);
|
||||
|
||||
var testSystem = server.System<SaveLoadSaveTestSystem>();
|
||||
testSystem.Enabled = true;
|
||||
|
||||
var rp1 = new ResPath("/save load save 1.yml");
|
||||
var rp2 = new ResPath("/save load save 2.yml");
|
||||
|
||||
await server.WaitPost(() =>
|
||||
{
|
||||
mapSystem.CreateMap(out var mapId0);
|
||||
// TODO: Properly find the "main" station grid.
|
||||
var grid0 = mapManager.CreateGridEntity(mapId0);
|
||||
mapLoader.Save(grid0.Owner, "save load save 1.yml");
|
||||
entManager.RunMapInit(grid0.Owner, entManager.GetComponent<MetaDataComponent>(grid0));
|
||||
Assert.That(mapLoader.TrySaveGrid(grid0.Owner, rp1));
|
||||
mapSystem.CreateMap(out var mapId1);
|
||||
EntityUid grid1 = default!;
|
||||
#pragma warning disable NUnit2045
|
||||
Assert.That(mapLoader.TryLoad(mapId1, "save load save 1.yml", out var roots, new MapLoadOptions() { LoadMap = false }), $"Failed to load test map {TestMap}");
|
||||
Assert.DoesNotThrow(() =>
|
||||
{
|
||||
grid1 = roots.First(uid => entManager.HasComponent<MapGridComponent>(uid));
|
||||
});
|
||||
#pragma warning restore NUnit2045
|
||||
mapLoader.Save(grid1, "save load save 2.yml");
|
||||
Assert.That(mapLoader.TryLoadGrid(mapId1, rp1, out var grid1));
|
||||
Assert.That(mapLoader.TrySaveGrid(grid1!.Value, rp2));
|
||||
});
|
||||
|
||||
await server.WaitIdleAsync();
|
||||
|
|
@ -54,14 +53,12 @@ namespace Content.IntegrationTests.Tests
|
|||
string one;
|
||||
string two;
|
||||
|
||||
var rp1 = new ResPath("/save load save 1.yml");
|
||||
await using (var stream = userData.Open(rp1, FileMode.Open))
|
||||
using (var reader = new StreamReader(stream))
|
||||
{
|
||||
one = await reader.ReadToEndAsync();
|
||||
}
|
||||
|
||||
var rp2 = new ResPath("/save load save 2.yml");
|
||||
await using (var stream = userData.Open(rp2, FileMode.Open))
|
||||
using (var reader = new StreamReader(stream))
|
||||
{
|
||||
|
|
@ -87,6 +84,7 @@ namespace Content.IntegrationTests.Tests
|
|||
TestContext.Error.WriteLine(twoTmp);
|
||||
}
|
||||
});
|
||||
testSystem.Enabled = false;
|
||||
await pair.CleanReturnAsync();
|
||||
}
|
||||
|
||||
|
|
@ -101,8 +99,12 @@ namespace Content.IntegrationTests.Tests
|
|||
await using var pair = await PoolManager.GetServerClient();
|
||||
var server = pair.Server;
|
||||
var mapLoader = server.ResolveDependency<IEntitySystemManager>().GetEntitySystem<MapLoaderSystem>();
|
||||
var mapManager = server.ResolveDependency<IMapManager>();
|
||||
var mapSystem = server.System<SharedMapSystem>();
|
||||
var mapSys = server.System<SharedMapSystem>();
|
||||
var testSystem = server.System<SaveLoadSaveTestSystem>();
|
||||
testSystem.Enabled = true;
|
||||
|
||||
var rp1 = new ResPath("/load save ticks save 1.yml");
|
||||
var rp2 = new ResPath("/load save ticks save 2.yml");
|
||||
|
||||
MapId mapId = default;
|
||||
var cfg = server.ResolveDependency<IConfigurationManager>();
|
||||
|
|
@ -111,10 +113,10 @@ namespace Content.IntegrationTests.Tests
|
|||
// Load bagel.yml as uninitialized map, and save it to ensure it's up to date.
|
||||
server.Post(() =>
|
||||
{
|
||||
mapSystem.CreateMap(out mapId, runMapInit: false);
|
||||
mapManager.SetMapPaused(mapId, true);
|
||||
Assert.That(mapLoader.TryLoad(mapId, TestMap, out _), $"Failed to load test map {TestMap}");
|
||||
mapLoader.SaveMap(mapId, "load save ticks save 1.yml");
|
||||
var path = new ResPath(TestMap);
|
||||
Assert.That(mapLoader.TryLoadMap(path, out var map, out _), $"Failed to load test map {TestMap}");
|
||||
mapId = map!.Value.Comp.MapId;
|
||||
Assert.That(mapLoader.TrySaveMap(mapId, rp1));
|
||||
});
|
||||
|
||||
// Run 5 ticks.
|
||||
|
|
@ -122,7 +124,7 @@ namespace Content.IntegrationTests.Tests
|
|||
|
||||
await server.WaitPost(() =>
|
||||
{
|
||||
mapLoader.SaveMap(mapId, "/load save ticks save 2.yml");
|
||||
Assert.That(mapLoader.TrySaveMap(mapId, rp2));
|
||||
});
|
||||
|
||||
await server.WaitIdleAsync();
|
||||
|
|
@ -131,13 +133,13 @@ namespace Content.IntegrationTests.Tests
|
|||
string one;
|
||||
string two;
|
||||
|
||||
await using (var stream = userData.Open(new ResPath("/load save ticks save 1.yml"), FileMode.Open))
|
||||
await using (var stream = userData.Open(rp1, FileMode.Open))
|
||||
using (var reader = new StreamReader(stream))
|
||||
{
|
||||
one = await reader.ReadToEndAsync();
|
||||
}
|
||||
|
||||
await using (var stream = userData.Open(new ResPath("/load save ticks save 2.yml"), FileMode.Open))
|
||||
await using (var stream = userData.Open(rp2, FileMode.Open))
|
||||
using (var reader = new StreamReader(stream))
|
||||
{
|
||||
two = await reader.ReadToEndAsync();
|
||||
|
|
@ -163,7 +165,8 @@ namespace Content.IntegrationTests.Tests
|
|||
}
|
||||
});
|
||||
|
||||
await server.WaitPost(() => mapManager.DeleteMap(mapId));
|
||||
testSystem.Enabled = false;
|
||||
await server.WaitPost(() => mapSys.DeleteMap(mapId));
|
||||
await pair.CleanReturnAsync();
|
||||
}
|
||||
|
||||
|
|
@ -184,29 +187,31 @@ namespace Content.IntegrationTests.Tests
|
|||
var server = pair.Server;
|
||||
|
||||
var mapLoader = server.System<MapLoaderSystem>();
|
||||
var mapSystem = server.System<SharedMapSystem>();
|
||||
var mapManager = server.ResolveDependency<IMapManager>();
|
||||
var mapSys = server.System<SharedMapSystem>();
|
||||
var userData = server.ResolveDependency<IResourceManager>().UserData;
|
||||
var cfg = server.ResolveDependency<IConfigurationManager>();
|
||||
Assert.That(cfg.GetCVar(CCVars.GridFill), Is.False);
|
||||
var testSystem = server.System<SaveLoadSaveTestSystem>();
|
||||
testSystem.Enabled = true;
|
||||
|
||||
MapId mapId = default;
|
||||
const string fileA = "/load tick load a.yml";
|
||||
const string fileB = "/load tick load b.yml";
|
||||
MapId mapId1 = default;
|
||||
MapId mapId2 = default;
|
||||
var fileA = new ResPath("/load tick load a.yml");
|
||||
var fileB = new ResPath("/load tick load b.yml");
|
||||
string yamlA;
|
||||
string yamlB;
|
||||
|
||||
// Load & save the first map
|
||||
server.Post(() =>
|
||||
{
|
||||
mapSystem.CreateMap(out mapId, runMapInit: false);
|
||||
mapManager.SetMapPaused(mapId, true);
|
||||
Assert.That(mapLoader.TryLoad(mapId, TestMap, out _), $"Failed to load test map {TestMap}");
|
||||
mapLoader.SaveMap(mapId, fileA);
|
||||
var path = new ResPath(TestMap);
|
||||
Assert.That(mapLoader.TryLoadMap(path, out var map, out _), $"Failed to load test map {TestMap}");
|
||||
mapId1 = map!.Value.Comp.MapId;
|
||||
Assert.That(mapLoader.TrySaveMap(mapId1, fileA));
|
||||
});
|
||||
|
||||
await server.WaitIdleAsync();
|
||||
await using (var stream = userData.Open(new ResPath(fileA), FileMode.Open))
|
||||
await using (var stream = userData.Open(fileA, FileMode.Open))
|
||||
using (var reader = new StreamReader(stream))
|
||||
{
|
||||
yamlA = await reader.ReadToEndAsync();
|
||||
|
|
@ -217,16 +222,15 @@ namespace Content.IntegrationTests.Tests
|
|||
// Load & save the second map
|
||||
server.Post(() =>
|
||||
{
|
||||
mapManager.DeleteMap(mapId);
|
||||
mapSystem.CreateMap(out mapId, runMapInit: false);
|
||||
mapManager.SetMapPaused(mapId, true);
|
||||
Assert.That(mapLoader.TryLoad(mapId, TestMap, out _), $"Failed to load test map {TestMap}");
|
||||
mapLoader.SaveMap(mapId, fileB);
|
||||
var path = new ResPath(TestMap);
|
||||
Assert.That(mapLoader.TryLoadMap(path, out var map, out _), $"Failed to load test map {TestMap}");
|
||||
mapId2 = map!.Value.Comp.MapId;
|
||||
Assert.That(mapLoader.TrySaveMap(mapId2, fileB));
|
||||
});
|
||||
|
||||
await server.WaitIdleAsync();
|
||||
|
||||
await using (var stream = userData.Open(new ResPath(fileB), FileMode.Open))
|
||||
await using (var stream = userData.Open(fileB, FileMode.Open))
|
||||
using (var reader = new StreamReader(stream))
|
||||
{
|
||||
yamlB = await reader.ReadToEndAsync();
|
||||
|
|
@ -234,8 +238,32 @@ namespace Content.IntegrationTests.Tests
|
|||
|
||||
Assert.That(yamlA, Is.EqualTo(yamlB));
|
||||
|
||||
await server.WaitPost(() => mapManager.DeleteMap(mapId));
|
||||
testSystem.Enabled = false;
|
||||
await server.WaitPost(() => mapSys.DeleteMap(mapId1));
|
||||
await server.WaitPost(() => mapSys.DeleteMap(mapId2));
|
||||
await pair.CleanReturnAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Simple system that modifies the data saved to a yaml file by removing the timestamp.
|
||||
/// Required by some tests that validate that re-saving a map does not modify it.
|
||||
/// </summary>
|
||||
private sealed class SaveLoadSaveTestSystem : EntitySystem
|
||||
{
|
||||
public bool Enabled;
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<AfterSerializationEvent>(OnAfterSave);
|
||||
}
|
||||
|
||||
private void OnAfterSave(AfterSerializationEvent ev)
|
||||
{
|
||||
if (!Enabled)
|
||||
return;
|
||||
|
||||
// Remove timestamp.
|
||||
((MappingDataNode)ev.Node["meta"]).Remove("time");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,10 +4,12 @@ using System.Numerics;
|
|||
using Content.Server.Shuttles.Systems;
|
||||
using Content.Tests;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.EntitySerialization.Systems;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.IntegrationTests.Tests.Shuttle;
|
||||
|
||||
|
|
@ -106,8 +108,9 @@ public sealed class DockTest : ContentUnitTest
|
|||
{
|
||||
mapGrid = entManager.AddComponent<MapGridComponent>(map.MapUid);
|
||||
entManager.DeleteEntity(map.Grid);
|
||||
Assert.That(entManager.System<MapLoaderSystem>().TryLoad(otherMap.MapId, "/Maps/Shuttles/emergency.yml", out var rootUids));
|
||||
shuttle = rootUids[0];
|
||||
var path = new ResPath("/Maps/Shuttles/emergency.yml");
|
||||
Assert.That(entManager.System<MapLoaderSystem>().TryLoadGrid(otherMap.MapId, path, out var grid));
|
||||
shuttle = grid!.Value.Owner;
|
||||
|
||||
var dockingConfig = dockingSystem.GetDockingConfig(shuttle, map.MapUid);
|
||||
Assert.That(dockingConfig, Is.EqualTo(null));
|
||||
|
|
|
|||
215
Content.Server/Administration/Commands/ChangeCvarCommand.cs
Normal file
215
Content.Server/Administration/Commands/ChangeCvarCommand.cs
Normal file
|
|
@ -0,0 +1,215 @@
|
|||
using System.Linq;
|
||||
using Content.Server.Administration.Logs;
|
||||
using Content.Server.Administration.Managers;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.Database;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Console;
|
||||
|
||||
namespace Content.Server.Administration.Commands;
|
||||
|
||||
/// <summary>
|
||||
/// Allows admins to change certain CVars. This is different than the "cvar" command which is host only and can change any CVar.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Possible todo for future, store default values for cvars, and allow resetting to default.
|
||||
/// </remarks>
|
||||
[AnyCommand]
|
||||
public sealed class ChangeCvarCommand : IConsoleCommand
|
||||
{
|
||||
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
|
||||
[Dependency] private readonly IAdminLogManager _adminLogManager = default!;
|
||||
[Dependency] private readonly CVarControlManager _cVarControlManager = default!;
|
||||
|
||||
/// <summary>
|
||||
/// Searches the list of cvars for a cvar that matches the search string.
|
||||
/// </summary>
|
||||
private void SearchCVars(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
if (args.Length < 2)
|
||||
{
|
||||
shell.WriteLine(Loc.GetString("cmd-changecvar-search-no-arguments"));
|
||||
return;
|
||||
}
|
||||
|
||||
var cvars = _cVarControlManager.GetAllRunnableCvars(shell);
|
||||
|
||||
var matches = cvars
|
||||
.Where(c =>
|
||||
c.Name.Contains(args[1], StringComparison.OrdinalIgnoreCase)
|
||||
|| c.ShortHelp?.Contains(args[1], StringComparison.OrdinalIgnoreCase) == true
|
||||
|| c.LongHelp?.Contains(args[1], StringComparison.OrdinalIgnoreCase) == true
|
||||
) // Might be very slow and stupid, but eh.
|
||||
.ToList();
|
||||
|
||||
if (matches.Count == 0)
|
||||
{
|
||||
shell.WriteLine(Loc.GetString("cmd-changecvar-search-no-matches"));
|
||||
return;
|
||||
}
|
||||
|
||||
shell.WriteLine(Loc.GetString("cmd-changecvar-search-matches", ("count", matches.Count)));
|
||||
shell.WriteLine(string.Join("\n", matches.Select(FormatCVarFullHelp)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Formats a CVar into a string for display.
|
||||
/// </summary>
|
||||
private string FormatCVarFullHelp(ChangableCVar cvar)
|
||||
{
|
||||
if (cvar.LongHelp != null && cvar.ShortHelp != null)
|
||||
{
|
||||
return $"{cvar.Name} - {cvar.LongHelp}";
|
||||
}
|
||||
|
||||
// There is no help, no one is coming. We are all doomed.
|
||||
return cvar.Name;
|
||||
}
|
||||
|
||||
public string Command => "changecvar";
|
||||
public string Description { get; } = Loc.GetString("cmd-changecvar-desc");
|
||||
public string Help { get; } = Loc.GetString("cmd-changecvar-help");
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
if (args.Length == 0)
|
||||
{
|
||||
shell.WriteLine(Loc.GetString("cmd-changecvar-no-arguments"));
|
||||
return;
|
||||
}
|
||||
|
||||
var cvars = _cVarControlManager.GetAllRunnableCvars(shell);
|
||||
|
||||
var cvar = args[0];
|
||||
if (cvar == "?")
|
||||
{
|
||||
if (cvars.Count == 0)
|
||||
{
|
||||
shell.WriteLine(Loc.GetString("cmd-changecvar-no-cvars"));
|
||||
return;
|
||||
}
|
||||
|
||||
shell.WriteLine(Loc.GetString("cmd-changecvar-available-cvars"));
|
||||
shell.WriteLine(string.Join("\n", cvars.Select(FormatCVarFullHelp)));
|
||||
return;
|
||||
}
|
||||
|
||||
if (cvar == "search")
|
||||
{
|
||||
SearchCVars(shell, argStr, args);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_configurationManager.IsCVarRegistered(cvar)) // Might be a redunat check with the if statement below.
|
||||
{
|
||||
shell.WriteLine(Loc.GetString("cmd-changecvar-cvar-not-registered", ("cvar", cvar)));
|
||||
return;
|
||||
}
|
||||
|
||||
if (cvars.All(c => c.Name != cvar))
|
||||
{
|
||||
shell.WriteLine(Loc.GetString("cmd-changecvar-cvar-not-allowed"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.Length == 1)
|
||||
{
|
||||
var value = _configurationManager.GetCVar<object>(cvar);
|
||||
shell.WriteLine(value.ToString()!);
|
||||
}
|
||||
else
|
||||
{
|
||||
var value = args[1];
|
||||
var type = _configurationManager.GetCVarType(cvar);
|
||||
try
|
||||
{
|
||||
var parsed = CVarCommandUtil.ParseObject(type, value);
|
||||
// Value check, is it in the min/max range?
|
||||
var control = _cVarControlManager.GetCVar(cvar)!.Control; // Null check is done above.
|
||||
var allowed = true;
|
||||
if (control is { Min: not null, Max: not null })
|
||||
{
|
||||
switch (parsed) // This looks bad, and im not sorry.
|
||||
{
|
||||
case int intVal:
|
||||
{
|
||||
if (intVal < (int)control.Min || intVal > (int)control.Max)
|
||||
{
|
||||
allowed = false;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case float floatVal:
|
||||
{
|
||||
if (floatVal < (float)control.Min || floatVal > (float)control.Max)
|
||||
{
|
||||
allowed = false;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case long longVal:
|
||||
{
|
||||
if (longVal < (long)control.Min || longVal > (long)control.Max)
|
||||
{
|
||||
allowed = false;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case ushort ushortVal:
|
||||
{
|
||||
if (ushortVal < (ushort)control.Min || ushortVal > (ushort)control.Max)
|
||||
{
|
||||
allowed = false;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!allowed)
|
||||
{
|
||||
shell.WriteError(Loc.GetString("cmd-changecvar-value-out-of-range",
|
||||
("min", control.Min ?? "-∞"),
|
||||
("max", control.Max ?? "∞")));
|
||||
return;
|
||||
}
|
||||
|
||||
var oldValue = _configurationManager.GetCVar<object>(cvar);
|
||||
_configurationManager.SetCVar(cvar, parsed);
|
||||
_adminLogManager.Add(LogType.AdminCommands,
|
||||
LogImpact.High,
|
||||
$"{shell.Player!.Name} ({shell.Player!.UserId}) changed CVAR {cvar} from {oldValue.ToString()} to {parsed.ToString()}"
|
||||
);
|
||||
|
||||
shell.WriteLine(Loc.GetString("cmd-changecvar-success", ("cvar", cvar), ("old", oldValue), ("value", parsed)));
|
||||
}
|
||||
catch (FormatException)
|
||||
{
|
||||
shell.WriteError(Loc.GetString("cmd-cvar-parse-error", ("type", type)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public CompletionResult GetCompletion(IConsoleShell shell, string[] args)
|
||||
{
|
||||
var cvars = _cVarControlManager.GetAllRunnableCvars(shell);
|
||||
|
||||
if (args.Length == 1)
|
||||
{
|
||||
return CompletionResult.FromHintOptions(
|
||||
cvars
|
||||
.Select(c => new CompletionOption(c.Name, c.ShortHelp ?? c.Name)),
|
||||
Loc.GetString("cmd-changecvar-arg-name"));
|
||||
}
|
||||
|
||||
var cvar = args[0];
|
||||
if (!_configurationManager.IsCVarRegistered(cvar))
|
||||
return CompletionResult.Empty;
|
||||
|
||||
var type = _configurationManager.GetCVarType(cvar);
|
||||
return CompletionResult.FromHint($"<{type.Name}>");
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +1,9 @@
|
|||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Content.Server.GameTicking;
|
||||
using Content.Server.Maps;
|
||||
using Content.Shared.Administration;
|
||||
using Robust.Server.Maps;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.EntitySerialization;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
|
|
@ -25,6 +23,7 @@ namespace Content.Server.Administration.Commands
|
|||
var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
|
||||
var entityManager = IoCManager.Resolve<IEntityManager>();
|
||||
var gameTicker = entityManager.EntitySysManager.GetEntitySystem<GameTicker>();
|
||||
var mapSys = entityManager.EntitySysManager.GetEntitySystem<SharedMapSystem>();
|
||||
|
||||
if (args.Length is not (2 or 4 or 5))
|
||||
{
|
||||
|
|
@ -32,29 +31,28 @@ namespace Content.Server.Administration.Commands
|
|||
return;
|
||||
}
|
||||
|
||||
if (prototypeManager.TryIndex<GameMapPrototype>(args[1], out var gameMap))
|
||||
{
|
||||
if (!int.TryParse(args[0], out var mapId))
|
||||
return;
|
||||
|
||||
var loadOptions = new MapLoadOptions()
|
||||
{
|
||||
LoadMap = false,
|
||||
};
|
||||
|
||||
var stationName = args.Length == 5 ? args[4] : null;
|
||||
|
||||
if (args.Length >= 4 && int.TryParse(args[2], out var x) && int.TryParse(args[3], out var y))
|
||||
{
|
||||
loadOptions.Offset = new Vector2(x, y);
|
||||
}
|
||||
var grids = gameTicker.LoadGameMap(gameMap, new MapId(mapId), loadOptions, stationName);
|
||||
shell.WriteLine($"Loaded {grids.Count} grids.");
|
||||
}
|
||||
else
|
||||
if (!prototypeManager.TryIndex<GameMapPrototype>(args[1], out var gameMap))
|
||||
{
|
||||
shell.WriteError($"The given map prototype {args[0]} is invalid.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!int.TryParse(args[0], out var mapId))
|
||||
return;
|
||||
|
||||
var stationName = args.Length == 5 ? args[4] : null;
|
||||
|
||||
Vector2? offset = null;
|
||||
if (args.Length >= 4)
|
||||
offset = new Vector2(int.Parse(args[2]), int.Parse(args[3]));
|
||||
|
||||
var id = new MapId(mapId);
|
||||
|
||||
var grids = mapSys.MapExists(id)
|
||||
? gameTicker.MergeGameMap(gameMap, id, stationName: stationName, offset: offset)
|
||||
: gameTicker.LoadGameMapWithId(gameMap, id, stationName: stationName, offset: offset);
|
||||
|
||||
shell.WriteLine($"Loaded {grids.Count} grids.");
|
||||
}
|
||||
|
||||
public CompletionResult GetCompletion(IConsoleShell shell, string[] args)
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
using Content.Shared.Administration;
|
||||
using Content.Shared.CCVar;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.Map;
|
||||
using System.Linq;
|
||||
using Robust.Shared.EntitySerialization.Systems;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.Administration.Commands;
|
||||
|
||||
|
|
@ -48,7 +48,7 @@ public sealed class PersistenceSave : IConsoleCommand
|
|||
}
|
||||
|
||||
var mapLoader = _system.GetEntitySystem<MapLoaderSystem>();
|
||||
mapLoader.SaveMap(mapId, saveFilePath);
|
||||
mapLoader.TrySaveMap(mapId, new ResPath(saveFilePath));
|
||||
shell.WriteLine(Loc.GetString("cmd-savemap-success"));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
125
Content.Server/Administration/Managers/CVarControlManager.cs
Normal file
125
Content.Server/Administration/Managers/CVarControlManager.cs
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Content.Shared.CCVar.CVarAccess;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Reflection;
|
||||
|
||||
namespace Content.Server.Administration.Managers;
|
||||
|
||||
/// <summary>
|
||||
/// Manages the control of CVars via the <see cref="Content.Shared.CCVar.CVarAccess.CVarControl"/> attribute.
|
||||
/// </summary>
|
||||
public sealed class CVarControlManager : IPostInjectInit
|
||||
{
|
||||
[Dependency] private readonly IReflectionManager _reflectionManager = default!;
|
||||
[Dependency] private readonly IAdminManager _adminManager = default!;
|
||||
[Dependency] private readonly ILocalizationManager _localizationManager = default!;
|
||||
[Dependency] private readonly ILogManager _logger = default!;
|
||||
|
||||
private readonly List<ChangableCVar> _changableCvars = new();
|
||||
private ISawmill _sawmill = default!;
|
||||
|
||||
void IPostInjectInit.PostInject()
|
||||
{
|
||||
_sawmill = _logger.GetSawmill("cvarcontrol");
|
||||
}
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
RegisterCVars();
|
||||
}
|
||||
|
||||
private void RegisterCVars()
|
||||
{
|
||||
if (_changableCvars.Count != 0)
|
||||
{
|
||||
_sawmill.Warning("CVars already registered, overwriting.");
|
||||
_changableCvars.Clear();
|
||||
}
|
||||
|
||||
var validCvarsDefs = _reflectionManager.FindTypesWithAttribute<CVarDefsAttribute>();
|
||||
|
||||
foreach (var type in validCvarsDefs)
|
||||
{
|
||||
foreach (var field in type.GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy))
|
||||
{
|
||||
var allowed = field.GetCustomAttribute<CVarControl>();
|
||||
if (allowed == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var cvarDef = (CVarDef)field.GetValue(null)!;
|
||||
_changableCvars.Add(new ChangableCVar(cvarDef.Name, allowed, _localizationManager));
|
||||
}
|
||||
}
|
||||
|
||||
_sawmill.Info($"Registered {_changableCvars.Count} CVars.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all CVars that the player can change.
|
||||
/// </summary>
|
||||
public List<ChangableCVar> GetAllRunnableCvars(IConsoleShell shell)
|
||||
{
|
||||
// Not a player, running as server. We COULD return all cvars,
|
||||
// but a check later down the line will prevent it from anyways. Use the "cvar" command instead.
|
||||
if (shell.Player == null)
|
||||
return [];
|
||||
|
||||
return GetAllRunnableCvars(shell.Player);
|
||||
}
|
||||
|
||||
public List<ChangableCVar> GetAllRunnableCvars(ICommonSession session)
|
||||
{
|
||||
var adminData = _adminManager.GetAdminData(session);
|
||||
if (adminData == null)
|
||||
return []; // Not an admin
|
||||
|
||||
return _changableCvars
|
||||
.Where(cvar => adminData.HasFlag(cvar.Control.AdminFlags))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public ChangableCVar? GetCVar(string name)
|
||||
{
|
||||
return _changableCvars.FirstOrDefault(cvar => cvar.Name == name);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class ChangableCVar
|
||||
{
|
||||
private const string LocPrefix = "changecvar";
|
||||
|
||||
public string Name { get; }
|
||||
|
||||
// Holding a reference to the attribute might be skrunkly? Not sure how much mem it eats up.
|
||||
public CVarControl Control { get; }
|
||||
|
||||
public string? ShortHelp;
|
||||
public string? LongHelp;
|
||||
|
||||
public ChangableCVar(string name, CVarControl control, ILocalizationManager loc)
|
||||
{
|
||||
Name = name;
|
||||
Control = control;
|
||||
|
||||
if (loc.TryGetString($"{LocPrefix}-simple-{name.Replace('.', '_')}", out var simple))
|
||||
{
|
||||
ShortHelp = simple;
|
||||
}
|
||||
|
||||
if (loc.TryGetString($"{LocPrefix}-full-{name.Replace('.', '_')}", out var longHelp))
|
||||
{
|
||||
LongHelp = longHelp;
|
||||
}
|
||||
|
||||
// If one is set and the other is not, we throw
|
||||
if (ShortHelp == null && LongHelp != null || ShortHelp != null && LongHelp == null)
|
||||
{
|
||||
throw new InvalidOperationException("Short and long help must both be set or both be null.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,7 @@
|
|||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.EntitySerialization.Systems;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.Administration.Systems;
|
||||
|
||||
|
|
@ -11,8 +10,7 @@ namespace Content.Server.Administration.Systems;
|
|||
/// </summary>
|
||||
public sealed class AdminTestArenaSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] private readonly MapLoaderSystem _map = default!;
|
||||
[Dependency] private readonly MapLoaderSystem _loader = default!;
|
||||
[Dependency] private readonly MetaDataSystem _metaDataSystem = default!;
|
||||
|
||||
public const string ArenaMapPath = "/Maps/Test/admin_test_arena.yml";
|
||||
|
|
@ -28,26 +26,24 @@ public sealed class AdminTestArenaSystem : EntitySystem
|
|||
{
|
||||
return (arenaMap, arenaGrid);
|
||||
}
|
||||
else
|
||||
{
|
||||
ArenaGrid[admin.UserId] = null;
|
||||
return (arenaMap, null);
|
||||
}
|
||||
}
|
||||
|
||||
ArenaMap[admin.UserId] = _mapManager.GetMapEntityId(_mapManager.CreateMap());
|
||||
_metaDataSystem.SetEntityName(ArenaMap[admin.UserId], $"ATAM-{admin.Name}");
|
||||
var grids = _map.LoadMap(Comp<MapComponent>(ArenaMap[admin.UserId]).MapId, ArenaMapPath);
|
||||
if (grids.Count != 0)
|
||||
{
|
||||
_metaDataSystem.SetEntityName(grids[0], $"ATAG-{admin.Name}");
|
||||
ArenaGrid[admin.UserId] = grids[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
ArenaGrid[admin.UserId] = null;
|
||||
return (arenaMap, null);
|
||||
}
|
||||
|
||||
return (ArenaMap[admin.UserId], ArenaGrid[admin.UserId]);
|
||||
var path = new ResPath(ArenaMapPath);
|
||||
if (!_loader.TryLoadMap(path, out var map, out var grids))
|
||||
throw new Exception($"Failed to load admin arena");
|
||||
|
||||
ArenaMap[admin.UserId] = map.Value.Owner;
|
||||
_metaDataSystem.SetEntityName(map.Value.Owner, $"ATAM-{admin.Name}");
|
||||
|
||||
var grid = grids.FirstOrNull();
|
||||
ArenaGrid[admin.UserId] = grid?.Owner;
|
||||
if (grid != null)
|
||||
_metaDataSystem.SetEntityName(grid.Value.Owner, $"ATAG-{admin.Name}");
|
||||
|
||||
return (map.Value.Owner, grid?.Owner);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ namespace Content.Server.Administration.Systems
|
|||
mark.Text = Loc.GetString("toolshed-verb-mark");
|
||||
mark.Message = Loc.GetString("toolshed-verb-mark-description");
|
||||
mark.Category = VerbCategory.Admin;
|
||||
mark.Act = () => _toolshed.InvokeCommand(player, "=> $marked", Enumerable.Repeat(args.Target, 1), out _);
|
||||
mark.Act = () => _toolshed.InvokeCommand(player, "=> $marked", new List<EntityUid> {args.Target}, out _);
|
||||
mark.Impact = LogImpact.Low;
|
||||
args.Verbs.Add(mark);
|
||||
|
||||
|
|
|
|||
|
|
@ -9,8 +9,7 @@ public sealed class MarkedCommand : ToolshedCommand
|
|||
[CommandImplementation]
|
||||
public IEnumerable<EntityUid> Marked(IInvocationContext ctx)
|
||||
{
|
||||
var res = (IEnumerable<EntityUid>?)ctx.ReadVar("marked");
|
||||
res ??= Array.Empty<EntityUid>();
|
||||
return res;
|
||||
var marked = ctx.ReadVar("marked") as IEnumerable<EntityUid>;
|
||||
return marked ?? Array.Empty<EntityUid>();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,11 +19,12 @@ public sealed partial class AntagSelectionSystem
|
|||
/// Tries to get the next non-filled definition based on the current amount of selected minds and other factors.
|
||||
/// </summary>
|
||||
public bool TryGetNextAvailableDefinition(Entity<AntagSelectionComponent> ent,
|
||||
[NotNullWhen(true)] out AntagSelectionDefinition? definition)
|
||||
[NotNullWhen(true)] out AntagSelectionDefinition? definition,
|
||||
int? players = null)
|
||||
{
|
||||
definition = null;
|
||||
|
||||
var totalTargetCount = GetTargetAntagCount(ent);
|
||||
var totalTargetCount = GetTargetAntagCount(ent, players);
|
||||
var mindCount = ent.Comp.SelectedMinds.Count;
|
||||
if (mindCount >= totalTargetCount)
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -160,7 +160,10 @@ public sealed partial class AntagSelectionSystem : GameRuleSystem<AntagSelection
|
|||
|
||||
DebugTools.AssertEqual(antag.SelectionTime, AntagSelectionTime.PostPlayerSpawn);
|
||||
|
||||
if (!TryGetNextAvailableDefinition((uid, antag), out var def))
|
||||
// do not count players in the lobby for the antag ratio
|
||||
var players = _playerManager.NetworkedSessions.Count(x => x.AttachedEntity != null);
|
||||
|
||||
if (!TryGetNextAvailableDefinition((uid, antag), out var def, players))
|
||||
continue;
|
||||
|
||||
// Sunrise-Start
|
||||
|
|
|
|||
|
|
@ -76,14 +76,13 @@ public sealed class GasAnalyzerSystem : EntitySystem
|
|||
/// </summary>
|
||||
private void OnUseInHand(Entity<GasAnalyzerComponent> entity, ref UseInHandEvent args)
|
||||
{
|
||||
// Not checking for Handled because ActivatableUISystem already marks it as such.
|
||||
|
||||
if (!entity.Comp.Enabled)
|
||||
{
|
||||
ActivateAnalyzer(entity, args.User);
|
||||
}
|
||||
else
|
||||
{
|
||||
DisableAnalyzer(entity, args.User);
|
||||
}
|
||||
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ public sealed class BeamSystem : SharedBeamSystem
|
|||
|
||||
_physics.SetBodyType(ent, BodyType.Dynamic, manager: manager, body: physics);
|
||||
_physics.SetCanCollide(ent, true, manager: manager, body: physics);
|
||||
_broadphase.RegenerateContacts(ent, physics, manager);
|
||||
_broadphase.RegenerateContacts((ent, physics, manager));
|
||||
|
||||
var distanceLength = distanceCorrection.Length();
|
||||
|
||||
|
|
|
|||
|
|
@ -216,17 +216,30 @@ public partial class ChatSystem
|
|||
/// <returns></returns>
|
||||
private bool AllowedToUseEmote(EntityUid source, EmotePrototype emote)
|
||||
{
|
||||
if ((_whitelistSystem.IsWhitelistFail(emote.Whitelist, source) || _whitelistSystem.IsBlacklistPass(emote.Blacklist, source)))
|
||||
return false;
|
||||
// If emote is in AllowedEmotes, it will bypass whitelist and blacklist
|
||||
if (TryComp<SpeechComponent>(source, out var speech) &&
|
||||
speech.AllowedEmotes.Contains(emote.ID))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!emote.Available &&
|
||||
TryComp<SpeechComponent>(source, out var speech) &&
|
||||
!speech.AllowedEmotes.Contains(emote.ID))
|
||||
// Check the whitelist and blacklist
|
||||
if (_whitelistSystem.IsWhitelistFail(emote.Whitelist, source) ||
|
||||
_whitelistSystem.IsBlacklistPass(emote.Blacklist, source))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if the emote is available for all
|
||||
if (!emote.Available)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private void InvokeEmoteEvent(EntityUid uid, EmotePrototype proto)
|
||||
{
|
||||
var ev = new EmoteEvent(proto);
|
||||
|
|
|
|||
|
|
@ -64,4 +64,9 @@ public sealed class ChemistryGuideDataSystem : SharedChemistryGuideDataSystem
|
|||
var ev = new ReagentGuideRegistryChangedEvent(changeset);
|
||||
RaiseNetworkEvent(ev);
|
||||
}
|
||||
|
||||
public override void ReloadAllReagentPrototypes()
|
||||
{
|
||||
InitializeServerRegistry();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,14 +17,14 @@ namespace Content.Server.Chemistry.TileReactions
|
|||
[DataDefinition]
|
||||
public sealed partial class SpillTileReaction : ITileReaction
|
||||
{
|
||||
[DataField("launchForwardsMultiplier")] private float _launchForwardsMultiplier = 1;
|
||||
[DataField("requiredSlipSpeed")] private float _requiredSlipSpeed = 6;
|
||||
[DataField("paralyzeTime")] private float _paralyzeTime = 1;
|
||||
[DataField("launchForwardsMultiplier")] public float LaunchForwardsMultiplier = 1;
|
||||
[DataField("requiredSlipSpeed")] public float RequiredSlipSpeed = 6;
|
||||
[DataField("paralyzeTime")] public float ParalyzeTime = 1;
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="SlipperyComponent.SuperSlippery"/>
|
||||
/// </summary>
|
||||
[DataField("superSlippery")] private bool _superSlippery;
|
||||
[DataField("superSlippery")] public bool SuperSlippery;
|
||||
|
||||
public FixedPoint2 TileReact(TileRef tile,
|
||||
ReagentPrototype reagent,
|
||||
|
|
@ -39,13 +39,13 @@ namespace Content.Server.Chemistry.TileReactions
|
|||
.TrySpillAt(tile, new Solution(reagent.ID, reactVolume, data), out var puddleUid, false, false))
|
||||
{
|
||||
var slippery = entityManager.EnsureComponent<SlipperyComponent>(puddleUid);
|
||||
slippery.LaunchForwardsMultiplier = _launchForwardsMultiplier;
|
||||
slippery.ParalyzeTime = _paralyzeTime;
|
||||
slippery.SuperSlippery = _superSlippery;
|
||||
slippery.LaunchForwardsMultiplier = LaunchForwardsMultiplier;
|
||||
slippery.ParalyzeTime = ParalyzeTime;
|
||||
slippery.SuperSlippery = SuperSlippery;
|
||||
entityManager.Dirty(puddleUid, slippery);
|
||||
|
||||
var step = entityManager.EnsureComponent<StepTriggerComponent>(puddleUid);
|
||||
entityManager.EntitySysManager.GetEntitySystem<StepTriggerSystem>().SetRequiredTriggerSpeed(puddleUid, _requiredSlipSpeed, step);
|
||||
entityManager.EntitySysManager.GetEntitySystem<StepTriggerSystem>().SetRequiredTriggerSpeed(puddleUid, RequiredSlipSpeed, step);
|
||||
|
||||
var slow = entityManager.EnsureComponent<SpeedModifierContactsComponent>(puddleUid);
|
||||
var speedModifier = 1 - reagent.Viscosity;
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ using Robust.Server.Player;
|
|||
using Robust.Shared.Console;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Toolshed.Commands.Generic;
|
||||
|
||||
namespace Content.Server.Commands
|
||||
{
|
||||
|
|
@ -50,45 +51,5 @@ namespace Content.Server.Commands
|
|||
attachedEntity = session.AttachedEntity.Value;
|
||||
return true;
|
||||
}
|
||||
|
||||
public static string SubstituteEntityDetails(IConsoleShell shell, EntityUid ent, string ruleString)
|
||||
{
|
||||
var entMan = IoCManager.Resolve<IEntityManager>();
|
||||
var transform = entMan.GetComponent<TransformComponent>(ent);
|
||||
var transformSystem = entMan.System<SharedTransformSystem>();
|
||||
var worldPosition = transformSystem.GetWorldPosition(transform);
|
||||
|
||||
// gross, is there a better way to do this?
|
||||
ruleString = ruleString.Replace("$ID", ent.ToString());
|
||||
ruleString = ruleString.Replace("$WX",
|
||||
worldPosition.X.ToString(CultureInfo.InvariantCulture));
|
||||
ruleString = ruleString.Replace("$WY",
|
||||
worldPosition.Y.ToString(CultureInfo.InvariantCulture));
|
||||
ruleString = ruleString.Replace("$LX",
|
||||
transform.LocalPosition.X.ToString(CultureInfo.InvariantCulture));
|
||||
ruleString = ruleString.Replace("$LY",
|
||||
transform.LocalPosition.Y.ToString(CultureInfo.InvariantCulture));
|
||||
ruleString = ruleString.Replace("$NAME", entMan.GetComponent<MetaDataComponent>(ent).EntityName);
|
||||
|
||||
if (shell.Player is { } player)
|
||||
{
|
||||
if (player.AttachedEntity is {Valid: true} p)
|
||||
{
|
||||
var pTransform = entMan.GetComponent<TransformComponent>(p);
|
||||
var pWorldPosition = transformSystem.GetWorldPosition(pTransform);
|
||||
|
||||
ruleString = ruleString.Replace("$PID", ent.ToString());
|
||||
ruleString = ruleString.Replace("$PWX",
|
||||
pWorldPosition.X.ToString(CultureInfo.InvariantCulture));
|
||||
ruleString = ruleString.Replace("$PWY",
|
||||
pWorldPosition.Y.ToString(CultureInfo.InvariantCulture));
|
||||
ruleString = ruleString.Replace("$PLX",
|
||||
pTransform.LocalPosition.X.ToString(CultureInfo.InvariantCulture));
|
||||
ruleString = ruleString.Replace("$PLY",
|
||||
pTransform.LocalPosition.Y.ToString(CultureInfo.InvariantCulture));
|
||||
}
|
||||
}
|
||||
return ruleString;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ namespace Content.Server.Damage.Systems
|
|||
if (TerminatingOrDeleted(args.Target))
|
||||
return;
|
||||
|
||||
var dmg = _damageable.TryChangeDamage(args.Target, component.Damage, component.IgnoreResistances, origin: args.Component.Thrower);
|
||||
var dmg = _damageable.TryChangeDamage(args.Target, component.Damage * _damageable.UniversalThrownDamageModifier, component.IgnoreResistances, origin: args.Component.Thrower);
|
||||
|
||||
// Log damage only for mobs. Useful for when people throw spears at each other, but also avoids log-spam when explosions send glass shards flying.
|
||||
if (dmg != null && HasComp<MobStateComponent>(args.Target))
|
||||
|
|
@ -58,7 +58,7 @@ namespace Content.Server.Damage.Systems
|
|||
|
||||
private void OnDamageExamine(EntityUid uid, DamageOtherOnHitComponent component, ref DamageExamineEvent args)
|
||||
{
|
||||
_damageExamine.AddDamageExamine(args.Message, component.Damage, Loc.GetString("damage-throw"));
|
||||
_damageExamine.AddDamageExamine(args.Message, _damageable.ApplyUniversalAllModifiers(component.Damage * _damageable.UniversalThrownDamageModifier), Loc.GetString("damage-throw"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ public sealed class DeviceListSystem : SharedDeviceListSystem
|
|||
SubscribeLocalEvent<DeviceListComponent, ComponentShutdown>(OnShutdown);
|
||||
SubscribeLocalEvent<DeviceListComponent, BeforeBroadcastAttemptEvent>(OnBeforeBroadcast);
|
||||
SubscribeLocalEvent<DeviceListComponent, BeforePacketSentEvent>(OnBeforePacketSent);
|
||||
SubscribeLocalEvent<BeforeSaveEvent>(OnMapSave);
|
||||
SubscribeLocalEvent<BeforeSerializationEvent>(OnMapSave);
|
||||
}
|
||||
|
||||
private void OnShutdown(EntityUid uid, DeviceListComponent component, ComponentShutdown args)
|
||||
|
|
@ -124,14 +124,14 @@ public sealed class DeviceListSystem : SharedDeviceListSystem
|
|||
Dirty(list);
|
||||
}
|
||||
|
||||
private void OnMapSave(BeforeSaveEvent ev)
|
||||
private void OnMapSave(BeforeSerializationEvent ev)
|
||||
{
|
||||
List<EntityUid> toRemove = new();
|
||||
var query = GetEntityQuery<TransformComponent>();
|
||||
var enumerator = AllEntityQuery<DeviceListComponent, TransformComponent>();
|
||||
while (enumerator.MoveNext(out var uid, out var device, out var xform))
|
||||
{
|
||||
if (xform.MapUid != ev.Map)
|
||||
if (!ev.MapIds.Contains(xform.MapID))
|
||||
continue;
|
||||
|
||||
foreach (var ent in device.Devices)
|
||||
|
|
@ -144,7 +144,10 @@ public sealed class DeviceListSystem : SharedDeviceListSystem
|
|||
continue;
|
||||
}
|
||||
|
||||
if (linkedXform.MapUid == ev.Map)
|
||||
// This is assuming that **all** of the map is getting saved.
|
||||
// Which is not necessarily true.
|
||||
// AAAAAAAAAAAAAA
|
||||
if (ev.MapIds.Contains(linkedXform.MapID))
|
||||
continue;
|
||||
|
||||
toRemove.Add(ent);
|
||||
|
|
|
|||
|
|
@ -67,15 +67,18 @@ public sealed class NetworkConfiguratorSystem : SharedNetworkConfiguratorSystem
|
|||
|
||||
SubscribeLocalEvent<DeviceListComponent, ComponentRemove>(OnComponentRemoved);
|
||||
|
||||
SubscribeLocalEvent<BeforeSaveEvent>(OnMapSave);
|
||||
SubscribeLocalEvent<BeforeSerializationEvent>(OnMapSave);
|
||||
}
|
||||
|
||||
private void OnMapSave(BeforeSaveEvent ev)
|
||||
private void OnMapSave(BeforeSerializationEvent ev)
|
||||
{
|
||||
var enumerator = AllEntityQuery<NetworkConfiguratorComponent>();
|
||||
while (enumerator.MoveNext(out var uid, out var conf))
|
||||
{
|
||||
if (CompOrNull<TransformComponent>(conf.ActiveDeviceList)?.MapUid != ev.Map)
|
||||
if (!TryComp(conf.ActiveDeviceList, out TransformComponent? listXform))
|
||||
continue;
|
||||
|
||||
if (!ev.MapIds.Contains(listXform.MapID))
|
||||
continue;
|
||||
|
||||
// The linked device list is (probably) being saved. Make sure that the configurator is also being saved
|
||||
|
|
@ -83,9 +86,10 @@ public sealed class NetworkConfiguratorSystem : SharedNetworkConfiguratorSystem
|
|||
// containing a set of all entities that are about to be saved, which would make checking this much easier.
|
||||
// This is a shitty bandaid, and will force close the UI during auto-saves.
|
||||
// TODO Map serialization refactor
|
||||
// I'm refactoring it now and I still dont know what to do
|
||||
|
||||
var xform = Transform(uid);
|
||||
if (xform.MapUid == ev.Map && IsSaveable(uid))
|
||||
if (ev.MapIds.Contains(xform.MapID) && IsSaveable(uid))
|
||||
continue;
|
||||
|
||||
_uiSystem.CloseUi(uid, NetworkConfiguratorUiKey.Configure);
|
||||
|
|
|
|||
|
|
@ -44,6 +44,26 @@ namespace Content.Server.EntityEffects.Effects
|
|||
|
||||
var damageSpec = new DamageSpecifier(Damage);
|
||||
|
||||
var universalReagentDamageModifier = entSys.GetEntitySystem<DamageableSystem>().UniversalReagentDamageModifier;
|
||||
var universalReagentHealModifier = entSys.GetEntitySystem<DamageableSystem>().UniversalReagentHealModifier;
|
||||
|
||||
if (universalReagentDamageModifier != 1 || universalReagentHealModifier != 1)
|
||||
{
|
||||
foreach (var (type, val) in damageSpec.DamageDict)
|
||||
{
|
||||
if (val < 0f)
|
||||
{
|
||||
damageSpec.DamageDict[type] = val * universalReagentHealModifier;
|
||||
}
|
||||
if (val > 0f)
|
||||
{
|
||||
damageSpec.DamageDict[type] = val * universalReagentDamageModifier;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
damageSpec = entSys.GetEntitySystem<DamageableSystem>().ApplyUniversalAllModifiers(damageSpec);
|
||||
|
||||
foreach (var group in prototype.EnumeratePrototypes<DamageGroupPrototype>())
|
||||
{
|
||||
if (!damageSpec.TryGetDamageInGroup(group, out var amount))
|
||||
|
|
@ -114,17 +134,37 @@ namespace Content.Server.EntityEffects.Effects
|
|||
public override void Effect(EntityEffectBaseArgs args)
|
||||
{
|
||||
var scale = FixedPoint2.New(1);
|
||||
var damageSpec = new DamageSpecifier(Damage);
|
||||
|
||||
if (args is EntityEffectReagentArgs reagentArgs)
|
||||
{
|
||||
scale = ScaleByQuantity ? reagentArgs.Quantity * reagentArgs.Scale : reagentArgs.Scale;
|
||||
}
|
||||
|
||||
args.EntityManager.System<DamageableSystem>().TryChangeDamage(
|
||||
args.TargetEntity,
|
||||
Damage * scale,
|
||||
IgnoreResistances,
|
||||
interruptsDoAfters: false);
|
||||
var universalReagentDamageModifier = args.EntityManager.System<DamageableSystem>().UniversalReagentDamageModifier;
|
||||
var universalReagentHealModifier = args.EntityManager.System<DamageableSystem>().UniversalReagentHealModifier;
|
||||
|
||||
if (universalReagentDamageModifier != 1 || universalReagentHealModifier != 1)
|
||||
{
|
||||
foreach (var (type, val) in damageSpec.DamageDict)
|
||||
{
|
||||
if (val < 0f)
|
||||
{
|
||||
damageSpec.DamageDict[type] = val * universalReagentHealModifier;
|
||||
}
|
||||
if (val > 0f)
|
||||
{
|
||||
damageSpec.DamageDict[type] = val * universalReagentDamageModifier;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
args.EntityManager.System<DamageableSystem>()
|
||||
.TryChangeDamage(
|
||||
args.TargetEntity,
|
||||
damageSpec * scale,
|
||||
IgnoreResistances,
|
||||
interruptsDoAfters: false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -181,6 +181,7 @@ namespace Content.Server.Entry
|
|||
IoCManager.Resolve<IBanManager>().Initialize();
|
||||
IoCManager.Resolve<IConnectionManager>().PostInit();
|
||||
IoCManager.Resolve<MultiServerKickManager>().Initialize();
|
||||
IoCManager.Resolve<CVarControlManager>().Initialize();
|
||||
// Sunrise-Sponsors-Start
|
||||
SunriseServerEntry.PostInit();
|
||||
// Sunrise-Sponsors-End
|
||||
|
|
|
|||
|
|
@ -464,7 +464,7 @@ public sealed partial class ExplosionSystem
|
|||
}
|
||||
|
||||
// TODO EXPLOSIONS turn explosions into entities, and pass the the entity in as the damage origin.
|
||||
_damageableSystem.TryChangeDamage(entity, damage, ignoreResistances: true);
|
||||
_damageableSystem.TryChangeDamage(entity, damage * _damageableSystem.UniversalExplosionDamageModifier, ignoreResistances: true);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ public sealed partial class TriggerSystem
|
|||
// Re-check for contacts as we cleared them.
|
||||
else if (TryComp<PhysicsComponent>(uid, out var body))
|
||||
{
|
||||
_broadphase.RegenerateContacts(uid, body);
|
||||
_broadphase.RegenerateContacts((uid, body));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
using System.Linq;
|
||||
using Content.Server.Administration.Logs;
|
||||
using Content.Server.Chemistry.TileReactions;
|
||||
using Content.Server.DoAfter;
|
||||
using Content.Server.Fluids.Components;
|
||||
using Content.Server.Spreader;
|
||||
|
|
@ -391,23 +393,36 @@ public sealed partial class PuddleSystem : SharedPuddleSystem
|
|||
private void UpdateSlip(EntityUid entityUid, PuddleComponent component, Solution solution)
|
||||
{
|
||||
var isSlippery = false;
|
||||
var isSuperSlippery = false;
|
||||
// The base sprite is currently at 0.3 so we require at least 2nd tier to be slippery or else it's too hard to see.
|
||||
var amountRequired = FixedPoint2.New(component.OverflowVolume.Float() * LowThreshold);
|
||||
var slipperyAmount = FixedPoint2.Zero;
|
||||
|
||||
// Utilize the defaults from their relevant systems... this sucks, and is a bandaid
|
||||
var launchForwardsMultiplier = SlipperyComponent.DefaultLaunchForwardsMultiplier;
|
||||
var paralyzeTime = SlipperyComponent.DefaultParalyzeTime;
|
||||
var requiredSlipSpeed = StepTriggerComponent.DefaultRequiredTriggeredSpeed;
|
||||
|
||||
foreach (var (reagent, quantity) in solution.Contents)
|
||||
{
|
||||
var reagentProto = _prototypeManager.Index<ReagentPrototype>(reagent.Prototype);
|
||||
|
||||
if (reagentProto.Slippery)
|
||||
{
|
||||
slipperyAmount += quantity;
|
||||
if (!reagentProto.Slippery)
|
||||
continue;
|
||||
slipperyAmount += quantity;
|
||||
|
||||
if (slipperyAmount > amountRequired)
|
||||
{
|
||||
isSlippery = true;
|
||||
break;
|
||||
}
|
||||
if (slipperyAmount <= amountRequired)
|
||||
continue;
|
||||
isSlippery = true;
|
||||
|
||||
foreach (var tileReaction in reagentProto.TileReactions)
|
||||
{
|
||||
if (tileReaction is not SpillTileReaction spillTileReaction)
|
||||
continue;
|
||||
isSuperSlippery = spillTileReaction.SuperSlippery;
|
||||
launchForwardsMultiplier = launchForwardsMultiplier < spillTileReaction.LaunchForwardsMultiplier ? spillTileReaction.LaunchForwardsMultiplier : launchForwardsMultiplier;
|
||||
requiredSlipSpeed = requiredSlipSpeed > spillTileReaction.RequiredSlipSpeed ? spillTileReaction.RequiredSlipSpeed : requiredSlipSpeed;
|
||||
paralyzeTime = paralyzeTime < spillTileReaction.ParalyzeTime ? spillTileReaction.ParalyzeTime : paralyzeTime;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -417,6 +432,14 @@ public sealed partial class PuddleSystem : SharedPuddleSystem
|
|||
_stepTrigger.SetActive(entityUid, true, comp);
|
||||
var friction = EnsureComp<TileFrictionModifierComponent>(entityUid);
|
||||
_tile.SetModifier(entityUid, TileFrictionController.DefaultFriction * 0.5f, friction);
|
||||
|
||||
if (!TryComp<SlipperyComponent>(entityUid, out var slipperyComponent))
|
||||
return;
|
||||
slipperyComponent.SuperSlippery = isSuperSlippery;
|
||||
_stepTrigger.SetRequiredTriggerSpeed(entityUid, requiredSlipSpeed);
|
||||
slipperyComponent.LaunchForwardsMultiplier = launchForwardsMultiplier;
|
||||
slipperyComponent.ParalyzeTime = paralyzeTime;
|
||||
|
||||
}
|
||||
else if (TryComp<StepTriggerComponent>(entityUid, out var comp))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -228,7 +228,7 @@ public sealed class SmokeSystem : EntitySystem
|
|||
var xform = Transform(uid);
|
||||
_physics.SetBodyType(uid, BodyType.Dynamic, fixtures, body, xform);
|
||||
_physics.SetCanCollide(uid, true, manager: fixtures, body: body);
|
||||
_broadphase.RegenerateContacts(uid, body, fixtures, xform);
|
||||
_broadphase.RegenerateContacts((uid, body, fixtures, xform));
|
||||
}
|
||||
|
||||
var timer = EnsureComp<TimedDespawnComponent>(uid);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Content.Server.Announcements;
|
||||
using Content.Server.Discord;
|
||||
using Content.Server.GameTicking.Events;
|
||||
|
|
@ -14,10 +15,12 @@ using Content.Shared.Players;
|
|||
using Content.Shared.Preferences;
|
||||
using JetBrains.Annotations;
|
||||
using Prometheus;
|
||||
using Robust.Server.Maps;
|
||||
using Robust.Shared.Asynchronous;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.EntitySerialization;
|
||||
using Robust.Shared.EntitySerialization.Systems;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Random;
|
||||
|
|
@ -96,9 +99,6 @@ namespace Content.Server.GameTicking
|
|||
|
||||
AddGamePresetRules();
|
||||
|
||||
DefaultMap = _mapManager.CreateMap();
|
||||
_mapManager.AddUninitializedMap(DefaultMap);
|
||||
|
||||
var maps = new List<GameMapPrototype>();
|
||||
|
||||
// the map might have been force-set by something
|
||||
|
|
@ -136,52 +136,202 @@ namespace Content.Server.GameTicking
|
|||
// Let game rules dictate what maps we should load.
|
||||
RaiseLocalEvent(new LoadingMapsEvent(maps));
|
||||
|
||||
foreach (var map in maps)
|
||||
if (maps.Count == 0)
|
||||
{
|
||||
var toLoad = DefaultMap;
|
||||
if (maps[0] != map)
|
||||
{
|
||||
// Create other maps for the others since we need to.
|
||||
toLoad = _mapManager.CreateMap();
|
||||
_mapManager.AddUninitializedMap(toLoad);
|
||||
}
|
||||
_map.CreateMap(out var mapId, runMapInit: false);
|
||||
DefaultMap = mapId;
|
||||
return;
|
||||
}
|
||||
|
||||
LoadGameMap(map, toLoad, null);
|
||||
for (var i = 0; i < maps.Count; i++)
|
||||
{
|
||||
LoadGameMap(maps[i], out var mapId);
|
||||
DebugTools.Assert(!_map.IsInitialized(mapId));
|
||||
|
||||
if (i == 0)
|
||||
DefaultMap = mapId;
|
||||
}
|
||||
}
|
||||
|
||||
public PreGameMapLoad RaisePreLoad(
|
||||
GameMapPrototype proto,
|
||||
DeserializationOptions? opts = null,
|
||||
Vector2? offset = null,
|
||||
Angle? rot = null)
|
||||
{
|
||||
offset ??= proto.MaxRandomOffset != 0f
|
||||
? _robustRandom.NextVector2(proto.MaxRandomOffset)
|
||||
: Vector2.Zero;
|
||||
|
||||
rot ??= proto.RandomRotation
|
||||
? _robustRandom.NextAngle()
|
||||
: Angle.Zero;
|
||||
|
||||
opts ??= DeserializationOptions.Default;
|
||||
var ev = new PreGameMapLoad(proto, opts.Value, offset.Value, rot.Value);
|
||||
RaiseLocalEvent(ev);
|
||||
return ev;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads a new map, allowing systems interested in it to handle loading events.
|
||||
/// In the base game, this is required to be used if you want to load a station.
|
||||
/// This does not initialze maps, unles specified via the <see cref="DeserializationOptions"/>.
|
||||
/// </summary>
|
||||
/// <param name="map">Game map prototype to load in.</param>
|
||||
/// <param name="targetMapId">Map to load into.</param>
|
||||
/// <param name="loadOptions">Map loading options, includes offset.</param>
|
||||
/// <remarks>
|
||||
/// This is basically a wrapper around a <see cref="MapLoaderSystem"/> method that auto generate
|
||||
/// some <see cref="MapLoadOptions"/> using information in a prototype, and raise some events to allow content
|
||||
/// to modify the options and react to the map creation.
|
||||
/// </remarks>
|
||||
/// <param name="proto">Game map prototype to load in.</param>
|
||||
/// <param name="mapId">The id of the map that was loaded.</param>
|
||||
/// <param name="options">Entity loading options, including whether the maps should be initialized.</param>
|
||||
/// <param name="stationName">Name to assign to the loaded station.</param>
|
||||
/// <returns>All loaded entities and grids.</returns>
|
||||
public IReadOnlyList<EntityUid> LoadGameMap(GameMapPrototype map, MapId targetMapId, MapLoadOptions? loadOptions, string? stationName = null)
|
||||
public IReadOnlyList<EntityUid> LoadGameMap(
|
||||
GameMapPrototype proto,
|
||||
out MapId mapId,
|
||||
DeserializationOptions? options = null,
|
||||
string? stationName = null,
|
||||
Vector2? offset = null,
|
||||
Angle? rot = null)
|
||||
{
|
||||
// Okay I specifically didn't set LoadMap here because this is typically called onto a new map.
|
||||
// whereas the command can also be used on an existing map.
|
||||
var loadOpts = loadOptions ?? new MapLoadOptions();
|
||||
var ev = RaisePreLoad(proto, options, offset, rot);
|
||||
|
||||
if (map.MaxRandomOffset != 0f)
|
||||
loadOpts.Offset = _robustRandom.NextVector2(map.MaxRandomOffset);
|
||||
if (ev.GameMap.IsGrid)
|
||||
{
|
||||
var mapUid = _map.CreateMap(out mapId);
|
||||
if (!_loader.TryLoadGrid(mapId,
|
||||
ev.GameMap.MapPath,
|
||||
out var grid,
|
||||
ev.Options,
|
||||
ev.Offset,
|
||||
ev.Rotation))
|
||||
{
|
||||
throw new Exception($"Failed to load game-map grid {ev.GameMap.ID}");
|
||||
}
|
||||
|
||||
if (map.RandomRotation)
|
||||
loadOpts.Rotation = _robustRandom.NextAngle();
|
||||
_metaData.SetEntityName(mapUid, proto.MapName);
|
||||
var g = new List<EntityUid> {grid.Value.Owner};
|
||||
RaiseLocalEvent(new PostGameMapLoad(proto, mapId, g, stationName));
|
||||
return g;
|
||||
}
|
||||
|
||||
var ev = new PreGameMapLoad(targetMapId, map, loadOpts);
|
||||
RaiseLocalEvent(ev);
|
||||
if (!_loader.TryLoadMap(ev.GameMap.MapPath,
|
||||
out var map,
|
||||
out var grids,
|
||||
ev.Options,
|
||||
ev.Offset,
|
||||
ev.Rotation))
|
||||
{
|
||||
throw new Exception($"Failed to load game map {ev.GameMap.ID}");
|
||||
}
|
||||
|
||||
var gridIds = _map.LoadMap(targetMapId, ev.GameMap.MapPath.ToString(), ev.Options);
|
||||
mapId = map.Value.Comp.MapId;
|
||||
_metaData.SetEntityName(map.Value.Owner, proto.MapName);
|
||||
var gridUids = grids.Select(x => x.Owner).ToList();
|
||||
RaiseLocalEvent(new PostGameMapLoad(proto, mapId, gridUids, stationName));
|
||||
return gridUids;
|
||||
}
|
||||
|
||||
_metaData.SetEntityName(_mapManager.GetMapEntityId(targetMapId), map.MapName);
|
||||
/// <summary>
|
||||
/// Variant of <see cref="LoadGameMap"/> that attempts to assign the provided <see cref="MapId"/> to the
|
||||
/// loaded map.
|
||||
/// </summary>
|
||||
public IReadOnlyList<EntityUid> LoadGameMapWithId(
|
||||
GameMapPrototype proto,
|
||||
MapId mapId,
|
||||
DeserializationOptions? opts = null,
|
||||
string? stationName = null,
|
||||
Vector2? offset = null,
|
||||
Angle? rot = null)
|
||||
{
|
||||
var ev = RaisePreLoad(proto, opts, offset, rot);
|
||||
|
||||
var gridUids = gridIds.ToList();
|
||||
RaiseLocalEvent(new PostGameMapLoad(map, targetMapId, gridUids, stationName));
|
||||
if (ev.GameMap.IsGrid)
|
||||
{
|
||||
var mapUid = _map.CreateMap(mapId);
|
||||
if (!_loader.TryLoadGrid(mapId,
|
||||
ev.GameMap.MapPath,
|
||||
out var grid,
|
||||
ev.Options,
|
||||
ev.Offset,
|
||||
ev.Rotation))
|
||||
{
|
||||
throw new Exception($"Failed to load game-map grid {ev.GameMap.ID}");
|
||||
}
|
||||
|
||||
_metaData.SetEntityName(mapUid, proto.MapName);
|
||||
var g = new List<EntityUid> {grid.Value.Owner};
|
||||
RaiseLocalEvent(new PostGameMapLoad(proto, mapId, g, stationName));
|
||||
return g;
|
||||
}
|
||||
|
||||
if (!_loader.TryLoadMapWithId(
|
||||
mapId,
|
||||
ev.GameMap.MapPath,
|
||||
out var map,
|
||||
out var grids,
|
||||
ev.Options,
|
||||
ev.Offset,
|
||||
ev.Rotation))
|
||||
{
|
||||
throw new Exception($"Failed to load map");
|
||||
}
|
||||
|
||||
_metaData.SetEntityName(map.Value.Owner, proto.MapName);
|
||||
var gridUids = grids.Select(x => x.Owner).ToList();
|
||||
RaiseLocalEvent(new PostGameMapLoad(proto, mapId, gridUids, stationName));
|
||||
return gridUids;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Variant of <see cref="LoadGameMap"/> that loads and then merges a game map onto an existing map.
|
||||
/// </summary>
|
||||
public IReadOnlyList<EntityUid> MergeGameMap(
|
||||
GameMapPrototype proto,
|
||||
MapId targetMap,
|
||||
DeserializationOptions? opts = null,
|
||||
string? stationName = null,
|
||||
Vector2? offset = null,
|
||||
Angle? rot = null)
|
||||
{
|
||||
// TODO MAP LOADING use a new event?
|
||||
// This is quite different from the other methods, which will actually create a **new** map.
|
||||
var ev = RaisePreLoad(proto, opts, offset, rot);
|
||||
|
||||
if (ev.GameMap.IsGrid)
|
||||
{
|
||||
if (!_loader.TryLoadGrid(targetMap,
|
||||
ev.GameMap.MapPath,
|
||||
out var grid,
|
||||
ev.Options,
|
||||
ev.Offset,
|
||||
ev.Rotation))
|
||||
{
|
||||
throw new Exception($"Failed to load game-map grid {ev.GameMap.ID}");
|
||||
}
|
||||
|
||||
var g = new List<EntityUid> {grid.Value.Owner};
|
||||
// TODO MAP LOADING use a new event?
|
||||
RaiseLocalEvent(new PostGameMapLoad(proto, targetMap, g, stationName));
|
||||
return g;
|
||||
}
|
||||
|
||||
if (!_loader.TryMergeMap(targetMap,
|
||||
ev.GameMap.MapPath,
|
||||
out var grids,
|
||||
ev.Options,
|
||||
ev.Offset,
|
||||
ev.Rotation))
|
||||
{
|
||||
throw new Exception($"Failed to load map");
|
||||
}
|
||||
|
||||
var gridUids = grids.Select(x => x.Owner).ToList();
|
||||
|
||||
// TODO MAP LOADING use a new event?
|
||||
RaiseLocalEvent(new PostGameMapLoad(proto, targetMap, gridUids, stationName));
|
||||
return gridUids;
|
||||
}
|
||||
|
||||
|
|
@ -278,7 +428,7 @@ namespace Content.Server.GameTicking
|
|||
}
|
||||
|
||||
// MapInitialize *before* spawning players, our codebase is too shit to do it afterwards...
|
||||
_mapManager.DoMapInitialize(DefaultMap);
|
||||
_map.InitializeMap(DefaultMap);
|
||||
|
||||
SpawnPlayers(readyPlayers, readyPlayerProfiles, force);
|
||||
|
||||
|
|
@ -736,21 +886,14 @@ namespace Content.Server.GameTicking
|
|||
/// You likely want to subscribe to this after StationSystem.
|
||||
/// </remarks>
|
||||
[PublicAPI]
|
||||
public sealed class PreGameMapLoad : EntityEventArgs
|
||||
public sealed class PreGameMapLoad(GameMapPrototype gameMap, DeserializationOptions options, Vector2 offset, Angle rotation) : EntityEventArgs
|
||||
{
|
||||
public readonly MapId Map;
|
||||
public GameMapPrototype GameMap;
|
||||
public MapLoadOptions Options;
|
||||
|
||||
public PreGameMapLoad(MapId map, GameMapPrototype gameMap, MapLoadOptions options)
|
||||
{
|
||||
Map = map;
|
||||
GameMap = gameMap;
|
||||
Options = options;
|
||||
}
|
||||
public readonly GameMapPrototype GameMap = gameMap;
|
||||
public DeserializationOptions Options = options;
|
||||
public Vector2 Offset = offset;
|
||||
public Angle Rotation = rotation;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Event raised after the game loads a given map.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -390,6 +390,7 @@ namespace Content.Server.GameTicking
|
|||
if (DummyTicker)
|
||||
return;
|
||||
|
||||
var makeObserver = false;
|
||||
Entity<MindComponent?>? mind = player.GetMind();
|
||||
if (mind == null)
|
||||
{
|
||||
|
|
@ -397,10 +398,13 @@ namespace Content.Server.GameTicking
|
|||
var (mindId, mindComp) = _mind.CreateMind(player.UserId, name);
|
||||
mind = (mindId, mindComp);
|
||||
_mind.SetUserId(mind.Value, player.UserId);
|
||||
_roles.MindAddRole(mind.Value, "MindRoleObserver");
|
||||
makeObserver = true;
|
||||
}
|
||||
|
||||
var ghost = _ghost.SpawnGhost(mind.Value);
|
||||
if (makeObserver)
|
||||
_roles.MindAddRole(mind.Value, "MindRoleObserver");
|
||||
|
||||
_adminLogger.Add(LogType.LateJoin,
|
||||
LogImpact.Low,
|
||||
$"{player.Name} late joined the round as an Observer with {ToPrettyString(ghost):entity}.");
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ using Robust.Server.GameObjects;
|
|||
using Robust.Server.GameStates;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.EntitySerialization.Systems;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
|
|
@ -48,7 +49,8 @@ namespace Content.Server.GameTicking
|
|||
[Dependency] private readonly IServerPreferencesManager _prefsManager = default!;
|
||||
[Dependency] private readonly IServerDbManager _db = default!;
|
||||
[Dependency] private readonly ChatSystem _chatSystem = default!;
|
||||
[Dependency] private readonly MapLoaderSystem _map = default!;
|
||||
[Dependency] private readonly MapLoaderSystem _loader = default!;
|
||||
[Dependency] private readonly SharedMapSystem _map = default!;
|
||||
[Dependency] private readonly GhostSystem _ghost = default!;
|
||||
[Dependency] private readonly SharedMindSystem _mind = default!;
|
||||
[Dependency] private readonly PlayTimeTrackingSystem _playTimeTrackings = default!;
|
||||
|
|
|
|||
|
|
@ -20,11 +20,17 @@ public sealed partial class LoadMapRuleComponent : Component
|
|||
public ProtoId<GameMapPrototype>? GameMap;
|
||||
|
||||
/// <summary>
|
||||
/// A map path to load on a new map.
|
||||
/// A map to load.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public ResPath? MapPath;
|
||||
|
||||
/// <summary>
|
||||
/// A grid to load on a new map.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public ResPath? GridPath;
|
||||
|
||||
/// <summary>
|
||||
/// A <see cref="PreloadedGridPrototype"/> to move to a new map.
|
||||
/// If there are no instances left nothing is done.
|
||||
|
|
|
|||
|
|
@ -1,10 +1,14 @@
|
|||
using System.Linq;
|
||||
using Content.Server.GameTicking.Rules.Components;
|
||||
using Content.Server.GridPreloader;
|
||||
using Content.Server.StationEvents.Events;
|
||||
using Content.Shared.GameTicking.Components;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.Maps;
|
||||
using Robust.Shared.EntitySerialization;
|
||||
using Robust.Shared.EntitySerialization.Systems;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.GameTicking.Rules;
|
||||
|
||||
|
|
@ -26,29 +30,50 @@ public sealed class LoadMapRuleSystem : StationEventSystem<LoadMapRuleComponent>
|
|||
return;
|
||||
}
|
||||
|
||||
// grid preloading needs map to init after moving it
|
||||
var mapUid = _map.CreateMap(out var mapId, runMapInit: comp.PreloadedGrid == null);
|
||||
|
||||
Log.Info($"Created map {mapId} for {ToPrettyString(uid):rule}");
|
||||
|
||||
MapId mapId;
|
||||
IReadOnlyList<EntityUid> grids;
|
||||
if (comp.GameMap != null)
|
||||
{
|
||||
// Component has one of three modes, only one of the three fields should ever be populated.
|
||||
DebugTools.AssertNull(comp.MapPath);
|
||||
DebugTools.AssertNull(comp.GridPath);
|
||||
DebugTools.AssertNull(comp.PreloadedGrid);
|
||||
|
||||
var gameMap = _prototypeManager.Index(comp.GameMap.Value);
|
||||
grids = GameTicker.LoadGameMap(gameMap, mapId, new MapLoadOptions());
|
||||
grids = GameTicker.LoadGameMap(gameMap, out mapId, null);
|
||||
Log.Info($"Created map {mapId} for {ToPrettyString(uid):rule}");
|
||||
}
|
||||
else if (comp.MapPath is {} path)
|
||||
{
|
||||
var options = new MapLoadOptions { LoadMap = true };
|
||||
if (!_mapLoader.TryLoad(mapId, path.ToString(), out var roots, options))
|
||||
DebugTools.AssertNull(comp.GridPath);
|
||||
DebugTools.AssertNull(comp.PreloadedGrid);
|
||||
|
||||
var opts = DeserializationOptions.Default with {InitializeMaps = true};
|
||||
if (!_mapLoader.TryLoadMap(path, out var map, out var gridSet, opts))
|
||||
{
|
||||
Log.Error($"Failed to load map from {path}!");
|
||||
Del(mapUid);
|
||||
ForceEndSelf(uid, rule);
|
||||
return;
|
||||
}
|
||||
|
||||
grids = roots;
|
||||
grids = gridSet.Select( x => x.Owner).ToList();
|
||||
mapId = map.Value.Comp.MapId;
|
||||
}
|
||||
else if (comp.GridPath is { } gPath)
|
||||
{
|
||||
DebugTools.AssertNull(comp.PreloadedGrid);
|
||||
|
||||
// I fucking love it when "map paths" choses to ar
|
||||
_map.CreateMap(out mapId);
|
||||
var opts = DeserializationOptions.Default with {InitializeMaps = true};
|
||||
if (!_mapLoader.TryLoadGrid(mapId, gPath, out var grid, opts))
|
||||
{
|
||||
Log.Error($"Failed to load grid from {gPath}!");
|
||||
ForceEndSelf(uid, rule);
|
||||
return;
|
||||
}
|
||||
|
||||
grids = new List<EntityUid> {grid.Value.Owner};
|
||||
}
|
||||
else if (comp.PreloadedGrid is {} preloaded)
|
||||
{
|
||||
|
|
@ -56,11 +81,11 @@ public sealed class LoadMapRuleSystem : StationEventSystem<LoadMapRuleComponent>
|
|||
if (!_gridPreloader.TryGetPreloadedGrid(preloaded, out var loadedShuttle))
|
||||
{
|
||||
Log.Error($"Failed to get a preloaded grid with {preloaded}!");
|
||||
Del(mapUid);
|
||||
ForceEndSelf(uid, rule);
|
||||
return;
|
||||
}
|
||||
|
||||
var mapUid = _map.CreateMap(out mapId, runMapInit: false);
|
||||
_transform.SetParent(loadedShuttle.Value, mapUid);
|
||||
grids = new List<EntityUid>() { loadedShuttle.Value };
|
||||
_map.InitializeMap(mapUid);
|
||||
|
|
@ -68,7 +93,6 @@ public sealed class LoadMapRuleSystem : StationEventSystem<LoadMapRuleComponent>
|
|||
else
|
||||
{
|
||||
Log.Error($"No valid map prototype or map path associated with the rule {ToPrettyString(uid)}");
|
||||
Del(mapUid);
|
||||
ForceEndSelf(uid, rule);
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ using Content.Shared.Movement.Events;
|
|||
using Content.Shared.Movement.Systems;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Storage.Components;
|
||||
using Content.Shared.Tag;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Configuration;
|
||||
|
|
@ -67,6 +68,7 @@ namespace Content.Server.Ghost
|
|||
[Dependency] private readonly DamageableSystem _damageable = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly TagSystem _tag = default!;
|
||||
[Dependency] private readonly NewLifeSystem _newLifeSystem = default!;
|
||||
[Dependency] private readonly EuiManager _euiManager = default!;
|
||||
|
||||
|
|
@ -435,8 +437,11 @@ namespace Content.Server.Ghost
|
|||
public void MakeVisible(bool visible)
|
||||
{
|
||||
var entityQuery = EntityQueryEnumerator<GhostComponent, VisibilityComponent>();
|
||||
while (entityQuery.MoveNext(out var uid, out _, out var vis))
|
||||
while (entityQuery.MoveNext(out var uid, out var _, out var vis))
|
||||
{
|
||||
if (!_tag.HasTag(uid, "AllowGhostShownByEvent"))
|
||||
continue;
|
||||
|
||||
if (visible)
|
||||
{
|
||||
_visibilitySystem.AddLayer((uid, vis), (int) VisibilityFlags.Normal, false);
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ using Content.Shared.CCVar;
|
|||
using Content.Shared.GridPreloader.Prototypes;
|
||||
using Content.Shared.GridPreloader.Systems;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.Maps;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
|
|
@ -13,6 +12,7 @@ using System.Numerics;
|
|||
using Content.Server.GameTicking;
|
||||
using Content.Shared.GameTicking;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.EntitySerialization.Systems;
|
||||
|
||||
namespace Content.Server.GridPreloader;
|
||||
public sealed class GridPreloaderSystem : SharedGridPreloaderSystem
|
||||
|
|
@ -72,23 +72,13 @@ public sealed class GridPreloaderSystem : SharedGridPreloaderSystem
|
|||
{
|
||||
for (var i = 0; i < proto.Copies; i++)
|
||||
{
|
||||
var options = new MapLoadOptions
|
||||
if (!_mapLoader.TryLoadGrid(mapId, proto.Path, out var grid))
|
||||
{
|
||||
LoadMap = false,
|
||||
};
|
||||
|
||||
if (!_mapLoader.TryLoad(mapId, proto.Path.ToString(), out var roots, options))
|
||||
Log.Error($"Failed to preload grid prototype {proto.ID}");
|
||||
continue;
|
||||
}
|
||||
|
||||
// only supports loading maps with one grid.
|
||||
if (roots.Count != 1)
|
||||
continue;
|
||||
|
||||
var gridUid = roots[0];
|
||||
|
||||
// gets grid + also confirms that the root we loaded is actually a grid
|
||||
if (!TryComp<MapGridComponent>(gridUid, out var mapGrid))
|
||||
continue;
|
||||
var (gridUid, mapGrid) = grid.Value;
|
||||
|
||||
if (!TryComp<PhysicsComponent>(gridUid, out var physics))
|
||||
continue;
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ using Content.Shared.Interaction.Events;
|
|||
using Content.Shared.Item;
|
||||
using Content.Shared.Whitelist;
|
||||
using Robust.Server.Audio;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
|
@ -26,6 +25,7 @@ public sealed class RandomGiftSystem : EntitySystem
|
|||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
|
||||
[Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
|
||||
private readonly List<string> _possibleGiftsSafe = new();
|
||||
private readonly List<string> _possibleGiftsUnsafe = new();
|
||||
|
|
@ -63,11 +63,16 @@ public sealed class RandomGiftSystem : EntitySystem
|
|||
if (component.Wrapper is not null)
|
||||
Spawn(component.Wrapper, coords);
|
||||
|
||||
args.Handled = true;
|
||||
_audio.PlayPvs(component.Sound, args.User);
|
||||
Del(uid);
|
||||
|
||||
// Don't delete the entity in the event bus, so we queue it for deletion.
|
||||
// We need the free hand for the new item, so we send it to nullspace.
|
||||
_transform.DetachEntity(uid, Transform(uid));
|
||||
QueueDel(uid);
|
||||
|
||||
_hands.PickupOrDrop(args.User, handsEnt);
|
||||
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void OnGiftMapInit(EntityUid uid, RandomGiftComponent component, MapInitEvent args)
|
||||
|
|
|
|||
|
|
@ -115,13 +115,13 @@ public sealed class HolopadSystem : SharedHolopadSystem
|
|||
if (source != null)
|
||||
{
|
||||
// Close any AI request windows
|
||||
if (_stationAiSystem.TryGetStationAiCore(args.Actor, out var stationAiCore) && stationAiCore != null)
|
||||
if (_stationAiSystem.TryGetCore(args.Actor, out var stationAiCore))
|
||||
_userInterfaceSystem.CloseUi(receiver.Owner, HolopadUiKey.AiRequestWindow, args.Actor);
|
||||
|
||||
// Try to warn the AI if the source of the call is out of its range
|
||||
if (TryComp<TelephoneComponent>(stationAiCore, out var stationAiTelephone) &&
|
||||
TryComp<TelephoneComponent>(source, out var sourceTelephone) &&
|
||||
!_telephoneSystem.IsSourceInRangeOfReceiver((stationAiCore.Value.Owner, stationAiTelephone), (source.Value.Owner, sourceTelephone)))
|
||||
!_telephoneSystem.IsSourceInRangeOfReceiver((stationAiCore.Owner, stationAiTelephone), (source.Value.Owner, sourceTelephone)))
|
||||
{
|
||||
_popupSystem.PopupEntity(Loc.GetString("holopad-ai-is-unable-to-reach-holopad"), receiver, args.Actor);
|
||||
return;
|
||||
|
|
@ -150,11 +150,11 @@ public sealed class HolopadSystem : SharedHolopadSystem
|
|||
// If the user is an AI, end all calls originating from its
|
||||
// associated core to ensure that any broadcasts will end
|
||||
if (!TryComp<StationAiHeldComponent>(args.Actor, out var stationAiHeld) ||
|
||||
!_stationAiSystem.TryGetStationAiCore((args.Actor, stationAiHeld), out var stationAiCore))
|
||||
!_stationAiSystem.TryGetCore(args.Actor, out var stationAiCore))
|
||||
return;
|
||||
|
||||
if (TryComp<TelephoneComponent>(stationAiCore, out var telephone))
|
||||
_telephoneSystem.EndTelephoneCalls((stationAiCore.Value, telephone));
|
||||
_telephoneSystem.EndTelephoneCalls((stationAiCore, telephone));
|
||||
}
|
||||
|
||||
private void OnHolopadActivateProjector(Entity<HolopadComponent> entity, ref HolopadActivateProjectorMessage args)
|
||||
|
|
@ -176,17 +176,17 @@ public sealed class HolopadSystem : SharedHolopadSystem
|
|||
// Link the AI to the holopad they are broadcasting from
|
||||
LinkHolopadToUser(source, args.Actor);
|
||||
|
||||
if (!_stationAiSystem.TryGetStationAiCore((args.Actor, stationAiHeld), out var stationAiCore) ||
|
||||
stationAiCore.Value.Comp.RemoteEntity == null ||
|
||||
if (!_stationAiSystem.TryGetCore(args.Actor, out var stationAiCore) ||
|
||||
stationAiCore.Comp?.RemoteEntity == null ||
|
||||
!TryComp<HolopadComponent>(stationAiCore, out var stationAiCoreHolopad))
|
||||
return;
|
||||
|
||||
// Execute the broadcast, but have it originate from the AI core
|
||||
ExecuteBroadcast((stationAiCore.Value, stationAiCoreHolopad), args.Actor);
|
||||
ExecuteBroadcast((stationAiCore, stationAiCoreHolopad), args.Actor);
|
||||
|
||||
// Switch the AI's perspective from free roaming to the target holopad
|
||||
_xformSystem.SetCoordinates(stationAiCore.Value.Comp.RemoteEntity.Value, Transform(source).Coordinates);
|
||||
_stationAiSystem.SwitchRemoteEntityMode(stationAiCore.Value, false);
|
||||
_xformSystem.SetCoordinates(stationAiCore.Comp.RemoteEntity.Value, Transform(source).Coordinates);
|
||||
_stationAiSystem.SwitchRemoteEntityMode(stationAiCore, false);
|
||||
|
||||
return;
|
||||
}
|
||||
|
|
@ -220,10 +220,10 @@ public sealed class HolopadSystem : SharedHolopadSystem
|
|||
|
||||
reachableAiCores.Add((receiverUid, receiverTelephone));
|
||||
|
||||
if (!_stationAiSystem.TryGetInsertedAI((receiver, receiverStationAiCore), out var insertedAi))
|
||||
if (!_stationAiSystem.TryGetHeld((receiver, receiverStationAiCore), out var insertedAi))
|
||||
continue;
|
||||
|
||||
if (_userInterfaceSystem.TryOpenUi(receiverUid, HolopadUiKey.AiRequestWindow, insertedAi.Value.Owner))
|
||||
if (_userInterfaceSystem.TryOpenUi(receiverUid, HolopadUiKey.AiRequestWindow, insertedAi))
|
||||
LinkHolopadToUser(entity, args.Actor);
|
||||
}
|
||||
|
||||
|
|
@ -274,8 +274,8 @@ public sealed class HolopadSystem : SharedHolopadSystem
|
|||
return;
|
||||
|
||||
// Auto-close the AI request window
|
||||
if (_stationAiSystem.TryGetInsertedAI((entity, stationAiCore), out var insertedAi))
|
||||
_userInterfaceSystem.CloseUi(entity.Owner, HolopadUiKey.AiRequestWindow, insertedAi.Value.Owner);
|
||||
if (_stationAiSystem.TryGetHeld((entity, stationAiCore), out var insertedAi))
|
||||
_userInterfaceSystem.CloseUi(entity.Owner, HolopadUiKey.AiRequestWindow, insertedAi);
|
||||
}
|
||||
|
||||
private void OnTelephoneMessageSent(Entity<HolopadComponent> holopad, ref TelephoneMessageSentEvent args)
|
||||
|
|
@ -381,13 +381,13 @@ public sealed class HolopadSystem : SharedHolopadSystem
|
|||
if (!TryComp<StationAiHeldComponent>(entity, out var entityStationAiHeld))
|
||||
return;
|
||||
|
||||
if (!_stationAiSystem.TryGetStationAiCore((entity, entityStationAiHeld), out var stationAiCore))
|
||||
if (!_stationAiSystem.TryGetCore(entity, out var stationAiCore))
|
||||
return;
|
||||
|
||||
if (!TryComp<TelephoneComponent>(stationAiCore, out var stationAiCoreTelephone))
|
||||
return;
|
||||
|
||||
_telephoneSystem.EndTelephoneCalls((stationAiCore.Value, stationAiCoreTelephone));
|
||||
_telephoneSystem.EndTelephoneCalls((stationAiCore, stationAiCoreTelephone));
|
||||
}
|
||||
|
||||
private void AddToggleProjectorVerb(Entity<HolopadComponent> entity, ref GetVerbsEvent<AlternativeVerb> args)
|
||||
|
|
@ -407,8 +407,8 @@ public sealed class HolopadSystem : SharedHolopadSystem
|
|||
if (!TryComp<StationAiHeldComponent>(user, out var userAiHeld))
|
||||
return;
|
||||
|
||||
if (!_stationAiSystem.TryGetStationAiCore((user, userAiHeld), out var stationAiCore) ||
|
||||
stationAiCore.Value.Comp.RemoteEntity == null)
|
||||
if (!_stationAiSystem.TryGetCore(user, out var stationAiCore) ||
|
||||
stationAiCore.Comp?.RemoteEntity == null)
|
||||
return;
|
||||
|
||||
AlternativeVerb verb = new()
|
||||
|
|
@ -595,17 +595,17 @@ public sealed class HolopadSystem : SharedHolopadSystem
|
|||
{
|
||||
// Check if the associated holopad user is an AI
|
||||
if (TryComp<StationAiHeldComponent>(entity.Comp.User, out var stationAiHeld) &&
|
||||
_stationAiSystem.TryGetStationAiCore((entity.Comp.User.Value, stationAiHeld), out var stationAiCore))
|
||||
_stationAiSystem.TryGetCore(entity.Comp.User.Value, out var stationAiCore))
|
||||
{
|
||||
// Return the AI eye to free roaming
|
||||
_stationAiSystem.SwitchRemoteEntityMode(stationAiCore.Value, true);
|
||||
_stationAiSystem.SwitchRemoteEntityMode(stationAiCore, true);
|
||||
|
||||
// If the AI core is still broadcasting, end its calls
|
||||
if (entity.Owner != stationAiCore.Value.Owner &&
|
||||
if (entity.Owner != stationAiCore.Owner &&
|
||||
TryComp<TelephoneComponent>(stationAiCore, out var stationAiCoreTelephone) &&
|
||||
_telephoneSystem.IsTelephoneEngaged((stationAiCore.Value.Owner, stationAiCoreTelephone)))
|
||||
_telephoneSystem.IsTelephoneEngaged((stationAiCore.Owner, stationAiCoreTelephone)))
|
||||
{
|
||||
_telephoneSystem.EndTelephoneCalls((stationAiCore.Value.Owner, stationAiCoreTelephone));
|
||||
_telephoneSystem.EndTelephoneCalls((stationAiCore.Owner, stationAiCoreTelephone));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -625,8 +625,8 @@ public sealed class HolopadSystem : SharedHolopadSystem
|
|||
if (!TryComp<StationAiHeldComponent>(user, out var userAiHeld))
|
||||
return;
|
||||
|
||||
if (!_stationAiSystem.TryGetStationAiCore((user, userAiHeld), out var stationAiCore) ||
|
||||
stationAiCore.Value.Comp.RemoteEntity == null)
|
||||
if (!_stationAiSystem.TryGetCore(user, out var stationAiCore) ||
|
||||
stationAiCore.Comp?.RemoteEntity == null)
|
||||
return;
|
||||
|
||||
if (!TryComp<TelephoneComponent>(stationAiCore, out var stationAiTelephone))
|
||||
|
|
@ -635,7 +635,7 @@ public sealed class HolopadSystem : SharedHolopadSystem
|
|||
if (!TryComp<HolopadComponent>(stationAiCore, out var stationAiHolopad))
|
||||
return;
|
||||
|
||||
var source = new Entity<TelephoneComponent>(stationAiCore.Value, stationAiTelephone);
|
||||
var source = new Entity<TelephoneComponent>(stationAiCore, stationAiTelephone);
|
||||
|
||||
// Check if the AI is unable to activate the projector (unlikely this will ever pass; its just a safeguard)
|
||||
if (!_telephoneSystem.IsSourceInRangeOfReceiver(source, receiver))
|
||||
|
|
@ -658,11 +658,11 @@ public sealed class HolopadSystem : SharedHolopadSystem
|
|||
if (!_telephoneSystem.IsSourceConnectedToReceiver(source, receiver))
|
||||
return;
|
||||
|
||||
LinkHolopadToUser((stationAiCore.Value, stationAiHolopad), user);
|
||||
LinkHolopadToUser((stationAiCore, stationAiHolopad), user);
|
||||
|
||||
// Switch the AI's perspective from free roaming to the target holopad
|
||||
_xformSystem.SetCoordinates(stationAiCore.Value.Comp.RemoteEntity.Value, Transform(entity).Coordinates);
|
||||
_stationAiSystem.SwitchRemoteEntityMode(stationAiCore.Value, false);
|
||||
_xformSystem.SetCoordinates(stationAiCore.Comp.RemoteEntity.Value, Transform(entity).Coordinates);
|
||||
_stationAiSystem.SwitchRemoteEntityMode(stationAiCore, false);
|
||||
|
||||
// Open the holopad UI if it hasn't been opened yet
|
||||
if (TryComp<UserInterfaceComponent>(entity, out var entityUserInterfaceComponent))
|
||||
|
|
|
|||
|
|
@ -80,6 +80,7 @@ namespace Content.Server.IoC
|
|||
IoCManager.Register<IWatchlistWebhookManager, WatchlistWebhookManager>();
|
||||
IoCManager.Register<ConnectionManager>();
|
||||
IoCManager.Register<MultiServerKickManager>();
|
||||
IoCManager.Register<CVarControlManager>();
|
||||
|
||||
// Sunrise-Start
|
||||
IoCManager.Register<ServersHubManager>();
|
||||
|
|
|
|||
11
Content.Server/Light/Components/SetRoofComponent.cs
Normal file
11
Content.Server/Light/Components/SetRoofComponent.cs
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
namespace Content.Server.Light.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Applies the roof flag to this tile and deletes the entity.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class SetRoofComponent : Component
|
||||
{
|
||||
[DataField(required: true)]
|
||||
public bool Value;
|
||||
}
|
||||
22
Content.Server/Light/EntitySystems/LightCycleSystem.cs
Normal file
22
Content.Server/Light/EntitySystems/LightCycleSystem.cs
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
using Content.Shared;
|
||||
using Content.Shared.Light.Components;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
namespace Content.Server.Light.EntitySystems;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public sealed class LightCycleSystem : SharedLightCycleSystem
|
||||
{
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
|
||||
protected override void OnCycleMapInit(Entity<LightCycleComponent> ent, ref MapInitEvent args)
|
||||
{
|
||||
base.OnCycleMapInit(ent, ref args);
|
||||
|
||||
if (ent.Comp.InitialOffset)
|
||||
{
|
||||
ent.Comp.Offset = _random.Next(ent.Comp.Duration);
|
||||
Dirty(ent);
|
||||
}
|
||||
}
|
||||
}
|
||||
33
Content.Server/Light/EntitySystems/RoofSystem.cs
Normal file
33
Content.Server/Light/EntitySystems/RoofSystem.cs
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
using Content.Server.Light.Components;
|
||||
using Content.Shared.Light.EntitySystems;
|
||||
using Robust.Shared.Map.Components;
|
||||
|
||||
namespace Content.Server.Light.EntitySystems;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public sealed class RoofSystem : SharedRoofSystem
|
||||
{
|
||||
[Dependency] private readonly SharedMapSystem _maps = default!;
|
||||
|
||||
private EntityQuery<MapGridComponent> _gridQuery;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
_gridQuery = GetEntityQuery<MapGridComponent>();
|
||||
SubscribeLocalEvent<SetRoofComponent, ComponentStartup>(OnFlagStartup);
|
||||
}
|
||||
|
||||
private void OnFlagStartup(Entity<SetRoofComponent> ent, ref ComponentStartup args)
|
||||
{
|
||||
var xform = Transform(ent.Owner);
|
||||
|
||||
if (_gridQuery.TryComp(xform.GridUid, out var grid))
|
||||
{
|
||||
var index = _maps.LocalToTile(xform.GridUid.Value, grid, xform.Coordinates);
|
||||
SetRoof((xform.GridUid.Value, grid, null), index, ent.Comp.Value);
|
||||
}
|
||||
|
||||
QueueDel(ent.Owner);
|
||||
}
|
||||
}
|
||||
|
|
@ -3,12 +3,14 @@ using Content.Server.Administration;
|
|||
using Content.Server.GameTicking;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.CCVar;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.Maps;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.EntitySerialization;
|
||||
using Robust.Shared.EntitySerialization.Systems;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.Mapping
|
||||
{
|
||||
|
|
@ -34,6 +36,8 @@ namespace Content.Server.Mapping
|
|||
var opts = CompletionHelper.UserFilePath(args[1], res.UserData)
|
||||
.Concat(CompletionHelper.ContentFilePath(args[1], res));
|
||||
return CompletionResult.FromHintOptions(opts, Loc.GetString("cmd-hint-mapping-path"));
|
||||
case 3:
|
||||
return CompletionResult.FromHintOptions(["false", "true"], Loc.GetString("cmd-mapping-hint-grid"));
|
||||
}
|
||||
return CompletionResult.Empty;
|
||||
}
|
||||
|
|
@ -46,7 +50,7 @@ namespace Content.Server.Mapping
|
|||
return;
|
||||
}
|
||||
|
||||
if (args.Length > 2)
|
||||
if (args.Length > 3)
|
||||
{
|
||||
shell.WriteLine(Help);
|
||||
return;
|
||||
|
|
@ -56,12 +60,20 @@ namespace Content.Server.Mapping
|
|||
shell.WriteLine(Loc.GetString("cmd-mapping-warning"));
|
||||
#endif
|
||||
|
||||
// For backwards compatibility, isGrid is optional and we allow mappers to try load grids without explicitly
|
||||
// specifying that they are loading a grid. Currently content is not allowed to override a map's MapId, so
|
||||
// without engine changes this needs to be done by brute force by just trying to load it as a map first.
|
||||
// This can result in errors being logged if the file is actually a grid, but the command should still work.
|
||||
// yipeeee
|
||||
bool? isGrid = args.Length < 3 ? null : bool.Parse(args[2]);
|
||||
|
||||
MapId mapId;
|
||||
string? toLoad = null;
|
||||
var mapSys = _entities.System<SharedMapSystem>();
|
||||
Entity<MapGridComponent>? grid = null;
|
||||
|
||||
// Get the map ID to use
|
||||
if (args.Length is 1 or 2)
|
||||
if (args.Length > 0)
|
||||
{
|
||||
if (!int.TryParse(args[0], out var intMapId))
|
||||
{
|
||||
|
|
@ -78,7 +90,7 @@ namespace Content.Server.Mapping
|
|||
return;
|
||||
}
|
||||
|
||||
if (_map.MapExists(mapId))
|
||||
if (mapSys.MapExists(mapId))
|
||||
{
|
||||
shell.WriteError(Loc.GetString("cmd-mapping-exists", ("mapId", mapId)));
|
||||
return;
|
||||
|
|
@ -91,12 +103,44 @@ namespace Content.Server.Mapping
|
|||
}
|
||||
else
|
||||
{
|
||||
var loadOptions = new MapLoadOptions {StoreMapUids = true};
|
||||
_entities.System<MapLoaderSystem>().TryLoad(mapId, args[1], out _, loadOptions);
|
||||
var path = new ResPath(args[1]);
|
||||
toLoad = path.FilenameWithoutExtension;
|
||||
var opts = new DeserializationOptions {StoreYamlUids = true};
|
||||
var loader = _entities.System<MapLoaderSystem>();
|
||||
|
||||
if (isGrid == true)
|
||||
{
|
||||
mapSys.CreateMap(mapId, runMapInit: false);
|
||||
if (!loader.TryLoadGrid(mapId, path, out grid, opts))
|
||||
{
|
||||
shell.WriteError(Loc.GetString("cmd-mapping-error"));
|
||||
mapSys.DeleteMap(mapId);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (!loader.TryLoadMapWithId(mapId, path, out _, out _, opts))
|
||||
{
|
||||
if (isGrid == false)
|
||||
{
|
||||
shell.WriteError(Loc.GetString("cmd-mapping-error"));
|
||||
return;
|
||||
}
|
||||
|
||||
// isGrid was not specified and loading it as a map failed, so we fall back to trying to load
|
||||
// the file as a grid
|
||||
shell.WriteLine(Loc.GetString("cmd-mapping-try-grid"));
|
||||
mapSys.CreateMap(mapId, runMapInit: false);
|
||||
if (!loader.TryLoadGrid(mapId, path, out grid, opts))
|
||||
{
|
||||
shell.WriteError(Loc.GetString("cmd-mapping-error"));
|
||||
mapSys.DeleteMap(mapId);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// was the map actually created or did it fail somehow?
|
||||
if (!_map.MapExists(mapId))
|
||||
if (!mapSys.MapExists(mapId))
|
||||
{
|
||||
shell.WriteError(Loc.GetString("cmd-mapping-error"));
|
||||
return;
|
||||
|
|
@ -115,19 +159,25 @@ namespace Content.Server.Mapping
|
|||
}
|
||||
|
||||
// don't interrupt mapping with events or auto-shuttle
|
||||
shell.ExecuteCommand("sudo cvar events.enabled false");
|
||||
shell.ExecuteCommand("sudo cvar shuttle.auto_call_time 0");
|
||||
shell.ExecuteCommand("changecvar events.enabled false");
|
||||
shell.ExecuteCommand("changecvar shuttle.auto_call_time 0");
|
||||
|
||||
var auto = _entities.System<MappingSystem>();
|
||||
if (grid != null)
|
||||
auto.ToggleAutosave(grid.Value.Owner, toLoad ?? "NEWGRID");
|
||||
else
|
||||
auto.ToggleAutosave(mapId, toLoad ?? "NEWMAP");
|
||||
|
||||
if (_cfg.GetCVar(CCVars.AutosaveEnabled))
|
||||
shell.ExecuteCommand($"toggleautosave {mapId} {toLoad ?? "NEWMAP"}");
|
||||
shell.ExecuteCommand($"tp 0 0 {mapId}");
|
||||
shell.RemoteExecuteCommand("mappingclientsidesetup");
|
||||
_map.SetMapPaused(mapId, true);
|
||||
DebugTools.Assert(mapSys.IsPaused(mapId));
|
||||
|
||||
if (args.Length == 2)
|
||||
shell.WriteLine(Loc.GetString("cmd-mapping-success-load",("mapId",mapId),("path", args[1])));
|
||||
else
|
||||
if (args.Length != 2)
|
||||
shell.WriteLine(Loc.GetString("cmd-mapping-success", ("mapId", mapId)));
|
||||
else if (grid == null)
|
||||
shell.WriteLine(Loc.GetString("cmd-mapping-success-load", ("mapId", mapId), ("path", args[1])));
|
||||
else
|
||||
shell.WriteLine(Loc.GetString("cmd-mapping-success-load-grid", ("mapId", mapId), ("path", args[1])));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ using Content.Shared.Administration;
|
|||
using Content.Shared.Mapping;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.EntitySerialization;
|
||||
using Robust.Shared.EntitySerialization.Systems;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Serialization;
|
||||
|
|
@ -21,6 +23,7 @@ public sealed class MappingManager : IPostInjectInit
|
|||
[Dependency] private readonly IServerNetManager _net = default!;
|
||||
[Dependency] private readonly IPlayerManager _players = default!;
|
||||
[Dependency] private readonly IEntitySystemManager _systems = default!;
|
||||
[Dependency] private readonly IEntityManager _ent = default!;
|
||||
|
||||
private ISawmill _sawmill = default!;
|
||||
private ZStdCompressionContext _zstd = default!;
|
||||
|
|
@ -45,14 +48,14 @@ public sealed class MappingManager : IPostInjectInit
|
|||
if (!_players.TryGetSessionByChannel(message.MsgChannel, out var session) ||
|
||||
!_admin.IsAdmin(session, true) ||
|
||||
!_admin.HasAdminFlag(session, AdminFlags.Host) ||
|
||||
session.AttachedEntity is not { } player)
|
||||
!_ent.TryGetComponent(session.AttachedEntity, out TransformComponent? xform) ||
|
||||
xform.MapUid is not {} mapUid)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var mapId = _systems.GetEntitySystem<TransformSystem>().GetMapCoordinates(player).MapId;
|
||||
var mapEntity = _map.GetMapEntityIdOrThrow(mapId);
|
||||
var data = _systems.GetEntitySystem<MapLoaderSystem>().GetSaveData(mapEntity);
|
||||
var sys = _systems.GetEntitySystem<MapLoaderSystem>();
|
||||
var data = sys.SerializeEntitiesRecursive([mapUid]).Node;
|
||||
var document = new YamlDocument(data.ToYaml());
|
||||
var stream = new YamlStream { document };
|
||||
var writer = new StringWriter();
|
||||
|
|
|
|||
|
|
@ -2,12 +2,12 @@ using System.IO;
|
|||
using Content.Server.Administration;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.CCVar;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.Maps;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.EntitySerialization.Systems;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
|
|
@ -21,16 +21,16 @@ public sealed class MappingSystem : EntitySystem
|
|||
[Dependency] private readonly IConsoleHost _conHost = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] private readonly SharedMapSystem _map = default!;
|
||||
[Dependency] private readonly IResourceManager _resMan = default!;
|
||||
[Dependency] private readonly MapLoaderSystem _map = default!;
|
||||
[Dependency] private readonly MapLoaderSystem _loader = default!;
|
||||
|
||||
// Not a comp because I don't want to deal with this getting saved onto maps ever
|
||||
/// <summary>
|
||||
/// map id -> next autosave timespan & original filename.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private Dictionary<MapId, (TimeSpan next, string fileName)> _currentlyAutosaving = new();
|
||||
private Dictionary<EntityUid, (TimeSpan next, string fileName)> _currentlyAutosaving = new();
|
||||
|
||||
private bool _autosaveEnabled;
|
||||
|
||||
|
|
@ -60,25 +60,29 @@ public sealed class MappingSystem : EntitySystem
|
|||
if (!_autosaveEnabled)
|
||||
return;
|
||||
|
||||
foreach (var (map, (time, name))in _currentlyAutosaving.ToArray())
|
||||
foreach (var (uid, (time, name))in _currentlyAutosaving)
|
||||
{
|
||||
if (_timing.RealTime <= time)
|
||||
continue;
|
||||
|
||||
if (!_mapManager.MapExists(map) || _mapManager.IsMapInitialized(map))
|
||||
if (LifeStage(uid) >= EntityLifeStage.MapInitialized)
|
||||
{
|
||||
Log.Warning($"Can't autosave map {map}; it doesn't exist, or is initialized. Removing from autosave.");
|
||||
_currentlyAutosaving.Remove(map);
|
||||
return;
|
||||
Log.Warning($"Can't autosave entity {uid}; it doesn't exist, or is initialized. Removing from autosave.");
|
||||
_currentlyAutosaving.Remove(uid);
|
||||
continue;
|
||||
}
|
||||
|
||||
_currentlyAutosaving[uid] = (CalculateNextTime(), name);
|
||||
var saveDir = Path.Combine(_cfg.GetCVar(CCVars.AutosaveDirectory), name);
|
||||
_resMan.UserData.CreateDir(new ResPath(saveDir).ToRootedPath());
|
||||
|
||||
var path = Path.Combine(saveDir, $"{DateTime.Now.ToString("yyyy-M-dd_HH.mm.ss")}-AUTO.yml");
|
||||
_currentlyAutosaving[map] = (CalculateNextTime(), name);
|
||||
Log.Info($"Autosaving map {name} ({map}) to {path}. Next save in {ReadableTimeLeft(map)} seconds.");
|
||||
_map.SaveMap(map, path);
|
||||
var path = new ResPath(Path.Combine(saveDir, $"{DateTime.Now:yyyy-M-dd_HH.mm.ss}-AUTO.yml"));
|
||||
Log.Info($"Autosaving map {name} ({uid}) to {path}. Next save in {ReadableTimeLeft(uid)} seconds.");
|
||||
|
||||
if (HasComp<MapComponent>(uid))
|
||||
_loader.TrySaveMap(uid, path);
|
||||
else
|
||||
_loader.TrySaveGrid(uid, path);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -87,34 +91,41 @@ public sealed class MappingSystem : EntitySystem
|
|||
return _timing.RealTime + TimeSpan.FromSeconds(_cfg.GetCVar(CCVars.AutosaveInterval));
|
||||
}
|
||||
|
||||
private double ReadableTimeLeft(MapId map)
|
||||
private double ReadableTimeLeft(EntityUid uid)
|
||||
{
|
||||
return Math.Round(_currentlyAutosaving[map].next.TotalSeconds - _timing.RealTime.TotalSeconds);
|
||||
return Math.Round(_currentlyAutosaving[uid].next.TotalSeconds - _timing.RealTime.TotalSeconds);
|
||||
}
|
||||
|
||||
#region Public API
|
||||
|
||||
public void ToggleAutosave(MapId map, string? path=null)
|
||||
public void ToggleAutosave(MapId map, string? path = null)
|
||||
{
|
||||
if (_map.TryGetMap(map, out var uid))
|
||||
ToggleAutosave(uid.Value, path);
|
||||
}
|
||||
|
||||
public void ToggleAutosave(EntityUid uid, string? path=null)
|
||||
{
|
||||
if (!_autosaveEnabled)
|
||||
return;
|
||||
|
||||
if (path != null && _currentlyAutosaving.TryAdd(map, (CalculateNextTime(), Path.GetFileName(path))))
|
||||
{
|
||||
if (!_mapManager.MapExists(map) || _mapManager.IsMapInitialized(map))
|
||||
{
|
||||
Log.Warning("Tried to enable autosaving on non-existant or already initialized map!");
|
||||
_currentlyAutosaving.Remove(map);
|
||||
return;
|
||||
}
|
||||
if (_currentlyAutosaving.Remove(uid) || path == null)
|
||||
return;
|
||||
|
||||
Log.Info($"Started autosaving map {path} ({map}). Next save in {ReadableTimeLeft(map)} seconds.");
|
||||
}
|
||||
else
|
||||
if (LifeStage(uid) >= EntityLifeStage.MapInitialized)
|
||||
{
|
||||
_currentlyAutosaving.Remove(map);
|
||||
Log.Info($"Stopped autosaving on map {map}");
|
||||
Log.Error("Tried to enable autosaving on a post map-init entity.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!HasComp<MapComponent>(uid) && !HasComp<MapGridComponent>(uid))
|
||||
{
|
||||
Log.Error($"{ToPrettyString(uid)} is neither a grid or map");
|
||||
return;
|
||||
}
|
||||
|
||||
_currentlyAutosaving[uid] = (CalculateNextTime(), Path.GetFileName(path));
|
||||
Log.Info($"Started autosaving map {path} ({uid}). Next save in {ReadableTimeLeft(uid)} seconds.");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
|
|
|||
|
|
@ -25,6 +25,11 @@ public sealed partial class GameMapPrototype : IPrototype
|
|||
[DataField]
|
||||
public float MaxRandomOffset = 1000f;
|
||||
|
||||
/// <summary>
|
||||
/// Turns out some of the map files are actually secretly grids. Excellent. I love map loading code.
|
||||
/// </summary>
|
||||
[DataField] public bool IsGrid;
|
||||
|
||||
[DataField]
|
||||
public bool RandomRotation = true;
|
||||
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.Maps;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.EntitySerialization.Systems;
|
||||
using Robust.Shared.Map.Events;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.Markdown;
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
using System.Linq;
|
||||
using Content.Server.Administration;
|
||||
using Content.Shared.Administration;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.Maps;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.EntitySerialization;
|
||||
using Robust.Shared.EntitySerialization.Components;
|
||||
using Robust.Shared.EntitySerialization.Systems;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.Maps;
|
||||
|
|
@ -17,8 +17,8 @@ namespace Content.Server.Maps;
|
|||
public sealed class ResaveCommand : LocalizedCommands
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] private readonly IResourceManager _res = default!;
|
||||
[Dependency] private readonly ILogManager _log = default!;
|
||||
|
||||
public override string Command => "resave";
|
||||
|
||||
|
|
@ -26,32 +26,56 @@ public sealed class ResaveCommand : LocalizedCommands
|
|||
{
|
||||
var loader = _entManager.System<MapLoaderSystem>();
|
||||
|
||||
foreach (var fn in _res.ContentFindFiles(new ResPath("/Maps/")))
|
||||
var opts = MapLoadOptions.Default with
|
||||
{
|
||||
var mapId = _mapManager.CreateMap();
|
||||
_mapManager.AddUninitializedMap(mapId);
|
||||
loader.Load(mapId, fn.ToString(), new MapLoadOptions()
|
||||
|
||||
DeserializationOptions = DeserializationOptions.Default with
|
||||
{
|
||||
StoreMapUids = true,
|
||||
LoadMap = true,
|
||||
});
|
||||
StoreYamlUids = true,
|
||||
LogOrphanedGrids = false
|
||||
}
|
||||
};
|
||||
|
||||
var log = _log.GetSawmill(Command);
|
||||
var files = _res.ContentFindFiles(new ResPath("/Maps/")).ToList();
|
||||
|
||||
for (var i = 0; i < files.Count; i++)
|
||||
{
|
||||
var fn = files[i];
|
||||
log.Info($"Re-saving file {i}/{files.Count} : {fn}");
|
||||
|
||||
if (!loader.TryLoadGeneric(fn, out var result, opts))
|
||||
continue;
|
||||
|
||||
if (result.Maps.Count != 1)
|
||||
{
|
||||
shell.WriteError(
|
||||
$"Multi-map or multi-grid files like {fn} are not yet supported by the {Command} command");
|
||||
loader.Delete(result);
|
||||
continue;
|
||||
}
|
||||
|
||||
var map = result.Maps.First();
|
||||
|
||||
// Process deferred component removals.
|
||||
_entManager.CullRemovedComponents();
|
||||
|
||||
var mapUid = _mapManager.GetMapEntityId(mapId);
|
||||
var mapXform = _entManager.GetComponent<TransformComponent>(mapUid);
|
||||
|
||||
if (_entManager.HasComponent<LoadedMapComponent>(mapUid) || mapXform.ChildCount != 1)
|
||||
if (_entManager.HasComponent<LoadedMapComponent>(map))
|
||||
{
|
||||
loader.SaveMap(mapId, fn.ToString());
|
||||
loader.TrySaveMap(map.Comp.MapId, fn);
|
||||
}
|
||||
else if (mapXform.ChildEnumerator.MoveNext(out var child))
|
||||
else if (result.Grids.Count == 1)
|
||||
{
|
||||
loader.Save(child, fn.ToString());
|
||||
loader.TrySaveGrid(result.Grids.First(), fn);
|
||||
}
|
||||
else
|
||||
{
|
||||
shell.WriteError($"Failed to resave {fn}");
|
||||
}
|
||||
|
||||
_mapManager.DeleteMap(mapId);
|
||||
loader.Delete(result);
|
||||
}
|
||||
|
||||
shell.WriteLine($"Resaved all maps");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ public sealed class HealingSystem : EntitySystem
|
|||
if (healing.ModifyBloodLevel != 0)
|
||||
_bloodstreamSystem.TryModifyBloodLevel(entity.Owner, healing.ModifyBloodLevel);
|
||||
|
||||
var healed = _damageable.TryChangeDamage(entity.Owner, healing.Damage, true, origin: args.Args.User);
|
||||
var healed = _damageable.TryChangeDamage(entity.Owner, healing.Damage * _damageable.UniversalTopicalsHealModifier, true, origin: args.Args.User);
|
||||
|
||||
if (healed == null && healing.BloodlossModifier != 0)
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -27,6 +27,9 @@ public sealed class MousetrapSystem : EntitySystem
|
|||
|
||||
private void OnUseInHand(EntityUid uid, MousetrapComponent component, UseInHandEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
component.IsActive = !component.IsActive;
|
||||
_popupSystem.PopupEntity(component.IsActive
|
||||
? Loc.GetString("mousetrap-on-activate")
|
||||
|
|
@ -35,6 +38,8 @@ public sealed class MousetrapSystem : EntitySystem
|
|||
args.User);
|
||||
|
||||
UpdateVisuals(uid);
|
||||
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void OnStepTriggerAttempt(EntityUid uid, MousetrapComponent component, ref StepTriggerAttemptEvent args)
|
||||
|
|
|
|||
|
|
@ -37,6 +37,8 @@ public sealed class PAISystem : SharedPAISystem
|
|||
|
||||
private void OnUseInHand(EntityUid uid, PAIComponent component, UseInHandEvent args)
|
||||
{
|
||||
// Not checking for Handled because ToggleableGhostRoleSystem already marks it as such.
|
||||
|
||||
if (!TryComp<MindContainerComponent>(uid, out var mind) || !mind.HasMind)
|
||||
component.LastUser = args.User;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ using Content.Shared.Atmos;
|
|||
using Content.Shared.Decals;
|
||||
using Content.Shared.Ghost;
|
||||
using Content.Shared.Gravity;
|
||||
using Content.Shared.Light.Components;
|
||||
using Content.Shared.Parallax.Biomes;
|
||||
using Content.Shared.Parallax.Biomes.Layers;
|
||||
using Content.Shared.Parallax.Biomes.Markers;
|
||||
|
|
@ -331,6 +332,9 @@ public sealed partial class BiomeSystem : SharedBiomeSystem
|
|||
|
||||
while (biomes.MoveNext(out var biome))
|
||||
{
|
||||
if (biome.LifeStage < ComponentLifeStage.Running)
|
||||
continue;
|
||||
|
||||
_activeChunks.Add(biome, _tilePool.Get());
|
||||
_markerChunks.GetOrNew(biome);
|
||||
}
|
||||
|
|
@ -380,6 +384,10 @@ public sealed partial class BiomeSystem : SharedBiomeSystem
|
|||
|
||||
while (loadBiomes.MoveNext(out var gridUid, out var biome, out var grid))
|
||||
{
|
||||
// If not MapInit don't run it.
|
||||
if (biome.LifeStage < ComponentLifeStage.Running)
|
||||
continue;
|
||||
|
||||
if (!biome.Enabled)
|
||||
continue;
|
||||
|
||||
|
|
@ -745,7 +753,10 @@ public sealed partial class BiomeSystem : SharedBiomeSystem
|
|||
}
|
||||
|
||||
if (modified.Count == 0)
|
||||
{
|
||||
component.ModifiedTiles.Remove(chunk);
|
||||
_tilePool.Return(modified);
|
||||
}
|
||||
|
||||
component.PendingMarkers.Remove(chunk);
|
||||
}
|
||||
|
|
@ -1014,11 +1025,14 @@ public sealed partial class BiomeSystem : SharedBiomeSystem
|
|||
// Midday: #E6CB8B
|
||||
// Moonlight: #2b3143
|
||||
// Lava: #A34931
|
||||
|
||||
var light = EnsureComp<MapLightComponent>(mapUid);
|
||||
light.AmbientLightColor = mapLight ?? Color.FromHex("#D8B059");
|
||||
Dirty(mapUid, light, metadata);
|
||||
|
||||
EnsureComp<RoofComponent>(mapUid);
|
||||
|
||||
EnsureComp<LightCycleComponent>(mapUid);
|
||||
|
||||
var moles = new float[Atmospherics.AdjustedNumberOfGases];
|
||||
moles[(int) Gas.Oxygen] = 21.824779f;
|
||||
moles[(int) Gas.Nitrogen] = 82.10312f;
|
||||
|
|
|
|||
|
|
@ -109,7 +109,7 @@ public sealed class ConveyorController : SharedConveyorController
|
|||
component.State = state;
|
||||
|
||||
if (TryComp<PhysicsComponent>(uid, out var physics))
|
||||
_broadphase.RegenerateContacts(uid, physics);
|
||||
_broadphase.RegenerateContacts((uid, physics));
|
||||
|
||||
UpdateAppearance(uid, component);
|
||||
Dirty(uid, component);
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ namespace Content.Server.Popups
|
|||
{
|
||||
[Dependency] private readonly IPlayerManager _player = default!;
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
[Dependency] private readonly TransformSystem _xform = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
|
||||
public override void PopupCursor(string? message, PopupType type = PopupType.Small)
|
||||
{
|
||||
|
|
@ -47,8 +47,7 @@ namespace Content.Server.Popups
|
|||
{
|
||||
if (message == null)
|
||||
return;
|
||||
|
||||
var mapPos = coordinates.ToMap(EntityManager, _xform);
|
||||
var mapPos = _transform.ToMapCoordinates(coordinates);
|
||||
var filter = Filter.Empty().AddPlayersByPvs(mapPos, entManager: EntityManager, playerMan: _player, cfgMan: _cfg);
|
||||
RaiseNetworkEvent(new PopupCoordinatesEvent(message, type, GetNetCoordinates(coordinates)), filter);
|
||||
}
|
||||
|
|
@ -70,6 +69,21 @@ namespace Content.Server.Popups
|
|||
RaiseNetworkEvent(new PopupCoordinatesEvent(message, type, GetNetCoordinates(coordinates)), actor.PlayerSession);
|
||||
}
|
||||
|
||||
public override void PopupPredictedCoordinates(string? message, EntityCoordinates coordinates, EntityUid? recipient, PopupType type = PopupType.Small)
|
||||
{
|
||||
if (message == null)
|
||||
return;
|
||||
|
||||
var mapPos = _transform.ToMapCoordinates(coordinates);
|
||||
var filter = Filter.Empty().AddPlayersByPvs(mapPos, entManager: EntityManager, playerMan: _player, cfgMan: _cfg);
|
||||
if (recipient != null)
|
||||
{
|
||||
// Don't send to recipient, since they predicted it locally
|
||||
filter = filter.RemovePlayerByAttachedEntity(recipient.Value);
|
||||
}
|
||||
RaiseNetworkEvent(new PopupCoordinatesEvent(message, type, GetNetCoordinates(coordinates)), filter);
|
||||
}
|
||||
|
||||
public override void PopupEntity(string? message, EntityUid uid, PopupType type = PopupType.Small)
|
||||
{
|
||||
if (message == null)
|
||||
|
|
|
|||
|
|
@ -15,10 +15,13 @@ using Robust.Server.GameObjects;
|
|||
using Robust.Shared.Collections;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.EntitySerialization;
|
||||
using Robust.Shared.EntitySerialization.Systems;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.Procedural;
|
||||
|
||||
|
|
@ -173,14 +176,18 @@ public sealed partial class DungeonSystem : SharedDungeonSystem
|
|||
return Transform(uid).MapID;
|
||||
}
|
||||
|
||||
var mapId = _mapManager.CreateMap();
|
||||
_mapManager.AddUninitializedMap(mapId);
|
||||
_loader.Load(mapId, proto.AtlasPath.ToString());
|
||||
var mapUid = _mapManager.GetMapEntityId(mapId);
|
||||
_mapManager.SetMapPaused(mapId, true);
|
||||
comp = AddComp<DungeonAtlasTemplateComponent>(mapUid);
|
||||
var opts = new MapLoadOptions
|
||||
{
|
||||
DeserializationOptions = DeserializationOptions.Default with {PauseMaps = true},
|
||||
ExpectedCategory = FileCategory.Map
|
||||
};
|
||||
|
||||
if (!_loader.TryLoadGeneric(proto.AtlasPath, out var res, opts) || !res.Maps.TryFirstOrNull(out var map))
|
||||
throw new Exception($"Failed to load dungeon template.");
|
||||
|
||||
comp = AddComp<DungeonAtlasTemplateComponent>(map.Value.Owner);
|
||||
comp.Path = proto.AtlasPath;
|
||||
return mapId;
|
||||
return map.Value.Comp.MapId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ public sealed class ProjectileSystem : SharedProjectileSystem
|
|||
return;
|
||||
}
|
||||
|
||||
var ev = new ProjectileHitEvent(component.Damage, target, component.Shooter);
|
||||
var ev = new ProjectileHitEvent(component.Damage * _damageableSystem.UniversalProjectileDamageModifier, target, component.Shooter);
|
||||
RaiseLocalEvent(uid, ref ev);
|
||||
|
||||
var otherName = ToPrettyString(target);
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ using Content.Shared.Mobs.Components;
|
|||
using Content.Shared.Procedural;
|
||||
using Content.Shared.Radio;
|
||||
using Content.Shared.Salvage.Magnet;
|
||||
using Robust.Server.Maps;
|
||||
using Robust.Shared.Exceptions;
|
||||
using Robust.Shared.Map;
|
||||
|
||||
|
|
@ -291,15 +290,10 @@ public sealed partial class SalvageSystem
|
|||
case SalvageOffering wreck:
|
||||
var salvageProto = wreck.SalvageMap;
|
||||
|
||||
var opts = new MapLoadOptions
|
||||
{
|
||||
Offset = new Vector2(0, 0)
|
||||
};
|
||||
|
||||
if (!_map.TryLoad(salvMapXform.MapID, salvageProto.MapPath.ToString(), out _, opts))
|
||||
if (!_loader.TryLoadGrid(salvMapXform.MapID, salvageProto.MapPath, out _))
|
||||
{
|
||||
Report(magnet, MagnetChannel, "salvage-system-announcement-spawn-debris-disintegrated");
|
||||
_mapManager.DeleteMap(salvMapXform.MapID);
|
||||
_mapSystem.DeleteMap(salvMapXform.MapID);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,38 +1,23 @@
|
|||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Content.Server.Cargo.Systems;
|
||||
using Content.Server.Construction;
|
||||
using Content.Server.GameTicking;
|
||||
using Content.Server.Radio.EntitySystems;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Radio;
|
||||
using Content.Shared.Salvage;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Utility;
|
||||
using Content.Server.Chat.Managers;
|
||||
using Content.Server.Gravity;
|
||||
using Content.Server.Parallax;
|
||||
using Content.Server.Procedural;
|
||||
using Content.Server.Shuttles.Systems;
|
||||
using Content.Server.Station.Systems;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Construction.EntitySystems;
|
||||
using Content.Shared.Random;
|
||||
using Content.Shared.Random.Helpers;
|
||||
using Content.Shared.Tools.Components;
|
||||
using Robust.Server.Maps;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Timing;
|
||||
using Content.Server.Labels;
|
||||
using Robust.Shared.EntitySerialization.Systems;
|
||||
|
||||
namespace Content.Server.Salvage
|
||||
{
|
||||
|
|
@ -50,7 +35,7 @@ namespace Content.Server.Salvage
|
|||
[Dependency] private readonly DungeonSystem _dungeon = default!;
|
||||
[Dependency] private readonly GravitySystem _gravity = default!;
|
||||
[Dependency] private readonly LabelSystem _labelSystem = default!;
|
||||
[Dependency] private readonly MapLoaderSystem _map = default!;
|
||||
[Dependency] private readonly MapLoaderSystem _loader = default!;
|
||||
[Dependency] private readonly MetaDataSystem _metaData = default!;
|
||||
[Dependency] private readonly RadioSystem _radioSystem = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
|
|
|
|||
|
|
@ -30,11 +30,13 @@ using Robust.Server.GameObjects;
|
|||
using Robust.Shared.Collections;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.EntitySerialization.Systems;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
using TimedDespawnComponent = Robust.Shared.Spawners.TimedDespawnComponent;
|
||||
|
||||
namespace Content.Server.Shuttles.Systems;
|
||||
|
|
@ -338,7 +340,7 @@ public sealed class ArrivalsSystem : EntitySystem
|
|||
return;
|
||||
|
||||
// We use arrivals as the default spawn so don't check for job prio.
|
||||
|
||||
|
||||
// Sunrise-Start
|
||||
if (ev.DesiredSpawnPointType == SpawnPointType.Job)
|
||||
return;
|
||||
|
|
@ -510,55 +512,53 @@ public sealed class ArrivalsSystem : EntitySystem
|
|||
}
|
||||
}
|
||||
|
||||
// Sunrise-Edit
|
||||
// private void OnRoundStarting(RoundStartingEvent ev)
|
||||
// {
|
||||
// // Setup arrivals station
|
||||
// if (!Enabled)
|
||||
// return;
|
||||
//
|
||||
// SetupArrivalsStation();
|
||||
// }
|
||||
//
|
||||
// private void SetupArrivalsStation()
|
||||
// {
|
||||
// var mapUid = _mapSystem.CreateMap(out var mapId, false);
|
||||
// _metaData.SetEntityName(mapUid, Loc.GetString("map-name-terminal"));
|
||||
//
|
||||
// if (!_loader.TryLoad(mapId, _cfgManager.GetCVar(CCVars.ArrivalsMap), out var uids))
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// foreach (var id in uids)
|
||||
// {
|
||||
// EnsureComp<ArrivalsSourceComponent>(id);
|
||||
// EnsureComp<ProtectedGridComponent>(id);
|
||||
// EnsureComp<PreventPilotComponent>(id);
|
||||
// }
|
||||
//
|
||||
// // Setup planet arrivals if relevant
|
||||
// if (_cfgManager.GetCVar(CCVars.ArrivalsPlanet))
|
||||
// {
|
||||
// var template = _random.Pick(_arrivalsBiomeOptions);
|
||||
// _biomes.EnsurePlanet(mapUid, _protoManager.Index(template));
|
||||
// var restricted = new RestrictedRangeComponent
|
||||
// {
|
||||
// Range = 32f
|
||||
// };
|
||||
// AddComp(mapUid, restricted);
|
||||
// }
|
||||
//
|
||||
// _mapSystem.InitializeMap(mapId);
|
||||
//
|
||||
// // Handle roundstart stations.
|
||||
// var query = AllEntityQuery<StationArrivalsComponent>();
|
||||
//
|
||||
// while (query.MoveNext(out var uid, out var comp))
|
||||
// {
|
||||
// SetupShuttle(uid, comp);
|
||||
// }
|
||||
// }
|
||||
/// Sunrise-Edit
|
||||
private void OnRoundStarting(RoundStartingEvent ev)
|
||||
{
|
||||
// Setup arrivals station
|
||||
if (!Enabled)
|
||||
return;
|
||||
|
||||
SetupArrivalsStation();
|
||||
}
|
||||
|
||||
private void SetupArrivalsStation()
|
||||
{
|
||||
var path = new ResPath(_cfgManager.GetCVar(CCVars.ArrivalsMap));
|
||||
if (!_loader.TryLoadMap(path, out var map, out var grids))
|
||||
return;
|
||||
|
||||
_metaData.SetEntityName(map.Value, Loc.GetString("map-name-terminal"));
|
||||
|
||||
foreach (var id in grids)
|
||||
{
|
||||
EnsureComp<ArrivalsSourceComponent>(id);
|
||||
EnsureComp<ProtectedGridComponent>(id);
|
||||
EnsureComp<PreventPilotComponent>(id);
|
||||
}
|
||||
|
||||
// Setup planet arrivals if relevant
|
||||
if (_cfgManager.GetCVar(CCVars.ArrivalsPlanet))
|
||||
{
|
||||
var template = _random.Pick(_arrivalsBiomeOptions);
|
||||
_biomes.EnsurePlanet(map.Value, _protoManager.Index(template));
|
||||
var restricted = new RestrictedRangeComponent
|
||||
{
|
||||
Range = 32f
|
||||
};
|
||||
AddComp(map.Value, restricted);
|
||||
}
|
||||
|
||||
_mapSystem.InitializeMap(map.Value.Comp.MapId);
|
||||
|
||||
// Handle roundstart stations.
|
||||
var query = AllEntityQuery<StationArrivalsComponent>();
|
||||
|
||||
while (query.MoveNext(out var uid, out var comp))
|
||||
{
|
||||
SetupShuttle(uid, comp);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetArrivals(bool obj)
|
||||
{
|
||||
|
|
@ -611,9 +611,9 @@ public sealed class ArrivalsSystem : EntitySystem
|
|||
var dummpMapEntity = _mapSystem.CreateMap(out var dummyMapId);
|
||||
|
||||
if (TryGetArrivals(out var arrivals) &&
|
||||
_loader.TryLoad(dummyMapId, component.ShuttlePath.ToString(), out var shuttleUids))
|
||||
_loader.TryLoadGrid(dummyMapId, component.ShuttlePath, out var shuttle))
|
||||
{
|
||||
component.Shuttle = shuttleUids[0];
|
||||
component.Shuttle = shuttle.Value;
|
||||
var shuttleComp = Comp<ShuttleComponent>(component.Shuttle);
|
||||
var arrivalsComp = EnsureComp<ArrivalsShuttleComponent>(component.Shuttle);
|
||||
arrivalsComp.Station = uid;
|
||||
|
|
|
|||
|
|
@ -330,7 +330,9 @@ public sealed partial class DockingSystem
|
|||
// If it's a map check no hard collidable anchored entities overlap
|
||||
if (isMap)
|
||||
{
|
||||
foreach (var tile in _mapSystem.GetLocalTilesIntersecting(gridEntity.Owner, gridEntity.Comp, aabb))
|
||||
var localTiles = _mapSystem.GetLocalTilesEnumerator(gridEntity.Owner, gridEntity.Comp, aabb);
|
||||
|
||||
while (localTiles.MoveNext(out var tile))
|
||||
{
|
||||
var anchoredEnumerator = _mapSystem.GetAnchoredEntitiesEnumerator(gridEntity.Owner, gridEntity.Comp, tile.GridIndices);
|
||||
|
||||
|
|
|
|||
|
|
@ -34,10 +34,9 @@ using Content.Shared.Shuttles.Events;
|
|||
using Content.Shared.Tag;
|
||||
using Content.Shared.Tiles;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.Maps;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.EntitySerialization.Systems;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
|
@ -79,6 +78,8 @@ public sealed partial class EmergencyShuttleSystem : EntitySystem
|
|||
[Dependency] private readonly DockingSystem _dock = default!;
|
||||
[Dependency] private readonly IdCardSystem _idSystem = default!;
|
||||
[Dependency] private readonly NavMapSystem _navMap = default!;
|
||||
[Dependency] private readonly MapLoaderSystem _loader = default!;
|
||||
[Dependency] private readonly MetaDataSystem _metaData = default!;
|
||||
[Dependency] private readonly PopupSystem _popup = default!;
|
||||
[Dependency] private readonly RoundEndSystem _roundEnd = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
|
|
@ -575,7 +576,7 @@ public sealed partial class EmergencyShuttleSystem : EntitySystem
|
|||
// Sunrise-start
|
||||
var mapUid = _mapSystem.CreateMap(out var mapId, runMapInit: false);
|
||||
|
||||
if (!_loader.TryLoad(mapId, component.Map.ToString(), out var uids) || uids.Count != 1)
|
||||
if (!_loader.TryLoadGrid(mapId, component.Map.ToString(), out var uids) || uids.Count != 1)
|
||||
{
|
||||
Log.Error($"Failed to set up transit hub map!");
|
||||
QueueDel(mapUid);
|
||||
|
|
@ -678,7 +679,7 @@ public sealed partial class EmergencyShuttleSystem : EntitySystem
|
|||
var mapId = _mapManager.CreateMap();
|
||||
|
||||
var mapOptions = new MapLoadOptions { LoadMap = false,};
|
||||
if (!_loader.TryLoad(mapId, shuttlePath.ToString(), out var uids, mapOptions) || uids.Count != 1)
|
||||
if (!_loader.TryLoadGrid(mapId, shuttlePath.ToString(), out var uids, mapOptions) || uids.Count != 1)
|
||||
{
|
||||
Log.Error($"Unable to spawn emergency shuttle {shuttlePath} for {ToPrettyString(ent)}");
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -72,17 +72,15 @@ public sealed partial class ShuttleSystem
|
|||
|
||||
_mapSystem.CreateMap(out var mapId);
|
||||
|
||||
if (_loader.TryLoad(mapId, component.Path.ToString(), out var ent) && ent.Count > 0)
|
||||
if (_loader.TryLoadGrid(mapId, component.Path, out var ent))
|
||||
{
|
||||
if (HasComp<ShuttleComponent>(ent[0]))
|
||||
{
|
||||
TryFTLProximity(ent[0], targetGrid.Value);
|
||||
}
|
||||
if (HasComp<ShuttleComponent>(ent))
|
||||
TryFTLProximity(ent.Value, targetGrid.Value);
|
||||
|
||||
_station.AddGridToStation(uid, ent[0]);
|
||||
_station.AddGridToStation(uid, ent.Value);
|
||||
}
|
||||
|
||||
_mapManager.DeleteMap(mapId);
|
||||
_mapSystem.DeleteMap(mapId);
|
||||
}
|
||||
|
||||
private bool TryDungeonSpawn(Entity<MapGridComponent?> targetGrid, DungeonSpawnGroup group, out EntityUid spawned)
|
||||
|
|
@ -143,20 +141,18 @@ public sealed partial class ShuttleSystem
|
|||
var path = paths[^1];
|
||||
paths.RemoveAt(paths.Count - 1);
|
||||
|
||||
if (_loader.TryLoad(mapId, path.ToString(), out var ent) && ent.Count == 1)
|
||||
if (_loader.TryLoadGrid(mapId, path, out var grid))
|
||||
{
|
||||
if (HasComp<ShuttleComponent>(ent[0]))
|
||||
{
|
||||
TryFTLProximity(ent[0], targetGrid);
|
||||
}
|
||||
if (HasComp<ShuttleComponent>(grid))
|
||||
TryFTLProximity(grid.Value, targetGrid);
|
||||
|
||||
if (group.NameGrid)
|
||||
{
|
||||
var name = path.FilenameWithoutExtension;
|
||||
_metadata.SetEntityName(ent[0], name);
|
||||
_metadata.SetEntityName(grid.Value, name);
|
||||
}
|
||||
|
||||
spawned = ent[0];
|
||||
spawned = grid.Value;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -227,7 +223,7 @@ public sealed partial class ShuttleSystem
|
|||
}
|
||||
}
|
||||
|
||||
_mapManager.DeleteMap(mapId);
|
||||
_mapSystem.DeleteMap(mapId);
|
||||
}
|
||||
|
||||
private void OnGridFillMapInit(EntityUid uid, GridFillComponent component, MapInitEvent args)
|
||||
|
|
@ -246,23 +242,22 @@ public sealed partial class ShuttleSystem
|
|||
_mapSystem.CreateMap(out var mapId);
|
||||
var valid = false;
|
||||
|
||||
if (_loader.TryLoad(mapId, component.Path.ToString(), out var ent) &&
|
||||
ent.Count == 1 &&
|
||||
TryComp(ent[0], out TransformComponent? shuttleXform))
|
||||
if (_loader.TryLoadGrid(mapId, component.Path, out var grid))
|
||||
{
|
||||
var escape = GetSingleDock(ent[0]);
|
||||
var escape = GetSingleDock(grid.Value);
|
||||
|
||||
if (escape != null)
|
||||
{
|
||||
var config = _dockSystem.GetDockingConfig(ent[0], xform.GridUid.Value, escape.Value.Entity, escape.Value.Component, uid, dock);
|
||||
var config = _dockSystem.GetDockingConfig(grid.Value, xform.GridUid.Value, escape.Value.Entity, escape.Value.Component, uid, dock);
|
||||
|
||||
if (config != null)
|
||||
{
|
||||
FTLDock((ent[0], shuttleXform), config);
|
||||
var shuttleXform = Transform(grid.Value);
|
||||
FTLDock((grid.Value, shuttleXform), config);
|
||||
|
||||
if (TryComp<StationMemberComponent>(xform.GridUid, out var stationMember))
|
||||
{
|
||||
_station.AddGridToStation(stationMember.Station, ent[0]);
|
||||
_station.AddGridToStation(stationMember.Station, grid.Value);
|
||||
}
|
||||
|
||||
valid = true;
|
||||
|
|
@ -273,11 +268,11 @@ public sealed partial class ShuttleSystem
|
|||
{
|
||||
var compType = compReg.Component.GetType();
|
||||
|
||||
if (HasComp(ent[0], compType))
|
||||
if (HasComp(grid.Value, compType))
|
||||
continue;
|
||||
|
||||
var comp = _factory.GetComponent(compType);
|
||||
AddComp(ent[0], comp, true);
|
||||
AddComp(grid.Value, comp, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -286,7 +281,7 @@ public sealed partial class ShuttleSystem
|
|||
Log.Error($"Error loading gridfill dock for {ToPrettyString(uid)} / {component.Path}");
|
||||
}
|
||||
|
||||
_mapManager.DeleteMap(mapId);
|
||||
_mapSystem.DeleteMap(mapId);
|
||||
}
|
||||
|
||||
private (EntityUid Entity, DockingComponent Component)? GetSingleDock(EntityUid uid)
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ using Robust.Server.GameStates;
|
|||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.EntitySerialization.Systems;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Physics;
|
||||
|
|
|
|||
|
|
@ -40,12 +40,12 @@ public sealed class StationAiSystem : SharedStationAiSystem
|
|||
var query = EntityManager.EntityQueryEnumerator<StationAiCoreComponent, TransformComponent>();
|
||||
while (query.MoveNext(out var ent, out var entStationAiCore, out var entXform))
|
||||
{
|
||||
var stationAiCore = new Entity<StationAiCoreComponent>(ent, entStationAiCore);
|
||||
var stationAiCore = new Entity<StationAiCoreComponent?>(ent, entStationAiCore);
|
||||
|
||||
if (!TryGetInsertedAI(stationAiCore, out var insertedAi) || !TryComp(insertedAi, out ActorComponent? actor))
|
||||
if (!TryGetHeld(stationAiCore, out var insertedAi) || !TryComp(insertedAi, out ActorComponent? actor))
|
||||
continue;
|
||||
|
||||
if (stationAiCore.Comp.RemoteEntity == null || stationAiCore.Comp.Remote)
|
||||
if (stationAiCore.Comp?.RemoteEntity == null || stationAiCore.Comp.Remote)
|
||||
continue;
|
||||
|
||||
var xform = Transform(stationAiCore.Comp.RemoteEntity.Value);
|
||||
|
|
|
|||
|
|
@ -4,10 +4,8 @@ using Content.Server.Storage.Components;
|
|||
using Content.Shared.Database;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.Interaction.Events;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Random;
|
||||
using static Content.Shared.Storage.EntitySpawnCollection;
|
||||
|
||||
|
|
@ -20,6 +18,7 @@ namespace Content.Server.Storage.EntitySystems
|
|||
[Dependency] private readonly SharedHandsSystem _hands = default!;
|
||||
[Dependency] private readonly PricingSystem _pricing = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
|
|
@ -80,27 +79,27 @@ namespace Content.Server.Storage.EntitySystems
|
|||
_adminLogger.Add(LogType.EntitySpawn, LogImpact.Low, $"{ToPrettyString(args.User)} used {ToPrettyString(uid)} which spawned {ToPrettyString(entityToPlaceInHands.Value)}");
|
||||
}
|
||||
|
||||
// The entity is often deleted, so play the sound at its position rather than parenting
|
||||
if (component.Sound != null)
|
||||
{
|
||||
// The entity is often deleted, so play the sound at its position rather than parenting
|
||||
var coordinates = Transform(uid).Coordinates;
|
||||
_audio.PlayPvs(component.Sound, coordinates);
|
||||
}
|
||||
_audio.PlayPvs(component.Sound, coords);
|
||||
|
||||
component.Uses--;
|
||||
|
||||
// Delete entity only if component was successfully used
|
||||
if (component.Uses <= 0)
|
||||
{
|
||||
args.Handled = true;
|
||||
EntityManager.DeleteEntity(uid);
|
||||
// Don't delete the entity in the event bus, so we queue it for deletion.
|
||||
// We need the free hand for the new item, so we send it to nullspace.
|
||||
_transform.DetachEntity(uid, Transform(uid));
|
||||
QueueDel(uid);
|
||||
}
|
||||
|
||||
if (entityToPlaceInHands != null)
|
||||
{
|
||||
_hands.PickupOrDrop(args.User, entityToPlaceInHands.Value);
|
||||
_audio.PlayPvs(component.Sound, entityToPlaceInHands.Value); // Sunrise-edit
|
||||
}
|
||||
|
||||
args.Handled = true;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ public sealed class SpawnTableOnUseSystem : EntitySystem
|
|||
[Dependency] private readonly EntityTableSystem _entityTable = default!;
|
||||
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
|
||||
[Dependency] private readonly SharedHandsSystem _hands = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
|
|
@ -25,17 +26,21 @@ public sealed class SpawnTableOnUseSystem : EntitySystem
|
|||
if (args.Handled)
|
||||
return;
|
||||
|
||||
args.Handled = true;
|
||||
|
||||
var coords = Transform(ent).Coordinates;
|
||||
var spawns = _entityTable.GetSpawns(ent.Comp.Table);
|
||||
|
||||
// Don't delete the entity in the event bus, so we queue it for deletion.
|
||||
// We need the free hand for the new item, so we send it to nullspace.
|
||||
_transform.DetachEntity(ent, Transform(ent));
|
||||
QueueDel(ent);
|
||||
|
||||
foreach (var id in spawns)
|
||||
{
|
||||
var spawned = Spawn(id, coords);
|
||||
_adminLogger.Add(LogType.EntitySpawn, LogImpact.Low, $"{ToPrettyString(args.User):user} used {ToPrettyString(ent):spawner} which spawned {ToPrettyString(spawned)}");
|
||||
_hands.TryPickupAnyHand(args.User, spawned);
|
||||
_hands.PickupOrDrop(args.User, spawned);
|
||||
}
|
||||
|
||||
Del(ent);
|
||||
args.Handled = true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,7 +38,6 @@ public sealed partial class StoreSystem
|
|||
|
||||
private void OnUseInHand(Entity<StoreRefundComponent> ent, ref UseInHandEvent args)
|
||||
{
|
||||
args.Handled = true;
|
||||
CheckDisableRefund(ent);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -41,6 +41,9 @@ public sealed class HandTeleporterSystem : EntitySystem
|
|||
|
||||
private void OnUseInHand(EntityUid uid, HandTeleporterComponent component, UseInHandEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
if (Deleted(component.FirstPortal))
|
||||
component.FirstPortal = null;
|
||||
|
||||
|
|
@ -67,6 +70,8 @@ public sealed class HandTeleporterSystem : EntitySystem
|
|||
|
||||
_doafter.TryStartDoAfter(doafterArgs);
|
||||
}
|
||||
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ using Robust.Shared.Toolshed.Errors;
|
|||
|
||||
namespace Content.Server.Toolshed.Commands;
|
||||
|
||||
[ToolshedCommand, AdminCommand(AdminFlags.Admin)]
|
||||
[ToolshedCommand, AdminCommand(AdminFlags.VarEdit)]
|
||||
public sealed class VisualizeCommand : ToolshedCommand
|
||||
{
|
||||
[Dependency] private readonly EuiManager _euiManager = default!;
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ public sealed class MeleeWeaponSystem : SharedMeleeWeaponSystem
|
|||
if (damageSpec.Empty)
|
||||
return;
|
||||
|
||||
_damageExamine.AddDamageExamine(args.Message, damageSpec, Loc.GetString("damage-melee"));
|
||||
_damageExamine.AddDamageExamine(args.Message, Damageable.ApplyUniversalAllModifiers(damageSpec), Loc.GetString("damage-melee"));
|
||||
}
|
||||
|
||||
protected override bool ArcRaySuccessful(EntityUid targetUid,
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ public sealed partial class GunSystem
|
|||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
_damageExamine.AddDamageExamineWithModifier(args.Message, damageSpec, shotCount, shootModifier, damageType);
|
||||
_damageExamine.AddDamageExamine(args.Message, Damageable.ApplyUniversalAllModifiers(damageSpec), shotCount, shootModifier, damageType);
|
||||
}
|
||||
|
||||
private DamageSpecifier? GetDamage(BatteryAmmoProviderComponent component)
|
||||
|
|
@ -115,7 +115,7 @@ public sealed partial class GunSystem
|
|||
|
||||
if (!p.Damage.Empty)
|
||||
{
|
||||
return p.Damage;
|
||||
return p.Damage * Damageable.UniversalProjectileDamageModifier;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -124,7 +124,8 @@ public sealed partial class GunSystem
|
|||
|
||||
if (component is HitscanBatteryAmmoProviderComponent hitscan)
|
||||
{
|
||||
return ProtoManager.Index<HitscanPrototype>(hitscan.Prototype).Damage;
|
||||
var dmg = ProtoManager.Index<HitscanPrototype>(hitscan.Prototype).Damage;
|
||||
return dmg == null ? dmg : dmg * Damageable.UniversalHitscanDamageModifier;
|
||||
}
|
||||
|
||||
return null;
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue