Files
unity/Assets/Game/Scripts/Runs/GameSession.cs
T

471 lines
15 KiB
C#

using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
namespace Ironspore
{
public sealed class GameSession : MonoBehaviour
{
public static GameSession Active
{
get; private set;
}
public ChassisDefinition[] chassis;
public EquipmentDefinition[] equipment;
public GameObject mechModel;
public InputActionAsset inputTemplate;
public UnityEngine.UIElements.StyleSheet uiStyle;
public UnityEngine.UIElements.PanelSettings uiPanel;
public LocalizationCatalog localization;
public bool startInRange;
public ProfileStore Store
{
get; private set;
}
ProfileStore mainStore;
public RunRecord Run => Store.Data.run;
public MechPilot Pilot
{
get; private set;
}
public WarzoneDirector World
{
get; private set;
}
public GameHud Hud
{
get; private set;
}
public InputActionAsset Input
{
get; private set;
}
public readonly List<string> SelectedCargo = new List<string>();
public bool MenuOpen { get; private set; } = true;
public bool Paused
{
get; private set;
}
public bool Trial
{
get; private set;
}
public int SelectedChassis = 1;
public readonly string[] SelectedInstances = new string[4];
public int[] InitialEquipment = { 0, 0, 0, 0 };
LocalizedMessage noticeMessage;
public string Notice => noticeMessage?.ToString() ?? "";
public float CargoProgress
{
get; private set;
}
public bool InField
{
get; private set;
}
LocalizedMessage reportMessage = GameLocalization.Message("report.ready");
public string Report => reportMessage.ToString();
float noticeUntil;
float autosaveTimer;
int cargoDamageFrame = -1;
bool ending;
void Awake()
{
Active = this;
GameLocalization.Initialize(localization);
Application.targetFrameRate = 120;
mainStore = new ProfileStore(ProfileStore.SavePath(false));
Store = mainStore;
Input = Instantiate(inputTemplate);
Input.Enable();
World = gameObject.AddComponent<WarzoneDirector>();
Hud = gameObject.AddComponent<GameHud>();
Hud.Initialize(this);
}
void Start()
{
World.BuildHangar();
Hud.ShowHangar();
SetMenu(true);
if (Run.active && (Run.destructionPending || Run.armor <= 0))
EndSortie(true);
if (startInRange)
StartTrial();
}
public EquipmentDefinition Part(int slot, int index)
{
string id = InventoryGrid.EquipmentId(slot, index);
return Array.Find(equipment, x => x.id == id);
}
public int PartCount(int slot) => Array.FindAll(equipment, x => x.slot == slot).Length;
public bool Fits(int frame, int[] parts)
{
float mass = 0, power = 0;
for (int i = 0; i < 4; i++)
{
var p = Part(i, parts[i]);
if (parts[i] == -1) continue;
if (!p) return false;
mass += p.mass;
power += p.power;
}
return mass <= chassis[frame].massCapacity && power <= chassis[frame].powerCapacity;
}
public bool StartingPartsUnlocked()
{
for (int slot = 0; slot < 4; slot++)
{
if (InitialEquipment[slot] == 0 && SelectedInstances[slot] == null) continue;
string id = InventoryGrid.EquipmentId(slot, InitialEquipment[slot]);
if (!mainStore.Data.warehouse.Exists(i => i.itemId == id && (SelectedInstances[slot] == null || i.uid == SelectedInstances[slot]))) return false;
}
return true;
}
public void BeginSortie()
{
if (Store.Data.run.active)
{
Notify("notice.unfinished");
return;
}
if (!Fits(SelectedChassis, InitialEquipment))
{
Notify("notice.budget");
return;
}
if (!StartingPartsUnlocked())
{
Notify("notice.researchFirst");
return;
}
Trial = false;
Store = mainStore;
if (!Store.Begin(SelectedChassis, InitialEquipment, chassis[SelectedChassis].armor, Environment.TickCount, selected: SelectedInstances))
{
NotifySaveFailure();
return;
}
EnterField();
}
public void StartTrial()
{
var range = new ProfileStore(ProfileStore.SavePath(true));
if ((range.Data.run.active && !range.Finish(true)) ||
!range.Begin(SelectedChassis, InitialEquipment, chassis[SelectedChassis].armor, 1739, trial: true))
{
Notify("notice.saveFailed");
return;
}
Trial = true;
Store = range;
EnterField();
}
public void Resume()
{
Trial = false;
Store = mainStore;
if (Run.active && (Run.destructionPending || Run.armor <= 0)) EndSortie(true);
else if (Run.active) EnterField();
}
void EnterField()
{
ending = false;
InField = true;
SelectedCargo.Clear();
CargoProgress = 0;
World.BuildWarzone();
Pilot = World.Player;
SetMenu(false);
Hud.ShowField();
Notify(Trial ? "notice.range" : "notice.enter");
}
public void SetMenu(bool open)
{
MenuOpen = open;
Cursor.lockState = open ? CursorLockMode.None : CursorLockMode.Locked;
Cursor.visible = open;
if (Pilot)
Pilot.RequireFreshTrigger();
if (open)
CargoProgress = 0;
}
public void CloseMenu()
{
Paused = false;
Time.timeScale = 1;
if (InField)
{
SetMenu(false);
Hud.ShowField();
}
}
public void Notify(string key, params object[] args)
{
noticeMessage = GameLocalization.Message(key, args);
noticeUntil = Time.unscaledTime + 5;
Hud.RefreshHangarNotice();
}
void NotifySaveFailure(string fallbackKey = "notice.saveFailed")
{
if (!string.IsNullOrEmpty(Store.Error))
Debug.LogError("Save failed: " + Store.Error);
Notify(string.IsNullOrEmpty(Store.Error) ? fallbackKey : "notice.saveFailed");
}
public InputAction Action(string name) => Input.FindAction("Pilot/" + name, true);
void Update()
{
if (Time.unscaledTime > noticeUntil)
noticeMessage = null;
if (!InField || !Pilot || ending)
return;
if (Pilot.Health <= 0 || Run.destructionPending)
{
EndSortie(true);
return;
}
if (Action("Pause").WasPressedThisFrame())
{
if (MenuOpen)
CloseMenu();
else
{
Paused = true;
Time.timeScale = 0;
SetMenu(true);
Hud.ShowPause();
}
}
if (Paused)
return;
Run.elapsed += Time.deltaTime;
if (Action("Inventory").WasPressedThisFrame())
{
if (MenuOpen)
CloseMenu();
else
{
SetMenu(true);
Hud.ShowCargo();
}
}
if (!MenuOpen && Action("Interact").WasPressedThisFrame() && World.Focused)
World.Focused.Activate();
TickCargo();
autosaveTimer += Time.deltaTime;
if (autosaveTimer >= 10)
{
SnapshotVitals();
Store.Commit(s => true);
autosaveTimer = 0;
}
}
void TickCargo()
{
bool uninterrupted = Pilot.LastDamageFrame == cargoDamageFrame;
cargoDamageFrame = Pilot.LastDamageFrame;
bool preparing = !MenuOpen && !Run.podUsed && SelectedCargo.Count > 0 && Action("Cargo").IsPressed() && Pilot.Grounded && Action("Move").ReadValue<Vector2>().sqrMagnitude < .01f && Pilot.HorizontalSpeed < .2f && uninterrupted;
if (!preparing)
{
CargoProgress = 0;
return;
}
CargoProgress += Time.deltaTime;
if (CargoProgress < 3)
return;
SnapshotVitals();
if (Store.SendCargo(SelectedCargo))
{
World.LaunchPod(Pilot.transform.position);
Notify("notice.podSecured");
GameAudio.Cue(720, .3f);
SelectedCargo.Clear();
}
else
NotifySaveFailure("notice.cargoUnavailable");
CargoProgress = 0;
}
public void ToggleCargo(string uid)
{
if (SelectedCargo.Contains(uid))
SelectedCargo.Remove(uid);
else
{
var next = new List<string>(SelectedCargo) { uid };
if (Store.CanSendCargo(next)) SelectedCargo.Add(uid);
else Notify("notice.twoSlots");
}
Hud.ShowCargo();
}
public void SnapshotVitals()
{
if (!Pilot)
return;
Run.armor = Mathf.Max(0, Pilot.Health);
Run.energy = Pilot.Energy;
}
public void Search(WorldInteractable target)
{
int index = target.offersPart ? Mathf.Clamp(target.partIndex, 0, PartCount(target.partSlot) - 1) : -1;
string offered = target.offersPart ? Part(target.partSlot, index).id : null;
if (!Store.AddCargo(target.itemId, target.amount, target.key, offered))
{
NotifySaveFailure("notice.holdFull");
return;
}
target.collected = true;
target.RefreshLabel();
Notify("notice.recovered", GameLocalization.Message("item." + target.itemId), target.amount);
GameAudio.Cue(520, .12f);
if (target.offersPart)
{
SetMenu(true);
Hud.ShowPart(target.partSlot, index);
}
}
public void Install(int slot, int index)
{
var item = Run.cargo.FindLast(i => i.itemId == InventoryGrid.EquipmentId(slot, index));
if (item == null || !InstallItem(item, Hold.Cargo)) return;
CloseMenu();
}
public bool InstallItem(CargoStack item, Hold from)
{
if (!Store.InstallItem(item.uid, from, parts => Fits(Run.chassis, parts)))
{
NotifySaveFailure("notice.installFailed");
return false;
}
SelectedCargo.Remove(item.uid);
Pilot.ApplyLoadout();
Notify("notice.refit", GameLocalization.Message("equipment." + item.itemId + ".name"));
return true;
}
public void CompleteObjective()
{
if (Run.objectiveDone)
return;
Store.Commit(s => { s.run.objectiveDone = true; return true; });
Notify("notice.routeOpen");
GameAudio.Cue(850, .2f);
}
public void CompleteTerminal()
{
if (!Store.CompleteRoute())
return;
Notify("notice.complete");
GameAudio.Cue(1100, .4f);
}
public void OpenRoutes()
{
if (!Run.objectiveDone)
{
Notify("notice.objectiveFirst");
return;
}
if (Trial)
{
Notify("notice.rangeComplete");
return;
}
if (Run.stage >= 4)
{
Notify("notice.extract");
return;
}
SetMenu(true);
Hud.ShowRoutes();
}
public void NextZone(int option)
{
SnapshotVitals();
bool ok = Store.Commit(s =>
{
if (s.run.stage == 1)
s.run.branch = option;
s.run.stage++;
s.run.biome = option;
s.run.objectiveDone = false;
s.run.upgrades++;
return true;
});
if (ok)
EnterField();
else
NotifySaveFailure();
}
public void Unlock(string id)
{
bool ok = Store.Commit(s =>
{
if (!s.blueprints.Contains(id) || s.unlocked.Contains(id) || s.alloy < 2 || s.cells < 1)
return false;
s.alloy -= 2;
s.cells--;
s.unlocked.Add(id);
return true;
});
Notify(ok ? "notice.researched" : "notice.researchCost");
Hud.ShowHangar();
}
public void EndSortie(bool destroyed)
{
if (ending)
return;
SnapshotVitals();
bool complete = Run.routeCompleted;
int lost = Run.cargo.Count + Run.installed.Count;
int protectedCount = Run.safeCase.FindAll(i => !i.borrowed).Count;
if (!Store.Finish(destroyed))
{
NotifySaveFailure();
return;
}
ending = true;
reportMessage = GameLocalization.Message(Trial ? "report.range" : destroyed ? "report.destroyed" : complete ? "report.complete" : "report.extracted", lost, protectedCount);
InField = false;
Paused = false;
Time.timeScale = 1;
Array.Clear(SelectedInstances, 0, SelectedInstances.Length);
Trial = false;
Store = mainStore;
Pilot = null;
World.BuildHangar();
SetMenu(true);
Hud.ShowHangar();
}
void OnApplicationFocus(bool focus)
{
if (!focus && InField && Hud)
{
Paused = true;
Time.timeScale = 0;
SetMenu(true);
Hud.ShowPause();
}
}
void OnApplicationQuit()
{
if (InField && Pilot)
{
SnapshotVitals();
Store.Commit(s => true);
}
}
void OnDestroy()
{
if (Input)
Destroy(Input);
Time.timeScale = 1;
if (Active == this)
Active = null;
}
}
}