PageRenderTime 76ms CodeModel.GetById 30ms RepoModel.GetById 0ms app.codeStats 0ms

/src/Autopilot/Autopilot.InstructionCommand.Enemy.cs

https://gitlab.com/N3X15/SECE-ARMS
C# | 341 lines | 296 code | 44 blank | 1 comment | 49 complexity | 19431e40ff853b37929ca5bb9ef98d3d MD5 | raw file
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using Rynchodon.Settings;
  6. using Sandbox.Game.Entities;
  7. using Sandbox.Game.Gui;
  8. using Sandbox.ModAPI;
  9. using Sandbox.ModAPI.Interfaces.Terminal;
  10. using VRage.ModAPI;
  11. using VRage.Utils;
  12. namespace Rynchodon.Autopilot.Instruction.Command
  13. {
  14. public class Enemy : ACommand
  15. {
  16. private MyTerminalControlListBoxItem[] value_allResponses;
  17. private List<EnemyFinder.Response> m_activeResponses = new List<EnemyFinder.Response>();
  18. /// <summary>0 is not set, -1 is invalid</summary>
  19. private long m_enemyId;
  20. private float m_range;
  21. private IMyTerminalControlListbox m_responseListbox;
  22. private EnemyFinder.Response m_selected;
  23. private bool m_addingResponse;
  24. private MyTerminalControlListBoxItem[] m_allResponses
  25. {
  26. get
  27. {
  28. if (value_allResponses == null)
  29. {
  30. value_allResponses = new MyTerminalControlListBoxItem[] {
  31. new MyTerminalControlListBoxItem(MyStringId.GetOrCompute("Off"), MyStringId.GetOrCompute("Not a response, used to stop searching for enemies"), EnemyFinder.Response.None),
  32. new MyTerminalControlListBoxItem(MyStringId.GetOrCompute("Flee"), MyStringId.GetOrCompute("Flee from enemy, requires working thrusters."), EnemyFinder.Response.Flee),
  33. new MyTerminalControlListBoxItem(MyStringId.GetOrCompute("Land"), MyStringId.GetOrCompute("Land on enemy, requires working thrusters and landing gear."), EnemyFinder.Response.Land),
  34. new MyTerminalControlListBoxItem(MyStringId.GetOrCompute("Fight"), MyStringId.GetOrCompute("Attack enemy with weapons, requires working weapons."), EnemyFinder.Response.Fight),
  35. new MyTerminalControlListBoxItem(MyStringId.GetOrCompute("Ram"), MyStringId.GetOrCompute("Ram enemy, requires working thrusters."), EnemyFinder.Response.Ram),
  36. new MyTerminalControlListBoxItem(MyStringId.GetOrCompute("Self-Destruct"), MyStringId.GetOrCompute("Starts the countdown on every warhead on the ship, then go to next response."), EnemyFinder.Response.Self_Destruct)};
  37. }
  38. return value_allResponses;
  39. }
  40. }
  41. public override ACommand Clone()
  42. {
  43. return new Enemy() { m_activeResponses = new List<EnemyFinder.Response>(m_activeResponses), m_enemyId = m_enemyId, m_range = m_range };
  44. }
  45. public override string Identifier
  46. {
  47. get { return "e"; }
  48. }
  49. public override string AddName
  50. {
  51. get { return "Enemy"; }
  52. }
  53. public override string AddDescription
  54. {
  55. get { return "React when an enemy is detected"; }
  56. }
  57. public override string Description
  58. {
  59. get
  60. {
  61. if (m_activeResponses.Contains(EnemyFinder.Response.None))
  62. return "Stop searching for enemies";
  63. string descr;
  64. if (m_range == 0)
  65. descr = "When any enemy is detected ";
  66. else
  67. descr = "When an enemy is within " + PrettySI.makePretty(m_range) + "m of this ship ";
  68. if (m_enemyId != 0)
  69. descr += " with the ID " + m_enemyId;
  70. descr += " : ";
  71. descr += string.Join(",", m_activeResponses);
  72. return descr;
  73. }
  74. }
  75. public override void AppendCustomInfo(StringBuilder sb)
  76. {
  77. sb.AppendLine("Autopilot starts searching for enemies when it encounters the Enemy command and keeps searching until the end of commands is reached or E Off is encountered.");
  78. sb.AppendLine("If a response's requirement is not met, autopilot moves to the next response.");
  79. }
  80. public override void AddControls(List<Sandbox.ModAPI.Interfaces.Terminal.IMyTerminalControl> controls)
  81. {
  82. if (!m_addingResponse)
  83. {
  84. MyTerminalControlSlider<MyShipController> range = new MyTerminalControlSlider<MyShipController>("RangeSlider", MyStringId.GetOrCompute("Range"),
  85. MyStringId.GetOrCompute("How close enemy needs to be for autopilot to respond to it. Zero indicates infinite range."));
  86. range.Normalizer = Normalizer;
  87. range.Denormalizer = Denormalizer;
  88. range.Writer = (block, sb) => {
  89. sb.Append(PrettySI.makePretty(m_range));
  90. sb.Append('m');
  91. };
  92. IMyTerminalValueControl<float> valueControler = range;
  93. valueControler.Getter = block => m_range;
  94. valueControler.Setter = (block, value) => m_range = value;
  95. controls.Add(range);
  96. MyTerminalControlTextbox<MyShipController> enemyId = new MyTerminalControlTextbox<MyShipController>("EnemyId", MyStringId.GetOrCompute("Enemy Entity ID"), MyStringId.GetOrCompute("If set, only target an enemy with this entity ID"));
  97. enemyId.Getter = block => new StringBuilder(m_enemyId.ToString());
  98. enemyId.Setter = (block, value) => {
  99. if (!long.TryParse(value.ToString(), out m_enemyId))
  100. m_enemyId = -1L;
  101. };
  102. controls.Add(enemyId);
  103. }
  104. if (m_responseListbox == null)
  105. {
  106. m_responseListbox = new MyTerminalControlListbox<MyShipController>("Responses", MyStringId.GetOrCompute("Responses"), MyStringId.NullOrEmpty);
  107. m_responseListbox.ListContent = ListContent;
  108. m_responseListbox.ItemSelected = ItemSelected;
  109. }
  110. controls.Add(m_responseListbox);
  111. if (!m_addingResponse)
  112. {
  113. controls.Add(new MyTerminalControlButton<MyShipController>("AddResponse", MyStringId.GetOrCompute("Add Response"), MyStringId.NullOrEmpty, AddResponse));
  114. controls.Add(new MyTerminalControlButton<MyShipController>("RemoveResponse", MyStringId.GetOrCompute("Remove Response"), MyStringId.NullOrEmpty, RemoveResponse));
  115. controls.Add(new MyTerminalControlButton<MyShipController>("MoveResponseUp", MyStringId.GetOrCompute("Move Response Up"), MyStringId.NullOrEmpty, MoveResponseUp));
  116. controls.Add(new MyTerminalControlButton<MyShipController>("MoveResponseDown", MyStringId.GetOrCompute("Move Response Down"), MyStringId.NullOrEmpty, MoveResponseDown));
  117. }
  118. }
  119. protected override Action<Movement.Mover> Parse(VRage.Game.ModAPI.IMyCubeBlock autopilot, string command, out string message)
  120. {
  121. if (!ServerSettings.GetSetting<bool>(ServerSettings.SettingName.bAllowWeaponControl))
  122. {
  123. message = "Weapon control is disabled in settings";
  124. return null;
  125. }
  126. string[] split = command.RemoveWhitespace().Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
  127. m_range = 0f;
  128. long entityId = 0L;
  129. m_activeResponses.Clear();
  130. foreach (string s in split)
  131. {
  132. if (s.Equals("off", StringComparison.InvariantCultureIgnoreCase))
  133. {
  134. m_activeResponses.Add(EnemyFinder.Response.None);
  135. message = null;
  136. return mover => mover.NavSet.Settings_Commands.EnemyFinder = null;
  137. }
  138. float range;
  139. if (PrettySI.TryParse(s, out range))
  140. {
  141. m_range = range;
  142. continue;
  143. }
  144. if (s.StartsWith("id", StringComparison.InvariantCultureIgnoreCase))
  145. {
  146. if (s.Length < 3)
  147. {
  148. message = "Could not get id from " + s;
  149. return null;
  150. }
  151. string idStr = s.Substring(2, s.Length - 2);
  152. IMyEntity entity;
  153. if (!long.TryParse(idStr, out entityId) || !MyAPIGateway.Entities.TryGetEntityById(entityId, out entity))
  154. {
  155. message = "Not an id: " + idStr;
  156. return null;
  157. }
  158. else
  159. m_enemyId = entityId;
  160. continue;
  161. }
  162. string resStr = s.Replace('-', '_');
  163. EnemyFinder.Response r;
  164. if (!Enum.TryParse<EnemyFinder.Response>(resStr, true, out r))
  165. {
  166. message = "Not a response: " + resStr;
  167. return null;
  168. }
  169. else
  170. m_activeResponses.Add(r);
  171. }
  172. if (m_activeResponses.Count == 0)
  173. {
  174. message = "No responses";
  175. return null;
  176. }
  177. message = null;
  178. return mover => {
  179. if (mover.NavSet.Settings_Commands.EnemyFinder == null)
  180. mover.NavSet.Settings_Commands.EnemyFinder = new EnemyFinder(mover, mover.NavSet, entityId);
  181. mover.NavSet.Settings_Commands.EnemyFinder.AddResponses(m_range, m_activeResponses);
  182. };
  183. }
  184. protected override string TermToString()
  185. {
  186. if (m_activeResponses.Contains(EnemyFinder.Response.None))
  187. return Identifier + " Off";
  188. string result = Identifier + ' ';
  189. if (m_range > 0f)
  190. result += PrettySI.makePretty(m_range) + ',';
  191. result += string.Join(",", m_activeResponses);
  192. if (m_enemyId != 0L)
  193. result += ",ID" + m_enemyId;
  194. return result;
  195. }
  196. private void ListContent(IMyTerminalBlock autopilot, List<MyTerminalControlListBoxItem> items, List<MyTerminalControlListBoxItem> selected)
  197. {
  198. if (m_addingResponse)
  199. {
  200. foreach (EnemyFinder.Response response in m_allResponses.Select(item => (EnemyFinder.Response)item.UserData).Except(m_activeResponses))
  201. items.Add(GetItem(response));
  202. return;
  203. }
  204. foreach (EnemyFinder.Response response in m_activeResponses)
  205. {
  206. MyTerminalControlListBoxItem item = GetItem(response);
  207. items.Add(item);
  208. if (m_selected == response)
  209. selected.Add(item);
  210. }
  211. }
  212. private void ItemSelected(IMyTerminalBlock autopilot, List<MyTerminalControlListBoxItem> selected)
  213. {
  214. if (m_addingResponse)
  215. {
  216. if (selected.Count == 0)
  217. return;
  218. m_activeResponses.Add((EnemyFinder.Response)selected[0].UserData);
  219. m_addingResponse = false;
  220. autopilot.SwitchTerminalTo();
  221. return;
  222. }
  223. if (selected.Count == 0)
  224. m_selected = EnemyFinder.Response.None;
  225. else
  226. m_selected = (EnemyFinder.Response)selected[0].UserData;
  227. }
  228. #region Button Actions
  229. private void AddResponse(IMyTerminalBlock block)
  230. {
  231. m_addingResponse = true;
  232. block.SwitchTerminalTo();
  233. }
  234. private void RemoveResponse(IMyTerminalBlock block)
  235. {
  236. m_activeResponses.Remove(m_selected);
  237. m_responseListbox.UpdateVisual();
  238. }
  239. private void MoveResponseUp(IMyTerminalBlock block)
  240. {
  241. int index = GetSelectedIndex();
  242. if (index == -1)
  243. {
  244. Logger.DebugLog("nothing selected");
  245. return;
  246. }
  247. if (index == 0)
  248. {
  249. Logger.DebugLog("already first element: " + m_selected);
  250. return;
  251. }
  252. Logger.DebugLog("move up: " + m_selected + ", index: " + index + ", count: " + m_activeResponses.Count);
  253. m_activeResponses.Swap(index - 1, index);
  254. m_responseListbox.UpdateVisual();
  255. }
  256. private void MoveResponseDown(IMyTerminalBlock block)
  257. {
  258. int index = GetSelectedIndex();
  259. if (index == -1)
  260. {
  261. Logger.DebugLog("nothing selected");
  262. return;
  263. }
  264. if (index == m_activeResponses.Count - 1)
  265. {
  266. Logger.DebugLog("already last element: " + m_selected);
  267. return;
  268. }
  269. Logger.DebugLog("move down: " + m_selected + ", index: " + index + ", count: " + m_activeResponses.Count);
  270. m_activeResponses.Swap(index, index + 1);
  271. m_responseListbox.UpdateVisual();
  272. }
  273. #endregion Button Actions
  274. private int GetSelectedIndex()
  275. {
  276. for (int i = 0; i < m_activeResponses.Count; i++)
  277. if (m_activeResponses[i] == m_selected)
  278. return i;
  279. return -1;
  280. }
  281. private MyTerminalControlListBoxItem GetItem(EnemyFinder.Response response)
  282. {
  283. foreach (MyTerminalControlListBoxItem item in m_allResponses)
  284. if ((EnemyFinder.Response)item.UserData == response)
  285. return item;
  286. return null;
  287. }
  288. private float Normalizer(IMyTerminalBlock block, float value)
  289. {
  290. return value / 100000f;
  291. }
  292. private float Denormalizer(IMyTerminalBlock block, float norm)
  293. {
  294. return norm * 100000f;
  295. }
  296. }
  297. }