using System.Reflection; using BumpCombat.Enemies; using BumpCombat.Progression; using BumpCombat.Player; using NUnit.Framework; using UnityEngine; namespace BumpCombat.Tests { public sealed class PassiveRelicRulesTests { [TestCase(0.149f, true)] [TestCase(0.15f, false)] [TestCase(0.99f, false)] public void RollChance_UsesExclusiveUpperThreshold(float roll, bool expected) { Assert.That(PassiveRelicRules.RollsChance(0.15f, roll), Is.EqualTo(expected)); } [TestCase(true, true, false, 0.1f, 20f)] [TestCase(true, false, false, 0.1f, 10f)] [TestCase(true, true, true, 0.1f, 10f)] [TestCase(false, true, false, 0.1f, 10f)] public void TwinHorn_DoublesOnlyOwnedOrdinaryBumps( bool ownsRelic, bool ordinaryBump, bool artifactHit, float roll, float expected) { Assert.That(PassiveRelicRules.ApplyTwinHornDamage( 10f, ownsRelic, ordinaryBump, artifactHit, 0.15f, roll), Is.EqualTo(expected)); } [Test] public void ZeroDamagePulseEcho_HasNoCastOrPlayerSideEffects() { GameObject owner = new("Passive Relic Echo Test Player"); ActiveArtifactDefinition pulse = ScriptableObject.CreateInstance(); try { owner.AddComponent(); owner.AddComponent(); ActiveArtifactController artifacts = owner.AddComponent(); SetField(pulse, "artifactId", "pulse-test"); SetField(pulse, "effect", ActiveArtifactEffect.Pulse); SetField(pulse, "normalDamage", 0f); artifacts.Configure(new[] { pulse }); Assert.That(artifacts.TryAddArtifact(pulse), Is.True); typeof(ActiveArtifactController) .GetField("k__BackingField", BindingFlags.Instance | BindingFlags.NonPublic) ?.SetValue(artifacts, 12f); float gaugeBefore = artifacts.CurrentGauge; Vector3 positionBefore = owner.transform.position; int successfulUses = 0; artifacts.OnArtifactUseSucceeded += (_, _) => successfulUses++; bool emitted = artifacts.TryEmitPassiveRelicEcho(owner.transform.position, Vector2.right, 0.5f); Assert.That(emitted, Is.False); Assert.That(artifacts.CurrentGauge, Is.EqualTo(gaugeBefore)); Assert.That(owner.transform.position, Is.EqualTo(positionBefore)); Assert.That(artifacts.LocksMovement, Is.False); Assert.That(artifacts.IsExecutingArtifact, Is.False); Assert.That(successfulUses, Is.Zero); } finally { Object.DestroyImmediate(owner); Object.DestroyImmediate(pulse); } } private static void SetField(ActiveArtifactDefinition definition, string fieldName, T value) { typeof(ActiveArtifactDefinition) .GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic) ?.SetValue(definition, value); } [Test] public void AvailableRelics_ExcludesOwnedIdsAndPreservesCatalogOrder() { PassiveRelicId[] all = PassiveRelicRules.GetAvailableRelics( System.Array.Empty()); PassiveRelicId[] afterOne = PassiveRelicRules.GetAvailableRelics( new[] { PassiveRelicId.ResonanceCore }); PassiveRelicId[] afterAll = PassiveRelicRules.GetAvailableRelics( new[] { PassiveRelicId.TwinHornTalisman, PassiveRelicId.ResonanceCore, PassiveRelicId.RuptureSeed, }); Assert.That(all, Is.EqualTo(new[] { PassiveRelicId.TwinHornTalisman, PassiveRelicId.ResonanceCore, PassiveRelicId.RuptureSeed, })); Assert.That(afterOne, Is.EqualTo(new[] { PassiveRelicId.TwinHornTalisman, PassiveRelicId.RuptureSeed, })); Assert.That(afterAll, Is.Empty); } [Test] public void RuptureDamageAndRadiusScaleFromEnemyHealthAndPlayerBody() { Assert.That(PassiveRelicRules.GetExplosionDamage(1000f, 0.1f), Is.EqualTo(100f)); Assert.That(PassiveRelicRules.GetExplosionRadius(1.2f, 3f), Is.EqualTo(3.6f).Within(0.0001f)); } [TestCase(EnemyDamageSource.Player, true)] [TestCase(EnemyDamageSource.Tower, false)] [TestCase(EnemyDamageSource.PassiveRelicExplosion, false)] [TestCase(EnemyDamageSource.None, false)] public void PlayerKillCredit_ExcludesTowerAndRelicExplosion(EnemyDamageSource source, bool expected) { Assert.That(PassiveRelicRules.IsPlayerCreditedKill(source), Is.EqualTo(expected)); } } }