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.

315 lines
12KB

  1. using System;
  2. using Gamecraft.Wires;
  3. using Svelto.ECS;
  4. using Svelto.ECS.Experimental;
  5. using TechbloxModdingAPI.Blocks.Engines;
  6. namespace TechbloxModdingAPI.Blocks
  7. {
  8. public class Wire : EcsObjectBase
  9. {
  10. internal static SignalEngine signalEngine;
  11. protected EGID startPortEGID;
  12. protected EGID endPortEGID;
  13. protected EGID startBlockEGID;
  14. protected EGID endBlockEGID;
  15. protected EGID wireEGID;
  16. protected bool inputToOutput;
  17. protected byte startPort;
  18. protected byte endPort;
  19. public static Wire Connect(SignalingBlock start, byte startPort, SignalingBlock end, byte endPort)
  20. {
  21. WireEntityStruct wire = signalEngine.CreateNewWire(start.Id, startPort, end.Id, endPort);
  22. return new Wire(wire, start, end);
  23. }
  24. /// <summary>
  25. /// An existing wire connection ending at the specified input.
  26. /// If multiple exist, this will return the first one found.
  27. /// </summary>
  28. /// <param name="end">Destination block.</param>
  29. /// <param name="endPort">Port number.</param>
  30. /// <returns>The wire, where the end of the wire is the block port specified, or null if does not exist.</returns>
  31. public static Wire ConnectedToInputPort(SignalingBlock end, byte endPort)
  32. {
  33. var port = signalEngine.MatchBlockIOToPort(end, endPort, false);
  34. if (!port) return null;
  35. WireEntityStruct wire = signalEngine.MatchPortToWire(port, end.Id, out var exists);
  36. return exists
  37. ? new Wire(wire.sourceBlockEGID, end.Id, wire.sourcePortUsage, endPort, wire.ID, false)
  38. : null;
  39. }
  40. /// <summary>
  41. /// An existing wire connection starting at the specified output.
  42. /// If multiple exist, this will return the first one found.
  43. /// </summary>
  44. /// <param name="start">Source block entity ID.</param>
  45. /// <param name="startPort">Port number.</param>
  46. /// <returns>The wire, where the start of the wire is the block port specified, or null if does not exist.</returns>
  47. public static Wire ConnectedToOutputPort(SignalingBlock start, byte startPort)
  48. {
  49. var port = signalEngine.MatchBlockIOToPort(start, startPort, true);
  50. if (!port) return null;
  51. WireEntityStruct wire = signalEngine.MatchPortToWire(port, start.Id, out var exists);
  52. return exists
  53. ? new Wire(start.Id, wire.destinationBlockEGID, startPort, wire.destinationPortUsage, wire.ID, false)
  54. : null;
  55. }
  56. /// <summary>
  57. /// Construct a wire object froam n existing connection.
  58. /// </summary>
  59. /// <param name="start">Starting block ID.</param>
  60. /// <param name="end">Ending block ID.</param>
  61. /// <param name="startPort">Starting port number, or guess if omitted.</param>
  62. /// <param name="endPort">Ending port number, or guess if omitted.</param>
  63. /// <exception cref="WireInvalidException">Guessing failed or wire does not exist.</exception>
  64. public Wire(Block start, Block end, byte startPort = Byte.MaxValue, byte endPort = Byte.MaxValue)
  65. {
  66. startBlockEGID = start.Id;
  67. endBlockEGID = end.Id;
  68. bool flipped = false;
  69. // find block ports
  70. EGID wire = signalEngine.MatchBlocksToWire(start.Id, end.Id, startPort, endPort);
  71. if (wire == default)
  72. {
  73. // flip I/O around and try again
  74. wire = signalEngine.MatchBlocksToWire(end.Id, start.Id, endPort, startPort);
  75. flipped = true;
  76. // NB: start and end are handled exactly as they're received as params.
  77. // This makes wire traversal easier, but makes logic in this class a bit more complex
  78. }
  79. if (wire != default)
  80. {
  81. Construct(start.Id, end.Id, startPort, endPort, wire, flipped);
  82. }
  83. else
  84. {
  85. throw new WireInvalidException("Wire not found");
  86. }
  87. }
  88. /// <summary>
  89. /// Construct a wire object from an existing wire connection.
  90. /// </summary>
  91. /// <param name="start">Starting block ID.</param>
  92. /// <param name="end">Ending block ID.</param>
  93. /// <param name="startPort">Starting port number.</param>
  94. /// <param name="endPort">Ending port number.</param>
  95. /// <param name="wire">The wire ID.</param>
  96. /// <param name="inputToOutput">Whether the wire direction goes input -> output (true) or output -> input (false, preferred).</param>
  97. public Wire(Block start, Block end, byte startPort, byte endPort, EGID wire, bool inputToOutput)
  98. : this(start.Id, end.Id, startPort, endPort, wire, inputToOutput)
  99. {
  100. }
  101. private Wire(EGID startBlock, EGID endBlock, byte startPort, byte endPort, EGID wire, bool inputToOutput)
  102. {
  103. Construct(startBlock, endBlock, startPort, endPort, wire, inputToOutput);
  104. }
  105. private void Construct(EGID startBlock, EGID endBlock, byte startPort, byte endPort, EGID wire, bool inputToOutput)
  106. {
  107. this.startBlockEGID = startBlock;
  108. this.endBlockEGID = endBlock;
  109. this.inputToOutput = inputToOutput;
  110. this.wireEGID = wire;
  111. endPortEGID = signalEngine.MatchBlockIOToPort(startBlock, startPort, inputToOutput).Nullable()?.ID ?? default;
  112. if (endPortEGID == default) throw new WireInvalidException("Wire end port not found");
  113. startPortEGID = signalEngine.MatchBlockIOToPort(endBlock, endPort, !inputToOutput).Nullable()?.ID ?? default;
  114. if (startPortEGID == default) throw new WireInvalidException("Wire start port not found");
  115. this.startPort = startPort;
  116. this.endPort = endPort;
  117. }
  118. /// <summary>
  119. /// Construct a wire object from an existing wire connection.
  120. /// </summary>
  121. /// <param name="wireEgid">The wire ID.</param>
  122. public Wire(EGID wireEgid)
  123. {
  124. WireEntityStruct wire = signalEngine.GetWire(wireEGID);
  125. Construct(wire.sourceBlockEGID, wire.destinationBlockEGID, wire.sourcePortUsage, wire.destinationPortUsage,
  126. wireEgid, false);
  127. }
  128. private Wire(WireEntityStruct wire, SignalingBlock src, SignalingBlock dest)
  129. : this(src, dest, wire.sourcePortUsage, wire.destinationPortUsage, wire.ID, false)
  130. {
  131. }
  132. /// <summary>
  133. /// The wire's in-game id.
  134. /// </summary>
  135. public override EGID Id
  136. {
  137. get => wireEGID;
  138. }
  139. /// <summary>
  140. /// The wire's signal value, as a float.
  141. /// </summary>
  142. public float Float
  143. {
  144. get
  145. {
  146. return signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsFloat;
  147. }
  148. set
  149. {
  150. signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsFloat = value;
  151. }
  152. }
  153. /// <summary>
  154. /// The wire's string signal.
  155. /// </summary>
  156. public string String
  157. {
  158. get
  159. {
  160. return signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsEcsString;
  161. }
  162. set
  163. {
  164. signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsEcsString.Set(value);
  165. }
  166. }
  167. /// <summary>
  168. /// The wire's raw string signal.
  169. /// </summary>
  170. public ECSString ECSString
  171. {
  172. get
  173. {
  174. return signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsEcsString;
  175. }
  176. set
  177. {
  178. signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsEcsString = value;
  179. }
  180. }
  181. /// <summary>
  182. /// The wire's signal id.
  183. /// I'm 50% sure this is useless.
  184. /// </summary>
  185. public uint SignalId
  186. {
  187. get
  188. {
  189. return signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsID;
  190. }
  191. set
  192. {
  193. signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsID = value;
  194. }
  195. }
  196. /// <summary>
  197. /// The block at the beginning of the wire.
  198. /// </summary>
  199. public SignalingBlock Start
  200. {
  201. get => (SignalingBlock)Block.New(startBlockEGID);
  202. }
  203. /// <summary>
  204. /// The port number that the beginning of the wire connects to.
  205. /// </summary>
  206. public byte StartPort
  207. {
  208. get => startPort;
  209. }
  210. /// <summary>
  211. /// The display name of the start port.
  212. /// </summary>
  213. public string StartPortName
  214. {
  215. get => signalEngine.GetPort(startPortEGID).portNameLocalised;
  216. }
  217. /// <summary>
  218. /// The block at the end of the wire.
  219. /// </summary>
  220. public SignalingBlock End
  221. {
  222. get => (SignalingBlock)Block.New(endBlockEGID);
  223. }
  224. /// <summary>
  225. /// The port number that the end of the wire connects to.
  226. /// </summary>
  227. public byte EndPort
  228. {
  229. get => endPort;
  230. }
  231. /// <summary>
  232. /// The display name of the end port.
  233. /// </summary>
  234. public string EndPortName
  235. {
  236. get => signalEngine.GetPort(endPortEGID).portNameLocalised;
  237. }
  238. /// <summary>
  239. /// 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.
  240. /// This is simply a different memory configuration and does not affect the in-game wire (which is always output -> input).
  241. /// </summary>
  242. /// <returns>A copy of the wire object.</returns>
  243. public Wire OutputToInputCopy()
  244. {
  245. return GetInstance(wireEGID, egid => new Wire(egid));
  246. }
  247. /// <summary>
  248. /// Convert the wire object to the direction the signal flows.
  249. /// Signals on wires always flow from a block output port to a block input port.
  250. /// This is simply a different memory configuration and does not affect the in-game wire (which is always output -> input).
  251. /// </summary>
  252. public void OutputToInputInPlace()
  253. {
  254. if (inputToOutput)
  255. {
  256. inputToOutput = false;
  257. // swap inputs and outputs
  258. (endBlockEGID, startBlockEGID) = (startBlockEGID, endBlockEGID);
  259. var tempPort = endPortEGID;
  260. endPortEGID = startPortEGID;
  261. startPortEGID = tempPort;
  262. (endPort, startPort) = (startPort, endPort);
  263. }
  264. }
  265. public override string ToString()
  266. {
  267. if (signalEngine.Exists<WireEntityStruct>(wireEGID))
  268. {
  269. 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) : "")})";
  270. }
  271. return $"{nameof(Id)}: {Id}, Start{nameof(Start.Id)}: {Start.Id}, End{nameof(End.Id)}: {End.Id}, ({Start.Type}::{StartPort} -> {End.Type}::{EndPort})";
  272. }
  273. internal static void Init() { }
  274. }
  275. }