Per-map parallax support (#9786)

* Per-map parallax support

* Comments for future sloth

* c

* bet

* Fix exception

* VV support

* Fix parallax

* mem

* weightless sounds

* Gravity stuff

* placeholder coz im too lazy to stash don't @ me son

* decent clouds

* sky

* Fast parallax

* Imagine spelling

* Loicense

* perish

* Fix weightless status

Co-authored-by: metalgearsloth <metalgearsloth@gmail.com>
This commit is contained in:
metalgearsloth
2022-07-25 15:10:23 +10:00
committed by GitHub
parent aad6a22a6a
commit bfac53e7bc
36 changed files with 607 additions and 412 deletions

View File

@@ -182,10 +182,10 @@ namespace Content.Client.Entry
ContentContexts.SetupContexts(inputMan.Contexts); ContentContexts.SetupContexts(inputMan.Contexts);
IoCManager.Resolve<IGameHud>().Initialize(); IoCManager.Resolve<IGameHud>().Initialize();
IoCManager.Resolve<IParallaxManager>().LoadParallax(); // Have to do this later because prototypes are needed. IoCManager.Resolve<IParallaxManager>().LoadDefaultParallax(); // Have to do this later because prototypes are needed.
var overlayMgr = IoCManager.Resolve<IOverlayManager>(); var overlayMgr = IoCManager.Resolve<IOverlayManager>();
overlayMgr.AddOverlay(new ParallaxOverlay());
overlayMgr.AddOverlay(new SingularityOverlay()); overlayMgr.AddOverlay(new SingularityOverlay());
overlayMgr.AddOverlay(new FlashOverlay()); overlayMgr.AddOverlay(new FlashOverlay());
overlayMgr.AddOverlay(new RadiationPulseOverlay()); overlayMgr.AddOverlay(new RadiationPulseOverlay());

View File

@@ -2,7 +2,7 @@ using Content.Shared.Gravity;
namespace Content.Client.Gravity namespace Content.Client.Gravity
{ {
internal sealed class GravitySystem : SharedGravitySystem public sealed class GravitySystem : SharedGravitySystem
{ {
} }

View File

@@ -1,7 +1,5 @@
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Collections.Generic;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Client.Graphics; using Robust.Client.Graphics;
namespace Content.Client.Parallax.Data namespace Content.Client.Parallax.Data

View File

@@ -1,12 +1,9 @@
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Collections.Generic;
using JetBrains.Annotations; using JetBrains.Annotations;
using Content.Client.Resources; using Content.Client.Resources;
using Content.Client.IoC; using Content.Client.IoC;
using Robust.Client.Graphics; using Robust.Client.Graphics;
using Robust.Client.ResourceManagement;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Utility; using Robust.Shared.Utility;
namespace Content.Client.Parallax.Data; namespace Content.Client.Parallax.Data;

View File

@@ -62,5 +62,12 @@ public sealed class ParallaxLayerConfig
/// </summary> /// </summary>
[DataField("slowness")] [DataField("slowness")]
public float Slowness { get; set; } = 0.5f; public float Slowness { get; set; } = 0.5f;
/// <summary>
/// Should the parallax scroll at a specific rate per second.
/// </summary>
[DataField("scrolling")] public Vector2 Scrolling = Vector2.Zero;
[DataField("shader")] public string? Shader = "unshaded";
} }

View File

@@ -1,35 +1,30 @@
using System; using System.Threading.Tasks;
using Robust.Client.Graphics; using Robust.Shared.Maths;
using Content.Client.Parallax;
namespace Content.Client.Parallax.Managers; namespace Content.Client.Parallax.Managers;
public interface IParallaxManager public interface IParallaxManager
{ {
/// <summary>
/// The current parallax.
/// Changing this causes a new parallax to be loaded (eventually).
/// Do not alter until prototype manager is available.
/// Useful "csi" input for testing new parallaxes:
/// using Content.Client.Parallax.Managers; IoCManager.Resolve<IParallaxManager>().ParallaxName = "test";
/// </summary>
string ParallaxName { get; set; }
/// <summary> /// <summary>
/// All WorldHomePosition values are offset by this. /// All WorldHomePosition values are offset by this.
/// </summary> /// </summary>
Vector2 ParallaxAnchor { get; set; } Vector2 ParallaxAnchor { get; set; }
/// <summary> bool IsLoaded(string name);
/// The layers of the currently loaded parallax.
/// This will change on a whim without notification.
/// </summary>
ParallaxLayerPrepared[] ParallaxLayers { get; }
/// <summary> /// <summary>
/// Used to initialize the manager. /// The layers of the selected parallax.
/// </summary>
ParallaxLayerPrepared[] GetParallaxLayers(string name);
/// <summary>
/// Loads in the default parallax to use.
/// Do not call until prototype manager is available. /// Do not call until prototype manager is available.
/// </summary> /// </summary>
void LoadParallax(); void LoadDefaultParallax();
Task LoadParallaxByName(string name);
void UnloadParallax(string name);
} }

View File

@@ -1,111 +1,103 @@
using System; using System.Collections.Concurrent;
using System.Linq;
using System.Collections.Generic;
using System.IO;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Content.Client.Parallax.Data; using Content.Client.Parallax.Data;
using Content.Shared;
using Content.Shared.CCVar; using Content.Shared.CCVar;
using Nett;
using Robust.Client.Graphics;
using Robust.Client.ResourceManagement;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Configuration; using Robust.Shared.Configuration;
using Robust.Shared.ContentPack;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Utility; using Robust.Shared.Utility;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
namespace Content.Client.Parallax.Managers; namespace Content.Client.Parallax.Managers;
internal sealed class ParallaxManager : IParallaxManager public sealed class ParallaxManager : IParallaxManager
{ {
[Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IConfigurationManager _configurationManager = default!; [Dependency] private readonly IConfigurationManager _configurationManager = default!;
private string _parallaxName = ""; private ISawmill _sawmill = Logger.GetSawmill("parallax");
public string ParallaxName
{
get => _parallaxName;
set
{
LoadParallaxByName(value);
}
}
public Vector2 ParallaxAnchor { get; set; } public Vector2 ParallaxAnchor { get; set; }
private CancellationTokenSource? _presentParallaxLoadCancel; private readonly ConcurrentDictionary<string, ParallaxLayerPrepared[]> _parallaxesLQ = new();
private readonly ConcurrentDictionary<string, ParallaxLayerPrepared[]> _parallaxesHQ = new();
private ParallaxLayerPrepared[] _parallaxLayersHQ = {}; private readonly ConcurrentDictionary<string, CancellationTokenSource> _loadingParallaxes = new();
private ParallaxLayerPrepared[] _parallaxLayersLQ = {};
public ParallaxLayerPrepared[] ParallaxLayers => _configurationManager.GetCVar(CCVars.ParallaxLowQuality) ? _parallaxLayersLQ : _parallaxLayersHQ; public bool IsLoaded(string name) => _parallaxesLQ.ContainsKey(name);
public async void LoadParallax() public ParallaxLayerPrepared[] GetParallaxLayers(string name)
{ {
await LoadParallaxByName("default"); if (_configurationManager.GetCVar(CCVars.ParallaxLowQuality))
{
return !_parallaxesLQ.TryGetValue(name, out var lq) ? Array.Empty<ParallaxLayerPrepared>() : lq;
}
return !_parallaxesLQ.TryGetValue(name, out var hq) ? Array.Empty<ParallaxLayerPrepared>() : hq;
} }
private async Task LoadParallaxByName(string name) public void UnloadParallax(string name)
{ {
// Update _parallaxName if (_loadingParallaxes.TryGetValue(name, out var loading))
if (_parallaxName == name)
{ {
loading.Cancel();
_loadingParallaxes.Remove(name, out _);
return; return;
} }
_parallaxName = name;
if (!_parallaxesLQ.ContainsKey(name)) return;
_parallaxesLQ.Remove(name, out _);
_parallaxesHQ.Remove(name, out _);
}
public async void LoadDefaultParallax()
{
await LoadParallaxByName("Default");
}
public async Task LoadParallaxByName(string name)
{
if (_parallaxesLQ.ContainsKey(name) || _loadingParallaxes.ContainsKey(name)) return;
// Cancel any existing load and setup the new cancellation token // Cancel any existing load and setup the new cancellation token
_presentParallaxLoadCancel?.Cancel(); var token = new CancellationTokenSource();
_presentParallaxLoadCancel = new CancellationTokenSource(); _loadingParallaxes[name] = token;
var cancel = _presentParallaxLoadCancel.Token; var cancel = token.Token;
// Empty parallax name = no layers (this is so that the initial "" parallax name is consistent)
if (_parallaxName == "")
{
_parallaxLayersHQ = _parallaxLayersLQ = new ParallaxLayerPrepared[] {};
return;
}
// Begin (for real) // Begin (for real)
Logger.InfoS("parallax", $"Loading parallax {name}"); _sawmill.Info($"Loading parallax {name}");
try try
{ {
var parallaxPrototype = _prototypeManager.Index<ParallaxPrototype>(name); var parallaxPrototype = _prototypeManager.Index<ParallaxPrototype>(name);
ParallaxLayerPrepared[] hq; ParallaxLayerPrepared[][] layers;
ParallaxLayerPrepared[] lq;
if (parallaxPrototype.LayersLQUseHQ) if (parallaxPrototype.LayersLQUseHQ)
{ {
lq = hq = await LoadParallaxLayers(parallaxPrototype.Layers, cancel); layers = new ParallaxLayerPrepared[2][];
layers[0] = layers[1] = await LoadParallaxLayers(parallaxPrototype.Layers, cancel);
} }
else else
{ {
var results = await Task.WhenAll( layers = await Task.WhenAll(
LoadParallaxLayers(parallaxPrototype.Layers, cancel), LoadParallaxLayers(parallaxPrototype.Layers, cancel),
LoadParallaxLayers(parallaxPrototype.LayersLQ, cancel) LoadParallaxLayers(parallaxPrototype.LayersLQ, cancel)
); );
hq = results[0];
lq = results[1];
} }
// Still keeping this check just in case. _loadingParallaxes.Remove(name, out _);
if (_parallaxName == name)
{ if (token.Token.IsCancellationRequested) return;
_parallaxLayersHQ = hq;
_parallaxLayersLQ = lq; _parallaxesLQ[name] = layers[0];
Logger.InfoS("parallax", $"Loaded parallax {name}"); _parallaxesHQ[name] = layers[1];
}
_sawmill.Info($"Loaded parallax {name}");
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.ErrorS("parallax", $"Failed to loaded parallax {name}: {ex}"); _sawmill.Error($"Failed to loaded parallax {name}: {ex}");
} }
} }

View File

@@ -1,10 +1,7 @@
using Content.Client.Parallax.Managers; using Content.Client.Parallax.Managers;
using Robust.Client.Graphics; using Robust.Client.Graphics;
using Robust.Client.UserInterface; using Robust.Client.UserInterface;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
using Robust.Shared.Random; using Robust.Shared.Random;
using Robust.Shared.ViewVariables;
namespace Content.Client.Parallax; namespace Content.Client.Parallax;
@@ -28,7 +25,7 @@ public sealed class ParallaxControl : Control
protected override void Draw(DrawingHandleScreen handle) protected override void Draw(DrawingHandleScreen handle)
{ {
foreach (var layer in _parallaxManager.ParallaxLayers) foreach (var layer in _parallaxManager.GetParallaxLayers("Default"))
{ {
var tex = layer.Texture; var tex = layer.Texture;
var texSize = tex.Size * layer.Config.Scale.Floored(); var texSize = tex.Size * layer.Config.Scale.Floored();

View File

@@ -1,47 +1,55 @@
using System;
using Content.Client.Parallax.Managers; using Content.Client.Parallax.Managers;
using Content.Shared.CCVar; using Content.Shared.CCVar;
using Robust.Client.Graphics; using Robust.Client.Graphics;
using Robust.Shared.Configuration; using Robust.Shared.Configuration;
using Robust.Shared.Enums; using Robust.Shared.Enums;
using Robust.Shared.IoC; using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
namespace Content.Client.Parallax; namespace Content.Client.Parallax;
public sealed class ParallaxOverlay : Overlay public sealed class ParallaxOverlay : Overlay
{ {
[Dependency] private readonly IParallaxManager _parallaxManager = default!; [Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IConfigurationManager _configurationManager = default!; [Dependency] private readonly IConfigurationManager _configurationManager = default!;
[Dependency] private readonly IParallaxManager _manager = default!;
private readonly ParallaxSystem _parallax;
public override OverlaySpace Space => OverlaySpace.WorldSpaceBelowWorld; public override OverlaySpace Space => OverlaySpace.WorldSpaceBelowWorld;
private readonly ShaderInstance _shader;
public ParallaxOverlay() public ParallaxOverlay()
{ {
IoCManager.InjectDependencies(this); IoCManager.InjectDependencies(this);
_shader = _prototypeManager.Index<ShaderPrototype>("unshaded").Instance(); _parallax = IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<ParallaxSystem>();
} }
protected override void Draw(in OverlayDrawArgs args) protected override void Draw(in OverlayDrawArgs args)
{ {
if (args.Viewport.Eye == null) if (args.MapId == MapId.Nullspace)
{
return; return;
}
if (!_configurationManager.GetCVar(CCVars.ParallaxEnabled)) if (!_configurationManager.GetCVar(CCVars.ParallaxEnabled))
{
return; return;
}
var position = args.Viewport.Eye?.Position.Position ?? Vector2.Zero;
var screenHandle = args.WorldHandle; var screenHandle = args.WorldHandle;
screenHandle.UseShader(_shader);
foreach (var layer in _parallaxManager.ParallaxLayers) var layers = _parallax.GetParallaxLayers(args.MapId);
var realTime = (float) _timing.RealTime.TotalSeconds;
foreach (var layer in layers)
{ {
ShaderInstance? shader;
if (!string.IsNullOrEmpty(layer.Config.Shader))
shader = _prototypeManager.Index<ShaderPrototype>(layer.Config.Shader).Instance();
else
shader = null;
screenHandle.UseShader(shader);
var tex = layer.Texture; var tex = layer.Texture;
// Size of the texture in world units. // Size of the texture in world units.
@@ -52,10 +60,11 @@ public sealed class ParallaxOverlay : Overlay
// The effects of this are such that a slowness of 1 anchors the layer to the centre of the screen, while a slowness of 0 anchors the layer to the world. // The effects of this are such that a slowness of 1 anchors the layer to the centre of the screen, while a slowness of 0 anchors the layer to the world.
// (For values 0.0 to 1.0 this is in effect a lerp, but it's deliberately unclamped.) // (For values 0.0 to 1.0 this is in effect a lerp, but it's deliberately unclamped.)
// The ParallaxAnchor adapts the parallax for station positioning and possibly map-specific tweaks. // The ParallaxAnchor adapts the parallax for station positioning and possibly map-specific tweaks.
var home = layer.Config.WorldHomePosition + _parallaxManager.ParallaxAnchor; var home = layer.Config.WorldHomePosition + _manager.ParallaxAnchor;
var scrolled = layer.Config.Scrolling * realTime;
// Origin - start with the parallax shift itself. // Origin - start with the parallax shift itself.
var originBL = (args.Viewport.Eye.Position.Position - home) * layer.Config.Slowness; var originBL = (position - home) * layer.Config.Slowness + scrolled;
// Place at the home. // Place at the home.
originBL += home; originBL += home;
@@ -90,6 +99,8 @@ public sealed class ParallaxOverlay : Overlay
screenHandle.DrawTextureRect(tex, Box2.FromDimensions(originBL, size)); screenHandle.DrawTextureRect(tex, Box2.FromDimensions(originBL, size));
} }
} }
screenHandle.UseShader(null);
} }
} }

View File

@@ -0,0 +1,71 @@
using Content.Client.Parallax.Managers;
using Content.Shared.Parallax;
using Robust.Client.Graphics;
using Robust.Shared.GameStates;
using Robust.Shared.Map;
using Robust.Shared.Prototypes;
namespace Content.Client.Parallax;
public sealed class ParallaxSystem : SharedParallaxSystem
{
[Dependency] private readonly IMapManager _map = default!;
[Dependency] private readonly IOverlayManager _overlay = default!;
[Dependency] private readonly IParallaxManager _parallax = default!;
[Dependency] private readonly IPrototypeManager _protoManager = default!;
private const string Fallback = "Default";
public override void Initialize()
{
base.Initialize();
_overlay.AddOverlay(new ParallaxOverlay());
SubscribeLocalEvent<ParallaxComponent, ComponentHandleState>(OnParallaxHandleState);
_protoManager.PrototypesReloaded += OnReload;
}
private void OnReload(PrototypesReloadedEventArgs obj)
{
_parallax.UnloadParallax(Fallback);
_parallax.LoadDefaultParallax();
foreach (var comp in EntityQuery<ParallaxComponent>(true))
{
_parallax.UnloadParallax(comp.Parallax);
_parallax.LoadParallaxByName(comp.Parallax);
}
}
public override void Shutdown()
{
base.Shutdown();
_overlay.RemoveOverlay<ParallaxOverlay>();
_protoManager.PrototypesReloaded -= OnReload;
}
private void OnParallaxHandleState(EntityUid uid, ParallaxComponent component, ref ComponentHandleState args)
{
if (args.Current is not ParallaxComponentState state) return;
component.Parallax = state.Parallax;
if (!_parallax.IsLoaded(component.Parallax))
{
_parallax.LoadParallaxByName(component.Parallax);
}
}
public ParallaxLayerPrepared[] GetParallaxLayers(MapId mapId)
{
return _parallax.GetParallaxLayers(GetParallax(_map.GetMapEntityId(mapId)));
}
public string GetParallax(MapId mapId)
{
return GetParallax(_map.GetMapEntityId(mapId));
}
public string GetParallax(EntityUid mapUid)
{
return TryComp<ParallaxComponent>(mapUid, out var parallax) ? parallax.Parallax : Fallback;
}
}

View File

@@ -1,3 +1,5 @@
using System;
using System.Threading.Tasks;
using Content.Client.Parallax.Managers; using Content.Client.Parallax.Managers;
using Content.Client.Parallax; using Content.Client.Parallax;
using Robust.Shared.Maths; using Robust.Shared.Maths;
@@ -6,13 +8,30 @@ namespace Content.IntegrationTests
{ {
public sealed class DummyParallaxManager : IParallaxManager public sealed class DummyParallaxManager : IParallaxManager
{ {
public string ParallaxName { get; set; } = "";
public Vector2 ParallaxAnchor { get; set; } public Vector2 ParallaxAnchor { get; set; }
public ParallaxLayerPrepared[] ParallaxLayers { get; } = {}; public bool IsLoaded(string name)
public void LoadParallax()
{ {
ParallaxName = "default"; return true;
}
public ParallaxLayerPrepared[] GetParallaxLayers(string name)
{
return Array.Empty<ParallaxLayerPrepared>();
}
public void LoadDefaultParallax()
{
return;
}
public Task LoadParallaxByName(string name)
{
return Task.CompletedTask;
}
public void UnloadParallax(string name)
{
return;
} }
} }
} }

View File

@@ -11,7 +11,7 @@ using Robust.Shared.Map;
namespace Content.IntegrationTests.Tests.Gravity namespace Content.IntegrationTests.Tests.Gravity
{ {
[TestFixture] [TestFixture]
[TestOf(typeof(WeightlessSystem))] [TestOf(typeof(GravitySystem))]
[TestOf(typeof(GravityGeneratorComponent))] [TestOf(typeof(GravityGeneratorComponent))]
public sealed class WeightlessStatusTests public sealed class WeightlessStatusTests
{ {
@@ -21,6 +21,9 @@ namespace Content.IntegrationTests.Tests.Gravity
id: HumanDummy id: HumanDummy
components: components:
- type: Alerts - type: Alerts
- type: Physics
bodyType: Dynamic
- type: entity - type: entity
name: GravityGeneratorDummy name: GravityGeneratorDummy
id: GravityGeneratorDummy id: GravityGeneratorDummy
@@ -38,7 +41,6 @@ namespace Content.IntegrationTests.Tests.Gravity
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true, ExtraPrototypes = Prototypes}); await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true, ExtraPrototypes = Prototypes});
var server = pairTracker.Pair.Server; var server = pairTracker.Pair.Server;
var mapManager = server.ResolveDependency<IMapManager>();
var entityManager = server.ResolveDependency<IEntityManager>(); var entityManager = server.ResolveDependency<IEntityManager>();
var alertsSystem = server.ResolveDependency<IEntitySystemManager>().GetEntitySystem<AlertsSystem>(); var alertsSystem = server.ResolveDependency<IEntitySystemManager>().GetEntitySystem<AlertsSystem>();

View File

@@ -70,8 +70,8 @@ namespace Content.IntegrationTests.Tests
var grid1Entity = grid1.GridEntityId; var grid1Entity = grid1.GridEntityId;
var grid2Entity = grid2.GridEntityId; var grid2Entity = grid2.GridEntityId;
Assert.That(!entityMan.GetComponent<GravityComponent>(grid1Entity).Enabled); Assert.That(!entityMan.GetComponent<GravityComponent>(grid1Entity).EnabledVV);
Assert.That(entityMan.GetComponent<GravityComponent>(grid2Entity).Enabled); Assert.That(entityMan.GetComponent<GravityComponent>(grid2Entity).EnabledVV);
// Re-enable needs power so it turns off again. // Re-enable needs power so it turns off again.
// Charge rate is ridiculously high so it finishes in one tick. // Charge rate is ridiculously high so it finishes in one tick.
@@ -88,7 +88,7 @@ namespace Content.IntegrationTests.Tests
var grid2Entity = grid2.GridEntityId; var grid2Entity = grid2.GridEntityId;
Assert.That(entityMan.GetComponent<GravityComponent>(grid2Entity).Enabled, Is.False); Assert.That(entityMan.GetComponent<GravityComponent>(grid2Entity).EnabledVV, Is.False);
}); });
await pairTracker.CleanReturnAsync(); await pairTracker.CleanReturnAsync();

View File

@@ -4,16 +4,16 @@ using JetBrains.Annotations;
namespace Content.Server.Gravity.EntitySystems namespace Content.Server.Gravity.EntitySystems
{ {
[UsedImplicitly] [UsedImplicitly]
internal sealed class GravitySystem : SharedGravitySystem public sealed class GravitySystem : SharedGravitySystem
{ {
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
SubscribeLocalEvent<GravityComponent, ComponentInit>(HandleGravityInitialize); SubscribeLocalEvent<GravityComponent, ComponentInit>(OnGravityInit);
SubscribeLocalEvent<GravityComponent, ComponentShutdown>(HandleGravityShutdown); SubscribeLocalEvent<GravityComponent, ComponentShutdown>(OnGravityShutdown);
} }
private void HandleGravityInitialize(EntityUid uid, GravityComponent component, ComponentInit args) private void OnGravityInit(EntityUid uid, GravityComponent component, ComponentInit args)
{ {
// Incase there's already a generator on the grid we'll just set it now. // Incase there's already a generator on the grid we'll just set it now.
var gridId = Transform(component.Owner).GridUid; var gridId = Transform(component.Owner).GridUid;
@@ -21,25 +21,25 @@ namespace Content.Server.Gravity.EntitySystems
if (gridId == null) if (gridId == null)
return; return;
GravityChangedMessage message; GravityChangedEvent message;
foreach (var generator in EntityManager.EntityQuery<GravityGeneratorComponent>()) foreach (var generator in EntityManager.EntityQuery<GravityGeneratorComponent>())
{ {
if (Transform(generator.Owner).GridUid == gridId && generator.GravityActive) if (Transform(generator.Owner).GridUid == gridId && generator.GravityActive)
{ {
component.Enabled = true; component.Enabled = true;
message = new GravityChangedMessage(gridId.Value, true); message = new GravityChangedEvent(gridId.Value, true);
RaiseLocalEvent(message); RaiseLocalEvent(message);
return; return;
} }
} }
component.Enabled = false; component.Enabled = false;
message = new GravityChangedMessage(gridId.Value, false); message = new GravityChangedEvent(gridId.Value, false);
RaiseLocalEvent(message); RaiseLocalEvent(message);
} }
private void HandleGravityShutdown(EntityUid uid, GravityComponent component, ComponentShutdown args) private void OnGravityShutdown(EntityUid uid, GravityComponent component, ComponentShutdown args)
{ {
DisableGravity(component); DisableGravity(component);
} }
@@ -53,7 +53,7 @@ namespace Content.Server.Gravity.EntitySystems
return; return;
comp.Enabled = true; comp.Enabled = true;
var message = new GravityChangedMessage(gridId.Value, true); var message = new GravityChangedEvent(gridId.Value, true);
RaiseLocalEvent(message); RaiseLocalEvent(message);
} }
@@ -66,7 +66,7 @@ namespace Content.Server.Gravity.EntitySystems
if (gridId == null) if (gridId == null)
return; return;
var message = new GravityChangedMessage(gridId.Value, false); var message = new GravityChangedEvent(gridId.Value, false);
RaiseLocalEvent(message); RaiseLocalEvent(message);
} }
} }

