736 lines
30 KiB
C#
736 lines
30 KiB
C#
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using System.Reflection;
|
||
using NUnit.Framework;
|
||
using UnityEngine;
|
||
using UnityEngine.EventSystems;
|
||
using UnityEngine.UI;
|
||
using Vanguard.Api;
|
||
using Vanguard.Api.Contracts;
|
||
using Vanguard.App;
|
||
using Vanguard.Auth;
|
||
using Vanguard.Board3D;
|
||
using Vanguard.Meta;
|
||
using Vanguard.Shared.Protocol;
|
||
using Vanguard.State;
|
||
using Vanguard.UI;
|
||
using BattleView = Vanguard.UI.BattleView;
|
||
|
||
namespace Vanguard.Tests
|
||
{
|
||
public sealed class ClientSmokeTests
|
||
{
|
||
[Test]
|
||
public void Client_Views_Build()
|
||
{
|
||
var config = new AppConfig();
|
||
config.Load();
|
||
|
||
var tokens = new TokenStore();
|
||
var api = new ApiClient(config, tokens);
|
||
var player = new PlayerState();
|
||
var auth = new AuthService(api, tokens, player);
|
||
var meta = new MetaService(api, player);
|
||
|
||
var canvas = UiKit.CreateCanvas("Test Canvas");
|
||
|
||
var login = new LoginView(canvas.transform, config, auth, () => { });
|
||
login.Show("smoke");
|
||
|
||
var lobby = new MetaView(
|
||
canvas.transform, auth, player, () => { },
|
||
() => { }, () => { }, () => { }, () => { }, () => { });
|
||
var onboarding = new OnboardingView(
|
||
canvas.transform, auth, meta, () => { }, () => { });
|
||
var decks = new DeckBuilderView(
|
||
canvas.transform, meta, auth, () => { }, _ => { }, _ => { });
|
||
var queue = new QueueView(
|
||
canvas.transform, meta, auth, () => { }, _ => { });
|
||
var store = new StoreView(
|
||
canvas.transform, meta, auth, player, () => { });
|
||
var history = new HistoryView(
|
||
canvas.transform, meta, auth, () => { });
|
||
var battle = new BattleView(
|
||
canvas.transform, meta, () => { });
|
||
|
||
Assert.IsNotNull(GameObject.Find("LoginScreen"));
|
||
Assert.IsTrue(Exists("MetaScreen"));
|
||
Assert.IsTrue(Exists("OnboardingScreen"));
|
||
Assert.IsTrue(Exists("DeckBuilderScreen"));
|
||
Assert.IsTrue(Exists("StoreScreen"));
|
||
Assert.IsTrue(Exists("HistoryScreen"));
|
||
Assert.IsTrue(Exists("ReplayScreen"));
|
||
Assert.IsTrue(Exists("QueueScreen"));
|
||
Assert.IsTrue(Exists("BattleScreen"));
|
||
|
||
Assert.IsNotNull(
|
||
Resources.Load<Texture2D>("CardArt/td01_005"),
|
||
"TD01 card art is not imported");
|
||
var standardBack = Resources.Load<Texture2D>("CardArt/vanguard_back");
|
||
Assert.IsNotNull(standardBack, "Standard Vanguard card back is not imported");
|
||
Assert.AreEqual(standardBack, CardArt.Back.texture);
|
||
|
||
var artGo = new GameObject("ArtProbe", typeof(RectTransform), typeof(Image));
|
||
var artImage = artGo.GetComponent<Image>();
|
||
CardImageService.Apply(artImage, "td01_005", null);
|
||
Assert.IsNotNull(artImage.sprite, "CardImageService did not load real card art");
|
||
Object.DestroyImmediate(artGo);
|
||
|
||
// После смены сцены старые views не должны реагировать на PlayerState.
|
||
lobby.Dispose();
|
||
store.Dispose();
|
||
player.Apply(new MeResponse
|
||
{
|
||
UserName = "after-dispose",
|
||
Gold = 10,
|
||
Dust = 2,
|
||
Tokens = 1,
|
||
});
|
||
|
||
Cleanup();
|
||
}
|
||
|
||
[Test]
|
||
public void Localization_Translates_Static_And_Dynamic_Strings()
|
||
{
|
||
Loc.SetLanguage(UiLanguage.Ru);
|
||
Assert.AreEqual("Коллекция", Loc.Translate("Коллекция"));
|
||
|
||
Loc.SetLanguage(UiLanguage.En);
|
||
Assert.AreEqual("Collection", Loc.Translate("Коллекция"));
|
||
Assert.AreEqual("Grade: all", Loc.Translate("Ранг: все"));
|
||
Assert.AreEqual("Cards in collection: 12", Loc.Translate("Карт в коллекции: 12"));
|
||
Assert.AreEqual("Victory!", Loc.Translate("Победа!"));
|
||
Assert.AreEqual("Sentinel: yes · Boost: no", Loc.Translate("Sentinel: да · Boost: нет"));
|
||
|
||
Loc.SetLanguage(UiLanguage.Ru);
|
||
|
||
var go = new GameObject("LocalizedLabel", typeof(RectTransform), typeof(Text));
|
||
var label = go.GetComponent<Text>();
|
||
var localized = go.AddComponent<LocalizedText>();
|
||
localized.Initialize("Коллекция");
|
||
Assert.AreEqual("Коллекция", label.text);
|
||
|
||
Loc.SetLanguage(UiLanguage.En);
|
||
Assert.AreEqual("Collection", label.text);
|
||
|
||
label.text = "Магазин";
|
||
localized.ApplyNow();
|
||
Assert.AreEqual("Store", label.text);
|
||
|
||
Loc.SetLanguage(UiLanguage.Ru);
|
||
Assert.AreEqual("Магазин", label.text);
|
||
Object.DestroyImmediate(go);
|
||
}
|
||
|
||
[Test]
|
||
public void Canvas_Reference_Resolution_Is_1920x1080()
|
||
{
|
||
var canvas = UiKit.CreateCanvas("Reference Resolution Canvas");
|
||
try
|
||
{
|
||
var scaler = canvas.GetComponent<CanvasScaler>();
|
||
Assert.IsNotNull(scaler);
|
||
Assert.AreEqual(1920f, scaler.referenceResolution.x);
|
||
Assert.AreEqual(1080f, scaler.referenceResolution.y);
|
||
}
|
||
finally
|
||
{
|
||
Object.DestroyImmediate(canvas.gameObject);
|
||
var eventSystem = Object.FindAnyObjectByType<EventSystem>();
|
||
if (eventSystem != null)
|
||
Object.DestroyImmediate(eventSystem.gameObject);
|
||
}
|
||
}
|
||
|
||
[Test]
|
||
public void Battle_Layout_Fits_Reference_Resolution()
|
||
{
|
||
var config = new AppConfig();
|
||
config.Load();
|
||
var tokens = new TokenStore();
|
||
var api = new ApiClient(config, tokens);
|
||
var player = new PlayerState();
|
||
var auth = new AuthService(api, tokens, player);
|
||
var meta = new MetaService(api, player);
|
||
var canvas = UiKit.CreateCanvas("Layout Test Canvas");
|
||
|
||
try
|
||
{
|
||
var battle = new BattleView(canvas.transform, meta, () => { });
|
||
var screen = Resources.FindObjectsOfTypeAll<GameObject>()
|
||
.First(go => go.name == "BattleScreen");
|
||
screen.SetActive(true);
|
||
|
||
var self = ReferenceRect(FindRect("ZoneBlocksSelf"));
|
||
var opponent = ReferenceRect(FindRect("ZoneBlocksOpponent"));
|
||
var phase = ReferenceRect(FindRect("PhasePanel"));
|
||
var stats = ReferenceRect(FindRect("CombatStats"));
|
||
|
||
AssertWithinScreen(self, "self zones");
|
||
AssertWithinScreen(opponent, "opponent zones");
|
||
AssertWithinScreen(phase, "phase panel");
|
||
AssertWithinScreen(stats, "combat stats");
|
||
Assert.IsFalse(self.Overlaps(opponent));
|
||
Assert.IsFalse(self.Overlaps(phase));
|
||
Assert.IsFalse(opponent.Overlaps(phase));
|
||
Assert.IsFalse(opponent.Overlaps(stats));
|
||
Assert.IsFalse(phase.Overlaps(stats));
|
||
|
||
var toggle = Resources.FindObjectsOfTypeAll<Button>()
|
||
.First(button => button.name == "ToggleIntentPanel"
|
||
&& button.gameObject.scene.IsValid());
|
||
toggle.onClick.Invoke();
|
||
|
||
var movedOpponent = ReferenceRect(FindRect("ZoneBlocksOpponent"));
|
||
var movedPhase = ReferenceRect(FindRect("PhasePanel"));
|
||
Assert.Greater(movedOpponent.xMin, opponent.xMin);
|
||
Assert.Greater(movedPhase.xMin, phase.xMin);
|
||
AssertWithinScreen(movedOpponent, "opponent zones after hide");
|
||
AssertWithinScreen(movedPhase, "phase panel after hide");
|
||
}
|
||
finally
|
||
{
|
||
Object.DestroyImmediate(canvas.gameObject);
|
||
Cleanup();
|
||
}
|
||
}
|
||
|
||
[Test]
|
||
public void Mulligan_Finish_Button_Is_Hidden_After_Exchange()
|
||
{
|
||
var config = new AppConfig();
|
||
config.Load();
|
||
var tokens = new TokenStore();
|
||
var api = new ApiClient(config, tokens);
|
||
var player = new PlayerState();
|
||
var auth = new AuthService(api, tokens, player);
|
||
var meta = new MetaService(api, player);
|
||
var canvas = UiKit.CreateCanvas("Mulligan Test Canvas");
|
||
|
||
try
|
||
{
|
||
var battle = new BattleView(canvas.transform, meta, () => { });
|
||
var match = new MatchView
|
||
{
|
||
You = 0,
|
||
ActivePlayer = 0,
|
||
Phase = GamePhase.Mulligan,
|
||
LegalIntents = new List<IntentPayload>
|
||
{
|
||
new FinishMulliganIntent(),
|
||
new ConcedeIntent(),
|
||
},
|
||
};
|
||
|
||
var viewField = typeof(BattleView)
|
||
.GetField("_view", BindingFlags.Instance | BindingFlags.NonPublic)!;
|
||
var usedField = typeof(BattleView)
|
||
.GetField("_mulliganUsed", BindingFlags.Instance | BindingFlags.NonPublic)!;
|
||
var rebuild = typeof(BattleView)
|
||
.GetMethod("RebuildPhaseControls", BindingFlags.Instance | BindingFlags.NonPublic)!;
|
||
|
||
viewField.SetValue(battle, match);
|
||
|
||
usedField.SetValue(battle, true);
|
||
rebuild.Invoke(battle, new object[] { match });
|
||
Assert.IsFalse(
|
||
FindRect("PhasePanel").gameObject.activeSelf,
|
||
"Phase panel must disappear after the mulligan exchange");
|
||
Assert.IsFalse(
|
||
Resources.FindObjectsOfTypeAll<GameObject>()
|
||
.Any(go => go.name == "Phase_FinishMulliganIntent"),
|
||
"Finish mulligan button must not remain after the exchange");
|
||
|
||
usedField.SetValue(battle, false);
|
||
rebuild.Invoke(battle, new object[] { match });
|
||
Assert.IsTrue(
|
||
FindRect("PhasePanel").gameObject.activeSelf,
|
||
"Finish mulligan button must be available before the exchange");
|
||
Assert.IsTrue(
|
||
Resources.FindObjectsOfTypeAll<GameObject>()
|
||
.Any(go => go.name == "Phase_FinishMulliganIntent"));
|
||
}
|
||
finally
|
||
{
|
||
Object.DestroyImmediate(canvas.gameObject);
|
||
Cleanup();
|
||
}
|
||
}
|
||
|
||
[Test]
|
||
public void Optional_Ability_Shows_Overlay_Instead_Of_Action_Panel()
|
||
{
|
||
var config = new AppConfig();
|
||
config.Load();
|
||
var tokens = new TokenStore();
|
||
var api = new ApiClient(config, tokens);
|
||
var player = new PlayerState();
|
||
var auth = new AuthService(api, tokens, player);
|
||
var meta = new MetaService(api, player);
|
||
var canvas = UiKit.CreateCanvas("Ability Test Canvas");
|
||
|
||
try
|
||
{
|
||
var battle = new BattleView(canvas.transform, meta, () => { });
|
||
var match = new MatchView
|
||
{
|
||
You = 0,
|
||
ActivePlayer = 0,
|
||
Ability = new AbilityView
|
||
{
|
||
Player = 0,
|
||
InstanceId = 42,
|
||
PrintingId = "BT01/003EN",
|
||
Optional = true,
|
||
},
|
||
LegalIntents = new List<IntentPayload>
|
||
{
|
||
new ResolveAbilityIntent { Accept = true },
|
||
new ResolveAbilityIntent { Accept = false },
|
||
new ConcedeIntent(),
|
||
},
|
||
};
|
||
|
||
typeof(BattleView)
|
||
.GetField("_view", BindingFlags.Instance | BindingFlags.NonPublic)!
|
||
.SetValue(battle, match);
|
||
typeof(BattleView)
|
||
.GetMethod("SyncAbilityPrompt", BindingFlags.Instance | BindingFlags.NonPublic)!
|
||
.Invoke(battle, null);
|
||
|
||
var overlay = Resources.FindObjectsOfTypeAll<GameObject>()
|
||
.First(go => go.name == "AbilityOverlay");
|
||
Assert.IsTrue(overlay.activeSelf);
|
||
Assert.IsTrue(Resources.FindObjectsOfTypeAll<GameObject>()
|
||
.Any(go => go.name == "Ability_Активировать"));
|
||
Assert.IsTrue(Resources.FindObjectsOfTypeAll<GameObject>()
|
||
.Any(go => go.name == "Ability_Пропустить"));
|
||
|
||
typeof(BattleView)
|
||
.GetMethod("RebuildIntents", BindingFlags.Instance | BindingFlags.NonPublic)!
|
||
.Invoke(battle, new object[] { match });
|
||
Assert.IsFalse(Resources.FindObjectsOfTypeAll<GameObject>()
|
||
.Any(go => go.name == "Intent_ResolveAbilityIntent"));
|
||
|
||
typeof(BattleView)
|
||
.GetMethod("HideAbilityOverlay", BindingFlags.Instance | BindingFlags.NonPublic)!
|
||
.Invoke(battle, null);
|
||
Assert.IsFalse(overlay.activeSelf);
|
||
}
|
||
finally
|
||
{
|
||
Object.DestroyImmediate(canvas.gameObject);
|
||
Cleanup();
|
||
}
|
||
}
|
||
|
||
[Test]
|
||
public void Rear_Guard_Choice_Is_Shown_Without_Buttons()
|
||
{
|
||
var config = new AppConfig();
|
||
config.Load();
|
||
var tokens = new TokenStore();
|
||
var api = new ApiClient(config, tokens);
|
||
var player = new PlayerState();
|
||
var auth = new AuthService(api, tokens, player);
|
||
var meta = new MetaService(api, player);
|
||
var canvas = UiKit.CreateCanvas("Choice Test Canvas");
|
||
|
||
try
|
||
{
|
||
var battle = new BattleView(canvas.transform, meta, () => { });
|
||
var match = new MatchView
|
||
{
|
||
You = 0,
|
||
ActivePlayer = 0,
|
||
Choice = new ChoiceView
|
||
{
|
||
Player = 0,
|
||
Prompt = "Выберите круг для призыва Barcgal",
|
||
Options = new List<ChoiceOptionView>
|
||
{
|
||
new() { Slot = 0, Label = "Круг 1" },
|
||
new() { Slot = 1, Label = "Круг 2" },
|
||
new() { Slot = 2, Label = "Круг 3" },
|
||
new() { Slot = 3, Label = "Круг 4" },
|
||
new() { Slot = 4, Label = "Круг 5" },
|
||
},
|
||
},
|
||
LegalIntents = new List<IntentPayload>(),
|
||
};
|
||
|
||
typeof(BattleView)
|
||
.GetField("_view", BindingFlags.Instance | BindingFlags.NonPublic)!
|
||
.SetValue(battle, match);
|
||
typeof(BattleView)
|
||
.GetMethod("RefreshActionPrompt", BindingFlags.Instance | BindingFlags.NonPublic)!
|
||
.Invoke(battle, null);
|
||
|
||
var prompt = Resources.FindObjectsOfTypeAll<GameObject>()
|
||
.First(go => go.name == "ActionPrompt");
|
||
Assert.IsTrue(prompt.activeSelf);
|
||
var title = Resources.FindObjectsOfTypeAll<Text>()
|
||
.First(text => text.name == "ActionPromptTitle");
|
||
Assert.AreEqual("ВЫБОР КРУГА", title.text);
|
||
Assert.IsFalse(Resources.FindObjectsOfTypeAll<GameObject>()
|
||
.Any(go => go.name == "ChoiceOverlay"));
|
||
Assert.IsFalse(Resources.FindObjectsOfTypeAll<GameObject>()
|
||
.Any(go => go.name == "Choice_3"));
|
||
}
|
||
finally
|
||
{
|
||
Object.DestroyImmediate(canvas.gameObject);
|
||
Cleanup();
|
||
}
|
||
}
|
||
|
||
[Test]
|
||
public void Card_Choice_Shows_Overlay_With_Card_Tiles()
|
||
{
|
||
var config = new AppConfig();
|
||
config.Load();
|
||
var tokens = new TokenStore();
|
||
var api = new ApiClient(config, tokens);
|
||
var player = new PlayerState();
|
||
var auth = new AuthService(api, tokens, player);
|
||
var meta = new MetaService(api, player);
|
||
var canvas = UiKit.CreateCanvas("Card Choice Test Canvas");
|
||
|
||
try
|
||
{
|
||
var battle = new BattleView(canvas.transform, meta, () => { });
|
||
var match = new MatchView
|
||
{
|
||
You = 0,
|
||
ActivePlayer = 0,
|
||
Choice = new ChoiceView
|
||
{
|
||
Player = 0,
|
||
Prompt = "Найдите Future Knight, Llew или Flogal",
|
||
Options = new List<ChoiceOptionView>
|
||
{
|
||
new() { Slot = 0, Label = "Flogal (BT01/046EN)" },
|
||
new() { Slot = 1, Label = "Future Knight, Llew (BT01/045EN)" },
|
||
},
|
||
},
|
||
LegalIntents = new List<IntentPayload>(),
|
||
};
|
||
|
||
typeof(BattleView)
|
||
.GetField("_view", BindingFlags.Instance | BindingFlags.NonPublic)!
|
||
.SetValue(battle, match);
|
||
typeof(BattleView)
|
||
.GetMethod("SyncCardChoicePrompt", BindingFlags.Instance | BindingFlags.NonPublic)!
|
||
.Invoke(battle, null);
|
||
|
||
var overlay = Resources.FindObjectsOfTypeAll<GameObject>()
|
||
.First(go => go.name == "CardChoiceOverlay");
|
||
Assert.IsTrue(overlay.activeSelf);
|
||
Assert.IsTrue(Resources.FindObjectsOfTypeAll<GameObject>()
|
||
.Any(go => go.name == "CardChoice_1"));
|
||
}
|
||
finally
|
||
{
|
||
Object.DestroyImmediate(canvas.gameObject);
|
||
Cleanup();
|
||
}
|
||
}
|
||
|
||
[Test]
|
||
public void Hand_Choice_Uses_Hand_Cards_Without_Intent_Buttons()
|
||
{
|
||
var config = new AppConfig();
|
||
config.Load();
|
||
var tokens = new TokenStore();
|
||
var api = new ApiClient(config, tokens);
|
||
var player = new PlayerState();
|
||
var auth = new AuthService(api, tokens, player);
|
||
var meta = new MetaService(api, player);
|
||
var canvas = UiKit.CreateCanvas("Hand Choice Test Canvas");
|
||
|
||
try
|
||
{
|
||
var battle = new BattleView(canvas.transform, meta, () => { });
|
||
var match = new MatchView
|
||
{
|
||
You = 0,
|
||
ActivePlayer = 0,
|
||
Players = new List<PlayerView>
|
||
{
|
||
new()
|
||
{
|
||
Index = 0,
|
||
Hand = new List<CardView>
|
||
{
|
||
new() { InstanceId = 11, PrintingId = "BT01/046EN" },
|
||
new() { InstanceId = 12, PrintingId = "BT01/042EN" },
|
||
},
|
||
},
|
||
},
|
||
Choice = new ChoiceView
|
||
{
|
||
Player = 0,
|
||
Prompt = "Выберите карту из руки",
|
||
Options = new List<ChoiceOptionView>
|
||
{
|
||
new() { Slot = 0, Label = "Flogal" },
|
||
new() { Slot = 1, Label = "Little Sage, Marron" },
|
||
},
|
||
},
|
||
LegalIntents = new List<IntentPayload>(),
|
||
};
|
||
typeof(BattleView)
|
||
.GetField("_view", BindingFlags.Instance | BindingFlags.NonPublic)!
|
||
.SetValue(battle, match);
|
||
|
||
var isHandChoice = typeof(BattleView)
|
||
.GetMethod("IsHandChoice", BindingFlags.Instance | BindingFlags.NonPublic)!
|
||
.Invoke(battle, null);
|
||
Assert.AreEqual(true, isHandChoice);
|
||
|
||
typeof(BattleView)
|
||
.GetMethod("RefreshActionPrompt", BindingFlags.Instance | BindingFlags.NonPublic)!
|
||
.Invoke(battle, null);
|
||
var title = Resources.FindObjectsOfTypeAll<Text>()
|
||
.First(text => text.name == "ActionPromptTitle");
|
||
Assert.AreEqual("ВЫБОР КАРТЫ", title.text);
|
||
|
||
typeof(BattleView)
|
||
.GetMethod("RebuildIntents", BindingFlags.Instance | BindingFlags.NonPublic)!
|
||
.Invoke(battle, new object[] { match });
|
||
Assert.IsFalse(Resources.FindObjectsOfTypeAll<GameObject>()
|
||
.Any(go => go.name == "Intent_ChooseIntent"));
|
||
}
|
||
finally
|
||
{
|
||
Object.DestroyImmediate(canvas.gameObject);
|
||
Cleanup();
|
||
}
|
||
}
|
||
|
||
[Test]
|
||
public void Action_Prompt_Shows_Current_Required_Action()
|
||
{
|
||
var config = new AppConfig();
|
||
config.Load();
|
||
var tokens = new TokenStore();
|
||
var api = new ApiClient(config, tokens);
|
||
var player = new PlayerState();
|
||
var auth = new AuthService(api, tokens, player);
|
||
var meta = new MetaService(api, player);
|
||
var canvas = UiKit.CreateCanvas("Action Prompt Test Canvas");
|
||
|
||
try
|
||
{
|
||
var battle = new BattleView(canvas.transform, meta, () => { });
|
||
var match = new MatchView
|
||
{
|
||
You = 0,
|
||
ActivePlayer = 0,
|
||
TurnNumber = 2,
|
||
Phase = GamePhase.Ride,
|
||
LegalIntents = new List<IntentPayload>(),
|
||
};
|
||
typeof(BattleView)
|
||
.GetField("_view", BindingFlags.Instance | BindingFlags.NonPublic)!
|
||
.SetValue(battle, match);
|
||
var refresh = typeof(BattleView)
|
||
.GetMethod("RefreshActionPrompt", BindingFlags.Instance | BindingFlags.NonPublic)!;
|
||
refresh.Invoke(battle, null);
|
||
|
||
var prompt = Resources.FindObjectsOfTypeAll<GameObject>()
|
||
.First(go => go.name == "ActionPrompt");
|
||
Assert.IsTrue(prompt.activeSelf);
|
||
var title = Resources.FindObjectsOfTypeAll<Text>()
|
||
.First(text => text.name == "ActionPromptTitle");
|
||
Assert.AreEqual("СМЕНА", title.text);
|
||
|
||
match.Phase = GamePhase.Main;
|
||
refresh.Invoke(battle, null);
|
||
Assert.IsFalse(prompt.activeSelf);
|
||
}
|
||
finally
|
||
{
|
||
Object.DestroyImmediate(canvas.gameObject);
|
||
Cleanup();
|
||
}
|
||
}
|
||
|
||
private static RectTransform FindRect(string name) =>
|
||
Resources.FindObjectsOfTypeAll<GameObject>()
|
||
.Where(go => go.scene.IsValid() && go.name == name)
|
||
.Select(go => go.GetComponent<RectTransform>())
|
||
.First(rect => rect != null);
|
||
|
||
private static Rect ReferenceRect(RectTransform rect)
|
||
{
|
||
var size = rect.sizeDelta;
|
||
var anchor = new Vector2(
|
||
Mathf.Lerp(0f, 1920f, rect.anchorMin.x),
|
||
Mathf.Lerp(0f, 1080f, rect.anchorMin.y));
|
||
return new Rect(
|
||
anchor + rect.anchoredPosition - Vector2.Scale(size, rect.pivot),
|
||
size);
|
||
}
|
||
|
||
private static void AssertWithinScreen(Rect rect, string name)
|
||
{
|
||
Assert.GreaterOrEqual(rect.xMin, 0f, name + " left");
|
||
Assert.GreaterOrEqual(rect.yMin, 0f, name + " bottom");
|
||
Assert.LessOrEqual(rect.xMax, 1920f, name + " right");
|
||
Assert.LessOrEqual(rect.yMax, 1080f, name + " top");
|
||
}
|
||
|
||
[Test]
|
||
public void Battle_Renders_3D_Damage_And_Rested_Cards()
|
||
{
|
||
var config = new AppConfig();
|
||
config.Load();
|
||
var tokens = new TokenStore();
|
||
var api = new ApiClient(config, tokens);
|
||
var player = new PlayerState();
|
||
var auth = new AuthService(api, tokens, player);
|
||
var meta = new MetaService(api, player);
|
||
var canvas = UiKit.CreateCanvas("Test Canvas");
|
||
|
||
var battle = new BattleView(canvas.transform, meta, () => { });
|
||
var screen = Resources.FindObjectsOfTypeAll<GameObject>()
|
||
.First(go => go.name == "BattleScreen");
|
||
screen.SetActive(true);
|
||
|
||
CardView Card(string id, int instance, bool rested = false) => new CardView
|
||
{
|
||
PrintingId = id,
|
||
InstanceId = instance,
|
||
Rested = rested,
|
||
FaceUp = true,
|
||
Power = 8000,
|
||
BasePower = 8000,
|
||
Critical = 1,
|
||
BaseCritical = 1,
|
||
};
|
||
|
||
var self = new PlayerView
|
||
{
|
||
Index = 0,
|
||
Name = "Me",
|
||
Vanguard = Card("BT01/001EN", 1),
|
||
RearGuards = new CardView?[5],
|
||
Hand = new List<CardView> { Card("BT01/042EN", 2) },
|
||
Soul = new List<CardView> { Card("BT01/003EN", 3) },
|
||
FaceDownDamage = 2,
|
||
FaceUpDamage = new List<CardView> { Card("BT01/056EN", 4) },
|
||
DeckCount = 40,
|
||
Drop = new List<CardView>(),
|
||
};
|
||
self.RearGuards[3] = Card("BT01/042EN", 5, rested: true);
|
||
|
||
var opponent = new PlayerView
|
||
{
|
||
Index = 1,
|
||
Name = "Opponent",
|
||
Vanguard = Card("BT01/001EN", 6),
|
||
RearGuards = new CardView?[5],
|
||
Hand = new List<CardView>(),
|
||
Soul = new List<CardView>(),
|
||
FaceDownDamage = 1,
|
||
FaceUpDamage = new List<CardView>(),
|
||
DeckCount = 40,
|
||
Drop = new List<CardView>(),
|
||
};
|
||
|
||
var match = new MatchView
|
||
{
|
||
MatchId = "render-test",
|
||
You = 0,
|
||
ActivePlayer = 0,
|
||
Phase = GamePhase.Main,
|
||
Players = new List<PlayerView> { self, opponent },
|
||
Battle = new Vanguard.Shared.Protocol.BattleView
|
||
{
|
||
GuardCards = new List<CardView> { Card("BT01/011EN", 7) },
|
||
},
|
||
Clock = new ClockView(),
|
||
LegalIntents = new List<IntentPayload>(),
|
||
};
|
||
|
||
Resources.FindObjectsOfTypeAll<GameBoard3D>()
|
||
.FirstOrDefault()
|
||
?.SetVisible(true);
|
||
|
||
typeof(BattleView)
|
||
.GetField("_view", BindingFlags.Instance | BindingFlags.NonPublic)!
|
||
.SetValue(battle, match);
|
||
typeof(BattleView)
|
||
.GetMethod("Render", BindingFlags.Instance | BindingFlags.NonPublic)!
|
||
.Invoke(battle, null);
|
||
|
||
Assert.IsTrue(Exists("GameBoard3D"));
|
||
|
||
var cards = Resources.FindObjectsOfTypeAll<Card3D>()
|
||
.Where(card => card.gameObject.activeInHierarchy)
|
||
.ToList();
|
||
Assert.GreaterOrEqual(cards.Count, 3);
|
||
|
||
var rested = cards.FirstOrDefault(card =>
|
||
card.Ref.Zone == BoardZone.RearGuard
|
||
&& card.Ref.Player == 0
|
||
&& card.Ref.Slot == 3);
|
||
Assert.IsNotNull(rested, "Rested rear-guard was not rendered in 3D");
|
||
Assert.IsTrue(rested.IsRested);
|
||
Assert.AreEqual(90f, Mathf.Abs(Mathf.DeltaAngle(rested.TargetYaw, 0f)), 0.1f);
|
||
|
||
var damage = cards.Where(card =>
|
||
card.Ref.Zone == BoardZone.Damage
|
||
&& card.Ref.Player == 0).ToList();
|
||
Assert.AreEqual(3, damage.Count);
|
||
Assert.IsFalse(damage.Single(card => card.Ref.Index == 2).IsFaceUp);
|
||
Assert.AreEqual(
|
||
-90f,
|
||
Mathf.DeltaAngle(0f, damage.Single(card => card.Ref.Index == 0).TargetYaw),
|
||
0.1f,
|
||
"Damage cards must be turned counter-clockwise");
|
||
|
||
Assert.IsTrue(cards.Any(card =>
|
||
card.Ref.Zone == BoardZone.Guardian
|
||
&& card.Ref.InstanceId == 7));
|
||
|
||
var selfZoneRoot = Resources.FindObjectsOfTypeAll<GameObject>()
|
||
.FirstOrDefault(go => go.name == "ZoneBlocksSelf");
|
||
var opponentZoneRoot = Resources.FindObjectsOfTypeAll<GameObject>()
|
||
.FirstOrDefault(go => go.name == "ZoneBlocksOpponent");
|
||
Assert.IsNotNull(selfZoneRoot);
|
||
Assert.IsNotNull(opponentZoneRoot);
|
||
|
||
var selfLabels = selfZoneRoot.GetComponentsInChildren<Text>();
|
||
var opponentLabels = opponentZoneRoot.GetComponentsInChildren<Text>();
|
||
Assert.IsTrue(selfLabels.Any(text => text.text.Contains("Рука 1")));
|
||
Assert.IsTrue(selfLabels.Any(text => text.text.Contains("Душа 1")));
|
||
Assert.IsTrue(opponentLabels.Any(text => text.text.Contains("Рука 0")));
|
||
|
||
Cleanup();
|
||
}
|
||
|
||
private static bool Exists(string name) =>
|
||
Resources.FindObjectsOfTypeAll<GameObject>().Any(go => go.name == name);
|
||
|
||
private static void Cleanup()
|
||
{
|
||
foreach (var board in Resources.FindObjectsOfTypeAll<GameBoard3D>())
|
||
{
|
||
if (board != null)
|
||
Object.DestroyImmediate(board.gameObject);
|
||
}
|
||
|
||
var canvas = GameObject.Find("Test Canvas");
|
||
if (canvas != null)
|
||
Object.DestroyImmediate(canvas);
|
||
|
||
var eventSystem = Object.FindAnyObjectByType<EventSystem>();
|
||
if (eventSystem != null)
|
||
Object.DestroyImmediate(eventSystem.gameObject);
|
||
}
|
||
}
|
||
}
|