Initial commit: Tiny Tackle Heroes Unity project
This commit is contained in:
@@ -0,0 +1,394 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user