diff options
| author | Somhairle H. Marisol <[email protected]> | 2026-09-20 21:58:31 +0800 |
|---|---|---|
| committer | Somhairle H. Marisol <[email protected]> | 2026-09-20 21:58:31 +0800 |
| commit | 0ea26337830ada438df144b3a51f429f00dc1967 (patch) | |
| tree | 6bfb937ca92eab143e466412143375571b05cd83 /src/LivingVillage.Desktop/VillageArt.fs | |
| parent | c64779031d8459c44d78916ab6e38e80d2532a1c (diff) | |
| download | living-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.fs | 95 |
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 } |
