diff --git a/Slipspace Engine/Audio/SC/GlideEntering.xwm b/Slipspace Engine/Audio/SC/GlideEntering.xwm new file mode 100644 index 00000000..d66954d2 Binary files /dev/null and b/Slipspace Engine/Audio/SC/GlideEntering.xwm differ diff --git a/Slipspace Engine/Audio/SC/GlideLeaving.xwm b/Slipspace Engine/Audio/SC/GlideLeaving.xwm new file mode 100644 index 00000000..7d5720b4 Binary files /dev/null and b/Slipspace Engine/Audio/SC/GlideLeaving.xwm differ diff --git a/Slipspace Engine/Audio/SC/SuperCruiseGravity.xwm b/Slipspace Engine/Audio/SC/SuperCruiseGravity.xwm new file mode 100644 index 00000000..68a6f242 Binary files /dev/null and b/Slipspace Engine/Audio/SC/SuperCruiseGravity.xwm differ diff --git a/Slipspace Engine/Audio/SC/charging_Fsd.xwm b/Slipspace Engine/Audio/SC/charging_Fsd.xwm new file mode 100644 index 00000000..b1e095ae Binary files /dev/null and b/Slipspace Engine/Audio/SC/charging_Fsd.xwm differ diff --git a/Slipspace Engine/Audio/SC/jump_in_supercruise.xwm b/Slipspace Engine/Audio/SC/jump_in_supercruise.xwm new file mode 100644 index 00000000..ead08a3e Binary files /dev/null and b/Slipspace Engine/Audio/SC/jump_in_supercruise.xwm differ diff --git a/Slipspace Engine/Audio/SC/jump_out_supercruise.xwm b/Slipspace Engine/Audio/SC/jump_out_supercruise.xwm new file mode 100644 index 00000000..6827a0dc Binary files /dev/null and b/Slipspace Engine/Audio/SC/jump_out_supercruise.xwm differ diff --git a/Slipspace Engine/Data/Audio.sbc b/Slipspace Engine/Data/Audio.sbc new file mode 100644 index 00000000..d801632b --- /dev/null +++ b/Slipspace Engine/Data/Audio.sbc @@ -0,0 +1,110 @@ + + + + + + MyObjectBuilder_AudioDefinition + quantum_charging + + SHIP + 1.7 + 1000 + false + + + Audio\SC\charging_Fsd.xwm + + + + + + MyObjectBuilder_AudioDefinition + glide_off + + SHIP + 1.7 + 5000 + false + + + Audio\SC\GlideLeaving.xwm + + + + + + MyObjectBuilder_AudioDefinition + glide_on + + SHIP + 1.7 + 5000 + false + + + Audio\SC\GlideEntering.xwm + + + + + + MyObjectBuilder_AudioDefinition + quantum_charging + + SHIP + 1.7 + 1000 + false + + + Audio\SC\charging_Fsd.xwm + + + + + + MyObjectBuilder_AudioDefinition + quantum_jumpout + + SHIP + 1.7 + 10000 + false + + + Audio\SC\jump_out_supercruise.xwm + + + + + + MyObjectBuilder_AudioDefinition + quantum_jumpin + + SHIP + 1.7 + 30000 + false + + + Audio\SC\jump_in_supercruise.xwm + + + + + + MyObjectBuilder_AudioDefinition + SuperCruiseGravity + + SHIP + 1.7 + 10000 + false + + + Audio\SC\SuperCruiseGravity.xwm + + + + + \ No newline at end of file diff --git a/Slipspace Engine/Data/BlockCategories.sbc b/Slipspace Engine/Data/BlockCategories.sbc new file mode 100644 index 00000000..6a839951 --- /dev/null +++ b/Slipspace Engine/Data/BlockCategories.sbc @@ -0,0 +1,29 @@ + + + + + + GuiBlockCategoryDefinition + + + DisplayName_Category_LargeBlocks + LargeBlocks + true + + UpgradeModule/FSDriveLarge + + + + + GuiBlockCategoryDefinition + + + DisplayName_Category_SmallBlocks + SmallBlocks + true + + UpgradeModule/FSDriveSmall + + + + \ No newline at end of file diff --git a/Slipspace Engine/Data/BlockVariantGroups.sbc b/Slipspace Engine/Data/BlockVariantGroups.sbc new file mode 100644 index 00000000..f784f446 --- /dev/null +++ b/Slipspace Engine/Data/BlockVariantGroups.sbc @@ -0,0 +1,15 @@ + + + + + + Textures\GUI\Icons\QDXL.dds + S7 Frameshift Drive + Friendship drive charging! + + + + + + + diff --git a/Slipspace Engine/Data/BlueprintClasses.sbc b/Slipspace Engine/Data/BlueprintClasses.sbc new file mode 100644 index 00000000..c7a8e8e2 --- /dev/null +++ b/Slipspace Engine/Data/BlueprintClasses.sbc @@ -0,0 +1,7 @@ + + + + + + + diff --git a/Slipspace Engine/Data/CubeBlocks.sbc b/Slipspace Engine/Data/CubeBlocks.sbc new file mode 100644 index 00000000..5ef6e0fe --- /dev/null +++ b/Slipspace Engine/Data/CubeBlocks.sbc @@ -0,0 +1,106 @@ + + + + + + UpgradeModule + FSDriveSmall + + true + D5 Frameshift Drive + Textures\GUI\Icons\QDVK.dds + Compact version of S7 + Small + TriangleMesh + + + Models\Cubes\Small\MiniJumpDrive.mwm + + + + + + + + + + + + + + + + + + + + + + + + + + FSDrive + Z + Y + Light + + Utility + 32 + 214 + 250 + true + + + + + UpgradeModule + FSDriveLarge + + true + S7 Frameshift Drive + Textures\GUI\Icons\QDXL.dds + Friendship drive charging! + Large + TriangleMesh + + + Models\Cubes\Large\JumpDrive.mwm + + + + + + + + + + + + + + + + + + + + + + + + + + FSDrive + Z + Y + Light + + Utility + 44 + 214 + 250 + true + + + diff --git a/Slipspace Engine/Data/Particles.sbc b/Slipspace Engine/Data/Particles.sbc new file mode 100644 index 00000000..cfa9a959 --- /dev/null +++ b/Slipspace Engine/Data/Particles.sbc @@ -0,0 +1,1649 @@ + + + + + + ParticleEffect + BlinkDriveTrail + + 0 + 74593 + 10 + 1 + 0 + 500 + true + + + GPU + + + + 32 + 16 + 0 + + + + 496 + + + 8 + + + + + + + + + + + 1 + 1 + 1 + 1 + + + + + + 1 + 1 + 1 + 1 + + + + + + 1 + 1 + 1 + 1 + + + + + + 1 + 1 + 1 + 1 + + + + + + + + + + + + + + + + 500 + + + + 400 + + + + 200 + + + + 100 + + + + + + + + 0.5 + + + + + + + 0 + 0 + 0 + + + + + + + + + 0 + + + + + + 0 + 1 + 0 + + + + + + + 0 + + + + + + + + 0 + + + + + + + + 0 + + + + + + + + 0 + + + + + + 0 + 0 + 0 + + + + + + + + + + + 0 + + + + 0 + + + + 0 + + + + 0 + + + + + + + + 0 + + + + + + + + + + 1 + + + + 0.85714285714 + + + + 1 + + + + 1 + + + + + + + + + 16384 + + + 2 + + + 5 + + + 0.02 + + + true + + + + + + Atlas_D_01 + + + 1 + + + false + + + false + + + false + + + false + + + 1 + + + 0 + + + + 0 + 0.0418 + 0.012 + + + + 0 + + + 0.05 + + + false + + + 1 + + + 0 + + + true + + + 1 + + + + 0 + 0 + 0 + + + + + 0 + 0 + 0 + + + + + + + + + + + 2.6 + + + + 2.6 + + + + 2.6 + + + + 2.6 + + + + + + + + + + + 1 + + + + + 0 + + + + + + + + + + 0 + + + + 0 + + + + 0 + + + + 0 + + + + + + + + 1 + + + false + + + true + + + 1 + + + 0 + + + 1 + + + 0 + + + 0 + + + + + GPU + + + + 32 + 16 + 0 + + + + 496 + + + 8 + + + + + + + + + + + 1 + 1 + 1 + 1 + + + + + + 1 + 1 + 1 + 1 + + + + + + 1 + 1 + 1 + 1 + + + + + + 1 + 1 + 1 + 1 + + + + + + + + + + + + + + + + 500 + + + + 400 + + + + 200 + + + + 100 + + + + + + + + 0.5 + + + + + + + 0 + 0 + 0 + + + + + + + + + 0 + + + + + + 0 + 1 + 0 + + + + + + + 0 + + + + + + + + 0 + + + + + + + + 0 + + + + + + + + 0 + + + + + + 0 + 0 + 0 + + + + + + + + + + + 0 + + + + 0 + + + + 0 + + + + 0 + + + + + + + + 0 + + + + + + + + + + 1 + + + + 0.85714285714 + + + + 1 + + + + 1 + + + + + + + + 16384 + + + 2 + + + 5 + + + 0.02 + + + true + + + + + + Atlas_D_01 + + + 1 + + + false + + + false + + + false + + + false + + + 1 + + + 0 + + + + 0 + 0.0418 + 0.012 + + + + 0 + + + 0.05 + + + false + + + 1 + + + 0 + + + true + + + 1 + + + + 0 + 90 + 0 + + + + + 0 + 0 + 0 + + + + + + + + + + + 2.6 + + + + 2.6 + + + + 2.6 + + + + 2.6 + + + + + + + + + + + 1 + + + + + 0 + + + + + + + + + + 0 + + + + 0 + + + + 0 + + + + 0 + + + + + + + + 1 + + + false + + + true + + + 1 + + + 0 + + + 1 + + + 0 + + + 0 + + + + + GPU + + + + 32 + 16 + 0 + + + + 496 + + + 8 + + + + + + + + + + + 1 + 1 + 1 + 1 + + + + + + 1 + 1 + 1 + 1 + + + + + + 1 + 1 + 1 + 1 + + + + + + 1 + 1 + 1 + 1 + + + + + + + + + + + + + + + + 500 + + + + 400 + + + + 200 + + + + 100 + + + + + + + + 0.5 + + + + + + + 0 + 0 + 0 + + + + + + + + + 0 + + + + + + 0 + 1 + 0 + + + + + + + 0 + + + + + + + + 0 + + + + + + + + 0 + + + + + + + + 0 + + + + + + 0 + 0 + 0 + + + + + + + + + + + 0 + + + + 0 + + + + 0 + + + + 0 + + + + + + + + 0 + + + + + + + + + + 1 + + + + 0.85714285714 + + + + 1 + + + + 1 + + + + + + + + 16384 + + + 2 + + + 5 + + + 0.02 + + + true + + + + + + Atlas_D_01 + + + 1 + + + false + + + false + + + false + + + false + + + 1 + + + 0 + + + + 0 + 0.0418 + 0.012 + + + + 0 + + + 0.05 + + + false + + + 1 + + + 0 + + + true + + + 1 + + + + 0 + 45 + 0 + + + + + 0 + 0 + 0 + + + + + + + + + + + 2.6 + + + + 2.6 + + + + 2.6 + + + + 2.6 + + + + + + + + + + + 1 + + + + + 0 + + + + + + + + + + 0 + + + + 0 + + + + 0 + + + + 0 + + + + + + + + 1 + + + false + + + true + + + 1 + + + 0 + + + 1 + + + 0 + + + 0 + + + + + GPU + + + + 32 + 16 + 0 + + + + 496 + + + 8 + + + + + + + + + + + 1 + 1 + 1 + 1 + + + + + + 1 + 1 + 1 + 1 + + + + + + 1 + 1 + 1 + 1 + + + + + + 1 + 1 + 1 + 1 + + + + + + + + + + + + + + + + 500 + + + + 400 + + + + 200 + + + + 100 + + + + + + + + 0.5 + + + + + + + 0 + 0 + 0 + + + + + + + + + 0 + + + + + + 0 + 1 + 0 + + + + + + + 0 + + + + + + + + 0 + + + + + + + + 0 + + + + + + + + 0 + + + + + + 0 + 0 + 0 + + + + + + + + + + + 0 + + + + 0 + + + + 0 + + + + 0 + + + + + + + + 0 + + + + + + + + + + 1 + + + + 0.85714285714 + + + + 1 + + + + 1 + + + + + + + + 1 + + + 2 + + + 5 + + + 0.02 + + + true + + + + + + Atlas_D_01 + + + 1 + + + false + + + false + + + false + + + false + + + 1 + + + 0 + + + + 0 + 0.0418 + 0.012 + + + + 0 + + + 0.05 + + + false + + + 1 + + + 0 + + + true + + + 1 + + + + 0 + 135 + 0 + + + + + 0 + 0 + 0 + + + + + + + + + + + 2.6 + + + + 2.6 + + + + 2.6 + + + + 2.6 + + + + + + + + + + + 1 + + + + + 0 + + + + + + + + + + 0 + + + + 0 + + + + 0 + + + + 0 + + + + + + + + 1 + + + false + + + true + + + 1 + + + 0 + + + 1 + + + 0 + + + 0 + + + + + + + + + + + + + + + + + + + 1 + 1 + 1 + 1 + + + + + + + + + + + + 16 + + + + + + + + + + + 3 + + + + + + + + true + + + 0 + + + 0.2 + + + 0.1 + + + + + + + + + \ No newline at end of file diff --git a/Slipspace Engine/Data/Scripts/WarpDrive/GridSystem.cs b/Slipspace Engine/Data/Scripts/WarpDrive/GridSystem.cs new file mode 100644 index 00000000..a06f2cb3 --- /dev/null +++ b/Slipspace Engine/Data/Scripts/WarpDrive/GridSystem.cs @@ -0,0 +1,425 @@ +using Sandbox.Game.Entities; +using Sandbox.ModAPI; +using System; +using System.Collections.Generic; +using VRage.Game.ModAPI; +using VRage.ModAPI; +using VRage.Utils; +using VRageMath; + +namespace WarpDriveMod +{ + public class GridSystem : IEquatable + { + /// + /// True if at least 1 of the grids in the system is static. + /// + public bool IsStatic => staticCount > 0; + public bool Valid => IsValid(); + public long InvalidOn { get; private set; } + public Dictionary BlockCounters { get; private set; } = new Dictionary(); + public IReadOnlyCollection Grids => grids; + public int Id { get; private set; } + public MyCubeGrid MainGrid + { + get + { + foreach (var grid in grids) + { + if (grid == null) + continue; + + return grid; + } + + return null; + } + } + + private int staticCount; + public Dictionary> cockpits = new Dictionary>(); + private readonly SortedSet grids = new SortedSet(new GridByCount()); + private bool _valid = true; + + /// + /// Called when a grid no longer belongs to this grid system. + /// + public event Action OnSystemInvalidated; + + public GridSystem(MyCubeGrid firstGrid) + { + if (firstGrid == null) + throw new NullReferenceException("Attempt to create a grid using a null grid."); + + Id = WarpDriveSession.Instance.Rand.Next(int.MinValue, int.MaxValue); + if (firstGrid.MarkedForClose) + return; + + List connectedGrids = new List(); + MyAPIGateway.GridGroups.GetGroup(firstGrid, GridLinkTypeEnum.Logical, connectedGrids); + + foreach (IMyCubeGrid grid in connectedGrids) + { + if (!Add((MyCubeGrid)grid)) + throw new ArgumentException($"Invalid add state with {firstGrid.EntityId} and {grid.EntityId}"); + } + } + + public bool Contains(MyCubeGrid grid) + { + return grids.Contains(grid); + } + + private bool Add(MyCubeGrid grid) + { + if (grid == null) + throw new NullReferenceException("Attempt to add a null grid."); + + if (!grids.Add(grid)) + throw new ArgumentException("Grid already exists."); + + if (grid.IsStatic) + staticCount++; + + grid.OnBlockAdded += Grid_OnBlockAdded; + grid.OnBlockRemoved += Grid_OnBlockRemoved; + grid.OnStaticChanged += Grid_OnIsStaticChanged; + grid.OnClose += Grid_OnClose; + grid.OnGridSplit += Grid_OnGridSplit; + + foreach (MyCubeBlock s in grid.GetFatBlocks()) + { + Grid_OnBlockAdded(s.SlimBlock); + } + + return true; + } + + public void AddCounter(string key, BlockCounter counter) + { + foreach (MyCubeGrid grid in grids) + { + foreach (MyCubeBlock block in grid.GetFatBlocks()) + { + counter.TryAddCount(block); + } + } + BlockCounters[key] = counter; + } + + private void Grid_OnBlockRemoved(IMySlimBlock obj) + { + MyCubeGrid grid = (MyCubeGrid)obj.CubeGrid; + IMyCubeBlock fat = obj.FatBlock; + if (fat == null || grid == null) + return; + + foreach (BlockCounter counter in BlockCounters.Values) + { + counter.TryRemoveCount(fat); + } + + if (IsShipController(fat)) + { + HashSet gridCockpits; + if (cockpits.TryGetValue(grid, out gridCockpits)) + { + gridCockpits.Remove((IMyShipController)fat); + cockpits[grid] = gridCockpits; + } + } + + Resort(grid); + } + + private void Grid_OnBlockAdded(IMySlimBlock obj) + { + MyCubeGrid grid = (MyCubeGrid)obj.CubeGrid; + IMyCubeBlock fat = obj.FatBlock; + if (fat == null || grid == null) + return; + + foreach (BlockCounter counter in BlockCounters.Values) + { + counter.TryAddCount(fat); + } + + if (IsShipController(fat)) + { + HashSet gridCockpits; + if (!cockpits.TryGetValue(grid, out gridCockpits)) + { + gridCockpits = new HashSet + { + (IMyShipController)fat + }; + cockpits[grid] = gridCockpits; + } + else + gridCockpits.Add((IMyShipController)fat); + } + Resort(grid); + } + + public void Resort(MyCubeGrid grid) + { + if (grids.Remove(grid)) + grids.Add(grid); + } + + private void Grid_OnClose(IMyEntity obj) + { + Invalidate(); + } + + private void Grid_OnGridSplit(MyCubeGrid arg1, MyCubeGrid arg2) + { + Invalidate(); + } + + public bool IsValid() + { + if (!_valid || InvalidOn == WarpDriveSession.Instance.Runtime) + return _valid; + + // Update the state of the Valid bool + if (grids.Count > 0 && grids.Count == 1) + { + InvalidOn = WarpDriveSession.Instance.Runtime; + return true; + } + else + { + if (grids.Count > 1 && MainGrid != null) + { + var realCountList = new List(); + MyAPIGateway.GridGroups.GetGroup(MainGrid, GridLinkTypeEnum.Logical, realCountList); + if (grids.Count == realCountList.Count) + { + InvalidOn = WarpDriveSession.Instance.Runtime; + return true; + } + else + { + Invalidate(); + return false; + } + } + + Invalidate(); + return false; + } + } + + public void Invalidate() + { + _valid = false; + OnSystemInvalidated?.Invoke(this); + OnSystemInvalidated = null; + foreach (BlockCounter counter in BlockCounters.Values) + { + counter.Dispose(); + } + + foreach (MyCubeGrid grid in grids) + { + grid.OnBlockAdded -= Grid_OnBlockAdded; + grid.OnBlockRemoved -= Grid_OnBlockRemoved; + grid.OnStaticChanged -= Grid_OnIsStaticChanged; + grid.OnClose -= Grid_OnClose; + grid.OnGridSplit -= Grid_OnGridSplit; + } + } + + private void Grid_OnIsStaticChanged(MyCubeGrid arg1, bool arg2) + { + if (arg1.IsStatic) + staticCount++; + else + staticCount--; + } + + #region WorldMatrix + + public bool IsShipController(IMyCubeBlock block) + { + if (block == null || !(block is IMyTerminalBlock) || block is IMyCryoChamber) + return false; + + return (block as IMyShipController)?.CanControlShip == true; + } + + private bool IsLiveShipController(IMyCubeBlock block) + { + if (block == null || !(block is IMyTerminalBlock) || block is IMyCryoChamber) + return false; + + if ((block as IMyShipController)?.CanControlShip == true) + { + if ((block as IMyShipController).IsUnderControl) + return true; + } + + return false; + } + + private IMyShipController FindMainCockpit() + { + if (grids.Count == 0) + return null; + + // Loop through all grids starting at largest until an in use one is found + foreach (MyCubeGrid grid in grids) + { + // Use the main cockpit if it exists + IMyTerminalBlock block = grid.MainCockpit; + if (block != null && IsLiveShipController(block)) + return (IMyShipController)block; + + HashSet controlledgridCockpits = new HashSet(); + if (cockpits.TryGetValue(grid, out controlledgridCockpits)) + { + foreach (IMyShipController cockpit in controlledgridCockpits) + { + if (cockpit.IsUnderControl) + return cockpit; + } + } + } + + // No in use cockpit was found. + if (MainGrid == null) + return null; + + HashSet gridCockpits; + if (cockpits.TryGetValue(MainGrid, out gridCockpits)) + return gridCockpits.FirstElement(); + + return null; + } + + public MatrixD FindWorldMatrix() + { + if (grids.Count == 0 || MainGrid == null) + return Matrix.Zero; + + IMyShipController cockpit = FindMainCockpit(); + if (cockpit != null) + { + MatrixD result = cockpit.WorldMatrix; + result.Translation = MainGrid.WorldMatrix.Translation; + return result; + } + return MainGrid.WorldMatrix; + } + + public MatrixD FindBlockWorldMatrix(IMyCubeGrid Grid) + { + var MyGrid = Grid as MyCubeGrid; + IMyTerminalBlock block = MyGrid.MainCockpit; + + // Use the main cockpit if it exists + if (block != null && block is IMyTerminalBlock && !(block is IMyCryoChamber)) + { + if ((block as IMyShipController)?.CanControlShip == true) + { + if ((block as IMyShipController).IsUnderControl) + { + IMyShipController maincockpit = (IMyShipController)block; + MatrixD result = maincockpit.WorldMatrix; + result.Translation = Grid.WorldMatrix.Translation; + return result; + } + } + } + + HashSet controlledgridCockpits; + if (cockpits.TryGetValue(MyGrid, out controlledgridCockpits)) + { + foreach (IMyShipController cockpit in controlledgridCockpits) + { + if (cockpit.IsUnderControl) + { + MatrixD result = cockpit.WorldMatrix; + result.Translation = Grid.WorldMatrix.Translation; + return result; + } + } + } + + // No in use cockpit was found. + HashSet gridCockpits; + if (cockpits.TryGetValue(MyGrid, out gridCockpits)) + { + MatrixD result = gridCockpits.FirstElement().WorldMatrix; + result.Translation = Grid.WorldMatrix.Translation; + return result; + } + + return MyGrid.WorldMatrix; + } + + #endregion + public override bool Equals(object obj) + { + return Equals(obj as GridSystem); + } + + public bool Equals(GridSystem other) + { + return other != null && Id == other.Id; + } + + public override int GetHashCode() + { + return 2108858624 + Id.GetHashCode(); + } + + public class BlockCounter + { + public int Count { get; private set; } + public event Action OnBlockAdded; + public event Action OnBlockRemoved; + private readonly Func method; + + public BlockCounter(Func method) + { + this.method = method; + } + + public void TryAddCount(IMyCubeBlock block) + { + if (method.Invoke(block)) + { + Count++; + OnBlockAdded?.Invoke(block); + } + } + public void TryRemoveCount(IMyCubeBlock block) + { + if (method.Invoke(block)) + { + Count--; + OnBlockRemoved?.Invoke(block); + } + } + + public void Dispose() + { + OnBlockAdded = null; + OnBlockRemoved = null; + } + } + + private class GridByCount : IComparer + { + public int Compare(MyCubeGrid x, MyCubeGrid y) + { + int result1 = y.BlocksCount.CompareTo(x.BlocksCount); + if (result1 == 0) + return x.EntityId.CompareTo(y.EntityId); + return result1; + } + } + } +} diff --git a/Slipspace Engine/Data/Scripts/WarpDrive/Settings.cs b/Slipspace Engine/Data/Scripts/WarpDrive/Settings.cs new file mode 100644 index 00000000..469940f5 --- /dev/null +++ b/Slipspace Engine/Data/Scripts/WarpDrive/Settings.cs @@ -0,0 +1,357 @@ +using System; +using System.IO; +using System.Xml.Serialization; +using ProtoBuf; +using Sandbox.ModAPI; +using VRage.Utils; + +namespace WarpDriveMod +{ + [ProtoContract] + public class Settings + { + public static Settings Instance; + + public const string Filename = "FSDriveConfig.cfg"; + + [ProtoMember(1)] + public double maxSpeed; + + [ProtoMember(2)] + public double startSpeed; + + [ProtoMember(3)] + public float maxHeat; + + [ProtoMember(4)] + public float heatGain; + + [ProtoMember(5)] + public float heatDissipationDrive; + + [ProtoMember(6)] + public float baseRequiredPower; + + [ProtoMember(7)] + public float baseRequiredPowerSmall; + + [ProtoMember(8)] + public int powerRequirementMultiplier; + + [ProtoMember(9)] + public float powerRequirementBySpeedDeviderLarge; + + [ProtoMember(10)] + public float powerRequirementBySpeedDeviderSmall; + + [ProtoMember(11)] + public bool AllowInGravity; + + [ProtoMember(12)] + public bool AllowUnlimittedSpeed; + + [ProtoMember(13)] + public bool AllowToDetectEnemyGrids; + + [ProtoMember(14)] + public double DetectEnemyGridInRange; + + [ProtoMember(15)] + public double DelayJumpIfEnemyIsNear; + + [ProtoMember(16)] + public double DelayJump; + + [ProtoMember(17)] + public float AllowInGravityMax; + + [ProtoMember(18)] + public double AllowInGravityMaxSpeed; + + [ProtoMember(19)] + public double AllowInGravityMinAltitude; + + [ProtoMember(20)] + public long BlockID; + + public static Settings GetDefaults() + { + return new Settings + { + maxSpeed = 50000 / 60d, // in settings numbers from higher than startSpeed + 1, max 100 or if AllowUnlimittedSpeed=true up to 2000 (game possile limit) + startSpeed = 1000 / 60d, // in settings numbers from 1 to less than maxSpeed and max 99, or if AllowUnlimittedSpeed=true up to 1999 + maxHeat = 180f, // Shutdown when this amount of heat has been reached. this is in seconds if heatGain = 1 / 60f so it's 3 minutes; + heatGain = 0 / 60f, // Amount of heat gained per tick = 1% per second if set 1, max possible 10 + heatDissipationDrive = 2 / 60f, // Amount of heat dissipated by warp drives every tick + baseRequiredPower = 126f, //MW + baseRequiredPowerSmall = 5f, // MW + powerRequirementMultiplier = 2, // each speed step will take baseRequiredPower/Small and * powerRequirementMultiplier + powerRequirementBySpeedDeviderLarge = 6f, // Now power requirement is based on mass + speed!, to lower power requirement set this to higher number. + powerRequirementBySpeedDeviderSmall = 12f, // Now power requirement is based on mass + speed!, to lower power requirement set this to higher number. + AllowInGravity = false, // allow to activate warp in gravity, ship will drop to 1km/s when in gravity and stop id altitude is below 300m + AllowUnlimittedSpeed = false, // if set to true, will allow setting max speed to any number, if false them max is 100km/s = 100000. + AllowToDetectEnemyGrids = false, // if set to true, then warp charge code will check if there is enemy grid in range, and delay jump by set amount. + DetectEnemyGridInRange = 2000, // sphere range from ship center to detect enemy grid, max range is 8000 meters. + DelayJumpIfEnemyIsNear = 30, // delay jump by this much seconds if enemy grid is in range, max is 90 seconds. + DelayJump = 10, // delay jump start by this much seconds. max 90 sec, min 3 sec + AllowInGravityMax = 1.8f, // Allow to enter gravity of planet till gravity level reach setting, Max possilbe 1.8 + AllowInGravityMaxSpeed = 3000 / 60d, // max speed in gravity, allowed up to speed 3 to prevent high load. + AllowInGravityMinAltitude = 300d // Minimum altitude on planet. + }; + } + + public static Settings Load() + { + Settings defaults = GetDefaults(); + Settings settings = GetDefaults(); + + try + { + if (MyAPIGateway.Utilities.FileExistsInWorldStorage(Filename, typeof(Settings))) + { + TextReader reader = MyAPIGateway.Utilities.ReadFileInWorldStorage(Filename, typeof(Settings)); + string text = reader.ReadToEnd(); + reader.Close(); + + settings = MyAPIGateway.Utilities.SerializeFromXML(text); + double startSpeed = 0; + double maxSpeed = 0; + double gravityMaxSpeed = 0; + float heatDissipationDrive = 0; + float heatGain = 0; + + // convert and check startSpeed settings + if (settings.startSpeed < 1 || settings.startSpeed > 99) + { + startSpeed = defaults.startSpeed; + settings.startSpeed = defaults.startSpeed * 60d / 1000; + Save(settings); + } + else if (settings.startSpeed > settings.maxSpeed) + { + startSpeed = defaults.startSpeed; + settings.startSpeed = defaults.startSpeed * 60d / 1000; + Save(settings); + } + else + startSpeed = settings.startSpeed * 1000 / 60d; + + // convert and check maxSpeed settings + if (settings.maxSpeed > 2000 && settings.AllowUnlimittedSpeed) + { + maxSpeed = 2000 * 1000 / 60d; + settings.maxSpeed = 2000; + Save(settings); + } + else if (settings.maxSpeed > 100 && !settings.AllowUnlimittedSpeed) + { + maxSpeed = 100 * 1000 / 60d; + settings.maxSpeed = 100; + Save(settings); + } + + if (settings.maxSpeed < settings.startSpeed) + { + maxSpeed = (settings.startSpeed + 5) * 1000 / 60d; + settings.maxSpeed = settings.startSpeed + 5; + Save(settings); + } + else + maxSpeed = settings.maxSpeed * 1000 / 60d; + + // convert and check maxHeat settings + if (settings.maxHeat <= 0) + { + settings.maxHeat = 60f; + Save(settings); + } + else if (settings.maxHeat < 30f) + { + settings.maxHeat = 30f; + Save(settings); + } + + // convert and check heatGain settings + if (settings.heatGain < 0f) + { + heatGain = 0f; + settings.heatGain = 0f; + Save(settings); + } + else if (settings.heatGain > 10f) + { + heatGain = 10 / 60f; + settings.heatGain = 10f; + Save(settings); + } + else + heatGain = settings.heatGain / 60f; + + // convert and check heatDissipationDrive settings + if (settings.heatDissipationDrive < 2f) + { + heatDissipationDrive = defaults.heatDissipationDrive; + settings.heatDissipationDrive = 2f; + Save(settings); + } + else + heatDissipationDrive = settings.heatDissipationDrive / 60f; + + // check DelayJump settings + if (settings.DelayJump > 90 || settings.DelayJump < 3) + { + settings.DelayJump = 10; + Save(settings); + } + + // check DelayJumpIfEnemyIsNear settings + if (settings.DelayJumpIfEnemyIsNear < settings.DelayJump) + { + settings.DelayJumpIfEnemyIsNear = 30; + Save(settings); + } + + if (settings.DelayJumpIfEnemyIsNear > 90) + { + settings.DelayJumpIfEnemyIsNear = 30; + Save(settings); + } + + // check DetectEnemyGridInRange settings + if (settings.DetectEnemyGridInRange > 8000 || settings.DetectEnemyGridInRange < 1000) + { + settings.DetectEnemyGridInRange = 2000; + Save(settings); + } + + if (settings.AllowInGravityMax > 1.8f || settings.AllowInGravityMax <= 0f) + { + settings.AllowInGravityMax = 1.8f; + Save(settings); + } + + if (settings.AllowInGravityMaxSpeed < 1 || settings.AllowInGravityMaxSpeed > 3) + { + gravityMaxSpeed = defaults.AllowInGravityMaxSpeed; + settings.AllowInGravityMaxSpeed = defaults.AllowInGravityMaxSpeed * 60d / 1000; + Save(settings); + } + else if (settings.AllowInGravityMaxSpeed > settings.maxSpeed) + { + gravityMaxSpeed = defaults.AllowInGravityMaxSpeed; + settings.AllowInGravityMaxSpeed = defaults.AllowInGravityMaxSpeed * 60d / 1000; + Save(settings); + } + else + gravityMaxSpeed = settings.AllowInGravityMaxSpeed * 1000 / 60d; + + if (settings.AllowInGravityMinAltitude < 300) + settings.AllowInGravityMinAltitude = 300; + + // loading settings for actual code + settings.maxSpeed = maxSpeed; + settings.startSpeed = startSpeed; + settings.AllowInGravityMaxSpeed = gravityMaxSpeed; + settings.heatGain = heatGain; + settings.heatDissipationDrive = heatDissipationDrive; + } + else + { + MyLog.Default.Info("[Frame Shift Drive] Config file not found. Loading default settings"); + + settings.maxSpeed = settings.maxSpeed * 60d / 1000; + settings.startSpeed = settings.startSpeed * 60d / 1000; + settings.AllowInGravityMaxSpeed = settings.AllowInGravityMaxSpeed * 60d / 1000; + settings.heatGain *= 60f; + settings.heatDissipationDrive *= 60f; + Save(settings); + + settings.maxSpeed = settings.maxSpeed * 1000 / 60d; + settings.startSpeed = settings.startSpeed * 1000 / 60d; + settings.AllowInGravityMaxSpeed = settings.AllowInGravityMaxSpeed * 1000 / 60d; + settings.heatGain /= 60f; + settings.heatDissipationDrive /= 60f; + } + } + catch (Exception e) + { + MyLog.Default.Info($"[Frame Shift Drive] Failed to load saved configuration. Loading defaults\n {e}"); + + settings = defaults; + + settings.maxSpeed = defaults.maxSpeed * 60d / 1000; + settings.startSpeed = defaults.startSpeed * 60d / 1000; + settings.AllowInGravityMaxSpeed = defaults.AllowInGravityMaxSpeed * 60d / 1000; + settings.heatGain = defaults.heatGain * 60f; + settings.heatDissipationDrive = defaults.heatDissipationDrive * 60f; + Save(settings); + + settings.maxSpeed = settings.maxSpeed * 1000 / 60d; + settings.startSpeed = settings.startSpeed * 1000 / 60d; + settings.AllowInGravityMaxSpeed = settings.AllowInGravityMaxSpeed * 1000 / 60d; + settings.heatGain /= 60f; + settings.heatDissipationDrive /= 60f; + } + + return settings; + } + + public static void Save(Settings settings) + { + try + { + MyLog.Default.Info($"[Frame Shift Drive] Saving Settings"); + TextWriter writer = MyAPIGateway.Utilities.WriteFileInWorldStorage(Filename, typeof(Settings)); + writer.Write(MyAPIGateway.Utilities.SerializeToXML(settings)); + writer.Close(); + } + catch (Exception e) + { + MyLog.Default.Info($"[Frame Shift Drive] Failed to save settings\n {e}"); + } + } + + public static void SaveClient(Settings settings) + { + try + { + if (MyAPIGateway.Utilities.FileExistsInWorldStorage(Filename, typeof(Settings))) + { + TextReader reader = MyAPIGateway.Utilities.ReadFileInWorldStorage(Filename, typeof(Settings)); + string text = reader.ReadToEnd(); + reader.Close(); + + var settingsClient = MyAPIGateway.Utilities.SerializeFromXML(text); + + settings.maxSpeed = settings.maxSpeed * 60d / 1000; + settings.startSpeed = settings.startSpeed * 60d / 1000; + settings.AllowInGravityMaxSpeed = settings.AllowInGravityMaxSpeed * 60d / 1000; + settings.heatGain *= 60f; + settings.heatDissipationDrive *= 60f; + + if (settingsClient != settings) + Save(settings); + } + else + { + settings.maxSpeed = settings.maxSpeed * 60d / 1000; + settings.startSpeed = settings.startSpeed * 60d / 1000; + settings.AllowInGravityMaxSpeed = settings.AllowInGravityMaxSpeed * 60d / 1000; + settings.heatGain *= 60f; + settings.heatDissipationDrive *= 60f; + Save(settings); + } + + settings.maxSpeed = settings.maxSpeed * 1000 / 60d; + settings.startSpeed = settings.startSpeed * 1000 / 60d; + settings.AllowInGravityMaxSpeed = settings.AllowInGravityMaxSpeed * 1000 / 60d; + settings.heatGain /= 60f; + settings.heatDissipationDrive /= 60f; + } + catch (Exception e) + { + MyLog.Default.Info($"[Frame Shift Drive] Failed to save client settings\n {e}"); + } + } + } +} diff --git a/Slipspace Engine/Data/Scripts/WarpDrive/WarpDrive.cs b/Slipspace Engine/Data/Scripts/WarpDrive/WarpDrive.cs new file mode 100644 index 00000000..3463c08b --- /dev/null +++ b/Slipspace Engine/Data/Scripts/WarpDrive/WarpDrive.cs @@ -0,0 +1,505 @@ +using Sandbox.Common.ObjectBuilders; +using Sandbox.Game; +using Sandbox.Game.Entities; +using Sandbox.Game.Entities.Character; +using Sandbox.Game.EntityComponents; +using Sandbox.Game.World; +using Sandbox.ModAPI; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using VRage.Game; +using VRage.Game.Components; +using VRage.Game.Entity; +using VRage.Game.ModAPI; +using VRage.ModAPI; +using VRage.ObjectBuilders; +using VRage.Utils; +using VRageMath; + +namespace WarpDriveMod +{ + [MyEntityComponentDescriptor(typeof(MyObjectBuilder_UpgradeModule), false, "FSDriveLarge", "FSDriveSmall")] + public class WarpDrive : MyGameLogicComponent + { + public IMyFunctionalBlock Block { get; private set; } + public WarpSystem System { get; private set; } + public Settings Settings { get; private set; } + public static WarpDrive Instance; + public bool HasPower => sink.CurrentInputByType(WarpConstants.ElectricityId) >= prevRequiredPower; + public bool BlockWasON = false; + + private T CastProhibit(T ptr, object val) => (T)val; + + // Ugly workaround + public float RequiredPower + { + get + { + return _requiredPower; + } + set + { + prevRequiredPower = _requiredPower; + _requiredPower = value; + } + } + private float prevRequiredPower; + private float _requiredPower; + private MyResourceSinkComponent sink; + private long initStart; + private bool started = false; + private int BlockOnTick = 0; + + public override void Init(MyObjectBuilder_EntityBase objectBuilder) + { + base.Init(objectBuilder); + + Instance = this; + Block = (IMyFunctionalBlock)Entity; + Settings = Settings.Load(); + + InitPowerSystem(); + + if (WarpDriveSession.Instance != null) + initStart = WarpDriveSession.Instance.Runtime; + + MyVisualScriptLogicProvider.PlayerLeftCockpit += PlayerLeftCockpit; + + if (!MyAPIGateway.Utilities.IsDedicated) + Block.AppendingCustomInfo += Block_AppendingCustomInfo; + + NeedsUpdate = MyEntityUpdateEnum.EACH_FRAME | MyEntityUpdateEnum.EACH_10TH_FRAME; + } + + private void Block_AppendingCustomInfo(IMyTerminalBlock arg1, StringBuilder Info) + { + if (arg1 == null || Settings == null || System == null) + return; + + float _mass = 1; + if (System.GridsMass != null && System.GridsMass.Count > 0 && arg1.CubeGrid != null && System.GridsMass.ContainsKey(arg1.CubeGrid.EntityId)) + System.GridsMass.TryGetValue(arg1.CubeGrid.EntityId, out _mass); + else + { + if (arg1.CubeGrid != null) + { + _mass = System.CulcucateGridGlobalMass(arg1.CubeGrid); + System.GridsMass[arg1.CubeGrid.EntityId] = _mass; + } + } + + float SpeedNormalize = (float)(Settings.maxSpeed * 0.06); // 60 / 1000 + float SpeedCalc = 1f + (SpeedNormalize * SpeedNormalize); + + float MassCalc; + if (arg1.CubeGrid.GridSizeEnum == MyCubeSize.Small) + MassCalc = _mass * (SpeedCalc / 0.528f) / 700000f; + else + MassCalc = _mass * (SpeedCalc / 0.528f) / 1000000f; + + float MaxNeededPower; + + if (arg1.CubeGrid.GridSizeEnum == MyCubeSize.Large) + { + if (System.currentSpeedPt != Settings.maxSpeed) + MaxNeededPower = (MassCalc + Settings.baseRequiredPower * 3) / Settings.powerRequirementBySpeedDeviderLarge * 0.9725f; + else + MaxNeededPower = RequiredPower; + } + else + { + if (System.currentSpeedPt != Settings.maxSpeed) + MaxNeededPower = (MassCalc + Settings.baseRequiredPowerSmall * 3) / Settings.powerRequirementBySpeedDeviderSmall * 0.9725f; + else + MaxNeededPower = RequiredPower; + } + + Info?.AppendLine("Max Required Power: " + MaxNeededPower.ToString("N") + " MW"); + + Info?.AppendLine("Required Power: " + RequiredPower.ToString("N") + " MW"); + + if (sink != null) + Info?.AppendLine("Current Power: " + sink.CurrentInputByType(WarpConstants.ElectricityId).ToString("N") + " MW"); + + Info?.Append("FSD Heat: ").Append(System.DriveHeat).Append("%\n"); + } + + public override void UpdateBeforeSimulation10() + { + if (WarpDriveSession.Instance == null || Block == null) + return; + + // init once + if (Block != null) + WarpDriveSession.Instance.InitJumpControl(); + + if (BlockWasON) + { + if (BlockOnTick++ > 20) + { + Block.Enabled = true; + BlockWasON = false; + BlockOnTick = 0; + } + } + + if (!MyAPIGateway.Utilities.IsDedicated) + Block.RefreshCustomInfo(); + } + + public override void UpdateBeforeSimulation() + { + if (WarpDriveSession.Instance == null) + return; + + if (!started) + { + if (System != null && System.Valid) + started = true; + else if (initStart <= WarpDriveSession.Instance.Runtime - WarpConstants.groupSystemDelay) + { + System = WarpDriveSession.Instance.GetWarpSystem(this); + if (System == null) + return; + + System.OnSystemInvalidatedAction += OnSystemInvalidated; + started = true; + } + } + else + { + sink.Update(); + } + } + + public override void Close() + { + if (System == null) + return; + + System.OnSystemInvalidatedAction -= OnSystemInvalidated; + + if (!MyAPIGateway.Utilities.IsDedicated) + { + if (Block != null) + Block.AppendingCustomInfo -= Block_AppendingCustomInfo; + + System.StopBlinkParticleEffect(); + } + + if (Block != null && Block.CubeGrid != null && System.GridsMass.ContainsKey(Block.CubeGrid.EntityId)) + System.GridsMass.Remove(Block.CubeGrid.EntityId); + } + + private void InitPowerSystem() + { + MyResourceSinkComponent powerSystem = new MyResourceSinkComponent(); + + var blocksubtupe = Block.BlockDefinition.SubtypeId; + + if (blocksubtupe == "FSDriveSmall") + powerSystem.Init(MyStringHash.GetOrCompute("Utility"), Settings.baseRequiredPowerSmall * Settings.powerRequirementMultiplier, + ComputeRequiredPower, (MyCubeBlock)Entity); + + if (blocksubtupe == "FSDriveLarge") + powerSystem.Init(MyStringHash.GetOrCompute("Utility"), Settings.baseRequiredPower * Settings.powerRequirementMultiplier, + ComputeRequiredPower, (MyCubeBlock)Entity); + + Entity.Components.Add(powerSystem); + sink = powerSystem; + sink.Update(); + } + + private float ComputeRequiredPower() + { + if (System == null || System.WarpState == WarpSystem.State.Idle) + RequiredPower = 0; + + return RequiredPower; + } + + public void PlayerLeftCockpit(string entityName, long playerId, string gridName) + { + if (Block == null || System == null) + return; + + WarpDrive drive = Block?.GameLogic?.GetAs(); + if (drive == null) + return; + else if (drive.System.WarpState == WarpSystem.State.Idle) + return; + + if (entityName != "") + { + long temp_id; + if (long.TryParse(entityName, out temp_id)) + { + var dump_cockpit = MyAPIGateway.Entities.GetEntityById(temp_id) as IMyShipController; + var CockpitGrid = dump_cockpit?.CubeGrid as MyCubeGrid; + HashSet FoundCockpits = new HashSet(); + + if (CockpitGrid == null) + return; + + if ((bool)(drive.System.grid?.cockpits?.TryGetValue(CockpitGrid, out FoundCockpits))) + { + if (FoundCockpits.Count > 0 && FoundCockpits.Contains(dump_cockpit)) + { + if (dump_cockpit.CubeGrid.EntityId != drive.Block.CubeGrid.EntityId) + return; + + drive.System.SafeTriggerON = true; + + if (MyAPIGateway.Utilities.IsDedicated || MyAPIGateway.Multiplayer.IsServer) + { + drive.System.currentSpeedPt = -1f; + dump_cockpit.CubeGrid?.Physics?.ClearSpeed(); + + drive.System.Dewarp(true); + Block.Enabled = false; + BlockWasON = true; + } + + drive.System.SafeTriggerON = false; + } + } + } + } + } + + public bool ProxymityDangerInWarp(MatrixD gridMatrix, MyCubeGrid MainGrid, double GridSpeed) + { + if (MainGrid == null) + return false; + + List entList; + IMyCubeGrid WarpGrid = MainGrid; + Vector3D forward = gridMatrix.Forward; + MatrixD FrontStart = MatrixD.CreateFromDir(-forward); + Vector3D PointFromFront; + + if (WarpGrid.GridSizeEnum == MyCubeSize.Small) + { + Vector3D effectOffsetSmall = forward * WarpGrid.WorldAABB.HalfExtents.AbsMax(); + FrontStart.Translation = WarpGrid.WorldAABB.Center + effectOffsetSmall; + FrontStart.Translation += forward * 400.0; + PointFromFront = FrontStart.Translation; + var sphere = new BoundingSphereD(PointFromFront, 300.0); + entList = MyAPIGateway.Entities.GetTopMostEntitiesInSphere(ref sphere); + } + else + { + Vector3D effectOffsetLarge = forward * WarpGrid.WorldAABB.HalfExtents.AbsMax(); + FrontStart.Translation = WarpGrid.WorldAABB.Center + effectOffsetLarge; + FrontStart.Translation += forward * 500.0; + PointFromFront = FrontStart.Translation; + var sphere = new BoundingSphereD(PointFromFront, 400.0); + entList = MyAPIGateway.Entities.GetTopMostEntitiesInSphere(ref sphere); + } + + if (entList == null || entList.Count == 0) + return false; + + var AttachedList = new List(); + + // get all subgrids grids and locked on landing gear. + MyAPIGateway.GridGroups.GetGroup(WarpGrid, GridLinkTypeEnum.Physical, AttachedList); + + foreach (var ent in entList) + { + if (ent is MySafeZone) + return true; + + if (!(ent is MyCubeGrid || ent is MyVoxelMap)) + continue; + + // dont stop if grid speed is 20 or above. + if (ent is MyVoxelMap && GridSpeed >= 333.333) + continue; + + if (ent is MyCubeGrid) + { + var FoundGrid = ent as IMyCubeGrid; + + if (FoundGrid != null && AttachedList != null && AttachedList.Count > 0 && AttachedList.Contains(FoundGrid)) + continue; + } + + var EntityPosition = ent.GetPosition() + Vector3D.Zero; + + if (WarpGrid.GridSizeEnum == MyCubeSize.Small) + { + if ((EntityPosition - PointFromFront).Length() <= 250.0) + return true; + } + else + { + if (ent is MyVoxelMap && (EntityPosition - PointFromFront).Length() <= 280.0) + return true; + else if ((EntityPosition - PointFromFront).Length() <= 220.0) + return true; + } + } + return false; + } + + public bool ProxymityDangerCharge(MatrixD gridMatrix, IMyCubeGrid WarpGrid) + { + if (WarpGrid == null || WarpGrid.Physics == null) + return false; + + List entList; + Vector3D forward = gridMatrix.Forward; + MatrixD FrontStart = MatrixD.CreateFromDir(-forward); + Vector3D PointFromFront; + + if (MyAPIGateway.Session?.Player != null) + { + bool allowed = MySessionComponentSafeZones.IsActionAllowed(MyAPIGateway.Session.Player.Character.WorldMatrix.Translation, CastProhibit(MySessionComponentSafeZones.AllowedActions, 1)); + if (!allowed) + return true; + } + + if (WarpGrid.GridSizeEnum == MyCubeSize.Small) + { + Vector3D effectOffsetSmall = forward * WarpGrid.WorldAABB.HalfExtents.AbsMax(); + FrontStart.Translation = WarpGrid.WorldAABB.Center + effectOffsetSmall; + FrontStart.Translation += forward * 400.0; + PointFromFront = FrontStart.Translation; + var sphere = new BoundingSphereD(PointFromFront, 300.0); + entList = MyAPIGateway.Entities.GetTopMostEntitiesInSphere(ref sphere); + } + else + { + Vector3D effectOffsetLarge = forward * WarpGrid.WorldAABB.HalfExtents.AbsMax(); + FrontStart.Translation = WarpGrid.WorldAABB.Center + effectOffsetLarge; + FrontStart.Translation += forward * 500.0; + PointFromFront = FrontStart.Translation; + var sphere = new BoundingSphereD(PointFromFront, 400.0); + entList = MyAPIGateway.Entities.GetTopMostEntitiesInSphere(ref sphere); + } + + if (entList == null || entList.Count == 0) + return false; + + var AttachedList = new List(); + + // get all subgrids grids and locked on landing gear. + MyAPIGateway.GridGroups.GetGroup(WarpGrid, GridLinkTypeEnum.Physical, AttachedList); + + foreach (var ent in entList) + { + if (ent is MySafeZone) + return true; + + if (!(ent is MyCubeGrid || ent is MyVoxelMap)) + continue; + + if (ent is MyCubeGrid) + { + var FoundGrid = ent as IMyCubeGrid; + + if (FoundGrid != null && AttachedList != null && AttachedList.Count > 0 && AttachedList.Contains(FoundGrid)) + continue; + } + + var EntityPosition = ent.PositionComp.GetPosition() + Vector3D.Zero; + + if ((EntityPosition - PointFromFront).Length() <= 250.0) + return true; + } + + return false; + } + + public bool EnemyProxymityDangerCharge(IMyCubeGrid WarpGrid) + { + if (WarpGrid == null || WarpGrid.Physics == null) + return false; + + var Gridlocation = WarpGrid.PositionComp.GetPosition(); + var sphere = new BoundingSphereD(Gridlocation, Settings.DetectEnemyGridInRange); + var entList = MyAPIGateway.Entities.GetTopMostEntitiesInSphere(ref sphere); + + if (entList == null || entList.Count == 0) + return false; + + var AttachedList = new List(); + + // get all subgrids grids and locked on landing gear. + MyAPIGateway.GridGroups.GetGroup(WarpGrid, GridLinkTypeEnum.Physical, AttachedList); + + var WarpGridOwner = WarpGrid.BigOwners.FirstOrDefault(); + var WarpGridFaction = MyAPIGateway.Session.Factions.TryGetPlayerFaction(WarpGridOwner); + + foreach (var ent in entList) + { + if (!(ent is MyCubeGrid)) + continue; + + if (ent is MyCubeGrid) + { + var FoundGrid = ent as IMyCubeGrid; + + if (FoundGrid != null && AttachedList != null && AttachedList.Count > 0 && AttachedList.Contains(FoundGrid)) + continue; + + if (FoundGrid.BigOwners != null && FoundGrid.BigOwners.FirstOrDefault() != 0L) + { + var FoundGridOwner = FoundGrid.BigOwners.FirstOrDefault(); + + if (FoundGridOwner == WarpGridOwner) + continue; + + var FoundGridFaction = MyAPIGateway.Session.Factions.TryGetPlayerFaction(FoundGridOwner); + + if (WarpGridFaction != null && FoundGridFaction != null) + { + if (FoundGridFaction.FactionId == WarpGridFaction.FactionId) + continue; + + var FactionsRelationship = MyAPIGateway.Session.Factions.GetRelationBetweenFactions(FoundGridFaction.FactionId, WarpGridFaction.FactionId); + if (FactionsRelationship != MyRelationsBetweenFactions.Enemies) + continue; + + // found enenmy grid in sphere! + return true; + } + else + return true; + } + } + } + + return false; + } + + private void OnSystemInvalidated(WarpSystem system) + { + if (Block.MarkedForClose || Block.CubeGrid.MarkedForClose) + return; + + WarpDriveSession.Instance.DelayedGetWarpSystem(this); + } + + public void SetWarpSystem(WarpSystem system) + { + System = system; + System.OnSystemInvalidatedAction += OnSystemInvalidated; + } + + public override bool Equals(object obj) + { + var drive = obj as WarpDrive; + + return drive != null && EqualityComparer.Default.Equals(Block, drive.Block); + } + + public override int GetHashCode() + { + return 957606482 + EqualityComparer.Default.GetHashCode(Block); + } + } +} diff --git a/Slipspace Engine/Data/Scripts/WarpDrive/WarpDriveSession.cs b/Slipspace Engine/Data/Scripts/WarpDrive/WarpDriveSession.cs new file mode 100644 index 00000000..8a69fc8f --- /dev/null +++ b/Slipspace Engine/Data/Scripts/WarpDrive/WarpDriveSession.cs @@ -0,0 +1,511 @@ +using VRage.Game.Components; +using Sandbox.ModAPI; +using System.Text; +using Sandbox.ModAPI.Interfaces.Terminal; +using System.Collections.Generic; +using System; +using VRage.Game.ModAPI; +using VRage.Game; +using Sandbox.Game.Entities; +using VRage.Utils; +using Sandbox.Game; +using Sandbox.Game.Screens.Terminal.Controls; +using Sandbox.Game.EntityComponents; +using VRage.ModAPI; +using VRage.Game.Entity; +using ProtoBuf; + +namespace WarpDriveMod +{ + public static class WarpConstants + { + public static MySoundPair EmergencyDropSound = new MySoundPair("SuperCruiseGravity", true); + public static MySoundPair chargingSound = new MySoundPair("quantum_charging", true); + public static MySoundPair jumpInSound = new MySoundPair("quantum_jumpin", true); + public static MySoundPair jumpOutSound = new MySoundPair("quantum_jumpout", true); + + public const int groupSystemDelay = 1; + + public static MyDefinitionId ElectricityId = MyResourceDistributorComponent.ElectricityId; + } + + [ProtoContract] + public class ItemsMessage + { + [ProtoMember(1)] + public long EntityId { get; set; } + [ProtoMember(2)] + public long SendingPlayerID { get; set; } + } + + [ProtoContract] + public class SpeedMessage + { + [ProtoMember(1)] + public long EntityId { get; set; } + [ProtoMember(2)] + public double WarpSpeed { get; set; } + } + + [MySessionComponentDescriptor(MyUpdateOrder.Simulation)] + public class WarpDriveSession : MySessionComponentBase + { + public static WarpDriveSession Instance; + public Random Rand { get; private set; } = new Random(); + public long Runtime { get; private set; } = 0; + public Dictionary warpDrivesSpeeds = new Dictionary(); + + private readonly List warpSystems = new List(); + private readonly List newSystems = new List(); + private readonly List requireSystem = new List(); + private bool isHost; + private bool isPlayer; + private bool _controlInit = false; + public const ushort toggleWarpPacketId = 4374; + public const ushort toggleWarpPacketIdSpeed = 4378; + public const ushort WarpConfigPacketId = 4389; + private Action toggle; + + public WarpDriveSession() + { + Instance = this; + } + + public void InitJumpControl() + { + if (Instance == null || WarpDrive.Instance == null || WarpSystem.Instance == null) + return; + + if (!_controlInit) + { + isPlayer = !MyAPIGateway.Utilities.IsDedicated; + isHost = MyAPIGateway.Multiplayer.IsServer; + + MyAPIGateway.Multiplayer.RegisterSecureMessageHandler(toggleWarpPacketId, ReceiveToggleWarp); + MyAPIGateway.Multiplayer.RegisterSecureMessageHandler(toggleWarpPacketIdSpeed, ReceiveWarpSpeed); + MyAPIGateway.Multiplayer.RegisterSecureMessageHandler(WarpConfigPacketId, ReceiveWarpConfig); + + if (isHost) + { + if (isPlayer) + { + // Session is host, toggle the warp drive directly. + MyLog.Default.WriteLineAndConsole("Initialized Warp Drive mod on a hosted multiplayer world."); + toggle = ToggleWarp; + } + else + { + // Do not create terminal controls on dedicated server. + MyLog.Default.WriteLineAndConsole("Initialized Warp Drive mod on dedicated server."); + } + } + else + { + if (isPlayer) + { + // Session is client, tell the host to toggle the warp drive. + toggle = TransmitToggleWarp; + MyLog.Default.WriteLineAndConsole("Initialized Frame Shift Drive mod on a multiplayer client."); + } + else + throw new Exception("Session is not host or client. What?!"); + } + + // no need to init controls on server. + if (MyAPIGateway.Utilities.IsDedicated) + { + _controlInit = true; + return; + } + + if (toggle == null) + return; + + IMyTerminalAction startWarp = MyAPIGateway.TerminalControls.CreateAction("ToggleWarp"); + startWarp.Enabled = IsWarpDrive; + startWarp.Name = new StringBuilder("Toggle Supercruise"); + startWarp.Action = toggle; + startWarp.Icon = "Textures\\GUI\\Icons\\Actions\\Toggle.dds"; + MyAPIGateway.TerminalControls.AddAction(startWarp); + + IMyTerminalControlButton startWarpBtn = MyAPIGateway.TerminalControls.CreateControl("StartWarpBtn"); + startWarpBtn.Tooltip = MyStringId.GetOrCompute("Toggles the status of the warp drives on the ship"); + startWarpBtn.Title = MyStringId.GetOrCompute("Toggle Warp"); + startWarpBtn.Enabled = IsWarpDrive; + startWarpBtn.Visible = IsWarpDrive; + startWarpBtn.SupportsMultipleBlocks = false; + startWarpBtn.Action = toggle; + MyAPIGateway.TerminalControls.AddControl(startWarpBtn); + + IMyTerminalControlProperty inWarp = MyAPIGateway.TerminalControls.CreateProperty("WarpStatus"); + inWarp.Enabled = IsWarpDrive; + inWarp.Visible = IsWarpDrive; + inWarp.SupportsMultipleBlocks = false; + inWarp.Setter = SetWarpStatus; + inWarp.Getter = GetWarpStatus; + MyAPIGateway.TerminalControls.AddControl(inWarp); + + _controlInit = true; + } + } + + private bool GetWarpStatus(IMyTerminalBlock block) + { + WarpDrive drive = block?.GameLogic?.GetAs(); + if (!HasValidSystem(drive)) + return false; + + return drive.System.WarpState != WarpSystem.State.Idle; + } + + private void SetWarpStatus(IMyTerminalBlock block, bool state) + { + WarpDrive drive = block?.GameLogic?.GetAs(); + if (!HasValidSystem(drive)) + return; + + long PlayerID = 0; + + if (state) + { + if (drive.System.WarpState == WarpSystem.State.Idle) + drive.System.ToggleWarp(block, block.CubeGrid, PlayerID); + } + else + { + if (drive.System.WarpState != WarpSystem.State.Idle) + drive.System.ToggleWarp(block, block.CubeGrid, PlayerID); + } + } + + private void ReceiveToggleWarp(ushort channel, byte[] data, ulong sender, bool fromServer) + { + var message = MyAPIGateway.Utilities.SerializeFromBinary(data); + if (message == null) + return; + + IMyEntity entity; + if (!MyAPIGateway.Entities.TryGetEntityById(message.EntityId, out entity)) + return; + + var block = entity as IMyFunctionalBlock; + if (block != null) + { + WarpDrive drive = block?.GameLogic?.GetAs(); + if (!HasValidSystem(drive)) + return; + + if (drive.System.WarpState == WarpSystem.State.Idle) + { + RefreshGridCockpits(block); + + var Gridmatrix = drive.System.grid.FindWorldMatrix(); + + if (WarpDrive.Instance.ProxymityDangerCharge(Gridmatrix, block.CubeGrid)) + { + drive.System.SendMessage(drive.System.ProximytyAlert, 2f, "Red", message.SendingPlayerID); + return; + } + } + + drive.System.ToggleWarp(block, block.CubeGrid, message.SendingPlayerID); + } + } + + public void TransmitToggleWarp(IMyTerminalBlock block) + { + WarpDrive drive = block?.GameLogic?.GetAs(); + var player = MyAPIGateway.Session?.Player; + + if (drive == null || player == null) + return; + + MyAPIGateway.Multiplayer.SendMessageToServer(toggleWarpPacketId, + message: MyAPIGateway.Utilities.SerializeToBinary(new ItemsMessage + { + EntityId = block.EntityId, + SendingPlayerID = player.IdentityId + })); + } + + private void ReceiveWarpSpeed(ushort channel, byte[] data, ulong sender, bool fromServer) + { + var message = MyAPIGateway.Utilities.SerializeFromBinary(data); + if (message == null) + return; + + IMyEntity entity; + if (!MyAPIGateway.Entities.TryGetEntityById(message.EntityId, out entity)) + return; + + var block = entity as IMyFunctionalBlock; + WarpDrive drive = block?.GameLogic?.GetAs(); + + if (!HasValidSystem(drive)) + return; + + if (!warpDrivesSpeeds.ContainsKey(block)) + warpDrivesSpeeds.Add(block, message.WarpSpeed); + else + warpDrivesSpeeds[block] = message.WarpSpeed; + + // Message is from client and should be relayed + //if (MyAPIGateway.Utilities.IsDedicated) + // MyAPIGateway.Multiplayer.SendMessageToOthers(toggleWarpPacketIdSpeed, data); + } + + public void TransmitWarpSpeed(IMyFunctionalBlock WarpBlock, double currentSpeedPt) + { + var DriveBlock = WarpBlock as IMyTerminalBlock; + WarpDrive drive = DriveBlock?.GameLogic?.GetAs(); + if (drive == null) + return; + + MyAPIGateway.Multiplayer.SendMessageToServer(toggleWarpPacketIdSpeed, + message: MyAPIGateway.Utilities.SerializeToBinary(new SpeedMessage + { + EntityId = DriveBlock.EntityId, + WarpSpeed = currentSpeedPt + })); + } + + private void ReceiveWarpConfig(ushort channel, byte[] data, ulong sender, bool fromServer) + { + var message = MyAPIGateway.Utilities.SerializeFromBinary(data); + if (message == null) + return; + + IMyEntity entity; + if (!MyAPIGateway.Entities.TryGetEntityById(message.BlockID, out entity)) + return; + + var block = entity as IMyFunctionalBlock; + if (block != null) + { + WarpDrive drive = block?.GameLogic?.GetAs(); + if (!HasValidSystem(drive)) + return; + + if (MyAPIGateway.Utilities.IsDedicated || MyAPIGateway.Multiplayer.IsServer) + { + MyAPIGateway.Multiplayer.SendMessageTo(WarpConfigPacketId, + message: MyAPIGateway.Utilities.SerializeToBinary(new Settings + { + maxSpeed = drive.Settings.maxSpeed, + startSpeed = drive.Settings.startSpeed, + maxHeat = drive.Settings.maxHeat, + heatGain = drive.Settings.heatGain, + heatDissipationDrive = drive.Settings.heatDissipationDrive, + baseRequiredPower = drive.Settings.baseRequiredPower, + baseRequiredPowerSmall = drive.Settings.baseRequiredPowerSmall, + powerRequirementMultiplier = drive.Settings.powerRequirementMultiplier, + powerRequirementBySpeedDeviderLarge = drive.Settings.powerRequirementBySpeedDeviderLarge, + powerRequirementBySpeedDeviderSmall = drive.Settings.powerRequirementBySpeedDeviderSmall, + AllowInGravity = drive.Settings.AllowInGravity, + AllowUnlimittedSpeed = drive.Settings.AllowUnlimittedSpeed, + AllowToDetectEnemyGrids = drive.Settings.AllowToDetectEnemyGrids, + DetectEnemyGridInRange = drive.Settings.DetectEnemyGridInRange, + DelayJumpIfEnemyIsNear = drive.Settings.DelayJumpIfEnemyIsNear, + DelayJump = drive.Settings.DelayJump, + AllowInGravityMax = drive.Settings.AllowInGravityMax, + AllowInGravityMaxSpeed = drive.Settings.AllowInGravityMaxSpeed, + AllowInGravityMinAltitude = drive.Settings.AllowInGravityMinAltitude, + BlockID = block.EntityId, + }), recipient: sender); + } + + if (!MyAPIGateway.Utilities.IsDedicated && !MyAPIGateway.Multiplayer.IsServer) + { + drive.Settings.maxSpeed = message.maxSpeed; + drive.Settings.startSpeed = message.startSpeed; + drive.Settings.maxHeat = message.maxHeat; + drive.Settings.heatGain = message.heatGain; + drive.Settings.heatDissipationDrive = message.heatDissipationDrive; + drive.Settings.baseRequiredPower = message.baseRequiredPower; + drive.Settings.baseRequiredPowerSmall = message.baseRequiredPowerSmall; + drive.Settings.powerRequirementMultiplier = message.powerRequirementMultiplier; + drive.Settings.powerRequirementBySpeedDeviderLarge = message.powerRequirementBySpeedDeviderLarge; + drive.Settings.powerRequirementBySpeedDeviderSmall = message.powerRequirementBySpeedDeviderSmall; + drive.Settings.AllowInGravity = message.AllowInGravity; + drive.Settings.AllowUnlimittedSpeed = message.AllowUnlimittedSpeed; + drive.Settings.AllowToDetectEnemyGrids = message.AllowToDetectEnemyGrids; + drive.Settings.DetectEnemyGridInRange = message.DetectEnemyGridInRange; + drive.Settings.DelayJumpIfEnemyIsNear = message.DelayJumpIfEnemyIsNear; + drive.Settings.DelayJump = message.DelayJump; + drive.Settings.AllowInGravityMax = message.AllowInGravityMax; + drive.Settings.AllowInGravityMaxSpeed = message.AllowInGravityMaxSpeed; + drive.Settings.AllowInGravityMinAltitude = message.AllowInGravityMinAltitude; + drive.Settings.BlockID = message.BlockID; + + Settings.SaveClient(message); + } + } + } + + public void TransmitWarpConfig(Settings SettingsData, long BlockID) + { + MyAPIGateway.Multiplayer.SendMessageToServer(WarpConfigPacketId, + message: MyAPIGateway.Utilities.SerializeToBinary(new Settings + { + BlockID = BlockID, + })); + } + + private bool IsWarpDrive(IMyTerminalBlock block) + { + return block?.GameLogic?.GetAs() != null; + } + + public override void Simulate() + { + Runtime++; + + for (int i = requireSystem.Count - 1; i >= 0; i--) + { + WarpDrive drive = requireSystem[i]; + if (drive.System == null || drive.System.InvalidOn <= Runtime - WarpConstants.groupSystemDelay) + { + requireSystem.RemoveAtFast(i); + + var DriveSystemNew = GetWarpSystem(drive); + if (DriveSystemNew != null) + drive.SetWarpSystem(DriveSystemNew); + } + else if (HasValidSystem(drive)) + requireSystem.RemoveAtFast(i); + } + + for (int i = warpSystems.Count - 1; i >= 0; i--) + { + WarpSystem s = warpSystems[i]; + if (s.Valid) + s.UpdateBeforeSimulation(); + else + warpSystems.RemoveAtFast(i); + } + + if (newSystems != null && newSystems.Count > 0) + { + foreach (WarpSystem s in newSystems) + { + if (!warpSystems.Contains(s)) + warpSystems.Add(s); + } + newSystems.Clear(); + } + } + + public WarpSystem GetWarpSystem(WarpDrive drive) + { + if (HasValidSystem(drive)) + return drive.System; // Why are you here?!?! + + foreach (WarpSystem s in warpSystems) + { + if (s == null) + continue; + + if (s.Valid && s.Contains(drive)) + return s; + } + + foreach (WarpSystem s in newSystems) + { + if (s == null) + continue; + + if (s.Contains(drive)) + return s; + } + + WarpSystem newSystem = new WarpSystem(drive, drive.System); + + if (newSystem == null) + return null; + + if (!newSystems.Contains(newSystem)) + newSystems.Add(newSystem); + + return newSystem; + } + + public void DelayedGetWarpSystem(WarpDrive drive) + { + requireSystem.Add(drive); + } + + private void ToggleWarp(IMyTerminalBlock block) + { + WarpDrive drive = block?.GameLogic?.GetAs(); + if (!HasValidSystem(drive)) + return; + + drive.System.ToggleWarp(block, block.CubeGrid, 0); + } + + public void RefreshGridCockpits(IMyTerminalBlock block) + { + if (block == null) + return; + + MyCubeGrid grid = (MyCubeGrid)block.CubeGrid; + if (grid == null) + return; + + var slimList = new List(); + var ShipControllerList = new HashSet(); + + block.CubeGrid.GetBlocks(slimList); + + // check all valid cockpits + foreach (var slim in slimList) + { + if (slim.FatBlock != null && slim.FatBlock is IMyShipController) + { + if (WarpSystem.Instance.grid.IsShipController(slim.FatBlock)) + ShipControllerList.Add(slim.FatBlock as IMyShipController); + } + } + + // remove found cockpits from active list + HashSet gridCockpits = new HashSet(); + if (WarpSystem.Instance.grid.cockpits.TryGetValue(grid, out gridCockpits)) + WarpSystem.Instance.grid.cockpits[grid] = null; + + // add updated cocpits to active list + WarpSystem.Instance.grid.cockpits[grid] = ShipControllerList; + } + + private bool HasValidSystem(WarpDrive drive) + { + return drive?.System != null && drive.System.Valid; + } + + protected override void UnloadData() + { + try + { + if (Instance == null) + return; + + if (WarpDrive.Instance != null) + MyVisualScriptLogicProvider.PlayerLeftCockpit -= WarpDrive.Instance.PlayerLeftCockpit; + + MyAPIGateway.Multiplayer.UnregisterSecureMessageHandler(toggleWarpPacketId, ReceiveToggleWarp); + MyAPIGateway.Multiplayer.UnregisterSecureMessageHandler(toggleWarpPacketIdSpeed, ReceiveWarpSpeed); + MyAPIGateway.Multiplayer.UnregisterSecureMessageHandler(WarpConfigPacketId, ReceiveWarpConfig); + + if (WarpDrive.Instance != null) + WarpDrive.Instance = null; + + if (WarpSystem.Instance != null) + WarpSystem.Instance = null; + + Instance = null; + + base.UnloadData(); + } + catch { } + } + } +} diff --git a/Slipspace Engine/Data/Scripts/WarpDrive/WarpSystem.cs b/Slipspace Engine/Data/Scripts/WarpDrive/WarpSystem.cs new file mode 100644 index 00000000..59370a3d --- /dev/null +++ b/Slipspace Engine/Data/Scripts/WarpDrive/WarpSystem.cs @@ -0,0 +1,2108 @@ +using Sandbox.Game; +using Sandbox.Game.Entities; +using Sandbox.Game.Entities.Blocks; +using Sandbox.Game.GameSystems; +using Sandbox.Game.Multiplayer; +using Sandbox.Game.World.Generator; +using Sandbox.ModAPI; +using System; +using System.Collections.Generic; +using System.Text; +using VRage.Game; +using VRage.Game.Entity; +using VRage.Game.ModAPI; +using VRage.ModAPI; +using VRage.Network; +using VRage.Utils; +using VRageMath; + +namespace WarpDriveMod +{ + public class WarpSystem + { + public bool Valid => grid.Valid; + public long InvalidOn => grid.InvalidOn; + public int Id { get; private set; } + public State WarpState { get; set; } + public static WarpSystem Instance; + public event Action OnSystemInvalidatedAction; + public List OnlinePlayersList = new List(); + public double currentSpeedPt = WarpDrive.Instance.Settings.startSpeed; + public int DriveHeat { get; set; } + public GridSystem grid; + public bool ProxymityStop = false; + public bool SafeTriggerON = false; + + private MatrixD gridMatrix; + private readonly Dictionary> warpDrives = new Dictionary>(); + private readonly List PlayersInWarpList = new List(); + private readonly List TempDisabledDrives = new List(); + public readonly Dictionary GridsMass = new Dictionary(); + public readonly Dictionary GridSpeedLinearVelocity = new Dictionary(); + public readonly Dictionary GridSpeedAngularVelocity = new Dictionary(); + private MyParticleEffect effect; + private readonly MyEntity3DSoundEmitter sound; + public MyParticleEffect BlinkTrailEffect; + private long startChargeRuntime = -1; + private bool hasEnoughPower = true; + private int functionalDrives; + private IMyCubeGrid startWarpSource; + private float totalHeat = 0; + private int _updateTicks = 0; + private int UpdatePlayersTick = 0; + private int SpeedUpSendToServerTick = 0; + private int SpeedDownSendToServerTick = 0; + private int BlockOnTick = 0; + private int ShipSpeedResetTick = 0; + private int PowerCheckTick = 0; + private int MassUpdateTick = 0; + private int MassChargeUpdate = 180; + private bool TeleportNow = false; + private bool WarpDropSound = false; + + public string warnDestablalized = "Supercruise destabilized!"; + public string warnAborted = "Charging procedure aborted!"; + public string warnOverload = "Frame shift drive overloaded!"; + public string warnDamaged = "Frame shift drive Offline or Damaged!"; + public string warnNoPower = "Not enough power!"; + public string TooFast = "Decrease your speed!"; + public string EmergencyDropSpeed = "Emergency Stop!"; + public string warnStatic = "Unable to move static grid!"; + public string warnInUse = "Grid is already at supercruise!"; + public string warnNoEstablish = "Unable to establish supercruise!"; + public string warnOverheat = "Frame shift drive overheated!"; + public string ProximytyAlert = "Can't Start FSD, Proximity Alert!"; + + public const float EARTH_GRAVITY = 9.806652f; + + public WarpSystem(WarpDrive block, WarpSystem oldSystem) + { + if (block == null || block.Block == null || block.Block.CubeGrid == null) + return; + + Id = WarpDriveSession.Instance.Rand.Next(int.MinValue, int.MaxValue); + + grid = new GridSystem((MyCubeGrid)block.Block.CubeGrid); + + GridSystem.BlockCounter warpDriveCounter = new GridSystem.BlockCounter((b) => b?.GameLogic.GetAs() != null); + warpDriveCounter.OnBlockAdded += OnDriveAdded; + warpDriveCounter.OnBlockRemoved += OnDriveRemoved; + grid.AddCounter("WarpDrives", warpDriveCounter); + + grid.OnSystemInvalidated += OnSystemInvalidated; + + if (!MyAPIGateway.Utilities.IsDedicated && grid.MainGrid != null) + { + sound = new MyEntity3DSoundEmitter(grid.MainGrid) + { + CanPlayLoopSounds = true + }; + } + + if (oldSystem != null) + { + startWarpSource = oldSystem.startWarpSource; + if (startWarpSource?.MarkedForClose == true) + startWarpSource = null; + + totalHeat = oldSystem.totalHeat; + WarpState = oldSystem.WarpState; + + if (WarpState == State.Charging) + { + if (!MyAPIGateway.Utilities.IsDedicated) + { + try + { + PlayParticleEffect(); + } + catch { } + }; + + startChargeRuntime = oldSystem.startChargeRuntime; + WarpState = State.Charging; + } + else if (WarpState == State.Active) + { + currentSpeedPt = oldSystem.currentSpeedPt; + WarpState = State.Active; + } + } + + block.SetWarpSystem(this); + } + + private void UpdateOnlinePlayers() + { + OnlinePlayersList.Clear(); + MyAPIGateway.Players.GetPlayers(OnlinePlayersList); + } + + public void UpdateBeforeSimulation() + { + if (Instance == null) + Instance = this; + + if (WarpDriveSession.Instance == null || WarpDrive.Instance == null || WarpDrive.Instance.Settings == null || grid == null || grid.MainGrid == null) + return; + + var MainGrid = grid.MainGrid; + + if (UpdatePlayersTick++ >= 300) + { + UpdateOnlinePlayers(); + UpdatePlayersTick = 0; + } + + if (BlockOnTick++ >= 60) + { + BlockOnTick = 0; + + if (TempDisabledDrives.Count > 0) + { + foreach (var block in TempDisabledDrives) + { + if (block != null) + block.Enabled = true; + } + TempDisabledDrives.Clear(); + } + } + + if (warpDrives.Count == 0) + grid.Invalidate(); + + UpdateHeatPower(); + + if (WarpState == State.Charging || WarpState == State.Active) + gridMatrix = grid.FindWorldMatrix(); + + if (WarpState == State.Charging) + InCharge(); + + if (WarpState == State.Active) + { + if (!MyAPIGateway.Utilities.IsDedicated) + sound.SetPosition(MainGrid.PositionComp.GetPosition()); + + if (InWarp()) + TeleportNow = true; + + if (!MyAPIGateway.Utilities.IsDedicated) + { + if (currentSpeedPt < 316.6666) + { + // DrawAllLines(); + DrawAllLinesCenter2(); + DrawAllLinesCenter3(); + + if (WarpDropSound) + { + sound.PlaySound(WarpConstants.jumpOutSound, true); + sound.VolumeMultiplier = 1; + WarpDropSound = false; + } + } + else + DrawAllLinesCenter2(); + } + } + + if (TeleportNow && !SafeTriggerON) + { + TeleportNow = false; + + if (currentSpeedPt > 1f && gridMatrix != null) + { + gridMatrix.Translation += gridMatrix.Forward * currentSpeedPt; + + if (MyAPIGateway.Utilities.IsDedicated || MyAPIGateway.Multiplayer.IsServer) + MainGrid.Teleport(gridMatrix); + + if (!MyAPIGateway.Utilities.IsDedicated) + { + DrawAllLinesCenter1(); + + if (currentSpeedPt > 316.6666) + { + //StartBlinkParticleEffect(); + DrawAllLinesCenter4(); + } + } + } + } + } + + private bool InWarp() + { + if (grid.MainGrid == null) + return false; + + var MainGrid = grid.MainGrid; + var WarpDriveOnGrid = GetActiveWarpDrive(MainGrid); + + if (ShipSpeedResetTick++ >= 120) + { + ShipSpeedResetTick = 0; + + // clear ship speed in warp to prevent damage from asteroids, if ship speed is high there is high chance to get damage from passing in too asteroid. + if (MainGrid.Physics?.LinearVelocity.Length() >= 1f || MainGrid.Physics?.AngularVelocity.Length() >= 1f) + MainGrid.Physics.ClearSpeed(); + } + + if (PlayersInWarpList.Count > 0) + { + foreach (var Player in PlayersInWarpList) + { + if (Player == null || Player.Character == null) + continue; + + if (Player.Character.Save) + Player.Character.Save = false; + } + } + + if (IsInGravity()) + { + SendMessage(warnDestablalized); + + if (WarpDrive.Instance.Settings.AllowInGravity && GridGravityNow() > 0) + Dewarp(true); + else + Dewarp(); + + return false; + } + + if (WarpDrive.Instance.ProxymityDangerInWarp(gridMatrix, MainGrid, currentSpeedPt)) + { + currentSpeedPt = -1f; + + if (!MyAPIGateway.Utilities.IsDedicated) + ProxymityStop = true; + + SendMessage(EmergencyDropSpeed); + + // true here for ship speed to 0! collision detected. + Dewarp(true); + + if (WarpDriveOnGrid != null) + { + foreach (var ActiveDrive in GetActiveWarpDrives()) + { + if (ActiveDrive.Enabled) + { + ActiveDrive.Enabled = false; + if (!TempDisabledDrives.Contains(ActiveDrive)) + TempDisabledDrives.Add(ActiveDrive); + } + } + } + + return false; + } + + if (!hasEnoughPower) + { + SendMessage(warnNoPower); + Dewarp(); + + if (WarpDriveOnGrid != null) + { + foreach (var ActiveDrive in GetActiveWarpDrives()) + { + if (ActiveDrive.Enabled) + { + ActiveDrive.Enabled = false; + if (!TempDisabledDrives.Contains(ActiveDrive)) + TempDisabledDrives.Add(ActiveDrive); + } + } + } + + return false; + } + + if (functionalDrives == 0) + { + SendMessage(warnDamaged); + Dewarp(); + + return false; + } + + if (totalHeat >= WarpDrive.Instance.Settings.maxHeat) + { + SendMessage(warnOverheat); + Dewarp(); + + foreach (var ActiveDrive in GetActiveWarpDrives()) + { + if (ActiveDrive.Enabled) + { + ActiveDrive.Enabled = false; + if (!TempDisabledDrives.Contains(ActiveDrive)) + TempDisabledDrives.Add(ActiveDrive); + } + } + + return false; + } + + if (MyAPIGateway.Utilities.IsDedicated && PlayersInWarpList.Count > 0) + { + var PlayerFound = false; + foreach (var Player in PlayersInWarpList) + { + if (OnlinePlayersList.Contains(Player)) + PlayerFound = true; + } + + if (!PlayerFound) + { + // if player left server, stop warp and stop ship! + Dewarp(true); + + foreach (var ActiveDrive in GetActiveWarpDrives()) + { + if (ActiveDrive.Enabled) + { + ActiveDrive.Enabled = false; + if (!TempDisabledDrives.Contains(ActiveDrive)) + TempDisabledDrives.Add(ActiveDrive); + } + } + + return false; + } + } + + // Update Server/Client with WarpSpeed change. + if (!MyAPIGateway.Utilities.IsDedicated && MyAPIGateway.Multiplayer.IsServer) + { + var Hostplayer = MyAPIGateway.Session?.Player; + var cockpit = Hostplayer?.Character?.Parent as IMyShipController; + + bool NotPressed_f = MyAPIGateway.Input.IsGameControlPressed(MyControlsSpace.FORWARD); + bool NotPressed_b = MyAPIGateway.Input.IsGameControlPressed(MyControlsSpace.BACKWARD); + + if (WarpDriveOnGrid != null && WarpDriveSession.Instance.warpDrivesSpeeds.Count > 0) + { + double NewSpeed; + WarpDriveSession.Instance.warpDrivesSpeeds.TryGetValue(WarpDriveOnGrid, out NewSpeed); + + if (WarpDrive.Instance.Settings.AllowInGravity && GridGravityNow() > 0) + { + if (NewSpeed > WarpDrive.Instance.Settings.AllowInGravityMaxSpeed) + { + currentSpeedPt = 1000 / 60d; + WarpDriveSession.Instance.warpDrivesSpeeds[WarpDriveOnGrid] = currentSpeedPt; + } + else + currentSpeedPt = NewSpeed; + } + else if (NewSpeed > WarpDrive.Instance.Settings.maxSpeed) + { + currentSpeedPt = WarpDrive.Instance.Settings.maxSpeed; + WarpDriveSession.Instance.warpDrivesSpeeds[WarpDriveOnGrid] = currentSpeedPt; + } + else + currentSpeedPt = NewSpeed; + } + + if (Hostplayer != null && cockpit?.CubeGrid != null && grid.Contains((MyCubeGrid)cockpit.CubeGrid)) + { + if (!NotPressed_b && NotPressed_f) + { + if (SpeedUpSendToServerTick++ >= 10) + { + SpeedUpSendToServerTick = 0; + + if (WarpDrive.Instance.Settings.AllowInGravity && GridGravityNow() > 0) + { + if (currentSpeedPt > WarpDrive.Instance.Settings.AllowInGravityMaxSpeed) + { + currentSpeedPt = WarpDrive.Instance.Settings.AllowInGravityMaxSpeed; + + if (!WarpDriveSession.Instance.warpDrivesSpeeds.ContainsKey(WarpDriveOnGrid)) + WarpDriveSession.Instance.warpDrivesSpeeds.Add(WarpDriveOnGrid, WarpDrive.Instance.Settings.AllowInGravityMaxSpeed); + else + WarpDriveSession.Instance.warpDrivesSpeeds[WarpDriveOnGrid] = WarpDrive.Instance.Settings.AllowInGravityMaxSpeed; + } + else + { + currentSpeedPt += 15f; + + if (!WarpDriveSession.Instance.warpDrivesSpeeds.ContainsKey(WarpDriveOnGrid)) + WarpDriveSession.Instance.warpDrivesSpeeds.Add(WarpDriveOnGrid, currentSpeedPt); + else + WarpDriveSession.Instance.warpDrivesSpeeds[WarpDriveOnGrid] = currentSpeedPt; + } + } + else if (currentSpeedPt > WarpDrive.Instance.Settings.maxSpeed) + { + currentSpeedPt = WarpDrive.Instance.Settings.maxSpeed; + + if (!WarpDriveSession.Instance.warpDrivesSpeeds.ContainsKey(WarpDriveOnGrid)) + WarpDriveSession.Instance.warpDrivesSpeeds.Add(WarpDriveOnGrid, WarpDrive.Instance.Settings.maxSpeed); + else + WarpDriveSession.Instance.warpDrivesSpeeds[WarpDriveOnGrid] = WarpDrive.Instance.Settings.maxSpeed; + } + else + { + currentSpeedPt += 15f; + + if (!WarpDriveSession.Instance.warpDrivesSpeeds.ContainsKey(WarpDriveOnGrid)) + WarpDriveSession.Instance.warpDrivesSpeeds.Add(WarpDriveOnGrid, currentSpeedPt); + else + WarpDriveSession.Instance.warpDrivesSpeeds[WarpDriveOnGrid] = currentSpeedPt; + } + } + } + + if (!NotPressed_f && NotPressed_b) + { + if (SpeedDownSendToServerTick++ >= 10) + { + SpeedDownSendToServerTick = 0; + + currentSpeedPt -= 15f; + + if (currentSpeedPt < -1f) + currentSpeedPt = -5f; + + if (!WarpDriveSession.Instance.warpDrivesSpeeds.ContainsKey(WarpDriveOnGrid)) + WarpDriveSession.Instance.warpDrivesSpeeds.Add(WarpDriveOnGrid, currentSpeedPt); + else + WarpDriveSession.Instance.warpDrivesSpeeds[WarpDriveOnGrid] = currentSpeedPt; + } + } + + if (WarpDrive.Instance.Settings.AllowInGravity && GridGravityNow() > 0) + { + if (currentSpeedPt > WarpDrive.Instance.Settings.AllowInGravityMaxSpeed) + { + currentSpeedPt = WarpDrive.Instance.Settings.AllowInGravityMaxSpeed; + + if (!WarpDriveSession.Instance.warpDrivesSpeeds.ContainsKey(WarpDriveOnGrid)) + WarpDriveSession.Instance.warpDrivesSpeeds.Add(WarpDriveOnGrid, WarpDrive.Instance.Settings.AllowInGravityMaxSpeed); + else + WarpDriveSession.Instance.warpDrivesSpeeds[WarpDriveOnGrid] = WarpDrive.Instance.Settings.AllowInGravityMaxSpeed; + } + } + else if (currentSpeedPt > WarpDrive.Instance.Settings.maxSpeed) + { + currentSpeedPt = WarpDrive.Instance.Settings.maxSpeed; + + if (!WarpDriveSession.Instance.warpDrivesSpeeds.ContainsKey(WarpDriveOnGrid)) + WarpDriveSession.Instance.warpDrivesSpeeds.Add(WarpDriveOnGrid, WarpDrive.Instance.Settings.maxSpeed); + else + WarpDriveSession.Instance.warpDrivesSpeeds[WarpDriveOnGrid] = WarpDrive.Instance.Settings.maxSpeed; + } + + if (WarpDriveOnGrid != null && currentSpeedPt > 1) + { + MyAPIGateway.Multiplayer.SendMessageToOthers(WarpDriveSession.toggleWarpPacketIdSpeed, + message: MyAPIGateway.Utilities.SerializeToBinary(new SpeedMessage + { + EntityId = WarpDriveOnGrid.EntityId, + WarpSpeed = currentSpeedPt + })); + } + + if (currentSpeedPt <= -1f) + { + Dewarp(); + + if (WarpDriveOnGrid != null) + { + foreach (var ActiveDrive in GetActiveWarpDrives()) + { + if (ActiveDrive.Enabled) + { + ActiveDrive.Enabled = false; + if (!TempDisabledDrives.Contains(ActiveDrive)) + TempDisabledDrives.Add(ActiveDrive); + } + } + } + + return false; + } + } + } + else if (!MyAPIGateway.Utilities.IsDedicated && !MyAPIGateway.Multiplayer.IsServer) + { + bool NotPressed_f = MyAPIGateway.Input.IsGameControlPressed(MyControlsSpace.FORWARD); + bool NotPressed_b = MyAPIGateway.Input.IsGameControlPressed(MyControlsSpace.BACKWARD); + + // update speed + if (WarpDriveOnGrid != null && WarpDriveSession.Instance.warpDrivesSpeeds.Count > 0) + { + double NewSpeed; + WarpDriveSession.Instance.warpDrivesSpeeds.TryGetValue(WarpDriveOnGrid, out NewSpeed); + + if (NewSpeed != 0f) + { + if (WarpDrive.Instance.Settings.AllowInGravity && GridGravityNow() > 0) + { + if (NewSpeed > WarpDrive.Instance.Settings.AllowInGravityMaxSpeed) + { + currentSpeedPt = 1000 / 60d; + + if (!WarpDriveSession.Instance.warpDrivesSpeeds.ContainsKey(WarpDriveOnGrid)) + WarpDriveSession.Instance.warpDrivesSpeeds.Add(WarpDriveOnGrid, currentSpeedPt); + else + WarpDriveSession.Instance.warpDrivesSpeeds[WarpDriveOnGrid] = currentSpeedPt; + + WarpDriveSession.Instance.TransmitWarpSpeed(WarpDriveOnGrid, currentSpeedPt); + } + else + { + currentSpeedPt = NewSpeed; + + if (!WarpDriveSession.Instance.warpDrivesSpeeds.ContainsKey(WarpDriveOnGrid)) + WarpDriveSession.Instance.warpDrivesSpeeds.Add(WarpDriveOnGrid, currentSpeedPt); + else + WarpDriveSession.Instance.warpDrivesSpeeds[WarpDriveOnGrid] = currentSpeedPt; + } + } + else if (NewSpeed > WarpDrive.Instance.Settings.maxSpeed) + { + currentSpeedPt = WarpDrive.Instance.Settings.maxSpeed; + + if (!WarpDriveSession.Instance.warpDrivesSpeeds.ContainsKey(WarpDriveOnGrid)) + WarpDriveSession.Instance.warpDrivesSpeeds.Add(WarpDriveOnGrid, currentSpeedPt); + else + WarpDriveSession.Instance.warpDrivesSpeeds[WarpDriveOnGrid] = currentSpeedPt; + + WarpDriveSession.Instance.TransmitWarpSpeed(WarpDriveOnGrid, WarpDrive.Instance.Settings.maxSpeed); + } + else + { + currentSpeedPt = NewSpeed; + + if (!WarpDriveSession.Instance.warpDrivesSpeeds.ContainsKey(WarpDriveOnGrid)) + WarpDriveSession.Instance.warpDrivesSpeeds.Add(WarpDriveOnGrid, currentSpeedPt); + else + WarpDriveSession.Instance.warpDrivesSpeeds[WarpDriveOnGrid] = currentSpeedPt; + } + } + } + + if (!NotPressed_b && NotPressed_f) + { + if (SpeedUpSendToServerTick++ >= 10) + { + SpeedUpSendToServerTick = 0; + + if (WarpDriveOnGrid != null) + { + currentSpeedPt += 15f; + + if (WarpDrive.Instance.Settings.AllowInGravity && GridGravityNow() > 0) + { + if (currentSpeedPt > WarpDrive.Instance.Settings.AllowInGravityMaxSpeed) + currentSpeedPt = WarpDrive.Instance.Settings.AllowInGravityMaxSpeed; + } + else if (currentSpeedPt > WarpDrive.Instance.Settings.maxSpeed) + currentSpeedPt = WarpDrive.Instance.Settings.maxSpeed; + + if (!WarpDriveSession.Instance.warpDrivesSpeeds.ContainsKey(WarpDriveOnGrid)) + WarpDriveSession.Instance.warpDrivesSpeeds.Add(WarpDriveOnGrid, currentSpeedPt); + else + WarpDriveSession.Instance.warpDrivesSpeeds[WarpDriveOnGrid] = currentSpeedPt; + + WarpDriveSession.Instance.TransmitWarpSpeed(WarpDriveOnGrid, currentSpeedPt); + } + } + } + + if (!NotPressed_f && NotPressed_b) + { + if (SpeedDownSendToServerTick++ >= 10) + { + SpeedDownSendToServerTick = 0; + + if (WarpDriveOnGrid != null) + { + currentSpeedPt -= 15f; + + if (currentSpeedPt < -1f) + currentSpeedPt = -5f; + + if (!WarpDriveSession.Instance.warpDrivesSpeeds.ContainsKey(WarpDriveOnGrid)) + WarpDriveSession.Instance.warpDrivesSpeeds.Add(WarpDriveOnGrid, currentSpeedPt); + else + WarpDriveSession.Instance.warpDrivesSpeeds[WarpDriveOnGrid] = currentSpeedPt; + + WarpDriveSession.Instance.TransmitWarpSpeed(WarpDriveOnGrid, currentSpeedPt); + } + } + } + + if (WarpDrive.Instance.Settings.AllowInGravity && GridGravityNow() > 0) + { + if (currentSpeedPt > WarpDrive.Instance.Settings.AllowInGravityMaxSpeed) + { + currentSpeedPt = WarpDrive.Instance.Settings.AllowInGravityMaxSpeed; + + if (WarpDriveOnGrid != null) + { + if (!WarpDriveSession.Instance.warpDrivesSpeeds.ContainsKey(WarpDriveOnGrid)) + WarpDriveSession.Instance.warpDrivesSpeeds.Add(WarpDriveOnGrid, WarpDrive.Instance.Settings.AllowInGravityMaxSpeed); + else + WarpDriveSession.Instance.warpDrivesSpeeds[WarpDriveOnGrid] = WarpDrive.Instance.Settings.AllowInGravityMaxSpeed; + + WarpDriveSession.Instance.TransmitWarpSpeed(WarpDriveOnGrid, WarpDrive.Instance.Settings.AllowInGravityMaxSpeed); + } + } + } + else if (currentSpeedPt > WarpDrive.Instance.Settings.maxSpeed) + { + currentSpeedPt = WarpDrive.Instance.Settings.maxSpeed; + + if (WarpDriveOnGrid != null) + { + if (!WarpDriveSession.Instance.warpDrivesSpeeds.ContainsKey(WarpDriveOnGrid)) + WarpDriveSession.Instance.warpDrivesSpeeds.Add(WarpDriveOnGrid, WarpDrive.Instance.Settings.maxSpeed); + else + WarpDriveSession.Instance.warpDrivesSpeeds[WarpDriveOnGrid] = WarpDrive.Instance.Settings.maxSpeed; + + WarpDriveSession.Instance.TransmitWarpSpeed(WarpDriveOnGrid, WarpDrive.Instance.Settings.maxSpeed); + } + } + + if (currentSpeedPt <= -1f) + { + WarpDriveSession.Instance.TransmitWarpSpeed(WarpDriveOnGrid, -1f); + WarpDriveSession.Instance.TransmitToggleWarp(WarpDriveOnGrid); + + if (WarpDriveOnGrid != null) + { + foreach (var ActiveDrive in GetActiveWarpDrives()) + { + if (ActiveDrive.Enabled) + { + ActiveDrive.Enabled = false; + if (!TempDisabledDrives.Contains(ActiveDrive)) + TempDisabledDrives.Add(ActiveDrive); + } + } + } + + return false; + } + } + else if (MyAPIGateway.Utilities.IsDedicated) + { + if (WarpDriveOnGrid != null && WarpDriveSession.Instance.warpDrivesSpeeds.Count > 0) + { + double NewSpeed; + WarpDriveSession.Instance.warpDrivesSpeeds.TryGetValue(WarpDriveOnGrid, out NewSpeed); + + if (WarpDrive.Instance.Settings.AllowInGravity && GridGravityNow() > 0) + { + if (NewSpeed > WarpDrive.Instance.Settings.AllowInGravityMaxSpeed) + { + currentSpeedPt = 1000 / 60d; + WarpDriveSession.Instance.warpDrivesSpeeds[WarpDriveOnGrid] = currentSpeedPt; + } + else + currentSpeedPt = NewSpeed; + } + else if (NewSpeed > WarpDrive.Instance.Settings.maxSpeed) + { + currentSpeedPt = WarpDrive.Instance.Settings.maxSpeed; + WarpDriveSession.Instance.warpDrivesSpeeds[WarpDriveOnGrid] = currentSpeedPt; + } + else + currentSpeedPt = NewSpeed; + + if (WarpDriveOnGrid != null && currentSpeedPt > 1) + { + MyAPIGateway.Multiplayer.SendMessageToOthers(WarpDriveSession.toggleWarpPacketIdSpeed, + message: MyAPIGateway.Utilities.SerializeToBinary(new SpeedMessage + { + EntityId = WarpDriveOnGrid.EntityId, + WarpSpeed = currentSpeedPt + })); + } + } + + if (currentSpeedPt <= -1f) + { + Dewarp(); + + return false; + } + } + + // go for teleport. + return true; + } + + private float GetRadiusCenter() + { + MyCubeGrid sys = grid.MainGrid; + float s = 0f; + if (sys.GridSizeEnum == MyCubeSize.Small) + s = 0f; + Vector3I v = sys.Max - sys.Min; + v.Z = 20; + return ((float)v.Length() / 10) * s; + } + + // Center 1 + private void DrawAllLinesCenter1() + { + if (grid.MainGrid == null) + return; + + var MainGrid = grid.MainGrid; + + try + { + float r = Math.Max(GetRadiusCenter() + 0, 12); + Vector3D pos = MainGrid.Physics.CenterOfMassWorld; + + var SpeedCorrector = 1200 - (currentSpeedPt / 3); + Vector3D centerEnd = pos + (gridMatrix.Forward * 240); + + if (MainGrid.GridSizeEnum == MyCubeSize.Small) + { + SpeedCorrector = 600 - (currentSpeedPt / 3); + centerEnd = pos + (gridMatrix.Forward * 120); + } + + Vector3D centerStart = pos - (gridMatrix.Forward * SpeedCorrector); + + // DrawLine(centerStart + (gridMatrix.Left * r), centerEnd + (gridMatrix.Left * r), 15); + // DrawLine(centerStart + (gridMatrix.Right * r), centerEnd + (gridMatrix.Right * r), 15); + // DrawLineC(centerStart + (gridMatrix.Up * r), centerEnd + (gridMatrix.Up * r), 15); + if (MainGrid.GridSizeEnum == MyCubeSize.Small) + DrawLineCenter1(centerEnd + (gridMatrix.Down * r), centerStart + (gridMatrix.Down * r), 18); + else + DrawLineCenter1(centerEnd + (gridMatrix.Down * r), centerStart + (gridMatrix.Down * r), 38); + } + catch { } + } + + private void DrawLineCenter1(Vector3D startPos, Vector3D endPos, float rad) + { + Vector4 baseCol = Color.SteelBlue; + string material = "SciFiEngineThrustMiddle"; // IlluminatingShell ReflectorGlareAlphaBlended + float ranf = MyUtils.GetRandomFloat(1.1f * rad, 1.8f * rad); + MySimpleObjectDraw.DrawLine(startPos, endPos, MyStringId.GetOrCompute(material), ref baseCol, ranf); + MySimpleObjectDraw.DrawLine(startPos, endPos, MyStringId.GetOrCompute(material), ref baseCol, ranf * 0.66f); + MySimpleObjectDraw.DrawLine(startPos, endPos, MyStringId.GetOrCompute(material), ref baseCol, ranf * 0.33f); + } + + // Center 2 + private void DrawAllLinesCenter2() + { + if (grid.MainGrid == null) + return; + + var MainGrid = grid.MainGrid; + + try + { + float r = Math.Max(GetRadiusCenter() + 0, 12); + Vector3D pos = MainGrid.Physics.CenterOfMassWorld; + var SpeedCorrector = 1000 - (currentSpeedPt / 3); + Vector3D centerEnd = pos + (gridMatrix.Forward * 180); + + if (MainGrid.GridSizeEnum == MyCubeSize.Small) + { + SpeedCorrector = 500 - (currentSpeedPt / 3); + centerEnd = pos + (gridMatrix.Forward * 90); + } + + Vector3D centerStart = pos - (gridMatrix.Forward * SpeedCorrector); + // DrawLine(centerStart + (gridMatrix.Left * r), centerEnd + (gridMatrix.Left * r), 15); + // DrawLine(centerStart + (gridMatrix.Right * r), centerEnd + (gridMatrix.Right * r), 15); + // DrawLineC(centerStart + (gridMatrix.Up * r), centerEnd + (gridMatrix.Up * r), 15); + if (MainGrid.GridSizeEnum == MyCubeSize.Small) + DrawLineCenter2(centerEnd + (gridMatrix.Down * r), centerStart + (gridMatrix.Down * r), 18); + else + DrawLineCenter2(centerEnd + (gridMatrix.Down * r), centerStart + (gridMatrix.Down * r), 38); + } + catch { } + } + + private void DrawLineCenter2(Vector3D startPos, Vector3D endPos, float rad) + { + Vector4 baseCol = Color.CornflowerBlue; + string material = "SciFiEngineThrustMiddle"; // IlluminatingShell ReflectorGlareAlphaBlended + float ranf = MyUtils.GetRandomFloat(1.1f * rad, 1.8f * rad); + MySimpleObjectDraw.DrawLine(startPos, endPos, MyStringId.GetOrCompute(material), ref baseCol, ranf); + MySimpleObjectDraw.DrawLine(startPos, endPos, MyStringId.GetOrCompute(material), ref baseCol, ranf * 0.66f); + MySimpleObjectDraw.DrawLine(startPos, endPos, MyStringId.GetOrCompute(material), ref baseCol, ranf * 0.33f); + } + + //Center 3 + private void DrawAllLinesCenter3() + { + if (grid.MainGrid == null) + return; + + var MainGrid = grid.MainGrid; + + try + { + float r = Math.Max(GetRadiusCenter() + 0, 12); + Vector3D pos = MainGrid.Physics.CenterOfMassWorld; + var SpeedCorrector = 800 - (currentSpeedPt / 3); + Vector3D centerEnd = pos + (gridMatrix.Forward * 220); + + if (MainGrid.GridSizeEnum == MyCubeSize.Small) + { + SpeedCorrector = 400 - (currentSpeedPt / 3); + centerEnd = pos + (gridMatrix.Forward * 110); + } + + Vector3D centerStart = pos - (gridMatrix.Forward * SpeedCorrector); + // DrawLine(centerStart + (gridMatrix.Left * r), centerEnd + (gridMatrix.Left * r), 15); + // DrawLine(centerStart + (gridMatrix.Right * r), centerEnd + (gridMatrix.Right * r), 15); + // DrawLineC(centerStart + (gridMatrix.Up * r), centerEnd + (gridMatrix.Up * r), 15); + if (MainGrid.GridSizeEnum == MyCubeSize.Small) + DrawLineCenter3(centerEnd + (gridMatrix.Down * r), centerStart + (gridMatrix.Down * r), 18); + else + DrawLineCenter3(centerEnd + (gridMatrix.Down * r), centerStart + (gridMatrix.Down * r), 38); + } + catch { } + } + + private void DrawLineCenter3(Vector3D startPos, Vector3D endPos, float rad) + { + Vector4 baseCol = Color.Indigo; + string material = "SciFiEngineThrustMiddle"; // IlluminatingShell ReflectorGlareAlphaBlended + float ranf = MyUtils.GetRandomFloat(1.1f * rad, 1.8f * rad); + MySimpleObjectDraw.DrawLine(startPos, endPos, MyStringId.GetOrCompute(material), ref baseCol, ranf); + MySimpleObjectDraw.DrawLine(startPos, endPos, MyStringId.GetOrCompute(material), ref baseCol, ranf * 0.66f); + MySimpleObjectDraw.DrawLine(startPos, endPos, MyStringId.GetOrCompute(material), ref baseCol, ranf * 0.33f); + } + + //Center 4 + private void DrawAllLinesCenter4() + { + if (grid.MainGrid == null) + return; + + var MainGrid = grid.MainGrid; + + try + { + float r = Math.Max(GetRadiusCenter() + 0, 12); + Vector3D pos = MainGrid.Physics.CenterOfMassWorld; + var SpeedCorrector = 1500 - (currentSpeedPt / 3); + Vector3D centerEnd = pos + (gridMatrix.Forward * 90); + + if (MainGrid.GridSizeEnum == MyCubeSize.Small) + { + SpeedCorrector = 750 - (currentSpeedPt / 3); + centerEnd = pos + (gridMatrix.Forward * 45); + } + + Vector3D centerStart = pos - (gridMatrix.Forward * SpeedCorrector); + // DrawLine(centerStart + (gridMatrix.Left * r), centerEnd + (gridMatrix.Left * r), 15); + // DrawLine(centerStart + (gridMatrix.Right * r), centerEnd + (gridMatrix.Right * r), 15); + // DrawLineC(centerStart + (gridMatrix.Up * r), centerEnd + (gridMatrix.Up * r), 15); + + if (MainGrid.GridSizeEnum == MyCubeSize.Small) + DrawLineCenter4(centerEnd + (gridMatrix.Down * r), centerStart + (gridMatrix.Down * r), 18); + else + DrawLineCenter4(centerEnd + (gridMatrix.Down * r), centerStart + (gridMatrix.Down * r), 38); + } + catch { } + } + + private void DrawLineCenter4(Vector3D startPos, Vector3D endPos, float rad) + { + Vector4 baseCol = Color.LightGoldenrodYellow; + string material = "SciFiEngineThrustMiddle"; // IlluminatingShell ReflectorGlareAlphaBlended + float ranf = MyUtils.GetRandomFloat(1.1f * rad, 1.8f * rad); + MySimpleObjectDraw.DrawLine(startPos, endPos, MyStringId.GetOrCompute(material), ref baseCol, ranf); + MySimpleObjectDraw.DrawLine(startPos, endPos, MyStringId.GetOrCompute(material), ref baseCol, ranf * 0.66f); + MySimpleObjectDraw.DrawLine(startPos, endPos, MyStringId.GetOrCompute(material), ref baseCol, ranf * 0.33f); + } + + /* + private void StartBlinkParticleEffect() + { + if (MyAPIGateway.Utilities.IsDedicated) + return; + + if (grid.MainGrid == null) + return; + + try + { + BlinkTrailEffect?.Stop(); + + var Grid = grid.MainGrid as IMyCubeGrid; + Vector3D direction = gridMatrix.Forward; + + float gridDepthOffset = 0.09f * Grid.LocalAABB.Depth; + + if (Grid.LocalAABB.Depth < 45 && grid.MainGrid.GridSizeEnum == MyCubeSize.Large) + gridDepthOffset = 0.3f * Grid.LocalAABB.Depth; + else if (Grid.LocalAABB.Depth > 120 && grid.MainGrid.GridSizeEnum == MyCubeSize.Large) + gridDepthOffset = 0.05f * Grid.LocalAABB.Depth; + + float gridWidth = Grid.LocalAABB.Width > Grid.LocalAABB.Height ? Grid.LocalAABB.Width : Grid.LocalAABB.Height; + float scale = gridWidth * 2; + float particleHalfLength = 2.565f; + + MatrixD rotationMatrix = MatrixD.CreateFromYawPitchRoll(MathHelper.ToRadians(0), MathHelper.ToRadians(-90), MathHelper.ToRadians(0)); + rotationMatrix.Translation = new Vector3D(0, 0, (particleHalfLength * scale) + gridDepthOffset + Grid.GridSize); + + Vector3D effectOffset = direction * Grid.WorldAABB.HalfExtents.AbsMax(); + Vector3D origin = Grid.WorldAABB.Center; + + MatrixD fromDir = MatrixD.CreateFromDir(direction); + fromDir.Translation = origin - effectOffset; + + fromDir = rotationMatrix * fromDir; + + MyParticlesManager.TryCreateParticleEffect("BlinkDriveTrail", ref fromDir, ref origin, uint.MaxValue, out BlinkTrailEffect); + + BlinkTrailEffect.UserScale = scale; + + if (Grid.Physics != null) + BlinkTrailEffect.Velocity = Grid.Physics.LinearVelocity; + } + catch (Exception e) + { + MyLog.Default.Error(e.ToString()); + } + } + */ + + public void StopBlinkParticleEffect() + { + if (!MyAPIGateway.Utilities.IsDedicated) + BlinkTrailEffect?.Stop(); + } + + private bool FindPlayerInCockpit() + { + if (grid.MainGrid == null) + return false; + + HashSet gridCockpits; + if (grid.cockpits.TryGetValue(grid.MainGrid, out gridCockpits)) + { + if (gridCockpits.Count > 0) + { + foreach (IMyShipController cockpit in gridCockpits) + { + if (cockpit != null && cockpit.IsUnderControl) + return true; + } + } + } + + return false; + } + + public void ToggleWarp(IMyTerminalBlock block, IMyCubeGrid source, long PlayerID) + { + WarpDrive drive = block?.GameLogic?.GetAs(); + if (drive != null) + { + if (drive.System.WarpState == State.Idle) + { + if (!hasEnoughPower || !FindPlayerInCockpit()) + return; + + if (MyAPIGateway.Utilities.IsDedicated || MyAPIGateway.Multiplayer.IsServer) + { + WarpDriveSession.Instance.RefreshGridCockpits(block); + MatrixD gridMatrix = drive.System.grid.FindWorldMatrix(); + + if (WarpDrive.Instance.ProxymityDangerCharge(gridMatrix, source)) + { + SendMessage(ProximytyAlert, 2f, "Red", PlayerID); + WarpState = State.Idle; + return; + } + + MyAPIGateway.Multiplayer.SendMessageToOthers(WarpDriveSession.toggleWarpPacketId, + message: MyAPIGateway.Utilities.SerializeToBinary(new ItemsMessage + { + EntityId = block.EntityId, + SendingPlayerID = PlayerID + })); + } + + StartCharging(PlayerID); + startWarpSource = source; + + if (!MyAPIGateway.Utilities.IsDedicated && !MyAPIGateway.Multiplayer.IsServer) + WarpDriveSession.Instance.TransmitWarpConfig(Settings.Instance, block.EntityId); + } + else + { + drive.System.Dewarp(); + + var MyGrid = drive.Block.CubeGrid as MyCubeGrid; + if (GetActiveWarpDrive(MyGrid) != null) + { + foreach (var ActiveDrive in GetActiveWarpDrives()) + { + if (ActiveDrive.Enabled) + { + ActiveDrive.Enabled = false; + if (!TempDisabledDrives.Contains(ActiveDrive)) + TempDisabledDrives.Add(ActiveDrive); + } + } + } + } + } + } + + public bool Contains(WarpDrive drive) + { + return grid.Contains((MyCubeGrid)drive.Block.CubeGrid); + } + + private List FindAllPlayersInGrid(GridSystem System) + { + var PlayersIdList = new List(); + + if (System != null) + { + foreach (var grid in System.Grids) + { + foreach (var Block in grid.GetFatBlocks()) + { + if (Block == null) + continue; + + var Cockpit = Block as IMyCockpit; + var CryoChamber = Block as IMyCryoChamber; + + if (Cockpit != null) + { + if (Cockpit.Pilot != null) + { + PlayersIdList.Add(Cockpit.Pilot.EntityId); + continue; + } + } + + if (CryoChamber != null) + { + if (CryoChamber.Pilot != null) + PlayersIdList.Add(CryoChamber.Pilot.EntityId); + } + } + } + } + return PlayersIdList; + } + + private bool ConnectedStatic(IMyCubeGrid MyGrid) + { + if (MyGrid == null) + return false; + + var AttachedList = new List(); + MyAPIGateway.GridGroups.GetGroup(MyGrid, GridLinkTypeEnum.Physical, AttachedList); + + if (AttachedList.Count > 1) + { + foreach (var AttachedGrid in AttachedList) + { + if (AttachedGrid != null) + { + if (AttachedGrid.IsStatic) + return true; + } + } + } + return false; + } + + private void StartCharging(long PlayerID) + { + if (grid.MainGrid == null) + return; + + if (IsInGravity()) + { + SendMessage(warnNoEstablish, 5f, "Red", PlayerID); + WarpState = State.Idle; + return; + } + + if (ConnectedStatic(grid.MainGrid)) + { + SendMessage(warnStatic, 5f, "Red", PlayerID); + WarpState = State.Idle; + return; + } + + if (!grid.IsStatic) + { + WarpState = State.Charging; + startChargeRuntime = WarpDriveSession.Instance.Runtime; + + if (MyAPIGateway.Utilities.IsDedicated) + { + if (PlayerID > 0) + { + foreach (var Player in OnlinePlayersList) + { + if (Player.IdentityId == PlayerID) + { + if (!PlayersInWarpList.Contains(Player)) + PlayersInWarpList.Add(Player); + } + } + } + } + + if (!MyAPIGateway.Utilities.IsDedicated) + { + sound.PlaySound(WarpConstants.chargingSound, true); + sound.VolumeMultiplier = 2; + + PlayParticleEffect(); + } + } + else + SendMessage(warnStatic, 5f, "Red", PlayerID); + } + + private void StartWarp() + { + if (grid.MainGrid == null) + return; + + var MainGrid = grid.MainGrid; + + if (IsInGravity()) + { + SendMessage(warnNoEstablish); + return; + } + + if (grid.IsStatic) + { + SendMessage(warnStatic); + return; + } + + if (ConnectedStatic(MainGrid)) + { + SendMessage(warnStatic); + return; + } + + if (!MyAPIGateway.Utilities.IsDedicated) + { + if (effect != null) + StopParticleEffect(); + + sound.PlaySound(WarpConstants.jumpInSound, true); + sound.VolumeMultiplier = 1; + } + + WarpState = State.Active; + + Vector3D? currentVelocity = MainGrid?.Physics?.LinearVelocity; + if (currentVelocity.HasValue) + { + gridMatrix = grid.FindWorldMatrix(); + + /* // people asked to get the start speed no matter what was the ship normal speed before warp. + double dot = Vector3D.Dot(currentVelocity.Value, gridMatrix.Forward); + if (double.IsNaN(dot) || gridMatrix == MatrixD.Zero) + dot = 0; + + currentSpeedPt = MathHelper.Clamp(dot, WarpDrive.Instance.Settings.startSpeed, WarpDrive.Instance.Settings.maxSpeed); + */ + + if (WarpDrive.Instance.Settings.AllowInGravity && GridGravityNow() > 0) + { + currentSpeedPt = 1000 / 60d; + } + else + currentSpeedPt = WarpDrive.Instance.Settings.startSpeed; + + var WarpDriveOnGrid = GetActiveWarpDrive(MainGrid); + if (WarpDriveOnGrid != null) + { + if (!WarpDriveSession.Instance.warpDrivesSpeeds.ContainsKey(WarpDriveOnGrid)) + WarpDriveSession.Instance.warpDrivesSpeeds.Add(WarpDriveOnGrid, currentSpeedPt); + else + WarpDriveSession.Instance.warpDrivesSpeeds[WarpDriveOnGrid] = currentSpeedPt; + } + } + else + { + if (WarpDrive.Instance.Settings.AllowInGravity && GridGravityNow() > 0) + { + currentSpeedPt = 1000 / 60d; + } + else + currentSpeedPt = WarpDrive.Instance.Settings.startSpeed; + + var WarpDriveOnGrid = GetActiveWarpDrive(MainGrid); + if (WarpDriveOnGrid != null) + { + if (!WarpDriveSession.Instance.warpDrivesSpeeds.ContainsKey(WarpDriveOnGrid)) + WarpDriveSession.Instance.warpDrivesSpeeds.Add(WarpDriveOnGrid, currentSpeedPt); + else + WarpDriveSession.Instance.warpDrivesSpeeds[WarpDriveOnGrid] = currentSpeedPt; + } + } + + var PlayersIdsOnGrid = FindAllPlayersInGrid(grid); + + if (PlayersIdsOnGrid != null && PlayersIdsOnGrid.Count > 0) + { + foreach (var OnlinePlayer in OnlinePlayersList) + { + if (OnlinePlayer.Character != null && PlayersIdsOnGrid.Contains(OnlinePlayer.Character.EntityId) && !PlayersInWarpList.Contains(OnlinePlayer)) + PlayersInWarpList.Add(OnlinePlayer); + } + } + } + + private IMyFunctionalBlock GetActiveWarpDrive(MyCubeGrid MyGrid) + { + HashSet controllingDrives; + if (startWarpSource == null || !warpDrives.TryGetValue(startWarpSource, out controllingDrives)) + { + if (MyGrid == null || !warpDrives.TryGetValue(MyGrid, out controllingDrives)) + controllingDrives = warpDrives.FirstPair().Value; + } + + if (controllingDrives == null) + return null; + + foreach (WarpDrive drive in controllingDrives) + { + if (drive.Block.IsFunctional && drive.Block.IsWorking) + return drive.Block; + } + return null; + } + + private List GetActiveWarpDrives() + { + HashSet controllingDrives; + var GridDrives = new List(); + if (startWarpSource == null || !warpDrives.TryGetValue(startWarpSource, out controllingDrives)) + { + if (grid.MainGrid == null || !warpDrives.TryGetValue(grid.MainGrid, out controllingDrives)) + controllingDrives = warpDrives.FirstPair().Value; + } + + if (controllingDrives == null) + controllingDrives = new HashSet(); + + foreach (WarpDrive drive in controllingDrives) + { + if (drive.Block.IsFunctional && drive.Block.IsWorking) + GridDrives.Add(drive.Block); + } + return GridDrives; + } + + public void Dewarp(bool Collision = false) + { + if (PlayersInWarpList.Count > 0) + { + foreach (var Player in PlayersInWarpList) + { + if (Player == null || Player.Character == null) + continue; + + if (!Player.Character.Save) + Player.Character.Save = true; + } + } + + TeleportNow = false; + + if (grid.MainGrid == null) + return; + + var MainGrid = grid.MainGrid; + var WarpDriveOnGrid = GetActiveWarpDrive(MainGrid); + + if (WarpDriveOnGrid != null && WarpState == State.Active && (MyAPIGateway.Multiplayer.IsServer || MyAPIGateway.Utilities.IsDedicated)) + { + if (WarpDriveOnGrid != null) + { + MyAPIGateway.Multiplayer.SendMessageToOthers(WarpDriveSession.toggleWarpPacketId, + message: MyAPIGateway.Utilities.SerializeToBinary(new ItemsMessage + { + EntityId = WarpDriveOnGrid.EntityId, + SendingPlayerID = 0 + })); + } + } + + if (!MyAPIGateway.Utilities.IsDedicated) + { + StopParticleEffect(); + StopBlinkParticleEffect(); + + sound.SetPosition(MainGrid.PositionComp.GetPosition()); + sound?.StopSound(false); + + if (WarpState == State.Active) + { + if (ProxymityStop) + { + sound.PlaySound(WarpConstants.jumpOutSound, true); + sound.VolumeMultiplier = 1; + ProxymityStop = false; + } + else + { + if (currentSpeedPt < -1) + { + sound.PlaySound(WarpConstants.jumpOutSound, true); + sound.VolumeMultiplier = 1; + } + + if (functionalDrives == 0) + { + sound.PlaySound(WarpConstants.EmergencyDropSound, true); + sound.VolumeMultiplier = 1; + } + + if (!hasEnoughPower) + { + sound.PlaySound(WarpConstants.EmergencyDropSound, true); + sound.VolumeMultiplier = 1; + } + + if (IsInGravity()) + { + sound.PlaySound(WarpConstants.EmergencyDropSound, true); + sound.VolumeMultiplier = 1; + } + + sound.PlaySound(WarpConstants.jumpOutSound, true); + sound.VolumeMultiplier = 1; + } + } + } + + if (WarpState == State.Active && !Collision) + { + if (MainGrid.Physics != null && GridSpeedLinearVelocity.ContainsKey(MainGrid.EntityId)) + { + MainGrid.Physics.LinearVelocity = GridSpeedLinearVelocity[MainGrid.EntityId]; + MainGrid.Physics.AngularVelocity = GridSpeedAngularVelocity[MainGrid.EntityId]; + } + } + else if (WarpState == State.Active && Collision) + MainGrid?.Physics?.ClearSpeed(); + + WarpState = State.Idle; + + currentSpeedPt = WarpDrive.Instance.Settings.startSpeed; + + if (PlayersInWarpList.Count > 0) + PlayersInWarpList.Clear(); + + if (WarpDriveOnGrid != null) + { + if (WarpDriveSession.Instance == null) + return; + + if (!WarpDriveSession.Instance.warpDrivesSpeeds.ContainsKey(WarpDriveOnGrid)) + WarpDriveSession.Instance.warpDrivesSpeeds.Add(WarpDriveOnGrid, currentSpeedPt); + else + WarpDriveSession.Instance.warpDrivesSpeeds[WarpDriveOnGrid] = currentSpeedPt; + } + } + + private void InCharge() + { + if (grid.MainGrid == null) + return; + + var MainGrid = grid.MainGrid; + + if (functionalDrives == 0) + { + if (!MyAPIGateway.Utilities.IsDedicated) + { + sound.PlaySound(WarpConstants.EmergencyDropSound, true); + sound.VolumeMultiplier = 1; + } + SendMessage(warnDamaged); + Dewarp(); + return; + } + + if (!hasEnoughPower) + { + if (!MyAPIGateway.Utilities.IsDedicated) + { + sound.PlaySound(WarpConstants.EmergencyDropSound, true); + sound.VolumeMultiplier = 1; + } + SendMessage(warnNoPower); + Dewarp(); + return; + } + + if (IsInGravity()) + { + if (!MyAPIGateway.Utilities.IsDedicated) + { + sound.PlaySound(WarpConstants.EmergencyDropSound, true); + sound.VolumeMultiplier = 1; + } + SendMessage(warnNoEstablish); + Dewarp(); + return; + } + + if (grid.IsStatic) + { + SendMessage(warnStatic); + Dewarp(); + return; + } + + if (ConnectedStatic(MainGrid)) + { + SendMessage(warnStatic); + Dewarp(); + return; + } + + if (!MyAPIGateway.Utilities.IsDedicated) + { + if (effect != null) + effect.WorldMatrix = MatrixD.CreateWorld(effect.WorldMatrix.Translation, -gridMatrix.Forward, gridMatrix.Up); + + UpdateParticleEffect(); + } + + if (WarpDrive.Instance.Settings.AllowToDetectEnemyGrids && WarpDrive.Instance.EnemyProxymityDangerCharge(MainGrid)) + { + var DelayTime = WarpDrive.Instance.Settings.DelayJumpIfEnemyIsNear * 60; + var ElapsedTime = Math.Abs(WarpDriveSession.Instance.Runtime - startChargeRuntime); + var ElapsedTimeDevided = ElapsedTime / 60; + + if (ElapsedTime >= DelayTime) + { + if (MainGrid != null && MainGrid.Physics != null) + { + // store ship speed before WARP. so we can restore it when exit warp. + GridSpeedLinearVelocity[MainGrid.EntityId] = MainGrid.Physics.LinearVelocity; + GridSpeedAngularVelocity[MainGrid.EntityId] = MainGrid.Physics.AngularVelocity; + } + + StartWarp(); + } + else if (ElapsedTimeDevided == 11 || ElapsedTimeDevided == 21 || ElapsedTimeDevided == 31 || ElapsedTimeDevided == 41 || ElapsedTimeDevided == 51) + { + if (!MyAPIGateway.Utilities.IsDedicated) + { + StopParticleEffectNow(); + PlayParticleEffect(); + } + } + } + else + { + if (Math.Abs(WarpDriveSession.Instance.Runtime - startChargeRuntime) >= WarpDrive.Instance.Settings.DelayJump * 60) + { + if (MainGrid.Physics != null) + { + // store ship speed before WARP. so we can restore it when exit warp. + GridSpeedLinearVelocity[MainGrid.EntityId] = MainGrid.Physics.LinearVelocity; + GridSpeedAngularVelocity[MainGrid.EntityId] = MainGrid.Physics.AngularVelocity; + } + + StartWarp(); + } + } + } + + bool IsInGravity() + { + if (grid == null || grid.MainGrid == null) + return true; + + var MainGrid = grid.MainGrid; + var gravityVectorTemp = 0.0f; + Vector3D position = MainGrid.PositionComp.GetPosition(); + var gravityVector = MyAPIGateway.Physics.CalculateNaturalGravityAt(position, out gravityVectorTemp); + var GridGravityCalc = gravityVector.Length() / EARTH_GRAVITY; + + if (WarpDrive.Instance.Settings.AllowInGravity) + { + if (GridGravityCalc > WarpDrive.Instance.Settings.AllowInGravityMax) + return true; + + if (GridGravityCalc > 0) + { + var worldAABB = MainGrid.PositionComp.WorldAABB; + var closestPlanet = MyGamePruningStructure.GetClosestPlanet(ref worldAABB); + + if (closestPlanet != null && MainGrid.Physics != null) + { + var centerOfMassWorld = MainGrid.Physics.CenterOfMassWorld; + var closestSurfacePointGlobal = closestPlanet.GetClosestSurfacePointGlobal(ref centerOfMassWorld); + var elevation = double.PositiveInfinity; + + elevation = Vector3D.Distance(closestSurfacePointGlobal, centerOfMassWorld); + + return elevation < WarpDrive.Instance.Settings.AllowInGravityMinAltitude && elevation != double.PositiveInfinity; + } + else + return false; + } + else + return false; + } + + return GridGravityCalc > 0.01; + } + + float GridGravityNow() + { + if (grid == null || grid.MainGrid == null) + return 0; + + var gravityVectorTemp = 0.0f; + Vector3D position = grid.MainGrid.PositionComp.GetPosition(); + var gravityVector = MyAPIGateway.Physics.CalculateNaturalGravityAt(position, out gravityVectorTemp); + var GridGravityCalc = gravityVector.Length() / EARTH_GRAVITY; + + return GridGravityCalc; + } + + private void UpdateHeatPower() + { + float totalPower = 0; + int numFunctional = 0; + hasEnoughPower = true; + + try + { + if (warpDrives == null || warpDrives.Count == 0) + return; + + HashSet controllingDrives = new HashSet(); + if (startWarpSource == null || !warpDrives.TryGetValue(startWarpSource, out controllingDrives)) + { + if (grid.MainGrid == null || !warpDrives.TryGetValue(grid.MainGrid, out controllingDrives)) + controllingDrives = warpDrives.FirstPair().Value; + } + + if (WarpState == State.Charging) + { + if (controllingDrives == null) + controllingDrives = new HashSet(); + + foreach (WarpDrive drive in controllingDrives) + { + if (drive == null || drive.Block == null || drive.Block.CubeGrid == null) + continue; + + float _mass = 0f; + + if (!GridsMass.ContainsKey(drive.Block.CubeGrid.EntityId)) + { + _mass = CulcucateGridGlobalMass(drive.Block.CubeGrid); + GridsMass.Add(drive.Block.CubeGrid.EntityId, _mass); + } + else + _mass = GridsMass[drive.Block.CubeGrid.EntityId]; + + if (MassChargeUpdate >= 60) + { + MassChargeUpdate = 0; + _mass = CulcucateGridGlobalMass(drive.Block.CubeGrid); + GridsMass[drive.Block.CubeGrid.EntityId] = _mass; + } + else + MassChargeUpdate++; + + if (_mass == 0) + { + if (drive.Block.CubeGrid.GridSizeEnum == MyCubeSize.Small) + _mass = 150000f; + else + _mass = 500000f; + } + + if (drive.Block.CubeGrid.GridSizeEnum == MyCubeSize.Small) + { + if (drive.Block.BlockDefinition.SubtypeId == "FSDriveSmall") + totalPower = WarpDrive.Instance.Settings.baseRequiredPowerSmall + (_mass * 2.1f / 100000f); + } + else + { + if (drive.Block.BlockDefinition.SubtypeId == "FSDriveLarge") + totalPower = WarpDrive.Instance.Settings.baseRequiredPower + (_mass * 2.1f / 1000000f); + } + } + } + + if (WarpState == State.Active && grid.MainGrid != null) + { + float _mass; + var MainGrid = grid.MainGrid; + + if (GridsMass.ContainsKey(MainGrid.EntityId)) + { + if (MassUpdateTick++ >= 1200) + { + MassUpdateTick = 0; + _mass = CulcucateGridGlobalMass(MainGrid); + GridsMass[MainGrid.EntityId] = _mass; + } + else + _mass = GridsMass[MainGrid.EntityId]; + } + else + { + _mass = CulcucateGridGlobalMass(MainGrid); + GridsMass.Add(MainGrid.EntityId, _mass); + } + + float SpeedNormalize = (float)(currentSpeedPt * 0.06); // 60 / 1000 + float SpeedCalc = 1f + (SpeedNormalize * SpeedNormalize); + + float MassCalc; + if (MainGrid.GridSizeEnum == MyCubeSize.Small) + MassCalc = _mass * (SpeedCalc / 0.528f) / 700000f; + else + MassCalc = _mass * (SpeedCalc / 0.528f) / 1000000f; + + float percent = (float)(1f + currentSpeedPt / WarpDrive.Instance.Settings.maxSpeed * WarpDrive.Instance.Settings.powerRequirementMultiplier) + MassCalc; + + if (percent == 0) + percent = 1; + + foreach (WarpDrive drive in controllingDrives) + { + if (drive == null || drive.Block == null || drive.Block.CubeGrid == null) + continue; + + if (drive.Block.IsFunctional && drive.Block.IsWorking) + { + if (drive.Block.CubeGrid.GridSizeEnum == MyCubeSize.Small) + { + if (drive.Block.BlockDefinition.SubtypeId == "FSDriveSmall") + totalPower = (WarpDrive.Instance.Settings.baseRequiredPowerSmall + percent) / WarpDrive.Instance.Settings.powerRequirementBySpeedDeviderSmall; + } + else + { + if (drive.Block.BlockDefinition.SubtypeId == "FSDriveLarge") + totalPower = (WarpDrive.Instance.Settings.baseRequiredPower + percent) / WarpDrive.Instance.Settings.powerRequirementBySpeedDeviderLarge; + } + } + } + } + + foreach (WarpDrive drive in controllingDrives) + { + if (drive == null || drive.Block == null) + continue; + + if (drive.Block.IsFunctional && drive.Block.IsWorking) + { + numFunctional++; + + if (functionalDrives == 0) + { + // First tick + drive.RequiredPower = totalPower / controllingDrives.Count; + } + else + { + if (WarpState != State.Idle) + { + // give SIM some chance before drop warp if power check missed. + if (PowerCheckTick++ > 20) + { + PowerCheckTick = 0; + var LocalcurrentSpeedPt = currentSpeedPt; + + if (!drive.HasPower) + { + if (LocalcurrentSpeedPt > 90) + { + currentSpeedPt -= 90f; + + if (MyAPIGateway.Utilities.IsDedicated) + { + if (WarpDriveSession.Instance.warpDrivesSpeeds.ContainsKey(drive.Block)) + WarpDriveSession.Instance.warpDrivesSpeeds[drive.Block] = currentSpeedPt; + } + else if (!MyAPIGateway.Utilities.IsDedicated && MyAPIGateway.Multiplayer.IsServer) + { + if (WarpDriveSession.Instance.warpDrivesSpeeds.ContainsKey(drive.Block)) + WarpDriveSession.Instance.warpDrivesSpeeds[drive.Block] = currentSpeedPt; + } + else if (!MyAPIGateway.Utilities.IsDedicated && !MyAPIGateway.Multiplayer.IsServer) + { + if (WarpDriveSession.Instance.warpDrivesSpeeds.ContainsKey(drive.Block)) + WarpDriveSession.Instance.warpDrivesSpeeds[drive.Block] = currentSpeedPt; + + WarpDriveSession.Instance.TransmitWarpSpeed(drive.Block, currentSpeedPt); + } + } + else + { + hasEnoughPower = false; + drive.RequiredPower = totalPower / functionalDrives; + return; + } + } + } + drive.RequiredPower = totalPower / functionalDrives; + } + else + { + if (drive.RequiredPower != 0) + drive.RequiredPower = 0; + } + } + } + else + { + if (drive.RequiredPower != 0) + drive.RequiredPower = 0; + } + } + + functionalDrives = numFunctional; + + if (WarpState == State.Active) + totalHeat += WarpDrive.Instance.Settings.heatGain; + else + totalHeat -= WarpDrive.Instance.Settings.heatDissipationDrive * numFunctional; + + if (!MyAPIGateway.Utilities.IsDedicated) + { + if (totalHeat <= 0) + { + totalHeat = 0; + DriveHeat = 0; + } + else + DriveHeat = (int)(totalHeat / WarpDrive.Instance.Settings.maxHeat * 100); + } + + if (totalHeat <= 0) + totalHeat = 0; + + if (WarpState == State.Charging && grid.MainGrid != null) + { + int percentHeat = (int)(totalHeat / WarpDrive.Instance.Settings.maxHeat * 100); + var ElapsedTime = Math.Abs(WarpDriveSession.Instance.Runtime - startChargeRuntime) / 60; + + var MaxSecondsToWarp = WarpDrive.Instance.Settings.DelayJump; + var SecondsToWarp = 0.0; + string display = ""; + string font = "White"; + + if (WarpDrive.Instance.Settings.AllowToDetectEnemyGrids && WarpDrive.Instance.EnemyProxymityDangerCharge(grid.MainGrid)) + { + MaxSecondsToWarp = WarpDrive.Instance.Settings.DelayJumpIfEnemyIsNear; + SecondsToWarp = MaxSecondsToWarp - ElapsedTime; + + font = "Red"; + if (percentHeat > 0) + display = $"Enemy Detected!!!\nHeat: {percentHeat}%\nPower Usage: {totalPower}Mw\nSeconds to Warp: {SecondsToWarp}"; + else + display = $"Enemy Detected!!!\nPower Usage: {totalPower}Mw\nSeconds to Warp: {SecondsToWarp}"; + } + else + { + SecondsToWarp = MaxSecondsToWarp - ElapsedTime; + if (percentHeat > 0) + display = $"Heat: {percentHeat}%\nPower Usage: {totalPower}Mw\nSeconds to Warp: {SecondsToWarp}"; + else + display = $"Power Usage: {totalPower}Mw\nSeconds to Warp: {SecondsToWarp}"; + } + + if (percentHeat >= 65) + font = "Red"; + if (percentHeat >= 75) + display += '!'; + if (percentHeat >= 85) + display += '*'; + if (percentHeat >= 90) + display += '*'; + if (percentHeat >= 95) + display += '*'; + + if (MyAPIGateway.Utilities.IsDedicated) + { + if (_updateTicks++ >= 61) + { + SendMessage(display, 1f, font); + _updateTicks = 0; + } + } + else + { + if (_updateTicks++ >= 62) + { + SendMessage(display, 1f, font); + _updateTicks = 0; + } + } + } + + if (WarpState == State.Active) + { + if (totalHeat > 0) + { + int percentHeat = (int)(totalHeat / WarpDrive.Instance.Settings.maxHeat * 100); + string display = $"Heat: {percentHeat}%\nPower Usage : {totalPower}Mw"; + string font = "White"; + if (percentHeat >= 75) + display += '!'; + if (percentHeat >= 85) + { + display += '*'; + font = "Red"; + } + if (percentHeat >= 90) + display += '*'; + if (percentHeat >= 95) + display += '*'; + + string msg = $"Speed: {currentSpeedPt * 60 / 1000:0} km/s\n{display}"; + + if (MyAPIGateway.Utilities.IsDedicated) + { + if (_updateTicks++ >= 61) + { + SendMessage(msg, 1f, font); + _updateTicks = 0; + } + } + else + { + if (_updateTicks++ >= 62) + { + SendMessage(msg, 1f, font); + _updateTicks = 0; + } + } + } + else + { + string msg = $"Speed: {currentSpeedPt * 60 / 1000:0} km/s\n Power Usage : {totalPower}Mw"; + + if (MyAPIGateway.Utilities.IsDedicated) + { + if (_updateTicks++ >= 61) + { + SendMessage(msg, 1f, "White"); + _updateTicks = 0; + } + } + else + { + if (_updateTicks++ >= 62) + { + SendMessage(msg, 1f, "White"); + _updateTicks = 0; + } + } + } + } + } + catch { } + } + + private void PlayParticleEffect() + { + if (effect != null) + { + effect.Play(); + return; + } + + if (grid.MainGrid == null) + return; + + var MainGrid = grid.MainGrid; + Vector3D forward = gridMatrix.Forward; + MatrixD fromDir = MatrixD.CreateFromDir(-forward); + Vector3D origin = MainGrid.PositionComp.WorldAABB.Center; + Vector3D effectOffset = forward * MainGrid.PositionComp.WorldAABB.HalfExtents.AbsMax() * 2.0; + fromDir.Translation = MainGrid.PositionComp.WorldAABB.Center + effectOffset; + + var IGrid = MainGrid as IMyCubeGrid; + float gridWidth = IGrid.LocalAABB.Width > IGrid.LocalAABB.Height ? IGrid.LocalAABB.Width : IGrid.LocalAABB.Height; + float scale = gridWidth / 30; + + if (MainGrid.GridSizeEnum == MyCubeSize.Large) + scale = gridWidth / 60; + + MyParticlesManager.TryCreateParticleEffect("WarpStart", ref fromDir, ref origin, uint.MaxValue, out effect); + + if (effect != null) + effect.UserScale = scale; + } + + private void UpdateParticleEffect() + { + if (effect == null || effect.IsStopped || grid.MainGrid == null) + return; + + var MainGrid = grid.MainGrid; + Vector3D forward = gridMatrix.Forward; + Vector3D effectOffset = forward * MainGrid.PositionComp.WorldAABB.HalfExtents.AbsMax() * 2.0; + Vector3D origin = MainGrid.PositionComp.WorldAABB.Center + effectOffset; + + effect.SetTranslation(ref origin); + } + + private void StopParticleEffect() + { + if (effect == null) + return; + + effect.StopEmitting(10f); + effect = null; + } + + private void StopParticleEffectNow() + { + if (effect == null) + return; + + effect.Stop(); + effect = null; + } + + public float CulcucateGridGlobalMass(IMyCubeGrid Grid) + { + float GlobalMass = 1f; + + float mass; + float physicalMass; + float currentMass = 0; + var MyGrid = Grid as MyCubeGrid; + + if (MyGrid != null) + currentMass = MyGrid.GetCurrentMass(out mass, out physicalMass, GridLinkTypeEnum.Physical); + + if (currentMass > 0) + GlobalMass = currentMass; + + return GlobalMass; + } + + private void OnSystemInvalidated(GridSystem system) + { + if (!MyAPIGateway.Utilities.IsDedicated) + { + sound?.StopSound(true); + effect?.Stop(); + BlinkTrailEffect?.Stop(); + } + OnSystemInvalidatedAction?.Invoke(this); + OnSystemInvalidatedAction = null; + } + + public void SendMessage(string msg, float seconds = 5, string font = "Red", long PlayerID = 0L) + { + var Hostplayer = MyAPIGateway.Session?.Player; + var cockpit = Hostplayer?.Character?.Parent as IMyShipController; + + if (OnlinePlayersList != null && OnlinePlayersList.Count > 0 && PlayerID > 0) + { + foreach (var SelectedPlayer in OnlinePlayersList) + { + if (SelectedPlayer.IdentityId == PlayerID) + { + MyVisualScriptLogicProvider.ShowNotification(msg, (int)(seconds * 1000), font, SelectedPlayer.IdentityId); + return; + } + } + } + + if (Hostplayer != null && cockpit?.CubeGrid != null && grid.Contains((MyCubeGrid)cockpit.CubeGrid)) + MyVisualScriptLogicProvider.ShowNotification(msg, (int)(seconds * 1000), font, Hostplayer.IdentityId); + + if (OnlinePlayersList != null && OnlinePlayersList.Count > 0) + { + foreach (var ClientPlayer in OnlinePlayersList) + { + if (Hostplayer != null && ClientPlayer.IdentityId == Hostplayer.IdentityId) + continue; + + var ClientCockpit = ClientPlayer?.Character?.Parent as IMyShipController; + + if (ClientCockpit?.CubeGrid != null && grid.Contains((MyCubeGrid)ClientCockpit.CubeGrid)) + MyVisualScriptLogicProvider.ShowNotification(msg, (int)(seconds * 1000), font, ClientPlayer.IdentityId); + } + } + } + + private void OnDriveAdded(IMyCubeBlock block) + { + WarpDrive drive = block.GameLogic.GetAs(); + HashSet gridDrives; + drive.SetWarpSystem(this); + + if (!warpDrives.TryGetValue(block.CubeGrid, out gridDrives)) + gridDrives = new HashSet(); + + gridDrives.Add(drive); + warpDrives[block.CubeGrid] = gridDrives; + } + + private void OnDriveRemoved(IMyCubeBlock block) + { + WarpDrive drive = block.GameLogic.GetAs(); + HashSet gridDrives; + + if (warpDrives.TryGetValue(block.CubeGrid, out gridDrives)) + { + gridDrives.Remove(drive); + + if (GridsMass.ContainsKey(drive.Block.CubeGrid.EntityId)) + GridsMass.Remove(drive.Block.CubeGrid.EntityId); + + if (gridDrives.Count > 0) + warpDrives[block.CubeGrid] = gridDrives; + else + warpDrives.Remove(block.CubeGrid); + } + } + + public override bool Equals(object obj) + { + var system = obj as WarpSystem; + return system != null && Id == system.Id; + } + + public override int GetHashCode() + { + return 2108858624 + Id.GetHashCode(); + } + + public enum State + { + Idle, Charging, Active + } + } +} diff --git a/Slipspace Engine/Data/WarpStart.sbc b/Slipspace Engine/Data/WarpStart.sbc new file mode 100644 index 00000000..11672be0 --- /dev/null +++ b/Slipspace Engine/Data/WarpStart.sbc @@ -0,0 +1,6055 @@ + + + + + + + + + 87538799 + 14 + 0 + false + false + 15 + 0 + 0 + + + + GPU + + + + 4 + 4 + 0 + + + + 7 + + + 1 + + + + + + + + + + + 0 + 0 + 0 + 0 + + + + + + + 0.02618081 + 0.04614253 + 0.09777576 + 0.04777576 + + + + + + + 0.02618081 + 0.04614253 + 0.14777576 + 0.04777576 + + + + + + + 0 + 0 + 0 + 0 + + + + + + + + + + + + + + + + + 500 + + + + + 1000 + + + + + 5000 + + + + + 100 + + + + + + + + + 0.5 + + + + + + + 20 + 20 + 40 + + + + + + + + + + 0 + + + + + + + 0 + 0 + -1 + + + + + + + 10 + + + + + 100 + + + + + + + + + 0 + + + + + + + + + 0 + + + + + + + + + 180 + + + + + + + 0 + 0 + -200 + + + + + + + + + + + 0 + + + + + 0 + + + + + 0 + + + + + 0 + + + + + + + + + 0 + + + + + + + + + + 1.5 + + + + + 1.5 + + + + + 1.5 + + + + + 1.5 + + + + + + + + + 0.5 + + + 1 + + + 2 + + + 1 + + + true + + + + + + 1000 + + + + + 0 + + + + + + Atlas_E_01 + + + 1 + + + false + + + false + + + false + + + false + + + 1 + + + 0 + + + + 0 + 0 + 0 + + + + 0 + + + 0 + + + false + + + 1 + + + 0 + + + false + + + 1 + + + + 0 + 0 + 0 + + + + + 0 + 0 + 0 + + + + + + + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + + + + + + + + 0 + + + + + + + + + + 0 + + + + + 0 + + + + + 0 + + + + + 0 + + + + + + + + + 5 + + + false + + + false + + + 1 + + + 0 + + + 1 + + + 0 + + + 0 + + + true + + + 0 + + + + + GPU + + + + 4 + 4 + 0 + + + + 7 + + + 1 + + + + + + + + + + + 0 + 0 + 0 + 0 + + + + + + + 0.02618081 + 0.00614253 + 0.00777576 + 0.04777576 + + + + + + + 0.02618081 + 0.04614253 + 0.14777576 + 0.04777576 + + + + + + + 0 + 0 + 0 + 0 + + + + + + + + + + + + + + + + + 10 + + + + + 180 + + + + + 1 + + + + + 0 + + + + + + + + + 0.5 + + + + + + + 0 + 0 + 0 + + + + + + + + + + 0 + + + + + + + 0 + 0 + -1 + + + + + + + 55 + + + + + 80 + + + + + 100 + + + + + + + + + 0 + + + + + + + + + 0.9 + + + + + + + + + 180 + + + + + + + 0 + 0 + 0 + + + + + + + + + + + 0 + + + + + 0 + + + + + 0 + + + + + 0 + + + + + + + + + 0 + + + + + + + + + + 0.9 + + + + + 1.1 + + + + + 1.3 + + + + + 1.8 + + + + + + + + + 4 + + + 1 + + + 0.5 + + + 1 + + + true + + + + + + 0 + + + + + 0 + + + + + 10000 + + + + + 10000 + + + + + 0 + + + + + + Atlas_E_01 + + + 1 + + + false + + + false + + + true + + + false + + + 1 + + + 0 + + + + 0 + 0 + 0 + + + + 0 + + + 0 + + + true + + + 1 + + + 0 + + + true + + + 0 + + + + 0 + 0 + 0 + + + + + 0 + 0 + 0 + + + + + + + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + + + + + + + + 0 + + + + + + + + + + 0 + + + + + 0 + + + + + 0 + + + + + 0 + + + + + + + + + 5 + + + false + + + false + + + 1 + + + 0 + + + 1 + + + 0 + + + 0 + + + true + + + 0 + + + + + + GPU + + + + 4 + 4 + 0 + + + + 7 + + + 1 + + + + + + + + + + + 0 + 0 + 0 + 0 + + + + + + + 0.02618081 + 0.04614253 + 0.09777576 + 0.04777576 + + + + + + + 0.02618081 + 0.04614253 + 0.14777576 + 0.04777576 + + + + + + + 0 + 0 + 0 + 0 + + + + + + + + + + + + + + + + + 500 + + + + + 1500 + + + + + 1400 + + + + + 0 + + + + + + + + + 0.5 + + + + + + + 0 + 0 + 0 + + + + + + + + + + 0 + + + + + + + 0 + 0 + -1 + + + + + + + 100 + + + + + + + + + 20 + + + + + + + + + 0 + + + + + + + + + 360 + + + + + + + 0 + 0 + 0 + + + + + + + + + + + 0 + + + + + 0 + + + + + 0 + + + + + 0 + + + + + + + + + 0 + + + + + + + + + + 1.3 + + + + + 1.3 + + + + + 1.3 + + + + + 1.3 + + + + + + + + + 10 + + + 1 + + + 0.05 + + + 1 + + + true + + + + + + 0 + + + + + 0 + + + + + 50000 + + + + + 10000 + + + + + 0 + + + + + + Atlas_E_01 + + + 1 + + + false + + + false + + + true + + + false + + + 1 + + + 0 + + + + 0 + 0 + 0 + + + + 0 + + + 0 + + + true + + + 1 + + + 0 + + + true + + + 0 + + + + 0 + 0 + 0 + + + + + 0 + 0 + 0 + + + + + + + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + + + + + + + + 0 + + + + + + + + + + 0 + + + + + 0 + + + + + 0 + + + + + 0 + + + + + + + + + 5 + + + false + + + false + + + 1 + + + 0 + + + 1 + + + 0 + + + 0 + + + true + + + 0 + + + + + + + GPU + + + + 4 + 4 + 0 + + + + 7 + + + 1 + + + + + + + + + + + 0 + 0 + 0 + 0 + + + + + + + 0.02618081 + 0.00614253 + 0.00777576 + 0.04777576 + + + + + + + 0.02618081 + 0.04614253 + 0.14777576 + 0.04777576 + + + + + + + 0 + 0 + 0 + 0 + + + + + + + + + + + + + + + + + 10 + + + + + 180 + + + + + 1 + + + + + 0 + + + + + + + + + 0.5 + + + + + + + 0 + 0 + 0 + + + + + + + + + + 0 + + + + + + + 0 + 0 + -1 + + + + + + + 30 + + + + + 60 + + + + + + + + + 0 + + + + + + + + + 0.9 + + + + + + + + + 180 + + + + + + + 0 + 0 + 0 + + + + + + + + + + + 0 + + + + + 0 + + + + + 0 + + + + + 0 + + + + + + + + + 0 + + + + + + + + + + 0.3 + + + + + 0.3 + + + + + 0.3 + + + + + 0.5 + + + + + + + + + 3 + + + 1 + + + 0.5 + + + 1 + + + true + + + + + + 0 + + + + + 100 + + + + + 0 + + + + + 0 + + + + + 0 + + + + + 0 + + + + + 0 + + + + + 0 + + + + + 10000 + + + + + 10000 + + + + + 0 + + + + + 0 + + + + + 10000 + + + + + 10000 + + + + + 0 + + + + + 0 + + + + + 10000 + + + + + 10000 + + + + + 0 + + + + + 0 + + + + + 10000 + + + + + 10000 + + + + + 0 + + + + + 0 + + + + + 10000 + + + + + 10000 + + + + + 0 + + + + + 0 + + + + + 10000 + + + + + 0 + + + + + + Atlas_E_01 + + + 1 + + + false + + + false + + + true + + + false + + + 1 + + + 0 + + + + 0 + 0 + 0 + + + + 0 + + + 0 + + + true + + + 1 + + + 0 + + + true + + + 0 + + + + 0 + 0 + 0 + + + + + 0 + 0 + 0 + + + + + + + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + + + + + + + + 0 + + + + + + + + + + 0 + + + + + 0 + + + + + 0 + + + + + 0 + + + + + + + + + 5 + + + false + + + false + + + 1 + + + 0 + + + 1 + + + 0 + + + 0 + + + true + + + 0 + + + + + + GPU + + + + 4 + 4 + 0 + + + + 7 + + + 1 + + + + + + + + + + + 0 + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + 0 + + + + + + + 0.1195825 + 0.2083884 + 0.2157644 + 0.2157644 + + + + + + + 0 + 0 + 0 + 0 + + + + + + + + + + + + + + + + + 100 + + + + + 600 + + + + + 600 + + + + + 50 + + + + + + + + + 0.5 + + + + + + + 20 + 20 + 40 + + + + + + + + + + 0.9 + + + + + + + 0 + 0 + -1 + + + + + + + 40 + + + + + 300 + + + + + + + + + 0 + + + + + + + + + 0.8 + + + + + + + + + 30 + + + + + + + 0 + 0 + -200 + + + + + + + + + + + 0 + + + + + 0 + + + + + 0 + + + + + 0 + + + + + + + + + 0 + + + + + + + + + + 0.4 + + + + + 0.5 + + + + + 0.8 + + + + + 1.0 + + + + + + + + + 0.5 + + + 1 + + + 0.3 + + + 1 + + + true + + + + + + 1 + + + + + 50 + + + + + 0 + + + + + + Atlas_E_01 + + + 1 + + + false + + + false + + + false + + + false + + + 1 + + + 0 + + + + 0 + 0 + 30 + + + + 0 + + + 0 + + + true + + + 1 + + + 0 + + + true + + + 0 + + + + 0 + 0 + 0 + + + + + 0 + 0 + 0 + + + + + + + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + + + + + + + + 0 + + + + + + + + + + 0 + + + + + 0 + + + + + 0 + + + + + 0 + + + + + + + + + 5 + + + false + + + false + + + 1 + + + 0 + + + 1 + + + 0 + + + 0 + + + true + + + 0 + + + + + + GPU + + + + 4 + 4 + 0 + + + + 6 + + + 1 + + + + + + + + + + + 0 + 0 + 0 + 0 + + + + + + + 0.02618081 + 0.04614253 + 0.09777576 + 0.04777576 + + + + + + + 0.02618081 + 0.04614253 + 0.14777576 + 0.04777576 + + + + + + + 0 + 0 + 0 + 0 + + + + + + + + + + + + + + + + + 1 + + + + + 10 + + + + + 10 + + + + + 0 + + + + + + + + + 0.5 + + + + + + + 0 + 0 + 0 + + + + + + + + + + 0 + + + + + + + 0 + 0 + 1 + + + + + + + -600 + + + + + -700 + + + + + -800 + + + + + + + + + 0 + + + + + + + + + 0 + + + + + + + + + 0 + + + + + + + 0 + 0 + 0 + + + + + + + + + + + 0 + + + + + 0 + + + + + 0 + + + + + 0 + + + + + + + + + 0 + + + + + + + + + + 30 + + + + + 40 + + + + + 60 + + + + + 60 + + + + + + + + + 0.5 + + + 1 + + + 4 + + + 1.5 + + + true + + + + + + 1 + + + + + 1 + + + + + 0 + + + + + + Atlas_E_01 + + + 1 + + + false + + + false + + + true + + + false + + + 1 + + + 0 + + + + 0 + 0 + 0 + + + + 0 + + + 0 + + + true + + + 1 + + + 0 + + + false + + + 1 + + + + 0 + 0 + 0 + + + + + 0 + 0 + 0 + + + + + + + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + + + + + + + + 0 + + + + + + + + + + 0 + + + + + 0 + + + + + 0 + + + + + 0 + + + + + + + + + 5 + + + false + + + false + + + 1 + + + 0 + + + 1 + + + 0 + + + 0 + + + true + + + 0 + + + + + GPU + + + + 4 + 4 + 0 + + + + 6 + + + 1 + + + + + + + + + + + 0 + 0 + 0 + 0 + + + + + + + 0.02618081 + 0.04614253 + 0.09777576 + 0.04777576 + + + + + + + 0.02618081 + 0.04614253 + 0.14777576 + 0.04777576 + + + + + + + 0 + 0 + 0 + 0 + + + + + + + + + + + + + + + + + 1 + + + + + 10 + + + + + 10 + + + + + 1 + + + + + + + + + 0.5 + + + + + + + 0 + 0 + 0 + + + + + + + + + + 0 + + + + + + + 0 + 0 + 1 + + + + + + + -200 + + + + + -350 + + + + + -500 + + + + + + + + + 0 + + + + + + + + + 0 + + + + + + + + + 0 + + + + + + + 0 + 0 + 0 + + + + + + + + + + + 0 + + + + + 0 + + + + + 0 + + + + + 0 + + + + + + + + + 0 + + + + + + + + + + 40 + + + + + 40 + + + + + 40 + + + + + 40 + + + + + + + + + 0.5 + + + 1 + + + 4 + + + 1.5 + + + true + + + + + + 1 + + + + + 5 + + + + + 0 + + + + + + Atlas_E_01 + + + 1 + + + false + + + false + + + true + + + false + + + 1 + + + 0 + + + + 0 + 0 + -1 + + + + 0 + + + 0 + + + true + + + 1 + + + 0 + + + false + + + 1 + + + + 0 + 0 + 0 + + + + + 0 + 0 + 0 + + + + + + + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + + + + + + + + 0 + + + + + + + + + + 0 + + + + + 0 + + + + + 0 + + + + + 0 + + + + + + + + + 5 + + + false + + + false + + + 1 + + + 0 + + + 1 + + + 0 + + + 0 + + + true + + + 0 + + + + + + GPU + + + + 16 + 16 + 0 + + + + 114 + + + 1 + + + + + + + + + + + 0 + 0 + 0 + 0 + + + + + + + 0.02618081 + 0.04614253 + 0.09777576 + 0.04777576 + + + + + + + 0.02618081 + 0.04614253 + 0.14777576 + 0.04777576 + + + + + + + 0 + 0 + 0 + 0 + + + + + + + + + + + + + + + + + 10 + + + + + 30 + + + + + 30 + + + + + 1 + + + + + + + + + 0.5 + + + + + + + 0 + 0 + 0 + + + + + + + + + + 0 + + + + + + + 0 + 0 + -1 + + + + + + + 0 + + + + + + + + + 0 + + + + + + + + + 0 + + + + + + + + + 0 + + + + + + + 0 + 0 + 0 + + + + + + + + + + + 0 + + + + + 0 + + + + + 0 + + + + + 0 + + + + + + + + + 1 + + + + + + + + + + 0 + + + + + 1 + + + + + 50 + + + + + 1 + + + + + + + + + 1.5 + + + 1 + + + 4 + + + 1 + + + true + + + + + + 0 + + + + + 10 + + + + + 10 + + + + + 0 + + + + + + Atlas_E_01 + + + 1 + + + false + + + false + + + false + + + false + + + 1 + + + 0 + + + + 0 + 0 + 0 + + + + 0 + + + 0 + + + true + + + 1 + + + 0 + + + false + + + 0 + + + + 0 + 0 + 0 + + + + + 0 + 0 + 0 + + + + + + + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + + + + + + + + 0 + + + + + + + + + + 0 + + + + + 0 + + + + + 0 + + + + + 0 + + + + + + + + + 5 + + + false + + + false + + + 1 + + + 0 + + + 1 + + + 0 + + + 0 + + + true + + + 0 + + + + + GPU + + + + 16 + 16 + 0 + + + + 113 + + + 1 + + + + + + + + + + + 0 + 0 + 0 + 0 + + + + + + + 0.02618081 + 0.04614253 + 0.09777576 + 0.04777576 + + + + + + + 0.02618081 + 0.04614253 + 0.14777576 + 0.04777576 + + + + + + + 0 + 0 + 0 + 0 + + + + + + + + + + + + + + + + + 10 + + + + + 10 + + + + + 10 + + + + + 10 + + + + + + + + + 0.5 + + + + + + + 0 + 0 + 0 + + + + + + + + + + 0 + + + + + + + 0 + 0 + -1 + + + + + + + 0 + + + + + + + + + 0 + + + + + + + + + 0 + + + + + + + + + 0 + + + + + + + 0 + 0 + 0 + + + + + + + + + + + 0 + + + + + 0 + + + + + 0 + + + + + 0 + + + + + + + + + 1 + + + + + + + + + + 0 + + + + + 5 + + + + + 15 + + + + + 1 + + + + + + + + + 2 + + + 1 + + + 4 + + + 1 + + + true + + + + + + 0 + + + + + 10 + + + + + 10 + + + + + 0 + + + + + + Atlas_E_01 + + + 1 + + + false + + + false + + + false + + + false + + + 1 + + + 0 + + + + 0 + 0 + 0 + + + + 0 + + + 0 + + + true + + + 1 + + + 0 + + + false + + + 0 + + + + 0 + 0 + 0 + + + + + 0 + 0 + 0 + + + + + + + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + + + + + + + + 0 + + + + + + + + + + 0 + + + + + 0 + + + + + 0 + + + + + 0 + + + + + + + + + 5 + + + false + + + false + + + 1 + + + 0 + + + 1 + + + 0 + + + 0 + + + true + + + 0 + + + + + GPU + + + + 4 + 4 + 0 + + + + 6 + + + 1 + + + + + + + + + + + 0 + 0 + 0 + 0 + + + + + + + 0.02618081 + 0.04614253 + 0.09777576 + 0.04777576 + + + + + + + 0.02618081 + 0.04614253 + 0.14777576 + 0.04777576 + + + + + + + 0 + 0 + 0 + 0 + + + + + + + + + + + + + + + + + 10 + + + + + 10 + + + + + 10 + + + + + 10 + + + + + + + + + 0.5 + + + + + + + 0 + 0 + 0 + + + + + + + + + + 0 + + + + + + + 0 + 0 + -1 + + + + + + + 0 + + + + + + + + + 0 + + + + + + + + + 0 + + + + + + + + + 0 + + + + + + + 0 + 0 + 0 + + + + + + + + + + + 0 + + + + + 0 + + + + + 0 + + + + + 0 + + + + + + + + + 0 + + + + + + + + + + 0 + + + + + 8 + + + + + 30 + + + + + 1 + + + + + + + + + 0.6 + + + 1 + + + 4 + + + 1 + + + true + + + + + + 0 + + + + + 0 + + + + + 0 + + + + + 0 + + + + + + Atlas_E_01 + + + 1 + + + false + + + false + + + false + + + false + + + 1 + + + 0 + + + + 0 + 0 + 0 + + + + 0 + + + 0 + + + false + + + 1 + + + 0 + + + false + + + 1 + + + + 0 + 0 + 0 + + + + + 0 + 0 + 0 + + + + + + + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + + + + + + + + 1 + + + + + + 0 + + + + + + + + + + 0 + + + + + 0 + + + + + 0 + + + + + 0 + + + + + + + + + 5 + + + false + + + false + + + 1 + + + 0 + + + 1 + + + 0 + + + 0 + + + true + + + 0 + + + + + GPU + + + + 8 + 8 + 0 + + + + 56 + + + 1 + + + + + + + + + + + 0 + 0 + 0 + 0 + + + + + + + 0.02618081 + 0.04614253 + 0.09777576 + 0.04777576 + + + + + + + 0.02618081 + 0.04614253 + 0.14777576 + 0.04777576 + + + + + + + 0 + 0 + 0 + 0 + + + + + + + + + + + + + + + + + 1 + + + + + 25 + + + + + 25 + + + + + 1 + + + + + + + + + 0.5 + + + + + + + 0 + 0 + 0 + + + + + + + + + + 0 + + + + + + + 0 + 0 + -1 + + + + + + + 0 + + + + + + + + + 0 + + + + + + + + + 0 + + + + + + + + + 0 + + + + + + + 0 + 0 + 0 + + + + + + + + + + + 0 + + + + + 0 + + + + + 0 + + + + + 0 + + + + + + + + + 10 + + + + + + + + + + 0 + + + + + 1 + + + + + 300 + + + + + 1 + + + + + + + + + 0.1 + + + 1 + + + 4 + + + 1 + + + true + + + + + + 0 + + + + + 8 + + + + + 8 + + + + + 0 + + + + + + Atlas_E_01 + + + 1 + + + false + + + false + + + false + + + false + + + 1 + + + 0 + + + + 0 + 0 + 0 + + + + 0 + + + 0 + + + true + + + 1 + + + 0 + + + false + + + 0 + + + + 0 + 0 + 0 + + + + + 0 + 0 + 0 + + + + + + + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + + + + + + + + 0 + + + + + + + + + + 0 + + + + + 0 + + + + + 0 + + + + + 0 + + + + + + + + + 5 + + + false + + + false + + + 1 + + + 0 + + + 1 + + + 0 + + + 0 + + + true + + + 0 + + + + + GPU + + + + 16 + 16 + 0 + + + + 64 + + + 8 + + + + + + + + + + + 0.023529413 + 0.0784314 + 0.2901961 + 1 + + + + + + + 0.023529413 + 0.047058824 + 0.286274523 + 1 + + + + + + + 0.0117647061 + 0.1254902 + 0.1764706 + 1 + + + + + + + + + + + + + + + + + 200 + + + + + 200 + + + + + 200 + + + + + 200 + + + + + + + + + 0.5 + + + + + + + 0 + 0 + 0 + + + + + + + + + + 0.99 + + + + + + + 1 + 0 + 0 + + + + + + + 0.001 + + + + + + + + + 0 + + + + + + + + + 0 + + + + + + + + + 15 + + + + + 20 + + + + + + + 0 + 0 + 0 + + + + + + + + + + + 0 + + + + + 0 + + + + + 0 + + + + + 0 + + + + + + + + + 0 + + + + + + + + + + 20.5 + + + + + 21 + + + + + 21 + + + + + 0.16 + + + + + + + + + 0.1 + + + 0.1 + + + 1 + + + 0.02 + + + true + + + + + + 10 + + + + + + Atlas_E_01 + + + 1 + + + true + + + false + + + false + + + false + + + 1 + + + 0 + + + + 0 + 0 + 0 + + + + 0 + + + 0 + + + true + + + 1 + + + 0.001 + + + false + + + 0 + + + + 0 + 0 + 0 + + + + + 0 + 0 + 0 + + + + + + + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + + + + + + + + 0 + + + + + + + + + + 0 + + + + + 0 + + + + + 0 + + + + + 0 + + + + + + + + + 0 + + + false + + + false + + + 1 + + + 0.9 + + + 1 + + + 0 + + + 0 + + + true + + + 0 + + + + + GPU + + + + 32 + 16 + 0 + + + + 448 + + + 32 + + + + + + + + + + + 0 + 0 + 0 + 0 + + + + + + + 0.043137258 + 0.043137258 + 0.443137258 + 0.196078435 + + + + + + + 0 + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + 0 + + + + + + + + + + + + + + + + + 1 + + + + + 100 + + + + + 50 + + + + + 1 + + + + + + + + + 0.5 + + + + + + + 0.1 + 0.1 + 0.1 + + + + + + + + + + 0.3 + + + + + + + 0 + 1 + 0 + + + + + + + 60 + + + + + 160 + + + + + 230 + + + + + + + + + 4 + + + + + + + + + 0 + + + + + + + + + 360 + + + + + + + 0 + 0 + 0 + + + + + + + + + + + 0 + + + + + 0 + + + + + 0 + + + + + 0 + + + + + + + + + 0 + + + + + + + + + + 20.6 + + + + + 25.8 + + + + + 28 + + + + + 25.5 + + + + + + + + + 0.2 + + + 5 + + + 1 + + + 0.005 + + + true + + + + + + 5 + + + + + 10 + + + + + + Atlas_D_01 + + + 1 + + + false + + + false + + + true + + + true + + + 1 + + + 0 + + + + 0 + 0.6 + 0 + + + + 0 + + + 0 + + + true + + + 1 + + + 0 + + + false + + + 0 + + + + 0 + 0 + 0 + + + + + 0 + 0 + 0 + + + + + + + + + + + 0 + + + + + 0.8 + + + + + 1.3 + + + + + 1.3 + + + + + + + + + + + + 0 + + + + + + 0 + + + + + + + + + + 0 + + + + + 3000 + + + + + 2000 + + + + + 0 + + + + + + + + + 1 + + + false + + + false + + + 1 + + + 0.5 + + + 1 + + + 0 + + + 0 + + + true + + + 0 + + + + + + + + + + + + + + + + + + + 1 + 1 + 1 + 1 + + + + + + + 0 + 0.3 + 1 + 10 + + + + + + + 0.3 + 0.3 + 1 + 100 + + + + + + + + + + + + + 50 + + + + + 500 + + + + + 1000 + + + + + 1500 + + + + + + + + + + + + 0 + + + + + 30 + + + + + 220 + + + + + 0 + + + + + + + + + true + + + 0 + + + 1 + + + 0.1 + + + + + 5000 + + + \ No newline at end of file diff --git a/Slipspace Engine/Models/Cubes/Small/MiniJumpDrive.mwm b/Slipspace Engine/Models/Cubes/Small/MiniJumpDrive.mwm new file mode 100644 index 00000000..69ea44d7 Binary files /dev/null and b/Slipspace Engine/Models/Cubes/Small/MiniJumpDrive.mwm differ diff --git a/Slipspace Engine/Models/Cubes/Small/MiniJumpDriveConstruction_1.mwm b/Slipspace Engine/Models/Cubes/Small/MiniJumpDriveConstruction_1.mwm new file mode 100644 index 00000000..34dceda4 Binary files /dev/null and b/Slipspace Engine/Models/Cubes/Small/MiniJumpDriveConstruction_1.mwm differ diff --git a/Slipspace Engine/Models/Cubes/Small/MiniJumpDriveConstruction_1_LOD1.mwm b/Slipspace Engine/Models/Cubes/Small/MiniJumpDriveConstruction_1_LOD1.mwm new file mode 100644 index 00000000..c90897e7 Binary files /dev/null and b/Slipspace Engine/Models/Cubes/Small/MiniJumpDriveConstruction_1_LOD1.mwm differ diff --git a/Slipspace Engine/Models/Cubes/Small/MiniJumpDriveConstruction_2.mwm b/Slipspace Engine/Models/Cubes/Small/MiniJumpDriveConstruction_2.mwm new file mode 100644 index 00000000..c4b61187 Binary files /dev/null and b/Slipspace Engine/Models/Cubes/Small/MiniJumpDriveConstruction_2.mwm differ diff --git a/Slipspace Engine/Models/Cubes/Small/MiniJumpDriveConstruction_2_LOD1.mwm b/Slipspace Engine/Models/Cubes/Small/MiniJumpDriveConstruction_2_LOD1.mwm new file mode 100644 index 00000000..aab8b5a0 Binary files /dev/null and b/Slipspace Engine/Models/Cubes/Small/MiniJumpDriveConstruction_2_LOD1.mwm differ diff --git a/Slipspace Engine/Models/Cubes/Small/MiniJumpDriveConstruction_3.mwm b/Slipspace Engine/Models/Cubes/Small/MiniJumpDriveConstruction_3.mwm new file mode 100644 index 00000000..39420348 Binary files /dev/null and b/Slipspace Engine/Models/Cubes/Small/MiniJumpDriveConstruction_3.mwm differ diff --git a/Slipspace Engine/Models/Cubes/Small/MiniJumpDriveConstruction_3_LOD1.mwm b/Slipspace Engine/Models/Cubes/Small/MiniJumpDriveConstruction_3_LOD1.mwm new file mode 100644 index 00000000..9bb14f84 Binary files /dev/null and b/Slipspace Engine/Models/Cubes/Small/MiniJumpDriveConstruction_3_LOD1.mwm differ diff --git a/Slipspace Engine/Models/Cubes/Small/MiniJumpDrive_LOD1.mwm b/Slipspace Engine/Models/Cubes/Small/MiniJumpDrive_LOD1.mwm new file mode 100644 index 00000000..87f5d6c9 Binary files /dev/null and b/Slipspace Engine/Models/Cubes/Small/MiniJumpDrive_LOD1.mwm differ diff --git a/Slipspace Engine/Models/Cubes/Small/MiniJumpDrive_LOD2.mwm b/Slipspace Engine/Models/Cubes/Small/MiniJumpDrive_LOD2.mwm new file mode 100644 index 00000000..9315d369 Binary files /dev/null and b/Slipspace Engine/Models/Cubes/Small/MiniJumpDrive_LOD2.mwm differ diff --git a/Slipspace Engine/Models/Cubes/Small/MiniJumpDrive_LOD3.mwm b/Slipspace Engine/Models/Cubes/Small/MiniJumpDrive_LOD3.mwm new file mode 100644 index 00000000..e7cbc782 Binary files /dev/null and b/Slipspace Engine/Models/Cubes/Small/MiniJumpDrive_LOD3.mwm differ diff --git a/Slipspace Engine/Models/JumpDrive.mwm b/Slipspace Engine/Models/JumpDrive.mwm new file mode 100644 index 00000000..d30b50fa Binary files /dev/null and b/Slipspace Engine/Models/JumpDrive.mwm differ diff --git a/Slipspace Engine/Models/JumpDriveConstruction_1.mwm b/Slipspace Engine/Models/JumpDriveConstruction_1.mwm new file mode 100644 index 00000000..d8161c81 Binary files /dev/null and b/Slipspace Engine/Models/JumpDriveConstruction_1.mwm differ diff --git a/Slipspace Engine/Models/JumpDriveConstruction_2.mwm b/Slipspace Engine/Models/JumpDriveConstruction_2.mwm new file mode 100644 index 00000000..284803b3 Binary files /dev/null and b/Slipspace Engine/Models/JumpDriveConstruction_2.mwm differ diff --git a/Slipspace Engine/Models/JumpDriveConstruction_3.mwm b/Slipspace Engine/Models/JumpDriveConstruction_3.mwm new file mode 100644 index 00000000..1e1b1204 Binary files /dev/null and b/Slipspace Engine/Models/JumpDriveConstruction_3.mwm differ diff --git a/Slipspace Engine/Textures/GUI/Icons/Cubes/JumpDriveSmall.dds b/Slipspace Engine/Textures/GUI/Icons/Cubes/JumpDriveSmall.dds new file mode 100644 index 00000000..9b4bcd47 Binary files /dev/null and b/Slipspace Engine/Textures/GUI/Icons/Cubes/JumpDriveSmall.dds differ diff --git a/Slipspace Engine/Textures/GUI/Icons/QDVK.dds b/Slipspace Engine/Textures/GUI/Icons/QDVK.dds new file mode 100644 index 00000000..b6d16d15 Binary files /dev/null and b/Slipspace Engine/Textures/GUI/Icons/QDVK.dds differ diff --git a/Slipspace Engine/Textures/GUI/Icons/QDXL.dds b/Slipspace Engine/Textures/GUI/Icons/QDXL.dds new file mode 100644 index 00000000..861a84d0 Binary files /dev/null and b/Slipspace Engine/Textures/GUI/Icons/QDXL.dds differ diff --git a/Slipspace Engine/metadata.mod b/Slipspace Engine/metadata.mod new file mode 100644 index 00000000..0a020fdf --- /dev/null +++ b/Slipspace Engine/metadata.mod @@ -0,0 +1,4 @@ + + + 1.0 + \ No newline at end of file