summaryrefslogtreecommitdiff
path: root/src/LivingVillage.Desktop/VillageArt.fs
diff options
context:
space:
mode:
authorSomhairle H. Marisol <[email protected]>2026-09-20 21:58:31 +0800
committerSomhairle H. Marisol <[email protected]>2026-09-20 21:58:31 +0800
commit0ea26337830ada438df144b3a51f429f00dc1967 (patch)
tree6bfb937ca92eab143e466412143375571b05cd83 /src/LivingVillage.Desktop/VillageArt.fs
parentc64779031d8459c44d78916ab6e38e80d2532a1c (diff)
downloadliving-village-0ea26337830ada438df144b3a51f429f00dc1967.tar.gz
feat(desktop): add deterministic Jiangnan art and Chinese text primitives
[Change Nature] This commit adds the Task 3a desktop presentation data slice. [New Capability] - Defines immutable Jiangnan props, water and paving tiles, NPC variants, directions, and two-frame animation metadata. - Provides formal Chinese scene, menu, status, and missing-glyph text layout primitives. [Implementation] - Keeps the new modules pure and deterministic with no GUI, network, or global mutable state. - Adds focused prototype tests, F# compile ordering, and asset/license boundary documentation. [Impact] - Extends desktop presentation APIs without modifying Kernel or Game.fs. - Leaves GUI rendering integration for Task 3b.
Diffstat (limited to 'src/LivingVillage.Desktop/VillageArt.fs')
-rw-r--r--src/LivingVillage.Desktop/VillageArt.fs95
1 files changed, 95 insertions, 0 deletions
diff --git a/src/LivingVillage.Desktop/VillageArt.fs b/src/LivingVillage.Desktop/VillageArt.fs
new file mode 100644
index 0000000..0be4786
--- /dev/null
+++ b/src/LivingVillage.Desktop/VillageArt.fs
@@ -0,0 +1,95 @@
+namespace LivingVillage.Desktop
+
+open LivingVillage.Kernel.Sim
+open LivingVillage.Desktop.VillagePresentation
+
+module VillageArt =
+
+ type PropKind =
+ | WhiteWallDarkTileHouse
+ | StoneBridge
+ | Bamboo
+ | VegetableGarden
+
+ type TileKind =
+ | RiverWater
+ | StonePaving
+
+ type ArtProp =
+ { Kind: PropKind
+ Position: TilePosition }
+
+ type ArtTile =
+ { Kind: TileKind
+ Position: TilePosition }
+
+ type JiangnanArtPlan =
+ { Props: ArtProp list
+ Tiles: ArtTile list }
+
+ type NpcVisualVariant =
+ | IndigoRobe
+ | OchreVest
+ | JadeSash
+
+ type FacingDirection =
+ | NorthFacing
+ | SouthFacing
+ | WestFacing
+ | EastFacing
+
+ type AnimationFrame =
+ | FrameOne
+ | FrameTwo
+
+ type NpcSpriteSpec =
+ { Variant: NpcVisualVariant
+ Direction: FacingDirection
+ Frame: AnimationFrame }
+
+ let private position x y : TilePosition =
+ { X = x
+ Y = y }
+
+ let private prop kind x y : ArtProp =
+ { Kind = kind
+ Position = position x y }
+
+ let private tile kind x y : ArtTile =
+ { Kind = kind
+ Position = position x y }
+
+ let samplePlan () : JiangnanArtPlan =
+ { Props =
+ [ prop WhiteWallDarkTileHouse 6 4
+ prop StoneBridge 10 4
+ prop Bamboo 3 2
+ prop VegetableGarden 8 7 ]
+ Tiles =
+ [ tile RiverWater 10 2
+ tile RiverWater 10 3
+ tile StonePaving 4 5
+ tile StonePaving 5 5
+ tile StonePaving 6 5 ] }
+
+ let private positiveModulo divisor value =
+ let remainder = value % divisor
+ if remainder < 0 then remainder + divisor else remainder
+
+ let npcVisualVariant (NpcId npcId) : NpcVisualVariant =
+ match positiveModulo 3 npcId with
+ | 0 -> IndigoRobe
+ | 1 -> OchreVest
+ | _ -> JadeSash
+
+ let private positiveModulo64 divisor value =
+ let remainder = value % divisor
+ if remainder < 0L then remainder + divisor else remainder
+
+ let animationFrame (tick: int64) : AnimationFrame =
+ if positiveModulo64 8L tick < 4L then FrameOne else FrameTwo
+
+ let npcSpriteSpec (npcId: NpcId) (direction: FacingDirection) (tick: int64) : NpcSpriteSpec =
+ { Variant = npcVisualVariant npcId
+ Direction = direction
+ Frame = animationFrame tick }