459 lines
14 KiB
C#
459 lines
14 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
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 SettingsService Settings { 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 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(Path.Combine(GameDataPaths.DirectoryPath, "profile.json"));
|
|
Store = mainStore;
|
|
Input = Instantiate(inputTemplate);
|
|
var defaults = SettingsService.RuntimeDefaults();
|
|
Settings = new SettingsService(new SettingsStore(Path.Combine(GameDataPaths.DirectoryPath, "settings.json"), defaults), Input, defaults);
|
|
Input.Enable();
|
|
World = gameObject.AddComponent<WarzoneDirector>();
|
|
Hud = gameObject.AddComponent<GameHud>();
|
|
Hud.Initialize(this);
|
|
}
|
|
|
|
void Start()
|
|
{
|
|
World.BuildHangar();
|
|
Hud.ShowHangar();
|
|
SetMenu(true);
|
|
if (startInRange)
|
|
StartTrial();
|
|
}
|
|
|
|
public EquipmentDefinition Part(int slot, int index)
|
|
{
|
|
var list = Array.FindAll(equipment, x => x.slot == slot);
|
|
return list[Mathf.Clamp(index, 0, list.Length - 1)];
|
|
}
|
|
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]);
|
|
mass += p.mass;
|
|
power += p.power;
|
|
}
|
|
return mass <= chassis[frame].massCapacity && power <= chassis[frame].powerCapacity;
|
|
}
|
|
public bool StartingPartsUnlocked()
|
|
{
|
|
var unlocked = mainStore.Data.unlocked;
|
|
return (InitialEquipment[0] != 1 || unlocked.Contains("blueprint_coil_capacitor"))
|
|
&& (InitialEquipment[2] != 1 || unlocked.Contains("blueprint_boost_efficient"))
|
|
&& (InitialEquipment[3] != 1 || unlocked.Contains("blueprint_reactive_guard"));
|
|
}
|
|
|
|
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))
|
|
{
|
|
NotifySaveFailure();
|
|
return;
|
|
}
|
|
EnterField();
|
|
}
|
|
public void StartTrial()
|
|
{
|
|
Trial = true;
|
|
Store = new ProfileStore(Path.Combine(GameDataPaths.DirectoryPath, "range.json"));
|
|
Store.Begin(SelectedChassis, InitialEquipment, chassis[SelectedChassis].armor, 1739);
|
|
EnterField();
|
|
}
|
|
public void Resume()
|
|
{
|
|
Trial = false;
|
|
Store = mainStore;
|
|
if (Run.active)
|
|
EnterField();
|
|
}
|
|
void EnterField()
|
|
{
|
|
ending = false;
|
|
InField = true;
|
|
SelectedCargo.Clear();
|
|
CargoProgress = 0;
|
|
World.BuildWarzone();
|
|
Pilot = World.Player;
|
|
SetMenu(false);
|
|
Hud.ShowField();
|
|
if (Trial)
|
|
Notify("notice.range", Settings.Bindings.Display("View"), Settings.Bindings.Display("Interact"));
|
|
else
|
|
Notify("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 (Hud && (Hud.SettingsOpen || Hud.MenuInputConsumedThisFrame))
|
|
return;
|
|
if (!InField || !Pilot || ending)
|
|
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();
|
|
if (Pilot.Health <= 0)
|
|
{
|
|
EndSortie(true);
|
|
return;
|
|
}
|
|
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 if (SelectedCargo.Count < 2)
|
|
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)
|
|
{
|
|
if (!Store.AddCargo(target.itemId, target.amount, target.key))
|
|
{
|
|
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, target.partIndex);
|
|
}
|
|
}
|
|
public void Install(int slot, int index)
|
|
{
|
|
var next = (int[])Run.equipment.Clone();
|
|
next[slot] = index;
|
|
if (!Fits(Run.chassis, next))
|
|
{
|
|
Notify("notice.partBudget");
|
|
return;
|
|
}
|
|
Store.Commit(s => { s.run.equipment = next; return true; });
|
|
Pilot.ApplyLoadout();
|
|
CloseMenu();
|
|
Notify("notice.refit", GameLocalization.Message("equipment." + Part(slot, index).id + ".name"));
|
|
}
|
|
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;
|
|
if (!Store.Finish(destroyed))
|
|
{
|
|
NotifySaveFailure();
|
|
return;
|
|
}
|
|
ending = true;
|
|
reportMessage = GameLocalization.Message(Trial ? "report.range" : destroyed ? "report.destroyed" : complete ? "report.complete" : "report.extracted", lost);
|
|
InField = false;
|
|
Trial = false;
|
|
Store = mainStore;
|
|
Pilot = null;
|
|
World.BuildHangar();
|
|
SetMenu(true);
|
|
Hud.ShowHangar();
|
|
}
|
|
void OnApplicationFocus(bool focus)
|
|
{
|
|
if (!focus && Hud && Hud.SettingsOpen)
|
|
{
|
|
Hud.CancelSettingsInput();
|
|
return;
|
|
}
|
|
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()
|
|
{
|
|
Settings?.Dispose();
|
|
if (Input)
|
|
Destroy(Input);
|
|
Time.timeScale = 1;
|
|
if (Active == this)
|
|
Active = null;
|
|
}
|
|
}
|
|
}
|