TechbloxModdingAPI/GamecraftModdingAPI/Blocks/Wire.cs

355 lines
14 KiB
C#

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;
protected byte startPort;
protected byte endPort;
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, start, end);
}
/// <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;
endPort = wire.destinationPortUsage;
startPort = wire.sourcePortUsage;
}
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
endPort = wire.sourcePortUsage;
startPort = wire.destinationPortUsage;
}
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");
}
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)
{
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");
this.endPort = wire.destinationPortUsage;
this.startPort = wire.sourcePortUsage;
}
internal Wire(WireEntityStruct wire, SignalingBlock src, SignalingBlock dest)
{
this.wireEGID = wire.ID;
this.startBlockEGID = wire.sourceBlockEGID;
this.endBlockEGID = wire.destinationBlockEGID;
inputToOutput = false;
endPortEGID = signalEngine.MatchBlockInputToPort(dest, wire.destinationPortUsage, out bool exists);
if (!exists) throw new WireInvalidException("Wire end port not found");
startPortEGID = signalEngine.MatchBlockOutputToPort(src, wire.sourcePortUsage, out exists);
if (!exists) throw new WireInvalidException("Wire start port not found");
this.endPort = wire.destinationPortUsage;
this.startPort = wire.sourcePortUsage;
}
/// <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 => startPort;
}
/// <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 => endPort;
}
/// <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 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
EGID temp = endBlockEGID;
endBlockEGID = startBlockEGID;
startBlockEGID = temp;
temp = endPortEGID;
endPortEGID = startPortEGID;
startPortEGID = temp;
byte tempPortNumber = endPort;
endPort = startPort;
startPort = tempPortNumber;
}
}
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}::{StartPort} -> {End.Type}::{EndPort})";
}
internal static void Init() { }
}
}