diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Data/BlockVariantGroups.sbc b/Utility Mods/Stealth Drive - Starcore Edition/Data/BlockVariantGroups.sbc
new file mode 100644
index 000000000..33e0c7ff6
--- /dev/null
+++ b/Utility Mods/Stealth Drive - Starcore Edition/Data/BlockVariantGroups.sbc
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+ Textures\GUI\Icons\Cubes\Aryx_AWE_TacticalModule.dds
+ Stealth Drive
+ Allows a ship to enter stealth, becoming almost invisible as well as undetectable by weapon systems.
+
+
+
+
+
+
+
+
+
+
+
+
+
+ StealthDrive
+
+ 6
+ 18
+
+
+
+
+
\ No newline at end of file
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Data/CubeBlocks_1x1Drive.sbc b/Utility Mods/Stealth Drive - Starcore Edition/Data/CubeBlocks_1x1Drive.sbc
new file mode 100644
index 000000000..7eb7b3d03
--- /dev/null
+++ b/Utility Mods/Stealth Drive - Starcore Edition/Data/CubeBlocks_1x1Drive.sbc
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+ UpgradeModule
+ StealthDrive1x1
+
+ Stealth Drive
+ Textures\GUI\Icons\Cubes\Aryx_AWE_TacticalModule.dds
+ Allows a ship to enter stealth, becoming almost invisible as well as undetectable by weapon systems.
+ Large
+ false
+ TriangleMesh
+
+
+ Models\Cubes\large\StealthDrive1x1.mwm
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ StealthDrive1x1
+ Z
+ Y
+ Light
+ true
+ BlockModuleEfficiency
+ Damage_Electrical_Damaged
+ ParticleElectrical
+ Default
+ BlockDestroyedExplosion_Large
+ WepSmallWarheadExpl
+ 500
+
+
+
+
\ No newline at end of file
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Data/CubeBlocks_Stealth.sbc b/Utility Mods/Stealth Drive - Starcore Edition/Data/CubeBlocks_Stealth.sbc
new file mode 100644
index 000000000..d0074524c
--- /dev/null
+++ b/Utility Mods/Stealth Drive - Starcore Edition/Data/CubeBlocks_Stealth.sbc
@@ -0,0 +1,206 @@
+
+
+
+
+
+
+ UpgradeModule
+ StealthDrive
+
+ Stealth Drive
+ Textures\GUI\Icons\Cubes\Aryx_AWE_TacticalModule.dds
+ Allows a ship to enter stealth, becoming almost invisible as well as undetectable by weapon systems.
+ Large
+ false
+ TriangleMesh
+
+
+ Models\AWE_Aegis\ARYX_TacticalModule.mwm
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ StealthDrive
+ Z
+ Y
+ Light
+ true
+ BlockModuleEfficiency
+ Damage_Electrical_Damaged
+ ParticleElectrical
+ Default
+ BlockDestroyedExplosion_Large
+ WepSmallWarheadExpl
+ 500
+
+
+
+
+ UpgradeModule
+ StealthDriveSmall
+
+ Stealth Drive
+ Textures\GUI\Icons\Cubes\Aryx_AWE_TacticalModule.dds
+ Allows a ship to enter stealth, becoming almost invisible as well as undetectable by weapon systems.
+ Small
+ false
+ TriangleMesh
+
+
+ Models\Cubes\small\StealthDriveSmall.mwm
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ StealthDrive
+ Z
+ Y
+ Light
+ true
+ BlockModuleEfficiency
+ Damage_Electrical_Damaged
+ ParticleElectrical
+ Default
+ BlockDestroyedExplosion_Large
+ WepSmallWarheadExpl
+ 500
+
+
+
+
+ UpgradeModule
+ StealthHeatSink
+
+ Heat Sink
+ Textures\GUI\Icons\Cubes\UpgradeEnergy.dds
+ Increases the duration a ship can stay in stealth before needing to drop out of stealth to vent heat.
+ Large
+ false
+ TriangleMesh
+
+
+ Models\Cubes\large\StealthHeatSink.mwm
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ StealthHeatSink
+ Y
+ X
+ Light
+ BlockModuleEfficiency
+ Damage_Electrical_Damaged
+ ParticleElectrical
+ Default
+ BlockDestroyedExplosion_Large
+ WepSmallWarheadExpl
+ 500
+
+
+
+
+ UpgradeModule
+ StealthHeatSinkSmall
+
+ Heat Sink
+ Textures\GUI\Icons\Cubes\UpgradeEnergy.dds
+ Increases the duration a ship can stay in stealth before needing to drop out of stealth to vent heat.
+ Small
+ false
+ TriangleMesh
+
+
+ Models\Cubes\small\StealthHeatSinkSmall.mwm
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ StealthHeatSink
+ Y
+ X
+ Light
+ BlockModuleEfficiency
+ Damage_Electrical_Damaged
+ ParticleElectrical
+ Default
+ BlockDestroyedExplosion_Large
+ WepSmallWarheadExpl
+ 500
+
+
+
+
\ No newline at end of file
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Data/EntityComponents.sbc b/Utility Mods/Stealth Drive - Starcore Edition/Data/EntityComponents.sbc
new file mode 100644
index 000000000..47eb42706
--- /dev/null
+++ b/Utility Mods/Stealth Drive - Starcore Edition/Data/EntityComponents.sbc
@@ -0,0 +1,13 @@
+
+
+
+
+ ModStorageComponent
+ StealthMod
+
+
+ 75BBB4F5-4FB9-4230-AAAA-BB79C9811507
+
+
+
+
\ No newline at end of file
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Data/Fonts.sbc b/Utility Mods/Stealth Drive - Starcore Edition/Data/Fonts.sbc
new file mode 100644
index 000000000..9c285804d
--- /dev/null
+++ b/Utility Mods/Stealth Drive - Starcore Edition/Data/Fonts.sbc
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+ FontDefinition
+ StealthOrange
+
+
+ 227
+ 69
+ 0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Data/Scripts/Stealth System/API/Backend/APIBackend.cs b/Utility Mods/Stealth Drive - Starcore Edition/Data/Scripts/Stealth System/API/Backend/APIBackend.cs
new file mode 100644
index 000000000..acc95cd17
--- /dev/null
+++ b/Utility Mods/Stealth Drive - Starcore Edition/Data/Scripts/Stealth System/API/Backend/APIBackend.cs
@@ -0,0 +1,115 @@
+using Sandbox.ModAPI;
+using System;
+using System.Collections.Generic;
+using IMyTerminalBlock = Sandbox.ModAPI.Ingame.IMyTerminalBlock;
+using IMyCubeGrid = VRage.Game.ModAPI.Ingame.IMyCubeGrid;
+
+namespace StealthSystem
+{
+ internal class APIBackend
+ {
+ internal readonly Dictionary ModApiMethods;
+ internal Dictionary PbApiMethods;
+
+ private readonly StealthSession _session;
+
+ internal APIBackend(StealthSession session)
+ {
+ _session = session;
+
+ ModApiMethods = new Dictionary
+ {
+ ["ToggleStealth"] = new Func(ToggleStealth),
+ ["GetStatus"] = new Func(GetStatus),
+ ["GetDuration"] = new Func(GetDuration),
+ ["GetMainDrive"] = new Func(GetMainDrive),
+ ["GetHeatSinks"] = new Action>(GetHeatSinks),
+ };
+ }
+
+
+ internal void PbInit()
+ {
+ PbApiMethods = new Dictionary
+ {
+ ["ToggleStealth"] = new Func(ToggleStealthPB),
+ ["GetStatus"] = new Func(GetStatus),
+ ["GetDuration"] = new Func(GetDuration),
+ ["GetMainDrive"] = new Func(GetMainDrive),
+ ["GetHeatSinks"] = new Action>(GetHeatSinksPB),
+ };
+ var pb = MyAPIGateway.TerminalControls.CreateProperty, Sandbox.ModAPI.IMyTerminalBlock>("StealthPbAPI");
+ pb.Getter = b => PbApiMethods;
+ MyAPIGateway.TerminalControls.AddControl(pb);
+ _session.PbApiInited = true;
+ }
+
+ private bool ToggleStealth(IMyTerminalBlock block, bool force)
+ {
+ DriveComp comp;
+ if (!_session.DriveMap.TryGetValue(block.EntityId, out comp))
+ return false;
+
+ return comp.ToggleStealth(force);
+ }
+
+ private bool ToggleStealthPB(IMyTerminalBlock block)
+ {
+ return ToggleStealth(block, false);
+ }
+
+ private int GetStatus(IMyTerminalBlock block)
+ {
+ DriveComp comp;
+ if (!_session.DriveMap.TryGetValue(block.EntityId, out comp))
+ return 4;
+
+ var status = !comp.Online ? 4 : !comp.SufficientPower ? 3 : comp.CoolingDown ? 2 : comp.StealthActive ? 1 : 0;
+ return status;
+ }
+
+ private int GetDuration(IMyTerminalBlock block)
+ {
+ DriveComp comp;
+ if (!_session.DriveMap.TryGetValue(block.EntityId, out comp))
+ return 0;
+
+ var duration = comp.StealthActive ? comp.TotalTime - comp.TimeElapsed : comp.CoolingDown ? comp.TimeElapsed : comp.MaxDuration;
+ return duration;
+ }
+
+ private Sandbox.ModAPI.IMyTerminalBlock GetMainDrive(IMyCubeGrid grid)
+ {
+ GridComp comp;
+ if (!_session.GridMap.TryGetValue(grid as VRage.Game.ModAPI.IMyCubeGrid, out comp))
+ return null;
+
+ return comp.MasterComp?.Block;
+ }
+
+ private void GetHeatSinksPB(IMyCubeGrid grid, ICollection blocks)
+ {
+ GridComp comp;
+ if (_session.GridMap.TryGetValue(grid as VRage.Game.ModAPI.IMyCubeGrid, out comp))
+ {
+ for (int i = 0; i < comp.HeatComps.Count; i++)
+ blocks.Add(comp.HeatComps[i].Block);
+ }
+
+ return;
+ }
+
+ private void GetHeatSinks(VRage.Game.ModAPI.IMyCubeGrid grid, ICollection blocks)
+ {
+ GridComp comp;
+ if (_session.GridMap.TryGetValue(grid, out comp))
+ {
+ for (int i = 0; i < comp.HeatComps.Count; i++)
+ blocks.Add(comp.HeatComps[i].Block);
+ }
+
+ return;
+ }
+
+ }
+}
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Data/Scripts/Stealth System/API/Backend/APIServer.cs b/Utility Mods/Stealth Drive - Starcore Edition/Data/Scripts/Stealth System/API/Backend/APIServer.cs
new file mode 100644
index 000000000..cc577f3ce
--- /dev/null
+++ b/Utility Mods/Stealth Drive - Starcore Edition/Data/Scripts/Stealth System/API/Backend/APIServer.cs
@@ -0,0 +1,65 @@
+using Sandbox.ModAPI;
+using System;
+using System.Collections.Generic;
+
+namespace StealthSystem
+{
+ internal class APIServer
+ {
+ private const long CHANNEL = 2172757427;
+
+ private readonly StealthSession _session;
+
+ internal APIServer(StealthSession session)
+ {
+ _session = session;
+ }
+
+ ///
+ /// Is the API ready to be serve
+ ///
+ public bool IsReady { get; private set; }
+
+ private void HandleMessage(object o)
+ {
+ if ((o as string) == "ApiEndpointRequest")
+ MyAPIGateway.Utilities.SendModMessage(CHANNEL, _session.API.ModApiMethods);
+ }
+
+ private bool _isRegistered;
+
+ ///
+ /// Prepares the client to receive API endpoints and requests an update.
+ ///
+ public void Load()
+ {
+ if (!_isRegistered)
+ {
+ _isRegistered = true;
+ MyAPIGateway.Utilities.RegisterMessageHandler(CHANNEL, HandleMessage);
+ }
+ IsReady = true;
+ try
+ {
+ MyAPIGateway.Utilities.SendModMessage(CHANNEL, _session.API.ModApiMethods);
+
+ }
+ catch (Exception ex) { Logs.WriteLine($"Exception in APIServer.Load() - {ex}"); }
+ }
+
+
+ ///
+ /// Unloads all API endpoints and detaches events.
+ ///
+ public void Unload()
+ {
+ if (_isRegistered)
+ {
+ _isRegistered = false;
+ MyAPIGateway.Utilities.UnregisterMessageHandler(CHANNEL, HandleMessage);
+ }
+ IsReady = false;
+ MyAPIGateway.Utilities.SendModMessage(CHANNEL, new Dictionary());
+ }
+ }
+}
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Data/Scripts/Stealth System/API/StealthAPI.cs b/Utility Mods/Stealth Drive - Starcore Edition/Data/Scripts/Stealth System/API/StealthAPI.cs
new file mode 100644
index 000000000..61b82f6a2
--- /dev/null
+++ b/Utility Mods/Stealth Drive - Starcore Edition/Data/Scripts/Stealth System/API/StealthAPI.cs
@@ -0,0 +1,119 @@
+using Sandbox.ModAPI;
+using System;
+using System.Collections.Generic;
+using VRage.Game.ModAPI;
+
+namespace StealthSystem
+{
+ internal class StealthAPI
+ {
+ /// Returns true if drive status was toggled successfully.
+ /// Ignore power requirements and overheat.
+ public bool ToggleStealth(IMyTerminalBlock drive, bool force) => _toggleStealth?.Invoke(drive, force) ?? false;
+
+ /// Returns status of drive. 0 = Ready, 1 = Active, 2 = Cooldown, 3 = Not enough power, 4 = Offline
+ public int GetStatus(IMyTerminalBlock drive) => _getStatus?.Invoke(drive) ?? 4;
+
+ /// Returns remaining duration of stealth/cooldown.
+ public int GetDuration(IMyTerminalBlock drive) => _getDuration?.Invoke(drive) ?? 0;
+
+ /// Retuns active stealth drive on grid if one exists, otherwise returns null.
+ public IMyTerminalBlock GetMainDrive(IMyCubeGrid grid) => _getMainDrive?.Invoke(grid);
+
+ /// Collection to populate with heat sinks on grid.
+ public void GetHeatSinks(IMyCubeGrid grid, ICollection sinks) => _getHeatSinks?.Invoke(grid, sinks);
+
+
+
+ private const long CHANNEL = 2172757427;
+ private bool _isRegistered;
+ private bool _apiInit;
+ private Action _readyCallback;
+
+ private Func _toggleStealth;
+ private Func _getStatus;
+ private Func _getDuration;
+ private Func _getMainDrive;
+ private Action> _getHeatSinks;
+
+ public bool IsReady { get; private set; }
+
+
+ ///
+ /// Ask CoreSystems to send the API methods.
+ /// Throws an exception if it gets called more than once per session without .
+ ///
+ /// Method to be called when CoreSystems replies.
+ public void Load(Action readyCallback = null)
+ {
+ if (_isRegistered)
+ throw new Exception($"{GetType().Name}.Load() should not be called multiple times!");
+
+ _readyCallback = readyCallback;
+ _isRegistered = true;
+ MyAPIGateway.Utilities.RegisterMessageHandler(CHANNEL, HandleMessage);
+ MyAPIGateway.Utilities.SendModMessage(CHANNEL, "ApiEndpointRequest");
+ }
+
+ public void Unload()
+ {
+ MyAPIGateway.Utilities.UnregisterMessageHandler(CHANNEL, HandleMessage);
+
+ ApiAssign(null);
+
+ _isRegistered = false;
+ _apiInit = false;
+ IsReady = false;
+ }
+
+ private void HandleMessage(object obj)
+ {
+ if (_apiInit || obj is string
+ ) // the sent "ApiEndpointRequest" will also be received here, explicitly ignoring that
+ return;
+
+ var dict = obj as IReadOnlyDictionary;
+
+ if (dict == null)
+ return;
+
+ ApiAssign(dict);
+
+ IsReady = true;
+ _readyCallback?.Invoke();
+ }
+
+ public void ApiAssign(IReadOnlyDictionary delegates)
+ {
+ _apiInit = (delegates != null);
+ /// base methods
+ AssignMethod(delegates, "ToggleStealth", ref _toggleStealth);
+ AssignMethod(delegates, "GetStatus", ref _getStatus);
+ AssignMethod(delegates, "GetDuration", ref _getDuration);
+ AssignMethod(delegates, "GetMainDrive", ref _getMainDrive);
+ AssignMethod(delegates, "GetHeatSinks", ref _getHeatSinks);
+ }
+
+ private void AssignMethod(IReadOnlyDictionary delegates, string name, ref T field)
+ where T : class
+ {
+ if (delegates == null)
+ {
+ field = null;
+ return;
+ }
+
+ Delegate del;
+ if (!delegates.TryGetValue(name, out del))
+ throw new Exception($"{GetType().Name} :: Couldn't find {name} delegate of type {typeof(T)}");
+
+ field = del as T;
+
+ if (field == null)
+ throw new Exception(
+ $"{GetType().Name} :: Delegate {name} is not type {typeof(T)}, instead it's: {del.GetType()}");
+ }
+
+ }
+
+}
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Data/Scripts/Stealth System/API/StealthAPI_PB.cs b/Utility Mods/Stealth Drive - Starcore Edition/Data/Scripts/Stealth System/API/StealthAPI_PB.cs
new file mode 100644
index 000000000..e31056e7e
--- /dev/null
+++ b/Utility Mods/Stealth Drive - Starcore Edition/Data/Scripts/Stealth System/API/StealthAPI_PB.cs
@@ -0,0 +1,73 @@
+using System;
+using System.Collections.Generic;
+using Sandbox.ModAPI.Interfaces;
+
+namespace StealthSystem
+{
+ internal class StealthPbAPI
+ {
+ /// Returns true if drive status was toggled successfully.
+ public bool ToggleStealth(Sandbox.ModAPI.Ingame.IMyTerminalBlock drive) => _toggleStealth?.Invoke(drive) ?? false;
+
+ /// Returns status of drive. 0 = Ready, 1 = Active, 2 = Cooldown, 3 = Not enough power, 4 = Offline
+ public int GetStatus(Sandbox.ModAPI.Ingame.IMyTerminalBlock drive) => _getStatus?.Invoke(drive) ?? 4;
+
+ /// Returns remaining duration of stealth/cooldown.
+ public int GetDuration(Sandbox.ModAPI.Ingame.IMyTerminalBlock drive) => _getDuration?.Invoke(drive) ?? 0;
+
+ /// Retuns active stealth drive on grid if one exists, otherwise returns null.
+ public Sandbox.ModAPI.Ingame.IMyTerminalBlock GetMainDrive(VRage.Game.ModAPI.Ingame.IMyCubeGrid grid) => _getMainDrive?.Invoke(grid);
+
+ /// Collection to populate with heat sinks on grid.
+ public void GetHeatSinks(VRage.Game.ModAPI.Ingame.IMyCubeGrid grid, ICollection sinks) => _getHeatSinks?.Invoke(grid, sinks);
+
+
+
+
+ private Func _toggleStealth;
+ private Func _getStatus;
+ private Func _getDuration;
+ private Func _getMainDrive;
+ private Action> _getHeatSinks;
+
+ public bool Activate(Sandbox.ModAPI.Ingame.IMyTerminalBlock pbBlock)
+ {
+ var dict = pbBlock.GetProperty("StealthPbAPI")?.As>().GetValue(pbBlock);
+ if (dict == null) throw new Exception("StealthPbAPI failed to activate");
+ return ApiAssign(dict);
+ }
+
+ public bool ApiAssign(IReadOnlyDictionary delegates)
+ {
+ if (delegates == null)
+ return false;
+
+ AssignMethod(delegates, "ToggleStealth", ref _toggleStealth);
+ AssignMethod(delegates, "GetStatus", ref _getStatus);
+ AssignMethod(delegates, "GetDuration", ref _getDuration);
+ AssignMethod(delegates, "GetMainDrive", ref _getMainDrive);
+ AssignMethod(delegates, "GetHeatSinks", ref _getHeatSinks);
+ return true;
+ }
+
+ private void AssignMethod(IReadOnlyDictionary delegates, string name, ref T field) where T : class
+ {
+ if (delegates == null)
+ {
+ field = null;
+ return;
+ }
+
+ Delegate del;
+ if (!delegates.TryGetValue(name, out del))
+ throw new Exception($"{GetType().Name} :: Couldn't find {name} delegate of type {typeof(T)}");
+
+ field = del as T;
+ if (field == null)
+ throw new Exception(
+ $"{GetType().Name} :: Delegate {name} is not type {typeof(T)}, instead it's: {del.GetType()}");
+ }
+
+ }
+
+}
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Data/Scripts/Stealth System/Comp/CompData.cs b/Utility Mods/Stealth Drive - Starcore Edition/Data/Scripts/Stealth System/Comp/CompData.cs
new file mode 100644
index 000000000..1ebec4775
--- /dev/null
+++ b/Utility Mods/Stealth Drive - Starcore Edition/Data/Scripts/Stealth System/Comp/CompData.cs
@@ -0,0 +1,486 @@
+using ProtoBuf;
+using Sandbox.ModAPI;
+using System;
+using VRage.Game.ModAPI;
+using VRage.ModAPI;
+using System.Collections.Generic;
+using VRageMath;
+using Sandbox.Game.Entities;
+
+namespace StealthSystem
+{
+
+ [ProtoContract]
+ public class DriveRepo
+ {
+ [ProtoMember(1)] public bool StealthActive;
+ [ProtoMember(2)] public bool CoolingDown;
+ [ProtoMember(3)] public int RemainingDuration; //TimeElapsed
+ [ProtoMember(4)] public int TotalTime;
+
+
+ public void Sync(DriveComp comp)
+ {
+ StealthActive = comp.StealthActive;
+ CoolingDown = comp.CoolingDown;
+ //RemainingDuration = comp.RemainingDuration;
+ RemainingDuration = comp.TimeElapsed;
+ TotalTime = comp.TotalTime;
+ }
+
+ }
+
+ [ProtoContract]
+ public class SinkRepo
+ {
+ [ProtoMember(1)] public bool Accumulating;
+ [ProtoMember(2)] public bool Radiating;
+ [ProtoMember(3)] public byte HeatPercent;
+
+
+ public void Sync(SinkComp comp)
+ {
+ Accumulating = comp.Accumulating;
+ Radiating = comp.Radiating;
+ HeatPercent = comp.HeatPercent;
+ }
+
+ }
+
+ internal class WaterData
+ {
+ public WaterData(MyPlanet planet)
+ {
+ Planet = planet;
+ WaterId = planet.EntityId;
+ }
+
+ internal MyPlanet Planet;
+ internal long WaterId;
+ internal Vector3D Centre;
+ internal float Radius;
+ }
+
+ internal class GridComp
+ {
+ internal List StealthComps = new List();
+ internal List HeatComps = new List();
+
+ internal List ShieldBlocks = new List();
+ internal List Turrets;
+
+ internal DriveComp MasterComp;
+ internal GroupMap GroupMap;
+ internal IMyCubeGrid Grid;
+ internal MyPlanet Planet;
+ internal BoundingSphereD Water;
+
+ internal bool GroupsDirty;
+ internal bool Revealed;
+ internal bool Underwater;
+ internal bool WaterValid = true;
+ internal bool DisableShields;
+ internal bool DisableWeapons;
+
+ internal int DamageTaken;
+ internal int SinkBonus;
+
+ private StealthSession _session;
+
+ internal void Init(IMyCubeGrid grid, StealthSession session)
+ {
+ _session = session;
+
+ Grid = grid;
+
+ Grid.OnBlockAdded += BlockAdded;
+ Grid.OnBlockRemoved += BlockRemoved;
+
+ DisableShields = _session.DisableShields;
+ DisableWeapons = _session.DisableWeapons && !_session.WcActive;
+
+ if (DisableWeapons) Turrets = new List();
+
+ var group = MyAPIGateway.GridGroups.GetGridGroup(GridLinkTypeEnum.Physical, grid);
+ if (group != null)
+ {
+ GroupMap map;
+ if (_session.GridGroupMap.TryGetValue(group, out map))
+ GroupMap = map;
+ }
+ else Logs.WriteLine("group null at GridComp.Init()");
+
+ GroupsDirty = true;
+
+ if (!DisableShields && !DisableWeapons) return;
+
+ var blocks = grid.GetFatBlocks();
+ foreach (var block in blocks)
+ {
+ if (block?.BlockDefinition == null) continue;
+
+ if (DisableShields && _session.ShieldBlocks.Contains(block.BlockDefinition.SubtypeName))
+ ShieldBlocks.Add(block as IMyFunctionalBlock);
+
+ if (DisableWeapons && block is IMyUserControllableGun)
+ Turrets.Add(block as IMyUserControllableGun);
+ }
+
+ if (_session.TrackWater)
+ {
+ Planet = MyGamePruningStructure.GetClosestPlanet(Grid.PositionComp.WorldAABB.Center);
+
+ WaterData waterData;
+ if (Planet != null && _session.WaterMap.TryGetValue(Planet.EntityId, out waterData))
+ {
+ Water = new BoundingSphereD(waterData.Centre, waterData.Radius + _session.WaterTransitionDepth);
+
+ var planetVector = Grid.PositionComp.WorldAABB.Center - waterData.Centre;
+ var radius = waterData.Radius + _session.WaterTransitionDepth;
+ var radiusSqr = radius * radius;
+ if (planetVector.LengthSquared() < radiusSqr)
+ {
+ Underwater = true;
+
+ var obb = new MyOrientedBoundingBoxD(Grid.PositionComp.LocalAABB, Grid.PositionComp.WorldMatrixRef);
+ obb.GetCorners(_session.ObbCorners, 0);
+ for (int j = 0; j < 8; j++)
+ {
+ var corner = _session.ObbCorners[j];
+ planetVector = corner = waterData.Centre;
+ if (planetVector.LengthSquared() > radiusSqr)
+ {
+ Underwater = false;
+ break;
+ }
+ }
+ }
+ WaterValid = Underwater == _session.WorkInWater;
+ }
+ }
+ }
+
+ private void BlockAdded(IMySlimBlock slim)
+ {
+ if (slim.FatBlock == null) return;
+
+ var fat = slim.FatBlock;
+ if (fat is IMyUpgradeModule)
+ {
+ var module = fat as IMyUpgradeModule;
+ if (_session.DriveDefinitions.ContainsKey(module.BlockDefinition.SubtypeName))
+ {
+ if (!_session.DriveMap.ContainsKey(module.EntityId))
+ {
+ Logs.WriteLine("BlockAdded() - Drive not in map!");
+ return;
+ }
+
+ var dComp = _session.DriveMap[module.EntityId];
+
+ if (!dComp.Inited)
+ {
+ Logs.WriteLine("BlockAdded() - Drive not yet Inited!");
+ return;
+ }
+
+ var gridComp = _session.GridMap[Grid];
+
+ if (gridComp.StealthComps.Contains(dComp))
+ {
+ Logs.WriteLine("BlockAdded() - Drive already in correct GridComp!");
+ return;
+ }
+
+ try
+ {
+ dComp.GridChange();
+ }
+ catch (Exception ex)
+ {
+ Logs.WriteLine($"Exception in GridChange() {ex}");
+ }
+ }
+ }
+
+ if (DisableShields && _session.ShieldBlocks.Contains(fat.BlockDefinition.SubtypeName))
+ ShieldBlocks.Add(fat as IMyFunctionalBlock);
+
+ if (DisableWeapons && fat is IMyUserControllableGun)
+ Turrets.Add(fat as IMyUserControllableGun);
+ }
+
+ private void BlockRemoved(IMySlimBlock slim)
+ {
+ if (slim.FatBlock == null) return;
+
+ var func = slim.FatBlock as IMyFunctionalBlock;
+ if (DisableShields && func != null && ShieldBlocks.Contains(func))
+ ShieldBlocks.Remove(func);
+
+ var wep = func as IMyUserControllableGun;
+ if (DisableWeapons && wep != null && Turrets.Contains(wep))
+ Turrets.Remove(wep);
+ }
+
+ internal void Clean()
+ {
+ Grid.OnBlockAdded -= BlockAdded;
+ Grid.OnBlockRemoved -= BlockRemoved;
+
+ StealthComps.Clear();
+ HeatComps.Clear();
+ ShieldBlocks.Clear();
+
+ if (DisableWeapons) Turrets.Clear();
+
+ MasterComp = null;
+ GroupMap = null;
+ Grid = null;
+
+ GroupsDirty = false;
+ Revealed = false;
+ DamageTaken = 0;
+
+ _session = null;
+ }
+ }
+
+ internal class GroupMap
+ {
+ private StealthSession _session;
+
+ public IMyGridGroupData GroupData;
+
+ internal List ConnectedGrids = new List();
+
+ internal List SlimBlocks = new List();
+ internal HashSet Children = new HashSet();
+
+ internal void Init(IMyGridGroupData data, StealthSession session)
+ {
+ GroupData = data;
+
+ _session = session;
+ }
+
+ public void OnGridAdded(IMyGridGroupData newGroup, IMyCubeGrid grid, IMyGridGroupData oldGroup)
+ {
+ try
+ {
+ ConnectedGrids.Add(grid);
+
+ GridComp gridComp;
+ if (!_session.GridMap.TryGetValue(grid, out gridComp))
+ return;
+
+ gridComp.GroupMap = this;
+ gridComp.GroupsDirty = true;
+
+ bool thisActive = false;
+ var thisMaster = gridComp.MasterComp;
+ if (thisMaster != null && thisMaster.StealthActive) //Added grid has active drive
+ thisActive = true;
+ else if (((uint)grid.Flags & StealthSession.IsStealthedFlag) > 0) //Added grid is being stealthed by another grid
+ return;
+
+ var newSubgrids = new List();
+ GridComp subgridComp;
+ DriveComp subgridMaster = null;
+
+ GroupData.GetGrids(newSubgrids);
+ for (int i = 0; i < newSubgrids.Count; i++)
+ {
+ var newSubgrid = newSubgrids[i];
+ if (newSubgrid == grid) continue;
+
+ if (thisActive)
+ {
+ if (((uint)newSubgrid.Flags & StealthSession.IsStealthedFlag) > 0) continue; //Other grid already stealthed
+
+ newSubgrid.Flags |= _session.StealthFlag;
+
+ if (!_session.IsDedicated && !thisMaster.VisibleToClient)
+ StealthConnectedGrid(newSubgrid, thisMaster, true);
+
+ //continue;
+ }
+
+ if (!_session.GridMap.TryGetValue(newSubgrid, out subgridComp))
+ continue;
+
+ if (thisActive) //Reenable shield emitters/vanilla turrets since previously connected grid is no longer stealthed
+ {
+ if (gridComp.DisableShields)
+ thisMaster.DisableShields(subgridComp);
+
+ if (gridComp.DisableWeapons)
+ thisMaster.DisableTurrets(subgridComp);
+
+ continue;
+ }
+
+ subgridMaster = subgridComp.MasterComp;
+ if (subgridMaster == null) continue;
+
+ if (subgridMaster.StealthActive) //Other grid has active drive so stealth this grid
+ {
+ grid.Flags |= _session.StealthFlag;
+
+ if (!_session.IsDedicated && !subgridMaster.VisibleToClient)
+ StealthConnectedGrid(grid, subgridMaster, true);
+
+ if (gridComp.DisableShields)
+ subgridMaster.DisableShields(gridComp);
+
+ if (gridComp.DisableWeapons)
+ subgridMaster.DisableTurrets(gridComp);
+
+ return;
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ Logs.WriteLine($"Exception in OnGridAdded(): {ex}");
+ }
+
+ }
+
+ public void OnGridRemoved(IMyGridGroupData oldGroup, IMyCubeGrid grid, IMyGridGroupData newGroup)
+ {
+ try
+ {
+ ConnectedGrids.Remove(grid);
+
+ GridComp gridComp;
+ if (!_session.GridMap.TryGetValue(grid, out gridComp))
+ return;
+
+ gridComp.GroupsDirty = true;
+
+ bool thisActive = false;
+ var thisMaster = gridComp.MasterComp;
+ if (thisMaster != null && thisMaster.StealthActive) //Removed grid has active drive
+ thisActive = true;
+ //else if (((uint)grid.Flags & IsStealthedFlag) > 0)
+ // return;
+
+ var formerSubgrids = new List();
+ GridComp subgridComp;
+ DriveComp subgridMaster = null;
+
+ GroupData.GetGrids(formerSubgrids);
+ for (int i = 0; i < formerSubgrids.Count; i++)
+ {
+ var formerSubgrid = formerSubgrids[i];
+ if (formerSubgrid == grid) continue;
+
+ if (thisActive) //Unstealth previously connected grid since this grid was providing stealth
+ {
+ formerSubgrid.Flags ^= _session.StealthFlag;
+
+ if (!_session.IsDedicated)
+ StealthConnectedGrid(formerSubgrid, thisMaster, false);
+ }
+
+ if (!_session.GridMap.TryGetValue(formerSubgrid, out subgridComp))
+ continue;
+
+ if (thisActive) //Reenable shield emitters/vanilla turrets since previously connected grid is no longer stealthed
+ {
+ if (gridComp.DisableShields)
+ thisMaster.ReEnableShields(subgridComp);
+
+ if (gridComp.DisableWeapons)
+ thisMaster.ReEnableTurrets(subgridComp);
+
+ continue;
+ }
+
+ //We only keep going if the removed grid wasn't providing stealth
+ //We check if the grid it was connected to was stealthing it
+
+ subgridMaster = subgridComp.MasterComp;
+ if (subgridMaster == null) continue;
+
+ if (subgridMaster.StealthActive) //Connected grid was providing stealth so destealth this
+ {
+ grid.Flags ^= _session.StealthFlag;
+
+ if (!_session.IsDedicated)
+ StealthConnectedGrid(grid, subgridMaster, false);
+
+ if (gridComp.DisableShields)
+ subgridMaster.ReEnableShields(gridComp);
+
+ if (gridComp.DisableWeapons)
+ subgridMaster.ReEnableTurrets(gridComp);
+
+ return;
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ Logs.WriteLine($"Exception in OnGridRemoved(): {ex}");
+ }
+
+ }
+
+ internal void StealthConnectedGrid(IMyCubeGrid grid, DriveComp comp, bool stealth)
+ {
+ if (stealth) _session.StealthedGrids.Add(grid);
+ else _session.StealthedGrids.Remove(grid);
+
+ grid.GetBlocks(SlimBlocks);
+
+ var dither = stealth ? _session.Transparency : 0f;
+ foreach (var slim in SlimBlocks)
+ {
+ var fatBlock = slim.FatBlock;
+ if (fatBlock == null)
+ {
+ slim.Dithering = dither;
+ continue;
+ }
+
+ fatBlock.Render.Transparency = dither;
+ fatBlock.Render.UpdateTransparency();
+
+ fatBlock.Hierarchy.GetChildrenRecursive(Children);
+ foreach (var child in Children)
+ {
+ child.Render.Transparency = dither;
+ child.Render.UpdateTransparency();
+ }
+ Children.Clear();
+
+ //var cockpit = fatBlock as IMyCockpit;
+ //if (cockpit != null && cockpit.Pilot != null)
+ // cockpit.Pilot.Render.Visible = !add;
+
+ var jump = fatBlock as IMyJumpDrive;
+ if (jump != null)
+ {
+ if (stealth) comp.JumpDrives.Add(jump, jump.CurrentStoredPower);
+ else comp.JumpDrives.Remove(jump);
+ }
+ }
+ SlimBlocks.Clear();
+ }
+
+ internal void Clean()
+ {
+ GroupData = null;
+
+ ConnectedGrids.Clear();
+
+ SlimBlocks.Clear();
+ Children.Clear();
+
+ _session = null;
+ }
+ }
+
+}
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Data/Scripts/Stealth System/Comp/DriveComp.cs b/Utility Mods/Stealth Drive - Starcore Edition/Data/Scripts/Stealth System/Comp/DriveComp.cs
new file mode 100644
index 000000000..89ea57241
--- /dev/null
+++ b/Utility Mods/Stealth Drive - Starcore Edition/Data/Scripts/Stealth System/Comp/DriveComp.cs
@@ -0,0 +1,1068 @@
+using Sandbox.ModAPI;
+using System;
+using VRage.Game;
+using VRage.Game.Components;
+using VRage.Game.ModAPI;
+using VRage.ModAPI;
+using VRageMath;
+using VRage.Utils;
+using Sandbox.Game.Entities;
+using SpaceEngineers.Game.ModAPI;
+using VRage.Game.Entity;
+using System.Collections.Generic;
+using Sandbox.Game.EntityComponents;
+using System.Text;
+using Sandbox.ModAPI.Interfaces.Terminal;
+using System.Collections.Concurrent;
+using Sandbox.ModAPI.Interfaces;
+
+namespace StealthSystem
+{
+ public class DriveComp : MyEntityComponentBase
+ {
+ internal IMyFunctionalBlock Block;
+ internal IMyCubeGrid Grid;
+ internal MyResourceSinkComponent Sink;
+ internal MyResourceDistributorComponent Source;
+ internal IMyTerminalControlOnOffSwitch ShowInToolbarSwitch;
+ internal IMyGps HeatSignature;
+
+ internal DriveRepo Repo;
+ internal GridComp GridComp;
+ internal Definitions.DriveDefinition Definition;
+
+ //internal List ConnectedGrids = new List();
+ internal List SlimBlocks = new List();
+ internal List FadeEntities = new List();
+ internal List FadeSlims = new List();
+ internal HashSet Children = new HashSet();
+ internal HashSet StealthedExternalGrids = new HashSet();
+ internal Dictionary JumpDrives = new Dictionary();
+ internal ConcurrentDictionary DisabledBlocks = new ConcurrentDictionary();
+ internal List ReplicatedClients = new List();
+ internal HashSet PreviousEntities = new HashSet();
+ internal HashSet CurrentEntities = new HashSet();
+
+ internal List NearbyTurrets;
+ internal MyOrientedBoundingBoxD ExpandedOBB;
+ internal Color OldColour;
+
+ internal bool IsPrimary;
+ internal bool Inited;
+ internal bool Online;
+ internal bool CoolingDown;
+ internal bool SufficientPower;
+ internal bool StealthActive;
+ internal bool EnterStealth;
+ internal bool ExitStealth;
+ internal bool VisibleToClient;
+ internal bool Fading;
+ internal bool GridUpdated;
+ internal bool BlocksDirty;
+ internal bool PowerDirty;
+ internal bool TransferFailed;
+ internal bool StealthOnInit;
+ internal bool CdOnInit;
+ internal bool Transfer;
+ internal bool ShieldWaiting;
+ internal bool IgnorePower;
+
+ internal int Fade;
+ internal int ShieldWait;
+ internal int SurfaceArea;
+ internal int MaxDuration;
+ internal int RemainingDuration;
+ internal int TimeElapsed;
+ internal int TotalTime;
+ internal long CompTick15;
+ internal long CompTick60;
+ internal long SignalDistance;
+ internal long SignalDistanceSquared;
+
+ internal float RequiredPower;
+ internal float Transparency;
+ internal float TransOffset = -0.35f;
+
+ private readonly StealthSession _session;
+
+ private List _entities;
+ private BoundingSphereD _sphere;
+ private readonly Vector3D[] _obbCorners = new Vector3D[8];
+
+ internal DriveComp(IMyFunctionalBlock stealthBlock, Definitions.DriveDefinition def, StealthSession session)
+ {
+ _session = session;
+
+ Block = stealthBlock;
+ Definition = def;
+
+ Transparency = -_session.Transparency;
+
+ if (!_session.WcActive)
+ {
+ NearbyTurrets = new List();
+ _entities = new List();
+ _sphere = new BoundingSphereD(Vector3D.Zero, 1200);
+ }
+ }
+
+ public override void OnAddedToContainer()
+ {
+ base.OnAddedToContainer();
+ }
+
+ public override void OnAddedToScene()
+ {
+ base.OnAddedToScene();
+
+ if (!MyAPIGateway.Session.IsServer) StealthSession.SendPacketToServer(new ReplicationPacket { EntityId = Block.EntityId, Fresh = true, Type = PacketType.Replicate });
+ }
+
+ public override void OnBeforeRemovedFromContainer()
+ {
+ base.OnBeforeRemovedFromContainer();
+
+ Close();
+ }
+
+ public override bool IsSerialized()
+ {
+ if (Block.Storage == null || Repo == null) return false;
+
+ Repo.Sync(this);
+
+ Block.Storage[_session.CompDataGuid] = Convert.ToBase64String(MyAPIGateway.Utilities.SerializeToBinary(Repo));
+
+ return false;
+ }
+
+ internal void Init()
+ {
+ Grid = Block.CubeGrid;
+ if (Grid == null)
+ {
+ Logs.WriteLine("DriveComp.Init() - Grid null");
+ return;
+ }
+
+ var gridData = _session.GridMap[Grid];
+ if (gridData.StealthComps.Count == 1)
+ {
+ IsPrimary = true;
+ GridComp = gridData;
+ gridData.MasterComp = this;
+ }
+
+ Block.Components.Add(this);
+ CompTick15 = Block.EntityId % 15;
+ CompTick60 = Block.EntityId % 60;
+
+ SinkInit();
+ StorageInit();
+
+ Inited = true;
+ GridUpdated = true;
+ VisibleToClient = true;
+
+ Grid.OnGridSplit += GridSplit;
+ Grid.OnBlockAdded += BlockAdded;
+ Grid.OnBlockRemoved += BlockRemoved;
+
+ Block.EnabledChanged += EnabledChanged;
+ Source.SystemChanged += SourceChanged;
+
+ if (!_session.IsDedicated)
+ {
+ GetShowInToolbarSwitch();
+ Block.AppendingCustomInfo += AppendingCustomData;
+ }
+
+ if (!MyAPIGateway.Session.IsServer)
+ StealthSession.SendPacketToServer(new ReplicationPacket { EntityId = Block.EntityId, Fresh = true, Type = PacketType.Replicate });
+ }
+
+ internal void Close()
+ {
+ if (Transfer) return;
+
+ if (IsPrimary)
+ TransferPrimary(true);
+
+ _session.DriveMap.Remove(Block.EntityId);
+
+ GridComp gridComp;
+ if (_session.GridMap.TryGetValue(Grid, out gridComp))
+ {
+ gridComp.StealthComps.Remove(this);
+ }
+
+ Grid.OnGridSplit -= GridSplit;
+ Grid.OnBlockAdded -= BlockAdded;
+ Grid.OnBlockRemoved -= BlockRemoved;
+
+ Block.EnabledChanged -= EnabledChanged;
+
+ if (Source != null)
+ Source.SystemChanged -= SourceChanged;
+ else Logs.WriteLine("Source null on close");
+
+ if (StealthActive && !VisibleToClient)
+ {
+ SwitchStealth(false);
+
+ foreach (var entity in PreviousEntities)
+ {
+ if (entity == null)
+ {
+ Logs.WriteLine($"Previous entity null on close");
+ continue;
+ }
+
+ if (!_session.IsDedicated)
+ {
+ if (entity is IMyCubeGrid)
+ StealthExternalGrid(false, entity as IMyCubeGrid);
+ else
+ entity.Render.Visible = true;
+ }
+
+ entity.Flags ^= _session.StealthFlag;
+ }
+ }
+
+ if (HeatSignature != null)
+ MyAPIGateway.Session.GPS.RemoveLocalGps(HeatSignature);
+
+ if (!_session.IsDedicated)
+ Block.AppendingCustomInfo -= AppendingCustomData;
+
+ if (!MyAPIGateway.Session.IsServer)
+ StealthSession.SendPacketToServer(new ReplicationPacket { EntityId = Block.EntityId, Fresh = false, Type = PacketType.Replicate });
+
+ Clean();
+ }
+
+ internal void Clean()
+ {
+ Block = null;
+ Grid = null;
+ Sink = null;
+ Source = null;
+ ShowInToolbarSwitch = null;
+ HeatSignature = null;
+
+ Repo = null;
+ GridComp = null;
+
+ //ConnectedGrids = null;
+ SlimBlocks = null;
+ FadeEntities = null;
+ FadeSlims = null;
+ StealthedExternalGrids = null;
+ JumpDrives = null;
+ DisabledBlocks = null;
+ ReplicatedClients = null;
+ PreviousEntities = null;
+ CurrentEntities = null;
+ NearbyTurrets = null;
+ }
+
+ internal void GridChange()
+ {
+ Grid.OnGridSplit -= GridSplit;
+ Grid.OnBlockAdded -= BlockAdded;
+ Grid.OnBlockRemoved -= BlockRemoved;
+
+ if (StealthActive)
+ SwitchStealth(false, true);
+
+ var gridData = _session.GridMap[Grid];
+ if (TransferPrimary(true))
+ {
+ var newPrimary = gridData.MasterComp;
+ newPrimary.IsPrimary = true;
+ //newPrimary.RemainingDuration = StealthActive ? MaxDuration - RemainingDuration : RemainingDuration;
+ //newPrimary.CoolingDown = RemainingDuration > 0;
+ newPrimary.TotalTime = TotalTime;
+ newPrimary.TimeElapsed = TimeElapsed;
+ newPrimary.CoolingDown = TimeElapsed < TotalTime;
+
+ }
+
+ gridData.StealthComps.Remove(this);
+
+ Grid = Block.CubeGrid;
+
+ //if (StealthActive)
+ // Grid.Visible = false;
+
+ var newGridData = _session.GridMap[Block.CubeGrid];
+ GridComp = newGridData;
+ newGridData.StealthComps.Add(this);
+ if (newGridData.MasterComp == null)
+ {
+ newGridData.MasterComp = this;
+ IsPrimary = true;
+ }
+ else
+ {
+ IsPrimary = false;
+ }
+
+ Grid.OnGridSplit += GridSplit;
+ Grid.OnBlockAdded += BlockAdded;
+ Grid.OnBlockRemoved += BlockRemoved;
+
+ Source = Grid.ResourceDistributor as MyResourceDistributorComponent;
+ CalculatePowerRequirements();
+ UpdateStatus(true);
+
+ Transfer = false;
+ }
+
+ internal bool TransferPrimary(bool force)
+ {
+ var gridData = _session.GridMap[Grid];
+
+ if (gridData.StealthComps.Count <= 1)
+ return false;
+
+ DriveComp newPrimary = null;
+ for (int i = 0; i < gridData.StealthComps.Count; i++)
+ {
+ var comp = gridData.StealthComps[i];
+
+ if (comp == this || comp.Block.CubeGrid != Grid)
+ continue;
+
+ if (comp.Block.IsFunctional)
+ {
+ newPrimary = comp;
+ break;
+ }
+
+ if (force && newPrimary == null)
+ newPrimary = comp;
+ }
+
+ if (newPrimary == null)
+ return false;
+
+ IsPrimary = false;
+ newPrimary.IsPrimary = true;
+ gridData.MasterComp = newPrimary;
+ return true;
+ }
+
+ private void SourceChanged()
+ {
+ PowerDirty = true;
+ GridUpdated = true;
+ }
+
+ private void EnabledChanged(IMyTerminalBlock block)
+ {
+ UpdateStatus();
+ block.RefreshCustomInfo();
+ }
+
+ private void GridSplit(IMyCubeGrid grid1, IMyCubeGrid grid2)
+ {
+ GridUpdated = true;
+ BlocksDirty = true;
+ }
+
+ private void BlockAdded(IMySlimBlock slim)
+ {
+ GridUpdated = true;
+ BlocksDirty = true;
+
+ if (StealthActive)
+ DitherBlock(true, slim);
+ }
+
+ private void BlockRemoved(IMySlimBlock slim)
+ {
+ GridUpdated = true;
+ BlocksDirty = true;
+
+ if (StealthActive)
+ DitherBlock(false, slim);
+ }
+
+ private void AppendingCustomData(IMyTerminalBlock block, StringBuilder builder)
+ {
+ var status = !IsPrimary ? "Standby"
+ : !Online ? "Offline"
+ : !SufficientPower ? "Insufficient Power"
+ : CoolingDown ? "Cooling Down"
+ : !GridComp.WaterValid ? _session.WorkInWater ? "Not Submerged" : "Submerged"
+ : StealthActive ? "Stealth Engaged"
+ : "Ready";
+
+ builder.Append("Drive Status: ")
+ .Append(status)
+ .Append("\n");
+
+ if (!IsPrimary) return;
+
+ if (Online)
+ {
+ if (!StealthActive && !CoolingDown)
+ builder.Append($"Stealth Duration: {MaxDuration / 60}s \n");
+
+ builder.Append($"Surface Area: {SurfaceArea} blocks square \n")
+ .Append($"Required Power: {RequiredPower.ToString("F1")}MW \n")
+ .Append($"Detection Radius: {SignalDistance}m \n");
+ }
+
+ if (StealthActive)
+ {
+ int timeLeft = (TotalTime - TimeElapsed) / 60;
+ int seconds = timeLeft % 60;
+ int minutes = (timeLeft - seconds) / 60;
+ builder.Append("Time Remaining: ")
+ .Append($"{minutes.ToString("00")}:{seconds.ToString("00")}\n");
+ }
+
+ if (CoolingDown)
+ {
+ int timeLeft = (TimeElapsed) / 60;
+ int seconds = timeLeft % 60;
+ int minutes = (timeLeft - seconds) / 60;
+ builder.Append("Time Remaining: ")
+ .Append($"{minutes.ToString("00")}:{seconds.ToString("00")}\n");
+ }
+ }
+
+ internal void UpdateStatus(bool gridChange = false)
+ {
+ if (PowerDirty || Source == null)
+ {
+ Source = Grid.ResourceDistributor as MyResourceDistributorComponent;
+ PowerDirty = false;
+ }
+ var available = Source.MaxAvailableResourceByType(MyResourceDistributorComponent.ElectricityId, (MyCubeGrid)Grid) - Source.TotalRequiredInputByType(MyResourceDistributorComponent.ElectricityId, (MyCubeGrid)Grid);
+ SufficientPower = StealthActive ? available >= 0 : available >= RequiredPower;
+ Online = Block.IsFunctional && Block.Enabled && available > 0;
+
+ if (!_session.IsDedicated)
+ SetEmissiveColor(gridChange);
+ }
+
+ internal void SetEmissiveColor(bool force)
+ {
+ var emissiveColor = !Block.IsFunctional ? Color.Black : !Online ? EmissiveValues.RED : StealthActive ? Color.Cyan : CoolingDown ? Color.OrangeRed : EmissiveValues.GREEN;
+ if (!force && emissiveColor == OldColour)
+ return;
+
+ OldColour = emissiveColor;
+ Block.SetEmissiveParts(StealthSession.STATUS_EMISSIVE, emissiveColor, 1f);
+ }
+
+ internal void RefreshTerminal()
+ {
+ Block.RefreshCustomInfo();
+
+ if (ShowInToolbarSwitch != null)
+ {
+ var originalSetting = ShowInToolbarSwitch.Getter(Block);
+ ShowInToolbarSwitch.Setter(Block, !originalSetting);
+ ShowInToolbarSwitch.Setter(Block, originalSetting);
+ }
+ }
+
+ internal void CalculatePowerRequirements()
+ {
+ //ConnectedGrids.Clear();
+ //MyAPIGateway.GridGroups.GetGroup(Grid, GridLinkTypeEnum.Physical, ConnectedGrids);
+
+ CalculateExpandedOBB();
+ var scale = Grid.GridSizeEnum == MyCubeSize.Large ? 6.25 : 0.25;
+ var areaMetres = (int)OBBSurfaceArea(ExpandedOBB);
+ SurfaceArea = (int)(areaMetres / scale);
+
+ RequiredPower = areaMetres * Definition.PowerScale;
+ SignalDistance = (int)(RequiredPower * Definition.SignalRangeScale);
+ SignalDistanceSquared = SignalDistance * SignalDistance;
+
+ }
+
+ internal void CalculateExpandedOBB()
+ {
+ var worldMat = Grid.PositionComp.WorldMatrixRef;
+ var halfExtents = (Vector3D)Grid.PositionComp.LocalAABB.HalfExtents;
+ var newCentre = Grid.PositionComp.WorldAABB.Center;
+
+ var left = worldMat.Left;
+ var up = worldMat.Up;
+ var back = worldMat.Backward;
+
+ var grids = GridComp.GroupMap.ConnectedGrids;
+ for (int i = 0; i < grids.Count; i++)
+ {
+ var cGrid = grids[i];
+ if (cGrid == Grid) continue;
+
+ var obb = new MyOrientedBoundingBoxD(cGrid.PositionComp.LocalAABB, cGrid.PositionComp.WorldMatrixRef);
+ obb.GetCorners(_obbCorners, 0);
+ for (int j = 0; j < 8; j++)
+ {
+ var point = _obbCorners[j];
+ var offset = point - newCentre;
+ if (offset.LengthSquared() < Math.Pow(halfExtents.Min(), 2))
+ continue;
+
+ var xDot = Vector3D.Dot(offset, left);
+ var xAbs = Math.Abs(xDot);
+ if (xAbs > halfExtents.X)
+ {
+ var dist = (xAbs - halfExtents.X) / 2;
+ halfExtents.X += dist;
+ newCentre += left * dist * Math.Sign(xDot);
+ }
+
+ var yDot = Vector3D.Dot(offset, up);
+ var yAbs = Math.Abs(yDot);
+ if (yAbs > halfExtents.Y)
+ {
+ var dist = (yAbs - halfExtents.Y) / 2;
+ halfExtents.Y += dist;
+ newCentre += up * dist * Math.Sign(yDot);
+ }
+
+ var zDot = Vector3D.Dot(offset, back);
+ var zAbs = Math.Abs(zDot);
+ if (zAbs > halfExtents.Z)
+ {
+ var dist = (zAbs - halfExtents.Z) / 2;
+ halfExtents.Z += dist;
+ newCentre += back * dist * Math.Sign(zDot);
+ }
+ }
+ }
+
+ var orientation = Quaternion.CreateFromRotationMatrix(worldMat);
+ ExpandedOBB = new MyOrientedBoundingBoxD(newCentre, halfExtents, orientation);
+
+ }
+
+ internal double OBBSurfaceArea(MyOrientedBoundingBoxD obb)
+ {
+ var halfExtent = obb.HalfExtent;
+
+ return 8 * (halfExtent.X * halfExtent.Y + halfExtent.X * halfExtent.Z + halfExtent.Y * halfExtent.Z);
+ }
+
+ internal void PrepGrids(bool set)
+ {
+ //ConnectedGrids.Clear();
+ //MyAPIGateway.GridGroups.GetGroup(Grid, GridLinkTypeEnum.Physical, ConnectedGrids);
+
+ var grids = GridComp.GroupMap.ConnectedGrids;
+ for (int i = 0; i < grids.Count; i++)
+ {
+ var grid = grids[i];
+ var comp = _session.GridMap[grid];
+
+ if (set)
+ {
+ grid.Flags |= _session.StealthFlag;
+ _session.StealthedGrids.Add(grid);
+
+ if (GridComp.DisableShields)
+ DisableShields(comp);
+
+ if (GridComp.DisableWeapons)
+ DisableTurrets(comp);
+ }
+ else
+ {
+ grid.Flags ^= _session.StealthFlag;
+ _session.StealthedGrids.Remove(grid);
+
+ if (GridComp.DisableWeapons)
+ ReEnableTurrets(comp);
+ }
+ }
+
+ if (!set && _session.DisableShields)
+ {
+ ShieldWait = _session.ShieldDelay;
+ ShieldWaiting = true;
+ }
+
+ }
+
+ internal void DisableShields(GridComp comp)
+ {
+ for (int j = 0; j < comp.ShieldBlocks.Count; j++)
+ {
+ var block = comp.ShieldBlocks[j];
+
+ DisabledBlocks[block] = block.Enabled;
+
+ block.Enabled = false;
+ block.EnabledChanged += OnEnabledChanged;
+ }
+ }
+
+ internal void DisableTurrets(GridComp comp)
+ {
+ for (int j = 0; j < comp.Turrets.Count; j++)
+ {
+ var block = comp.Turrets[j];
+
+ DisabledBlocks[block] = block.Enabled;
+
+ block.Enabled = false;
+ block.EnabledChanged += OnEnabledChanged;
+ }
+ }
+
+ internal void ReEnableShields(GridComp comp)
+ {
+ for (int j = 0; j < comp.ShieldBlocks.Count; j++)
+ {
+ var block = comp.ShieldBlocks[j];
+
+ bool wasEnabled;
+ if (!DisabledBlocks.TryGetValue(block, out wasEnabled))
+ continue;
+
+ block.EnabledChanged -= OnEnabledChanged;
+ block.Enabled = wasEnabled;
+
+ DisabledBlocks.Remove(block);
+ }
+ }
+
+ internal void ReEnableTurrets(GridComp comp)
+ {
+ for (int j = 0; j < comp.Turrets.Count; j++)
+ {
+ var block = comp.Turrets[j];
+
+ bool wasEnabled;
+ if (!DisabledBlocks.TryGetValue(block, out wasEnabled))
+ continue;
+
+ block.EnabledChanged -= OnEnabledChanged;
+ block.Enabled = wasEnabled;
+
+ DisabledBlocks.Remove(block);
+ }
+ }
+
+ internal void OnEnabledChanged(IMyTerminalBlock block)
+ {
+ (block as IMyFunctionalBlock).Enabled = false;
+ }
+
+ internal bool ToggleStealth(bool force = false)
+ {
+ if (!Online || !StealthActive && !force && (!SufficientPower || CoolingDown || !GridComp.WaterValid))
+ {
+ var status = !Online ? "Drive Offline"
+ : !SufficientPower ? "Insufficient Power"
+ : CoolingDown ? $"Drive Cooling Down - {TimeElapsed / 60}s Remaining"
+ : !GridComp.WaterValid ? _session.WorkInWater ? "Drive not Submerged"
+ : "Drive Submerged" : "";
+ MyAPIGateway.Utilities.ShowNotification(status, 2000, "Red");
+
+ return false;
+ }
+
+ EnterStealth = !StealthActive;
+ ExitStealth = StealthActive;
+
+ var message = EnterStealth ? $"Engaging Stealth - {TotalTime / 60}s Remaining" : $"Disengaging Stealth - {TimeElapsed / 60}s Cooldown";
+ var colour = EnterStealth ? "Green" : "StealthOrange";
+ MyAPIGateway.Utilities.ShowNotification(message, 2000, colour);
+
+ IgnorePower = force && EnterStealth;
+
+ return true;
+ }
+
+ internal void SwitchStealth(bool stealth, bool fade = false)
+ {
+ if (stealth)
+ {
+ var antiAliasEnabled = (uint)MyAPIGateway.Session?.Config?.AntialiasingMode == 1u;
+ Transparency = antiAliasEnabled ? -_session.Transparency : -1f;
+ TransOffset = antiAliasEnabled ? -0.35f : -0.2f;
+
+ JumpDrives.Clear();
+ }
+
+ var dither = stealth ? Transparency : 0f;
+
+ if (fade)
+ {
+ var steps = _session.FadeSteps;
+ float fraction = (stealth ? 1 : steps - 1) / (float)steps;
+ dither = TransOffset + fraction * (Transparency - TransOffset);
+
+ FadeEntities.Clear();
+ FadeSlims.Clear();
+ }
+
+ for (int i = 0; i < GridComp.GroupMap.ConnectedGrids.Count; i++)
+ {
+ var grid = GridComp.GroupMap.ConnectedGrids[i];
+ grid.GetBlocks(SlimBlocks);
+
+ for (int j = 0; j < SlimBlocks.Count; j++)
+ {
+ var slim = SlimBlocks[j];
+ var fatBlock = slim.FatBlock;
+ if (fatBlock == null || fatBlock is IMyOxygenFarm)
+ {
+ slim.Dithering = dither;
+ if (fade) FadeSlims.Add(slim);
+ continue;
+ }
+ if (fatBlock is MyThrust && _session.HideThrusterFlames)
+ {
+ var thrust = (MyThrust)fatBlock;
+ if (stealth)
+ {
+ if (_session.RecolourableThrust)
+ (thrust as Sandbox.ModAPI.Ingame.IMyTerminalBlock).GetProperty("HideThrustFlames").AsBool().SetValue(fatBlock, true);
+ else
+ {
+ var def = thrust.BlockDefinition;
+ var flameIdle = def.FlameIdleColor;
+ var flameFull = def.FlameFullColor;
+
+ def.FlameIdleColor = Vector4.Zero;
+ def.FlameFullColor = Vector4.Zero;
+ thrust.Render.UpdateFlameAnimatorData();
+
+ def.FlameIdleColor = flameIdle;
+ def.FlameFullColor = flameFull;
+ }
+
+ }
+ else if (!fade)
+ {
+ if (_session.RecolourableThrust)
+ (thrust as Sandbox.ModAPI.Ingame.IMyTerminalBlock).GetProperty("HideThrustFlames").AsBool().SetValue(fatBlock, false);
+ else
+ thrust.Render.UpdateFlameAnimatorData();
+ }
+
+ }
+
+ if (fade) FadeEntities.Add(fatBlock);
+
+ fatBlock.Render.Transparency = dither;
+ fatBlock.Render.UpdateTransparency();
+
+ fatBlock.Hierarchy.GetChildrenRecursive(Children);
+ foreach (var child in Children)
+ {
+ if (fade) FadeEntities.Add(child);
+
+ child.Render.Transparency = dither;
+ child.Render.UpdateTransparency();
+ }
+ Children.Clear();
+
+ if (stealth)
+ {
+ var jump = fatBlock as IMyJumpDrive;
+ if (jump != null)
+ JumpDrives.Add(jump, jump.CurrentStoredPower);
+ }
+ }
+ SlimBlocks.Clear();
+ }
+
+ if (fade)
+ {
+ Fade = Fading ? _session.FadeTime - Fade : _session.FadeTime;
+ Fading = true;
+ }
+
+ VisibleToClient = !stealth;
+ }
+
+ internal void ReCacheBlocks()
+ {
+ FadeSlims.Clear();
+ FadeEntities.Clear();
+
+ var grids = GridComp.GroupMap.ConnectedGrids;
+ for (int i = 0; i < grids.Count; i++)
+ {
+ var grid = grids[i];
+ grid.GetBlocks(SlimBlocks);
+
+ for (int j = 0; j < SlimBlocks.Count; j++)
+ {
+ var slim = SlimBlocks[j];
+
+ if (slim.IsDestroyed)
+ continue;
+
+ var fatBlock = slim.FatBlock;
+ if (fatBlock == null)
+ {
+ FadeSlims.Add(slim);
+ continue;
+ }
+
+ FadeEntities.Add(fatBlock);
+
+ fatBlock.Hierarchy.GetChildrenRecursive(Children);
+ foreach (var child in Children)
+ FadeEntities.Add(child);
+
+ Children.Clear();
+ }
+ SlimBlocks.Clear();
+ }
+ BlocksDirty = false;
+ }
+
+ internal void FadeBlocks(bool fadeOut, int step)
+ {
+ var steps = _session.FadeSteps;
+ var fraction = (fadeOut ? steps - step : step) / (float)steps;
+ var reset = !fadeOut && step == 0;
+ var dither = reset? 0f : TransOffset + fraction * (Transparency - TransOffset);
+
+ Fading = step != 0;
+
+ for (int i = 0; i < FadeSlims.Count; i++)
+ {
+ var slim = FadeSlims[i];
+ if (slim.IsDestroyed)
+ {
+ FadeSlims.RemoveAtFast(i);
+ i--;
+ continue;
+ }
+
+ slim.Dithering = dither;
+ }
+
+ for (int i = 0; i < FadeEntities.Count; i++)
+ {
+ var entity = FadeEntities[i];
+ entity.Render.Transparency = dither;
+ entity.Render.UpdateTransparency();
+
+ if (Fading || fadeOut || entity.Render is MyNullRenderComponent) //Not final step
+ continue;
+
+ var thrust = entity as MyThrust;
+ if (thrust != null && _session.HideThrusterFlames)
+ {
+ if (_session.RecolourableThrust)
+ (thrust as Sandbox.ModAPI.Ingame.IMyTerminalBlock).GetProperty("HideThrustFlames").AsBool().SetValue(thrust, false);
+ else
+ thrust.Render.UpdateFlameAnimatorData();
+ }
+
+ }
+ Grid.Render.UpdateTransparency();
+
+ }
+
+ internal void StealthExternalGrid(bool stealth, IMyCubeGrid grid)
+ {
+ if (stealth) StealthedExternalGrids.Add(grid);
+ else StealthedExternalGrids.Remove(grid);
+
+ var dither = stealth ? Transparency : 0f;
+
+ grid.GetBlocks(SlimBlocks);
+ foreach (var slim in SlimBlocks)
+ {
+ var block = slim.FatBlock;
+ if (block == null)
+ {
+ slim.Dithering = dither;
+ continue;
+ }
+
+ block.Render.Transparency = dither;
+ block.Render.UpdateTransparency();
+
+ block.Hierarchy.GetChildrenRecursive(Children);
+ foreach (var child in Children)
+ {
+ child.Render.Transparency = dither;
+ child.Render.UpdateTransparency();
+ }
+ }
+ SlimBlocks.Clear();
+
+ (grid as MyCubeGrid).UpdateDirty(null, true);
+ }
+
+ internal void DitherBlock(bool stealth, IMySlimBlock slim)
+ {
+ var dither = stealth ? Transparency : 0f;
+
+ if (slim.FatBlock == null)
+ {
+ if (!slim.IsDestroyed)
+ slim.Dithering = dither;
+ return;
+ }
+
+ var fat = slim.FatBlock;
+
+ fat.Render.Transparency = dither;
+ fat.Render.UpdateTransparency();
+
+ fat.Hierarchy.GetChildrenRecursive(Children);
+ foreach (var child in Children)
+ {
+ child.Render.Transparency = dither;
+ child.Render.UpdateTransparency();
+ }
+ Children.Clear();
+ }
+
+ internal void CreateHeatSignature()
+ {
+ var gps = MyAPIGateway.Session.GPS.Create("Heat Signature", "Heat signature from a cooling down stealth drive.", Block.PositionComp.WorldAABB.Center, true, true);
+ gps.GPSColor = Color.OrangeRed;
+ HeatSignature = gps;
+ MyAPIGateway.Session.GPS.AddLocalGps(gps);
+ }
+
+ internal void SinkInit()
+ {
+ var sinkInfo = new MyResourceSinkInfo()
+ {
+ MaxRequiredInput = 0,
+ RequiredInputFunc = PowerFunc,
+ ResourceTypeId = MyResourceDistributorComponent.ElectricityId
+ };
+
+ Sink = Block.Components?.Get();
+ if (Sink != null)
+ {
+ Sink.RemoveType(ref sinkInfo.ResourceTypeId);
+ Sink.AddType(ref sinkInfo);
+ }
+ else
+ {
+ Sink = new MyResourceSinkComponent();
+ Sink.Init(MyStringHash.GetOrCompute("Utility"), sinkInfo);
+ (Block as MyCubeBlock).Components.Add(Sink);
+ }
+
+ Source = Grid.ResourceDistributor as MyResourceDistributorComponent;
+ if (Source != null)
+ Source.AddSink(Sink);
+ else
+ Logs.WriteLine($"DriveComp.SinkInit() - Distributor null");
+
+ Sink.Update();
+ }
+
+ private void GetShowInToolbarSwitch()
+ {
+ List items;
+ MyAPIGateway.TerminalControls.GetControls(out items);
+
+ foreach (var item in items)
+ {
+
+ if (item.Id == "ShowInToolbarConfig")
+ {
+ ShowInToolbarSwitch = (IMyTerminalControlOnOffSwitch)item;
+ break;
+ }
+ }
+ }
+
+ private void StorageInit()
+ {
+ string rawData;
+ DriveRepo loadRepo = null;
+ if (Block.Storage == null)
+ {
+ Block.Storage = new MyModStorageComponent();
+ }
+ else if (Block.Storage.TryGetValue(_session.CompDataGuid, out rawData))
+ {
+ try
+ {
+ var base64 = Convert.FromBase64String(rawData);
+ loadRepo = MyAPIGateway.Utilities.SerializeFromBinary(base64);
+ }
+ catch (Exception ex)
+ {
+ Logs.WriteLine($"DriveComp - Exception at StorageInit() - {ex}");
+ }
+ }
+
+ if (loadRepo != null)
+ {
+ Sync(loadRepo);
+ }
+ else
+ {
+ Repo = new DriveRepo();
+ }
+ }
+
+ private float PowerFunc()
+ {
+ if (!Online)
+ return 0f;
+ if (StealthActive)
+ return RequiredPower;
+ return 0.001f;
+ }
+
+ private void Sync(DriveRepo repo)
+ {
+ Repo = repo;
+
+ StealthActive = repo.StealthActive;
+ CoolingDown = repo.CoolingDown;
+ TimeElapsed = repo.RemainingDuration;
+ TotalTime = repo.TotalTime;
+
+ StealthOnInit = repo.StealthActive;
+ CdOnInit = repo.CoolingDown;
+ }
+
+ //
+ // Vanilla Cope
+ //
+
+ internal void GetNearbyTurrets()
+ {
+ _sphere.Center = Block.PositionComp.WorldAABB.Center;
+
+ MyGamePruningStructure.GetAllEntitiesInSphere(ref _sphere, _entities);
+
+ NearbyTurrets.Clear();
+ for (int i = 0; i < _entities.Count; i++)
+ {
+ var entity = _entities[i];
+ if (!(entity is IMyLargeTurretBase)) continue;
+
+ var turret = entity as IMyLargeTurretBase;
+ if (turret.CubeGrid == Grid) continue;
+
+ NearbyTurrets.Add(turret);
+ }
+ _entities.Clear();
+
+ }
+
+ public override string ComponentTypeDebugString => "StealthMod";
+
+ }
+}
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Data/Scripts/Stealth System/Comp/SinkComp.cs b/Utility Mods/Stealth Drive - Starcore Edition/Data/Scripts/Stealth System/Comp/SinkComp.cs
new file mode 100644
index 000000000..f0d21a8fd
--- /dev/null
+++ b/Utility Mods/Stealth Drive - Starcore Edition/Data/Scripts/Stealth System/Comp/SinkComp.cs
@@ -0,0 +1,362 @@
+using Sandbox.ModAPI;
+using System;
+using VRage.Game;
+using VRage.Game.Components;
+using VRage.Game.ModAPI;
+using VRageMath;
+using VRage.Utils;
+using Sandbox.Game.Entities;
+using VRage.Game.Entity;
+using System.Collections.Generic;
+using Sandbox.Game.EntityComponents;
+using System.Text;
+using Sandbox.ModAPI.Interfaces.Terminal;
+using VRage.Game.ModAPI.Interfaces;
+using static VRage.Game.ObjectBuilders.Definitions.MyObjectBuilder_GameDefinition;
+
+namespace StealthSystem
+{
+ public class SinkComp : MyEntityComponentBase
+ {
+ internal IMyFunctionalBlock Block;
+ internal IMyCubeGrid Grid;
+ internal MyResourceSinkComponent Sink;
+ internal MyResourceDistributorComponent Source;
+ internal IMyTerminalControlOnOffSwitch ShowInToolbarSwitch;
+
+ internal SinkRepo Repo;
+ internal DriveComp Master;
+ internal Definitions.SinkDefinition Definition;
+
+ internal Color OldColour;
+ internal MyOrientedBoundingBoxD DamageBox;
+ internal MyOrientedBoundingBoxD BlockBox;
+
+ internal bool Inited;
+ internal bool PowerDirty;
+ internal bool Working = true;
+ internal bool SufficientPower;
+ internal bool Accumulating;
+ internal bool Radiating;
+ internal bool WasAccumulating;
+ internal bool WorkingChanged;
+
+ internal long CompTick;
+ internal byte HeatPercent;
+
+ private StealthSession _session;
+
+ internal SinkComp(IMyFunctionalBlock sinkBlock, Definitions.SinkDefinition def, StealthSession session)
+ {
+ _session = session;
+
+ Block = sinkBlock;
+ Definition = def;
+ }
+
+ public override void OnBeforeRemovedFromContainer()
+ {
+ base.OnBeforeRemovedFromContainer();
+
+ Close();
+ }
+
+ public override bool IsSerialized()
+ {
+ if (Block.Storage == null || Repo == null) return false;
+
+ Repo.Sync(this);
+
+ Block.Storage[_session.CompDataGuid] = Convert.ToBase64String(MyAPIGateway.Utilities.SerializeToBinary(Repo));
+
+ return false;
+ }
+
+ internal void Init()
+ {
+ Grid = Block.CubeGrid;
+
+ //Block.IsWorkingChanged += IsWorkingChanged;
+
+ Block.Components.Add(this);
+ CompTick = Block.EntityId % 20;
+
+ Block.SetEmissiveParts(StealthSession.RADIANT_EMISSIVE, Color.DarkSlateGray, 0.1f);
+
+ SinkInit();
+ StorageInit();
+
+ Source.SystemChanged += SourceChanged;
+
+ Inited = true;
+
+ if (!_session.IsDedicated)
+ {
+ GetShowInToolbarSwitch();
+ Block.AppendingCustomInfo += AppendingCustomData;
+ }
+ }
+
+ internal void Close()
+ {
+ GridComp gridComp;
+ if (_session.GridMap.TryGetValue(Grid, out gridComp))
+ {
+ gridComp.HeatComps.Remove(this);
+ }
+
+ //Block.IsWorkingChanged -= IsWorkingChanged;
+
+ Source.SystemChanged -= SourceChanged;
+
+ if (!_session.IsDedicated)
+ Block.AppendingCustomInfo -= AppendingCustomData;
+
+ Clean();
+ }
+
+ internal void Clean()
+ {
+ Block = null;
+ Grid = null;
+ Sink = null;
+ Source = null;
+ ShowInToolbarSwitch = null;
+
+ Repo = null;
+ Master = null;
+ }
+
+ private void IsWorkingChanged(IMyCubeBlock block)
+ {
+ Working = block.IsWorking;
+ }
+
+ private void AppendingCustomData(IMyTerminalBlock block, StringBuilder builder)
+ {
+ var status = !Working ? "Offline" : !SufficientPower ? "Insufficient Power" : Radiating ? "Venting ඞ" : Accumulating ? "Accumulating Heat" : "Ready";
+
+ builder.Append("Heat Sink Status: ")
+ .Append(status)
+ .Append("\n")
+ .Append("Stored Heat: ")
+ .Append($"{HeatPercent}%");
+ }
+
+ private void SourceChanged()
+ {
+ PowerDirty = true;
+ }
+
+ internal void GridChange(GridComp gridComp)
+ {
+ gridComp.HeatComps.Remove(this);
+
+ Grid = Block.CubeGrid;
+
+ var newGridComp = _session.GridMap[Grid];
+ newGridComp.HeatComps.Add(this);
+
+ Source = Grid.ResourceDistributor as MyResourceDistributorComponent;
+ }
+
+ internal void UpdateStatus()
+ {
+ if (PowerDirty || Source == null)
+ {
+ Source = Grid.ResourceDistributor as MyResourceDistributorComponent;
+ PowerDirty = false;
+ }
+ var available = Source.MaxAvailableResourceByType(MyResourceDistributorComponent.ElectricityId, (MyCubeGrid)Grid) - Source.TotalRequiredInputByType(MyResourceDistributorComponent.ElectricityId, (MyCubeGrid)Grid);
+ SufficientPower = available > 0;
+
+ var isWorking = Block.IsFunctional && Block.Enabled && SufficientPower;
+ if (isWorking != Working)
+ {
+ Working = isWorking;
+ WorkingChanged = true;
+ }
+ //SufficientPower = StealthActive ? available >= 0 : available >= RequiredPower;
+ //Online = Block.IsFunctional && Block.Enabled && available > 0;
+
+ if (!_session.IsDedicated)
+ SetEmissiveColor();
+ }
+
+ internal void SetEmissiveColor()
+ {
+ if (Radiating)
+ Block.SetEmissiveParts(StealthSession.RADIANT_EMISSIVE, Color.DarkRed, HeatPercent / 200);
+
+ var emissiveColor = !Block.IsFunctional ? Color.Black : !Working ? EmissiveValues.RED : Accumulating ? Color.Cyan : Radiating ? Color.OrangeRed : EmissiveValues.GREEN;
+ if (emissiveColor == OldColour)
+ return;
+
+ OldColour = emissiveColor;
+ Block.SetEmissiveParts(StealthSession.STATUS_EMISSIVE, emissiveColor, 1f);
+ }
+
+ internal List BlockBoxes = new List();
+
+ internal void DamageBlocks()
+ {
+ var large = Grid.GridSizeEnum == MyCubeSize.Large;
+ var box = large ? _session.LargeBox : _session.SmallBox;
+ var radius = large ? 7.25 : 6.45;
+ var offset = large ? 7.75 : 7.25;
+ var matrix = Block.WorldMatrix;
+ matrix.Translation += Block.WorldMatrix.Up * offset;
+ var obb = new MyOrientedBoundingBoxD(box, matrix);
+ //DamageBox = obb;
+
+ var hits = new List();
+ MyGamePruningStructure.GetAllEntitiesInOBB(ref obb, hits);
+
+ //BlockBoxes.Clear();
+ for (int i = 0; i < hits.Count; i++)
+ {
+ var ent = hits[i];
+
+ var dest = ent as IMyDestroyableObject;
+ if (dest != null)
+ {
+ var entObb = new MyOrientedBoundingBoxD(ent.PositionComp.LocalAABB, ent.PositionComp.WorldMatrixRef);
+ if (entObb.Contains(ref obb) != ContainmentType.Disjoint)
+ dest.DoDamage(9f, MyDamageType.Temperature, true);
+
+ continue;
+ }
+
+ var grid = ent as IMyCubeGrid;
+ if (grid != null)
+ {
+ var sphere = new BoundingSphereD(matrix.Translation, radius);
+ var slims = grid.GetBlocksInsideSphere(ref sphere);
+
+ for (int j = 0; j < slims.Count; j++)
+ {
+ var slim = slims[j];
+ var fat = slim.FatBlock;
+ MyOrientedBoundingBoxD blockBox;
+ if (fat == null)
+ {
+ var gridSize = (double)Grid.GridSize;
+ var aabb = new BoundingBoxD(slim.Min * gridSize - gridSize / 2, slim.Max * gridSize + gridSize / 2);
+ blockBox = new MyOrientedBoundingBoxD(aabb, grid.PositionComp.WorldMatrixRef);
+ }
+ else
+ {
+ blockBox = new MyOrientedBoundingBoxD(fat.PositionComp.LocalAABB, fat.PositionComp.WorldMatrixRef);
+ }
+
+ if (obb.Contains(ref blockBox) != ContainmentType.Disjoint)
+ {
+ slim.DoDamage(500f, MyDamageType.Temperature, true);
+ //BlockBoxes.Add(blockBox);
+ }
+
+ }
+ }
+ }
+ }
+
+ internal void SinkInit()
+ {
+ var sinkInfo = new MyResourceSinkInfo()
+ {
+ MaxRequiredInput = 0,
+ RequiredInputFunc = PowerFunc,
+ ResourceTypeId = MyResourceDistributorComponent.ElectricityId
+ };
+
+ Sink = Block.Components?.Get();
+ if (Sink != null)
+ {
+ Sink.RemoveType(ref sinkInfo.ResourceTypeId);
+ Sink.AddType(ref sinkInfo);
+ }
+ else
+ {
+ Sink = new MyResourceSinkComponent();
+ Sink.Init(MyStringHash.GetOrCompute("Utility"), sinkInfo);
+ (Block as MyCubeBlock).Components.Add(Sink);
+ }
+
+ Source = Grid.ResourceDistributor as MyResourceDistributorComponent;
+ if (Source != null)
+ Source.AddSink(Sink);
+ else
+ Logs.WriteLine($"SinkComp.SinkInit() - Distributor null");
+
+ Sink.Update();
+ }
+
+ private float PowerFunc()
+ {
+ if (!Working)
+ return 0f;
+ if (Accumulating)
+ return Definition.Power;
+ return 0.001f;
+ }
+
+ private void GetShowInToolbarSwitch()
+ {
+ List items;
+ MyAPIGateway.TerminalControls.GetControls(out items);
+
+ foreach (var item in items)
+ {
+
+ if (item.Id == "ShowInToolbarConfig")
+ {
+ ShowInToolbarSwitch = (IMyTerminalControlOnOffSwitch)item;
+ break;
+ }
+ }
+ }
+
+ private void StorageInit()
+ {
+ string rawData;
+ SinkRepo loadRepo = null;
+ if (Block.Storage == null)
+ {
+ Block.Storage = new MyModStorageComponent();
+ }
+ else if (Block.Storage.TryGetValue(_session.CompDataGuid, out rawData))
+ {
+ try
+ {
+ var base64 = Convert.FromBase64String(rawData);
+ loadRepo = MyAPIGateway.Utilities.SerializeFromBinary(base64);
+ }
+ catch (Exception ex)
+ {
+ Logs.WriteLine($"SinkComp - Exception at StorageInit() - {ex}");
+ }
+ }
+
+ if (loadRepo != null)
+ {
+ Sync(loadRepo);
+ }
+ else
+ {
+ Repo = new SinkRepo();
+ }
+ }
+
+ private void Sync(SinkRepo repo)
+ {
+ Repo = repo;
+
+ Accumulating = repo.Accumulating;
+ Radiating = repo.Radiating;
+ HeatPercent = repo.HeatPercent;
+ }
+
+ public override string ComponentTypeDebugString => "StealthMod";
+
+ }
+}
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Data/Scripts/Stealth System/Definitions/Definitions.cs b/Utility Mods/Stealth Drive - Starcore Edition/Data/Scripts/Stealth System/Definitions/Definitions.cs
new file mode 100644
index 000000000..36816b7d2
--- /dev/null
+++ b/Utility Mods/Stealth Drive - Starcore Edition/Data/Scripts/Stealth System/Definitions/Definitions.cs
@@ -0,0 +1,40 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace StealthSystem
+{
+ internal class Definitions
+ {
+ internal class DriveDefinition
+ {
+ internal int Duration;
+ internal float PowerScale;
+ internal float SignalRangeScale;
+
+ public DriveDefinition(int duration, float powerScale, float signalScale)
+ {
+ Duration = duration;
+ PowerScale = powerScale;
+ SignalRangeScale = signalScale;
+ }
+ }
+
+ internal class SinkDefinition
+ {
+ internal int Duration;
+ internal float Power;
+ internal bool DoDamage;
+
+ public SinkDefinition(int duration, float power, bool damage)
+ {
+ Duration = duration;
+ Power = power;
+ DoDamage = damage;
+ }
+ }
+
+ }
+}
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Data/Scripts/Stealth System/Session/SessionControls.cs b/Utility Mods/Stealth Drive - Starcore Edition/Data/Scripts/Stealth System/Session/SessionControls.cs
new file mode 100644
index 000000000..971d5e017
--- /dev/null
+++ b/Utility Mods/Stealth Drive - Starcore Edition/Data/Scripts/Stealth System/Session/SessionControls.cs
@@ -0,0 +1,255 @@
+using Sandbox.ModAPI;
+using VRage.Utils;
+using System.Collections.Generic;
+using Sandbox.ModAPI.Interfaces.Terminal;
+using System.Text;
+
+namespace StealthSystem
+{
+ public partial class StealthSession
+ {
+ private readonly List _customControls = new List();
+ private readonly List _customActions = new List();
+
+ internal IMyTerminalBlock LastTerminal;
+
+ private void CustomControlGetter(IMyTerminalBlock block, List controls)
+ {
+ if (block is IMyUpgradeModule && DriveDefinitions.ContainsKey(block.BlockDefinition.SubtypeName))
+ {
+ foreach (var control in _customControls)
+ controls.Add(control);
+ }
+
+ LastTerminal = block;
+ }
+
+ private void CustomActionGetter(IMyTerminalBlock block, List actions)
+ {
+ if (block is IMyUpgradeModule && DriveDefinitions.ContainsKey(block.BlockDefinition.SubtypeName))
+ {
+ foreach (var action in _customActions)
+ actions.Add(action);
+ }
+ }
+
+ internal void CreateTerminalControls() where T : IMyUpgradeModule
+ {
+ _customControls.Add(Separator());
+ _customControls.Add(CreateEnterStealth());
+ _customControls.Add(CreateExitStealth());
+
+ _customActions.Add(CreateEnterAction());
+ _customActions.Add(CreateExitAction());
+ _customActions.Add(CreateSwitchAction());
+ }
+
+ internal IMyTerminalControlSeparator Separator() where T : IMyTerminalBlock
+ {
+ var c = MyAPIGateway.TerminalControls.CreateControl("Stealth_Separator");
+
+ c.Enabled = IsTrue;
+ c.Visible = IsTrue;
+
+ return c;
+ }
+
+ internal IMyTerminalControlButton CreateEnterStealth() where T : IMyUpgradeModule
+ {
+ var control = MyAPIGateway.TerminalControls.CreateControl($"Stealth_Enter");
+
+ control.Title = MyStringId.GetOrCompute("Enter Stealth");
+ control.Tooltip = MyStringId.GetOrCompute("Engage Stealth Drive to become virtually undetectable.");
+ control.Action = EnterStealth;
+ control.Visible = IsTrue;
+ control.Enabled = CanEnterStealth;
+
+ return control;
+ }
+
+ internal IMyTerminalControlButton CreateExitStealth() where T : IMyUpgradeModule
+ {
+ var control = MyAPIGateway.TerminalControls.CreateControl($"Stealth_Exit");
+
+ control.Title = MyStringId.GetOrCompute("Leave Stealth");
+ control.Tooltip = MyStringId.GetOrCompute("Disengage Stealth Drive.");
+ control.Action = ExitStealth;
+ control.Visible = IsTrue;
+ control.Enabled = CanExitStealth;
+
+ return control;
+ }
+
+ internal IMyTerminalAction CreateEnterAction() where T : IMyUpgradeModule
+ {
+ var action = MyAPIGateway.TerminalControls.CreateAction("Stealth_Enter_Action");
+ action.Icon = ModPath + @"\Textures\GUI\Icons\Actions\StealthSwitchOn.dds";
+ action.Name = new StringBuilder("Enter Stealth");
+ action.Action = EnterStealth;
+ action.Writer = EnterStealthWriter;
+ action.Enabled = IsTrue;
+
+ return action;
+ }
+
+ internal IMyTerminalAction CreateExitAction() where T : IMyUpgradeModule
+ {
+ var action = MyAPIGateway.TerminalControls.CreateAction("Stealth_Exit_Action");
+ action.Icon = ModPath + @"\Textures\GUI\Icons\Actions\StealthSwitchOff.dds";
+ action.Name = new StringBuilder("Leave Stealth");
+ action.Action = ExitStealth;
+ action.Writer = ExitStealthWriter;
+ action.Enabled = IsTrue;
+
+ return action;
+ }
+
+ internal IMyTerminalAction CreateSwitchAction() where T : IMyUpgradeModule
+ {
+ var action = MyAPIGateway.TerminalControls.CreateAction("Stealth_Switch_Action");
+ action.Icon = ModPath + @"\Textures\GUI\Icons\Actions\StealthSwitchToggle.dds";
+ action.Name = new StringBuilder("Switch Stealth");
+ action.Action = SwitchStealth;
+ action.Writer = SwitchStealthWriter;
+ action.Enabled = IsTrue;
+
+ return action;
+ }
+
+ internal bool IsTrue(IMyTerminalBlock block)
+ {
+ return true;
+ }
+
+ internal bool CanEnterStealth(IMyTerminalBlock block)
+ {
+ DriveComp comp;
+ if (!DriveMap.TryGetValue(block.EntityId, out comp))
+ {
+ Logs.WriteLine("CanEnterStealth() - Comp not found!");
+ return false;
+ }
+
+ return comp.Online && comp.SufficientPower && !comp.CoolingDown && !comp.StealthActive && comp.GridComp.WaterValid;
+ }
+
+ internal bool CanExitStealth(IMyTerminalBlock block)
+ {
+ DriveComp comp;
+ if (!DriveMap.TryGetValue(block.EntityId, out comp))
+ {
+ Logs.WriteLine("CanExitStealth() - Comp not found!");
+ return false;
+ }
+
+ return comp.Online && comp.StealthActive;
+ }
+
+ internal void EnterStealthWriter(IMyTerminalBlock block, StringBuilder builder)
+ {
+ DriveComp comp;
+ if (!DriveMap.TryGetValue(block.EntityId, out comp))
+ {
+ Logs.WriteLine("EnterStealthWriter() - Comp not found!");
+ return;
+ }
+
+ if (comp.StealthActive)
+ builder.Append("Cloaked");
+ else
+ builder.Append("Cloak");
+ }
+
+ internal void ExitStealthWriter(IMyTerminalBlock block, StringBuilder builder)
+ {
+ DriveComp comp;
+ if (!DriveMap.TryGetValue(block.EntityId, out comp))
+ {
+ Logs.WriteLine("ExitStealthWriter() - Comp not found!");
+ return;
+ }
+
+ if (comp.StealthActive)
+ builder.Append("Uncloak");
+ else
+ builder.Append("Uncloaked");
+ }
+
+ internal void SwitchStealthWriter(IMyTerminalBlock block, StringBuilder builder)
+ {
+ DriveComp comp;
+ if (!DriveMap.TryGetValue(block.EntityId, out comp))
+ {
+ Logs.WriteLine("ExitStealthWriter() - Comp not found!");
+ return;
+ }
+
+ if (comp.StealthActive)
+ builder.Append("Uncloak");
+ else
+ builder.Append("Cloak");
+ }
+
+ internal void EnterStealth(IMyTerminalBlock block)
+ {
+ DriveComp comp;
+ if (!DriveMap.TryGetValue(block.EntityId, out comp))
+ {
+ Logs.WriteLine("EnterStealth() - Comp not found!");
+ return;
+ }
+
+ if (!comp.Online || !comp.SufficientPower || comp.CoolingDown || comp.StealthActive || !comp.GridComp.WaterValid)
+ {
+ var status = !comp.Online ? "Drive Offline"
+ : !comp.SufficientPower ? "Insufficient Power"
+ : comp.CoolingDown ? $"Drive Cooling Down - {comp.TimeElapsed / 60}s Remaining"
+ : comp.StealthActive ? "Drive Already Engaged"
+ : !comp.GridComp.WaterValid ? WorkInWater ? "Drive not Submerged"
+ : "Drive Submerged" : "";
+ MyAPIGateway.Utilities.ShowNotification(status, 2000, "Red");
+ return;
+ }
+
+ comp.EnterStealth = true;
+ MyAPIGateway.Utilities.ShowNotification($"Engaging Stealth - {comp.TotalTime / 60}s Remaining", 2000, "Green");
+
+ foreach (var control in _customControls)
+ control.UpdateVisual();
+ }
+
+ internal void ExitStealth(IMyTerminalBlock block)
+ {
+ DriveComp comp;
+ if (!DriveMap.TryGetValue(block.EntityId, out comp))
+ {
+ Logs.WriteLine("ExitStealth() - Comp not found!");
+ return;
+ }
+
+ if (!comp.Online || !comp.StealthActive) return;
+
+ comp.ExitStealth = true;
+ MyAPIGateway.Utilities.ShowNotification($"Disengaging Stealth - {comp.TimeElapsed / 60}s Cooldown", 2000, "StealthOrange");
+
+ foreach (var control in _customControls)
+ control.UpdateVisual();
+ }
+
+ internal void SwitchStealth(IMyTerminalBlock block)
+ {
+ DriveComp comp;
+ if (!DriveMap.TryGetValue(block.EntityId, out comp))
+ {
+ Logs.WriteLine("SwitchStealth() - Comp not found!");
+ return;
+ }
+
+ comp.ToggleStealth();
+
+ foreach (var control in _customControls)
+ control.UpdateVisual();
+ }
+
+ }
+}
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Data/Scripts/Stealth System/Session/SessionEvents.cs b/Utility Mods/Stealth Drive - Starcore Edition/Data/Scripts/Stealth System/Session/SessionEvents.cs
new file mode 100644
index 000000000..27a28fbec
--- /dev/null
+++ b/Utility Mods/Stealth Drive - Starcore Edition/Data/Scripts/Stealth System/Session/SessionEvents.cs
@@ -0,0 +1,184 @@
+using Sandbox.ModAPI;
+using System;
+using VRage.Game.ModAPI;
+using VRage.ModAPI;
+using Sandbox.Game.Entities;
+using VRage.Game.Entity;
+using System.Collections.Generic;
+
+namespace StealthSystem
+{
+ public partial class StealthSession
+ {
+ private void OnEntityCreate(MyEntity entity)
+ {
+ try
+ {
+ if (!Inited) lock (InitObj) Init();
+
+ var planet = entity as MyPlanet;
+ if (planet != null)
+ PlanetTemp.TryAdd(planet, byte.MaxValue); //More keen jank workarounds
+
+ var grid = entity as IMyCubeGrid;
+ if (grid != null)
+ {
+ (grid as MyCubeGrid).AddedToScene += AddToStart => _startGrids.Add(grid);
+ return;
+ }
+
+ var upgrade = entity as IMyUpgradeModule;
+ if (upgrade != null)
+ {
+ var subtype = upgrade.BlockDefinition.SubtypeName;
+ if (Enforced && !DriveDefinitions.ContainsKey(subtype) && !SinkDefinitions.ContainsKey(subtype))
+ return;
+
+ (upgrade as MyCubeBlock).AddedToScene += AddToStart => _startBlocks.Add(upgrade);
+ }
+
+ if (!PbApiInited && IsServer && entity is IMyProgrammableBlock)
+ {
+ MyAPIGateway.Utilities.InvokeOnGameThread(() => API.PbInit());
+ PbApiInited = true;
+ }
+ }
+ catch (Exception ex)
+ {
+ Logs.WriteLine($"Exception in EntityCreate: {entity.GetType()} - {ex}");
+ }
+
+ }
+
+ private void OnGridClose(IMyEntity entity)
+ {
+ var grid = entity as IMyCubeGrid;
+
+ if (GridMap.ContainsKey(grid))
+ {
+ var comp = GridMap[grid];
+ GridMap.Remove(grid);
+ GridList.Remove(comp);
+
+ comp.Clean();
+ _gridCompPool.Push(comp);
+ }
+ else Logs.WriteLine("OnGridClose() - grid not in map!!!");
+ }
+
+ private void OnCloseAll()
+ {
+ try
+ {
+ var list = new List(GridGroupMap.Keys);
+ foreach (var value in list)
+ GridGroupsOnOnGridGroupDestroyed(value);
+
+ MyAPIGateway.GridGroups.OnGridGroupDestroyed -= GridGroupsOnOnGridGroupDestroyed;
+ MyAPIGateway.GridGroups.OnGridGroupCreated -= GridGroupsOnOnGridGroupCreated;
+
+ GridGroupMap.Clear();
+ }
+ catch (Exception ex)
+ {
+ Logs.WriteLine($"Exception in CloseAll: {ex}");
+ }
+
+ }
+
+ private void GridGroupsOnOnGridGroupCreated(IMyGridGroupData groupData)
+ {
+ if (groupData.LinkType != GridLinkTypeEnum.Physical)
+ return;
+
+ var map = _groupMapPool.Count > 0 ? _groupMapPool.Pop() : new GroupMap();
+ map.Init(groupData, this);
+
+ //groupData.OnReleased += map.OnReleased;
+ groupData.OnGridAdded += map.OnGridAdded;
+ groupData.OnGridRemoved += map.OnGridRemoved;
+ GridGroupMap[groupData] = map;
+ }
+
+ private void GridGroupsOnOnGridGroupDestroyed(IMyGridGroupData groupData)
+ {
+ if (groupData.LinkType != GridLinkTypeEnum.Physical)
+ return;
+
+ GroupMap map;
+ if (GridGroupMap.TryGetValue(groupData, out map))
+ {
+ //groupData.OnReleased -= map.OnReleased;
+ groupData.OnGridAdded -= map.OnGridAdded;
+ groupData.OnGridRemoved -= map.OnGridRemoved;
+
+ GridGroupMap.Remove(groupData);
+ map.Clean();
+ _groupMapPool.Push(map);
+ }
+ else
+ Logs.WriteLine($"GridGroupsOnOnGridGroupDestroyed could not find map");
+ }
+
+ private void PlayerConnected(long id)
+ {
+ try
+ {
+ MyAPIGateway.Multiplayer.Players.GetPlayers(null, myPlayer => FindPlayer(myPlayer, id));
+ }
+ catch (Exception ex) { Logs.WriteLine($"Exception in PlayerConnected: {ex}"); }
+ }
+
+ private bool FindPlayer(IMyPlayer player, long id)
+ {
+ if (player.IdentityId == id)
+ {
+ var packet = new SettingsPacket { EntityId = 0, Settings = ConfigSettings.Config, Type = PacketType.Settings };
+ SendPacketToClient(packet, player.SteamUserId);
+ }
+ return false;
+ }
+
+ internal void AfterDamageApplied(object target, MyDamageInformation info)
+ {
+ if (!DisableWeapons && RevealOnDamage) //Reveal grid on dealing damage
+ {
+ var ent = MyEntities.GetEntityById(info.AttackerId);
+ if (!(ent is MyCubeBlock)) return;
+
+ var attackingGrid = (ent as IMyCubeBlock).CubeGrid;
+ if (attackingGrid == null) return;
+
+ if (!StealthedGrids.Contains(attackingGrid))
+ return;
+
+ GridComp gridCompA;
+ if (!GridMap.TryGetValue(attackingGrid, out gridCompA))
+ {
+ Logs.WriteLine("Attacking grid not mapped in damage handler");
+ return;
+ }
+
+ gridCompA.Revealed = true;
+ }
+
+ if (!TrackDamage) return;
+
+ if (info.AttackerId == 0 || !(target is IMySlimBlock))
+ return;
+
+ var targetGrid = (target as IMySlimBlock).CubeGrid;
+
+ if (targetGrid == null || !StealthedGrids.Contains(targetGrid)) return;
+
+ GridComp gridComp;
+ if (!GridMap.TryGetValue(targetGrid, out gridComp))
+ {
+ Logs.WriteLine("Grid not mapped in damage handler");
+ return;
+ }
+
+ gridComp.DamageTaken += (int)info.Amount;
+ }
+ }
+}
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Data/Scripts/Stealth System/Session/SessionFields.cs b/Utility Mods/Stealth Drive - Starcore Edition/Data/Scripts/Stealth System/Session/SessionFields.cs
new file mode 100644
index 000000000..181da45a1
--- /dev/null
+++ b/Utility Mods/Stealth Drive - Starcore Edition/Data/Scripts/Stealth System/Session/SessionFields.cs
@@ -0,0 +1,133 @@
+using Sandbox.ModAPI;
+using System;
+using VRage.Game.ModAPI;
+using VRage.ModAPI;
+using VRageMath;
+using VRage.Utils;
+using VRage.Game.Entity;
+using System.Collections.Generic;
+using VRage.Collections;
+using Jakaria.API;
+using System.Collections.Concurrent;
+using Sandbox.Game.Entities;
+using Sandbox.Definitions;
+
+namespace StealthSystem
+{
+ public partial class StealthSession
+ {
+ internal const string STATUS_EMISSIVE = "Emissive";
+ internal const string RADIANT_EMISSIVE = "Emissive0";
+
+ internal const int FADE_INTERVAL = 5;
+ internal const int IsStealthedFlag = 0x20000000;
+
+ internal readonly Dictionary DriveDefinitions = new Dictionary();
+ internal readonly Dictionary SinkDefinitions = new Dictionary();
+
+ internal readonly HashSet ShieldBlocks = new HashSet()
+ {
+ "EmitterL",
+ "EmitterS",
+ "EmitterST",
+ "EmitterLA",
+ "EmitterSA",
+ "LargeShipSmallShieldGeneratorBase",
+ "LargeShipLargeShieldGeneratorBase",
+ "SmallShipSmallShieldGeneratorBase",
+ "SmallShipMicroShieldGeneratorBase",
+ "LargeGridLargeShield",
+ "LargeGridSmallShield",
+ "SmallGridLargeShield",
+ "SmallGridSmallShield",
+ };
+
+ internal string ModPath;
+ internal readonly Guid CompDataGuid = new Guid("75BBB4F5-4FB9-4230-AAAA-BB79C9811507");
+ internal static readonly MyStringId _square = MyStringId.GetOrCompute("Square");
+
+ internal BoundingBoxD LargeBox;
+ internal BoundingBoxD SmallBox;
+
+ internal EntityFlags StealthFlag;
+
+ internal int ShieldDelay;
+ internal int JumpPenalty;
+ internal int FadeTime;
+ internal int FadeSteps;
+ internal int DamageThreshold;
+ internal float Transparency;
+ internal float WaterTransitionDepth;
+ internal float WaterOffsetSqr;
+ internal bool DisableShields;
+ internal bool DisableWeapons;
+ internal bool HideThrusterFlames;
+ internal bool WorkInWater;
+ internal bool WorkOutOfWater;
+ internal bool TrackWater;
+ internal bool TrackDamage;
+ internal bool RevealOnDamage;
+
+ internal readonly Dictionary DriveMap = new Dictionary();
+ internal readonly Dictionary GridMap = new Dictionary();
+ internal readonly Dictionary GridGroupMap = new Dictionary();
+ internal readonly List GridList = new List();
+ internal readonly HashSet StealthedGrids = new HashSet();
+ internal readonly Vector3D[] ObbCorners = new Vector3D[8];
+
+ internal Settings ConfigSettings;
+ internal APIBackend API;
+ internal APIServer APIServer;
+ internal readonly WaterModAPI WaterAPI = new WaterModAPI();
+
+ internal object InitObj = new object();
+ internal bool Enforced;
+ internal bool Inited;
+ internal bool PbApiInited;
+
+ internal bool WcActive;
+ internal bool WaterMod;
+ internal bool RecolourableThrust;
+
+ internal readonly ConcurrentDictionary WaterMap = new ConcurrentDictionary();
+ internal readonly ConcurrentDictionary PlanetMap = new ConcurrentDictionary();
+ internal readonly ConcurrentDictionary PlanetTemp = new ConcurrentDictionary();
+
+ private readonly List _entities = new List();
+ private readonly ConcurrentCachingList _startBlocks = new ConcurrentCachingList();
+ private readonly ConcurrentCachingList _startGrids = new ConcurrentCachingList();
+ private readonly Stack _groupMapPool = new Stack(64);
+ private readonly Stack _gridCompPool = new Stack(128);
+
+ private readonly Vector3D _large = new Vector3D(1.125, 6.25, 3.5);
+ private readonly Vector3D _small = new Vector3D(1.125, 6.25, 1.125);
+
+ public StealthSession()
+ {
+ API = new APIBackend(this);
+ APIServer = new APIServer(this);
+ }
+
+ private void Clean()
+ {
+ DriveDefinitions.Clear();
+ SinkDefinitions.Clear();
+ ShieldBlocks.Clear();
+
+ DriveMap.Clear();
+ GridMap.Clear();
+ GridGroupMap.Clear();
+ GridList.Clear();
+ StealthedGrids.Clear();
+
+ _entities.Clear();
+ _startBlocks.ClearImmediate();
+ _startGrids.ClearImmediate();
+ _groupMapPool.Clear();
+ _gridCompPool.Clear();
+
+ _customControls.Clear();
+ _customActions.Clear();
+ }
+ }
+}
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Data/Scripts/Stealth System/Session/SessionLogic.cs b/Utility Mods/Stealth Drive - Starcore Edition/Data/Scripts/Stealth System/Session/SessionLogic.cs
new file mode 100644
index 000000000..1f598a77d
--- /dev/null
+++ b/Utility Mods/Stealth Drive - Starcore Edition/Data/Scripts/Stealth System/Session/SessionLogic.cs
@@ -0,0 +1,614 @@
+using Sandbox.ModAPI;
+using System;
+using VRage.Game.ModAPI;
+using VRageMath;
+using Sandbox.Game.Entities;
+using VRage.Game.Entity;
+using System.Collections.Generic;
+using VRage.Game.ModAPI.Interfaces;
+
+namespace StealthSystem
+{
+ public partial class StealthSession
+ {
+ internal void CompLoop()
+ {
+ //var position = MyAPIGateway.Session.LocalHumanPlayer?.Character?.PositionComp.WorldAABB.Center ?? MyAPIGateway.Session?.Camera?.Position;
+ //var controlledGrid = (MyAPIGateway.Session.ControlledObject as MyCubeBlock)?.GetTopMostParent();
+
+ if (GridList.Count == 0) return;
+
+ for (int i = 0; i < GridList.Count; i++)
+ {
+ var gridComp = GridList[i];
+ var master = gridComp.MasterComp;
+
+ if (gridComp.GroupMap == null)
+ {
+ var group = MyAPIGateway.GridGroups.GetGridGroup(GridLinkTypeEnum.Physical, gridComp.Grid);
+ if (group != null)
+ {
+ GroupMap map;
+ if (GridGroupMap.TryGetValue(group, out map))
+ gridComp.GroupMap = map;
+ }
+ }
+
+ bool enter = false;
+ bool exit = false;
+ bool cold = false;
+
+ try
+ {
+ for (int j = 0; j < gridComp.StealthComps.Count; j++)
+ {
+ var comp = gridComp.StealthComps[j];
+
+ if (comp.Grid != comp.Block.CubeGrid)
+ {
+ if (!GridMap.ContainsKey(comp.Block.CubeGrid))
+ {
+ comp.Transfer = true;
+ continue;
+ }
+
+ comp.GridChange();
+ }
+
+ if (!IsDedicated && comp.Fading)
+ {
+ if (comp.StealthActive && (gridComp.GroupsDirty || comp.BlocksDirty))
+ comp.ReCacheBlocks();
+
+ if (comp.Fade-- % FADE_INTERVAL == 0)
+ comp.FadeBlocks(comp.StealthActive, comp.Fade / FADE_INTERVAL);
+ }
+
+ if (comp.ShieldWaiting)
+ {
+ if (comp.ShieldWait-- <= 0)
+ {
+ comp.ShieldWaiting = false;
+
+ foreach (var block in comp.DisabledBlocks.Keys)
+ {
+ block.EnabledChanged -= comp.OnEnabledChanged;
+ block.Enabled = comp.DisabledBlocks[block];
+ }
+ comp.DisabledBlocks.Clear();
+ }
+ }
+
+ //Update cooldown and heat signal
+ if (comp.CoolingDown)
+ {
+ if (comp.TimeElapsed-- <= 0 || comp.EnterStealth) //comp.RemainingDuration-- <= 0
+ {
+ if (!IsDedicated && comp.HeatSignature != null)
+ {
+ MyAPIGateway.Session.GPS.RemoveLocalGps(comp.HeatSignature);
+ comp.HeatSignature = null;
+ }
+ comp.CoolingDown = false;
+ cold = true;
+ }
+ else if (!IsDedicated && comp.HeatSignature != null)
+ {
+ comp.HeatSignature.Coords = comp.Block.PositionComp.WorldAABB.Center;
+ }
+
+ if (!IsDedicated && comp.CdOnInit)
+ {
+ var position = MyAPIGateway.Session?.Camera?.Position ?? Vector3D.Zero;
+ if (Vector3D.DistanceSquared(position, comp.Block.PositionComp.WorldAABB.Center) < comp.SignalDistanceSquared)
+ comp.CreateHeatSignature();
+
+ comp.CdOnInit = false;
+ }
+ }
+
+ //Hide/unhide main grid after delay to match slimblock transparency update
+ //if (comp.DelayedRender)
+ //{
+ // if (comp.Delay-- == 0)
+ // {
+ // foreach (var grid in comp.ConnectedGrids)
+ // comp.DitherFatBlocks(!comp.VisibleToClient);
+
+ // comp.DelayedRender = false;
+ // }
+ //}
+
+ if (!comp.IsPrimary && !comp.StealthActive)
+ continue;
+
+ if (comp != master && (master == null || !master.Online))
+ {
+ Logs.WriteLine($"[StealthMod] Primary != master - master null: {master == null}");
+ master = comp;
+ }
+
+ if (!comp.Block.IsFunctional && (!comp.TransferFailed || Tick120))
+ comp.TransferFailed = !comp.TransferPrimary(false);
+
+ //Calculate grid surface area and drive power
+ if (gridComp.GroupsDirty || Tick60 && comp.GridUpdated)
+ {
+ comp.CalculatePowerRequirements();
+ gridComp.GroupsDirty = false;
+ comp.GridUpdated = false;
+ }
+
+ if (TrackWater)
+ {
+ WaterData waterData;
+ if (Tick3600)
+ {
+ var planet = MyGamePruningStructure.GetClosestPlanet(gridComp.Grid.PositionComp.WorldAABB.Center);
+
+ if (planet != gridComp.Planet && planet != null && WaterMap.TryGetValue(planet.EntityId, out waterData))
+ gridComp.Water = new BoundingSphereD(waterData.Centre, waterData.Radius + WaterTransitionDepth);
+
+ gridComp.Planet = planet;
+ }
+
+ if (Tick60 && gridComp.Planet != null && WaterMap.TryGetValue(gridComp.Planet.EntityId, out waterData))
+ {
+ gridComp.Underwater = false;
+
+ var planetVector = gridComp.Grid.PositionComp.WorldAABB.Center - waterData.Centre;
+ var radius = waterData.Radius + WaterTransitionDepth;
+ var radiusSqr = radius * radius;
+ if (planetVector.LengthSquared() < radiusSqr)
+ {
+ gridComp.Underwater = true;
+
+ var obb = new MyOrientedBoundingBoxD(gridComp.Grid.PositionComp.LocalAABB, gridComp.Grid.PositionComp.WorldMatrixRef);
+ obb.GetCorners(ObbCorners, 0);
+ for (int k = 0; k < 8; k++)
+ {
+ var corner = ObbCorners[j];
+ planetVector = corner - waterData.Centre;
+
+ if (planetVector.LengthSquared() > radiusSqr)
+ {
+ gridComp.Underwater = false;
+ break;
+ }
+ }
+ }
+ gridComp.WaterValid = gridComp.Underwater == WorkInWater;
+ }
+ }
+
+ //Update comp state and refresh custom info
+ if (Tick20 || comp.PowerDirty)
+ {
+ comp.UpdateStatus();
+ if (!IsDedicated && LastTerminal == comp.Block && MyAPIGateway.Gui.GetCurrentScreen == MyTerminalPageEnum.ControlPanel)
+ comp.RefreshTerminal();
+ }
+
+ if (comp.StealthActive)
+ {
+ //Exit stealth conditions
+ var forcedExit = !comp.IsPrimary || !comp.Online || gridComp.Revealed || !gridComp.WaterValid || TrackDamage && gridComp.DamageTaken > DamageThreshold;
+ if (forcedExit || !comp.IgnorePower && (!comp.SufficientPower || comp.TimeElapsed++ >= comp.TotalTime)) //comp.RemainingDuration-- <= 0
+ comp.ExitStealth = true;
+
+ //Decrease remaining stealth duration after jump
+ if (Tick120)
+ {
+ var jumpList = new List(comp.JumpDrives.Keys);
+ foreach (var jump in jumpList)
+ {
+ if (jump.CurrentStoredPower < comp.JumpDrives[jump])
+ comp.TotalTime -= JumpPenalty;
+ //comp.RemainingDuration -= JumpPenalty;
+
+ comp.JumpDrives[jump] = jump.CurrentStoredPower;
+ }
+ }
+
+ //Vanilla fuckery
+ if (!WcActive)
+ {
+ if (TickMod60 == comp.CompTick60)
+ comp.GetNearbyTurrets();
+
+ for (int k = 0; k < comp.NearbyTurrets.Count; k++)
+ {
+ var turret = comp.NearbyTurrets[k];
+
+ if (!turret.HasTarget) continue;
+
+ var target = turret.Target;
+
+ var block = target as IMyCubeBlock;
+ if (block != null && ((uint)block.CubeGrid.Flags & IsStealthedFlag) > 0)
+ {
+ turret.ResetTargetingToDefault();
+ continue;
+ }
+
+ if (((uint)target.Flags & IsStealthedFlag) > 0)
+ turret.ResetTargetingToDefault();
+ }
+ }
+ }
+
+ //if (comp.ExpandedOBB != null) DrawBox(comp.ExpandedOBB, Color.AliceBlue);
+
+ if (comp.EnterStealth || comp.ExitStealth || comp.StealthOnInit || comp.StealthActive && TickMod15 == comp.CompTick15)
+ {
+ comp.CalculateExpandedOBB();
+
+ Vector3D position = Vector3D.Zero;
+ bool inside = false;
+ if (!IsDedicated)
+ {
+ position = MyAPIGateway.Session?.Camera?.Position ?? Vector3D.Zero;
+ inside = comp.ExpandedOBB.Contains(ref position);
+ }
+
+ if (comp.EnterStealth)
+ {
+ comp.EnterStealth = false;
+ comp.UpdateStatus();
+ if (!comp.Online || !comp.IgnorePower && !comp.SufficientPower)
+ continue;
+
+ enter = true;
+
+ gridComp.DamageTaken = 0;
+ gridComp.Revealed = false;
+ comp.StealthActive = true;
+ //comp.RemainingDuration = comp.MaxDuration;
+ comp.TotalTime = comp.MaxDuration;
+ comp.TimeElapsed = 0;
+
+ //comp.Grid.Flags |= (EntityFlags)IsStealthedFlag;
+
+ comp.Sink.Update();
+
+ var packet = new UpdateStatePacket { EntityId = comp.Block.EntityId, EnterStealth = true, Type = PacketType.UpdateState };
+ if (IsServer)
+ SendPacketToClients(packet, comp.ReplicatedClients);
+
+ comp.PrepGrids(true);
+
+ if (!WcActive) comp.GetNearbyTurrets();
+
+ if (!IsDedicated)
+ {
+ if (IsClient)
+ SendPacketToServer(packet);
+
+ if (!inside)
+ comp.SwitchStealth(true, true);
+
+ comp.RefreshTerminal();
+
+ }
+
+ }
+ else if (comp.ExitStealth)
+ {
+ exit = true;
+ Logs.WriteLine($"Exiting stealth: {comp.IsPrimary} {comp.Online} {gridComp.Revealed} {gridComp.WaterValid} {TrackDamage && gridComp.DamageTaken > DamageThreshold}" +
+ $" {comp.SufficientPower} {comp.TimeElapsed} / {comp.TotalTime}");
+
+ comp.ExitStealth = false;
+ comp.StealthActive = false;
+ comp.IgnorePower = false;
+
+ comp.CoolingDown = true;
+ //comp.RemainingDuration = comp.MaxDuration - comp.RemainingDuration;
+ //comp.TotalTime = comp.TimeElapsed;
+ //comp.TimeElapsed = 0;
+
+ //comp.Grid.Flags ^= (EntityFlags)IsStealthedFlag;
+
+ comp.Sink.Update();
+
+ var packet = new UpdateStatePacket { EntityId = comp.Block.EntityId, ExitStealth = true, Type = PacketType.UpdateState };
+ if (IsServer)
+ SendPacketToClients(packet, comp.ReplicatedClients);
+
+ comp.PrepGrids(false);
+
+ foreach (var entity in comp.PreviousEntities)
+ {
+ if (!IsDedicated)
+ {
+ if (entity is IMyCubeGrid)
+ comp.StealthExternalGrid(false, entity as IMyCubeGrid);
+ else
+ entity.Render.Visible = true;
+ }
+
+ entity.Flags ^= StealthFlag;
+ }
+
+ if (!IsDedicated)
+ {
+ if (IsClient)
+ SendPacketToServer(packet);
+
+ if (!comp.VisibleToClient)
+ comp.SwitchStealth(false, true);
+
+ comp.RefreshTerminal();
+
+ if (Vector3D.DistanceSquared(position, comp.Block.PositionComp.WorldAABB.Center) < comp.SignalDistanceSquared)
+ comp.CreateHeatSignature();
+ }
+
+ }
+ else
+ {
+ if (comp.StealthOnInit)
+ {
+ if (!WcActive) comp.GetNearbyTurrets();
+ comp.PrepGrids(true);
+ comp.StealthOnInit = false;
+ }
+
+ if (!IsDedicated && (comp.StealthOnInit || inside != comp.VisibleToClient))
+ {
+ comp.SwitchStealth(!inside);
+ comp.Fading = false;
+ }
+
+ MyGamePruningStructure.GetAllEntitiesInOBB(ref comp.ExpandedOBB, _entities);
+
+ for (int k = 0; k < _entities.Count; k++)
+ {
+ MyEntity entity = _entities[k];
+ if (!(entity is IMyDestroyableObject || entity is IMyCubeGrid))
+ continue;
+
+ if (entity is IMyCubeGrid)
+ {
+ var grid = (IMyCubeGrid)entity;
+ if (StealthedGrids.Contains(grid)) continue;
+
+ var obb = new MyOrientedBoundingBoxD(grid.PositionComp.LocalAABB, grid.PositionComp.WorldMatrixRef);
+ if (comp.ExpandedOBB.Contains(ref obb) != ContainmentType.Contains) continue;
+
+
+ if (!IsDedicated && inside == comp.StealthedExternalGrids.Contains(grid))
+ comp.StealthExternalGrid(!inside, grid);
+ }
+ else if (!IsDedicated) entity.Render.Visible = inside;
+
+ comp.CurrentEntities.Add(entity);
+
+ if (comp.PreviousEntities.Remove(entity))
+ continue;
+
+ entity.Flags |= StealthFlag;
+ }
+ _entities.Clear();
+
+ foreach (var entity in comp.PreviousEntities)
+ {
+ var grid = entity as IMyCubeGrid;
+ if (grid != null && gridComp.GroupMap.ConnectedGrids.Contains(grid))
+ {
+ comp.StealthedExternalGrids.Remove(grid);
+ continue;
+ }
+
+ if (!IsDedicated)
+ {
+ if (grid != null)
+ comp.StealthExternalGrid(false, grid);
+ else
+ entity.Render.Visible = true;
+ }
+ entity.Flags ^= StealthFlag;
+ }
+
+ comp.PreviousEntities = new HashSet(comp.CurrentEntities);
+ comp.CurrentEntities.Clear();
+ }
+ }
+ }
+
+ }
+ catch (Exception ex)
+ {
+ Logs.WriteLine($"Exception in stealth comp loop: {ex}");
+ }
+
+
+ if (Tick20)
+ {
+ if (master != null)
+ master.MaxDuration = master.Definition.Duration + gridComp.SinkBonus;
+
+ gridComp.SinkBonus = 0;
+ }
+
+ if (gridComp.HeatComps.Count == 0) continue;
+
+ try
+ {
+ for (int j = 0; j < gridComp.HeatComps.Count; j++)
+ {
+ var comp = gridComp.HeatComps[j];
+
+ if (comp.Grid != comp.Block.CubeGrid)
+ {
+ comp.GridChange(gridComp);
+ j--;
+ // Deal with conditionals?
+ continue;
+ }
+
+ if (enter)
+ {
+ comp.Accumulating = true;
+ }
+ else if (exit)
+ {
+ comp.Accumulating = false;
+ comp.Radiating = true;
+ comp.Block.SetEmissiveParts(RADIANT_EMISSIVE, Color.DarkRed, 0.5f);
+ }
+ else if (cold)
+ {
+ comp.Radiating = false;
+ comp.Block.SetEmissiveParts(RADIANT_EMISSIVE, Color.DarkSlateGray, 0.1f);
+ comp.HeatPercent = 0;
+ }
+
+ //DrawBox(comp.DamageBox, Color.OrangeRed);
+
+ //foreach (var box in comp.BlockBoxes)
+ // DrawBox(box, Color.Red);
+
+ if (TickMod20 != comp.CompTick)
+ continue;
+
+ comp.UpdateStatus();
+ if (!IsDedicated && LastTerminal == comp.Block && MyAPIGateway.Gui.GetCurrentScreen == MyTerminalPageEnum.ControlPanel)
+ RefreshTerminal(comp.Block, comp.ShowInToolbarSwitch);
+
+ if (comp.WorkingChanged && master != null)
+ {
+ if (master.StealthActive)
+ {
+ var timeChange = comp.Working ? comp.Definition.Duration : -comp.Definition.Duration;
+ master.TotalTime += timeChange;
+
+ comp.Accumulating = comp.Working;
+ }
+ comp.WorkingChanged = false;
+ }
+
+ if (comp.Working)
+ {
+ if (comp.Accumulating)
+ {
+ //comp.HeatPercent = (byte)(100 * (1 - (master.RemainingDuration / (float)_duration)));
+ comp.HeatPercent = (byte)(100f * (float)master.TimeElapsed / (float)master.TotalTime);
+ }
+ else if (comp.Radiating)
+ {
+ //comp.HeatPercent = (byte)(100 * (master.RemainingDuration / (float)_duration));
+ comp.HeatPercent = (byte)(100f * ((float)master.TimeElapsed / (float)master.TotalTime));
+ if (!IsClient && comp.Definition.DoDamage) comp.DamageBlocks();
+ }
+
+ gridComp.SinkBonus += comp.Definition.Duration;
+
+ //if (master != null)
+ //master.MaxDuration += SinkDuration;
+
+ }
+ else
+ {
+ if (comp.Accumulating)
+ {
+ comp.Accumulating = false;
+ comp.WasAccumulating = true;
+ //master.RemainingDuration -= SinkDuration * (100 - comp.HeatPercent) / 100;
+ }
+
+ if (comp.HeatPercent > 0)
+ {
+ var loss = (byte)(100 / (comp.Definition.Duration / 20f));
+ if (loss >= comp.HeatPercent)
+ comp.HeatPercent = 0;
+ else
+ comp.HeatPercent -= loss;
+
+ if (!IsClient && comp.Definition.DoDamage) comp.DamageBlocks();
+ //do heat signature
+ }
+ }
+ }
+
+ }
+ catch (Exception ex)
+ {
+ Logs.WriteLine($"Exception in heat comp loop: {ex}");
+ }
+
+ }
+
+ }
+
+ internal void StartComps()
+ {
+ try
+ {
+ _startGrids.ApplyAdditions();
+ if (_startGrids.Count > 0)
+ {
+ for (int i = 0; i < _startGrids.Count; i++)
+ {
+ var grid = _startGrids[i];
+
+ if ((grid as MyCubeGrid).IsPreview)
+ continue;
+
+ var gridComp = _gridCompPool.Count > 0 ? _gridCompPool.Pop() : new GridComp();
+ gridComp.Init(grid, this);
+
+ GridList.Add(gridComp);
+ GridMap[grid] = gridComp;
+ grid.OnClose += OnGridClose;
+ }
+ _startGrids.ClearImmediate();
+ }
+
+ _startBlocks.ApplyAdditions();
+ for (int i = 0; i < _startBlocks.Count; i++)
+ {
+ var module = _startBlocks[i];
+
+ if (module?.CubeGrid == null || !GridMap.ContainsKey(module.CubeGrid))
+ continue;
+
+ if (module.CubeGrid.Physics == null || (module.CubeGrid as MyCubeGrid).IsPreview)
+ {
+ Logs.WriteLine($"invalid grid in startblocks - IsPreview {(module.CubeGrid as MyCubeGrid).IsPreview} - physics null {module.CubeGrid.Physics == null}");
+ continue;
+ }
+
+ var gridData = GridMap[module.CubeGrid];
+
+ Definitions.DriveDefinition dDef;
+ if (DriveDefinitions.TryGetValue(module.BlockDefinition.SubtypeName, out dDef))
+ {
+ if (DriveMap.ContainsKey(module.EntityId)) continue;
+
+ var comp = new DriveComp(module, dDef, this);
+ DriveMap[module.EntityId] = comp;
+ gridData.StealthComps.Add(comp);
+ comp.Init();
+
+ continue;
+ }
+
+ Definitions.SinkDefinition sDef;
+ if (SinkDefinitions.TryGetValue(module.BlockDefinition.SubtypeName, out sDef))
+ {
+ var comp = new SinkComp(module, sDef, this);
+ gridData.HeatComps.Add(comp);
+ comp.Init();
+ }
+ }
+ _startBlocks.ClearImmediate();
+ }
+ catch (Exception ex)
+ {
+ Logs.WriteLine($"Exception in StartComps: {ex}");
+ }
+
+ }
+ }
+}
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Data/Scripts/Stealth System/Session/SessionMethods.cs b/Utility Mods/Stealth Drive - Starcore Edition/Data/Scripts/Stealth System/Session/SessionMethods.cs
new file mode 100644
index 000000000..8af8f4121
--- /dev/null
+++ b/Utility Mods/Stealth Drive - Starcore Edition/Data/Scripts/Stealth System/Session/SessionMethods.cs
@@ -0,0 +1,205 @@
+using Sandbox.Definitions;
+using Sandbox.ModAPI;
+using VRage.Game;
+using VRage.Game.ModAPI;
+using VRage.ModAPI;
+using VRageMath;
+using VRage.Utils;
+using System.Collections.Generic;
+using Sandbox.ModAPI.Interfaces.Terminal;
+using Jakaria.API;
+using System;
+using Sandbox.Game.Entities;
+using Sandbox.Common.ObjectBuilders;
+
+namespace StealthSystem
+{
+ public partial class StealthSession
+ {
+ private void Init()
+ {
+ if (Inited) return;
+ Inited = true;
+
+ MyAPIGateway.GridGroups.OnGridGroupCreated += GridGroupsOnOnGridGroupCreated;
+ MyAPIGateway.GridGroups.OnGridGroupDestroyed += GridGroupsOnOnGridGroupDestroyed;
+ }
+
+ internal void ModCheck()
+ {
+ foreach (var mod in Session.Mods)
+ {
+ if (mod.PublishedFileId == 1918681825 || mod.PublishedFileId == 2496225055 || mod.PublishedFileId == 2726343161)
+ WcActive = true;
+
+ else if (mod.Name == "WeaponCore" || mod.Name == "CoreSystems")
+ WcActive = true;
+
+ else if (mod.PublishedFileId == 2200451495)
+ WaterMod = true;
+
+ else if (mod.PublishedFileId == 1354870812)
+ RecolourableThrust = true;
+
+ }
+ }
+
+ internal bool PlayerInit()
+ {
+ try
+ {
+ //if (MyAPIGateway.Session.LocalHumanPlayer == null)
+ // return false;
+
+ List players = new List();
+ MyAPIGateway.Multiplayer.Players.GetPlayers(players);
+
+ for (int i = 0; i < players.Count; i++)
+ PlayerConnected(players[i].IdentityId);
+
+ return true;
+ }
+ catch (Exception ex)
+ {
+ Logs.WriteLine($"Caught exception in PlayerInit() - {ex}");
+ }
+
+ return false;
+ }
+
+ internal void UpdateWaters()
+ {
+ if (IsClient && PlayersLoaded && MyAPIGateway.Session.Player?.Character != null)
+ {
+ var character = MyAPIGateway.Session.Player.Character.PositionComp.WorldAABB.Center;
+ var closestPlanet = MyGamePruningStructure.GetClosestPlanet(character);
+ if (closestPlanet.EntityId != 0 && !PlanetMap.ContainsKey(closestPlanet.EntityId))
+ PlanetTemp.TryAdd(closestPlanet, closestPlanet.EntityId);
+ }
+
+ if (!PlanetTemp.IsEmpty)
+ {
+ foreach (var planetToAdd in PlanetTemp)
+ {
+ if (planetToAdd.Key.EntityId != 0)
+ PlanetMap.TryAdd(planetToAdd.Key.EntityId, planetToAdd.Key);
+ }
+
+ PlanetTemp.Clear();
+ }
+
+ foreach (var planet in PlanetMap.Values)
+ {
+ WaterData data;
+ if (WaterModAPI.HasWater(planet))
+ {
+ if (!WaterMap.TryGetValue(planet.EntityId, out data))
+ {
+ data = new WaterData(planet);
+ WaterMap[planet.EntityId] = data;
+ }
+
+ var radiusInfo = WaterModAPI.GetPhysical(planet);
+ data.Centre = radiusInfo.Item1;
+ data.Radius = radiusInfo.Item2;
+ }
+ else WaterMap.TryRemove(planet.EntityId, out data);
+ }
+ }
+
+ internal void UpdateEnforcement(StealthSettings settings)
+ {
+ Enforced = true;
+ Logs.WriteLine($"Config settings loaded");
+
+ JumpPenalty = settings.JumpPenalty;
+ Transparency = settings.Transparency;
+ ShieldDelay = settings.ShieldDelay;
+ FadeTime = settings.FadeTime;
+ DamageThreshold = settings.DamageThreshold;
+ DisableShields = settings.DisableShields;
+ DisableWeapons = settings.DisableWeapons;
+ HideThrusterFlames = settings.HideThrusterFlames;
+ WorkInWater = settings.WorkInWater;
+ WorkOutOfWater = settings.WorkOutOfWater;
+ WaterTransitionDepth = settings.WaterTransitionDepth;
+ RevealOnDamage = settings.RevealOnDamage;
+
+ WaterOffsetSqr = WaterTransitionDepth * Math.Abs(WaterTransitionDepth);
+ TrackWater = WaterMod && WorkInWater != WorkOutOfWater;
+ TrackDamage = DamageThreshold > 0;
+
+ FadeSteps = FadeTime / FADE_INTERVAL + 1;
+
+ foreach (var drive in settings.DriveConfigs)
+ {
+ var def = new Definitions.DriveDefinition(drive.Duration, drive.PowerScale, drive.SignalRangeScale);
+ DriveDefinitions[drive.Subtype] = def;
+ }
+
+ foreach (var sink in settings.SinkConfigs)
+ {
+ var def = new Definitions.SinkDefinition(sink.Duration, sink.Power, sink.DoDamage);
+ SinkDefinitions[sink.Subtype] = def;
+ }
+
+ StealthFlag = (EntityFlags)(DisableWeapons ? IsStealthedFlag + 4 : IsStealthedFlag);
+ }
+
+ internal void RemoveEdges()
+ {
+ var defs = MyDefinitionManager.Static.GetAllDefinitions();
+ foreach (var def in defs)
+ {
+ if (def is MyCubeBlockDefinition && def.Id.SubtypeName.Contains("Armor"))
+ {
+ var armorDef = (MyCubeBlockDefinition)def;
+ if (armorDef.CubeDefinition == null)
+ continue;
+
+ armorDef.CubeDefinition.ShowEdges = false;
+ }
+ }
+ }
+
+ internal void RefreshTerminal(IMyFunctionalBlock block, IMyTerminalControlOnOffSwitch control)
+ {
+ block.RefreshCustomInfo();
+
+ if (control != null)
+ {
+ var originalSetting = control.Getter(block);
+ control.Setter(block, !originalSetting);
+ control.Setter(block, originalSetting);
+ }
+ }
+
+ internal static void DrawBox(MyOrientedBoundingBoxD obb, Color color)
+ {
+ var box = new BoundingBoxD(-obb.HalfExtent, obb.HalfExtent);
+ var wm = MatrixD.CreateFromTransformScale(obb.Orientation, obb.Center, Vector3D.One);
+ var material = MyStringId.GetOrCompute("Square");
+ MySimpleObjectDraw.DrawTransparentBox(ref wm, ref box, ref color, MySimpleObjectRasterizer.Wireframe, 1, 0.01f, null, material);
+ }
+
+ internal static void DrawScaledPoint(Vector3D pos, double radius, Color color, int divideRatio = 20, bool solid = true, float lineWidth = 0.5f)
+ {
+ var posMatCenterScaled = MatrixD.CreateTranslation(pos);
+ var posMatScaler = MatrixD.Rescale(posMatCenterScaled, radius);
+ var material = MyStringId.GetOrCompute("Square");
+ MySimpleObjectDraw.DrawTransparentSphere(ref posMatScaler, 1f, ref color, solid ? MySimpleObjectRasterizer.Solid : MySimpleObjectRasterizer.Wireframe, divideRatio, null, material, lineWidth);
+ }
+
+ internal static void DrawLine(Vector3D start, Vector3D end, Vector4 color, float width)
+ {
+ var c = color;
+ MySimpleObjectDraw.DrawLine(start, end, _square, ref c, width);
+ }
+
+ internal static void DrawLine(Vector3D start, Vector3D dir, Vector4 color, float width, float length)
+ {
+ var c = color;
+ MySimpleObjectDraw.DrawLine(start, start + (dir * length), _square, ref c, width);
+ }
+ }
+}
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Data/Scripts/Stealth System/Session/SessionNetwork.cs b/Utility Mods/Stealth Drive - Starcore Edition/Data/Scripts/Stealth System/Session/SessionNetwork.cs
new file mode 100644
index 000000000..086091a16
--- /dev/null
+++ b/Utility Mods/Stealth Drive - Starcore Edition/Data/Scripts/Stealth System/Session/SessionNetwork.cs
@@ -0,0 +1,126 @@
+using ProtoBuf;
+using Sandbox.ModAPI;
+using System;
+using System.Collections.Generic;
+
+namespace StealthSystem
+{
+ public partial class StealthSession
+ {
+ internal const ushort ServerPacketId = 65347;
+ internal const ushort ClientPacketId = 65348;
+
+ public static void SendPacketToServer(Packet packet)
+ {
+ var rawData = MyAPIGateway.Utilities.SerializeToBinary(packet);
+ MyModAPIHelper.MyMultiplayer.Static.SendMessageToServer(ServerPacketId, rawData, true);
+ }
+
+ public static void SendPacketToClient(Packet packet, ulong client)
+ {
+ var rawData = MyAPIGateway.Utilities.SerializeToBinary(packet);
+ MyModAPIHelper.MyMultiplayer.Static.SendMessageTo(ClientPacketId, rawData, client, true);
+ }
+
+ public static void SendPacketToClients(Packet packet, List clients)
+ {
+ var rawData = MyAPIGateway.Utilities.SerializeToBinary(packet);
+
+ foreach (var client in clients)
+ MyModAPIHelper.MyMultiplayer.Static.SendMessageTo(ClientPacketId, rawData, client, true);
+ }
+
+ internal void ProcessPacket(ushort id, byte[] rawData, ulong sender, bool reliable)
+ {
+ try
+ {
+ var packet = MyAPIGateway.Utilities.SerializeFromBinary(rawData);
+ if (packet == null || packet.EntityId != 0 && !DriveMap.ContainsKey(packet.EntityId))
+ {
+ Logs.WriteLine($"Invalid packet - null:{packet == null}");
+ return;
+ }
+
+ var comp = packet.EntityId == 0 ? null : DriveMap[packet.EntityId];
+ switch (packet.Type)
+ {
+ case PacketType.UpdateState:
+ var uPacket = packet as UpdateStatePacket;
+ comp.EnterStealth = uPacket.EnterStealth && !comp.StealthActive;
+ comp.ExitStealth = uPacket.ExitStealth && comp.StealthActive;
+ break;
+ case PacketType.UpdateDuration:
+ var dPacket = packet as UpdateDurationPacket;
+ //comp.RemainingDuration += dPacket.DurationChange;
+ comp.TotalTime += dPacket.DurationChange;
+ break;
+ case PacketType.Replicate:
+ var rPacket = packet as ReplicationPacket;
+ if (rPacket.Fresh)
+ comp.ReplicatedClients.Add(sender);
+ else
+ comp.ReplicatedClients.Remove(sender);
+ break;
+ case PacketType.Settings:
+ var sPacket = packet as SettingsPacket;
+ UpdateEnforcement(sPacket.Settings);
+ break;
+ default:
+ Logs.WriteLine($"Invalid packet type - {packet.GetType()}");
+ break;
+ }
+ }
+ catch (Exception ex)
+ {
+ Logs.WriteLine($"Exception in ProcessPacket: {ex}");
+ }
+
+ }
+
+ }
+
+ [ProtoContract]
+ [ProtoInclude(4, typeof(UpdateStatePacket))]
+ [ProtoInclude(5, typeof(UpdateDurationPacket))]
+ [ProtoInclude(6, typeof(ReplicationPacket))]
+ [ProtoInclude(7, typeof(SettingsPacket))]
+ public class Packet
+ {
+ [ProtoMember(1)] internal long EntityId;
+ [ProtoMember(2)] internal PacketType Type;
+ }
+
+ [ProtoContract]
+ public class UpdateStatePacket : Packet
+ {
+ [ProtoMember(1)] internal bool EnterStealth;
+ [ProtoMember(2)] internal bool ExitStealth;
+ }
+
+ [ProtoContract]
+ public class UpdateDurationPacket : Packet
+ {
+ [ProtoMember(1)] internal int DurationChange;
+ }
+
+ [ProtoContract]
+ public class ReplicationPacket : Packet
+ {
+ [ProtoMember(1)] internal bool Fresh;
+ }
+
+ [ProtoContract]
+ public class SettingsPacket : Packet
+ {
+ [ProtoMember(1)] internal StealthSettings Settings;
+ }
+
+ public enum PacketType
+ {
+ UpdateState,
+ UpdateDuration,
+ Replicate,
+ Settings
+ }
+
+}
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Data/Scripts/Stealth System/Session/SessionRun.cs b/Utility Mods/Stealth Drive - Starcore Edition/Data/Scripts/Stealth System/Session/SessionRun.cs
new file mode 100644
index 000000000..59e9ba63f
--- /dev/null
+++ b/Utility Mods/Stealth Drive - Starcore Edition/Data/Scripts/Stealth System/Session/SessionRun.cs
@@ -0,0 +1,130 @@
+using Sandbox.ModAPI;
+using VRage.Game.Components;
+using VRageMath;
+using Sandbox.Game.Entities;
+using Sandbox.Game;
+using VRage.Game;
+
+namespace StealthSystem
+{
+ [MySessionComponentDescriptor(MyUpdateOrder.AfterSimulation)]
+ public partial class StealthSession : MySessionComponentBase
+ {
+ internal static int Tick;
+ internal int TickMod15;
+ internal int TickMod20;
+ internal int TickMod60;
+ internal bool Tick10;
+ internal bool Tick20;
+ internal bool Tick60;
+ internal bool Tick120;
+ internal bool Tick600;
+ internal bool Tick3600;
+ internal bool IsServer;
+ internal bool IsClient;
+ internal bool IsDedicated;
+ internal bool PlayersLoaded;
+
+ public override void LoadData()
+ {
+ IsServer = MyAPIGateway.Multiplayer.MultiplayerActive && MyAPIGateway.Session.IsServer;
+ IsClient = MyAPIGateway.Multiplayer.MultiplayerActive && !MyAPIGateway.Session.IsServer;
+ IsDedicated = MyAPIGateway.Utilities.IsDedicated;
+
+ LargeBox = new BoundingBoxD(-_large, _large);
+ SmallBox = new BoundingBoxD(-_small, _small);
+
+ Logs.InitLogs();
+
+ ModPath = ModContext.ModPath;
+ ModCheck();
+
+ //RemoveEdges();
+ CreateTerminalControls();
+
+ MyEntities.OnEntityCreate += OnEntityCreate;
+ //MyEntities.OnEntityDelete += OnEntityDelete;
+ MyEntities.OnCloseAll += OnCloseAll;
+ MyAPIGateway.TerminalControls.CustomControlGetter += CustomControlGetter;
+ MyAPIGateway.TerminalControls.CustomActionGetter += CustomActionGetter;
+ }
+
+ public override void BeforeStart()
+ {
+ if (IsClient)
+ MyAPIGateway.Multiplayer.RegisterSecureMessageHandler(ClientPacketId, ProcessPacket);
+ else if (IsServer)
+ {
+ MyAPIGateway.Multiplayer.RegisterSecureMessageHandler(ServerPacketId, ProcessPacket);
+ MyVisualScriptLogicProvider.PlayerRespawnRequest += PlayerConnected;
+ }
+
+ if (!IsClient)
+ MyAPIGateway.Session.DamageSystem.RegisterAfterDamageHandler(0, AfterDamageApplied);
+
+ ConfigSettings = new Settings(this);
+
+ APIServer.Load();
+
+ if (WaterMod)
+ WaterAPI.Register();
+ }
+
+ public override void UpdateAfterSimulation()
+ {
+ Tick++;
+
+ TickMod15 = Tick % 15;
+ TickMod20 = Tick % 20;
+ TickMod60 = Tick % 60;
+
+ Tick10 = Tick % 10 == 0;
+ Tick20 = TickMod20 == 0;
+ Tick60 = TickMod60 == 0;
+ Tick120 = Tick % 120 == 0;
+ Tick600 = Tick % 600 == 0;
+ Tick3600 = Tick % 3600 == 0;
+
+ if (!PlayersLoaded && IsServer && PlayerInit())
+ PlayersLoaded = true;
+
+ if (TrackWater && (Tick3600 || Tick60 && WaterMap.IsEmpty))
+ UpdateWaters();
+
+ if (Enforced && (!_startBlocks.IsEmpty || !_startGrids.IsEmpty))
+ StartComps();
+
+ CompLoop();
+ }
+
+ protected override void UnloadData()
+ {
+ if (IsClient)
+ MyAPIGateway.Multiplayer.UnregisterSecureMessageHandler(ClientPacketId, ProcessPacket);
+ else if (IsServer)
+ {
+ MyAPIGateway.Multiplayer.UnregisterSecureMessageHandler(ServerPacketId, ProcessPacket);
+ MyVisualScriptLogicProvider.PlayerRespawnRequest -= PlayerConnected;
+ }
+
+ MyEntities.OnEntityCreate -= OnEntityCreate;
+ MyEntities.OnCloseAll -= OnCloseAll;
+
+ MyAPIGateway.TerminalControls.CustomControlGetter -= CustomControlGetter;
+ MyAPIGateway.TerminalControls.CustomActionGetter -= CustomActionGetter;
+
+ Logs.Close();
+ APIServer.Unload();
+ if (WaterMod)
+ WaterAPI.Unregister();
+
+ Clean();
+ }
+
+ public override MyObjectBuilder_SessionComponent GetObjectBuilder()
+ {
+ return base.GetObjectBuilder();
+ }
+
+ }
+}
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Data/Scripts/Stealth System/Support/WaterModAPI.cs b/Utility Mods/Stealth Drive - Starcore Edition/Data/Scripts/Stealth System/Support/WaterModAPI.cs
new file mode 100644
index 000000000..34ba4ba3e
--- /dev/null
+++ b/Utility Mods/Stealth Drive - Starcore Edition/Data/Scripts/Stealth System/Support/WaterModAPI.cs
@@ -0,0 +1,281 @@
+using Sandbox.Game.Entities;
+using Sandbox.ModAPI;
+using System;
+using System.Collections.Generic;
+using VRage;
+using VRage.Game;
+using VRage.Game.Components;
+using VRage.Utils;
+using VRageMath;
+
+namespace Jakaria.API
+{
+ //See the steam guide for how to use this
+ //https://steamcommunity.com/sharedfiles/filedetails/?id=2639207010
+ ///
+ /// https://github.com/jakarianstudios/SE-Water/blob/master/API/WaterModAPI.cs
+ ///
+
+ [MySessionComponentDescriptor(MyUpdateOrder.NoUpdate)]
+ public class WaterModAPI : MySessionComponentBase
+ {
+ public static string ModName = "";
+ public const ushort ModHandlerID = 50271;
+ public const int ModAPIVersion = 19;
+ public static bool Registered { get; private set; } = false;
+
+ private static Dictionary ModAPIMethods;
+
+ private static Func _VerifyVersion;
+
+ private static Func _IsUnderwater;
+ private static Func _LineIntersectsWater;
+ private static Action, ICollection, MyPlanet> _LineIntersectsWaterList;
+ private static Func _GetClosestWater;
+ private static Func _SphereIntersectsWater;
+ private static Action, ICollection, MyPlanet> _SphereIntersectsWaterList;
+ private static Func _GetClosestSurfacePoint;
+ private static Action, ICollection, MyPlanet> _GetClosestSurfacePointList;
+ private static Func _GetDepth;
+ private static Action _ForceSync;
+ private static Action _RunCommand;
+ private static Func _GetUpDirection;
+ private static Func _HasWater;
+ private static Func _GetBuoyancyMultiplier;
+ private static Func _GetCrushDepth;
+
+ private static Func> _GetPhysicalData;
+ private static Func> _GetWaveData;
+ private static Func> _GetRenderData;
+ private static Func> _GetPhysicsData;
+ private static Func> _GetTideData;
+ private static Func _GetTideDirection;
+
+ private static Action _CreateSplash;
+ private static Action _CreateBubble;
+ private static Action _CreatePhysicsSplash;
+
+ ///
+ /// Returns true if the version is compatibile with the API Backend, this is automatically called
+ ///
+ public static bool VerifyVersion(int Version, string ModName) => _VerifyVersion?.Invoke(Version, ModName) ?? false;
+
+ ///
+ /// Returns true if the provided planet entity ID has water
+ ///
+ public static bool HasWater(MyPlanet planet) => _HasWater?.Invoke(planet) ?? false;
+
+ ///
+ /// Returns true if the position is underwater
+ ///
+ public static bool IsUnderwater(Vector3D Position, MyPlanet ID = null) => _IsUnderwater?.Invoke(Position, ID) ?? false;
+
+ ///
+ /// Overwater = 0, ExitsWater = 1, EntersWater = 2, Underwater = 3
+ ///
+ public static int LineIntersectsWater(LineD Line, MyPlanet ID = null) => _LineIntersectsWater?.Invoke(Line, ID) ?? 0;
+
+ ///
+ /// Overwater = 0, ExitsWater = 1, EntersWater = 2, Underwater = 3
+ ///
+ public static void LineIntersectsWater(List Lines, ICollection Intersections, MyPlanet ID = null) => _LineIntersectsWaterList?.Invoke(Lines, Intersections, ID);
+
+ ///
+ /// Gets the closest water to the provided water
+ ///
+ public static MyPlanet GetClosestWater(Vector3D Position) => _GetClosestWater?.Invoke(Position) ?? null;
+
+ ///
+ /// Overwater = 0, ExitsWater = 1, EntersWater = 2, Underwater = 3
+ ///
+ public static int SphereIntersectsWater(BoundingSphereD Sphere, MyPlanet ID = null) => _SphereIntersectsWater?.Invoke(Sphere, ID) ?? 0;
+
+ ///
+ /// Overwater = 0, ExitsWater = 1, EntersWater = 2, Underwater = 3
+ ///
+ public static void SphereIntersectsWater(List Spheres, ICollection Intersections, MyPlanet ID = null) => _SphereIntersectsWaterList?.Invoke(Spheres, Intersections, ID);
+
+
+ ///
+ /// Returns the closest position on the water surface
+ ///
+ public static Vector3D GetClosestSurfacePoint(Vector3D Position, MyPlanet ID = null) => _GetClosestSurfacePoint?.Invoke(Position, ID) ?? Position;
+
+ ///
+ /// Returns the closest position on the water surface
+ ///
+ public static void GetClosestSurfacePoint(List Positions, ICollection Points, MyPlanet ID = null) => _GetClosestSurfacePointList?.Invoke(Positions, Points, ID);
+
+
+ ///
+ /// Returns the depth the position is underwater
+ ///
+ public static float? GetDepth(Vector3D Position, MyPlanet ID = null) => _GetDepth?.Invoke(Position, ID) ?? null;
+
+ ///
+ /// Creates a splash at the provided position
+ ///
+ public static void CreateSplash(Vector3D Position, float Radius, bool Audible) => _CreateSplash?.Invoke(Position, Radius, Audible);
+
+ ///
+ /// Creates a physical splash at the provided position (Particles outside of the water)
+ ///
+ public static void CreatePhysicsSplash(Vector3D Position, Vector3D Velocity, float Radius, int Count = 1) => _CreatePhysicsSplash?.Invoke(Position, Velocity, Radius, Count);
+
+ ///
+ /// Creates a bubble at the provided position
+ ///
+ public static void CreateBubble(Vector3D Position, float Radius) => _CreateBubble?.Invoke(Position, Radius);
+
+ ///
+ /// Forces the server to sync with the client
+ ///
+ public static void ForceSync() => _ForceSync?.Invoke();
+
+ ///
+ /// Simulates a command being run by the client, EX: /wcreate, client must have permissions to run the command
+ ///
+ public static void RunCommand(string MessageText) => _RunCommand?.Invoke(MessageText);
+
+ ///
+ /// Gets the up direction at the position
+ ///
+ public static Vector3D GetUpDirection(Vector3D Position, MyPlanet ID = null) => _GetUpDirection?.Invoke(Position, ID) ?? Vector3D.Up;
+
+ ///
+ /// Gets the buoyancy multiplier to help calculate buoyancy of a grid, used in the final calculation of grid buoyancy.
+ ///
+ public static float GetBuoyancyMultiplier(Vector3D Position, MyCubeSize GridSize, MyPlanet ID = null) => _GetBuoyancyMultiplier?.Invoke(Position, GridSize, ID) ?? 0;
+
+ ///
+ /// Gets crush damage
+ ///
+ [Obsolete]
+ public static float GetCrushDepth(MyPlanet planet) => _GetCrushDepth?.Invoke(planet) ?? 500;
+
+ ///
+ /// Gets position, radius, minimum radius, and maximum radius- in that order.
+ ///
+ public static MyTuple GetPhysical(MyPlanet planet) => (MyTuple)(_GetPhysicalData?.Invoke(planet) ?? null);
+
+ ///
+ /// Gets wave height, wave speed, wave scale, and seed- in that order.
+ ///
+ public static MyTuple GetWaveData(MyPlanet planet) => (MyTuple)(_GetWaveData?.Invoke(planet) ?? null);
+
+ ///
+ /// Gets fog color, transparency toggle, and lighting toggle- in that order.
+ ///
+ public static MyTuple GetRenderData(MyPlanet planet) => (MyTuple)(_GetRenderData?.Invoke(planet) ?? null);
+
+ ///
+ /// Gets tide height and tide speed- in that order.
+ ///
+ public static MyTuple GetTideData(MyPlanet planet) => (MyTuple)(_GetTideData?.Invoke(planet) ?? null);
+
+ ///
+ /// Gets density and buoyancy multiplier- in that order.
+ ///
+ public static MyTuple GetPhysicsData(MyPlanet planet) => (MyTuple)(_GetPhysicsData?.Invoke(planet) ?? null);
+
+ ///
+ /// Gets the direction of high tide, from center of the water to the surface
+ ///
+ public static Vector3D GetTideDirection(MyPlanet planet) => (Vector3D)(_GetTideDirection?.Invoke(planet) ?? null);
+
+ ///
+ /// Do not use. This is for the session component to register automatically
+ ///
+ public override void LoadData()
+ {
+ Register();
+ }
+
+ ///
+ /// Do not use. This is for the session component to register automatically
+ ///
+ protected override void UnloadData()
+ {
+ Unregister();
+ }
+
+ ///
+ /// Registers the mod and sets the mod name if it is not already set
+ ///
+ public void Register()
+ {
+ MyAPIGateway.Utilities.RegisterMessageHandler(ModHandlerID, ModHandler);
+
+ if (ModName == "")
+ {
+ if (MyAPIGateway.Utilities.GamePaths.ModScopeName.Contains("_"))
+ ModName = MyAPIGateway.Utilities.GamePaths.ModScopeName.Split('_')[1];
+ else
+ ModName = MyAPIGateway.Utilities.GamePaths.ModScopeName;
+ }
+ }
+
+ ///
+ /// Unregisters the mod
+ ///
+ public void Unregister()
+ {
+ MyAPIGateway.Utilities.UnregisterMessageHandler(ModHandlerID, ModHandler);
+ Registered = false;
+ }
+
+ private void ModHandler(object obj)
+ {
+ if (obj == null)
+ {
+ return;
+ }
+
+ if (obj is Dictionary)
+ {
+ ModAPIMethods = (Dictionary)obj;
+ _VerifyVersion = (Func)ModAPIMethods["VerifyVersion"];
+
+ Registered = VerifyVersion(ModAPIVersion, ModName);
+
+ MyLog.Default.WriteLine("Registering WaterAPI for Mod '" + ModName + "'");
+
+ if (Registered)
+ {
+ try
+ {
+ _IsUnderwater = (Func)ModAPIMethods["IsUnderwater"];
+ _GetClosestWater = (Func)ModAPIMethods["GetClosestWater"];
+ _SphereIntersectsWater = (Func)ModAPIMethods["SphereIntersectsWater"];
+ _SphereIntersectsWaterList = (Action, ICollection, MyPlanet>)ModAPIMethods["SphereIntersectsWaterList"];
+ _GetClosestSurfacePoint = (Func)ModAPIMethods["GetClosestSurfacePoint"];
+ _GetClosestSurfacePointList = (Action, ICollection, MyPlanet>)ModAPIMethods["GetClosestSurfacePointList"];
+ _LineIntersectsWater = (Func)ModAPIMethods["LineIntersectsWater"];
+ _LineIntersectsWaterList = (Action, ICollection, MyPlanet>)ModAPIMethods["LineIntersectsWaterList"];
+ _GetDepth = (Func)ModAPIMethods["GetDepth"];
+ _CreateSplash = (Action)ModAPIMethods["CreateSplash"];
+ _CreatePhysicsSplash = (Action)ModAPIMethods["CreatePhysicsSplash"];
+ _CreateBubble = (Action)ModAPIMethods["CreateBubble"];
+ _ForceSync = (Action)ModAPIMethods["ForceSync"];
+ _RunCommand = (Action)ModAPIMethods["RunCommand"];
+ _GetUpDirection = (Func)ModAPIMethods["GetUpDirection"];
+ _HasWater = (Func)ModAPIMethods["HasWater"];
+ _GetBuoyancyMultiplier = (Func)ModAPIMethods["GetBuoyancyMultiplier"];
+ _GetCrushDepth = (Func)ModAPIMethods["GetCrushDepth"];
+ _GetPhysicalData = (Func>)ModAPIMethods["GetPhysicalData"];
+ _GetWaveData = (Func>)ModAPIMethods["GetWaveData"];
+ _GetRenderData = (Func>)ModAPIMethods["GetRenderData"];
+ _GetPhysicsData = (Func>)ModAPIMethods["GetPhysicsData"];
+ _GetTideData = (Func>)ModAPIMethods["GetTideData"];
+ _GetTideDirection = (Func)ModAPIMethods["GetTideDirection"];
+ }
+ catch (Exception e)
+ {
+ MyAPIGateway.Utilities.ShowMessage("WaterMod", "Mod '" + ModName + "' encountered an error when registering the Water Mod API, see log for more info.");
+ MyLog.Default.WriteLine("WaterMod: " + e);
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Data/Scripts/Stealth System/Utils/EmissiveValues.cs b/Utility Mods/Stealth Drive - Starcore Edition/Data/Scripts/Stealth System/Utils/EmissiveValues.cs
new file mode 100644
index 000000000..d093238bf
--- /dev/null
+++ b/Utility Mods/Stealth Drive - Starcore Edition/Data/Scripts/Stealth System/Utils/EmissiveValues.cs
@@ -0,0 +1,48 @@
+using Sandbox.ModAPI;
+using VRage.Game.Components;
+using VRageMath;
+
+namespace StealthSystem
+{
+ [MySessionComponentDescriptor(MyUpdateOrder.NoUpdate)]
+ public class EmissiveValues : MySessionComponentBase
+ {
+ internal static Color GREEN = new Color(0, 255, 0);
+ internal static Color RED = new Color(255, 0, 0);
+
+ public override void BeforeStart()
+ {
+ if (MyAPIGateway.Utilities.IsDedicated) return;
+
+ UpdateEmissiveValues();
+
+ }
+
+ private void UpdateEmissiveValues()
+ {
+ bool aqdVisualsPresent = false;
+ bool emissiveColorsPresent = false;
+
+ foreach (var mod in MyAPIGateway.Session.Mods)
+ {
+ if (mod.PublishedFileId == 2244563617) // AQD - Visuals
+ aqdVisualsPresent = true;
+ else if (mod.PublishedFileId == 2212516940) // Emissive Colors - Red / Green Color Vision Deficiency
+ emissiveColorsPresent = true;
+
+ if (aqdVisualsPresent && emissiveColorsPresent)
+ break;
+ }
+
+ if (aqdVisualsPresent)
+ {
+ RED = new Color(171, 42, 29);
+ GREEN = emissiveColorsPresent ? new Color(10, 255, 25) : new Color(60, 163, 33);
+ }
+ else if (emissiveColorsPresent)
+ {
+ GREEN = new Color(10, 255, 25);
+ }
+ }
+ }
+}
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Data/Scripts/Stealth System/Utils/Logs.cs b/Utility Mods/Stealth Drive - Starcore Edition/Data/Scripts/Stealth System/Utils/Logs.cs
new file mode 100644
index 000000000..ee6246781
--- /dev/null
+++ b/Utility Mods/Stealth Drive - Starcore Edition/Data/Scripts/Stealth System/Utils/Logs.cs
@@ -0,0 +1,98 @@
+using Sandbox.ModAPI;
+using System;
+using System.IO;
+using VRage.Game.ModAPI;
+
+namespace StealthSystem
+{
+ internal class Logs
+ {
+ internal const string LOG_PREFIX = "StealthMod_";
+ internal const string LOG_SUFFIX = ".log";
+ internal const int LOGS_TO_KEEP = 5;
+
+ internal static TextWriter TextWriter;
+
+ internal static void InitLogs()
+ {
+ int last = LOGS_TO_KEEP - 1;
+ string lastName = LOG_PREFIX + last + LOG_SUFFIX;
+ if (MyAPIGateway.Utilities.FileExistsInLocalStorage(lastName, typeof(Logs)))
+ MyAPIGateway.Utilities.DeleteFileInLocalStorage(lastName, typeof(Logs));
+
+ if (last > 0)
+ {
+ for (int i = last; i > 0; i--)
+ {
+ string oldName = LOG_PREFIX + (i - 1) + LOG_SUFFIX;
+ string newName = LOG_PREFIX + i + LOG_SUFFIX;
+ RenameFileInLocalStorage(oldName, newName, typeof(Logs));
+ }
+ }
+
+ string fileName = LOG_PREFIX + 0 + LOG_SUFFIX;
+ TextWriter = MyAPIGateway.Utilities.WriteFileInLocalStorage(fileName, typeof(Logs));
+
+ var message = $"{DateTime.Now:dd-MM-yy HH-mm-ss} - Logging Started";
+ TextWriter.WriteLine(message);
+ TextWriter.WriteLine(" Tick - Log");
+ TextWriter.Flush();
+
+ }
+
+ internal static void RenameFileInLocalStorage(string oldName, string newName, Type anyObjectInYourMod)
+ {
+ if (!MyAPIGateway.Utilities.FileExistsInLocalStorage(oldName, anyObjectInYourMod))
+ return;
+
+ if (MyAPIGateway.Utilities.FileExistsInLocalStorage(newName, anyObjectInYourMod))
+ return;
+
+ using (var read = MyAPIGateway.Utilities.ReadFileInLocalStorage(oldName, anyObjectInYourMod))
+ {
+ using (var write = MyAPIGateway.Utilities.WriteFileInLocalStorage(newName, anyObjectInYourMod))
+ {
+ write.Write(read.ReadToEnd());
+ write.Flush();
+ write.Dispose();
+ }
+ }
+
+ MyAPIGateway.Utilities.DeleteFileInLocalStorage(oldName, anyObjectInYourMod);
+ }
+
+ internal static void WriteLine(string text)
+ {
+ string line = $"{StealthSession.Tick,6} - " + text;
+ TextWriter.WriteLine(line);
+ TextWriter.Flush();
+ }
+
+ internal static void Close()
+ {
+ var message = $"{DateTime.Now:dd-MM-yy HH-mm-ss} - Logging Stopped";
+ TextWriter.WriteLine(message);
+
+ TextWriter.Flush();
+ TextWriter.Close();
+ TextWriter.Dispose();
+ }
+
+ internal static void CheckGrid()
+ {
+ if (MyAPIGateway.Session.LocalHumanPlayer?.Character == null) return;
+
+ var from = MyAPIGateway.Session.LocalHumanPlayer.Character.PositionComp.WorldMatrixRef.Translation;
+ var to = from + MyAPIGateway.Session.LocalHumanPlayer.Character.PositionComp.WorldMatrixRef.Forward * 100;
+ IHitInfo info;
+ MyAPIGateway.Physics.CastRay(from, to, out info);
+
+ if (info == null) return;
+
+ var grid = info.HitEntity as IMyCubeGrid;
+ if (grid == null) return;
+
+ Logs.WriteLine($"Grid: {grid.DisplayName} - has flag: {((uint)grid.Flags & StealthSession.IsStealthedFlag) > 0}");
+ }
+ }
+}
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Data/Scripts/Stealth System/Utils/Settings.cs b/Utility Mods/Stealth Drive - Starcore Edition/Data/Scripts/Stealth System/Utils/Settings.cs
new file mode 100644
index 000000000..5b414ee95
--- /dev/null
+++ b/Utility Mods/Stealth Drive - Starcore Edition/Data/Scripts/Stealth System/Utils/Settings.cs
@@ -0,0 +1,342 @@
+using ProtoBuf;
+using Sandbox.ModAPI;
+using System;
+using System.Collections.Generic;
+using System.IO;
+
+namespace StealthSystem
+{
+ internal class Settings
+ {
+ private readonly StealthSession _session;
+
+ internal const string CONFIG_FILE = "StealthMod.cfg";
+ internal const int CONFIG_VERSION = 8;
+
+ internal StealthSettings Config;
+
+ internal Settings(StealthSession session)
+ {
+ _session = session;
+
+ LoadConfig();
+ }
+
+ private void LoadConfig()
+ {
+ try
+ {
+ if (MyAPIGateway.Utilities.FileExistsInWorldStorage(CONFIG_FILE, typeof(StealthSettings)))
+ {
+
+ var writer = MyAPIGateway.Utilities.ReadFileInWorldStorage(CONFIG_FILE, typeof(StealthSettings));
+
+ StealthSettings xmlData = null;
+
+ try { xmlData = MyAPIGateway.Utilities.SerializeFromXML(writer.ReadToEnd()); }
+ catch (Exception ex)
+ {
+ writer.Dispose();
+ Logs.WriteLine($"Exception in SerializeFromXML: {ex}");
+ }
+
+ writer.Dispose();
+
+ if (xmlData?.Version == CONFIG_VERSION)
+ {
+ Logs.WriteLine($"Found up to date config file");
+
+ Config = xmlData;
+ CorruptionCheck();
+ SaveConfig();
+ }
+ else
+ {
+ var versionStr = xmlData != null ? xmlData.Version.ToString() : "null";
+ Logs.WriteLine($"Found config file with version {versionStr} : updating to version {CONFIG_VERSION}");
+
+ GenerateConfig(xmlData);
+ }
+ }
+ else
+ {
+ Logs.WriteLine($"No config file found, generating...");
+
+ GenerateConfig();
+ }
+
+ _session.UpdateEnforcement(Config);
+ }
+ catch (Exception ex)
+ {
+ Logs.WriteLine($"Exception in LoadConfig: {ex}");
+ }
+ }
+
+ private void GenerateConfig(StealthSettings oldSettings = null)
+ {
+
+ if (oldSettings != null)
+ {
+ RebuildConfig(oldSettings);
+ }
+ else
+ Config = new StealthSettings { Version = CONFIG_VERSION };
+
+ CorruptionCheck();
+ SaveConfig();
+ }
+
+ private void RebuildConfig(StealthSettings oldSettings)
+ {
+ Config = new StealthSettings { Version = CONFIG_VERSION };
+
+ var fade = oldSettings.Version < 4;
+ var five = oldSettings.Version < 5;
+ var six = oldSettings.Version < 6;
+ var seven = oldSettings.Version < 7;
+ var eight = oldSettings.Version < 8;
+
+ Config.FadeTime = fade ? 150 : oldSettings.FadeTime;
+ Config.ShieldDelay = five ? 300 : oldSettings.ShieldDelay;
+ Config.JumpPenalty = oldSettings.JumpPenalty;
+ Config.Transparency = oldSettings.Transparency;
+ Config.DisableShields = five ? true : oldSettings.DisableShields;
+ Config.DamageThreshold = oldSettings.DamageThreshold;
+ Config.DisableWeapons = six ? true : oldSettings.DisableWeapons;
+ Config.HideThrusterFlames = seven ? true : oldSettings.HideThrusterFlames;
+ Config.WorkInWater = eight ? true : oldSettings.WorkInWater;
+ Config.WorkOutOfWater = eight ? true : oldSettings.WorkOutOfWater;
+ Config.WaterTransitionDepth = eight ? 0f : oldSettings.WaterTransitionDepth;
+
+ Config.DriveConfig = oldSettings.DriveConfig;
+ Config.SinkConfig = oldSettings.SinkConfig;
+
+ Config.DriveConfigs = oldSettings.DriveConfigs;
+ Config.SinkConfigs = oldSettings.SinkConfigs;
+
+
+ }
+
+ private void CorruptionCheck()
+ {
+ if (Config.FadeTime < 0)
+ {
+ Config.FadeTime = 210;
+ Logs.WriteLine($"Config error: FadeTime cannot be negative!");
+ }
+ if (Config.ShieldDelay < 0)
+ {
+ Config.ShieldDelay = 300;
+ Logs.WriteLine($"Config error: ShieldDelay cannot be negative!");
+ }
+ if (Config.JumpPenalty < 0)
+ {
+ Config.JumpPenalty = 180;
+ Logs.WriteLine($"Config error: JumpPenalty cannot be negative!");
+ }
+ if (Config.Transparency <= 0)
+ {
+ Config.Transparency = 0.9f;
+ Logs.WriteLine($"Config error: Transparency must be greater than zero!");
+ }
+
+ if (Config.WorkInWater == false && Config.WorkOutOfWater == false)
+ {
+ Config.WorkInWater = Config.WorkOutOfWater = true;
+ Logs.WriteLine($"Config error: WorkInWater and WorkOutOfWater cannot both be false!");
+ }
+
+ if (Config.DriveConfigs == null || Config.DriveConfigs.Length == 0)
+ {
+ var oldDrive = Config.DriveConfig;
+ Config.DriveConfigs = new StealthSettings.DriveSettings[3]
+ {
+ new StealthSettings.DriveSettings(oldDrive)
+ {
+ Subtype = "StealthDrive",
+ },
+ new StealthSettings.DriveSettings(oldDrive)
+ {
+ Subtype = "StealthDriveSmall",
+ },
+ new StealthSettings.DriveSettings(oldDrive)
+ {
+ Subtype = "StealthDrive1x1",
+ Duration = 600,
+ },
+ };
+ Logs.WriteLine($"Config error: No Drive configs found, regenerating...");
+ }
+ else
+ {
+ var drives = new List();
+ for (int i = 0; i < Config.DriveConfigs.Length; i++)
+ {
+ var drive = Config.DriveConfigs[i];
+ if (string.IsNullOrEmpty(drive.Subtype))
+ {
+ Logs.WriteLine($"Drive config error: Invalid SubtypeId!");
+ continue;
+ }
+
+ if (drive.Duration <= 0)
+ {
+ drive.Duration = 1800;
+ Logs.WriteLine($"Drive config error ({drive.Subtype}): Duration must be greater than zero!");
+ }
+ if (drive.PowerScale <= 0f)
+ {
+ drive.PowerScale = 0.02f;
+ Logs.WriteLine($"Drive config error ({drive.Subtype}): PowerScale must be greater than zero!");
+ }
+ if (drive.SignalRangeScale <= 0f)
+ {
+ drive.SignalRangeScale = 20f;
+ Logs.WriteLine($"Drive config error ({drive.Subtype}): SignalRangeScale must be greater than zero!");
+ }
+
+ drives.Add(drive);
+ }
+ if (drives.Count > 0)
+ Config.DriveConfigs = drives.ToArray();
+ }
+
+ if (Config.SinkConfigs == null || Config.SinkConfigs.Length == 0)
+ {
+ var oldSink = Config.SinkConfig;
+ Config.SinkConfigs = new StealthSettings.SinkSettings[2]
+ {
+ new StealthSettings.SinkSettings(oldSink)
+ {
+ Subtype = "StealthHeatSink",
+ },
+ new StealthSettings.SinkSettings(oldSink)
+ {
+ Subtype = "StealthHeatSinkSmall",
+ },
+ };
+ Logs.WriteLine($"Config error: No Heatsink configs found, regenerating...");
+ }
+ else
+ {
+ var sinks = new List();
+ for (int i = 0; i < Config.SinkConfigs.Length; i++)
+ {
+ var sink = Config.SinkConfigs[i];
+ if (string.IsNullOrEmpty(sink.Subtype))
+ {
+ Logs.WriteLine($"Heatsink config error: Invalid SubtypeId!");
+ continue;
+ }
+
+ if (sink.Duration <= 0)
+ {
+ sink.Duration = 900;
+ Logs.WriteLine($"Heatsink config error ({sink.Subtype}): Duration must be greater than zero!");
+ }
+ if (sink.Power <= 0f)
+ {
+ sink.Power = 10f;
+ Logs.WriteLine($"Heatsink config error ({sink.Subtype}): Power must be greater than zero!");
+ }
+
+ sinks.Add(sink);
+ }
+ if (sinks.Count > 0)
+ Config.SinkConfigs = sinks.ToArray();
+ }
+
+ Config.DriveConfig = null;
+ Config.SinkConfig = null;
+
+ }
+
+ private void SaveConfig()
+ {
+ MyAPIGateway.Utilities.DeleteFileInWorldStorage(CONFIG_FILE, typeof(StealthSettings));
+ var writer = MyAPIGateway.Utilities.WriteFileInWorldStorage(CONFIG_FILE, typeof(StealthSettings));
+ var data = MyAPIGateway.Utilities.SerializeToXML(Config);
+ Write(writer, data);
+ }
+
+ private static void Write(TextWriter writer, string data)
+ {
+ writer.Write(data);
+ writer.Flush();
+ writer.Dispose();
+ }
+ }
+
+ [ProtoContract]
+ public class StealthSettings
+ {
+ [ProtoMember(1)] public int Version = -1;
+ [ProtoMember(2)] public int FadeTime = 210;
+ [ProtoMember(3)] public int ShieldDelay = 300;
+ [ProtoMember(4)] public int JumpPenalty = 180;
+
+ [ProtoMember(7)] public float Transparency = 0.9f;
+ [ProtoMember(8)] public bool DisableShields = true;
+ [ProtoMember(9)] public int DamageThreshold = 1000;
+ [ProtoMember(10)] public bool DisableWeapons = true;
+ [ProtoMember(11)] public bool HideThrusterFlames = true;
+ [ProtoMember(12)] public bool WorkInWater = true;
+ [ProtoMember(13)] public bool WorkOutOfWater = true;
+ [ProtoMember(14)] public float WaterTransitionDepth = 0f;
+ [ProtoMember(15)] public bool RevealOnDamage = true;
+
+ [ProtoMember(20)] public DriveSettings DriveConfig;
+ [ProtoMember(21)] public SinkSettings SinkConfig;
+
+ [ProtoMember(30)] public DriveSettings[] DriveConfigs;
+ [ProtoMember(31)] public SinkSettings[] SinkConfigs;
+
+ [ProtoContract]
+ public class DriveSettings
+ {
+ [ProtoMember(1)] public string Subtype;
+ [ProtoMember(2)] public int Duration = 1800;
+ [ProtoMember(3)] public float PowerScale = 0.02f;
+ [ProtoMember(4)] public float SignalRangeScale = 20f;
+
+ public DriveSettings()
+ {
+
+ }
+
+ public DriveSettings(DriveSettings old)
+ {
+ if (old == null) return;
+
+ Duration = old.Duration;
+ PowerScale = old.PowerScale;
+ SignalRangeScale = old.SignalRangeScale;
+ }
+ }
+
+ [ProtoContract]
+ public class SinkSettings
+ {
+ [ProtoMember(1)] public string Subtype;
+ [ProtoMember(2)] public int Duration = 600;
+ [ProtoMember(3)] public float Power = 10f;
+ [ProtoMember(4)] public bool DoDamage = true;
+
+ public SinkSettings()
+ {
+
+ }
+
+ public SinkSettings(SinkSettings old)
+ {
+ if (old == null) return;
+
+ Duration = old.Duration;
+ Power = old.Power;
+ DoDamage = old.DoDamage;
+ }
+ }
+
+ }
+}
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Models/AWE_Aegis/ARYX_TacticalModule.mwm b/Utility Mods/Stealth Drive - Starcore Edition/Models/AWE_Aegis/ARYX_TacticalModule.mwm
new file mode 100644
index 000000000..1eedbbca2
Binary files /dev/null and b/Utility Mods/Stealth Drive - Starcore Edition/Models/AWE_Aegis/ARYX_TacticalModule.mwm differ
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Models/AWE_Aegis/ARYX_TacticalModule.sbc b/Utility Mods/Stealth Drive - Starcore Edition/Models/AWE_Aegis/ARYX_TacticalModule.sbc
new file mode 100644
index 000000000..05c215b6b
--- /dev/null
+++ b/Utility Mods/Stealth Drive - Starcore Edition/Models/AWE_Aegis/ARYX_TacticalModule.sbc
@@ -0,0 +1,39 @@
+
+
+
+
+
+ CubeBlock
+ ARYX_TacticalModule
+
+ DisplayName_ARYX_TacticalModule
+ Description_ARYX_TacticalModule
+ Textures\GUI\Icons\AstronautBackpack.dds
+ Large
+ TriangleMesh
+
+
+ Models\AWE_Aegis\ARYX_TacticalModule.mwm
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ARYX_TacticalModule
+ Z
+ Y
+
+
+
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Models/AWE_Aegis/ARYX_TacticalModule_BS1.mwm b/Utility Mods/Stealth Drive - Starcore Edition/Models/AWE_Aegis/ARYX_TacticalModule_BS1.mwm
new file mode 100644
index 000000000..8eecf63c3
Binary files /dev/null and b/Utility Mods/Stealth Drive - Starcore Edition/Models/AWE_Aegis/ARYX_TacticalModule_BS1.mwm differ
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Models/AWE_Aegis/ARYX_TacticalModule_BS2.mwm b/Utility Mods/Stealth Drive - Starcore Edition/Models/AWE_Aegis/ARYX_TacticalModule_BS2.mwm
new file mode 100644
index 000000000..31a0d40a8
Binary files /dev/null and b/Utility Mods/Stealth Drive - Starcore Edition/Models/AWE_Aegis/ARYX_TacticalModule_BS2.mwm differ
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Models/AWE_Aegis/ARYX_TacticalModule_BS3.mwm b/Utility Mods/Stealth Drive - Starcore Edition/Models/AWE_Aegis/ARYX_TacticalModule_BS3.mwm
new file mode 100644
index 000000000..79b719a64
Binary files /dev/null and b/Utility Mods/Stealth Drive - Starcore Edition/Models/AWE_Aegis/ARYX_TacticalModule_BS3.mwm differ
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Models/AWE_Aegis/ARYX_TacticalModule_LOD1.mwm b/Utility Mods/Stealth Drive - Starcore Edition/Models/AWE_Aegis/ARYX_TacticalModule_LOD1.mwm
new file mode 100644
index 000000000..e5c9f5ccc
Binary files /dev/null and b/Utility Mods/Stealth Drive - Starcore Edition/Models/AWE_Aegis/ARYX_TacticalModule_LOD1.mwm differ
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Models/AWE_Aegis/ARYX_TacticalModule_LOD2.mwm b/Utility Mods/Stealth Drive - Starcore Edition/Models/AWE_Aegis/ARYX_TacticalModule_LOD2.mwm
new file mode 100644
index 000000000..a30694882
Binary files /dev/null and b/Utility Mods/Stealth Drive - Starcore Edition/Models/AWE_Aegis/ARYX_TacticalModule_LOD2.mwm differ
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Models/AWE_Aegis/ARYX_TacticalModule_LOD3.mwm b/Utility Mods/Stealth Drive - Starcore Edition/Models/AWE_Aegis/ARYX_TacticalModule_LOD3.mwm
new file mode 100644
index 000000000..eaea06c2f
Binary files /dev/null and b/Utility Mods/Stealth Drive - Starcore Edition/Models/AWE_Aegis/ARYX_TacticalModule_LOD3.mwm differ
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Models/Cubes/large/StealthDrive1x1.mwm b/Utility Mods/Stealth Drive - Starcore Edition/Models/Cubes/large/StealthDrive1x1.mwm
new file mode 100644
index 000000000..2cb1cc92e
Binary files /dev/null and b/Utility Mods/Stealth Drive - Starcore Edition/Models/Cubes/large/StealthDrive1x1.mwm differ
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Models/Cubes/large/StealthDrive1x1_BS1.mwm b/Utility Mods/Stealth Drive - Starcore Edition/Models/Cubes/large/StealthDrive1x1_BS1.mwm
new file mode 100644
index 000000000..a66247ddf
Binary files /dev/null and b/Utility Mods/Stealth Drive - Starcore Edition/Models/Cubes/large/StealthDrive1x1_BS1.mwm differ
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Models/Cubes/large/StealthDrive1x1_BS2.mwm b/Utility Mods/Stealth Drive - Starcore Edition/Models/Cubes/large/StealthDrive1x1_BS2.mwm
new file mode 100644
index 000000000..979bc9195
Binary files /dev/null and b/Utility Mods/Stealth Drive - Starcore Edition/Models/Cubes/large/StealthDrive1x1_BS2.mwm differ
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Models/Cubes/large/StealthDrive1x1_BS3.mwm b/Utility Mods/Stealth Drive - Starcore Edition/Models/Cubes/large/StealthDrive1x1_BS3.mwm
new file mode 100644
index 000000000..6e4dc5c38
Binary files /dev/null and b/Utility Mods/Stealth Drive - Starcore Edition/Models/Cubes/large/StealthDrive1x1_BS3.mwm differ
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Models/Cubes/large/StealthDrive1x1_LOD1.mwm b/Utility Mods/Stealth Drive - Starcore Edition/Models/Cubes/large/StealthDrive1x1_LOD1.mwm
new file mode 100644
index 000000000..b2dd01f47
Binary files /dev/null and b/Utility Mods/Stealth Drive - Starcore Edition/Models/Cubes/large/StealthDrive1x1_LOD1.mwm differ
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Models/Cubes/large/StealthDrive1x1_LOD2.mwm b/Utility Mods/Stealth Drive - Starcore Edition/Models/Cubes/large/StealthDrive1x1_LOD2.mwm
new file mode 100644
index 000000000..ac9627c70
Binary files /dev/null and b/Utility Mods/Stealth Drive - Starcore Edition/Models/Cubes/large/StealthDrive1x1_LOD2.mwm differ
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Models/Cubes/large/StealthDrive1x1_LOD3.mwm b/Utility Mods/Stealth Drive - Starcore Edition/Models/Cubes/large/StealthDrive1x1_LOD3.mwm
new file mode 100644
index 000000000..3f8405d5e
Binary files /dev/null and b/Utility Mods/Stealth Drive - Starcore Edition/Models/Cubes/large/StealthDrive1x1_LOD3.mwm differ
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Models/Cubes/large/StealthHeatSink.mwm b/Utility Mods/Stealth Drive - Starcore Edition/Models/Cubes/large/StealthHeatSink.mwm
new file mode 100644
index 000000000..68b8e0e8c
Binary files /dev/null and b/Utility Mods/Stealth Drive - Starcore Edition/Models/Cubes/large/StealthHeatSink.mwm differ
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Models/Cubes/large/StealthHeatSink_BS1.mwm b/Utility Mods/Stealth Drive - Starcore Edition/Models/Cubes/large/StealthHeatSink_BS1.mwm
new file mode 100644
index 000000000..6fe24ce16
Binary files /dev/null and b/Utility Mods/Stealth Drive - Starcore Edition/Models/Cubes/large/StealthHeatSink_BS1.mwm differ
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Models/Cubes/large/StealthHeatSink_BS2.mwm b/Utility Mods/Stealth Drive - Starcore Edition/Models/Cubes/large/StealthHeatSink_BS2.mwm
new file mode 100644
index 000000000..9ca488cb5
Binary files /dev/null and b/Utility Mods/Stealth Drive - Starcore Edition/Models/Cubes/large/StealthHeatSink_BS2.mwm differ
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Models/Cubes/large/StealthHeatSink_BS3.mwm b/Utility Mods/Stealth Drive - Starcore Edition/Models/Cubes/large/StealthHeatSink_BS3.mwm
new file mode 100644
index 000000000..af2334420
Binary files /dev/null and b/Utility Mods/Stealth Drive - Starcore Edition/Models/Cubes/large/StealthHeatSink_BS3.mwm differ
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Models/Cubes/small/StealthDriveSmall.mwm b/Utility Mods/Stealth Drive - Starcore Edition/Models/Cubes/small/StealthDriveSmall.mwm
new file mode 100644
index 000000000..b7b27e2b0
Binary files /dev/null and b/Utility Mods/Stealth Drive - Starcore Edition/Models/Cubes/small/StealthDriveSmall.mwm differ
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Models/Cubes/small/StealthDriveSmall_BS1.mwm b/Utility Mods/Stealth Drive - Starcore Edition/Models/Cubes/small/StealthDriveSmall_BS1.mwm
new file mode 100644
index 000000000..57b8a8f81
Binary files /dev/null and b/Utility Mods/Stealth Drive - Starcore Edition/Models/Cubes/small/StealthDriveSmall_BS1.mwm differ
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Models/Cubes/small/StealthDriveSmall_BS2.mwm b/Utility Mods/Stealth Drive - Starcore Edition/Models/Cubes/small/StealthDriveSmall_BS2.mwm
new file mode 100644
index 000000000..fe8e6f9eb
Binary files /dev/null and b/Utility Mods/Stealth Drive - Starcore Edition/Models/Cubes/small/StealthDriveSmall_BS2.mwm differ
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Models/Cubes/small/StealthDriveSmall_BS3.mwm b/Utility Mods/Stealth Drive - Starcore Edition/Models/Cubes/small/StealthDriveSmall_BS3.mwm
new file mode 100644
index 000000000..8ea3c46cc
Binary files /dev/null and b/Utility Mods/Stealth Drive - Starcore Edition/Models/Cubes/small/StealthDriveSmall_BS3.mwm differ
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Models/Cubes/small/StealthDriveSmall_LOD1.mwm b/Utility Mods/Stealth Drive - Starcore Edition/Models/Cubes/small/StealthDriveSmall_LOD1.mwm
new file mode 100644
index 000000000..fffcb4d7e
Binary files /dev/null and b/Utility Mods/Stealth Drive - Starcore Edition/Models/Cubes/small/StealthDriveSmall_LOD1.mwm differ
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Models/Cubes/small/StealthDriveSmall_LOD2.mwm b/Utility Mods/Stealth Drive - Starcore Edition/Models/Cubes/small/StealthDriveSmall_LOD2.mwm
new file mode 100644
index 000000000..ef7ef5c6f
Binary files /dev/null and b/Utility Mods/Stealth Drive - Starcore Edition/Models/Cubes/small/StealthDriveSmall_LOD2.mwm differ
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Models/Cubes/small/StealthDriveSmall_LOD3.mwm b/Utility Mods/Stealth Drive - Starcore Edition/Models/Cubes/small/StealthDriveSmall_LOD3.mwm
new file mode 100644
index 000000000..07f290bf7
Binary files /dev/null and b/Utility Mods/Stealth Drive - Starcore Edition/Models/Cubes/small/StealthDriveSmall_LOD3.mwm differ
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Models/Cubes/small/StealthHeatSinkSmall.mwm b/Utility Mods/Stealth Drive - Starcore Edition/Models/Cubes/small/StealthHeatSinkSmall.mwm
new file mode 100644
index 000000000..3ef3c0f0f
Binary files /dev/null and b/Utility Mods/Stealth Drive - Starcore Edition/Models/Cubes/small/StealthHeatSinkSmall.mwm differ
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Models/Cubes/small/StealthHeatSinkSmall_BS1.mwm b/Utility Mods/Stealth Drive - Starcore Edition/Models/Cubes/small/StealthHeatSinkSmall_BS1.mwm
new file mode 100644
index 000000000..11f63b4a8
Binary files /dev/null and b/Utility Mods/Stealth Drive - Starcore Edition/Models/Cubes/small/StealthHeatSinkSmall_BS1.mwm differ
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Models/Cubes/small/StealthHeatSinkSmall_BS2.mwm b/Utility Mods/Stealth Drive - Starcore Edition/Models/Cubes/small/StealthHeatSinkSmall_BS2.mwm
new file mode 100644
index 000000000..ac6305c84
Binary files /dev/null and b/Utility Mods/Stealth Drive - Starcore Edition/Models/Cubes/small/StealthHeatSinkSmall_BS2.mwm differ
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Models/Cubes/small/StealthHeatSinkSmall_BS3.mwm b/Utility Mods/Stealth Drive - Starcore Edition/Models/Cubes/small/StealthHeatSinkSmall_BS3.mwm
new file mode 100644
index 000000000..114d95c45
Binary files /dev/null and b/Utility Mods/Stealth Drive - Starcore Edition/Models/Cubes/small/StealthHeatSinkSmall_BS3.mwm differ
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Textures/GUI/Icons/Actions/StealthSwitchOff.dds b/Utility Mods/Stealth Drive - Starcore Edition/Textures/GUI/Icons/Actions/StealthSwitchOff.dds
new file mode 100644
index 000000000..f271e6c87
Binary files /dev/null and b/Utility Mods/Stealth Drive - Starcore Edition/Textures/GUI/Icons/Actions/StealthSwitchOff.dds differ
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Textures/GUI/Icons/Actions/StealthSwitchOff.png b/Utility Mods/Stealth Drive - Starcore Edition/Textures/GUI/Icons/Actions/StealthSwitchOff.png
new file mode 100644
index 000000000..5e812623a
Binary files /dev/null and b/Utility Mods/Stealth Drive - Starcore Edition/Textures/GUI/Icons/Actions/StealthSwitchOff.png differ
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Textures/GUI/Icons/Actions/StealthSwitchOn.dds b/Utility Mods/Stealth Drive - Starcore Edition/Textures/GUI/Icons/Actions/StealthSwitchOn.dds
new file mode 100644
index 000000000..c10376a55
Binary files /dev/null and b/Utility Mods/Stealth Drive - Starcore Edition/Textures/GUI/Icons/Actions/StealthSwitchOn.dds differ
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Textures/GUI/Icons/Actions/StealthSwitchOn.png b/Utility Mods/Stealth Drive - Starcore Edition/Textures/GUI/Icons/Actions/StealthSwitchOn.png
new file mode 100644
index 000000000..8fdd4ae60
Binary files /dev/null and b/Utility Mods/Stealth Drive - Starcore Edition/Textures/GUI/Icons/Actions/StealthSwitchOn.png differ
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Textures/GUI/Icons/Actions/StealthSwitchOn.xcf b/Utility Mods/Stealth Drive - Starcore Edition/Textures/GUI/Icons/Actions/StealthSwitchOn.xcf
new file mode 100644
index 000000000..5eb1221e9
Binary files /dev/null and b/Utility Mods/Stealth Drive - Starcore Edition/Textures/GUI/Icons/Actions/StealthSwitchOn.xcf differ
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Textures/GUI/Icons/Actions/StealthSwitchToggle.dds b/Utility Mods/Stealth Drive - Starcore Edition/Textures/GUI/Icons/Actions/StealthSwitchToggle.dds
new file mode 100644
index 000000000..3d36a45aa
Binary files /dev/null and b/Utility Mods/Stealth Drive - Starcore Edition/Textures/GUI/Icons/Actions/StealthSwitchToggle.dds differ
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Textures/GUI/Icons/Actions/StealthSwitchToggle.png b/Utility Mods/Stealth Drive - Starcore Edition/Textures/GUI/Icons/Actions/StealthSwitchToggle.png
new file mode 100644
index 000000000..2a3e826ff
Binary files /dev/null and b/Utility Mods/Stealth Drive - Starcore Edition/Textures/GUI/Icons/Actions/StealthSwitchToggle.png differ
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Textures/GUI/Icons/Cubes/Aryx_AWE_TacticalModule.dds b/Utility Mods/Stealth Drive - Starcore Edition/Textures/GUI/Icons/Cubes/Aryx_AWE_TacticalModule.dds
new file mode 100644
index 000000000..91236f0c1
Binary files /dev/null and b/Utility Mods/Stealth Drive - Starcore Edition/Textures/GUI/Icons/Cubes/Aryx_AWE_TacticalModule.dds differ
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Textures/Models/Cubes/Atlas3_Colorable_add.dds b/Utility Mods/Stealth Drive - Starcore Edition/Textures/Models/Cubes/Atlas3_Colorable_add.dds
new file mode 100644
index 000000000..a573fbbea
Binary files /dev/null and b/Utility Mods/Stealth Drive - Starcore Edition/Textures/Models/Cubes/Atlas3_Colorable_add.dds differ
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Textures/Models/Cubes/Atlas3_Colorable_cm.dds b/Utility Mods/Stealth Drive - Starcore Edition/Textures/Models/Cubes/Atlas3_Colorable_cm.dds
new file mode 100644
index 000000000..d89e64fe4
Binary files /dev/null and b/Utility Mods/Stealth Drive - Starcore Edition/Textures/Models/Cubes/Atlas3_Colorable_cm.dds differ
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Textures/Models/Cubes/Atlas3_Colorable_ng.dds b/Utility Mods/Stealth Drive - Starcore Edition/Textures/Models/Cubes/Atlas3_Colorable_ng.dds
new file mode 100644
index 000000000..0f7243303
Binary files /dev/null and b/Utility Mods/Stealth Drive - Starcore Edition/Textures/Models/Cubes/Atlas3_Colorable_ng.dds differ
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Textures/Models/Cubes/Atlas3_alphamask.dds b/Utility Mods/Stealth Drive - Starcore Edition/Textures/Models/Cubes/Atlas3_alphamask.dds
new file mode 100644
index 000000000..277f2c57b
Binary files /dev/null and b/Utility Mods/Stealth Drive - Starcore Edition/Textures/Models/Cubes/Atlas3_alphamask.dds differ
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Textures/Models/Cubes/Atlas4_Colorable_add.dds b/Utility Mods/Stealth Drive - Starcore Edition/Textures/Models/Cubes/Atlas4_Colorable_add.dds
new file mode 100644
index 000000000..f6b1c3209
Binary files /dev/null and b/Utility Mods/Stealth Drive - Starcore Edition/Textures/Models/Cubes/Atlas4_Colorable_add.dds differ
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Textures/Models/Cubes/Atlas4_Colorable_cm.dds b/Utility Mods/Stealth Drive - Starcore Edition/Textures/Models/Cubes/Atlas4_Colorable_cm.dds
new file mode 100644
index 000000000..2dd7709ed
Binary files /dev/null and b/Utility Mods/Stealth Drive - Starcore Edition/Textures/Models/Cubes/Atlas4_Colorable_cm.dds differ
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Textures/Models/Cubes/Atlas4_Colorable_ng.dds b/Utility Mods/Stealth Drive - Starcore Edition/Textures/Models/Cubes/Atlas4_Colorable_ng.dds
new file mode 100644
index 000000000..48a0daab1
Binary files /dev/null and b/Utility Mods/Stealth Drive - Starcore Edition/Textures/Models/Cubes/Atlas4_Colorable_ng.dds differ
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Textures/Models/Cubes/Atlas4_alphamask.dds b/Utility Mods/Stealth Drive - Starcore Edition/Textures/Models/Cubes/Atlas4_alphamask.dds
new file mode 100644
index 000000000..d893de672
Binary files /dev/null and b/Utility Mods/Stealth Drive - Starcore Edition/Textures/Models/Cubes/Atlas4_alphamask.dds differ
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Textures/Models/Cubes/Copper1_add.dds b/Utility Mods/Stealth Drive - Starcore Edition/Textures/Models/Cubes/Copper1_add.dds
new file mode 100644
index 000000000..f5eeac05f
Binary files /dev/null and b/Utility Mods/Stealth Drive - Starcore Edition/Textures/Models/Cubes/Copper1_add.dds differ
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Textures/Models/Cubes/Copper1_cm.dds b/Utility Mods/Stealth Drive - Starcore Edition/Textures/Models/Cubes/Copper1_cm.dds
new file mode 100644
index 000000000..2fc8f1943
Binary files /dev/null and b/Utility Mods/Stealth Drive - Starcore Edition/Textures/Models/Cubes/Copper1_cm.dds differ
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Textures/Models/Cubes/Copper1_ng.dds b/Utility Mods/Stealth Drive - Starcore Edition/Textures/Models/Cubes/Copper1_ng.dds
new file mode 100644
index 000000000..fcabe892d
Binary files /dev/null and b/Utility Mods/Stealth Drive - Starcore Edition/Textures/Models/Cubes/Copper1_ng.dds differ
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Textures/Models/Cubes/PaintedMetalColorable_add.dds b/Utility Mods/Stealth Drive - Starcore Edition/Textures/Models/Cubes/PaintedMetalColorable_add.dds
new file mode 100644
index 000000000..fb2f2a0bb
Binary files /dev/null and b/Utility Mods/Stealth Drive - Starcore Edition/Textures/Models/Cubes/PaintedMetalColorable_add.dds differ
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Textures/Models/Cubes/PaintedMetalColorable_cm.dds b/Utility Mods/Stealth Drive - Starcore Edition/Textures/Models/Cubes/PaintedMetalColorable_cm.dds
new file mode 100644
index 000000000..0cd4ec850
Binary files /dev/null and b/Utility Mods/Stealth Drive - Starcore Edition/Textures/Models/Cubes/PaintedMetalColorable_cm.dds differ
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/Textures/Models/Cubes/PaintedMetalColorable_ng.dds b/Utility Mods/Stealth Drive - Starcore Edition/Textures/Models/Cubes/PaintedMetalColorable_ng.dds
new file mode 100644
index 000000000..9f6ea4a4e
Binary files /dev/null and b/Utility Mods/Stealth Drive - Starcore Edition/Textures/Models/Cubes/PaintedMetalColorable_ng.dds differ
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/metadata.mod b/Utility Mods/Stealth Drive - Starcore Edition/metadata.mod
new file mode 100644
index 000000000..83173346a
--- /dev/null
+++ b/Utility Mods/Stealth Drive - Starcore Edition/metadata.mod
@@ -0,0 +1,4 @@
+
+
+ 1.0
+
\ No newline at end of file
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/modinfo.sbmi b/Utility Mods/Stealth Drive - Starcore Edition/modinfo.sbmi
new file mode 100644
index 000000000..ad81a71c6
--- /dev/null
+++ b/Utility Mods/Stealth Drive - Starcore Edition/modinfo.sbmi
@@ -0,0 +1,11 @@
+
+
+ 76561198049738491
+ 0
+
+
+ 3436896074
+ Steam
+
+
+
\ No newline at end of file
diff --git a/Utility Mods/Stealth Drive - Starcore Edition/thumb.png b/Utility Mods/Stealth Drive - Starcore Edition/thumb.png
new file mode 100644
index 000000000..3a3e809ab
Binary files /dev/null and b/Utility Mods/Stealth Drive - Starcore Edition/thumb.png differ