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