|
- using System;
-
- using Gamecraft.Wires;
- using Svelto.ECS;
- using Svelto.ECS.Experimental;
-
- using GamecraftModdingAPI.Utility;
-
- namespace GamecraftModdingAPI.Blocks
- {
- public class Wire
- {
- internal static SignalEngine signalEngine;
-
- protected EGID startPortEGID;
-
- protected EGID endPortEGID;
-
- protected EGID startBlockEGID;
-
- protected EGID endBlockEGID;
-
- protected EGID wireEGID;
-
- protected bool inputToOutput;
-
- public static Wire Connect(SignalingBlock start, byte startPort, SignalingBlock end, byte endPort)
- {
- WireEntityStruct wire = signalEngine.CreateNewWire(start.Id, startPort, end.Id, endPort);
- return new Wire(wire);
- }
-
- /// <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)
- {
- EGID port = signalEngine.MatchBlockInputToPort(end, endPort, out bool exists);
- if (!exists) return null;
- WireEntityStruct wire = signalEngine.MatchPortToWire(port, end.Id, out exists);
- if (exists)
- {
- return new Wire(new Block(wire.sourceBlockEGID), end, wire.sourcePortUsage, endPort);
- }
- return 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)
- {
- EGID port = signalEngine.MatchBlockOutputToPort(start, startPort, out bool exists);
- if (!exists) return null;
- WireEntityStruct wire = signalEngine.MatchPortToWire(port, start.Id, out exists);
- if (exists)
- {
- return new Wire(start, new Block(wire.destinationBlockEGID), startPort, wire.destinationPortUsage);
- }
- return null;
- }
-
- /// <summary>
- /// Construct a wire object from an 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)
- {
- startBlockEGID = start.Id;
- endBlockEGID = end.Id;
- // find block ports
- WireEntityStruct wire = signalEngine.MatchBlocksToWire(start.Id, end.Id, out bool exists, startPort, endPort);
- if (exists)
- {
- wireEGID = wire.ID;
- endPortEGID = signalEngine.MatchBlockInputToPort(end, wire.destinationPortUsage, out exists);
- if (!exists) throw new WireInvalidException("Wire end port not found");
- startPortEGID = signalEngine.MatchBlockOutputToPort(start, wire.sourcePortUsage, out exists);
- if (!exists) throw new WireInvalidException("Wire start port not found");
- inputToOutput = false;
- }
- else
- {
- // flip I/O around and try again
- wire = signalEngine.MatchBlocksToWire(end.Id, start.Id, out exists, endPort, startPort);
- if (exists)
- {
- wireEGID = wire.ID;
- endPortEGID = signalEngine.MatchBlockOutputToPort(end, wire.sourcePortUsage, out exists);
- if (!exists) throw new WireInvalidException("Wire end port not found");
- startPortEGID = signalEngine.MatchBlockInputToPort(start, wire.destinationPortUsage, out exists);
- if (!exists) throw new WireInvalidException("Wire start port not found");
- inputToOutput = true; // end is actually the source
- // 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
- }
- else
- {
- throw new WireInvalidException("Wire not found");
- }
- }
- }
-
- /// <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.startBlockEGID = start.Id;
- this.endBlockEGID = end.Id;
- this.inputToOutput = inputToOutput;
- this.wireEGID = wire;
- if (inputToOutput)
- {
- endPortEGID = signalEngine.MatchBlockOutputToPort(start, startPort, out bool exists);
- if (!exists) throw new WireInvalidException("Wire end port not found");
- startPortEGID = signalEngine.MatchBlockInputToPort(end, endPort, out exists);
- if (!exists) throw new WireInvalidException("Wire start port not found");
- }
- else
- {
- endPortEGID = signalEngine.MatchBlockInputToPort(end, endPort, out bool exists);
- if (!exists) throw new WireInvalidException("Wire end port not found");
- startPortEGID = signalEngine.MatchBlockOutputToPort(start, startPort, out exists);
- if (!exists) throw new WireInvalidException("Wire start port not found");
- }
- }
-
- /// <summary>
- /// Construct a wire object from an existing wire connection.
- /// </summary>
- /// <param name="wireEgid">The wire ID.</param>
- public Wire(EGID wireEgid)
- {
- this.wireEGID = wireEgid;
- WireEntityStruct wire = signalEngine.GetWire(wireEGID);
- this.startBlockEGID = wire.sourceBlockEGID;
- this.endBlockEGID = wire.destinationBlockEGID;
- this.inputToOutput = false;
- endPortEGID = signalEngine.MatchBlockInputToPort(wire.destinationBlockEGID, wire.destinationPortUsage, out bool exists);
- if (!exists) throw new WireInvalidException("Wire end port not found");
- startPortEGID = signalEngine.MatchBlockOutputToPort(wire.sourceBlockEGID, wire.sourcePortUsage, out exists);
- if (!exists) throw new WireInvalidException("Wire start port not found");
- }
-
- internal Wire(WireEntityStruct wire)
- {
- this.wireEGID = wire.ID;
- this.startBlockEGID = wire.sourceBlockEGID;
- this.endBlockEGID = wire.destinationBlockEGID;
- inputToOutput = false;
- endPortEGID = signalEngine.MatchBlockInputToPort(wire.destinationBlockEGID, wire.destinationPortUsage, out bool exists);
- if (!exists) throw new WireInvalidException("Wire end port not found");
- startPortEGID = signalEngine.MatchBlockOutputToPort(wire.sourceBlockEGID, wire.sourcePortUsage, out exists);
- if (!exists) throw new WireInvalidException("Wire start port not found");
- }
-
- /// <summary>
- /// The wire's in-game id.
- /// </summary>
- public EGID Id
- {
- get => wireEGID;
- }
-
- /// <summary>
- /// The wire's signal value, as a float.
- /// </summary>
- public float Float
- {
- get
- {
- ref ChannelDataStruct cds = ref signalEngine.GetChannelDataStruct(startPortEGID, out bool exists);
- if (!exists) return 0f;
- return cds.valueAsFloat;
- }
-
- set
- {
- ref ChannelDataStruct cds = ref signalEngine.GetChannelDataStruct(startPortEGID, out bool exists);
- if (!exists) return;
- cds.valueAsFloat = value;
- }
- }
-
- /// <summary>
- /// The wire's string signal.
- /// </summary>
- public string String
- {
- get
- {
- ref ChannelDataStruct cds = ref signalEngine.GetChannelDataStruct(startPortEGID, out bool exists);
- if (!exists) return "";
- return cds.valueAsEcsString;
- }
-
- set
- {
- ref ChannelDataStruct cds = ref signalEngine.GetChannelDataStruct(startPortEGID, out bool exists);
- if (!exists) return;
- cds.valueAsEcsString.Set(value);
- }
- }
-
- /// <summary>
- /// The wire's raw string signal.
- /// </summary>
- public ECSString ECSString
- {
- get
- {
- ref ChannelDataStruct cds = ref signalEngine.GetChannelDataStruct(startPortEGID, out bool exists);
- if (!exists) return default;
- return cds.valueAsEcsString;
- }
-
- set
- {
- ref ChannelDataStruct cds = ref signalEngine.GetChannelDataStruct(startPortEGID, out bool exists);
- if (!exists) return;
- cds.valueAsEcsString = value;
- }
- }
-
- /// <summary>
- /// The wire's signal id.
- /// I'm 50% sure this is useless.
- /// </summary>
- public uint SignalId
- {
- get
- {
- ref ChannelDataStruct cds = ref signalEngine.GetChannelDataStruct(startPortEGID, out bool exists);
- if (!exists) return uint.MaxValue;
- return cds.valueAsID;
- }
-
- set
- {
- ref ChannelDataStruct cds = ref signalEngine.GetChannelDataStruct(startPortEGID, out bool exists);
- if (!exists) return;
- cds.valueAsID = value;
- }
- }
-
- /// <summary>
- /// The block at the beginning of the wire.
- /// </summary>
- public SignalingBlock Start
- {
- get => new SignalingBlock(startBlockEGID);
- }
-
- /// <summary>
- /// The port number that the beginning of the wire connects to.
- /// </summary>
- public byte StartPort
- {
- get
- {
- WireEntityStruct wire = signalEngine.GetWire(wireEGID);
- if (inputToOutput)
- {
- return wire.destinationPortUsage;
- }
- return wire.sourcePortUsage;
- }
- }
-
- /// <summary>
- /// The block at the end of the wire.
- /// </summary>
- public SignalingBlock End
- {
- get => new SignalingBlock(endBlockEGID);
- }
-
- /// <summary>
- /// The port number that the end of the wire connects to.
- /// </summary>
- public byte EndPort
- {
- get
- {
- WireEntityStruct wire = signalEngine.GetWire(wireEGID);
- if (inputToOutput)
- {
- return wire.sourcePortUsage;
- }
- return wire.destinationPortUsage;
- }
- }
-
- /// <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 new Wire(wireEGID);
- }
-
- /// <summary>
- /// Convert the wire object to the direction the signal flows.
- /// Signals on wires always flows 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
- EGID temp = endBlockEGID;
- endBlockEGID = startBlockEGID;
- startBlockEGID = temp;
- temp = endPortEGID;
- endPortEGID = startPortEGID;
- startPortEGID = temp;
- }
- }
-
- 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 {Start.PortName(StartPort, inputToOutput)}) -> ({End.Type}::{EndPort} aka {End.PortName(EndPort, !inputToOutput)})";
- }
- return $"{nameof(Id)}: {Id}, Start{nameof(Start.Id)}: {Start.Id}, End{nameof(End.Id)}: {End.Id}, ({Start.Type} -> {End.Type})";
- }
-
- internal static void Init() { }
- }
- }
|