Game Scene Split up

This commit is contained in:
2026-06-04 13:45:46 -07:00
parent dbc4a92a86
commit 16b01bec38
49 changed files with 11976 additions and 188 deletions
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 527bfff0c1216544093c653466a5e5af
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,64 @@
#if UNITY_EDITOR
using System.Collections.Generic;
using ProjectM.Simulation;
using Unity.Entities;
using Unity.NetCode;
using UnityEngine;
namespace ProjectM.Client
{
/// <summary>
/// EDITOR-ONLY client sender for dev-tool <see cref="DebugCommandRequest"/> RPCs. Mirrors
/// <c>StorageOpSendSystem</c>: static convenience methods enqueue into a queue that this client
/// <see cref="SystemBase"/> drains into request entities each tick (so it works from the DebugOverlay's IMGUI
/// AND headless from execute_code). The statics are reset on play-enter so a fast-enter-playmode reload can't
/// replay a stale queue. The wire type is unconditional; this system is #if UNITY_EDITOR (stripped from builds).
/// </summary>
[WorldSystemFilter(WorldSystemFilterFlags.ClientSimulation)]
public partial class DebugCommandSendSystem : SystemBase
{
struct Pending { public byte Op; public int ArgA; public int ArgB; }
static readonly List<Pending> s_Pending = new List<Pending>();
/// <summary>Queue a raw dev command for the next client tick.</summary>
public static void Send(byte op, int argA = 0, int argB = 0)
=> s_Pending.Add(new Pending { Op = op, ArgA = argA, ArgB = argB });
// Convenience wrappers (overlay buttons + execute_code).
public static void SpawnWave(int size) => Send(DebugOp.SpawnWave, size);
public static void EndSiege() => Send(DebugOp.EndSiege);
public static void ClearEnemies() => Send(DebugOp.ClearEnemies);
public static void SetCalm() => Send(DebugOp.SetCalm);
public static void GrantResource(byte itemId, int count) => Send(DebugOp.GrantResource, itemId, count);
public static void GrantUpgrade() => Send(DebugOp.GrantUpgrade);
public static void Teleport(byte region) => Send(DebugOp.Teleport, region);
public static void ToggleGod() => Send(DebugOp.ToggleGod);
public static void Heal() => Send(DebugOp.Heal);
public static void Kill() => Send(DebugOp.KillPlayer);
public static void AdvanceGoal(int by) => Send(DebugOp.AdvanceGoal, by);
public static void SetHeat(int heat) => Send(DebugOp.SetHeat, heat);
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
static void ResetOnEnterPlayMode() => s_Pending.Clear();
protected override void OnUpdate()
{
if (s_Pending.Count == 0)
return;
if (!SystemAPI.TryGetSingletonEntity<NetworkId>(out var connection))
return; // not connected yet — hold the queue
var em = EntityManager;
for (int i = 0; i < s_Pending.Count; i++)
{
var p = s_Pending[i];
var req = em.CreateEntity();
em.AddComponentData(req, new DebugCommandRequest { Op = p.Op, ArgA = p.ArgA, ArgB = p.ArgB });
em.AddComponentData(req, new SendRpcCommandRequest { TargetConnection = connection });
}
s_Pending.Clear();
}
}
}
#endif
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 6f0d6e2babc53f3489bcd6f7988d31ca
@@ -0,0 +1,78 @@
#if UNITY_EDITOR
using ProjectM.Simulation;
using UnityEngine;
namespace ProjectM.Client
{
/// <summary>
/// EDITOR-ONLY in-game dev-tools overlay (IMGUI, mirrors <c>ConnectionUI</c>). Buttons enqueue
/// <see cref="DebugCommandRequest"/> RPCs through <see cref="DebugCommandSendSystem"/>, so they drive the REAL
/// server-authoritative paths (and work over a live connection too, not just in-editor). Drop it on a
/// GameObject in the DevSandbox (or Game) scene. While open it forces the OS cursor visible
/// (<see cref="AimPresentation.ForceCursorVisible"/>) so its buttons stay clickable even while aiming.
/// Stripped from player builds (#if UNITY_EDITOR).
/// </summary>
public class DebugOverlay : MonoBehaviour
{
bool _open = true;
int _siegeSize = 5;
int _grantAmount = 50;
void OnDisable() => AimPresentation.ForceCursorVisible = false;
void OnGUI()
{
if (GUI.Button(new Rect(Screen.width - 96, 10, 86, 24), _open ? "DEV ▲" : "DEV ▼"))
_open = !_open;
AimPresentation.ForceCursorVisible = _open;
if (!_open)
return;
GUILayout.BeginArea(new Rect(Screen.width - 232, 40, 222, 540), GUI.skin.box);
GUILayout.Label("DEV TOOLS");
GUILayout.Label("- World -");
_siegeSize = IntField("Siege size", _siegeSize);
if (GUILayout.Button("Spawn Wave / Force Siege")) DebugCommandSendSystem.SpawnWave(_siegeSize);
if (GUILayout.Button("End Siege")) DebugCommandSendSystem.EndSiege();
if (GUILayout.Button("Clear Enemies")) DebugCommandSendSystem.ClearEnemies();
if (GUILayout.Button("Force Calm")) DebugCommandSendSystem.SetCalm();
if (GUILayout.Button("Advance Goal +1")) DebugCommandSendSystem.AdvanceGoal(1);
GUILayout.Space(6);
GUILayout.Label("- Resources -");
_grantAmount = IntField("Amount", _grantAmount);
GUILayout.BeginHorizontal();
if (GUILayout.Button("Aether")) DebugCommandSendSystem.GrantResource(ResourceId.Aether, _grantAmount);
if (GUILayout.Button("Ore")) DebugCommandSendSystem.GrantResource(ResourceId.Ore, _grantAmount);
if (GUILayout.Button("Bio")) DebugCommandSendSystem.GrantResource(ResourceId.Biomass, _grantAmount);
GUILayout.EndHorizontal();
if (GUILayout.Button("Grant Damage Upgrade")) DebugCommandSendSystem.GrantUpgrade();
GUILayout.Space(6);
GUILayout.Label("- Player -");
GUILayout.BeginHorizontal();
if (GUILayout.Button("Heal")) DebugCommandSendSystem.Heal();
if (GUILayout.Button("Kill")) DebugCommandSendSystem.Kill();
if (GUILayout.Button("God")) DebugCommandSendSystem.ToggleGod();
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
if (GUILayout.Button("Go Base")) DebugCommandSendSystem.Teleport(RegionId.Base);
if (GUILayout.Button("Go Expedition")) DebugCommandSendSystem.Teleport(RegionId.Expedition);
GUILayout.EndHorizontal();
GUILayout.EndArea();
}
static int IntField(string label, int value)
{
GUILayout.BeginHorizontal();
GUILayout.Label(label, GUILayout.Width(70));
string s = GUILayout.TextField(value.ToString(), GUILayout.Width(60));
GUILayout.EndHorizontal();
return int.TryParse(s, out var v) ? v : value;
}
}
}
#endif
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: d86864d4624a5a749994e6b7b14461fa
@@ -12,10 +12,19 @@ namespace ProjectM.Client
/// <summary>Active scheme (<see cref="ProjectM.Simulation.InputSchemeId"/>): 0 = mouse/keyboard, 1 = gamepad.</summary>
public static byte Scheme;
/// <summary>When true, <see cref="AimReticleSystem"/> keeps the OS cursor VISIBLE even while aiming (the
/// editor-only DebugOverlay sets this so its IMGUI buttons stay clickable). Non-#if so the reticle system
/// can read it in any build; nothing sets it outside the editor.</summary>
public static bool ForceCursorVisible;
// Static fields can survive editor domain reloads (fast enter-play-mode); reset on every play-enter so a
// stale gamepad value from a prior session can't briefly hide the cursor / show the world reticle before
// the input gather republishes the real scheme.
[UnityEngine.RuntimeInitializeOnLoadMethod(UnityEngine.RuntimeInitializeLoadType.SubsystemRegistration)]
static void ResetOnEnterPlayMode() => Scheme = ProjectM.Simulation.InputSchemeId.KeyboardMouse;
static void ResetOnEnterPlayMode()
{
Scheme = ProjectM.Simulation.InputSchemeId.KeyboardMouse;
ForceCursorVisible = false;
}
}
}
@@ -144,7 +144,7 @@ namespace ProjectM.Client
// Hide the OS cursor only while aiming AND focused; restore otherwise (focus loss / pre-spawn) so an
// unfocused editor or a windowed session is never stranded with an invisible pointer.
bool wantHidden = haveTarget && Application.isFocused;
bool wantHidden = haveTarget && Application.isFocused && !AimPresentation.ForceCursorVisible;
if (wantHidden != _cursorHidden)
{
if (wantHidden) Cursor.lockState = CursorLockMode.None;
@@ -74,15 +74,13 @@ namespace ProjectM.Client
}
// Ease the drone intensity toward the phase target (tenser during Defend).
float target = phase == CyclePhase.Defend ? AmbientBaseVolume * 1.7f : AmbientBaseVolume;
float target = phase == CyclePhase.Siege ? AmbientBaseVolume * 1.7f : AmbientBaseVolume;
_ambient.volume = Mathf.MoveTowards(_ambient.volume, target, SystemAPI.Time.DeltaTime * 0.25f);
}
void PlaySting(byte phase)
{
AudioClip clip = phase == CyclePhase.Defend ? _stingDefend
: phase == CyclePhase.Build ? _stingBuild
: _stingExpedition;
AudioClip clip = phase == CyclePhase.Siege ? _stingDefend : _stingBuild;
if (clip != null && _ambient != null)
_ambient.PlayOneShot(clip, 0.6f);
}
@@ -59,13 +59,13 @@ namespace ProjectM.Client
{
var endTick = new NetworkTick(cyc.PhaseEndTick);
string detail;
if (cyc.Phase == CyclePhase.Defend)
if (cyc.Phase == CyclePhase.Siege)
detail = "WAVE " + cyc.WaveNumber + " - " + _huskQuery.CalculateEntityCount() + " HUSKS";
else if (haveTick && cyc.PhaseEndTick != 0 && endTick.IsValid && endTick.IsNewerThan(nt.ServerTick))
detail = (endTick.TicksSince(nt.ServerTick) / 60) + "s";
detail = "INCURSION IN " + (endTick.TicksSince(nt.ServerTick) / 60 + 1) + "s";
else
detail = "";
_phaseText.text = PhaseLabel(cyc.Phase) + (detail.Length > 0 ? " - " + detail : "") + " CYCLE " + cyc.CycleNumber;
_phaseText.text = PhaseLabel(cyc.Phase) + (detail.Length > 0 ? " - " + detail : "");
_phaseText.color = PhaseColor(cyc.Phase);
}
else if (_phaseText != null)
@@ -79,7 +79,7 @@ namespace ProjectM.Client
bool onExpedition = cam != null && cam.transform.position.x > 500f;
_locationText.text = onExpedition
? "ON EXPEDITION - return through the gate"
: "AT BASE" + (haveCycle && cyc.Phase == CyclePhase.Expedition ? " - step into the gate to deploy" : "");
: "AT BASE - deploy through the gate when you're ready";
_locationText.color = onExpedition ? new Color(1f, 0.8f, 0.4f) : new Color(0.6f, 0.85f, 1f);
}
@@ -304,9 +304,8 @@ namespace ProjectM.Client
{
switch (phase)
{
case CyclePhase.Expedition: return new Color(0.45f, 0.85f, 1f);
case CyclePhase.Defend: return new Color(1f, 0.5f, 0.3f);
case CyclePhase.Build: return new Color(0.45f, 0.95f, 0.6f);
case CyclePhase.Calm: return new Color(0.45f, 0.9f, 0.7f);
case CyclePhase.Siege: return new Color(1f, 0.45f, 0.3f);
default: return Color.white;
}
}
@@ -315,9 +314,8 @@ namespace ProjectM.Client
{
switch (phase)
{
case CyclePhase.Expedition: return "EXPEDITION";
case CyclePhase.Defend: return "DEFEND";
case CyclePhase.Build: return "BUILD";
case CyclePhase.Calm: return "AT BASE";
case CyclePhase.Siege: return "UNDER SIEGE";
default: return "";
}
}