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.

217 lines
7.4KB

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