From 9e946c77725787fa128956e27d6af70be1ecad83 Mon Sep 17 00:00:00 2001 From: ud18010 Date: Thu, 30 Jul 2026 13:13:49 +0800 Subject: [PATCH] test: cover actor behavior targeting shapes --- .../ActorBehavior/BehaviorTargetingTests.cs | 141 ++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 Server/Framework.Shared.Tests/ActorBehavior/BehaviorTargetingTests.cs diff --git a/Server/Framework.Shared.Tests/ActorBehavior/BehaviorTargetingTests.cs b/Server/Framework.Shared.Tests/ActorBehavior/BehaviorTargetingTests.cs new file mode 100644 index 00000000..b0e78d4b --- /dev/null +++ b/Server/Framework.Shared.Tests/ActorBehavior/BehaviorTargetingTests.cs @@ -0,0 +1,141 @@ +using Xunit; +using XWorld.Framework.ActorBehavior; + +namespace XWorld.Framework.Tests.ActorBehavior +{ + public class BehaviorTargetingTests + { + [Theory] + [InlineData("sector_test", 2)] + [InlineData("sphere_test", 2)] + [InlineData("ring_test", 3)] + [InlineData("line_test", 2)] + public void LogicEffectShapes_SelectExpectedTargets(string behaviorId, int expectedTarget) + { + BehaviorWorld world = TestTargetWorld.Create(); + + world.TryStartBehavior(1, behaviorId, BehaviorInput.FromDirection(1, 0)); + world.Tick(0.01f); + + Assert.Contains(world.DrainEvents(), e => + e.Kind == BehaviorEventKind.AttributeEffectRequested && e.TargetActorId == expectedTarget); + } + + [Fact] + public void LockedEffect_UsesTargetActorInput() + { + BehaviorWorld world = TestTargetWorld.Create(); + + world.TryStartBehavior(1, "locked_test", BehaviorInput.FromTargetActor(3)); + world.Tick(0.01f); + + Assert.Contains(world.DrainEvents(), e => + e.Kind == BehaviorEventKind.AttributeEffectRequested && e.TargetActorId == 3); + } + } + + internal static class TestTargetWorld + { + public static BehaviorWorld Create() + { + BehaviorConfigCatalog catalog = BehaviorConfigCatalog.LoadFromJson( + "fighter", + Skill, + EmptyFlyObject, + EmptyBuff); + var world = new BehaviorWorld(new BehaviorWorldOptions { Mode = BehaviorRunMode.ServerLogic }); + world.RegisterCatalog(catalog); + world.AddActor(new BehaviorActorState { ActorId = 1, CatalogType = "fighter", TeamId = 1, Position = new BehaviorVector3(0, 0, 0), Forward = new BehaviorVector3(1, 0, 0), Radius = 0.3f, Alive = true }); + world.AddActor(new BehaviorActorState { ActorId = 2, CatalogType = "fighter", TeamId = 2, Position = new BehaviorVector3(1, 0, 0), Forward = new BehaviorVector3(-1, 0, 0), Radius = 0.3f, Alive = true }); + world.AddActor(new BehaviorActorState { ActorId = 3, CatalogType = "fighter", TeamId = 2, Position = new BehaviorVector3(3, 0, 0), Forward = new BehaviorVector3(-1, 0, 0), Radius = 0.3f, Alive = true }); + world.AddActor(new BehaviorActorState { ActorId = 4, CatalogType = "fighter", TeamId = 2, Position = new BehaviorVector3(0, 0, 2), Forward = new BehaviorVector3(0, 0, -1), Radius = 0.3f, Alive = true }); + return world; + } + + private const string EmptyFlyObject = @"{ + ""type"": ""fighter"", + ""version"": 1, + ""flyObjects"": [] + }"; + + private const string EmptyBuff = @"{ + ""type"": ""fighter"", + ""version"": 1, + ""buffs"": [] + }"; + + private const string Skill = @"{ + ""type"": ""fighter"", + ""version"": 1, + ""behaviors"": [ + { + ""id"": ""sector_test"", + ""duration"": 0.1, + ""canBeInterrupted"": true, + ""timeline"": [{ ""time"": 0, ""kind"": ""LogicEffect"", ""effectId"": ""sector_hit"" }], + ""logicEffects"": [{ + ""id"": ""sector_hit"", + ""shape"": ""Sector"", + ""radius"": 2, + ""angle"": 90, + ""target"": ""Enemy"", + ""effects"": [{ ""type"": ""Attribute"", ""attribute"": ""hp"", ""value"": -1 }] + }] + }, + { + ""id"": ""sphere_test"", + ""duration"": 0.1, + ""canBeInterrupted"": true, + ""timeline"": [{ ""time"": 0, ""kind"": ""LogicEffect"", ""effectId"": ""sphere_hit"" }], + ""logicEffects"": [{ + ""id"": ""sphere_hit"", + ""shape"": ""Sphere"", + ""radius"": 1.5, + ""target"": ""Enemy"", + ""effects"": [{ ""type"": ""Attribute"", ""attribute"": ""hp"", ""value"": -1 }] + }] + }, + { + ""id"": ""ring_test"", + ""duration"": 0.1, + ""canBeInterrupted"": true, + ""timeline"": [{ ""time"": 0, ""kind"": ""LogicEffect"", ""effectId"": ""ring_hit"" }], + ""logicEffects"": [{ + ""id"": ""ring_hit"", + ""shape"": ""Ring"", + ""minRadius"": 2, + ""radius"": 3.5, + ""target"": ""Enemy"", + ""effects"": [{ ""type"": ""Attribute"", ""attribute"": ""hp"", ""value"": -1 }] + }] + }, + { + ""id"": ""line_test"", + ""duration"": 0.1, + ""canBeInterrupted"": true, + ""timeline"": [{ ""time"": 0, ""kind"": ""LogicEffect"", ""effectId"": ""line_hit"" }], + ""logicEffects"": [{ + ""id"": ""line_hit"", + ""shape"": ""Line"", + ""radius"": 2, + ""width"": 0.5, + ""target"": ""Enemy"", + ""effects"": [{ ""type"": ""Attribute"", ""attribute"": ""hp"", ""value"": -1 }] + }] + }, + { + ""id"": ""locked_test"", + ""duration"": 0.1, + ""canBeInterrupted"": true, + ""timeline"": [{ ""time"": 0, ""kind"": ""LogicEffect"", ""effectId"": ""locked_hit"" }], + ""logicEffects"": [{ + ""id"": ""locked_hit"", + ""shape"": ""Locked"", + ""target"": ""HitActor"", + ""effects"": [{ ""type"": ""Attribute"", ""attribute"": ""hp"", ""value"": -1 }] + }] + } + ] + }"; + } +}