Skip to content

Instantly share code, notes, and snippets.

@thexa4
Last active January 24, 2023 03:23
Show Gist options
  • Save thexa4/1b106528767cba12629f42838f6fecf1 to your computer and use it in GitHub Desktop.
Save thexa4/1b106528767cba12629f42838f6fecf1 to your computer and use it in GitHub Desktop.
Space Engineers scripts
bool _autoPowerSave = true;
IMyTextSurface _cockpitSurface;
RectangleF _viewport;
List<IMyCargoContainer> _containers = new List<IMyCargoContainer>();
List<IMyEntity> _storages = new List<IMyEntity>();
IMyGasGenerator _splitter;
List<IMyBatteryBlock> _batteries = new List<IMyBatteryBlock>();
IMyPowerProducer _generator;
List<IMyGasTank> _tanks = new List<IMyGasTank>();
IMyCockpit _cockpit;
IMyRemoteControl _remoteControl;
IMyRadioAntenna _antenna;
List<IMyLightingBlock> _headLights = new List<IMyLightingBlock>();
IMyMotorStator _leftHinge;
IMyMotorStator _rightHinge;
IMyShipMergeBlock _topLeftMerge;
IMyShipMergeBlock _bottomLeftMerge;
IMyShipMergeBlock _topRightMerge;
IMyShipMergeBlock _bottomRightMerge;
IMyMotorSuspension _frontLeftWheel;
IMyMotorSuspension _frontRightWheel;
MyItemType _iceType = new MyItemType("MyObjectBuilder_Ore", "Ice");
const double _iceConversionRatio = 20.0;
double _fuelTankSize = 1;
bool _wasDocked = false;
bool _isDeployed = false;
bool _isInconsistent = true;
bool _isStowing = false;
bool _isDeploying = false;
TimeSpan _lastDeployAction = TimeSpan.Zero;
TimeSpan _uptime = TimeSpan.Zero;
TimeSpan _timeIdle = TimeSpan.Zero;
private void ReInit()
{
_timeIdle = TimeSpan.Zero;
_lastDeployAction = TimeSpan.Zero;
_isStowing = false;
_isDeploying = false;
GridTerminalSystem.GetBlocksOfType(_containers, b => b.IsSameConstructAs(Me));
GridTerminalSystem.GetBlocksOfType(_batteries, b => b.IsSameConstructAs(Me));
GridTerminalSystem.GetBlocksOfType(_tanks, b => b.IsSameConstructAs(Me));
GridTerminalSystem.GetBlocksOfType(_headLights);
_fuelTankSize = 0;
foreach (var tank in _tanks) {
_fuelTankSize += tank.Capacity;
}
if (_fuelTankSize < 0.01) {
Echo("Fatal: No hydrogen tanks");
_fuelTankSize = 1;
}
var splitters = new List<IMyGasGenerator>();
GridTerminalSystem.GetBlocksOfType(splitters, b => b.IsSameConstructAs(Me));
if (splitters.Any())
_splitter = splitters.First();
var generators = new List<IMyPowerProducer>();
GridTerminalSystem.GetBlocksOfType(generators, b => b.IsSameConstructAs(Me) && b.BlockDefinition.ToString().Contains("HydrogenEngine"));
if (generators.Any())
_generator = generators.First();
var remoteControls = new List<IMyRemoteControl>();
GridTerminalSystem.GetBlocksOfType(remoteControls, b => b.IsSameConstructAs(Me));
if (remoteControls.Any())
_remoteControl = remoteControls.First();
var antennas = new List<IMyRadioAntenna>();
GridTerminalSystem.GetBlocksOfType(antennas, b => b.IsSameConstructAs(Me));
if (antennas.Any())
_antenna = antennas.First();
var cockpits = new List<IMyCockpit>();
GridTerminalSystem.GetBlocksOfType(cockpits, b => b.IsSameConstructAs(Me));
if (cockpits.Any())
_cockpit = cockpits.First();
_storages.Clear();
_storages.AddRange(_containers);
_storages.Add(_cockpit);
_cockpitSurface = _cockpit.GetSurface(0);
_cockpitSurface.ContentType = ContentType.SCRIPT;
_cockpitSurface.Script = "";
_viewport = new RectangleF(
(_cockpitSurface.TextureSize - _cockpitSurface.SurfaceSize) / 2f,
_cockpitSurface.SurfaceSize
);
foreach (var battery in _batteries) {
battery.ChargeMode = ChargeMode.Auto;
}
_leftHinge = GridTerminalSystem.GetBlockWithName("Hinge Left") as IMyMotorStator;
_rightHinge = GridTerminalSystem.GetBlockWithName("Hinge Right") as IMyMotorStator;
_topLeftMerge = GridTerminalSystem.GetBlockWithName("Top Left Merge Block Fork") as IMyShipMergeBlock;
_bottomLeftMerge = GridTerminalSystem.GetBlockWithName("Bottom Left Merge Block Fork") as IMyShipMergeBlock;
_topRightMerge = GridTerminalSystem.GetBlockWithName("Top Right Merge Block Fork") as IMyShipMergeBlock;
_bottomRightMerge = GridTerminalSystem.GetBlockWithName("Bottom Right Merge Block Fork") as IMyShipMergeBlock;
bool leftDeployed = false;
bool leftInconsistent = true;
bool rightDeployed = false;
bool rightInconsistent = true;
if (_topLeftMerge != null && _bottomLeftMerge != null && _topRightMerge != null && _bottomRightMerge != null && _leftHinge != null && _rightHinge != null) {
if (_topLeftMerge.IsConnected && !_bottomLeftMerge.IsConnected) {
leftDeployed = false;
leftInconsistent = false;
}
if (!_topLeftMerge.IsConnected && _bottomLeftMerge.IsConnected) {
leftDeployed = true;
leftInconsistent = false;
}
if (_topRightMerge.IsConnected && !_bottomRightMerge.IsConnected) {
rightDeployed = false;
rightInconsistent = false;
}
if (!_topRightMerge.IsConnected && _bottomRightMerge.IsConnected) {
rightDeployed = true;
rightInconsistent = false;
}
_isInconsistent = leftInconsistent || rightInconsistent || (leftDeployed != rightDeployed);
_isDeployed = leftDeployed && rightDeployed;
if (_isDeployed) {
_leftHinge.LowerLimitDeg = -20;
_leftHinge.UpperLimitDeg = -1;
_rightHinge.LowerLimitDeg = -20;
_rightHinge.UpperLimitDeg = -1;
_leftHinge.Torque = 100000;
_rightHinge.Torque = 100000;
_leftHinge.TargetVelocityRPM = -5;
_rightHinge.TargetVelocityRPM = -5;
} else {
if (!_isInconsistent) {
//_leftHinge.LowerLimitDeg = 0;
//_leftHinge.UpperLimitDeg = 0;
//_rightHinge.LowerLimitDeg = 0;
//_rightHinge.UpperLimitDeg = 0;
//_leftHinge.TargetVelocityRPM = 5;
//_rightHinge.TargetVelocityRPM = 5;
}
}
}
_frontLeftWheel = GridTerminalSystem.GetBlockWithName("Front Left Wheel") as IMyMotorSuspension;
_frontRightWheel = GridTerminalSystem.GetBlockWithName("Front Right Wheel") as IMyMotorSuspension;
if (_frontLeftWheel != null && _frontRightWheel != null) {
_frontLeftWheel.SteeringOverride = 0;
_frontRightWheel.SteeringOverride = 0;
_frontLeftWheel.PropulsionOverride = 0;
_frontRightWheel.PropulsionOverride = 0;
} else {
_isInconsistent = true;
}
_wasDocked = IsDocked();
}
public Program()
{
Runtime.UpdateFrequency = UpdateFrequency.Update10;
ReInit();
}
public bool IsDocked() {
return _cockpit.HandBrake;
}
public void Save()
{
// Called when the program needs to save its state. Use
// this method to save your state to the Storage field
// or some other means.
//
// This method is optional and can be removed if not
// needed.
}
private void DoHousekeeping()
{
double hydrogen = 0;
double ice = 0;
double power = 0;
double powerMax = 0;
foreach (var tank in _tanks) {
if (!tank.IsFunctional)
continue;
hydrogen += tank.Capacity * tank.FilledRatio;
}
foreach (var storage in _storages) {
var inventory = storage.GetInventory();
if (inventory == null)
continue;
var iceCount = inventory.GetItemAmount(_iceType);
ice += (double)iceCount * _iceConversionRatio;
}
foreach (var battery in _batteries) {
if (!battery.IsFunctional)
continue;
powerMax += battery.MaxStoredPower;
power += battery.CurrentStoredPower;
}
double fuelPercentage = Math.Ceiling((ice + hydrogen) / _fuelTankSize * 100);
Echo("Fuel: " + fuelPercentage.ToString() + "%");
double hydrogenPercentage = Math.Ceiling(hydrogen / _fuelTankSize * 100);
Echo("Hydrogen: " + hydrogenPercentage.ToString() + "%");
double powerPercentage = Math.Ceiling(power / powerMax * 100);
Echo("Power: " + powerPercentage.ToString() + "%");
if (_isInconsistent) {
Echo("Forks inconsistent");
} else {
if (_isDeployed) {
Echo("Forks deployed");
_frontLeftWheel.SteeringOverride = _cockpit.MoveIndicator.X;
_frontRightWheel.SteeringOverride = _cockpit.MoveIndicator.X;
_frontLeftWheel.PropulsionOverride = -_cockpit.MoveIndicator.Z;
_frontRightWheel.PropulsionOverride = _cockpit.MoveIndicator.Z;
} else {
Echo ("Forks stowed");
}
if ((_uptime - _lastDeployAction).TotalSeconds > 1) {
if (_isDeploying) {
if (_topLeftMerge.Enabled) {
_topLeftMerge.Enabled = false;
_topRightMerge.Enabled = false;
_lastDeployAction = _uptime;
} else {
if (_bottomLeftMerge.IsConnected && _bottomRightMerge.IsConnected) {
if ((_uptime - _lastDeployAction).TotalSeconds > 2)
ReInit();
} else {
_lastDeployAction = _uptime;
}
}
}
if (_isStowing) {
Echo("Left: " + _leftHinge.Angle);
Echo("Right: " + _rightHinge.Angle);
if (_leftHinge.Angle < 0.001 && _rightHinge.Angle < 0.001) {
if (_bottomLeftMerge.Enabled) {
_bottomLeftMerge.Enabled = false;
_bottomRightMerge.Enabled = false;
_lastDeployAction = _uptime;
} else {
_lastDeployAction = _uptime;
if (_topLeftMerge.IsConnected && _topRightMerge.IsConnected) {
ReInit();
}
}
} else {
_lastDeployAction = _uptime;
}
}
}
}
bool newDockStatus = IsDocked();
if (newDockStatus != _wasDocked) {
_wasDocked = newDockStatus;
if (newDockStatus) {
foreach (var light in _headLights) {
light.Enabled = false;
}
} else {
_timeIdle = TimeSpan.Zero;
foreach (var light in _headLights) {
light.Enabled = true;
}
}
}
if (_antenna != null) {
bool nearEmpty = (fuelPercentage < 300);
_antenna.Enabled = !nearEmpty;
}
if (_generator != null) {
if (_batteries.Count() > 1) {
if (_generator.Enabled) {
if (powerPercentage == 100)
_generator.Enabled = false;
if (powerPercentage > 5) {
foreach (var battery in _batteries) {
if (battery.ChargeMode == ChargeMode.Recharge)
battery.ChargeMode = ChargeMode.Auto;
}
}
} else {
if (powerPercentage < 80)
_generator.Enabled = true;
}
} else {
_generator.Enabled = true;
}
}
if (_splitter != null) {
if (_tanks.Count() > 1) {
if (_splitter.Enabled) {
if (hydrogenPercentage == 100)
_splitter.Enabled = false;
} else {
if (hydrogenPercentage < 100)
_splitter.Enabled = true;
}
} else {
_splitter.Enabled = true;
}
}
if (powerPercentage < 5) {
if (_cockpit != null && !_cockpit.IsUnderControl) {
foreach (var battery in _batteries)
battery.ChargeMode = ChargeMode.Recharge;
}
}
if (_cockpit.IsUnderControl)
_timeIdle = TimeSpan.Zero;
if (_autoPowerSave && _timeIdle.TotalSeconds > 600) {
foreach (var battery in _batteries) {
battery.ChargeMode = ChargeMode.Auto;
}
_timeIdle = TimeSpan.Zero;
}
DrawCockpitScreen(hydrogen / _fuelTankSize, ice / _fuelTankSize, -1, -1);
}
private MySprite CreateBar(double offset, double angleDegrees, double start, double end, double width, Color color)
{
if (start < -0.2f)
start = -0.2f;
double borderTop = 0;
double borderBottom = 0.055;
start = (start - borderTop) / (1 + borderBottom) + borderTop;
end = (end - borderTop) / (1 + borderBottom) + borderTop;
double radianAngle = angleDegrees / 180.0 * Math.PI;
float midY = (float)((start + end) / 2.0 * _viewport.Height) + _viewport.Y;
float midX = (float)offset - (float)(Math.Tan(radianAngle) * _viewport.Width * (start + end) / 4.0) + _viewport.X;
float maxHeight = (float)(_viewport.Height / Math.Cos(radianAngle));
float rotatedHeight = (float)(maxHeight * (end - start));
float rotatedWidth = (float)(_viewport.Width * width);
return new MySprite() {
Type = SpriteType.TEXTURE,
Data = "White screen",
Position = new Vector2(midX, midY),
Size = new Vector2(rotatedWidth, rotatedHeight),
Color = color,
Alignment = TextAlignment.CENTER,
RotationOrScale = (float)radianAngle,
};
}
private void DrawCockpitScreen(double hydrogen, double fuel, int mainAmmo, int autoAmmo) {
if (_cockpit == null)
return;
if (!_cockpit.IsUnderControl)
return;
if (_cockpitSurface == null)
return;
using (var frame = _cockpitSurface.DrawFrame()) {
var bgSprite = new MySprite() {
Type = SpriteType.TEXTURE,
Data = "Grid",
Position = _viewport.Center,
Size = _viewport.Size,
Color = _cockpitSurface.ScriptForegroundColor.Alpha(0.66f),
Alignment = TextAlignment.CENTER
};
frame.Add(bgSprite);
if (_uptime.TotalSeconds < 5) {
frame.Add(MySprite.CreateText("Wasp OS", "White", _cockpitSurface.ScriptForegroundColor, 1.0f, TextAlignment.CENTER));
return;
}
frame.Add(CreateBar(72, 30, 1.0 - fuel, 1.0, 1.0 / 20.0, _cockpitSurface.ScriptForegroundColor));
Color hydrogenColor = new Color(_cockpitSurface.ScriptForegroundColor.R, _cockpitSurface.ScriptForegroundColor.G, 255 - _cockpitSurface.ScriptForegroundColor.B);
frame.Add(CreateBar(72, 30, 1.0 - hydrogen, 1.0, 1.0 / 20.0, hydrogenColor));
frame.Add(CreateBar(188, -30, 1.0 - (fuel / 25.0), 1.0, 1.0 / 20.0, _cockpitSurface.ScriptForegroundColor));
int velocity = (int)Math.Round(_cockpit.GetShipVelocities().LinearVelocity.Length());
frame.Add(new MySprite() {
Type = SpriteType.TEXT,
Data = velocity.ToString(),
Position = new Vector2(_viewport.X + _viewport.Width / 2.0f, _viewport.Y + 32f),
FontId = "White",
Color = _cockpitSurface.ScriptForegroundColor,
RotationOrScale = 1.0f,
Alignment = TextAlignment.CENTER
});
}
}
public void Main(string argument, UpdateType updateSource)
{
_uptime += Runtime.TimeSinceLastRun;
_timeIdle += Runtime.TimeSinceLastRun;
if (Runtime.TimeSinceLastRun.TotalSeconds > 5.0) {
_uptime = TimeSpan.Zero;
ReInit();
}
if ((updateSource & UpdateType.Update10) != 0) {
DoHousekeeping();
}
if (argument == "toggle_fork") {
if (!_isInconsistent && _bottomLeftMerge != null && _topLeftMerge != null && _bottomRightMerge != null && _topRightMerge != null) {
if (_isDeployed) {
_isDeploying = false;
_isStowing = true;
_lastDeployAction = _uptime;
_leftHinge.UpperLimitDeg = 0;
_rightHinge.UpperLimitDeg = 0;
_leftHinge.TargetVelocityRPM = 5;
_rightHinge.TargetVelocityRPM = 5;
_topLeftMerge.Enabled = true;
_topRightMerge.Enabled = true;
} else {
_isDeploying = true;
_isStowing = false;
_lastDeployAction = _uptime;
_bottomLeftMerge.Enabled = true;
_bottomRightMerge.Enabled = true;
}
}
}
}
class ItemTarget {
public ItemTarget(string category, string subtype, string blueprintCategory, string blueprintSubtype, long destCount)
{
Type = new MyItemType(category, subtype);
Blueprint = MyDefinitionId.Parse(blueprintCategory + "/" + blueprintSubtype);
DestCount = destCount;
}
public ItemTarget(MyItemType type, long destCount)
{
Type = type;
DestCount = destCount;
}
public MyItemType Type;
public long DestCount;
public long Count;
public List<IMyProductionBlock> ProductionBlocks = new List<IMyProductionBlock>();
public MyDefinitionId Blueprint;
}
List<ItemTarget> _targets = new List<ItemTarget>() {
new ItemTarget("MyObjectBuilder_Component", "SteelPlate", "MyObjectBuilder_BlueprintDefinition", "POSteelPlate", 3000),
new ItemTarget("MyObjectBuilder_Component", "TitaniumPlate", "MyObjectBuilder_BlueprintDefinition", "TitaniumPlate", 200),
new ItemTarget("MyObjectBuilder_Component", "CopperWire", "MyObjectBuilder_BlueprintDefinition", "CopperWire", 1000),
new ItemTarget("MyObjectBuilder_Component", "GoldWire", "MyObjectBuilder_BlueprintDefinition", "GoldWire", 500),
new ItemTarget("MyObjectBuilder_Component", "LargeTube", "MyObjectBuilder_BlueprintDefinition", "POLargeTube", 400),
new ItemTarget("MyObjectBuilder_Component", "SmallTube", "MyObjectBuilder_BlueprintDefinition", "POSmallTube", 400),
new ItemTarget("MyObjectBuilder_Component", "Electromagnet", "MyObjectBuilder_BlueprintDefinition", "Electromagnet", 300),
new ItemTarget("MyObjectBuilder_Component", "Motor", "MyObjectBuilder_BlueprintDefinition", "POMotorComponent", 300),
new ItemTarget("MyObjectBuilder_Component", "Computer", "MyObjectBuilder_BlueprintDefinition", "POComputerComponent", 300),
new ItemTarget("MyObjectBuilder_Component", "AdvancedComputer", "MyObjectBuilder_BlueprintDefinition", "AdvancedComputer", 300),
new ItemTarget("MyObjectBuilder_Component", "Construction", "MyObjectBuilder_BlueprintDefinition", "POConstructionComponent", 1000),
new ItemTarget("MyObjectBuilder_Component", "MetalGrid", "MyObjectBuilder_BlueprintDefinition", "POMetalGrid", 100),
new ItemTarget("MyObjectBuilder_Component", "Girder", "MyObjectBuilder_BlueprintDefinition", "GirderComponent", 100),
new ItemTarget("MyObjectBuilder_Component", "Display", "MyObjectBuilder_BlueprintDefinition", "PODisplay", 50),
new ItemTarget("MyObjectBuilder_Component", "Rubber", "MyObjectBuilder_BlueprintDefinition", "Rubber", 500),
new ItemTarget("MyObjectBuilder_Component", "Lightbulb", "MyObjectBuilder_BlueprintDefinition", "Lightbulb", 10),
new ItemTarget("MyObjectBuilder_Component", "Ceramic", "MyObjectBuilder_BlueprintDefinition", "Ceramic", 100),
new ItemTarget("MyObjectBuilder_Component", "HeatingElement", "MyObjectBuilder_BlueprintDefinition", "HeatingElement", 100),
};
List<MyItemType> _ignoreList = new List<MyItemType>() {
};
List<IMyInventory> _inventories = new List<IMyInventory>();
List<MyProductionItem> _tmpQueue = new List<MyProductionItem>();
IMyTextSurface _statusSurface;
TimeSpan _uptime = TimeSpan.Zero;
private void ReInit()
{
_statusSurface = Me.GetSurface(0);
List<IMyTerminalBlock> inventoryEntities = new List<IMyTerminalBlock>();
GridTerminalSystem.GetBlocksOfType(inventoryEntities, b => b.IsSameConstructAs(Me));
List<IMyProductionBlock> productionBlocks = new List<IMyProductionBlock>();
GridTerminalSystem.GetBlocksOfType(productionBlocks, b => b.IsSameConstructAs(Me));
_inventories.Clear();
foreach (var block in inventoryEntities) {
if (productionBlocks.Contains(block)) {
if (block.InventoryCount != 2) {
Echo(block.ToString() + " has " + block.InventoryCount.ToString() + " inventories!");
} else {
_inventories.Add(block.GetInventory(1));
}
} else {
for (int i = 0; i < block.InventoryCount; i++)
_inventories.Add(block.GetInventory(i));
}
}
foreach (var target in _targets) {
target.ProductionBlocks.Clear();
}
foreach (var block in productionBlocks) {
foreach (var target in _targets) {
if (block.CanUseBlueprint(target.Blueprint))
target.ProductionBlocks.Add(block);
}
//if (block.CustomName == "Assembler") {
// _tmpQueue.Clear();
// block.GetQueue(_tmpQueue);
// foreach (var item in _tmpQueue) {
// Echo(item.BlueprintId.ToString());
// }
//}
}
}
public Program()
{
Runtime.UpdateFrequency = UpdateFrequency.Update100;
ReInit();
}
private void DoHousekeeping()
{
foreach (var target in _targets) {
target.Count = 0;
}
foreach (var inventory in _inventories) {
foreach (var target in _targets) {
var amount = inventory.GetItemAmount(target.Type);
target.Count += (long)amount;
}
}
foreach (var target in _targets) {
if (target.Count < target.DestCount) {
long remaining = target.DestCount - target.Count;
long inProgress = 0;
foreach (var block in target.ProductionBlocks) {
_tmpQueue.Clear();
block.GetQueue(_tmpQueue);
foreach (var item in _tmpQueue) {
if (item.BlueprintId == target.Blueprint)
inProgress += (long)item.Amount;
}
}
long needed = Math.Min(10 * target.ProductionBlocks.Count(), remaining);
if (needed > inProgress) {
long left = needed - inProgress;
foreach (var block in target.ProductionBlocks) {
long available = 10;
_tmpQueue.Clear();
block.GetQueue(_tmpQueue);
foreach (var item in _tmpQueue) {
if (item.BlueprintId == target.Blueprint)
available -= (long)item.Amount;
}
if (available > 0) {
block.AddQueueItem(target.Blueprint, (double)available);
left -= available;
}
}
}
if (target.ProductionBlocks.Count() == 0) {
Echo(target.Type.SubtypeId + " has no producers!");
} else {
Echo(target.Type.SubtypeId + ": " + remaining.ToString());
}
}
}
}
public void Main(string argument, UpdateType updateSource)
{
_uptime += Runtime.TimeSinceLastRun;
if (Runtime.TimeSinceLastRun.TotalSeconds > 5.0) {
_uptime = TimeSpan.Zero;
ReInit();
}
if ((updateSource & UpdateType.Update100) != 0) {
DoHousekeeping();
}
}
TimeSpan _uptime = TimeSpan.Zero;
IMyBroadcastListener _channel;
List<IMyShipConnector> _connectors = new List<IMyShipConnector>();
private void ReInit()
{
_channel = IGC.RegisterBroadcastListener("nl.maxmaton.miningv1.docking.request");
_channel.SetMessageCallback("igc-interrupt");
GridTerminalSystem.GetBlocksOfType(_connectors, b => b.CustomData == "nl.maxmaton.miningv1.connector");
}
public Program()
{
Runtime.UpdateFrequency = UpdateFrequency.None;
ReInit();
}
private void DoHousekeeping()
{
}
private void HandleComms() {
while (_channel.HasPendingMessage) {
var message = _channel.AcceptMessage();
var connector = _connectors[0];
var pos = connector.GetPosition();
var facing = connector.WorldMatrix.Forward;
double[] numbers = new double[] {
pos.X,
pos.Y,
pos.Z,
facing.X,
facing.Y,
facing.Z
};
var response = string.Join("|", numbers.Select((a) => a.ToString()));
IGC.SendUnicastMessage(message.Source, "nl.maxmaton.miningv1.docking.response", response);
}
}
public void Main(string argument, UpdateType updateSource)
{
_uptime += Runtime.TimeSinceLastRun;
if (Runtime.TimeSinceLastRun.TotalSeconds > 5.0) {
_uptime = TimeSpan.Zero;
ReInit();
}
if ((updateSource & UpdateType.Update100) != 0) {
DoHousekeeping();
}
if (argument == "igc-interrupt")
HandleComms();
}
class PID {
private double P;
private double I;
private double D;
private double _maxErrorSum;
private double _errorSum;
private double _lastValue;
private TimeSpan _lastTime;
private bool _hasData;
private double _setpoint;
public double SetPoint {
get { return _setpoint; }
set {
if (_setpoint != value) {
_setpoint = value;
Reset();
}
}
}
public PID(double p, double i, double d, double maxErrorSum = 1.0) {
P = p;
I = i;
D = d;
_maxErrorSum = maxErrorSum;
}
public void Reset() {
_errorSum = 0;
_lastTime = TimeSpan.Zero;
_lastValue = 0;
_hasData = false;
}
public double Step(TimeSpan currentTime, double curval) {
double error = _setpoint - curval;
double result = P * error + I * _errorSum;
if (_hasData) {
double lastError = _setpoint - _lastValue;
double totalSeconds = (currentTime - _lastTime).TotalSeconds;
if (totalSeconds > 2) {
result -= I * _errorSum;
_errorSum = 0;
} else {
double deltaError = error - lastError;
result += D * deltaError;
_errorSum += error * totalSeconds;
if (_errorSum > _maxErrorSum)
_errorSum = _maxErrorSum;
if (_errorSum < -_maxErrorSum)
_errorSum = -_maxErrorSum;
}
}
_lastTime = currentTime;
_lastValue = curval;
_hasData = true;
return result;
}
}
class AttitudeController {
public IMyShipController Controller { get; private set; }
public bool CanOverrideUser { get; set; }
public Vector3D DesiredForward {
get { return _desiredForward; }
set {
if (value == Vector3D.Zero)
_desiredForward = Vector3D.Zero;
else {
Vector3D newForward = Vector3D.Normalize(value);
if (newForward != _desiredForward) {
_desiredForward = newForward;
_yawController.Reset();
_pitchController.Reset();
_rollController.Reset();
}
}
ComputeAligned();
}
}
public Vector3D DesiredUp {
get { return _desiredUp; }
set {
if (value == Vector3D.Zero)
_desiredUp = Vector3D.Zero;
else {
Vector3D newUp = Vector3D.Normalize(value);
if (newUp != _desiredUp) {
_desiredUp = newUp;
_rollController.Reset();
}
}
ComputeAligned();
}
}
public bool IsAligned { get; private set; }
private List<IMyGyro> _gyros = null;
private Vector3D _desiredForward;
private Vector3D _desiredUp;
private double _alignmentBoundary = 0.99;
private bool _forwardAligned;
private PID _yawController;
private PID _pitchController;
private PID _rollController;
public AttitudeController(IMyShipController controller, List<IMyGyro> gyros) {
Controller = controller;
_gyros = gyros;
if (gyros.Count() == 0)
throw new Exception("Not enough gyros");
_yawController = new PID(1, 0, 0);
_pitchController = new PID(1, 0, 0);
_rollController = new PID(1, 0, 0);
ComputeAligned();
}
public void ComputeAligned() {
IsAligned = true;
_forwardAligned = true;
if (DesiredForward != Vector3D.Zero) {
Vector3D ShipForward = Controller.WorldMatrix.Forward;
double forwardAlignment = Vector3D.Dot(ShipForward, DesiredForward);
if (forwardAlignment < _alignmentBoundary) {
IsAligned = false;
_forwardAligned = false;
return;
}
if (DesiredUp != Vector3D.Zero) {
Vector3D ShipUp = Controller.WorldMatrix.Up;
double upAlignment = Vector3D.Dot(ShipUp, DesiredUp);
if (forwardAlignment < _alignmentBoundary) {
IsAligned = false;
return;
}
}
}
}
public Vector3D ComputeRotation(TimeSpan currentTime) {
double yaw = 0;
double pitch = 0;
double roll = 0;
Vector3D ShipForward = Controller.WorldMatrix.Forward;
Vector3D ShipUp = Controller.WorldMatrix.Up;
Vector3D ShipLeft = Vector3D.Cross(ShipForward, ShipUp);
if (_forwardAligned) {
double desiredRoll = -Vector3D.Dot(ShipLeft, DesiredUp);
roll = _rollController.Step(currentTime, desiredRoll);
} else {
_rollController.Reset();
}
double desiredPitch = Vector3D.Dot(ShipUp, DesiredForward);
pitch = _pitchController.Step(currentTime, desiredPitch);
double desiredYaw = -Vector3D.Dot(ShipLeft, DesiredForward);
yaw = _yawController.Step(currentTime, desiredYaw);
return new Vector3D(pitch, yaw, roll);
}
public void Tick(TimeSpan currentTime) {
ComputeAligned();
Vector3D rotation = ComputeRotation(currentTime);
if (Controller.IsUnderControl) {
foreach (var gyro in _gyros) {
gyro.GyroOverride = false;
}
} else {
var Vector = Vector3.Transform(rotation, Controller.WorldMatrix.GetOrientation()); //Converts To World
foreach (var gyro in _gyros) {
gyro.GyroOverride = true;
var TRANS_VECT = Vector3.Transform(Vector, Matrix.Transpose(gyro.WorldMatrix.GetOrientation())); //Converts To Gyro Local
gyro.Pitch = (float)TRANS_VECT.X;
gyro.Yaw = (float)TRANS_VECT.Y;
gyro.Roll = (float)TRANS_VECT.Z;
}
}
}
}
class VelocityController {
IMyShipController _controller;
List<IMyThrust> _thrusters;
PID _xController;
PID _yController;
PID _zController;
Vector3D _desiredSpeed;
public Vector3D DesiredSpeed {
get { return _desiredSpeed; }
set {
_desiredSpeed = value;
_xController.SetPoint = value.X;
_yController.SetPoint = value.Y;
_zController.SetPoint = value.Z;
}
}
public VelocityController(IMyShipController controller, List<IMyThrust> thrusters) {
_controller = controller;
_thrusters = thrusters;
_xController = new PID(2, 0.1, 0.1, 100);
_yController = new PID(2, 0.1, 0.1, 100);
_zController = new PID(2, 0.1, 0.1, 100);
}
public void Reset() {
_xController.Reset();
_yController.Reset();
_zController.Reset();
}
public Vector3D ComputeThrust(TimeSpan currentTime) {
double weight = _controller.CalculateShipMass().PhysicalMass;
var velocities = _controller.GetShipVelocities().LinearVelocity;
double x = _xController.Step(currentTime, velocities.X);
double y = _yController.Step(currentTime, velocities.Y);
double z = _zController.Step(currentTime, velocities.Z);
return new Vector3D(x, y, z) * weight;
}
public void Tick(TimeSpan currentTime) {
var desiredThrust = ComputeThrust(currentTime);
var RC_Matrix = _controller.WorldMatrix.GetOrientation();
var desiredThrustWorld = Vector3.Transform(desiredThrust, RC_Matrix);
if (_desiredSpeed == Vector3D.Zero || _controller.IsUnderControl) {
_controller.DampenersOverride = true;
foreach (var thruster in _thrusters) {
thruster.ThrustOverride = 0;
}
Reset();
} else {
_controller.DampenersOverride = false;
foreach (var thruster in _thrusters) {
thruster.ThrustOverride = -(float)Vector3D.Dot(desiredThrust, thruster.WorldMatrix.Forward);
}
}
}
}
class MiningState {
public class Lane {
public int X { get; private set; }
public int Y { get; private set; }
public Vector3D StartPos { get; private set; }
public Vector3D EndPos { get; private set; }
public Lane(int x, int y, Vector3D startPos, Vector3D endPos) {
X = x;
Y = y;
StartPos = startPos;
EndPos = endPos;
}
}
public string State { get; set; }
long _entityId;
public Vector3D Mins { get; private set; }
public Vector3D Maxs { get; private set; }
public Vector3D DockConnectorPosition { get; set; }
public Vector3D DockConnectorForward { get; set; }
double _shipWidth;
double _shipHeight;
string[] _laneDone;
int curx = 0;
int cury = 0;
int curScanY = 0;
int lanesDoneCount = 0;
public MiningState(string state, long entityId, Vector3D mins, Vector3D maxs, double shipWidth, double shipHeight, string laneState, Vector3D connectorPos, Vector3D connectorNormal) {
State = state;
_entityId = entityId;
Mins = mins;
Maxs = maxs;
_shipWidth = shipWidth;
_shipHeight = shipHeight;
string[] slices = laneState.Split('|');
int width = slices[0].Length;
_laneDone = new string[slices.Count()];
for (int i = 0; i < slices.Count(); i++) {
_laneDone[i] = slices[i];
}
DockConnectorPosition = connectorPos;
DockConnectorForward = connectorNormal;
}
public MiningState(IMyShipController controller, IMyGridTerminalSystem GridTerminalSystem, MyDetectedEntityInfo asteroid) {
State = "init";
_entityId = asteroid.EntityId;
Mins = asteroid.BoundingBox.Min;
Maxs = asteroid.BoundingBox.Max;
List<IMyShipDrill> drills = new List<IMyShipDrill>();
GridTerminalSystem.GetBlocksOfType(drills, b => b.IsSameConstructAs(controller));
double DistToDrill = Math.Sqrt(drills.Count)*0.9; //Size of Ship
if (controller.CubeGrid.ToString().Contains("Large")) //if large grid
DistToDrill = DistToDrill * 1.5;
_shipWidth = DistToDrill; //Wrong but usable
_shipHeight = DistToDrill; //Wrong but usable;
double asteroidWidth = asteroid.BoundingBox.Size.X;
double asteroidHeight = asteroid.BoundingBox.Size.Z;
if (_shipWidth <= 0 || _shipHeight <= 0) {
throw new Exception("Unable to mine without drills");
}
int laneWidth = (int)Math.Ceiling(asteroidWidth / _shipWidth);
int laneHeight = (int)Math.Ceiling(asteroidHeight / _shipHeight);
_laneDone = new string[laneHeight];
for (int i = 0; i < laneHeight; i++) {
_laneDone[i] = new String(' ', laneWidth);
}
}
public static MiningState FromSaveData(string saveData) {
var lines = saveData.Split('\n');
string state = lines[0];
long entityId = long.Parse(lines[1]);
var minsParts = lines[2].Split('|');
Vector3D mins = new Vector3D(double.Parse(minsParts[0]), double.Parse(minsParts[1]), double.Parse(minsParts[2]));
var maxsParts = lines[3].Split('|');
Vector3D maxs = new Vector3D(double.Parse(maxsParts[0]), double.Parse(maxsParts[1]), double.Parse(maxsParts[2]));
double shipWidth = double.Parse(lines[4]);
double shipHeight = double.Parse(lines[5]);
string laneState = lines[6];
var connectorPosParts = lines[7].Split('|');
Vector3D connectorPos = new Vector3D(double.Parse(connectorPosParts[0]), double.Parse(connectorPosParts[1]), double.Parse(connectorPosParts[2]));
var connectorNormParts = lines[8].Split('|');
Vector3D connectorNormal = new Vector3D(double.Parse(connectorNormParts[0]), double.Parse(connectorNormParts[1]), double.Parse(connectorNormParts[2]));
if (state != "init" && state != "drain")
state = "init";
return new MiningState(state, entityId, mins, maxs, shipWidth, shipHeight, laneState, connectorPos, connectorNormal);
}
public int LaneCount {
get { return _laneDone.Length * _laneDone[0].Length; }
}
public int LanesDone {
get {
if (curScanY < _laneDone.Length) {
var line = _laneDone[curScanY];
curScanY++;
lanesDoneCount += line.Count(c => !Char.IsWhiteSpace(c));
}
return lanesDoneCount;
}
}
public Lane NextLane {
get {
int iterationCount = 0;
for (int y = cury; y < _laneDone.Length; y++) {
var line = _laneDone[y];
for (int x = curx; x < line.Length; x++) {
if (line[x] == 'x') {
iterationCount++;
if (iterationCount > 1000)
{
cury = y;
curx = x;
return null;
}
continue;
}
Vector3D startPos = Maxs - y * new Vector3D(0, 0, _shipHeight) - x * new Vector3D(_shipWidth, 0, 0) - new Vector3D(0, _shipWidth * 10, 0);
Vector3D endPos = new Vector3D(startPos.X, Mins.Y - _shipWidth * 10, startPos.Z);
cury = y;
curx = x;
return new Lane(x, y, startPos, endPos);
}
curx = 0;
}
return null;
}
}
public void Finish(Lane lane) {
Finish(lane.X, lane.Y);
}
public void Finish(int x, int y) {
if (y < curScanY && _laneDone[y][x] != 'x')
lanesDoneCount++;
var line = _laneDone[y];
var prefix = line.Substring(0, x);
var postfix = line.Substring(x + 1);
_laneDone[y] = prefix + "x" + postfix;
if (line.Length != _laneDone[y].Length)
throw new Exception("Bad programmer");
}
public string SaveData {
get {
string[] lines = new string[]{
State,
_entityId.ToString(),
Mins.X.ToString() + "|" + Mins.Y.ToString() + "|" + Mins.Z.ToString(),
Maxs.X.ToString() + "|" + Maxs.Y.ToString() + "|" + Maxs.Z.ToString(),
_shipWidth.ToString(),
_shipHeight.ToString(),
string.Join("|", _laneDone),
DockConnectorPosition.X.ToString() + "|" + DockConnectorPosition.Y.ToString() + "|" + DockConnectorPosition.Z.ToString(),
DockConnectorForward.X.ToString() + "|" + DockConnectorForward.Y.ToString() + "|" + DockConnectorForward.Z.ToString()
};
return string.Join("\n", lines);
}
}
}
class SpaceStripMiner {
AttitudeController _attitude;
VelocityController _velocity;
IMyShipController _controller;
MiningState _state;
IMyCameraBlock _camera;
MiningState.Lane _lane;
List<IMyInventory> _inventories;
List<IMyShipDrill> _drills;
IMyShipConnector _connector;
IMyGridTerminalSystem GridTerminalSystem { get; set; }
List<IMyCargoContainer> _drainInventories = new List<IMyCargoContainer>();
public Func<bool> CanUndock { get; set; }
const double TRANSIT_HEIGHT = 20.0;
const double CRUISE_SPEED = 10.0;
const double MINING_SPEED = 2.0;
const double DOCKING_DISTANCE = 25.0;
public SpaceStripMiner(IMyShipController controller, IMyGridTerminalSystem GridTerminalSystem, List<IMyThrust> thrusters, List<IMyGyro> gyros, List<IMyInventory> inventories, List<IMyShipDrill> drills, IMyCameraBlock camera, IMyShipConnector connector, MyDetectedEntityInfo asteroid) {
_controller = controller;
_camera = camera;
_attitude = new AttitudeController(controller, gyros);
_velocity = new VelocityController(controller, thrusters);
_state = new MiningState(controller, GridTerminalSystem, asteroid);
if (connector.Status == MyShipConnectorStatus.Connected)
_state.State = "drain";
_inventories = inventories;
_drills = drills;
_connector = connector;
this.GridTerminalSystem = GridTerminalSystem;
}
public SpaceStripMiner(IMyShipController controller, IMyGridTerminalSystem GridTerminalSystem, List<IMyThrust> thrusters, List<IMyGyro> gyros, List<IMyInventory> inventories, List<IMyShipDrill> drills, IMyCameraBlock camera, IMyShipConnector connector, string saveData) {
_controller = controller;
_camera = camera;
_attitude = new AttitudeController(controller, gyros);
_velocity = new VelocityController(controller, thrusters);
_state = MiningState.FromSaveData(saveData);
if (connector.Status == MyShipConnectorStatus.Connected)
_state.State = "drain";
_inventories = inventories;
_drills = drills;
_connector = connector;
this.GridTerminalSystem = GridTerminalSystem;
}
public void SeedConnectorData(Vector3D connectorPos, Vector3D connectorFacing) {
_state.DockConnectorPosition = connectorPos;
_state.DockConnectorForward = connectorFacing;
}
public string SaveData {
get { return _state.SaveData; }
}
public void Tick(TimeSpan currentTime, Action<string> Echo) {
Echo("State: " + _state.State);
Echo("Progress: " + _state.LanesDone.ToString() + "/" + _state.LaneCount.ToString());
switch (_state.State) {
case "init":
InitState(Echo);
break;
case "realign":
RealignState(Echo);
break;
case "lower":
LowerState(Echo);
break;
case "mine":
MineState(Echo);
break;
case "return":
ReturnState(Echo);
break;
case "dock":
DockState(Echo);
break;
case "drain":
DrainState(Echo);
break;
case "undock":
UndockState(Echo);
break;
}
_attitude.Tick(currentTime);
_velocity.Tick(currentTime);
}
public bool IsInventoryFull {
get {
double totalVolume = 0.001;
double usedVolume = 0.001;
foreach (var inv in _inventories) {
totalVolume += (double)inv.MaxVolume;
usedVolume += (double)inv.CurrentVolume;
}
return usedVolume / totalVolume > 0.95;
}
}
void InitState(Action<string> Echo) {
foreach (var drill in _drills) {
drill.Enabled = false;
}
double metersLeft = _state.Maxs.Z + TRANSIT_HEIGHT - _controller.GetPosition().Z;
if (metersLeft < 0) {
_state.State = "realign";
_velocity.DesiredSpeed = Vector3D.Zero;
_attitude.DesiredForward = Vector3D.Zero;
return;
}
Echo("Distance: " + Math.Ceiling(metersLeft).ToString());
_attitude.DesiredForward = Vector3D.Zero;
if (!_attitude.IsAligned) {
_velocity.DesiredSpeed = Vector3D.Zero;
} else {
_velocity.DesiredSpeed = new Vector3D(0, 0, CRUISE_SPEED);
}
}
void RealignState(Action<string> Echo) {
if (IsInventoryFull) {
_state.State = "return";
return;
}
if (_lane == null) {
Echo ("Computing next lane");
_lane = _state.NextLane;
if (_lane == null)
return; // Staggered computation
}
var dest = new Vector3D(_lane.StartPos.X, _lane.StartPos.Y, _state.Maxs.Z + TRANSIT_HEIGHT);
var arrow = dest - _controller.GetPosition();
var distance = arrow.Length();
Echo("Distance: " + Math.Ceiling(distance).ToString());
if (distance < 1) {
_state.State = "lower";
_velocity.DesiredSpeed = Vector3D.Zero;
_attitude.DesiredForward = Vector3D.Zero;
return;
}
var dir = arrow / Math.Max(distance, 1);
if (distance > 100)
_attitude.DesiredForward = arrow;
_velocity.DesiredSpeed = dir * Math.Min(distance / 2, CRUISE_SPEED);
}
void LowerState(Action<string> Echo) {
var dest = _lane.StartPos;
var arrow = dest - _controller.GetPosition();
var distance = arrow.Length();
_camera.EnableRaycast = true;
Echo("Distance: " + Math.Ceiling(distance).ToString());
if (distance < 1) {
_state.State = "mine";
_velocity.DesiredSpeed = Vector3D.Zero;
var distToEnd = (_lane.EndPos - _controller.GetPosition()).Length();
if (_camera.AvailableScanRange < distToEnd) {
Echo("Charging camera: " + Math.Floor(_camera.AvailableScanRange / distToEnd * 100).ToString() + "%");
return;
}
var raycastResult = _camera.Raycast(_lane.EndPos);
if (raycastResult.Type == MyDetectedEntityType.None) {
_state.Finish(_lane);
var startDist = (_lane.StartPos - _controller.GetPosition()).Length();
if (startDist < 5) {
var lastY = _lane.Y;
_lane = _state.NextLane;
if (_lane.Y == lastY)
_state.State = "lower";
else
_state.State = "init";
return;
}
_lane = null;
_state.State = "init";
_camera.EnableRaycast = false;
}
return;
}
_attitude.DesiredForward = new Vector3D(0, -1, 0);
_attitude.DesiredUp = new Vector3D(0, 0, 1);
if (_attitude.IsAligned) {
_velocity.DesiredSpeed = Vector3D.Normalize(arrow);
} else {
_velocity.DesiredSpeed = Vector3D.Zero;
}
}
void MineState(Action<string> Echo) {
if (IsInventoryFull) {
_state.State = "init";
foreach (var drill in _drills) {
drill.Enabled = false;
}
return;
}
foreach (var drill in _drills) {
drill.Enabled = true;
}
var dest = _lane.EndPos;
var arrow = dest - _controller.GetPosition();
var distance = arrow.Length();
Echo ("Distance: " + Math.Ceiling(distance).ToString());
if (distance < 1) {
_state.Finish(_lane);
_state.State = "init";
foreach (var drill in _drills) {
drill.Enabled = false;
}
return;
}
var speed = Vector3D.Normalize(new Vector3D(arrow.X, -MINING_SPEED, arrow.Z));
_velocity.DesiredSpeed = speed;
}
void ReturnState(Action<string> Echo) {
if (_connector.Status == MyShipConnectorStatus.Connected) {
_state.DockConnectorPosition = _connector.OtherConnector.GetPosition();
_state.DockConnectorForward = _connector.OtherConnector.WorldMatrix.Forward;
_drainInventories.Clear();
_state.State = "drain";
return;
}
if (_state.DockConnectorForward == Vector3D.Zero) {
Echo("Please manually dock me, I don't know where to go.");
_attitude.DesiredForward = Vector3D.Zero;
_velocity.DesiredSpeed = Vector3D.Zero;
return;
}
var dest = _state.DockConnectorPosition + DOCKING_DISTANCE * _state.DockConnectorForward;
var arrow = dest - _controller.GetPosition();
var distance = arrow.Length();
Echo("Distance: " + Math.Ceiling(distance).ToString());
if (distance < 1) {
_state.State = "dock";
_velocity.DesiredSpeed = Vector3D.Zero;
_attitude.DesiredForward = Vector3D.Zero;
return;
}
_velocity.DesiredSpeed = Vector3D.Normalize(arrow) * Math.Max(1.0, Math.Min(distance / 30, CRUISE_SPEED));
_attitude.DesiredForward = Vector3D.Normalize(arrow);
}
void DockState(Action<string> Echo) {
_attitude.DesiredForward = _state.DockConnectorForward;
var arrow = _state.DockConnectorPosition - _connector.GetPosition();
var distance = arrow.Length();
if (_attitude.IsAligned) {
_velocity.DesiredSpeed = Vector3D.Normalize(arrow) * 0.5;
} else {
_velocity.DesiredSpeed = Vector3D.Zero;
}
if (_connector.Status == MyShipConnectorStatus.Connected) {
_state.State = "drain";
_velocity.DesiredSpeed = Vector3D.Zero;
_attitude.DesiredForward = Vector3D.Zero;
return;
}
if (_connector.Status == MyShipConnectorStatus.Connectable) {
_connector.Connect();
}
}
void DrainState(Action<string> Echo) {
if (_connector.Status == MyShipConnectorStatus.Connected) {
_state.DockConnectorPosition = _connector.OtherConnector.GetPosition();
_state.DockConnectorForward = _connector.OtherConnector.WorldMatrix.Forward;
} else {
_state.State = "init";
return;
}
if (_drainInventories.Count == 0) {
GridTerminalSystem.GetBlocksOfType(_drainInventories, b => !b.IsSameConstructAs(_controller));
if (_drainInventories.Count == 0) {
throw new Exception("No drain inventories available");
}
}
foreach(var inv in _inventories) {
if (inv.ItemCount == 0)
continue;
var curVolume = inv.CurrentVolume;
foreach (var dest in _drainInventories) {
if (inv.TransferItemTo(dest.GetInventory(), 0) && curVolume > inv.CurrentVolume)
return;
}
Echo("Destination inventory full, pausing");
return;
}
if (CanUndock == null || CanUndock())
_state.State = "undock";
}
void UndockState(Action<string> Echo) {
if (_connector.Status == MyShipConnectorStatus.Connected) {
_connector.Disconnect();
}
_velocity.DesiredSpeed = _state.DockConnectorForward * 0.5;
var arrow = _state.DockConnectorPosition - _controller.GetPosition();
var distance = arrow.Length();
Echo ("Distance: " + Math.Ceiling(distance).ToString());
if (distance > DOCKING_DISTANCE) {
_state.State = "init";
}
}
}
SpaceStripMiner _stripMiner;
Vector3D _connectorPos;
Vector3D _connectorFacing;
List<IMyShipConnector> _connectors = new List<IMyShipConnector>();
private IMyShipController GetController() {
List<IMyShipController> controllers = new List<IMyShipController>();
GridTerminalSystem.GetBlocksOfType(controllers, b => b.IsSameConstructAs(Me));
return controllers[0];
}
private void ReInit()
{
var controller = GetController();
_stripMiner = null;
List<IMySensorBlock> sensors = new List<IMySensorBlock>();
GridTerminalSystem.GetBlocksOfType(sensors, b => b.IsSameConstructAs(Me));
GridTerminalSystem.GetBlocksOfType(_connectors, b => b.IsSameConstructAs(Me));
if (_stripMiner == null) {
Echo("Initialized, waiting for command to acquire asteroid");
}
}
public Program()
{
Runtime.UpdateFrequency = UpdateFrequency.None;
ReInit();
if (Storage.Length > 0) {
try {
var controller = GetController();
List<IMyCameraBlock> cameras = new List<IMyCameraBlock>();
GridTerminalSystem.GetBlocksOfType(cameras, b => b.IsSameConstructAs(controller) && b.Orientation.Forward == controller.Orientation.Forward);
var camera = cameras[0];
List<IMyGyro> gyros = new List<IMyGyro>();
GridTerminalSystem.GetBlocksOfType(gyros, b => b.IsSameConstructAs(Me));
List<IMyThrust> thrusters = new List<IMyThrust>();
GridTerminalSystem.GetBlocksOfType(thrusters, b => b.IsSameConstructAs(Me));
List<IMyShipDrill> drills = new List<IMyShipDrill>();
GridTerminalSystem.GetBlocksOfType(drills, b => b.IsSameConstructAs(Me));
List<IMyCargoContainer> containers = new List<IMyCargoContainer>();
GridTerminalSystem.GetBlocksOfType(containers, b => b.IsSameConstructAs(Me));
List<IMyInventory> inventories = containers.Select((c) => c.GetInventory()).ToList();
inventories.Add(_connectors[0].GetInventory());
_stripMiner = new SpaceStripMiner(controller, GridTerminalSystem, thrusters, gyros, inventories, drills, camera, _connectors[0], Storage);
if (_connectorFacing != Vector3D.Zero)
_stripMiner.SeedConnectorData(_connectorPos, _connectorFacing);
Runtime.UpdateFrequency = UpdateFrequency.Update10;
Echo("loaded save");
} catch (Exception) {
Echo("Failure during loading, clearing storage");
Echo(Storage);
Storage = "";
}
}
DoHousekeeping();
}
private void DoHousekeeping()
{
if (_connectorFacing == Vector3D.Zero) {
if (_connectors[0].Status == MyShipConnectorStatus.Connected) {
_connectorPos = _connectors[0].OtherConnector.GetPosition();
_connectorFacing = _connectors[0].OtherConnector.WorldMatrix.Forward;
Echo("Acquired docking port");
}
} else {
Echo("Acquired docking port");
}
if (_stripMiner == null)
return;
if (_stripMiner.CanUndock == null) {
List<IMyBatteryBlock> batteries = new List<IMyBatteryBlock>();
GridTerminalSystem.GetBlocksOfType(batteries, b => b.IsSameConstructAs(Me));
_stripMiner.CanUndock = () => {
bool canUndock = true;
foreach (var batt in batteries) {
batt.ChargeMode = ChargeMode.Recharge;
canUndock = canUndock && !batt.HasCapacityRemaining;
}
if (canUndock) {
foreach (var batt in batteries) {
batt.ChargeMode = ChargeMode.Auto;
}
}
return canUndock;
};
}
_stripMiner.Tick(_uptime, Echo);
}
private void AcquireAsteroid() {
var controller = GetController();
List<IMyCameraBlock> cameras = new List<IMyCameraBlock>();
GridTerminalSystem.GetBlocksOfType(cameras, b => b.IsSameConstructAs(controller) && b.Orientation.Forward == controller.Orientation.Forward);
var camera = cameras[0];
camera.EnableRaycast = true;
Runtime.UpdateFrequency = UpdateFrequency.Update100;
if (camera.AvailableScanRange < 1000) {
Echo("Camera charging: " + camera.AvailableScanRange.ToString());
return;
}
var trace = camera.Raycast(camera.AvailableScanRange);
if (trace.Type != MyDetectedEntityType.Asteroid) {
Echo("Detected non-asteroid, retrying");
return;
}
List<IMyGyro> gyros = new List<IMyGyro>();
GridTerminalSystem.GetBlocksOfType(gyros, b => b.IsSameConstructAs(Me));
List<IMyThrust> thrusters = new List<IMyThrust>();
GridTerminalSystem.GetBlocksOfType(thrusters, b => b.IsSameConstructAs(Me));
List<IMyShipDrill> drills = new List<IMyShipDrill>();
GridTerminalSystem.GetBlocksOfType(drills, b => b.IsSameConstructAs(Me));
List<IMyCargoContainer> containers = new List<IMyCargoContainer>();
GridTerminalSystem.GetBlocksOfType(containers, b => b.IsSameConstructAs(Me));
List<IMyInventory> inventories = containers.Select((c) => c.GetInventory()).ToList();
inventories.Add(_connectors[0].GetInventory());
_stripMiner = new SpaceStripMiner(GetController(), GridTerminalSystem, thrusters, gyros, inventories, drills, camera, _connectors[0], trace);
if (_connectorFacing != Vector3D.Zero)
_stripMiner.SeedConnectorData(_connectorPos, _connectorFacing);
Echo("Asteroid found");
Runtime.UpdateFrequency = UpdateFrequency.Update10;
Save();
}
private TimeSpan _uptime;
public void Main(string argument, UpdateType updateSource)
{
if (argument == "acquire") {
AcquireAsteroid();
return;
}
_uptime += Runtime.TimeSinceLastRun;
if (Runtime.TimeSinceLastRun.TotalSeconds > 5.0) {
_uptime = TimeSpan.Zero;
ReInit();
}
if ((updateSource & UpdateType.Update10) != 0) {
DoHousekeeping();
}
if ((updateSource & UpdateType.Update100) != 0) {
AcquireAsteroid();
}
}
public void Save() {
if (_stripMiner != null)
Storage = _stripMiner.SaveData;
else
Storage = "";
}
List<IMyGasTank> _tanks = new List<IMyGasTank>();
List<IMyCockpit> _cockpits = new List<IMyCockpit>();
List<IMyTextSurface> _surfaces = new List<IMyTextSurface>();
List<IMySoundBlock> _soundblocks = new List<IMySoundBlock>();
float _alertLevel = 20;
double _fuelTankSize = 0;
string _state = "starting";
double _secondsUntilNextAlert = 4;
private void ReInit()
{
GridTerminalSystem.GetBlocksOfType(_tanks, b => b.IsSameConstructAs(Me) && b.BlockDefinition.SubtypeName.Contains("RocketFuel"));
GridTerminalSystem.GetBlocksOfType(_cockpits, b => b.IsSameConstructAs(Me));
GridTerminalSystem.GetBlocksOfType(_soundblocks, b => b.IsSameConstructAs(Me));
_fuelTankSize = 0;
foreach (var tank in _tanks) {
_fuelTankSize += tank.Capacity;
Echo (tank.BlockDefinition.SubtypeName);
}
if (_fuelTankSize < 0.01) {
Echo("Fatal: No tanks");
_fuelTankSize = 1;
}
_surfaces.Clear();
foreach (var cockpit in _cockpits) {
var screen = cockpit.GetSurface(0);
_surfaces.Add(screen);
screen.ContentType = ContentType.TEXT_AND_IMAGE;
screen.FontSize = 8.0f;
screen.Alignment = TextAlignment.RIGHT;
screen.WriteText("?%", false);
}
}
public Program()
{
Runtime.UpdateFrequency = UpdateFrequency.Update100;
ReInit();
}
private void DoHousekeeping()
{
double fuel = 0;
foreach (var tank in _tanks) {
if (tank.CustomData == "ignore")
continue;
if (!tank.Stockpile)
fuel += tank.Capacity * tank.FilledRatio;
}
int percentage = (int)Math.Ceiling(fuel / _fuelTankSize * 100);
foreach (var screen in _surfaces) {
screen.WriteText(percentage.ToString() + "%", false);
}
if (percentage < _alertLevel) {
_secondsUntilNextAlert -= Runtime.TimeSinceLastRun.TotalSeconds;
if (_secondsUntilNextAlert <= 0) {
if (_state == "starting") {
foreach (var block in _soundblocks) {
block.Play();
}
_state = "stopping";
_secondsUntilNextAlert = 1;
} else {
foreach (var block in _soundblocks) {
block.Stop();
}
_state = "starting";
_secondsUntilNextAlert = 4;
}
}
}
}
public void Main(string argument, UpdateType updateSource)
{
if ((updateSource & UpdateType.Update100) != 0) {
DoHousekeeping();
}
}
bool _autoPowerSave = true;
IMyTextSurface _cockpitSurface;
RectangleF _viewport;
List<IMyCargoContainer> _containers = new List<IMyCargoContainer>();
List<IMyEntity> _storages = new List<IMyEntity>();
IMyGasGenerator _splitter;
List<IMyBatteryBlock> _batteries = new List<IMyBatteryBlock>();
IMyPowerProducer _generator;
List<IMyGasTank> _tanks = new List<IMyGasTank>();
IMyShipConnector _connector;
List<IMyLandingGear> _landingGear = new List<IMyLandingGear>();
IMyCockpit _cockpit;
IMyRemoteControl _remoteControl;
IMyRadioAntenna _antenna;
IMyBeacon _beacon;
List<IMyLightingBlock> _navLights = new List<IMyLightingBlock>();
List<IMyThrust> _thrusters = new List<IMyThrust>();
//List<IMySmallGattlingGun> _gattlingGuns = new List<IMySmallGattlingGun>();
MyItemType _iceType = new MyItemType("MyObjectBuilder_Ore", "Ice");
const double _iceConversionRatio = 20.0;
double _fuelTankSize = 1;
bool _wasDocked = false;
TimeSpan _uptime = TimeSpan.Zero;
TimeSpan _timeIdle = TimeSpan.Zero;
private void ReInit()
{
_timeIdle = TimeSpan.Zero;
GridTerminalSystem.GetBlocksOfType(_containers, b => b.IsSameConstructAs(Me));
GridTerminalSystem.GetBlocksOfType(_batteries, b => b.IsSameConstructAs(Me));
GridTerminalSystem.GetBlocksOfType(_tanks, b => b.IsSameConstructAs(Me));
GridTerminalSystem.GetBlocksOfType(_navLights, b => b.IsSameConstructAs(Me));
GridTerminalSystem.GetBlocksOfType(_thrusters, b => b.IsSameConstructAs(Me));
GridTerminalSystem.GetBlocksOfType(_landingGear, b => b.IsSameConstructAs(Me));
_fuelTankSize = 0;
foreach (var tank in _tanks) {
_fuelTankSize += tank.Capacity;
}
if (_fuelTankSize < 0.01) {
Echo("Fatal: No hydrogen tanks");
_fuelTankSize = 1;
}
var splitters = new List<IMyGasGenerator>();
GridTerminalSystem.GetBlocksOfType(splitters, b => b.IsSameConstructAs(Me));
if (splitters.Any())
_splitter = splitters.First();
var generators = new List<IMyPowerProducer>();
GridTerminalSystem.GetBlocksOfType(generators, b => b.IsSameConstructAs(Me) && b.BlockDefinition.ToString().Contains("HydrogenEngine"));
if (generators.Any())
_generator = generators.First();
var connectors = new List<IMyShipConnector>();
GridTerminalSystem.GetBlocksOfType(connectors, b => b.IsSameConstructAs(Me));
if (connectors.Any())
_connector = connectors.First();
var remoteControls = new List<IMyRemoteControl>();
GridTerminalSystem.GetBlocksOfType(remoteControls, b => b.IsSameConstructAs(Me));
if (remoteControls.Any())
_remoteControl = remoteControls.First();
var antennas = new List<IMyRadioAntenna>();
GridTerminalSystem.GetBlocksOfType(antennas, b => b.IsSameConstructAs(Me));
if (antennas.Any())
_antenna = antennas.First();
var beacons = new List<IMyBeacon>();
GridTerminalSystem.GetBlocksOfType(beacons, b => b.IsSameConstructAs(Me));
if (beacons.Any())
_beacon = beacons.First();
var cockpits = new List<IMyCockpit>();
GridTerminalSystem.GetBlocksOfType(cockpits, b => b.IsSameConstructAs(Me));
if (cockpits.Any())
_cockpit = cockpits.First();
_storages.Clear();
_storages.AddRange(_containers);
_storages.Add(_cockpit);
_storages.Add(_connector);
_cockpitSurface = _cockpit.GetSurface(0);
_cockpitSurface.ContentType = ContentType.SCRIPT;
_cockpitSurface.Script = "";
_viewport = new RectangleF(
(_cockpitSurface.TextureSize - _cockpitSurface.SurfaceSize) / 2f,
_cockpitSurface.SurfaceSize
);
foreach (var battery in _batteries) {
battery.ChargeMode = ChargeMode.Auto;
}
_wasDocked = IsDocked();
}
public Program()
{
Runtime.UpdateFrequency = UpdateFrequency.Update100;
ReInit();
}
public bool IsDocked() {
if (_connector != null)
if (_connector.Status == MyShipConnectorStatus.Connected)
return true;
foreach (var leg in _landingGear) {
if (leg.IsLocked)
return true;
}
return false;
}
public void Save()
{
// Called when the program needs to save its state. Use
// this method to save your state to the Storage field
// or some other means.
//
// This method is optional and can be removed if not
// needed.
}
private void DoHousekeeping()
{
double hydrogen = 0;
double ice = 0;
double power = 0;
double powerMax = 0;
foreach (var tank in _tanks) {
if (!tank.IsFunctional)
continue;
hydrogen += tank.Capacity * tank.FilledRatio;
}
foreach (var storage in _storages) {
var inventory = storage.GetInventory();
if (inventory == null)
continue;
var iceCount = inventory.GetItemAmount(_iceType);
ice += (double)iceCount * _iceConversionRatio;
}
foreach (var battery in _batteries) {
if (!battery.IsFunctional)
continue;
powerMax += battery.MaxStoredPower;
power += battery.CurrentStoredPower;
}
double fuelPercentage = Math.Ceiling((ice + hydrogen) / _fuelTankSize * 100);
Echo("Fuel: " + fuelPercentage.ToString() + "%");
double hydrogenPercentage = Math.Ceiling(hydrogen / _fuelTankSize * 100);
Echo("Hydrogen: " + hydrogenPercentage.ToString() + "%");
double powerPercentage = Math.Ceiling(power / powerMax * 100);
Echo("Power: " + powerPercentage.ToString() + "%");
bool newDockStatus = IsDocked();
if (newDockStatus != _wasDocked) {
_wasDocked = newDockStatus;
if (newDockStatus) {
foreach (var thruster in _thrusters) {
thruster.Enabled = false;
}
foreach (var light in _navLights) {
light.Enabled = false;
}
} else {
_timeIdle = TimeSpan.Zero;
foreach (var thruster in _thrusters) {
thruster.Enabled = true;
}
foreach (var light in _navLights) {
light.Enabled = true;
}
}
}
if (_antenna != null) {
if (_connector != null && _connector.Status == MyShipConnectorStatus.Connected) {
_antenna.Enabled = false;
if (_beacon != null)
_beacon.Enabled = false;
} else {
bool nearEmpty = (fuelPercentage < 300);
_antenna.Enabled = !nearEmpty;
if (_beacon != null) {
_beacon.Enabled = nearEmpty;
}
}
}
if (_generator != null) {
if (_batteries.Count() > 1) {
if (_generator.Enabled) {
if (powerPercentage == 100)
_generator.Enabled = false;
if (powerPercentage > 5) {
foreach (var battery in _batteries) {
if (battery.ChargeMode == ChargeMode.Recharge)
battery.ChargeMode = ChargeMode.Auto;
}
}
} else {
if (powerPercentage < 80)
_generator.Enabled = true;
}
} else {
_generator.Enabled = true;
}
}
if (_splitter != null) {
if (_tanks.Count() > 1) {
if (_splitter.Enabled) {
if (hydrogenPercentage == 100)
_splitter.Enabled = false;
} else {
if (hydrogenPercentage < 100)
_splitter.Enabled = true;
}
} else {
_splitter.Enabled = true;
}
}
if (powerPercentage < 5) {
if (_cockpit != null && !_cockpit.IsUnderControl) {
foreach (var battery in _batteries)
battery.ChargeMode = ChargeMode.Recharge;
}
}
if (_cockpit.IsUnderControl)
_timeIdle = TimeSpan.Zero;
if (_autoPowerSave && _timeIdle.TotalSeconds > 600) {
foreach (var battery in _batteries) {
battery.ChargeMode = ChargeMode.Auto;
}
_timeIdle = TimeSpan.Zero;
}
DrawCockpitScreen(hydrogen / _fuelTankSize, ice / _fuelTankSize, -1, -1);
}
private MySprite CreateBar(double offset, double angleDegrees, double start, double end, double width, Color color)
{
if (start < -0.2f)
start = -0.2f;
double borderTop = 0;
double borderBottom = 0.055;
start = (start - borderTop) / (1 + borderBottom) + borderTop;
end = (end - borderTop) / (1 + borderBottom) + borderTop;
double radianAngle = angleDegrees / 180.0 * Math.PI;
float midY = (float)((start + end) / 2.0 * _viewport.Height) + _viewport.Y;
float midX = (float)offset - (float)(Math.Tan(radianAngle) * _viewport.Width * (start + end) / 4.0) + _viewport.X;
float maxHeight = (float)(_viewport.Height / Math.Cos(radianAngle));
float rotatedHeight = (float)(maxHeight * (end - start));
float rotatedWidth = (float)(_viewport.Width * width);
return new MySprite() {
Type = SpriteType.TEXTURE,
Data = "White screen",
Position = new Vector2(midX, midY),
Size = new Vector2(rotatedWidth, rotatedHeight),
Color = color,
Alignment = TextAlignment.CENTER,
RotationOrScale = (float)radianAngle,
};
}
private void DrawCockpitScreen(double hydrogen, double fuel, int mainAmmo, int autoAmmo) {
if (_cockpit == null)
return;
if (!_cockpit.IsUnderControl)
return;
if (_cockpitSurface == null)
return;
using (var frame = _cockpitSurface.DrawFrame()) {
var bgSprite = new MySprite() {
Type = SpriteType.TEXTURE,
Data = "Grid",
Position = _viewport.Center,
Size = _viewport.Size,
Color = _cockpitSurface.ScriptForegroundColor.Alpha(0.66f),
Alignment = TextAlignment.CENTER
};
frame.Add(bgSprite);
if (_uptime.TotalSeconds < 5) {
frame.Add(MySprite.CreateText("Wasp OS", "White", _cockpitSurface.ScriptForegroundColor, 1.0f, TextAlignment.CENTER));
return;
}
frame.Add(CreateBar(72, 30, 1.0 - fuel, 1.0, 1.0 / 20.0, _cockpitSurface.ScriptForegroundColor));
Color hydrogenColor = new Color(_cockpitSurface.ScriptForegroundColor.R, _cockpitSurface.ScriptForegroundColor.G, 255 - _cockpitSurface.ScriptForegroundColor.B);
frame.Add(CreateBar(72, 30, 1.0 - hydrogen, 1.0, 1.0 / 20.0, hydrogenColor));
frame.Add(CreateBar(188, -30, 1.0 - (fuel / 25.0), 1.0, 1.0 / 20.0, _cockpitSurface.ScriptForegroundColor));
int velocity = (int)Math.Round(_cockpit.GetShipVelocities().LinearVelocity.Length());
frame.Add(new MySprite() {
Type = SpriteType.TEXT,
Data = velocity.ToString(),
Position = new Vector2(_viewport.X + _viewport.Width / 2.0f, _viewport.Y + 32f),
FontId = "White",
Color = _cockpitSurface.ScriptForegroundColor,
RotationOrScale = 1.0f,
Alignment = TextAlignment.CENTER
});
}
}
public void Main(string argument, UpdateType updateSource)
{
_uptime += Runtime.TimeSinceLastRun;
_timeIdle += Runtime.TimeSinceLastRun;
if (Runtime.TimeSinceLastRun.TotalSeconds > 5.0) {
_uptime = TimeSpan.Zero;
ReInit();
}
if ((updateSource & UpdateType.Update100) != 0) {
DoHousekeeping();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment