211 lines
10 KiB
C#
211 lines
10 KiB
C#
using System.Collections;
|
|
using BumpCombat.Combat;
|
|
using BumpCombat.Core;
|
|
using BumpCombat.Player;
|
|
using BumpCombat.Progression;
|
|
using BumpCombat.Spawning;
|
|
using NUnit.Framework;
|
|
using UnityEngine;
|
|
using UnityEngine.SceneManagement;
|
|
using UnityEngine.TestTools;
|
|
|
|
namespace BumpCombat.PlayModeTests
|
|
{
|
|
public sealed class GuardVisualTests
|
|
{
|
|
[Test]
|
|
public void GuardMasks_CoverAllSelectedColorAnimationFrames()
|
|
{
|
|
string[] sheets = { "Idle", "Walk", "Dash", "Hurt", "Death",
|
|
"BumpAttack-v1", "BackBumpAttack-v2", "ArtifactUse-v1" };
|
|
foreach (string sheet in sheets)
|
|
{
|
|
Sprite[] masks = Resources.LoadAll<Sprite>("Combat/Guard-v1/Swordsman_" + sheet + "-outline");
|
|
Assert.That(masks.Length, Is.GreaterThan(0), sheet);
|
|
foreach (string color in new[] { "Green", "Red", "Blue" })
|
|
{
|
|
Sprite[] frames = Resources.LoadAll<Sprite>("Artifacts/ThreeColor-v1/Player/Swordsman_" + sheet + "-" + color + "-v1");
|
|
Assert.That(masks.Length, Is.EqualTo(frames.Length), sheet + color);
|
|
foreach (Sprite frame in frames)
|
|
{
|
|
Sprite mask = System.Array.Find(masks, item => item.rect == frame.rect);
|
|
Assert.That(mask, Is.Not.Null, frame.name);
|
|
Assert.That(mask.pivot, Is.EqualTo(frame.pivot));
|
|
Assert.That(mask.pixelsPerUnit, Is.EqualTo(frame.pixelsPerUnit));
|
|
Assert.That(mask.texture.filterMode, Is.EqualTo(FilterMode.Point));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
[UnityTest]
|
|
public IEnumerator GuardOutline_FollowsFrameFlipPauseAndLifetime()
|
|
{
|
|
ArtifactRewardController.GrantCatalogForTests = true;
|
|
RunManager.ForceProductionModeForTests = false;
|
|
Time.timeScale = 1f;
|
|
yield return SceneManager.LoadSceneAsync("CombatPrototype");
|
|
yield return null;
|
|
yield return null;
|
|
Object.FindAnyObjectByType<SpawnDirector>().enabled = false;
|
|
var health = Object.FindAnyObjectByType<PlayerHealth>();
|
|
var visual = health.GetComponent<ArtifactChargeVisual>();
|
|
var body = health.GetComponent<SpriteRenderer>();
|
|
Assert.That(health.TryActivateGuard(), Is.True);
|
|
visual.RefreshEquipmentSprite();
|
|
visual.RefreshGuardOutline();
|
|
var outline = health.transform.Find("Player Guard Outline").GetComponent<SpriteRenderer>();
|
|
Assert.That(outline.enabled, Is.True);
|
|
float guardedHealth = health.CurrentHealth;
|
|
Assert.That(health.TryTakeDamage(25f, Vector2.right, 1.1f), Is.False);
|
|
Assert.That(health.CurrentHealth, Is.EqualTo(guardedHealth));
|
|
Assert.That(health.IsHurtMovementLocked, Is.False);
|
|
Assert.That(outline.sprite.rect, Is.EqualTo(body.sprite.rect));
|
|
Assert.That(outline.sprite.pivot, Is.EqualTo(body.sprite.pivot));
|
|
Assert.That(outline.sprite.pixelsPerUnit, Is.EqualTo(body.sprite.pixelsPerUnit));
|
|
body.flipX = true;
|
|
body.sortingOrder = 123;
|
|
visual.RefreshGuardOutline();
|
|
Assert.That(outline.flipX, Is.True);
|
|
Assert.That(outline.sortingOrder, Is.EqualTo(124));
|
|
Time.timeScale = 0f;
|
|
yield return new WaitForSecondsRealtime(0.6f);
|
|
Assert.That(outline.enabled, Is.True);
|
|
Assert.That(health.IsGuarding, Is.True);
|
|
body.enabled = false;
|
|
visual.RefreshGuardOutline();
|
|
Assert.That(outline.enabled, Is.False);
|
|
body.enabled = true;
|
|
visual.RefreshGuardOutline();
|
|
Assert.That(outline.enabled, Is.True);
|
|
visual.enabled = false;
|
|
Assert.That(outline.enabled, Is.False);
|
|
visual.enabled = true;
|
|
visual.RefreshGuardOutline();
|
|
Assert.That(outline.enabled, Is.True);
|
|
Time.timeScale = 1f;
|
|
yield return new WaitForSeconds(health.GuardDuration + 0.05f);
|
|
visual.RefreshGuardOutline();
|
|
Assert.That(outline.enabled, Is.False);
|
|
Assert.That(health.IsGuarding, Is.False);
|
|
Assert.That(health.TryTakeDamage(1f, Vector2.zero, 0f), Is.True);
|
|
Assert.That(health.CurrentHealth, Is.LessThan(guardedHealth));
|
|
}
|
|
|
|
[UnityTest]
|
|
public IEnumerator GuardBlockImpact_FollowsPlayerPausesAndCleansUp()
|
|
{
|
|
ArtifactRewardController.GrantCatalogForTests = true;
|
|
RunManager.ForceProductionModeForTests = false;
|
|
Time.timeScale = 1f;
|
|
yield return SceneManager.LoadSceneAsync("CombatPrototype");
|
|
yield return null;
|
|
yield return null;
|
|
Object.FindAnyObjectByType<SpawnDirector>().enabled = false;
|
|
PlayerHealth health = Object.FindAnyObjectByType<PlayerHealth>();
|
|
CombatFeedback feedback = health.GetComponent<CombatFeedback>();
|
|
SpriteRenderer playerRenderer = health.GetComponent<SpriteRenderer>();
|
|
Collider2D playerCollider = health.GetComponent<Collider2D>();
|
|
Assert.That(feedback, Is.Not.Null);
|
|
Assert.That(playerRenderer, Is.Not.Null);
|
|
Assert.That(playerCollider, Is.Not.Null);
|
|
Sprite impactSprite = Resources.Load<Sprite>(
|
|
CombatFeedback.GetGuardBlockImpactResourcePath());
|
|
Assert.That(impactSprite, Is.Not.Null);
|
|
Assert.That(health.TryActivateGuard(), Is.True);
|
|
|
|
Vector2 expectedPosition = (Vector2)playerCollider.bounds.center
|
|
+ Vector2.left * 0.35f;
|
|
Assert.That(
|
|
health.TryTakeDamage(5f, Vector2.right * 3f, 0.75f),
|
|
Is.False);
|
|
Assert.That(feedback.ActiveGuardBlockImpactVisualCount, Is.EqualTo(1));
|
|
Transform impact = health.transform.Find("Player Guard Block Impact");
|
|
Assert.That(impact, Is.Not.Null);
|
|
Assert.That(impact.parent, Is.EqualTo(health.transform));
|
|
Assert.That(
|
|
Vector2.Distance((Vector2)impact.position, expectedPosition),
|
|
Is.LessThan(0.001f));
|
|
SpriteRenderer impactRenderer = impact.GetComponent<SpriteRenderer>();
|
|
Assert.That(impactRenderer.sprite, Is.EqualTo(impactSprite));
|
|
Assert.That(impactRenderer.color, Is.EqualTo(Color.white));
|
|
Assert.That(
|
|
impactRenderer.sortingOrder,
|
|
Is.EqualTo(playerRenderer.sortingOrder + 20));
|
|
|
|
Assert.That(health.TryTakeDamage(5f, Vector2.left, 0.75f), Is.False);
|
|
Assert.That(feedback.ActiveGuardBlockImpactVisualCount, Is.EqualTo(1),
|
|
"A second impact inside the 0.08-second interval must be suppressed.");
|
|
|
|
yield return new WaitForSeconds(0.09f);
|
|
Assert.That(health.TryTakeDamage(5f, Vector2.zero, 0.75f), Is.False);
|
|
Assert.That(feedback.ActiveGuardBlockImpactVisualCount, Is.EqualTo(2));
|
|
Transform centeredImpact = null;
|
|
foreach (Transform child in health.GetComponentsInChildren<Transform>(true))
|
|
{
|
|
if (child.name == "Player Guard Block Impact"
|
|
&& Vector2.Distance(
|
|
(Vector2)child.position,
|
|
(Vector2)playerCollider.bounds.center) < 0.001f)
|
|
{
|
|
centeredImpact = child;
|
|
break;
|
|
}
|
|
}
|
|
Assert.That(centeredImpact, Is.Not.Null,
|
|
"A zero attack direction must place the impact at the body center.");
|
|
|
|
Vector3 initialImpactPosition = impact.position;
|
|
health.transform.position += Vector3.up * 0.25f;
|
|
Assert.That(impact.position, Is.EqualTo(initialImpactPosition + Vector3.up * 0.25f));
|
|
|
|
Time.timeScale = 0f;
|
|
yield return new WaitForSecondsRealtime(0.22f);
|
|
Assert.That(feedback.ActiveGuardBlockImpactVisualCount, Is.EqualTo(2),
|
|
"The impact lifetime must pause with game time.");
|
|
feedback.enabled = false;
|
|
Assert.That(feedback.ActiveGuardBlockImpactVisualCount, Is.Zero);
|
|
|
|
feedback.enabled = true;
|
|
Time.timeScale = 1f;
|
|
yield return new WaitForSeconds(0.09f);
|
|
Assert.That(health.TryTakeDamage(5f, Vector2.right, 0.75f), Is.False);
|
|
Assert.That(feedback.ActiveGuardBlockImpactVisualCount, Is.EqualTo(1));
|
|
yield return new WaitForSeconds(0.2f);
|
|
Assert.That(feedback.ActiveGuardBlockImpactVisualCount, Is.Zero);
|
|
}
|
|
|
|
[UnityTest]
|
|
public IEnumerator GuardBlockImpact_ClearsWhenPlayerDies()
|
|
{
|
|
ArtifactRewardController.GrantCatalogForTests = true;
|
|
RunManager.ForceProductionModeForTests = false;
|
|
Time.timeScale = 1f;
|
|
yield return SceneManager.LoadSceneAsync("CombatPrototype");
|
|
yield return null;
|
|
yield return null;
|
|
Object.FindAnyObjectByType<SpawnDirector>().enabled = false;
|
|
PlayerHealth health = Object.FindAnyObjectByType<PlayerHealth>();
|
|
CombatFeedback feedback = health.GetComponent<CombatFeedback>();
|
|
Assert.That(health.TryActivateGuard(), Is.True);
|
|
Assert.That(health.TryTakeDamage(1f, Vector2.right, 0.75f), Is.False);
|
|
Assert.That(feedback.ActiveGuardBlockImpactVisualCount, Is.EqualTo(1));
|
|
|
|
health.enabled = false;
|
|
Assert.That(
|
|
health.TryTakeDamage(health.CurrentHealth, Vector2.right, 0f),
|
|
Is.True);
|
|
Assert.That(health.CurrentHealth, Is.Zero);
|
|
Assert.That(feedback.ActiveGuardBlockImpactVisualCount, Is.Zero);
|
|
}
|
|
|
|
[TearDown]
|
|
public void TearDown()
|
|
{
|
|
ArtifactRewardController.GrantCatalogForTests = false;
|
|
RunManager.ForceProductionModeForTests = false;
|
|
Time.timeScale = 1f;
|
|
}
|
|
}
|
|
}
|