|
- using System;
-
- using Gamecraft.Wires;
- using Svelto.ECS;
- using Svelto.ECS.Experimental;
-
- using TechbloxModdingAPI.Blocks.Engines;
- using TechbloxModdingAPI.Utility;
-
- namespace TechbloxModdingAPI.Blocks
- {
- public class Wire : EcsObjectBase
- {
- internal static SignalEngine signalEngine;
-
- protected EGID startPortEGID;
-
- protected EGID endPortEGID;
-
- protected EGID startBlockEGID;
-
- protected EGID endBlockEGID;
-
- protected EGID wireEGID;
-
- protected bool inputToOutput;
-
- protected byte startPort;
-
- protected byte endPort;
-
- public static Wire Connect(SignalingBlock start, byte startPort, SignalingBlock end, byte endPort)
- {
- var (wire, id) = signalEngine.CreateNewWire(start.Id, startPort, end.Id, endPort);
- return new Wire(wire, start, end, id);
- }
-
- /// <summary>
- /// An existing wire connection ending at the specified input.
- /// If multiple exist, this will return the first one found.
- /// </summary>
- /// <param name="end">Destination block.</param>
- /// <param name="endPort">Port number.</param>
- /// <returns>The wire, where the end of the wire is the block port specified, or null if does not exist.</returns>
- public static Wire ConnectedToInputPort(SignalingBlock end, byte endPort)
- {
- var port = signalEngine.MatchBlockIOToPort(end, endPort, false);
- if (!port) return null;
- var wire = signalEngine.MatchPortToWire(port, end.Id, out var egid);
- return wire
- ? new Wire(wire.Get().sourceBlockEGID, end.Id, wire.Get().sourcePortUsage, endPort, egid, false)
- : null;
- }
-
- /// <summary>
- /// An existing wire connection starting at the specified output.
- /// If multiple exist, this will return the first one found.
- /// </summary>
- /// <param name="start">Source block entity ID.</param>
- /// <param name="startPort">Port number.</param>
- /// <returns>The wire, where the start of the wire is the block port specified, or null if does not exist.</returns>
- public static Wire ConnectedToOutputPort(SignalingBlock start, byte startPort)
- {
- var port = signalEngine.MatchBlockIOToPort(start, startPort, true);
- if (!port) return null;
- var wire = signalEngine.MatchPortToWire(port, start.Id, out var egid);
- return wire
- ? new Wire(start.Id, wire.Get().destinationBlockEGID, startPort, wire.Get().destinationPortUsage, egid, false)
- : null;
- }
-
- /// <summary>
- /// Construct a wire object froam n existing connection.
- /// </summary>
- /// <param name="start">Starting block ID.</param>
- /// <param name="end">Ending block ID.</param>
- /// <param name="startPort">Starting port number, or guess if omitted.</param>
- /// <param name="endPort">Ending port number, or guess if omitted.</param>
- /// <exception cref="WireInvalidException">Guessing failed or wire does not exist.</exception>
- public Wire(Block start, Block end, byte startPort = Byte.MaxValue, byte endPort = Byte.MaxValue) : base(ecs =>
- {
- var th = (Wire)ecs;
- th.startBlockEGID = start.Id;
- th.endBlockEGID = end.Id;
- bool flipped = false;
- // find block ports
- EGID wire = signalEngine.MatchBlocksToWire(start.Id, end.Id, startPort, endPort);
- if (wire == default)
- {
- // flip I/O around and try again
- wire = signalEngine.MatchBlocksToWire(end.Id, start.Id, endPort, startPort);
- flipped = true;
- // NB: start and end are handled exactly as they're received as params.
- // This makes wire traversal easier, but makes logic in this class a bit more complex
- }
-
- if (wire != default)
- {
- th.Construct(start.Id, end.Id, startPort, endPort, wire, flipped);
- }
- else
- {
- throw new WireInvalidException("Wire not found");
- }
-
- return th.wireEGID;
- })
- {
- }
-
- /// <summary>
- /// Construct a wire object from an existing wire connection.
- /// </summary>
- /// <param name="start">Starting block ID.</param>
- /// <param name="end">Ending block ID.</param>
- /// <param name="startPort">Starting port number.</param>
- /// <param name="endPort">Ending port number.</param>
- /// <param name="wire">The wire ID.</param>
- /// <param name="inputToOutput">Whether the wire direction goes input -> output (true) or output -> input (false, preferred).</param>
- public Wire(Block start, Block end, byte startPort, byte endPort, EGID wire, bool inputToOutput)
- : this(start.Id, end.Id, startPort, endPort, wire, inputToOutput)
- {
- }
-
- private Wire(EGID startBlock, EGID endBlock, byte startPort, byte endPort, EGID wire, bool inputToOutput) : base(wire)
- {
- Construct(startBlock, endBlock, startPort, endPort, wire, inputToOutput);
- }
-
- private void Construct(EGID startBlock, EGID endBlock, byte startPort, byte endPort, EGID wire, bool inputToOutput)
- {
- this.startBlockEGID = startBlock;
- this.endBlockEGID = endBlock;
- this.inputToOutput = inputToOutput;
- this.wireEGID = wire;
- endPortEGID = signalEngine.MatchBlockIOToPort(startBlock, startPort, inputToOutput).EGID;
- if (endPortEGID == default) throw new WireInvalidException("Wire end port not found");
- startPortEGID = signalEngine.MatchBlockIOToPort(endBlock, endPort, !inputToOutput).EGID;
- if (startPortEGID == default) throw new WireInvalidException("Wire start port not found");
- this.startPort = startPort;
- this.endPort = endPort;
- }
-
- /// <summary>
- /// Construct a wire object from an existing wire connection.
- /// </summary>
- /// <param name="wireEgid">The wire ID.</param>
- public Wire(EGID wireEgid) : base(wireEgid)
- {
- WireEntityStruct wire = signalEngine.GetWire(wireEGID);
- Construct(wire.sourceBlockEGID, wire.destinationBlockEGID, wire.sourcePortUsage, wire.destinationPortUsage,
- wireEgid, false);
- }
-
- private Wire(WireEntityStruct wire, SignalingBlock src, SignalingBlock dest, EGID wireEgid)
- : this(src, dest, wire.sourcePortUsage, wire.destinationPortUsage, wireEgid, false)
- {
- }
-
- /// <summary>
- /// The wire's signal value, as a float.
- /// </summary>
- public float Float
- {
- get
- {
- return signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsFloat;
- }
-
- set
- {
- signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsFloat = value;
- }
- }
-
- /// <summary>
- /// The wire's string signal.
- /// </summary>
- public string String
- {
- get
- {
- return signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsEcsString;
- }
-
- set
- {
- signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsEcsString.Set(value);
- }
- }
-
- /// <summary>
- /// The wire's raw string signal.
- /// </summary>
- public ECSString ECSString
- {
- get
- {
- return signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsEcsString;
- }
-
- set
- {
- signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsEcsString = value;
- }
- }
-
- /// <summary>
- /// The wire's signal id.
- /// I'm 50% sure this is useless.
- /// </summary>
- public uint SignalId
- {
- get
- {
- return signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsID;
- }
-
- set
- {
- signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsID = value;
- }
- }
-
- /// <summary>
- /// The block at the beginning of the wire.
- /// </summary>
- public SignalingBlock Start
- {
- get => (SignalingBlock)Block.New(startBlockEGID);
- }
-
- /// <summary>
- /// The port number that the beginning of the wire connects to.
- /// </summary>
- public byte StartPort
- {
- get => startPort;
- }
-
- /// <summary>
- /// The display name of the start port.
- /// </summary>
- public string StartPortName
- {
- get => signalEngine.GetPort(startPortEGID).portNameLocalised;
- }
-
- /// <summary>
- /// The block at the end of the wire.
- /// </summary>
- public SignalingBlock End
- {
- get => (SignalingBlock)Block.New(endBlockEGID);
- }
-
- /// <summary>
- /// The port number that the end of the wire connects to.
- /// </summary>
- public byte EndPort
- {
- get => endPort;
- }
-
- /// <summary>
- /// The display name of the end port.
- /// </summary>
- public string EndPortName
- {
- get => signalEngine.GetPort(endPortEGID).portNameLocalised;
- }
-
- /// <summary>
- /// Create a copy of the wire object where the direction of the wire is guaranteed to be from a block output to a block input.
- /// This is simply a different memory configuration and does not affect the in-game wire (which is always output -> input).
- /// </summary>
- /// <returns>A copy of the wire object.</returns>
- public Wire OutputToInputCopy()
- {
- return GetInstance(wireEGID, egid => new Wire(egid));
- }
-
- /// <summary>
- /// Convert the wire object to the direction the signal flows.
- /// Signals on wires always flow from a block output port to a block input port.
- /// This is simply a different memory configuration and does not affect the in-game wire (which is always output -> input).
- /// </summary>
- public void OutputToInputInPlace()
- {
- if (inputToOutput)
- {
- inputToOutput = false;
- // swap inputs and outputs
- (endBlockEGID, startBlockEGID) = (startBlockEGID, endBlockEGID);
- var tempPort = endPortEGID;
- endPortEGID = startPortEGID;
- startPortEGID = tempPort;
- (endPort, startPort) = (startPort, endPort);
- }
- }
-
- public override string ToString()
- {
- if (signalEngine.Exists<WireEntityStruct>(wireEGID))
- {
- return $"{nameof(Id)}: {Id}, Start{nameof(Start.Id)}: {Start.Id}, End{nameof(End.Id)}: {End.Id}, ({Start.Type}::{StartPort} aka {(StartPort != byte.MaxValue ? Start.PortName(StartPort, inputToOutput) : "")}) -> ({End.Type}::{EndPort} aka {(EndPort != byte.MaxValue ? End.PortName(EndPort, !inputToOutput) : "")})";
- }
- return $"{nameof(Id)}: {Id}, Start{nameof(Start.Id)}: {Start.Id}, End{nameof(End.Id)}: {End.Id}, ({Start.Type}::{StartPort} -> {End.Type}::{EndPort})";
- }
-
- internal static void Init() { }
- }
- }
|