champions-of-luneraria/Assets/Luneraria/View/LuneMatchDriver.cs
Martin Stickelbroeck 8e4b52f4ab S1 Strategie-Regeln nach Legion TD 2, Dota 2 und League of Legends
Hinter MatchOptions.Extended (Standard im Spiel, Schalter im Startbildschirm):
Söldner-Einkommen (Angriffschampion +Preis/12 Einkommen dauerhaft), Kasernen
(gefallener innerer Turm: Gegnerwellen +25 % und ein Monster mehr), Erschöpfung
und Rückkauf (60 % Leben, Rückkauf 60 + 20 x Stufe), Mondsegen (Grottensieg
+4 % je Stapel, Mondseele bei 4). Bots wählen mit erweiterten Regeln auch
Sylvara und kaufen Helden zurück. Netz überträgt Regeln und neue Zustände.
HUD: Einkommen, Kasernen-Symbol, Rückkauf, Segen im Teamkopf.
Kern 531/531 (s1 31), EditMode 31/31, PlayMode 16/16, Verify 718/0,
Selbsttest 30/30, Golden 0 Abweichungen, Fingerabdruck unverändert.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 12:18:47 +02:00

119 lines
5.1 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System.Collections.Generic;
using Luneraria.Core;
using UnityEngine;
namespace Luneraria.View
{
/// <summary>
/// Hosts a match in the scene (U2C/U2D; human seat and tempo since U3). Update only hands frame
/// time (times the speed) to the runner's fixed clock; it never decides anything in combat.
/// Seed, start round and extra gold are the public fixtures of the golden scenarios. With
/// HumanSlot &gt;= 0 the HUD plans that seat and planning waits for its Ready; tempo steps
/// (1×, 2×, 4×) and pause act only in combat, as in Godot (lune_game.gd).
/// </summary>
public sealed class LuneMatchDriver : MonoBehaviour
{
/// <summary>Tempo steps of the HUD's tempo button.</summary>
public static readonly int[] SpeedSteps = { 1, 2, 4 };
/// <summary>
/// True: the scene's match starts at once, without the start screen, as before U4. Tests
/// set it before they load the scene; the benchmark (-luneBenchmark) skips the start screen
/// by itself. False (the game): the start screen (Luneraria.UI.LuneNetPlay) starts matches.
/// </summary>
public static bool DirectStart;
public int Seed = 2;
public int StartRound = 1;
public int ExtraGold;
public string Profile = "";
/// <summary>Optional Bünde of the six seats (fixtures like Godot's start option "bunds"); empty seats follow the seed.</summary>
[System.NonSerialized] public string[] Bunds;
/// <summary>The seat a human plays through the HUD; -1 for a bot match (U2 tests, benchmark).</summary>
public int HumanSlot = -1;
/// <summary>Match speed: simulated seconds per real second (tests run faster than 1).</summary>
public float Speed = 1f;
/// <summary>Index into SpeedSteps (the player's tempo, on top of Speed).</summary>
public int SpeedIndex;
public bool Paused;
public bool RecordTrace = true;
/// <summary>S1: extended rules for matches the driver starts itself (tests, benchmark); the start screen decides for the game.</summary>
public bool Extended;
public LuneMatchRunner Runner { get; private set; }
/// <summary>Main-thread cost of the last frame's simulation (milliseconds).</summary>
public double LastAdvanceMs { get; private set; }
/// <summary>Bound network session (U4); null offline.</summary>
public LuneSession Session;
/// <summary>True on a guest: the host simulates, this side only shows snapshots.</summary>
public bool IsClient;
readonly System.Diagnostics.Stopwatch _clock = new System.Diagnostics.Stopwatch();
public int Tempo { get { return SpeedSteps[Mathf.Clamp(SpeedIndex, 0, SpeedSteps.Length - 1)]; } }
public void Restart()
{
var options = new MatchOptions { Seed = Seed, HumanSlot = HumanSlot, Bunds = Bunds != null ? new List<string>(Bunds) : new List<string>(), Extended = Extended };
Runner = new LuneMatchRunner(options, StartRound, ExtraGold, Profile);
if (RecordTrace)
Runner.Trace = new LuneTrace();
SpeedIndex = 0;
Paused = false;
Session = null;
IsClient = false;
}
/// <summary>
/// Adopts the session's match (U4). The host still advances combat and ticks snapshots;
/// a guest never advances — its match is a display mirror of the host's snapshots.
/// </summary>
public void Adopt(LuneMatch match, int humanSlot, bool isClient, LuneSession session)
{
Session = session;
IsClient = isClient;
Runner = new LuneMatchRunner(match, humanSlot, session);
SpeedIndex = 0;
Paused = false;
}
/// <summary>Next tempo step (1× → 2× → 4× → 1×).</summary>
public void CycleSpeed()
{
SpeedIndex = (SpeedIndex + 1) % SpeedSteps.Length;
}
/// <summary>Pause exists only in combat; planning waits for the player anyway.</summary>
public bool TogglePause()
{
if (Runner == null || Runner.Current != LuneMatchRunner.Stage.Combat)
return false;
Paused = !Paused;
return true;
}
void Awake()
{
if (Runner == null)
Restart();
}
void Update()
{
if (Runner == null)
return;
_clock.Restart();
// A guest shows the host's snapshots and never advances the match.
if (IsClient)
{
LastAdvanceMs = 0.0;
return;
}
// Pause holds only the combat steps (Godot: a paused game still plans and settles).
if (!(Paused && Runner.Current == LuneMatchRunner.Stage.Combat))
Runner.Advance(Time.deltaTime * Speed * Tempo);
if (Session != null && Session.IsNetworked() && Session.IsAuthority())
Session.HostTick(Time.deltaTime * Speed * Tempo);
LastAdvanceMs = _clock.Elapsed.TotalMilliseconds;
}
}
}