This repository has been archived on 2026-10-29. You can view files and clone it, but you cannot make any changes to its state, such as pushing and creating new issues, pull requests or comments.
EveUnity/packages/org.gamecult.eve.unity-scene/Tests/Editor/EveUnityCombatPresentationTests.cs
2026-07-16 16:15:27 +01:00

126 lines
5.9 KiB
C#

using System;
using System.Collections.Generic;
using System.Reflection;
using NUnit.Framework;
using UnityEngine;
#nullable enable
namespace GameCult.Eve.UnityScene.Tests
{
public sealed class EveUnityCombatPresentationTests
{
[Test]
public void ProjectionPreservesProviderSelectionLockMetersAndFossilTiming()
{
var projection = Projection(Combat());
var combat = EveUnityCombatPresentation.Find(projection);
Assert.That(combat, Is.Not.Null);
Assert.That(combat!.ControlledEntityIndex, Is.EqualTo(4));
Assert.That(combat.SelectedTargetEntityId, Is.EqualTo("run.zone.0.entity.9"));
Assert.That(combat.TargetVisible, Is.True);
Assert.That(combat.TargetHostile, Is.True);
Assert.That(combat.LockProgress, Is.EqualTo(0.6f).Within(0.0001f));
Assert.That(combat.RadialFill(0), Is.EqualTo(0.25f).Within(0.0001f));
Assert.That(combat.RadialFill(1), Is.EqualTo(0.75f).Within(0.0001f));
Assert.That(combat.HitMarkerDurationSeconds, Is.EqualTo(0.25f));
}
[Test]
public void HitMarkerRequiresControlledSourceSelectedTargetAndAuthoritativeDamage()
{
var combat = new EveUnityCombatPresentation(Combat());
Assert.That(combat.PresentsHit(Shot(4, 9, true, 12, 0)), Is.True);
Assert.That(combat.PresentsHit(Shot(3, 9, true, 12, 0)), Is.False);
Assert.That(combat.PresentsHit(Shot(4, 8, true, 12, 0)), Is.False);
Assert.That(combat.PresentsHit(Shot(4, 9, false, 0, 0)), Is.False);
Assert.That(combat.PresentsHit(Shot(4, 9, true, 0, 0)), Is.False);
Assert.That(combat.PresentsHit(Shot(4, 9, true, 0, 12)), Is.True);
}
[Test]
public void HitMarkerRetainsAuthoritativeEndpointAfterTargetLeavesTheWorld()
{
var root = new GameObject("combat-hit-retention-test");
try
{
var renderer = root.AddComponent<EveUnityCombatPresentationRenderer>();
var presentation = new EveUnityCombatPresentation(Combat());
typeof(EveUnityCombatPresentationRenderer)
.GetField("_lastPresentation", BindingFlags.Instance | BindingFlags.NonPublic)!
.SetValue(renderer, presentation);
typeof(EveUnityCombatPresentationRenderer)
.GetMethod("OnShotAvailable", BindingFlags.Instance | BindingFlags.NonPublic)!
.Invoke(renderer, new object[] { Shot(4, 9, true, 12, 0, "7.5,-3.25") });
typeof(EveUnityCombatPresentationRenderer)
.GetField("_hitUntil", BindingFlags.Instance | BindingFlags.NonPublic)!
.SetValue(renderer, Time.unscaledTime - 1f);
renderer.RefreshNow();
Assert.That(renderer.HitMarkerVisible, Is.True,
"A destroyed target must not erase receipt-owned hit feedback.");
var line = root.transform.Find("Eve combat presentation/Hit marker A")!
.GetComponent<LineRenderer>();
var center = (line.GetPosition(0) + line.GetPosition(1)) * 0.5f;
Assert.That(center.x, Is.EqualTo(7.5f).Within(0.0001f));
Assert.That(center.z, Is.EqualTo(-3.25f).Within(0.0001f));
}
finally
{
UnityEngine.Object.DestroyImmediate(root);
}
}
private static EveUnitySceneProjection Projection(params EveUnitySceneNode[] children) =>
new EveUnitySceneProjection("provider", "surface", "world", "commands", "receipt", "provider", null,
Node("root", "surface", children));
private static EveUnitySceneNode Combat() => Node("combat", "combat.presentation", new Dictionary<string, string>(StringComparer.Ordinal)
{
["controlledEntityId"] = "run.zone.0.entity.4",
["controlledEntityIndex"] = "4",
["selectedTargetEntityId"] = "run.zone.0.entity.9",
["selectedTargetEntityIndex"] = "9",
["targetVisible"] = "true",
["targetHostile"] = "true",
["contactInformation"] = "0.8",
["shieldRatio"] = "0.4",
["hullRatio"] = "0.9",
["lockProgress"] = "0.6",
["hitMarkerDurationSeconds"] = "0.25",
["radialFillMinimum"] = "0.25",
["radialFillMaximum"] = "0.75"
});
private static EveUnityShotReceipt Shot(
int source,
int target,
bool hit,
double damage,
double shield,
string endpoint = "0,0") =>
new EveUnityShotReceipt(Node("shot", "shot.receipt", new Dictionary<string, string>(StringComparer.Ordinal)
{
["shotId"] = Guid.NewGuid().ToString("N"),
["sourceEntityIndex"] = source.ToString(),
["targetEntityIndex"] = target.ToString(),
["hit"] = hit.ToString(),
["appliedDamage"] = damage.ToString(System.Globalization.CultureInfo.InvariantCulture),
["shieldAbsorbedDamage"] = shield.ToString(System.Globalization.CultureInfo.InvariantCulture),
["endpoint"] = endpoint
}));
private static EveUnitySceneNode Node(string id, string kind, params EveUnitySceneNode[] children) =>
Node(id, kind, Empty(), children);
private static EveUnitySceneNode Node(string id, string kind, IReadOnlyDictionary<string, string> props, params EveUnitySceneNode[] children) =>
new EveUnitySceneNode(id, kind, "scene-node", props, Empty(), Empty(), 0, 0,
Array.Empty<EveUnitySceneEmbeddedDocumentSlot>(), null, children);
private static IReadOnlyDictionary<string, string> Empty() =>
new Dictionary<string, string>(StringComparer.Ordinal);
}
}