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(); } } }