Merge branch 'lazers-and-hop-console' into great-merge
This commit is contained in:
@@ -42,7 +42,9 @@ namespace Content.Client.Access.UI
|
|||||||
};
|
};
|
||||||
|
|
||||||
_window.CrewManifestButton.OnPressed += _ => SendMessage(new CrewManifestOpenUiMessage());
|
_window.CrewManifestButton.OnPressed += _ => SendMessage(new CrewManifestOpenUiMessage());
|
||||||
_window.PrivilegedIdButton.OnPressed += _ => SendMessage(new ItemSlotButtonPressedEvent(PrivilegedIdCardSlotId));
|
_window.PrivilegedIdButton.OnPressed +=
|
||||||
|
_ => SendMessage(new ItemSlotButtonPressedEvent(PrivilegedIdCardSlotId));
|
||||||
|
|
||||||
_window.TargetIdButton.OnPressed += _ => SendMessage(new ItemSlotButtonPressedEvent(TargetIdCardSlotId));
|
_window.TargetIdButton.OnPressed += _ => SendMessage(new ItemSlotButtonPressedEvent(TargetIdCardSlotId));
|
||||||
|
|
||||||
_window.OnClose += Close;
|
_window.OnClose += Close;
|
||||||
@@ -65,7 +67,12 @@ namespace Content.Client.Access.UI
|
|||||||
_window?.UpdateState(castState);
|
_window?.UpdateState(castState);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SubmitData(string newFullName, string newJobTitle, List<string> newAccessList, string newJobPrototype)
|
public void SubmitData(
|
||||||
|
string newFullName,
|
||||||
|
string newJobTitle,
|
||||||
|
List<string> newAccessList,
|
||||||
|
string newJobPrototype,
|
||||||
|
string? newJobIcon)
|
||||||
{
|
{
|
||||||
if (newFullName.Length > MaxFullNameLength)
|
if (newFullName.Length > MaxFullNameLength)
|
||||||
newFullName = newFullName[..MaxFullNameLength];
|
newFullName = newFullName[..MaxFullNameLength];
|
||||||
@@ -77,7 +84,8 @@ namespace Content.Client.Access.UI
|
|||||||
newFullName,
|
newFullName,
|
||||||
newJobTitle,
|
newJobTitle,
|
||||||
newAccessList,
|
newAccessList,
|
||||||
newJobPrototype));
|
newJobPrototype,
|
||||||
|
newJobIcon));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,11 +4,11 @@
|
|||||||
<GridContainer Columns="2">
|
<GridContainer Columns="2">
|
||||||
<GridContainer Columns="3" HorizontalExpand="True">
|
<GridContainer Columns="3" HorizontalExpand="True">
|
||||||
<Label Text="{Loc 'id-card-console-window-privileged-id'}" />
|
<Label Text="{Loc 'id-card-console-window-privileged-id'}" />
|
||||||
<Button Name="PrivilegedIdButton" Access="Public"/>
|
<Button Name="PrivilegedIdButton" Access="Public" />
|
||||||
<Label Name="PrivilegedIdLabel" />
|
<Label Name="PrivilegedIdLabel" />
|
||||||
|
|
||||||
<Label Text="{Loc 'id-card-console-window-target-id'}" />
|
<Label Text="{Loc 'id-card-console-window-target-id'}" />
|
||||||
<Button Name="TargetIdButton" Access="Public"/>
|
<Button Name="TargetIdButton" Access="Public" />
|
||||||
<Label Name="TargetIdLabel" />
|
<Label Name="TargetIdLabel" />
|
||||||
</GridContainer>
|
</GridContainer>
|
||||||
<BoxContainer Orientation="Vertical">
|
<BoxContainer Orientation="Vertical">
|
||||||
@@ -35,5 +35,13 @@
|
|||||||
<!-- Access level buttons are added here by the C# code -->
|
<!-- Access level buttons are added here by the C# code -->
|
||||||
|
|
||||||
</GridContainer>
|
</GridContainer>
|
||||||
|
<!-- WD EDIT -->
|
||||||
|
<GridContainer Name="CurrentJobIcon" Columns="2">
|
||||||
|
<Label Text="Текущая выбранная иконка для роли: " />
|
||||||
|
</GridContainer>
|
||||||
|
<GridContainer Name="JobIconsGrid" Columns="10" HorizontalAlignment="Center">
|
||||||
|
<!-- Job icon buttons are generated in the code -->
|
||||||
|
</GridContainer>
|
||||||
|
<!-- WD EDIT END -->
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
</DefaultWindow>
|
</DefaultWindow>
|
||||||
|
|||||||
@@ -1,12 +1,16 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Numerics;
|
||||||
using Content.Shared.Access;
|
using Content.Shared.Access;
|
||||||
|
using Content.Shared.Access.Components;
|
||||||
using Content.Shared.Access.Systems;
|
using Content.Shared.Access.Systems;
|
||||||
using Content.Shared.Roles;
|
using Content.Shared.Roles;
|
||||||
using Robust.Client.AutoGenerated;
|
using Robust.Client.AutoGenerated;
|
||||||
|
using Robust.Client.ResourceManagement;
|
||||||
using Robust.Client.UserInterface.Controls;
|
using Robust.Client.UserInterface.Controls;
|
||||||
using Robust.Client.UserInterface.CustomControls;
|
using Robust.Client.UserInterface.CustomControls;
|
||||||
using Robust.Client.UserInterface.XAML;
|
using Robust.Client.UserInterface.XAML;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
using static Content.Shared.Access.Components.IdCardConsoleComponent;
|
using static Content.Shared.Access.Components.IdCardConsoleComponent;
|
||||||
|
|
||||||
namespace Content.Client.Access.UI
|
namespace Content.Client.Access.UI
|
||||||
@@ -16,18 +20,24 @@ namespace Content.Client.Access.UI
|
|||||||
{
|
{
|
||||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||||
[Dependency] private readonly ILogManager _logManager = default!;
|
[Dependency] private readonly ILogManager _logManager = default!;
|
||||||
|
[Dependency] private readonly IResourceCache _resource = default!; //WD-EDIT
|
||||||
|
[Dependency] private readonly IEntityManager _entityManager = default!; //WD-EDIT
|
||||||
private readonly ISawmill _logMill = default!;
|
private readonly ISawmill _logMill = default!;
|
||||||
|
|
||||||
private readonly IdCardConsoleBoundUserInterface _owner;
|
private readonly IdCardConsoleBoundUserInterface _owner;
|
||||||
|
|
||||||
private readonly Dictionary<string, Button> _accessButtons = new();
|
private readonly Dictionary<string, Button> _accessButtons = new();
|
||||||
|
private readonly Dictionary<string, TextureButton> _jobIconButtons = new(); //WD-EDIT
|
||||||
private readonly List<string> _jobPrototypeIds = new();
|
private readonly List<string> _jobPrototypeIds = new();
|
||||||
|
|
||||||
private string? _lastFullName;
|
private string? _lastFullName;
|
||||||
private string? _lastJobTitle;
|
private string? _lastJobTitle;
|
||||||
private string? _lastJobProto;
|
private string? _lastJobProto;
|
||||||
|
private string? _lastJobIcon; //WD-EDIT
|
||||||
|
|
||||||
public IdCardConsoleWindow(IdCardConsoleBoundUserInterface owner, IPrototypeManager prototypeManager,
|
public IdCardConsoleWindow(
|
||||||
|
IdCardConsoleBoundUserInterface owner,
|
||||||
|
IPrototypeManager prototypeManager,
|
||||||
List<ProtoId<AccessLevelPrototype>> accessLevels)
|
List<ProtoId<AccessLevelPrototype>> accessLevels)
|
||||||
{
|
{
|
||||||
RobustXamlLoader.Load(this);
|
RobustXamlLoader.Load(this);
|
||||||
@@ -41,6 +51,7 @@ namespace Content.Client.Access.UI
|
|||||||
{
|
{
|
||||||
FullNameSaveButton.Disabled = FullNameSaveButton.Text == _lastFullName;
|
FullNameSaveButton.Disabled = FullNameSaveButton.Text == _lastFullName;
|
||||||
};
|
};
|
||||||
|
|
||||||
FullNameSaveButton.OnPressed += _ => SubmitData();
|
FullNameSaveButton.OnPressed += _ => SubmitData();
|
||||||
|
|
||||||
JobTitleLineEdit.OnTextEntered += _ => SubmitData();
|
JobTitleLineEdit.OnTextEntered += _ => SubmitData();
|
||||||
@@ -48,6 +59,7 @@ namespace Content.Client.Access.UI
|
|||||||
{
|
{
|
||||||
JobTitleSaveButton.Disabled = JobTitleLineEdit.Text == _lastJobTitle;
|
JobTitleSaveButton.Disabled = JobTitleLineEdit.Text == _lastJobTitle;
|
||||||
};
|
};
|
||||||
|
|
||||||
JobTitleSaveButton.OnPressed += _ => SubmitData();
|
JobTitleSaveButton.OnPressed += _ => SubmitData();
|
||||||
|
|
||||||
var jobs = _prototypeManager.EnumeratePrototypes<JobPrototype>().ToList();
|
var jobs = _prototypeManager.EnumeratePrototypes<JobPrototype>().ToList();
|
||||||
@@ -69,7 +81,7 @@ namespace Content.Client.Access.UI
|
|||||||
|
|
||||||
foreach (var access in accessLevels)
|
foreach (var access in accessLevels)
|
||||||
{
|
{
|
||||||
if (!prototypeManager.TryIndex<AccessLevelPrototype>(access, out var accessLevel))
|
if (!prototypeManager.TryIndex(access, out var accessLevel))
|
||||||
{
|
{
|
||||||
_logMill.Error($"Unable to find accesslevel for {access}");
|
_logMill.Error($"Unable to find accesslevel for {access}");
|
||||||
continue;
|
continue;
|
||||||
@@ -78,19 +90,50 @@ namespace Content.Client.Access.UI
|
|||||||
var newButton = new Button
|
var newButton = new Button
|
||||||
{
|
{
|
||||||
Text = GetAccessLevelName(accessLevel),
|
Text = GetAccessLevelName(accessLevel),
|
||||||
ToggleMode = true,
|
ToggleMode = true
|
||||||
};
|
};
|
||||||
|
|
||||||
_accessButtons.Add(accessLevel.ID, newButton);
|
_accessButtons.Add(accessLevel.ID, newButton);
|
||||||
newButton.OnPressed += _ => SubmitData();
|
newButton.OnPressed += _ => SubmitData();
|
||||||
buttonsToAdd.Add(newButton);
|
buttonsToAdd.Add(newButton);
|
||||||
}
|
}
|
||||||
|
|
||||||
buttonsToAdd.Sort((x,y) => string.Compare(x.Text, y.Text, StringComparison.Ordinal));
|
buttonsToAdd.Sort((x, y) => string.Compare(x.Text, y.Text, StringComparison.Ordinal));
|
||||||
|
|
||||||
foreach (var button in buttonsToAdd)
|
foreach (var button in buttonsToAdd)
|
||||||
{
|
{
|
||||||
AccessLevelGrid.AddChild(button);
|
AccessLevelGrid.AddChild(button);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//WD-EDIT
|
||||||
|
if (!_entityManager.TryGetComponent<IdCardConsoleComponent>(owner.Owner, out var idConsoleComponent))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var path = new ResPath("/Textures/Interface/Misc/job_icons.rsi");
|
||||||
|
_resource.TryGetResource(path, out RSIResource? rsi);
|
||||||
|
if (rsi == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
foreach (var jobIcon in idConsoleComponent.JobIcons)
|
||||||
|
{
|
||||||
|
var newButton = new TextureButton
|
||||||
|
{
|
||||||
|
TextureNormal = rsi.RSI.TryGetState(jobIcon, out var state) ? state.Frame0
|
||||||
|
: rsi.RSI.TryGetState("CustomId", out var customState) ? customState.Frame0
|
||||||
|
: null,
|
||||||
|
Scale = new Vector2(5, 5)
|
||||||
|
};
|
||||||
|
|
||||||
|
_jobIconButtons.Add(jobIcon, newButton);
|
||||||
|
newButton.OnPressed += _ =>
|
||||||
|
{
|
||||||
|
_lastJobIcon = jobIcon;
|
||||||
|
SubmitData();
|
||||||
|
};
|
||||||
|
|
||||||
|
JobIconsGrid.AddChild(newButton);
|
||||||
|
}
|
||||||
|
//WD-EDIT
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetAccessLevelName(AccessLevelPrototype prototype)
|
private static string GetAccessLevelName(AccessLevelPrototype prototype)
|
||||||
@@ -149,6 +192,7 @@ namespace Content.Client.Access.UI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_lastJobIcon = job.Icon;
|
||||||
SubmitData();
|
SubmitData();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,11 +239,11 @@ namespace Content.Client.Access.UI
|
|||||||
foreach (var (accessName, button) in _accessButtons)
|
foreach (var (accessName, button) in _accessButtons)
|
||||||
{
|
{
|
||||||
button.Disabled = !interfaceEnabled;
|
button.Disabled = !interfaceEnabled;
|
||||||
if (interfaceEnabled)
|
if (!interfaceEnabled)
|
||||||
{
|
continue;
|
||||||
|
|
||||||
button.Pressed = state.TargetIdAccessList?.Contains(accessName) ?? false;
|
button.Pressed = state.TargetIdAccessList?.Contains(accessName) ?? false;
|
||||||
button.Disabled = (!state.AllowedModifyAccessList?.Contains(accessName)) ?? true;
|
button.Disabled = !state.AllowedModifyAccessList?.Contains(accessName) ?? true;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var jobIndex = _jobPrototypeIds.IndexOf(state.TargetIdJobPrototype);
|
var jobIndex = _jobPrototypeIds.IndexOf(state.TargetIdJobPrototype);
|
||||||
@@ -208,6 +252,37 @@ namespace Content.Client.Access.UI
|
|||||||
JobPresetOptionButton.SelectId(jobIndex);
|
JobPresetOptionButton.SelectId(jobIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//WD-EDIT
|
||||||
|
if (_resource.TryGetResource(new ResPath("/Textures/Interface/Misc/job_icons.rsi"), out RSIResource? rsi))
|
||||||
|
{
|
||||||
|
CurrentJobIcon.RemoveAllChildren();
|
||||||
|
var newLabel = new Label
|
||||||
|
{
|
||||||
|
Text = "Текущая выбранная иконка для роли: "
|
||||||
|
};
|
||||||
|
|
||||||
|
CurrentJobIcon.AddChild(newLabel);
|
||||||
|
var newIcon = new TextureRect
|
||||||
|
{
|
||||||
|
Texture = rsi.RSI.TryGetState(state.TargetIdJobIcon?.Replace("JobIcon", ""), out var iconState)
|
||||||
|
? iconState.Frame0
|
||||||
|
: rsi.RSI.TryGetState("CustomId", out var customState)
|
||||||
|
? customState.Frame0
|
||||||
|
: null,
|
||||||
|
|
||||||
|
TextureScale = new Vector2(4, 4)
|
||||||
|
};
|
||||||
|
|
||||||
|
CurrentJobIcon.AddChild(newIcon);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var (jobIcon, button) in _jobIconButtons)
|
||||||
|
{
|
||||||
|
button.Disabled = !interfaceEnabled;
|
||||||
|
button.Pressed = state.TargetIdJobIcon == jobIcon;
|
||||||
|
}
|
||||||
|
//WD-EDIT
|
||||||
|
|
||||||
_lastFullName = state.TargetIdFullName;
|
_lastFullName = state.TargetIdFullName;
|
||||||
_lastJobTitle = state.TargetIdJobTitle;
|
_lastJobTitle = state.TargetIdJobTitle;
|
||||||
_lastJobProto = state.TargetIdJobPrototype;
|
_lastJobProto = state.TargetIdJobPrototype;
|
||||||
@@ -224,7 +299,8 @@ namespace Content.Client.Access.UI
|
|||||||
JobTitleLineEdit.Text,
|
JobTitleLineEdit.Text,
|
||||||
// Iterate over the buttons dictionary, filter by `Pressed`, only get key from the key/value pair
|
// Iterate over the buttons dictionary, filter by `Pressed`, only get key from the key/value pair
|
||||||
_accessButtons.Where(x => x.Value.Pressed).Select(x => x.Key).ToList(),
|
_accessButtons.Where(x => x.Value.Pressed).Select(x => x.Key).ToList(),
|
||||||
jobProtoDirty ? _jobPrototypeIds[JobPresetOptionButton.SelectedId] : string.Empty);
|
jobProtoDirty ? _jobPrototypeIds[JobPresetOptionButton.SelectedId] : string.Empty,
|
||||||
|
_lastJobIcon);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Folder Include="Spawners\" />
|
<Folder Include="Spawners\" />
|
||||||
|
<Folder Include="White\Trail\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="..\RobustToolbox\MSBuild\Robust.Properties.targets" />
|
<Import Project="..\RobustToolbox\MSBuild\Robust.Properties.targets" />
|
||||||
<Import Project="..\RobustToolbox\MSBuild\XamlIL.targets" />
|
<Import Project="..\RobustToolbox\MSBuild\XamlIL.targets" />
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ using Content.Client.White.JoinQueue;
|
|||||||
using Content.Client.White.Jukebox;
|
using Content.Client.White.Jukebox;
|
||||||
using Content.Client.White.Sponsors;
|
using Content.Client.White.Sponsors;
|
||||||
using Content.Client.White.Stalin;
|
using Content.Client.White.Stalin;
|
||||||
|
using Content.Client.White.Trail.Line.Manager;
|
||||||
using Content.Client.White.TTS;
|
using Content.Client.White.TTS;
|
||||||
using Content.Shared.Administration.Managers;
|
using Content.Shared.Administration.Managers;
|
||||||
|
|
||||||
@@ -57,6 +58,7 @@ namespace Content.Client.IoC
|
|||||||
IoCManager.Register<StalinManager>();
|
IoCManager.Register<StalinManager>();
|
||||||
IoCManager.Register<ClientJukeboxSongsSyncManager>();
|
IoCManager.Register<ClientJukeboxSongsSyncManager>();
|
||||||
IoCManager.Register<TTSManager>();
|
IoCManager.Register<TTSManager>();
|
||||||
|
IoCManager.Register<ITrailLineManager, TrailSplineManager>();
|
||||||
//WD-EDIT
|
//WD-EDIT
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
15
Content.Client/White/Trail/Line/ITrailLine.cs
Normal file
15
Content.Client/White/Trail/Line/ITrailLine.cs
Normal 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);
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
namespace Content.Client.White.Trail.Line.Manager;
|
||||||
|
|
||||||
|
public interface ITrailLineHolder
|
||||||
|
{
|
||||||
|
public ITrailLine? TrailLine { get; set; }
|
||||||
|
}
|
||||||
15
Content.Client/White/Trail/Line/Manager/ITrailLineManager.cs
Normal file
15
Content.Client/White/Trail/Line/Manager/ITrailLineManager.cs
Normal 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);
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
183
Content.Client/White/Trail/Line/TrailSpline.cs
Normal file
183
Content.Client/White/Trail/Line/TrailSpline.cs
Normal 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; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -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()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
56
Content.Client/White/Trail/TrailComponent.cs
Normal file
56
Content.Client/White/Trail/TrailComponent.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
69
Content.Client/White/Trail/TrailOverlay.cs
Normal file
69
Content.Client/White/Trail/TrailOverlay.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
76
Content.Client/White/Trail/TrailSystem.cs
Normal file
76
Content.Client/White/Trail/TrailSystem.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -45,7 +45,8 @@ public sealed class IdCardConsoleSystem : SharedIdCardConsoleSystem
|
|||||||
if (args.Session.AttachedEntity is not { Valid: true } player)
|
if (args.Session.AttachedEntity is not { Valid: true } player)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
TryWriteToTargetId(uid, args.FullName, args.JobTitle, args.AccessList, args.JobPrototype, player, component);
|
TryWriteToTargetId(uid, args.FullName, args.JobTitle, args.AccessList, args.JobPrototype, args.SelectedIcon,
|
||||||
|
player, component);
|
||||||
|
|
||||||
UpdateUserInterface(uid, component, args);
|
UpdateUserInterface(uid, component, args);
|
||||||
}
|
}
|
||||||
@@ -77,7 +78,8 @@ public sealed class IdCardConsoleSystem : SharedIdCardConsoleSystem
|
|||||||
possibleAccess,
|
possibleAccess,
|
||||||
string.Empty,
|
string.Empty,
|
||||||
privilegedIdName,
|
privilegedIdName,
|
||||||
string.Empty);
|
string.Empty,
|
||||||
|
"JobIconNoId"); // WD EDIt
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -85,12 +87,16 @@ public sealed class IdCardConsoleSystem : SharedIdCardConsoleSystem
|
|||||||
var targetAccessComponent = EntityManager.GetComponent<AccessComponent>(targetId);
|
var targetAccessComponent = EntityManager.GetComponent<AccessComponent>(targetId);
|
||||||
|
|
||||||
var jobProto = string.Empty;
|
var jobProto = string.Empty;
|
||||||
|
var jobIcon = string.Empty; //WD-EDIT
|
||||||
|
|
||||||
if (_station.GetOwningStation(uid) is { } station
|
if (_station.GetOwningStation(uid) is { } station
|
||||||
&& EntityManager.TryGetComponent<StationRecordKeyStorageComponent>(targetId, out var keyStorage)
|
&& EntityManager.TryGetComponent<StationRecordKeyStorageComponent>(targetId, out var keyStorage)
|
||||||
&& keyStorage.Key != null
|
&& keyStorage.Key != null
|
||||||
&& _record.TryGetRecord<GeneralStationRecord>(station, keyStorage.Key.Value, out var record))
|
&& _record.TryGetRecord<GeneralStationRecord>(station, keyStorage.Key.Value, out var record))
|
||||||
{
|
{
|
||||||
jobProto = record.JobPrototype;
|
jobProto = record.JobPrototype;
|
||||||
|
jobIcon = record.JobIcon;
|
||||||
|
Dirty(targetId, targetIdComponent);
|
||||||
}
|
}
|
||||||
|
|
||||||
newState = new IdCardConsoleBoundUserInterfaceState(
|
newState = new IdCardConsoleBoundUserInterfaceState(
|
||||||
@@ -103,7 +109,9 @@ public sealed class IdCardConsoleSystem : SharedIdCardConsoleSystem
|
|||||||
possibleAccess,
|
possibleAccess,
|
||||||
jobProto,
|
jobProto,
|
||||||
privilegedIdName,
|
privilegedIdName,
|
||||||
EntityManager.GetComponent<MetaDataComponent>(targetId).EntityName);
|
EntityManager.GetComponent<MetaDataComponent>(targetId).EntityName,
|
||||||
|
jobIcon
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
_userInterface.TrySetUiState(uid, IdCardConsoleUiKey.Key, newState);
|
_userInterface.TrySetUiState(uid, IdCardConsoleUiKey.Key, newState);
|
||||||
@@ -113,11 +121,14 @@ public sealed class IdCardConsoleSystem : SharedIdCardConsoleSystem
|
|||||||
/// Called whenever an access button is pressed, adding or removing that access from the target ID card.
|
/// Called whenever an access button is pressed, adding or removing that access from the target ID card.
|
||||||
/// Writes data passed from the UI into the ID stored in <see cref="IdCardConsoleComponent.TargetIdSlot"/>, if present.
|
/// Writes data passed from the UI into the ID stored in <see cref="IdCardConsoleComponent.TargetIdSlot"/>, if present.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void TryWriteToTargetId(EntityUid uid,
|
/// WD-INFO: Also called when icon is changed, to update it on ID.
|
||||||
|
private void TryWriteToTargetId(
|
||||||
|
EntityUid uid,
|
||||||
string newFullName,
|
string newFullName,
|
||||||
string newJobTitle,
|
string newJobTitle,
|
||||||
List<string> newAccessList,
|
List<string> newAccessList,
|
||||||
string newJobProto,
|
string newJobProto,
|
||||||
|
string? newJobIcon,
|
||||||
EntityUid player,
|
EntityUid player,
|
||||||
IdCardConsoleComponent? component = null)
|
IdCardConsoleComponent? component = null)
|
||||||
{
|
{
|
||||||
@@ -127,6 +138,13 @@ public sealed class IdCardConsoleSystem : SharedIdCardConsoleSystem
|
|||||||
if (component.TargetIdSlot.Item is not { Valid: true } targetId || !PrivilegedIdIsAuthorized(uid, component))
|
if (component.TargetIdSlot.Item is not { Valid: true } targetId || !PrivilegedIdIsAuthorized(uid, component))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
//WD-EDIT
|
||||||
|
if (TryComp<IdCardComponent>(targetId, out var idCardComponent) && newJobIcon != null)
|
||||||
|
{
|
||||||
|
idCardComponent.JobIcon = newJobIcon;
|
||||||
|
}
|
||||||
|
//WD-EDIT
|
||||||
|
|
||||||
_idCard.TryChangeFullName(targetId, newFullName, player: player);
|
_idCard.TryChangeFullName(targetId, newFullName, player: player);
|
||||||
_idCard.TryChangeJobTitle(targetId, newJobTitle, player: player);
|
_idCard.TryChangeJobTitle(targetId, newJobTitle, player: player);
|
||||||
|
|
||||||
@@ -148,7 +166,10 @@ public sealed class IdCardConsoleSystem : SharedIdCardConsoleSystem
|
|||||||
var privilegedId = component.PrivilegedIdSlot.Item;
|
var privilegedId = component.PrivilegedIdSlot.Item;
|
||||||
|
|
||||||
if (oldTags.SequenceEqual(newAccessList))
|
if (oldTags.SequenceEqual(newAccessList))
|
||||||
|
{
|
||||||
|
UpdateStationRecord(uid, targetId, newFullName, newJobTitle, job, newJobIcon); //WD-EDIT
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// I hate that C# doesn't have an option for this and don't desire to write this out the hard way.
|
// I hate that C# doesn't have an option for this and don't desire to write this out the hard way.
|
||||||
// var difference = newAccessList.Difference(oldTags);
|
// var difference = newAccessList.Difference(oldTags);
|
||||||
@@ -170,7 +191,7 @@ public sealed class IdCardConsoleSystem : SharedIdCardConsoleSystem
|
|||||||
_adminLogger.Add(LogType.Action, LogImpact.Medium,
|
_adminLogger.Add(LogType.Action, LogImpact.Medium,
|
||||||
$"{ToPrettyString(player):player} has modified {ToPrettyString(targetId):entity} with the following accesses: [{string.Join(", ", addedTags.Union(removedTags))}] [{string.Join(", ", newAccessList)}]");
|
$"{ToPrettyString(player):player} has modified {ToPrettyString(targetId):entity} with the following accesses: [{string.Join(", ", addedTags.Union(removedTags))}] [{string.Join(", ", newAccessList)}]");
|
||||||
|
|
||||||
UpdateStationRecord(uid, targetId, newFullName, newJobTitle, job);
|
UpdateStationRecord(uid, targetId, newFullName, newJobTitle, job, newJobIcon);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -191,7 +212,13 @@ public sealed class IdCardConsoleSystem : SharedIdCardConsoleSystem
|
|||||||
return privilegedId != null && _accessReader.IsAllowed(privilegedId.Value, uid, reader);
|
return privilegedId != null && _accessReader.IsAllowed(privilegedId.Value, uid, reader);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateStationRecord(EntityUid uid, EntityUid targetId, string newFullName, string newJobTitle, JobPrototype? newJobProto)
|
private void UpdateStationRecord(
|
||||||
|
EntityUid uid,
|
||||||
|
EntityUid targetId,
|
||||||
|
string newFullName,
|
||||||
|
string newJobTitle,
|
||||||
|
JobPrototype? newJobProto,
|
||||||
|
string? newJobIcon) // WD EDIT
|
||||||
{
|
{
|
||||||
if (_station.GetOwningStation(uid) is not { } station
|
if (_station.GetOwningStation(uid) is not { } station
|
||||||
|| !EntityManager.TryGetComponent<StationRecordKeyStorageComponent>(targetId, out var keyStorage)
|
|| !EntityManager.TryGetComponent<StationRecordKeyStorageComponent>(targetId, out var keyStorage)
|
||||||
@@ -210,6 +237,9 @@ public sealed class IdCardConsoleSystem : SharedIdCardConsoleSystem
|
|||||||
record.JobIcon = newJobProto.Icon;
|
record.JobIcon = newJobProto.Icon;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(newJobIcon))
|
||||||
|
record.JobIcon = newJobIcon;
|
||||||
|
|
||||||
_record.Synchronize(station);
|
_record.Synchronize(station);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,10 @@
|
|||||||
using Content.Server.Power.Components;
|
using Content.Server.Power.Components;
|
||||||
using Content.Shared.Damage;
|
using Content.Shared.Damage;
|
||||||
using Content.Shared.Damage.Events;
|
using Content.Shared.Damage.Events;
|
||||||
using Content.Shared.FixedPoint;
|
|
||||||
using Content.Shared.Interaction.Events;
|
using Content.Shared.Interaction.Events;
|
||||||
using Content.Shared.Projectiles;
|
|
||||||
using Content.Shared.Weapons.Ranged;
|
using Content.Shared.Weapons.Ranged;
|
||||||
using Content.Shared.Weapons.Ranged.Components;
|
using Content.Shared.Weapons.Ranged.Components;
|
||||||
using Robust.Server.Audio;
|
using Robust.Server.Audio;
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
|
|
||||||
namespace Content.Server.Weapons.Ranged.Systems;
|
namespace Content.Server.Weapons.Ranged.Systems;
|
||||||
|
|
||||||
@@ -41,28 +38,30 @@ public sealed partial class GunSystem
|
|||||||
if (!TryComp<GunComponent>(uid, out var gun))
|
if (!TryComp<GunComponent>(uid, out var gun))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (component.CurrentMode == EnergyModes.Stun)
|
switch (component.CurrentMode)
|
||||||
{
|
{
|
||||||
|
case EnergyModes.Stun:
|
||||||
component.InStun = false;
|
component.InStun = false;
|
||||||
gun.SoundGunshot = component.HitscanSound;
|
|
||||||
component.CurrentMode = EnergyModes.Laser;
|
component.CurrentMode = EnergyModes.Laser;
|
||||||
component.FireCost = component.HitscanFireCost;
|
component.FireCost = component.LaserFireCost;
|
||||||
|
gun.SoundGunshot = component.LaserSound;
|
||||||
|
gun.ProjectileSpeed = component.LaserProjectileSpeed;
|
||||||
_audio.PlayPvs(component.ToggleSound, args.User);
|
_audio.PlayPvs(component.ToggleSound, args.User);
|
||||||
}
|
break;
|
||||||
else if (component.CurrentMode == EnergyModes.Laser)
|
case EnergyModes.Laser:
|
||||||
{
|
|
||||||
component.InStun = true;
|
component.InStun = true;
|
||||||
gun.SoundGunshot = component.ProjSound;
|
|
||||||
component.CurrentMode = EnergyModes.Stun;
|
component.CurrentMode = EnergyModes.Stun;
|
||||||
component.FireCost = component.ProjFireCost;
|
component.FireCost = component.StunFireCost;
|
||||||
|
gun.SoundGunshot = component.StunSound;
|
||||||
|
gun.ProjectileSpeed = component.StunProjectileSpeed;
|
||||||
_audio.PlayPvs(component.ToggleSound, args.User);
|
_audio.PlayPvs(component.ToggleSound, args.User);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateShots(uid, component);
|
UpdateShots(uid, component);
|
||||||
UpdateTwoModeAppearance(uid, component);
|
UpdateTwoModeAppearance(uid, component);
|
||||||
UpdateBatteryAppearance(uid, component);
|
UpdateBatteryAppearance(uid, component);
|
||||||
UpdateAmmoCount(uid);
|
UpdateAmmoCount(uid);
|
||||||
Dirty(gun);
|
|
||||||
Dirty(component);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnBatteryStartup(EntityUid uid, BatteryAmmoProviderComponent component, ComponentStartup args)
|
private void OnBatteryStartup(EntityUid uid, BatteryAmmoProviderComponent component, ComponentStartup args)
|
||||||
@@ -70,7 +69,10 @@ public sealed partial class GunSystem
|
|||||||
UpdateShots(uid, component);
|
UpdateShots(uid, component);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnBatteryChargeChange(EntityUid uid, BatteryAmmoProviderComponent component, ref ChargeChangedEvent args)
|
private void OnBatteryChargeChange(
|
||||||
|
EntityUid uid,
|
||||||
|
BatteryAmmoProviderComponent component,
|
||||||
|
ref ChargeChangedEvent args)
|
||||||
{
|
{
|
||||||
UpdateShots(uid, component, args.Charge, args.MaxCharge);
|
UpdateShots(uid, component, args.Charge, args.MaxCharge);
|
||||||
}
|
}
|
||||||
@@ -90,7 +92,7 @@ public sealed partial class GunSystem
|
|||||||
|
|
||||||
if (component.Shots != shots || component.Capacity != maxShots)
|
if (component.Shots != shots || component.Capacity != maxShots)
|
||||||
{
|
{
|
||||||
Dirty(component);
|
Dirty(uid, component);
|
||||||
}
|
}
|
||||||
|
|
||||||
component.Shots = shots;
|
component.Shots = shots;
|
||||||
@@ -98,78 +100,41 @@ public sealed partial class GunSystem
|
|||||||
UpdateBatteryAppearance(uid, component);
|
UpdateBatteryAppearance(uid, component);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnBatteryDamageExamine(EntityUid uid, BatteryAmmoProviderComponent component, ref DamageExamineEvent args)
|
private void OnBatteryDamageExamine(
|
||||||
|
EntityUid uid,
|
||||||
|
BatteryAmmoProviderComponent component,
|
||||||
|
ref DamageExamineEvent args)
|
||||||
{
|
{
|
||||||
var damageSpec = GetDamage(component);
|
var damageSpec = GetDamage(component);
|
||||||
|
|
||||||
if (damageSpec == null)
|
if (damageSpec == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
string? damageType;
|
var damageType = component switch
|
||||||
switch (component)
|
|
||||||
{
|
{
|
||||||
case HitscanBatteryAmmoProviderComponent:
|
HitscanBatteryAmmoProviderComponent => Loc.GetString("damage-hitscan"),
|
||||||
damageType = Loc.GetString("damage-hitscan");
|
ProjectileBatteryAmmoProviderComponent => Loc.GetString("damage-projectile"),
|
||||||
break;
|
TwoModeEnergyAmmoProviderComponent twoMode => Loc.GetString(twoMode.CurrentMode == EnergyModes.Stun
|
||||||
case ProjectileBatteryAmmoProviderComponent:
|
? "damage-projectile"
|
||||||
damageType = Loc.GetString("damage-projectile");
|
: "damage-hitscan"),
|
||||||
break;
|
_ => throw new ArgumentOutOfRangeException()
|
||||||
case TwoModeEnergyAmmoProviderComponent twoMode:
|
};
|
||||||
if (twoMode.CurrentMode == EnergyModes.Stun)
|
|
||||||
damageType = Loc.GetString("damage-projectile");
|
|
||||||
else
|
|
||||||
damageType = Loc.GetString("damage-hitscan");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new ArgumentOutOfRangeException();
|
|
||||||
}
|
|
||||||
|
|
||||||
_damageExamine.AddDamageExamine(args.Message, damageSpec, damageType);
|
_damageExamine.AddDamageExamine(args.Message, damageSpec, damageType);
|
||||||
}
|
}
|
||||||
|
|
||||||
private DamageSpecifier? GetDamage(BatteryAmmoProviderComponent component)
|
private DamageSpecifier? GetDamage(BatteryAmmoProviderComponent component)
|
||||||
{
|
{
|
||||||
if (component is ProjectileBatteryAmmoProviderComponent battery)
|
return component switch
|
||||||
{
|
{
|
||||||
if (ProtoManager.Index<EntityPrototype>(battery.Prototype).Components
|
HitscanBatteryAmmoProviderComponent hitscan =>
|
||||||
.TryGetValue(_factory.GetComponentName(typeof(ProjectileComponent)), out var projectile))
|
ProtoManager.Index<HitscanPrototype>(hitscan.Prototype).Damage,
|
||||||
{
|
ProjectileBatteryAmmoProviderComponent battery => GetProjectileDamage(battery.Prototype),
|
||||||
var p = (ProjectileComponent) projectile.Component;
|
TwoModeEnergyAmmoProviderComponent twoMode => GetProjectileDamage(twoMode.CurrentMode == EnergyModes.Laser
|
||||||
|
? twoMode.LaserPrototype
|
||||||
if (!p.Damage.Empty)
|
: twoMode.StunPrototype),
|
||||||
{
|
_ => null
|
||||||
return p.Damage;
|
};
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (component is HitscanBatteryAmmoProviderComponent hitscan)
|
|
||||||
{
|
|
||||||
return ProtoManager.Index<HitscanPrototype>(hitscan.Prototype).Damage;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (component is TwoModeEnergyAmmoProviderComponent twoMode)
|
|
||||||
{
|
|
||||||
if (twoMode.CurrentMode == EnergyModes.Stun)
|
|
||||||
{
|
|
||||||
if (ProtoManager.Index<EntityPrototype>(twoMode.ProjectilePrototype).Components
|
|
||||||
.TryGetValue(_factory.GetComponentName(typeof(ProjectileComponent)), out var projectile))
|
|
||||||
{
|
|
||||||
var p = (ProjectileComponent) projectile.Component;
|
|
||||||
|
|
||||||
if (p.Damage.Total > FixedPoint2.Zero)
|
|
||||||
{
|
|
||||||
return p.Damage;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return ProtoManager.Index<HitscanPrototype>(twoMode.HitscanPrototype).Damage;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void TakeCharge(EntityUid uid, BatteryAmmoProviderComponent component)
|
protected override void TakeCharge(EntityUid uid, BatteryAmmoProviderComponent component)
|
||||||
|
|||||||
51
Content.Server/White/Trail/TrailComponent.cs
Normal file
51
Content.Server/White/Trail/TrailComponent.cs
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
using System.Numerics;
|
||||||
|
using Content.Shared.White.Spline;
|
||||||
|
using Content.Shared.White.Trail;
|
||||||
|
using Vector4 = Robust.Shared.Maths.Vector4;
|
||||||
|
|
||||||
|
namespace Content.Server.White.Trail;
|
||||||
|
|
||||||
|
[RegisterComponent]
|
||||||
|
public sealed partial class TrailComponent : SharedTrailComponent
|
||||||
|
{
|
||||||
|
public TrailComponent()
|
||||||
|
{
|
||||||
|
var defaultTrail = TrailSettings.Default;
|
||||||
|
Scale = defaultTrail.Scale;
|
||||||
|
СreationDistanceThresholdSquared = defaultTrail.СreationDistanceThresholdSquared;
|
||||||
|
СreationMethod = defaultTrail.СreationMethod;
|
||||||
|
CreationOffset = defaultTrail.CreationOffset;
|
||||||
|
Gravity = defaultTrail.Gravity;
|
||||||
|
MaxRandomWalk = defaultTrail.MaxRandomWalk;
|
||||||
|
Lifetime = defaultTrail.Lifetime;
|
||||||
|
TexurePath = defaultTrail.TexurePath;
|
||||||
|
Gradient = defaultTrail.Gradient;
|
||||||
|
GradientIteratorType = defaultTrail.GradientIteratorType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Vector2 Gravity { get; set; }
|
||||||
|
|
||||||
|
public override float Lifetime { get; set; }
|
||||||
|
|
||||||
|
public override Vector2 MaxRandomWalk { get; set; }
|
||||||
|
|
||||||
|
public override Vector2 Scale { get; set; }
|
||||||
|
|
||||||
|
public override string? TexurePath { get; set; }
|
||||||
|
|
||||||
|
public override Vector2 CreationOffset { get; set; }
|
||||||
|
|
||||||
|
public override float СreationDistanceThresholdSquared { get; set; }
|
||||||
|
|
||||||
|
public override SegmentCreationMethod СreationMethod { get; set; }
|
||||||
|
|
||||||
|
public override Vector4[] Gradient { get; set; }
|
||||||
|
|
||||||
|
public override float LengthStep { get; set; }
|
||||||
|
|
||||||
|
public override Spline2DType SplineIteratorType { get; set; }
|
||||||
|
|
||||||
|
public override TrailSplineRendererType SplineRendererType { get; set; }
|
||||||
|
|
||||||
|
public override Spline4DType GradientIteratorType { get; set; }
|
||||||
|
}
|
||||||
21
Content.Server/White/Trail/TrailSystem.cs
Normal file
21
Content.Server/White/Trail/TrailSystem.cs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
using Content.Shared.White.Trail;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
|
|
||||||
|
namespace Content.Server.White.Trail;
|
||||||
|
|
||||||
|
public sealed class TrailSystem : EntitySystem
|
||||||
|
{
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<TrailComponent, ComponentGetState>(OnGetState);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnGetState(EntityUid uid, TrailComponent component, ref ComponentGetState args)
|
||||||
|
{
|
||||||
|
var settings = new TrailSettings();
|
||||||
|
TrailSettings.Inject(settings, component);
|
||||||
|
args.State = new TrailComponentState(settings);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -29,13 +29,20 @@ public sealed partial class IdCardConsoleComponent : Component
|
|||||||
public readonly string JobTitle;
|
public readonly string JobTitle;
|
||||||
public readonly List<string> AccessList;
|
public readonly List<string> AccessList;
|
||||||
public readonly string JobPrototype;
|
public readonly string JobPrototype;
|
||||||
|
public readonly string? SelectedIcon; //WD-EDIT
|
||||||
|
|
||||||
public WriteToTargetIdMessage(string fullName, string jobTitle, List<string> accessList, string jobPrototype)
|
public WriteToTargetIdMessage(
|
||||||
|
string fullName,
|
||||||
|
string jobTitle,
|
||||||
|
List<string> accessList,
|
||||||
|
string jobPrototype,
|
||||||
|
string? selectedIcon) //WD-EDIT (selectedIcon)
|
||||||
{
|
{
|
||||||
FullName = fullName;
|
FullName = fullName;
|
||||||
JobTitle = jobTitle;
|
JobTitle = jobTitle;
|
||||||
AccessList = accessList;
|
AccessList = accessList;
|
||||||
JobPrototype = jobPrototype;
|
JobPrototype = jobPrototype;
|
||||||
|
SelectedIcon = "JobIcon" + selectedIcon;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,9 +79,61 @@ public sealed partial class IdCardConsoleComponent : Component
|
|||||||
"Salvage",
|
"Salvage",
|
||||||
"Security",
|
"Security",
|
||||||
"Service",
|
"Service",
|
||||||
"Theatre",
|
"Theatre"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//WD-EDIT
|
||||||
|
[DataField("jobIcons")]
|
||||||
|
public List<string> JobIcons = new()
|
||||||
|
{
|
||||||
|
"AtmosphericTechnician",
|
||||||
|
"Bartender",
|
||||||
|
"Botanist",
|
||||||
|
"Boxer",
|
||||||
|
"Brigmedic",
|
||||||
|
"Captain",
|
||||||
|
"CargoTechnician",
|
||||||
|
"Chaplain",
|
||||||
|
"Chef",
|
||||||
|
"Chemist",
|
||||||
|
"ChiefEngineer",
|
||||||
|
"ChiefMedicalOfficer",
|
||||||
|
"Clown",
|
||||||
|
"Detective",
|
||||||
|
"Geneticist",
|
||||||
|
"HeadOfPersonnel",
|
||||||
|
"HeadOfSecurity",
|
||||||
|
"Janitor",
|
||||||
|
"Lawyer",
|
||||||
|
"Librarian",
|
||||||
|
"MedicalDoctor",
|
||||||
|
"MedicalIntern",
|
||||||
|
"Mime",
|
||||||
|
"Musician",
|
||||||
|
"Paramedic",
|
||||||
|
"Passenger",
|
||||||
|
"Psychologist",
|
||||||
|
"QuarterMaster",
|
||||||
|
"Reporter",
|
||||||
|
"ResearchAssistant",
|
||||||
|
"ResearchDirector",
|
||||||
|
"Roboticist",
|
||||||
|
"Scientist",
|
||||||
|
"SecurityCadet",
|
||||||
|
"SecurityOfficer",
|
||||||
|
"SeniorEngineer",
|
||||||
|
"SeniorOfficer",
|
||||||
|
"SeniorResearcher",
|
||||||
|
"ServiceWorker",
|
||||||
|
"ShaftMiner",
|
||||||
|
"StationEngineer",
|
||||||
|
"TechnicalAssistant",
|
||||||
|
"Virologist",
|
||||||
|
"Warden",
|
||||||
|
"Zookeeper"
|
||||||
|
};
|
||||||
|
// WD EDIT END
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
public sealed class IdCardConsoleBoundUserInterfaceState : BoundUserInterfaceState
|
public sealed class IdCardConsoleBoundUserInterfaceState : BoundUserInterfaceState
|
||||||
{
|
{
|
||||||
@@ -88,8 +147,10 @@ public sealed partial class IdCardConsoleComponent : Component
|
|||||||
public readonly string[]? TargetIdAccessList;
|
public readonly string[]? TargetIdAccessList;
|
||||||
public readonly string[]? AllowedModifyAccessList;
|
public readonly string[]? AllowedModifyAccessList;
|
||||||
public readonly string TargetIdJobPrototype;
|
public readonly string TargetIdJobPrototype;
|
||||||
|
public readonly string? TargetIdJobIcon; //WD-EDIT
|
||||||
|
|
||||||
public IdCardConsoleBoundUserInterfaceState(bool isPrivilegedIdPresent,
|
public IdCardConsoleBoundUserInterfaceState(
|
||||||
|
bool isPrivilegedIdPresent,
|
||||||
bool isPrivilegedIdAuthorized,
|
bool isPrivilegedIdAuthorized,
|
||||||
bool isTargetIdPresent,
|
bool isTargetIdPresent,
|
||||||
string? targetIdFullName,
|
string? targetIdFullName,
|
||||||
@@ -98,7 +159,8 @@ public sealed partial class IdCardConsoleComponent : Component
|
|||||||
string[]? allowedModifyAccessList,
|
string[]? allowedModifyAccessList,
|
||||||
string targetIdJobPrototype,
|
string targetIdJobPrototype,
|
||||||
string privilegedIdName,
|
string privilegedIdName,
|
||||||
string targetIdName)
|
string targetIdName,
|
||||||
|
string? targetIdJobIcon) // #WD EDIT (targetIdJobIcon)
|
||||||
{
|
{
|
||||||
IsPrivilegedIdPresent = isPrivilegedIdPresent;
|
IsPrivilegedIdPresent = isPrivilegedIdPresent;
|
||||||
IsPrivilegedIdAuthorized = isPrivilegedIdAuthorized;
|
IsPrivilegedIdAuthorized = isPrivilegedIdAuthorized;
|
||||||
@@ -110,12 +172,13 @@ public sealed partial class IdCardConsoleComponent : Component
|
|||||||
TargetIdJobPrototype = targetIdJobPrototype;
|
TargetIdJobPrototype = targetIdJobPrototype;
|
||||||
PrivilegedIdName = privilegedIdName;
|
PrivilegedIdName = privilegedIdName;
|
||||||
TargetIdName = targetIdName;
|
TargetIdName = targetIdName;
|
||||||
|
TargetIdJobIcon = targetIdJobIcon; //WD-EDIT
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
public enum IdCardConsoleUiKey : byte
|
public enum IdCardConsoleUiKey : byte
|
||||||
{
|
{
|
||||||
Key,
|
Key
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,27 +9,33 @@ namespace Content.Shared.Weapons.Ranged.Components;
|
|||||||
public sealed partial class TwoModeEnergyAmmoProviderComponent : BatteryAmmoProviderComponent
|
public sealed partial class TwoModeEnergyAmmoProviderComponent : BatteryAmmoProviderComponent
|
||||||
{
|
{
|
||||||
[ViewVariables(VVAccess.ReadOnly),
|
[ViewVariables(VVAccess.ReadOnly),
|
||||||
DataField("projProto", required: true, customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
DataField("stunPrototype", required: true, customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
||||||
public string ProjectilePrototype = default!;
|
public string StunPrototype = default!;
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadOnly),
|
[ViewVariables(VVAccess.ReadOnly),
|
||||||
DataField("hitscanProto", required: true, customTypeSerializer: typeof(PrototypeIdSerializer<HitscanPrototype>))]
|
DataField("laserPrototype", required: true, customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
||||||
public string HitscanPrototype = default!;
|
public string LaserPrototype = default!;
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadOnly), DataField("projFireCost")]
|
[ViewVariables(VVAccess.ReadOnly), DataField("stunFireCost")]
|
||||||
public float ProjFireCost = 50;
|
public float StunFireCost = 142;
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadOnly), DataField("hitscanFireCost")]
|
[ViewVariables(VVAccess.ReadOnly), DataField("laserFireCost")]
|
||||||
public float HitscanFireCost = 100;
|
public float LaserFireCost = 65;
|
||||||
|
|
||||||
|
[ViewVariables(VVAccess.ReadOnly), DataField("stunProjectileSpeed")]
|
||||||
|
public float StunProjectileSpeed = 12;
|
||||||
|
|
||||||
|
[ViewVariables(VVAccess.ReadOnly), DataField("laserProjectileSpeed")]
|
||||||
|
public float LaserProjectileSpeed = 25;
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadOnly), DataField("currentMode")]
|
[ViewVariables(VVAccess.ReadOnly), DataField("currentMode")]
|
||||||
public EnergyModes CurrentMode { get; set; } = EnergyModes.Stun;
|
public EnergyModes CurrentMode { get; set; } = EnergyModes.Stun;
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadOnly), DataField("projSound")]
|
[ViewVariables(VVAccess.ReadOnly), DataField("stunSound")]
|
||||||
public SoundSpecifier? ProjSound = new SoundPathSpecifier("/Audio/Weapons/Guns/Gunshots/taser2.ogg");
|
public SoundSpecifier? StunSound = new SoundPathSpecifier("/Audio/Weapons/Guns/Gunshots/taser2.ogg");
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadOnly), DataField("hitscanSound")]
|
[ViewVariables(VVAccess.ReadOnly), DataField("laserSound")]
|
||||||
public SoundSpecifier? HitscanSound = new SoundPathSpecifier("/Audio/Weapons/Guns/Gunshots/laser_cannon.ogg");
|
public SoundSpecifier? LaserSound = new SoundPathSpecifier("/Audio/Weapons/Guns/Gunshots/laser_cannon.ogg");
|
||||||
|
|
||||||
public SoundSpecifier? ToggleSound = new SoundPathSpecifier("/Audio/Weapons/Guns/Misc/egun_toggle.ogg");
|
public SoundSpecifier? ToggleSound = new SoundPathSpecifier("/Audio/Weapons/Guns/Misc/egun_toggle.ogg");
|
||||||
|
|
||||||
|
|||||||
@@ -38,15 +38,18 @@ public abstract partial class SharedGunSystem
|
|||||||
SubscribeLocalEvent<TwoModeEnergyAmmoProviderComponent, ExaminedEvent>(OnBatteryExamine);
|
SubscribeLocalEvent<TwoModeEnergyAmmoProviderComponent, ExaminedEvent>(OnBatteryExamine);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void OnTwoModeInit(EntityUid uid, TwoModeEnergyAmmoProviderComponent component, ComponentInit args)
|
private void OnTwoModeInit(EntityUid uid, TwoModeEnergyAmmoProviderComponent component, ComponentInit args)
|
||||||
{
|
{
|
||||||
if (!Timing.IsFirstTimePredicted || !TryComp<AppearanceComponent>(component.Owner, out var appearance)) return;
|
if (!Timing.IsFirstTimePredicted || !TryComp<AppearanceComponent>(component.Owner, out var appearance))
|
||||||
|
return;
|
||||||
|
|
||||||
Appearance.SetData(appearance.Owner, AmmoVisuals.InStun, component.InStun, appearance);
|
Appearance.SetData(appearance.Owner, AmmoVisuals.InStun, component.InStun, appearance);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnBatteryTwoModeHandleState(EntityUid uid, TwoModeEnergyAmmoProviderComponent component, ref ComponentHandleState args)
|
private void OnBatteryTwoModeHandleState(
|
||||||
|
EntityUid uid,
|
||||||
|
TwoModeEnergyAmmoProviderComponent component,
|
||||||
|
ref ComponentHandleState args)
|
||||||
{
|
{
|
||||||
if (args.Current is not TwoModeComponentState state)
|
if (args.Current is not TwoModeComponentState state)
|
||||||
return;
|
return;
|
||||||
@@ -58,7 +61,10 @@ public abstract partial class SharedGunSystem
|
|||||||
component.InStun = state.InStun;
|
component.InStun = state.InStun;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnBatteryTwoModeGetState(EntityUid uid, TwoModeEnergyAmmoProviderComponent component, ref ComponentGetState args)
|
private void OnBatteryTwoModeGetState(
|
||||||
|
EntityUid uid,
|
||||||
|
TwoModeEnergyAmmoProviderComponent component,
|
||||||
|
ref ComponentGetState args)
|
||||||
{
|
{
|
||||||
args.State = new TwoModeComponentState()
|
args.State = new TwoModeComponentState()
|
||||||
{
|
{
|
||||||
@@ -74,19 +80,20 @@ public abstract partial class SharedGunSystem
|
|||||||
{
|
{
|
||||||
if (!TryComp<AppearanceComponent>(uid, out var appearance))
|
if (!TryComp<AppearanceComponent>(uid, out var appearance))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!TryComp<ItemComponent>(uid, out var item))
|
if (!TryComp<ItemComponent>(uid, out var item))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (component.InStun)
|
_item.SetHeldPrefix(uid, component.InStun ? null : "laser", false, item);
|
||||||
_item.SetHeldPrefix(uid, null, false, item);
|
|
||||||
else
|
|
||||||
_item.SetHeldPrefix(uid, "laser", false, item);
|
|
||||||
|
|
||||||
|
|
||||||
Appearance.SetData(uid, AmmoVisuals.InStun, component.InStun, appearance);
|
Appearance.SetData(uid, AmmoVisuals.InStun, component.InStun, appearance);
|
||||||
|
Dirty(uid, component);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnBatteryHandleState(EntityUid uid, BatteryAmmoProviderComponent component, ref ComponentHandleState args)
|
private void OnBatteryHandleState(
|
||||||
|
EntityUid uid,
|
||||||
|
BatteryAmmoProviderComponent component,
|
||||||
|
ref ComponentHandleState args)
|
||||||
{
|
{
|
||||||
if (args.Current is not BatteryAmmoProviderComponentState state)
|
if (args.Current is not BatteryAmmoProviderComponentState state)
|
||||||
return;
|
return;
|
||||||
@@ -139,7 +146,7 @@ public abstract partial class SharedGunSystem
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Update the battery (server-only) whenever fired.
|
/// Update the battery (server-only) whenever fired.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected virtual void TakeCharge(EntityUid uid, BatteryAmmoProviderComponent component) {}
|
protected virtual void TakeCharge(EntityUid uid, BatteryAmmoProviderComponent component) { }
|
||||||
|
|
||||||
protected void UpdateBatteryAppearance(EntityUid uid, BatteryAmmoProviderComponent component)
|
protected void UpdateBatteryAppearance(EntityUid uid, BatteryAmmoProviderComponent component)
|
||||||
{
|
{
|
||||||
@@ -151,7 +158,9 @@ public abstract partial class SharedGunSystem
|
|||||||
Appearance.SetData(uid, AmmoVisuals.AmmoMax, component.Capacity, appearance);
|
Appearance.SetData(uid, AmmoVisuals.AmmoMax, component.Capacity, appearance);
|
||||||
}
|
}
|
||||||
|
|
||||||
private (EntityUid? Entity, IShootable) GetShootable(BatteryAmmoProviderComponent component, EntityCoordinates coordinates)
|
private (EntityUid? Entity, IShootable) GetShootable(
|
||||||
|
BatteryAmmoProviderComponent component,
|
||||||
|
EntityCoordinates coordinates)
|
||||||
{
|
{
|
||||||
switch (component)
|
switch (component)
|
||||||
{
|
{
|
||||||
@@ -161,12 +170,13 @@ public abstract partial class SharedGunSystem
|
|||||||
case HitscanBatteryAmmoProviderComponent hitscan:
|
case HitscanBatteryAmmoProviderComponent hitscan:
|
||||||
return (null, ProtoManager.Index<HitscanPrototype>(hitscan.Prototype));
|
return (null, ProtoManager.Index<HitscanPrototype>(hitscan.Prototype));
|
||||||
case TwoModeEnergyAmmoProviderComponent twoMode:
|
case TwoModeEnergyAmmoProviderComponent twoMode:
|
||||||
if (twoMode.CurrentMode == EnergyModes.Stun)
|
var projEntity =
|
||||||
{
|
Spawn(twoMode.CurrentMode == EnergyModes.Stun
|
||||||
var projEnt = Spawn(twoMode.ProjectilePrototype, coordinates);
|
? twoMode.StunPrototype
|
||||||
return (projEnt, EnsureComp<AmmoComponent>(projEnt));
|
: twoMode.LaserPrototype,
|
||||||
}
|
coordinates);
|
||||||
return (null, ProtoManager.Index<HitscanPrototype>(twoMode.HitscanPrototype));
|
|
||||||
|
return (projEntity, EnsureComp<AmmoComponent>(projEntity));
|
||||||
default:
|
default:
|
||||||
throw new ArgumentOutOfRangeException();
|
throw new ArgumentOutOfRangeException();
|
||||||
}
|
}
|
||||||
@@ -184,6 +194,7 @@ public abstract partial class SharedGunSystem
|
|||||||
public sealed class TwoModeComponentState : ComponentState
|
public sealed class TwoModeComponentState : ComponentState
|
||||||
{
|
{
|
||||||
public EnergyModes CurrentMode { get; init; }
|
public EnergyModes CurrentMode { get; init; }
|
||||||
|
|
||||||
public int Shots;
|
public int Shots;
|
||||||
public int MaxShots;
|
public int MaxShots;
|
||||||
public float FireCost;
|
public float FireCost;
|
||||||
|
|||||||
85
Content.Shared/White/Spline/CatmullRom/SplineCatmullRom.cs
Normal file
85
Content.Shared/White/Spline/CatmullRom/SplineCatmullRom.cs
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace Content.Shared.White.Spline.CatmullRom;
|
||||||
|
|
||||||
|
public abstract class SplineCatmullRom<T> : Spline<T>
|
||||||
|
{
|
||||||
|
protected const int LookupPrecision = 100;
|
||||||
|
|
||||||
|
protected static readonly (float c0, float c1, float c2, float c3)[] PositionCoefficientLookup
|
||||||
|
= Enumerable.Range(0, LookupPrecision + 1)
|
||||||
|
.Select(x => CalculateCoefficientsPosition((float) x / LookupPrecision)).ToArray();
|
||||||
|
|
||||||
|
protected static readonly (float c0, float c1, float c2, float c3)[] GradientCoefficientLookup
|
||||||
|
= Enumerable.Range(0, LookupPrecision + 1)
|
||||||
|
.Select(x => CalculateCoefficientsTangent((float) x / LookupPrecision)).ToArray();
|
||||||
|
|
||||||
|
protected static (float c0, float c1, float c2, float c3) CalculateCoefficientsPosition(float t)
|
||||||
|
{
|
||||||
|
var tt = t * t;
|
||||||
|
var ttt = tt * t;
|
||||||
|
return (
|
||||||
|
-ttt + 2.0f * tt - t,
|
||||||
|
3.0f * ttt - 5.0f * tt + 2.0f,
|
||||||
|
-3.0f * ttt + 4.0f * tt + t,
|
||||||
|
ttt - tt
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static (float c0, float c1, float c2, float c3) CalculateCoefficientsTangent(float t)
|
||||||
|
{
|
||||||
|
var tt = t * t;
|
||||||
|
return (
|
||||||
|
-3.0f * tt + 4.0f * t - 1,
|
||||||
|
9.0f * tt - 10.0f * t,
|
||||||
|
-9.0f * tt + 8.0f * t + 1.0f,
|
||||||
|
3.0f * tt - 2.0f * t
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private static int GetLookupIndex(float t)
|
||||||
|
{
|
||||||
|
return (int) (t * LookupPrecision);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public override T SamplePosition(ReadOnlySpan<T> controlPoints, float u)
|
||||||
|
{
|
||||||
|
return CalculateCatmullRom(GetCurrentControlPoints(controlPoints, (int) u),
|
||||||
|
PositionCoefficientLookup[GetLookupIndex(u % 1)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public override T SampleVelocity(ReadOnlySpan<T> controlPoints, float u)
|
||||||
|
{
|
||||||
|
return CalculateCatmullRom(GetCurrentControlPoints(controlPoints, (int) u),
|
||||||
|
GradientCoefficientLookup[GetLookupIndex(u % 1)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public override (T Position, T Velocity) SamplePositionVelocity(ReadOnlySpan<T> controlPoints, float u)
|
||||||
|
{
|
||||||
|
var lookupIndex = GetLookupIndex(u % 1);
|
||||||
|
var currentControlPoints = GetCurrentControlPoints(controlPoints, (int) u);
|
||||||
|
return (
|
||||||
|
CalculateCatmullRom(currentControlPoints, PositionCoefficientLookup[lookupIndex]),
|
||||||
|
CalculateCatmullRom(currentControlPoints, GradientCoefficientLookup[lookupIndex])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
protected virtual (T p0, T p1, T p2, T p3) GetCurrentControlPoints(ReadOnlySpan<T> controlPoints, int u)
|
||||||
|
{
|
||||||
|
var p1 = controlPoints[u];
|
||||||
|
var p2 = controlPoints[u + 1];
|
||||||
|
var p0 = u == 0 ? Add(p1, Subtract(p1, p2)) : controlPoints[u - 1];
|
||||||
|
var p3 = u + 2 == controlPoints.Length ? Add(p2, Subtract(p2, p2)) : controlPoints[u + 2];
|
||||||
|
return (p0, p1, p2, p3);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract T CalculateCatmullRom(
|
||||||
|
(T p0, T p1, T p2, T p3) points,
|
||||||
|
(float c0, float c1, float c2, float c3) coeffs);
|
||||||
|
}
|
||||||
38
Content.Shared/White/Spline/CatmullRom/SplineCatmullRom2D.cs
Normal file
38
Content.Shared/White/Spline/CatmullRom/SplineCatmullRom2D.cs
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
using System.Numerics;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace Content.Shared.White.Spline.CatmullRom;
|
||||||
|
|
||||||
|
public sealed class SplineCatmullRom2D : SplineCatmullRom<Vector2>
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
protected override Vector2 Add(Vector2 op1, Vector2 op2)
|
||||||
|
{
|
||||||
|
return op1 + op2;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
protected override Vector2 Subtract(Vector2 op1, Vector2 op2)
|
||||||
|
{
|
||||||
|
return op1 - op2;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
protected override float Magnitude(Vector2 op1)
|
||||||
|
{
|
||||||
|
return op1.Length();
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
protected override Vector2 CalculateCatmullRom(
|
||||||
|
(Vector2 p0, Vector2 p1, Vector2 p2, Vector2 p3) points,
|
||||||
|
(float c0, float c1, float c2, float c3) coeffs)
|
||||||
|
{
|
||||||
|
return new Vector2(
|
||||||
|
0.5f * (points.p0.X * coeffs.c0 + points.p1.X * coeffs.c1 + points.p2.X * coeffs.c2 +
|
||||||
|
points.p3.X * coeffs.c3),
|
||||||
|
0.5f * (points.p0.Y * coeffs.c0 + points.p1.Y * coeffs.c1 + points.p2.Y * coeffs.c2 +
|
||||||
|
points.p3.Y * coeffs.c3)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
81
Content.Shared/White/Spline/CubicBezier/SplineCubicBezier.cs
Normal file
81
Content.Shared/White/Spline/CubicBezier/SplineCubicBezier.cs
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace Content.Shared.White.Spline.CubicBezier;
|
||||||
|
|
||||||
|
public abstract class SplineCubicBezier<T> : Spline<T>
|
||||||
|
{
|
||||||
|
protected const int LookupPrecision = 100;
|
||||||
|
|
||||||
|
protected static readonly (float c0, float c1, float c2, float c3)[] PositionCoefficientLookup
|
||||||
|
= Enumerable.Range(0, LookupPrecision + 1).Select(x => CalculateCoefficientsPosition((float) x / LookupPrecision)).ToArray();
|
||||||
|
|
||||||
|
protected static readonly (float c0, float c1, float c2, float c3)[] GradientCoefficientLookup
|
||||||
|
= Enumerable.Range(0, LookupPrecision + 1).Select(x => CalculateCoefficientsTangent((float) x / LookupPrecision)).ToArray();
|
||||||
|
|
||||||
|
protected static (float c0, float c1, float c2, float c3) CalculateCoefficientsPosition(float t)
|
||||||
|
{
|
||||||
|
var tt = t * t;
|
||||||
|
var ttt = tt * t;
|
||||||
|
return (
|
||||||
|
-ttt + 3f * tt - 3f * t + 1f,
|
||||||
|
3f * ttt - 6f * tt + 3f * t,
|
||||||
|
-3f * ttt + 3f * tt,
|
||||||
|
ttt
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static (float c0, float c1, float c2, float c3) CalculateCoefficientsTangent(float t)
|
||||||
|
{
|
||||||
|
var tt = t * t;
|
||||||
|
return (
|
||||||
|
-3f * tt + 6f * t - 3,
|
||||||
|
9f * tt - 12f * t + 3,
|
||||||
|
-9f * tt + 6f * t,
|
||||||
|
3f * tt
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private static int GetLookupIndex(float t)
|
||||||
|
{
|
||||||
|
return (int) (t * LookupPrecision);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public override T SamplePosition(ReadOnlySpan<T> controlPoints, float u)
|
||||||
|
{
|
||||||
|
return CalculateBezier(GetCurrentControlPoints(controlPoints, (int) u), PositionCoefficientLookup[GetLookupIndex(u % 1)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public override T SampleVelocity(ReadOnlySpan<T> controlPoints, float u)
|
||||||
|
{
|
||||||
|
return CalculateBezier(GetCurrentControlPoints(controlPoints, (int) u), GradientCoefficientLookup[GetLookupIndex(u % 1)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public override (T Position, T Velocity) SamplePositionVelocity(ReadOnlySpan<T> controlPoints, float u)
|
||||||
|
{
|
||||||
|
var lookupIndex = GetLookupIndex(u % 1);
|
||||||
|
var currentControlPoints = GetCurrentControlPoints(controlPoints, (int) u);
|
||||||
|
return (
|
||||||
|
CalculateBezier(currentControlPoints, PositionCoefficientLookup[lookupIndex]),
|
||||||
|
CalculateBezier(currentControlPoints, GradientCoefficientLookup[lookupIndex])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public override float GetControlGroupAmount(int controlPointAmount)
|
||||||
|
{
|
||||||
|
return (controlPointAmount - 1) / 3f;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
protected virtual (T p0, T p1, T p2, T p3) GetCurrentControlPoints(ReadOnlySpan<T> controlPoints, int u)
|
||||||
|
{
|
||||||
|
return (controlPoints[u], controlPoints[u + 1], controlPoints[u + 2], controlPoints[u + 3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract T CalculateBezier((T p0, T p1, T p2, T p3) points, (float c0, float c1, float c2, float c3) coeffs);
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace Content.Shared.White.Spline.CubicBezier;
|
||||||
|
|
||||||
|
public sealed class SplineCubicBezier4D : SplineCubicBezier<Vector4>
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
protected override Vector4 Add(Vector4 op1, Vector4 op2)
|
||||||
|
{
|
||||||
|
return new Vector4(op1.X + op2.X, op1.Y + op2.Y, op1.Z + op2.Z, op1.W + op2.W);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
protected override Vector4 Subtract(Vector4 op1, Vector4 op2)
|
||||||
|
{
|
||||||
|
return new Vector4(op1.X - op2.X, op1.Y - op2.Y, op1.Z - op2.Z, op1.W - op2.W);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
protected override float Magnitude(Vector4 op1)
|
||||||
|
{
|
||||||
|
return MathF.Sqrt(op1.X * op1.X + op1.Y * op1.Y + op1.Z * op1.Z + op1.W * op1.W);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
protected override Vector4 CalculateBezier(
|
||||||
|
(Vector4 p0, Vector4 p1, Vector4 p2, Vector4 p3) points,
|
||||||
|
(float c0, float c1, float c2, float c3) coeffs)
|
||||||
|
{
|
||||||
|
return new Vector4(
|
||||||
|
points.p0.X * coeffs.c0 + points.p1.X * coeffs.c1 + points.p2.X * coeffs.c2 + points.p3.X * coeffs.c3,
|
||||||
|
points.p0.Y * coeffs.c0 + points.p1.Y * coeffs.c1 + points.p2.Y * coeffs.c2 + points.p3.Y * coeffs.c3,
|
||||||
|
points.p0.Z * coeffs.c0 + points.p1.Z * coeffs.c1 + points.p2.Z * coeffs.c2 + points.p3.Z * coeffs.c3,
|
||||||
|
points.p0.W * coeffs.c0 + points.p1.W * coeffs.c1 + points.p2.W * coeffs.c2 + points.p3.W * coeffs.c3
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace Content.Shared.White.Spline.CubicBezier;
|
||||||
|
|
||||||
|
public sealed class SplineCubicBezierColor : SplineCubicBezier<Color>
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
protected override Color Add(Color op1, Color op2)
|
||||||
|
{
|
||||||
|
return new Color(op1.R + op2.R, op1.G + op2.G, op1.B + op2.B, op1.A + op2.A);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
protected override Color Subtract(Color op1, Color op2)
|
||||||
|
{
|
||||||
|
return new Color(op1.R - op2.R, op1.G - op2.G, op1.B - op2.B, op1.A - op2.A);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
protected override float Magnitude(Color op1)
|
||||||
|
{
|
||||||
|
return MathF.Sqrt(op1.R * op1.R + op1.G * op1.G + op1.B * op1.B + op1.A * op1.A);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
protected override Color CalculateBezier(
|
||||||
|
(Color p0, Color p1, Color p2, Color p3) points,
|
||||||
|
(float c0, float c1, float c2, float c3) coeffs)
|
||||||
|
{
|
||||||
|
return new Color(
|
||||||
|
points.p0.R * coeffs.c0 + points.p1.R * coeffs.c1 + points.p2.R * coeffs.c2 + points.p3.R * coeffs.c3,
|
||||||
|
points.p0.G * coeffs.c0 + points.p1.G * coeffs.c1 + points.p2.G * coeffs.c2 + points.p3.G * coeffs.c3,
|
||||||
|
points.p0.B * coeffs.c0 + points.p1.B * coeffs.c1 + points.p2.B * coeffs.c2 + points.p3.B * coeffs.c3,
|
||||||
|
points.p0.A * coeffs.c0 + points.p1.A * coeffs.c1 + points.p2.A * coeffs.c2 + points.p3.A * coeffs.c3
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
14
Content.Shared/White/Spline/ISpline.cs
Normal file
14
Content.Shared/White/Spline/ISpline.cs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
namespace Content.Shared.White.Spline;
|
||||||
|
|
||||||
|
public interface ISpline<T>
|
||||||
|
{
|
||||||
|
T SamplePosition(ReadOnlySpan<T> controlPoints, float u);
|
||||||
|
|
||||||
|
T SampleVelocity(ReadOnlySpan<T> controlPoints, float u);
|
||||||
|
|
||||||
|
(T Position, T Velocity) SamplePositionVelocity(ReadOnlySpan<T> controlPoints, float u);
|
||||||
|
|
||||||
|
IEnumerable<float> IteratePointParamsByLength(T[] controlPoints, float lengthStepSize);
|
||||||
|
|
||||||
|
float GetControlGroupAmount(int controlPointAmount);
|
||||||
|
}
|
||||||
34
Content.Shared/White/Spline/Linear/SplineLinear.cs
Normal file
34
Content.Shared/White/Spline/Linear/SplineLinear.cs
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace Content.Shared.White.Spline.Linear;
|
||||||
|
|
||||||
|
public abstract class SplineLinear<T> : Spline<T>
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public override T SamplePosition(ReadOnlySpan<T> controlPoints, float u)
|
||||||
|
{
|
||||||
|
var iu = (int) u;
|
||||||
|
var t = u % 1;
|
||||||
|
return Add(Multiply(controlPoints[iu], 1 - t), Multiply(controlPoints[iu + 1], t));
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public override T SampleVelocity(ReadOnlySpan<T> controlPoints, float u)
|
||||||
|
{
|
||||||
|
var iu = (int) u;
|
||||||
|
return iu == 0
|
||||||
|
? Subtract(controlPoints[iu + 1], controlPoints[iu])
|
||||||
|
: Subtract(controlPoints[iu + 1], controlPoints[iu - 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public override (T Position, T Velocity) SamplePositionVelocity(ReadOnlySpan<T> controlPoints, float u)
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
SamplePosition(controlPoints, u),
|
||||||
|
SampleVelocity(controlPoints, u)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract T Multiply(T op1, float scalar);
|
||||||
|
}
|
||||||
31
Content.Shared/White/Spline/Linear/SplineLinear2D.cs
Normal file
31
Content.Shared/White/Spline/Linear/SplineLinear2D.cs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
using System.Numerics;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace Content.Shared.White.Spline.Linear;
|
||||||
|
|
||||||
|
public sealed class SplineLinear2D : SplineLinear<Vector2>
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
protected override Vector2 Add(Vector2 op1, Vector2 op2)
|
||||||
|
{
|
||||||
|
return op1 + op2;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
protected override Vector2 Subtract(Vector2 op1, Vector2 op2)
|
||||||
|
{
|
||||||
|
return op1 - op2;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
protected override float Magnitude(Vector2 op1)
|
||||||
|
{
|
||||||
|
return op1.Length();
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
protected override Vector2 Multiply(Vector2 op1, float scalar)
|
||||||
|
{
|
||||||
|
return op1 * scalar;
|
||||||
|
}
|
||||||
|
}
|
||||||
30
Content.Shared/White/Spline/Linear/SplineLinear4D.cs
Normal file
30
Content.Shared/White/Spline/Linear/SplineLinear4D.cs
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace Content.Shared.White.Spline.Linear;
|
||||||
|
|
||||||
|
public sealed class SplineLinear4D : SplineLinear<Vector4>
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
protected override Vector4 Add(Vector4 op1, Vector4 op2)
|
||||||
|
{
|
||||||
|
return new Vector4(op1.X + op2.X, op1.Y + op2.Y, op1.Z + op2.Z, op1.W + op2.W);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
protected override Vector4 Subtract(Vector4 op1, Vector4 op2)
|
||||||
|
{
|
||||||
|
return new Vector4(op1.X - op2.X, op1.Y - op2.Y, op1.Z - op2.Z, op1.W - op2.W);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
protected override float Magnitude(Vector4 op1)
|
||||||
|
{
|
||||||
|
return MathF.Sqrt(op1.X * op1.X + op1.Y * op1.Y + op1.Z * op1.Z + op1.W * op1.W);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
protected override Vector4 Multiply(Vector4 op1, float scalar)
|
||||||
|
{
|
||||||
|
return new Vector4(op1.X * scalar, op1.Y * scalar, op1.Z * scalar, op1.W * scalar);
|
||||||
|
}
|
||||||
|
}
|
||||||
30
Content.Shared/White/Spline/Linear/SplineLinearColor.cs
Normal file
30
Content.Shared/White/Spline/Linear/SplineLinearColor.cs
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace Content.Shared.White.Spline.Linear;
|
||||||
|
|
||||||
|
public sealed class SplineLinearColor : SplineLinear<Color>
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
protected override Color Add(Color op1, Color op2)
|
||||||
|
{
|
||||||
|
return new Color(op1.R + op2.R, op1.G + op2.G, op1.B + op2.B, op1.A + op2.A);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
protected override Color Subtract(Color op1, Color op2)
|
||||||
|
{
|
||||||
|
return new Color(op1.R - op2.R, op1.G - op2.G, op1.B - op2.B, op1.A - op2.A);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
protected override float Magnitude(Color op1)
|
||||||
|
{
|
||||||
|
return MathF.Sqrt(op1.R * op1.R + op1.G * op1.G + op1.B * op1.B + op1.A * op1.A);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
protected override Color Multiply(Color op1, float scalar)
|
||||||
|
{
|
||||||
|
return new Color(op1.R * scalar, op1.G * scalar, op1.B * scalar, op1.A * scalar);
|
||||||
|
}
|
||||||
|
}
|
||||||
44
Content.Shared/White/Spline/Spline.cs
Normal file
44
Content.Shared/White/Spline/Spline.cs
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace Content.Shared.White.Spline;
|
||||||
|
|
||||||
|
public abstract class Spline<T> : ISpline<T>
|
||||||
|
{
|
||||||
|
public abstract T SamplePosition(ReadOnlySpan<T> controlPoints, float u);
|
||||||
|
|
||||||
|
public abstract T SampleVelocity(ReadOnlySpan<T> controlPoints, float u);
|
||||||
|
|
||||||
|
public abstract (T Position, T Velocity) SamplePositionVelocity(ReadOnlySpan<T> controlPoints, float u);
|
||||||
|
|
||||||
|
public virtual IEnumerable<float> IteratePointParamsByLength(T[] controlPoints, float lengthStepSize)
|
||||||
|
{
|
||||||
|
//ну а хули нам наивным салюшонам
|
||||||
|
for (var u = 0; u < controlPoints.Length - 1; u++)
|
||||||
|
{
|
||||||
|
var segmentLength = ApproximateArcLength(controlPoints, u);
|
||||||
|
var tStepSize = lengthStepSize / segmentLength;
|
||||||
|
for (var t = 0f; t < 1; t += tStepSize)
|
||||||
|
{
|
||||||
|
yield return u + t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public virtual float GetControlGroupAmount(int controlPointAmount)
|
||||||
|
{
|
||||||
|
return controlPointAmount - 1f;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
protected virtual float ApproximateArcLength(ReadOnlySpan<T> controlPoints, float u)
|
||||||
|
{
|
||||||
|
return Magnitude(Subtract(controlPoints[(int) u], controlPoints[(int) u + 1]));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract T Add(T op1, T op2);
|
||||||
|
|
||||||
|
protected abstract T Subtract(T op1, T op2);
|
||||||
|
|
||||||
|
protected abstract float Magnitude(T op1);
|
||||||
|
}
|
||||||
59
Content.Shared/White/Spline/SplineEnums.cs
Normal file
59
Content.Shared/White/Spline/SplineEnums.cs
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
using System.Numerics;
|
||||||
|
using Content.Shared.White.Spline.CatmullRom;
|
||||||
|
using Content.Shared.White.Spline.CubicBezier;
|
||||||
|
using Content.Shared.White.Spline.Linear;
|
||||||
|
using Vector4 = Robust.Shared.Maths.Vector4;
|
||||||
|
|
||||||
|
namespace Content.Shared.White.Spline;
|
||||||
|
|
||||||
|
public static class Spline
|
||||||
|
{
|
||||||
|
public static ISpline<Vector2> From2DType(Spline2DType type)
|
||||||
|
{
|
||||||
|
return type switch
|
||||||
|
{
|
||||||
|
Spline2DType.Linear => new SplineLinear2D(),
|
||||||
|
Spline2DType.CatmullRom => new SplineCatmullRom2D(),
|
||||||
|
_ => throw new NotImplementedException()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ISpline<Vector4> From4DType(Spline4DType type)
|
||||||
|
{
|
||||||
|
return type switch
|
||||||
|
{
|
||||||
|
Spline4DType.Linear => new SplineLinear4D(),
|
||||||
|
Spline4DType.Bezier => new SplineCubicBezier4D(),
|
||||||
|
_ => throw new NotImplementedException()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ISpline<Color> FromColorType(SplineColorType type)
|
||||||
|
{
|
||||||
|
return type switch
|
||||||
|
{
|
||||||
|
SplineColorType.Linear => new SplineLinearColor(),
|
||||||
|
SplineColorType.Bezier => new SplineCubicBezierColor(),
|
||||||
|
_ => throw new NotImplementedException()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Spline2DType : byte
|
||||||
|
{
|
||||||
|
Linear,
|
||||||
|
CatmullRom
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Spline4DType : byte
|
||||||
|
{
|
||||||
|
Linear,
|
||||||
|
Bezier
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum SplineColorType : byte
|
||||||
|
{
|
||||||
|
Linear,
|
||||||
|
Bezier
|
||||||
|
}
|
||||||
|
|
||||||
74
Content.Shared/White/Trail/SharedTrailComponent.cs
Normal file
74
Content.Shared/White/Trail/SharedTrailComponent.cs
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
using System.Numerics;
|
||||||
|
using Content.Shared.White.Spline;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
using Vector4 = Robust.Shared.Maths.Vector4;
|
||||||
|
|
||||||
|
namespace Content.Shared.White.Trail;
|
||||||
|
|
||||||
|
[NetworkedComponent]
|
||||||
|
public abstract partial class SharedTrailComponent : Component, ITrailSettings
|
||||||
|
{
|
||||||
|
[DataField("gravity")]
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public virtual Vector2 Gravity { get; set; }
|
||||||
|
|
||||||
|
[DataField("lifetime", required: true)]
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public virtual float Lifetime { get; set; }
|
||||||
|
|
||||||
|
[DataField("lengthStep")]
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public virtual float LengthStep { get; set; }
|
||||||
|
|
||||||
|
[DataField("randomWalk")]
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public virtual Vector2 MaxRandomWalk { get; set; }
|
||||||
|
|
||||||
|
[DataField("scale", required: true)]
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public virtual Vector2 Scale { get; set; }
|
||||||
|
|
||||||
|
[DataField("texturePath")]
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public virtual string? TexurePath { get; set; }
|
||||||
|
|
||||||
|
[DataField("creationOffset")]
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public virtual Vector2 CreationOffset { get; set; }
|
||||||
|
|
||||||
|
[DataField("сreationDistanceThresholdSquared")]
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public virtual float СreationDistanceThresholdSquared { get; set; }
|
||||||
|
|
||||||
|
[DataField("creationMethod")]
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public virtual SegmentCreationMethod СreationMethod { get; set; }
|
||||||
|
|
||||||
|
[DataField("gradient", required: true)]
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public virtual Vector4[] Gradient { get; set; } = new[] { Vector4.One, new Vector4(1f, 1f, 1f, 0f) };
|
||||||
|
|
||||||
|
[DataField("gradientIteratorType")]
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public virtual Spline4DType GradientIteratorType { get; set; }
|
||||||
|
|
||||||
|
[DataField("splineIteratorType")]
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public virtual Spline2DType SplineIteratorType { get; set; }
|
||||||
|
|
||||||
|
[DataField("splineRendererType")]
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public virtual TrailSplineRendererType SplineRendererType { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed class TrailComponentState : ComponentState
|
||||||
|
{
|
||||||
|
public TrailSettings Settings;
|
||||||
|
|
||||||
|
public TrailComponentState(TrailSettings settings)
|
||||||
|
{
|
||||||
|
Settings = settings;
|
||||||
|
}
|
||||||
|
}
|
||||||
97
Content.Shared/White/Trail/TrailSettings.cs
Normal file
97
Content.Shared/White/Trail/TrailSettings.cs
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
using System.Numerics;
|
||||||
|
using Content.Shared.White.Spline;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
using Vector4 = Robust.Shared.Maths.Vector4;
|
||||||
|
|
||||||
|
namespace Content.Shared.White.Trail;
|
||||||
|
|
||||||
|
[DataDefinition]
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed partial class TrailSettings : ITrailSettings
|
||||||
|
{
|
||||||
|
public static readonly TrailSettings Default = new();
|
||||||
|
|
||||||
|
public Vector2 Scale { get; set; } = new(0.5f, 1f);
|
||||||
|
|
||||||
|
public float СreationDistanceThresholdSquared { get; set; } = 0.1f;
|
||||||
|
|
||||||
|
public SegmentCreationMethod СreationMethod { get; set; } = SegmentCreationMethod.OnFrameUpdate;
|
||||||
|
|
||||||
|
public Vector2 CreationOffset { get; set; } = Vector2.Zero;
|
||||||
|
|
||||||
|
public Vector2 Gravity { get; set; } = new(0.01f, 0.01f);
|
||||||
|
|
||||||
|
public Vector2 MaxRandomWalk { get; set; } = new(0.005f, 0.005f);
|
||||||
|
|
||||||
|
public float Lifetime { get; set; }
|
||||||
|
|
||||||
|
public float LengthStep { get; set; } = 0.1f;
|
||||||
|
|
||||||
|
public string? TexurePath { get; set; }
|
||||||
|
|
||||||
|
public Vector4[] Gradient { get; set; } = { new(1f, 1f, 1f, 1f), new(1f, 1f, 1f, 0f) };
|
||||||
|
|
||||||
|
public Spline4DType GradientIteratorType { get; set; }
|
||||||
|
|
||||||
|
public Spline2DType SplineIteratorType { get; set; }
|
||||||
|
|
||||||
|
public TrailSplineRendererType SplineRendererType { get; set; }
|
||||||
|
|
||||||
|
public static void Inject(ITrailSettings into, ITrailSettings from)
|
||||||
|
{
|
||||||
|
into.Scale = from.Scale;
|
||||||
|
into.СreationDistanceThresholdSquared = from.СreationDistanceThresholdSquared;
|
||||||
|
into.СreationMethod = from.СreationMethod;
|
||||||
|
into.CreationOffset = from.CreationOffset;
|
||||||
|
into.Gravity = from.Gravity;
|
||||||
|
into.MaxRandomWalk = from.MaxRandomWalk;
|
||||||
|
into.Lifetime = from.Lifetime;
|
||||||
|
into.LengthStep = from.LengthStep;
|
||||||
|
into.TexurePath = from.TexurePath;
|
||||||
|
into.Gradient = from.Gradient;
|
||||||
|
into.SplineIteratorType = from.SplineIteratorType;
|
||||||
|
into.SplineRendererType = from.SplineRendererType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface ITrailSettings
|
||||||
|
{
|
||||||
|
Vector2 Gravity { get; set; }
|
||||||
|
|
||||||
|
float Lifetime { get; set; }
|
||||||
|
|
||||||
|
float LengthStep { get; set; }
|
||||||
|
|
||||||
|
Vector2 MaxRandomWalk { get; set; }
|
||||||
|
|
||||||
|
Vector2 Scale { get; set; }
|
||||||
|
|
||||||
|
string? TexurePath { get; set; }
|
||||||
|
|
||||||
|
Vector2 CreationOffset { get; set; }
|
||||||
|
|
||||||
|
float СreationDistanceThresholdSquared { get; set; }
|
||||||
|
|
||||||
|
SegmentCreationMethod СreationMethod { get; set; }
|
||||||
|
|
||||||
|
Vector4[] Gradient { get; set; }
|
||||||
|
|
||||||
|
Spline4DType GradientIteratorType { get; set; }
|
||||||
|
|
||||||
|
Spline2DType SplineIteratorType { get; set; }
|
||||||
|
|
||||||
|
TrailSplineRendererType SplineRendererType { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum SegmentCreationMethod : byte
|
||||||
|
{
|
||||||
|
OnFrameUpdate,
|
||||||
|
OnMove
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum TrailSplineRendererType : byte
|
||||||
|
{
|
||||||
|
Continuous,
|
||||||
|
Point,
|
||||||
|
Debug
|
||||||
|
}
|
||||||
@@ -136,8 +136,8 @@
|
|||||||
- state: mag-unshaded-4
|
- state: mag-unshaded-4
|
||||||
map: ["enum.GunVisualLayers.MagUnshaded"]
|
map: ["enum.GunVisualLayers.MagUnshaded"]
|
||||||
shader: unshaded
|
shader: unshaded
|
||||||
- type: HitscanBatteryAmmoProvider
|
- type: ProjectileBatteryAmmoProvider
|
||||||
proto: RedMediumLaser
|
proto: BulletTrailLaser
|
||||||
fireCost: 62.5
|
fireCost: 62.5
|
||||||
- type: MagazineVisuals
|
- type: MagazineVisuals
|
||||||
magState: mag
|
magState: mag
|
||||||
@@ -161,8 +161,8 @@
|
|||||||
shader: unshaded
|
shader: unshaded
|
||||||
- type: Clothing
|
- type: Clothing
|
||||||
sprite: Objects/Weapons/Guns/Battery/makeshift.rsi
|
sprite: Objects/Weapons/Guns/Battery/makeshift.rsi
|
||||||
- type: HitscanBatteryAmmoProvider
|
- type: ProjectileBatteryAmmoProvider
|
||||||
proto: RedLaser
|
proto: BulletTrailLaser
|
||||||
fireCost: 62.5
|
fireCost: 62.5
|
||||||
- type: Battery
|
- type: Battery
|
||||||
maxCharge: 500
|
maxCharge: 500
|
||||||
@@ -217,8 +217,8 @@
|
|||||||
selectedMode: SemiAuto
|
selectedMode: SemiAuto
|
||||||
availableModes:
|
availableModes:
|
||||||
- SemiAuto
|
- SemiAuto
|
||||||
- type: HitscanBatteryAmmoProvider
|
- type: ProjectileBatteryAmmoProvider
|
||||||
proto: RedLaser
|
proto: BulletTrailLaser
|
||||||
fireCost: 62.5
|
fireCost: 62.5
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
@@ -227,8 +227,8 @@
|
|||||||
id: WeaponLaserCarbinePractice
|
id: WeaponLaserCarbinePractice
|
||||||
description: This modified laser rifle fires harmless beams in the 40-watt range, for target practice.
|
description: This modified laser rifle fires harmless beams in the 40-watt range, for target practice.
|
||||||
components:
|
components:
|
||||||
- type: HitscanBatteryAmmoProvider
|
- type: ProjectileBatteryAmmoProvider
|
||||||
proto: RedLaserPractice
|
proto: BulletTrailLaserPractice
|
||||||
fireCost: 62.5
|
fireCost: 62.5
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
@@ -253,8 +253,8 @@
|
|||||||
- SemiAuto
|
- SemiAuto
|
||||||
soundGunshot:
|
soundGunshot:
|
||||||
path: /Audio/Weapons/Guns/Gunshots/laser_cannon.ogg
|
path: /Audio/Weapons/Guns/Gunshots/laser_cannon.ogg
|
||||||
- type: HitscanBatteryAmmoProvider
|
- type: ProjectileBatteryAmmoProvider
|
||||||
proto: Pulse
|
proto: PulseBoltProjectile
|
||||||
fireCost: 200
|
fireCost: 200
|
||||||
- type: Battery
|
- type: Battery
|
||||||
maxCharge: 2000
|
maxCharge: 2000
|
||||||
@@ -284,8 +284,8 @@
|
|||||||
- FullAuto
|
- FullAuto
|
||||||
soundGunshot:
|
soundGunshot:
|
||||||
path: /Audio/Weapons/Guns/Gunshots/laser_cannon.ogg
|
path: /Audio/Weapons/Guns/Gunshots/laser_cannon.ogg
|
||||||
- type: HitscanBatteryAmmoProvider
|
- type: ProjectileBatteryAmmoProvider
|
||||||
proto: Pulse
|
proto: PulseBoltProjectile
|
||||||
fireCost: 200
|
fireCost: 200
|
||||||
- type: Battery
|
- type: Battery
|
||||||
maxCharge: 5000
|
maxCharge: 5000
|
||||||
@@ -311,8 +311,8 @@
|
|||||||
fireRate: 1.5
|
fireRate: 1.5
|
||||||
soundGunshot:
|
soundGunshot:
|
||||||
path: /Audio/Weapons/Guns/Gunshots/laser3.ogg
|
path: /Audio/Weapons/Guns/Gunshots/laser3.ogg
|
||||||
- type: HitscanBatteryAmmoProvider
|
- type: ProjectileBatteryAmmoProvider
|
||||||
proto: Pulse
|
proto: PulseBoltProjectile
|
||||||
fireCost: 100
|
fireCost: 100
|
||||||
- type: Battery
|
- type: Battery
|
||||||
maxCharge: 40000
|
maxCharge: 40000
|
||||||
@@ -338,8 +338,8 @@
|
|||||||
fireRate: 1.5
|
fireRate: 1.5
|
||||||
soundGunshot:
|
soundGunshot:
|
||||||
path: /Audio/Weapons/Guns/Gunshots/laser_cannon.ogg
|
path: /Audio/Weapons/Guns/Gunshots/laser_cannon.ogg
|
||||||
- type: HitscanBatteryAmmoProvider
|
- type: ProjectileBatteryAmmoProvider
|
||||||
proto: RedHeavyLaser
|
proto: BulletTrailLaserHeavy
|
||||||
fireCost: 100
|
fireCost: 100
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
@@ -392,8 +392,8 @@
|
|||||||
- type: Gun
|
- type: Gun
|
||||||
soundGunshot:
|
soundGunshot:
|
||||||
path: /Audio/Weapons/Guns/Gunshots/laser3.ogg
|
path: /Audio/Weapons/Guns/Gunshots/laser3.ogg
|
||||||
- type: HitscanBatteryAmmoProvider
|
- type: ProjectileBatteryAmmoProvider
|
||||||
proto: XrayLaser
|
proto: BulletTrailLaserXray
|
||||||
fireCost: 100
|
fireCost: 100
|
||||||
- type: MagazineVisuals
|
- type: MagazineVisuals
|
||||||
magState: mag
|
magState: mag
|
||||||
@@ -554,8 +554,8 @@
|
|||||||
- type: Gun
|
- type: Gun
|
||||||
soundGunshot:
|
soundGunshot:
|
||||||
path: /Audio/Weapons/Guns/Gunshots/laser_cannon.ogg
|
path: /Audio/Weapons/Guns/Gunshots/laser_cannon.ogg
|
||||||
- type: HitscanBatteryAmmoProvider
|
- type: ProjectileBatteryAmmoProvider
|
||||||
proto: RedMediumLaser
|
proto: BulletTrailLaserMedium
|
||||||
fireCost: 100
|
fireCost: 100
|
||||||
- type: BatterySelfRecharger
|
- type: BatterySelfRecharger
|
||||||
autoRecharge: true
|
autoRecharge: true
|
||||||
@@ -595,8 +595,8 @@
|
|||||||
- type: Gun
|
- type: Gun
|
||||||
soundGunshot:
|
soundGunshot:
|
||||||
path: /Audio/Weapons/Guns/Gunshots/laser_cannon.ogg
|
path: /Audio/Weapons/Guns/Gunshots/laser_cannon.ogg
|
||||||
- type: HitscanBatteryAmmoProvider
|
- type: ProjectileBatteryAmmoProvider
|
||||||
proto: RedMediumLaser
|
proto: BulletTrailLaserMedium
|
||||||
fireCost: 100
|
fireCost: 100
|
||||||
- type: BatterySelfRecharger
|
- type: BatterySelfRecharger
|
||||||
autoRecharge: true
|
autoRecharge: true
|
||||||
@@ -682,8 +682,8 @@
|
|||||||
fireRate: 1
|
fireRate: 1
|
||||||
soundGunshot:
|
soundGunshot:
|
||||||
path: /Audio/Weapons/Guns/Gunshots/laser_clown.ogg
|
path: /Audio/Weapons/Guns/Gunshots/laser_clown.ogg
|
||||||
- type: HitscanBatteryAmmoProvider
|
- type: ProjectileBatteryAmmoProvider
|
||||||
proto: RedMediumLaser
|
proto: BulletTrailLaserMedium
|
||||||
fireCost: 100
|
fireCost: 100
|
||||||
- type: BatterySelfRecharger
|
- type: BatterySelfRecharger
|
||||||
autoRecharge: true
|
autoRecharge: true
|
||||||
@@ -721,15 +721,18 @@
|
|||||||
- suitStorage
|
- suitStorage
|
||||||
- type: Gun
|
- type: Gun
|
||||||
soundGunshot:
|
soundGunshot:
|
||||||
path: /Audio/Weapons/Guns/Gunshots/taser2.ogg
|
path: /Audio/Weapons/Guns/Gunshots/laser_cannon.ogg
|
||||||
|
projectileSpeed: 12
|
||||||
- type: TwoModeEnergyAmmoProvider
|
- type: TwoModeEnergyAmmoProvider
|
||||||
projProto: BulletDisabler
|
stunPrototype: BulletDisabler
|
||||||
fireCost: 50
|
laserPrototype: BulletTrailLaser
|
||||||
projFireCost: 50
|
fireCost: 100
|
||||||
hitscanProto: RedLaser
|
laserFireCost: 99
|
||||||
hitscanFireCost: 100
|
stunFireCost: 49
|
||||||
projSound: "/Audio/Weapons/Guns/Gunshots/taser2.ogg"
|
stunProjectileSpeed: 25
|
||||||
hitscanSound: "/Audio/Weapons/Guns/Gunshots/laser_cannon.ogg"
|
laserProjectileSpeed: 25
|
||||||
|
stunSound: "/Audio/Weapons/Guns/Gunshots/taser2.ogg"
|
||||||
|
laserSound: "/Audio/Weapons/Guns/Gunshots/laser_cannon.ogg"
|
||||||
- type: MagazineVisuals
|
- type: MagazineVisuals
|
||||||
magState: mag
|
magState: mag
|
||||||
steps: 5
|
steps: 5
|
||||||
|
|||||||
@@ -192,9 +192,11 @@
|
|||||||
soundHit:
|
soundHit:
|
||||||
path: "/Audio/Weapons/Guns/Hits/taser_hit.ogg"
|
path: "/Audio/Weapons/Guns/Hits/taser_hit.ogg"
|
||||||
soundForce: true
|
soundForce: true
|
||||||
- type: StunOnCollide
|
- type: StaminaDamageOnCollide
|
||||||
stunAmount: 5
|
damage: 100
|
||||||
knockdownAmount: 5
|
- type: Reflective
|
||||||
|
reflective:
|
||||||
|
- Energy
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
name : disabler bolt
|
name : disabler bolt
|
||||||
@@ -879,3 +881,303 @@
|
|||||||
- type: Tag
|
- type: Tag
|
||||||
tags:
|
tags:
|
||||||
- HideContextMenu
|
- HideContextMenu
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
name: laser bolt
|
||||||
|
id: BulletTrailLaser
|
||||||
|
parent: BaseBullet
|
||||||
|
noSpawn: true
|
||||||
|
components:
|
||||||
|
- type: Sprite
|
||||||
|
sprite: Objects/Weapons/Guns/Projectiles/projectiles_tg.rsi
|
||||||
|
layers:
|
||||||
|
- shader: unshaded
|
||||||
|
- type: Ammo
|
||||||
|
muzzleFlash: null
|
||||||
|
- type: Physics
|
||||||
|
- type: Fixtures
|
||||||
|
fixtures:
|
||||||
|
projectile:
|
||||||
|
shape:
|
||||||
|
!type:PhysShapeAabb
|
||||||
|
bounds: "-0.2,-0.2,0.2,0.2"
|
||||||
|
hard: false
|
||||||
|
mask:
|
||||||
|
- Opaque
|
||||||
|
fly-by: *flybyfixture
|
||||||
|
- type: Projectile
|
||||||
|
# soundHit: Waiting on serv3
|
||||||
|
damage:
|
||||||
|
types:
|
||||||
|
Heat: 23
|
||||||
|
- type: TimedDespawn
|
||||||
|
lifetime: 3
|
||||||
|
- type: Trail
|
||||||
|
splineIteratorType: Linear
|
||||||
|
splineRendererType: Continuous
|
||||||
|
creationMethod: OnMove
|
||||||
|
scale: 0.05, 0.0
|
||||||
|
lifetime: 0.1
|
||||||
|
randomWalk: 0.001, 0.001
|
||||||
|
gravity: 0, 0
|
||||||
|
gradient:
|
||||||
|
- 1, 0, 0, 1
|
||||||
|
- 1, 0, 0, 0
|
||||||
|
- type: PointLight
|
||||||
|
radius: 3.5
|
||||||
|
color: red
|
||||||
|
energy: 1
|
||||||
|
- type: Reflective
|
||||||
|
reflective:
|
||||||
|
- Energy
|
||||||
|
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
name: practice laser bolt
|
||||||
|
id: BulletTrailLaserPractice
|
||||||
|
parent: BaseBullet
|
||||||
|
noSpawn: true
|
||||||
|
components:
|
||||||
|
- type: Sprite
|
||||||
|
sprite: Objects/Weapons/Guns/Projectiles/projectiles_tg.rsi
|
||||||
|
layers:
|
||||||
|
- shader: unshaded
|
||||||
|
- type: Ammo
|
||||||
|
muzzleFlash: null
|
||||||
|
- type: Physics
|
||||||
|
- type: Fixtures
|
||||||
|
fixtures:
|
||||||
|
projectile:
|
||||||
|
shape:
|
||||||
|
!type:PhysShapeAabb
|
||||||
|
bounds: "-0.2,-0.2,0.2,0.2"
|
||||||
|
hard: false
|
||||||
|
mask:
|
||||||
|
- Opaque
|
||||||
|
fly-by: *flybyfixture
|
||||||
|
- type: Projectile
|
||||||
|
# soundHit: Waiting on serv3
|
||||||
|
damage:
|
||||||
|
types:
|
||||||
|
Heat: 0
|
||||||
|
- type: TimedDespawn
|
||||||
|
lifetime: 3
|
||||||
|
- type: Trail
|
||||||
|
splineIteratorType: Linear
|
||||||
|
splineRendererType: Continuous
|
||||||
|
creationMethod: OnMove
|
||||||
|
scale: 0.05, 0.0
|
||||||
|
lifetime: 0.1
|
||||||
|
randomWalk: 0.001, 0.001
|
||||||
|
gravity: 0, 0
|
||||||
|
gradient:
|
||||||
|
- 1, 0, 0, 1
|
||||||
|
- 1, 0, 0, 0
|
||||||
|
- type: PointLight
|
||||||
|
radius: 3.5
|
||||||
|
color: red
|
||||||
|
energy: 1
|
||||||
|
- type: Reflective
|
||||||
|
reflective:
|
||||||
|
- Energy
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
name: pulse bolt
|
||||||
|
id: PulseBoltProjectile
|
||||||
|
parent: BaseBullet
|
||||||
|
noSpawn: true
|
||||||
|
components:
|
||||||
|
- type: Sprite
|
||||||
|
sprite: Objects/Weapons/Guns/Projectiles/projectiles_tg.rsi
|
||||||
|
layers:
|
||||||
|
- shader: unshaded
|
||||||
|
- type: Ammo
|
||||||
|
muzzleFlash: null
|
||||||
|
- type: Physics
|
||||||
|
- type: Fixtures
|
||||||
|
fixtures:
|
||||||
|
projectile:
|
||||||
|
shape:
|
||||||
|
!type:PhysShapeAabb
|
||||||
|
bounds: "-0.2,-0.2,0.2,0.2"
|
||||||
|
hard: false
|
||||||
|
mask:
|
||||||
|
- Opaque
|
||||||
|
fly-by: *flybyfixture
|
||||||
|
- type: Projectile
|
||||||
|
# soundHit: Waiting on serv3
|
||||||
|
damage:
|
||||||
|
types:
|
||||||
|
Heat: 35
|
||||||
|
- type: TimedDespawn
|
||||||
|
lifetime: 3
|
||||||
|
- type: Trail
|
||||||
|
splineIteratorType: Linear
|
||||||
|
splineRendererType: Continuous
|
||||||
|
creationMethod: OnMove
|
||||||
|
scale: 0.10, 0.0
|
||||||
|
lifetime: 0.1
|
||||||
|
randomWalk: 0.001, 0.001
|
||||||
|
gravity: 0, 0
|
||||||
|
gradient:
|
||||||
|
- 0, 0, 1, 1
|
||||||
|
- 0, 0, 1, 0
|
||||||
|
- type: PointLight
|
||||||
|
radius: 3.5
|
||||||
|
color: blue
|
||||||
|
energy: 1
|
||||||
|
- type: Reflective
|
||||||
|
reflective:
|
||||||
|
- Energy
|
||||||
|
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
name: heavy laser bolt
|
||||||
|
id: BulletTrailLaserHeavy
|
||||||
|
parent: BaseBullet
|
||||||
|
noSpawn: true
|
||||||
|
components:
|
||||||
|
- type: Sprite
|
||||||
|
sprite: Objects/Weapons/Guns/Projectiles/projectiles_tg.rsi
|
||||||
|
layers:
|
||||||
|
- shader: unshaded
|
||||||
|
- type: Ammo
|
||||||
|
muzzleFlash: null
|
||||||
|
- type: Physics
|
||||||
|
- type: Fixtures
|
||||||
|
fixtures:
|
||||||
|
projectile:
|
||||||
|
shape:
|
||||||
|
!type:PhysShapeAabb
|
||||||
|
bounds: "-0.2,-0.2,0.2,0.2"
|
||||||
|
hard: false
|
||||||
|
mask:
|
||||||
|
- Opaque
|
||||||
|
fly-by: *flybyfixture
|
||||||
|
- type: Projectile
|
||||||
|
# soundHit: Waiting on serv3
|
||||||
|
damage:
|
||||||
|
types:
|
||||||
|
Heat: 35
|
||||||
|
- type: TimedDespawn
|
||||||
|
lifetime: 3
|
||||||
|
- type: Trail
|
||||||
|
splineIteratorType: Linear
|
||||||
|
splineRendererType: Continuous
|
||||||
|
creationMethod: OnMove
|
||||||
|
scale: 0.11, 0.0
|
||||||
|
lifetime: 0.1
|
||||||
|
randomWalk: 0.001, 0.001
|
||||||
|
gravity: 0, 0
|
||||||
|
gradient:
|
||||||
|
- 1, 0, 0, 1
|
||||||
|
- 1, 0, 0, 0
|
||||||
|
- type: PointLight
|
||||||
|
radius: 3.5
|
||||||
|
color: red
|
||||||
|
energy: 1
|
||||||
|
- type: Reflective
|
||||||
|
reflective:
|
||||||
|
- Energy
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
name: xray laser bolt
|
||||||
|
id: BulletTrailLaserXray
|
||||||
|
parent: BaseBullet
|
||||||
|
noSpawn: true
|
||||||
|
components:
|
||||||
|
- type: Sprite
|
||||||
|
sprite: Objects/Weapons/Guns/Projectiles/projectiles_tg.rsi
|
||||||
|
layers:
|
||||||
|
- shader: unshaded
|
||||||
|
- type: Ammo
|
||||||
|
muzzleFlash: null
|
||||||
|
- type: Physics
|
||||||
|
- type: Fixtures
|
||||||
|
fixtures:
|
||||||
|
projectile:
|
||||||
|
shape:
|
||||||
|
!type:PhysShapeAabb
|
||||||
|
bounds: "-0.2,-0.2,0.2,0.2"
|
||||||
|
hard: false
|
||||||
|
mask:
|
||||||
|
- Opaque
|
||||||
|
fly-by: *flybyfixture
|
||||||
|
- type: Projectile
|
||||||
|
# soundHit: Waiting on serv3
|
||||||
|
damage:
|
||||||
|
types:
|
||||||
|
Heat: 14
|
||||||
|
Radiation: 14
|
||||||
|
- type: TimedDespawn
|
||||||
|
lifetime: 3
|
||||||
|
- type: Trail
|
||||||
|
splineIteratorType: Linear
|
||||||
|
splineRendererType: Continuous
|
||||||
|
creationMethod: OnMove
|
||||||
|
scale: 0.11, 0.0
|
||||||
|
lifetime: 0.1
|
||||||
|
randomWalk: 0.001, 0.001
|
||||||
|
gravity: 0, 0
|
||||||
|
gradient:
|
||||||
|
- 0, 1, 0, 1
|
||||||
|
- 0, 1, 0, 0
|
||||||
|
- type: PointLight
|
||||||
|
radius: 3.5
|
||||||
|
color: green
|
||||||
|
energy: 1
|
||||||
|
- type: Reflective
|
||||||
|
reflective:
|
||||||
|
- Energy
|
||||||
|
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
name: medium laser bolt
|
||||||
|
id: BulletTrailLaserMedium
|
||||||
|
parent: BaseBullet
|
||||||
|
noSpawn: true
|
||||||
|
components:
|
||||||
|
- type: Sprite
|
||||||
|
sprite: Objects/Weapons/Guns/Projectiles/projectiles_tg.rsi
|
||||||
|
layers:
|
||||||
|
- shader: unshaded
|
||||||
|
- type: Ammo
|
||||||
|
muzzleFlash: null
|
||||||
|
- type: Physics
|
||||||
|
- type: Fixtures
|
||||||
|
fixtures:
|
||||||
|
projectile:
|
||||||
|
shape:
|
||||||
|
!type:PhysShapeAabb
|
||||||
|
bounds: "-0.2,-0.2,0.2,0.2"
|
||||||
|
hard: false
|
||||||
|
mask:
|
||||||
|
- Opaque
|
||||||
|
fly-by: *flybyfixture
|
||||||
|
- type: Projectile
|
||||||
|
# soundHit: Waiting on serv3
|
||||||
|
damage:
|
||||||
|
types:
|
||||||
|
Heat: 23
|
||||||
|
- type: TimedDespawn
|
||||||
|
lifetime: 3
|
||||||
|
- type: Trail
|
||||||
|
splineIteratorType: Linear
|
||||||
|
splineRendererType: Continuous
|
||||||
|
creationMethod: OnMove
|
||||||
|
scale: 0.05, 0.0
|
||||||
|
lifetime: 0.1
|
||||||
|
randomWalk: 0.001, 0.001
|
||||||
|
gravity: 0, 0
|
||||||
|
gradient:
|
||||||
|
- 1, 0, 0, 1
|
||||||
|
- 1, 0, 0, 0
|
||||||
|
- type: PointLight
|
||||||
|
radius: 3.5
|
||||||
|
color: red
|
||||||
|
energy: 1
|
||||||
|
- type: Reflective
|
||||||
|
reflective:
|
||||||
|
- Energy
|
||||||
|
|||||||
Reference in New Issue
Block a user