Configure Mechanical Door to use CustomData for configuration. Also refactor Sequenceables to use the yield enumerator pattern.

This commit is contained in:
Anna Rose 2025-02-10 19:43:10 -05:00
parent 8c55176aed
commit c8babebaef
5 changed files with 75 additions and 66 deletions

View File

@ -24,21 +24,20 @@ namespace IngameScript
_doors = new Dictionary<string, Sequencer>(); _doors = new Dictionary<string, Sequencer>();
List<IMyTerminalBlock> doorBlocks = new List<IMyTerminalBlock>(); List<IMyTerminalBlock> doorBlocks = new List<IMyTerminalBlock>();
GridTerminalSystem.GetBlocksOfType<IMyTerminalBlock>(doorBlocks, block => block.CustomName.Contains("!Door")); GridTerminalSystem.GetBlocksOfType(doorBlocks, block => MyIni.HasSection(block.CustomData, "mechDoor"));
foreach (IMyTerminalBlock block in doorBlocks) foreach (IMyTerminalBlock block in doorBlocks)
{ {
string doorName = Utils.ExtractTag(block, "!Door"); _ini.TryParse(block.CustomData);
string doorName = _ini.Get("mechDoor", "id").ToString();
// Create the door if this is a new tag // Create the door if this is a new id
if (!_doors.ContainsKey(doorName)) if (!_doors.ContainsKey(doorName))
{ {
_doors[doorName] = new Sequencer(doorName, new PrefixedConsole(_console, doorName)); _doors[doorName] = new Sequencer(doorName, new PrefixedConsole(_console, doorName));
} }
// Add the part; the Door object handles typing and sequencing. // Add the part; the Door object handles typing and sequencing.
int defaultStep = 1; ISequenceable wrapped = SequenceableFactory.MakeSequenceable(block, _ini, "mechDoor");
if (block is IMyShipMergeBlock) defaultStep = 0; if (!(block is IMyShipMergeBlock)) wrapped.Step = 1;
ISequenceable wrapped = SequenceableFactory.MakeSequenceable(block, _ini, defaultStep);
if (wrapped == null) { _console.Print($"Tried to add incompatible block '{block.CustomName}'"); continue; } if (wrapped == null) { _console.Print($"Tried to add incompatible block '{block.CustomName}'"); continue; }
_doors[doorName].AddBlock(wrapped); _doors[doorName].AddBlock(wrapped);
} }
@ -68,7 +67,7 @@ namespace IngameScript
for (int i = 0; i < _cli.ArgumentCount; i++) for (int i = 0; i < _cli.ArgumentCount; i++)
{ {
string key = "Door" + _cli.Argument(i); string key = _cli.Argument(i);
if (!_doors.ContainsKey(key)) if (!_doors.ContainsKey(key))
{ {
_console.Print($"Door '{key}' not found. Skipping."); _console.Print($"Door '{key}' not found. Skipping.");
@ -89,10 +88,10 @@ namespace IngameScript
else else
{ {
_console.Print("Creating new job(s)."); _console.Print("Creating new job(s).");
bool close = _cli.Switch("close"); bool deploy = _cli.Switch("deploy") || _cli.Switch("open");
foreach (Sequencer door in doorsToControl) foreach (Sequencer door in doorsToControl)
{ {
_jobs.Add(door.RunSequence(close)); _jobs.Add(door.RunSequence(deploy));
} }
Runtime.UpdateFrequency |= UpdateFrequency.Update1; Runtime.UpdateFrequency |= UpdateFrequency.Update1;
} }
@ -101,13 +100,12 @@ namespace IngameScript
// Process running jobs // Process running jobs
for (int i = 0; i < _jobs.Count; i++) for (int i = 0; i < _jobs.Count; i++)
{ {
if (!_jobs[i].MoveNext()) if (_jobs[i].MoveNext()) continue;
{
_jobs[i].Dispose(); _jobs[i].Dispose();
_jobs.Remove(_jobs[i]); _jobs.Remove(_jobs[i]);
i--; i--;
_console.Print("Operation Complete."); _console.Print("Operation Complete.");
}
} }
if (_jobs.Count == 0) if (_jobs.Count == 0)

View File

@ -1,3 +1,5 @@
using System.Collections.Generic;
namespace IngameScript namespace IngameScript
{ {
partial class Program partial class Program
@ -5,10 +7,8 @@ namespace IngameScript
public interface ISequenceable public interface ISequenceable
{ {
bool Running { get; } bool Running { get; }
int Step { get; } int Step { get; set; }
void Start(bool reverse = true); IEnumerator<bool> Run(bool deploy);
bool Check();
void Finish();
} }
} }
} }

View File

@ -13,11 +13,11 @@ namespace IngameScript
public static ISequenceable MakeSequenceable( public static ISequenceable MakeSequenceable(
IMyTerminalBlock block, IMyTerminalBlock block,
MyIni ini, MyIni ini,
int step = 0) string sectionName = "sequence")
{ {
if (block is IMyMotorStator) if (block is IMyMotorStator)
{ {
return new SequenceableRotor(block as IMyMotorStator, ini, step); return new SequenceableRotor(block as IMyMotorStator, ini, sectionName);
} }
if (block is IMyPistonBase) if (block is IMyPistonBase)
{ {

View File

@ -1,5 +1,6 @@
using Sandbox.ModAPI.Ingame; using Sandbox.ModAPI.Ingame;
using System; using System;
using System.Collections.Generic;
using VRage.Game.ModAPI.Ingame.Utilities; using VRage.Game.ModAPI.Ingame.Utilities;
namespace IngameScript namespace IngameScript
@ -9,55 +10,71 @@ namespace IngameScript
public class SequenceableRotor : ISequenceable public class SequenceableRotor : ISequenceable
{ {
public bool Running { get; private set; } = false; public bool Running { get; private set; } = false;
public int Step { get; private set; } public int Step { get; set; }
private float _targetAngle;
private float _lastAngle;
private float _velocity; private float _velocity;
private float _openAngle; private float _deployAngle;
private float _closedAngle; private float _stowAngle;
private IMyMotorStator _rotor; private IMyMotorStator _rotor;
private MyRotationDirection _deployDirection;
private MyRotationDirection _stowDirection;
public SequenceableRotor( public SequenceableRotor(
IMyMotorStator rotor, IMyMotorStator rotor,
MyIni ini, MyIni ini,
int defaultStep = 0) string sectionName)
{ {
_rotor = rotor; _rotor = rotor;
ini.TryParse(rotor.CustomData); ini.TryParse(rotor.CustomData);
_openAngle = ini.Get("sequencer", "openAngle").ToSingle(90F); _deployAngle = ini.Get(sectionName, "deployAngle").ToSingle(90F);
_closedAngle = ini.Get("sequencer", "closedAngle").ToSingle(0F); _stowAngle = ini.Get(sectionName, "stowAngle").ToSingle(0F);
_velocity = ini.Get("sequencer", "velocity").ToSingle(5F); _velocity = ini.Get(sectionName, "velocity").ToSingle(5F);
Step = ini.Get("sequencer", "step").ToInt32(defaultStep); switch (ini.Get(sectionName, "deployDirection").ToString("auto"))
}
public void Start(bool reverse)
{
float degAngle = reverse ? _closedAngle : _openAngle;
_targetAngle = degToRad(degAngle);
_rotor.RotorLock = false;
_rotor.RotateToAngle(MyRotationDirection.AUTO, degAngle, _velocity);
Running = true;
}
public bool Check()
{
if (Math.Abs(_rotor.Angle - _targetAngle) < 0.1 && _rotor.Angle == _lastAngle)
{ {
return false; case "auto":
_deployDirection = MyRotationDirection.AUTO;
_stowDirection = MyRotationDirection.AUTO;
break;
case "cw":
_deployDirection = MyRotationDirection.CW;
_stowDirection = MyRotationDirection.CCW;
break;
case "ccw":
_deployDirection = MyRotationDirection.CCW;
_stowDirection = MyRotationDirection.CW;
break;
} }
_lastAngle = _rotor.Angle; Step = ini.Get(sectionName, "step").ToInt32(0);
return true;
} }
public void Finish() public IEnumerator<bool> Run(bool deploy = true)
{ {
float _targetAngle = setup(deploy);
float _lastAngle = _rotor.Angle;
while (Math.Abs(_rotor.Angle - _targetAngle) > 0.1 || _rotor.Angle != _lastAngle)
{
_lastAngle = _rotor.Angle;
yield return true;
}
_rotor.RotorLock = true; _rotor.RotorLock = true;
Running = false; Running = false;
} }
private float setup(bool deploy = true)
{
float degAngle = deploy ? _deployAngle : _stowAngle;
MyRotationDirection dir = deploy ? _deployDirection : _stowDirection;
_rotor.RotorLock = false;
_rotor.RotateToAngle(dir, degAngle, _velocity);
Running = true;
return degToRad(degAngle);
}
private float degToRad(float degrees) private float degToRad(float degrees)
{ {
return degrees * ((float)Math.PI / 180F); return degrees * ((float)Math.PI / 180F);

View File

@ -34,7 +34,7 @@ namespace IngameScript
// To activate the Sequencer, call this once, then call `MoveNext()` once per tick // To activate the Sequencer, call this once, then call `MoveNext()` once per tick
// on the returned object until it returns false. // on the returned object until it returns false.
// (then be sure to call `Dispose() on that object`) // (then be sure to call `Dispose() on that object`)
public IEnumerator<bool> RunSequence(bool reverse = false) public IEnumerator<bool> RunSequence(bool deploy = true)
{ {
if (Running) if (Running)
{ {
@ -44,36 +44,30 @@ namespace IngameScript
// This is a bit convoluted, but we're extracting the iterator for use in the foreach directly. // This is a bit convoluted, but we're extracting the iterator for use in the foreach directly.
// This allows us to iterate in reverse when reversing/"closing" the sequence. // This allows us to iterate in reverse when reversing/"closing" the sequence.
IEnumerable<KeyValuePair<int, List<ISequenceable>>> steps = reverse ? _sequence.Reverse() : _sequence; IEnumerable<KeyValuePair<int, List<ISequenceable>>> steps = deploy ? _sequence : _sequence.Reverse();
foreach (KeyValuePair<int, List<ISequenceable>> kvp in steps) foreach (KeyValuePair<int, List<ISequenceable>> kvp in steps)
{ {
List<ISequenceable> blocks = kvp.Value; List<ISequenceable> blocks = kvp.Value;
List<IEnumerator<bool>> subJobs = new List<IEnumerator<bool>>();
Running = true; Running = true;
foreach (ISequenceable block in blocks) foreach (ISequenceable block in blocks)
{ {
// TODO: add some sort of handling for block.Running == true // TODO: add some sort of handling for block.Running == true, maybe?
block.Start(reverse); // It *should* be an impossible case, but...
subJobs.Add(block.Run(deploy));
} }
yield return true;
while (Running) while (Running)
{ {
Running = false; Running = false;
foreach (ISequenceable block in blocks) foreach (IEnumerator<bool> subJob in subJobs)
{ {
if (block.Check()) Running = true; if (subJob.MoveNext()) Running = true;
} }
yield return true; yield return true;
} }
foreach (ISequenceable block in blocks)
{
block.Finish();
}
} }
yield break;
} }
} }
} }