summaryrefslogtreecommitdiff
path: root/src/LivingVillage.Desktop
diff options
context:
space:
mode:
Diffstat (limited to 'src/LivingVillage.Desktop')
-rw-r--r--src/LivingVillage.Desktop/ChineseText.fs84
-rw-r--r--src/LivingVillage.Desktop/LivingVillage.Desktop.fsproj2
-rw-r--r--src/LivingVillage.Desktop/VillageArt.fs95
3 files changed, 181 insertions, 0 deletions
diff --git a/src/LivingVillage.Desktop/ChineseText.fs b/src/LivingVillage.Desktop/ChineseText.fs
new file mode 100644
index 0000000..b0c2fc6
--- /dev/null
+++ b/src/LivingVillage.Desktop/ChineseText.fs
@@ -0,0 +1,84 @@
+namespace LivingVillage.Desktop
+
+open System
+
+module ChineseText =
+
+ type SceneKind =
+ | VillageScene
+ | Waterway
+ | WhiteWallHome
+ | StoneBridgeScene
+ | BambooGrove
+ | VegetableGardenScene
+
+ type DayNightKind =
+ | DayTime
+ | NightTime
+
+ type MenuHeadingKind =
+ | MainMenuHeading
+ | SettingsHeading
+ | ControlsHeading
+
+ type StatusHeadingKind =
+ | StatusHeading
+ | ReadyStatus
+ | PausedStatus
+
+ type ChineseCopy =
+ { SceneTitle: string
+ Ready: string }
+
+ type TextLayout =
+ { Lines: string list
+ MissingGlyphs: char list }
+
+ let sceneLabel scene =
+ match scene with
+ | VillageScene -> "江南水乡"
+ | Waterway -> "水道"
+ | WhiteWallHome -> "白墙黛瓦民居"
+ | StoneBridgeScene -> "石桥"
+ | BambooGrove -> "竹林"
+ | VegetableGardenScene -> "菜圃"
+
+ let dayNightLabel time =
+ match time with
+ | DayTime -> "白天"
+ | NightTime -> "夜晚"
+
+ let menuHeading heading =
+ match heading with
+ | MainMenuHeading -> "主菜单"
+ | SettingsHeading -> "设置"
+ | ControlsHeading -> "操作说明"
+
+ let statusHeading status =
+ match status with
+ | StatusHeading -> "状态"
+ | ReadyStatus -> "准备就绪"
+ | PausedStatus -> "已暂停"
+
+ let copy : ChineseCopy =
+ { SceneTitle = "江南水乡"
+ Ready = "准备就绪" }
+
+ let layoutText (maxWidth: int) (supported: Set<char>) (text: string) : TextLayout =
+ let width = max 1 maxWidth
+ let normalized, missing =
+ text
+ |> Seq.fold
+ (fun (characters, missingGlyphs) character ->
+ if Set.contains character supported then
+ character :: characters, missingGlyphs
+ else
+ '□' :: characters, character :: missingGlyphs)
+ ([], [])
+
+ { Lines =
+ normalized
+ |> List.rev
+ |> List.chunkBySize width
+ |> List.map (fun line -> new String(Array.ofList line))
+ MissingGlyphs = List.rev missing }
diff --git a/src/LivingVillage.Desktop/LivingVillage.Desktop.fsproj b/src/LivingVillage.Desktop/LivingVillage.Desktop.fsproj
index fbb7c4a..171ba71 100644
--- a/src/LivingVillage.Desktop/LivingVillage.Desktop.fsproj
+++ b/src/LivingVillage.Desktop/LivingVillage.Desktop.fsproj
@@ -7,6 +7,8 @@
<ItemGroup>
<Compile Include="VillagePresentation.fs" />
+ <Compile Include="VillageArt.fs" />
+ <Compile Include="ChineseText.fs" />
<Compile Include="MenuState.fs" />
<Compile Include="Interaction.fs" />
<Compile Include="M6Presentation.fs" />
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 }