View File

@@ -1,157 +0,0 @@
using Content.Shared.Alert;
using Content.Shared.GameTicking;
using Content.Shared.Gravity;
using Content.Shared.Movement.Components;
using JetBrains.Annotations;
using Robust.Shared.Map;
using Robust.Shared.Utility;
namespace Content.Server.Gravity.EntitySystems
{
[UsedImplicitly]
public sealed class WeightlessSystem : EntitySystem
{
[Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly AlertsSystem _alertsSystem = default!;
private readonly Dictionary<EntityUid, List<AlertsComponent>> _alerts = new();
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<RoundRestartCleanupEvent>(Reset);
SubscribeLocalEvent<GravityChangedMessage>(GravityChanged);
SubscribeLocalEvent<AlertsComponent, EntParentChangedMessage>(EntParentChanged);
SubscribeLocalEvent<AlertsComponent, AlertSyncEvent>(HandleAlertSyncEvent);
}
public void Reset(RoundRestartCleanupEvent ev)
{
_alerts.Clear();
}
public void AddAlert(AlertsComponent status)
{
var xform = Transform(status.Owner);
if (xform.GridUid != null)
{
var alerts = _alerts.GetOrNew(xform.GridUid.Value);
alerts.Add(status);
}
if (_mapManager.TryGetGrid(xform.GridUid, out var grid))
{
var alerts = _alerts.GetOrNew(xform.GridUid.Value);
alerts.Add(status);
if (EntityManager.GetComponent<GravityComponent>(grid.GridEntityId).Enabled)
{
RemoveWeightless(status.Owner);
}
else
{
AddWeightless(status.Owner);
}
}
else
{
AddWeightless(status.Owner);
}
}
public void RemoveAlert(AlertsComponent status)
{
var grid = EntityManager.GetComponent<TransformComponent>(status.Owner).GridUid;
if (grid == null || !_alerts.TryGetValue(grid.Value, out var statuses))
{
return;
}
statuses.Remove(status);
}
private void GravityChanged(GravityChangedMessage ev)
{
if (!_alerts.TryGetValue(ev.ChangedGridIndex, out var statuses))
{
return;
}
if (ev.HasGravity)
{
foreach (var status in statuses)
{
RemoveWeightless(status.Owner);
}
}
else
{
foreach (var status in statuses)
{
AddWeightless(status.Owner);
}
}
}
private void AddWeightless(EntityUid euid)
{
_alertsSystem.ShowAlert(euid, AlertType.Weightless);
}
private void RemoveWeightless(EntityUid euid)
{
_alertsSystem.ClearAlert(euid, AlertType.Weightless);
}
private void EntParentChanged(EntityUid uid, AlertsComponent status, ref EntParentChangedMessage ev)
{
// First, update the `_alerts` dictionary
if (ev.OldParent is {Valid: true} old &&
EntityManager.TryGetComponent(old, out IMapGridComponent? mapGrid))
{
var oldGrid = mapGrid.Owner;
if (_alerts.TryGetValue(oldGrid, out var oldStatuses))
{
oldStatuses.Remove(status);
}
}
if (ev.Transform.MapID == MapId.Nullspace)
return;
var newGrid = ev.Transform.GridUid;
if (newGrid != null)
{
var newStatuses = _alerts.GetOrNew(newGrid.Value);
newStatuses.Add(status);
}
// then update the actual alert. The alert is only removed if either the player is on a grid with gravity,
// or if they ignore gravity-based movement altogether.
// TODO: update this when planets and the like are added.
// TODO: update alert when the ignore-gravity component is added or removed.
if (_mapManager.TryGetGrid(newGrid, out var grid)
&& TryComp(grid.GridEntityId, out GravityComponent? gravity)
&& gravity.Enabled)
RemoveWeightless(status.Owner);
else if (!HasComp<MovementIgnoreGravityComponent>(uid))
AddWeightless(status.Owner);
}
private void HandleAlertSyncEvent(EntityUid uid, AlertsComponent component, AlertSyncEvent args)
{
switch (component.LifeStage)
{
case ComponentLifeStage.Starting:
AddAlert(component);
break;
case ComponentLifeStage.Removing:
RemoveAlert(component);
break;
}
}
}
}

