using System.Collections.Generic; using UnityEngine; namespace Luneraria.View { /// /// A tower (Grenzwacht, Bastion, Kernwacht) or an Ancient (Mondherz) of the G2 set. Shows the /// authoritative building HP as one of five looks, like scripts/luneraria/view/lune_tower_view.gd /// and lune_ancient_view.gd: intact, lightly and heavily damaged, collapsing, ruin. The view never /// decides anything; HP comes from the core (U2C binds it). /// public sealed class LuneBuildingView : MonoBehaviour { public enum Look { Intact, Light, Heavy, Destroying, Ruin } /// HP fractions where the look changes (the same bands for towers and Ancients). public const float LightBelow = .605f; public const float HeavyBelow = .305f; public string BuildingId = ""; public int Team; /// outer, inner, base or core. public string Tier = ""; public Look Current = Look.Intact; /// Length of the collapse before the ruin shows (Godot DESTROY_SECONDS). public const float DestroySeconds = 1.7f; readonly Dictionary _parts = new Dictionary(); readonly Dictionary _rest = new Dictionary(); float _destroyTime = -1f; bool _destroyFromHeavy; /// Crystal tip (where tower bolts leave). public Vector3 Muzzle { get { Collect(); if (_parts.TryGetValue("Crystal", out Transform crystal)) return crystal.position + Vector3.up * .35f; return transform.position + Vector3.up * 5f; } } /// /// Authoritative HP to a look. A building that falls while the view watches collapses first; /// one that is already down when the view is built appears as a ruin at once. /// public void SetHealth(float hp, float maxHp, bool animate) { Look next = LookFor(hp, maxHp); if (Current == Look.Destroying) return; if (next == Look.Ruin && Current != Look.Ruin && animate) { _destroyFromHeavy = Current == Look.Heavy; Show(Look.Destroying); _destroyTime = 0f; return; } if (next != Current) Show(next); } void Update() { if (Current != Look.Destroying) return; _destroyTime += Time.deltaTime; float k = Mathf.Clamp01(_destroyTime / DestroySeconds); // The upper part tips over and sinks; the Ancient's structure sinks into its dais. foreach (KeyValuePair part in _parts) { if (!_rest.TryGetValue(part.Key, out Pose rest)) continue; bool falls = IsAncient ? !part.Key.StartsWith("Ruin") && part.Key != "Dais" && part.Key != "Pedestal" : part.Key == "Upper" || part.Key == "Upper_Damaged" || part.Key == "Crystal" || part.Key == "Ring" || part.Key.StartsWith("Shard") || part.Key.StartsWith("Pennant") || part.Key.StartsWith("Banner"); if (!falls) continue; part.Value.localRotation = rest.rotation * Quaternion.Euler(0f, 0f, 70f * k * k); part.Value.localPosition = rest.position + Vector3.down * 3f * k * k; } if (k >= 1f) { foreach (KeyValuePair part in _parts) if (_rest.TryGetValue(part.Key, out Pose rest)) part.Value.SetLocalPositionAndRotation(rest.position, rest.rotation); Show(Look.Ruin); } } public bool IsAncient { get { return Tier == "core"; } } void Collect() { if (_parts.Count > 0) return; Transform model = transform.childCount > 0 ? transform.GetChild(0) : transform; foreach (Transform part in model) { _parts[part.name] = part; _rest[part.name] = new Pose(part.localPosition, part.localRotation); } } /// Look for an HP fraction; a destroyed building shows its ruin. public static Look LookFor(float hp, float maxHp) { float fraction = Mathf.Clamp01(hp / Mathf.Max(1f, maxHp)); if (hp <= 0f) return Look.Ruin; if (fraction < HeavyBelow) return Look.Heavy; if (fraction < LightBelow) return Look.Light; return Look.Intact; } /// Part visibility of a look (Godot _enter of the tower and Ancient views). public void Show(Look look) { Collect(); Current = look; bool standing = look != Look.Ruin; if (IsAncient) { foreach (KeyValuePair part in _parts) { string key = part.Key; bool visible; if (key.StartsWith("Ruin")) visible = !standing; else if (key == "Crest_A") visible = standing && Team == 0; else if (key == "Crest_B") visible = standing && Team != 0; else if (key == "Dais" || key == "Pedestal") visible = true; else visible = standing; part.Value.gameObject.SetActive(visible); } return; } bool damagedTop = look == Look.Heavy || (look == Look.Destroying && _destroyFromHeavy); Set("Upper", standing && !damagedTop); Set("Upper_Damaged", standing && damagedTop); Set("Debris", look == Look.Heavy || look == Look.Destroying); Set("Lower", standing); Set("Ruin", !standing); foreach (KeyValuePair part in _parts) { string key = part.Key; if (key == "Crystal" || key == "Ring" || key.StartsWith("Shard") || key.StartsWith("Banner") || key.StartsWith("Pennant")) part.Value.gameObject.SetActive(standing); } } void Set(string key, bool visible) { if (_parts.TryGetValue(key, out Transform part)) part.gameObject.SetActive(visible); } } }