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.

216 lines
7.3KB

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