View File

@@ -0,0 +1,23 @@
using Content.Shared.Parallax;
using Robust.Shared.GameStates;
namespace Content.Server.Parallax;
public sealed class ParallaxSystem : SharedParallaxSystem
{
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<ParallaxComponent, ComponentGetState>(OnParallaxGetState);
}
private void OnParallaxGetState(EntityUid uid, ParallaxComponent component, ref ComponentGetState args)
{
args.State = new ParallaxComponentState
{
Parallax = component.Parallax
};
}
}

View File

@@ -5,6 +5,7 @@ using Content.Server.Doors.Systems;
using Content.Server.Shuttles.Components; using Content.Server.Shuttles.Components;
using Content.Server.Station.Systems; using Content.Server.Station.Systems;
using Content.Server.Stunnable; using Content.Server.Stunnable;
using Content.Shared.Parallax;
using Content.Shared.Shuttles.Systems; using Content.Shared.Shuttles.Systems;
using Content.Shared.Sound; using Content.Shared.Sound;
using Content.Shared.StatusEffect; using Content.Shared.StatusEffect;
@@ -195,6 +196,8 @@ public sealed partial class ShuttleSystem
component = AddComp<FTLComponent>(uid); component = AddComp<FTLComponent>(uid);
// TODO: Need BroadcastGrid to not be bad. // TODO: Need BroadcastGrid to not be bad.
SoundSystem.Play(_startupSound.GetSound(), Filter.Empty().AddInRange(Transform(uid).MapPosition, GetSoundRange(component.Owner)), _startupSound.Params); SoundSystem.Play(_startupSound.GetSound(), Filter.Empty().AddInRange(Transform(uid).MapPosition, GetSoundRange(component.Owner)), _startupSound.Params);
// Make sure the map is setup before we leave to avoid pop-in (e.g. parallax).
SetupHyperspace();
return true; return true;
} }
@@ -217,7 +220,6 @@ public sealed partial class ShuttleSystem
DoTheDinosaur(xform); DoTheDinosaur(xform);
comp.State = FTLState.Travelling; comp.State = FTLState.Travelling;
SetupHyperspace();
var width = Comp<IMapGridComponent>(comp.Owner).Grid.LocalAABB.Width; var width = Comp<IMapGridComponent>(comp.Owner).Grid.LocalAABB.Width;
xform.Coordinates = new EntityCoordinates(_mapManager.GetMapEntityId(_hyperSpaceMap!.Value), new Vector2(_index + width / 2f, 0f)); xform.Coordinates = new EntityCoordinates(_mapManager.GetMapEntityId(_hyperSpaceMap!.Value), new Vector2(_index + width / 2f, 0f));
@@ -352,6 +354,8 @@ public sealed partial class ShuttleSystem
_hyperSpaceMap = _mapManager.CreateMap(); _hyperSpaceMap = _mapManager.CreateMap();
_sawmill.Info($"Setup hyperspace map at {_hyperSpaceMap.Value}"); _sawmill.Info($"Setup hyperspace map at {_hyperSpaceMap.Value}");
DebugTools.Assert(!_mapManager.IsMapPaused(_hyperSpaceMap.Value)); DebugTools.Assert(!_mapManager.IsMapPaused(_hyperSpaceMap.Value));
var parallax = EnsureComp<ParallaxComponent>(_mapManager.GetMapEntityId(_hyperSpaceMap.Value));
parallax.Parallax = "FastSpace";
} }
private void CleanupHyperspace() private void CleanupHyperspace()

