using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using Luneraria.Core;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;
namespace Luneraria.UI.Tests
{
///
/// Interactive playtest of the real game (port of tools/luneraria/g3_playtest.gd). It never
/// touches the match state: every action is an input a player gives, at pixel coordinates of
/// the 1920×1080 picture (origin top left). A click reaches the HUD panel as pointer events
/// and the field as LuneGame.ClickField, which ignores points on the HUD, because in the
/// player both see the same mouse; game keys go through LuneGame.Key, the camera keys R, Q,
/// E and O through the rig as in its own key handling. Pointer moves give hover (tooltips).
/// A tester writes command batches and gets a screenshot back after each batch:
/// /tmp/lune_play/cmd-N.txt lines: click X Y · move X Y · key NAME · wait SECONDS · shot NAME · quit
/// /tmp/lune_play/out-N.png the screen after the batch; /tmp/lune_play/ok-N holds round and phase
/// The log of all commands with round and phase goes to docs/port/logs/u3/playtest-log.txt.
/// There is no main menu in the port yet, so the playtest starts in the match of the G1 scene
/// (seed of the scene, 2; LUNE_PLAYTEST_SEED overrides). It ends with quit or after 30 idle minutes.
/// Run: -testFilter "Luneraria.UI.Tests.HudPlaytest".
///
[Explicit("Spieltest: wartet auf Befehlsdateien")]
public sealed class HudPlaytest
{
const string Dir = "/tmp/lune_play/";
const string LogFile = "docs/port/logs/u3/playtest-log.txt";
HudSession _s;
readonly List _log = new List();
float _started;
bool _quit;
[UnityTest, Timeout(int.MaxValue)]
public IEnumerator Play()
{
Directory.CreateDirectory(Dir);
foreach (string file in Directory.GetFiles(Dir))
File.Delete(file);
int seed = int.TryParse(Environment.GetEnvironmentVariable("LUNE_PLAYTEST_SEED"), out int value) ? value : 2;
_s = new HudSession();
yield return _s.Open(1920, 1080, seed: seed);
_started = Time.realtimeSinceStartup;
yield return new WaitForSecondsRealtime(.5f);
_s.Capture(Dir + "out-0.png");
File.WriteAllText(Dir + "ok-0", Status());
int next = 1;
float idle = 0f;
while (!_quit && idle < 1800f)
{
string path = Dir + "cmd-" + next + ".txt";
if (!File.Exists(path))
{
idle += Time.unscaledDeltaTime;
yield return null;
continue;
}
idle = 0f;
foreach (string raw in File.ReadAllLines(path))
{
string line = raw.Trim();
if (line.Length == 0 || line.StartsWith("#", StringComparison.Ordinal))
continue;
Write(next, line);
yield return Command(line.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries));
if (_quit)
break;
}
yield return new WaitForSecondsRealtime(.35f);
_s.Capture(Dir + "out-" + next + ".png");
File.WriteAllText(Dir + "ok-" + next, Status());
next++;
}
_s.Close();
Assert.IsTrue(_quit, "the playtest ended with quit");
}
string Status()
{
LuneMatch match = _s.Match;
string phase = match.Phase == Phase.Planning ? "PLANUNG" : match.Phase == Phase.Combat ? "KAMPF" : "ENDE";
return "round=" + match.RoundNumber + " phase=" + phase;
}
void Write(int index, string line)
{
_log.Add(string.Format(CultureInfo.InvariantCulture, "{0,7:0.0} s #{1} [{2}] {3}", Time.realtimeSinceStartup - _started, index, Status(), line));
string file = Path.Combine(HudSession.Root, LogFile);
Directory.CreateDirectory(Path.GetDirectoryName(file));
File.WriteAllText(file, string.Join("\n", _log) + "\n");
}
IEnumerator Command(string[] parts)
{
switch (parts[0])
{
case "click":
{
Vector2 pixel = Pixel(parts);
Vector2 at = _s.Hud.PixelToPanel(pixel);
_s.MoveTo(at);
yield return null;
_s.Pointer(at, EventType.MouseDown);
// The field sees the same press (LuneGame.HandleClicks); it ignores points on the HUD.
_s.Game.ClickField(new Vector2(pixel.x, _s.Height - pixel.y));
yield return null;
_s.Pointer(at, EventType.MouseUp);
yield return null;
break;
}
case "move":
_s.MoveTo(_s.Hud.PixelToPanel(Pixel(parts)));
yield return null;
break;
case "key":
Key(parts[1].ToUpperInvariant());
yield return null;
break;
case "wait":
yield return new WaitForSecondsRealtime(float.Parse(parts[1], CultureInfo.InvariantCulture));
break;
case "shot":
yield return null;
_s.Capture(Dir + parts[1] + ".png");
break;
case "quit":
_quit = true;
break;
default:
Debug.LogWarning("LUNE_PLAYTEST unknown command: " + string.Join(" ", parts));
break;
}
}
static Vector2 Pixel(string[] parts)
{
return new Vector2(float.Parse(parts[1], CultureInfo.InvariantCulture), float.Parse(parts[2], CultureInfo.InvariantCulture));
}
void Key(string name)
{
// Camera keys of the rig (LuneCameraRig.HandleInput), blocked while the options are open.
if (name == "R" || name == "Q" || name == "E" || name == "O")
{
if (_s.Hud.Settings.Open)
return;
if (name == "R")
_s.Rig.ResetRequested?.Invoke();
else if (name == "O")
_s.Rig.Overview();
else
_s.Rig.RotateStep(name == "Q" ? -1 : 1);
return;
}
KeyCode code;
if (name.Length == 1 && char.IsDigit(name[0]))
code = KeyCode.Alpha0 + (name[0] - '0');
else if (name == "ESC")
code = KeyCode.Escape;
else
code = (KeyCode)Enum.Parse(typeof(KeyCode), name, true);
_s.Game.Key(code);
}
}
}