338 lines
No EOL
13 KiB
C#
338 lines
No EOL
13 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;
|
|
|
|
public static Wire Connect(SignalingBlock start, byte startPort, SignalingBlock end, byte endPort)
|
|
{
|
|
EGID wireEgid = signalEngine.CreateNewWire(start.Id, startPort, end.Id, endPort);
|
|
return new Wire(start, end, startPort, endPort, wireEgid, false);
|
|
}
|
|
|
|
/// <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;
|
|
}
|
|
|
|
/// <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(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(Id)}: {End.Id}, ({Start.Type}::{StartPort}) -> ({End.Type}::{EndPort})";
|
|
}
|
|
|
|
internal static void Init() { }
|
|
}
|
|
} |