View File

@@ -25,6 +25,7 @@ namespace Content.Shared.Friction
private float _stopSpeed; private float _stopSpeed;
private float _frictionModifier; private float _frictionModifier;
private const float DefaultFriction = 0.3f;
public override void Initialize() public override void Initialize()
{ {
@@ -168,15 +169,19 @@ namespace Content.Shared.Friction
private float GetTileFriction(PhysicsComponent body, TransformComponent xform) private float GetTileFriction(PhysicsComponent body, TransformComponent xform)
{ {
// TODO: Make IsWeightless event-based; we already have grid traversals tracked so just raise events // TODO: Make IsWeightless event-based; we already have grid traversals tracked so just raise events
if (_gravity.IsWeightless(body.Owner, body, xform) || if (_gravity.IsWeightless(body.Owner, body, xform))
!_mapManager.TryGetGrid(xform.GridUid, out var grid))
return 0.0f; return 0.0f;
if (!xform.Coordinates.IsValid(EntityManager)) return 0.0f; if (!xform.Coordinates.IsValid(EntityManager)) return 0.0f;
var tile = grid.GetTileRef(xform.Coordinates); if (_mapManager.TryGetGrid(xform.GridUid, out var grid))
var tileDef = _tileDefinitionManager[tile.Tile.TypeId]; {
return tileDef.Friction; var tile = grid.GetTileRef(xform.Coordinates);
var tileDef = _tileDefinitionManager[tile.Tile.TypeId];
return tileDef.Friction;
}
return TryComp<TileFrictionModifierComponent>(xform.MapUid, out var friction) ? friction.Modifier : DefaultFriction;
} }
[NetSerializable, Serializable] [NetSerializable, Serializable]

View File

@@ -1,10 +1,8 @@
using Robust.Shared.Map;
namespace Content.Shared.Gravity namespace Content.Shared.Gravity
{ {
public sealed class GravityChangedMessage : EntityEventArgs public sealed class GravityChangedEvent : EntityEventArgs
{ {
public GravityChangedMessage(EntityUid changedGridIndex, bool newGravityState) public GravityChangedEvent(EntityUid changedGridIndex, bool newGravityState)
{ {
HasGravity = newGravityState; HasGravity = newGravityState;
ChangedGridIndex = changedGridIndex; ChangedGridIndex = changedGridIndex;

View File

@@ -12,48 +12,19 @@ namespace Content.Shared.Gravity
public SoundSpecifier GravityShakeSound { get; set; } = new SoundPathSpecifier("/Audio/Effects/alert.ogg"); public SoundSpecifier GravityShakeSound { get; set; } = new SoundPathSpecifier("/Audio/Effects/alert.ogg");
[ViewVariables(VVAccess.ReadWrite)] [ViewVariables(VVAccess.ReadWrite)]
public bool Enabled public bool EnabledVV
{ {
get => _enabled; get => Enabled;
set set
{ {
if (_enabled == value) return; if (Enabled == value) return;
_enabled = value; Enabled = value;
if (_enabled) IoCManager.Resolve<IEntityManager>().EventBus.RaiseLocalEvent(Owner, new GravityChangedEvent(Owner, value));
{
Logger.Info($"Enabled gravity for {Owner}");
}
else
{
Logger.Info($"Disabled gravity for {Owner}");
}
Dirty(); Dirty();
} }
} }
private bool _enabled; [DataField("enabled")]
public bool Enabled;
public override ComponentState GetComponentState()
{
return new GravityComponentState(_enabled);
}
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
{
base.HandleComponentState(curState, nextState);
if (curState is not GravityComponentState state) return;
Enabled = state.Enabled;
}
[Serializable, NetSerializable]
private sealed class GravityComponentState : ComponentState
{
public bool Enabled { get; }
public GravityComponentState(bool enabled)
{
Enabled = enabled;
}
}
} }
} }

View File

@@ -1,14 +1,17 @@
using Content.Shared.Alert;
using Content.Shared.Clothing; using Content.Shared.Clothing;
using Content.Shared.Inventory; using Content.Shared.Inventory;
using Content.Shared.Movement.Components; using Content.Shared.Movement.Components;
using Robust.Shared.GameStates;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Physics; using Robust.Shared.Physics;
using Robust.Shared.Serialization;
namespace Content.Shared.Gravity namespace Content.Shared.Gravity
{ {
public abstract class SharedGravitySystem : EntitySystem public abstract class SharedGravitySystem : EntitySystem
{ {
[Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly AlertsSystem _alerts = default!;
[Dependency] private readonly InventorySystem _inventory = default!; [Dependency] private readonly InventorySystem _inventory = default!;
public bool IsWeightless(EntityUid uid, PhysicsComponent? body = null, TransformComponent? xform = null) public bool IsWeightless(EntityUid uid, PhysicsComponent? body = null, TransformComponent? xform = null)
@@ -21,23 +24,16 @@ namespace Content.Shared.Gravity
if (TryComp<MovementIgnoreGravityComponent>(uid, out var ignoreGravityComponent)) if (TryComp<MovementIgnoreGravityComponent>(uid, out var ignoreGravityComponent))
return ignoreGravityComponent.Weightless; return ignoreGravityComponent.Weightless;
if (!Resolve(uid, ref xform)) return true; if (!Resolve(uid, ref xform))
return true;
bool gravityEnabled = false;
// If grid / map has gravity // If grid / map has gravity
if ((TryComp<GravityComponent>(xform.GridUid, out var gravity) || if ((TryComp<GravityComponent>(xform.GridUid, out var gravity) ||
TryComp(xform.MapUid, out gravity)) && gravity.Enabled) TryComp(xform.MapUid, out gravity)) && gravity.Enabled)
{ {
gravityEnabled = gravity.Enabled; return false;
if (gravityEnabled) return false;
} }
// On the map then always weightless (unless it has gravity comp obv).
if (!_mapManager.TryGetGrid(xform.GridUid, out var grid))
return true;
// Something holding us down // Something holding us down
if (_inventory.TryGetSlotEntity(uid, "shoes", out var ent)) if (_inventory.TryGetSlotEntity(uid, "shoes", out var ent))
{ {
@@ -45,21 +41,76 @@ namespace Content.Shared.Gravity
return false; return false;
} }
if (!gravityEnabled || !xform.Coordinates.IsValid(EntityManager)) return true; return true;
var tile = grid.GetTileRef(xform.Coordinates).Tile;
return tile.IsEmpty;
} }
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
SubscribeLocalEvent<GridInitializeEvent>(HandleGridInitialize); SubscribeLocalEvent<GridInitializeEvent>(HandleGridInitialize);
SubscribeLocalEvent<AlertsComponent, EntParentChangedMessage>(OnAlertsParentChange);
SubscribeLocalEvent<GravityChangedEvent>(OnGravityChange);
SubscribeLocalEvent<GravityComponent, ComponentGetState>(OnGetState);
SubscribeLocalEvent<GravityComponent, ComponentHandleState>(OnHandleState);
}
private void OnHandleState(EntityUid uid, GravityComponent component, ref ComponentHandleState args)
{
if (args.Current is not GravityComponentState state) return;
if (component.EnabledVV == state.Enabled) return;
component.EnabledVV = state.Enabled;
RaiseLocalEvent(new GravityChangedEvent(uid, component.EnabledVV));
}
private void OnGetState(EntityUid uid, GravityComponent component, ref ComponentGetState args)
{
args.State = new GravityComponentState(component.EnabledVV);
}
private void OnGravityChange(GravityChangedEvent ev)
{
foreach (var (comp, xform) in EntityQuery<AlertsComponent, TransformComponent>(true))
{
if (xform.GridUid != ev.ChangedGridIndex) continue;
if (!ev.HasGravity)
{
_alerts.ShowAlert(comp.Owner, AlertType.Weightless);
}
else
{
_alerts.ClearAlert(comp.Owner, AlertType.Weightless);
}
}
}
private void OnAlertsParentChange(EntityUid uid, AlertsComponent component, ref EntParentChangedMessage args)
{
if (IsWeightless(component.Owner))
{
_alerts.ShowAlert(uid, AlertType.Weightless);
}
else
{
_alerts.ClearAlert(uid, AlertType.Weightless);
}
} }
private void HandleGridInitialize(GridInitializeEvent ev) private void HandleGridInitialize(GridInitializeEvent ev)
{ {
EntityManager.EnsureComponent<GravityComponent>(ev.EntityUid); EntityManager.EnsureComponent<GravityComponent>(ev.EntityUid);
} }
[Serializable, NetSerializable]
private sealed class GravityComponentState : ComponentState
{
public bool Enabled { get; }
public GravityComponentState(bool enabled)
{
Enabled = enabled;
}
}
} }
} }

View File

@@ -1,17 +1,15 @@
using Content.Shared.Sound; using Content.Shared.Sound;
using Robust.Shared.GameStates;
namespace Content.Shared.Movement.Components namespace Content.Shared.Movement.Components
{ {
/// <summary> /// <summary>
/// Changes footstep sound /// Changes footstep sound
/// </summary> /// </summary>
[RegisterComponent] [RegisterComponent, NetworkedComponent]
public sealed class FootstepModifierComponent : Component public sealed class FootstepModifierComponent : Component
{ {
[DataField("footstepSoundCollection", required: true)] [DataField("footstepSoundCollection", required: true)]
public SoundSpecifier SoundCollection = default!; public SoundSpecifier Sound = default!;
[DataField("variation")]
public float Variation = default;
} }
} }

View File

@@ -49,7 +49,7 @@ namespace Content.Shared.Movement.Components
var gridId = transform.GridUid; var gridId = transform.GridUid;
if ((entityManager.TryGetComponent<GravityComponent>(transform.GridUid, out var gravity) || if ((entityManager.TryGetComponent<GravityComponent>(transform.GridUid, out var gravity) ||
entityManager.TryGetComponent(transform.MapUid, out gravity)) && gravity.Enabled) entityManager.TryGetComponent(transform.MapUid, out gravity)) && gravity.EnabledVV)
return false; return false;
if (gridId == null) if (gridId == null)
@@ -67,7 +67,7 @@ namespace Content.Shared.Movement.Components
return false; return false;
} }
if (!entityManager.GetComponent<GravityComponent>(grid.GridEntityId).Enabled) if (!entityManager.GetComponent<GravityComponent>(grid.GridEntityId).EnabledVV)
{ {
return true; return true;
} }

