summaryrefslogtreecommitdiff
path: root/src/LivingVillage.Desktop.Tests
diff options
context:
space:
mode:
Diffstat (limited to 'src/LivingVillage.Desktop.Tests')
-rw-r--r--src/LivingVillage.Desktop.Tests/LivingVillage.Desktop.Tests.fsproj1
-rw-r--r--src/LivingVillage.Desktop.Tests/P45CharacterAnimTests.fs18
-rw-r--r--src/LivingVillage.Desktop.Tests/P72CharacterWalkTests.fs40
-rw-r--r--src/LivingVillage.Desktop.Tests/PrototypeTests.fs36
4 files changed, 73 insertions, 22 deletions
diff --git a/src/LivingVillage.Desktop.Tests/LivingVillage.Desktop.Tests.fsproj b/src/LivingVillage.Desktop.Tests/LivingVillage.Desktop.Tests.fsproj
index a736a83..432cd03 100644
--- a/src/LivingVillage.Desktop.Tests/LivingVillage.Desktop.Tests.fsproj
+++ b/src/LivingVillage.Desktop.Tests/LivingVillage.Desktop.Tests.fsproj
@@ -43,6 +43,7 @@
<Compile Include="P68MapSnapshotTests.fs" />
<Compile Include="P69CopyAuditTests.fs" />
<Compile Include="P71ApiDocsTests.fs" />
+ <Compile Include="P72CharacterWalkTests.fs" />
<Compile Include="SampleTests.fs" />
</ItemGroup>
diff --git a/src/LivingVillage.Desktop.Tests/P45CharacterAnimTests.fs b/src/LivingVillage.Desktop.Tests/P45CharacterAnimTests.fs
index eba03aa..b9241ad 100644
--- a/src/LivingVillage.Desktop.Tests/P45CharacterAnimTests.fs
+++ b/src/LivingVillage.Desktop.Tests/P45CharacterAnimTests.fs
@@ -5,7 +5,7 @@ open LivingVillage.Kernel
open LivingVillage.Kernel.Sim
open LivingVillage.Desktop
-/// P45 角色动画帧升级回归:四向 x 6 帧原创点阵、帧序列确定性、行走推进、停止回 idle。
+/// P45 角色动画帧升级回归:四向 x 8 帧(6 走 + 2 待机)原创点阵、帧序列确定性、行走推进、停止回 idle。
[<TestClass>]
type P45CharacterAnimTests () =
@@ -76,16 +76,18 @@ type P45CharacterAnimTests () =
[ VillageArt.NorthFacing; VillageArt.SouthFacing; VillageArt.WestFacing; VillageArt.EastFacing ]
|> List.map (fun d -> (rect VillageArt.IndigoRobe d (VillageArt.WalkFrame VillageArt.FrameOne)).X)
assertEqualMsg 4 (xs |> List.distinct |> List.length) "四向必须落在不同源格"
- // 同向 6 帧源格逐一不同且按序排列。
+ // 同向 8 帧源格逐一不同且按序排列。
let frameXs =
[ VillageArt.WalkFrame VillageArt.FrameOne
VillageArt.WalkFrame VillageArt.FrameTwo
VillageArt.WalkFrame VillageArt.FrameThree
VillageArt.WalkFrame VillageArt.FrameFour
+ VillageArt.WalkFrame VillageArt.FrameFive
+ VillageArt.WalkFrame VillageArt.FrameSix
VillageArt.IdleFrame VillageArt.IdleOne
VillageArt.IdleFrame VillageArt.IdleTwo ]
|> List.map (fun f -> (rect VillageArt.IndigoRobe VillageArt.SouthFacing f).X)
- assertEqualMsg 6 (frameXs |> List.distinct |> List.length) "同向 6 帧源格须互不同"
+ assertEqualMsg 8 (frameXs |> List.distinct |> List.length) "同向 8 帧源格须互不同"
assertEqualMsg frameXs (List.sort frameXs) "帧源格须按帧序递增"
[<TestMethod>]
@@ -95,18 +97,22 @@ type P45CharacterAnimTests () =
assertEqualMsg (VillageArt.WalkFrame VillageArt.FrameTwo) (VillageArt.characterFrame true 4L) "走 4"
assertEqualMsg (VillageArt.WalkFrame VillageArt.FrameThree) (VillageArt.characterFrame true 8L) "走 8"
assertEqualMsg (VillageArt.WalkFrame VillageArt.FrameFour) (VillageArt.characterFrame true 12L) "走 12"
- assertEqualMsg (VillageArt.WalkFrame VillageArt.FrameOne) (VillageArt.characterFrame true 16L) "走 16 循环"
+ assertEqualMsg (VillageArt.WalkFrame VillageArt.FrameFive) (VillageArt.characterFrame true 16L) "走 16"
+ assertEqualMsg (VillageArt.WalkFrame VillageArt.FrameSix) (VillageArt.characterFrame true 20L) "走 20"
+ assertEqualMsg (VillageArt.WalkFrame VillageArt.FrameOne) (VillageArt.characterFrame true 24L) "走 24 循环"
// 停止:无论 tick,回 idle 两帧呼吸循环。
assertEqualMsg (VillageArt.IdleFrame VillageArt.IdleOne) (VillageArt.characterFrame false 0L) "停 0"
assertEqualMsg (VillageArt.IdleFrame VillageArt.IdleTwo) (VillageArt.characterFrame false 24L) "停 24"
assertEqualMsg (VillageArt.IdleFrame VillageArt.IdleOne) (VillageArt.characterFrame false 9999L) "停任意 tick 仍在 idle"
// 玩家行走规格:方向 + 是否移动 + tick 决定帧,四向共享同一帧序(方向只换源格)。
let framesFor direction =
- [ 0L; 4L; 8L; 12L ] |> List.map (fun t -> (VillageArt.avatarSpriteSpec direction true t).Frame)
+ [ 0L; 4L; 8L; 12L; 16L; 20L ] |> List.map (fun t -> (VillageArt.avatarSpriteSpec direction true t).Frame)
let north = framesFor VillageArt.NorthFacing
assertEqualMsg [ VillageArt.WalkFrame VillageArt.FrameOne
VillageArt.WalkFrame VillageArt.FrameTwo
VillageArt.WalkFrame VillageArt.FrameThree
- VillageArt.WalkFrame VillageArt.FrameFour ] north "方向->帧序确定"
+ VillageArt.WalkFrame VillageArt.FrameFour
+ VillageArt.WalkFrame VillageArt.FrameFive
+ VillageArt.WalkFrame VillageArt.FrameSix ] north "方向->帧序确定"
for direction in [ VillageArt.SouthFacing; VillageArt.WestFacing; VillageArt.EastFacing ] do
assertEqualMsg north (framesFor direction) "四向帧序一致"
diff --git a/src/LivingVillage.Desktop.Tests/P72CharacterWalkTests.fs b/src/LivingVillage.Desktop.Tests/P72CharacterWalkTests.fs
new file mode 100644
index 0000000..781d22c
--- /dev/null
+++ b/src/LivingVillage.Desktop.Tests/P72CharacterWalkTests.fs
@@ -0,0 +1,40 @@
+namespace LivingVillage.Desktop.Tests
+
+open Microsoft.VisualStudio.TestTools.UnitTesting
+open LivingVillage.Desktop
+
+/// P72 回归:角色行走帧表完整性——四向各 8 格(6 走 + 2 待机),
+/// 六张行走帧两两可见区分(含左右过渡步),且与待机帧不同。
+[<TestClass>]
+type P72CharacterWalkTests () =
+
+ let letters variant direction frame = CharacterArt.frameLetters variant direction frame
+
+ let cellDiff (a: string list) (b: string list) : int =
+ List.zip a b
+ |> List.sumBy (fun (ra, rb) -> Seq.zip ra rb |> Seq.filter (fun (x, y) -> x <> y) |> Seq.length)
+
+ [<TestMethod>]
+ member _.WalkFrameTableHasSixDistinctPosesPerDirection () =
+ Assert.AreEqual<int>(8, CharacterArt.frameCount)
+ Assert.AreEqual<int>(6, CharacterArt.walkFrameCount)
+ Assert.AreEqual<int>(2, CharacterArt.idleFrameCount)
+ for variant in 0 .. CharacterArt.variantCount - 1 do
+ for direction in 0 .. CharacterArt.directionCount - 1 do
+ let walk = [ 0 .. CharacterArt.walkFrameCount - 1 ] |> List.map (letters variant direction)
+ // 6 张行走帧两两必须有可见差异(不允许重复姿态)。
+ for i in 0 .. walk.Length - 1 do
+ for j in i + 1 .. walk.Length - 1 do
+ Assert.IsTrue(
+ cellDiff walk.[i] walk.[j] >= 4,
+ sprintf "v%d d%d 走帧 %d 与 %d 姿态重复" variant direction i j)
+ // 行走与待机必须不同。
+ let idle = letters variant direction CharacterArt.walkFrameCount
+ Assert.IsTrue(cellDiff walk.[0] idle >= 4, "行走帧与待机帧相同")
+
+ [<TestMethod>]
+ member _.AtlasStridePlacesSixWalkThenTwoIdleCells () =
+ Assert.AreEqual<int>(5120, CharacterArt.atlasWidth)
+ Assert.AreEqual<int>(5120, VillageArt.characterAtlasWidth)
+ let stride = VillageArt.characterAtlasWidth / 32 / (5 * 4)
+ Assert.AreEqual<int>(8, stride)
diff --git a/src/LivingVillage.Desktop.Tests/PrototypeTests.fs b/src/LivingVillage.Desktop.Tests/PrototypeTests.fs
index 61fda7d..68e5b93 100644
--- a/src/LivingVillage.Desktop.Tests/PrototypeTests.fs
+++ b/src/LivingVillage.Desktop.Tests/PrototypeTests.fs
@@ -326,7 +326,7 @@ type PrototypeTests () =
Assert.IsTrue(first.PanelY + first.PanelHeight <= 720)
[<TestMethod>]
- member _.NpcVariantsAreStableAndAnimationCoversFourDirectionsAndFourFrames () =
+ member _.NpcVariantsAreStableAndAnimationCoversFourDirectionsAndSixFrames () =
let sameNpc = npcVisualVariant (NpcId 7)
let repeatedNpc = npcVisualVariant (NpcId 7)
let otherNpc = npcVisualVariant (NpcId 8)
@@ -345,7 +345,9 @@ type PrototypeTests () =
Assert.AreEqual<AnimationFrame>(FrameTwo, (VillageArt.animationFrame 4L))
Assert.AreEqual<AnimationFrame>(FrameThree, (VillageArt.animationFrame 8L))
Assert.AreEqual<AnimationFrame>(FrameFour, (VillageArt.animationFrame 12L))
- Assert.AreEqual<AnimationFrame>(FrameOne, (VillageArt.animationFrame 16L))
+ Assert.AreEqual<AnimationFrame>(FrameFive, (VillageArt.animationFrame 16L))
+ Assert.AreEqual<AnimationFrame>(FrameSix, (VillageArt.animationFrame 20L))
+ Assert.AreEqual<AnimationFrame>(FrameOne, (VillageArt.animationFrame 24L))
[<TestMethod>]
member _.VillagersUseFourDistinctVariantsDistinctFromTheAvatar () =
@@ -363,28 +365,27 @@ type PrototypeTests () =
rect.X)
|> Set.ofList
Assert.AreEqual<int>(4, variantXes.Count)
- Assert.AreEqual<int>(3840, VillageArt.characterAtlasWidth)
+ Assert.AreEqual<int>(5120, VillageArt.characterAtlasWidth)
[<TestMethod>]
- member _.WalkAnimationCoversFourFramesDeterministically () =
- // 16-tick cycle, new frame every 4 sim ticks, no wall-clock dependency.
+ member _.WalkAnimationCoversSixFramesDeterministically () =
+ // 24-tick cycle, new frame every 4 sim ticks, no wall-clock dependency.
let frames = [ 0L; 4L; 8L; 12L; 16L; 20L ] |> List.map VillageArt.animationFrame
- Assert.AreEqual<AnimationFrame list>([ FrameOne; FrameTwo; FrameThree; FrameFour; FrameOne; FrameTwo ], frames)
+ Assert.AreEqual<AnimationFrame list>([ FrameOne; FrameTwo; FrameThree; FrameFour; FrameFive; FrameSix ], frames)
let cycleFrames =
- [ 0L; 4L; 8L; 12L ]
+ [ 0L; 4L; 8L; 12L; 16L; 20L ]
|> List.map VillageArt.animationFrame
|> List.distinct
- Assert.AreEqual<AnimationFrame list>([ FrameOne; FrameTwo; FrameThree; FrameFour ], cycleFrames)
+ Assert.AreEqual<AnimationFrame list>([ FrameOne; FrameTwo; FrameThree; FrameFour; FrameFive; FrameSix ], cycleFrames)
// Every frame maps to a distinct atlas cell column across the five variants.
- // Every frame maps to a distinct atlas cell column across the five variants.
let perFrameX =
- [ FrameOne; FrameTwo; FrameThree; FrameFour ]
+ [ FrameOne; FrameTwo; FrameThree; FrameFour; FrameFive; FrameSix ]
|> List.map (fun frame ->
let rect = characterSourceRectangle { Variant = IndigoRobe; Direction = NorthFacing; Frame = WalkFrame frame }
rect.X / 32)
- Assert.AreEqual<int list>([ 0; 1; 2; 3 ], perFrameX)
+ Assert.AreEqual<int list>([ 0; 1; 2; 3; 4; 5 ], perFrameX)
// Idle breath poses occupy the two cells after the walk cycle for each (variant, direction).
let idleXes =
@@ -392,7 +393,7 @@ type PrototypeTests () =
|> List.map (fun frame ->
let rect = characterSourceRectangle { Variant = IndigoRobe; Direction = NorthFacing; Frame = IdleFrame frame }
rect.X / 32)
- Assert.AreEqual<int list>([ 4; 5 ], idleXes)
+ Assert.AreEqual<int list>([ 6; 7 ], idleXes)
[<TestMethod>]
member _.ChineseCopyProvidesFormalSceneTimeMenuAndStatusLabels () =
@@ -533,6 +534,9 @@ type PrototypeTests () =
Assert.AreEqual<CharacterFrame>(IdleFrame IdleTwo, VillageArt.characterFrame false 24L)
Assert.AreEqual<CharacterFrame>(WalkFrame FrameOne, VillageArt.characterFrame true 0L)
Assert.AreEqual<CharacterFrame>(WalkFrame FrameTwo, VillageArt.characterFrame true 4L)
+ Assert.AreEqual<CharacterFrame>(WalkFrame FrameFive, VillageArt.characterFrame true 16L)
+ Assert.AreEqual<CharacterFrame>(WalkFrame FrameSix, VillageArt.characterFrame true 20L)
+ Assert.AreEqual<CharacterFrame>(WalkFrame FrameOne, VillageArt.characterFrame true 24L)
Assert.AreEqual<CharacterFrame>(VillageArt.characterFrame true 12L, VillageArt.characterFrame true 12L)
[<TestMethod>]
@@ -550,18 +554,18 @@ type PrototypeTests () =
Assert.IsTrue(peddler.WalkWindow < farmer.WalkWindow && farmer.WalkWindow < scholar.WalkWindow)
Assert.IsTrue(peddler.IdleWindow < farmer.IdleWindow && farmer.IdleWindow < scholar.IdleWindow)
- // Same tick, three trade-specific frames (at 12 ticks: Farmer IV, Peddler II, Scholar I).
+ // Same tick, three trade-specific frames (at 12 ticks: Farmer IV, Peddler VI, Scholar V).
let frameAt trade tick = VillageArt.animationFrameFor (VillageArt.rhythmFor trade) tick
let frames =
[ VillageArt.Farmer; VillageArt.Peddler; VillageArt.Scholar ]
|> List.map (fun trade -> frameAt trade 12L)
- Assert.AreEqual<AnimationFrame list>([ FrameFour; FrameTwo; FrameOne ], frames)
+ Assert.AreEqual<AnimationFrame list>([ FrameFour; FrameSix; FrameFive ], frames)
Assert.AreEqual<VillageArt.WalkRhythm>(VillageArt.rhythmFor VillageArt.Scholar, VillageArt.rhythmFor VillageArt.Scholar)
// npcSpriteSpec now follows the NPC's own trade cadence.
Assert.AreEqual<CharacterFrame>(WalkFrame FrameFour, (VillageArt.npcSpriteSpec (NpcId 0) VillageArt.SouthFacing true 12L).Frame)
- Assert.AreEqual<CharacterFrame>(WalkFrame FrameTwo, (VillageArt.npcSpriteSpec (NpcId 1) VillageArt.SouthFacing true 12L).Frame)
- Assert.AreEqual<CharacterFrame>(WalkFrame FrameOne, (VillageArt.npcSpriteSpec (NpcId 2) VillageArt.SouthFacing true 12L).Frame)
+ Assert.AreEqual<CharacterFrame>(WalkFrame FrameSix, (VillageArt.npcSpriteSpec (NpcId 1) VillageArt.SouthFacing true 12L).Frame)
+ Assert.AreEqual<CharacterFrame>(WalkFrame FrameFive, (VillageArt.npcSpriteSpec (NpcId 2) VillageArt.SouthFacing true 12L).Frame)
[<TestMethod>]
member _.ChimneySmokeFramesAreDeterministicAndThreePhase () =