//#define LOG using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Text; using AptGames; using TerrorSquid.BulletSystem; using UnityEngine; using TerrorSquid.Entities; using UnityEngine.AddressableAssets; using UnityEngine.ResourceManagement.AsyncOperations; using UnityEngine.SceneManagement; using Debug = UnityEngine.Debug; namespace TerrorSquid.ExplosionSystem { public static class Explosions { private static readonly Dictionary current = new Dictionary(); private static readonly Dictionary lengths = new Dictionary(); private static readonly Dictionary offsetMap = new Dictionary(); private static float[] radius; private static Explosion[] instances; private static bool[] active; private static Transform[] transforms; private static int[] explosionHashes; private static int[] offsets; private static int[] counts; private static int totalBullets; public static void Init(BulletPatternSequence sequence) { totalBullets = sequence.GetTotalBullets(); instances = new Explosion[totalBullets]; transforms = new Transform[totalBullets]; radius = new float[totalBullets]; active = new bool[totalBullets]; } public static int GetLength(int hash) { return lengths[hash]; } #if DEV public static string GetStatus() { StringBuilder sb = new StringBuilder(); for (int i = 0; i < explosionHashes.Length; ++i) { int explHash = explosionHashes[i]; int cur = current[explHash]; sb.AppendLine($"ID {explHash} cur: {cur}"); } return sb.ToString(); } #endif public static void Spawn(int explosionHash, Vector3 pos, Quaternion rotLocalSphere) { int typeIndex = Array.IndexOf(explosionHashes, explosionHash); #if DEV if (typeIndex < 0) { throw new Exception($"Cannot find explosion hash {explosionHash} in list of hashes"); } #endif #if DEV if (typeIndex >= offsets.Length) { throw new Exception($"Type index {typeIndex} for hash {explosionHash} is greater than the length of offsets array"); } if (!current.ContainsKey(explosionHash)) { throw new Exception($"Current dictionary does not contain a key for hash {explosionHash}"); } #endif int offset = offsets[typeIndex]; int cur = current[explosionHash]; int instanceIndex = offset + cur; #if DEV if (instanceIndex >= instances.Length || instanceIndex < 0) { throw new Exception($"Instance index {instanceIndex} is out of range of instances array"); } #endif transforms[instanceIndex].SetPositionAndRotation(pos, rotLocalSphere); instances[instanceIndex].Play(); active[instanceIndex] = true; counts[typeIndex] = counts[typeIndex] + 1; int len = lengths[explosionHash]; for (int i = cur + 1; i < len; ++i) { if (!active[offset + i]) { current[explosionHash] = i; break; } } Log($"Spawn"+ $", hash: {explosionHash}"+ $", typeIndex: {typeIndex}"+ $", offset: {offset}"+ $", instanceIndex: {instanceIndex}"+ $", current: {current[explosionHash]}"+ $", count: {counts[typeIndex]}"); } public static void Pause() { int len = explosionHashes.Length; for (int h = 0; h < len; ++h) { int offset = offsets[h]; int count = counts[h]; for (int i = offset; i < offset + count; ++i) { instances[i].Pause(); } } } public static void Resume() { int len = explosionHashes.Length; for (int h = 0; h < len; ++h) { int offset = offsets[h]; int count = counts[h]; for (int i = offset; i < offset + count; ++i) { instances[i].Resume(); } } } private static void Despawn(int instanceIndex) { int seqIndex = GetSequenceIndex(instanceIndex); instances[instanceIndex].Stop(); transforms[instanceIndex].localPosition = Vector3.zero; active[instanceIndex] = false; int explosionHash = explosionHashes[seqIndex]; if (instanceIndex < current[explosionHash]) { current[explosionHash] = instanceIndex; } counts[seqIndex] = counts[seqIndex] - 1; Log($"Despawn"+ $", instanceIndex: {instanceIndex}"+ $", current: {current[explosionHash]}"+ $", count: {counts[seqIndex]}"+ $", active: {active[instanceIndex]}"); } public static void Update(float dt) { int len = explosionHashes.Length; for (int h = 0; h < len; ++h) { int hash = explosionHashes[h]; int offset = offsets[h]; int count = counts[h]; int cur = count; for (int it = count; it > 0; --it) { int i = it - 1; int n = offset + i; if (!instances[n].IsAlive()) { Despawn(n); cur = i; } } current[hash] = cur; } } private static int GetSequenceIndex(int instanceIndex) { int index = 0; for (int i = 0; i < offsets.Length - 1; ++i) { if (offsets[i + 1] > instanceIndex) { return index; } ++index; } return index; } public static void DespawnAll() { for (int i = 0; i < totalBullets; ++i) { instances[i].Stop(); active[i] = false; } int hashes = explosionHashes.Length; for (int h = 0; h < hashes; ++h) { int hash = explosionHashes[h]; current[hash] = 0; } } public static float GetRadiusByNameHash(int nameHash) { return radius[Array.IndexOf(explosionHashes, nameHash)]; } public static IEnumerator Load(LoadState loadState, BulletPatternSequence sequence) { loadState.explosionsLoaded = 0; loadState.explosionsToLoad = sequence.GetTotalBullets(); // Load assets by label AsyncOperationHandle> handle = Addressables.LoadAssetsAsync("explosion", null); yield return handle; Dictionary prefabs = new Dictionary(); for (int i = 0; i < handle.Result.Count; ++i) { prefabs.Add(HashUtils.Simple(handle.Result[i].name), handle.Result[i]); } int seqLen = sequence.Count; for (int i = 0; i < seqLen; ++i) { PatternConfig cfg = sequence.Get(i); int explosionHash = cfg.ExplosionHash; current[explosionHash] = 0; if (lengths.ContainsKey(explosionHash)) { lengths[explosionHash] += cfg.MaxNum; } else { lengths[explosionHash] = cfg.MaxNum; } } List uniqueHashes = new List(); for (int i = 0; i < seqLen; ++i) { if (!uniqueHashes.Contains(sequence.Get(i).ExplosionHash)) { uniqueHashes.Add(sequence.Get(i).ExplosionHash); } } explosionHashes = uniqueHashes.ToArray(); offsets = new int[explosionHashes.Length]; counts = new int[explosionHashes.Length]; { int offset = 0; for (int i = 0; i < explosionHashes.Length; ++i) { offsets[i] = offset; offsetMap.Add(explosionHashes[i], offset); offset += lengths[explosionHashes[i]]; } } Scene sceneExplosions = SceneManager.CreateScene("Explosions"); for (int i = 0; i < explosionHashes.Length; ++i) { int explosionHash = explosionHashes[i]; int length = lengths[explosionHash]; int offset = offsets[i]; GameObject prefab = prefabs[explosionHash]; for (int e = 0; e < length; ++e) { int n = offset + e; instances[n] = GameObject.Instantiate(prefab).GetComponent(); instances[n].gameObject.name = $"{prefab.name}_{e:0000}"; transforms[n] = instances[n].transform; radius[n] = instances[n].Radius; SceneManager.MoveGameObjectToScene(instances[n].gameObject, sceneExplosions); ++loadState.explosionsLoaded; if (n % CONST.INSTANTIATES_PER_FRAME == 0) { yield return null; } } } } [Conditional("LOG")] private static void Log(object msg) { Debug.Log(msg); } } }