Initial commit: Tiny Tackle Heroes Unity project
This commit is contained in:
@@ -0,0 +1,537 @@
|
||||
using System.Collections;
|
||||
using System.IO;
|
||||
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 RunMissionTrackerPlayModeTests
|
||||
{
|
||||
private const BindingFlags NonPublicInstance =
|
||||
BindingFlags.Instance | BindingFlags.NonPublic;
|
||||
|
||||
private bool previousGrantCatalogForTests;
|
||||
private bool previousProductionModeForTests;
|
||||
private float previousTimeScale;
|
||||
private string isolatedProfilePath;
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
previousGrantCatalogForTests = ArtifactRewardController.GrantCatalogForTests;
|
||||
previousProductionModeForTests = RunManager.ForceProductionModeForTests;
|
||||
previousTimeScale = Time.timeScale;
|
||||
ArtifactRewardController.GrantCatalogForTests = true;
|
||||
RunManager.ForceProductionModeForTests = false;
|
||||
Time.timeScale = 1f;
|
||||
isolatedProfilePath = Path.Combine(
|
||||
Path.GetTempPath(),
|
||||
"BumpCombatMissionTests",
|
||||
System.Guid.NewGuid().ToString("N") + ".json");
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
{
|
||||
ArtifactRewardController.GrantCatalogForTests = previousGrantCatalogForTests;
|
||||
RunManager.ForceProductionModeForTests = previousProductionModeForTests;
|
||||
Time.timeScale = previousTimeScale;
|
||||
if (!string.IsNullOrEmpty(isolatedProfilePath))
|
||||
{
|
||||
if (File.Exists(isolatedProfilePath))
|
||||
{
|
||||
File.Delete(isolatedProfilePath);
|
||||
}
|
||||
|
||||
string temporaryPath = isolatedProfilePath + ".tmp";
|
||||
if (File.Exists(temporaryPath))
|
||||
{
|
||||
File.Delete(temporaryPath);
|
||||
}
|
||||
|
||||
string directory = Path.GetDirectoryName(isolatedProfilePath);
|
||||
if (Directory.Exists(directory)
|
||||
&& Directory.GetFiles(directory).Length == 0)
|
||||
{
|
||||
Directory.Delete(directory);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator ConfirmedCombatEvents_CountOnceBySpawnAndActivation()
|
||||
{
|
||||
yield return LoadCombatScene();
|
||||
RunMissionTracker tracker = FindAny<RunMissionTracker>();
|
||||
RunManager runManager = RunManager.Instance;
|
||||
ExperienceSystem experience = FindAny<ExperienceSystem>();
|
||||
PlayerHealth health = FindAny<PlayerHealth>();
|
||||
SpawnDirector director = FindAny<SpawnDirector>();
|
||||
Assert.That(tracker, Is.Not.Null);
|
||||
Assert.That(runManager, Is.Not.Null);
|
||||
Assert.That(experience, Is.Not.Null);
|
||||
Assert.That(health, Is.Not.Null);
|
||||
Assert.That(director, Is.Not.Null);
|
||||
tracker.UseProfileStoreForTests(new UserProfileStore(isolatedProfilePath));
|
||||
director.enabled = false;
|
||||
foreach (EnemyController existing in Object.FindObjectsByType<EnemyController>(
|
||||
FindObjectsSortMode.None))
|
||||
{
|
||||
existing.gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
ActiveArtifactController artifacts = health.GetComponent<ActiveArtifactController>();
|
||||
Assert.That(artifacts, Is.Not.Null);
|
||||
for (int i = 0; i < artifacts.OwnedArtifactCount
|
||||
&& artifacts.CurrentArtifact?.Effect != ActiveArtifactEffect.Pulse; i++)
|
||||
{
|
||||
artifacts.SelectNext();
|
||||
}
|
||||
Assert.That(artifacts.CurrentArtifact?.Effect, Is.EqualTo(ActiveArtifactEffect.Pulse),
|
||||
"The development catalog should expose the real Pulse activation for integration coverage.");
|
||||
|
||||
EnemyController artifactTarget = SpawnEnemy(
|
||||
director,
|
||||
health.transform.position + Vector3.right * 0.75f,
|
||||
disableImmediately: false);
|
||||
yield return null;
|
||||
artifactTarget.enabled = false;
|
||||
Physics2D.SyncTransforms();
|
||||
Assert.That(artifacts.TryUseCurrent(charged: false), Is.True,
|
||||
"The real artifact activation should execute with available gauge.");
|
||||
float artifactDeadline = Time.realtimeSinceStartup + 1f;
|
||||
while (tracker.GetProgress(3).Progress == 0
|
||||
&& Time.realtimeSinceStartup < artifactDeadline)
|
||||
{
|
||||
yield return null;
|
||||
}
|
||||
Assert.That(tracker.GetProgress(3).Progress, Is.EqualTo(1),
|
||||
"An actual successful Pulse contact must reach the tracker event hook.");
|
||||
yield return new WaitForSeconds(0.2f);
|
||||
|
||||
EnemyController wrongColorShield = SpawnEnemy(
|
||||
director,
|
||||
health.transform.position + Vector3.right * 0.8f,
|
||||
EnemyKind.ArmoredSkeleton,
|
||||
disableImmediately: false);
|
||||
yield return null;
|
||||
wrongColorShield.enabled = false;
|
||||
ConfigureShieldForArtifactTest(wrongColorShield, ArtifactColor.Blue);
|
||||
Assert.That(wrongColorShield.ShieldColor, Is.EqualTo(ArtifactColor.Blue));
|
||||
Assert.That(wrongColorShield.ShieldHitsRemaining, Is.EqualTo(2));
|
||||
Assert.That(tracker.GetProgress(8).Progress, Is.Zero);
|
||||
Physics2D.SyncTransforms();
|
||||
Assert.That(artifacts.TryUseCurrent(charged: false), Is.True);
|
||||
yield return new WaitForSeconds(0.2f);
|
||||
Assert.That(tracker.GetProgress(3).Progress, Is.EqualTo(1),
|
||||
"A wrong-color shield contact is not an effective artifact hit.");
|
||||
Assert.That(tracker.GetProgress(8).Progress, Is.Zero,
|
||||
"A wrong-color shield contact does not decrement its required hits.");
|
||||
Assert.That(wrongColorShield.ShieldHitsRemaining, Is.EqualTo(2),
|
||||
"Wrong-color contacts leave the shield's required-hit counter unchanged.");
|
||||
|
||||
EnemyController matchingColorShield = SpawnEnemy(
|
||||
director,
|
||||
health.transform.position + Vector3.right * 0.8f,
|
||||
EnemyKind.ArmoredSkeleton,
|
||||
disableImmediately: false);
|
||||
yield return null;
|
||||
matchingColorShield.enabled = false;
|
||||
ConfigureShieldForArtifactTest(matchingColorShield, ArtifactColor.Red);
|
||||
Physics2D.SyncTransforms();
|
||||
Assert.That(artifacts.TryUseCurrent(charged: false), Is.True);
|
||||
yield return new WaitForSeconds(0.2f);
|
||||
Assert.That(tracker.GetProgress(3).Progress, Is.EqualTo(2),
|
||||
"A matching shield pip is an effective artifact activation.");
|
||||
Assert.That(tracker.GetProgress(8).Progress, Is.EqualTo(1),
|
||||
"Only its actual matching-color decrement advances the shield mission.");
|
||||
|
||||
EnemyController first = SpawnEnemy(director, Vector2.right * 3f);
|
||||
EnemyController second = SpawnEnemy(director, Vector2.left * 3f);
|
||||
|
||||
CombatEvents.RaiseValidHit(CreateBumpHit(experience.gameObject, first.gameObject,
|
||||
HitSide.Back, 0f));
|
||||
Assert.That(tracker.GetProgress(2).Progress, Is.Zero,
|
||||
"A contact that dealt no damage must not count as a successful bump.");
|
||||
|
||||
CombatHitResult rearHit = CreateBumpHit(
|
||||
experience.gameObject,
|
||||
first.gameObject,
|
||||
HitSide.Back,
|
||||
1f);
|
||||
CombatEvents.RaiseValidHit(rearHit);
|
||||
CombatEvents.RaiseValidHit(rearHit);
|
||||
CombatEvents.RaiseValidHit(CreateBumpHit(
|
||||
experience.gameObject,
|
||||
second.gameObject,
|
||||
HitSide.Back,
|
||||
1f));
|
||||
Assert.That(tracker.GetProgress(2).Progress, Is.EqualTo(3));
|
||||
Assert.That(tracker.GetProgress(4).Progress, Is.EqualTo(2),
|
||||
"Rear progress is once per enemy lifetime, while bump progress records each real hit.");
|
||||
|
||||
EnemyController lethal = SpawnEnemy(
|
||||
director,
|
||||
health.transform.position + Vector3.left * 4f,
|
||||
disableImmediately: false);
|
||||
yield return null;
|
||||
Assert.That(lethal.CurrentHealth, Is.GreaterThan(0f));
|
||||
SetPrivateField<ExperienceOrb>(lethal, "experienceOrbPrefab", null);
|
||||
Assert.That(lethal.TryTakeDamage(
|
||||
lethal.CurrentHealth + 1f,
|
||||
Vector2.right,
|
||||
0.1f), Is.True,
|
||||
"A real lethal EnemyController hit should publish its death event.");
|
||||
Assert.That(tracker.GetProgress(0).Progress, Is.EqualTo(1));
|
||||
CombatEvents.RaiseEnemyDied(lethal.gameObject);
|
||||
Assert.That(tracker.GetProgress(0).Progress, Is.EqualTo(1),
|
||||
"A duplicate callback for that same spawn lifetime must not count twice.");
|
||||
|
||||
experience.AddExperience(1);
|
||||
Assert.That(tracker.GetProgress(1).Progress, Is.Zero,
|
||||
"Free mission XP must not masquerade as an orb pickup.");
|
||||
|
||||
GameObject orbObject = new("Mission test experience orb");
|
||||
orbObject.transform.position = experience.transform.position;
|
||||
ExperienceOrb orb = orbObject.AddComponent<ExperienceOrb>();
|
||||
Rigidbody2D orbBody = orbObject.GetComponent<Rigidbody2D>();
|
||||
orbBody.gravityScale = 0f;
|
||||
CircleCollider2D orbCollider = orbObject.GetComponent<CircleCollider2D>();
|
||||
orbCollider.isTrigger = true;
|
||||
orb.Initialize(1);
|
||||
yield return null;
|
||||
yield return new WaitForFixedUpdate();
|
||||
Assert.That(tracker.GetProgress(1).Progress, Is.EqualTo(1),
|
||||
"A nearby orb should report one actual pickup through its normal physics path.");
|
||||
|
||||
health.GrantInvulnerability(2f);
|
||||
health.TryTakeDamage(0f, Vector2.zero, 0f);
|
||||
health.TryTakeDamage(5f, Vector2.right, 0.1f);
|
||||
Assert.That(tracker.GetProgress(7).Progress, Is.Zero,
|
||||
"Zero damage and ordinary invulnerability do not count as a guard block.");
|
||||
Assert.That(health.TryActivateGuard(), Is.True);
|
||||
Assert.That(health.TryTakeDamage(5f, Vector2.right, 0.1f), Is.False);
|
||||
Assert.That(health.TryTakeDamage(5f, Vector2.right, 0.1f), Is.False);
|
||||
Assert.That(tracker.GetProgress(7).Progress, Is.EqualTo(1),
|
||||
"Multiple blocked hits from one guard activation count once.");
|
||||
|
||||
CombatEvents.RaiseArtifactEffectiveHit(
|
||||
first.gameObject,
|
||||
2001,
|
||||
ActiveArtifactEffect.Pulse,
|
||||
isCharged: true,
|
||||
matchedShieldRequirement: false);
|
||||
CombatEvents.RaiseArtifactEffectiveHit(
|
||||
first.gameObject,
|
||||
2001,
|
||||
ActiveArtifactEffect.Pulse,
|
||||
isCharged: true,
|
||||
matchedShieldRequirement: false);
|
||||
Assert.That(tracker.GetProgress(3).Progress, Is.EqualTo(3));
|
||||
Assert.That(tracker.GetProgress(9).Progress, Is.EqualTo(1),
|
||||
"One charged activation counts once even if it affects a target repeatedly.");
|
||||
Assert.That(tracker.GetProgress(8).Progress, Is.EqualTo(1),
|
||||
"Only the earlier actual matching-color shield decrement has counted.");
|
||||
|
||||
EnemyController[] crowdTargets = new EnemyController[5];
|
||||
for (int i = 0; i < crowdTargets.Length; i++)
|
||||
{
|
||||
crowdTargets[i] = i == 0
|
||||
? first
|
||||
: SpawnEnemy(director, new Vector2(i + 5f, 0f));
|
||||
CombatEvents.RaiseArtifactEffectiveHit(
|
||||
crowdTargets[i].gameObject,
|
||||
3001,
|
||||
ActiveArtifactEffect.Cyclone,
|
||||
isCharged: false,
|
||||
matchedShieldRequirement: i == 0);
|
||||
}
|
||||
|
||||
Assert.That(tracker.GetProgress(5).Progress, Is.EqualTo(5));
|
||||
Assert.That(tracker.GetProgress(5).Completed, Is.True);
|
||||
Assert.That(tracker.GetProgress(8).Progress, Is.EqualTo(2),
|
||||
"Only a counted matching-color shield decrement advances the shield mission.");
|
||||
Assert.That(tracker.GetLifetimeMissionCompletions(), Is.Zero,
|
||||
"A development/test run must not write permanent mission records.");
|
||||
LogAssert.NoUnexpectedReceived();
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator RampageWindowIncludesExactThreeSecondBoundary_AndGameOverStopsEvents()
|
||||
{
|
||||
yield return LoadCombatScene();
|
||||
RunMissionTracker tracker = FindAny<RunMissionTracker>();
|
||||
RunManager runManager = RunManager.Instance;
|
||||
ExperienceSystem experience = FindAny<ExperienceSystem>();
|
||||
SpawnDirector director = FindAny<SpawnDirector>();
|
||||
Assert.That(tracker, Is.Not.Null);
|
||||
Assert.That(runManager, Is.Not.Null);
|
||||
Assert.That(experience, Is.Not.Null);
|
||||
Assert.That(director, Is.Not.Null);
|
||||
tracker.UseProfileStoreForTests(new UserProfileStore(isolatedProfilePath));
|
||||
director.enabled = false;
|
||||
foreach (EnemyController existing in Object.FindObjectsByType<EnemyController>(
|
||||
FindObjectsSortMode.None))
|
||||
{
|
||||
existing.gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
SetAutoProperty(runManager, "ElapsedTime", 0f);
|
||||
EnemyController first = SpawnEnemy(director, new Vector2(2f, 0f));
|
||||
CombatEvents.RaiseEnemyDied(first.gameObject);
|
||||
CombatEvents.RaiseEnemyDied(first.gameObject);
|
||||
Assert.That(tracker.GetProgress(10).Progress, Is.EqualTo(1),
|
||||
"A duplicate death callback from one spawn lifetime counts once.");
|
||||
|
||||
EnemyController summoned = SpawnEnemy(director, new Vector2(3f, 0f));
|
||||
summoned.ConfigureSummonedEnemy(
|
||||
null,
|
||||
RunEventEnemyTuning.Create(
|
||||
EnemyKind.Skeleton,
|
||||
1f,
|
||||
1,
|
||||
1f,
|
||||
Color.white,
|
||||
1f,
|
||||
1f,
|
||||
1f,
|
||||
1f,
|
||||
1f,
|
||||
1f,
|
||||
1f,
|
||||
1f,
|
||||
1f,
|
||||
1,
|
||||
1f,
|
||||
1f,
|
||||
1,
|
||||
1f,
|
||||
true),
|
||||
phaseProtected: false);
|
||||
CombatEvents.RaiseEnemyDied(summoned.gameObject);
|
||||
Assert.That(tracker.GetProgress(10).Progress, Is.EqualTo(1),
|
||||
"Summons do not count toward the non-summoned kill streak.");
|
||||
Assert.That(tracker.GetProgress(0).Progress, Is.EqualTo(1),
|
||||
"Summons also do not count toward the first-sweep mission.");
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
EnemyController enemy = SpawnEnemy(director, new Vector2(i + 4f, 0f));
|
||||
CombatEvents.RaiseEnemyDied(enemy.gameObject);
|
||||
}
|
||||
Assert.That(tracker.GetProgress(10).Progress, Is.EqualTo(9));
|
||||
|
||||
SetAutoProperty(runManager, "ElapsedTime", 4f);
|
||||
EnemyController tenth = SpawnEnemy(director, Vector2.left * 4f);
|
||||
CombatEvents.RaiseEnemyDied(tenth.gameObject);
|
||||
Assert.That(tracker.GetProgress(10).Progress, Is.EqualTo(1),
|
||||
"Kills older than three combat seconds leave the sliding window.");
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
SetAutoProperty(runManager, "ElapsedTime", 4.1f + i * 0.1f);
|
||||
EnemyController enemy = SpawnEnemy(director, new Vector2(i + 6f, 0f));
|
||||
CombatEvents.RaiseEnemyDied(enemy.gameObject);
|
||||
}
|
||||
Assert.That(tracker.GetProgress(10).Progress, Is.EqualTo(9));
|
||||
|
||||
SetAutoProperty(runManager, "ElapsedTime", 7f);
|
||||
EnemyController final = SpawnEnemy(director, Vector2.left * 6f);
|
||||
CombatEvents.RaiseEnemyDied(final.gameObject);
|
||||
Assert.That(tracker.GetProgress(10).Progress, Is.EqualTo(10));
|
||||
Assert.That(tracker.GetProgress(10).Completed, Is.True,
|
||||
"A kill exactly three combat seconds after the oldest valid kill completes the inclusive window.");
|
||||
|
||||
runManager.EndRun();
|
||||
int bumpBeforeTerminalEvents = tracker.GetProgress(2).Progress;
|
||||
int orbBeforeTerminalEvents = tracker.GetProgress(1).Progress;
|
||||
CombatEvents.RaiseValidHit(CreateBumpHit(
|
||||
experience.gameObject,
|
||||
tenth.gameObject,
|
||||
HitSide.Front,
|
||||
1f));
|
||||
CombatEvents.RaiseArtifactEffectiveHit(
|
||||
tenth.gameObject,
|
||||
4001,
|
||||
ActiveArtifactEffect.Pulse,
|
||||
isCharged: false,
|
||||
matchedShieldRequirement: false);
|
||||
experience.CollectExperienceOrb(1);
|
||||
|
||||
Assert.That(tracker.GetProgress(2).Progress, Is.EqualTo(bumpBeforeTerminalEvents));
|
||||
Assert.That(tracker.GetProgress(1).Progress, Is.EqualTo(orbBeforeTerminalEvents));
|
||||
Assert.That(tracker.GetProgress(3).Progress, Is.Zero);
|
||||
Assert.That(tracker.GetProgress(10).Progress, Is.EqualTo(10),
|
||||
"Completed rampage progress must not decay after its success or after game over.");
|
||||
LogAssert.NoUnexpectedReceived();
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator ProductionRun_PersistsMissionCompletionAndHonorsUnlockGates()
|
||||
{
|
||||
ArtifactRewardController.GrantCatalogForTests = false;
|
||||
yield return LoadCombatScene();
|
||||
RunMissionTracker tracker = FindAny<RunMissionTracker>();
|
||||
RunManager runManager = RunManager.Instance;
|
||||
ExperienceSystem experience = FindAny<ExperienceSystem>();
|
||||
Assert.That(tracker, Is.Not.Null);
|
||||
Assert.That(runManager, Is.Not.Null);
|
||||
Assert.That(experience, Is.Not.Null);
|
||||
Assert.That(runManager.IsTitleScreen, Is.True);
|
||||
tracker.UseProfileStoreForTests(new UserProfileStore(isolatedProfilePath));
|
||||
|
||||
Assert.That(runManager.BeginRun(RunMode.Production), Is.True);
|
||||
Assert.That(tracker.GetProgress(7).Locked, Is.True,
|
||||
"Guard missions stay locked until the run unlocks guard.");
|
||||
Assert.That(tracker.GetProgress(9).Locked, Is.True,
|
||||
"Charged missions stay locked until the run unlocks charge.");
|
||||
|
||||
for (int i = 0; i < 50; i++)
|
||||
{
|
||||
experience.CollectExperienceOrb(0);
|
||||
}
|
||||
|
||||
Assert.That(tracker.GetProgress(1).Completed, Is.True);
|
||||
Assert.That(tracker.GetLifetimeMissionCompletions(), Is.EqualTo(1));
|
||||
Assert.That(File.Exists(isolatedProfilePath), Is.True);
|
||||
string savedProfile = File.ReadAllText(isolatedProfilePath);
|
||||
Assert.That(savedProfile, Does.Contain("mission.growth_footing"));
|
||||
|
||||
runManager.UnlockGuard();
|
||||
runManager.UnlockArtifactCharge();
|
||||
Assert.That(tracker.GetProgress(7).Locked, Is.False);
|
||||
Assert.That(tracker.GetProgress(9).Locked, Is.False);
|
||||
Assert.That(tracker.ProfileStore.Profile.achievements, Is.Empty,
|
||||
"No uncommissioned achievements are synthesized into the profile.");
|
||||
Assert.That(tracker.ProfileStore.Profile.unlockedSkinIds, Is.Empty);
|
||||
LogAssert.NoUnexpectedReceived();
|
||||
}
|
||||
|
||||
private static IEnumerator LoadCombatScene()
|
||||
{
|
||||
AsyncOperation load = SceneManager.LoadSceneAsync("CombatPrototype");
|
||||
while (!load.isDone)
|
||||
{
|
||||
yield return null;
|
||||
}
|
||||
|
||||
yield return null;
|
||||
yield return null;
|
||||
}
|
||||
|
||||
private static T FindAny<T>() where T : Object
|
||||
{
|
||||
return Object.FindAnyObjectByType<T>();
|
||||
}
|
||||
|
||||
private static EnemyController SpawnEnemy(
|
||||
SpawnDirector director,
|
||||
Vector2 position,
|
||||
EnemyKind kind = EnemyKind.Skeleton,
|
||||
bool disableImmediately = true)
|
||||
{
|
||||
EnemyController prefab = FindCatalogPrefab(director, kind);
|
||||
Assert.That(prefab, Is.Not.Null);
|
||||
EnemyController enemy = Object.Instantiate(
|
||||
prefab,
|
||||
position,
|
||||
Quaternion.identity);
|
||||
if (disableImmediately)
|
||||
{
|
||||
enemy.enabled = false;
|
||||
}
|
||||
return enemy;
|
||||
}
|
||||
|
||||
private static EnemyController FindCatalogPrefab(
|
||||
SpawnDirector director,
|
||||
EnemyKind kind)
|
||||
{
|
||||
string[] fieldNames =
|
||||
{
|
||||
"enemyPrefabs",
|
||||
"elitePrefabs",
|
||||
"midBossPrefabs",
|
||||
"finalBossPrefabs",
|
||||
};
|
||||
foreach (string fieldName in fieldNames)
|
||||
{
|
||||
FieldInfo field = typeof(SpawnDirector).GetField(
|
||||
fieldName,
|
||||
NonPublicInstance);
|
||||
EnemyController[] prefabs = field?.GetValue(director) as EnemyController[];
|
||||
if (prefabs == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (EnemyController prefab in prefabs)
|
||||
{
|
||||
if (prefab != null
|
||||
&& prefab.Definition != null
|
||||
&& prefab.Definition.Kind == kind)
|
||||
{
|
||||
return prefab;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static CombatHitResult CreateBumpHit(
|
||||
GameObject attacker,
|
||||
GameObject target,
|
||||
HitSide side,
|
||||
float damage)
|
||||
{
|
||||
return new CombatHitResult(
|
||||
attacker,
|
||||
target,
|
||||
false,
|
||||
side,
|
||||
damage,
|
||||
Vector2.right,
|
||||
1f,
|
||||
target.transform.position);
|
||||
}
|
||||
|
||||
private static void ConfigureShieldForArtifactTest(
|
||||
EnemyController enemy,
|
||||
ArtifactColor shieldColor)
|
||||
{
|
||||
SetPrivateField(enemy, "groggyTierConfigured", true);
|
||||
SetPrivateField(enemy, "groggyTier", RunTimedEvent.Elite);
|
||||
SetPrivateField(enemy, "requiredGroggyDistinctArtifacts", 1);
|
||||
SetPrivateField(enemy, "requiredGroggyContactsPerArtifact", 2);
|
||||
SetPrivateField(enemy, "groggyContactCount", 0);
|
||||
SetPrivateField(enemy, "isGroggy", false);
|
||||
SetPrivateField(enemy, "shieldColor", shieldColor);
|
||||
}
|
||||
|
||||
private static void SetPrivateField<T>(object target, string fieldName, T value)
|
||||
{
|
||||
target.GetType()
|
||||
.GetField(fieldName, NonPublicInstance)
|
||||
?.SetValue(target, value);
|
||||
}
|
||||
|
||||
private static void SetAutoProperty<T>(object target, string propertyName, T value)
|
||||
{
|
||||
target.GetType()
|
||||
.GetField(
|
||||
$"<{propertyName}>k__BackingField",
|
||||
NonPublicInstance)
|
||||
?.SetValue(target, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user