using UnityEngine; namespace Ironspore { public sealed class WarzoneDirector : MonoBehaviour { public MechPilot Player { get; private set; } public WorldInteractable Focused { get; private set; } public float ExtractionRemaining { get; private set; } = -1; public bool TransportReady { get; private set; } public EscortUnit Escort { get; private set; } Transform relay, onwardGate, terminalTarget; public Transform NavigationTarget => game.Run.routeCompleted ? null : !game.Trial && game.Run.stage == 4 ? terminalTarget : game.Run.objectiveDone ? onwardGate : relay; public Vector3 ExtractionPosition => extractionPosition; readonly System.Collections.Generic.List cover = new System.Collections.Generic.List(); public System.Collections.Generic.IReadOnlyList Cover => cover; Transform root; GameSession game; Vector3 extractionPosition = new Vector3(-19, 0, -8); GameObject transport; void ResetWorld() { game = GetComponent(); if (root) { root.gameObject.SetActive(false); Destroy(root.gameObject); } root = new GameObject("World").transform; ExtractionRemaining = -1; TransportReady = false; Focused = null; Escort = null; relay = onwardGate = terminalTarget = null; cover.Clear(); RenderSettings.ambientLight = new Color(.27f, .34f, .38f); RenderSettings.fog = true; RenderSettings.fogColor = new Color(.09f, .14f, .18f); RenderSettings.fogDensity = .009f; var sun = new GameObject("Sun"); sun.transform.SetParent(root); sun.transform.rotation = Quaternion.Euler(48, -35, 0); var light = sun.AddComponent(); light.type = LightType.Directional; light.intensity = 1.7f; light.color = new Color(1, .87f, .72f); light.shadows = LightShadows.Soft; WorldFactory.Box("Ground", new Vector3(0, -.3f, 8), new Vector3(72, .6f, 100), new Color(.11f, .16f, .18f), root); for (int x = -30; x <= 30; x += 5) WorldFactory.Box("FloorSeam", new Vector3(x, .008f, 8), new Vector3(.035f, .015f, 96), new Color(.18f, .24f, .26f), root, false); for (int z = -35; z <= 50; z += 5) WorldFactory.Box("FloorSeam", new Vector3(0, .009f, z), new Vector3(68, .016f, .035f), new Color(.18f, .24f, .26f), root, false); for (int side = -1; side <= 1; side += 2) { WorldFactory.Box("Boundary", new Vector3(side * 35, 2, 10), new Vector3(1, 4, 96), new Color(.15f, .22f, .25f), root); for (int z = -25; z < 50; z += 12) { WorldFactory.Box("Pillar", new Vector3(side * 30, 5, z), new Vector3(1.5f, 10, 1.5f), new Color(.19f, .27f, .29f), root); WorldFactory.Box("AmberStripe", new Vector3(side * 29.2f, 3, z), new Vector3(.07f, .25f, 1.7f), new Color(1, .57f, .18f), root, false, true); } } } public void BuildHangar() { ResetWorld(); for (int i = 0; i < 3; i++) { var model = Instantiate(game.mechModel, root); model.name = "HangarChassis_" + i; model.transform.position = new Vector3(i * 4 - 2, 0, 2); model.transform.localScale = game.chassis[i].modelScale; model.transform.rotation = Quaternion.AngleAxis(180, Vector3.up) * model.transform.rotation; WorldFactory.Cylinder("DisplayPad", new Vector3(i * 4 - 2, .05f, 2), new Vector3(3, .1f, 3), game.chassis[i].accent, root, false); } var camera = new GameObject("HangarCamera"); camera.transform.SetParent(root); camera.AddComponent().fieldOfView = 47; camera.transform.position = new Vector3(11, 4.3f, -11); camera.transform.LookAt(new Vector3(2, 1.5f, 2)); camera.AddComponent(); WorldFactory.Box("RoofBeam", new Vector3(0, 9, 4), new Vector3(62, 1.3f, 2), new Color(.16f, .22f, .24f), root); } public void BuildWarzone() { ResetWorld(); var run = game.Run; var avatar = new GameObject("PlayerMech"); avatar.transform.SetParent(root); avatar.transform.position = new Vector3(0, .1f, -18); Player = avatar.AddComponent(); Player.Initialize(game, game.mechModel); var random = new System.Random(run.seed + run.stage * 7919); for (int i = 0; i < 8; i++) { float x = (i % 2 == 0 ? -1 : 1) * (8 + (i % 3) * 5); float z = -10 + i * 6; cover.Add(new Rect(x - 2.75f, z - 1.5f, 5.5f, 3)); var color = i % 2 == 0 ? new Color(.22f, .34f, .37f) : new Color(.45f, .26f, .15f); WorldFactory.Box("FreightContainer", new Vector3(x, 1.1f, z), new Vector3(5.5f, 2.2f, 3), color, root); for (int rib = 0; rib < 5; rib++) WorldFactory.Box("ContainerRib", new Vector3(x - 2 + rib, 1.1f, z - 1.54f), new Vector3(.09f, 2.1f, .08f), new Color(.11f, .17f, .19f), root, false); } WorldFactory.Box("HighWalkway", new Vector3(19, 4, 14), new Vector3(6, .6f, 28), new Color(.23f, .3f, .3f), root); var ramp = WorldFactory.Box("AccessRamp", new Vector3(19, 2, -6), new Vector3(6, .5f, 13), new Color(.3f, .34f, .31f), root); ramp.transform.rotation = Quaternion.Euler(-18, 0, 0); for (int i = 0; i < 6; i++) { Vector3 pos = new Vector3(i % 2 == 0 ? -8 : 9, 0, -6 + i * 6); string key = "zone" + run.stage + "_crate" + i; if (run.searched.Contains(key)) continue; string item = i == 0 ? "blueprint_boost_efficient" : i == 3 ? "blueprint_coil_capacitor" : i == 5 ? "blueprint_reactive_guard" : (i + run.biome) % 2 == 0 ? "alloy" : "cells"; var crate = WorldFactory.Box("SalvageCrate", pos + Vector3.up * .65f, new Vector3(1.8f, 1.3f, 1.4f), new Color(.24f, .42f, .39f), root); WorldFactory.Box("CrateLatch", pos + new Vector3(0, 1.32f, 0), new Vector3(1.4f, .08f, .85f), new Color(.15f, .95f, .73f), root, false, true); var interaction = crate.AddComponent(); interaction.kind = 0; interaction.key = key; interaction.itemId = item; interaction.amount = item.StartsWith("blueprint_") ? 1 : random.Next(2, 6); interaction.offersPart = i == 2 || i == 4; interaction.partSlot = i == 2 ? 2 : 0; interaction.partIndex = run.stage % 2 == 0 ? 1 : 2; interaction.RefreshLabel(); } var objective = WorldFactory.Box("NavigationRelay", new Vector3(0, 1.5f, 23), new Vector3(2.2f, 3, 1.6f), new Color(.21f, .33f, .36f), root); WorldFactory.Box("RelayScreen", new Vector3(0, 2.4f, 22.14f), new Vector3(1.5f, .65f, .1f), new Color(.1f, .9f, .83f), root, false, true); relay = objective.transform; var objectiveAction = objective.AddComponent(); objectiveAction.kind = 1; objectiveAction.RefreshLabel(); var onward = WorldFactory.Cylinder("OnwardGate", new Vector3(0, .05f, 36), new Vector3(7, .1f, 7), new Color(.3f, .6f, .65f), root, false); onwardGate = onward.transform; var onwardAction = onward.AddComponent(); onwardAction.kind = 2; onwardAction.RefreshLabel(); WorldFactory.Cylinder("ExtractionPad", extractionPosition + Vector3.up * .06f, new Vector3(7, .12f, 7), new Color(.85f, .47f, .14f), root, false); for (int i = 0; i < 4; i++) WorldFactory.Box("LandingLight", extractionPosition + new Vector3(i % 2 == 0 ? -3 : 3, .25f, i < 2 ? -3 : 3), new Vector3(.3f, .5f, .3f), new Color(1, .65f, .2f), root, false, true); var pad = new GameObject("ExtractionBeacon"); pad.transform.SetParent(root); pad.transform.position = extractionPosition; var evac = pad.AddComponent(); evac.kind = 3; evac.RefreshLabel(); WorldFactory.Label("world.extraction", extractionPosition + Vector3.up * 3, new Color(1, .7f, .3f), root); WorldFactory.Label("world.relay", new Vector3(0, 5, 23), new Color(.25f, 1, .85f), root); int count = game.Trial ? 2 : 3 + run.stage; for (int i = 0; i < count; i++) SpawnEnemy(new Vector3((i % 2 == 0 ? -1 : 1) * (4 + i % 3 * 3), .1f, 6 + i * 5), i % 3, i); if (!game.Trial && run.stage == 4) { objective.SetActive(false); if (run.branch == 0 && !run.routeCompleted) SpawnEnemy(new Vector3(0, .1f, 29), 3, 99); if (run.branch == 1 && !run.routeCompleted) { var cart = WorldFactory.Box("EscortEngineer", new Vector3(0, 1.3f, 0), new Vector3(3, 2.6f, 4), new Color(.2f, .55f, .5f), root); terminalTarget = cart.transform; Escort = cart.AddComponent(); Escort.Initialize(game); } } } void SpawnEnemy(Vector3 pos, int type, int number) { var obj = new GameObject("Enemy_" + number); obj.transform.SetParent(root); obj.transform.position = pos; obj.layer = 9; if (type == 3) terminalTarget = obj.transform; obj.AddComponent().Initialize(game, type, game.mechModel, root); } public void CallExtraction() { if (TransportReady) { game.EndSortie(false); return; } if (ExtractionRemaining >= 0) { game.Notify("notice.transportInbound"); return; } ExtractionRemaining = 15; game.Notify("notice.extractionCalled"); GameAudio.Cue(410, .2f); } void Update() { if (!game || !game.InField || game.Paused || !Player) return; if (ExtractionRemaining >= 0 && !TransportReady) { ExtractionRemaining = Mathf.Max(0, ExtractionRemaining - Time.deltaTime); if (ExtractionRemaining == 0) { TransportReady = true; transport = WorldFactory.Box("RecoveryTransport", extractionPosition + new Vector3(0, 5.5f, 0), new Vector3(7, 1.8f, 10), new Color(.24f, .3f, .32f), root, false); WorldFactory.Box("TransportWing", transport.transform.position, new Vector3(14, .5f, 3), new Color(.32f, .4f, .41f), root, false); game.Notify("notice.transportReady", game.Settings.Bindings.Display("Interact")); GameAudio.Cue(950, .3f); } } Focused = null; float nearest = 4.5f; foreach (var interaction in root.GetComponentsInChildren()) { if (interaction.collected) continue; float distance = Vector3.Distance(Player.transform.position, interaction.transform.position); if (distance < nearest) { nearest = distance; Focused = interaction; } } } public void LaunchPod(Vector3 position) { var pod = WorldFactory.Box("CargoPod", position + Vector3.up * 2, new Vector3(1.4f, 1, 1.4f), new Color(.1f, .8f, .6f), root, false, true); pod.AddComponent(); } } public sealed class WorldInteractable : MonoBehaviour { public int kind; public string key, itemId; public int amount; public bool collected, offersPart; public int partSlot, partIndex; public string Prompt { get { var g = GameSession.Active; string interact = g.Settings.Bindings.Display("Interact"); if (kind == 0) return GameLocalization.Text("prompt.search", interact); if (kind == 1) return GameLocalization.Text(g.Run.objectiveDone ? "prompt.relayOnline" : "prompt.objective", interact); if (kind == 2) return GameLocalization.Text(g.Run.objectiveDone ? "prompt.route" : "prompt.routeLocked", interact); return GameLocalization.Text(g.World.TransportReady ? "prompt.board" : g.World.ExtractionRemaining >= 0 ? "prompt.inbound" : "prompt.call", interact); } } public void RefreshLabel() { if (collected) transform.localScale = Vector3.one * .5f; } public void Activate() { var g = GameSession.Active; if (kind == 0 && !collected) g.Search(this); else if (kind == 1) g.CompleteObjective(); else if (kind == 2) g.OpenRoutes(); else if (kind == 3) g.World.CallExtraction(); } } public sealed class PodFlight : MonoBehaviour { float age; void Update() { age += Time.deltaTime; transform.position += Vector3.up * Time.deltaTime * (4 + age * 3); if (age > 5) Destroy(gameObject); } } }