View File

@@ -36,7 +36,7 @@ public abstract class SharedJetpackSystem : EntitySystem
SubscribeLocalEvent<JetpackUserComponent, ComponentGetState>(OnJetpackUserGetState); SubscribeLocalEvent<JetpackUserComponent, ComponentGetState>(OnJetpackUserGetState);
SubscribeLocalEvent<JetpackUserComponent, ComponentHandleState>(OnJetpackUserHandleState); SubscribeLocalEvent<JetpackUserComponent, ComponentHandleState>(OnJetpackUserHandleState);
SubscribeLocalEvent<GravityChangedMessage>(OnJetpackUserGravityChanged); SubscribeLocalEvent<GravityChangedEvent>(OnJetpackUserGravityChanged);
} }
private void OnJetpackCanWeightlessMove(EntityUid uid, JetpackComponent component, ref CanWeightlessMoveEvent args) private void OnJetpackCanWeightlessMove(EntityUid uid, JetpackComponent component, ref CanWeightlessMoveEvent args)
@@ -44,7 +44,7 @@ public abstract class SharedJetpackSystem : EntitySystem
args.CanMove = true; args.CanMove = true;
} }
private void OnJetpackUserGravityChanged(GravityChangedMessage ev) private void OnJetpackUserGravityChanged(GravityChangedEvent ev)
{ {
var gridUid = ev.ChangedGridIndex; var gridUid = ev.ChangedGridIndex;
var jetpackQuery = GetEntityQuery<JetpackComponent>(); var jetpackQuery = GetEntityQuery<JetpackComponent>();

View File

@@ -0,0 +1,35 @@
using Content.Shared.Movement.Components;
using Content.Shared.Sound;
using Robust.Shared.GameStates;
using Robust.Shared.Serialization;
namespace Content.Shared.Movement.Systems;
public abstract partial class SharedMoverController
{
private void InitializeFootsteps()
{
SubscribeLocalEvent<FootstepModifierComponent, ComponentGetState>(OnFootGetState);
SubscribeLocalEvent<FootstepModifierComponent, ComponentHandleState>(OnFootHandleState);
}
private void OnFootHandleState(EntityUid uid, FootstepModifierComponent component, ref ComponentHandleState args)
{
if (args.Current is not FootstepModifierComponentState state) return;
component.Sound = state.Sound;
}
private void OnFootGetState(EntityUid uid, FootstepModifierComponent component, ref ComponentGetState args)
{
args.State = new FootstepModifierComponentState()
{
Sound = component.Sound,
};
}
[Serializable, NetSerializable]
private sealed class FootstepModifierComponentState : ComponentState
{
public SoundSpecifier Sound = default!;
}
}

View File

@@ -10,6 +10,7 @@ using Content.Shared.MobState.EntitySystems;
using Content.Shared.Movement.Components; using Content.Shared.Movement.Components;
using Content.Shared.Movement.Events; using Content.Shared.Movement.Events;
using Content.Shared.Pulling.Components; using Content.Shared.Pulling.Components;
using Content.Shared.Sound;
using Content.Shared.Tag; using Content.Shared.Tag;
using Robust.Shared.Audio; using Robust.Shared.Audio;
using Robust.Shared.Configuration; using Robust.Shared.Configuration;
@@ -61,6 +62,7 @@ namespace Content.Shared.Movement.Systems
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
InitializeFootsteps();
InitializeInput(); InitializeInput();
InitializeMob(); InitializeMob();
InitializePushing(); InitializePushing();
@@ -215,12 +217,13 @@ namespace Content.Shared.Movement.Systems
: worldTotal.ToWorldAngle(); : worldTotal.ToWorldAngle();
rotateXform.DeferUpdates = false; rotateXform.DeferUpdates = false;
if (!weightless && TryComp<MobMoverComponent>(mover.Owner, out var mobMover) && TryGetSound(mover, mobMover, xform, out var variation, out var sound)) if (!weightless && TryComp<MobMoverComponent>(mover.Owner, out var mobMover) &&
TryGetSound(weightless, mover, mobMover, xform, out var sound))
{ {
var soundModifier = mover.Sprinting ? 1.0f : FootstepWalkingAddedVolumeMultiplier; var soundModifier = mover.Sprinting ? 1.0f : FootstepWalkingAddedVolumeMultiplier;
SoundSystem.Play(sound, SoundSystem.Play(sound.GetSound(),
GetSoundPlayers(mover.Owner), GetSoundPlayers(mover.Owner),
mover.Owner, AudioHelpers.WithVariation(variation).WithVolume(FootstepVolume * soundModifier)); mover.Owner, sound.Params.WithVolume(FootstepVolume * soundModifier));
} }
} }
@@ -313,19 +316,17 @@ namespace Content.Shared.Movement.Systems
protected abstract bool CanSound(); protected abstract bool CanSound();
private bool TryGetSound(InputMoverComponent mover, MobMoverComponent mobMover, TransformComponent xform, out float variation, [NotNullWhen(true)] out string? sound) private bool TryGetSound(bool weightless, InputMoverComponent mover, MobMoverComponent mobMover, TransformComponent xform, [NotNullWhen(true)] out SoundSpecifier? sound)
{ {
sound = null; sound = null;
variation = 0f;
if (!CanSound() || !_tags.HasTag(mover.Owner, "FootstepSound")) return false; if (!CanSound() || !_tags.HasTag(mover.Owner, "FootstepSound")) return false;
var coordinates = xform.Coordinates; var coordinates = xform.Coordinates;
var gridId = coordinates.GetGridUid(EntityManager);
var distanceNeeded = mover.Sprinting ? StepSoundMoveDistanceRunning : StepSoundMoveDistanceWalking; var distanceNeeded = mover.Sprinting ? StepSoundMoveDistanceRunning : StepSoundMoveDistanceWalking;
// Handle footsteps. // Handle footsteps.
if (_mapManager.GridExists(gridId)) if (!weightless)
{ {
// Can happen when teleporting between grids. // Can happen when teleporting between grids.
if (!coordinates.TryDistance(EntityManager, mobMover.LastPosition, out var distance) || if (!coordinates.TryDistance(EntityManager, mobMover.LastPosition, out var distance) ||
@@ -344,7 +345,6 @@ namespace Content.Shared.Movement.Systems
return false; return false;
} }
DebugTools.Assert(gridId != null);
mobMover.LastPosition = coordinates; mobMover.LastPosition = coordinates;
if (mobMover.StepSoundDistance < distanceNeeded) return false; if (mobMover.StepSoundDistance < distanceNeeded) return false;
@@ -354,19 +354,31 @@ namespace Content.Shared.Movement.Systems
if (_inventory.TryGetSlotEntity(mover.Owner, "shoes", out var shoes) && if (_inventory.TryGetSlotEntity(mover.Owner, "shoes", out var shoes) &&
EntityManager.TryGetComponent<FootstepModifierComponent>(shoes, out var modifier)) EntityManager.TryGetComponent<FootstepModifierComponent>(shoes, out var modifier))
{ {
sound = modifier.SoundCollection.GetSound(); sound = modifier.Sound;
variation = modifier.Variation;
return true; return true;
} }
return TryGetFootstepSound(gridId!.Value, coordinates, out variation, out sound); return TryGetFootstepSound(coordinates, out sound);
} }
private bool TryGetFootstepSound(EntityUid gridId, EntityCoordinates coordinates, out float variation, [NotNullWhen(true)] out string? sound) private bool TryGetFootstepSound(EntityCoordinates coordinates, [NotNullWhen(true)] out SoundSpecifier? sound)
{ {
variation = 0f;
sound = null; sound = null;
var grid = _mapManager.GetGrid(gridId); var gridUid = coordinates.GetGridUid(EntityManager);
// Fallback to the map
if (gridUid == null)
{
if (TryComp<FootstepModifierComponent>(coordinates.GetMapUid(EntityManager), out var modifier))
{
sound = modifier.Sound;
return true;
}
return false;
}
var grid = _mapManager.GetGrid(gridUid.Value);
var tile = grid.GetTileRef(coordinates); var tile = grid.GetTileRef(coordinates);
if (tile.IsSpace(_tileDefinitionManager)) return false; if (tile.IsSpace(_tileDefinitionManager)) return false;
@@ -377,18 +389,15 @@ namespace Content.Shared.Movement.Systems
{ {
if (EntityManager.TryGetComponent(maybeFootstep, out FootstepModifierComponent? footstep)) if (EntityManager.TryGetComponent(maybeFootstep, out FootstepModifierComponent? footstep))
{ {
sound = footstep.SoundCollection.GetSound(); sound = footstep.Sound;
variation = footstep.Variation;
return true; return true;
} }
} }
// Walking on a tile. // Walking on a tile.
var def = (ContentTileDefinition) _tileDefinitionManager[tile.Tile.TypeId]; var def = (ContentTileDefinition) _tileDefinitionManager[tile.Tile.TypeId];
sound = def.FootstepSounds?.GetSound(); sound = def.FootstepSounds;
variation = FootstepVariation; return sound != null;
return !string.IsNullOrEmpty(sound);
} }
} }
} }

