using UnityEngine; namespace Ironspore { public sealed class EnemyUnit : MonoBehaviour { // DynamicProps keeps its original physical obstruction semantics after render-layer separation. const int WorldObstructionMask = (1 << 0) | (1 << 10); GameSession game; float health, cooldown, age; int type; public void Initialize(GameSession session, int enemyType, GameObject mech, Transform world) { game = session; type = enemyType; health = type == 3 ? 650 : type == 2 ? 140 : 60; var model = Instantiate(mech, transform); model.transform.localScale = Vector3.one * (type == 3 ? 1.7f : type == 0 ? .65f : 1); foreach (var renderer in model.GetComponentsInChildren()) renderer.sharedMaterial = WorldFactory.Material(new Color(.5f, .2f, .12f)); foreach (var node in model.GetComponentsInChildren()) node.gameObject.layer = 9; var capsule = gameObject.AddComponent(); capsule.height = type == 3 ? 5.2f : 3; capsule.radius = type == 3 ? 1.5f : .8f; capsule.center = Vector3.up * capsule.height * .5f; WorldFactory.Label(type == 3 ? "world.overseer" : type == 2 ? "world.bulwark" : type == 0 ? "world.skimmer" : "world.sentinel", transform.position + Vector3.up * (capsule.height + 1), new Color(1, .5f, .25f), transform); cooldown = 1 + Random.value; } void Update() { if (!game.InField || game.Paused || game.Trial || !game.World.Player) return; age += Time.deltaTime; Vector3 target = game.World.Player.transform.position + Vector3.up * 1.5f; if (game.World.Escort && game.World.Escort.Health > 0 && Vector3.Distance(transform.position, game.World.Escort.transform.position) < 15) target = game.World.Escort.transform.position; Vector3 flat = target - transform.position; flat.y = 0; if (flat.sqrMagnitude > .1f) transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(flat), Time.deltaTime * 2); float distance = flat.magnitude; if (type != 1 && distance > 10) { Vector3 step = flat.normalized * Time.deltaTime * (type == 3 ? 1.4f : type == 0 ? 2.5f : 1.2f); if (!Physics.Raycast(transform.position + Vector3.up, step.normalized, 1.1f, WorldObstructionMask, QueryTriggerInteraction.Ignore)) transform.position += step; } if (type == 0) { Vector3 p = transform.position; p.y = 1.8f + Mathf.Sin(age) * .4f; transform.position = p; } cooldown -= Time.deltaTime; if (cooldown <= 0 && distance < 42) { Vector3 origin = transform.position + Vector3.up * (type == 3 ? 3.5f : 2); if (!Physics.Linecast(origin, target, WorldObstructionMask, QueryTriggerInteraction.Ignore)) { var bolt = WorldFactory.Primitive(PrimitiveType.Sphere, "EnemyBolt", origin, Vector3.one * .3f, new Color(1, .35f, .08f), null, false, true); bolt.AddComponent().Initialize((target - origin).normalized, type == 3 ? 23 : 12); } cooldown = type == 3 ? .7f : type == 1 ? 1.7f : 2.5f; } } public void Damage(float damage) { health -= damage; if (health > 0) return; if (type == 3) game.CompleteTerminal(); GameAudio.Cue(95, .15f); Destroy(gameObject); } } public sealed class EnemyBolt : MonoBehaviour { Vector3 direction; float damage, life; public void Initialize(Vector3 travel, float amount) { direction = travel; damage = amount; } void Update() { if (!GameSession.Active || !GameSession.Active.InField) { Destroy(gameObject); return; } float distance = Time.deltaTime * 17; if (Physics.SphereCast(transform.position, .12f, direction, out var hit, distance, ~(1 << 9), QueryTriggerInteraction.Ignore)) { var pilot = hit.collider.GetComponentInParent(); if (pilot) pilot.Damage(damage); var escort = hit.collider.GetComponentInParent(); if (escort) escort.Damage(damage); Destroy(gameObject); return; } transform.position += direction * distance; life += Time.deltaTime; if (life > 5) Destroy(gameObject); } } public sealed class EscortUnit : MonoBehaviour { GameSession game; public float Health { get; private set; } = 250; public void Initialize(GameSession session) { game = session; Health = game.Run.escortArmor; WorldFactory.Label("world.escort", transform.position + Vector3.up * 3, Color.cyan, transform); } void Update() { if (!game.InField || game.Paused || Health <= 0 || game.Run.routeCompleted) return; if (Vector3.Distance(game.World.Player.transform.position, transform.position) < 14) transform.position += Vector3.forward * Time.deltaTime * 1.5f; if (transform.position.z >= 33) game.CompleteTerminal(); } public void Damage(float amount) { Health = Mathf.Max(0, Health - amount); game.Run.escortArmor = Health; if (Health <= 0) { game.Store.Commit(s => true); game.Notify("notice.escortLost"); } } } }