feat: перенос лазеров-снарядов

This commit is contained in:
Remuchi
2024-01-24 12:58:57 +07:00
parent 7120f4d85d
commit cf8709f1ea
36 changed files with 1920 additions and 144 deletions

View File

@@ -25,6 +25,7 @@
</ItemGroup>
<ItemGroup>
<Folder Include="Spawners\" />
<Folder Include="White\Trail\" />
</ItemGroup>
<Import Project="..\RobustToolbox\MSBuild\Robust.Properties.targets" />
<Import Project="..\RobustToolbox\MSBuild\XamlIL.targets" />

View File

@@ -21,6 +21,7 @@ using Content.Client.White.JoinQueue;
using Content.Client.White.Jukebox;
using Content.Client.White.Sponsors;
using Content.Client.White.Stalin;
using Content.Client.White.Trail.Line.Manager;
using Content.Client.White.TTS;
using Content.Shared.Administration.Managers;
@@ -57,6 +58,7 @@ namespace Content.Client.IoC
IoCManager.Register<StalinManager>();
IoCManager.Register<ClientJukeboxSongsSyncManager>();
IoCManager.Register<TTSManager>();
IoCManager.Register<ITrailLineManager, TrailSplineManager>();
//WD-EDIT
}
}

View File

@@ -0,0 +1,15 @@
using System.Numerics;
using Content.Shared.White.Trail;
using Robust.Client.Graphics;
using Robust.Shared.Map;
namespace Content.Client.White.Trail.Line;
public interface ITrailLine
{
ITrailSettings Settings { get; }
void TryCreateSegment((Vector2 WorldPosition, Angle WorldRotation) worldPosRot, MapId mapId);
void Render(DrawingHandleWorld handle, Texture? texture);
}

View File

@@ -0,0 +1,6 @@
namespace Content.Client.White.Trail.Line.Manager;
public interface ITrailLineHolder
{
public ITrailLine? TrailLine { get; set; }
}

View File

@@ -0,0 +1,15 @@
using Content.Shared.White.Trail;
using Robust.Shared.Map;
namespace Content.Client.White.Trail.Line.Manager;
public interface ITrailLineManager
{
IEnumerable<ITrailLine> Lines { get; }
ITrailLine CreateTrail(ITrailSettings settings, MapId mapId);
void Detach(ITrailLineHolder holder);
void Update(float dt);
}

View File

@@ -0,0 +1,64 @@
using Content.Client.White.Trail.SplineRenderer;
using Content.Shared.White.Spline;
using Content.Shared.White.Trail;
using Robust.Shared.Map;
namespace Content.Client.White.Trail.Line.Manager;
public sealed class TrailSplineManager : ITrailLineManager
{
private readonly LinkedList<TrailSpline> _lines = new();
public IEnumerable<ITrailLine> Lines => _lines;
public ITrailLine CreateTrail(ITrailSettings settings, MapId mapId)
{
var tline = new TrailSpline
{
Attached = true,
Settings = settings,
MapId = mapId,
SplineIterator = Spline.From2DType(settings.SplineIteratorType),
GradientIterator = Spline.From4DType(settings.GradientIteratorType),
Renderer = TrailSplineRenderer.FromType(settings.SplineRendererType)
};
_lines.AddLast(tline);
return tline;
}
public void Detach(ITrailLineHolder holder)
{
if (holder.TrailLine is TrailSpline trailSpline)
{
trailSpline.Attached = false;
var detachedSettings = new TrailSettings();
TrailSettings.Inject(detachedSettings, trailSpline.Settings);
trailSpline.Settings = detachedSettings;
}
}
public void Update(float dt)
{
var curNode = _lines.First;
while (curNode != null)
{
var curLine = curNode.Value;
curNode = curNode.Next;
if (!curLine.HasSegments())
{
if (curLine.Attached)
curLine.ResetLifetime();
else
_lines.Remove(curLine);
continue;
}
curLine.AddLifetime(dt);
curLine.RemoveExpiredSegments();
curLine.UpdateSegments(dt);
}
}
}

View File

