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

/src/NaniteConstructionSystem/Entities/Targets/NaniteMiningTargets.cs

https://gitlab.com/N3X15/SECE-NaniteControlFactories
C# | 426 lines | 341 code | 70 blank | 15 comment | 61 complexity | 767b9e6b2f8e43f3534cb54c5e3d8727 MD5 | raw file
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Threading.Tasks;
  6. using Sandbox.ModAPI;
  7. using VRage;
  8. using VRage.Game.ModAPI;
  9. using VRageMath;
  10. using VRage.ModAPI;
  11. using VRage.Game.Entity;
  12. using Sandbox.Common.ObjectBuilders;
  13. using Sandbox.Game.Entities;
  14. using Sandbox.Game;
  15. using Sandbox.Definitions;
  16. //using Ingame = Sandbox.ModAPI.Ingame;
  17. using VRage.Game;
  18. using VRage.Voxels;
  19. using VRage.ObjectBuilders;
  20. using NaniteConstructionSystem.Particles;
  21. using NaniteConstructionSystem.Extensions;
  22. using NaniteConstructionSystem.Entities.Beacons;
  23. namespace NaniteConstructionSystem.Entities.Targets
  24. {
  25. public class NaniteMiningTarget
  26. {
  27. public int ParticleCount { get; set; }
  28. public double StartTime { get; set; }
  29. public double CarryTime { get; set; }
  30. public double LastUpdate { get; set; }
  31. }
  32. public class NaniteMiningTargets : NaniteTargetBlocksBase
  33. {
  34. public override string TargetName
  35. {
  36. get
  37. {
  38. return "Mining";
  39. }
  40. }
  41. private float m_maxDistance = 500f;
  42. private Dictionary<NaniteMiningItem, NaniteMiningTarget> m_targetTracker;
  43. private static HashSet<Vector3D> m_globalPositionList;
  44. private Random rnd;
  45. public NaniteMiningTargets(NaniteConstructionBlock constructionBlock) : base(constructionBlock)
  46. {
  47. m_maxDistance = NaniteConstructionManager.Settings.MiningMaxDistance;
  48. m_targetTracker = new Dictionary<NaniteMiningItem, NaniteMiningTarget>();
  49. m_globalPositionList = new HashSet<Vector3D>();
  50. rnd = new Random();
  51. }
  52. public override int GetMaximumTargets()
  53. {
  54. MyCubeBlock block = (MyCubeBlock)m_constructionBlock.ConstructionBlock;
  55. return (int)Math.Min(NaniteConstructionManager.Settings.MiningNanitesNoUpgrade + (block.UpgradeValues["MiningNanites"] * NaniteConstructionManager.Settings.MiningNanitesPerUpgrade), NaniteConstructionManager.Settings.MiningMaxStreams);
  56. }
  57. public override float GetPowerUsage()
  58. {
  59. MyCubeBlock block = (MyCubeBlock)m_constructionBlock.ConstructionBlock;
  60. return Math.Max(1, NaniteConstructionManager.Settings.MiningPowerPerStream - (int)(block.UpgradeValues["PowerNanites"] * NaniteConstructionManager.Settings.PowerDecreasePerUpgrade));
  61. }
  62. public override float GetMinTravelTime()
  63. {
  64. MyCubeBlock block = (MyCubeBlock)m_constructionBlock.ConstructionBlock;
  65. return Math.Max(1f, NaniteConstructionManager.Settings.MiningMinTravelTime - (block.UpgradeValues["SpeedNanites"] * NaniteConstructionManager.Settings.MinTravelTimeReductionPerUpgrade));
  66. }
  67. public override float GetSpeed()
  68. {
  69. MyCubeBlock block = (MyCubeBlock)m_constructionBlock.ConstructionBlock;
  70. return NaniteConstructionManager.Settings.MiningDistanceDivisor + (block.UpgradeValues["SpeedNanites"] * (float)NaniteConstructionManager.Settings.SpeedIncreasePerUpgrade);
  71. }
  72. public override bool IsEnabled()
  73. {
  74. bool result = true;
  75. if (!((IMyFunctionalBlock)m_constructionBlock.ConstructionBlock).Enabled ||
  76. !((IMyFunctionalBlock)m_constructionBlock.ConstructionBlock).IsFunctional ||
  77. m_constructionBlock.ConstructionBlock.CustomName.ToLower().Contains("NoMining".ToLower()))
  78. result = false;
  79. if (NaniteConstructionManager.TerminalSettings.ContainsKey(m_constructionBlock.ConstructionBlock.EntityId))
  80. {
  81. if (!NaniteConstructionManager.TerminalSettings[m_constructionBlock.ConstructionBlock.EntityId].AllowMining)
  82. return false;
  83. }
  84. return result;
  85. }
  86. public override void ParallelUpdate(List<IMyCubeGrid> gridList, List<IMySlimBlock> gridBlocks)
  87. {
  88. using (Lock.AcquireExclusiveUsing())
  89. PotentialTargetList.Clear();
  90. DateTime start = DateTime.Now;
  91. List<object> finalAddList = new List<object>();
  92. int listCount = 0;
  93. foreach (var miningBlock in NaniteConstructionManager.MiningList.Where(x => x.IsWorking && Vector3D.DistanceSquared(m_constructionBlock.ConstructionBlock.GetPosition(), x.MiningBlock.GetPosition()) < m_maxDistance * m_maxDistance).OrderBy(x => rnd.Next(100)))
  94. {
  95. IMyCubeBlock item = (IMyCubeBlock)miningBlock.MiningBlock;
  96. MyRelationsBetweenPlayerAndBlock relation = item.GetUserRelationToOwner(m_constructionBlock.ConstructionBlock.OwnerId);
  97. if (!(relation == MyRelationsBetweenPlayerAndBlock.Owner || relation == MyRelationsBetweenPlayerAndBlock.FactionShare || (MyAPIGateway.Session.CreativeMode && relation == MyRelationsBetweenPlayerAndBlock.NoOwnership)))
  98. continue;
  99. if (!((IMyFunctionalBlock)item).Enabled)
  100. continue;
  101. if (miningBlock.OreList == null || miningBlock.OreList.Count < 1)
  102. continue;
  103. int sum = miningBlock.OreList.Sum(x => x.Value.Count);
  104. Dictionary<MyVoxelMaterialDefinition, List<NaniteMiningItem>> lookup = null;
  105. using (miningBlock.Lock.AcquireExclusiveUsing())
  106. {
  107. lookup = miningBlock.OreList.ToDictionary(x => x.Key, x => x.Value);
  108. }
  109. List<object> addList = new List<object>();
  110. int count = 0;
  111. int pos = 0;
  112. while (true)
  113. {
  114. var group = lookup.ElementAt(count % miningBlock.OreList.Count);
  115. if (pos < group.Value.Count)
  116. {
  117. addList.Insert(0, group.Value[pos]);
  118. }
  119. count++;
  120. if (count % miningBlock.OreList.Count == 0)
  121. pos++;
  122. if (count >= 1000)
  123. break;
  124. if (count >= sum)
  125. break;
  126. }
  127. DistributeList(addList, finalAddList, listCount);
  128. listCount++;
  129. if (listCount > 5)
  130. break;
  131. }
  132. var listToAdd = finalAddList.Take(1000).ToList();
  133. listToAdd.Reverse();
  134. using (Lock.AcquireExclusiveUsing())
  135. {
  136. PotentialTargetList.AddRange(listToAdd);
  137. }
  138. //Logging.Instance.WriteLine(string.Format("ParallelUpdate() took {0} ms", (DateTime.Now - start).TotalMilliseconds));
  139. }
  140. private void DistributeList(List<object> listToAdd, List<object> finalList, int count)
  141. {
  142. if(count < 1)
  143. {
  144. finalList.AddRange(listToAdd);
  145. return;
  146. }
  147. for(int r = listToAdd.Count - 1; r >= 0; r--)
  148. {
  149. var item = listToAdd[r];
  150. var realPos = r * (count + 1);
  151. if (realPos >= finalList.Count)
  152. realPos = finalList.Count - 1;
  153. finalList.Insert(realPos, item);
  154. }
  155. }
  156. public override void FindTargets(ref Dictionary<string, int> available)
  157. {
  158. if (!IsEnabled())
  159. return;
  160. if (TargetList.Count >= GetMaximumTargets())
  161. {
  162. if (PotentialTargetList.Count > 0)
  163. m_lastInvalidTargetReason = "Maximum targets reached. Add more upgrades!";
  164. return;
  165. }
  166. DateTime start = DateTime.Now;
  167. using (Lock.AcquireExclusiveUsing())
  168. {
  169. if (m_constructionBlock.IsUserDefinedLimitReached())
  170. {
  171. m_lastInvalidTargetReason = "User defined maximum nanite limit reached";
  172. return;
  173. }
  174. //foreach (NaniteMiningItem item in PotentialTargetList)
  175. for (int r = PotentialTargetList.Count - 1; r >= 0; r--)
  176. {
  177. var item = (NaniteMiningItem)PotentialTargetList[r];
  178. if (TargetList.Contains(item))
  179. continue;
  180. if (m_globalPositionList.Contains(item.Position))
  181. {
  182. m_lastInvalidTargetReason = "Another factory has this voxel as a target";
  183. continue;
  184. }
  185. var blockList = NaniteConstructionManager.GetConstructionBlocks((IMyCubeGrid)m_constructionBlock.ConstructionBlock.CubeGrid);
  186. bool found = false;
  187. foreach (var block in blockList)
  188. {
  189. // This can be sped up if necessary by indexing items by position
  190. if (block.GetTarget<NaniteMiningTargets>().TargetList.FirstOrDefault(x => ((NaniteMiningItem)x).Position == item.Position) != null)
  191. {
  192. found = true;
  193. break;
  194. }
  195. }
  196. if (found)
  197. {
  198. m_lastInvalidTargetReason = "Another factory has this voxel as a target";
  199. continue;
  200. }
  201. if (!NaniteMining.CheckVoxelContent(item.VoxelId, item.Position))
  202. {
  203. continue;
  204. }
  205. if (Vector3D.DistanceSquared(m_constructionBlock.ConstructionBlock.GetPosition(), item.Position) < m_maxDistance * m_maxDistance &&
  206. NaniteConstructionPower.HasRequiredPowerForNewTarget((IMyFunctionalBlock)m_constructionBlock.ConstructionBlock, this))
  207. {
  208. Logging.Instance.WriteLine(string.Format("ADDING Mining Target: conid={0} pos={1} type={2}", m_constructionBlock.ConstructionBlock.EntityId, item.Position, MyDefinitionManager.Static.GetVoxelMaterialDefinition(item.VoxelMaterial).MinedOre));
  209. //PotentialTargetList.Remove(item);
  210. TargetList.Add(item);
  211. m_globalPositionList.Add(item.Position);
  212. if (TargetList.Count >= GetMaximumTargets())
  213. break;
  214. }
  215. }
  216. }
  217. //Logging.Instance.WriteLine(string.Format("FindTargets took {0}ms", (DateTime.Now - start).TotalMilliseconds));
  218. }
  219. public override void Update()
  220. {
  221. foreach(var item in TargetList.ToList())
  222. {
  223. ProcessItem(item);
  224. }
  225. }
  226. private void ProcessItem(object miningTarget)
  227. {
  228. var target = miningTarget as NaniteMiningItem;
  229. if (target == null)
  230. return;
  231. if (Sync.IsServer)
  232. {
  233. if (!IsEnabled())
  234. {
  235. Logging.Instance.WriteLine("CANCELLING Mining Target due to being disabled");
  236. CancelTarget(target);
  237. return;
  238. }
  239. if (m_constructionBlock.FactoryState != NaniteConstructionBlock.FactoryStates.Active)
  240. return;
  241. if(!target.MiningHammer.IsWorking)
  242. {
  243. Logging.Instance.WriteLine("CANCELLING Mining Target due to hammer functionality change");
  244. CancelTarget(target);
  245. return;
  246. }
  247. if (!NaniteConstructionPower.HasRequiredPowerForCurrentTarget((IMyFunctionalBlock)m_constructionBlock.ConstructionBlock))
  248. {
  249. Logging.Instance.WriteLine("CANCELLING Mining Target due to power shortage");
  250. CancelTarget(target);
  251. return;
  252. }
  253. if (!m_targetTracker.ContainsKey(target))
  254. {
  255. m_constructionBlock.SendAddTarget(target);
  256. }
  257. if (m_targetTracker.ContainsKey(target))
  258. {
  259. var trackedItem = m_targetTracker[target];
  260. if (MyAPIGateway.Session.ElapsedPlayTime.TotalMilliseconds - trackedItem.StartTime >= trackedItem.CarryTime &&
  261. MyAPIGateway.Session.ElapsedPlayTime.TotalMilliseconds - trackedItem.LastUpdate > 2000)
  262. {
  263. trackedItem.LastUpdate = MyAPIGateway.Session.ElapsedPlayTime.TotalMilliseconds;
  264. if (!TransferFromTarget(target))
  265. CancelTarget(target);
  266. else
  267. CompleteTarget(target);
  268. }
  269. }
  270. }
  271. CreateMiningParticles(target);
  272. }
  273. private void CreateMiningParticles(NaniteMiningItem target)
  274. {
  275. if (!m_targetTracker.ContainsKey(target))
  276. CreateTrackerItem(target);
  277. if (NaniteParticleManager.TotalParticleCount > NaniteParticleManager.MaxTotalParticles)
  278. return;
  279. m_targetTracker[target].ParticleCount++;
  280. int size = (int)Math.Max(60f, NaniteParticleManager.TotalParticleCount);
  281. if ((float)m_targetTracker[target].ParticleCount / size < 1f)
  282. return;
  283. m_targetTracker[target].ParticleCount = 0;
  284. // Create Particle
  285. Vector4 startColor = new Vector4(0.7f, 0.2f, 0.0f, 1f);
  286. Vector4 endColor = new Vector4(0.2f, 0.05f, 0.0f, 0.35f);
  287. m_constructionBlock.ParticleManager.AddParticle(startColor, endColor, GetMinTravelTime() * 1000f, GetSpeed(), target, (IMyTerminalBlock)target.MiningHammer.MiningBlock);
  288. }
  289. private void CreateTrackerItem(NaniteMiningItem target)
  290. {
  291. double distance = Vector3D.Distance(m_constructionBlock.ConstructionBlock.GetPosition(), target.Position);
  292. int time = (int)Math.Max(GetMinTravelTime() * 1000f, (distance / GetSpeed()) * 1000f);
  293. NaniteMiningTarget miningTarget = new NaniteMiningTarget();
  294. miningTarget.ParticleCount = 0;
  295. miningTarget.StartTime = MyAPIGateway.Session.ElapsedPlayTime.TotalMilliseconds;
  296. miningTarget.LastUpdate = MyAPIGateway.Session.ElapsedPlayTime.TotalMilliseconds;
  297. miningTarget.CarryTime = time - 1000;
  298. m_targetTracker.Add(target, miningTarget);
  299. }
  300. private bool TransferFromTarget(NaniteMiningItem target)
  301. {
  302. byte material = 0;
  303. float amount = 0;
  304. //NaniteMining.CheckVoxelContent(target.VoxelId, target.Position, target.LocalPos);
  305. NaniteMining.RemoveVoxelContent(target.VoxelId, target.Position, out material, out amount);
  306. /*
  307. if (material == 0)
  308. {
  309. Logging.Instance.WriteLine(string.Format("Material is 0", target.VoxelId));
  310. return false;
  311. }
  312. */
  313. if (amount == 0f)
  314. {
  315. Logging.Instance.WriteLine(string.Format("Amount is 0", target.VoxelId));
  316. return false;
  317. }
  318. var def = MyDefinitionManager.Static.GetVoxelMaterialDefinition(target.VoxelMaterial);
  319. var builder = MyObjectBuilderSerializer.CreateNewObject<MyObjectBuilder_Ore>(def.MinedOre);
  320. if (!GridHelper.FindFreeCargo((MyCubeBlock)m_constructionBlock.ConstructionBlock, builder, (int)amount, false))
  321. {
  322. Logging.Instance.WriteLine(string.Format("Can not find free cargo space!"));
  323. return false;
  324. }
  325. return true;
  326. }
  327. public override void CancelTarget(object obj)
  328. {
  329. var target = obj as NaniteMiningItem;
  330. Logging.Instance.WriteLine(string.Format("CANCELLED Mining Target: {0} - {1} (VoxelID={2},Position={3})", m_constructionBlock.ConstructionBlock.EntityId, obj.GetType().Name, target.VoxelId, target.Position));
  331. if (Sync.IsServer)
  332. {
  333. m_constructionBlock.SendCompleteTarget((NaniteMiningItem)obj);
  334. }
  335. m_constructionBlock.ParticleManager.CancelTarget(target);
  336. if (m_targetTracker.ContainsKey(target))
  337. m_targetTracker.Remove(target);
  338. m_globalPositionList.Remove(target.Position);
  339. Remove(obj);
  340. }
  341. public override void CompleteTarget(object obj)
  342. {
  343. var target = obj as NaniteMiningItem;
  344. Logging.Instance.WriteLine(string.Format("COMPLETED Mining Target: {0} - {1} (VoxelID={2},Position={3})", m_constructionBlock.ConstructionBlock.EntityId, obj.GetType().Name, target.VoxelId, target.Position));
  345. if (Sync.IsServer)
  346. {
  347. m_constructionBlock.SendCompleteTarget((NaniteMiningItem)obj);
  348. }
  349. m_constructionBlock.ParticleManager.CompleteTarget(target);
  350. if (m_targetTracker.ContainsKey(target))
  351. m_targetTracker.Remove(target);
  352. m_globalPositionList.Remove(target.Position);
  353. Remove(obj);
  354. }
  355. }
  356. }