View File

@@ -0,0 +1,28 @@
using JetBrains.Annotations;
using Robust.Shared.GameStates;
namespace Content.Shared.Parallax;
/// <summary>
/// Handles per-map parallax
/// </summary>
[RegisterComponent, NetworkedComponent]
public sealed class ParallaxComponent : Component
{
// I wish I could use a typeserializer here but parallax is extremely client-dependent.
[DataField("parallax")]
public string Parallax = "Default";
[UsedImplicitly, ViewVariables(VVAccess.ReadWrite)]
// ReSharper disable once InconsistentNaming
public string ParallaxVV
{
get => Parallax;
set
{
if (value.Equals(Parallax)) return;
Parallax = value;
IoCManager.Resolve<IEntityManager>().Dirty(this);
}
}
}

View File

@@ -0,0 +1,16 @@
using Robust.Shared.GameStates;
using Robust.Shared.Serialization;
namespace Content.Shared.Parallax;
/// <summary>
/// Handles per-map parallax in sim. Out of sim parallax is handled by ParallaxManager.
/// </summary>
public abstract class SharedParallaxSystem: EntitySystem
{
[Serializable, NetSerializable]
protected sealed class ParallaxComponentState : ComponentState
{
public string Parallax = string.Empty;
}
}

View File

@@ -89,8 +89,6 @@ grids:
tiles: MAAAAC0AAAAtAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAALQAAAC0AAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAAAAAAAAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAAAAAAAAAAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAAAAAAAAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAADAAAAAwAAAALQAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== tiles: MAAAAC0AAAAtAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAALQAAAC0AAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAAAAAAAAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAAAAAAAAAAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAAAAAAAAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAADAAAAAwAAAALQAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
- ind: 1,-2 - ind: 1,-2
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAAAAAAAAwAAAAMAAAADAAAAAwAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAtAAAAMAAAADAAAAAtAAAAMAAAADAAAAAtAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAALQAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAALQAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAAB4AAAAeAAAAHgAAAB4AAAAeAAAAHgAAAB4AAAAeAAAAMAAAADAAAAAwAAAALQAAADAAAAAwAAAAMAAAAA== tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAAAAAAAAwAAAAMAAAADAAAAAwAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAtAAAAMAAAADAAAAAtAAAAMAAAADAAAAAtAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAALQAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAALQAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAAB4AAAAeAAAAHgAAAB4AAAAeAAAAHgAAAB4AAAAeAAAAMAAAADAAAAAwAAAALQAAADAAAAAwAAAAMAAAAA==
- ind: -2,-1
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
- ind: -2,0 - ind: -2,0
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAMAAAADAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAADAAAAAwAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAwAAAAMAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAADAAAAAwAAAAMAAAAC0AAAAtAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAwAAAAMAAAADAAAAAtAAAALQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAMAAAAC0AAAAtAAAALQAAAC0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAAA== tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAMAAAADAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAADAAAAAwAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAwAAAAMAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAADAAAAAwAAAAMAAAAC0AAAAtAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAwAAAAMAAAADAAAAAtAAAALQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAMAAAAC0AAAAtAAAALQAAAC0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAAA==
- ind: -2,1 - ind: -2,1
@@ -107,12 +105,6 @@ grids:
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAADAAAAAwAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAADAAAAAwAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
- ind: -3,-2 - ind: -3,-2
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtAAAALQAAAC0AAAAtAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALQAAAC0AAAAtAAAALQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC0AAAAtAAAALQAAAC0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtAAAALQAAAC0AAAAtAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtAAAALQAAAC0AAAAtAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALQAAAC0AAAAtAAAALQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC0AAAAtAAAALQAAAC0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtAAAALQAAAC0AAAAtAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
- ind: -4,0
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
- ind: -4,1
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
- ind: -4,-1
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
- ind: 0,2 - ind: 0,2
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
- ind: 1,2 - ind: 1,2
@@ -221,8 +213,6 @@ entities:
- pos: 3.9415665,-8.34479 - pos: 3.9415665,-8.34479
parent: 104 parent: 104
type: Transform type: Transform
- canCollide: False
type: Physics
- solution: drink - solution: drink
type: DrainableSolution type: DrainableSolution
- uid: 16 - uid: 16
@@ -238,8 +228,6 @@ entities:
- pos: 3.5882664,-8.344303 - pos: 3.5882664,-8.344303
parent: 104 parent: 104
type: Transform type: Transform
- canCollide: False
type: Physics
- uid: 18 - uid: 18
type: computerBodyScanner type: computerBodyScanner
components: components:
@@ -293,8 +281,6 @@ entities:
- pos: -0.5303154,-6.2851996 - pos: -0.5303154,-6.2851996
parent: 104 parent: 104
type: Transform type: Transform
- canCollide: False
type: Physics
- solution: drink - solution: drink
type: DrainableSolution type: DrainableSolution
- uid: 26 - uid: 26
@@ -303,8 +289,6 @@ entities:
- pos: 4.4214306,-6.3946886 - pos: 4.4214306,-6.3946886
parent: 104 parent: 104
type: Transform type: Transform
- canCollide: False
type: Physics
- uid: 27 - uid: 27
type: Windoor type: Windoor
components: components:
@@ -792,7 +776,7 @@ entities:
- name: Syndicate Outpost - name: Syndicate Outpost
type: MetaData type: MetaData
- pos: -23.318382,8.069332 - pos: -23.318382,8.069332
parent: null parent: 1291
type: Transform type: Transform
- whitelist: - whitelist:
tags: tags:
@@ -800,8 +784,8 @@ entities:
type: FTLDestination type: FTLDestination
- index: 0 - index: 0
type: MapGrid type: MapGrid
- angularDamping: 100 - angularDamping: 0.05
linearDamping: 50 linearDamping: 0.05
fixedRotation: False fixedRotation: False
bodyType: Dynamic bodyType: Dynamic
type: Physics type: Physics
@@ -1119,47 +1103,47 @@ entities:
color: '#FFFFFFFF' color: '#FFFFFFFF'
id: WarnLineS id: WarnLineS
coordinates: -9,-13 coordinates: -9,-13
171: 166:
color: '#FFFFFFFF'
id: WarnLineS
coordinates: -9,-15
170:
color: '#FFFFFFFF' color: '#FFFFFFFF'
id: WarnLineS id: WarnLineS
coordinates: -9,-14 coordinates: -9,-14
173: 167:
color: '#FFFFFFFF' color: '#FFFFFFFF'
id: WarnLineS id: WarnLineS
coordinates: -9,-17 coordinates: -9,-15
172: 168:
color: '#FFFFFFFF' color: '#FFFFFFFF'
id: WarnLineS id: WarnLineS
coordinates: -9,-16 coordinates: -9,-16
174: 169:
color: '#FFFFFFFF'
id: WarnLineS
coordinates: -9,-17
170:
color: '#FFFFFFFF' color: '#FFFFFFFF'
id: WarnLineS id: WarnLineS
coordinates: -9,-18 coordinates: -9,-18
175: 171:
color: '#FFFFFFFF' color: '#FFFFFFFF'
id: WarnLineS id: WarnLineS
coordinates: -9,-19 coordinates: -9,-19
176: 172:
color: '#FFFFFFFF' color: '#FFFFFFFF'
id: WarnLineS id: WarnLineS
coordinates: -9,-20 coordinates: -9,-20
177: 173:
color: '#FFFFFFFF' color: '#FFFFFFFF'
id: WarnLineS id: WarnLineS
coordinates: -9,-21 coordinates: -9,-21
178: 174:
color: '#FFFFFFFF' color: '#FFFFFFFF'
id: WarnLineS id: WarnLineS
coordinates: -9,-22 coordinates: -9,-22
179: 175:
color: '#FFFFFFFF' color: '#FFFFFFFF'
id: WarnLineS id: WarnLineS
coordinates: -9,-23 coordinates: -9,-23
180: 176:
color: '#FFFFFFFF' color: '#FFFFFFFF'
id: WarnLineS id: WarnLineS
coordinates: -9,-24 coordinates: -9,-24
@@ -1533,19 +1517,19 @@ entities:
color: '#FFFFFFFF' color: '#FFFFFFFF'
id: Caution id: Caution
coordinates: -18,1 coordinates: -18,1
164: 162:
color: '#FFFFFFFF' color: '#FFFFFFFF'
id: WarningLine id: WarningLine
coordinates: -18,1 coordinates: -18,1
167: 163:
color: '#FFFFFFFF' color: '#FFFFFFFF'
id: Box id: Box
coordinates: -14,1 coordinates: -14,1
168: 164:
color: '#FFFFFFFF' color: '#FFFFFFFF'
id: StandClear id: StandClear
coordinates: -14,1 coordinates: -14,1
169: 165:
color: '#FFFFFFFF' color: '#FFFFFFFF'
id: WarnEnd id: WarnEnd
coordinates: -6,21 coordinates: -6,21
@@ -5703,6 +5687,46 @@ entities:
18,32: 0 18,32: 0
19,32: 0 19,32: 0
20,32: 0 20,32: 0
-32,1: 1
-32,2: 1
-32,3: 1
-32,4: 1
-31,1: 1
-31,2: 1
-31,3: 1
-31,4: 1
-30,1: 1
-30,2: 1
-30,3: 1
-30,4: 1
-29,1: 1
-29,2: 1
-29,3: 1
-29,4: 1
-28,1: 1
-28,2: 1
-28,3: 1
-28,4: 1
-27,1: 1
-27,2: 1
-27,3: 1
-27,4: 1
-26,1: 1
-26,2: 1
-26,3: 1
-26,4: 1
-35,1: 1
-35,2: 1
-35,3: 1
-35,4: 1
-34,1: 1
-34,2: 1
-34,3: 1
-34,4: 1
-33,1: 1
-33,2: 1
-33,3: 1
-33,4: 1
uniqueMixes: uniqueMixes:
- volume: 2500 - volume: 2500
temperature: 293.15 temperature: 293.15
@@ -5715,6 +5739,17 @@ entities:
- 0 - 0
- 0 - 0
- 0 - 0
- volume: 2500
temperature: 293.15
moles:
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
type: GridAtmosphere type: GridAtmosphere
- uid: 105 - uid: 105
type: WallRiveted type: WallRiveted
@@ -13554,6 +13589,32 @@ entities:
- pos: -10.5,-1.5 - pos: -10.5,-1.5
parent: 104 parent: 104
type: Transform type: Transform
- uid: 1291
components:
- index: 2
type: Map
- footstepSoundCollection:
collection: footstep_snow
type: FootstepModifier
- gravityShakeSound: !type:SoundPathSpecifier
path: /Audio/Effects/alert.ogg
type: Gravity
- parallax: Sky
type: Parallax
- space: False
mixture:
volume: 2500
temperature: 248.15
moles:
- 21.824879
- 82.10312
- 0
- 0
- 0
- 0
- 0
- 0
type: MapAtmosphere
- uid: 1298 - uid: 1298
type: AsteroidRock type: AsteroidRock
components: components:

