diff --git a/Content.Client/Shuttles/UI/ShuttleNavControl.xaml.cs b/Content.Client/Shuttles/UI/ShuttleNavControl.xaml.cs
index f2c68d4959..b896ecc49e 100644
--- a/Content.Client/Shuttles/UI/ShuttleNavControl.xaml.cs
+++ b/Content.Client/Shuttles/UI/ShuttleNavControl.xaml.cs
@@ -1,6 +1,7 @@
using System;
using System.Numerics;
using Content.Client.Station; // Frontier
+using Content.Client.Weapons.Ranged.Systems;
using Content.Shared.Projectiles;
using Content.Shared.Shuttles.BUIStates;
using Content.Shared.Shuttles.Components;
@@ -29,6 +30,7 @@ public sealed partial class ShuttleNavControl : BaseShuttleControl
private readonly SharedShuttleSystem _shuttles;
private readonly SharedTransformSystem _transform;
private readonly EntityLookupSystem _lookup; // WD EDIT
+ private readonly GunSystem _gun; // WD EDIT
///
/// Used to transform all of the radar objects. Typically is a shuttle console parented to a grid.
@@ -57,6 +59,7 @@ public sealed partial class ShuttleNavControl : BaseShuttleControl
_transform = EntManager.System();
_station = EntManager.System(); // Frontier
_lookup = EntManager.System(); // WWDP EDIT
+ _gun = EntManager.System(); // WWDP EDIT
}
public void SetMatrix(EntityCoordinates? coordinates, Angle? angle)
@@ -141,7 +144,7 @@ public sealed partial class ShuttleNavControl : BaseShuttleControl
var fixturesQuery = EntManager.GetEntityQuery();
var bodyQuery = EntManager.GetEntityQuery();
- EntManager.TryGetComponent(_coordinates.Value.EntityId, out var ourGunComp); // WD EDIT
+ _gun.TryGetGun(_coordinates.Value.EntityId, out _, out var ourGunComp); // WWDP EDIT
if (!xformQuery.TryGetComponent(_coordinates.Value.EntityId, out var xform)
|| xform.MapID == MapId.Nullspace)
diff --git a/Content.Client/Weapons/Ranged/Systems/GunSystem.cs b/Content.Client/Weapons/Ranged/Systems/GunSystem.cs
index 58eb70e0a8..e67c144d98 100644
--- a/Content.Client/Weapons/Ranged/Systems/GunSystem.cs
+++ b/Content.Client/Weapons/Ranged/Systems/GunSystem.cs
@@ -157,15 +157,8 @@ public sealed partial class GunSystem : SharedGunSystem
if (a.Sprite is not SpriteSpecifier.Rsi rsi)
continue;
- var coords = GetCoordinates(a.coordinates);
-
- if (Deleted(coords.EntityId))
- continue;
-
- var ent = Spawn(HitscanProto, coords);
+ var ent = Spawn(HitscanProto, a.coordinates, rotation: a.angle); // WWDP EDIT
var sprite = Comp(ent);
- var xform = Transform(ent);
- xform.LocalRotation = a.angle;
sprite[EffectLayers.Unshaded].AutoAnimated = false;
sprite.LayerSetSprite(EffectLayers.Unshaded, rsi);
sprite.LayerSetState(EffectLayers.Unshaded, rsi.RsiState);
diff --git a/Content.Client/_White/DollyMixture/DollyMixtureComponent.cs b/Content.Client/_White/DollyMixture/DollyMixtureComponent.cs
deleted file mode 100644
index 1e6501778b..0000000000
--- a/Content.Client/_White/DollyMixture/DollyMixtureComponent.cs
+++ /dev/null
@@ -1,39 +0,0 @@
-using Robust.Client.Graphics;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Numerics;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace Content.Client._White.DollyMixture;
-
-[RegisterComponent]
-public sealed partial class DollyMixtureComponent : Component
-{
- [DataField("sprite")]
- public string RSIPath = "";
-
- [DataField(required: true)]
- public List States = default!;
-
- public RSI? RSI;
-
- [DataField]
- public Vector2 LayerOffset = new(0, 1);
-
- [DataField]
- public Vector2 Offset;
-
- public Angle LastAngle;
- public List LayerIndices = new();
-
- [DataField]
- public int RepeatLayers = 1;
-
- [DataField]
- public Vector2 LayerScale = Vector2.One;
-
- [DataField]
- public string? DefaultShader;
-}
diff --git a/Content.Client/_White/DollyMixture/DollyMixtureSystem.cs b/Content.Client/_White/DollyMixture/DollyMixtureSystem.cs
index d0e563bfae..3c4771682a 100644
--- a/Content.Client/_White/DollyMixture/DollyMixtureSystem.cs
+++ b/Content.Client/_White/DollyMixture/DollyMixtureSystem.cs
@@ -1,19 +1,25 @@
+using Content.Shared._White.DollyMixture;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Client.ResourceManagement;
+using Robust.Shared.Timing;
using System.Numerics;
namespace Content.Client._White.DollyMixture;
-public sealed class DollyMixtureSystem : EntitySystem
+public sealed class DollyMixtureSystem : SharedDollyMixtureSystem
{
[Dependency] private readonly IResourceCache _res = default!;
[Dependency] private readonly IEyeManager _eye = default!;
[Dependency] private readonly SharedTransformSystem _xform = default!;
+ [Dependency] private readonly IGameTiming _timing = default!;
public override void Initialize()
{
+ base.Initialize();
SubscribeLocalEvent(OnInit);
+ SubscribeLocalEvent(OnRemove);
+ SubscribeLocalEvent(OnAutoState);
}
public override void FrameUpdate(float frameTime)
@@ -22,85 +28,153 @@ public sealed class DollyMixtureSystem : EntitySystem
while (query.MoveNext(out var uid, out var dollymix, out var sprite, out var xform))
{
Angle angle = _xform.GetWorldRotation(xform) + _eye.CurrentEye.Rotation;
+ if (dollymix.DirectionCount > 0)
+ angle = Math.Round(angle / Math.Tau * dollymix.DirectionCount) * Math.Tau / dollymix.DirectionCount;
+
const float MinAngleDelta = MathF.PI / 180 * 0.01f;
if (MathHelper.CloseTo(dollymix.LastAngle, angle, MinAngleDelta))
continue;
dollymix.LastAngle = angle;
- for (int i = 0; i < dollymix.LayerIndices.Count; i++)
- {
- sprite.LayerSetRotation(dollymix.LayerIndices[i], angle);
- }
+ for (int i = 0; i < dollymix.LayerMappings.Count; i++)
+ sprite.LayerSetRotation(dollymix.LayerMappings[i], angle);
}
}
+ private void OnAutoState(EntityUid uid, DollyMixtureComponent comp, AfterAutoHandleStateEvent args)
+ {
+ UpdateDollyMixture(uid, comp);
+ }
+
+ private void UpdateDollyMixture(EntityUid uid, DollyMixtureComponent comp)
+ {
+ if (comp.CurrentRSIPath == comp.RSIPath)
+ return;
+
+ if (comp.RSIPath is null)
+ {
+ RemoveLayers(uid, comp);
+ return;
+ }
+
+ if (comp.CurrentRSIPath is not null)
+ RemoveLayers(uid, comp);
+
+ BuildLayers(uid, comp);
+ }
+
+ private void OnRemove(EntityUid uid, DollyMixtureComponent comp, ComponentRemove args)
+ {
+ if (!_timing.IsFirstTimePredicted)
+ return;
+
+ RemoveLayers(uid, comp);
+ }
+
// more than half of this method is handling missing shit. gg.
private void OnInit(EntityUid uid, DollyMixtureComponent comp, ComponentInit args)
{
- if (!TryComp(uid, out var sprite))
- {
+ if (!_timing.IsFirstTimePredicted)
+ return;
+
+ if (!TryComp(uid, out var sprite)) // unlike OnRemove() and RemoveLayers(), this doesn't get executed when placing a prototype
+ { // i cry
Log.Error($"Failed to get SpriteComponent for {ToPrettyString(uid)}. Removing DollyMixtureComponent.");
RemComp(uid);
return;
}
- if (!TryComp(uid, out var xform))
+ var xform = Transform(uid);
+ sprite.NoRotation = true;
+
+ if (comp.RSIPath is not null)
+ BuildLayers(uid, comp, sprite);
+ }
+
+ public override void Apply3D(EntityUid uid, string RsiPath, string? statePrefix = null, Vector2? layerOffset = null, DollyMixtureComponent? comp = null)
+ {
+ comp ??= EnsureComp(uid);
+
+ base.Apply3D(uid, RsiPath, statePrefix, layerOffset, comp);
+ UpdateDollyMixture(uid, comp);
+ }
+
+ public override void Remove3D(EntityUid uid, DollyMixtureComponent? comp = null)
+ {
+ if (!Resolve(uid, ref comp))
+ return;
+
+ base.Remove3D(uid, comp);
+ UpdateDollyMixture(uid, comp);
+ }
+
+ private void RemoveLayers(EntityUid uid, DollyMixtureComponent comp)
+ {
+ SpriteComponent? sprite = null;
+ if (!Resolve(uid, ref sprite, false)) // this gets executed after simply placing a prototype with this comp
+ return; // i assume it is some prediction-related bullshit
+ foreach (var layerMapping in comp.LayerMappings)
+ sprite.RemoveLayer(layerMapping);
+ comp.CurrentRSIPath = null;
+ comp.LayerMappings.Clear();
+ }
+
+ private void BuildLayers(EntityUid uid, DollyMixtureComponent comp, SpriteComponent? sprite = null)
+ {
+ if (string.IsNullOrEmpty(comp.RSIPath))
{
- Log.Error($"Failed to get SpriteComponent for {ToPrettyString(uid)}. Removing DollyMixtureComponent.");
- RemComp(uid);
+ Log.Error($"An empty rsi path was passed to BuildLayers().");
return;
}
- RSIResource? RSIres = null;
- if (!string.IsNullOrEmpty(comp.RSIPath) && !_res.TryGetResource($"/Textures/{comp.RSIPath}", out RSIres))
+ if (!Resolve(uid, ref sprite, false))
+ return;
+
+ var xform = Transform(uid);
+ if (!_res.TryGetResource($"/Textures/{comp.RSIPath}", out RSIResource? RSIres))
{
- Log.Error($"Failed to get RSI {$"/Textures/{comp.RSIPath}"} for a dolly mixture component. Removing component. ({ToPrettyString(uid)})");
- RemComp(uid);
+ Log.Error($"Failed to get RSI {$"/Textures/{comp.RSIPath}"} for a dolly mixture component.");
return;
}
- var RSI = RSIres?.RSI ?? sprite.BaseRSI;
- if(RSI is null)
- {
- Log.Error($"No RSI specified for both DollyMixtureComponent and SpriteComponent. Removing DollyMixtureComponent. ({ToPrettyString(uid)})");
- RemComp(uid);
- return;
- }
+ var RSI = RSIres.RSI;
- comp.RSI = RSI;
- comp.LayerIndices = new(comp.States.Count);
-
- for (int i = 0; i < comp.States.Count; i++)
+ int i = 1;
+ while (RSI.TryGetState($"{comp.StatePrefix}{i}", out var state))
{
- string stateId = comp.States[i];
- for (int repeat = 0; repeat < comp.RepeatLayers; repeat++)
+ for (int repeat = 0; repeat <= comp.RepeatLayers; repeat++)
{
- Vector2 layerOffset = comp.Offset / EyeManager.PixelsPerMeter + comp.LayerOffset / EyeManager.PixelsPerMeter * i;
- layerOffset += comp.LayerOffset * ((float)repeat / comp.RepeatLayers);
+ float fraction = comp.RepeatLayers > 0 ? (float) repeat / comp.RepeatLayers : 0f;
+
+ Vector2 layerOffset = comp.Offset / EyeManager.PixelsPerMeter + comp.LayerOffset / EyeManager.PixelsPerMeter * (i - 1 + fraction);
int layerIndex = sprite.AddBlankLayer();
sprite.LayerSetRSI(layerIndex, RSI);
- sprite.LayerSetState(layerIndex, stateId);
+ sprite.LayerSetState(layerIndex, state.StateId);
sprite.LayerSetOffset(layerIndex, layerOffset);
- sprite.LayerSetScale(layerIndex, comp.LayerScale);
sprite.LayerSetRotation(layerIndex, xform.LocalRotation + _eye.CurrentEye.Rotation);
- if (comp.DefaultShader is not null)
- sprite.LayerSetShader(layerIndex, comp.DefaultShader); // crutch, need to assign a proper datadef for each dollymix layer instead of this.
- comp.LayerIndices.Add(layerIndex);
+ if (comp.DefaultShader is string defaultshader)
+ sprite.LayerSetShader(layerIndex, defaultshader); // crutch for customghosts
+ string layerMap = $"dmm-{comp.StatePrefix}{i}({repeat}/{comp.RepeatLayers})";
+ sprite.LayerMapSet(layerMap, layerIndex);
+ comp.LayerMappings.Add(layerMap);
- if (sprite.BaseRSI?.TryGetState($"{stateId}-unshaded", out var unshadedState) ?? false) // todo: assign a proper datadef for each dollymix layer instead of this.
+ if (RSI.TryGetState($"{comp.StatePrefix}{i}-unshaded", out var unshadedState))
{
layerIndex = sprite.AddBlankLayer();
sprite.LayerSetRSI(layerIndex, RSI);
- sprite.LayerSetState(layerIndex, $"{stateId}-unshaded");
- sprite.LayerSetShader(layerIndex, "unshaded");
+ sprite.LayerSetState(layerIndex, unshadedState.StateId);
sprite.LayerSetOffset(layerIndex, layerOffset);
- sprite.LayerSetScale(layerIndex, comp.LayerScale);
sprite.LayerSetRotation(layerIndex, xform.LocalRotation + _eye.CurrentEye.Rotation);
- comp.LayerIndices.Add(layerIndex);
+ sprite.LayerSetShader(layerIndex, "unshaded");
+ layerMap = $"{layerMap}u";
+ sprite.LayerMapSet(layerMap, layerIndex);
+ comp.LayerMappings.Add(layerMap);
}
}
+ i++;
}
- //sprite.GranularLayersRendering = true;
+ comp.CurrentRSIPath = comp.RSIPath;
}
}
+
diff --git a/Content.Client/_White/Guns/ModularTurretSystem.cs b/Content.Client/_White/Guns/ModularTurretSystem.cs
new file mode 100644
index 0000000000..9db7a3b8f4
--- /dev/null
+++ b/Content.Client/_White/Guns/ModularTurretSystem.cs
@@ -0,0 +1,5 @@
+using Content.Shared._White.Guns.ModularTurret;
+
+namespace Content.Client._White.Guns;
+
+public sealed class ModularTurretSystem : SharedModularTurretSystem;
diff --git a/Content.Server/Entry/IgnoredComponents.cs b/Content.Server/Entry/IgnoredComponents.cs
index 13d7f6a600..3affeaa89c 100644
--- a/Content.Server/Entry/IgnoredComponents.cs
+++ b/Content.Server/Entry/IgnoredComponents.cs
@@ -21,7 +21,6 @@ namespace Content.Server.Entry
"HolidayRsiSwap",
"OptionsVisualizer",
"ToggleableLightWieldable", // Goobstation
- "DollyMixture", // WWDP EDIT
"ItemSlotRenderer", // WWDP EDIT
};
}
diff --git a/Content.Server/Explosion/Components/SpawnOnTriggerComponent.cs b/Content.Server/Explosion/Components/SpawnOnTriggerComponent.cs
index a8b36fbd84..0aa00c0abd 100644
--- a/Content.Server/Explosion/Components/SpawnOnTriggerComponent.cs
+++ b/Content.Server/Explosion/Components/SpawnOnTriggerComponent.cs
@@ -1,6 +1,7 @@
using Content.Server.Explosion.EntitySystems;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
+using System.Numerics;
namespace Content.Server.Explosion.Components;
@@ -9,4 +10,9 @@ public sealed partial class SpawnOnTriggerComponent : Component
{
[ViewVariables(VVAccess.ReadWrite), DataField("proto", required: true, customTypeSerializer:typeof(PrototypeIdSerializer))]
public string Proto = string.Empty;
+
+ // WWDP EDIT START
+ [DataField]
+ public List Offsets = new() { Vector2.Zero };
+ // WWDP EDIT END
}
diff --git a/Content.Server/Explosion/EntitySystems/TriggerSystem.cs b/Content.Server/Explosion/EntitySystems/TriggerSystem.cs
index a18f3ef725..d02fe9aed6 100644
--- a/Content.Server/Explosion/EntitySystems/TriggerSystem.cs
+++ b/Content.Server/Explosion/EntitySystems/TriggerSystem.cs
@@ -141,8 +141,15 @@ namespace Content.Server.Explosion.EntitySystems
if (!coords.IsValid(EntityManager))
return;
-
- Spawn(component.Proto, coords);
+ // WWDP EDIT START
+ if(component.Offsets.Count == 0)
+ {
+ Log.Warning($"SpawnOnTriggerComponent on {ToPrettyString(uid)} has empty offsets.");
+ return;
+ }
+ foreach(var offset in component.Offsets)
+ Spawn(component.Proto, new Robust.Shared.Map.EntityCoordinates(uid, offset));
+ // WWDP EDIT END
}
private void HandleExplodeTrigger(EntityUid uid, ExplodeOnTriggerComponent component, TriggerEvent args)
diff --git a/Content.Server/Language/LanguageSystem.cs b/Content.Server/Language/LanguageSystem.cs
index 7b40d895d2..932bf27f7d 100644
--- a/Content.Server/Language/LanguageSystem.cs
+++ b/Content.Server/Language/LanguageSystem.cs
@@ -76,7 +76,7 @@ public sealed partial class LanguageSystem : SharedLanguageSystem
// WWDP EDIT START
// quick fix
// todo: reimplement as an event handler on RemoteControllableComponent.
- if(TryComp(ent.Owner, out var remoteControlTarget) && remoteControlTarget.User is { } controller)
+ if(TryComp(ent.Owner, out var remoteControlTarget) && remoteControlTarget.User is { } controller)
return CanUnderstand(controller, language);
// WWDP EDIT END
return Resolve(ent, ref ent.Comp, logMissing: false) && ent.Comp.UnderstoodLanguages.Contains(language);
@@ -89,7 +89,7 @@ public sealed partial class LanguageSystem : SharedLanguageSystem
// WWDP EDIT START
// quick fix
// todo: reimplement as an event handler on RemoteControllableComponent.
- if (TryComp(ent.Owner, out var remoteControlTarget) && remoteControlTarget.User is { } controller)
+ if (TryComp(ent.Owner, out var remoteControlTarget) && remoteControlTarget.User is { } controller)
return CanSpeak(controller, language);
// WWDP EDIT END
return ent.Comp.SpokenLanguages.Contains(language);
diff --git a/Content.Server/Power/EntitySystems/UpgradeBatterySystem.cs b/Content.Server/Power/EntitySystems/UpgradeBatterySystem.cs
index 734cf9d89c..efe60462a5 100644
--- a/Content.Server/Power/EntitySystems/UpgradeBatterySystem.cs
+++ b/Content.Server/Power/EntitySystems/UpgradeBatterySystem.cs
@@ -23,7 +23,7 @@ namespace Content.Server.Power.EntitySystems
if (TryComp(uid, out var batteryComp))
{
- _batterySystem.SetMaxCharge(uid, MathF.Pow(component.MaxChargeMultiplier, powerCellRating - 1) * component.BaseMaxCharge, batteryComp);
+ _batterySystem.SetMaxCharge(uid, MathF.Pow(component.MaxChargeMultiplier, powerCellRating) * component.BaseMaxCharge, batteryComp);
}
}
diff --git a/Content.Server/Salvage/SalvageSystem.Magnet.cs b/Content.Server/Salvage/SalvageSystem.Magnet.cs
index 7d7845bf68..0ca3282e2a 100644
--- a/Content.Server/Salvage/SalvageSystem.Magnet.cs
+++ b/Content.Server/Salvage/SalvageSystem.Magnet.cs
@@ -1,6 +1,7 @@
using System.Linq;
using System.Numerics;
using System.Threading.Tasks;
+using Content.Server.Popups;
using Content.Server.Salvage.Magnet;
using Content.Shared._White.CCVar;
using Content.Shared.Humanoid;
diff --git a/Content.Server/Weapons/Ranged/Systems/GunSystem.Battery.cs b/Content.Server/Weapons/Ranged/Systems/GunSystem.Battery.cs
index cc9af0e490..72990110f3 100644
--- a/Content.Server/Weapons/Ranged/Systems/GunSystem.Battery.cs
+++ b/Content.Server/Weapons/Ranged/Systems/GunSystem.Battery.cs
@@ -1,12 +1,16 @@
using Content.Server.Power.Components;
+using Content.Server.Power.EntitySystems;
using Content.Server.Temperature.Components;
using Content.Server.Temperature.Systems;
+using Content.Shared._White.Guns;
using Content.Shared.Damage;
using Content.Shared.Damage.Events;
using Content.Shared.FixedPoint;
+using Content.Shared.PowerCell.Components;
using Content.Shared.Projectiles;
using Content.Shared.Weapons.Ranged;
using Content.Shared.Weapons.Ranged.Components;
+using Robust.Shared.Containers;
using Robust.Shared.Prototypes;
namespace Content.Server.Weapons.Ranged.Systems;
@@ -28,6 +32,18 @@ public sealed partial class GunSystem
SubscribeLocalEvent(OnBatteryStartup);
SubscribeLocalEvent(OnBatteryChargeChange);
SubscribeLocalEvent(OnBatteryDamageExamine);
+ //// WWDP EDIT START
+ SubscribeLocalEvent(OnBatteryStartup);
+ SubscribeLocalEvent(OnContainerBatteryInserted);
+ SubscribeLocalEvent(OnContainerBatteryRemoved);
+ SubscribeLocalEvent(OnBatteryDamageExamine);
+ SubscribeLocalEvent(OnBatteryChargeChangeTracker);
+
+ SubscribeLocalEvent(OnBatteryStartup);
+ SubscribeLocalEvent(OnContainerBatteryInserted);
+ SubscribeLocalEvent(OnContainerBatteryRemoved);
+ SubscribeLocalEvent(OnBatteryDamageExamine);
+ //// WWDP EDIT END
}
private void OnBatteryStartup(EntityUid uid, BatteryAmmoProviderComponent component, ComponentStartup args)
@@ -42,7 +58,8 @@ public sealed partial class GunSystem
public void UpdateShots(EntityUid uid, BatteryAmmoProviderComponent component) // WWDP EDIT - private -> public
{
- if (!TryComp(uid, out var battery))
+ var batteryUid = component is ContainerBatteryAmmoProviderComponent ? Transform(uid).ParentUid : uid; // WWDP EDIT
+ if (!TryComp(batteryUid, out var battery)) // WWDP EDIT
return;
UpdateShots(uid, component, battery.CurrentCharge, battery.MaxCharge);
@@ -108,7 +125,44 @@ public sealed partial class GunSystem
protected override void TakeCharge(EntityUid uid, BatteryAmmoProviderComponent component)
{
+ if(component is ContainerBatteryAmmoProviderComponent comp)
+ {
+ uid = comp.Linked!.Value; // the validity of this should be enforced by EntGotInserted/Removed event handlers below.
+ }
// Will raise ChargeChangedEvent
_battery.UseCharge(uid, component.FireCost);
}
+
+ // WWDP EDIT START
+ private void OnContainerBatteryInserted(EntityUid uid, ContainerBatteryAmmoProviderComponent comp, EntGotInsertedIntoContainerMessage args)
+ {
+ var contUid = args.Container.Owner;
+ var tracker = EnsureComp(contUid);
+ tracker.Linked.Add(uid);
+ comp.Linked = contUid;
+ UpdateShots(uid, comp);
+ }
+
+ private void OnContainerBatteryRemoved(EntityUid uid, ContainerBatteryAmmoProviderComponent comp, EntGotRemovedFromContainerMessage args)
+ {
+ var contUid = args.Container.Owner;
+ var tracker = Comp(contUid); // intended to throw on failure, should not happen
+ tracker.Linked.Remove(uid);
+ comp.Linked = null;
+ if (tracker.Linked.Count == 0)
+ RemComp(contUid, tracker);
+ UpdateShots(uid, comp, 0, 0);
+ }
+
+ private void OnBatteryChargeChangeTracker(EntityUid uid, ContainerBatteryAmmoTrackerComponent comp, ref ChargeChangedEvent args)
+ {
+ foreach (var gunUid in comp.Linked)
+ {
+ if(TryComp(gunUid, out var projectileAmmoProvider))
+ UpdateShots(gunUid, projectileAmmoProvider, args.Charge, args.MaxCharge);
+ else if(TryComp(gunUid, out var hitscanAmmoProvider))
+ UpdateShots(gunUid, hitscanAmmoProvider, args.Charge, args.MaxCharge);
+ }
+ }
+ // WWDP EDIT END
}
diff --git a/Content.Server/Weapons/Ranged/Systems/GunSystem.cs b/Content.Server/Weapons/Ranged/Systems/GunSystem.cs
index 8ab8fcc9df..c2b4d92643 100644
--- a/Content.Server/Weapons/Ranged/Systems/GunSystem.cs
+++ b/Content.Server/Weapons/Ranged/Systems/GunSystem.cs
@@ -25,8 +25,8 @@ using Robust.Shared.Player;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
using Robust.Shared.Containers;
-using Content.Shared._Lavaland.Weapons.Ranged.Events;
-using ProjectileShotEvent = Content.Shared._Lavaland.Weapons.Ranged.Events.ProjectileShotEvent; // Lavaland Change
+using ProjectileShotEvent = Content.Shared._Lavaland.Weapons.Ranged.Events.ProjectileShotEvent;
+using Content.Shared.Mech.Components;
namespace Content.Server.Weapons.Ranged.Systems;
@@ -71,6 +71,13 @@ public sealed partial class GunSystem : SharedGunSystem
EntityCoordinates fromCoordinates, EntityCoordinates toCoordinates, out bool userImpulse, EntityUid? user = null, bool throwItems = false)
{
userImpulse = true;
+ // WWDP EDIT START // All code related to mechs is fucking vile.
+ var trueUser = user;
+ if(TryComp(user, out var mechComp))
+ {
+ trueUser = mechComp.PilotSlot.ContainedEntity;
+ }
+ // WWDP EDIT END
if (user != null)
{
@@ -157,66 +164,64 @@ public sealed partial class GunSystem : SharedGunSystem
EntityUid? lastHit = null;
var from = fromMap;
- // can't use map coords above because funny FireEffects
- var fromEffect = fromCoordinates;
+ // can't use map coords above because funny FireEffects // my brother in christ, you wrote that method
var dir = mapDirection.Normalized();
-
//in the situation when user == null, means that the cannon fires on its own (via signals). And we need the gun to not fire by itself in this case
var lastUser = user ?? gunUid;
-
- if (hitscan.Reflective != ReflectType.None)
+ RayCastResults? lastResult = null; // WWDP EDIT
+ for (var reflectAttempt = 0; reflectAttempt < 3; reflectAttempt++)
{
- for (var reflectAttempt = 0; reflectAttempt < 3; reflectAttempt++)
+ var ray = new CollisionRay(from.Position, dir, hitscan.CollisionMask);
+ var rayCastResults =
+ Physics.IntersectRay(from.MapId, ray, hitscan.MaxLength, lastUser, false).ToList();
+ if (!rayCastResults.Any())
+ break;
+
+ var raycastEvent = new HitScanAfterRayCastEvent(rayCastResults);
+ RaiseLocalEvent(lastUser, ref raycastEvent);
+
+ if (raycastEvent.RayCastResults == null)
+ break;
+
+ var result = raycastEvent.RayCastResults[0];
+ lastResult = result; // WWDP EDIT
+ // Check if laser is shot from in a container
+ if (!_container.IsEntityOrParentInContainer(lastUser))
{
- var ray = new CollisionRay(from.Position, dir, hitscan.CollisionMask);
- var rayCastResults =
- Physics.IntersectRay(from.MapId, ray, hitscan.MaxLength, lastUser, false).ToList();
- if (!rayCastResults.Any())
- break;
-
- var raycastEvent = new HitScanAfterRayCastEvent(rayCastResults);
- RaiseLocalEvent(lastUser, ref raycastEvent);
-
- if (raycastEvent.RayCastResults == null)
- break;
-
- var result = raycastEvent.RayCastResults[0];
-
- // Check if laser is shot from in a container
- if (!_container.IsEntityOrParentInContainer(lastUser))
+ // Checks if the laser should pass over unless targeted by its user
+ foreach (var collide in rayCastResults)
{
- // Checks if the laser should pass over unless targeted by its user
- foreach (var collide in rayCastResults)
+ if (collide.HitEntity != gun.Target &&
+ CompOrNull(collide.HitEntity)?.Active == true)
{
- if (collide.HitEntity != gun.Target &&
- CompOrNull(collide.HitEntity)?.Active == true)
- {
- continue;
- }
-
- result = collide;
- break;
+ continue;
}
- }
- var hit = result.HitEntity;
- lastHit = hit;
-
- FireEffects(fromEffect, result.Distance, dir.Normalized().ToAngle(), hitscan, hit);
-
- var ev = new HitScanReflectAttemptEvent(user, gunUid, hitscan.Reflective, dir, false, hitscan.Damage); // WD EDIT
- RaiseLocalEvent(hit, ref ev);
-
- if (!ev.Reflected)
+ result = collide;
break;
-
- fromEffect = Transform(hit).Coordinates;
- from = fromEffect.ToMap(EntityManager, _transform);
- dir = ev.Direction;
- lastUser = hit;
+ }
}
+
+ var hit = result.HitEntity;
+ lastHit = hit;
+
+ FireEffects(from, result.Distance, dir.Normalized().ToAngle(), hitscan, hit);
+
+ if (hitscan.Reflective == ReflectType.None) // WWDP EDIT
+ break; // WWDP EDIT
+
+ var ev = new HitScanReflectAttemptEvent(user, gunUid, hitscan.Reflective, dir, false, hitscan.Damage); // WD EDIT
+ RaiseLocalEvent(hit, ref ev);
+
+ if (!ev.Reflected)
+ break;
+
+ from = _transform.GetMapCoordinates(hit); // WWDP EDIT
+ dir = ev.Direction;
+ lastUser = hit;
}
+
if (lastHit != null)
{
var hitEntity = lastHit.Value;
@@ -246,21 +251,33 @@ public sealed partial class GunSystem : SharedGunSystem
if (user != null)
{
Logs.Add(LogType.HitScanHit,
- $"{ToPrettyString(user.Value):user} hit {hitName:target} using hitscan and dealt {dmg.GetTotal():damage} damage");
+ $"{ToPrettyString(user.Value):user} hit {hitName:target} using hitscan ({hitscan.ID}) and dealt {dmg.GetTotal():damage} damage");
}
else
{
Logs.Add(LogType.HitScanHit,
- $"{hitName:target} hit by hitscan dealing {dmg.GetTotal():damage} damage");
+ $"{hitName:target} hit by hitscan ({hitscan.ID}) dealing {dmg.GetTotal():damage} damage");
}
}
+ // WWDP EDIT START
+ if (hitscan.SpawnAtImpact is EntProtoId impactEntity)
+ {
+ Angle ang = hitscan.ImpactSpawnRandomAngle ? Random.NextAngle() : dir.ToAngle() - Math.PI/2;
+ Spawn(impactEntity, new MapCoordinates(lastResult!.Value.HitPos, from.MapId), rotation: ang);
+ }
+ // WWDP EDIT END
}
else
{
- FireEffects(fromEffect, hitscan.MaxLength, dir.ToAngle(), hitscan);
+ FireEffects(from, hitscan.MaxLength, dir.ToAngle(), hitscan);
+ // WWDP EDIT START
+ Angle ang = hitscan.ImpactSpawnRandomAngle ? Random.NextAngle() : dir.ToAngle() - Math.PI/2;
+ if (hitscan.SpawnAtMaxLength is EntProtoId impactEntity)
+ Spawn(impactEntity, from.Offset(dir * hitscan.MaxLength), rotation: ang);
+ // WWDP EDIT END
}
- Audio.PlayPredicted(gun.SoundGunshotModified, gunUid, user);
+ Audio.PlayPredicted(gun.SoundGunshotModified, gunUid, trueUser); // WWDP EDIT
break;
default:
throw new ArgumentOutOfRangeException();
@@ -330,8 +347,8 @@ public sealed partial class GunSystem : SharedGunSystem
}
SpreadBreak:
- MuzzleFlash(gunUid, ammoComp, mapDirection.ToAngle(), user);
- Audio.PlayPredicted(gun.SoundGunshotModified, gunUid, user);
+ MuzzleFlash(gunUid, ammoComp, mapDirection.ToAngle(), trueUser); // WWDP EDIT
+ Audio.PlayPredicted(gun.SoundGunshotModified, gunUid, trueUser); // WWDP EDIT
}
}
@@ -444,55 +461,37 @@ public sealed partial class GunSystem : SharedGunSystem
// TODO: Pseudo RNG so the client can predict these.
#region Hitscan effects
- private void FireEffects(EntityCoordinates fromCoordinates, float distance, Angle mapDirection, HitscanPrototype hitscan, EntityUid? hitEntity = null)
+ private void FireEffects(MapCoordinates fromCoordinates, float distance, Angle mapDirection, HitscanPrototype hitscan, EntityUid? hitEntity = null)
{
// Lord
- // Forgive me for the shitcode I am about to do
+ // Forgive me for the shitcode I am about to do // https://www.youtube.com/watch?v=sCAdVQNaDTE go fuck yourself
// Effects tempt me not
- var sprites = new List<(NetCoordinates coordinates, Angle angle, SpriteSpecifier sprite, float scale)>();
- var gridUid = fromCoordinates.GetGridUid(EntityManager);
+ var sprites = new List<(MapCoordinates coordinates, Angle angle, SpriteSpecifier sprite, float scale)>();
+
var angle = mapDirection;
- // We'll get the effects relative to the grid / map of the firer
- // Look you could probably optimise this a bit with redundant transforms at this point.
- var xformQuery = GetEntityQuery();
-
- if (xformQuery.TryGetComponent(gridUid, out var gridXform))
- {
- var (_, gridRot, gridInvMatrix) = TransformSystem.GetWorldPositionRotationInvMatrix(gridXform, xformQuery);
-
- fromCoordinates = new EntityCoordinates(gridUid.Value,
- Vector2.Transform(fromCoordinates.ToMapPos(EntityManager, TransformSystem), gridInvMatrix));
-
- // Use the fallback angle I guess?
- angle -= gridRot;
- }
-
if (distance >= 1f)
{
if (hitscan.MuzzleFlash != null)
{
var coords = fromCoordinates.Offset(angle.ToVec().Normalized() / 2);
- var netCoords = GetNetCoordinates(coords);
- sprites.Add((netCoords, angle, hitscan.MuzzleFlash, 1f));
+ sprites.Add((coords, angle, hitscan.MuzzleFlash, 1f));
}
if (hitscan.TravelFlash != null)
{
var coords = fromCoordinates.Offset(angle.ToVec() * (distance + 0.5f) / 2);
- var netCoords = GetNetCoordinates(coords);
- sprites.Add((netCoords, angle, hitscan.TravelFlash, distance - 1.5f));
+ sprites.Add((coords, angle, hitscan.TravelFlash, distance - 1.5f));
}
}
if (hitscan.ImpactFlash != null)
{
var coords = fromCoordinates.Offset(angle.ToVec() * distance);
- var netCoords = GetNetCoordinates(coords);
- sprites.Add((netCoords, angle.FlipPositive(), hitscan.ImpactFlash, 1f));
+ sprites.Add((coords, angle.FlipPositive(), hitscan.ImpactFlash, 1f));
}
if (sprites.Count > 0)
@@ -500,7 +499,7 @@ public sealed partial class GunSystem : SharedGunSystem
RaiseNetworkEvent(new HitscanEvent
{
Sprites = sprites,
- }, Filter.Pvs(fromCoordinates, entityMan: EntityManager));
+ }, Filter.Pvs(fromCoordinates));
}
}
diff --git a/Content.Server/_White/DollyMixture/DollyMixtureSystem.cs b/Content.Server/_White/DollyMixture/DollyMixtureSystem.cs
new file mode 100644
index 0000000000..4c3dd524e1
--- /dev/null
+++ b/Content.Server/_White/DollyMixture/DollyMixtureSystem.cs
@@ -0,0 +1,8 @@
+using Content.Shared._White.DollyMixture;
+
+namespace Content.Server._White.DollyMixture;
+
+public sealed class DollyMixtureSystem : SharedDollyMixtureSystem
+{
+
+}
diff --git a/Content.Server/_White/Guns/ModularTurret/ModularTurretSystem.cs b/Content.Server/_White/Guns/ModularTurret/ModularTurretSystem.cs
new file mode 100644
index 0000000000..7bfb27af20
--- /dev/null
+++ b/Content.Server/_White/Guns/ModularTurret/ModularTurretSystem.cs
@@ -0,0 +1,53 @@
+using Content.Server.Power.Components;
+using Content.Server.Power.EntitySystems;
+using Content.Shared._White.Guns.ModularTurret;
+using Content.Shared.Containers.ItemSlots;
+using Content.Shared.Weapons.Ranged.Events;
+using Content.Shared.Weapons.Ranged.Systems;
+
+namespace Content.Server._White.Guns.ModularTurret;
+
+public sealed class ModularTurretSystem : SharedModularTurretSystem
+{
+ [Dependency] private readonly BatterySystem _battery = default!;
+ [Dependency] private readonly ItemSlotsSystem _slot = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent(OnModularTurretShotAttempt);
+ SubscribeLocalEvent(OnModularTurretWeaponShot);
+ }
+
+
+ private void OnModularTurretShotAttempt(EntityUid turretUid, ModularTurretComponent comp, ref ShotAttemptedEvent args)
+ {
+ RechargeWeapon(turretUid, args.Used);
+ }
+
+ private void OnModularTurretWeaponShot(EntityUid gunUid, ModularTurretWeaponComponent comp, ref GunShotEvent args)
+ {
+ if(comp.CurrentTurretHolder is EntityUid turretUid)
+ RechargeWeapon(turretUid, gunUid);
+ }
+
+
+ private void RechargeWeapon(EntityUid turretUid, EntityUid gunUid)
+ {
+ if (!HasComp(gunUid))
+ return;
+
+ //var turretComp = Comp(turretUid);
+ if (!TryComp(gunUid, out var gunBattery) || // || !turretComp.CanChargeWeapon
+ !TryComp(turretUid, out var turretBattery))
+ return;
+
+ float missing = gunBattery.MaxCharge - gunBattery.CurrentCharge;
+
+ float recharged = -_battery.UseCharge(turretUid, missing, turretBattery);
+
+ _battery.SetCharge(gunUid, gunBattery.CurrentCharge + recharged, gunBattery);
+ }
+}
+
diff --git a/Content.Server/_White/RemoteControl/Systems/RemoteControlSystem.Console.cs b/Content.Server/_White/RemoteControl/Systems/RemoteControlSystem.Console.cs
index 5ba44fc874..bc1fbd5b50 100644
--- a/Content.Server/_White/RemoteControl/Systems/RemoteControlSystem.Console.cs
+++ b/Content.Server/_White/RemoteControl/Systems/RemoteControlSystem.Console.cs
@@ -13,12 +13,14 @@ public partial class RemoteControlSystem
{
private void InitializeConsole()
{
+ SubscribeLocalEvent(OnConsoleInit);
SubscribeLocalEvent(OnConsoleMapInit);
SubscribeLocalEvent(OnConsoleShutdown);
SubscribeLocalEvent(OnActivateInWorld);
SubscribeLocalEvent(OnUseInHand);
+ SubscribeLocalEvent(OnNewLinkAttempt);
SubscribeLocalEvent(OnNewLink);
SubscribeLocalEvent(OnPortDisconnected);
@@ -27,13 +29,14 @@ public partial class RemoteControlSystem
SubscribeLocalEvent(OnPowerChanged);
}
+ private void OnConsoleInit(EntityUid uid, RemoteControlConsoleComponent comp, ComponentInit args)
+ {
+ _link.EnsureSourcePorts(uid, SourcePortId);
+ }
+
private void OnConsoleMapInit(EntityUid uid, RemoteControlConsoleComponent component, MapInitEvent args)
{
- EntityUid? actionUid = null;
- _action.AddAction(uid, ref actionUid, component.SwitchToNextAction);
-
- if (actionUid.HasValue)
- component.SwitchToNextActionUid = actionUid.Value;
+ _action.AddAction(uid, ref component.SwitchToNextActionUid, component.SwitchToNextAction);
}
private void OnConsoleShutdown(EntityUid uid, RemoteControlConsoleComponent component, ComponentShutdown args)
@@ -50,16 +53,22 @@ public partial class RemoteControlSystem
private void OnActivateInWorld(EntityUid uid, RemoteControlConsoleComponent component, ActivateInWorldEvent args) =>
TryActivate(uid, component, args.User);
- private void OnNewLink(EntityUid uid, RemoteControlConsoleComponent component, NewLinkEvent args)
+ private void OnNewLinkAttempt(EntityUid uid, RemoteControlConsoleComponent component, LinkAttemptEvent args)
{
- if (!_whitelist.CheckBoth(args.Sink, component.Blacklist, component.Whitelist)
- || args.Source != uid
- || args.SourcePort != SourcePortId
- || args.SinkPort != SinkPortId
- || !HasComp(args.Sink))
+ if (args.Source != uid || args.SourcePort != SourcePortId)
return;
- component.LinkedEntities.Add(args.Sink);
+ if (!HasComp(args.Sink) ||
+ !_whitelist.CheckBoth(args.Sink, component.Blacklist, component.Whitelist) ||
+ args.Source != uid ||
+ args.SourcePort != SourcePortId ||
+ args.SinkPort != SinkPortId)
+ args.Cancel();
+ }
+ private void OnNewLink(EntityUid uid, RemoteControlConsoleComponent component, NewLinkEvent args)
+ {
+ if (args.Source == uid && args.SourcePort == SourcePortId)
+ component.LinkedEntities.Add(args.Sink);
}
private void OnPortDisconnected(EntityUid uid, RemoteControlConsoleComponent component, PortDisconnectedEvent args)
@@ -71,7 +80,7 @@ public partial class RemoteControlSystem
// the device link got severed while the turret was in use; either relink to another turret or kick the user out of the console.
if (component.User is { } user
- && TryComp(user, out var controlling)
+ && TryComp(user, out var controlling)
&& controlling.Target == args.RemovedPortUid)
EndRemoteControl(user, (uid, component), true);
}
@@ -90,7 +99,7 @@ public partial class RemoteControlSystem
private void TryActivate(EntityUid uid, RemoteControlConsoleComponent component, EntityUid user)
{
if (!this.IsPowered(uid, EntityManager)
- || HasComp(user)
+ || HasComp(user)
|| component.User is not null
|| component.LinkedEntities.Count == 0
|| GetFirstValid(component) is not { } target)
@@ -101,7 +110,7 @@ public partial class RemoteControlSystem
private bool TrySwitchToNextAvailable(EntityUid console, RemoteControlConsoleComponent component)
{
- if (component.User is not { } user || GetFirstValid(component, component.Target) is not {} target)
+ if (component.User is not { } user || GetFirstValid(component, component.Target) is not { } target)
return false;
component.LastIndex = component.LinkedEntities.IndexOf(target);
@@ -123,7 +132,7 @@ public partial class RemoteControlSystem
var ent = list[index];
if ((!exclude.HasValue || ent != exclude)
- && HasComp(ent)
+ && HasComp(ent)
|| TryComp(ent, out var mindContainer)
&& mindContainer.HasMind)
return ent;
diff --git a/Content.Server/_White/RemoteControl/Systems/RemoteControlSystem.Target.cs b/Content.Server/_White/RemoteControl/Systems/RemoteControlSystem.Target.cs
index b3ff840e4d..f0bfab34c3 100644
--- a/Content.Server/_White/RemoteControl/Systems/RemoteControlSystem.Target.cs
+++ b/Content.Server/_White/RemoteControl/Systems/RemoteControlSystem.Target.cs
@@ -11,19 +11,19 @@ public partial class RemoteControlSystem
{
private void InitializeTarget()
{
- SubscribeLocalEvent(OnTargetMapInit);
- SubscribeLocalEvent(OnTargetShutdown);
+ SubscribeLocalEvent(OnTargetMapInit);
+ SubscribeLocalEvent(OnTargetShutdown);
- SubscribeLocalEvent(OnTargetSpeechSourceOverride);
+ SubscribeLocalEvent(OnTargetSpeechSourceOverride);
- SubscribeLocalEvent>(GetAltVerb);
+ SubscribeLocalEvent>(GetAltVerb);
- SubscribeLocalEvent(OnTargetMobStateChanged);
+ SubscribeLocalEvent(OnTargetMobStateChanged);
- SubscribeLocalEvent(OnExitAction);
+ SubscribeLocalEvent(OnExitAction);
}
- private void OnTargetMapInit(EntityUid uid, RemoteControlTargetComponent comp, MapInitEvent args)
+ private void OnTargetMapInit(EntityUid uid, RemoteControllableComponent comp, MapInitEvent args)
{
EntityUid? actionUid = null;
_action.AddAction(uid, ref actionUid, comp.EndRemoteControlAction);
@@ -32,11 +32,11 @@ public partial class RemoteControlSystem
comp.EndRemoteControlActionUid = actionUid.Value;
}
- private void OnTargetShutdown(EntityUid uid, RemoteControlTargetComponent comp, ComponentShutdown args)
+ private void OnTargetShutdown(EntityUid uid, RemoteControllableComponent comp, ComponentShutdown args)
{
_action.RemoveAction(uid, comp.EndRemoteControlActionUid);
- if (!TryComp(comp.User, out var userComponent))
+ if (!TryComp(comp.User, out var userComponent))
return;
if (TryComp(userComponent.Console, out var consoleComponent))
@@ -45,15 +45,15 @@ public partial class RemoteControlSystem
EndRemoteControl((comp.User.Value, userComponent), (uid, comp), true);
}
- private void OnTargetSpeechSourceOverride(EntityUid uid, RemoteControlTargetComponent comp, SpeechSourceOverrideEvent args)
+ private void OnTargetSpeechSourceOverride(EntityUid uid, RemoteControllableComponent comp, SpeechSourceOverrideEvent args)
{
if (comp.User is { } user)
args.Override = user;
}
- private void GetAltVerb(EntityUid uid, RemoteControlTargetComponent component, GetVerbsEvent args)
+ private void GetAltVerb(EntityUid uid, RemoteControllableComponent component, GetVerbsEvent args)
{
- if (!component.CanManually || !args.CanAccess || !args.CanInteract || !args.CanComplexInteract)
+ if (!component.ManualControl || !args.CanAccess || !args.CanInteract || !args.CanComplexInteract)
return;
args.Verbs.Add(
@@ -82,13 +82,13 @@ public partial class RemoteControlSystem
}
- private void OnExitAction(EntityUid uid, RemoteControlTargetComponent component, RemoteControlExitActionEvent args)
+ private void OnExitAction(EntityUid uid, RemoteControllableComponent component, RemoteControlExitActionEvent args)
{
if(component.User is not null)
EndRemoteControl(component.User.Value, (uid, component));
}
- private void OnTargetMobStateChanged(EntityUid uid, RemoteControlTargetComponent component, MobStateChangedEvent args)
+ private void OnTargetMobStateChanged(EntityUid uid, RemoteControllableComponent component, MobStateChangedEvent args)
{
if (args.NewMobState != MobState.Alive && component.User.HasValue)
EndRemoteControl(component.User.Value, (uid, component), true);
diff --git a/Content.Server/_White/RemoteControl/Systems/RemoteControlSystem.User.cs b/Content.Server/_White/RemoteControl/Systems/RemoteControlSystem.User.cs
index ff8c2f0361..f09677e27b 100644
--- a/Content.Server/_White/RemoteControl/Systems/RemoteControlSystem.User.cs
+++ b/Content.Server/_White/RemoteControl/Systems/RemoteControlSystem.User.cs
@@ -7,18 +7,18 @@ public partial class RemoteControlSystem
{
private void InitializeUser()
{
- SubscribeLocalEvent(OnUserShutdown);
+ SubscribeLocalEvent(OnUserShutdown);
- SubscribeLocalEvent(OnUserMobStateChanged);
+ SubscribeLocalEvent(OnUserMobStateChanged);
}
- private void OnUserShutdown(EntityUid uid, RemoteControlUserComponent comp, ComponentShutdown args)
+ private void OnUserShutdown(EntityUid uid, RemoteControllingComponent comp, ComponentShutdown args)
{
- if (TryComp(comp.Target, out var targetComp) && targetComp.User == uid)
+ if (TryComp(comp.Target, out var targetComp) && targetComp.User == uid)
EndRemoteControl((uid, comp), (comp.Target, targetComp));
}
- private void OnUserMobStateChanged(EntityUid uid, RemoteControlUserComponent comp, MobStateChangedEvent args)
+ private void OnUserMobStateChanged(EntityUid uid, RemoteControllingComponent comp, MobStateChangedEvent args)
{
if (args.NewMobState != MobState.Alive)
EndRemoteControl((uid, comp));
diff --git a/Content.Server/_White/RemoteControl/Systems/RemoteControlSystem.cs b/Content.Server/_White/RemoteControl/Systems/RemoteControlSystem.cs
index 7a286972a5..d63e2c0d4a 100644
--- a/Content.Server/_White/RemoteControl/Systems/RemoteControlSystem.cs
+++ b/Content.Server/_White/RemoteControl/Systems/RemoteControlSystem.cs
@@ -1,5 +1,6 @@
using Content.Server.Actions;
using Content.Server.Chat.Systems;
+using Content.Server.DeviceLinking.Systems;
using Content.Server.Interaction;
using Content.Server.Mind;
using Content.Server.Popups;
@@ -13,11 +14,13 @@ using Content.Shared.Whitelist;
using Robust.Server.GameObjects;
using Robust.Shared.Player;
using Robust.Shared.Prototypes;
+using Robust.Shared.Utility;
namespace Content.Server._White.RemoteControl.Systems;
public sealed partial class RemoteControlSystem : EntitySystem
{
+ [Dependency] private readonly DeviceLinkSystem _link = default!;
[Dependency] private readonly ActionBlockerSystem _actionBlocker = default!;
[Dependency] private readonly ActionsSystem _action = default!;
[Dependency] private readonly EntityWhitelistSystem _whitelist = default!;
@@ -27,8 +30,8 @@ public sealed partial class RemoteControlSystem : EntitySystem
[Dependency] private readonly PopupSystem _popup = default!;
[Dependency] private readonly TransformSystem _transform = default!;
- public static ProtoId SinkPortId = "RemoteControlOutputPort";
- public static ProtoId SourcePortId = "RemoteControlInputPort";
+ public static ProtoId SinkPortId = "RemoteControlSinkPort";
+ public static ProtoId SourcePortId = "RemoteControlSourcePort";
public override void Initialize()
{
@@ -44,7 +47,7 @@ public sealed partial class RemoteControlSystem : EntitySystem
{
var sourceXform = Transform(args.Source);
- var query = EntityQueryEnumerator();
+ var query = EntityQueryEnumerator();
while (query.MoveNext(out _, out var comp, out var xform))
{
var range = xform.MapID != sourceXform.MapID
@@ -61,7 +64,7 @@ public sealed partial class RemoteControlSystem : EntitySystem
public override void Update(float frameTime)
{
- var query = EntityQueryEnumerator();
+ var query = EntityQueryEnumerator();
while (query.MoveNext(out var uid, out var comp))
{
if(!_actionBlocker.CanInteract(uid, comp.Console)
@@ -70,17 +73,17 @@ public sealed partial class RemoteControlSystem : EntitySystem
}
}
- private bool RemoteControl(Entity user, Entity target, Entity console, bool overlay = false)
+ private bool RemoteControl(EntityUid user, Entity target, Entity console, bool overlay = false)
{
if (!Resolve(target, ref target.Comp)
- || HasComp(user)
+ || HasComp(user)
|| HasComp(target)
|| _mind.GetMind(user) is not { } userMind)
return false;
- user.Comp = EnsureComp(user);
- user.Comp.Target = target;
- user.Comp.Console = console;
+ var userComp = EnsureComp(user);
+ userComp.Target = target;
+ userComp.Console = console;
target.Comp.User = user;
@@ -89,8 +92,8 @@ public sealed partial class RemoteControlSystem : EntitySystem
console.Comp.User = user;
console.Comp.Target = target;
console.Comp.LastIndex = console.Comp.LinkedEntities.IndexOf(target);
-
- _action.AddAction(target, console.Comp.SwitchToNextActionUid, console);
+ DebugTools.Assert(console.Comp.SwitchToNextActionUid.HasValue);
+ _action.AddAction(target, console.Comp.SwitchToNextActionUid.Value, console);
}
_mind.Visit(userMind, target);
@@ -103,7 +106,7 @@ public sealed partial class RemoteControlSystem : EntitySystem
return true;
}
- private bool EndRemoteControl(Entity user, bool shouldAutoSwitch = false)
+ private bool EndRemoteControl(Entity user, bool shouldAutoSwitch = false)
{
if (!Resolve(user, ref user.Comp))
return false;
@@ -111,11 +114,7 @@ public sealed partial class RemoteControlSystem : EntitySystem
return EndRemoteControl(user, user.Comp.Target, user.Comp.Console, shouldAutoSwitch);
}
- private bool EndRemoteControl(
- Entity user,
- Entity target,
- bool shouldAutoSwitch = false
- )
+ private bool EndRemoteControl(Entity user, Entity target, bool shouldAutoSwitch = false)
{
if (!Resolve(user, ref user.Comp) || !Resolve(target, ref target.Comp))
return false;
@@ -123,11 +122,7 @@ public sealed partial class RemoteControlSystem : EntitySystem
return EndRemoteControl(user, target, user.Comp.Console, shouldAutoSwitch);
}
- private bool EndRemoteControl(
- Entity user,
- Entity console,
- bool shouldAutoSwitch = false
- )
+ private bool EndRemoteControl(Entity user, Entity console, bool shouldAutoSwitch = false )
{
if (!Resolve(user, ref user.Comp) || !Resolve(console, ref console.Comp))
return false;
@@ -135,12 +130,7 @@ public sealed partial class RemoteControlSystem : EntitySystem
return EndRemoteControl(user, user.Comp.Target, console, shouldAutoSwitch);
}
- private bool EndRemoteControl(
- Entity user,
- Entity target,
- Entity console,
- bool shouldAutoSwitch = false
- )
+ private bool EndRemoteControl(Entity user, Entity target, Entity console, bool shouldAutoSwitch = false )
{
if (!Resolve(user, ref user.Comp)
|| !Resolve(target, ref target.Comp)
diff --git a/Content.Shared/Mech/EntitySystems/SharedMechSystem.cs b/Content.Shared/Mech/EntitySystems/SharedMechSystem.cs
index f977a2eeb4..f16ac85777 100644
--- a/Content.Shared/Mech/EntitySystems/SharedMechSystem.cs
+++ b/Content.Shared/Mech/EntitySystems/SharedMechSystem.cs
@@ -31,6 +31,7 @@ using Content.Shared.Hands.EntitySystems;
using Content.Shared.Inventory.VirtualItem;
using Robust.Shared.Configuration;
using Content.Shared.Implants.Components;
+using Content.Shared.Weapons.Ranged.Systems;
namespace Content.Shared.Mech.EntitySystems;
@@ -75,6 +76,7 @@ public abstract class SharedMechSystem : EntitySystem
SubscribeLocalEvent(OnAttackAttempt);
SubscribeLocalEvent(OnEntGotRemovedFromContainer);
SubscribeLocalEvent(OnShotAttempted); // Goobstation
+ SubscribeLocalEvent(OnGunShot); // WWDP EDIT
Subs.CVar(_config, GoobCVars.MechGunOutsideMech, value => _canUseMechGunOutside = value, true); // Goobstation
}
@@ -509,6 +511,14 @@ public abstract class SharedMechSystem : EntitySystem
RaiseLocalEvent(uid, ev);
}
+ // WWDP EDIT START
+ private void OnGunShot(EntityUid uid, MechEquipmentComponent component, ref GunShotEvent args)
+ {
+ var ev = new HandleMechEquipmentBatteryEvent();
+ RaiseLocalEvent(uid, ev);
+ }
+ // WWDP EDIT END
+
private void UpdateAppearance(EntityUid uid, MechComponent? component = null,
AppearanceComponent? appearance = null)
{
diff --git a/Content.Shared/Movement/Components/InputMoverComponent.cs b/Content.Shared/Movement/Components/InputMoverComponent.cs
index 40cb532e60..16c712f3f8 100644
--- a/Content.Shared/Movement/Components/InputMoverComponent.cs
+++ b/Content.Shared/Movement/Components/InputMoverComponent.cs
@@ -32,7 +32,7 @@ namespace Content.Shared.Movement.Components
// We change which vector we write into based on whether we were sprinting after the previous input.
// (well maybe we do but the code is designed such that MoverSystem applies movement speed)
// (and I'm not changing that)
-
+
///
/// Should our velocity be applied to our parent?
///
diff --git a/Content.Shared/Radio/Components/SharedRadioJammerComponent.cs b/Content.Shared/Radio/Components/SharedRadioJammerComponent.cs
index e5e52a3e47..4209c47b82 100644
--- a/Content.Shared/Radio/Components/SharedRadioJammerComponent.cs
+++ b/Content.Shared/Radio/Components/SharedRadioJammerComponent.cs
@@ -30,20 +30,20 @@ public sealed partial class RadioJammerComponent : Component
/// to this setting.
///
[DataField(required: true)]
- public LocId Message = string.Empty;
+ public LocId Message;
///
/// Name of the setting.
///
[DataField(required: true)]
- public LocId Name = string.Empty;
+ public LocId Name;
}
///
/// List of all the settings for the radio jammer.
///
[DataField(required: true), ViewVariables(VVAccess.ReadOnly)]
- public RadioJamSetting[] Settings;
+ public RadioJamSetting[] Settings = default!;
///
/// Index of the currently selected setting.
diff --git a/Content.Shared/Shuttles/Systems/SharedShuttleSystem.IFF.cs b/Content.Shared/Shuttles/Systems/SharedShuttleSystem.IFF.cs
index e435bc1c4c..ba0a546c77 100644
--- a/Content.Shared/Shuttles/Systems/SharedShuttleSystem.IFF.cs
+++ b/Content.Shared/Shuttles/Systems/SharedShuttleSystem.IFF.cs
@@ -87,6 +87,7 @@ public abstract partial class SharedShuttleSystem
UpdateIFFInterfaces(gridUid, component);
}
+
[PublicAPI]
public void RemoveIFFFlag(EntityUid gridUid, IFFFlags flags, IFFComponent? component = null)
{
diff --git a/Content.Shared/Weapons/Ranged/HitscanPrototype.cs b/Content.Shared/Weapons/Ranged/HitscanPrototype.cs
index a5063e3ab5..6f2163857c 100644
--- a/Content.Shared/Weapons/Ranged/HitscanPrototype.cs
+++ b/Content.Shared/Weapons/Ranged/HitscanPrototype.cs
@@ -43,6 +43,15 @@ public sealed partial class HitscanPrototype : IPrototype, IShootable
[DataField("sound")]
public SoundSpecifier? Sound;
+ [DataField]
+ public EntProtoId? SpawnAtImpact = null;
+
+ [DataField]
+ public EntProtoId? SpawnAtMaxLength = null;
+
+ [DataField]
+ public bool ImpactSpawnRandomAngle = false;
+
///
/// Force the hitscan sound to play rather than potentially playing the entity's sound.
///
diff --git a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Battery.cs b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Battery.cs
index f44273257d..c0e496e663 100644
--- a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Battery.cs
+++ b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Battery.cs
@@ -1,3 +1,4 @@
+using Content.Shared._White.Guns;
using Content.Shared.Examine;
using Content.Shared.Weapons.Ranged.Components;
using Content.Shared.Weapons.Ranged.Events;
@@ -25,6 +26,21 @@ public abstract partial class SharedGunSystem
SubscribeLocalEvent(OnBatteryTakeAmmo);
SubscribeLocalEvent(OnBatteryAmmoCount);
SubscribeLocalEvent(OnBatteryExamine);
+
+ // WWDP EDIT START
+ // Container, Projectile
+ SubscribeLocalEvent(OnBatteryGetState);
+ SubscribeLocalEvent(OnBatteryHandleState);
+ SubscribeLocalEvent(OnBatteryTakeAmmo);
+ SubscribeLocalEvent(OnBatteryAmmoCount);
+ //SubscribeLocalEvent(OnBatteryExamine); // you'll have trouble examining something inside a container
+
+ // Container, Hitscan
+ SubscribeLocalEvent(OnBatteryGetState);
+ SubscribeLocalEvent(OnBatteryHandleState);
+ SubscribeLocalEvent(OnBatteryTakeAmmo);
+ SubscribeLocalEvent(OnBatteryAmmoCount);
+ // WWDP EDIT END
}
private void OnBatteryHandleState(EntityUid uid, BatteryAmmoProviderComponent component, ref ComponentHandleState args)
@@ -109,6 +125,11 @@ public abstract partial class SharedGunSystem
return (ent, EnsureShootable(ent));
case HitscanBatteryAmmoProviderComponent hitscan:
return (null, ProtoManager.Index(hitscan.Prototype));
+ case ProjectileContainerBatteryAmmoProviderComponent proj:
+ var entc = Spawn(proj.Prototype, coordinates);
+ return (entc, EnsureShootable(entc));
+ case HitscanContainerBatteryAmmoProviderComponent hitscan:
+ return (null, ProtoManager.Index(hitscan.Prototype));
default:
throw new ArgumentOutOfRangeException();
}
diff --git a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs
index 7021f0d817..13fd2985dc 100644
--- a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs
+++ b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs
@@ -707,7 +707,7 @@ public abstract partial class SharedGunSystem : EntitySystem
[Serializable, NetSerializable]
public sealed class HitscanEvent : EntityEventArgs
{
- public List<(NetCoordinates coordinates, Angle angle, SpriteSpecifier Sprite, float Distance)> Sprites = new();
+ public List<(MapCoordinates coordinates, Angle angle, SpriteSpecifier Sprite, float Distance)> Sprites = new(); // WWDP EDIT
}
}
diff --git a/Content.Shared/_White/DollyMixture/DollyMixtureComponent.cs b/Content.Shared/_White/DollyMixture/DollyMixtureComponent.cs
new file mode 100644
index 0000000000..1e26f20ec3
--- /dev/null
+++ b/Content.Shared/_White/DollyMixture/DollyMixtureComponent.cs
@@ -0,0 +1,40 @@
+using Robust.Shared.GameStates;
+using System.Numerics;
+
+namespace Content.Shared._White.DollyMixture;
+
+
+// this now lives in shared because it allows to add this component server-side and
+// have it appear for all clients without additional syncing boilerplate
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true)]
+public sealed partial class DollyMixtureComponent : Component
+{
+ [DataField("sprite"), AutoNetworkedField]
+ public string? RSIPath = null;
+ public string? CurrentRSIPath = null;
+
+ [DataField]
+ public string StatePrefix = "dollymix";
+
+ // Crutch for customghosts, since i can't modify an existing spritecomponent through a ComponentRegistry. Or, at best, i don't know how.
+ [DataField]
+ public string? DefaultShader = null;
+
+ // 0 to disable
+ [DataField]
+ public int DirectionCount = 0;
+
+ [DataField]
+ public Vector2 LayerOffset = new(0, 1);
+
+ [DataField]
+ public Vector2 Offset;
+
+ [DataField]
+ public int RepeatLayers = 0;
+
+ public Angle LastAngle;
+
+ [ViewVariables(VVAccess.ReadOnly)]
+ public List LayerMappings = new();
+}
diff --git a/Content.Shared/_White/DollyMixture/SharedDollyMixtureSystem.cs b/Content.Shared/_White/DollyMixture/SharedDollyMixtureSystem.cs
new file mode 100644
index 0000000000..3efdc0d42c
--- /dev/null
+++ b/Content.Shared/_White/DollyMixture/SharedDollyMixtureSystem.cs
@@ -0,0 +1,29 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Numerics;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Content.Shared._White.DollyMixture;
+
+public abstract class SharedDollyMixtureSystem : EntitySystem
+{
+ public virtual void Apply3D(EntityUid uid, string RsiPath, string? statePrefix = null, Vector2? layerOffset = null, DollyMixtureComponent? comp = null)
+ {
+ comp ??= EnsureComp(uid);
+
+ comp.RSIPath = RsiPath;
+ Dirty(uid, comp);
+ }
+
+ public virtual void Remove3D(EntityUid uid, DollyMixtureComponent? comp = null)
+ {
+ if (!Resolve(uid, ref comp))
+ return;
+
+ comp.RSIPath = null;
+ Dirty(uid, comp);
+ }
+}
+
diff --git a/Content.Shared/_White/Guns/AmmoProviderRedirect/AmmoProviderRedirectComponents.cs b/Content.Shared/_White/Guns/AmmoProviderRedirect/AmmoProviderRedirectComponents.cs
new file mode 100644
index 0000000000..58d1747ae7
--- /dev/null
+++ b/Content.Shared/_White/Guns/AmmoProviderRedirect/AmmoProviderRedirectComponents.cs
@@ -0,0 +1,6 @@
+namespace Content.Shared._White.Guns.AmmoProviderRedirect;
+
+[RegisterComponent]
+public sealed partial class ParentAmmoProviderComponent : Component
+{
+}
diff --git a/Content.Shared/_White/Guns/AmmoProviderRedirect/AmmoProviderRedirectSystem.cs b/Content.Shared/_White/Guns/AmmoProviderRedirect/AmmoProviderRedirectSystem.cs
new file mode 100644
index 0000000000..02bbe1cc51
--- /dev/null
+++ b/Content.Shared/_White/Guns/AmmoProviderRedirect/AmmoProviderRedirectSystem.cs
@@ -0,0 +1,21 @@
+using Content.Shared.Weapons.Ranged.Events;
+
+namespace Content.Shared._White.Guns.AmmoProviderRedirect;
+
+public sealed class AmmoProviderRedirectSystem : EntitySystem
+{
+ [Dependency] private readonly SharedTransformSystem _transform = default!;
+
+ public override void Initialize()
+ {
+ SubscribeLocalEvent(RedirectParent);
+ }
+
+ // todo figure out a way to check bullets' caliber before firing
+ // don't let people feed .50 cal rounds into methaphorical pea shooters
+ private void RedirectParent(EntityUid uid, ParentAmmoProviderComponent comp, TakeAmmoEvent args)
+ {
+ if (_transform.GetParentUid(uid) is { Valid: true } parent)
+ RaiseLocalEvent(parent, args);
+ }
+}
diff --git a/Content.Shared/_White/Guns/ModularTurret/ModularTurretComponents.cs b/Content.Shared/_White/Guns/ModularTurret/ModularTurretComponents.cs
new file mode 100644
index 0000000000..311800caf3
--- /dev/null
+++ b/Content.Shared/_White/Guns/ModularTurret/ModularTurretComponents.cs
@@ -0,0 +1,28 @@
+namespace Content.Shared._White.Guns.ModularTurret;
+
+
+[RegisterComponent]
+public sealed partial class ModularTurretWeaponComponent : Component
+{
+ [DataField(required: true)]
+ public List WeaponClass = new();
+
+ [DataField]
+ public bool OnlyUsableByTurret = true;
+
+ [DataField]
+ public EntityUid? CurrentTurretHolder;
+
+ [DataField("dollyMixSprite")]
+ public string? DollyMixRSIPath = null;
+}
+
+[RegisterComponent]
+public sealed partial class ModularTurretComponent : Component
+{
+ [DataField]
+ public string? MountClass;
+
+ [DataField(required: true)]
+ public string Slot = "";
+}
diff --git a/Content.Shared/_White/Guns/ModularTurret/SharedModularTurretSystem.cs b/Content.Shared/_White/Guns/ModularTurret/SharedModularTurretSystem.cs
new file mode 100644
index 0000000000..d82cc1ed58
--- /dev/null
+++ b/Content.Shared/_White/Guns/ModularTurret/SharedModularTurretSystem.cs
@@ -0,0 +1,61 @@
+using Content.Shared._White.DollyMixture;
+using Content.Shared.Weapons.Ranged.Events;
+using Robust.Shared.Containers;
+
+namespace Content.Shared._White.Guns.ModularTurret;
+
+public abstract class SharedModularTurretSystem : EntitySystem
+{
+ [Dependency] private readonly SharedDollyMixtureSystem _dolly = default!;
+
+ public override void Initialize()
+ {
+ SubscribeLocalEvent(OnInsertAttempt);
+ SubscribeLocalEvent(OnInserted);
+ SubscribeLocalEvent(OnRemoved);
+ SubscribeLocalEvent(OnModularTurretWeaponShotAttempt);
+ }
+
+ private void OnModularTurretWeaponShotAttempt(EntityUid turretUid, ModularTurretWeaponComponent comp, ref ShotAttemptedEvent args)
+ {
+ if (comp.OnlyUsableByTurret && !HasComp(args.User))
+ args.Cancel();
+ }
+
+ private void OnInsertAttempt(EntityUid uid, ModularTurretComponent comp, ContainerIsInsertingAttemptEvent args)
+ {
+ if (args.Container.ID != comp.Slot)
+ return;
+
+ if (!TryComp(args.EntityUid, out var modweapon))
+ {
+ args.Cancel();
+ return;
+ }
+
+ if (comp.MountClass is string turretClass &&
+ !modweapon.WeaponClass.Contains(turretClass))
+ args.Cancel();
+ }
+
+ protected virtual void OnInserted(EntityUid uid, ModularTurretComponent comp, EntInsertedIntoContainerMessage args)
+ {
+ if (args.Container.ID == comp.Slot && TryComp(args.Entity, out var weapon))
+ {
+ weapon.CurrentTurretHolder = uid;
+ if (weapon.DollyMixRSIPath is string path)
+ _dolly.Apply3D(uid, path);
+ }
+ }
+
+ protected virtual void OnRemoved(EntityUid uid, ModularTurretComponent comp, EntRemovedFromContainerMessage args)
+ {
+ if (args.Container.ID == comp.Slot && TryComp(args.Entity, out var weapon))
+ {
+ weapon.CurrentTurretHolder = null;
+ if (weapon.DollyMixRSIPath is not null)
+ _dolly.Remove3D(uid);
+ }
+ }
+
+}
diff --git a/Content.Shared/_White/Guns/ProjectileContainerBatteryAmmoProviderComponent.cs b/Content.Shared/_White/Guns/ProjectileContainerBatteryAmmoProviderComponent.cs
new file mode 100644
index 0000000000..c232f93b5a
--- /dev/null
+++ b/Content.Shared/_White/Guns/ProjectileContainerBatteryAmmoProviderComponent.cs
@@ -0,0 +1,36 @@
+using Content.Shared.Weapons.Ranged;
+using Content.Shared.Weapons.Ranged.Components;
+using Robust.Shared.GameStates;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
+
+namespace Content.Shared._White.Guns;
+
+
+public abstract partial class ContainerBatteryAmmoProviderComponent : BatteryAmmoProviderComponent
+{
+ public EntityUid? Linked = null;
+}
+
+///
+/// Uses the battery of the container it's currently in.
+///
+[RegisterComponent, NetworkedComponent]
+public sealed partial class ProjectileContainerBatteryAmmoProviderComponent : ContainerBatteryAmmoProviderComponent
+{
+ [ViewVariables(VVAccess.ReadWrite), DataField("proto", required: true, customTypeSerializer: typeof(PrototypeIdSerializer))]
+ public string Prototype = default!;
+}
+
+[RegisterComponent, NetworkedComponent]
+public sealed partial class HitscanContainerBatteryAmmoProviderComponent : ContainerBatteryAmmoProviderComponent
+{
+ [ViewVariables(VVAccess.ReadWrite), DataField("proto", required: true, customTypeSerializer: typeof(PrototypeIdSerializer))]
+ public string Prototype = default!;
+}
+
+[RegisterComponent]
+public sealed partial class ContainerBatteryAmmoTrackerComponent : Component
+{
+ public List Linked = new();
+}
diff --git a/Content.Shared/_White/Guns/ProjectileDamageModifier.cs b/Content.Shared/_White/Guns/ProjectileDamageModifier.cs
new file mode 100644
index 0000000000..f462bb4ab5
--- /dev/null
+++ b/Content.Shared/_White/Guns/ProjectileDamageModifier.cs
@@ -0,0 +1,53 @@
+using Content.Shared.Projectiles;
+using Content.Shared.Whitelist;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Content.Shared._White.Guns;
+
+[RegisterComponent]
+public sealed partial class ProjectileDamageModifierComponent : Component
+{
+ [DataField(required: true)]
+ public List Multipliers = new();
+
+ [DataField]
+ public float DefaultMultiplier = 1f;
+
+}
+
+[DataDefinition]
+public sealed partial class WhitelistMultiplierPair
+{
+ [DataField(required: true)]
+ public EntityWhitelist Whitelist = new();
+
+ [DataField(required: true)]
+ public float Multiplier;
+}
+
+public sealed class ProjectileDamageMultiplierSystem : EntitySystem
+{
+ [Dependency] private readonly EntityWhitelistSystem _whitelist = default!;
+
+ public override void Initialize()
+ {
+ SubscribeLocalEvent(OnHit);
+ }
+
+ private void OnHit(EntityUid uid, ProjectileDamageModifierComponent comp, ref ProjectileHitEvent args)
+ {
+ foreach(var pair in comp.Multipliers)
+ {
+ if (_whitelist.IsWhitelistPass(pair.Whitelist, args.Target))
+ {
+ args.Damage *= pair.Multiplier;
+ return;
+ }
+ }
+ args.Damage *= comp.DefaultMultiplier;
+ }
+}
diff --git a/Content.Shared/_White/Lock/LockDeviceLinksComponent.cs b/Content.Shared/_White/Lock/LockDeviceLinksComponent.cs
new file mode 100644
index 0000000000..5f74e497e2
--- /dev/null
+++ b/Content.Shared/_White/Lock/LockDeviceLinksComponent.cs
@@ -0,0 +1,23 @@
+using Content.Shared.DeviceLinking.Events;
+using Content.Shared.Lock;
+
+namespace Content.Shared._White.Lock;
+
+[RegisterComponent]
+public sealed partial class LockDeviceLinksComponent : Component;
+
+public sealed class LockDeviceLinksSystem : EntitySystem
+{
+ [Dependency] private readonly LockSystem _lock = default!;
+
+ public override void Initialize()
+ {
+ SubscribeLocalEvent(OnLinkAttempt);
+ }
+
+ private void OnLinkAttempt(EntityUid uid, LockDeviceLinksComponent comp, LinkAttemptEvent args)
+ {
+ if (_lock.IsLocked(uid))
+ args.Cancel();
+ }
+}
diff --git a/Content.Shared/_White/RemoteControl/Components/RemoteControlConsoleComponent.cs b/Content.Shared/_White/RemoteControl/Components/RemoteControlConsoleComponent.cs
index 43a9ae0253..7cf4a353d2 100644
--- a/Content.Shared/_White/RemoteControl/Components/RemoteControlConsoleComponent.cs
+++ b/Content.Shared/_White/RemoteControl/Components/RemoteControlConsoleComponent.cs
@@ -23,7 +23,7 @@ public sealed partial class RemoteControlConsoleComponent : Component
public EntProtoId SwitchToNextAction = "RemoteControlConsoleSwitchToNextAction";
[ViewVariables]
- public EntityUid SwitchToNextActionUid;
+ public EntityUid? SwitchToNextActionUid;
[ViewVariables]
public EntityUid? User;
diff --git a/Content.Shared/_White/RemoteControl/Components/RemoteControlTargetComponent.cs b/Content.Shared/_White/RemoteControl/Components/RemoteControlTargetComponent.cs
index d644cf2a29..c351db325d 100644
--- a/Content.Shared/_White/RemoteControl/Components/RemoteControlTargetComponent.cs
+++ b/Content.Shared/_White/RemoteControl/Components/RemoteControlTargetComponent.cs
@@ -4,13 +4,13 @@ using Robust.Shared.Prototypes;
namespace Content.Shared._White.RemoteControl.Components;
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
-public sealed partial class RemoteControlTargetComponent : Component
+public sealed partial class RemoteControllableComponent : Component
{
[DataField]
public EntProtoId EndRemoteControlAction = "ActionEndRemoteControl";
[DataField]
- public bool CanManually;
+ public bool ManualControl;
[ViewVariables, AutoNetworkedField]
public EntityUid? User;
diff --git a/Content.Shared/_White/RemoteControl/Components/RemoteControlUserComponent.cs b/Content.Shared/_White/RemoteControl/Components/RemoteControlUserComponent.cs
index 9185e3045e..1da5ee7c6b 100644
--- a/Content.Shared/_White/RemoteControl/Components/RemoteControlUserComponent.cs
+++ b/Content.Shared/_White/RemoteControl/Components/RemoteControlUserComponent.cs
@@ -3,7 +3,7 @@ using Robust.Shared.GameStates;
namespace Content.Shared._White.RemoteControl.Components;
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
-public sealed partial class RemoteControlUserComponent : Component
+public sealed partial class RemoteControllingComponent : Component
{
[ViewVariables, AutoNetworkedField]
public EntityUid Target;
diff --git a/Resources/Prototypes/Damage/modifier_sets.yml b/Resources/Prototypes/Damage/modifier_sets.yml
index 46b9b3d75b..d2092eef3d 100644
--- a/Resources/Prototypes/Damage/modifier_sets.yml
+++ b/Resources/Prototypes/Damage/modifier_sets.yml
@@ -35,7 +35,7 @@
Slash: 10
Piercing: 10
Heat: 10
- # Structural: 10 # WWDP its for structures bruh
+ Structural: 5
- type: damageModifierSet
id: Rock
@@ -51,17 +51,17 @@
- type: damageModifierSet
id: RockStrong
coefficients:
- Structural: 1 # WWDP
+ Structural: 2 # WWDP
Blunt: 0.25
Slash: 0.1
Piercing: 0.25
Heat: 0.5
flatReductions:
- Blunt: 10
- Slash: 10
- Piercing: 10
- Heat: 10
- # Structural: 10 # WWDP
+ Blunt: 15
+ Slash: 15
+ Piercing: 15
+ Heat: 15
+ # Structural: 5 # WWDP # no
- type: damageModifierSet
id: PerforatedMetallic
diff --git a/Resources/Prototypes/Entities/Effects/admin_triggers.yml b/Resources/Prototypes/Entities/Effects/admin_triggers.yml
index e1f366678d..c9baefd00d 100644
--- a/Resources/Prototypes/Entities/Effects/admin_triggers.yml
+++ b/Resources/Prototypes/Entities/Effects/admin_triggers.yml
@@ -109,4 +109,33 @@
- type: SingularityDistortion
intensity: 10
falloffPower: 1.5
-
+
+# WWDP #
+
+# This kills the SpawnAndDirtyAllEntities test.
+# The test tries to get this entity's netuid to send the projectileComp's "Shooter" field to clients,
+# but does this not fast enough, so this entity is long since deleted by that point.
+# todo: kill that test
+#- type: entity
+# id: AdminInstantEffectShrapnel
+# suffix: Shrapnel
+# parent: AdminInstantEffectBase
+# components:
+# - type: ClusterGrenade
+# fillPrototype: PelletClusterLethal
+# maxGrenadesCount: 30
+# grenadeType: enum.GrenadeType.Shoot
+# - type: ContainerContainer
+# containers:
+# cluster-payload: !type:Container
+
+- type: entity
+ id: AdminInstantEffectExplosionSmall
+ suffix: Small Explosion
+ parent: AdminInstantEffectBase
+ components:
+ - type: ExplodeOnTrigger
+ - type: Explosive
+ totalIntensity: 30
+ explosionType: Default
+ tileBreakScale: 5
diff --git a/Resources/Prototypes/_Goobstation/Entities/Objects/Specific/Mech/Weapons/Gun/base.yml b/Resources/Prototypes/_Goobstation/Entities/Objects/Specific/Mech/Weapons/Gun/base.yml
index f4b8f23f12..874ba6c56e 100644
--- a/Resources/Prototypes/_Goobstation/Entities/Objects/Specific/Mech/Weapons/Gun/base.yml
+++ b/Resources/Prototypes/_Goobstation/Entities/Objects/Specific/Mech/Weapons/Gun/base.yml
@@ -15,4 +15,4 @@
- type: ClothingSpeedModifier
walkModifier: 0.5
sprintModifier: 0.5
- - type: HeldSpeedModifier
\ No newline at end of file
+ - type: HeldSpeedModifier
diff --git a/Resources/Prototypes/_White/Catalog/Cargo/ship_weapons.yml b/Resources/Prototypes/_White/Catalog/Cargo/ship_weapons.yml
new file mode 100644
index 0000000000..685919210b
--- /dev/null
+++ b/Resources/Prototypes/_White/Catalog/Cargo/ship_weapons.yml
@@ -0,0 +1,119 @@
+- type: cargoProduct
+ id: WeaponShipLightIR
+ description: A single IR laser. Low damage and even lower power draw.
+ icon:
+ sprite: _White/Objects/Weapons/Guns/Turrets/IR_laser.rsi
+ state: icon
+ product: CrateWeaponSecureWeaponShipLightIR
+ cost: 7500
+ category: cargoproduct-category-name-shuttle
+ group: market
+
+- type: entity
+ parent: CrateWeaponSecure
+ id: CrateWeaponSecureWeaponShipLightIR
+ name: IR laser crate
+ components:
+ - type: StorageFill
+ contents:
+ - id: WeaponShipLightIR
+
+- type: cargoProduct
+ id: WeaponShipLightAnnihilator
+ description: A single annihilator MLRS. Fires a burst of low-damage rockets in a wide arc for suppression and area denial.
+ icon:
+ sprite: _White/Objects/Weapons/Guns/Turrets/annihilator.rsi
+ state: icon
+ product: CrateWeaponSecureWeaponShipLightAnnihilator
+ cost: 27500
+ category: cargoproduct-category-name-shuttle
+ group: market
+
+- type: entity
+ parent: CrateWeaponSecure
+ id: CrateWeaponSecureWeaponShipLightAnnihilator
+ name: annihilator crate
+ components:
+ - type: StorageFill
+ contents:
+ - id: WeaponShipLightAnnihilator
+
+- type: cargoProduct
+ id: WeaponShipLightHotspot
+ description: A single Hotspot CIWS. Inaccurate dual-minigun, capable of firing in short bursts, intended for point-defense against soft targets.
+ icon:
+ sprite: _White/Objects/Weapons/Guns/Turrets/hotspot.rsi
+ state: icon
+ product: CrateWeaponSecureWeaponShipLightHotspot
+ cost: 16500
+ category: cargoproduct-category-name-shuttle
+ group: market
+
+- type: entity
+ parent: CrateWeaponSecure
+ id: CrateWeaponSecureWeaponShipLightHotspot
+ name: hotspot crate
+ components:
+ - type: StorageFill
+ contents:
+ - id: WeaponShipLightHotspot
+
+- type: cargoProduct
+ id: WeaponShipLightHMG
+ description: A single heavy machine gun. Well suited for any task.
+ icon:
+ sprite: _White/Objects/Weapons/Guns/Turrets/hmg.rsi
+ state: icon
+ product: CrateWeaponSecureWeaponShipLightHMG
+ cost: 23000
+ category: cargoproduct-category-name-shuttle
+ group: market
+
+- type: entity
+ parent: CrateWeaponSecure
+ id: CrateWeaponSecureWeaponShipLightHMG
+ name: ship HMG crate
+ components:
+ - type: StorageFill
+ contents:
+ - id: WeaponShipLightHMG
+
+- type: cargoProduct
+ id: WeaponShipLightBlaster
+ description: A single mining blaster. Tears apart hull and rock alike, but near useless in actual combat due to slow projectiles.
+ icon:
+ sprite: _White/Objects/Weapons/Guns/Turrets/blaster.rsi
+ state: icon
+ product: CrateWeaponSecureWeaponShipLightBlaster
+ cost: 18000
+ category: cargoproduct-category-name-shuttle
+ group: market
+
+- type: entity
+ parent: CrateWeaponSecure
+ id: CrateWeaponSecureWeaponShipLightBlaster
+ name: mining blaster crate
+ components:
+ - type: StorageFill
+ contents:
+ - id: WeaponShipLightBlaster
+
+- type: cargoProduct
+ id: WeaponShipLightMiningLaser
+ description: A single mining laser. Effective at presicely cutting pieces of rock and hull, it is slower than a mining blaster.
+ icon:
+ sprite: _White/Objects/Weapons/Guns/Turrets/mining_laser.rsi
+ state: icon
+ product: CrateWeaponSecureWeaponShipLightMiningLaser
+ cost: 20500
+ category: cargoproduct-category-name-shuttle
+ group: market
+
+- type: entity
+ parent: CrateWeaponSecure
+ id: CrateWeaponSecureWeaponShipLightMiningLaser
+ name: mining laser crate
+ components:
+ - type: StorageFill
+ contents:
+ - id: WeaponShipLightMiningLaser
diff --git a/Resources/Prototypes/_White/DeviceLinking/sink_ports.yml b/Resources/Prototypes/_White/DeviceLinking/sink_ports.yml
index b845b47a6f..2f6d1ef9de 100644
--- a/Resources/Prototypes/_White/DeviceLinking/sink_ports.yml
+++ b/Resources/Prototypes/_White/DeviceLinking/sink_ports.yml
@@ -1,4 +1,10 @@
+- type: sourcePort
+ id: RemoteControlSourcePort
+ name: signal-port-name-remote-control-source
+ description: signal-port-description-remote-control-source
+ defaultLinks: [ RemoteControlSinkPort ]
+
- type: sinkPort
- id: RemoteControlOutputPort
- name: signal-port-name-remote-control-output
- description: signal-port-description-remote-control-output
+ id: RemoteControlSinkPort
+ name: signal-port-name-remote-control-sink
+ description: signal-port-description-remote-control-sink
diff --git a/Resources/Prototypes/_White/DeviceLinking/source_ports.yml b/Resources/Prototypes/_White/DeviceLinking/source_ports.yml
deleted file mode 100644
index 99fa14c837..0000000000
--- a/Resources/Prototypes/_White/DeviceLinking/source_ports.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-- type: sourcePort
- id: RemoteControlInputPort
- name: signal-port-name-remote-control-input
- description: signal-port-description-remote-control-input
- defaultLinks: [ RemoteControlOutputPort ]
diff --git a/Resources/Prototypes/_White/Entities/Objects/Devices/Circuitboards/misc.yml b/Resources/Prototypes/_White/Entities/Objects/Devices/Circuitboards/misc.yml
index e85b54a32f..2d1a31ebef 100644
--- a/Resources/Prototypes/_White/Entities/Objects/Devices/Circuitboards/misc.yml
+++ b/Resources/Prototypes/_White/Entities/Objects/Devices/Circuitboards/misc.yml
@@ -14,7 +14,7 @@
- AiCoreBoard
- type: entity
- id: WeaponTurretModularCircuitBoard
+ id: WeaponTurretUniversalCircuitboard
parent: BaseMachineCircuitboard
name: modular turret machine board
description: Circuitboard for a modular turret, which is a fancy way of saying "a servomotor tied to a gun's trigger with a string".
@@ -22,10 +22,9 @@
- type: Sprite
state: engineering
- type: MachineBoard
- prototype: WeaponTurretModular
+ prototype: WeaponTurretUniversal
requirements:
- MatterBin: 1
- Manipulator: 3
+ Manipulator: 1
Capacitor: 1
materialRequirements:
Cable: 5
diff --git a/Resources/Prototypes/_White/Entities/Objects/Weapons/Guns/Ship/weapons.yml b/Resources/Prototypes/_White/Entities/Objects/Weapons/Guns/Ship/weapons.yml
new file mode 100644
index 0000000000..fe133f1478
--- /dev/null
+++ b/Resources/Prototypes/_White/Entities/Objects/Weapons/Guns/Ship/weapons.yml
@@ -0,0 +1,369 @@
+- type: entity
+ parent: BaseItem
+ id: BaseWeaponShip
+ abstract: True
+ components:
+ - type: Appearance
+ - type: StaticPrice
+ price: 1
+ - type: Item
+ size: Ginormous
+ - type: MultiHandedItem
+ - type: ClothingSpeedModifier
+ walkModifier: 0.5
+ sprintModifier: 0.5
+ - type: HeldSpeedModifier
+ - type: ModularTurretWeapon
+ onlyUsableByTurret: true
+ - type: Gun
+ fireOnDropChance: 0
+ shipWeapon: true
+
+- type: entity
+ id: BaseWeaponShipLightEnergy
+ parent: BaseWeaponShip
+ abstract: True
+ components:
+ - type: ModularTurretWeapon
+ weaponClass:
+ - LightEnergy
+
+- type: entity
+ id: BaseWeaponShipLightBallistic
+ parent: BaseWeaponShip
+ abstract: True
+ components:
+ - type: ModularTurretWeapon
+ weaponClass:
+ - LightBallistic
+
+
+
+- type: entity
+ parent: BaseWeaponShipLightEnergy
+ id: WeaponShipLightIR
+ name: IR Pulse Laser
+ description: A power-efficient, albeit weak infrared pulse laser. Being essentially a scaled-up version of a regular laser carbine, this cannon trades damage for extreme power efficiency.
+ components:
+ - type: Sprite
+ sprite: _White/Objects/Weapons/Guns/Turrets/IR_laser.rsi
+ state: icon
+ - type: Gun
+ fireRate: 3.5
+ selectedMode: FullAuto
+ availableModes:
+ - FullAuto
+ soundGunshot:
+ path: /Audio/Weapons/Guns/Gunshots/laser.ogg
+ params:
+ volume: -5
+ - type: ProjectileContainerBatteryAmmoProvider
+ proto: BulletLaser
+ fireCost: 75
+ - type: ModularTurretWeapon
+ dollyMixSprite: _White/Objects/Weapons/Guns/Turrets/IR_laser.rsi
+
+
+- type: entity
+ parent: BaseWeaponShipLightEnergy
+ id: WeaponShipLightAnnihilator
+ name: Annihilator Rocket Launcher
+ description: Fires a spread of unguided flash-printed microrockets that cover a wide arc. Despite the low damage output, it can still deal a devastating blow in hands of a skilled fighter pilot.
+ components:
+ - type: Sprite
+ sprite: _White/Objects/Weapons/Guns/Turrets/annihilator.rsi
+ state: icon
+ - type: Gun
+ selectedMode: Burst
+ availableModes:
+ - Burst
+ burstFireRate: 10
+ burstCooldown: 3
+ shotsPerBurst: 10
+ minAngle: 15
+ maxAngle: 15
+ soundGunshot:
+ path: /Audio/Weapons/Guns/Gunshots/rpgfire.ogg
+ params:
+ volume: -5
+ - type: ProjectileContainerBatteryAmmoProvider
+ proto: BulletWeakRocket
+ fireCost: 1000
+ - type: ModularTurretWeapon
+ dollyMixSprite: _White/Objects/Weapons/Guns/Turrets/annihilator.rsi
+
+- type: entity
+ parent: BaseWeaponShipLightEnergy
+ id: WeaponShipLightMiningLaser
+ name: Mining Laser
+ description: A cheap utility laser, excels at melting loose rock and cutting various salvage into pieces, but performs poorly against softer targets. Has a rather short focus range, making it ill-suited for combat use.
+ components:
+ - type: Sprite
+ sprite: _White/Objects/Weapons/Guns/Turrets/mining_laser.rsi
+ state: icon
+ - type: Gun
+ fireRate: 5
+ angleDecay: -12
+ angleIncrease: -6
+ maxAngle: 40
+ selectedMode: FullAuto
+ availableModes:
+ - FullAuto
+ soundGunshot:
+ path: /Audio/Weapons/Guns/Gunshots/laser.ogg
+ params:
+ volume: -10
+ - type: HitscanContainerBatteryAmmoProvider
+ proto: RedMiningLaser
+ fireCost: 500
+ - type: ModularTurretWeapon
+ dollyMixSprite: _White/Objects/Weapons/Guns/Turrets/mining_laser.rsi
+
+# I'll put the mining beam prototype right after the mining laser itself to
+# make it obvious what it does.
+- type: hitscan
+ id: RedMiningLaser
+ maxLength: 30
+ damage:
+ types:
+ Heat: 3
+ Structural: 15
+ muzzleFlash:
+ sprite: _White/Objects/Weapons/Guns/Projectiles/projectiles.rsi
+ state: muzzle_mining_laser
+ travelFlash:
+ sprite: _White/Objects/Weapons/Guns/Projectiles/projectiles.rsi
+ state: mining_laser
+ impactFlash:
+ sprite: _White/Objects/Weapons/Guns/Projectiles/projectiles.rsi
+ state: impact_mining_laser
+
+- type: entity
+ parent: BaseWeaponShipLightEnergy
+ id: WeaponShipLightRiftLance
+ name: Rift lance
+ description: We commit this body to the holy vaccuum, dust to dust, stars to stars, that the Creator may renew our us in the cycle of eternal life. - Luddic funereal prayer, traditional.
+ components:
+ - type: Sprite
+ sprite: _White/Objects/Weapons/Guns/Turrets/rift_lance.rsi
+ state: icon
+ - type: Gun
+ fireRate: 0.1
+ selectedMode: FullAuto
+ availableModes:
+ - FullAuto
+ soundGunshot:
+ path: /Audio/Weapons/Guns/Gunshots/mateba.ogg
+ - type: HitscanContainerBatteryAmmoProvider
+ proto: RiftLanceBeam
+ fireCost: 8000
+ - type: ModularTurretWeapon
+ dollyMixSprite: _White/Objects/Weapons/Guns/Turrets/rift_lance.rsi
+
+# I'll put the rift lance beam prototype right after the rift lance itself to
+# make it obvious why it should *not* be given to players.
+- type: hitscan
+ id: RiftLanceBeam
+ maxLength: 800 # fuck it we ballin'
+ damage:
+ types:
+ Blunt: 2000 # oh yeah
+ spawnAtImpact: OmegaBlastPierce # extra funny
+ muzzleFlash:
+ sprite: _White/Objects/Weapons/Guns/Projectiles/projectiles.rsi
+ state: muzzle_fast
+ travelFlash:
+ sprite: _White/Objects/Weapons/Guns/Projectiles/projectiles.rsi
+ state: fast
+ impactFlash:
+ sprite: _White/Objects/Weapons/Guns/Projectiles/projectiles.rsi
+ state: impact_fast
+
+- type: entity
+ id: OmegaBlastPierce
+ categories: [ HideSpawnMenu ]
+ components:
+ - type: TriggerOnSpawn
+ - type: SpawnOnTrigger
+ proto: AdminInstantEffectExplosionSmall
+ offsets:
+ - 0,0
+ - 0,2
+ - 0,4
+ - 0,6
+ - 0,8
+ - 0,10
+ - type: TimedDespawn
+ lifetime: 1
+
+
+#- type: entity
+# parent: BaseWeaponShipLightEnergy
+# id: WeaponShipLightAutopulse
+# name: Autopulse Laser Cannon
+# description: Numerous built-in capacitors are at the core of this extremely inefficient, yet potent energy weapon.
+# components:
+# - type: Sprite
+# sprite: Objects/Specific/Mech/mecha_equipment.rsi
+# layers:
+# - map: [ "icon" ]
+# state: mecha_pulse
+# - type: Gun
+# fireRate: 5
+# selectedMode: FullAuto
+# availableModes:
+# - FullAuto
+# soundGunshot:
+# path: /Audio/Weapons/Guns/Gunshots/laser3.ogg
+# - type: ProjectileContainerBatteryAmmoProvider
+# proto: BulletPulse
+# fireCost: 150
+
+
+- type: entity
+ parent: BaseWeaponShipLightEnergy
+ id: WeaponShipLightHotspot
+ name: Hotspot PD
+ description: A rapid fire ballistic weapon effective against soft targets. A respectable close-in weapon system, it's effectiveness is severely limited by poor accuracy and low damage against any substantial armor.
+ components:
+ - type: Sprite
+ sprite: _White/Objects/Weapons/Guns/Turrets/hotspot.rsi
+ state: icon
+ - type: Gun
+ fireRate: 20
+ angleDecay: 12
+ angleIncrease: 3
+ maxAngle: 20
+ selectedMode: FullAuto
+ availableModes:
+ - FullAuto
+ soundGunshot:
+ path: /Audio/Weapons/Guns/Gunshots/minigun.ogg
+ params:
+ volume: -4
+ - type: ProjectileContainerBatteryAmmoProvider
+ proto: BulletHotspot
+ fireCost: 250
+ - type: ModularTurretWeapon
+ dollyMixSprite: _White/Objects/Weapons/Guns/Turrets/hotspot.rsi
+
+- type: entity
+ id: BulletHotspot
+ name: bullet (.13 point-defense)
+ parent: BaseBullet
+ categories: [ HideSpawnMenu ]
+ components:
+ - type: Projectile
+ damage:
+ types:
+ Piercing: 4
+ Blunt: 2
+ Slash: 2
+
+
+- type: entity
+ id: BulletHotspotSpread
+ categories: [ HideSpawnMenu ]
+ parent: BulletHotspot
+ components:
+ - type: ProjectileSpread
+ proto: BulletHotspot
+ count: 3
+ spread: 4
+
+
+- type: entity
+ parent: BaseWeaponShipLightEnergy
+ id: WeaponShipLightHMG
+ name: Ship HMG
+ description: A jack of all trades, this weapon is a well-rounded option for any type of job.
+ components:
+ - type: Sprite
+ sprite: _White/Objects/Weapons/Guns/Turrets/hmg.rsi
+ state: icon
+ - type: Gun
+ fireRate: 5
+ angleDecay: 10
+ angleIncrease: 5
+ maxAngle: 25
+ selectedMode: FullAuto
+ availableModes:
+ - FullAuto
+ soundGunshot:
+ path: /Audio/Weapons/Guns/Gunshots/lmg.ogg
+ params:
+ volume: 0
+ - type: ProjectileContainerBatteryAmmoProvider
+ proto: BulletHighCaliberWeak
+ fireCost: 1000
+ - type: ModularTurretWeapon
+ dollyMixSprite: _White/Objects/Weapons/Guns/Turrets/hmg.rsi
+
+
+
+- type: entity
+ categories: [ HideSpawnMenu ]
+ parent: BaseBullet
+ id: BulletHighCaliberWeak
+ name: bullet (.50 flash-printed)
+ components:
+ - type: Projectile
+ damage:
+ types:
+ Piercing: 25
+ Structural: 25 # weaker compared to other .50 cal bullets
+
+
+
+
+- type: entity
+ parent: BaseWeaponShipLightEnergy
+ id: WeaponShipLightBlaster
+ name: Mining Blaster
+ description: Able to carve out chunks of stone and hull alike, this weapon's performance is severely hampered by it's low firerate and projectile speed.
+ components:
+ - type: Sprite
+ sprite: _White/Objects/Weapons/Guns/Turrets/blaster.rsi
+ state: icon
+ - type: Gun
+ projectileSpeed: 10
+ fireRate: 0.333
+ angleDecay: 2.5
+ angleIncrease: 10
+ maxAngle: 25
+ selectedMode: FullAuto
+ availableModes:
+ - FullAuto
+ soundGunshot:
+ path: /Audio/Weapons/Guns/Gunshots/laser.ogg
+ params:
+ volume: 0
+ - type: ProjectileContainerBatteryAmmoProvider
+ proto: BulletMiningBlast
+ fireCost: 4000
+ - type: ModularTurretWeapon
+ dollyMixSprite: _White/Objects/Weapons/Guns/Turrets/blaster.rsi
+
+
+- type: entity
+ id: BulletMiningBlast
+ name: unstable energy orb
+ parent: BaseBulletTrigger
+ categories: [ HideSpawnMenu ]
+ components:
+ - type: Sprite
+ sprite: Objects/Weapons/Guns/Projectiles/projectiles_tg.rsi
+ color: "#FF00FFFF" #E7C60F?
+ layers:
+ - state: impact_laser_greyscale
+ - type: ExplodeOnTrigger
+ - type: Explosive
+ explosionType: DemolitionCharge
+ maxIntensity: 1
+ intensitySlope: 1
+ totalIntensity: 21
+ maxTileBreak: 0
+ - type: PointLight
+ radius: 1.5
+ color: red
+ energy: 0.25
diff --git a/Resources/Prototypes/_White/Entities/Objects/Weapons/Guns/Turrets/ship_turrets.yml b/Resources/Prototypes/_White/Entities/Objects/Weapons/Guns/Turrets/ship_turrets.yml
new file mode 100644
index 0000000000..c41f91efd9
--- /dev/null
+++ b/Resources/Prototypes/_White/Entities/Objects/Weapons/Guns/Turrets/ship_turrets.yml
@@ -0,0 +1,176 @@
+- type: entity
+ parent: [BaseStructure, ConstructibleMachine]
+ id: BaseWeaponTurretShip
+ suffix: Naval, Remote Controlled
+ abstract: true
+ name: ship turret
+ description: A remotely-operated turret designed to accept anti-ship weapons.
+ components:
+ - type: Clickable
+ - type: InteractionOutline
+ - type: Actions
+ - type: Fixtures
+ fixtures:
+ fix1:
+ shape:
+ !type:PhysShapeAabb
+ bounds: "-0.45,-0.45,0.45,0.45"
+ density: 60
+ mask:
+ - MachineMask
+ layer:
+ - MachineLayer
+ - type: Sprite
+ sprite: Objects/Weapons/Guns/Turrets/turrets.rsi
+ drawdepth: Mobs
+ noRot: true
+ state: syndie_base
+ - type: DollyMixture
+ directionCount: 64
+ repeatLayers: 3
+ - type: CombatMode
+ isInCombatMode: true
+ toggleMouseRotator: false
+ - type: Damageable
+ damageContainer: Inorganic
+ - type: Destructible
+ thresholds:
+ - trigger:
+ !type:DamageTrigger
+ damage: 450
+ behaviors:
+ - !type:DoActsBehavior
+ acts: [ "Destruction" ]
+ - !type:SpawnEntitiesBehavior
+ spawn:
+ WeaponTurretSyndicateBroken:
+ min: 1
+ max: 1
+ - trigger:
+ !type:DamageTrigger
+ damage: 300
+ behaviors:
+ - !type:PlaySoundBehavior
+ sound:
+ collection: MetalGlassBreak
+ - trigger:
+ !type:DamageTrigger
+ damage: 150
+ behaviors:
+ - !type:PlaySoundBehavior
+ sound:
+ collection: MetalGlassBreak
+ - type: GunFireAngleRestriction
+ - type: MouseRotator
+ angleTolerance: 0.05
+ rotationSpeed: 90
+ simple4DirMode: false
+ - type: NoRotateOnInteract
+ - type: NoRotateOnMove
+ - type: Input
+ context: "human"
+ - type: ContainerContainer
+ containers:
+ weapon_slot: !type:ContainerSlot
+ machine_board: !type:Container
+ machine_parts: !type:Container
+ - type: GunSlot
+ slot: weapon_slot
+ - type: ModularTurret
+ slot: weapon_slot
+ - type: ItemSlots
+ slots:
+ weapon_slot:
+ ejectOnInteract: false
+ ejectOnBreak: true
+ swap: false
+ name: Weapon slot
+ priority: 1
+ whitelist:
+ components:
+ - Gun
+ - ModularTurretWeapon
+ - type: RemoteControllable
+ - type: RemoteControlOverlay
+ - type: Lock
+ locked: false
+ lockTime: 5
+ unlockTime: 20
+ - type: ItemSlotsLock
+ slots:
+ - weapon_slot
+ - type: GunSignalControl
+ - type: DeviceNetwork
+ deviceNetId: Wireless
+ receiveFrequencyId: BasicDevice
+ - type: WiredNetworkConnection
+ - type: DeviceLinkSink
+ ports:
+ - RemoteControlSinkPort
+ - type: Battery
+ maxCharge: 2000
+ startingCharge: 0
+ - type: ApcPowerReceiverBattery
+ idleLoad: 5
+ batteryRechargeRate: 200
+ batteryRechargeEfficiency: 1.225
+ - type: ApcPowerReceiver
+ powerLoad: 5
+ - type: ExtensionCableReceiver
+ - type: Alerts
+ - type: Telescope
+ lerpAmount: 0.25
+ - type: Silicon
+ entityType: enum.SiliconType.Player
+ batteryPowered: true
+ drainPerSecond: 2
+ speedModifierThresholds:
+ 4: 1
+ 3: 1
+ 2: 1
+ 1: 1
+ 0: 1
+
+
+- type: entity
+ parent: [ BaseWeaponTurretShip, InnateMassScanner150 ]
+ id: WeaponTurretShipLight
+ suffix: Naval, Remote Controlled
+ name: small weapon mount
+ description: A remotely-operated weapon mount designed to accept light anti-ship and anti-infantry weaponry.
+ components:
+ - type: Sprite
+ sprite: _White/Objects/Weapons/Guns/Turrets/mounts.rsi
+ state: light
+ - type: Battery
+ maxCharge: 10000
+ startingCharge: 0
+ - type: ApcPowerReceiverBattery
+ idleLoad: 50
+ batteryRechargeRate: 1000
+ batteryRechargeEfficiency: 2.45
+ - type: ApcPowerReceiver
+ powerLoad: 50
+ - type: Machine
+ board: WeaponTurretShipLightCircuitBoard
+ - type: UpgradeBattery
+ maxChargeMultiplier: 3
+ baseMaxCharge: 10000
+
+- type: entity
+ id: WeaponTurretShipLightCircuitBoard
+ parent: BaseMachineCircuitboard
+ name: modular turret machine board
+ description: Circuitboard for a light ship weapon mount.
+ components:
+ - type: Sprite
+ state: security
+ - type: MachineBoard
+ prototype: WeaponTurretShipLight
+ requirements:
+ MatterBin: 1
+ Manipulator: 2
+ Capacitor: 3
+ materialRequirements:
+ Cable: 5
+ Steel: 10
diff --git a/Resources/Prototypes/_White/Entities/Objects/Weapons/Guns/Turrets/turret_base.yml b/Resources/Prototypes/_White/Entities/Objects/Weapons/Guns/Turrets/turret_base.yml
index 4021de8f63..f4d3b4d7e5 100644
--- a/Resources/Prototypes/_White/Entities/Objects/Weapons/Guns/Turrets/turret_base.yml
+++ b/Resources/Prototypes/_White/Entities/Objects/Weapons/Guns/Turrets/turret_base.yml
@@ -76,6 +76,7 @@
forceShootForward: true
- type: GunFireAngleRestriction
+# this is dumb
- type: entity
id: BaseTurret3D
abstract: true
@@ -87,10 +88,7 @@
layers:
- state: syndie_base
- type: DollyMixture
- states:
- - dm1
- - dm2
- - dm3
- - dm4
- - dm5
+ sprite: Objects/Weapons/Guns/Turrets/turrets.rsi
+ statePrefix: dm
+ repeatLayers: 3
layerOffset: 0,0.75
diff --git a/Resources/Prototypes/_White/Entities/Objects/Weapons/Guns/Turrets/turrets_energy.yml b/Resources/Prototypes/_White/Entities/Objects/Weapons/Guns/Turrets/turrets_energy.yml
index e7af01dae7..e69de29bb2 100644
--- a/Resources/Prototypes/_White/Entities/Objects/Weapons/Guns/Turrets/turrets_energy.yml
+++ b/Resources/Prototypes/_White/Entities/Objects/Weapons/Guns/Turrets/turrets_energy.yml
@@ -1,36 +0,0 @@
-- type: entity
- parent: BaseWeaponEnergyTurret
- id: WeaponEnergyTurretShip
- name: ship laser turret
- description: A burst fire heavy laser weapon mounted to a simple actuator rig with a camera. Its weapon will recharge while connected to an active power grid.
- suffix: Remote Controlled
- components:
- - type: DeviceLinkSink
- ports:
- - RemoteControlOutputPort
- - type: Gun
- shipWeapon: true
- burstCooldown: 0.4
- projectileSpeed: 50
- angleIncrease: 1.25
- maxAngle: 5
- selectedMode: Burst
- availableModes:
- - Burst
- - type: HTN # since there is no functionality to discard inherited components, we just disable HTN instead
- enabled: false
- - type: IntrinsicUI
- uis:
- enum.RadarConsoleUiKey.Key:
- toggleAction: ActionAGhostShowRadar
- - type: MouseRotator
- angleTolerance: 0.5
- - type: RadarConsole
- followEntity: true
- fieldOfVision: 90
- - type: RemoteControlTarget
- - type: RemoteControlOverlay
- - type: UserInterface
- interfaces:
- enum.RadarConsoleUiKey.Key:
- type: RadarConsoleBoundUserInterface
diff --git a/Resources/Prototypes/_White/Entities/Objects/Weapons/Guns/Turrets/turrets_ballistic.yml b/Resources/Prototypes/_White/Entities/Objects/Weapons/Guns/Turrets/universal_mount.yml
similarity index 57%
rename from Resources/Prototypes/_White/Entities/Objects/Weapons/Guns/Turrets/turrets_ballistic.yml
rename to Resources/Prototypes/_White/Entities/Objects/Weapons/Guns/Turrets/universal_mount.yml
index 28590ed083..16c1532861 100644
--- a/Resources/Prototypes/_White/Entities/Objects/Weapons/Guns/Turrets/turrets_ballistic.yml
+++ b/Resources/Prototypes/_White/Entities/Objects/Weapons/Guns/Turrets/universal_mount.yml
@@ -1,16 +1,18 @@
- type: entity
parent: [BaseWeaponTurretNoHTR, ConstructibleMachine]
- id: WeaponTurretModular
- name: modular turret
- description: A remotely-operated turret designed to accept standard hand-held weapons. Being a simple gimbal mount, it is unable to recharge energy weapons.
- suffix: Remote controlled
+ id: WeaponTurretUniversal
+ suffix: Remote Controlled
+ name: universal weapon mount
+ description: A remotely-operated turret designed to accept standard hand-held weapons. Being a simple gimbal mount, it is unable to do much besides aiming a gun and firing it.
components:
- type: Sprite
granularLayersRendering: true
+ sprite: Objects/Weapons/Guns/Turrets/turrets.rsi
+ drawdepth: Mobs
layers:
- state: syndie_base
renderingStrategy: NoRotation
- - map: [ "weapon" ]
+ - map: [ "weapon_layer" ]
renderingStrategy: Default
rotation: -90
- type: ContainerContainer
@@ -26,19 +28,19 @@
ejectOnInteract: false
ejectOnBreak: true
swap: false
- name: weapon-slot
+ name: Weapon slot
priority: 1
whitelist:
components:
- Gun
- type: ItemSlotRenderer
mapping:
- weapon_slot: weapon
- - type: RemoteControlTarget
- canManually: true
+ weapon_slot: weapon_layer
+ - type: RemoteControllable
+ manualControl: true
- type: RemoteControlOverlay
- type: Machine
- board: WeaponTurretModularCircuitBoard
+ board: WeaponTurretUniversalCircuitboard
- type: Lock
locked: false
lockTime: 3
@@ -50,7 +52,25 @@
- type: DeviceNetwork
deviceNetId: Wireless
receiveFrequencyId: BasicDevice
- - type: DeviceLinkSink
- ports:
- - RemoteControlOutputPort
- type: WiredNetworkConnection
+ - type: Battery
+ maxCharge: 2000
+ startingCharge: 0
+ - type: ApcPowerReceiverBattery
+ idleLoad: 5
+ batteryRechargeRate: 200
+ batteryRechargeEfficiency: 1.225
+ - type: ApcPowerReceiver
+ powerLoad: 5
+ - type: ExtensionCableReceiver
+ - type: Alerts
+ - type: Silicon
+ entityType: enum.SiliconType.Player
+ batteryPowered: true
+ drainPerSecond: 2
+ speedModifierThresholds:
+ 4: 1
+ 3: 1
+ 2: 1
+ 1: 1
+ 0: 0
diff --git a/Resources/Prototypes/_White/Entities/Structures/Machines/Computers/computers.yml b/Resources/Prototypes/_White/Entities/Structures/Machines/Computers/remote_control.yml
similarity index 96%
rename from Resources/Prototypes/_White/Entities/Structures/Machines/Computers/computers.yml
rename to Resources/Prototypes/_White/Entities/Structures/Machines/Computers/remote_control.yml
index e7c44f32cc..9c2bbc9442 100644
--- a/Resources/Prototypes/_White/Entities/Structures/Machines/Computers/computers.yml
+++ b/Resources/Prototypes/_White/Entities/Structures/Machines/Computers/remote_control.yml
@@ -25,4 +25,4 @@
- type: RemoteControlConsole
- type: DeviceLinkSource
ports:
- - RemoteControlInputPort
+ - RemoteControlSourcePort
diff --git a/Resources/Prototypes/_White/Ghosts/custom_ghosts.yml b/Resources/Prototypes/_White/Ghosts/custom_ghosts.yml
index 43bbd0e1d5..70fef228ab 100644
--- a/Resources/Prototypes/_White/Ghosts/custom_ghosts.yml
+++ b/Resources/Prototypes/_White/Ghosts/custom_ghosts.yml
@@ -188,7 +188,7 @@
#just look at the ckey field you dumbass
- type: customGhost
id: redfoxiv-ghost
- ckey: RedFoxIV
+ ckey: RedFoxIV@localhost
sprite: _White/Ghosts/empty.rsi
alpha: 1
ghostName: яицевозка
@@ -200,108 +200,10 @@
simple4DirMode: false
- type: DollyMixture
sprite: _White/Ghosts/redfoxiv-3Dghost.rsi
+ statePrefix: hqyaitca
offset: 0,-16
layerOffset: 0,0.675
- repeatLayers: 1
defaultShader: unshaded
- states:
- - hqyaitca97
- - hqyaitca96
- - hqyaitca95
- - hqyaitca94
- - hqyaitca93
- - hqyaitca92
- - hqyaitca91
- - hqyaitca90
- - hqyaitca89
- - hqyaitca88
- - hqyaitca87
- - hqyaitca86
- - hqyaitca85
- - hqyaitca84
- - hqyaitca83
- - hqyaitca82
- - hqyaitca81
- - hqyaitca80
- - hqyaitca79
- - hqyaitca78
- - hqyaitca77
- - hqyaitca76
- - hqyaitca75
- - hqyaitca74
- - hqyaitca73
- - hqyaitca72
- - hqyaitca71
- - hqyaitca70
- - hqyaitca69
- - hqyaitca68
- - hqyaitca67
- - hqyaitca66
- - hqyaitca65
- - hqyaitca64
- - hqyaitca63
- - hqyaitca62
- - hqyaitca61
- - hqyaitca60
- - hqyaitca59
- - hqyaitca58
- - hqyaitca57
- - hqyaitca56
- - hqyaitca55
- - hqyaitca54
- - hqyaitca53
- - hqyaitca52
- - hqyaitca51
- - hqyaitca50
- - hqyaitca49
- - hqyaitca48
- - hqyaitca47
- - hqyaitca46
- - hqyaitca45
- - hqyaitca44
- - hqyaitca43
- - hqyaitca42
- - hqyaitca41
- - hqyaitca40
- - hqyaitca39
- - hqyaitca38
- - hqyaitca37
- - hqyaitca36
- - hqyaitca35
- - hqyaitca34
- - hqyaitca33
- - hqyaitca32
- - hqyaitca31
- - hqyaitca30
- - hqyaitca29
- - hqyaitca28
- - hqyaitca27
- - hqyaitca26
- - hqyaitca25
- - hqyaitca24
- - hqyaitca23
- - hqyaitca22
- - hqyaitca21
- - hqyaitca20
- - hqyaitca19
- - hqyaitca18
- - hqyaitca17
- - hqyaitca16
- - hqyaitca15
- - hqyaitca14
- - hqyaitca13
- - hqyaitca12
- - hqyaitca11
- - hqyaitca10
- - hqyaitca9
- - hqyaitca8
- - hqyaitca7
- - hqyaitca6
- - hqyaitca5
- - hqyaitca4
- - hqyaitca3
- - hqyaitca2
- - hqyaitca1
##xivi
diff --git a/Resources/Prototypes/_White/Innate/InnateMassScanner.yml b/Resources/Prototypes/_White/Innate/InnateMassScanner.yml
new file mode 100644
index 0000000000..f723d92733
--- /dev/null
+++ b/Resources/Prototypes/_White/Innate/InnateMassScanner.yml
@@ -0,0 +1,62 @@
+- type: entity
+ id: InnateMassScanner360
+ abstract: true
+ components:
+ - type: IntrinsicUI
+ uis:
+ enum.RadarConsoleUiKey.Key:
+ toggleAction: ActionAGhostShowRadar
+ - type: UserInterface
+ interfaces:
+ enum.RadarConsoleUiKey.Key:
+ type: RadarConsoleBoundUserInterface
+ - type: RadarConsole
+ followEntity: true
+
+- type: entity
+ parent: InnateMassScanner360
+ id: InnateMassScanner30
+ abstract: true
+ components:
+ - type: RadarConsole
+ fieldOfVision: 30
+
+- type: entity
+ parent: InnateMassScanner360
+ id: InnateMassScanner60
+ abstract: true
+ components:
+ - type: RadarConsole
+ fieldOfVision: 60
+
+- type: entity
+ parent: InnateMassScanner360
+ id: InnateMassScanner90
+ abstract: true
+ components:
+ - type: RadarConsole
+ fieldOfVision: 90
+
+- type: entity
+ parent: InnateMassScanner360
+ id: InnateMassScanner120
+ abstract: true
+ components:
+ - type: RadarConsole
+ fieldOfVision: 120
+
+- type: entity
+ parent: InnateMassScanner360
+ id: InnateMassScanner150
+ abstract: true
+ components:
+ - type: RadarConsole
+ fieldOfVision: 150
+
+- type: entity
+ parent: InnateMassScanner360
+ id: InnateMassScanner180
+ abstract: true
+ components:
+ - type: RadarConsole
+ fieldOfVision: 180
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Projectiles/projectiles.rsi/fast.png b/Resources/Textures/_White/Objects/Weapons/Guns/Projectiles/projectiles.rsi/fast.png
new file mode 100644
index 0000000000..c53795c635
Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/Projectiles/projectiles.rsi/fast.png differ
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Projectiles/projectiles.rsi/impact_fast.png b/Resources/Textures/_White/Objects/Weapons/Guns/Projectiles/projectiles.rsi/impact_fast.png
new file mode 100644
index 0000000000..1ac335913e
Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/Projectiles/projectiles.rsi/impact_fast.png differ
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Projectiles/projectiles.rsi/impact_mining_laser.png b/Resources/Textures/_White/Objects/Weapons/Guns/Projectiles/projectiles.rsi/impact_mining_laser.png
new file mode 100644
index 0000000000..653466cbce
Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/Projectiles/projectiles.rsi/impact_mining_laser.png differ
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Projectiles/projectiles.rsi/meta.json b/Resources/Textures/_White/Objects/Weapons/Guns/Projectiles/projectiles.rsi/meta.json
new file mode 100644
index 0000000000..f33e7dba7c
--- /dev/null
+++ b/Resources/Textures/_White/Objects/Weapons/Guns/Projectiles/projectiles.rsi/meta.json
@@ -0,0 +1,101 @@
+{
+ "version": 1,
+ "license": "CC-BY-SA-3.0",
+ "copyright": "Taken and modified from cev-eris at https://github.com/discordia-space/CEV-Eris/tree/7ff8f28ea4de734f3cc3cb70f2d4e4b4263a988d",
+ "size": {
+ "x": 32,
+ "y": 32
+ },
+ "states": [
+ {
+ "name": "muzzle_mining_laser",
+ "delays": [
+ [
+ 0.060000002,
+ 0.060000002,
+ 0.060000002,
+ 0.060000002,
+ 0.060000002,
+ 0.060000002,
+ 0.060000002,
+ 0.060000002
+ ]
+ ]
+ },
+ {
+ "name": "mining_laser",
+ "delays": [
+ [
+ 0.060000002,
+ 0.060000002,
+ 0.060000002,
+ 0.060000002,
+ 0.060000002,
+ 0.060000002,
+ 0.060000002,
+ 0.060000002
+ ]
+ ]
+ },
+ {
+ "name": "impact_mining_laser",
+ "delays": [
+ [
+ 0.060000002,
+ 0.060000002,
+ 0.060000002,
+ 0.060000002,
+ 0.060000002,
+ 0.060000002,
+ 0.060000002,
+ 0.060000002
+ ]
+ ]
+ },
+ {
+ "name": "muzzle_fast",
+ "delays": [
+ [
+ 0.060000002,
+ 0.060000002,
+ 0.060000002,
+ 0.060000002,
+ 0.060000002,
+ 0.060000002,
+ 0.060000002,
+ 0.060000002
+ ]
+ ]
+ },
+ {
+ "name": "fast",
+ "delays": [
+ [
+ 0.060000002,
+ 0.060000002,
+ 0.060000002,
+ 0.060000002,
+ 0.060000002,
+ 0.060000002,
+ 0.060000002,
+ 0.060000002
+ ]
+ ]
+ },
+ {
+ "name": "impact_fast",
+ "delays": [
+ [
+ 0.060000002,
+ 0.060000002,
+ 0.060000002,
+ 0.060000002,
+ 0.060000002,
+ 0.060000002,
+ 0.060000002,
+ 0.060000002
+ ]
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Projectiles/projectiles.rsi/mining_laser.png b/Resources/Textures/_White/Objects/Weapons/Guns/Projectiles/projectiles.rsi/mining_laser.png
new file mode 100644
index 0000000000..e1231014db
Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/Projectiles/projectiles.rsi/mining_laser.png differ
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Projectiles/projectiles.rsi/muzzle_fast.png b/Resources/Textures/_White/Objects/Weapons/Guns/Projectiles/projectiles.rsi/muzzle_fast.png
new file mode 100644
index 0000000000..02756543c5
Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/Projectiles/projectiles.rsi/muzzle_fast.png differ
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Projectiles/projectiles.rsi/muzzle_mining_laser.png b/Resources/Textures/_White/Objects/Weapons/Guns/Projectiles/projectiles.rsi/muzzle_mining_laser.png
new file mode 100644
index 0000000000..b1aced7708
Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/Projectiles/projectiles.rsi/muzzle_mining_laser.png differ
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/IR_laser.rsi/dollymix1.png b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/IR_laser.rsi/dollymix1.png
new file mode 100644
index 0000000000..b2cb37625c
Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/IR_laser.rsi/dollymix1.png differ
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/IR_laser.rsi/dollymix2.png b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/IR_laser.rsi/dollymix2.png
new file mode 100644
index 0000000000..bc35ee0747
Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/IR_laser.rsi/dollymix2.png differ
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/IR_laser.rsi/dollymix3.png b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/IR_laser.rsi/dollymix3.png
new file mode 100644
index 0000000000..723356c9c5
Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/IR_laser.rsi/dollymix3.png differ
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/IR_laser.rsi/dollymix4-unshaded.png b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/IR_laser.rsi/dollymix4-unshaded.png
new file mode 100644
index 0000000000..6c6609442d
Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/IR_laser.rsi/dollymix4-unshaded.png differ
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/IR_laser.rsi/dollymix4.png b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/IR_laser.rsi/dollymix4.png
new file mode 100644
index 0000000000..e445ed1636
Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/IR_laser.rsi/dollymix4.png differ
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/IR_laser.rsi/dollymix5.png b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/IR_laser.rsi/dollymix5.png
new file mode 100644
index 0000000000..a72458fdeb
Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/IR_laser.rsi/dollymix5.png differ
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/IR_laser.rsi/icon.png b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/IR_laser.rsi/icon.png
new file mode 100644
index 0000000000..e95c366bae
Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/IR_laser.rsi/icon.png differ
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/IR_laser.rsi/meta.json b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/IR_laser.rsi/meta.json
new file mode 100644
index 0000000000..4595ca25d9
--- /dev/null
+++ b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/IR_laser.rsi/meta.json
@@ -0,0 +1,18 @@
+{
+ "version": 1,
+ "license": "CC-BY-SA-3.0",
+ "copyright": "WWDP",
+ "size": {
+ "x": 32,
+ "y": 32
+ },
+ "states": [
+ { "name": "dollymix1"},
+ { "name": "dollymix2"},
+ { "name": "dollymix3"},
+ { "name": "dollymix4-unshaded", "delays":[[1, 1, 1, 1]] },
+ { "name": "dollymix4"},
+ { "name": "dollymix5"},
+ { "name": "icon"}
+ ]
+}
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/annihilator.rsi/dollymix1.png b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/annihilator.rsi/dollymix1.png
new file mode 100644
index 0000000000..ddac8f5a5b
Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/annihilator.rsi/dollymix1.png differ
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/annihilator.rsi/dollymix2.png b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/annihilator.rsi/dollymix2.png
new file mode 100644
index 0000000000..2f00f868db
Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/annihilator.rsi/dollymix2.png differ
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/annihilator.rsi/dollymix3.png b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/annihilator.rsi/dollymix3.png
new file mode 100644
index 0000000000..a4c9c3416e
Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/annihilator.rsi/dollymix3.png differ
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/annihilator.rsi/dollymix4.png b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/annihilator.rsi/dollymix4.png
new file mode 100644
index 0000000000..5441cf8e59
Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/annihilator.rsi/dollymix4.png differ
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/annihilator.rsi/dollymix5.png b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/annihilator.rsi/dollymix5.png
new file mode 100644
index 0000000000..8b621e4639
Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/annihilator.rsi/dollymix5.png differ
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/annihilator.rsi/dollymix6.png b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/annihilator.rsi/dollymix6.png
new file mode 100644
index 0000000000..b9e0775b96
Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/annihilator.rsi/dollymix6.png differ
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/annihilator.rsi/dollymix7.png b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/annihilator.rsi/dollymix7.png
new file mode 100644
index 0000000000..6fc7dd1c80
Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/annihilator.rsi/dollymix7.png differ
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/annihilator.rsi/dollymix8.png b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/annihilator.rsi/dollymix8.png
new file mode 100644
index 0000000000..abeb7c49c3
Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/annihilator.rsi/dollymix8.png differ
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/annihilator.rsi/icon.png b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/annihilator.rsi/icon.png
new file mode 100644
index 0000000000..11a0f33df8
Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/annihilator.rsi/icon.png differ
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/annihilator.rsi/meta.json b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/annihilator.rsi/meta.json
new file mode 100644
index 0000000000..8c8a797a7a
--- /dev/null
+++ b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/annihilator.rsi/meta.json
@@ -0,0 +1,20 @@
+{
+ "version": 1,
+ "license": "CC-BY-SA-3.0",
+ "copyright": "WWDP",
+ "size": {
+ "x": 32,
+ "y": 32
+ },
+ "states": [
+ { "name": "dollymix1"},
+ { "name": "dollymix2"},
+ { "name": "dollymix3"},
+ { "name": "dollymix4"},
+ { "name": "dollymix5"},
+ { "name": "dollymix6"},
+ { "name": "dollymix7"},
+ { "name": "dollymix8"},
+ { "name": "icon"}
+ ]
+}
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/blaster.rsi/dollymix1.png b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/blaster.rsi/dollymix1.png
new file mode 100644
index 0000000000..b898d00812
Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/blaster.rsi/dollymix1.png differ
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/blaster.rsi/dollymix2.png b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/blaster.rsi/dollymix2.png
new file mode 100644
index 0000000000..988e90030c
Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/blaster.rsi/dollymix2.png differ
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/blaster.rsi/dollymix3.png b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/blaster.rsi/dollymix3.png
new file mode 100644
index 0000000000..283daf6f62
Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/blaster.rsi/dollymix3.png differ
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/blaster.rsi/dollymix4.png b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/blaster.rsi/dollymix4.png
new file mode 100644
index 0000000000..76c64b4bfe
Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/blaster.rsi/dollymix4.png differ
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/blaster.rsi/dollymix5.png b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/blaster.rsi/dollymix5.png
new file mode 100644
index 0000000000..d249549b5f
Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/blaster.rsi/dollymix5.png differ
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/blaster.rsi/icon.png b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/blaster.rsi/icon.png
new file mode 100644
index 0000000000..d274c6cfda
Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/blaster.rsi/icon.png differ
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/blaster.rsi/meta.json b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/blaster.rsi/meta.json
new file mode 100644
index 0000000000..d75991f1a1
--- /dev/null
+++ b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/blaster.rsi/meta.json
@@ -0,0 +1,17 @@
+{
+ "version": 1,
+ "license": "CC-BY-SA-3.0",
+ "copyright": "WWDP autogenerated",
+ "size": {
+ "x": 32,
+ "y": 32
+ },
+ "states": [
+ { "name": "dollymix1" },
+ { "name": "dollymix2" },
+ { "name": "dollymix3" },
+ { "name": "dollymix4" },
+ { "name": "dollymix5" },
+ { "name": "icon" }
+ ]
+}
\ No newline at end of file
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/hmg.rsi/dollymix1.png b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/hmg.rsi/dollymix1.png
new file mode 100644
index 0000000000..9fbb64fb0e
Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/hmg.rsi/dollymix1.png differ
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/hmg.rsi/dollymix2.png b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/hmg.rsi/dollymix2.png
new file mode 100644
index 0000000000..9edcc5aee6
Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/hmg.rsi/dollymix2.png differ
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/hmg.rsi/dollymix3.png b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/hmg.rsi/dollymix3.png
new file mode 100644
index 0000000000..d9a7c527b1
Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/hmg.rsi/dollymix3.png differ
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/hmg.rsi/dollymix4.png b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/hmg.rsi/dollymix4.png
new file mode 100644
index 0000000000..b72d55f4b7
Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/hmg.rsi/dollymix4.png differ
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/hmg.rsi/icon.png b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/hmg.rsi/icon.png
new file mode 100644
index 0000000000..c20651fcf4
Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/hmg.rsi/icon.png differ
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/hmg.rsi/meta.json b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/hmg.rsi/meta.json
new file mode 100644
index 0000000000..73e0eb5872
--- /dev/null
+++ b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/hmg.rsi/meta.json
@@ -0,0 +1,16 @@
+{
+ "version": 1,
+ "license": "CC-BY-SA-3.0",
+ "copyright": "WWDP autogenerated",
+ "size": {
+ "x": 32,
+ "y": 32
+ },
+ "states": [
+ { "name": "dollymix1" },
+ { "name": "dollymix2" },
+ { "name": "dollymix3" },
+ { "name": "dollymix4" },
+ { "name": "icon" }
+ ]
+}
\ No newline at end of file
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/hotspot.rsi/dollymix1.png b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/hotspot.rsi/dollymix1.png
new file mode 100644
index 0000000000..d440dc7d8e
Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/hotspot.rsi/dollymix1.png differ
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/hotspot.rsi/dollymix2.png b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/hotspot.rsi/dollymix2.png
new file mode 100644
index 0000000000..bd6f3ace8b
Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/hotspot.rsi/dollymix2.png differ
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/hotspot.rsi/dollymix3.png b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/hotspot.rsi/dollymix3.png
new file mode 100644
index 0000000000..0f3af05b37
Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/hotspot.rsi/dollymix3.png differ
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/hotspot.rsi/dollymix4.png b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/hotspot.rsi/dollymix4.png
new file mode 100644
index 0000000000..477f8cfcf4
Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/hotspot.rsi/dollymix4.png differ
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/hotspot.rsi/dollymix5.png b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/hotspot.rsi/dollymix5.png
new file mode 100644
index 0000000000..8ece2b9fa2
Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/hotspot.rsi/dollymix5.png differ
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/hotspot.rsi/icon.png b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/hotspot.rsi/icon.png
new file mode 100644
index 0000000000..ce46002a8d
Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/hotspot.rsi/icon.png differ
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/hotspot.rsi/meta.json b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/hotspot.rsi/meta.json
new file mode 100644
index 0000000000..d75991f1a1
--- /dev/null
+++ b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/hotspot.rsi/meta.json
@@ -0,0 +1,17 @@
+{
+ "version": 1,
+ "license": "CC-BY-SA-3.0",
+ "copyright": "WWDP autogenerated",
+ "size": {
+ "x": 32,
+ "y": 32
+ },
+ "states": [
+ { "name": "dollymix1" },
+ { "name": "dollymix2" },
+ { "name": "dollymix3" },
+ { "name": "dollymix4" },
+ { "name": "dollymix5" },
+ { "name": "icon" }
+ ]
+}
\ No newline at end of file
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/mining_laser.rsi/dollymix1.png b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/mining_laser.rsi/dollymix1.png
new file mode 100644
index 0000000000..f3d3b77ff3
Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/mining_laser.rsi/dollymix1.png differ
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/mining_laser.rsi/dollymix2.png b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/mining_laser.rsi/dollymix2.png
new file mode 100644
index 0000000000..02934a6382
Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/mining_laser.rsi/dollymix2.png differ
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/mining_laser.rsi/dollymix3-unshaded.png b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/mining_laser.rsi/dollymix3-unshaded.png
new file mode 100644
index 0000000000..be35105a0b
Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/mining_laser.rsi/dollymix3-unshaded.png differ
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/mining_laser.rsi/dollymix3.png b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/mining_laser.rsi/dollymix3.png
new file mode 100644
index 0000000000..6736113c6e
Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/mining_laser.rsi/dollymix3.png differ
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/mining_laser.rsi/dollymix4.png b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/mining_laser.rsi/dollymix4.png
new file mode 100644
index 0000000000..ec6c027da8
Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/mining_laser.rsi/dollymix4.png differ
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/mining_laser.rsi/dollymix5-unshaded.png b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/mining_laser.rsi/dollymix5-unshaded.png
new file mode 100644
index 0000000000..21b16235f1
Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/mining_laser.rsi/dollymix5-unshaded.png differ
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/mining_laser.rsi/dollymix5.png b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/mining_laser.rsi/dollymix5.png
new file mode 100644
index 0000000000..9060a6fdb4
Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/mining_laser.rsi/dollymix5.png differ
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/mining_laser.rsi/dollymix6.png b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/mining_laser.rsi/dollymix6.png
new file mode 100644
index 0000000000..8c467d971c
Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/mining_laser.rsi/dollymix6.png differ
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/mining_laser.rsi/dollymix7-unshaded.png b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/mining_laser.rsi/dollymix7-unshaded.png
new file mode 100644
index 0000000000..853d3fe805
Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/mining_laser.rsi/dollymix7-unshaded.png differ
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/mining_laser.rsi/dollymix7.png b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/mining_laser.rsi/dollymix7.png
new file mode 100644
index 0000000000..9e189fde05
Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/mining_laser.rsi/dollymix7.png differ
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/mining_laser.rsi/dollymix8.png b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/mining_laser.rsi/dollymix8.png
new file mode 100644
index 0000000000..91303a24a7
Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/mining_laser.rsi/dollymix8.png differ
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/mining_laser.rsi/dollymix9-unshaded.png b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/mining_laser.rsi/dollymix9-unshaded.png
new file mode 100644
index 0000000000..ea3b033069
Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/mining_laser.rsi/dollymix9-unshaded.png differ
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/mining_laser.rsi/dollymix9.png b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/mining_laser.rsi/dollymix9.png
new file mode 100644
index 0000000000..9c099943e1
Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/mining_laser.rsi/dollymix9.png differ
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/mining_laser.rsi/icon.png b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/mining_laser.rsi/icon.png
new file mode 100644
index 0000000000..093d9f201b
Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/mining_laser.rsi/icon.png differ
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/mining_laser.rsi/meta.json b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/mining_laser.rsi/meta.json
new file mode 100644
index 0000000000..1285c3e6e0
--- /dev/null
+++ b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/mining_laser.rsi/meta.json
@@ -0,0 +1,25 @@
+{
+ "version": 1,
+ "license": "CC-BY-SA-3.0",
+ "copyright": "WWDP",
+ "size": {
+ "x": 32,
+ "y": 32
+ },
+ "states": [
+ { "name": "dollymix1"},
+ { "name": "dollymix2"},
+ { "name": "dollymix3"},
+ { "name": "dollymix3-unshaded", "delays": [[0.2, 0.2, 0.2, 0.2]]},
+ { "name": "dollymix4"},
+ { "name": "dollymix5"},
+ { "name": "dollymix5-unshaded", "delays": [[0.2, 0.2, 0.2, 0.2]]},
+ { "name": "dollymix6"},
+ { "name": "dollymix7"},
+ { "name": "dollymix7-unshaded", "delays": [[0.2, 0.2, 0.2, 0.2]]},
+ { "name": "dollymix8"},
+ { "name": "dollymix9"},
+ { "name": "dollymix9-unshaded", "delays": [[0.2, 0.2, 0.2, 0.2]]},
+ { "name": "icon"}
+ ]
+}
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/mounts.rsi/light.png b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/mounts.rsi/light.png
new file mode 100644
index 0000000000..55f6724a1c
Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/mounts.rsi/light.png differ
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/mounts.rsi/light_broken.png b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/mounts.rsi/light_broken.png
new file mode 100644
index 0000000000..6c3eb9a654
Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/mounts.rsi/light_broken.png differ
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/mounts.rsi/meta.json b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/mounts.rsi/meta.json
new file mode 100644
index 0000000000..b44648e414
--- /dev/null
+++ b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/mounts.rsi/meta.json
@@ -0,0 +1,13 @@
+{
+ "version": 1,
+ "license": "CC-BY-SA-3.0",
+ "copyright": "Taken from https://github.com/tgstation/tgstation/commit/42e0275d860551197687c571c689c270ff423288; edited by WWDP",
+ "size": {
+ "x": 32,
+ "y": 32
+ },
+ "states": [
+ { "name": "light" },
+ { "name": "light_broken" }
+ ]
+}
\ No newline at end of file
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/rift_lance.rsi/dollymix1-unshaded.png b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/rift_lance.rsi/dollymix1-unshaded.png
new file mode 100644
index 0000000000..9113c476ed
Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/rift_lance.rsi/dollymix1-unshaded.png differ
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/rift_lance.rsi/dollymix1.png b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/rift_lance.rsi/dollymix1.png
new file mode 100644
index 0000000000..b6eb4b66a3
Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/rift_lance.rsi/dollymix1.png differ
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/rift_lance.rsi/dollymix2-unshaded.png b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/rift_lance.rsi/dollymix2-unshaded.png
new file mode 100644
index 0000000000..bb0f858eae
Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/rift_lance.rsi/dollymix2-unshaded.png differ
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/rift_lance.rsi/dollymix2.png b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/rift_lance.rsi/dollymix2.png
new file mode 100644
index 0000000000..3d537ed3bc
Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/rift_lance.rsi/dollymix2.png differ
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/rift_lance.rsi/dollymix3-unshaded.png b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/rift_lance.rsi/dollymix3-unshaded.png
new file mode 100644
index 0000000000..bb0f858eae
Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/rift_lance.rsi/dollymix3-unshaded.png differ
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/rift_lance.rsi/dollymix3.png b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/rift_lance.rsi/dollymix3.png
new file mode 100644
index 0000000000..7b6f4989b9
Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/rift_lance.rsi/dollymix3.png differ
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/rift_lance.rsi/dollymix4-unshaded.png b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/rift_lance.rsi/dollymix4-unshaded.png
new file mode 100644
index 0000000000..2034eebe92
Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/rift_lance.rsi/dollymix4-unshaded.png differ
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/rift_lance.rsi/dollymix4.png b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/rift_lance.rsi/dollymix4.png
new file mode 100644
index 0000000000..916c70614f
Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/rift_lance.rsi/dollymix4.png differ
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/rift_lance.rsi/dollymix5-unshaded.png b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/rift_lance.rsi/dollymix5-unshaded.png
new file mode 100644
index 0000000000..621c8881ac
Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/rift_lance.rsi/dollymix5-unshaded.png differ
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/rift_lance.rsi/dollymix5.png b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/rift_lance.rsi/dollymix5.png
new file mode 100644
index 0000000000..621c8881ac
Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/rift_lance.rsi/dollymix5.png differ
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/rift_lance.rsi/icon.png b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/rift_lance.rsi/icon.png
new file mode 100644
index 0000000000..c162fa84ef
Binary files /dev/null and b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/rift_lance.rsi/icon.png differ
diff --git a/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/rift_lance.rsi/meta.json b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/rift_lance.rsi/meta.json
new file mode 100644
index 0000000000..7ac543be08
--- /dev/null
+++ b/Resources/Textures/_White/Objects/Weapons/Guns/Turrets/rift_lance.rsi/meta.json
@@ -0,0 +1,22 @@
+{
+ "version": 1,
+ "license": "CC-BY-SA-3.0",
+ "copyright": "WWDP",
+ "size": {
+ "x": 32,
+ "y": 32
+ },
+ "states": [
+ { "name": "dollymix1"},
+ { "name": "dollymix2"},
+ { "name": "dollymix3"},
+ { "name": "dollymix4"},
+ { "name": "dollymix5"},
+ { "name": "dollymix1-unshaded"},
+ { "name": "dollymix2-unshaded"},
+ { "name": "dollymix3-unshaded"},
+ { "name": "dollymix4-unshaded"},
+ { "name": "dollymix5-unshaded"},
+ { "name": "icon"}
+ ]
+}
diff --git a/Tools/dollymix/readme.txt b/Tools/dollymix/readme.txt
new file mode 100644
index 0000000000..d1c21baa4d
--- /dev/null
+++ b/Tools/dollymix/readme.txt
@@ -0,0 +1,19 @@
+Copy these into the .rsi folder you're working on.
+
+These require the pillow module ("pip install pillow")
+
+zslice - splits a vertical slice strip produced by MagicaVoxel into separate files.
+ Expects the source file to be named "dollymix.png".
+ Asks for vertical size and then will split the image into that many rows.
+ If "dollymix-unshaded.png" is present, splits that aswell.
+ Does not automatically remove empty images.
+
+zicon - uses the same "dollymix.png" file to produce a default sprite for the model.
+
+ztest - does the same thing as zicon at a higher resolution and different angles.
+ For checking how the "model" looks without starting the game.
+
+All file names start with a "z" so that they appear last in the folder when sorted by name.
+Also for the purposes of goida.
+
+Recommended toolchain - MagicaVoxel for initial modeling -> Aseprite for layer edits and very basic animation -> the shit in this folder.
\ No newline at end of file
diff --git a/Tools/dollymix/zicon.py b/Tools/dollymix/zicon.py
new file mode 100644
index 0000000000..1969e63d81
--- /dev/null
+++ b/Tools/dollymix/zicon.py
@@ -0,0 +1,25 @@
+from PIL import Image
+import os, sys
+
+path = os.getcwd()
+basefile = "dollymix"
+ext = ".png"
+size = int(input("size: "))
+offset = int(input("downward offset (in pixels): "))
+
+base = Image.open(os.path.join(path,basefile+ext))
+
+reverse = size > 0
+size = abs(size)
+
+amount = base.height // size
+
+out = Image.new("RGBA", (size, size), (0,0,0,0))
+
+for i in range(amount):
+ if(reverse):
+ out.alpha_composite(base.crop((0,size*(amount-1-i),base.width,size*(amount-i))).rotate(90), (0,-i-offset))
+ else:
+ out.alpha_composite(base.crop((0,size*i,base.width,size*(i+1))).rotate(90), (0,-i-offset))
+
+out.save(os.path.join(path,"icon.png"))
diff --git a/Tools/dollymix/zslice.py b/Tools/dollymix/zslice.py
new file mode 100644
index 0000000000..2a7748d2f5
--- /dev/null
+++ b/Tools/dollymix/zslice.py
@@ -0,0 +1,64 @@
+from PIL import Image
+import os, sys
+
+path = os.getcwd()
+basefile = "dollymix"
+ext = ".png"
+size = int(input("size: "))
+reverse = size > 0
+size = abs(size)
+
+
+base = Image.open(os.path.join(path,basefile+ext))
+amount = base.height // size
+
+states = []
+
+for i in range(amount):
+ name = basefile+str(i+1)
+ filename = name+ext
+ states.append(name)
+ print(filename)
+ if(reverse):
+ base.crop((0,size*(amount-1-i),base.width,size*(amount-i))).save(os.path.join(path,filename))
+ else:
+ base.crop((0,size*i,base.width,size*(i+1))).save(os.path.join(path,filename))
+
+unshadedfile = os.path.join(path,basefile+"-unshaded"+ext)
+if os.path.exists(unshadedfile):
+ base = Image.open(unshadedfile)
+ amount = base.height // size
+
+ for i in range(amount):
+ name = basefile+str(i+1)+"-unshaded"
+ filename = name+ext
+ states.append(name)
+ print(filename)
+ if(reverse):
+ base.crop((0,size*(amount-1-i),base.width,size*(amount-i))).save(os.path.join(path,filename))
+ else:
+ base.crop((0,size*i,base.width,size*(i+1))).save(os.path.join(path,filename))
+
+if(len(states) == 0):
+ exit(1)
+
+metaheader = """{
+ "version": 1,
+ "license": "CC-BY-SA-3.0",
+ "copyright": "WWDP autogenerated",
+ "size": {
+ "x": 32,
+ "y": 32
+ },
+ "states": [
+"""
+metaentry = """ {{ "name": "{0}" }}"""
+metaterm = """
+ ]
+}"""
+
+states.append("icon")
+with open(os.path.join(path,"meta.json"), 'w') as metajson:
+ metajson.write(metaheader)
+ metajson.write(",\n".join([metaentry.format(state) for state in states]))
+ metajson.write(metaterm)
\ No newline at end of file
diff --git a/Tools/dollymix/ztest.py b/Tools/dollymix/ztest.py
new file mode 100644
index 0000000000..bbbefb9206
--- /dev/null
+++ b/Tools/dollymix/ztest.py
@@ -0,0 +1,36 @@
+from PIL import Image
+import os, sys
+
+path = os.getcwd()
+basefile = "dollymix"
+ext = ".png"
+size = int(input("size: "))
+offset = float(input("offset: "))
+repeat = int(input("repeat: "))
+
+base = Image.open(os.path.join(path,basefile+ext))
+
+reverse = size > 0
+size = abs(size)
+
+amount = base.height // size
+
+testfolder = os.path.join(path, "test")
+if not os.path.exists(testfolder):
+ os.makedirs(testfolder)
+
+copies = 24
+scaleFactor = 5
+for rot in range(copies):
+ angle = rot*(360/copies)
+ print(f"{angle:03}.png")
+ out = Image.new("RGBA", (size*scaleFactor, size*scaleFactor), (0,0,0,0))
+ for i in range(amount):
+ for r in range(repeat):
+ vertOffset = round(-(i-1+r/repeat)*scaleFactor*offset)
+ if(reverse):
+ out.alpha_composite(base.crop((0,size*(amount-1-i),base.width,size*(amount-i))).resize((size*scaleFactor, size*scaleFactor), 0).rotate(angle), (0,vertOffset))
+ else:
+ out.alpha_composite(base.crop((0,size*i,base.width,size*(i+1))).resize((size*scaleFactor, size*scaleFactor), 0).rotate(angle), (0,vertOffset))
+
+ out.save(os.path.join(path, "test", f"{angle:03}.png"))