2.5d test
This commit is contained in:
@@ -0,0 +1,172 @@
|
||||
using System.Collections;
|
||||
using System.Reflection;
|
||||
using BumpCombat.Combat;
|
||||
using BumpCombat.Constants;
|
||||
using BumpCombat.Defense.Comparison;
|
||||
using BumpCombat.Enemies;
|
||||
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 Defense25DRelicReplayPlayModeTests
|
||||
{
|
||||
private const string SettingsPath = "Assets/_Project/Constants/Defense25DComparison/ComparisonSettings.asset";
|
||||
private GameplayConstants originalConstants;
|
||||
private GameplayConstants testConstants;
|
||||
private PassiveRelicConstants testRelicTuning;
|
||||
private Defense25DComparisonSettings settings;
|
||||
private Defense25DComparison world;
|
||||
private Defense25DEnemy enemy;
|
||||
private ActiveArtifactDefinition phoenix;
|
||||
private int ordinaryHits;
|
||||
private int replayHits;
|
||||
private int ordinaryHitFrame = -1;
|
||||
private int replayHitFrame = -1;
|
||||
private float previousTimeScale;
|
||||
|
||||
[UnitySetUp]
|
||||
public IEnumerator SetUp()
|
||||
{
|
||||
previousTimeScale = Time.timeScale;
|
||||
Time.timeScale = 1f;
|
||||
ordinaryHits = replayHits = 0;
|
||||
ordinaryHitFrame = replayHitFrame = -1;
|
||||
originalConstants = GameplayConstants.Current;
|
||||
testConstants = Object.Instantiate(originalConstants);
|
||||
testRelicTuning = Object.Instantiate(originalConstants.PassiveRelics);
|
||||
testRelicTuning.ResonanceEchoChance = 1f;
|
||||
testRelicTuning.ResonantCrystalChance = 1f;
|
||||
testRelicTuning.ResonantCrystalDelaySeconds = .1f;
|
||||
typeof(GameplayConstants).GetField("passiveRelics", BindingFlags.Instance | BindingFlags.NonPublic)
|
||||
.SetValue(testConstants, testRelicTuning);
|
||||
GameplayConstants.SetCurrentForTests(testConstants);
|
||||
|
||||
settings = Object.Instantiate(AssetDatabase.LoadAssetAtPath<Defense25DComparisonSettings>(SettingsPath));
|
||||
Assert.That(settings, Is.Not.Null);
|
||||
world = new GameObject("Relic replay integration world").AddComponent<Defense25DComparison>();
|
||||
world.Initialize(settings);
|
||||
world.AutoSpawn = false;
|
||||
world.ReadInput = false;
|
||||
world.Player.ResetAt(Vector2.zero);
|
||||
phoenix = world.Loadout.FindArtifact(ActiveArtifactEffect.Phoenix);
|
||||
Assert.That(world.Loadout.TryAddArtifact(phoenix), Is.True);
|
||||
testRelicTuning.ResonantCrystalDelaySeconds = .5f;
|
||||
enemy = world.Enemies[0];
|
||||
enemy.Actor.SetEnemyMaximumHealth(1000f);
|
||||
enemy.Spawn(Vector2.right * .45f);
|
||||
Physics.SyncTransforms();
|
||||
CombatEvents.OnValidHit += OnValidHit;
|
||||
yield return new WaitForFixedUpdate();
|
||||
}
|
||||
|
||||
[UnityTearDown]
|
||||
public IEnumerator TearDown()
|
||||
{
|
||||
CombatEvents.OnValidHit -= OnValidHit;
|
||||
if (world != null) Object.Destroy(world.gameObject);
|
||||
if (settings != null) Object.Destroy(settings);
|
||||
if (testRelicTuning != null) Object.Destroy(testRelicTuning);
|
||||
if (testConstants != null) Object.Destroy(testConstants);
|
||||
GameplayConstants.SetCurrentForTests(originalConstants);
|
||||
Time.timeScale = previousTimeScale;
|
||||
yield return null;
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator ResonanceCoreReplaysImmediatelyWithoutExtraGaugeOrRelicRecursion()
|
||||
{
|
||||
Assert.That(world.Loadout.TryAcquireRelic(PassiveRelicId.ResonanceCore), Is.True);
|
||||
world.Loadout.AddGauge(phoenix.NormalGaugeCost);
|
||||
Assert.That(world.Loadout.Gauge, Is.GreaterThanOrEqualTo(phoenix.NormalGaugeCost));
|
||||
float initialHealth = enemy.Actor.CurrentHealth;
|
||||
float initialGauge = world.Loadout.Gauge;
|
||||
|
||||
Assert.That(world.Loadout.TryUse(false), Is.True);
|
||||
Assert.That(ordinaryHits, Is.EqualTo(1));
|
||||
Assert.That(replayHits, Is.EqualTo(1));
|
||||
Assert.That(replayHitFrame, Is.EqualTo(ordinaryHitFrame),
|
||||
"Echo damage must be applied synchronously in the original hit's frame.");
|
||||
Assert.That(world.ArtifactExecutor.IsExecutingArtifact, Is.False);
|
||||
|
||||
float expectedDamage = DamageCalculator.FinalizeDamage(
|
||||
world.Player.CalculateDamage(phoenix.NormalDamage, phoenix.DamageTag))
|
||||
+ DamageCalculator.FinalizeDamage(world.Player.CalculateDamage(
|
||||
phoenix.NormalDamage * testRelicTuning.ResonanceEchoDamageMultiplier, phoenix.DamageTag));
|
||||
Assert.That(initialHealth - enemy.Actor.CurrentHealth,
|
||||
Is.EqualTo(expectedDamage).Within(.02f));
|
||||
Assert.That(ordinaryHits, Is.EqualTo(1));
|
||||
Assert.That(replayHits, Is.EqualTo(1));
|
||||
Assert.That(world.Loadout.Gauge,
|
||||
Is.EqualTo(initialGauge - phoenix.NormalGaugeCost).Within(.001f));
|
||||
|
||||
yield return null;
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator ResonantCrystalReplaysImmediatelyWhenDelayExpiresWithoutExtraGaugeOrAnotherPendingRepeat()
|
||||
{
|
||||
Assert.That(world.Loadout.TryAcquireRelic(PassiveRelicId.ResonantCrystal), Is.True);
|
||||
world.Loadout.AddGauge(phoenix.NormalGaugeCost);
|
||||
Assert.That(world.Loadout.Gauge, Is.GreaterThanOrEqualTo(phoenix.NormalGaugeCost));
|
||||
float initialHealth = enemy.Actor.CurrentHealth;
|
||||
float initialGauge = world.Loadout.Gauge;
|
||||
|
||||
Assert.That(world.Loadout.TryUse(false), Is.True);
|
||||
Assert.That(world.RelicRuntime.IsRepeatPending, Is.True);
|
||||
Assert.That(ordinaryHits, Is.EqualTo(1));
|
||||
Assert.That(replayHits, Is.Zero);
|
||||
Assert.That(world.Loadout.Gauge,
|
||||
Is.EqualTo(initialGauge - phoenix.NormalGaugeCost).Within(.001f));
|
||||
|
||||
float timeout = Time.realtimeSinceStartup + 2f;
|
||||
while (world.RelicRuntime.IsRepeatPending && Time.realtimeSinceStartup < timeout)
|
||||
{
|
||||
yield return new WaitForFixedUpdate();
|
||||
Assert.That(world.Player.IsAlive, Is.True, "The player must remain alive throughout the repeat delay.");
|
||||
Assert.That(enemy.Actor.IsAlive, Is.True, "The target must survive the initial Phoenix hit and ignite ticks.");
|
||||
Assert.That(Vector2.Distance(world.Player.Position, enemy.Actor.Position),
|
||||
Is.LessThanOrEqualTo(phoenix.NormalRange), "The target must stay within the replay ray during its delay.");
|
||||
}
|
||||
|
||||
float expectedDamage = DamageCalculator.FinalizeDamage(
|
||||
world.Player.CalculateDamage(phoenix.NormalDamage, phoenix.DamageTag))
|
||||
+ DamageCalculator.FinalizeDamage(world.Player.CalculateDamage(
|
||||
phoenix.NormalDamage * testRelicTuning.ResonantCrystalDamageFraction, phoenix.DamageTag));
|
||||
Assert.That(world.RelicRuntime.IsRepeatPending, Is.False);
|
||||
Assert.That(replayHits, Is.EqualTo(1));
|
||||
Assert.That(replayHitFrame, Is.GreaterThan(ordinaryHitFrame));
|
||||
Assert.That(replayHitFrame, Is.EqualTo(Time.frameCount),
|
||||
"Repeat damage must land in the frame the relic delay expires, not in a later cast step.");
|
||||
Assert.That(world.ArtifactExecutor.IsExecutingArtifact, Is.False);
|
||||
Assert.That(initialHealth - enemy.Actor.CurrentHealth,
|
||||
Is.EqualTo(expectedDamage).Within(.02f));
|
||||
Assert.That(ordinaryHits, Is.EqualTo(1));
|
||||
Assert.That(replayHits, Is.EqualTo(1));
|
||||
Assert.That(world.Loadout.Gauge,
|
||||
Is.EqualTo(initialGauge - phoenix.NormalGaugeCost).Within(.001f));
|
||||
|
||||
yield return null;
|
||||
}
|
||||
|
||||
private void OnValidHit(CombatHitResult hit)
|
||||
{
|
||||
if (hit.Target != enemy?.gameObject || !hit.IsArtifactHit) return;
|
||||
if (hit.SourceId == Defense25DRelicRuntime.EchoSourceId)
|
||||
{
|
||||
replayHits++;
|
||||
replayHitFrame = Time.frameCount;
|
||||
}
|
||||
else if (hit.SourceId == phoenix?.ArtifactId)
|
||||
{
|
||||
ordinaryHits++;
|
||||
ordinaryHitFrame = Time.frameCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user