Files
tiny_tackle_heroes/Assets/_Project/Tests/PlayMode/Defense25DComparisonPresentationPlayModeTests.cs
T
2026-09-27 21:08:05 +09:00

158 lines
7.4 KiB
C#

using System.Collections;
using System.Linq;
using BumpCombat.Combat;
using BumpCombat.Constants;
using BumpCombat.Defense;
using BumpCombat.Defense.Comparison;
using BumpCombat.Player;
using BumpCombat.Progression;
using NUnit.Framework;
using UnityEditor;
using UnityEngine;
using UnityEngine.TestTools;
using Object = UnityEngine.Object;
namespace BumpCombat.Tests
{
public sealed class Defense25DComparisonPresentationPlayModeTests
{
private Defense25DComparison world;
private Defense25DComparisonSettings settings;
private float previousTimeScale;
private const string SettingsPath = "Assets/_Project/Constants/Defense25DComparison/ComparisonSettings.asset";
[UnitySetUp]
public IEnumerator SetUp()
{
previousTimeScale = Time.timeScale;
Time.timeScale = 1f;
settings = Object.Instantiate(AssetDatabase.LoadAssetAtPath<Defense25DComparisonSettings>(SettingsPath));
Assert.That(settings, Is.Not.Null);
settings.Enemy = AssetDatabase.LoadAssetAtPath<BumpCombat.Enemies.EnemyDefinition>(
"Assets/_Project/Constants/Enemies/Slime.asset");
settings.EnemyArt = ReadCharacter();
world = new GameObject("Presentation integration world").AddComponent<Defense25DComparison>();
world.Initialize(settings);
world.AutoSpawn = false;
world.ReadInput = false;
yield return new WaitForFixedUpdate();
}
[UnityTearDown]
public IEnumerator TearDown()
{
if (world != null) Object.Destroy(world.gameObject);
if (settings != null) Object.Destroy(settings);
Time.timeScale = previousTimeScale;
yield return null;
}
[UnityTest]
public IEnumerator BuildCatalogUsesAllSixRealTowerSpritesAndBuildsSelectedTypeAtSelectedPad()
{
Assert.That(world.TowerShowcaseKinds.Count(), Is.EqualTo(6));
foreach (DefenseTowerKind kind in world.TowerShowcaseKinds)
Assert.That(world.GetTowerShowcaseSprite(kind), Is.Not.Null, $"Missing actual idle art for {kind}");
const int pad = 2;
Vector2 position = GameplayConstants.Current.Defense.GetBuildPadPosition(pad);
world.Player.ResetAt(position);
Assert.That(world.SelectBuildKind(DefenseTowerKind.Lightning), Is.True);
Assert.That(world.SelectBuildPad(pad), Is.True);
int before = world.TowerManager.ConstructionCurrency;
Assert.That(world.BuildSelectedTower(), Is.True);
Defense25DTower tower = world.TowerManager.GetTowerAtPad(pad);
Assert.That(tower, Is.Not.Null);
Assert.That(tower.Kind, Is.EqualTo(DefenseTowerKind.Lightning));
Assert.That(world.TowerManager.ConstructionCurrency, Is.LessThan(before));
yield return null;
}
[UnityTest]
public IEnumerator SelectedTowerOffersOnlyLegalTierAndFinalBranchUpgrades()
{
const int pad = 0;
world.Player.ResetAt(GameplayConstants.Current.Defense.GetBuildPadPosition(pad));
world.TowerManager.AddCurrency(1000);
Assert.That(world.SelectBuildPad(pad), Is.True);
Assert.That(world.BuildSelectedTower(), Is.True);
Assert.That(world.UpgradeSelectedTower(DefenseTowerBranch.First), Is.False,
"Branches are only selected when upgrading from tier three.");
Assert.That(world.UpgradeSelectedTower(DefenseTowerBranch.None), Is.True);
Assert.That(world.TowerManager.GetTowerAtPad(pad).Tier, Is.EqualTo(2));
Assert.That(world.UpgradeSelectedTower(DefenseTowerBranch.None), Is.True);
Assert.That(world.TowerManager.GetTowerAtPad(pad).Tier, Is.EqualTo(3));
Assert.That(world.UpgradeSelectedTower(DefenseTowerBranch.Second), Is.True);
Defense25DTower tierFour = world.TowerManager.GetTowerAtPad(pad);
Assert.That(tierFour.Tier, Is.EqualTo(4));
Assert.That(tierFour.Branch, Is.EqualTo(DefenseTowerBranch.Second));
Assert.That(world.UpgradeSelectedTower(DefenseTowerBranch.First), Is.False);
yield return null;
}
[UnityTest]
public IEnumerator RelicCannotBeGrantedOutsideACollectedPortalReward()
{
PassiveRelicId available = world.Loadout.GetAvailableRelics().First();
Assert.That(world.QueueFieldRelicRewardForCollectedPortal(0), Is.False);
Assert.That(world.HasPendingFieldRelicReward, Is.False);
Assert.That(world.ResolveFieldRelicChoice(available), Is.False);
Assert.That(world.Loadout.OwnsRelic(available), Is.False);
Assert.That(world.Paused, Is.False);
yield return null;
}
[UnityTest]
public IEnumerator PresentationPointerRegionsConsumeUiClicksButLeaveArenaPointerFree()
{
Rect viewport = Defense25DView.GetViewport(Screen.width, Screen.height);
float scale = viewport.width / Defense25DView.Width;
Vector2 ScreenPoint(float x, float y) => new(viewport.x + x * scale,
Screen.height - (viewport.y + y * scale));
foreach (Rect control in world.GetPersistentDefenseUiHitRects())
Assert.That(world.IsPointerOverDefenseUI(ScreenPoint(control.center.x, control.center.y)), Is.True,
$"Clicks on persistent HUD control {control} must not also operate the arena.");
Assert.That(world.SelectBuildPad(0), Is.True);
Rect details = world.GetSelectedPadDetailsRect();
Assert.That(details.width, Is.GreaterThan(0f));
Assert.That(world.IsPointerOverDefenseUI(ScreenPoint(details.center.x, details.center.y)), Is.True,
"Clicks in the selected pad's contextual panel must not reach the arena.");
Assert.That(world.IsPointerOverDefenseUI(ScreenPoint(320, 180)), Is.False,
"The central field remains available for aiming and movement.");
yield return null;
}
[UnityTest]
public IEnumerator ShowcaseModeUsesDedicatedEntryAndExitHooks()
{
int entered = 0;
int exited = 0;
world.BindValidationShowcase(() => { entered++; return true; }, () => exited++);
Assert.That(world.EnterValidationShowcase(), Is.True);
Assert.That(world.IsValidationShowcaseActive, Is.True);
Assert.That(world.EnterValidationShowcase(), Is.False);
Assert.That(entered, Is.EqualTo(1));
Assert.That(world.ExitValidationShowcase(), Is.True);
Assert.That(world.IsValidationShowcaseActive, Is.False);
Assert.That(exited, Is.EqualTo(1));
yield return null;
}
private static ComparisonSpriteFrames ReadCharacter()
{
Sprite[] Read(string suffix) => AssetDatabase.LoadAllAssetsAtPath(
$"Assets/_Project/Art/Characters/Slime/Slime_{suffix}.png")
.OfType<Sprite>()
.OrderBy(sprite => int.TryParse(sprite.name[(sprite.name.LastIndexOf('_') + 1)..], out int frame) ? frame : 0)
.ToArray();
return new ComparisonSpriteFrames
{
Idle = Read("Idle"), Walk = Read("Walk"), Attack = Read("Attack"),
Hurt = Read("Hurt"), Death = Read("Death"),
};
}
}
}