2.5 update
This commit is contained in:
@@ -0,0 +1,262 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using BumpCombat.Constants;
|
||||
using BumpCombat.Defense.Comparison;
|
||||
using NUnit.Framework;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.TestTools;
|
||||
|
||||
namespace BumpCombat.Tests
|
||||
{
|
||||
public sealed class Defense25DArtifactDropPlayModeTests
|
||||
{
|
||||
private sealed class ScheduleRecord
|
||||
{
|
||||
public readonly List<int> SpawnOrder = new();
|
||||
public readonly List<(string id, float start, float duration)> Reliefs = new();
|
||||
}
|
||||
|
||||
private GameObject worldObject;
|
||||
private Defense25DComparison world;
|
||||
private Defense25DArtifactDropConstants settings;
|
||||
private Defense25DComparisonSettings comparisonSettings;
|
||||
private Defense25DRunDefinition runDefinition;
|
||||
private float timeScaleBeforeInteractionTest;
|
||||
private bool restoreInteractionTestTimeScale;
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
worldObject = new GameObject("Artifact drop test world");
|
||||
world = worldObject.AddComponent<Defense25DComparison>();
|
||||
settings = ScriptableObject.CreateInstance<Defense25DArtifactDropConstants>();
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
{
|
||||
if (restoreInteractionTestTimeScale)
|
||||
{
|
||||
Time.timeScale = timeScaleBeforeInteractionTest;
|
||||
restoreInteractionTestTimeScale = false;
|
||||
Assert.That(Time.timeScale, Is.EqualTo(timeScaleBeforeInteractionTest));
|
||||
Debug.Log($"[ArtifactDropInteraction] teardown restored timeScale={Time.timeScale}");
|
||||
}
|
||||
|
||||
if (worldObject != null) Object.DestroyImmediate(worldObject);
|
||||
if (settings != null) Object.DestroyImmediate(settings);
|
||||
if (comparisonSettings != null) Object.DestroyImmediate(comparisonSettings);
|
||||
if (runDefinition != null) Object.DestroyImmediate(runDefinition);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DropsUseFirstActualPortalOpenAndRegisterEachReliefWindowOnlyOnce()
|
||||
{
|
||||
Assert.That(Defense25DBattlefieldDefinition.CreateDefault().TryCreateLayout(
|
||||
8026, out Defense25DRunLayout layout, out string error), Is.True, error);
|
||||
Defense25DArtifactDrops drops = new();
|
||||
drops.Bind(world, layout, settings, 419);
|
||||
List<(string id, float start, float duration)> reliefs = new();
|
||||
int spawned = 0;
|
||||
drops.ReliefWindowRequested += (id, start, duration) => reliefs.Add((id, start, duration));
|
||||
drops.DropSpawned += _ => spawned++;
|
||||
|
||||
drops.NotifyPortalOpened(0, 12.5f);
|
||||
drops.NotifyPortalOpened(1, 20f);
|
||||
drops.Step(72.49f);
|
||||
Assert.That(drops.DropStates[0].Status, Is.EqualTo(Defense25DArtifactDropStatus.Scheduled));
|
||||
|
||||
drops.Step(72.5f);
|
||||
Assert.That(drops.DropStates[0].Status, Is.EqualTo(Defense25DArtifactDropStatus.Available));
|
||||
Assert.That(drops.DropStates[0].AvailableAt, Is.EqualTo(72.5f).Within(.001f));
|
||||
|
||||
drops.Step(360f);
|
||||
drops.Step(360f);
|
||||
|
||||
Assert.That(spawned, Is.EqualTo(3));
|
||||
Assert.That(drops.DropStates.Count, Is.EqualTo(3));
|
||||
Assert.That(drops.DropStates[1].Status, Is.EqualTo(Defense25DArtifactDropStatus.Available));
|
||||
Assert.That(drops.DropStates[2].Status, Is.EqualTo(Defense25DArtifactDropStatus.Available));
|
||||
Assert.That(reliefs, Is.EqualTo(new[]
|
||||
{
|
||||
("artifact-drop-0-0", 72.5f, 12f),
|
||||
("artifact-drop-0-1", 180f, 15f),
|
||||
("artifact-drop-0-2", 360f, 15f),
|
||||
}));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void LargeDeltaAndOneSecondStepsKeepReliefWindowsAtTheirScheduledTimes()
|
||||
{
|
||||
Assert.That(Defense25DBattlefieldDefinition.CreateDefault().TryCreateLayout(
|
||||
6028, out Defense25DRunLayout layout, out string error), Is.True, error);
|
||||
|
||||
ScheduleRecord largeDelta = CaptureSchedule(layout, useOneSecondSteps: false);
|
||||
ScheduleRecord smallSteps = CaptureSchedule(layout, useOneSecondSteps: true);
|
||||
|
||||
Assert.That(largeDelta.SpawnOrder, Is.EqualTo(new[] { 0, 1 }));
|
||||
Assert.That(largeDelta.Reliefs, Is.EqualTo(new[]
|
||||
{
|
||||
("artifact-drop-0-0", 60f, 12f),
|
||||
("artifact-drop-0-1", 180f, 15f),
|
||||
}));
|
||||
Assert.That(smallSteps.SpawnOrder, Is.EqualTo(largeDelta.SpawnOrder));
|
||||
Assert.That(smallSteps.Reliefs, Is.EqualTo(largeDelta.Reliefs));
|
||||
Assert.That(largeDelta.Reliefs[0].start, Is.InRange(60f, 72f));
|
||||
Assert.That(largeDelta.Reliefs[1].start, Is.InRange(180f, 195f));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void LateFirstPortalDropIsEmittedAfterEarlierScheduledLaterDrop()
|
||||
{
|
||||
Assert.That(Defense25DBattlefieldDefinition.CreateDefault().TryCreateLayout(
|
||||
6029, out Defense25DRunLayout layout, out string error), Is.True, error);
|
||||
Defense25DArtifactDrops drops = new();
|
||||
drops.Bind(world, layout, settings, 419);
|
||||
ScheduleRecord record = new();
|
||||
drops.DropSpawned += state => record.SpawnOrder.Add(state.DropId);
|
||||
drops.ReliefWindowRequested += (id, start, duration) => record.Reliefs.Add((id, start, duration));
|
||||
|
||||
drops.NotifyPortalOpened(0, 130f);
|
||||
drops.Step(200f);
|
||||
|
||||
Assert.That(drops.DropStates[0].AvailableAt, Is.EqualTo(190f).Within(.001f));
|
||||
Assert.That(drops.DropStates[1].AvailableAt, Is.EqualTo(180f).Within(.001f));
|
||||
Assert.That(record.SpawnOrder, Is.EqualTo(new[] { 1, 0 }));
|
||||
Assert.That(record.Reliefs, Is.EqualTo(new[]
|
||||
{
|
||||
("artifact-drop-0-1", 180f, 15f),
|
||||
("artifact-drop-0-0", 190f, 12f),
|
||||
}));
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator InteractedDropSnapshotsCandidatesAndClaimsOnlyTheChosenArtifactOnce()
|
||||
{
|
||||
timeScaleBeforeInteractionTest = Time.timeScale;
|
||||
restoreInteractionTestTimeScale = true;
|
||||
Debug.Log($"[ArtifactDropInteraction] start frame={Time.frameCount} timeScale={timeScaleBeforeInteractionTest}");
|
||||
Time.timeScale = 1f;
|
||||
|
||||
comparisonSettings = Object.Instantiate(AssetDatabase.LoadAssetAtPath<Defense25DComparisonSettings>(
|
||||
"Assets/_Project/Constants/Defense25DComparison/ComparisonSettings.asset"));
|
||||
Assert.That(comparisonSettings, Is.Not.Null);
|
||||
runDefinition = Object.Instantiate(AssetDatabase.LoadAssetAtPath<Defense25DRunDefinition>(
|
||||
"Assets/_Project/Constants/Defense25DComparison/LongDefenseRun.asset"));
|
||||
Assert.That(runDefinition, Is.Not.Null);
|
||||
runDefinition.PreparationSeconds = 0f;
|
||||
world.InitializeLongRun(comparisonSettings, runDefinition, layoutSeed: 1741);
|
||||
world.AutoSpawn = false;
|
||||
world.ReadInput = false;
|
||||
Debug.Log($"[ArtifactDropInteraction] before null frame frame={Time.frameCount} timeScale={Time.timeScale}");
|
||||
yield return null;
|
||||
Debug.Log($"[ArtifactDropInteraction] before fixed update frame={Time.frameCount} timeScale={Time.timeScale}");
|
||||
yield return new WaitForFixedUpdate();
|
||||
Debug.Log($"[ArtifactDropInteraction] after fixed update frame={Time.frameCount} timeScale={Time.timeScale}");
|
||||
Assert.That(world.RunPhase, Is.EqualTo(Defense25DRunPhase.Running));
|
||||
|
||||
Defense25DArtifactDrops drops = new();
|
||||
drops.Bind(world, world.RunLayout, settings, 508);
|
||||
world.Player.ResetAt(drops.DropStates[0].Position);
|
||||
drops.NotifyPortalOpened(0, 0f);
|
||||
drops.Step(60f);
|
||||
|
||||
Assert.That(drops.TryInteract(0), Is.True);
|
||||
IReadOnlyList<BumpCombat.Player.ActiveArtifactDefinition> snapshot = drops.CurrentCandidates;
|
||||
int generation = drops.RunGeneration;
|
||||
int token = drops.CurrentChoiceToken;
|
||||
Assert.That(snapshot, Has.Count.EqualTo(2));
|
||||
Assert.That(snapshot[0].Effect, Is.Not.EqualTo(snapshot[1].Effect));
|
||||
Assert.That(drops.TryInteract(0), Is.False);
|
||||
Assert.That(drops.TryChoose(0, generation + 1, token), Is.False);
|
||||
|
||||
Assert.That(drops.TryChoose(0, generation, token), Is.True);
|
||||
Assert.That(drops.DropStates[0].Status, Is.EqualTo(Defense25DArtifactDropStatus.Claimed));
|
||||
Assert.That(world.Loadout.OwnedArtifactCount, Is.EqualTo(1));
|
||||
Assert.That(drops.TryChoose(1, generation, token), Is.False);
|
||||
Assert.That(drops.TryInteract(0), Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SeededDropPositionsUseTheActualHomeAndStayOnClearRouteFreeGround()
|
||||
{
|
||||
Assert.That(Defense25DBattlefieldDefinition.CreateDefault().TryCreateLayout(
|
||||
2718, out Defense25DRunLayout layout, out string error), Is.True, error);
|
||||
Defense25DArtifactDrops first = new();
|
||||
Defense25DArtifactDrops replay = new();
|
||||
first.Bind(world, layout, settings, 9501);
|
||||
replay.Bind(world, layout, settings, 9501);
|
||||
|
||||
Assert.That(first.DropStates.Count, Is.EqualTo(3));
|
||||
for (int i = 0; i < first.DropStates.Count; i++)
|
||||
{
|
||||
Vector2 position = first.DropStates[i].Position;
|
||||
Assert.That(position, Is.EqualTo(replay.DropStates[i].Position));
|
||||
Assert.That(layout.Bounds.Contains(position), Is.True);
|
||||
Assert.That(position.x - layout.Bounds.xMin, Is.GreaterThan(settings.DropFootprintRadius));
|
||||
Assert.That(layout.Bounds.xMax - position.x, Is.GreaterThan(settings.DropFootprintRadius));
|
||||
Assert.That(position.y - layout.Bounds.yMin, Is.GreaterThan(settings.DropFootprintRadius));
|
||||
Assert.That(layout.Bounds.yMax - position.y, Is.GreaterThan(settings.DropFootprintRadius));
|
||||
Assert.That(DistanceToRoutes(position, layout.Routes), Is.GreaterThan(.75f));
|
||||
Assert.That(DistanceToPads(position, layout.PadPositions), Is.GreaterThan(1.25f));
|
||||
Assert.That(DistanceToPads(position, layout.PortalPositions), Is.GreaterThan(1.25f));
|
||||
|
||||
float homeDistance = Vector2.Distance(position, layout.HomePosition);
|
||||
if (i == 0)
|
||||
Assert.That(homeDistance, Is.InRange(2f, 4f));
|
||||
else
|
||||
Assert.That(homeDistance, Is.InRange(18f, 26f));
|
||||
}
|
||||
|
||||
Assert.That(Vector2.Distance(first.DropStates[1].Position, first.DropStates[2].Position),
|
||||
Is.GreaterThanOrEqualTo(12f));
|
||||
}
|
||||
|
||||
private static float DistanceToRoutes(Vector2 position, IReadOnlyList<IReadOnlyList<Vector2>> routes)
|
||||
{
|
||||
float nearest = float.PositiveInfinity;
|
||||
foreach (IReadOnlyList<Vector2> route in routes)
|
||||
for (int i = 0; i < route.Count - 1; i++)
|
||||
nearest = Mathf.Min(nearest, DistanceToSegment(position, route[i], route[i + 1]));
|
||||
return nearest;
|
||||
}
|
||||
|
||||
private ScheduleRecord CaptureSchedule(Defense25DRunLayout layout, bool useOneSecondSteps)
|
||||
{
|
||||
Defense25DArtifactDrops drops = new();
|
||||
drops.Bind(world, layout, settings, 7401);
|
||||
ScheduleRecord record = new();
|
||||
drops.DropSpawned += state => record.SpawnOrder.Add(state.DropId);
|
||||
drops.ReliefWindowRequested += (id, start, duration) => record.Reliefs.Add((id, start, duration));
|
||||
drops.NotifyPortalOpened(0, 0f);
|
||||
drops.Step(59f);
|
||||
if (useOneSecondSteps)
|
||||
{
|
||||
for (int second = 60; second <= 200; second++)
|
||||
drops.Step(second);
|
||||
}
|
||||
else
|
||||
{
|
||||
drops.Step(200f);
|
||||
}
|
||||
|
||||
return record;
|
||||
}
|
||||
|
||||
private static float DistanceToPads(Vector2 position, IReadOnlyList<Vector2> pads)
|
||||
{
|
||||
float nearest = float.PositiveInfinity;
|
||||
foreach (Vector2 pad in pads)
|
||||
nearest = Mathf.Min(nearest, Vector2.Distance(position, pad));
|
||||
return nearest;
|
||||
}
|
||||
|
||||
private static float DistanceToSegment(Vector2 point, Vector2 start, Vector2 end)
|
||||
{
|
||||
Vector2 segment = end - start;
|
||||
float t = Mathf.Clamp01(Vector2.Dot(point - start, segment) / segment.sqrMagnitude);
|
||||
return Vector2.Distance(point, start + segment * t);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user