A stable modding interface between Techblox and mods https://mod.exmods.org/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

252 lines
9.5KB

  1. using Gamecraft.Wires;
  2. using Svelto.ECS;
  3. using TechbloxModdingAPI.Blocks.Engines;
  4. using TechbloxModdingAPI.Common;
  5. namespace TechbloxModdingAPI.Blocks
  6. {
  7. public class Wire : EcsObjectBase
  8. {
  9. internal static SignalEngine signalEngine;
  10. protected EGID startPortEGID;
  11. protected EGID endPortEGID;
  12. protected EGID startBlockEGID;
  13. protected EGID endBlockEGID;
  14. protected EGID wireEGID;
  15. protected bool inputToOutput;
  16. protected byte startPort;
  17. protected byte endPort;
  18. public static Wire Connect(SignalingBlock start, byte startPort, SignalingBlock end, byte endPort)
  19. {
  20. var (wire, id) = signalEngine.CreateNewWire(start.Id, startPort, end.Id, endPort);
  21. return new Wire(wire, start, end, id);
  22. }
  23. /// <summary>
  24. /// An existing wire connection ending at the specified input.
  25. /// If multiple exist, this will return the first one found.
  26. /// </summary>
  27. /// <param name="end">Destination block.</param>
  28. /// <param name="endPort">Port number.</param>
  29. /// <returns>The wire, where the end of the wire is the block port specified, or null if does not exist.</returns>
  30. public static Wire ConnectedToInputPort(SignalingBlock end, byte endPort)
  31. {
  32. var port = signalEngine.MatchBlockIOToPort(end, endPort, false);
  33. if (!port) return null;
  34. var wire = signalEngine.MatchPortToWire(port, end.Id, out var egid);
  35. return wire
  36. ? new Wire(wire.Get().sourceBlockEGID, end.Id, wire.Get().sourcePortUsage, endPort, egid, false)
  37. : null;
  38. }
  39. /// <summary>
  40. /// An existing wire connection starting at the specified output.
  41. /// If multiple exist, this will return the first one found.
  42. /// </summary>
  43. /// <param name="start">Source block entity ID.</param>
  44. /// <param name="startPort">Port number.</param>
  45. /// <returns>The wire, where the start of the wire is the block port specified, or null if does not exist.</returns>
  46. public static Wire ConnectedToOutputPort(SignalingBlock start, byte startPort)
  47. {
  48. var port = signalEngine.MatchBlockIOToPort(start, startPort, true);
  49. if (!port) return null;
  50. var wire = signalEngine.MatchPortToWire(port, start.Id, out var egid);
  51. return wire
  52. ? new Wire(start.Id, wire.Get().destinationBlockEGID, startPort, wire.Get().destinationPortUsage, egid, false)
  53. : null;
  54. }
  55. /// <summary>
  56. /// Construct a wire object from an existing wire connection.
  57. /// </summary>
  58. /// <param name="start">Starting block ID.</param>
  59. /// <param name="end">Ending block ID.</param>
  60. /// <param name="startPort">Starting port number.</param>
  61. /// <param name="endPort">Ending port number.</param>
  62. /// <param name="wire">The wire ID.</param>
  63. /// <param name="inputToOutput">Whether the wire direction goes input -> output (true) or output -> input (false, preferred).</param>
  64. public Wire(Block start, Block end, byte startPort, byte endPort, EGID wire, bool inputToOutput)
  65. : this(start.Id, end.Id, startPort, endPort, wire, inputToOutput)
  66. {
  67. } // TODO: Convert all constructors (including the removed one) to static methods
  68. private Wire(EGID startBlock, EGID endBlock, byte startPort, byte endPort, EGID wire, bool inputToOutput) : base(wire, typeof(WireEntityDescriptor))
  69. {
  70. Construct(startBlock, endBlock, startPort, endPort, wire, inputToOutput);
  71. }
  72. private void Construct(EGID startBlock, EGID endBlock, byte startPort, byte endPort, EGID wire, bool inputToOutput)
  73. {
  74. this.startBlockEGID = startBlock;
  75. this.endBlockEGID = endBlock;
  76. this.inputToOutput = inputToOutput;
  77. this.wireEGID = wire;
  78. endPortEGID = signalEngine.MatchBlockIOToPort(startBlock, startPort, inputToOutput).EGID;
  79. if (endPortEGID == default) throw new WireInvalidException("Wire end port not found");
  80. startPortEGID = signalEngine.MatchBlockIOToPort(endBlock, endPort, !inputToOutput).EGID;
  81. if (startPortEGID == default) throw new WireInvalidException("Wire start port not found");
  82. this.startPort = startPort;
  83. this.endPort = endPort;
  84. }
  85. /// <summary>
  86. /// Construct a wire object from an existing wire connection.
  87. /// </summary>
  88. /// <param name="wireEgid">The wire ID.</param>
  89. public Wire(EGID wireEgid) : base(wireEgid, typeof(WireEntityDescriptor))
  90. {
  91. WireEntityStruct wire = signalEngine.GetWire(wireEGID);
  92. Construct(wire.sourceBlockEGID, wire.destinationBlockEGID, wire.sourcePortUsage, wire.destinationPortUsage,
  93. wireEgid, false);
  94. }
  95. private Wire(WireEntityStruct wire, SignalingBlock src, SignalingBlock dest, EGID wireEgid)
  96. : this(src, dest, wire.sourcePortUsage, wire.destinationPortUsage, wireEgid, false)
  97. {
  98. }
  99. /// <summary>
  100. /// The wire's signal value, as a float.
  101. /// </summary>
  102. public float Float
  103. {
  104. get
  105. {
  106. return signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsFloat;
  107. }
  108. set
  109. {
  110. signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsFloat = value;
  111. }
  112. }
  113. /// <summary>
  114. /// The wire's string signal.
  115. /// </summary>
  116. public string String
  117. {
  118. get
  119. {
  120. return signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsEcsString;
  121. }
  122. set
  123. {
  124. signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsEcsString.Set(value);
  125. }
  126. }
  127. /// <summary>
  128. /// The wire's signal id.
  129. /// I'm 50% sure this is useless.
  130. /// </summary>
  131. public uint SignalId
  132. {
  133. get
  134. {
  135. return signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsID;
  136. }
  137. set
  138. {
  139. signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsID = value;
  140. }
  141. }
  142. /// <summary>
  143. /// The block at the beginning of the wire.
  144. /// </summary>
  145. public SignalingBlock Start
  146. {
  147. get => (SignalingBlock)Block.New(startBlockEGID);
  148. }
  149. /// <summary>
  150. /// The port number that the beginning of the wire connects to.
  151. /// </summary>
  152. public byte StartPort
  153. {
  154. get => startPort;
  155. }
  156. /// <summary>
  157. /// The display name of the start port.
  158. /// </summary>
  159. public string StartPortName
  160. {
  161. get => signalEngine.GetPort(startPortEGID).portNameLocalised;
  162. }
  163. /// <summary>
  164. /// The block at the end of the wire.
  165. /// </summary>
  166. public SignalingBlock End
  167. {
  168. get => (SignalingBlock)Block.New(endBlockEGID);
  169. }
  170. /// <summary>
  171. /// The port number that the end of the wire connects to.
  172. /// </summary>
  173. public byte EndPort
  174. {
  175. get => endPort;
  176. }
  177. /// <summary>
  178. /// The display name of the end port.
  179. /// </summary>
  180. public string EndPortName
  181. {
  182. get => signalEngine.GetPort(endPortEGID).portNameLocalised;
  183. }
  184. /// <summary>
  185. /// 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.
  186. /// This is simply a different memory configuration and does not affect the in-game wire (which is always output -> input).
  187. /// </summary>
  188. /// <returns>A copy of the wire object.</returns>
  189. public Wire OutputToInputCopy()
  190. {
  191. return GetInstanceExisting(wireEGID, egid => new Wire(egid));
  192. }
  193. /// <summary>
  194. /// Convert the wire object to the direction the signal flows.
  195. /// Signals on wires always flow from a block output port to a block input port.
  196. /// This is simply a different memory configuration and does not affect the in-game wire (which is always output -> input).
  197. /// </summary>
  198. public void OutputToInputInPlace()
  199. {
  200. if (inputToOutput)
  201. {
  202. inputToOutput = false;
  203. // swap inputs and outputs
  204. (endBlockEGID, startBlockEGID) = (startBlockEGID, endBlockEGID);
  205. (endPortEGID, startPortEGID) = (startPortEGID, endPortEGID);
  206. (endPort, startPort) = (startPort, endPort);
  207. }
  208. }
  209. public override string ToString()
  210. {
  211. if (signalEngine.Exists<WireEntityStruct>(wireEGID))
  212. {
  213. 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) : "")})";
  214. }
  215. return $"{nameof(Id)}: {Id}, Start{nameof(Start.Id)}: {Start.Id}, End{nameof(End.Id)}: {End.Id}, ({Start.Type}::{StartPort} -> {End.Type}::{EndPort})";
  216. }
  217. internal static void Init() { }
  218. }
  219. }