Gateway destinations (#21040)
* Gateway generation * Gateway stuff * gatewehs * mercenaries * play area * Range fixes and tweaks * weh * Gateway UI polish * Lots of fixes * Knock some items off * Fix dungeon spawning Realistically we should probably be using a salvage job. * wahwah * wehvs * expression * weh * eee * a * a * WEH * frfr * Gatwey * Fix gateway windows * Fix gateway windows * a * a * Better layer masking * a * a * Noise fixes * a * Fix fractal calculations * a * More fixes * Fixes * Add layers back in * Fixes * namespaces and ftl * Other TODO * Fix distance * Cleanup * Fix test
This commit is contained in:
@@ -17,7 +17,8 @@ public sealed class GatewayBoundUserInterface : BoundUserInterface
|
|||||||
{
|
{
|
||||||
base.Open();
|
base.Open();
|
||||||
|
|
||||||
_window = new GatewayWindow();
|
_window = new GatewayWindow(EntMan.GetNetEntity(Owner));
|
||||||
|
|
||||||
_window.OpenPortal += destination =>
|
_window.OpenPortal += destination =>
|
||||||
{
|
{
|
||||||
SendMessage(new GatewayOpenPortalMessage(destination));
|
SendMessage(new GatewayOpenPortalMessage(destination));
|
||||||
|
|||||||
@@ -3,11 +3,26 @@
|
|||||||
Title="{Loc 'gateway-window-title'}"
|
Title="{Loc 'gateway-window-title'}"
|
||||||
MinSize="800 360">
|
MinSize="800 360">
|
||||||
<BoxContainer Orientation="Vertical">
|
<BoxContainer Orientation="Vertical">
|
||||||
<BoxContainer Orientation="Horizontal">
|
<BoxContainer Orientation="Horizontal">
|
||||||
<Label Name="NextCloseLabel"
|
<!-- This is wide as shit but makes it consistent with the cooldown label +
|
||||||
Text="{Loc 'gateway-window-portal-closing'}"
|
handles localisations a bit better -->
|
||||||
Margin="5"></Label>
|
<Label Name="NextUnlockLabel"
|
||||||
<ProgressBar Name="NextCloseBar"
|
Text="{Loc 'gateway-window-portal-unlock'}"
|
||||||
|
Margin="5"
|
||||||
|
SetWidth="128"/>
|
||||||
|
<ProgressBar Name="NextUnlockBar"
|
||||||
|
HorizontalExpand="True"
|
||||||
|
MinValue="0"
|
||||||
|
MaxValue="1"
|
||||||
|
SetHeight="25"/>
|
||||||
|
<Label Name="NextUnlockText" Text="0" Margin="5"/>
|
||||||
|
</BoxContainer>
|
||||||
|
<BoxContainer Orientation="Horizontal">
|
||||||
|
<Label Name="NextReadyLabel"
|
||||||
|
Text="{Loc 'gateway-window-portal-cooldown'}"
|
||||||
|
Margin="5"
|
||||||
|
SetWidth="128"/>
|
||||||
|
<ProgressBar Name="NextReadyBar"
|
||||||
HorizontalExpand="True"
|
HorizontalExpand="True"
|
||||||
MinValue="0"
|
MinValue="0"
|
||||||
MaxValue="1"
|
MaxValue="1"
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using System.Numerics;
|
||||||
using Content.Client.Computer;
|
using Content.Client.Computer;
|
||||||
using Content.Client.Stylesheets;
|
using Content.Client.Stylesheets;
|
||||||
using Content.Client.UserInterface.Controls;
|
using Content.Client.UserInterface.Controls;
|
||||||
@@ -19,30 +20,55 @@ public sealed partial class GatewayWindow : FancyWindow,
|
|||||||
private readonly IGameTiming _timing;
|
private readonly IGameTiming _timing;
|
||||||
|
|
||||||
public event Action<NetEntity>? OpenPortal;
|
public event Action<NetEntity>? OpenPortal;
|
||||||
private List<(NetEntity, string, TimeSpan, bool)> _destinations = default!;
|
private List<GatewayDestinationData> _destinations = new();
|
||||||
private NetEntity? _current;
|
|
||||||
private TimeSpan _nextClose;
|
|
||||||
private TimeSpan _lastOpen;
|
|
||||||
private List<Label> _readyLabels = default!;
|
|
||||||
private List<Button> _openButtons = default!;
|
|
||||||
|
|
||||||
public GatewayWindow()
|
public readonly NetEntity Owner;
|
||||||
|
|
||||||
|
private NetEntity? _current;
|
||||||
|
private TimeSpan _nextReady;
|
||||||
|
private TimeSpan _cooldown;
|
||||||
|
|
||||||
|
private TimeSpan _unlockTime;
|
||||||
|
private TimeSpan _nextUnlock;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Re-apply the state if the timer has elapsed.
|
||||||
|
/// </summary>
|
||||||
|
private GatewayBoundUserInterfaceState? _lastState;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Are we currently waiting on an unlock timer.
|
||||||
|
/// </summary>
|
||||||
|
private bool _isUnlockPending = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Are we currently waiting on a cooldown timer.
|
||||||
|
/// </summary>
|
||||||
|
private bool _isCooldownPending = true;
|
||||||
|
|
||||||
|
public GatewayWindow(NetEntity netEntity)
|
||||||
{
|
{
|
||||||
RobustXamlLoader.Load(this);
|
RobustXamlLoader.Load(this);
|
||||||
var dependencies = IoCManager.Instance!;
|
var dependencies = IoCManager.Instance!;
|
||||||
_timing = dependencies.Resolve<IGameTiming>();
|
_timing = dependencies.Resolve<IGameTiming>();
|
||||||
|
Owner = netEntity;
|
||||||
|
|
||||||
|
NextUnlockBar.ForegroundStyleBoxOverride = new StyleBoxFlat(Color.FromHex("#C74EBD"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateState(GatewayBoundUserInterfaceState state)
|
public void UpdateState(GatewayBoundUserInterfaceState state)
|
||||||
{
|
{
|
||||||
_destinations = state.Destinations;
|
_destinations = state.Destinations;
|
||||||
_current = state.Current;
|
_current = state.Current;
|
||||||
_nextClose = state.NextClose;
|
_nextReady = state.NextReady;
|
||||||
_lastOpen = state.LastOpen;
|
_cooldown = state.Cooldown;
|
||||||
|
_unlockTime = state.UnlockTime;
|
||||||
|
_nextUnlock = state.NextUnlock;
|
||||||
|
|
||||||
|
_isUnlockPending = _nextUnlock >= _timing.CurTime;
|
||||||
|
_isCooldownPending = _nextReady >= _timing.CurTime;
|
||||||
|
|
||||||
Container.DisposeAllChildren();
|
Container.DisposeAllChildren();
|
||||||
_readyLabels = new List<Label>(_destinations.Count);
|
|
||||||
_openButtons = new List<Button>(_destinations.Count);
|
|
||||||
|
|
||||||
if (_destinations.Count == 0)
|
if (_destinations.Count == 0)
|
||||||
{
|
{
|
||||||
@@ -63,38 +89,63 @@ public sealed partial class GatewayWindow : FancyWindow,
|
|||||||
}
|
}
|
||||||
|
|
||||||
var now = _timing.CurTime;
|
var now = _timing.CurTime;
|
||||||
|
|
||||||
foreach (var dest in _destinations)
|
foreach (var dest in _destinations)
|
||||||
{
|
{
|
||||||
var ent = dest.Item1;
|
var ent = dest.Entity;
|
||||||
var name = dest.Item2;
|
var name = dest.Name;
|
||||||
var nextReady = dest.Item3;
|
var locked = dest.Locked && _nextUnlock > _timing.CurTime;
|
||||||
var busy = dest.Item4;
|
|
||||||
|
|
||||||
var box = new BoxContainer()
|
var box = new BoxContainer()
|
||||||
{
|
{
|
||||||
Orientation = BoxContainer.LayoutOrientation.Horizontal,
|
Orientation = BoxContainer.LayoutOrientation.Horizontal,
|
||||||
Margin = new Thickness(5f, 5f)
|
Margin = new Thickness(5f, 5f),
|
||||||
};
|
};
|
||||||
|
|
||||||
box.AddChild(new Label()
|
// HOW DO I ALIGN THESE GOODER
|
||||||
|
var nameLabel = new RichTextLabel()
|
||||||
{
|
{
|
||||||
Text = name
|
VerticalAlignment = VAlignment.Center,
|
||||||
|
SetWidth = 156f,
|
||||||
|
};
|
||||||
|
|
||||||
|
nameLabel.SetMessage(name);
|
||||||
|
box.AddChild(nameLabel);
|
||||||
|
// Buffer
|
||||||
|
box.AddChild(new Control()
|
||||||
|
{
|
||||||
|
HorizontalExpand = true,
|
||||||
});
|
});
|
||||||
|
|
||||||
var readyLabel = new Label
|
bool Pressable() => ent == _current || ent == Owner;
|
||||||
|
|
||||||
|
var buttonStripe = new StripeBack()
|
||||||
{
|
{
|
||||||
Text = ReadyText(now, nextReady, busy),
|
Visible = locked,
|
||||||
Margin = new Thickness(10f, 0f, 0f, 0f)
|
HorizontalExpand = true,
|
||||||
|
VerticalExpand = true,
|
||||||
|
Margin = new Thickness(10f, 0f, 0f, 0f),
|
||||||
|
Children =
|
||||||
|
{
|
||||||
|
new Label()
|
||||||
|
{
|
||||||
|
Text = Loc.GetString("gateway-window-locked"),
|
||||||
|
HorizontalAlignment = HAlignment.Center,
|
||||||
|
VerticalAlignment = VAlignment.Center,
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
_readyLabels.Add(readyLabel);
|
|
||||||
box.AddChild(readyLabel);
|
|
||||||
|
|
||||||
var openButton = new Button()
|
var openButton = new Button()
|
||||||
{
|
{
|
||||||
Text = Loc.GetString("gateway-window-open-portal"),
|
Text = Loc.GetString("gateway-window-open-portal"),
|
||||||
Pressed = ent == _current,
|
Pressed = Pressable(),
|
||||||
ToggleMode = true,
|
ToggleMode = true,
|
||||||
Disabled = _current != null || busy || now < nextReady
|
Disabled = now < _nextReady || Pressable(),
|
||||||
|
HorizontalAlignment = HAlignment.Right,
|
||||||
|
Margin = new Thickness(10f, 0f, 0f, 0f),
|
||||||
|
Visible = !locked,
|
||||||
|
SetHeight = 32f,
|
||||||
};
|
};
|
||||||
|
|
||||||
openButton.OnPressed += args =>
|
openButton.OnPressed += args =>
|
||||||
@@ -102,21 +153,22 @@ public sealed partial class GatewayWindow : FancyWindow,
|
|||||||
OpenPortal?.Invoke(ent);
|
OpenPortal?.Invoke(ent);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (ent == _current)
|
if (Pressable())
|
||||||
{
|
{
|
||||||
openButton.AddStyleClass(StyleBase.ButtonCaution);
|
openButton.AddStyleClass(StyleBase.ButtonCaution);
|
||||||
}
|
}
|
||||||
|
|
||||||
_openButtons.Add(openButton);
|
var buttonContainer = new BoxContainer()
|
||||||
box.AddChild(new BoxContainer()
|
|
||||||
{
|
{
|
||||||
HorizontalExpand = true,
|
|
||||||
Align = BoxContainer.AlignMode.End,
|
|
||||||
Children =
|
Children =
|
||||||
{
|
{
|
||||||
openButton
|
buttonStripe,
|
||||||
}
|
openButton,
|
||||||
});
|
},
|
||||||
|
SetSize = new Vector2(128f, 40f),
|
||||||
|
};
|
||||||
|
|
||||||
|
box.AddChild(buttonContainer);
|
||||||
|
|
||||||
Container.AddChild(new PanelContainer()
|
Container.AddChild(new PanelContainer()
|
||||||
{
|
{
|
||||||
@@ -128,6 +180,8 @@ public sealed partial class GatewayWindow : FancyWindow,
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_lastState = state;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void FrameUpdate(FrameEventArgs args)
|
protected override void FrameUpdate(FrameEventArgs args)
|
||||||
@@ -135,50 +189,66 @@ public sealed partial class GatewayWindow : FancyWindow,
|
|||||||
base.FrameUpdate(args);
|
base.FrameUpdate(args);
|
||||||
|
|
||||||
var now = _timing.CurTime;
|
var now = _timing.CurTime;
|
||||||
|
var dirtyState = false;
|
||||||
|
|
||||||
// if its not going to close then show it as empty
|
// if its not going to close then show it as empty
|
||||||
if (_current == null)
|
if (_nextUnlock == TimeSpan.Zero)
|
||||||
{
|
{
|
||||||
NextCloseBar.Value = 0f;
|
NextUnlockBar.Value = 1f;
|
||||||
|
NextUnlockText.Text = "00:00";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var remaining = _nextUnlock - now;
|
||||||
|
if (remaining < TimeSpan.Zero)
|
||||||
|
{
|
||||||
|
if (_isUnlockPending)
|
||||||
|
{
|
||||||
|
dirtyState = true;
|
||||||
|
_isUnlockPending = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
NextUnlockBar.Value = 1f;
|
||||||
|
NextUnlockText.Text = "00:00";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
NextUnlockBar.Value = 1f - (float) (remaining.TotalSeconds / _unlockTime.TotalSeconds);
|
||||||
|
NextUnlockText.Text = $"{remaining.Minutes:00}:{remaining.Seconds:00}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if its not going to close then show it as empty
|
||||||
|
if (_current == null || _cooldown == TimeSpan.Zero)
|
||||||
|
{
|
||||||
|
NextReadyBar.Value = 1f;
|
||||||
NextCloseText.Text = "00:00";
|
NextCloseText.Text = "00:00";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var remaining = _nextClose - _timing.CurTime;
|
var remaining = _nextReady - now;
|
||||||
if (remaining < TimeSpan.Zero)
|
if (remaining < TimeSpan.Zero)
|
||||||
{
|
{
|
||||||
NextCloseBar.Value = 1f;
|
if (_isCooldownPending)
|
||||||
|
{
|
||||||
|
dirtyState = true;
|
||||||
|
_isCooldownPending = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
NextReadyBar.Value = 1f;
|
||||||
NextCloseText.Text = "00:00";
|
NextCloseText.Text = "00:00";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var openTime = _nextClose - _lastOpen;
|
NextReadyBar.Value = 1f - (float) (remaining.TotalSeconds / _cooldown.TotalSeconds);
|
||||||
NextCloseBar.Value = 1f - (float) (remaining / openTime);
|
|
||||||
NextCloseText.Text = $"{remaining.Minutes:00}:{remaining.Seconds:00}";
|
NextCloseText.Text = $"{remaining.Minutes:00}:{remaining.Seconds:00}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i = 0; i < _destinations.Count; i++)
|
if (dirtyState && _lastState != null)
|
||||||
{
|
{
|
||||||
var dest = _destinations[i];
|
// Refresh UI buttons.
|
||||||
var nextReady = dest.Item3;
|
UpdateState(_lastState);
|
||||||
var busy = dest.Item4;
|
|
||||||
_readyLabels[i].Text = ReadyText(now, nextReady, busy);
|
|
||||||
_openButtons[i].Disabled = _current != null || busy || now < nextReady;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private string ReadyText(TimeSpan now, TimeSpan nextReady, bool busy)
|
|
||||||
{
|
|
||||||
if (busy)
|
|
||||||
return Loc.GetString("gateway-window-already-active");
|
|
||||||
|
|
||||||
if (now < nextReady)
|
|
||||||
{
|
|
||||||
var time = nextReady - now;
|
|
||||||
return Loc.GetString("gateway-window-ready-in", ("time", $"{time.Minutes:00}:{time.Seconds:00}"));
|
|
||||||
}
|
|
||||||
|
|
||||||
return Loc.GetString("gateway-window-ready");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
57
Content.Client/Overlays/StencilOverlay.RestrictedRange.cs
Normal file
57
Content.Client/Overlays/StencilOverlay.RestrictedRange.cs
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
using System.Numerics;
|
||||||
|
using Content.Shared.Salvage;
|
||||||
|
using Robust.Client.Graphics;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
|
namespace Content.Client.Overlays;
|
||||||
|
|
||||||
|
public sealed partial class StencilOverlay
|
||||||
|
{
|
||||||
|
private void DrawRestrictedRange(in OverlayDrawArgs args, RestrictedRangeComponent rangeComp, Matrix3 invMatrix)
|
||||||
|
{
|
||||||
|
var worldHandle = args.WorldHandle;
|
||||||
|
var renderScale = args.Viewport.RenderScale.X;
|
||||||
|
// TODO: This won't handle non-standard zooms so uhh yeah, not sure how to structure it on the shader side.
|
||||||
|
var zoom = args.Viewport.Eye?.Zoom ?? Vector2.One;
|
||||||
|
var length = zoom.X;
|
||||||
|
var bufferRange = MathF.Min(10f, rangeComp.Range);
|
||||||
|
|
||||||
|
var pixelCenter = invMatrix.Transform(rangeComp.Origin);
|
||||||
|
// Something something offset?
|
||||||
|
var vertical = args.Viewport.Size.Y;
|
||||||
|
|
||||||
|
var pixelMaxRange = rangeComp.Range * renderScale / length * EyeManager.PixelsPerMeter;
|
||||||
|
var pixelBufferRange = bufferRange * renderScale / length * EyeManager.PixelsPerMeter;
|
||||||
|
var pixelMinRange = pixelMaxRange - pixelBufferRange;
|
||||||
|
|
||||||
|
_shader.SetParameter("position", new Vector2(pixelCenter.X, vertical - pixelCenter.Y));
|
||||||
|
_shader.SetParameter("maxRange", pixelMaxRange);
|
||||||
|
_shader.SetParameter("minRange", pixelMinRange);
|
||||||
|
_shader.SetParameter("bufferRange", pixelBufferRange);
|
||||||
|
_shader.SetParameter("gradient", 0.80f);
|
||||||
|
|
||||||
|
var worldAABB = args.WorldAABB;
|
||||||
|
var worldBounds = args.WorldBounds;
|
||||||
|
var position = args.Viewport.Eye?.Position.Position ?? Vector2.Zero;
|
||||||
|
var localAABB = invMatrix.TransformBox(worldAABB);
|
||||||
|
|
||||||
|
// Cut out the irrelevant bits via stencil
|
||||||
|
// This is why we don't just use parallax; we might want specific tiles to get drawn over
|
||||||
|
// particularly for planet maps or stations.
|
||||||
|
worldHandle.RenderInRenderTarget(_blep!, () =>
|
||||||
|
{
|
||||||
|
worldHandle.UseShader(_shader);
|
||||||
|
worldHandle.DrawRect(localAABB, Color.White);
|
||||||
|
}, Color.Transparent);
|
||||||
|
|
||||||
|
worldHandle.SetTransform(Matrix3.Identity);
|
||||||
|
worldHandle.UseShader(_protoManager.Index<ShaderPrototype>("StencilMask").Instance());
|
||||||
|
worldHandle.DrawTextureRect(_blep!.Texture, worldBounds);
|
||||||
|
var curTime = _timing.RealTime;
|
||||||
|
var sprite = _sprite.GetFrame(new SpriteSpecifier.Texture(new ResPath("/Textures/Parallaxes/noise.png")), curTime);
|
||||||
|
|
||||||
|
// Draw the rain
|
||||||
|
worldHandle.UseShader(_protoManager.Index<ShaderPrototype>("StencilDraw").Instance());
|
||||||
|
_parallax.DrawParallax(worldHandle, worldAABB, sprite, curTime, position, new Vector2(0.5f, 0f));
|
||||||
|
}
|
||||||
|
}
|
||||||
68
Content.Client/Overlays/StencilOverlay.Weather.cs
Normal file
68
Content.Client/Overlays/StencilOverlay.Weather.cs
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
using System.Numerics;
|
||||||
|
using Content.Shared.Weather;
|
||||||
|
using Robust.Client.Graphics;
|
||||||
|
using Robust.Shared.Map.Components;
|
||||||
|
using Robust.Shared.Physics.Components;
|
||||||
|
|
||||||
|
namespace Content.Client.Overlays;
|
||||||
|
|
||||||
|
public sealed partial class StencilOverlay
|
||||||
|
{
|
||||||
|
private void DrawWeather(in OverlayDrawArgs args, WeatherPrototype weatherProto, float alpha, Matrix3 invMatrix)
|
||||||
|
{
|
||||||
|
var worldHandle = args.WorldHandle;
|
||||||
|
var mapId = args.MapId;
|
||||||
|
var worldAABB = args.WorldAABB;
|
||||||
|
var worldBounds = args.WorldBounds;
|
||||||
|
var position = args.Viewport.Eye?.Position.Position ?? Vector2.Zero;
|
||||||
|
|
||||||
|
// Cut out the irrelevant bits via stencil
|
||||||
|
// This is why we don't just use parallax; we might want specific tiles to get drawn over
|
||||||
|
// particularly for planet maps or stations.
|
||||||
|
worldHandle.RenderInRenderTarget(_blep!, () =>
|
||||||
|
{
|
||||||
|
var bodyQuery = _entManager.GetEntityQuery<PhysicsComponent>();
|
||||||
|
var xformQuery = _entManager.GetEntityQuery<TransformComponent>();
|
||||||
|
var weatherIgnoreQuery = _entManager.GetEntityQuery<IgnoreWeatherComponent>();
|
||||||
|
|
||||||
|
// idk if this is safe to cache in a field and clear sloth help
|
||||||
|
var grids = new List<Entity<MapGridComponent>>();
|
||||||
|
_mapManager.FindGridsIntersecting(mapId, worldAABB, ref grids);
|
||||||
|
|
||||||
|
foreach (var grid in grids)
|
||||||
|
{
|
||||||
|
var matrix = _transform.GetWorldMatrix(grid, xformQuery);
|
||||||
|
Matrix3.Multiply(in matrix, in invMatrix, out var matty);
|
||||||
|
worldHandle.SetTransform(matty);
|
||||||
|
|
||||||
|
foreach (var tile in grid.Comp.GetTilesIntersecting(worldAABB))
|
||||||
|
{
|
||||||
|
// Ignored tiles for stencil
|
||||||
|
if (_weather.CanWeatherAffect(grid, tile, weatherIgnoreQuery, bodyQuery))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var gridTile = new Box2(tile.GridIndices * grid.Comp.TileSize,
|
||||||
|
(tile.GridIndices + Vector2i.One) * grid.Comp.TileSize);
|
||||||
|
|
||||||
|
worldHandle.DrawRect(gridTile, Color.White);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}, Color.Transparent);
|
||||||
|
|
||||||
|
worldHandle.SetTransform(Matrix3.Identity);
|
||||||
|
worldHandle.UseShader(_protoManager.Index<ShaderPrototype>("StencilMask").Instance());
|
||||||
|
worldHandle.DrawTextureRect(_blep!.Texture, worldBounds);
|
||||||
|
var curTime = _timing.RealTime;
|
||||||
|
var sprite = _sprite.GetFrame(weatherProto.Sprite, curTime);
|
||||||
|
|
||||||
|
// Draw the rain
|
||||||
|
worldHandle.UseShader(_protoManager.Index<ShaderPrototype>("StencilDraw").Instance());
|
||||||
|
_parallax.DrawParallax(worldHandle, worldAABB, sprite, curTime, position, Vector2.Zero, modulate: (weatherProto.Color ?? Color.White).WithAlpha(alpha));
|
||||||
|
|
||||||
|
worldHandle.SetTransform(Matrix3.Identity);
|
||||||
|
worldHandle.UseShader(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
77
Content.Client/Overlays/StencilOverlay.cs
Normal file
77
Content.Client/Overlays/StencilOverlay.cs
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
using Content.Client.Parallax;
|
||||||
|
using Content.Client.Weather;
|
||||||
|
using Content.Shared.Salvage;
|
||||||
|
using Content.Shared.Weather;
|
||||||
|
using Robust.Client.GameObjects;
|
||||||
|
using Robust.Client.Graphics;
|
||||||
|
using Robust.Shared.Enums;
|
||||||
|
using Robust.Shared.Map;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Timing;
|
||||||
|
|
||||||
|
namespace Content.Client.Overlays;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Simple re-useable overlay with stencilled texture.
|
||||||
|
/// </summary>
|
||||||
|
public sealed partial class StencilOverlay : Overlay
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IClyde _clyde = default!;
|
||||||
|
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||||
|
[Dependency] private readonly IGameTiming _timing = default!;
|
||||||
|
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||||
|
[Dependency] private readonly IPrototypeManager _protoManager = default!;
|
||||||
|
private readonly ParallaxSystem _parallax;
|
||||||
|
private readonly SharedTransformSystem _transform;
|
||||||
|
private readonly SpriteSystem _sprite;
|
||||||
|
private readonly WeatherSystem _weather;
|
||||||
|
|
||||||
|
public override OverlaySpace Space => OverlaySpace.WorldSpaceBelowFOV;
|
||||||
|
|
||||||
|
private IRenderTexture? _blep;
|
||||||
|
|
||||||
|
private readonly ShaderInstance _shader;
|
||||||
|
|
||||||
|
public StencilOverlay(ParallaxSystem parallax, SharedTransformSystem transform, SpriteSystem sprite, WeatherSystem weather)
|
||||||
|
{
|
||||||
|
ZIndex = ParallaxSystem.ParallaxZIndex + 1;
|
||||||
|
_parallax = parallax;
|
||||||
|
_transform = transform;
|
||||||
|
_sprite = sprite;
|
||||||
|
_weather = weather;
|
||||||
|
IoCManager.InjectDependencies(this);
|
||||||
|
_shader = _protoManager.Index<ShaderPrototype>("WorldGradientCircle").InstanceUnique();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Draw(in OverlayDrawArgs args)
|
||||||
|
{
|
||||||
|
var mapUid = _mapManager.GetMapEntityId(args.MapId);
|
||||||
|
var invMatrix = args.Viewport.GetWorldToLocalMatrix();
|
||||||
|
|
||||||
|
if (_blep?.Texture.Size != args.Viewport.Size)
|
||||||
|
{
|
||||||
|
_blep?.Dispose();
|
||||||
|
_blep = _clyde.CreateRenderTarget(args.Viewport.Size, new RenderTargetFormatParameters(RenderTargetColorFormat.Rgba8Srgb), name: "weather-stencil");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_entManager.TryGetComponent<WeatherComponent>(mapUid, out var comp))
|
||||||
|
{
|
||||||
|
foreach (var (proto, weather) in comp.Weather)
|
||||||
|
{
|
||||||
|
if (!_protoManager.TryIndex<WeatherPrototype>(proto, out var weatherProto))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var alpha = _weather.GetPercent(weather, mapUid);
|
||||||
|
DrawWeather(args, weatherProto, alpha, invMatrix);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_entManager.TryGetComponent<RestrictedRangeComponent>(mapUid, out var restrictedRangeComponent))
|
||||||
|
{
|
||||||
|
DrawRestrictedRange(args, restrictedRangeComponent, invMatrix);
|
||||||
|
}
|
||||||
|
|
||||||
|
args.WorldHandle.UseShader(null);
|
||||||
|
args.WorldHandle.SetTransform(Matrix3.Identity);
|
||||||
|
}
|
||||||
|
}
|
||||||
27
Content.Client/Overlays/StencilOverlaySystem.cs
Normal file
27
Content.Client/Overlays/StencilOverlaySystem.cs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
using Content.Client.Parallax;
|
||||||
|
using Content.Client.Weather;
|
||||||
|
using Robust.Client.GameObjects;
|
||||||
|
using Robust.Client.Graphics;
|
||||||
|
|
||||||
|
namespace Content.Client.Overlays;
|
||||||
|
|
||||||
|
public sealed class StencilOverlaySystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IOverlayManager _overlay = default!;
|
||||||
|
[Dependency] private readonly ParallaxSystem _parallax = default!;
|
||||||
|
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||||
|
[Dependency] private readonly SpriteSystem _sprite = default!;
|
||||||
|
[Dependency] private readonly WeatherSystem _weather = default!;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
_overlay.AddOverlay(new StencilOverlay(_parallax, _transform, _sprite, _weather));
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Shutdown()
|
||||||
|
{
|
||||||
|
base.Shutdown();
|
||||||
|
_overlay.RemoveOverlay<StencilOverlay>();
|
||||||
|
}
|
||||||
|
}
|
||||||
88
Content.Client/Parallax/BiomeDebugOverlay.cs
Normal file
88
Content.Client/Parallax/BiomeDebugOverlay.cs
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
using System.Numerics;
|
||||||
|
using System.Text;
|
||||||
|
using Content.Shared.Parallax.Biomes;
|
||||||
|
using Robust.Client.Graphics;
|
||||||
|
using Robust.Client.Input;
|
||||||
|
using Robust.Client.ResourceManagement;
|
||||||
|
using Robust.Shared.Enums;
|
||||||
|
using Robust.Shared.Map;
|
||||||
|
using Robust.Shared.Map.Components;
|
||||||
|
|
||||||
|
namespace Content.Client.Parallax;
|
||||||
|
|
||||||
|
public sealed class BiomeDebugOverlay : Overlay
|
||||||
|
{
|
||||||
|
public override OverlaySpace Space => OverlaySpace.ScreenSpace;
|
||||||
|
|
||||||
|
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||||
|
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
||||||
|
[Dependency] private readonly IInputManager _inputManager = default!;
|
||||||
|
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||||
|
[Dependency] private readonly IResourceCache _cache = default!;
|
||||||
|
[Dependency] private readonly ITileDefinitionManager _tileDefManager = default!;
|
||||||
|
|
||||||
|
private BiomeSystem _biomes;
|
||||||
|
private SharedMapSystem _maps;
|
||||||
|
|
||||||
|
private Font _font;
|
||||||
|
|
||||||
|
public BiomeDebugOverlay()
|
||||||
|
{
|
||||||
|
IoCManager.InjectDependencies(this);
|
||||||
|
|
||||||
|
_biomes = _entManager.System<BiomeSystem>();
|
||||||
|
_maps = _entManager.System<SharedMapSystem>();
|
||||||
|
|
||||||
|
_font = new VectorFont(_cache.GetResource<FontResource>("/EngineFonts/NotoSans/NotoSans-Regular.ttf"), 12);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool BeforeDraw(in OverlayDrawArgs args)
|
||||||
|
{
|
||||||
|
var mapUid = _mapManager.GetMapEntityId(args.MapId);
|
||||||
|
|
||||||
|
return _entManager.HasComponent<BiomeComponent>(mapUid);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Draw(in OverlayDrawArgs args)
|
||||||
|
{
|
||||||
|
var mouseScreenPos = _inputManager.MouseScreenPosition;
|
||||||
|
var mousePos = _eyeManager.ScreenToMap(mouseScreenPos);
|
||||||
|
|
||||||
|
if (mousePos.MapId == MapId.Nullspace || mousePos.MapId != args.MapId)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var mapUid = _mapManager.GetMapEntityId(args.MapId);
|
||||||
|
|
||||||
|
if (!_entManager.TryGetComponent(mapUid, out BiomeComponent? biomeComp) || !_entManager.TryGetComponent(mapUid, out MapGridComponent? grid))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
var nodePos = _maps.WorldToTile(mapUid, grid, mousePos.Position);
|
||||||
|
|
||||||
|
if (_biomes.TryGetEntity(nodePos, biomeComp, grid, out var ent))
|
||||||
|
{
|
||||||
|
var text = $"Entity: {ent}";
|
||||||
|
sb.AppendLine(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_biomes.TryGetDecals(nodePos, biomeComp.Layers, biomeComp.Seed, grid, out var decals))
|
||||||
|
{
|
||||||
|
var text = $"Decals: {decals.Count}";
|
||||||
|
sb.AppendLine(text);
|
||||||
|
|
||||||
|
foreach (var decal in decals)
|
||||||
|
{
|
||||||
|
var decalText = $"- {decal.ID}";
|
||||||
|
sb.AppendLine(decalText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_biomes.TryGetBiomeTile(nodePos, biomeComp.Layers, biomeComp.Seed, grid, out var tile))
|
||||||
|
{
|
||||||
|
var tileText = $"Tile: {_tileDefManager[tile.Value.TypeId].ID}";
|
||||||
|
sb.AppendLine(tileText);
|
||||||
|
}
|
||||||
|
|
||||||
|
args.ScreenHandle.DrawString(_font, mouseScreenPos.Position + new Vector2(0f, 32f), sb.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
22
Content.Client/Parallax/Commands/ShowBiomeCommand.cs
Normal file
22
Content.Client/Parallax/Commands/ShowBiomeCommand.cs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
using Robust.Client.Graphics;
|
||||||
|
using Robust.Shared.Console;
|
||||||
|
|
||||||
|
namespace Content.Client.Parallax.Commands;
|
||||||
|
|
||||||
|
public sealed class ShowBiomeCommand : LocalizedCommands
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IOverlayManager _overlayMgr = default!;
|
||||||
|
|
||||||
|
public override string Command => "showbiome";
|
||||||
|
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||||
|
{
|
||||||
|
if (_overlayMgr.HasOverlay<BiomeDebugOverlay>())
|
||||||
|
{
|
||||||
|
_overlayMgr.RemoveOverlay<BiomeDebugOverlay>();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_overlayMgr.AddOverlay(new BiomeDebugOverlay());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using System.Numerics;
|
||||||
using Content.Client.Parallax.Data;
|
using Content.Client.Parallax.Data;
|
||||||
using Content.Client.Parallax.Managers;
|
using Content.Client.Parallax.Managers;
|
||||||
using Content.Shared.Parallax;
|
using Content.Shared.Parallax;
|
||||||
@@ -72,4 +73,56 @@ public sealed class ParallaxSystem : SharedParallaxSystem
|
|||||||
{
|
{
|
||||||
return TryComp<ParallaxComponent>(mapUid, out var parallax) ? parallax.Parallax : Fallback;
|
return TryComp<ParallaxComponent>(mapUid, out var parallax) ? parallax.Parallax : Fallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Draws a texture as parallax in the specified world handle.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="worldHandle"></param>
|
||||||
|
/// <param name="worldAABB">WorldAABB to use</param>
|
||||||
|
/// <param name="sprite">Sprite to draw</param>
|
||||||
|
/// <param name="curTime">Current time, unused if scrolling not set</param>
|
||||||
|
/// <param name="position">Current position of the parallax</param>
|
||||||
|
/// <param name="scrolling">How much to scroll the parallax texture per second</param>
|
||||||
|
/// <param name="scale">Scale of the texture</param>
|
||||||
|
/// <param name="slowness">How slow the parallax moves compared to position</param>
|
||||||
|
/// <param name="modulate">Color modulation applied to drawing the texture</param>
|
||||||
|
public void DrawParallax(
|
||||||
|
DrawingHandleWorld worldHandle,
|
||||||
|
Box2 worldAABB,
|
||||||
|
Texture sprite,
|
||||||
|
TimeSpan curTime,
|
||||||
|
Vector2 position,
|
||||||
|
Vector2 scrolling,
|
||||||
|
float scale = 1f,
|
||||||
|
float slowness = 0f,
|
||||||
|
Color? modulate = null)
|
||||||
|
{
|
||||||
|
// Size of the texture in world units.
|
||||||
|
var size = sprite.Size / (float) EyeManager.PixelsPerMeter * scale;
|
||||||
|
var scrolled = scrolling * (float) curTime.TotalSeconds;
|
||||||
|
|
||||||
|
// Origin - start with the parallax shift itself.
|
||||||
|
var originBL = position * slowness + scrolled;
|
||||||
|
|
||||||
|
// Centre the image.
|
||||||
|
originBL -= size / 2;
|
||||||
|
|
||||||
|
// Remove offset so we can floor.
|
||||||
|
var flooredBL = worldAABB.BottomLeft - originBL;
|
||||||
|
|
||||||
|
// Floor to background size.
|
||||||
|
flooredBL = (flooredBL / size).Floored() * size;
|
||||||
|
|
||||||
|
// Re-offset.
|
||||||
|
flooredBL += originBL;
|
||||||
|
|
||||||
|
for (var x = flooredBL.X; x < worldAABB.Right; x += size.X)
|
||||||
|
{
|
||||||
|
for (var y = flooredBL.Y; y < worldAABB.Top; y += size.Y)
|
||||||
|
{
|
||||||
|
var box = Box2.FromDimensions(new Vector2(x, y), size);
|
||||||
|
worldHandle.DrawTextureRect(sprite, box, modulate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
7
Content.Client/Salvage/RestrictedRangeSystem.cs
Normal file
7
Content.Client/Salvage/RestrictedRangeSystem.cs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
using Content.Shared.Salvage;
|
||||||
|
|
||||||
|
namespace Content.Client.Salvage;
|
||||||
|
|
||||||
|
public sealed class RestrictedRangeSystem : SharedRestrictedRangeSystem
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -1,218 +0,0 @@
|
|||||||
using System.Linq;
|
|
||||||
using System.Numerics;
|
|
||||||
using Content.Client.Parallax;
|
|
||||||
using Content.Shared.Weather;
|
|
||||||
using Robust.Client.GameObjects;
|
|
||||||
using Robust.Client.Graphics;
|
|
||||||
using Robust.Client.ResourceManagement;
|
|
||||||
using Robust.Client.Utility;
|
|
||||||
using Robust.Shared.Enums;
|
|
||||||
using Robust.Shared.Graphics.RSI;
|
|
||||||
using Robust.Shared.Map;
|
|
||||||
using Robust.Shared.Map.Components;
|
|
||||||
using Robust.Shared.Physics.Components;
|
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
using Robust.Shared.Timing;
|
|
||||||
using Robust.Shared.Utility;
|
|
||||||
|
|
||||||
namespace Content.Client.Weather;
|
|
||||||
|
|
||||||
public sealed class WeatherOverlay : Overlay
|
|
||||||
{
|
|
||||||
[Dependency] private readonly IClyde _clyde = default!;
|
|
||||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
|
||||||
[Dependency] private readonly IGameTiming _timing = default!;
|
|
||||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
|
||||||
[Dependency] private readonly IPrototypeManager _protoManager = default!;
|
|
||||||
[Dependency] private readonly IResourceCache _cache = default!;
|
|
||||||
private readonly SharedTransformSystem _transform;
|
|
||||||
private readonly SpriteSystem _sprite;
|
|
||||||
private readonly WeatherSystem _weather;
|
|
||||||
|
|
||||||
public override OverlaySpace Space => OverlaySpace.WorldSpaceBelowFOV;
|
|
||||||
|
|
||||||
private IRenderTexture? _blep;
|
|
||||||
|
|
||||||
public WeatherOverlay(SharedTransformSystem transform, SpriteSystem sprite, WeatherSystem weather)
|
|
||||||
{
|
|
||||||
ZIndex = ParallaxSystem.ParallaxZIndex + 1;
|
|
||||||
_transform = transform;
|
|
||||||
_weather = weather;
|
|
||||||
_sprite = sprite;
|
|
||||||
IoCManager.InjectDependencies(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override bool BeforeDraw(in OverlayDrawArgs args)
|
|
||||||
{
|
|
||||||
if (args.MapId == MapId.Nullspace)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!_entManager.TryGetComponent<WeatherComponent>(_mapManager.GetMapEntityId(args.MapId), out var weather) ||
|
|
||||||
weather.Weather.Count == 0)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return base.BeforeDraw(in args);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Draw(in OverlayDrawArgs args)
|
|
||||||
{
|
|
||||||
var mapUid = _mapManager.GetMapEntityId(args.MapId);
|
|
||||||
|
|
||||||
if (!_entManager.TryGetComponent<WeatherComponent>(mapUid, out var comp))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var (proto, weather) in comp.Weather)
|
|
||||||
{
|
|
||||||
if (!_protoManager.TryIndex<WeatherPrototype>(proto, out var weatherProto))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var alpha = _weather.GetPercent(weather, mapUid);
|
|
||||||
DrawWorld(args, weatherProto, alpha);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawWorld(in OverlayDrawArgs args, WeatherPrototype weatherProto, float alpha)
|
|
||||||
{
|
|
||||||
var worldHandle = args.WorldHandle;
|
|
||||||
var mapId = args.MapId;
|
|
||||||
var worldAABB = args.WorldAABB;
|
|
||||||
var worldBounds = args.WorldBounds;
|
|
||||||
var invMatrix = args.Viewport.GetWorldToLocalMatrix();
|
|
||||||
var position = args.Viewport.Eye?.Position.Position ?? Vector2.Zero;
|
|
||||||
|
|
||||||
if (_blep?.Texture.Size != args.Viewport.Size)
|
|
||||||
{
|
|
||||||
_blep?.Dispose();
|
|
||||||
_blep = _clyde.CreateRenderTarget(args.Viewport.Size, new RenderTargetFormatParameters(RenderTargetColorFormat.Rgba8Srgb), name: "weather-stencil");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cut out the irrelevant bits via stencil
|
|
||||||
// This is why we don't just use parallax; we might want specific tiles to get drawn over
|
|
||||||
// particularly for planet maps or stations.
|
|
||||||
worldHandle.RenderInRenderTarget(_blep, () =>
|
|
||||||
{
|
|
||||||
var bodyQuery = _entManager.GetEntityQuery<PhysicsComponent>();
|
|
||||||
var xformQuery = _entManager.GetEntityQuery<TransformComponent>();
|
|
||||||
var weatherIgnoreQuery = _entManager.GetEntityQuery<IgnoreWeatherComponent>();
|
|
||||||
|
|
||||||
// idk if this is safe to cache in a field and clear sloth help
|
|
||||||
var grids = new List<Entity<MapGridComponent>>();
|
|
||||||
_mapManager.FindGridsIntersecting(mapId, worldAABB, ref grids);
|
|
||||||
|
|
||||||
foreach (var grid in grids)
|
|
||||||
{
|
|
||||||
var matrix = _transform.GetWorldMatrix(grid, xformQuery);
|
|
||||||
Matrix3.Multiply(in matrix, in invMatrix, out var matty);
|
|
||||||
worldHandle.SetTransform(matty);
|
|
||||||
|
|
||||||
foreach (var tile in grid.Comp.GetTilesIntersecting(worldAABB))
|
|
||||||
{
|
|
||||||
// Ignored tiles for stencil
|
|
||||||
if (_weather.CanWeatherAffect(grid, tile, weatherIgnoreQuery, bodyQuery))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var gridTile = new Box2(tile.GridIndices * grid.Comp.TileSize,
|
|
||||||
(tile.GridIndices + Vector2i.One) * grid.Comp.TileSize);
|
|
||||||
|
|
||||||
worldHandle.DrawRect(gridTile, Color.White);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}, Color.Transparent);
|
|
||||||
|
|
||||||
worldHandle.SetTransform(Matrix3.Identity);
|
|
||||||
worldHandle.UseShader(_protoManager.Index<ShaderPrototype>("StencilMask").Instance());
|
|
||||||
worldHandle.DrawTextureRect(_blep.Texture, worldBounds);
|
|
||||||
Texture? sprite = null;
|
|
||||||
var curTime = _timing.RealTime;
|
|
||||||
|
|
||||||
switch (weatherProto.Sprite)
|
|
||||||
{
|
|
||||||
case SpriteSpecifier.Rsi rsi:
|
|
||||||
var rsiActual = _cache.GetResource<RSIResource>(rsi.RsiPath).RSI;
|
|
||||||
rsiActual.TryGetState(rsi.RsiState, out var state);
|
|
||||||
var frames = state!.GetFrames(RsiDirection.South);
|
|
||||||
var delays = state.GetDelays();
|
|
||||||
var totalDelay = delays.Sum();
|
|
||||||
var time = curTime.TotalSeconds % totalDelay;
|
|
||||||
var delaySum = 0f;
|
|
||||||
|
|
||||||
for (var i = 0; i < delays.Length; i++)
|
|
||||||
{
|
|
||||||
var delay = delays[i];
|
|
||||||
delaySum += delay;
|
|
||||||
|
|
||||||
if (time > delaySum)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
sprite = frames[i];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
sprite ??= _sprite.Frame0(weatherProto.Sprite);
|
|
||||||
break;
|
|
||||||
case SpriteSpecifier.Texture texture:
|
|
||||||
sprite = texture.GetTexture(_cache);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw the rain
|
|
||||||
worldHandle.UseShader(_protoManager.Index<ShaderPrototype>("StencilDraw").Instance());
|
|
||||||
|
|
||||||
// TODO: This is very similar to parallax but we need stencil support but we can probably combine these somehow
|
|
||||||
// and not make it spaghetti, while getting the advantages of not-duped code?
|
|
||||||
|
|
||||||
|
|
||||||
// Okay I have spent like 5 hours on this at this point and afaict you have one of the following comprises:
|
|
||||||
// - No scrolling so the weather is always centered on the player
|
|
||||||
// - Crappy looking rotation but strafing looks okay and scrolls
|
|
||||||
// - Crappy looking strafing but rotation looks okay.
|
|
||||||
// - No rotation
|
|
||||||
// - Storing state across frames to do scrolling and just having it always do topdown.
|
|
||||||
|
|
||||||
// I have chosen no rotation.
|
|
||||||
|
|
||||||
const float scale = 1f;
|
|
||||||
const float slowness = 0f;
|
|
||||||
var scrolling = Vector2.Zero;
|
|
||||||
|
|
||||||
// Size of the texture in world units.
|
|
||||||
var size = (sprite.Size / (float) EyeManager.PixelsPerMeter) * scale;
|
|
||||||
var scrolled = scrolling * (float) curTime.TotalSeconds;
|
|
||||||
|
|
||||||
// Origin - start with the parallax shift itself.
|
|
||||||
var originBL = position * slowness + scrolled;
|
|
||||||
|
|
||||||
// Centre the image.
|
|
||||||
originBL -= size / 2;
|
|
||||||
|
|
||||||
// Remove offset so we can floor.
|
|
||||||
var flooredBL = args.WorldAABB.BottomLeft - originBL;
|
|
||||||
|
|
||||||
// Floor to background size.
|
|
||||||
flooredBL = (flooredBL / size).Floored() * size;
|
|
||||||
|
|
||||||
// Re-offset.
|
|
||||||
flooredBL += originBL;
|
|
||||||
|
|
||||||
for (var x = flooredBL.X; x < args.WorldAABB.Right; x += size.X)
|
|
||||||
{
|
|
||||||
for (var y = flooredBL.Y; y < args.WorldAABB.Top; y += size.Y)
|
|
||||||
{
|
|
||||||
var box = Box2.FromDimensions(new Vector2(x, y), size);
|
|
||||||
worldHandle.DrawTextureRect(sprite, box, (weatherProto.Color ?? Color.White).WithAlpha(alpha));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
worldHandle.SetTransform(Matrix3.Identity);
|
|
||||||
worldHandle.UseShader(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -15,7 +15,6 @@ namespace Content.Client.Weather;
|
|||||||
|
|
||||||
public sealed class WeatherSystem : SharedWeatherSystem
|
public sealed class WeatherSystem : SharedWeatherSystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IOverlayManager _overlayManager = default!;
|
|
||||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||||
[Dependency] private readonly AudioSystem _audio = default!;
|
[Dependency] private readonly AudioSystem _audio = default!;
|
||||||
[Dependency] private readonly MapSystem _mapSystem = default!;
|
[Dependency] private readonly MapSystem _mapSystem = default!;
|
||||||
@@ -28,16 +27,9 @@ public sealed class WeatherSystem : SharedWeatherSystem
|
|||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
_overlayManager.AddOverlay(new WeatherOverlay(_transform, EntityManager.System<SpriteSystem>(), this));
|
|
||||||
SubscribeLocalEvent<WeatherComponent, ComponentHandleState>(OnWeatherHandleState);
|
SubscribeLocalEvent<WeatherComponent, ComponentHandleState>(OnWeatherHandleState);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Shutdown()
|
|
||||||
{
|
|
||||||
base.Shutdown();
|
|
||||||
_overlayManager.RemoveOverlay<WeatherOverlay>();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Run(EntityUid uid, WeatherData weather, WeatherPrototype weatherProto, float frameTime)
|
protected override void Run(EntityUid uid, WeatherData weather, WeatherPrototype weatherProto, float frameTime)
|
||||||
{
|
{
|
||||||
base.Run(uid, weather, weatherProto, frameTime);
|
base.Run(uid, weather, weatherProto, frameTime);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using Content.Server.Gateway.Systems;
|
using Content.Server.Gateway.Systems;
|
||||||
using Robust.Shared.Audio;
|
using Robust.Shared.Audio;
|
||||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
namespace Content.Server.Gateway.Components;
|
namespace Content.Server.Gateway.Components;
|
||||||
|
|
||||||
@@ -10,6 +11,25 @@ namespace Content.Server.Gateway.Components;
|
|||||||
[RegisterComponent, Access(typeof(GatewaySystem))]
|
[RegisterComponent, Access(typeof(GatewaySystem))]
|
||||||
public sealed partial class GatewayComponent : Component
|
public sealed partial class GatewayComponent : Component
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Whether this destination is shown in the gateway ui.
|
||||||
|
/// If you are making a gateway for an admeme set this once you are ready for players to select it.
|
||||||
|
/// </summary>
|
||||||
|
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public bool Enabled;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Can the gateway be interacted with? If false then only settable via admins / mappers.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public bool Interactable = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Name as it shows up on the ui of station gateways.
|
||||||
|
/// </summary>
|
||||||
|
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public FormattedMessage Name = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sound to play when opening the portal.
|
/// Sound to play when opening the portal.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -32,24 +52,14 @@ public sealed partial class GatewayComponent : Component
|
|||||||
public SoundSpecifier AccessDeniedSound = new SoundPathSpecifier("/Audio/Machines/custom_deny.ogg");
|
public SoundSpecifier AccessDeniedSound = new SoundPathSpecifier("/Audio/Machines/custom_deny.ogg");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Every other gateway destination on the server.
|
/// Cooldown between opening portal / closing.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
|
||||||
/// Added on startup and when a new destination portal is created.
|
|
||||||
/// </remarks>
|
|
||||||
[DataField]
|
[DataField]
|
||||||
public HashSet<EntityUid> Destinations = new();
|
public TimeSpan Cooldown = TimeSpan.FromSeconds(30);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The time at which the portal will be closed.
|
/// The time at which the portal can next be opened.
|
||||||
/// </summary>
|
|
||||||
[ViewVariables(VVAccess.ReadWrite), DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
|
|
||||||
public TimeSpan NextClose;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The time at which the portal was last opened.
|
|
||||||
/// Only used for UI.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
|
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
|
||||||
public TimeSpan LastOpen;
|
public TimeSpan NextReady;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,48 +0,0 @@
|
|||||||
using Content.Server.Gateway.Systems;
|
|
||||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
|
||||||
|
|
||||||
namespace Content.Server.Gateway.Components;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A gateway destination linked to by station gateway(s).
|
|
||||||
/// </summary>
|
|
||||||
[RegisterComponent, Access(typeof(GatewaySystem))]
|
|
||||||
public sealed partial class GatewayDestinationComponent : Component
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Whether this destination is shown in the gateway ui.
|
|
||||||
/// If you are making a gateway for an admeme set this once you are ready for players to select it.
|
|
||||||
/// </summary>
|
|
||||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
public bool Enabled;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Name as it shows up on the ui of station gateways.
|
|
||||||
/// </summary>
|
|
||||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
public string Name = string.Empty;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Time at which this destination is ready to be linked to.
|
|
||||||
/// </summary>
|
|
||||||
[ViewVariables(VVAccess.ReadWrite), DataField(customTypeSerializer:typeof(TimeOffsetSerializer))]
|
|
||||||
public TimeSpan NextReady;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// How long the portal will be open for after linking.
|
|
||||||
/// </summary>
|
|
||||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
public TimeSpan OpenTime = TimeSpan.FromSeconds(600);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// How long the destination is not ready for after the portal closes.
|
|
||||||
/// </summary>
|
|
||||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
public TimeSpan Cooldown = TimeSpan.FromSeconds(60);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// If true, the portal can be closed by alt clicking it.
|
|
||||||
/// </summary>
|
|
||||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
public bool Closeable;
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
using Content.Shared.Parallax.Biomes.Markers;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||||
|
|
||||||
|
namespace Content.Server.Gateway.Components;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates gateway destinations at a regular interval.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent]
|
||||||
|
public sealed partial class GatewayGeneratorComponent : Component
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Prototype to spawn on the generated map if applicable.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public EntProtoId? Proto = "Gateway";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Next time another seed unlocks.
|
||||||
|
/// </summary>
|
||||||
|
[DataField(customTypeSerializer:typeof(TimeOffsetSerializer))]
|
||||||
|
public TimeSpan NextUnlock;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// How long it takes to unlock another destination once one is taken.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public TimeSpan UnlockCooldown = TimeSpan.FromMinutes(45);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Maps we've generated.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public List<EntityUid> Generated = new();
|
||||||
|
|
||||||
|
[DataField]
|
||||||
|
public int MobLayerCount = 1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Mob layers to pick from.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public List<ProtoId<BiomeMarkerLayerPrototype>> MobLayers = new()
|
||||||
|
{
|
||||||
|
"Carps",
|
||||||
|
"Xenos",
|
||||||
|
};
|
||||||
|
|
||||||
|
[DataField]
|
||||||
|
public int LootLayerCount = 3;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Loot layers to pick from.
|
||||||
|
/// </summary>
|
||||||
|
public List<ProtoId<BiomeMarkerLayerPrototype>> LootLayers = new()
|
||||||
|
{
|
||||||
|
"OreTin",
|
||||||
|
"OreQuartz",
|
||||||
|
"OreGold",
|
||||||
|
"OreSilver",
|
||||||
|
"OrePlasma",
|
||||||
|
"OreUranium",
|
||||||
|
"OreBananium",
|
||||||
|
"OreArtifactFragment",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
namespace Content.Server.Gateway.Components;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Destination created by <see cref="GatewayGeneratorComponent"/>
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent]
|
||||||
|
public sealed partial class GatewayGeneratorDestinationComponent : Component
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Generator that created this destination.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public EntityUid Generator;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Is the map locked from being used still or unlocked.
|
||||||
|
/// Used in conjunction with the attached generator's NextUnlock.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public bool Locked = true;
|
||||||
|
|
||||||
|
[DataField]
|
||||||
|
public bool Loaded;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Seed used for this destination.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public int Seed;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Origin of the gateway.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public Vector2i Origin;
|
||||||
|
}
|
||||||
|
|
||||||
234
Content.Server/Gateway/Systems/GatewayGeneratorSystem.cs
Normal file
234
Content.Server/Gateway/Systems/GatewayGeneratorSystem.cs
Normal file
@@ -0,0 +1,234 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using System.Numerics;
|
||||||
|
using Content.Server.Gateway.Components;
|
||||||
|
using Content.Server.Parallax;
|
||||||
|
using Content.Server.Procedural;
|
||||||
|
using Content.Shared.Dataset;
|
||||||
|
using Content.Shared.Movement.Components;
|
||||||
|
using Content.Shared.Parallax.Biomes;
|
||||||
|
using Content.Shared.Physics;
|
||||||
|
using Content.Shared.Procedural;
|
||||||
|
using Content.Shared.Salvage;
|
||||||
|
using Robust.Shared.Map;
|
||||||
|
using Robust.Shared.Map.Components;
|
||||||
|
using Robust.Shared.Physics.Collision.Shapes;
|
||||||
|
using Robust.Shared.Physics.Components;
|
||||||
|
using Robust.Shared.Physics.Systems;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Random;
|
||||||
|
using Robust.Shared.Timing;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
|
namespace Content.Server.Gateway.Systems;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates gateway destinations regularly and indefinitely that can be chosen from.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class GatewayGeneratorSystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IGameTiming _timing = default!;
|
||||||
|
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||||
|
[Dependency] private readonly IPrototypeManager _protoManager = default!;
|
||||||
|
[Dependency] private readonly IRobustRandom _random = default!;
|
||||||
|
[Dependency] private readonly ITileDefinitionManager _tileDefManager = default!;
|
||||||
|
[Dependency] private readonly BiomeSystem _biome = default!;
|
||||||
|
[Dependency] private readonly DungeonSystem _dungeon = default!;
|
||||||
|
[Dependency] private readonly FixtureSystem _fixtures = default!;
|
||||||
|
[Dependency] private readonly GatewaySystem _gateway = default!;
|
||||||
|
[Dependency] private readonly MetaDataSystem _metadata = default!;
|
||||||
|
[Dependency] private readonly SharedMapSystem _maps = default!;
|
||||||
|
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
|
||||||
|
|
||||||
|
[ValidatePrototypeId<DatasetPrototype>]
|
||||||
|
private const string PlanetNames = "names_borer";
|
||||||
|
|
||||||
|
// TODO:
|
||||||
|
// Fix shader some more
|
||||||
|
// Show these in UI
|
||||||
|
// Use regular mobs for thingo.
|
||||||
|
|
||||||
|
// Use salvage mission params
|
||||||
|
// Add the funny song
|
||||||
|
// Put salvage params in the UI
|
||||||
|
|
||||||
|
// Re-use salvage config stuff for the RNG
|
||||||
|
// Have it in the UI like expeditions.
|
||||||
|
|
||||||
|
// Also add weather coz it's funny.
|
||||||
|
|
||||||
|
// Add songs (incl. the downloaded one) to the ambient music playlist for planet probably.
|
||||||
|
// Copy most of salvage mission spawner
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
SubscribeLocalEvent<GatewayGeneratorComponent, EntityUnpausedEvent>(OnGeneratorUnpaused);
|
||||||
|
SubscribeLocalEvent<GatewayGeneratorComponent, MapInitEvent>(OnGeneratorMapInit);
|
||||||
|
SubscribeLocalEvent<GatewayGeneratorComponent, ComponentShutdown>(OnGeneratorShutdown);
|
||||||
|
SubscribeLocalEvent<GatewayGeneratorDestinationComponent, AttemptGatewayOpenEvent>(OnGeneratorAttemptOpen);
|
||||||
|
SubscribeLocalEvent<GatewayGeneratorDestinationComponent, GatewayOpenEvent>(OnGeneratorOpen);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnGeneratorShutdown(EntityUid uid, GatewayGeneratorComponent component, ComponentShutdown args)
|
||||||
|
{
|
||||||
|
foreach (var genUid in component.Generated)
|
||||||
|
{
|
||||||
|
if (Deleted(genUid))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
QueueDel(genUid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnGeneratorUnpaused(Entity<GatewayGeneratorComponent> ent, ref EntityUnpausedEvent args)
|
||||||
|
{
|
||||||
|
ent.Comp.NextUnlock += args.PausedTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnGeneratorMapInit(EntityUid uid, GatewayGeneratorComponent generator, MapInitEvent args)
|
||||||
|
{
|
||||||
|
for (var i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
GenerateDestination(uid, generator);
|
||||||
|
}
|
||||||
|
|
||||||
|
Dirty(uid, generator);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GenerateDestination(EntityUid uid, GatewayGeneratorComponent? generator = null)
|
||||||
|
{
|
||||||
|
if (!Resolve(uid, ref generator))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var tileDef = _tileDefManager["FloorSteel"];
|
||||||
|
const int MaxOffset = 256;
|
||||||
|
var tiles = new List<(Vector2i Index, Tile Tile)>();
|
||||||
|
var seed = _random.Next();
|
||||||
|
var random = new Random(seed);
|
||||||
|
var mapId = _mapManager.CreateMap();
|
||||||
|
var mapUid = _mapManager.GetMapEntityId(mapId);
|
||||||
|
var origin = new Vector2i(random.Next(-MaxOffset, MaxOffset), random.Next(-MaxOffset, MaxOffset));
|
||||||
|
var restriction = AddComp<RestrictedRangeComponent>(mapUid);
|
||||||
|
restriction.Origin = origin;
|
||||||
|
_biome.EnsurePlanet(mapUid, _protoManager.Index<BiomeTemplatePrototype>("Continental"), seed);
|
||||||
|
|
||||||
|
var grid = Comp<MapGridComponent>(mapUid);
|
||||||
|
|
||||||
|
for (var x = -2; x <= 2; x++)
|
||||||
|
{
|
||||||
|
for (var y = -2; y <= 2; y++)
|
||||||
|
{
|
||||||
|
tiles.Add((new Vector2i(x, y) + origin, new Tile(tileDef.TileId, variant: random.NextByte(tileDef.Variants))));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear area nearby as a sort of landing pad.
|
||||||
|
_maps.SetTiles(mapUid, grid, tiles);
|
||||||
|
|
||||||
|
var gatewayName = SharedSalvageSystem.GetFTLName(_protoManager.Index<DatasetPrototype>(PlanetNames), seed);
|
||||||
|
|
||||||
|
_metadata.SetEntityName(mapUid, gatewayName);
|
||||||
|
var originCoords = new EntityCoordinates(mapUid, origin);
|
||||||
|
|
||||||
|
var genDest = AddComp<GatewayGeneratorDestinationComponent>(mapUid);
|
||||||
|
genDest.Origin = origin;
|
||||||
|
genDest.Seed = seed;
|
||||||
|
genDest.Generator = uid;
|
||||||
|
|
||||||
|
// Enclose the area
|
||||||
|
var boundaryUid = Spawn(null, originCoords);
|
||||||
|
var boundaryPhysics = AddComp<PhysicsComponent>(boundaryUid);
|
||||||
|
var cShape = new ChainShape();
|
||||||
|
// Don't need it to be a perfect circle, just need it to be loosely accurate.
|
||||||
|
cShape.CreateLoop(Vector2.Zero, restriction.Range + 1f, false, count: 4);
|
||||||
|
_fixtures.TryCreateFixture(
|
||||||
|
boundaryUid,
|
||||||
|
cShape,
|
||||||
|
"boundary",
|
||||||
|
collisionLayer: (int) (CollisionGroup.HighImpassable | CollisionGroup.Impassable | CollisionGroup.LowImpassable),
|
||||||
|
body: boundaryPhysics);
|
||||||
|
_physics.WakeBody(boundaryUid, body: boundaryPhysics);
|
||||||
|
AddComp<BoundaryComponent>(boundaryUid);
|
||||||
|
|
||||||
|
// Create the gateway.
|
||||||
|
var gatewayUid = SpawnAtPosition(generator.Proto, originCoords);
|
||||||
|
var gatewayComp = Comp<GatewayComponent>(gatewayUid);
|
||||||
|
_gateway.SetDestinationName(gatewayUid, FormattedMessage.FromMarkup($"[color=#D381C996]{gatewayName}[/color]"), gatewayComp);
|
||||||
|
_gateway.SetEnabled(gatewayUid, true, gatewayComp);
|
||||||
|
generator.Generated.Add(mapUid);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnGeneratorAttemptOpen(Entity<GatewayGeneratorDestinationComponent> ent, ref AttemptGatewayOpenEvent args)
|
||||||
|
{
|
||||||
|
if (ent.Comp.Loaded || args.Cancelled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!TryComp(ent.Comp.Generator, out GatewayGeneratorComponent? generatorComp))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (generatorComp.NextUnlock + _metadata.GetPauseTime(ent.Owner) <= _timing.CurTime)
|
||||||
|
return;
|
||||||
|
|
||||||
|
args.Cancelled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnGeneratorOpen(Entity<GatewayGeneratorDestinationComponent> ent, ref GatewayOpenEvent args)
|
||||||
|
{
|
||||||
|
if (ent.Comp.Loaded)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (TryComp(ent.Comp.Generator, out GatewayGeneratorComponent? generatorComp))
|
||||||
|
{
|
||||||
|
generatorComp.NextUnlock = _timing.CurTime + generatorComp.UnlockCooldown;
|
||||||
|
_gateway.UpdateAllGateways();
|
||||||
|
// Generate another destination to keep them going.
|
||||||
|
GenerateDestination(ent.Comp.Generator);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!TryComp(args.MapUid, out MapGridComponent? grid))
|
||||||
|
return;
|
||||||
|
|
||||||
|
ent.Comp.Locked = false;
|
||||||
|
ent.Comp.Loaded = true;
|
||||||
|
|
||||||
|
// Do dungeon
|
||||||
|
var seed = ent.Comp.Seed;
|
||||||
|
var origin = ent.Comp.Origin;
|
||||||
|
var random = new Random(seed);
|
||||||
|
var dungeonDistance = random.Next(3, 6);
|
||||||
|
var dungeonRotation = _dungeon.GetDungeonRotation(seed);
|
||||||
|
var dungeonPosition = (origin + dungeonRotation.RotateVec(new Vector2i(0, dungeonDistance))).Floored();
|
||||||
|
|
||||||
|
_dungeon.GenerateDungeon(_protoManager.Index<DungeonConfigPrototype>("Experiment"), args.MapUid, grid, dungeonPosition, seed);
|
||||||
|
|
||||||
|
// TODO: Dungeon mobs + loot.
|
||||||
|
|
||||||
|
// Do markers on the map.
|
||||||
|
if (TryComp(ent.Owner, out BiomeComponent? biomeComp) && generatorComp != null)
|
||||||
|
{
|
||||||
|
// - Loot
|
||||||
|
var lootLayers = generatorComp.LootLayers.ToList();
|
||||||
|
|
||||||
|
for (var i = 0; i < generatorComp.LootLayerCount; i++)
|
||||||
|
{
|
||||||
|
var layerIdx = random.Next(lootLayers.Count);
|
||||||
|
var layer = lootLayers[layerIdx];
|
||||||
|
lootLayers.RemoveSwap(layerIdx);
|
||||||
|
|
||||||
|
_biome.AddMarkerLayer(biomeComp, layer.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// - Mobs
|
||||||
|
var mobLayers = generatorComp.MobLayers.ToList();
|
||||||
|
|
||||||
|
for (var i = 0; i < generatorComp.MobLayerCount; i++)
|
||||||
|
{
|
||||||
|
var layerIdx = random.Next(mobLayers.Count);
|
||||||
|
var layer = mobLayers[layerIdx];
|
||||||
|
mobLayers.RemoveSwap(layerIdx);
|
||||||
|
|
||||||
|
_biome.AddMarkerLayer(biomeComp, layer.Id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
using Content.Server.Gateway.Components;
|
using Content.Server.Gateway.Components;
|
||||||
|
using Content.Server.Station.Systems;
|
||||||
|
using Content.Server.UserInterface;
|
||||||
using Content.Shared.Access.Systems;
|
using Content.Shared.Access.Systems;
|
||||||
using Content.Shared.Gateway;
|
using Content.Shared.Gateway;
|
||||||
using Content.Shared.Popups;
|
using Content.Shared.Popups;
|
||||||
@@ -6,9 +8,8 @@ using Content.Shared.Teleportation.Components;
|
|||||||
using Content.Shared.Teleportation.Systems;
|
using Content.Shared.Teleportation.Systems;
|
||||||
using Content.Shared.Verbs;
|
using Content.Shared.Verbs;
|
||||||
using Robust.Server.GameObjects;
|
using Robust.Server.GameObjects;
|
||||||
using Robust.Shared.Audio;
|
|
||||||
using Robust.Shared.GameObjects;
|
|
||||||
using Robust.Shared.Timing;
|
using Robust.Shared.Timing;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
namespace Content.Server.Gateway.Systems;
|
namespace Content.Server.Gateway.Systems;
|
||||||
|
|
||||||
@@ -19,6 +20,8 @@ public sealed class GatewaySystem : EntitySystem
|
|||||||
[Dependency] private readonly LinkedEntitySystem _linkedEntity = default!;
|
[Dependency] private readonly LinkedEntitySystem _linkedEntity = default!;
|
||||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||||
|
[Dependency] private readonly MetaDataSystem _metadata = default!;
|
||||||
|
[Dependency] private readonly StationSystem _stations = default!;
|
||||||
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||||
[Dependency] private readonly UserInterfaceSystem _ui = default!;
|
[Dependency] private readonly UserInterfaceSystem _ui = default!;
|
||||||
|
|
||||||
@@ -26,62 +29,109 @@ public sealed class GatewaySystem : EntitySystem
|
|||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<GatewayComponent, EntityUnpausedEvent>(OnGatewayUnpaused);
|
||||||
SubscribeLocalEvent<GatewayComponent, ComponentStartup>(OnStartup);
|
SubscribeLocalEvent<GatewayComponent, ComponentStartup>(OnStartup);
|
||||||
|
SubscribeLocalEvent<GatewayComponent, ActivatableUIOpenAttemptEvent>(OnGatewayOpenAttempt);
|
||||||
SubscribeLocalEvent<GatewayComponent, BoundUIOpenedEvent>(UpdateUserInterface);
|
SubscribeLocalEvent<GatewayComponent, BoundUIOpenedEvent>(UpdateUserInterface);
|
||||||
SubscribeLocalEvent<GatewayComponent, GatewayOpenPortalMessage>(OnOpenPortal);
|
SubscribeLocalEvent<GatewayComponent, GatewayOpenPortalMessage>(OnOpenPortal);
|
||||||
|
|
||||||
SubscribeLocalEvent<GatewayDestinationComponent, ComponentStartup>(OnDestinationStartup);
|
|
||||||
SubscribeLocalEvent<GatewayDestinationComponent, ComponentShutdown>(OnDestinationShutdown);
|
|
||||||
SubscribeLocalEvent<GatewayDestinationComponent, GetVerbsEvent<AlternativeVerb>>(OnDestinationGetVerbs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Update(float frameTime)
|
public void SetEnabled(EntityUid uid, bool value, GatewayComponent? component = null)
|
||||||
{
|
{
|
||||||
base.Update(frameTime);
|
if (!Resolve(uid, ref component) || component.Enabled == value)
|
||||||
|
return;
|
||||||
|
|
||||||
// close portals after theyve been open long enough
|
component.Enabled = value;
|
||||||
var query = EntityQueryEnumerator<GatewayComponent, PortalComponent>();
|
UpdateAllGateways();
|
||||||
while (query.MoveNext(out var uid, out var comp, out var _))
|
}
|
||||||
{
|
|
||||||
if (_timing.CurTime < comp.NextClose)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
ClosePortal(uid, comp);
|
private void OnGatewayUnpaused(EntityUid uid, GatewayComponent component, ref EntityUnpausedEvent args)
|
||||||
}
|
{
|
||||||
|
component.NextReady += args.PausedTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnStartup(EntityUid uid, GatewayComponent comp, ComponentStartup args)
|
private void OnStartup(EntityUid uid, GatewayComponent comp, ComponentStartup args)
|
||||||
{
|
{
|
||||||
// add existing destinations
|
|
||||||
var query = EntityQueryEnumerator<GatewayDestinationComponent>();
|
|
||||||
while (query.MoveNext(out var dest, out _))
|
|
||||||
{
|
|
||||||
comp.Destinations.Add(dest);
|
|
||||||
}
|
|
||||||
|
|
||||||
// no need to update ui since its just been created, just do portal
|
// no need to update ui since its just been created, just do portal
|
||||||
UpdateAppearance(uid);
|
UpdateAppearance(uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnGatewayOpenAttempt(EntityUid uid, GatewayComponent component, ref ActivatableUIOpenAttemptEvent args)
|
||||||
|
{
|
||||||
|
if (!component.Enabled || !component.Interactable)
|
||||||
|
args.Cancel();
|
||||||
|
}
|
||||||
|
|
||||||
private void UpdateUserInterface<T>(EntityUid uid, GatewayComponent comp, T args)
|
private void UpdateUserInterface<T>(EntityUid uid, GatewayComponent comp, T args)
|
||||||
{
|
{
|
||||||
UpdateUserInterface(uid, comp);
|
UpdateUserInterface(uid, comp);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateUserInterface(EntityUid uid, GatewayComponent comp)
|
public void UpdateAllGateways()
|
||||||
{
|
{
|
||||||
var destinations = new List<(NetEntity, string, TimeSpan, bool)>();
|
var query = AllEntityQuery<GatewayComponent, TransformComponent>();
|
||||||
foreach (var destUid in comp.Destinations)
|
|
||||||
|
while (query.MoveNext(out var uid, out var comp, out var xform))
|
||||||
{
|
{
|
||||||
var dest = Comp<GatewayDestinationComponent>(destUid);
|
UpdateUserInterface(uid, comp, xform);
|
||||||
if (!dest.Enabled)
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateUserInterface(EntityUid uid, GatewayComponent comp, TransformComponent? xform = null)
|
||||||
|
{
|
||||||
|
if (!Resolve(uid, ref xform))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var destinations = new List<GatewayDestinationData>();
|
||||||
|
var query = AllEntityQuery<GatewayComponent, TransformComponent>();
|
||||||
|
|
||||||
|
var nextUnlock = TimeSpan.Zero;
|
||||||
|
var unlockTime = TimeSpan.Zero;
|
||||||
|
|
||||||
|
// Next unlock is based off of:
|
||||||
|
// - Our station's unlock timer (if we have a station)
|
||||||
|
// - If our map is a generated destination then use the generator that made it
|
||||||
|
|
||||||
|
if (TryComp(_stations.GetOwningStation(uid), out GatewayGeneratorComponent? generatorComp) ||
|
||||||
|
(TryComp(xform.MapUid, out GatewayGeneratorDestinationComponent? generatorDestination) &&
|
||||||
|
TryComp(generatorDestination.Generator, out generatorComp)))
|
||||||
|
{
|
||||||
|
nextUnlock = generatorComp.NextUnlock;
|
||||||
|
unlockTime = generatorComp.UnlockCooldown;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (query.MoveNext(out var destUid, out var dest, out var destXform))
|
||||||
|
{
|
||||||
|
if (!dest.Enabled || destUid == uid)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
destinations.Add((GetNetEntity(destUid), dest.Name, dest.NextReady, HasComp<PortalComponent>(destUid)));
|
// Show destination if either no destination comp on the map or it's ours.
|
||||||
|
TryComp<GatewayGeneratorDestinationComponent>(destXform.MapUid, out var gatewayDestination);
|
||||||
|
|
||||||
|
destinations.Add(new GatewayDestinationData()
|
||||||
|
{
|
||||||
|
Entity = GetNetEntity(destUid),
|
||||||
|
// Fallback to grid's ID if applicable.
|
||||||
|
Name = dest.Name.IsEmpty && destXform.GridUid != null ? FormattedMessage.FromUnformatted(MetaData(destXform.GridUid.Value).EntityName) : dest.Name ,
|
||||||
|
Portal = HasComp<PortalComponent>(destUid),
|
||||||
|
// If NextUnlock < CurTime it's unlocked, however
|
||||||
|
// we'll always send the client if it's locked
|
||||||
|
// It can just infer unlock times locally and not have to worry about it here.
|
||||||
|
Locked = gatewayDestination != null && gatewayDestination.Locked
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_linkedEntity.GetLink(uid, out var current);
|
_linkedEntity.GetLink(uid, out var current);
|
||||||
var state = new GatewayBoundUserInterfaceState(destinations, GetNetEntity(current), comp.NextClose, comp.LastOpen);
|
|
||||||
|
var state = new GatewayBoundUserInterfaceState(
|
||||||
|
destinations,
|
||||||
|
GetNetEntity(current),
|
||||||
|
comp.NextReady,
|
||||||
|
comp.Cooldown,
|
||||||
|
nextUnlock,
|
||||||
|
unlockTime
|
||||||
|
);
|
||||||
|
|
||||||
_ui.TrySetUiState(uid, GatewayUiKey.Key, state);
|
_ui.TrySetUiState(uid, GatewayUiKey.Key, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,38 +142,58 @@ public sealed class GatewaySystem : EntitySystem
|
|||||||
|
|
||||||
private void OnOpenPortal(EntityUid uid, GatewayComponent comp, GatewayOpenPortalMessage args)
|
private void OnOpenPortal(EntityUid uid, GatewayComponent comp, GatewayOpenPortalMessage args)
|
||||||
{
|
{
|
||||||
if (args.Session.AttachedEntity == null)
|
if (args.Session.AttachedEntity == null || GetNetEntity(uid) == args.Destination ||
|
||||||
|
!comp.Enabled || !comp.Interactable)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// if the gateway has an access reader check it before allowing opening
|
// if the gateway has an access reader check it before allowing opening
|
||||||
var user = args.Session.AttachedEntity.Value;
|
var user = args.Session.AttachedEntity.Value;
|
||||||
if (CheckAccess(user, uid))
|
if (CheckAccess(user, uid, comp))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// can't link if portal is already open on either side, the destination is invalid or on cooldown
|
// can't link if portal is already open on either side, the destination is invalid or on cooldown
|
||||||
var desto = GetEntity(args.Destination);
|
var desto = GetEntity(args.Destination);
|
||||||
|
|
||||||
if (HasComp<PortalComponent>(uid) ||
|
// If it's already open / not enabled / we're not ready DENY.
|
||||||
HasComp<PortalComponent>(desto) ||
|
if (!TryComp<GatewayComponent>(desto, out var dest) ||
|
||||||
!TryComp<GatewayDestinationComponent>(desto, out var dest) ||
|
|
||||||
!dest.Enabled ||
|
!dest.Enabled ||
|
||||||
_timing.CurTime < dest.NextReady)
|
_timing.CurTime < _metadata.GetPauseTime(uid) + comp.NextReady)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: admin log???
|
// TODO: admin log???
|
||||||
|
ClosePortal(uid, comp, false);
|
||||||
OpenPortal(uid, comp, desto, dest);
|
OpenPortal(uid, comp, desto, dest);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OpenPortal(EntityUid uid, GatewayComponent comp, EntityUid dest, GatewayDestinationComponent destComp)
|
private void OpenPortal(EntityUid uid, GatewayComponent comp, EntityUid dest, GatewayComponent destComp, TransformComponent? destXform = null)
|
||||||
{
|
{
|
||||||
_linkedEntity.TryLink(uid, dest);
|
if (!Resolve(dest, ref destXform) || destXform.MapUid == null)
|
||||||
EnsureComp<PortalComponent>(uid).CanTeleportToOtherMaps = true;
|
return;
|
||||||
EnsureComp<PortalComponent>(dest).CanTeleportToOtherMaps = true;
|
|
||||||
|
var ev = new AttemptGatewayOpenEvent(destXform.MapUid.Value, dest);
|
||||||
|
RaiseLocalEvent(destXform.MapUid.Value, ref ev);
|
||||||
|
|
||||||
|
if (ev.Cancelled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_linkedEntity.OneWayLink(uid, dest);
|
||||||
|
|
||||||
|
var sourcePortal = EnsureComp<PortalComponent>(uid);
|
||||||
|
var targetPortal = EnsureComp<PortalComponent>(dest);
|
||||||
|
|
||||||
|
sourcePortal.CanTeleportToOtherMaps = true;
|
||||||
|
targetPortal.CanTeleportToOtherMaps = true;
|
||||||
|
|
||||||
|
sourcePortal.RandomTeleport = false;
|
||||||
|
targetPortal.RandomTeleport = false;
|
||||||
|
|
||||||
|
var openEv = new GatewayOpenEvent(destXform.MapUid.Value, dest);
|
||||||
|
RaiseLocalEvent(destXform.MapUid.Value, ref openEv);
|
||||||
|
|
||||||
// for ui
|
// for ui
|
||||||
comp.LastOpen = _timing.CurTime;
|
comp.NextReady = _timing.CurTime + comp.Cooldown;
|
||||||
// close automatically after time is up
|
|
||||||
comp.NextClose = comp.LastOpen + destComp.OpenTime;
|
|
||||||
|
|
||||||
_audio.PlayPvs(comp.OpenSound, uid);
|
_audio.PlayPvs(comp.OpenSound, uid);
|
||||||
_audio.PlayPvs(comp.OpenSound, dest);
|
_audio.PlayPvs(comp.OpenSound, dest);
|
||||||
@@ -133,7 +203,7 @@ public sealed class GatewaySystem : EntitySystem
|
|||||||
UpdateAppearance(dest);
|
UpdateAppearance(dest);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ClosePortal(EntityUid uid, GatewayComponent? comp = null)
|
private void ClosePortal(EntityUid uid, GatewayComponent? comp = null, bool update = true)
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref comp))
|
if (!Resolve(uid, ref comp))
|
||||||
return;
|
return;
|
||||||
@@ -142,7 +212,7 @@ public sealed class GatewaySystem : EntitySystem
|
|||||||
if (!_linkedEntity.GetLink(uid, out var dest))
|
if (!_linkedEntity.GetLink(uid, out var dest))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (TryComp<GatewayDestinationComponent>(dest, out var destComp))
|
if (TryComp<GatewayComponent>(dest, out var destComp))
|
||||||
{
|
{
|
||||||
// portals closed, put it on cooldown and let it eventually be opened again
|
// portals closed, put it on cooldown and let it eventually be opened again
|
||||||
destComp.NextReady = _timing.CurTime + destComp.Cooldown;
|
destComp.NextReady = _timing.CurTime + destComp.Cooldown;
|
||||||
@@ -153,46 +223,35 @@ public sealed class GatewaySystem : EntitySystem
|
|||||||
|
|
||||||
_linkedEntity.TryUnlink(uid, dest.Value);
|
_linkedEntity.TryUnlink(uid, dest.Value);
|
||||||
RemComp<PortalComponent>(dest.Value);
|
RemComp<PortalComponent>(dest.Value);
|
||||||
UpdateUserInterface(uid, comp);
|
|
||||||
UpdateAppearance(uid);
|
if (update)
|
||||||
UpdateAppearance(dest.Value);
|
{
|
||||||
|
UpdateUserInterface(uid, comp);
|
||||||
|
UpdateAppearance(uid);
|
||||||
|
UpdateAppearance(dest.Value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnDestinationStartup(EntityUid uid, GatewayDestinationComponent comp, ComponentStartup args)
|
private void OnDestinationStartup(EntityUid uid, GatewayComponent comp, ComponentStartup args)
|
||||||
{
|
{
|
||||||
var query = EntityQueryEnumerator<GatewayComponent>();
|
var query = AllEntityQuery<GatewayComponent>();
|
||||||
while (query.MoveNext(out var gatewayUid, out var gateway))
|
while (query.MoveNext(out var gatewayUid, out var gateway))
|
||||||
{
|
{
|
||||||
gateway.Destinations.Add(uid);
|
|
||||||
UpdateUserInterface(gatewayUid, gateway);
|
UpdateUserInterface(gatewayUid, gateway);
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateAppearance(uid);
|
UpdateAppearance(uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnDestinationShutdown(EntityUid uid, GatewayDestinationComponent comp, ComponentShutdown args)
|
private void OnDestinationShutdown(EntityUid uid, GatewayComponent comp, ComponentShutdown args)
|
||||||
{
|
{
|
||||||
var query = EntityQueryEnumerator<GatewayComponent>();
|
var query = AllEntityQuery<GatewayComponent>();
|
||||||
while (query.MoveNext(out var gatewayUid, out var gateway))
|
while (query.MoveNext(out var gatewayUid, out var gateway))
|
||||||
{
|
{
|
||||||
gateway.Destinations.Remove(uid);
|
|
||||||
UpdateUserInterface(gatewayUid, gateway);
|
UpdateUserInterface(gatewayUid, gateway);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnDestinationGetVerbs(EntityUid uid, GatewayDestinationComponent comp, GetVerbsEvent<AlternativeVerb> args)
|
|
||||||
{
|
|
||||||
if (!comp.Closeable || !args.CanInteract || !args.CanAccess)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// a portal is open so add verb to close it
|
|
||||||
args.Verbs.Add(new AlternativeVerb()
|
|
||||||
{
|
|
||||||
Act = () => TryClose(uid, args.User),
|
|
||||||
Text = Loc.GetString("gateway-close-portal")
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void TryClose(EntityUid uid, EntityUid user)
|
private void TryClose(EntityUid uid, EntityUid user)
|
||||||
{
|
{
|
||||||
// portal already closed so cant close it
|
// portal already closed so cant close it
|
||||||
@@ -222,4 +281,31 @@ public sealed class GatewaySystem : EntitySystem
|
|||||||
_audio.PlayPvs(comp.AccessDeniedSound, uid);
|
_audio.PlayPvs(comp.AccessDeniedSound, uid);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetDestinationName(EntityUid gatewayUid, FormattedMessage gatewayName, GatewayComponent? gatewayComp = null)
|
||||||
|
{
|
||||||
|
if (!Resolve(gatewayUid, ref gatewayComp))
|
||||||
|
return;
|
||||||
|
|
||||||
|
gatewayComp.Name = gatewayName;
|
||||||
|
Dirty(gatewayUid, gatewayComp);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raised directed on the target map when a GatewayDestination is attempted to be opened.
|
||||||
|
/// </summary>
|
||||||
|
[ByRefEvent]
|
||||||
|
public record struct AttemptGatewayOpenEvent(EntityUid MapUid, EntityUid GatewayDestinationUid)
|
||||||
|
{
|
||||||
|
public readonly EntityUid MapUid = MapUid;
|
||||||
|
public readonly EntityUid GatewayDestinationUid = GatewayDestinationUid;
|
||||||
|
|
||||||
|
public bool Cancelled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raised directed on the target map when a gateway is opened.
|
||||||
|
/// </summary>
|
||||||
|
[ByRefEvent]
|
||||||
|
public readonly record struct GatewayOpenEvent(EntityUid MapUid, EntityUid GatewayDestinationUid);
|
||||||
|
|||||||
@@ -27,9 +27,8 @@ public sealed class PlanetCommand : IConsoleCommand
|
|||||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||||
[Dependency] private readonly IPrototypeManager _protoManager = default!;
|
[Dependency] private readonly IPrototypeManager _protoManager = default!;
|
||||||
[Dependency] private readonly IRobustRandom _random = default!;
|
|
||||||
|
|
||||||
public string Command => $"planet";
|
public string Command => "planet";
|
||||||
public string Description => Loc.GetString("cmd-planet-desc");
|
public string Description => Loc.GetString("cmd-planet-desc");
|
||||||
public string Help => Loc.GetString("cmd-planet-help", ("command", Command));
|
public string Help => Loc.GetString("cmd-planet-help", ("command", Command));
|
||||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||||
@@ -60,46 +59,10 @@ public sealed class PlanetCommand : IConsoleCommand
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var mapUid = _mapManager.GetMapEntityId(mapId);
|
|
||||||
MetaDataComponent? metadata = null;
|
|
||||||
|
|
||||||
var biome = _entManager.EnsureComponent<BiomeComponent>(mapUid);
|
|
||||||
var biomeSystem = _entManager.System<BiomeSystem>();
|
var biomeSystem = _entManager.System<BiomeSystem>();
|
||||||
biomeSystem.SetSeed(biome, _random.Next());
|
var mapUid = _mapManager.GetMapEntityId(mapId);
|
||||||
biomeSystem.SetTemplate(biome, biomeTemplate);
|
biomeSystem.EnsurePlanet(mapUid, biomeTemplate);
|
||||||
_entManager.Dirty(biome);
|
|
||||||
|
|
||||||
var gravity = _entManager.EnsureComponent<GravityComponent>(mapUid);
|
|
||||||
gravity.Enabled = true;
|
|
||||||
gravity.Inherent = true;
|
|
||||||
_entManager.Dirty(gravity, metadata);
|
|
||||||
|
|
||||||
// Day lighting
|
|
||||||
// Daylight: #D8B059
|
|
||||||
// Midday: #E6CB8B
|
|
||||||
// Moonlight: #2b3143
|
|
||||||
// Lava: #A34931
|
|
||||||
|
|
||||||
var light = _entManager.EnsureComponent<MapLightComponent>(mapUid);
|
|
||||||
light.AmbientLightColor = Color.FromHex("#D8B059");
|
|
||||||
_entManager.Dirty(light, metadata);
|
|
||||||
|
|
||||||
// Atmos
|
|
||||||
var atmos = _entManager.EnsureComponent<MapAtmosphereComponent>(mapUid);
|
|
||||||
|
|
||||||
var moles = new float[Atmospherics.AdjustedNumberOfGases];
|
|
||||||
moles[(int) Gas.Oxygen] = 21.824779f;
|
|
||||||
moles[(int) Gas.Nitrogen] = 82.10312f;
|
|
||||||
|
|
||||||
var mixture = new GasMixture(2500)
|
|
||||||
{
|
|
||||||
Temperature = 293.15f,
|
|
||||||
Moles = moles,
|
|
||||||
};
|
|
||||||
|
|
||||||
_entManager.System<AtmosphereSystem>().SetMapAtmosphere(mapUid, false, mixture, atmos);
|
|
||||||
|
|
||||||
_entManager.EnsureComponent<MapGridComponent>(mapUid);
|
|
||||||
shell.WriteLine(Loc.GetString("cmd-planet-success", ("mapId", mapId)));
|
shell.WriteLine(Loc.GetString("cmd-planet-success", ("mapId", mapId)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
32
Content.Server/Movement/Systems/BoundarySystem.cs
Normal file
32
Content.Server/Movement/Systems/BoundarySystem.cs
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
using Content.Shared.Movement.Components;
|
||||||
|
using Robust.Shared.Physics.Events;
|
||||||
|
|
||||||
|
namespace Content.Server.Movement.Systems;
|
||||||
|
|
||||||
|
public sealed class BoundarySystem : EntitySystem
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* The real reason this even exists is because with out mover controller it's really easy to clip out of bounds on chain shapes.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[Dependency] private readonly SharedTransformSystem _xform = default!;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
SubscribeLocalEvent<BoundaryComponent, StartCollideEvent>(OnBoundaryCollide);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnBoundaryCollide(Entity<BoundaryComponent> ent, ref StartCollideEvent args)
|
||||||
|
{
|
||||||
|
var center = _xform.GetWorldPosition(ent.Owner);
|
||||||
|
var otherXform = Transform(args.OtherEntity);
|
||||||
|
var collisionPoint = _xform.GetWorldPosition(otherXform);
|
||||||
|
var offset = collisionPoint - center;
|
||||||
|
offset = offset.Normalized() * (offset.Length() - ent.Comp.Offset);
|
||||||
|
// If for whatever reason you want to yeet them to the other side.
|
||||||
|
// offset = new Angle(MathF.PI).RotateVec(offset);
|
||||||
|
|
||||||
|
_xform.SetWorldPosition(otherXform, center + offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -67,7 +67,7 @@ public sealed partial class BiomeSystem
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_proto.TryIndex<BiomeTemplatePrototype>(args[1], out var template))
|
if (!ProtoManager.TryIndex<BiomeTemplatePrototype>(args[1], out var template))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -92,7 +92,7 @@ public sealed partial class BiomeSystem
|
|||||||
if (args.Length == 2)
|
if (args.Length == 2)
|
||||||
{
|
{
|
||||||
return CompletionResult.FromHintOptions(
|
return CompletionResult.FromHintOptions(
|
||||||
CompletionHelper.PrototypeIDs<BiomeTemplatePrototype>(proto: _proto), "Biome template");
|
CompletionHelper.PrototypeIDs<BiomeTemplatePrototype>(proto: ProtoManager), "Biome template");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (args.Length == 3)
|
if (args.Length == 3)
|
||||||
@@ -146,7 +146,7 @@ public sealed partial class BiomeSystem
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_proto.HasIndex<BiomeMarkerLayerPrototype>(args[1]))
|
if (!ProtoManager.HasIndex<BiomeMarkerLayerPrototype>(args[1]))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -164,7 +164,7 @@ public sealed partial class BiomeSystem
|
|||||||
if (args.Length == 2)
|
if (args.Length == 2)
|
||||||
{
|
{
|
||||||
return CompletionResult.FromHintOptions(
|
return CompletionResult.FromHintOptions(
|
||||||
CompletionHelper.PrototypeIDs<BiomeMarkerLayerPrototype>(proto: _proto), "Marker");
|
CompletionHelper.PrototypeIDs<BiomeMarkerLayerPrototype>(proto: ProtoManager), "Marker");
|
||||||
}
|
}
|
||||||
|
|
||||||
return CompletionResult.Empty;
|
return CompletionResult.Empty;
|
||||||
|
|||||||
@@ -1,9 +1,14 @@
|
|||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Content.Server.Atmos;
|
||||||
|
using Content.Server.Atmos.Components;
|
||||||
|
using Content.Server.Atmos.EntitySystems;
|
||||||
using Content.Server.Decals;
|
using Content.Server.Decals;
|
||||||
using Content.Server.Ghost.Roles.Components;
|
using Content.Server.Ghost.Roles.Components;
|
||||||
using Content.Server.Shuttles.Events;
|
using Content.Server.Shuttles.Events;
|
||||||
|
using Content.Shared.Atmos;
|
||||||
using Content.Shared.Decals;
|
using Content.Shared.Decals;
|
||||||
|
using Content.Shared.Gravity;
|
||||||
using Content.Shared.Parallax.Biomes;
|
using Content.Shared.Parallax.Biomes;
|
||||||
using Content.Shared.Parallax.Biomes.Layers;
|
using Content.Shared.Parallax.Biomes.Layers;
|
||||||
using Content.Shared.Parallax.Biomes.Markers;
|
using Content.Shared.Parallax.Biomes.Markers;
|
||||||
@@ -32,10 +37,11 @@ public sealed partial class BiomeSystem : SharedBiomeSystem
|
|||||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||||
[Dependency] private readonly IParallelManager _parallel = default!;
|
[Dependency] private readonly IParallelManager _parallel = default!;
|
||||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||||
[Dependency] private readonly IPrototypeManager _proto = default!;
|
|
||||||
[Dependency] private readonly IRobustRandom _random = default!;
|
[Dependency] private readonly IRobustRandom _random = default!;
|
||||||
[Dependency] private readonly ISerializationManager _serManager = default!;
|
[Dependency] private readonly ISerializationManager _serManager = default!;
|
||||||
|
[Dependency] private readonly AtmosphereSystem _atmos = default!;
|
||||||
[Dependency] private readonly DecalSystem _decals = default!;
|
[Dependency] private readonly DecalSystem _decals = default!;
|
||||||
|
[Dependency] private readonly SharedMapSystem _mapSystem = default!;
|
||||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||||
|
|
||||||
private readonly HashSet<EntityUid> _handledEntities = new();
|
private readonly HashSet<EntityUid> _handledEntities = new();
|
||||||
@@ -61,20 +67,20 @@ public sealed partial class BiomeSystem : SharedBiomeSystem
|
|||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
SubscribeLocalEvent<BiomeComponent, ComponentStartup>(OnBiomeStartup);
|
Log.Level = LogLevel.Debug;
|
||||||
SubscribeLocalEvent<BiomeComponent, MapInitEvent>(OnBiomeMapInit);
|
SubscribeLocalEvent<BiomeComponent, MapInitEvent>(OnBiomeMapInit);
|
||||||
SubscribeLocalEvent<FTLStartedEvent>(OnFTLStarted);
|
SubscribeLocalEvent<FTLStartedEvent>(OnFTLStarted);
|
||||||
SubscribeLocalEvent<ShuttleFlattenEvent>(OnShuttleFlatten);
|
SubscribeLocalEvent<ShuttleFlattenEvent>(OnShuttleFlatten);
|
||||||
_configManager.OnValueChanged(CVars.NetMaxUpdateRange, SetLoadRange, true);
|
_configManager.OnValueChanged(CVars.NetMaxUpdateRange, SetLoadRange, true);
|
||||||
InitializeCommands();
|
InitializeCommands();
|
||||||
_proto.PrototypesReloaded += ProtoReload;
|
ProtoManager.PrototypesReloaded += ProtoReload;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Shutdown()
|
public override void Shutdown()
|
||||||
{
|
{
|
||||||
base.Shutdown();
|
base.Shutdown();
|
||||||
_configManager.UnsubValueChanged(CVars.NetMaxUpdateRange, SetLoadRange);
|
_configManager.UnsubValueChanged(CVars.NetMaxUpdateRange, SetLoadRange);
|
||||||
_proto.PrototypesReloaded -= ProtoReload;
|
ProtoManager.PrototypesReloaded -= ProtoReload;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ProtoReload(PrototypesReloadedEventArgs obj)
|
private void ProtoReload(PrototypesReloadedEventArgs obj)
|
||||||
@@ -100,11 +106,6 @@ public sealed partial class BiomeSystem : SharedBiomeSystem
|
|||||||
_loadArea = new Box2(-_loadRange, -_loadRange, _loadRange, _loadRange);
|
_loadArea = new Box2(-_loadRange, -_loadRange, _loadRange, _loadRange);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnBiomeStartup(EntityUid uid, BiomeComponent component, ComponentStartup args)
|
|
||||||
{
|
|
||||||
component.Noise.SetSeed(component.Seed);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnBiomeMapInit(EntityUid uid, BiomeComponent component, MapInitEvent args)
|
private void OnBiomeMapInit(EntityUid uid, BiomeComponent component, MapInitEvent args)
|
||||||
{
|
{
|
||||||
if (component.Seed != -1)
|
if (component.Seed != -1)
|
||||||
@@ -116,7 +117,6 @@ public sealed partial class BiomeSystem : SharedBiomeSystem
|
|||||||
public void SetSeed(BiomeComponent component, int seed)
|
public void SetSeed(BiomeComponent component, int seed)
|
||||||
{
|
{
|
||||||
component.Seed = seed;
|
component.Seed = seed;
|
||||||
component.Noise.SetSeed(seed);
|
|
||||||
Dirty(component);
|
Dirty(component);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,7 +165,7 @@ public sealed partial class BiomeSystem : SharedBiomeSystem
|
|||||||
|
|
||||||
public void AddMarkerLayer(BiomeComponent component, string marker)
|
public void AddMarkerLayer(BiomeComponent component, string marker)
|
||||||
{
|
{
|
||||||
if (!_proto.HasIndex<BiomeMarkerLayerPrototype>(marker))
|
if (!ProtoManager.HasIndex<BiomeMarkerLayerPrototype>(marker))
|
||||||
{
|
{
|
||||||
// TODO: Log when we get a sawmill
|
// TODO: Log when we get a sawmill
|
||||||
return;
|
return;
|
||||||
@@ -234,7 +234,7 @@ public sealed partial class BiomeSystem : SharedBiomeSystem
|
|||||||
|
|
||||||
var mod = biome.ModifiedTiles.GetOrNew(chunk * ChunkSize);
|
var mod = biome.ModifiedTiles.GetOrNew(chunk * ChunkSize);
|
||||||
|
|
||||||
if (!mod.Add(index) || !TryGetBiomeTile(index, biome.Layers, biome.Noise, grid, out var tile))
|
if (!mod.Add(index) || !TryGetBiomeTile(index, biome.Layers, biome.Seed, grid, out var tile))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// If we flag it as modified then the tile is never set so need to do it ourselves.
|
// If we flag it as modified then the tile is never set so need to do it ourselves.
|
||||||
@@ -243,7 +243,7 @@ public sealed partial class BiomeSystem : SharedBiomeSystem
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
grid.SetTiles(tiles);
|
_mapSystem.SetTiles(ev.MapUid, grid, tiles);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -256,7 +256,7 @@ public sealed partial class BiomeSystem : SharedBiomeSystem
|
|||||||
|
|
||||||
foreach (var layer in markers)
|
foreach (var layer in markers)
|
||||||
{
|
{
|
||||||
var proto = _proto.Index<BiomeMarkerLayerPrototype>(layer);
|
var proto = ProtoManager.Index<BiomeMarkerLayerPrototype>(layer);
|
||||||
var enumerator = new ChunkIndicesEnumerator(area, proto.Size);
|
var enumerator = new ChunkIndicesEnumerator(area, proto.Size);
|
||||||
|
|
||||||
while (enumerator.MoveNext(out var chunk))
|
while (enumerator.MoveNext(out var chunk))
|
||||||
@@ -294,7 +294,7 @@ public sealed partial class BiomeSystem : SharedBiomeSystem
|
|||||||
|
|
||||||
foreach (var layer in biome.MarkerLayers)
|
foreach (var layer in biome.MarkerLayers)
|
||||||
{
|
{
|
||||||
var layerProto = _proto.Index<BiomeMarkerLayerPrototype>(layer);
|
var layerProto = ProtoManager.Index<BiomeMarkerLayerPrototype>(layer);
|
||||||
AddMarkerChunksInRange(biome, worldPos, layerProto);
|
AddMarkerChunksInRange(biome, worldPos, layerProto);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -313,7 +313,7 @@ public sealed partial class BiomeSystem : SharedBiomeSystem
|
|||||||
|
|
||||||
foreach (var layer in biome.MarkerLayers)
|
foreach (var layer in biome.MarkerLayers)
|
||||||
{
|
{
|
||||||
var layerProto = _proto.Index<BiomeMarkerLayerPrototype>(layer);
|
var layerProto = ProtoManager.Index<BiomeMarkerLayerPrototype>(layer);
|
||||||
AddMarkerChunksInRange(biome, worldPos, layerProto);
|
AddMarkerChunksInRange(biome, worldPos, layerProto);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -323,12 +323,10 @@ public sealed partial class BiomeSystem : SharedBiomeSystem
|
|||||||
|
|
||||||
while (loadBiomes.MoveNext(out var gridUid, out var biome, out var grid))
|
while (loadBiomes.MoveNext(out var gridUid, out var biome, out var grid))
|
||||||
{
|
{
|
||||||
var noise = biome.Noise;
|
|
||||||
|
|
||||||
// Load new chunks
|
// Load new chunks
|
||||||
LoadChunks(biome, gridUid, grid, noise, xformQuery);
|
LoadChunks(biome, gridUid, grid, biome.Seed, xformQuery);
|
||||||
// Unload old chunks
|
// Unload old chunks
|
||||||
UnloadChunks(biome, gridUid, grid, noise);
|
UnloadChunks(biome, gridUid, grid, biome.Seed);
|
||||||
}
|
}
|
||||||
|
|
||||||
_handledEntities.Clear();
|
_handledEntities.Clear();
|
||||||
@@ -376,36 +374,42 @@ public sealed partial class BiomeSystem : SharedBiomeSystem
|
|||||||
BiomeComponent component,
|
BiomeComponent component,
|
||||||
EntityUid gridUid,
|
EntityUid gridUid,
|
||||||
MapGridComponent grid,
|
MapGridComponent grid,
|
||||||
FastNoiseLite noise,
|
int seed,
|
||||||
EntityQuery<TransformComponent> xformQuery)
|
EntityQuery<TransformComponent> xformQuery)
|
||||||
{
|
{
|
||||||
var markers = _markerChunks[component];
|
var markers = _markerChunks[component];
|
||||||
var loadedMarkers = component.LoadedMarkers;
|
var loadedMarkers = component.LoadedMarkers;
|
||||||
|
var idx = 0;
|
||||||
|
|
||||||
foreach (var (layer, chunks) in markers)
|
foreach (var (layer, chunks) in markers)
|
||||||
{
|
{
|
||||||
|
// I know dictionary ordering isn't guaranteed but I just need something to differentiate seeds.
|
||||||
|
idx++;
|
||||||
|
var localIdx = idx;
|
||||||
|
|
||||||
Parallel.ForEach(chunks, new ParallelOptions() { MaxDegreeOfParallelism = _parallel.ParallelProcessCount }, chunk =>
|
Parallel.ForEach(chunks, new ParallelOptions() { MaxDegreeOfParallelism = _parallel.ParallelProcessCount }, chunk =>
|
||||||
{
|
{
|
||||||
if (loadedMarkers.TryGetValue(layer, out var mobChunks) && mobChunks.Contains(chunk))
|
if (loadedMarkers.TryGetValue(layer, out var mobChunks) && mobChunks.Contains(chunk))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var noiseCopy = new FastNoiseLite();
|
// Get the set of spawned nodes to avoid overlap.
|
||||||
_serManager.CopyTo(component.Noise, ref noiseCopy, notNullableOverride: true);
|
|
||||||
var spawnSet = _tilePool.Get();
|
var spawnSet = _tilePool.Get();
|
||||||
var frontier = new ValueList<Vector2i>(32);
|
var frontier = new ValueList<Vector2i>(32);
|
||||||
|
|
||||||
// Make a temporary version and copy back in later.
|
// Make a temporary version and copy back in later.
|
||||||
var pending = new Dictionary<Vector2i, Dictionary<string, List<Vector2i>>>();
|
var pending = new Dictionary<Vector2i, Dictionary<string, List<Vector2i>>>();
|
||||||
|
|
||||||
var layerProto = _proto.Index<BiomeMarkerLayerPrototype>(layer);
|
var layerProto = ProtoManager.Index<BiomeMarkerLayerPrototype>(layer);
|
||||||
var buffer = layerProto.Radius / 2f;
|
var buffer = layerProto.Radius / 2f;
|
||||||
var rand = new Random(noise.GetSeed() + chunk.X * 8 + chunk.Y + layerProto.GetHashCode());
|
var markerSeed = seed + chunk.X * ChunkSize + chunk.Y + localIdx;
|
||||||
|
var rand = new Random(markerSeed);
|
||||||
|
|
||||||
// We treat a null entity mask as requiring nothing else on the tile
|
// We treat a null entity mask as requiring nothing else on the tile
|
||||||
var lower = (int) Math.Floor(buffer);
|
var lower = (int) Math.Floor(buffer);
|
||||||
var upper = (int) Math.Ceiling(layerProto.Size - buffer);
|
var upper = (int) Math.Ceiling(layerProto.Size - buffer);
|
||||||
|
|
||||||
// TODO: Need poisson but crashes whenever I use moony's due to inputs or smth idk
|
// TODO: Need poisson but crashes whenever I use moony's due to inputs or smth idk
|
||||||
|
// Get the total amount of groups to spawn across the entire chunk.
|
||||||
var count = (int) ((layerProto.Size - buffer) * (layerProto.Size - buffer) /
|
var count = (int) ((layerProto.Size - buffer) * (layerProto.Size - buffer) /
|
||||||
(layerProto.Radius * layerProto.Radius));
|
(layerProto.Radius * layerProto.Radius));
|
||||||
count = Math.Min(count, layerProto.MaxCount);
|
count = Math.Min(count, layerProto.MaxCount);
|
||||||
@@ -414,14 +418,14 @@ public sealed partial class BiomeSystem : SharedBiomeSystem
|
|||||||
// It will bias edge tiles significantly more but will make the CPU cry less.
|
// It will bias edge tiles significantly more but will make the CPU cry less.
|
||||||
for (var i = 0; i < count; i++)
|
for (var i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
var groupCount = layerProto.GroupCount;
|
var groupSize = rand.Next(layerProto.MinGroupSize, layerProto.MaxGroupSize + 1);
|
||||||
var startNodeX = rand.Next(lower, upper + 1);
|
var startNodeX = rand.Next(lower, upper + 1);
|
||||||
var startNodeY = rand.Next(lower, upper + 1);
|
var startNodeY = rand.Next(lower, upper + 1);
|
||||||
var startNode = new Vector2i(startNodeX, startNodeY);
|
var startNode = new Vector2i(startNodeX, startNodeY);
|
||||||
frontier.Clear();
|
frontier.Clear();
|
||||||
frontier.Add(startNode + chunk);
|
frontier.Add(startNode + chunk);
|
||||||
|
|
||||||
while (groupCount > 0 && frontier.Count > 0)
|
while (groupSize >= 0 && frontier.Count > 0)
|
||||||
{
|
{
|
||||||
var frontierIndex = rand.Next(frontier.Count);
|
var frontierIndex = rand.Next(frontier.Count);
|
||||||
var node = frontier[frontierIndex];
|
var node = frontier[frontierIndex];
|
||||||
@@ -435,7 +439,7 @@ public sealed partial class BiomeSystem : SharedBiomeSystem
|
|||||||
if (x != 0 && y != 0)
|
if (x != 0 && y != 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var neighbor = new Vector2i(x + node.X, y + node.Y);
|
var neighbor = new Vector2i(node.X + x, node.Y + y);
|
||||||
var chunkOffset = neighbor - chunk;
|
var chunkOffset = neighbor - chunk;
|
||||||
|
|
||||||
// Check if it's inbounds.
|
// Check if it's inbounds.
|
||||||
@@ -455,19 +459,29 @@ public sealed partial class BiomeSystem : SharedBiomeSystem
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if it's a valid spawn, if so then use it.
|
// Check if it's a valid spawn, if so then use it.
|
||||||
var enumerator = grid.GetAnchoredEntitiesEnumerator(node);
|
var enumerator = _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, node);
|
||||||
|
|
||||||
if (enumerator.MoveNext(out _))
|
if (enumerator.MoveNext(out _))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Check if mask matches.
|
// Check if mask matches // anything blocking.
|
||||||
TryGetEntity(node, component.Layers, noiseCopy, grid, out var proto);
|
TryGetEntity(node, component, grid, out var proto);
|
||||||
|
|
||||||
if (proto != layerProto.EntityMask)
|
// If there's an existing entity and it doesn't match the mask then skip.
|
||||||
|
if (layerProto.EntityMask.Count > 0 &&
|
||||||
|
(proto == null ||
|
||||||
|
!layerProto.EntityMask.ContainsKey(proto)))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If it's just a flat spawn then just check for anything blocking.
|
||||||
|
if (proto != null && layerProto.Prototype != null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
DebugTools.Assert(layerProto.EntityMask.Count == 0 || !string.IsNullOrEmpty(proto));
|
||||||
var chunkOrigin = SharedMapSystem.GetChunkIndices(node, ChunkSize) * ChunkSize;
|
var chunkOrigin = SharedMapSystem.GetChunkIndices(node, ChunkSize) * ChunkSize;
|
||||||
|
|
||||||
if (!pending.TryGetValue(chunkOrigin, out var pendingMarkers))
|
if (!pending.TryGetValue(chunkOrigin, out var pendingMarkers))
|
||||||
@@ -482,9 +496,9 @@ public sealed partial class BiomeSystem : SharedBiomeSystem
|
|||||||
pendingMarkers[layer] = layerMarkers;
|
pendingMarkers[layer] = layerMarkers;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log.Info($"Added node at {actualNode} for chunk {chunkOrigin}");
|
|
||||||
layerMarkers.Add(node);
|
layerMarkers.Add(node);
|
||||||
groupCount--;
|
groupSize--;
|
||||||
|
spawnSet.Add(node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -527,7 +541,7 @@ public sealed partial class BiomeSystem : SharedBiomeSystem
|
|||||||
|
|
||||||
tiles ??= new List<(Vector2i, Tile)>(ChunkSize * ChunkSize);
|
tiles ??= new List<(Vector2i, Tile)>(ChunkSize * ChunkSize);
|
||||||
// Load NOW!
|
// Load NOW!
|
||||||
LoadChunk(component, gridUid, grid, chunk, noise, tiles, xformQuery);
|
LoadChunk(component, gridUid, grid, chunk, seed, tiles, xformQuery);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -539,7 +553,7 @@ public sealed partial class BiomeSystem : SharedBiomeSystem
|
|||||||
EntityUid gridUid,
|
EntityUid gridUid,
|
||||||
MapGridComponent grid,
|
MapGridComponent grid,
|
||||||
Vector2i chunk,
|
Vector2i chunk,
|
||||||
FastNoiseLite noise,
|
int seed,
|
||||||
List<(Vector2i, Tile)> tiles,
|
List<(Vector2i, Tile)> tiles,
|
||||||
EntityQuery<TransformComponent> xformQuery)
|
EntityQuery<TransformComponent> xformQuery)
|
||||||
{
|
{
|
||||||
@@ -551,7 +565,7 @@ public sealed partial class BiomeSystem : SharedBiomeSystem
|
|||||||
{
|
{
|
||||||
foreach (var (layer, nodes) in layers)
|
foreach (var (layer, nodes) in layers)
|
||||||
{
|
{
|
||||||
var layerProto = _proto.Index<BiomeMarkerLayerPrototype>(layer);
|
var layerProto = ProtoManager.Index<BiomeMarkerLayerPrototype>(layer);
|
||||||
|
|
||||||
foreach (var node in nodes)
|
foreach (var node in nodes)
|
||||||
{
|
{
|
||||||
@@ -559,15 +573,27 @@ public sealed partial class BiomeSystem : SharedBiomeSystem
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Need to ensure the tile under it has loaded for anchoring.
|
// Need to ensure the tile under it has loaded for anchoring.
|
||||||
if (TryGetBiomeTile(node, component.Layers, component.Noise, grid, out var tile))
|
if (TryGetBiomeTile(node, component.Layers, seed, grid, out var tile))
|
||||||
{
|
{
|
||||||
grid.SetTile(node, tile.Value);
|
_mapSystem.SetTile(gridUid, grid, node, tile.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
string? prototype;
|
||||||
|
|
||||||
|
if (TryGetEntity(node, component, grid, out var proto) &&
|
||||||
|
layerProto.EntityMask.TryGetValue(proto, out var maskedProto))
|
||||||
|
{
|
||||||
|
prototype = maskedProto;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
prototype = layerProto.Prototype;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If it is a ghost role then purge it
|
// If it is a ghost role then purge it
|
||||||
// TODO: This is *kind* of a bandaid but natural mobs spawns needs a lot more work.
|
// TODO: This is *kind* of a bandaid but natural mobs spawns needs a lot more work.
|
||||||
// Ideally we'd just have ghost role and non-ghost role variants for some stuff.
|
// Ideally we'd just have ghost role and non-ghost role variants for some stuff.
|
||||||
var uid = EntityManager.CreateEntityUninitialized(layerProto.Prototype, grid.GridTileToLocal(node));
|
var uid = EntityManager.CreateEntityUninitialized(prototype, _mapSystem.GridTileToLocal(gridUid, grid, node));
|
||||||
RemComp<GhostTakeoverAvailableComponent>(uid);
|
RemComp<GhostTakeoverAvailableComponent>(uid);
|
||||||
RemComp<GhostRoleComponent>(uid);
|
RemComp<GhostRoleComponent>(uid);
|
||||||
EntityManager.InitializeAndStartEntity(uid);
|
EntityManager.InitializeAndStartEntity(uid);
|
||||||
@@ -585,22 +611,22 @@ public sealed partial class BiomeSystem : SharedBiomeSystem
|
|||||||
{
|
{
|
||||||
var indices = new Vector2i(x + chunk.X, y + chunk.Y);
|
var indices = new Vector2i(x + chunk.X, y + chunk.Y);
|
||||||
|
|
||||||
|
// Pass in null so we don't try to get the tileref.
|
||||||
if (modified.Contains(indices))
|
if (modified.Contains(indices))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// If there's existing data then don't overwrite it.
|
// If there's existing data then don't overwrite it.
|
||||||
if (grid.TryGetTileRef(indices, out var tileRef) && !tileRef.Tile.IsEmpty)
|
if (_mapSystem.TryGetTileRef(gridUid, grid, indices, out var tileRef) && !tileRef.Tile.IsEmpty)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Pass in null so we don't try to get the tileref.
|
if (!TryGetBiomeTile(indices, component.Layers, seed, grid, out var biomeTile) || biomeTile.Value == tileRef.Tile)
|
||||||
if (!TryGetBiomeTile(indices, component.Layers, noise, null, out var biomeTile) || biomeTile.Value == tileRef.Tile)
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
tiles.Add((indices, biomeTile.Value));
|
tiles.Add((indices, biomeTile.Value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
grid.SetTiles(tiles);
|
_mapSystem.SetTiles(gridUid, grid, tiles);
|
||||||
tiles.Clear();
|
tiles.Clear();
|
||||||
|
|
||||||
// Now do entities
|
// Now do entities
|
||||||
@@ -617,14 +643,14 @@ public sealed partial class BiomeSystem : SharedBiomeSystem
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Don't mess with anything that's potentially anchored.
|
// Don't mess with anything that's potentially anchored.
|
||||||
var anchored = grid.GetAnchoredEntitiesEnumerator(indices);
|
var anchored = _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, indices);
|
||||||
|
|
||||||
if (anchored.MoveNext(out _) || !TryGetEntity(indices, component.Layers, noise, grid, out var entPrototype))
|
if (anchored.MoveNext(out _) || !TryGetEntity(indices, component, grid, out var entPrototype))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// TODO: Fix non-anchored ents spawning.
|
// TODO: Fix non-anchored ents spawning.
|
||||||
// Just track loaded chunks for now.
|
// Just track loaded chunks for now.
|
||||||
var ent = Spawn(entPrototype, grid.GridTileToLocal(indices));
|
var ent = Spawn(entPrototype, _mapSystem.GridTileToLocal(gridUid, grid, indices));
|
||||||
|
|
||||||
// At least for now unless we do lookups or smth, only work with anchoring.
|
// At least for now unless we do lookups or smth, only work with anchoring.
|
||||||
if (xformQuery.TryGetComponent(ent, out var xform) && !xform.Anchored)
|
if (xformQuery.TryGetComponent(ent, out var xform) && !xform.Anchored)
|
||||||
@@ -650,9 +676,9 @@ public sealed partial class BiomeSystem : SharedBiomeSystem
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Don't mess with anything that's potentially anchored.
|
// Don't mess with anything that's potentially anchored.
|
||||||
var anchored = grid.GetAnchoredEntitiesEnumerator(indices);
|
var anchored = _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, indices);
|
||||||
|
|
||||||
if (anchored.MoveNext(out _) || !TryGetDecals(indices, component.Layers, noise, grid, out var decals))
|
if (anchored.MoveNext(out _) || !TryGetDecals(indices, component.Layers, seed, grid, out var decals))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
foreach (var decal in decals)
|
foreach (var decal in decals)
|
||||||
@@ -683,7 +709,7 @@ public sealed partial class BiomeSystem : SharedBiomeSystem
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Handles all of the queued chunk unloads for a particular biome.
|
/// Handles all of the queued chunk unloads for a particular biome.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void UnloadChunks(BiomeComponent component, EntityUid gridUid, MapGridComponent grid, FastNoiseLite noise)
|
private void UnloadChunks(BiomeComponent component, EntityUid gridUid, MapGridComponent grid, int seed)
|
||||||
{
|
{
|
||||||
var active = _activeChunks[component];
|
var active = _activeChunks[component];
|
||||||
List<(Vector2i, Tile)>? tiles = null;
|
List<(Vector2i, Tile)>? tiles = null;
|
||||||
@@ -695,14 +721,14 @@ public sealed partial class BiomeSystem : SharedBiomeSystem
|
|||||||
|
|
||||||
// Unload NOW!
|
// Unload NOW!
|
||||||
tiles ??= new List<(Vector2i, Tile)>(ChunkSize * ChunkSize);
|
tiles ??= new List<(Vector2i, Tile)>(ChunkSize * ChunkSize);
|
||||||
UnloadChunk(component, gridUid, grid, chunk, noise, tiles);
|
UnloadChunk(component, gridUid, grid, chunk, seed, tiles);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Unloads a specific biome chunk.
|
/// Unloads a specific biome chunk.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void UnloadChunk(BiomeComponent component, EntityUid gridUid, MapGridComponent grid, Vector2i chunk, FastNoiseLite noise, List<(Vector2i, Tile)> tiles)
|
private void UnloadChunk(BiomeComponent component, EntityUid gridUid, MapGridComponent grid, Vector2i chunk, int seed, List<(Vector2i, Tile)> tiles)
|
||||||
{
|
{
|
||||||
// Reverse order to loading
|
// Reverse order to loading
|
||||||
component.ModifiedTiles.TryGetValue(chunk, out var modified);
|
component.ModifiedTiles.TryGetValue(chunk, out var modified);
|
||||||
@@ -735,7 +761,7 @@ public sealed partial class BiomeSystem : SharedBiomeSystem
|
|||||||
}
|
}
|
||||||
|
|
||||||
// It's moved
|
// It's moved
|
||||||
var entTile = grid.LocalToTile(xform.Coordinates);
|
var entTile = _mapSystem.LocalToTile(gridUid, grid, xform.Coordinates);
|
||||||
|
|
||||||
if (!xform.Anchored || entTile != tile)
|
if (!xform.Anchored || entTile != tile)
|
||||||
{
|
{
|
||||||
@@ -775,8 +801,8 @@ public sealed partial class BiomeSystem : SharedBiomeSystem
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If it's default data unload the tile.
|
// If it's default data unload the tile.
|
||||||
if (!TryGetBiomeTile(indices, component.Layers, noise, null, out var biomeTile) ||
|
if (!TryGetBiomeTile(indices, component.Layers, seed, null, out var biomeTile) ||
|
||||||
grid.TryGetTileRef(indices, out var tileRef) && tileRef.Tile != biomeTile.Value)
|
_mapSystem.TryGetTileRef(gridUid, grid, indices, out var tileRef) && tileRef.Tile != biomeTile.Value)
|
||||||
{
|
{
|
||||||
modified.Add(indices);
|
modified.Add(indices);
|
||||||
continue;
|
continue;
|
||||||
@@ -801,4 +827,51 @@ public sealed partial class BiomeSystem : SharedBiomeSystem
|
|||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a simple planet setup for a map.
|
||||||
|
/// </summary>
|
||||||
|
public void EnsurePlanet(EntityUid mapUid, BiomeTemplatePrototype biomeTemplate, int? seed = null, MetaDataComponent? metadata = null)
|
||||||
|
{
|
||||||
|
if (!Resolve(mapUid, ref metadata))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var biome = EnsureComp<BiomeComponent>(mapUid);
|
||||||
|
seed ??= _random.Next();
|
||||||
|
SetSeed(biome, seed.Value);
|
||||||
|
SetTemplate(biome, biomeTemplate);
|
||||||
|
Dirty(mapUid, biome, metadata);
|
||||||
|
|
||||||
|
var gravity = EnsureComp<GravityComponent>(mapUid);
|
||||||
|
gravity.Enabled = true;
|
||||||
|
gravity.Inherent = true;
|
||||||
|
Dirty(mapUid, gravity, metadata);
|
||||||
|
|
||||||
|
// Day lighting
|
||||||
|
// Daylight: #D8B059
|
||||||
|
// Midday: #E6CB8B
|
||||||
|
// Moonlight: #2b3143
|
||||||
|
// Lava: #A34931
|
||||||
|
|
||||||
|
var light = EnsureComp<MapLightComponent>(mapUid);
|
||||||
|
light.AmbientLightColor = Color.FromHex("#D8B059");
|
||||||
|
Dirty(mapUid, light, metadata);
|
||||||
|
|
||||||
|
// Atmos
|
||||||
|
var atmos = EnsureComp<MapAtmosphereComponent>(mapUid);
|
||||||
|
|
||||||
|
var moles = new float[Atmospherics.AdjustedNumberOfGases];
|
||||||
|
moles[(int) Gas.Oxygen] = 21.824779f;
|
||||||
|
moles[(int) Gas.Nitrogen] = 82.10312f;
|
||||||
|
|
||||||
|
var mixture = new GasMixture(2500)
|
||||||
|
{
|
||||||
|
Temperature = 293.15f,
|
||||||
|
Moles = moles,
|
||||||
|
};
|
||||||
|
|
||||||
|
_atmos.SetMapAtmosphere(mapUid, false, mixture, atmos);
|
||||||
|
|
||||||
|
EnsureComp<MapGridComponent>(mapUid);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ using Content.Server.Decals;
|
|||||||
using Content.Server.GameTicking.Events;
|
using Content.Server.GameTicking.Events;
|
||||||
using Content.Shared.CCVar;
|
using Content.Shared.CCVar;
|
||||||
using Content.Shared.Construction.EntitySystems;
|
using Content.Shared.Construction.EntitySystems;
|
||||||
|
using Content.Shared.GameTicking;
|
||||||
using Content.Shared.Physics;
|
using Content.Shared.Physics;
|
||||||
using Content.Shared.Procedural;
|
using Content.Shared.Procedural;
|
||||||
using Robust.Server.GameObjects;
|
using Robust.Server.GameObjects;
|
||||||
@@ -48,6 +49,7 @@ public sealed partial class DungeonSystem : SharedDungeonSystem
|
|||||||
_console.RegisterCommand("dungen_preset_vis", Loc.GetString("cmd-dungen_preset_vis-desc"), Loc.GetString("cmd-dungen_preset_vis-help"), DungeonPresetVis, PresetCallback);
|
_console.RegisterCommand("dungen_preset_vis", Loc.GetString("cmd-dungen_preset_vis-desc"), Loc.GetString("cmd-dungen_preset_vis-help"), DungeonPresetVis, PresetCallback);
|
||||||
_console.RegisterCommand("dungen_pack_vis", Loc.GetString("cmd-dungen_pack_vis-desc"), Loc.GetString("cmd-dungen_pack_vis-help"), DungeonPackVis, PackCallback);
|
_console.RegisterCommand("dungen_pack_vis", Loc.GetString("cmd-dungen_pack_vis-desc"), Loc.GetString("cmd-dungen_pack_vis-help"), DungeonPackVis, PackCallback);
|
||||||
_prototype.PrototypesReloaded += PrototypeReload;
|
_prototype.PrototypesReloaded += PrototypeReload;
|
||||||
|
SubscribeLocalEvent<RoundRestartCleanupEvent>(OnRoundCleanup);
|
||||||
SubscribeLocalEvent<RoundStartingEvent>(OnRoundStart);
|
SubscribeLocalEvent<RoundStartingEvent>(OnRoundStart);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,7 +59,7 @@ public sealed partial class DungeonSystem : SharedDungeonSystem
|
|||||||
_dungeonJobQueue.Process();
|
_dungeonJobQueue.Process();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnRoundStart(RoundStartingEvent ev)
|
private void OnRoundCleanup(RoundRestartCleanupEvent ev)
|
||||||
{
|
{
|
||||||
foreach (var token in _dungeonJobs.Values)
|
foreach (var token in _dungeonJobs.Values)
|
||||||
{
|
{
|
||||||
@@ -65,6 +67,10 @@ public sealed partial class DungeonSystem : SharedDungeonSystem
|
|||||||
}
|
}
|
||||||
|
|
||||||
_dungeonJobs.Clear();
|
_dungeonJobs.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnRoundStart(RoundStartingEvent ev)
|
||||||
|
{
|
||||||
var query = AllEntityQuery<DungeonAtlasTemplateComponent>();
|
var query = AllEntityQuery<DungeonAtlasTemplateComponent>();
|
||||||
|
|
||||||
while (query.MoveNext(out var uid, out _))
|
while (query.MoveNext(out var uid, out _))
|
||||||
@@ -192,7 +198,6 @@ public sealed partial class DungeonSystem : SharedDungeonSystem
|
|||||||
|
|
||||||
_dungeonJobs.Add(job, cancelToken);
|
_dungeonJobs.Add(job, cancelToken);
|
||||||
_dungeonJobQueue.EnqueueJob(job);
|
_dungeonJobQueue.EnqueueJob(job);
|
||||||
job.Run();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Dungeon> GenerateDungeonAsync(
|
public async Task<Dungeon> GenerateDungeonAsync(
|
||||||
|
|||||||
8
Content.Server/Salvage/RestrictedRangeSystem.cs
Normal file
8
Content.Server/Salvage/RestrictedRangeSystem.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
using Content.Shared.Salvage;
|
||||||
|
|
||||||
|
namespace Content.Server.Salvage;
|
||||||
|
|
||||||
|
public sealed class RestrictedRangeSystem : SharedRestrictedRangeSystem
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
@@ -196,7 +196,7 @@ public sealed class SpawnSalvageMissionJob : Job<bool>
|
|||||||
if (!lootProto.Guaranteed)
|
if (!lootProto.Guaranteed)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
await SpawnDungeonLoot(dungeon, missionBiome, lootProto, mapUid, grid, random, reservedTiles);
|
await SpawnDungeonLoot(lootProto, mapUid);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle boss loot (when relevant).
|
// Handle boss loot (when relevant).
|
||||||
@@ -298,7 +298,7 @@ public sealed class SpawnSalvageMissionJob : Job<bool>
|
|||||||
// oh noooooooooooo
|
// oh noooooooooooo
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task SpawnDungeonLoot(Dungeon dungeon, SalvageBiomeModPrototype biomeMod, SalvageLootPrototype loot, EntityUid gridUid, MapGridComponent grid, Random random, List<Vector2i> reservedTiles)
|
private async Task SpawnDungeonLoot(SalvageLootPrototype loot, EntityUid gridUid)
|
||||||
{
|
{
|
||||||
for (var i = 0; i < loot.LootRules.Count; i++)
|
for (var i = 0; i < loot.LootRules.Count; i++)
|
||||||
{
|
{
|
||||||
@@ -308,10 +308,9 @@ public sealed class SpawnSalvageMissionJob : Job<bool>
|
|||||||
{
|
{
|
||||||
case BiomeMarkerLoot biomeLoot:
|
case BiomeMarkerLoot biomeLoot:
|
||||||
{
|
{
|
||||||
if (_entManager.TryGetComponent<BiomeComponent>(gridUid, out var biome) &&
|
if (_entManager.TryGetComponent<BiomeComponent>(gridUid, out var biome))
|
||||||
biomeLoot.Prototype.TryGetValue(biomeMod.ID, out var mod))
|
|
||||||
{
|
{
|
||||||
_biome.AddMarkerLayer(biome, mod);
|
_biome.AddMarkerLayer(biome, biomeLoot.Prototype);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Robust.Shared.Serialization;
|
using Robust.Shared.Serialization;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
namespace Content.Shared.Gateway;
|
namespace Content.Shared.Gateway;
|
||||||
|
|
||||||
@@ -26,7 +27,7 @@ public sealed class GatewayBoundUserInterfaceState : BoundUserInterfaceState
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// List of enabled destinations and information about them.
|
/// List of enabled destinations and information about them.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly List<(NetEntity, string, TimeSpan, bool)> Destinations;
|
public readonly List<GatewayDestinationData> Destinations;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Which destination it is currently linked to, if any.
|
/// Which destination it is currently linked to, if any.
|
||||||
@@ -34,25 +35,52 @@ public sealed class GatewayBoundUserInterfaceState : BoundUserInterfaceState
|
|||||||
public readonly NetEntity? Current;
|
public readonly NetEntity? Current;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Time the portal will close at.
|
/// Next time the portal is ready to be used.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly TimeSpan NextClose;
|
public readonly TimeSpan NextReady;
|
||||||
|
|
||||||
|
public readonly TimeSpan Cooldown;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Time the portal last opened at.
|
/// Next time the destination generator unlocks another destination.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly TimeSpan LastOpen;
|
public readonly TimeSpan NextUnlock;
|
||||||
|
|
||||||
public GatewayBoundUserInterfaceState(List<(NetEntity, string, TimeSpan, bool)> destinations,
|
/// <summary>
|
||||||
NetEntity? current, TimeSpan nextClose, TimeSpan lastOpen)
|
/// How long an unlock takes.
|
||||||
|
/// </summary>
|
||||||
|
public readonly TimeSpan UnlockTime;
|
||||||
|
|
||||||
|
public GatewayBoundUserInterfaceState(List<GatewayDestinationData> destinations,
|
||||||
|
NetEntity? current, TimeSpan nextReady, TimeSpan cooldown, TimeSpan nextUnlock, TimeSpan unlockTime)
|
||||||
{
|
{
|
||||||
Destinations = destinations;
|
Destinations = destinations;
|
||||||
Current = current;
|
Current = current;
|
||||||
NextClose = nextClose;
|
NextReady = nextReady;
|
||||||
LastOpen = lastOpen;
|
Cooldown = cooldown;
|
||||||
|
NextUnlock = nextUnlock;
|
||||||
|
UnlockTime = unlockTime;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public record struct GatewayDestinationData
|
||||||
|
{
|
||||||
|
public NetEntity Entity;
|
||||||
|
|
||||||
|
public FormattedMessage Name;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Is the portal currently open.
|
||||||
|
/// </summary>
|
||||||
|
public bool Portal;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Is the map the gateway on locked or unlocked.
|
||||||
|
/// </summary>
|
||||||
|
public bool Locked;
|
||||||
|
}
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
public sealed class GatewayOpenPortalMessage : BoundUserInterfaceMessage
|
public sealed class GatewayOpenPortalMessage : BoundUserInterfaceMessage
|
||||||
{
|
{
|
||||||
|
|||||||
12
Content.Shared/Gateway/SharedGatewayGeneratorSystem.cs
Normal file
12
Content.Shared/Gateway/SharedGatewayGeneratorSystem.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.Gateway;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sent from client to server upon taking a gateway destination.
|
||||||
|
/// </summary>
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed class GatewayDestinationMessage : EntityEventArgs
|
||||||
|
{
|
||||||
|
public int Index;
|
||||||
|
}
|
||||||
13
Content.Shared/Movement/Components/BoundaryComponent.cs
Normal file
13
Content.Shared/Movement/Components/BoundaryComponent.cs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
using Robust.Shared.GameStates;
|
||||||
|
|
||||||
|
namespace Content.Shared.Movement.Components;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a boundary that can bump someone back when touched.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||||
|
public sealed partial class BoundaryComponent : Component
|
||||||
|
{
|
||||||
|
[DataField, AutoNetworkedField]
|
||||||
|
public float Offset = 2f;
|
||||||
|
}
|
||||||
@@ -11,8 +11,6 @@ namespace Content.Shared.Parallax.Biomes;
|
|||||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true), Access(typeof(SharedBiomeSystem))]
|
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true), Access(typeof(SharedBiomeSystem))]
|
||||||
public sealed partial class BiomeComponent : Component
|
public sealed partial class BiomeComponent : Component
|
||||||
{
|
{
|
||||||
public FastNoiseLite Noise = new();
|
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite), DataField("seed")]
|
[ViewVariables(VVAccess.ReadWrite), DataField("seed")]
|
||||||
[AutoNetworkedField]
|
[AutoNetworkedField]
|
||||||
public int Seed = -1;
|
public int Seed = -1;
|
||||||
|
|||||||
@@ -11,14 +11,17 @@ public sealed class BiomeMarkerLayerPrototype : IBiomeMarkerLayer
|
|||||||
{
|
{
|
||||||
[IdDataField] public string ID { get; } = default!;
|
[IdDataField] public string ID { get; } = default!;
|
||||||
|
|
||||||
[DataField("proto", required: true, customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
|
||||||
public string Prototype { get; private set; } = string.Empty;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks for the relevant entity for the tile before spawning. Useful for substituting walls with ore veins for example.
|
/// Checks for the relevant entity for the tile before spawning. Useful for substituting walls with ore veins for example.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("entityMask", customTypeSerializer:typeof(PrototypeIdSerializer<EntityPrototype>))]
|
[DataField]
|
||||||
public string? EntityMask { get; private set; }
|
public Dictionary<EntProtoId, EntProtoId> EntityMask { get; private set; } = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default prototype to spawn. If null will fall back to entity mask.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public string? Prototype { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Minimum radius between 2 points
|
/// Minimum radius between 2 points
|
||||||
@@ -33,10 +36,16 @@ public sealed class BiomeMarkerLayerPrototype : IBiomeMarkerLayer
|
|||||||
public int MaxCount = int.MaxValue;
|
public int MaxCount = int.MaxValue;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// How many mobs to spawn in one group.
|
/// Minimum entities to spawn in one group.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("groupCount")]
|
[DataField]
|
||||||
public int GroupCount = 1;
|
public int MinGroupSize = 1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Maximum entities to spawn in one group.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public int MaxGroupSize = 1;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
[DataField("size")]
|
[DataField("size")]
|
||||||
|
|||||||
@@ -11,9 +11,9 @@ public interface IBiomeMarkerLayer : IPrototype
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Biome template to use as a mask for this layer.
|
/// Biome template to use as a mask for this layer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string? EntityMask { get; }
|
public Dictionary<EntProtoId, EntProtoId> EntityMask { get; }
|
||||||
|
|
||||||
public string Prototype { get; }
|
public string? Prototype { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// How large the pre-generated points area is.
|
/// How large the pre-generated points area is.
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ using Robust.Shared.Map;
|
|||||||
using Robust.Shared.Map.Components;
|
using Robust.Shared.Map.Components;
|
||||||
using Robust.Shared.Noise;
|
using Robust.Shared.Noise;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Serialization.Manager;
|
||||||
using Robust.Shared.Utility;
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
namespace Content.Shared.Parallax.Biomes;
|
namespace Content.Shared.Parallax.Biomes;
|
||||||
@@ -13,21 +14,11 @@ namespace Content.Shared.Parallax.Biomes;
|
|||||||
public abstract class SharedBiomeSystem : EntitySystem
|
public abstract class SharedBiomeSystem : EntitySystem
|
||||||
{
|
{
|
||||||
[Dependency] protected readonly IPrototypeManager ProtoManager = default!;
|
[Dependency] protected readonly IPrototypeManager ProtoManager = default!;
|
||||||
|
[Dependency] private readonly ISerializationManager _serManager = default!;
|
||||||
[Dependency] protected readonly ITileDefinitionManager TileDefManager = default!;
|
[Dependency] protected readonly ITileDefinitionManager TileDefManager = default!;
|
||||||
|
|
||||||
protected const byte ChunkSize = 8;
|
protected const byte ChunkSize = 8;
|
||||||
|
|
||||||
public override void Initialize()
|
|
||||||
{
|
|
||||||
base.Initialize();
|
|
||||||
SubscribeLocalEvent<BiomeComponent, AfterAutoHandleStateEvent>(OnBiomeAfterHandleState);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnBiomeAfterHandleState(EntityUid uid, BiomeComponent component, ref AfterAutoHandleStateEvent args)
|
|
||||||
{
|
|
||||||
component.Noise.SetSeed(component.Seed);
|
|
||||||
}
|
|
||||||
|
|
||||||
private T Pick<T>(List<T> collection, float value)
|
private T Pick<T>(List<T> collection, float value)
|
||||||
{
|
{
|
||||||
// Listen I don't need this exact and I'm too lazy to finetune just for random ent picking.
|
// Listen I don't need this exact and I'm too lazy to finetune just for random ent picking.
|
||||||
@@ -89,13 +80,13 @@ public abstract class SharedBiomeSystem : EntitySystem
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return TryGetBiomeTile(indices, biome.Layers, biome.Noise, grid, out tile);
|
return TryGetBiomeTile(indices, biome.Layers, biome.Seed, grid, out tile);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tries to get the tile, real or otherwise, for the specified indices.
|
/// Tries to get the tile, real or otherwise, for the specified indices.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool TryGetBiomeTile(Vector2i indices, List<IBiomeLayer> layers, FastNoiseLite noise, MapGridComponent? grid, [NotNullWhen(true)] out Tile? tile)
|
public bool TryGetBiomeTile(Vector2i indices, List<IBiomeLayer> layers, int seed, MapGridComponent? grid, [NotNullWhen(true)] out Tile? tile)
|
||||||
{
|
{
|
||||||
if (grid?.TryGetTileRef(indices, out var tileRef) == true && !tileRef.Tile.IsEmpty)
|
if (grid?.TryGetTileRef(indices, out var tileRef) == true && !tileRef.Tile.IsEmpty)
|
||||||
{
|
{
|
||||||
@@ -103,23 +94,23 @@ public abstract class SharedBiomeSystem : EntitySystem
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
var oldSeed = noise.GetSeed();
|
|
||||||
|
|
||||||
for (var i = layers.Count - 1; i >= 0; i--)
|
for (var i = layers.Count - 1; i >= 0; i--)
|
||||||
{
|
{
|
||||||
var layer = layers[i];
|
var layer = layers[i];
|
||||||
|
var noiseCopy = GetNoise(layer.Noise, seed);
|
||||||
|
|
||||||
|
var invert = layer.Invert;
|
||||||
|
var value = noiseCopy.GetNoise(indices.X, indices.Y);
|
||||||
|
value = invert ? value * -1 : value;
|
||||||
|
|
||||||
|
if (value < layer.Threshold)
|
||||||
|
continue;
|
||||||
|
|
||||||
// Check if the tile is from meta layer, otherwise fall back to default layers.
|
// Check if the tile is from meta layer, otherwise fall back to default layers.
|
||||||
if (layer is BiomeMetaLayer meta)
|
if (layer is BiomeMetaLayer meta)
|
||||||
{
|
{
|
||||||
SetNoise(noise, oldSeed, layer.Noise);
|
if (TryGetBiomeTile(indices, ProtoManager.Index<BiomeTemplatePrototype>(meta.Template).Layers, seed, grid, out tile))
|
||||||
var found = noise.GetNoise(indices.X, indices.Y);
|
|
||||||
found *= layer.Invert ? -1 : 1;
|
|
||||||
|
|
||||||
if (found > layer.Threshold && TryGetBiomeTile(indices, ProtoManager.Index<BiomeTemplatePrototype>(meta.Template).Layers, noise,
|
|
||||||
grid, out tile))
|
|
||||||
{
|
{
|
||||||
noise.SetSeed(oldSeed);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,16 +120,12 @@ public abstract class SharedBiomeSystem : EntitySystem
|
|||||||
if (layer is not BiomeTileLayer tileLayer)
|
if (layer is not BiomeTileLayer tileLayer)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
SetNoise(noise, oldSeed, layer.Noise);
|
if (TryGetTile(indices, noiseCopy, tileLayer.Invert, tileLayer.Threshold, ProtoManager.Index<ContentTileDefinition>(tileLayer.Tile), tileLayer.Variants, out tile))
|
||||||
|
|
||||||
if (TryGetTile(indices, noise, tileLayer.Invert, tileLayer.Threshold, ProtoManager.Index<ContentTileDefinition>(tileLayer.Tile), tileLayer.Variants, out tile))
|
|
||||||
{
|
{
|
||||||
noise.SetSeed(oldSeed);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
noise.SetSeed(oldSeed);
|
|
||||||
tile = null;
|
tile = null;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -146,9 +133,9 @@ public abstract class SharedBiomeSystem : EntitySystem
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the underlying biome tile, ignoring any existing tile that may be there.
|
/// Gets the underlying biome tile, ignoring any existing tile that may be there.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private bool TryGetTile(Vector2i indices, FastNoiseLite seed, bool invert, float threshold, ContentTileDefinition tileDef, List<byte>? variants, [NotNullWhen(true)] out Tile? tile)
|
private bool TryGetTile(Vector2i indices, FastNoiseLite noise, bool invert, float threshold, ContentTileDefinition tileDef, List<byte>? variants, [NotNullWhen(true)] out Tile? tile)
|
||||||
{
|
{
|
||||||
var found = seed.GetNoise(indices.X, indices.Y);
|
var found = noise.GetNoise(indices.X, indices.Y);
|
||||||
found = invert ? found * -1 : found;
|
found = invert ? found * -1 : found;
|
||||||
|
|
||||||
if (found < threshold)
|
if (found < threshold)
|
||||||
@@ -163,7 +150,7 @@ public abstract class SharedBiomeSystem : EntitySystem
|
|||||||
// Pick a variant tile if they're available as well
|
// Pick a variant tile if they're available as well
|
||||||
if (variantCount > 1)
|
if (variantCount > 1)
|
||||||
{
|
{
|
||||||
var variantValue = (seed.GetNoise(indices.X * 8, indices.Y * 8, variantCount) + 1f) / 2f;
|
var variantValue = (noise.GetNoise(indices.X * 8, indices.Y * 8, variantCount) + 1f) / 2f;
|
||||||
variant = (byte) Pick(variantCount, variantValue);
|
variant = (byte) Pick(variantCount, variantValue);
|
||||||
|
|
||||||
if (variants != null)
|
if (variants != null)
|
||||||
@@ -179,23 +166,28 @@ public abstract class SharedBiomeSystem : EntitySystem
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tries to get the relevant entity for this tile.
|
/// Tries to get the relevant entity for this tile.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected bool TryGetEntity(Vector2i indices, List<IBiomeLayer> layers, FastNoiseLite noise, MapGridComponent grid,
|
public bool TryGetEntity(Vector2i indices, BiomeComponent component, MapGridComponent grid,
|
||||||
[NotNullWhen(true)] out string? entity)
|
[NotNullWhen(true)] out string? entity)
|
||||||
{
|
{
|
||||||
if (!TryGetBiomeTile(indices, layers, noise, grid, out var tileRef))
|
if (!TryGetBiomeTile(indices, component.Layers, component.Seed, grid, out var tile))
|
||||||
{
|
{
|
||||||
entity = null;
|
entity = null;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var tileId = TileDefManager[tileRef.Value.TypeId].ID;
|
return TryGetEntity(indices, component.Layers, tile.Value, component.Seed, grid, out entity);
|
||||||
var oldSeed = noise.GetSeed();
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private bool TryGetEntity(Vector2i indices, List<IBiomeLayer> layers, Tile tileRef, int seed, MapGridComponent grid,
|
||||||
|
[NotNullWhen(true)] out string? entity)
|
||||||
|
{
|
||||||
|
var tileId = TileDefManager[tileRef.TypeId].ID;
|
||||||
|
|
||||||
for (var i = layers.Count - 1; i >= 0; i--)
|
for (var i = layers.Count - 1; i >= 0; i--)
|
||||||
{
|
{
|
||||||
var layer = layers[i];
|
var layer = layers[i];
|
||||||
|
|
||||||
// Decals might block entity so need to check if there's one in front of us.
|
|
||||||
switch (layer)
|
switch (layer)
|
||||||
{
|
{
|
||||||
case BiomeDummyLayer:
|
case BiomeDummyLayer:
|
||||||
@@ -211,9 +203,10 @@ public abstract class SharedBiomeSystem : EntitySystem
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
SetNoise(noise, oldSeed, layer.Noise);
|
var noiseCopy = GetNoise(layer.Noise, seed);
|
||||||
|
|
||||||
var invert = layer.Invert;
|
var invert = layer.Invert;
|
||||||
var value = noise.GetNoise(indices.X, indices.Y);
|
var value = noiseCopy.GetNoise(indices.X, indices.Y);
|
||||||
value = invert ? value * -1 : value;
|
value = invert ? value * -1 : value;
|
||||||
|
|
||||||
if (value < layer.Threshold)
|
if (value < layer.Threshold)
|
||||||
@@ -221,29 +214,26 @@ public abstract class SharedBiomeSystem : EntitySystem
|
|||||||
|
|
||||||
if (layer is BiomeMetaLayer meta)
|
if (layer is BiomeMetaLayer meta)
|
||||||
{
|
{
|
||||||
if (TryGetEntity(indices, ProtoManager.Index<BiomeTemplatePrototype>(meta.Template).Layers, noise, grid, out entity))
|
if (TryGetEntity(indices, ProtoManager.Index<BiomeTemplatePrototype>(meta.Template).Layers, tileRef, seed, grid, out entity))
|
||||||
{
|
{
|
||||||
noise.SetSeed(oldSeed);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Decals might block entity so need to check if there's one in front of us.
|
||||||
if (layer is not BiomeEntityLayer biomeLayer)
|
if (layer is not BiomeEntityLayer biomeLayer)
|
||||||
{
|
{
|
||||||
entity = null;
|
entity = null;
|
||||||
noise.SetSeed(oldSeed);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var noiseValue = noise.GetNoise(indices.X, indices.Y, i);
|
var noiseValue = noiseCopy.GetNoise(indices.X, indices.Y, i);
|
||||||
entity = Pick(biomeLayer.Entities, (noiseValue + 1f) / 2f);
|
entity = Pick(biomeLayer.Entities, (noiseValue + 1f) / 2f);
|
||||||
noise.SetSeed(oldSeed);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
noise.SetSeed(oldSeed);
|
|
||||||
entity = null;
|
entity = null;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -251,17 +241,16 @@ public abstract class SharedBiomeSystem : EntitySystem
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tries to get the relevant decals for this tile.
|
/// Tries to get the relevant decals for this tile.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool TryGetDecals(Vector2i indices, List<IBiomeLayer> layers, FastNoiseLite noise, MapGridComponent grid,
|
public bool TryGetDecals(Vector2i indices, List<IBiomeLayer> layers, int seed, MapGridComponent grid,
|
||||||
[NotNullWhen(true)] out List<(string ID, Vector2 Position)>? decals)
|
[NotNullWhen(true)] out List<(string ID, Vector2 Position)>? decals)
|
||||||
{
|
{
|
||||||
if (!TryGetBiomeTile(indices, layers, noise, grid, out var tileRef))
|
if (!TryGetBiomeTile(indices, layers, seed, grid, out var tileRef))
|
||||||
{
|
{
|
||||||
decals = null;
|
decals = null;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var tileId = TileDefManager[tileRef.Value.TypeId].ID;
|
var tileId = TileDefManager[tileRef.Value.TypeId].ID;
|
||||||
var oldSeed = noise.GetSeed();
|
|
||||||
|
|
||||||
for (var i = layers.Count - 1; i >= 0; i--)
|
for (var i = layers.Count - 1; i >= 0; i--)
|
||||||
{
|
{
|
||||||
@@ -283,17 +272,18 @@ public abstract class SharedBiomeSystem : EntitySystem
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
SetNoise(noise, oldSeed, layer.Noise);
|
|
||||||
var invert = layer.Invert;
|
var invert = layer.Invert;
|
||||||
|
var noiseCopy = GetNoise(layer.Noise, seed);
|
||||||
|
var value = noiseCopy.GetNoise(indices.X, indices.Y);
|
||||||
|
value = invert ? value * -1 : value;
|
||||||
|
|
||||||
|
if (value < layer.Threshold)
|
||||||
|
continue;
|
||||||
|
|
||||||
if (layer is BiomeMetaLayer meta)
|
if (layer is BiomeMetaLayer meta)
|
||||||
{
|
{
|
||||||
var found = noise.GetNoise(indices.X, indices.Y);
|
if (TryGetDecals(indices, ProtoManager.Index<BiomeTemplatePrototype>(meta.Template).Layers, seed, grid, out decals))
|
||||||
found *= layer.Invert ? -1 : 1;
|
|
||||||
|
|
||||||
if (found > layer.Threshold && TryGetDecals(indices, ProtoManager.Index<BiomeTemplatePrototype>(meta.Template).Layers, noise, grid, out decals))
|
|
||||||
{
|
{
|
||||||
noise.SetSeed(oldSeed);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -303,14 +293,7 @@ public abstract class SharedBiomeSystem : EntitySystem
|
|||||||
// Check if the other layer should even render, if not then keep going.
|
// Check if the other layer should even render, if not then keep going.
|
||||||
if (layer is not BiomeDecalLayer decalLayer)
|
if (layer is not BiomeDecalLayer decalLayer)
|
||||||
{
|
{
|
||||||
var value = noise.GetNoise(indices.X, indices.Y);
|
|
||||||
value = invert ? value * -1 : value;
|
|
||||||
|
|
||||||
if (value < layer.Threshold)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
decals = null;
|
decals = null;
|
||||||
noise.SetSeed(oldSeed);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -321,13 +304,13 @@ public abstract class SharedBiomeSystem : EntitySystem
|
|||||||
for (var y = 0; y < decalLayer.Divisions; y++)
|
for (var y = 0; y < decalLayer.Divisions; y++)
|
||||||
{
|
{
|
||||||
var index = new Vector2(indices.X + x * 1f / decalLayer.Divisions, indices.Y + y * 1f / decalLayer.Divisions);
|
var index = new Vector2(indices.X + x * 1f / decalLayer.Divisions, indices.Y + y * 1f / decalLayer.Divisions);
|
||||||
var decalValue = noise.GetNoise(index.X, index.Y);
|
var decalValue = noiseCopy.GetNoise(index.X, index.Y);
|
||||||
decalValue = invert ? decalValue * -1 : decalValue;
|
decalValue = invert ? decalValue * -1 : decalValue;
|
||||||
|
|
||||||
if (decalValue < decalLayer.Threshold)
|
if (decalValue < decalLayer.Threshold)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
decals.Add((Pick(decalLayer.Decals, (noise.GetNoise(indices.X, indices.Y, x + y * decalLayer.Divisions) + 1f) / 2f), index));
|
decals.Add((Pick(decalLayer.Decals, (noiseCopy.GetNoise(indices.X, indices.Y, x + y * decalLayer.Divisions) + 1f) / 2f), index));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -335,34 +318,20 @@ public abstract class SharedBiomeSystem : EntitySystem
|
|||||||
if (decals.Count == 0)
|
if (decals.Count == 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
noise.SetSeed(oldSeed);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
noise.SetSeed(oldSeed);
|
|
||||||
decals = null;
|
decals = null;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetNoise(FastNoiseLite noise, int oldSeed, FastNoiseLite data)
|
private FastNoiseLite GetNoise(FastNoiseLite seedNoise, int seed)
|
||||||
{
|
{
|
||||||
// General
|
var noiseCopy = new FastNoiseLite();
|
||||||
noise.SetSeed(oldSeed + data.GetSeed());
|
_serManager.CopyTo(seedNoise, ref noiseCopy, notNullableOverride: true);
|
||||||
noise.SetFrequency(data.GetFrequency());
|
noiseCopy.SetSeed(noiseCopy.GetSeed() + seed);
|
||||||
noise.SetNoiseType(data.GetNoiseType());
|
// Ensure re-calculate is run.
|
||||||
|
noiseCopy.SetFractalOctaves(noiseCopy.GetFractalOctaves());
|
||||||
noise.GetRotationType3D();
|
return noiseCopy;
|
||||||
|
|
||||||
// Fractal
|
|
||||||
noise.SetFractalType(data.GetFractalType());
|
|
||||||
noise.SetFractalOctaves(data.GetFractalOctaves());
|
|
||||||
noise.SetFractalLacunarity(data.GetFractalLacunarity());
|
|
||||||
|
|
||||||
// Cellular
|
|
||||||
noise.SetCellularDistanceFunction(data.GetCellularDistanceFunction());
|
|
||||||
noise.SetCellularReturnType(data.GetCellularReturnType());
|
|
||||||
noise.SetCellularJitter(data.GetCellularJitter());
|
|
||||||
|
|
||||||
// Domain warps require separate noise
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Content.Shared.Parallax.Biomes.Markers;
|
using Content.Shared.Parallax.Biomes.Markers;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary;
|
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary;
|
||||||
|
|
||||||
@@ -9,7 +10,6 @@ namespace Content.Shared.Procedural.Loot;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed partial class BiomeMarkerLoot : IDungeonLoot
|
public sealed partial class BiomeMarkerLoot : IDungeonLoot
|
||||||
{
|
{
|
||||||
[DataField("proto", required: true,
|
[DataField("proto", required: true)]
|
||||||
customTypeSerializer: typeof(PrototypeIdValueDictionarySerializer<string, BiomeMarkerLayerPrototype>))]
|
public ProtoId<BiomeMarkerLayerPrototype> Prototype = new();
|
||||||
public Dictionary<string, string> Prototype = new();
|
|
||||||
}
|
}
|
||||||
|
|||||||
17
Content.Shared/Salvage/RestrictedRangeComponent.cs
Normal file
17
Content.Shared/Salvage/RestrictedRangeComponent.cs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
using System.Numerics;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
|
|
||||||
|
namespace Content.Shared.Salvage;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Restricts entities to the specified range on the attached map entity.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||||
|
public sealed partial class RestrictedRangeComponent : Component
|
||||||
|
{
|
||||||
|
[DataField(required: true), AutoNetworkedField]
|
||||||
|
public float Range = 72f;
|
||||||
|
|
||||||
|
[DataField, AutoNetworkedField]
|
||||||
|
public Vector2 Origin;
|
||||||
|
}
|
||||||
6
Content.Shared/Salvage/SharedRestrictedRangeSystem.cs
Normal file
6
Content.Shared/Salvage/SharedRestrictedRangeSystem.cs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
namespace Content.Shared.Salvage;
|
||||||
|
|
||||||
|
public abstract class SharedRestrictedRangeSystem : EntitySystem
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
@@ -5,7 +5,7 @@ using Robust.Shared.Serialization;
|
|||||||
namespace Content.Shared.Teleportation.Components;
|
namespace Content.Shared.Teleportation.Components;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents an entity which is linked to other entities (perhaps portals), and which can be walked through/
|
/// Represents an entity which is linked to other entities (perhaps portals), and which can be walked through /
|
||||||
/// thrown into to teleport an entity.
|
/// thrown into to teleport an entity.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||||
|
|||||||
@@ -47,4 +47,10 @@ public sealed partial class PortalComponent : Component
|
|||||||
/// </remarks>
|
/// </remarks>
|
||||||
[DataField("maxTeleportRadius")]
|
[DataField("maxTeleportRadius")]
|
||||||
public float? MaxTeleportRadius;
|
public float? MaxTeleportRadius;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Should we teleport randomly if nothing is linked.
|
||||||
|
/// </summary>
|
||||||
|
[DataField, AutoNetworkedField]
|
||||||
|
public bool RandomTeleport = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,13 +54,29 @@ public sealed class LinkedEntitySystem : EntitySystem
|
|||||||
_appearance.SetData(first, LinkedEntityVisuals.HasAnyLinks, true);
|
_appearance.SetData(first, LinkedEntityVisuals.HasAnyLinks, true);
|
||||||
_appearance.SetData(second, LinkedEntityVisuals.HasAnyLinks, true);
|
_appearance.SetData(second, LinkedEntityVisuals.HasAnyLinks, true);
|
||||||
|
|
||||||
Dirty(firstLink);
|
Dirty(first, firstLink);
|
||||||
Dirty(secondLink);
|
Dirty(second, secondLink);
|
||||||
|
|
||||||
return firstLink.LinkedEntities.Add(second)
|
return firstLink.LinkedEntities.Add(second)
|
||||||
&& secondLink.LinkedEntities.Add(first);
|
&& secondLink.LinkedEntities.Add(first);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Does a one-way link from source to target.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="deleteOnEmptyLinks">Whether both entities should now delete once their links are removed</param>
|
||||||
|
public bool OneWayLink(EntityUid source, EntityUid target, bool deleteOnEmptyLinks=false)
|
||||||
|
{
|
||||||
|
var firstLink = EnsureComp<LinkedEntityComponent>(source);
|
||||||
|
firstLink.DeleteOnEmptyLinks = deleteOnEmptyLinks;
|
||||||
|
|
||||||
|
_appearance.SetData(source, LinkedEntityVisuals.HasAnyLinks, true);
|
||||||
|
|
||||||
|
Dirty(source, firstLink);
|
||||||
|
|
||||||
|
return firstLink.LinkedEntities.Add(target);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Unlinks two entities. Deletes either entity if <see cref="LinkedEntityComponent.DeleteOnEmptyLinks"/>
|
/// Unlinks two entities. Deletes either entity if <see cref="LinkedEntityComponent.DeleteOnEmptyLinks"/>
|
||||||
/// was true and its links are now empty. Symmetrical, so order doesn't matter.
|
/// was true and its links are now empty. Symmetrical, so order doesn't matter.
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ public abstract class SharedPortalSystem : EntitySystem
|
|||||||
// if target is a portal, signal that they shouldn't be immediately portaled back
|
// if target is a portal, signal that they shouldn't be immediately portaled back
|
||||||
var timeout = EnsureComp<PortalTimeoutComponent>(subject);
|
var timeout = EnsureComp<PortalTimeoutComponent>(subject);
|
||||||
timeout.EnteredPortal = uid;
|
timeout.EnteredPortal = uid;
|
||||||
Dirty(timeout);
|
Dirty(subject, timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
TeleportEntity(uid, subject, Transform(target).Coordinates, target);
|
TeleportEntity(uid, subject, Transform(target).Coordinates, target);
|
||||||
@@ -142,7 +142,8 @@ public abstract class SharedPortalSystem : EntitySystem
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// no linked entity--teleport randomly
|
// no linked entity--teleport randomly
|
||||||
TeleportRandomly(uid, subject, component);
|
if (component.RandomTeleport)
|
||||||
|
TeleportRandomly(uid, subject, component);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnEndCollide(EntityUid uid, PortalComponent component, ref EndCollideEvent args)
|
private void OnEndCollide(EntityUid uid, PortalComponent component, ref EndCollideEvent args)
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
gateway-window-title = Gateway
|
gateway-window-title = Gateway
|
||||||
gateway-window-ready = Ready!
|
|
||||||
gateway-window-ready-in = Ready in: {$time}s
|
|
||||||
gateway-window-already-active = Already active
|
|
||||||
gateway-window-open-portal = Open Portal
|
gateway-window-open-portal = Open Portal
|
||||||
gateway-window-no-destinations = No destinations found.
|
gateway-window-no-destinations = No destinations found.
|
||||||
gateway-window-portal-closing = Portal closing
|
gateway-window-portal-cooldown = Cooldown
|
||||||
|
gateway-window-portal-unlock = Next unlock
|
||||||
|
gateway-window-locked = Locked
|
||||||
|
|
||||||
gateway-access-denied = Access denied!
|
gateway-access-denied = Access denied!
|
||||||
gateway-close-portal = Close Portal
|
gateway-close-portal = Close Portal
|
||||||
|
|||||||
@@ -32,6 +32,13 @@
|
|||||||
components:
|
components:
|
||||||
- type: StationArrivals
|
- type: StationArrivals
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: BaseStationGateway
|
||||||
|
abstract: true
|
||||||
|
components:
|
||||||
|
- type: GatewayGenerator
|
||||||
|
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: BaseStationShuttles
|
id: BaseStationShuttles
|
||||||
abstract: true
|
abstract: true
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
- BaseStationJobsSpawning
|
- BaseStationJobsSpawning
|
||||||
- BaseStationRecords
|
- BaseStationRecords
|
||||||
- BaseStationArrivals
|
- BaseStationArrivals
|
||||||
|
- BaseStationGateway
|
||||||
- BaseStationShuttles
|
- BaseStationShuttles
|
||||||
- BaseStationCentcomm
|
- BaseStationCentcomm
|
||||||
- BaseStationEvacuation
|
- BaseStationEvacuation
|
||||||
|
|||||||
@@ -50,20 +50,8 @@
|
|||||||
components:
|
components:
|
||||||
- type: ActivatableUI
|
- type: ActivatableUI
|
||||||
key: enum.GatewayUiKey.Key
|
key: enum.GatewayUiKey.Key
|
||||||
- type: ActivatableUIRequiresPower
|
|
||||||
- type: UserInterface
|
- type: UserInterface
|
||||||
interfaces:
|
interfaces:
|
||||||
- key: enum.GatewayUiKey.Key
|
- key: enum.GatewayUiKey.Key
|
||||||
type: GatewayBoundUserInterface
|
type: GatewayBoundUserInterface
|
||||||
- type: ApcPowerReceiver
|
|
||||||
powerLoad: 3000
|
|
||||||
- type: ExtensionCableReceiver
|
|
||||||
- type: Gateway
|
- type: Gateway
|
||||||
|
|
||||||
- type: entity
|
|
||||||
parent: BaseGateway
|
|
||||||
id: GatewayDestination
|
|
||||||
suffix: Destination
|
|
||||||
components:
|
|
||||||
- type: GatewayDestination
|
|
||||||
name: Unknown
|
|
||||||
|
|||||||
@@ -1,31 +1,35 @@
|
|||||||
- type: biomeMarkerLayer
|
- type: biomeMarkerLayer
|
||||||
id: Lizards
|
id: Lizards
|
||||||
proto: MobLizard
|
prototype: MobLizard
|
||||||
groupCount: 5
|
minGroupSize: 3
|
||||||
|
maxGroupSize: 5
|
||||||
|
|
||||||
- type: biomeMarkerLayer
|
- type: biomeMarkerLayer
|
||||||
id: WatchersLavaland
|
id: WatchersLavaland
|
||||||
proto: MobWatcherLavaland
|
prototype: MobWatcherLavaland
|
||||||
groupCount: 3
|
minGroupSize: 3
|
||||||
|
maxGroupSize: 3
|
||||||
|
|
||||||
- type: biomeMarkerLayer
|
- type: biomeMarkerLayer
|
||||||
id: WatchersIcewing
|
id: WatchersIcewing
|
||||||
proto: MobWatcherIcewing
|
prototype: MobWatcherIcewing
|
||||||
groupCount: 3
|
minGroupSize: 3
|
||||||
|
maxGroupSize: 3
|
||||||
|
|
||||||
- type: biomeMarkerLayer
|
- type: biomeMarkerLayer
|
||||||
id: WatchersMagmawing
|
id: WatchersMagmawing
|
||||||
proto: MobWatcherMagmawing
|
prototype: MobWatcherMagmawing
|
||||||
groupCount: 3
|
minGroupSize: 3
|
||||||
|
maxGroupSize: 3
|
||||||
|
|
||||||
# TODO: Needs to be more robust
|
# TODO: Needs to be more robust
|
||||||
- type: biomeMarkerLayer
|
- type: biomeMarkerLayer
|
||||||
id: Xenos
|
id: Xenos
|
||||||
proto: MobXeno
|
prototype: MobXeno
|
||||||
|
|
||||||
- type: biomeMarkerLayer
|
- type: biomeMarkerLayer
|
||||||
id: Carps
|
id: Carps
|
||||||
proto: MobCarpDungeon
|
prototype: MobCarpDungeon
|
||||||
|
|
||||||
|
|
||||||
#- type: biomeMarkerLayer
|
#- type: biomeMarkerLayer
|
||||||
|
|||||||
@@ -1,290 +1,103 @@
|
|||||||
# Low value
|
# Low value
|
||||||
- type: biomeMarkerLayer
|
- type: biomeMarkerLayer
|
||||||
id: OreTin
|
id: OreTin
|
||||||
proto: WallRockTin
|
entityMask:
|
||||||
entityMask: WallRock
|
WallRock: WallRockTin
|
||||||
|
WallRockBasalt: WallRockBasaltTin
|
||||||
|
WallRockChromite: WallRockChromiteTin
|
||||||
|
WallRockSnow: WallRockSnowTin
|
||||||
maxCount: 30
|
maxCount: 30
|
||||||
groupCount: 10
|
minGroupSize: 10
|
||||||
|
maxGroupSize: 20
|
||||||
radius: 4
|
radius: 4
|
||||||
|
|
||||||
- type: biomeMarkerLayer
|
- type: biomeMarkerLayer
|
||||||
id: OreQuartz
|
id: OreQuartz
|
||||||
proto: WallRockQuartz
|
entityMask:
|
||||||
entityMask: WallRock
|
WallRock: WallRockQuartz
|
||||||
|
WallRockBasalt: WallRockBasaltQuartz
|
||||||
|
WallRockChromite: WallRockChromiteQuartz
|
||||||
|
WallRockSnow: WallRockSnowQuartz
|
||||||
maxCount: 30
|
maxCount: 30
|
||||||
groupCount: 10
|
minGroupSize: 10
|
||||||
|
maxGroupSize: 20
|
||||||
radius: 4
|
radius: 4
|
||||||
|
|
||||||
# Medium value
|
# Medium value
|
||||||
# Gold
|
# Gold
|
||||||
- type: biomeMarkerLayer
|
- type: biomeMarkerLayer
|
||||||
id: OreGold
|
id: OreGold
|
||||||
proto: WallRockGold
|
entityMask:
|
||||||
entityMask: WallRock
|
WallRock: WallRockGold
|
||||||
|
WallRockBasalt: WallRockBasaltGold
|
||||||
|
WallRockChromite: WallRockChromiteGold
|
||||||
|
WallRockSnow: WallRockSnowGold
|
||||||
maxCount: 30
|
maxCount: 30
|
||||||
groupCount: 5
|
minGroupSize: 5
|
||||||
|
maxGroupSize: 10
|
||||||
radius: 4
|
radius: 4
|
||||||
|
|
||||||
# Silver
|
# Silver
|
||||||
- type: biomeMarkerLayer
|
- type: biomeMarkerLayer
|
||||||
id: OreSilver
|
id: OreSilver
|
||||||
proto: WallRockSilver
|
entityMask:
|
||||||
entityMask: WallRock
|
WallRock: WallRockSilver
|
||||||
|
WallRockBasalt: WallRockBasaltSilver
|
||||||
|
WallRockChromite: WallRockChromiteSilver
|
||||||
|
WallRockSnow: WallRockSnowSilver
|
||||||
maxCount: 30
|
maxCount: 30
|
||||||
groupCount: 5
|
minGroupSize: 5
|
||||||
|
maxGroupSize: 10
|
||||||
radius: 4
|
radius: 4
|
||||||
|
|
||||||
# High value
|
# High value
|
||||||
# Plasma
|
# Plasma
|
||||||
- type: biomeMarkerLayer
|
- type: biomeMarkerLayer
|
||||||
id: OrePlasma
|
id: OrePlasma
|
||||||
proto: WallRockPlasma
|
entityMask:
|
||||||
entityMask: WallRock
|
WallRock: WallRockPlasma
|
||||||
|
WallRockBasalt: WallRockBasaltPlasma
|
||||||
|
WallRockChromite: WallRockChromitePlasma
|
||||||
|
WallRockSnow: WallRockSnowPlasma
|
||||||
maxCount: 12
|
maxCount: 12
|
||||||
groupCount: 5
|
minGroupSize: 5
|
||||||
|
maxGroupSize: 10
|
||||||
radius: 4
|
radius: 4
|
||||||
|
|
||||||
# Uranium
|
# Uranium
|
||||||
- type: biomeMarkerLayer
|
- type: biomeMarkerLayer
|
||||||
id: OreUranium
|
id: OreUranium
|
||||||
proto: WallRockUranium
|
entityMask:
|
||||||
entityMask: WallRock
|
WallRock: WallRockUranium
|
||||||
|
WallRockBasalt: WallRockBasaltUranium
|
||||||
|
WallRockChromite: WallRockChromiteUranium
|
||||||
|
WallRockSnow: WallRockSnowUranium
|
||||||
maxCount: 12
|
maxCount: 12
|
||||||
groupCount: 5
|
minGroupSize: 5
|
||||||
|
maxGroupSize: 10
|
||||||
radius: 4
|
radius: 4
|
||||||
|
|
||||||
- type: biomeMarkerLayer
|
- type: biomeMarkerLayer
|
||||||
id: OreBananium
|
id: OreBananium
|
||||||
proto: WallRockBananium
|
entityMask:
|
||||||
entityMask: WallRock
|
WallRock: WallRockBananium
|
||||||
|
WallRockBasalt: WallRockBasaltBananium
|
||||||
|
WallRockChromite: WallRockChromiteBananium
|
||||||
|
WallRockSnow: WallRockSnowBananium
|
||||||
maxCount: 12
|
maxCount: 12
|
||||||
groupCount: 5
|
minGroupSize: 5
|
||||||
|
maxGroupSize: 10
|
||||||
radius: 4
|
radius: 4
|
||||||
|
|
||||||
# Artifact Fragment
|
# Artifact Fragment
|
||||||
- type: biomeMarkerLayer
|
- type: biomeMarkerLayer
|
||||||
id: OreArtifactFragment
|
id: OreArtifactFragment
|
||||||
proto: WallRockArtifactFragment
|
entityMask:
|
||||||
entityMask: WallRock
|
WallRock: WallRockArtifactFragment
|
||||||
|
WallRockBasalt: WallRockBasaltArtifactFragment
|
||||||
|
WallRockChromite: WallRockChromiteArtifactFragment
|
||||||
|
WallRockSnow: WallRockSnowArtifactFragment
|
||||||
maxCount: 6
|
maxCount: 6
|
||||||
groupCount: 1
|
minGroupSize: 1
|
||||||
radius: 4
|
maxGroupSize: 2
|
||||||
|
|
||||||
# Basalt variant
|
|
||||||
# Low value
|
|
||||||
- type: biomeMarkerLayer
|
|
||||||
id: BasaltOreTin
|
|
||||||
proto: WallRockBasaltTin
|
|
||||||
entityMask: WallRockBasalt
|
|
||||||
maxCount: 30
|
|
||||||
groupCount: 10
|
|
||||||
radius: 4
|
|
||||||
|
|
||||||
- type: biomeMarkerLayer
|
|
||||||
id: BasaltOreQuartz
|
|
||||||
proto: WallRockBasaltQuartz
|
|
||||||
entityMask: WallRockBasalt
|
|
||||||
maxCount: 30
|
|
||||||
groupCount: 10
|
|
||||||
radius: 4
|
|
||||||
|
|
||||||
# Medium value
|
|
||||||
# Gold
|
|
||||||
- type: biomeMarkerLayer
|
|
||||||
id: BasaltOreGold
|
|
||||||
proto: WallRockBasaltGold
|
|
||||||
entityMask: WallRockBasalt
|
|
||||||
maxCount: 30
|
|
||||||
groupCount: 5
|
|
||||||
radius: 4
|
|
||||||
|
|
||||||
# Silver
|
|
||||||
- type: biomeMarkerLayer
|
|
||||||
id: BasaltOreSilver
|
|
||||||
proto: WallRockBasaltSilver
|
|
||||||
entityMask: WallRockBasalt
|
|
||||||
maxCount: 30
|
|
||||||
groupCount: 5
|
|
||||||
radius: 4
|
|
||||||
|
|
||||||
# High value
|
|
||||||
# Plasma
|
|
||||||
- type: biomeMarkerLayer
|
|
||||||
id: BasaltOrePlasma
|
|
||||||
proto: WallRockBasaltPlasma
|
|
||||||
entityMask: WallRockBasalt
|
|
||||||
maxCount: 12
|
|
||||||
groupCount: 5
|
|
||||||
radius: 4
|
|
||||||
|
|
||||||
# Uranium
|
|
||||||
- type: biomeMarkerLayer
|
|
||||||
id: BasaltOreUranium
|
|
||||||
proto: WallRockBasaltUranium
|
|
||||||
entityMask: WallRockBasalt
|
|
||||||
maxCount: 12
|
|
||||||
groupCount: 5
|
|
||||||
radius: 4
|
|
||||||
|
|
||||||
- type: biomeMarkerLayer
|
|
||||||
id: BasaltOreBananium
|
|
||||||
proto: WallRockBasaltBananium
|
|
||||||
entityMask: WallRockBasalt
|
|
||||||
maxCount: 12
|
|
||||||
groupCount: 5
|
|
||||||
radius: 4
|
|
||||||
|
|
||||||
# Artifact Fragment
|
|
||||||
- type: biomeMarkerLayer
|
|
||||||
id: BasaltOreArtifactFragment
|
|
||||||
proto: WallRockBasaltArtifactFragment
|
|
||||||
entityMask: WallRockBasalt
|
|
||||||
maxCount: 6
|
|
||||||
groupCount: 1
|
|
||||||
radius: 4
|
|
||||||
|
|
||||||
# Shadow basalt variant
|
|
||||||
# Low value
|
|
||||||
- type: biomeMarkerLayer
|
|
||||||
id: ChromiteOreTin
|
|
||||||
proto: WallRockChromiteTin
|
|
||||||
entityMask: WallRockChromite
|
|
||||||
maxCount: 30
|
|
||||||
groupCount: 10
|
|
||||||
radius: 4
|
|
||||||
|
|
||||||
- type: biomeMarkerLayer
|
|
||||||
id: ChromiteOreQuartz
|
|
||||||
proto: WallRockChromiteQuartz
|
|
||||||
entityMask: WallRockChromite
|
|
||||||
maxCount: 30
|
|
||||||
groupCount: 10
|
|
||||||
radius: 4
|
|
||||||
|
|
||||||
# Medium value
|
|
||||||
# Gold
|
|
||||||
- type: biomeMarkerLayer
|
|
||||||
id: ChromiteOreGold
|
|
||||||
proto: WallRockChromiteGold
|
|
||||||
entityMask: WallRockChromite
|
|
||||||
maxCount: 30
|
|
||||||
groupCount: 5
|
|
||||||
radius: 4
|
|
||||||
|
|
||||||
# Silver
|
|
||||||
- type: biomeMarkerLayer
|
|
||||||
id: ChromiteOreSilver
|
|
||||||
proto: WallRockChromiteSilver
|
|
||||||
entityMask: WallRockChromite
|
|
||||||
maxCount: 30
|
|
||||||
groupCount: 5
|
|
||||||
radius: 4
|
|
||||||
|
|
||||||
# High value
|
|
||||||
# Plasma
|
|
||||||
- type: biomeMarkerLayer
|
|
||||||
id: ChromiteOrePlasma
|
|
||||||
proto: WallRockChromitePlasma
|
|
||||||
entityMask: WallRockChromite
|
|
||||||
maxCount: 12
|
|
||||||
groupCount: 5
|
|
||||||
radius: 4
|
|
||||||
|
|
||||||
# Uranium
|
|
||||||
- type: biomeMarkerLayer
|
|
||||||
id: ChromiteOreUranium
|
|
||||||
proto: WallRockChromiteUranium
|
|
||||||
entityMask: WallRockChromite
|
|
||||||
maxCount: 12
|
|
||||||
groupCount: 5
|
|
||||||
radius: 4
|
|
||||||
|
|
||||||
- type: biomeMarkerLayer
|
|
||||||
id: ChromiteOreBananium
|
|
||||||
proto: WallRockChromiteBananium
|
|
||||||
entityMask: WallRockChromite
|
|
||||||
maxCount: 12
|
|
||||||
groupCount: 5
|
|
||||||
radius: 4
|
|
||||||
|
|
||||||
# Artifact Fragment
|
|
||||||
- type: biomeMarkerLayer
|
|
||||||
id: ChromiteOreArtifactFragment
|
|
||||||
proto: WallRockChromiteArtifactFragment
|
|
||||||
entityMask: WallRockChromite
|
|
||||||
maxCount: 6
|
|
||||||
groupCount: 1
|
|
||||||
radius: 4
|
|
||||||
|
|
||||||
# Snow variant
|
|
||||||
# Low value
|
|
||||||
- type: biomeMarkerLayer
|
|
||||||
id: SnowOreTin
|
|
||||||
proto: WallRockSnowTin
|
|
||||||
entityMask: WallRockSnow
|
|
||||||
maxCount: 30
|
|
||||||
groupCount: 10
|
|
||||||
radius: 4
|
|
||||||
|
|
||||||
- type: biomeMarkerLayer
|
|
||||||
id: SnowOreQuartz
|
|
||||||
proto: WallRockSnowQuartz
|
|
||||||
entityMask: WallRockSnow
|
|
||||||
maxCount: 30
|
|
||||||
groupCount: 10
|
|
||||||
radius: 4
|
|
||||||
|
|
||||||
# Medium value
|
|
||||||
# Gold
|
|
||||||
- type: biomeMarkerLayer
|
|
||||||
id: SnowOreGold
|
|
||||||
proto: WallRockSnowGold
|
|
||||||
entityMask: WallRockSnow
|
|
||||||
maxCount: 30
|
|
||||||
groupCount: 5
|
|
||||||
radius: 4
|
|
||||||
|
|
||||||
# Silver
|
|
||||||
- type: biomeMarkerLayer
|
|
||||||
id: SnowOreSilver
|
|
||||||
proto: WallRockSnowSilver
|
|
||||||
entityMask: WallRockSnow
|
|
||||||
maxCount: 30
|
|
||||||
groupCount: 5
|
|
||||||
radius: 4
|
|
||||||
|
|
||||||
# High value
|
|
||||||
# Plasma
|
|
||||||
- type: biomeMarkerLayer
|
|
||||||
id: SnowOrePlasma
|
|
||||||
proto: WallRockSnowPlasma
|
|
||||||
entityMask: WallRockSnow
|
|
||||||
maxCount: 12
|
|
||||||
groupCount: 5
|
|
||||||
radius: 4
|
|
||||||
|
|
||||||
# Uranium
|
|
||||||
- type: biomeMarkerLayer
|
|
||||||
id: SnowOreUranium
|
|
||||||
proto: WallRockSnowUranium
|
|
||||||
entityMask: WallRockSnow
|
|
||||||
maxCount: 12
|
|
||||||
groupCount: 5
|
|
||||||
radius: 4
|
|
||||||
|
|
||||||
- type: biomeMarkerLayer
|
|
||||||
id: SnowOreBananium
|
|
||||||
proto: WallRockSnowBananium
|
|
||||||
entityMask: WallRockSnow
|
|
||||||
maxCount: 12
|
|
||||||
groupCount: 5
|
|
||||||
radius: 4
|
|
||||||
|
|
||||||
# Artifact Fragment
|
|
||||||
- type: biomeMarkerLayer
|
|
||||||
id: SnowOreArtifactFragment
|
|
||||||
proto: WallRockSnowArtifactFragment
|
|
||||||
entityMask: WallRockSnow
|
|
||||||
maxCount: 6
|
|
||||||
groupCount: 1
|
|
||||||
radius: 4
|
radius: 4
|
||||||
|
|||||||
@@ -13,7 +13,6 @@
|
|||||||
fractalType: FBm
|
fractalType: FBm
|
||||||
octaves: 2
|
octaves: 2
|
||||||
lacunarity: 2
|
lacunarity: 2
|
||||||
gain: 0.5
|
|
||||||
- !type:BiomeMetaLayer
|
- !type:BiomeMetaLayer
|
||||||
template: Grasslands
|
template: Grasslands
|
||||||
threshold: 0
|
threshold: 0
|
||||||
@@ -23,7 +22,6 @@
|
|||||||
fractalType: FBm
|
fractalType: FBm
|
||||||
octaves: 2
|
octaves: 2
|
||||||
lacunarity: 2
|
lacunarity: 2
|
||||||
gain: 0.5
|
|
||||||
- !type:BiomeMetaLayer
|
- !type:BiomeMetaLayer
|
||||||
template: Snow
|
template: Snow
|
||||||
threshold: 0.5
|
threshold: 0.5
|
||||||
@@ -33,7 +31,6 @@
|
|||||||
fractalType: FBm
|
fractalType: FBm
|
||||||
octaves: 2
|
octaves: 2
|
||||||
lacunarity: 2
|
lacunarity: 2
|
||||||
gain: 0.5
|
|
||||||
|
|
||||||
# Desert
|
# Desert
|
||||||
# TODO: Water in desert
|
# TODO: Water in desert
|
||||||
@@ -62,7 +59,6 @@
|
|||||||
fractalType: FBm
|
fractalType: FBm
|
||||||
octaves: 5
|
octaves: 5
|
||||||
lacunarity: 2
|
lacunarity: 2
|
||||||
gain: 1
|
|
||||||
cellularDistanceFunction: Euclidean
|
cellularDistanceFunction: Euclidean
|
||||||
cellularReturnType: Distance2
|
cellularReturnType: Distance2
|
||||||
allowedTiles:
|
allowedTiles:
|
||||||
@@ -129,7 +125,6 @@
|
|||||||
fractalType: FBm
|
fractalType: FBm
|
||||||
octaves: 5
|
octaves: 5
|
||||||
lacunarity: 2
|
lacunarity: 2
|
||||||
gain: 1
|
|
||||||
cellularDistanceFunction: Euclidean
|
cellularDistanceFunction: Euclidean
|
||||||
cellularReturnType: Distance2
|
cellularReturnType: Distance2
|
||||||
decals:
|
decals:
|
||||||
@@ -174,7 +169,6 @@
|
|||||||
lacunarity: 2
|
lacunarity: 2
|
||||||
fractalType: FBm
|
fractalType: FBm
|
||||||
octaves: 5
|
octaves: 5
|
||||||
gain: 1
|
|
||||||
cellularDistanceFunction: Euclidean
|
cellularDistanceFunction: Euclidean
|
||||||
cellularReturnType: Distance2
|
cellularReturnType: Distance2
|
||||||
entities:
|
entities:
|
||||||
@@ -229,7 +223,6 @@
|
|||||||
lacunarity: 2
|
lacunarity: 2
|
||||||
fractalType: FBm
|
fractalType: FBm
|
||||||
octaves: 5
|
octaves: 5
|
||||||
gain: 1
|
|
||||||
cellularDistanceFunction: Euclidean
|
cellularDistanceFunction: Euclidean
|
||||||
cellularReturnType: Distance2
|
cellularReturnType: Distance2
|
||||||
|
|
||||||
@@ -305,7 +298,6 @@
|
|||||||
lacunarity: 2
|
lacunarity: 2
|
||||||
fractalType: FBm
|
fractalType: FBm
|
||||||
octaves: 5
|
octaves: 5
|
||||||
gain: 1
|
|
||||||
cellularDistanceFunction: Euclidean
|
cellularDistanceFunction: Euclidean
|
||||||
cellularReturnType: Distance2
|
cellularReturnType: Distance2
|
||||||
entities:
|
entities:
|
||||||
@@ -370,7 +362,6 @@
|
|||||||
fractalType: FBm
|
fractalType: FBm
|
||||||
octaves: 5
|
octaves: 5
|
||||||
lacunarity: 2
|
lacunarity: 2
|
||||||
gain: 1
|
|
||||||
cellularDistanceFunction: Euclidean
|
cellularDistanceFunction: Euclidean
|
||||||
cellularReturnType: Distance2
|
cellularReturnType: Distance2
|
||||||
decals:
|
decals:
|
||||||
@@ -436,7 +427,6 @@
|
|||||||
lacunarity: 2
|
lacunarity: 2
|
||||||
fractalType: FBm
|
fractalType: FBm
|
||||||
octaves: 5
|
octaves: 5
|
||||||
gain: 1
|
|
||||||
cellularDistanceFunction: Euclidean
|
cellularDistanceFunction: Euclidean
|
||||||
cellularReturnType: Distance2
|
cellularReturnType: Distance2
|
||||||
entities:
|
entities:
|
||||||
@@ -452,7 +442,6 @@
|
|||||||
lacunarity: 2
|
lacunarity: 2
|
||||||
fractalType: FBm
|
fractalType: FBm
|
||||||
octaves: 5
|
octaves: 5
|
||||||
gain: 1
|
|
||||||
cellularDistanceFunction: Euclidean
|
cellularDistanceFunction: Euclidean
|
||||||
cellularReturnType: Distance2
|
cellularReturnType: Distance2
|
||||||
- !type:BiomeDummyLayer
|
- !type:BiomeDummyLayer
|
||||||
@@ -586,7 +575,7 @@
|
|||||||
fractalType: Ridged
|
fractalType: Ridged
|
||||||
octaves: 1
|
octaves: 1
|
||||||
frequency: 0.1
|
frequency: 0.1
|
||||||
gain: 0
|
gain: 0.5
|
||||||
allowedTiles:
|
allowedTiles:
|
||||||
- FloorAsteroidSand
|
- FloorAsteroidSand
|
||||||
entities:
|
entities:
|
||||||
|
|||||||
@@ -123,22 +123,14 @@
|
|||||||
guaranteed: true
|
guaranteed: true
|
||||||
loots:
|
loots:
|
||||||
- !type:BiomeMarkerLoot
|
- !type:BiomeMarkerLoot
|
||||||
proto:
|
proto: OreTin
|
||||||
Caves: OreTin
|
|
||||||
Grasslands: OreTin
|
|
||||||
Lava: BasaltOreTin
|
|
||||||
Snow: SnowOreTin
|
|
||||||
|
|
||||||
- type: salvageLoot
|
- type: salvageLoot
|
||||||
id: OreQuartz
|
id: OreQuartz
|
||||||
guaranteed: true
|
guaranteed: true
|
||||||
loots:
|
loots:
|
||||||
- !type:BiomeMarkerLoot
|
- !type:BiomeMarkerLoot
|
||||||
proto:
|
proto: OreQuartz
|
||||||
Caves: OreQuartz
|
|
||||||
Grasslands: OreQuartz
|
|
||||||
Lava: BasaltOreQuartz
|
|
||||||
Snow: SnowOreQuartz
|
|
||||||
|
|
||||||
# - Medium value
|
# - Medium value
|
||||||
- type: salvageLoot
|
- type: salvageLoot
|
||||||
@@ -146,22 +138,14 @@
|
|||||||
guaranteed: true
|
guaranteed: true
|
||||||
loots:
|
loots:
|
||||||
- !type:BiomeMarkerLoot
|
- !type:BiomeMarkerLoot
|
||||||
proto:
|
proto: OreGold
|
||||||
Caves: OreGold
|
|
||||||
Grasslands: OreGold
|
|
||||||
Lava: BasaltOreGold
|
|
||||||
Snow: SnowOreGold
|
|
||||||
|
|
||||||
- type: salvageLoot
|
- type: salvageLoot
|
||||||
id: OreSilver
|
id: OreSilver
|
||||||
guaranteed: true
|
guaranteed: true
|
||||||
loots:
|
loots:
|
||||||
- !type:BiomeMarkerLoot
|
- !type:BiomeMarkerLoot
|
||||||
proto:
|
proto: OreSilver
|
||||||
Caves: OreSilver
|
|
||||||
Grasslands: OreSilver
|
|
||||||
Lava: BasaltOreSilver
|
|
||||||
Snow: SnowOreSilver
|
|
||||||
|
|
||||||
# - High value
|
# - High value
|
||||||
- type: salvageLoot
|
- type: salvageLoot
|
||||||
@@ -169,41 +153,25 @@
|
|||||||
guaranteed: true
|
guaranteed: true
|
||||||
loots:
|
loots:
|
||||||
- !type:BiomeMarkerLoot
|
- !type:BiomeMarkerLoot
|
||||||
proto:
|
proto: OrePlasma
|
||||||
Caves: OrePlasma
|
|
||||||
Grasslands: OrePlasma
|
|
||||||
Lava: BasaltOrePlasma
|
|
||||||
Snow: SnowOrePlasma
|
|
||||||
|
|
||||||
- type: salvageLoot
|
- type: salvageLoot
|
||||||
id: OreUranium
|
id: OreUranium
|
||||||
guaranteed: true
|
guaranteed: true
|
||||||
loots:
|
loots:
|
||||||
- !type:BiomeMarkerLoot
|
- !type:BiomeMarkerLoot
|
||||||
proto:
|
proto: OreUranium
|
||||||
Caves: OreUranium
|
|
||||||
Grasslands: OreUranium
|
|
||||||
Lava: BasaltOreUranium
|
|
||||||
Snow: SnowOreUranium
|
|
||||||
|
|
||||||
- type: salvageLoot
|
- type: salvageLoot
|
||||||
id: OreBananium
|
id: OreBananium
|
||||||
guaranteed: true
|
guaranteed: true
|
||||||
loots:
|
loots:
|
||||||
- !type:BiomeMarkerLoot
|
- !type:BiomeMarkerLoot
|
||||||
proto:
|
proto: OreBananium
|
||||||
Caves: OreBananium
|
|
||||||
Grasslands: OreBananium
|
|
||||||
Lava: BasaltOreBananium
|
|
||||||
Snow: SnowOreBananium
|
|
||||||
|
|
||||||
- type: salvageLoot
|
- type: salvageLoot
|
||||||
id: OreArtifactFragment
|
id: OreArtifactFragment
|
||||||
guaranteed: true
|
guaranteed: true
|
||||||
loots:
|
loots:
|
||||||
- !type:BiomeMarkerLoot
|
- !type:BiomeMarkerLoot
|
||||||
proto:
|
proto: OreArtifactFragment
|
||||||
Caves: OreArtifactFragment
|
|
||||||
Grasslands: OreArtifactFragment
|
|
||||||
Lava: BasaltOreArtifactFragment
|
|
||||||
Snow: SnowOreArtifactFragment
|
|
||||||
|
|||||||
@@ -8,6 +8,11 @@
|
|||||||
kind: source
|
kind: source
|
||||||
path: "/Textures/Shaders/gradient_circle_mask.swsl"
|
path: "/Textures/Shaders/gradient_circle_mask.swsl"
|
||||||
|
|
||||||
|
- type: shader
|
||||||
|
id: WorldGradientCircle
|
||||||
|
kind: source
|
||||||
|
path: "/Textures/Shaders/world_gradient_circle.swsl"
|
||||||
|
|
||||||
- type: shader
|
- type: shader
|
||||||
id: ColoredScreenBorder
|
id: ColoredScreenBorder
|
||||||
kind: source
|
kind: source
|
||||||
|
|||||||
27
Resources/Textures/Shaders/world_gradient_circle.swsl
Normal file
27
Resources/Textures/Shaders/world_gradient_circle.swsl
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
// Has 2 circles, an inner one that is unaffected and an outer one.
|
||||||
|
light_mode unshaded;
|
||||||
|
|
||||||
|
const highp vec4 color = vec4(1.0, 1.0, 1.0, 1.0);
|
||||||
|
|
||||||
|
// Position of the center in pixel terms.
|
||||||
|
uniform highp vec2 position;
|
||||||
|
uniform highp float maxRange;
|
||||||
|
uniform highp float minRange;
|
||||||
|
uniform highp float bufferRange;
|
||||||
|
uniform highp float gradient;
|
||||||
|
|
||||||
|
void fragment() {
|
||||||
|
highp float distance = length(FRAGCOORD.xy - position);
|
||||||
|
|
||||||
|
if (distance > maxRange) {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
else if (distance < minRange) {
|
||||||
|
COLOR = color;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
highp float ratio = 1.0 - pow((distance - minRange) / bufferRange, gradient);
|
||||||
|
COLOR = vec4(color.x, color.y, color.z, ratio);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -57,6 +57,7 @@
|
|||||||
- toggledecals
|
- toggledecals
|
||||||
- nodevis
|
- nodevis
|
||||||
- nodevisfilter
|
- nodevisfilter
|
||||||
|
- showbiome
|
||||||
- net_draw_interp
|
- net_draw_interp
|
||||||
- showmeleespread
|
- showmeleespread
|
||||||
- showgunspread
|
- showgunspread
|
||||||
|
|||||||
Reference in New Issue
Block a user