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.

312 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) : base(ecs =>
  65. {
  66. var th = (Wire)ecs;
  67. th.startBlockEGID = start.Id;
  68. th.endBlockEGID = end.Id;
  69. bool flipped = false;
  70. // find block ports
  71. EGID wire = signalEngine.MatchBlocksToWire(start.Id, end.Id, startPort, endPort);
  72. if (wire == default)
  73. {
  74. // flip I/O around and try again
  75. wire = signalEngine.MatchBlocksToWire(end.Id, start.Id, endPort, startPort);
  76. flipped = true;
  77. // NB: start and end are handled exactly as they're received as params.
  78. // This makes wire traversal easier, but makes logic in this class a bit more complex
  79. }
  80. if (wire != default)
  81. {
  82. th.Construct(start.Id, end.Id, startPort, endPort, wire, flipped);
  83. }
  84. else
  85. {
  86. throw new WireInvalidException("Wire not found");
  87. }
  88. return th.wireEGID;
  89. })
  90. {
  91. }
  92. /// <summary>
  93. /// Construct a wire object from an existing wire connection.
  94. /// </summary>
  95. /// <param name="start">Starting block ID.</param>
  96. /// <param name="end">Ending block ID.</param>
  97. /// <param name="startPort">Starting port number.</param>
  98. /// <param name="endPort">Ending port number.</param>
  99. /// <param name="wire">The wire ID.</param>
  100. /// <param name="inputToOutput">Whether the wire direction goes input -> output (true) or output -> input (false, preferred).</param>
  101. public Wire(Block start, Block end, byte startPort, byte endPort, EGID wire, bool inputToOutput)
  102. : this(start.Id, end.Id, startPort, endPort, wire, inputToOutput)
  103. {
  104. }
  105. private Wire(EGID startBlock, EGID endBlock, byte startPort, byte endPort, EGID wire, bool inputToOutput) : base(wire)
  106. {
  107. Construct(startBlock, endBlock, startPort, endPort, wire, inputToOutput);
  108. }
  109. private void Construct(EGID startBlock, EGID endBlock, byte startPort, byte endPort, EGID wire, bool inputToOutput)
  110. {
  111. this.startBlockEGID = startBlock;
  112. this.endBlockEGID = endBlock;
  113. this.inputToOutput = inputToOutput;
  114. this.wireEGID = wire;
  115. endPortEGID = signalEngine.MatchBlockIOToPort(startBlock, startPort, inputToOutput).Nullable()?.ID ?? default;
  116. if (endPortEGID == default) throw new WireInvalidException("Wire end port not found");
  117. startPortEGID = signalEngine.MatchBlockIOToPort(endBlock, endPort, !inputToOutput).Nullable()?.ID ?? default;
  118. if (startPortEGID == default) throw new WireInvalidException("Wire start port not found");
  119. this.startPort = startPort;
  120. this.endPort = endPort;
  121. }
  122. /// <summary>
  123. /// Construct a wire object from an existing wire connection.
  124. /// </summary>
  125. /// <param name="wireEgid">The wire ID.</param>
  126. public Wire(EGID wireEgid) : base(wireEgid)
  127. {
  128. WireEntityStruct wire = signalEngine.GetWire(wireEGID);
  129. Construct(wire.sourceBlockEGID, wire.destinationBlockEGID, wire.sourcePortUsage, wire.destinationPortUsage,
  130. wireEgid, false);
  131. }
  132. private Wire(WireEntityStruct wire, SignalingBlock src, SignalingBlock dest)
  133. : this(src, dest, wire.sourcePortUsage, wire.destinationPortUsage, wire.ID, false)
  134. {
  135. }
  136. /// <summary>
  137. /// The wire's signal value, as a float.
  138. /// </summary>
  139. public float Float
  140. {
  141. get
  142. {
  143. return signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsFloat;
  144. }
  145. set
  146. {
  147. signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsFloat = value;
  148. }
  149. }
  150. /// <summary>
  151. /// The wire's string signal.
  152. /// </summary>
  153. public string String
  154. {
  155. get
  156. {
  157. return signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsEcsString;
  158. }
  159. set
  160. {
  161. signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsEcsString.Set(value);
  162. }
  163. }
  164. /// <summary>
  165. /// The wire's raw string signal.
  166. /// </summary>
  167. public ECSString ECSString
  168. {
  169. get
  170. {
  171. return signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsEcsString;
  172. }
  173. set
  174. {
  175. signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsEcsString = value;
  176. }
  177. }
  178. /// <summary>
  179. /// The wire's signal id.
  180. /// I'm 50% sure this is useless.
  181. /// </summary>
  182. public uint SignalId
  183. {
  184. get
  185. {
  186. return signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsID;
  187. }
  188. set
  189. {
  190. signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsID = value;
  191. }
  192. }
  193. /// <summary>
  194. /// The block at the beginning of the wire.
  195. /// </summary>
  196. public SignalingBlock Start
  197. {
  198. get => (SignalingBlock)Block.New(startBlockEGID);
  199. }
  200. /// <summary>
  201. /// The port number that the beginning of the wire connects to.
  202. /// </summary>
  203. public byte StartPort
  204. {
  205. get => startPort;
  206. }
  207. /// <summary>
  208. /// The display name of the start port.
  209. /// </summary>
  210. public string StartPortName
  211. {
  212. get => signalEngine.GetPort(startPortEGID).portNameLocalised;
  213. }
  214. /// <summary>
  215. /// The block at the end of the wire.
  216. /// </summary>
  217. public SignalingBlock End
  218. {
  219. get => (SignalingBlock)Block.New(endBlockEGID);
  220. }
  221. /// <summary>
  222. /// The port number that the end of the wire connects to.
  223. /// </summary>
  224. public byte EndPort
  225. {
  226. get => endPort;
  227. }
  228. /// <summary>
  229. /// The display name of the end port.
  230. /// </summary>
  231. public string EndPortName
  232. {
  233. get => signalEngine.GetPort(endPortEGID).portNameLocalised;
  234. }
  235. /// <summary>
  236. /// 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.
  237. /// This is simply a different memory configuration and does not affect the in-game wire (which is always output -> input).
  238. /// </summary>
  239. /// <returns>A copy of the wire object.</returns>
  240. public Wire OutputToInputCopy()
  241. {
  242. return GetInstance(wireEGID, egid => new Wire(egid));
  243. }
  244. /// <summary>
  245. /// Convert the wire object to the direction the signal flows.
  246. /// Signals on wires always flow from a block output port to a block input port.
  247. /// This is simply a different memory configuration and does not affect the in-game wire (which is always output -> input).
  248. /// </summary>
  249. public void OutputToInputInPlace()
  250. {
  251. if (inputToOutput)
  252. {
  253. inputToOutput = false;
  254. // swap inputs and outputs
  255. (endBlockEGID, startBlockEGID) = (startBlockEGID, endBlockEGID);
  256. var tempPort = endPortEGID;
  257. endPortEGID = startPortEGID;
  258. startPortEGID = tempPort;
  259. (endPort, startPort) = (startPort, endPort);
  260. }
  261. }
  262. public override string ToString()
  263. {
  264. if (signalEngine.Exists<WireEntityStruct>(wireEGID))
  265. {
  266. 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) : "")})";
  267. }
  268. return $"{nameof(Id)}: {Id}, Start{nameof(Start.Id)}: {Start.Id}, End{nameof(End.Id)}: {End.Id}, ({Start.Type}::{StartPort} -> {End.Type}::{EndPort})";
  269. }
  270. internal static void Init() { }
  271. }
  272. }