using UnityEngine; namespace ProjectI.Enemies { // Arrival brackets have their own geometry; faction, charge, health and status visuals retain ownership. [DefaultExecutionOrder(220)] public sealed class EnemyArrivalPresentation : MonoBehaviour { private PrototypeEnemy actor; private LineRenderer[] lines; private static Material shared; private ulong observedIdentity; #if UNITY_EDITOR || DEVELOPMENT_BUILD public double FirstVisibleAt { get; private set; } = -1; public bool CueVisible => lines != null && lines[0] != null && lines[0].enabled; #endif private void Awake() => actor = GetComponent(); private void LateUpdate() { var state = actor != null ? actor.Arrival : default; if (actor != null && observedIdentity != actor.SpawnIdentity) { observedIdentity = actor.SpawnIdentity; #if UNITY_EDITOR || DEVELOPMENT_BUILD FirstVisibleAt = -1; #endif } bool visible = actor != null && actor.IsAlive && state.Valid && state.SpawnIdentity == actor.SpawnIdentity && actor.ServerNow < state.ReadyAt; if (!visible) { Hide(); return; } if (lines == null) { if (shared == null) { shared = new Material(Shader.Find("Universal Render Pipeline/Unlit")); shared.name = "Shared arrival brackets"; shared.SetColor("_BaseColor", new Color(.8f, .95f, 1)); } lines = new LineRenderer[4]; for (int i = 0; i < 4; i++) { var child = new GameObject("Arrival bracket " + i); child.layer = 2; child.transform.SetParent(transform, false); var line = child.AddComponent(); line.sharedMaterial = shared; line.useWorldSpace = false; line.widthMultiplier = .065f; line.positionCount = 3; line.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off; lines[i] = line; } } #if UNITY_EDITOR || DEVELOPMENT_BUILD if (FirstVisibleAt < 0) FirstVisibleAt = actor.ServerNow; #endif float progress = Mathf.Clamp01((float)((actor.ServerNow - state.BornAt) / (state.ReadyAt - state.BornAt))); float radius = .65f, height = 1.8f; if (actor.IsBoss) { radius = .9f; height = 3.3f; } else EnemySpawnCircleRules.TryResolveClearance(actor.definition, actor.IsElite, out radius, out height, out _); radius += .3f; height = Mathf.Max(.75f, height); for (int i = 0; i < 4; i++) { var axis = Quaternion.Euler(0, 45 + i * 90, 0) * Vector3.forward; float bottom = .1f + progress * height * .5f, top = height + .3f - progress * height * .3f; lines[i].SetPosition(0, axis * (radius - .18f) + Vector3.up * top); lines[i].SetPosition(1, axis * radius + Vector3.up * top); lines[i].SetPosition(2, axis * radius + Vector3.up * bottom); lines[i].enabled = true; } } private void Hide() { if (lines != null) foreach (var line in lines) if (line != null) line.enabled = false; } private void OnDisable() => Hide(); } }