feat: add actor behavior config catalog

This commit is contained in:
ud18010
2026-07-30 13:10:18 +08:00
parent 6a3e3d298d
commit 7ef8427e01
6 changed files with 1010 additions and 0 deletions
@@ -0,0 +1,118 @@
using System;
using Xunit;
using XWorld.Framework.ActorBehavior;
namespace XWorld.Framework.Tests.ActorBehavior
{
public class BehaviorConfigCatalogTests
{
[Fact]
public void LoadFromJson_LoadsCompleteFighterCatalog()
{
BehaviorConfigCatalog catalog = BehaviorConfigCatalog.LoadFromJson(
"fighter",
TestBehaviorJson.Skill,
TestBehaviorJson.FlyObject,
TestBehaviorJson.Buff);
Assert.Equal("fighter", catalog.Type);
Assert.Equal(1, catalog.Version);
Assert.True(catalog.Behaviors.ContainsKey("slash"));
Assert.True(catalog.FlyObjects.ContainsKey("blade_wave"));
Assert.True(catalog.Buffs.ContainsKey("slow_01"));
Assert.Equal(4, catalog.Behaviors["slash"].Timeline.Count);
}
[Fact]
public void LoadFromJson_RejectsMismatchedType()
{
string mismatchedBuff = TestBehaviorJson.Buff.Replace("\"type\": \"fighter\"", "\"type\": \"mage\"");
InvalidOperationException ex = Assert.Throws<InvalidOperationException>(() =>
BehaviorConfigCatalog.LoadFromJson("fighter", TestBehaviorJson.Skill, TestBehaviorJson.FlyObject, mismatchedBuff));
Assert.Contains("type", ex.Message, StringComparison.OrdinalIgnoreCase);
}
[Fact]
public void LoadFromJson_RejectsMissingReferences()
{
string badSkill = TestBehaviorJson.Skill.Replace("\"flyObjectId\": \"blade_wave\"", "\"flyObjectId\": \"missing_wave\"");
InvalidOperationException ex = Assert.Throws<InvalidOperationException>(() =>
BehaviorConfigCatalog.LoadFromJson("fighter", badSkill, TestBehaviorJson.FlyObject, TestBehaviorJson.Buff));
Assert.Contains("missing_wave", ex.Message);
}
}
internal static class TestBehaviorJson
{
public const string Skill = @"{
""type"": ""fighter"",
""version"": 1,
""behaviors"": [{
""id"": ""slash"",
""duration"": 0.8,
""canBeInterrupted"": true,
""input"": ""DirectionOrTarget"",
""timeline"": [
{ ""time"": 0.0, ""kind"": ""Animation"", ""animation"": ""slash_01"", ""duration"": 0.8 },
{ ""time"": 0.12, ""kind"": ""Effect"", ""prefab"": ""Assets/Game/Art/Effect/slash.prefab"", ""duration"": 0.5 },
{ ""time"": 0.25, ""kind"": ""LogicEffect"", ""effectId"": ""slash_hit"" },
{ ""time"": 0.32, ""kind"": ""Projectile"", ""flyObjectId"": ""blade_wave"" }
],
""logicEffects"": [{
""id"": ""slash_hit"",
""shape"": ""Sector"",
""radius"": 2.4,
""angle"": 90,
""target"": ""Enemy"",
""effects"": [{ ""type"": ""Attribute"", ""attribute"": ""hp"", ""value"": -30 }]
}]
}]
}";
public const string FlyObject = @"{
""type"": ""fighter"",
""version"": 1,
""flyObjects"": [{
""id"": ""blade_wave"",
""duration"": 1.5,
""movement"": { ""kind"": ""Line"", ""speed"": 6.0 },
""collision"": { ""shape"": ""Sphere"", ""radius"": 0.4, ""hitLimit"": 1 },
""timeline"": [
{ ""time"": 0.0, ""kind"": ""Effect"", ""prefab"": ""Assets/Game/Art/Effect/blade_wave.prefab"", ""duration"": 1.5 },
{ ""time"": 0.0, ""kind"": ""LogicEffect"", ""effectId"": ""blade_wave_hit"", ""trigger"": ""OnCollision"" }
],
""logicEffects"": [{
""id"": ""blade_wave_hit"",
""shape"": ""Locked"",
""target"": ""HitActor"",
""effects"": [
{ ""type"": ""Attribute"", ""attribute"": ""hp"", ""value"": -20 },
{ ""type"": ""Buff"", ""buffId"": ""slow_01"" }
]
}]
}]
}";
public const string Buff = @"{
""type"": ""fighter"",
""version"": 1,
""buffs"": [{
""id"": ""slow_01"",
""duration"": 3.0,
""tickInterval"": 1.0,
""repeatCount"": 3,
""stacking"": ""RefreshDuration"",
""logicEffects"": [{
""id"": ""slow_tick"",
""shape"": ""Locked"",
""target"": ""Owner"",
""effects"": [{ ""type"": ""Attribute"", ""attribute"": ""moveSpeed"", ""value"": -0.2 }]
}]
}]
}";
}
}