summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/LivingVillage.Desktop.Tests/PrototypeTests.fs29
-rw-r--r--src/LivingVillage.Desktop/Assets/jiangnan-characters.pngbin2539 -> 3047 bytes
-rw-r--r--src/LivingVillage.Desktop/VillageArt.fs15
3 files changed, 38 insertions, 6 deletions
diff --git a/src/LivingVillage.Desktop.Tests/PrototypeTests.fs b/src/LivingVillage.Desktop.Tests/PrototypeTests.fs
index d77abd6..450e94b 100644
--- a/src/LivingVillage.Desktop.Tests/PrototypeTests.fs
+++ b/src/LivingVillage.Desktop.Tests/PrototypeTests.fs
@@ -324,7 +324,7 @@ type PrototypeTests () =
Assert.IsTrue(first.PanelY + first.PanelHeight <= 720)
[<TestMethod>]
- member _.NpcVariantsAreStableAndAnimationCoversFourDirectionsAndTwoFrames () =
+ member _.NpcVariantsAreStableAndAnimationCoversFourDirectionsAndFourFrames () =
let sameNpc = npcVisualVariant (NpcId 7)
let repeatedNpc = npcVisualVariant (NpcId 7)
let otherNpc = npcVisualVariant (NpcId 8)
@@ -341,7 +341,9 @@ type PrototypeTests () =
Assert.IsTrue(sprites |> List.forall (fun sprite -> sprite.Frame = FrameTwo))
Assert.AreEqual<AnimationFrame>(FrameOne, (VillageArt.animationFrame 0L))
Assert.AreEqual<AnimationFrame>(FrameTwo, (VillageArt.animationFrame 4L))
- Assert.AreEqual<AnimationFrame>(FrameOne, (VillageArt.animationFrame 8L))
+ Assert.AreEqual<AnimationFrame>(FrameThree, (VillageArt.animationFrame 8L))
+ Assert.AreEqual<AnimationFrame>(FrameFour, (VillageArt.animationFrame 12L))
+ Assert.AreEqual<AnimationFrame>(FrameOne, (VillageArt.animationFrame 16L))
[<TestMethod>]
member _.VillagersUseFourDistinctVariantsDistinctFromTheAvatar () =
@@ -359,7 +361,28 @@ type PrototypeTests () =
rect.X)
|> Set.ofList
Assert.AreEqual<int>(4, variantXes.Count)
- Assert.AreEqual<int>(1280, VillageArt.characterAtlasWidth)
+ Assert.AreEqual<int>(2560, VillageArt.characterAtlasWidth)
+
+ [<TestMethod>]
+ member _.WalkAnimationCoversFourFramesDeterministically () =
+ // 16-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)
+
+ let cycleFrames =
+ [ 0L; 4L; 8L; 12L ]
+ |> List.map VillageArt.animationFrame
+ |> List.distinct
+ Assert.AreEqual<AnimationFrame list>([ FrameOne; FrameTwo; FrameThree; FrameFour ], 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 ]
+ |> List.map (fun frame ->
+ let rect = characterSourceRectangle { Variant = IndigoRobe; Direction = NorthFacing; Frame = frame }
+ rect.X / 32)
+ Assert.AreEqual<int list>([ 0; 1; 2; 3 ], perFrameX)
[<TestMethod>]
member _.ChineseCopyProvidesFormalSceneTimeMenuAndStatusLabels () =
diff --git a/src/LivingVillage.Desktop/Assets/jiangnan-characters.png b/src/LivingVillage.Desktop/Assets/jiangnan-characters.png
index cbbe570..f6c8711 100644
--- a/src/LivingVillage.Desktop/Assets/jiangnan-characters.png
+++ b/src/LivingVillage.Desktop/Assets/jiangnan-characters.png
Binary files differ
diff --git a/src/LivingVillage.Desktop/VillageArt.fs b/src/LivingVillage.Desktop/VillageArt.fs
index db25757..6f5af07 100644
--- a/src/LivingVillage.Desktop/VillageArt.fs
+++ b/src/LivingVillage.Desktop/VillageArt.fs
@@ -84,6 +84,8 @@ module VillageArt =
type AnimationFrame =
| FrameOne
| FrameTwo
+ | FrameThree
+ | FrameFour
type NpcSpriteSpec =
{ Variant: NpcVisualVariant
@@ -250,8 +252,13 @@ module VillageArt =
let remainder = value % divisor
if remainder < 0L then remainder + divisor else remainder
+ /// Four-pose walk cycle over a 16-tick window (one pose per 4 sim ticks).
let animationFrame (tick: int64) : AnimationFrame =
- if positiveModulo64 8L tick < 4L then FrameOne else FrameTwo
+ match positiveModulo64 4L (tick / 4L) with
+ | 0L -> FrameOne
+ | 1L -> FrameTwo
+ | 2L -> FrameThree
+ | _ -> FrameFour
/// Water surface ripple phase: flips every 32 simulation ticks, fully deterministic.
let waterFrameTick (tick: int64) : int =
@@ -303,7 +310,9 @@ module VillageArt =
match spec.Frame with
| FrameOne -> 0
| FrameTwo -> 1
- let cellIndex = (variantIndex * 4 + directionIndex) * 2 + frameIndex
+ | FrameThree -> 2
+ | FrameFour -> 3
+ let cellIndex = (variantIndex * 4 + directionIndex) * 4 + frameIndex
Microsoft.Xna.Framework.Rectangle(cellIndex * 32, 0, 32, 48)
let private rgba (r: int) (g: int) (b: int) (a: int) : XnaColor = XnaColor(r, g, b, a)
@@ -320,7 +329,7 @@ module VillageArt =
let worldTileCount = 21
let worldAtlasWidth = Sim.tilePixels * worldTileCount
let worldAtlasHeight = Sim.tilePixels
- let characterAtlasWidth = 32 * 5 * 4 * 2
+ let characterAtlasWidth = 32 * 5 * 4 * 4
let characterAtlasHeight = 48
let interiorAtlasWidth = 392
let interiorAtlasHeight = 96