using System; using System.Collections; using System.Collections.Generic; using Gamecraft.Blocks.BlockGroups; using Unity.Mathematics; using UnityEngine; using GamecraftModdingAPI.Blocks; using GamecraftModdingAPI.Utility; namespace GamecraftModdingAPI { /// /// A group of blocks that can be selected together. The placed version of blueprints. /// public class BlockGroup : ICollection { internal static BlueprintEngine _engine = new BlueprintEngine(); public int Id { get; } private readonly Block sourceBlock; private readonly List blocks; private float3 position, rotation; internal bool PosAndRotCalculated; internal BlockGroup(int id, Block block) { if (id == BlockGroupUtility.GROUP_UNASSIGNED) throw new BlockException("Cannot create a block group for blocks without a group!"); Id = id; sourceBlock = block; blocks = new List(GetBlocks()); } /// /// The position of the block group (center). Recalculated if blocks have been added/removed since the last query. /// public float3 Position { get { if (!PosAndRotCalculated) Refresh(); return position; } set { var diff = value - position; foreach (var block in blocks) block.Position += diff; if (!PosAndRotCalculated) //The condition can only be true if a block has been added/removed manually Refresh(); //So the blocks array is up to date else position += diff; } } /// /// The rotation of the block group. Recalculated if blocks have been added/removed since the last query. /// public float3 Rotation { get { if (!PosAndRotCalculated) Refresh(); return rotation; } set { var diff = value - rotation; var qdiff = Quaternion.Euler(diff); foreach (var block in blocks) { block.Rotation += diff; block.Position = qdiff * block.Position; } if (!PosAndRotCalculated) Refresh(); else rotation += diff; } } /*/// /// Removes all of the blocks in this group from the world. /// public void RemoveBlocks() { _engine.RemoveBlockGroup(Id); - TODO: Causes a hard crash }*/ /// /// Creates a new block group consisting of a single block. /// You can add more blocks using the Add() method or by setting the BlockGroup property of the blocks.
/// Note that only newly placed blocks should be added to groups. ///
/// The block to add /// A new block group containing the given block public static BlockGroup Create(Block block) { return new BlockGroup(_engine.CreateBlockGroup(default, default), block); } /// /// Collects each block that is a part of this group. Also sets the position and rotation. /// /// An array of blocks private Block[] GetBlocks() { if (!sourceBlock.Exists) return new[] {sourceBlock}; //The block must exist to get the others var ret = _engine.GetBlocksFromGroup(sourceBlock.Id, out var pos, out var rot); position = pos; rotation = ((Quaternion) rot).eulerAngles; PosAndRotCalculated = true; return ret; } private void Refresh() { blocks.Clear(); blocks.AddRange(GetBlocks()); } internal static void Init() { GameEngineManager.AddGameEngine(_engine); } public IEnumerator GetEnumerator() => blocks.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => blocks.GetEnumerator(); /// /// Adds a block to the group. You should only add newly placed blocks /// so that the game initializes the group membership properly. /// /// /// public void Add(Block item) { if (item == null) throw new NullReferenceException("Cannot add null to a block group"); item.BlockGroup = this; //Calls AddInternal } internal void AddInternal(Block item) => blocks.Add(item); /// /// Removes all blocks from this group. /// You should not remove blocks that have been initialized, only those that you placed recently. /// public void Clear() { while (blocks.Count > 0) Remove(blocks[blocks.Count - 1]); } public bool Contains(Block item) => blocks.Contains(item); public void CopyTo(Block[] array, int arrayIndex) => blocks.CopyTo(array, arrayIndex); /// /// Removes a block from this group. /// You should not remove blocks that have been initialized, only those that you placed recently. /// /// /// /// public bool Remove(Block item) { if (item == null) throw new NullReferenceException("Cannot remove null from a block group"); bool ret = item.BlockGroup == this; if (ret) item.BlockGroup = null; //Calls RemoveInternal return ret; } internal void RemoveInternal(Block item) => blocks.Remove(item); public int Count => blocks.Count; public bool IsReadOnly { get; } = false; public Block this[int index] => blocks[index]; //Setting is not supported, since the order doesn't matter } }