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