395 lines
14 KiB
C#
395 lines
14 KiB
C#
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Reflection;
|
|
using BumpCombat.Combat;
|
|
using BumpCombat.Core;
|
|
using BumpCombat.Enemies;
|
|
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 PressureStreakPlayModeTests
|
|
{
|
|
private const BindingFlags NonPublicInstance =
|
|
BindingFlags.Instance | BindingFlags.NonPublic;
|
|
|
|
private readonly List<GameObject> testTargets = new();
|
|
private bool previousGrantCatalogForTests;
|
|
private bool previousProductionModeForTests;
|
|
private float previousTimeScale;
|
|
private PlayerStats playerStats;
|
|
private RunManager runManager;
|
|
private SpawnDirector spawnDirector;
|
|
private EnemyController ordinarySceneTarget;
|
|
|
|
[SetUp]
|
|
public void SetUp()
|
|
{
|
|
previousGrantCatalogForTests = ArtifactRewardController.GrantCatalogForTests;
|
|
previousProductionModeForTests = RunManager.ForceProductionModeForTests;
|
|
previousTimeScale = Time.timeScale;
|
|
ArtifactRewardController.GrantCatalogForTests = true;
|
|
RunManager.ForceProductionModeForTests = false;
|
|
Time.timeScale = 1f;
|
|
}
|
|
|
|
[TearDown]
|
|
public void TearDown()
|
|
{
|
|
foreach (GameObject target in testTargets)
|
|
{
|
|
if (target != null)
|
|
{
|
|
Object.Destroy(target);
|
|
}
|
|
}
|
|
|
|
testTargets.Clear();
|
|
ArtifactRewardController.GrantCatalogForTests = previousGrantCatalogForTests;
|
|
RunManager.ForceProductionModeForTests = previousProductionModeForTests;
|
|
Time.timeScale = previousTimeScale;
|
|
}
|
|
|
|
[UnityTest]
|
|
public IEnumerator TwoKillsDoNotApply_ThirdKillAppliesTwentyPercent()
|
|
{
|
|
yield return LoadCombatPrototype();
|
|
|
|
SetRunTime(100f);
|
|
EnemyController first = CreateDeadEnemy();
|
|
yield return null;
|
|
RaiseOrdinaryHit(first);
|
|
Assert.That(playerStats.PressureStreakRemaining, Is.GreaterThan(0f));
|
|
Assert.That(playerStats.IsPressureStreakActive, Is.False);
|
|
|
|
SetRunTime(101f);
|
|
EnemyController second = CreateDeadEnemy();
|
|
RaiseOrdinaryHit(second);
|
|
Assert.That(playerStats.PressureStreakKillCount, Is.EqualTo(2));
|
|
Assert.That(playerStats.PressureStreakRemaining, Is.GreaterThan(0f));
|
|
Assert.That(playerStats.IsPressureStreakActive, Is.False);
|
|
|
|
EnemyController third = CreateDeadEnemy();
|
|
yield return null;
|
|
RaiseOrdinaryHit(third);
|
|
Assert.That(playerStats.PressureStreakKillCount, Is.EqualTo(3));
|
|
Assert.That(playerStats.IsPressureStreakActive, Is.True);
|
|
Assert.That(playerStats.MoveSpeed, Is.EqualTo(3.6f).Within(0.001f));
|
|
}
|
|
|
|
[UnityTest]
|
|
public IEnumerator ConsecutiveWindow_UsesKillToKillGap_AndFourthRefreshesWithoutStacking()
|
|
{
|
|
yield return LoadCombatPrototype();
|
|
|
|
SetRunTime(200f);
|
|
RaiseOrdinaryHit(CreateDeadEnemy());
|
|
SetRunTime(202f);
|
|
RaiseOrdinaryHit(CreateDeadEnemy());
|
|
SetRunTime(204f);
|
|
RaiseOrdinaryHit(CreateDeadEnemy());
|
|
Assert.That(playerStats.IsPressureStreakActive, Is.True);
|
|
|
|
SetRunTime(205f);
|
|
playerStats.RefreshPressureStreak();
|
|
float beforeRefresh = playerStats.PressureStreakRemaining;
|
|
RaiseOrdinaryHit(CreateDeadEnemy());
|
|
Assert.That(playerStats.PressureStreakKillCount, Is.EqualTo(4));
|
|
Assert.That(
|
|
playerStats.GetStackCount("player.pressure-streak", CharacterStat.MoveSpeed),
|
|
Is.EqualTo(1));
|
|
Assert.That(playerStats.MoveSpeed, Is.EqualTo(3.6f).Within(0.001f));
|
|
Assert.That(playerStats.PressureStreakRemaining, Is.GreaterThan(beforeRefresh));
|
|
|
|
SetRunTime(208.01f);
|
|
playerStats.RefreshPressureStreak();
|
|
Assert.That(playerStats.PressureStreakKillCount, Is.EqualTo(0));
|
|
Assert.That(playerStats.IsPressureStreakActive, Is.False);
|
|
|
|
SetRunTime(220f);
|
|
RaiseOrdinaryHit(CreateDeadEnemy());
|
|
SetRunTime(223f);
|
|
RaiseOrdinaryHit(CreateDeadEnemy());
|
|
SetRunTime(226f);
|
|
RaiseOrdinaryHit(CreateDeadEnemy());
|
|
Assert.That(playerStats.PressureStreakKillCount, Is.EqualTo(3));
|
|
Assert.That(playerStats.IsPressureStreakActive, Is.True);
|
|
|
|
SetRunTime(230.01f);
|
|
playerStats.RefreshPressureStreak();
|
|
SetRunTime(240f);
|
|
RaiseOrdinaryHit(CreateDeadEnemy());
|
|
SetRunTime(243.01f);
|
|
RaiseOrdinaryHit(CreateDeadEnemy());
|
|
SetRunTime(246.02f);
|
|
RaiseOrdinaryHit(CreateDeadEnemy());
|
|
Assert.That(playerStats.PressureStreakKillCount, Is.EqualTo(1));
|
|
Assert.That(playerStats.IsPressureStreakActive, Is.False);
|
|
}
|
|
|
|
[UnityTest]
|
|
public IEnumerator ArtifactDashNonlethalAndRetiredEvents_DoNotCount()
|
|
{
|
|
yield return LoadCombatPrototype();
|
|
|
|
EnemyController artifact = CreateDeadEnemy();
|
|
EnemyController dash = CreateDeadEnemy();
|
|
EnemyController nonlethal = CreateLivingEnemy();
|
|
EnemyController retired = CreateDeadEnemy();
|
|
yield return null;
|
|
|
|
CombatEvents.RaiseValidHit(CreateHit(
|
|
artifact.gameObject,
|
|
isArtifactHit: true,
|
|
sourceId: "artifact.pulse"));
|
|
CombatEvents.RaiseValidHit(CreateHit(
|
|
dash.gameObject,
|
|
isDash: true));
|
|
RaiseOrdinaryHit(nonlethal);
|
|
CombatEvents.RaiseEnemyRetired(retired.gameObject);
|
|
|
|
Assert.That(playerStats.PressureStreakKillCount, Is.Zero);
|
|
Assert.That(playerStats.IsPressureStreakActive, Is.False);
|
|
}
|
|
|
|
[UnityTest]
|
|
public IEnumerator SameEnemyLifetime_IsCountedOnce_AndOtherModifiersRemainOwned()
|
|
{
|
|
yield return LoadCombatPrototype();
|
|
|
|
playerStats.AddModifier(new StatModifier(
|
|
"level-up.move-speed",
|
|
CharacterStat.MoveSpeed,
|
|
ModifierOperation.Increased,
|
|
0.1f));
|
|
SetRunTime(300f);
|
|
EnemyController target = CreateDeadEnemy();
|
|
yield return null;
|
|
RaiseOrdinaryHit(target);
|
|
RaiseOrdinaryHit(target);
|
|
Assert.That(playerStats.PressureStreakKillCount, Is.EqualTo(1));
|
|
|
|
RaiseOrdinaryHit(CreateDeadEnemy());
|
|
RaiseOrdinaryHit(CreateDeadEnemy());
|
|
Assert.That(playerStats.MoveSpeed, Is.EqualTo(3.9f).Within(0.001f));
|
|
|
|
SetRunTime(304.01f);
|
|
playerStats.RefreshPressureStreak();
|
|
Assert.That(playerStats.GetStackCount(
|
|
"level-up.move-speed",
|
|
CharacterStat.MoveSpeed), Is.EqualTo(1));
|
|
Assert.That(playerStats.GetStackCount(
|
|
"player.pressure-streak",
|
|
CharacterStat.MoveSpeed), Is.Zero);
|
|
Assert.That(playerStats.MoveSpeed, Is.EqualTo(3.3f).Within(0.001f));
|
|
}
|
|
|
|
[UnityTest]
|
|
public IEnumerator PauseAndDisable_DoNotConsumeOrRetainPressureStreak()
|
|
{
|
|
yield return LoadCombatPrototype();
|
|
|
|
SetRunTime(400f);
|
|
RaiseOrdinaryHit(CreateDeadEnemy());
|
|
RaiseOrdinaryHit(CreateDeadEnemy());
|
|
RaiseOrdinaryHit(CreateDeadEnemy());
|
|
float remainingBeforePause = playerStats.PressureStreakRemaining;
|
|
runManager.SetSelectionOpen(true);
|
|
yield return new WaitForSecondsRealtime(0.2f);
|
|
playerStats.RefreshPressureStreak();
|
|
Assert.That(
|
|
playerStats.PressureStreakRemaining,
|
|
Is.EqualTo(remainingBeforePause).Within(0.001f));
|
|
runManager.SetSelectionOpen(false);
|
|
|
|
playerStats.enabled = false;
|
|
Assert.That(playerStats.GetStackCount(
|
|
"player.pressure-streak",
|
|
CharacterStat.MoveSpeed), Is.Zero);
|
|
playerStats.enabled = true;
|
|
Assert.That(playerStats.PressureStreakKillCount, Is.Zero);
|
|
Assert.That(playerStats.IsPressureStreakActive, Is.False);
|
|
}
|
|
|
|
[UnityTest]
|
|
public IEnumerator LethalResolverBump_RaisesOrdinaryDeathHitForTheStreak()
|
|
{
|
|
yield return LoadCombatPrototype();
|
|
|
|
if (ordinarySceneTarget == null)
|
|
{
|
|
Assert.That(spawnDirector, Is.Not.Null);
|
|
spawnDirector.DebugSpawnImmediate(1);
|
|
yield return null;
|
|
ordinarySceneTarget = FindOrdinarySceneTarget();
|
|
}
|
|
|
|
Assert.That(ordinarySceneTarget, Is.Not.Null);
|
|
ordinarySceneTarget.gameObject.SetActive(true);
|
|
yield return null;
|
|
SetEnemyState(ordinarySceneTarget, EnemyState.Chase);
|
|
FieldInfo healthBackingField = typeof(EnemyController).GetField(
|
|
"<CurrentHealth>k__BackingField",
|
|
NonPublicInstance);
|
|
Assert.That(healthBackingField, Is.Not.Null);
|
|
healthBackingField.SetValue(ordinarySceneTarget, 1f);
|
|
|
|
BumpCombatResolver resolver =
|
|
playerStats.GetComponent<BumpCombatResolver>();
|
|
Assert.That(resolver, Is.Not.Null);
|
|
MethodInfo resolveEnemy = typeof(BumpCombatResolver).GetMethod(
|
|
"ResolveEnemy",
|
|
NonPublicInstance);
|
|
Assert.That(resolveEnemy, Is.Not.Null);
|
|
object result = resolveEnemy.Invoke(
|
|
resolver,
|
|
new object[]
|
|
{
|
|
ordinarySceneTarget,
|
|
false,
|
|
(Vector2)ordinarySceneTarget.transform.position,
|
|
(Vector2)playerStats.transform.position,
|
|
Vector2.right,
|
|
HitSide.Front,
|
|
false,
|
|
100f,
|
|
null,
|
|
null,
|
|
DamageTag.Collision,
|
|
null,
|
|
0,
|
|
null,
|
|
});
|
|
|
|
Assert.That(result, Is.EqualTo(true));
|
|
Assert.That(ordinarySceneTarget.IsDead, Is.True);
|
|
Assert.That(playerStats.PressureStreakKillCount, Is.EqualTo(1));
|
|
}
|
|
|
|
private IEnumerator LoadCombatPrototype()
|
|
{
|
|
AsyncOperation load = SceneManager.LoadSceneAsync("CombatPrototype");
|
|
while (!load.isDone)
|
|
{
|
|
yield return null;
|
|
}
|
|
|
|
yield return null;
|
|
yield return null;
|
|
yield return null;
|
|
runManager = RunManager.Instance;
|
|
playerStats = Object.FindAnyObjectByType<PlayerStats>();
|
|
Assert.That(runManager, Is.Not.Null);
|
|
Assert.That(playerStats, Is.Not.Null);
|
|
|
|
spawnDirector = Object.FindAnyObjectByType<SpawnDirector>();
|
|
if (spawnDirector != null)
|
|
{
|
|
spawnDirector.enabled = false;
|
|
}
|
|
|
|
foreach (EnemyController enemy in Object.FindObjectsByType<EnemyController>(
|
|
FindObjectsSortMode.None))
|
|
{
|
|
if (ordinarySceneTarget == null
|
|
&& !enemy.IsEventEnemy
|
|
&& !enemy.IsDead)
|
|
{
|
|
ordinarySceneTarget = enemy;
|
|
}
|
|
enemy.gameObject.SetActive(false);
|
|
}
|
|
|
|
SetRunTime(100f);
|
|
playerStats.RefreshPressureStreak();
|
|
}
|
|
|
|
private EnemyController CreateDeadEnemy()
|
|
{
|
|
GameObject target = new(
|
|
"Pressure Streak Dead Target",
|
|
typeof(BoxCollider2D));
|
|
testTargets.Add(target);
|
|
EnemyController enemy = target.AddComponent<EnemyController>();
|
|
SetEnemyState(enemy, EnemyState.Dead);
|
|
return enemy;
|
|
}
|
|
|
|
private EnemyController CreateLivingEnemy()
|
|
{
|
|
GameObject target = new(
|
|
"Pressure Streak Living Target",
|
|
typeof(BoxCollider2D));
|
|
testTargets.Add(target);
|
|
EnemyController enemy = target.AddComponent<EnemyController>();
|
|
SetEnemyState(enemy, EnemyState.Chase);
|
|
return enemy;
|
|
}
|
|
|
|
private void RaiseOrdinaryHit(EnemyController enemy)
|
|
{
|
|
CombatEvents.RaiseValidHit(CreateHit(enemy.gameObject));
|
|
}
|
|
|
|
private CombatHitResult CreateHit(
|
|
GameObject target,
|
|
bool isDash = false,
|
|
bool isArtifactHit = false,
|
|
string sourceId = null)
|
|
{
|
|
return new CombatHitResult(
|
|
playerStats.gameObject,
|
|
target,
|
|
isDash,
|
|
HitSide.Front,
|
|
1f,
|
|
Vector2.right,
|
|
0f,
|
|
target.transform.position,
|
|
sourceId: sourceId,
|
|
isArtifactHit: isArtifactHit);
|
|
}
|
|
|
|
private void SetRunTime(float value)
|
|
{
|
|
FieldInfo elapsedTime = typeof(RunManager).GetField(
|
|
"<ElapsedTime>k__BackingField",
|
|
NonPublicInstance);
|
|
Assert.That(elapsedTime, Is.Not.Null);
|
|
elapsedTime.SetValue(runManager, value);
|
|
}
|
|
|
|
private static void SetEnemyState(EnemyController enemy, EnemyState state)
|
|
{
|
|
FieldInfo stateBackingField = typeof(EnemyController).GetField(
|
|
"<State>k__BackingField",
|
|
NonPublicInstance);
|
|
Assert.That(stateBackingField, Is.Not.Null);
|
|
stateBackingField.SetValue(enemy, state);
|
|
}
|
|
|
|
private static EnemyController FindOrdinarySceneTarget()
|
|
{
|
|
foreach (EnemyController enemy in Object.FindObjectsByType<EnemyController>(
|
|
FindObjectsSortMode.None))
|
|
{
|
|
if (enemy != null
|
|
&& !enemy.IsDead
|
|
&& !enemy.IsEventEnemy
|
|
&& !enemy.IsSummoned)
|
|
{
|
|
return enemy;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|
|
}
|