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.

313 lines
11KB

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