using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Luneraria.Core;
using Luneraria.View;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;
namespace Luneraria.UI.Tests
{
///
/// The fourteen G3 motifs (port of tools/luneraria/g3_views.gd) from the real game with the
/// real HUD at one window size. Like Godot, fixtures only set public match state before or
/// between phases (gold, round, building HP, boss claim, the armies of round 14), the starter
/// army comes from the HUD's own button (its toast stays, as in Godot's pictures), further
/// commands go straight to the match followed by the preview rebuild (Godot command() and
/// _rebuild_preview()), and HUD views open through the HUD's public methods. After a click
/// the pointer leaves the HUD, so no tooltip covers a picture. A capture tool, not part of the
/// suite: -testFilter "Luneraria.UI.Tests.HudViewsTests" (all sizes) or one size by its test
/// name. 1920×1080 is written as PNG (the reference set next to Godot's), the other sizes as
/// JPEG to keep the repository small.
///
[Explicit("Bildwerkzeug: nur gezielt per -testFilter")]
public sealed class HudViewsTests
{
/// The Bünde of Godot's g3_views.gd (the human seat 1 plays the Wurzelbund).
static readonly string[] Bunds = { "wurzelbund", "wurzelbund", "glutpakt", "glutpakt", "wurzelbund", "glutpakt" };
int _width;
int _height;
string _folder;
string _extension;
readonly List _shots = new List();
[UnityTest, Timeout(1800000)] public IEnumerator Motifs_1920x1080() { yield return Run(1920, 1080); }
[UnityTest, Timeout(1800000)] public IEnumerator Motifs_2560x1440() { yield return Run(2560, 1440); }
[UnityTest, Timeout(1800000)] public IEnumerator Motifs_1280x720() { yield return Run(1280, 720); }
[UnityTest, Timeout(1800000)] public IEnumerator Motifs_2560x1080() { yield return Run(2560, 1080); }
IEnumerator Run(int width, int height)
{
_width = width;
_height = height;
_folder = "docs/port/screenshots/u3/motive-" + width + "x" + height + "/";
_extension = width == 1920 && height == 1080 ? ".png" : ".jpg";
_shots.Clear();
yield return PlanningViews();
yield return BossViews();
yield return CombatViews();
yield return AncientViews();
Debug.Log("LUNE_VIEWS " + width + "x" + height + ": " + _shots.Count + " " + string.Join(", ", _shots));
Assert.AreEqual(14, _shots.Count, "all fourteen motifs");
}
IEnumerator Fresh(HudSession s)
{
yield return s.Open(_width, _height, seed: 4, bunds: Bunds);
}
IEnumerator Shot(HudSession s, string label)
{
yield return s.Shot(_folder + label + _extension);
_shots.Add(label);
}
static IEnumerator Wait(float seconds)
{
yield return new WaitForSecondsRealtime(seconds);
}
/// Godot command(): a command of the human seat straight to the match, then the preview.
static void Send(HudSession s, Command command)
{
CommandResult result = s.Match.Submit(HudSession.Human, command);
Assert.IsTrue(result.Ok, command.Type + ": " + result.Message);
s.Game.RebuildPreview();
s.Sync();
}
///
/// Godot armies(): the given seats plan round n with plenty of gold (profile "ausgewogen"),
/// grotto orders go to the own lane; the human keeps an ordinary amount afterwards.
///
static void Armies(HudSession s, int round)
{
LuneMatch match = s.Match;
match.RoundNumber = round;
for (int slot = 0; slot < 6; slot++)
{
Draft draft = match.Players[slot].Draft;
draft.Roster.Clear();
draft.Gold = 100000;
LuneBot.Plan(match, slot, "ausgewogen");
foreach (RosterEntry entry in draft.Roster)
{
if (entry.Lane == LuneMatch.Grotto)
entry.Lane = match.Players[slot].Lane;
}
if (draft.Hero != null && draft.Hero.Lane == LuneMatch.Grotto)
draft.Hero.Lane = match.Players[slot].Lane;
}
match.Players[HudSession.Human].Draft.Gold = 185;
}
// ---------------------------------------------------------------- planning
IEnumerator PlanningViews()
{
var s = new HudSession();
yield return Fresh(s);
// A normal planning round: round 3, starter army bought, some gold left.
s.Match.RoundNumber = 3;
s.Me.Draft.Gold = 420;
yield return s.Starter();
s.Leave();
yield return Wait(.4f);
Assert.AreEqual(3, s.Model.OwnRoster.Count, "starter army");
yield return Shot(s, "01-planung");
// Hero hired and shown.
s.Me.Draft.Gold = 900;
Send(s, Command.HeroBuy("held_ringborke"));
s.Hud.OpenHero();
yield return Wait(.4f);
yield return Shot(s, "02-held-ausgewaehlt");
// Item choice with one item equipped.
Send(s, Command.ItemBuy("glutklinge", s.Me.Draft.Hero.Uid));
s.Hud.OpenItems();
yield return Wait(.3f);
yield return Shot(s, "03-itemauswahl");
s.Hud.CloseModal();
// A normal unit selected.
s.Hud.SelectUnit(s.Model.OwnRoster.First().Uid);
yield return Wait(.3f);
yield return Shot(s, "04-einheit-ausgewaehlt");
s.Hud.ClearSelection();
// Attack champions: one booked for the next round.
Send(s, Command.ChampionBook("ch_belagerung", "mid"));
s.Hud.OpenChampions();
yield return Wait(.3f);
yield return Shot(s, "05-angriffschampion");
s.Close();
}
// ---------------------------------------------------------------- boss
IEnumerator BossViews()
{
var s = new HudSession();
yield return Fresh(s);
s.Match.RoundNumber = 10;
s.Match.Boss.NextAvailableRound = 10;
s.Game.FocusTarget("grotto");
s.Sync();
yield return Wait(1f);
yield return Shot(s, "06-boss-verfuegbar");
// The own team conquered the boss in round 11: deployment in round 12 on Mid, planning of 12.
s.Match.RoundNumber = 12;
s.Match.Boss.Claim = new BossClaim { Team = 0, WonRound = 11, DeployRound = 12, ChooserSlot = HudSession.Human, Lane = "mid" };
s.Match.Boss.NextAvailableRound = 15;
s.Game.FocusLane("mid", true);
s.Sync();
yield return Wait(1f);
yield return Shot(s, "07-boss-angekuendigt");
s.Close();
}
// ---------------------------------------------------------------- combat
IEnumerator CombatViews()
{
var s = new HudSession();
yield return Fresh(s);
Armies(s, 14);
s.Match.BuildingHp["rot_top_outer"] = 0.0;
s.Match.BuildingHp["blau_bot_outer"] = 0.0;
s.Game.Key(KeyCode.Space);
yield return HudSession.Frames(1);
Assert.AreEqual(LuneMatchRunner.Stage.Combat, s.Runner.Current, "combat of round 14");
// Godot advance_combat(22): the clock runs to 22 s, then holds.
yield return s.Fight(22.0);
s.Driver.Paused = true;
foreach (var spec in new[] { ("08-kampf-top", "top"), ("09-kampf-mid", "mid"), ("10-kampf-bottom", "bot") })
{
s.Game.FocusLane(spec.Item2, true);
yield return Wait(.5f);
yield return Shot(s, spec.Item1);
}
Vec2 ruin = s.Match.Map.BuildingDef("blau_bot_outer").Position;
s.Rig.LookAt(ruin, 22f, true);
yield return Wait(.5f);
yield return Shot(s, "13-turm-zerstoert");
s.Rig.LookAt(Vec2.Zero, 46f, true);
yield return Wait(.5f);
yield return Shot(s, "14-grosse-kampfansicht");
s.Close();
}
// ---------------------------------------------------------------- Ancients
IEnumerator AncientViews()
{
var s = new HudSession();
yield return Fresh(s);
s.Match.RoundNumber = 21;
foreach (string id in new[] { "blau_mid_outer", "blau_mid_inner", "blau_base_1", "blau_base_2" })
s.Match.BuildingHp[id] = 0.0;
s.Match.BuildingHp["blau_ancient"] = s.Match.Map.BuildingDef("blau_ancient").MaxHp * .22;
s.Game.FocusTarget("base");
s.Sync();
yield return Wait(1.2f);
yield return Shot(s, "11-eigener-ancient-kritisch");
s.Close();
s = new HudSession();
yield return Fresh(s);
s.Match.RoundNumber = 137;
foreach (string id in new[] { "rot_top_outer", "rot_top_inner", "rot_base_1", "rot_base_2" })
s.Match.BuildingHp[id] = 0.0;
s.Match.BuildingHp["rot_ancient"] = s.Match.Map.BuildingDef("rot_ancient").MaxHp * .18;
s.Rig.LookAt(s.Match.Map.CorePosition(1), 26f, true);
s.Sync();
yield return Wait(1.2f);
yield return Shot(s, "12-gegnerischer-ancient-kritisch");
s.Close();
}
}
}