View File

@@ -43,7 +43,6 @@
- type: Clothing - type: Clothing
sprite: Clothing/Shoes/Misc/duck-slippers.rsi sprite: Clothing/Shoes/Misc/duck-slippers.rsi
- type: FootstepModifier - type: FootstepModifier
variation: 0.07
footstepSoundCollection: footstepSoundCollection:
collection: footstep_duck collection: footstep_duck

View File

@@ -16,7 +16,6 @@
- type: EmitSoundOnUse - type: EmitSoundOnUse
sound: sound:
collection: BikeHorn collection: BikeHorn
variation: 0.125
- type: UseDelay - type: UseDelay
delay: 0.5 delay: 0.5
- type: EmitSoundOnTrigger - type: EmitSoundOnTrigger

View File

@@ -1,5 +1,5 @@
- type: parallax - type: parallax
id: default id: Default
layers: layers:
- texture: - texture:
!type:ImageParallaxTextureSource !type:ImageParallaxTextureSource
@@ -33,3 +33,41 @@
configPath: "/Prototypes/Parallaxes/parallax_config.toml" configPath: "/Prototypes/Parallaxes/parallax_config.toml"
slowness: 0.875 slowness: 0.875
layersLQUseHQ: false layersLQUseHQ: false
# Because hyperspace and the menu need their own.
- type: parallax
id: FastSpace
layers:
- texture:
!type:ImageParallaxTextureSource
path: "/Textures/Parallaxes/layer1.png"
slowness: 0.5
scale: "1, 1"
- texture:
!type:GeneratedParallaxTextureSource
id: "hq_wizard_stars"
configPath: "/Prototypes/Parallaxes/parallax_config_stars.toml"
slowness: 0.25
- texture:
!type:GeneratedParallaxTextureSource
id: "hq_wizards_star_slower"
configPath: "/Prototypes/Parallaxes/parallax_config_stars-2.toml"
slowness: 0.15
- texture:
!type:GeneratedParallaxTextureSource
id: "hq_wizard_stars_dim"
configPath: "/Prototypes/Parallaxes/parallax_config_stars_dim.toml"
slowness: 0.375
- texture:
!type:GeneratedParallaxTextureSource
id: "hq_wizard_stars_dim_faster"
configPath: "/Prototypes/Parallaxes/parallax_config_stars_dim-2.toml"
slowness: 0.125
layersLQ:
- texture:
!type:GeneratedParallaxTextureSource
id: ""
configPath: "/Prototypes/Parallaxes/parallax_config.toml"
slowness: 0.5
layersLQUseHQ: false

View File

@@ -0,0 +1,24 @@
- type: parallax
id: Sky
layers:
- texture:
!type:ImageParallaxTextureSource
path: "/Textures/Parallaxes/land.png"
slowness: 0.98
scale: 1,1
- texture:
!type:ImageParallaxTextureSource
path: "/Textures/Parallaxes/noise.png"
slowness: 0.95
scale: "2, 2"
scrolling: "0.1, -0.05"
- type: parallax
id: Snow
layers:
- texture:
!type:ImageParallaxTextureSource
path: "/Textures/Tiles/snow.png"
slowness: 0
scale: "1, 1"
shader: ""

Binary file not shown.

After

Width:  |  Height:  |  Size: 834 KiB

View File

@@ -0,0 +1,4 @@
land.png generated from https://cpetry.github.io/TextureGenerator-Online/
Licensed under MIT at https://github.com/cpetry/TextureGenerator-Online/blob/gh-pages/LICENSE
noise.png generated from https://giggster.com/guide/cloud-texture-generator/

Binary file not shown.

After

Width:  |  Height:  |  Size: 643 KiB