Restructures the game around the cyclical run loop from Game Design Doc V2: 5 waves = a cycle, 3 cycles = a phase, each phase ends in a boss. - New TD.Gameplay.Waves: WaveGroup / PhaseDefinition / RunDefinition author the run as draggable weighted pools; RunState owns phase/cycle position, the drawn wave slots, and the per-slot enemy buff sets. WaveManager's flat wave array is gone -- it now only runs the encounter RunState points at. - New TD.Gameplay.EnemyUpgrades: the post-wave enemy-buff vote, with public live-replicated ballots so the HUD can show who voted for what. - Inter-wave flow is now strictly sequential: draft -> vote -> build, each stage ending early once every player has acted. - Enemy abilities inverted from per-instance random rolls to deterministic, stacking per-wave-slot sets. Six cards ship: Split (reworked), Flight, Blink, No Bounty, Gold Theft, Double Up. - Tower upgrades are a two-step tree: a draft pick unlocks a node, gold converts an already-placed tower in place. - Boss encounters flag their enemies and drive a boss HP bar. - Player cap reduced to 3 via MatchRules.MaxPlayers. - GoldConfig is now keyed by global encounter number rather than wave index. Compiles clean; NOT yet verified in-engine. Editor wiring still required -- see Docs/2.0_Setup_Checklist.md. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
69 lines
2.8 KiB
C#
69 lines
2.8 KiB
C#
// Assets/_Project/Scripts/Editor/Gameplay/RunDefinitionEditor.cs
|
|
using UnityEditor;
|
|
using UnityEngine;
|
|
using TD.Gameplay.Waves;
|
|
|
|
namespace TD.Editor.Gameplay
|
|
{
|
|
/// <summary>
|
|
/// Inspector for <see cref="RunDefinition"/>. Draws the normal fields, then a live
|
|
/// validation panel reporting whether the authored pools can actually produce a run.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// The check that matters here is the distinct-enemy-type one: because enemy upgrades are
|
|
/// keyed to a wave slot, a phase must be able to fill its cycle with waves that use different
|
|
/// enemies. A pool can look healthy (plenty of entries) and still fail that. Reporting it in
|
|
/// the inspector turns a startup error into something a designer sees while authoring.
|
|
/// </remarks>
|
|
[CustomEditor(typeof(RunDefinition))]
|
|
public class RunDefinitionEditor : UnityEditor.Editor
|
|
{
|
|
public override void OnInspectorGUI()
|
|
{
|
|
DrawDefaultInspector();
|
|
|
|
var run = (RunDefinition)target;
|
|
|
|
EditorGUILayout.Space();
|
|
EditorGUILayout.LabelField("Validation", EditorStyles.boldLabel);
|
|
|
|
bool ok = run.Validate(out string report);
|
|
EditorGUILayout.HelpBox(report, ok ? MessageType.Info : MessageType.Error);
|
|
|
|
// Per-phase capacity readout — the fastest way to see how much headroom a pool has
|
|
// before the distinct-type requirement starts biting.
|
|
if (run.Phases != null)
|
|
{
|
|
EditorGUILayout.Space();
|
|
EditorGUILayout.LabelField("Phase capacity", EditorStyles.boldLabel);
|
|
|
|
for (int i = 0; i < run.Phases.Length; i++)
|
|
{
|
|
var phase = run.Phases[i];
|
|
if (phase == null)
|
|
{
|
|
EditorGUILayout.LabelField($"Phase {i + 1}", "— empty —");
|
|
continue;
|
|
}
|
|
|
|
int distinct = phase.CountDistinctEnemyTypes();
|
|
string value = $"{distinct} distinct enemy type(s) / {run.WavesPerCycle} needed";
|
|
var style = new GUIStyle(EditorStyles.label)
|
|
{
|
|
normal = { textColor = distinct >= run.WavesPerCycle
|
|
? EditorStyles.label.normal.textColor
|
|
: new Color(0.9f, 0.4f, 0.3f) }
|
|
};
|
|
EditorGUILayout.LabelField(phase.Label, value, style);
|
|
}
|
|
}
|
|
|
|
EditorGUILayout.Space();
|
|
if (GUILayout.Button("Rebuild Wave Index"))
|
|
{
|
|
run.RebuildIndex();
|
|
Debug.Log($"[RunDefinition] Rebuilt wave index: {run.WaveCount} distinct wave(s).");
|
|
}
|
|
}
|
|
}
|
|
}
|