PageRenderTime 1596ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/src/Autopilot/Autopilot.Navigator.WeldBlock.cs

https://gitlab.com/N3X15/SECE-ARMS
C# | 378 lines | 319 code | 53 blank | 6 comment | 57 complexity | b8bef1aaa9a681a3aa75ab00e14989a3 MD5 | raw file
  1. using System.Collections.Generic;
  2. using System.Text; // from mscorlib.dll
  3. using Rynchodon.Autopilot.Data;
  4. using Rynchodon.Autopilot.Movement;
  5. using Sandbox.Common.ObjectBuilders; // from MedievalEngineers.ObjectBuilders.dll and SpaceEngineers.ObjectBuilders.dll
  6. using Sandbox.Game.Entities;
  7. using Sandbox.ModAPI; // from Sandbox.Common.dll
  8. using VRage.Game.Entity;
  9. using VRage.Game.ModAPI; // from VRage.Math.dll
  10. using VRageMath;
  11. namespace Rynchodon.Autopilot.Navigator
  12. {
  13. public class WeldBlock : NavigatorMover, INavigatorRotator
  14. {
  15. private struct ComponentIndex
  16. {
  17. public MyPhysicalInventoryItem Component;
  18. public int Index;
  19. }
  20. private const float OffsetAdd = 5f;
  21. private const ulong TimeoutStart = 1200ul;
  22. private enum Stage : byte { Lineup, Approach, Weld, Retreat }
  23. private readonly Logger m_logger;
  24. private readonly PseudoBlock m_welder;
  25. private readonly List<Vector3I> m_neighbours = new List<Vector3I>();
  26. private readonly List<Vector3I> m_emptyNeighbours = new List<Vector3I>();
  27. private readonly float m_offset, m_slimTarget_initDmg;
  28. private IMySlimBlock m_targetSlim;
  29. private IMyCubeGrid m_otherGrid;
  30. private Vector3I m_targetCell;
  31. private bool m_weldProjection;
  32. private IMyCubeGrid m_realGrid
  33. { get { return m_weldProjection ? m_otherGrid : m_targetSlim.CubeGrid; } }
  34. private bool m_weldersEnabled;
  35. private float m_damage;
  36. private ulong m_timeout_start, m_lastWeld;
  37. private Vector3D m_targetWorld;
  38. private Vector3I? m_closestEmptyNeighbour;
  39. private Stage value_stage;
  40. private readonly LineSegmentD m_lineUp = new LineSegmentD();
  41. private Stage m_stage
  42. {
  43. get { return value_stage; }
  44. set
  45. {
  46. if (value_stage == value)
  47. return;
  48. m_logger.debugLog("stage changed from " + value_stage + " to " + value, Logger.severity.DEBUG);
  49. value_stage = value;
  50. m_navSet.OnTaskComplete_NavWay();
  51. switch (value_stage)
  52. {
  53. case Stage.Approach:
  54. m_lastWeld = Globals.UpdateCount;
  55. break;
  56. case Stage.Weld:
  57. m_lastWeld = Globals.UpdateCount;
  58. m_navSet.Settings_Task_NavWay.DestinationEntity = m_realGrid;
  59. m_navSet.Settings_Task_NavWay.SpeedMaxRelative = 1f;
  60. break;
  61. }
  62. }
  63. }
  64. public WeldBlock(Mover mover, AllNavigationSettings navSet, PseudoBlock welder, IMySlimBlock block)
  65. : base(mover)
  66. {
  67. this.m_logger = new Logger(GetType().Name, () => mover.Block.CubeGrid.DisplayName, () => block.getBestName(), () => m_stage.ToString());
  68. this.m_offset = welder.Block.LocalAABB.GetLongestDim() * 0.5f; // this works for default welders, may not work if mod has an exotic design
  69. this.m_welder = welder;
  70. this.m_targetSlim = block;
  71. this.m_timeout_start = Globals.UpdateCount + TimeoutStart;
  72. IMyCubeBlock Projector = ((MyCubeGrid)block.CubeGrid).Projector;
  73. if (Projector != null)
  74. {
  75. this.m_weldProjection = true;
  76. this.m_otherGrid = Projector.CubeGrid;
  77. this.m_slimTarget_initDmg = 1f;
  78. this.m_targetCell = Projector.CubeGrid.WorldToGridInteger(block.CubeGrid.GridIntegerToWorld(block.Position));
  79. }
  80. else
  81. {
  82. this.m_weldProjection = false;
  83. this.m_slimTarget_initDmg = block.Damage();
  84. this.m_targetCell = block.Position;
  85. }
  86. m_navSet.Settings_Task_NavEngage.NavigatorMover = this;
  87. m_navSet.Settings_Task_NavEngage.NavigatorRotator = this;
  88. m_navSet.Settings_Task_NavEngage.DestinationEntity = m_realGrid;
  89. IEnumerator<Vector3I> neighbours = this.m_targetSlim.ForEachNeighbourCell();
  90. while (neighbours.MoveNext())
  91. {
  92. Vector3I cell = m_weldProjection ? Projector.CubeGrid.WorldToGridInteger(block.CubeGrid.GridIntegerToWorld(neighbours.Current)) : neighbours.Current;
  93. m_neighbours.Add(cell);
  94. if (this.m_realGrid.GetCubeBlock(cell) == null)
  95. m_emptyNeighbours.Add(cell);
  96. }
  97. m_targetSlim.ComputeWorldCenter(out m_targetWorld);
  98. m_lineUp.To = m_targetWorld;
  99. }
  100. public override void Move()
  101. {
  102. if (m_targetSlim.Closed())
  103. {
  104. m_logger.debugLog("target block closed: " + m_targetSlim.getBestName(), Logger.severity.INFO);
  105. m_navSet.OnTaskComplete_NavEngage();
  106. EnableWelders(false);
  107. return;
  108. }
  109. m_targetSlim.ComputeWorldCenter(out m_targetWorld);
  110. if (m_stage == Stage.Retreat)
  111. {
  112. Retreat();
  113. return;
  114. }
  115. float offsetSquared = m_offset + OffsetAdd + OffsetAdd; offsetSquared *= offsetSquared;
  116. if (Vector3.DistanceSquared(m_welder.WorldPosition, m_targetWorld) > offsetSquared)
  117. {
  118. EnableWelders(false);
  119. if (m_closestEmptyNeighbour.HasValue && Globals.UpdateCount > m_timeout_start)
  120. {
  121. m_logger.debugLog("failed to start, dropping neighbour: " + m_closestEmptyNeighbour, Logger.severity.DEBUG);
  122. if (m_emptyNeighbours.Count > 1)
  123. {
  124. m_emptyNeighbours.Remove(m_closestEmptyNeighbour.Value);
  125. m_closestEmptyNeighbour = null;
  126. }
  127. else
  128. {
  129. m_logger.debugLog("tried every empty neighbour, giving up", Logger.severity.INFO);
  130. EnableWelders(false);
  131. m_stage = Stage.Retreat;
  132. return;
  133. }
  134. }
  135. if (!m_closestEmptyNeighbour.HasValue)
  136. {
  137. GetClosestEmptyNeighbour();
  138. if (!m_closestEmptyNeighbour.HasValue)
  139. {
  140. m_logger.debugLog("tried every empty neighbour, giving up", Logger.severity.INFO);
  141. EnableWelders(false);
  142. m_stage = Stage.Retreat;
  143. return;
  144. }
  145. m_timeout_start = Globals.UpdateCount + TimeoutStart;
  146. Vector3D from = m_realGrid.GridIntegerToWorld(m_closestEmptyNeighbour.Value);
  147. m_lineUp.From = m_lineUp.To + (from - m_lineUp.To) * 100d;
  148. }
  149. Vector3 closestPoint = m_lineUp.ClosestPoint(m_welder.WorldPosition);
  150. if (Vector3.DistanceSquared(m_welder.WorldPosition, closestPoint) > 1f || m_navSet.Settings_Current.DistanceAngle > 0.1f)
  151. {
  152. m_stage = Stage.Lineup;
  153. m_mover.CalcMove(m_welder, m_lineUp.ClosestPoint(m_welder.WorldPosition), m_realGrid.Physics.LinearVelocity, false);
  154. return;
  155. }
  156. else // linedup up
  157. m_stage = Stage.Approach;
  158. }
  159. else // near target
  160. {
  161. m_stage = Stage.Weld;
  162. EnableWelders(true);
  163. }
  164. MoveToTarget();
  165. }
  166. private void Retreat()
  167. {
  168. float minDist = m_offset + 10f; minDist *= minDist;
  169. if (Vector3D.DistanceSquared(m_welder.WorldPosition, m_targetWorld) > minDist)
  170. {
  171. m_logger.debugLog("moved away from: " + m_targetSlim.getBestName(), Logger.severity.DEBUG);
  172. if (!m_weldProjection && m_targetSlim.Damage() < m_slimTarget_initDmg)
  173. {
  174. m_logger.debugLog(m_targetSlim.Damage() != 0f, "assuming ship ran out of components, damage: " + m_targetSlim.Damage());
  175. m_navSet.OnTaskComplete_NavEngage();
  176. m_mover.StopMove();
  177. m_mover.StopRotate();
  178. return;
  179. }
  180. else
  181. {
  182. m_logger.debugLog("no welding done, trying another approach");
  183. if (m_emptyNeighbours.Count > 1)
  184. {
  185. // if we were very close when we started, no neighbour would have been chosen
  186. if (m_closestEmptyNeighbour.HasValue)
  187. {
  188. m_emptyNeighbours.Remove(m_closestEmptyNeighbour.Value);
  189. m_closestEmptyNeighbour = null;
  190. }
  191. m_stage = Stage.Lineup;
  192. return;
  193. }
  194. else
  195. {
  196. m_logger.debugLog("tried every empty neighbour, giving up", Logger.severity.INFO);
  197. m_navSet.OnTaskComplete_NavEngage();
  198. m_mover.StopMove();
  199. m_mover.StopRotate();
  200. return;
  201. }
  202. }
  203. }
  204. Vector3D direction = m_welder.WorldPosition - m_targetWorld;
  205. direction.Normalize();
  206. Vector3D destination = m_welder.WorldPosition + direction * 10d;
  207. m_mover.CalcMove(m_welder, destination, m_realGrid.Physics.LinearVelocity, true);
  208. }
  209. private void MoveToTarget()
  210. {
  211. m_logger.debugLog(m_stage != Stage.Approach && m_stage != Stage.Weld, "moving to target in wrong stage", Logger.severity.FATAL);
  212. if ((Globals.UpdateCount - m_lastWeld) > 1200ul)
  213. {
  214. m_logger.debugLog("failed to repair block");
  215. EnableWelders(false);
  216. m_stage = Stage.Retreat;
  217. return;
  218. }
  219. if (m_weldProjection)
  220. {
  221. // check for slim being placed
  222. IMySlimBlock placed = m_realGrid.GetCubeBlock(m_targetCell);
  223. if (placed != null)
  224. {
  225. m_logger.debugLog("projected target placed", Logger.severity.DEBUG);
  226. m_weldProjection = false;
  227. m_targetSlim = placed;
  228. }
  229. }
  230. // check for weld
  231. float damage = m_targetSlim.Damage();
  232. foreach (Vector3I cell in m_neighbours)
  233. {
  234. IMySlimBlock slim = m_realGrid.GetCubeBlock(cell);
  235. if (slim != null)
  236. damage += slim.Damage();
  237. }
  238. if (damage < m_damage)
  239. m_lastWeld = Globals.UpdateCount;
  240. m_damage = damage;
  241. if (!m_weldProjection && m_targetSlim.Damage() == 0f && (Globals.UpdateCount - m_lastWeld) > 120ul)
  242. {
  243. m_logger.debugLog("target block repaired: " + m_targetSlim.getBestName(), Logger.severity.DEBUG);
  244. EnableWelders(false);
  245. m_stage = Stage.Retreat;
  246. return;
  247. }
  248. else
  249. {
  250. float offset = m_stage == Stage.Weld ? m_offset : m_offset + OffsetAdd;
  251. Vector3D welderFromTarget = m_controlBlock.CubeBlock.GetPosition() - m_targetWorld;
  252. welderFromTarget.Normalize();
  253. m_mover.CalcMove(m_welder, m_targetWorld + welderFromTarget * offset, m_realGrid.Physics.LinearVelocity, true);
  254. }
  255. }
  256. public void Rotate()
  257. {
  258. m_mover.CalcRotate(m_welder, RelativeDirection3F.FromWorld(m_welder.Grid, m_targetWorld - m_welder.WorldPosition));
  259. }
  260. public override void AppendCustomInfo(StringBuilder customInfo)
  261. {
  262. customInfo.Append(m_stage);
  263. customInfo.Append(": ");
  264. customInfo.AppendLine(m_targetSlim.getBestName());
  265. }
  266. /// <summary>
  267. /// Enabled/disable all welders.
  268. /// </summary>
  269. private void EnableWelders(bool enable)
  270. {
  271. if (enable == m_weldersEnabled)
  272. return;
  273. m_weldersEnabled = enable;
  274. if (enable)
  275. m_logger.debugLog("Enabling welders", Logger.severity.DEBUG);
  276. else
  277. m_logger.debugLog("Disabling welders", Logger.severity.DEBUG);
  278. var cache = CubeGridCache.GetFor(m_controlBlock.CubeGrid);
  279. if (cache == null)
  280. {
  281. m_logger.debugLog("Failed to get cache", Logger.severity.INFO);
  282. return;
  283. }
  284. var allWelders = cache.GetBlocksOfType(typeof(MyObjectBuilder_ShipWelder));
  285. if (allWelders == null)
  286. {
  287. m_logger.debugLog("Failed to get block list", Logger.severity.INFO);
  288. return;
  289. }
  290. MyAPIGateway.Utilities.TryInvokeOnGameThread(() => {
  291. foreach (IMyShipWelder welder in allWelders)
  292. if (!welder.Closed)
  293. welder.RequestEnable(enable);
  294. });
  295. }
  296. private void GetClosestEmptyNeighbour()
  297. {
  298. double closest = float.MaxValue;
  299. List<Vector3I> removeList = null;
  300. foreach (Vector3I emptyCell in m_emptyNeighbours)
  301. {
  302. IMySlimBlock slim = m_realGrid.GetCubeBlock(emptyCell);
  303. if (slim != null)
  304. {
  305. m_logger.debugLog("block placed at " + emptyCell, Logger.severity.DEBUG);
  306. if (removeList == null)
  307. removeList = new List<Vector3I>();
  308. removeList.Add(emptyCell);
  309. continue;
  310. }
  311. Vector3D emptyPos = m_realGrid.GridIntegerToWorld(emptyCell);
  312. double dist = Vector3D.DistanceSquared(m_welder.WorldPosition, emptyPos);
  313. if (dist < closest)
  314. {
  315. closest = dist;
  316. m_closestEmptyNeighbour = emptyCell;
  317. }
  318. }
  319. if (removeList != null)
  320. foreach (Vector3I cell in removeList)
  321. m_emptyNeighbours.Remove(cell);
  322. m_logger.debugLog(m_closestEmptyNeighbour.HasValue, () => "closest cell: " + m_closestEmptyNeighbour + ", closest position: " + m_realGrid.GridIntegerToWorld(m_closestEmptyNeighbour.Value), Logger.severity.DEBUG);
  323. }
  324. }
  325. }