using System.Collections.Generic; using UnityEngine; using UnityEngine.AddressableAssets; using cfg; using asap.core; using UnityEngine.ResourceManagement.AsyncOperations; public class BlockManager : MonoBehaviour { private Dictionary _blockPrefabs = new Dictionary(); //[SerializeField] private BoxCollider2D _deathWall; [SerializeField] List _blocks = new List(); [SerializeField] private Transform _focus; public Block CurrentBlock; private TbConstructionBasicUnits _blockTypesTable; private TbConstructionUnits _blocksTable; private TbConstructionHeightEvent _heightEventTable; private InfiniteBuildingConf _cfg; private InfiniteBuildingData _data; private EventAggregator _eventAggregator; private List _prefabOps; public List Blocks => _blocks; private void Start() { _blockTypesTable = GContext.container.Resolve().TbConstructionBasicUnits; _blocksTable = GContext.container.Resolve().TbConstructionUnits; _prefabOps = new List(); } private void OnDestroy() { foreach (var op in _prefabOps) Addressables.Release(op); } public void Init(InfiniteBuildingConf cfg, InfiniteBuildingData data, EventAggregator ea) { _cfg = cfg; _data = data; _eventAggregator = ea; _heightEventTable = GContext.container.Resolve().TbConstructionHeightEvent; } /// /// Get prefab according to PrefabID, will be used in init and block generation /// /// name of the prefab in addressables /// the prefab GameObject private async System.Threading.Tasks.Task GetBlockPrefab(string id) { if (_blockPrefabs.TryGetValue(id, out var prefab)) return prefab; _prefabOps.Add(Addressables.LoadAssetAsync(id)); var go = (GameObject) await _prefabOps[^1].Task; _blockPrefabs.Add(id, go); return go; } private int GetRandomIDFromWeightTable(Dictionary weightTable) { Dictionary weightSum = new Dictionary(); float sum = 0; foreach (var e in weightTable) { sum += e.Value; weightSum.Add(e.Key, sum); } float randomNum = UnityEngine.Random.Range(0, sum); foreach (var e in weightSum) { if (randomNum <= e.Value) { return e.Key; } } Debug.LogError($"Random ID not available. WeightTable entries count: {weightTable.Count}."); return -1; } public async System.Threading.Tasks.Task GenerateBlock(Vector3 spawnPos, bool isFirst = false) { Block generatedBlock; Dictionary weightTable = new Dictionary(); //_crane.ResetPosition(); //await Awaiters.NextFrame; int type, block; foreach (var bt in _blockTypesTable.DataList) { weightTable.Add(bt.ID, bt.Weight); } type = GetRandomIDFromWeightTable(weightTable); weightTable.Clear(); foreach (int blockID in _blockTypesTable[type].UnitList) { if (_data.Lvl < _blocksTable[blockID].UnlockedHeight) continue; weightTable.Add(blockID, _blocksTable[blockID].Weight); } block = GetRandomIDFromWeightTable(weightTable); if (isFirst && _data.LastHangingBlock != -1) { block = _data.LastHangingBlock; _data.LastHangingBlock = -1; } TbConstructionInfiniteInit initTable = GContext.container.Resolve().TbConstructionInfiniteInit; if (_data.Lvl < initTable.DataList.Count) block = GContext.container.Resolve().TbConstructionInfiniteInit.DataList[_data.Lvl].UnitID; #if UNITY_EDITOR if (_cfg.NextBlockToSpawn != -1 && _blocksTable.DataMap.ContainsKey(_cfg.NextBlockToSpawn)) block = _cfg.NextBlockToSpawn; #endif string id = _blocksTable[block].Prefab; GameObject prefab = await GetBlockPrefab(id); //GameObject prefab = await GetBlockPrefab("block_000");//Get test prefab //Debug.Log($"{_crane.transform.position}"); //Debug.Log($"{_cfg.blockSpawnOffset}"); generatedBlock = Instantiate(prefab, spawnPos + _blocksTable[block].Height * Vector3.down, prefab.transform.rotation, transform).GetComponent(); generatedBlock.InitHangingBlock(BlockState.Hanging, block, _eventAggregator, _cfg); generatedBlock.Rb.angularDrag = GetDataByIBLvl(_data.Lvl, _heightEventTable.DataList).AngularDrag; generatedBlock.Rb.drag = GetDataByIBLvl(_data.Lvl, _heightEventTable.DataList).LinearDrag; //_currentBlock.SetParams(_cfg, _deathWall.name, block, _focus); //generatedBlock.OnRest += async d => await OnBlockRest(d); //generatedBlock.OnDestroy += async () => await OnBlockDestroy(); //_currentBlock.AttachJoints(_crane); //_crane.AttachJoints(generatedBlock); //await _crane.Descend(); //if (doSwitchState) // _stateMachine.SetState(EInfiniteBuildingState.PlayerCtrl); return generatedBlock; } private async System.Threading.Tasks.Task GenerateBlock(InfiniteBuildingData.BlockRecord record, IEventAggregator ea) { GameObject b = Instantiate(await GetBlockPrefab(record.PrefabID), new Vector3(record.Pos.x, record.Pos.y, _cfg.blockDepth), Quaternion.identity, transform); b.GetComponent().isKinematic = true; b.GetComponent().velocity = Vector2.zero; b.GetComponent().Idx = record.ID; b.GetComponent().EventAggregator = ea; //_blocks[^1].SetParams(_cfg, _deathWall.name, record.id, _focus); return b; } public int GetExcellentScore() { int fallingBlockScore = _blocksTable[CurrentBlock.Idx].FallPoint; int stackedBlockScore = _blocks is { Count: > 0 } ? _blocksTable[_blocks[^1].Idx].StackPoint : 0; // Debug.Log($"Incoming: {fallingBlockScore}, stacked: {stackedBlockScore}"); return fallingBlockScore + stackedBlockScore; } public bool TrySnap() { if (_blocks is not { Count: > 0 }) { if (Mathf.Abs(CurrentBlock.Rb.position.x - _focus.position.x) > _cfg.snapThresh) return false; CurrentBlock.Rb.MovePosition(new Vector2(_focus.position.x, CurrentBlock.Rb.position.y)); return true; } if (Mathf.Abs(CurrentBlock.Rb.position.x - _blocks[^1].Rb.position.x) > _cfg.snapThresh) return false; CurrentBlock.Rb.MovePosition(new Vector2(_blocks[^1].Rb.position.x, CurrentBlock.Rb.position.y)); return true; } public async void AlignBlocks(IEventAggregator ea) { Block b; for (int j = _blocks.Count - 1; j >= 0; j--) { b = _blocks[j]; //_blocks.RemoveAt(j); if (b != null) Destroy(b.gameObject); } Vector3 pivot = _focus.position; for (int i = _data.Record.Count - 1; i >= 0; i--) { InfiniteBuildingData.BlockRecord r = _data.Record[i]; b = Instantiate(await GetBlockPrefab(r.PrefabID), pivot + r.Height * Vector3.down, Quaternion.identity, transform).GetComponent(); pivot += r.Height * Vector3.down; //_blocks[_data.Record.Count - 1 - i] = b; _blocks[i] = b; b.Rb.isKinematic = true; b.Rb.velocity = Vector2.zero; b.Idx = r.ID; b.EventAggregator = ea; } for (int i = _blocks.Count - 1; i >= 0; i--) { b = _blocks[i]; if (b.WorldHeight < _focus.position.y + _cfg.blockDestroyOffset) { _blocks.RemoveAt(i); Destroy(b.gameObject); } else if (b.WorldHeight < _focus.position.y + _cfg.blockRestingOffset) { b.Rb.isKinematic = true; b.State = BlockState.Locked; } else { b.Rb.isKinematic = false; b.State = BlockState.Resting; } } //for (int i = 0; i < _blocks.Count; i++) //{ // if (_blocks[i] == null) // continue; // _blocks[i].Rb.MovePosition(new Vector2(0, _data.Record[i].pos.y)); //} } private float _blockDestructionTimer = 1000f; public void BlockFallFixedUpdate() { float rot = CurrentBlock.Rb.rotation; _blockDestructionTimer -= Time.fixedDeltaTime; if (_blockDestructionTimer < 0) { _eventAggregator.Publish(new EventInfiniteBuildingFail()); return; } if (!CurrentBlock.Freezable || !CurrentBlock.IsCurrentBlockStill) return; if ((rot <= _cfg.blockDestroyThreshAngle && rot > -_cfg.blockDestroyThreshAngle) || (rot >= 180 - _cfg.blockDestroyThreshAngle && rot <= 180) || (rot >= -180 && rot <= -180 + _cfg.blockDestroyThreshAngle)) _eventAggregator.Publish(new EventInfiniteBuildingSucceed()); else _eventAggregator.Publish(new EventInfiniteBuildingFail()); } public void StartDestroyCountDown() { //Debug.Log("111111111111111"); _blockDestructionTimer = _cfg.blockDestroyDelay; } public void ResetDestroyCountDown() { _blockDestructionTimer = float.PositiveInfinity; } public void AddCurrentBlock() { _blocks.Add(CurrentBlock); } private void CurrentBlockFail() { Debug.Log("Build failure."); } /// /// Lock or clear blocks according to their height, need focus updated first /// public void LockOrClearBlocks(bool isFilterOnly = false) { for (int i = _blocks.Count - 1; i >= 0; i--) { Block b = _blocks[i]; if (b.WorldHeight < _cfg.blockDestroyOffset + _focus.position.y) { _blocks.RemoveAt(i); Destroy(b.gameObject); } else if (b.WorldHeight < _cfg.blockRestingOffset + _focus.position.y ) { b.Rb.velocity = Vector2.zero; b.Rb.rotation = 0; b.Rb.angularVelocity = 0; b.Rb.isKinematic = true; } else if (!isFilterOnly) b.Rb.isKinematic = false; } } /// /// Clear stage, generate blocks according to data, and reset focus position /// /// Task public async System.Threading.Tasks.Task RebuildAllBlocks() { //clear the act if (CurrentBlock != null) Destroy(CurrentBlock.gameObject); await RebuildBlocks(); } /// /// reset stage, bug leave the current block alone /// /// public async System.Threading.Tasks.Task RebuildBlocks(bool doesLockAll = false) { Block b; for (int j = _blocks.Count - 1; j >= 0; j--) { b = _blocks[j]; _blocks.RemoveAt(j); if (b != null) Destroy(b.gameObject); } //regenerate _focus.position = new Vector3(_focus.position.x, 0, _focus.position.z); foreach (var record in _data.Record) { b = (await GenerateBlock(record, _eventAggregator)).GetComponent(); b.InitRecordBlock(_cfg); b.LitBlock(); _blocks.Add(b); if (_focus.position.y < b.WorldHeight) _focus.position = new Vector3(_focus.position.x, b.WorldHeight, _focus.position.z); } for (int i = _blocks.Count - 1; i >= 0; i--) { b = _blocks[i]; if (b.WorldHeight < _focus.position.y + _cfg.blockDestroyOffset) { _blocks.RemoveAt(i); Destroy(b.gameObject); } else if (b.WorldHeight < _focus.position.y + _cfg.blockRestingOffset || doesLockAll) { b.Rb.isKinematic = true; b.State = BlockState.Locked; } else { b.Rb.isKinematic = false; b.State = BlockState.Resting; } } } private ConstructionHeightEvent GetDataByIBLvl(int lvl, List dataList) { for (int i = 0; i < dataList.Count - 1; i++) { if (lvl < dataList[i + 1].Height) return dataList[i]; } return dataList[^1]; } }