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.

218 lines
7.5KB

  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using Gamecraft.Blocks.BlockGroups;
  5. using Svelto.ECS;
  6. using Unity.Mathematics;
  7. using UnityEngine;
  8. using TechbloxModdingAPI.Blocks;
  9. using TechbloxModdingAPI.Blocks.Engines;
  10. using TechbloxModdingAPI.Utility;
  11. namespace TechbloxModdingAPI
  12. {
  13. /// <summary>
  14. /// A group of blocks that can be selected together. The placed version of blueprints. Dispose after usage.
  15. /// </summary>
  16. public class BlockGroup : EcsObjectBase, ICollection<Block>, IDisposable
  17. {
  18. internal static BlueprintEngine _engine = new BlueprintEngine();
  19. public override EGID Id { get; }
  20. private readonly Block sourceBlock;
  21. private readonly List<Block> blocks;
  22. private float3 position, rotation;
  23. internal bool PosAndRotCalculated;
  24. internal BlockGroup(int id, Block block)
  25. {
  26. if (id == BlockGroupUtility.GROUP_UNASSIGNED)
  27. throw new BlockException("Cannot create a block group for blocks without a group!");
  28. Id = new EGID((uint) id, BlockGroupExclusiveGroups.BlockGroupEntityGroup);
  29. sourceBlock = block;
  30. blocks = new List<Block>(GetBlocks());
  31. Block.Removed += OnBlockRemoved;
  32. }
  33. private void OnBlockRemoved(object sender, BlockPlacedRemovedEventArgs e)
  34. {
  35. //blocks.RemoveAll(block => block.Id == e.ID); - Allocation heavy
  36. int index = -1;
  37. for (int i = 0; i < blocks.Count; i++)
  38. {
  39. if (blocks[i].Id == e.ID)
  40. {
  41. index = i;
  42. break;
  43. }
  44. }
  45. if (index != -1) blocks.RemoveAt(index);
  46. }
  47. public void Dispose()
  48. {
  49. Block.Removed -= OnBlockRemoved;
  50. }
  51. /// <summary>
  52. /// The position of the block group (center). Can only be used after initialization is complete.
  53. /// </summary>
  54. public float3 Position
  55. {
  56. get
  57. {
  58. if (!PosAndRotCalculated)
  59. Refresh();
  60. return position;
  61. }
  62. set
  63. {
  64. var diff = value - position;
  65. foreach (var block in blocks)
  66. block.Position += diff;
  67. if (!PosAndRotCalculated) //The condition can only be true if a block has been added/removed manually
  68. Refresh(); //So the blocks array is up to date
  69. else
  70. position += diff;
  71. }
  72. }
  73. /// <summary>
  74. /// The rotation of the block group. Can only be used after initialization is complete.
  75. /// </summary>
  76. public float3 Rotation
  77. {
  78. get
  79. {
  80. if (!PosAndRotCalculated)
  81. Refresh();
  82. return rotation;
  83. }
  84. set
  85. {
  86. var diff = value - rotation;
  87. var qdiff = Quaternion.Euler(diff);
  88. foreach (var block in blocks)
  89. {
  90. block.Rotation += diff;
  91. block.Position = qdiff * block.Position;
  92. }
  93. if (!PosAndRotCalculated)
  94. Refresh();
  95. else
  96. rotation += diff;
  97. }
  98. }
  99. /*/// <summary>
  100. /// Removes all of the blocks in this group from the world.
  101. /// </summary>
  102. public void RemoveBlocks()
  103. {
  104. _engine.RemoveBlockGroup(Id); - TODO: Causes a hard crash
  105. }*/
  106. /// <summary>
  107. /// Creates a new block group consisting of a single block.
  108. /// You can add more blocks using the Add() method or by setting the BlockGroup property of the blocks.<br />
  109. /// Note that only newly placed blocks can be added to groups.
  110. /// </summary>
  111. /// <param name="block">The block to add</param>
  112. /// <returns>A new block group containing the given block</returns>
  113. public static BlockGroup Create(Block block)
  114. {
  115. var bg = new BlockGroup(_engine.CreateBlockGroup(block.Position, Quaternion.Euler(block.Rotation)), block);
  116. block.BlockGroup = bg;
  117. return bg;
  118. }
  119. /// <summary>
  120. /// Collects each block that is a part of this group. Also sets the position and rotation.
  121. /// </summary>
  122. /// <returns>An array of blocks</returns>
  123. private Block[] GetBlocks()
  124. {
  125. if (!sourceBlock.Exists) return new[] {sourceBlock}; //The block must exist to get the others
  126. var ret = _engine.GetBlocksFromGroup(sourceBlock.Id, out var pos, out var rot);
  127. position = pos;
  128. rotation = ((Quaternion) rot).eulerAngles;
  129. PosAndRotCalculated = true;
  130. return ret;
  131. }
  132. private void Refresh()
  133. {
  134. blocks.Clear();
  135. blocks.AddRange(GetBlocks());
  136. }
  137. internal static void Init()
  138. {
  139. GameEngineManager.AddGameEngine(_engine);
  140. }
  141. public IEnumerator<Block> GetEnumerator() => blocks.GetEnumerator();
  142. IEnumerator IEnumerable.GetEnumerator() => blocks.GetEnumerator();
  143. /// <summary>
  144. /// Adds a block to the group. You can only add newly placed blocks
  145. /// so that the game initializes the group membership properly.
  146. /// </summary>
  147. /// <param name="item"></param>
  148. /// <exception cref="NullReferenceException"></exception>
  149. public void Add(Block item)
  150. {
  151. if (item == null) throw new NullReferenceException("Cannot add null to a block group");
  152. item.BlockGroup = this; //Calls AddInternal
  153. }
  154. internal void AddInternal(Block item)
  155. {
  156. blocks.Add(item);
  157. _engine.AddBlockToGroup(item.Id, (int) Id.entityID);
  158. }
  159. /// <summary>
  160. /// Removes all blocks from this group.
  161. /// You cannot remove blocks that have been initialized, only those that you placed recently.
  162. /// </summary>
  163. public void Clear()
  164. {
  165. while (blocks.Count > 0)
  166. Remove(blocks[blocks.Count - 1]);
  167. }
  168. public bool Contains(Block item) => blocks.Contains(item);
  169. public void CopyTo(Block[] array, int arrayIndex) => blocks.CopyTo(array, arrayIndex);
  170. /// <summary>
  171. /// Removes a block from this group.
  172. /// You cannot remove blocks that have been initialized, only those that you placed recently.
  173. /// </summary>
  174. /// <param name="item"></param>
  175. /// <returns></returns>
  176. /// <exception cref="NullReferenceException"></exception>
  177. public bool Remove(Block item)
  178. {
  179. if (item == null) throw new NullReferenceException("Cannot remove null from a block group");
  180. bool ret = item.BlockGroup == this;
  181. if (ret)
  182. item.BlockGroup = null; //Calls RemoveInternal
  183. return ret;
  184. }
  185. internal void RemoveInternal(Block item) => blocks.Remove(item);
  186. public int Count => blocks.Count;
  187. public bool IsReadOnly { get; } = false;
  188. public Block this[int index] => blocks[index]; //Setting is not supported, since the order doesn't matter
  189. public override string ToString()
  190. {
  191. return $"{nameof(Id)}: {Id}, {nameof(Position)}: {Position}, {nameof(Rotation)}: {Rotation}, {nameof(Count)}: {Count}";
  192. }
  193. }
  194. }