@@ -0,0 +1,183 @@
using Content.Client.White.Trail.SplineRenderer;
using Content.Shared.White.Spline;
using Content.Shared.White.Spline.Linear;
using Content.Shared.White.Trail;
using Robust.Client.Graphics;
using Robust.Shared.Map;
using Robust.Shared.Random;
using System.Linq;
using System.Numerics;
using System.Runtime.CompilerServices;
using Vector4 = Robust.Shared.Maths.Vector4;
namespace Content.Client.White.Trail.Line;
public sealed class TrailSpline : ITrailLine
{
private static readonly IRobustRandom Random = IoCManager.Resolve<IRobustRandom>();
[ViewVariables]
private readonly LinkedList<TrailSplineSegment> _segments = new();
[ViewVariables]
private Vector2 _lastCreationPos;
[ViewVariables]
private float _curLifetime;
[ViewVariables]
private Vector2? _virtualSegmentPos;
[ViewVariables]
public MapId MapId { get; set; }
[ViewVariables]
public bool Attached { get; set; }
[ViewVariables]
public ITrailSettings Settings { get; set; } = TrailSettings.Default;
[ViewVariables]
public ISpline<Vector2> SplineIterator { get; set; } = new SplineLinear2D();
[ViewVariables]
public ISpline<Vector4> GradientIterator { get; set; } = new SplineLinear4D();
[ViewVariables]
public ITrailSplineRenderer Renderer { get; set; } = new TrailSplineRendererDebug();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool HasSegments()
{
return _segments.Count > 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddLifetime(float time)
{
_curLifetime += time;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ResetLifetime()
{
_curLifetime = 0f;
}
public void TryCreateSegment((Vector2 WorldPosition, Angle WorldRotation) worldPosRot, MapId mapId)
{
if (!Attached)
return;
if (mapId != MapId)
return;
if (worldPosRot.WorldPosition == Vector2.Zero)
return;
var pos = worldPosRot.WorldPosition + worldPosRot.WorldRotation.RotateVec(Settings.CreationOffset);
_lastCreationPos = pos;
if (_virtualSegmentPos.HasValue)
{
var vPos = _virtualSegmentPos.Value;
if ((vPos - pos).LengthSquared() > Settings.СreationDistanceThresholdSquared)
{
_segments.AddLast(new TrailSplineSegment()
{ Position = vPos, ExistTil = _curLifetime + Settings.Lifetime });
_virtualSegmentPos = null;
}
return;
}
var lastPos = _segments.Last?.Value.Position;
if (!lastPos.HasValue || (lastPos.Value - pos).LengthSquared() > Settings.СreationDistanceThresholdSquared)
_virtualSegmentPos = pos;
}
public void UpdateSegments(float dt)
{
var gravity = Settings.Gravity;
var maxRandomWalk = Settings.MaxRandomWalk;
var lifetime = Settings.Lifetime;
if (_segments.Last != null)
{
var i = 0;
var positions = new Vector2[_segments.Count + 1];
positions[_segments.Count] = _lastCreationPos;
var curNode = _segments.First;
while (curNode != null)
{
var offset = gravity;
var curValue = curNode.Value;
if (maxRandomWalk != Vector2.Zero)
{
positions[i] = curValue.Position;
if (curNode.Next != null)
{
positions[i + 1] = curNode.Next.Value.Position;
if (curNode.Next.Next != null)
positions[i + 2] = curNode.Next.Next.Value.Position;
}
var effectiveRandomWalk = maxRandomWalk * (curValue.ExistTil - _curLifetime) / lifetime;
var gradientNorm = -SplineIterator.SampleVelocity(positions, i).Normalized();
offset += gradientNorm * effectiveRandomWalk.Y * Random.NextFloat(-1.0f, 1.0f);
var rotated90Degrees = new Vector2(-gradientNorm.Y, gradientNorm.X);
offset += rotated90Degrees * effectiveRandomWalk.X * Random.NextFloat(-1.0f, 1.0f);
}
curValue.Position += offset;
i++;
curNode = curNode.Next;
}
}
if (_virtualSegmentPos.HasValue)
_virtualSegmentPos = _virtualSegmentPos.Value + gravity;
if (!Attached)
_lastCreationPos += gravity;
}
public void RemoveExpiredSegments()
{
while (_segments.First?.Value.ExistTil < _curLifetime)
{
_segments.RemoveFirst();
}
}
public void Render(DrawingHandleWorld handle, Texture? texture)
{
if (_segments.Last == null)
return;
var arrSize = _segments.Count + 1;
var paPositions = new Vector2[arrSize];
var paLifetimes = new float[arrSize];
paPositions[0] = _lastCreationPos;
paLifetimes[0] = 1f;
var reversedIndexedSegments = _segments.Reverse().Select((x, i) => (x, i + 1));
foreach (var (x, i) in reversedIndexedSegments)
{
paPositions[i] = x.Position;
paLifetimes[i] = (x.ExistTil - _curLifetime) / Settings.Lifetime;
}
Renderer.Render(handle, texture, SplineIterator, GradientIterator, Settings, paPositions, paLifetimes);
}
private sealed class TrailSplineSegment
{
public Vector2 Position { get; set; }
public float ExistTil { get; init; }
}
}

View File

@@ -0,0 +1,20 @@
using System.Numerics;
using Content.Shared.White.Spline;
using Content.Shared.White.Trail;
using Robust.Client.Graphics;
using Vector4 = Robust.Shared.Maths.Vector4;
namespace Content.Client.White.Trail.SplineRenderer;
public interface ITrailSplineRenderer
{
void Render(
DrawingHandleWorld handle,
Texture? texture,
ISpline<Vector2> splineIterator,
ISpline<Vector4> gradientIterator,
ITrailSettings settings,
Vector2[] paPositions,
float[] paLifetimes
);
}

View File

@@ -0,0 +1,17 @@
using Content.Shared.White.Trail;
namespace Content.Client.White.Trail.SplineRenderer;
public static class TrailSplineRenderer
{
public static ITrailSplineRenderer FromType(TrailSplineRendererType type)
{
return type switch
{
TrailSplineRendererType.Continuous => new TrailSplineRendererContinuous(),
TrailSplineRendererType.Point => new TrailSplineRendererPoint(),
TrailSplineRendererType.Debug => new TrailSplineRendererDebug(),
_ => throw new NotImplementedException()
};
}
}

View File

@@ -0,0 +1,90 @@
using Content.Shared.White.Spline;
using Content.Shared.White.Trail;
using Robust.Client.Graphics;
using System.Linq;
using System.Numerics;
using Vector4 = Robust.Shared.Maths.Vector4;
namespace Content.Client.White.Trail.SplineRenderer;
public sealed class TrailSplineRendererContinuous : ITrailSplineRenderer
{
public void Render(
DrawingHandleWorld handle,
Texture? texture,
ISpline<Vector2> splineIterator,
ISpline<Vector4> gradientIterator,
ITrailSettings settings,
Vector2[] paPositions,
float[] paLifetimes
)
{
float[] splinePointParams;
if (settings.LengthStep == 0f)
{
splinePointParams = Enumerable.Range(0, paPositions.Length - 1).Select(x => (float) x).ToArray();
}
else
{
splinePointParams = splineIterator
.IteratePointParamsByLength(paPositions, Math.Max(settings.LengthStep, 0.1f)).ToArray();
}
var gradientControlGroups = gradientIterator.GetControlGroupAmount(settings.Gradient.Length);
var colorToPointMul = 0f;
if (gradientControlGroups > 0)
colorToPointMul = gradientControlGroups / splineIterator.GetControlGroupAmount(paPositions.Length);
(Vector2, Vector2)? prevPoints = null;
foreach (var u in splinePointParams)
{
var (position, velocity) = splineIterator.SamplePositionVelocity(paPositions, u);
var offset = new Vector2(-velocity.Y, velocity.X).Normalized() *
settings.Scale.X; // 90-degree anticlockwise rotation
var curPoints = (position - offset, position + offset);
if (prevPoints.HasValue)
{
var colorVec = Vector4.One;
if (settings.Gradient != null && settings.Gradient.Length > 0)
{
if (gradientControlGroups > 0)
colorVec = gradientIterator.SamplePosition(settings.Gradient, u * colorToPointMul);
else
colorVec = settings.Gradient[0];
}
if (texture != null)
{
var verts = new DrawVertexUV2D[]
{
new(curPoints.Item1, Vector2.Zero),
new(curPoints.Item2, Vector2.UnitY),
new(prevPoints.Value.Item2, Vector2.One),
new(prevPoints.Value.Item1, Vector2.UnitX),
};
handle.DrawPrimitives(DrawPrimitiveTopology.TriangleFan, texture, verts,
new Color(colorVec.X, colorVec.Y, colorVec.Z, colorVec.W));
}
else
{
var verts = new[]
{
curPoints.Item1,
curPoints.Item2,
prevPoints.Value.Item2,
prevPoints.Value.Item1,
};
handle.DrawPrimitives(DrawPrimitiveTopology.TriangleFan, verts,
new Color(colorVec.X, colorVec.Y, colorVec.Z, colorVec.W));
}
}
prevPoints = curPoints;
}
}
}

View File

@@ -0,0 +1,54 @@
using Content.Shared.White.Spline;
using Content.Shared.White.Trail;
using Robust.Client.Graphics;
using System.Linq;
using System.Numerics;
using Vector4 = Robust.Shared.Maths.Vector4;
namespace Content.Client.White.Trail.SplineRenderer;
public sealed class TrailSplineRendererDebug : ITrailSplineRenderer
{
public void Render(
DrawingHandleWorld handle,
Texture? texture,
ISpline<Vector2> splineIterator,
ISpline<Vector4> gradientIterator,
ITrailSettings settings,
Vector2[] paPositions,
float[] paLifetimes
)
{
float[] splinePointParams;
if (settings.LengthStep == 0f)
{
splinePointParams = Enumerable.Range(0, paPositions.Length - 1).Select(x => (float) x).ToArray();
}
else
{
splinePointParams = splineIterator
.IteratePointParamsByLength(paPositions, Math.Max(settings.LengthStep, 0.1f)).ToArray();
}
Vector2? prevPosControlPoint = null;
foreach (var item in paPositions)
{
if (prevPosControlPoint.HasValue)
handle.DrawLine(item, prevPosControlPoint.Value, Color.Blue);
prevPosControlPoint = item;
}
Vector2? prevPosSplinePoint = null;
foreach (var u in splinePointParams)
{
var (position, velocity) = splineIterator.SamplePositionVelocity(paPositions, u);
if (prevPosSplinePoint.HasValue)
handle.DrawLine(position, prevPosSplinePoint.Value, Color.Red);
handle.DrawLine(position, position + velocity, Color.White);
handle.DrawCircle(position, 0.03f, new Color(0, 255, 0));
prevPosSplinePoint = position;
}
}
}

View File

@@ -0,0 +1,58 @@
using Content.Shared.White.Spline;
using Content.Shared.White.Trail;
using Robust.Client.Graphics;
using System.Linq;
using System.Numerics;
using Vector4 = Robust.Shared.Maths.Vector4;
namespace Content.Client.White.Trail.SplineRenderer;
public sealed class TrailSplineRendererPoint : ITrailSplineRenderer
{
public void Render(
DrawingHandleWorld handle,
Texture? texture,
ISpline<Vector2> splineIterator,
ISpline<Vector4> gradientIterator,
ITrailSettings settings,
Vector2[] paPositions,
float[] paLifetimes
)
{
if (texture == null)
return;
float[] splinePointParams;
if (settings.LengthStep == 0f)
{
splinePointParams = Enumerable.Range(0, paPositions.Length - 1).Select(x => (float) x).ToArray();
}
else
{
splinePointParams = splineIterator
.IteratePointParamsByLength(paPositions, Math.Max(settings.LengthStep, 0.1f)).ToArray();
}
var gradientControlGroups = gradientIterator.GetControlGroupAmount(settings.Gradient.Length);
var colorToPointMul = 0f;
if (gradientControlGroups > 0)
colorToPointMul = gradientControlGroups / gradientIterator.GetControlGroupAmount(paPositions.Length);
foreach (var u in splinePointParams)
{
var (position, velocity) = splineIterator.SamplePositionVelocity(paPositions, u);
var colorVec = Vector4.One;
if (settings.Gradient != null && settings.Gradient.Length > 0)
{
colorVec = gradientControlGroups > 0
? gradientIterator.SamplePosition(settings.Gradient, u * colorToPointMul)
: settings.Gradient[0];
}
var quad = Box2.FromDimensions(position, texture.Size * settings.Scale / EyeManager.PixelsPerMeter);
handle.DrawTextureRect(texture, new Box2Rotated(quad, velocity.ToAngle(), quad.Center),
new Color(colorVec.X, colorVec.Y, colorVec.Z, colorVec.W));
}
}
}

View File

@@ -0,0 +1,56 @@
using Content.Client.White.Trail.Line;
using Content.Client.White.Trail.Line.Manager;
using Content.Client.White.Trail.SplineRenderer;
using Content.Shared.White.Spline;
using Content.Shared.White.Trail;
namespace Content.Client.White.Trail;
[RegisterComponent]
public sealed partial class TrailComponent : SharedTrailComponent, ITrailLineHolder
{
[ViewVariables]
public ITrailLine? TrailLine { get; set; }
public override Spline2DType SplineIteratorType
{
get => base.SplineIteratorType;
set
{
if (base.SplineIteratorType == value)
return;
base.SplineIteratorType = value;
if (TrailLine is TrailSpline trailSpline)
trailSpline.SplineIterator = Spline.From2DType(value);
}
}
public override Spline4DType GradientIteratorType
{
get => base.GradientIteratorType;
set
{
if (base.GradientIteratorType == value)
return;
base.GradientIteratorType = value;
if (TrailLine is TrailSpline trailSpline)
trailSpline.GradientIterator = Spline.From4DType(value);
}
}
public override TrailSplineRendererType SplineRendererType
{
get => base.SplineRendererType;
set
{
if (base.SplineRendererType == value)
return;
base.SplineRendererType = value;
if (TrailLine is TrailSpline trailSpline)
trailSpline.Renderer = TrailSplineRenderer.FromType(value);
}
}
}

View File

@@ -0,0 +1,69 @@
using Content.Client.White.Trail.Line.Manager;
using Robust.Client.Graphics;
using Robust.Client.ResourceManagement;
using Robust.Shared.Enums;
using Robust.Shared.Prototypes;
namespace Content.Client.White.Trail;
public sealed class TrailOverlay : Overlay
{
private readonly IPrototypeManager _protoManager;
private readonly IResourceCache _cache;
private readonly ITrailLineManager _lineManager;
private readonly Dictionary<string, ShaderInstance?> _shaderDict;
private readonly Dictionary<string, Texture?> _textureDict;
public override OverlaySpace Space => OverlaySpace.WorldSpaceEntities;
public TrailOverlay(
IPrototypeManager protoManager,
IResourceCache cache,
ITrailLineManager lineManager
)
{
_protoManager = protoManager;
_cache = cache;
_lineManager = lineManager;
_shaderDict = new Dictionary<string, ShaderInstance?>();
_textureDict = new Dictionary<string, Texture?>();
ZIndex = (int) Shared.DrawDepth.DrawDepth.Effects;
}
protected override void Draw(in OverlayDrawArgs args)
{
var handle = args.WorldHandle;
foreach (var item in _lineManager.Lines)
{
item.Render(handle, GetCachedTexture(item.Settings.TexurePath ?? ""));
}
}
//влепить на ети два метода мемори кеш со слайдинг експирейшоном вместо дикта если проблемы будут
private ShaderInstance? GetCachedShader(string id)
{
if (_shaderDict.TryGetValue(id, out var shader))
return shader;
if (_protoManager.TryIndex<ShaderPrototype>(id, out var shaderRes))
shader = shaderRes?.InstanceUnique();
_shaderDict.Add(id, shader);
return shader;
}
private Texture? GetCachedTexture(string path)
{
if (_textureDict.TryGetValue(path, out var texture))
return texture;
if (_cache.TryGetResource<TextureResource>(path, out var texRes))
texture = texRes;
_textureDict.Add(path, texture);
return texture;
}
}

View File

@@ -0,0 +1,76 @@
using Content.Client.White.Trail.Line.Manager;
using Content.Shared.White.Trail;
using Robust.Client.Graphics;
using Robust.Client.ResourceManagement;
using Robust.Shared.GameStates;
using Robust.Shared.Map;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
namespace Content.Client.White.Trail;
public sealed class TrailSystem : EntitySystem
{
[Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly ITrailLineManager _lineManager = default!;
[Dependency] private readonly SharedTransformSystem _transformSystem = default!;
public override void Initialize()
{
base.Initialize();
IoCManager.Resolve<IOverlayManager>().AddOverlay(
new TrailOverlay(
IoCManager.Resolve<IPrototypeManager>(),
IoCManager.Resolve<IResourceCache>(),
_lineManager
));
SubscribeLocalEvent<TrailComponent, MoveEvent>(OnTrailMove);
SubscribeLocalEvent<TrailComponent, ComponentRemove>(OnTrailRemove);
SubscribeLocalEvent<TrailComponent, ComponentHandleState>(OnHandleState);
}
private void OnHandleState(EntityUid uid, TrailComponent component, ref ComponentHandleState args)
{
if (args.Current is not TrailComponentState state)
return;
TrailSettings.Inject(component, state.Settings);
}
private void OnTrailRemove(EntityUid uid, TrailComponent comp, ComponentRemove args)
{
_lineManager.Detach(comp);
}
private void OnTrailMove(EntityUid uid, TrailComponent comp, ref MoveEvent args)
{
if (comp.СreationMethod != SegmentCreationMethod.OnMove || _gameTiming.InPrediction)
return;
TryCreateSegment(comp, args.Component);
}
private void TryCreateSegment(TrailComponent comp, TransformComponent xform)
{
if (xform.MapID == MapId.Nullspace)
return;
comp.TrailLine ??= _lineManager.CreateTrail(comp, xform.MapID);
comp.TrailLine.TryCreateSegment(_transformSystem.GetWorldPositionRotation(xform), xform.MapID);
}
public override void FrameUpdate(float frameTime)
{
base.FrameUpdate(frameTime);
_lineManager.Update(frameTime);
foreach (var (comp, xform) in EntityQuery<TrailComponent, TransformComponent>())
{
if (comp.СreationMethod == SegmentCreationMethod.OnFrameUpdate)
TryCreateSegment(comp, xform);
}
}
}