diff options
| author | Somhairle H. Marisol <[email protected]> | 2026-09-21 11:12:52 +0800 |
|---|---|---|
| committer | Somhairle H. Marisol <[email protected]> | 2026-09-21 11:12:52 +0800 |
| commit | 326575888b9c8edf0ded1dcf1c01f5c78d3eca84 (patch) | |
| tree | 1a31ec020d3db972d2e39b9e20f2d2750249fcdc /src | |
| parent | 623ee728e939663aa4c98a731dcd0dcfe58bdb90 (diff) | |
| download | living-village-326575888b9c8edf0ded1dcf1c01f5c78d3eca84.tar.gz | |
feat(desktop): 河畔植被与中式道具点缀
图集新增柳树(垂枝两段)、灌木、花丛、芦苇、石凳、灯笼七类图块并经复制规则进构建;场景加入两株垂柳、两丛灌木、两处花丛、水中两簇芦苇、河畔石凳与两盏岸边灯笼,芦苇立于水面、灯笼立于岸道,全部入图集无程序几何占位。
Diffstat (limited to 'src')
| -rw-r--r-- | src/LivingVillage.Desktop.Tests/PrototypeTests.fs | 26 | ||||
| -rw-r--r-- | src/LivingVillage.Desktop/Assets/jiangnan-world.png | bin | 1576 -> 3045 bytes | |||
| -rw-r--r-- | src/LivingVillage.Desktop/VillageArt.fs | 57 |
3 files changed, 78 insertions, 5 deletions
diff --git a/src/LivingVillage.Desktop.Tests/PrototypeTests.fs b/src/LivingVillage.Desktop.Tests/PrototypeTests.fs index 7959d27..4d698ad 100644 --- a/src/LivingVillage.Desktop.Tests/PrototypeTests.fs +++ b/src/LivingVillage.Desktop.Tests/PrototypeTests.fs @@ -301,8 +301,30 @@ type PrototypeTests () = Assert.IsTrue(footprint.Contains({ X = door.X + dx; Y = door.Y - 2 } : TilePosition), sprintf "missing ridge tile dx=%d" dx) Assert.IsTrue(footprint.Contains({ X = door.X + dx; Y = door.Y - 1 } : TilePosition), sprintf "missing eave tile dx=%d" dx) Assert.IsTrue(footprint.Contains({ X = door.X + dx; Y = door.Y } : TilePosition), sprintf "missing wall tile dx=%d" dx) - // 12 house tiles + 3 bridge + 3 bamboo + 2 garden. - Assert.AreEqual<int>(20, footprint.Count) + // 12 house + 3 bridge + 3 bamboo + 2 garden + 2x2 willow + 2 shrub + 2 flowers + 2 reeds + 1 bench + 2 lanterns. + Assert.AreEqual<int>(33, footprint.Count) + + [<TestMethod>] + member _.VegetationAndRiversidePropsDecorateTheScene () = + let plan = VillageArt.sampleRenderPlan () + let kinds = plan.Props |> List.map (fun prop -> prop.Kind) + let count kind = kinds |> List.filter (fun candidate -> candidate = kind) |> List.length + let grounds = plan.Grounds |> List.map fst |> Set.ofList + + Assert.AreEqual<int>(2, count VillageArt.WillowTree) + Assert.IsTrue(count VillageArt.ShrubPatch >= 2) + Assert.IsTrue(count VillageArt.FlowerBush >= 2) + Assert.AreEqual<int>(2, count VillageArt.ReedCluster) + Assert.AreEqual<int>(1, count VillageArt.StoneBench) + Assert.AreEqual<int>(2, count VillageArt.RiverLantern) + + // Reeds must root in water; lanterns stand on the bank path. + for prop in plan.Props do + let tile = VillageArt.worldTile plan prop.Position + match prop.Kind with + | VillageArt.ReedCluster -> Assert.IsTrue(grounds.Contains(prop.Position), "reeds must sit on water tiles") + | VillageArt.RiverLantern -> Assert.IsTrue(plan.Grounds |> List.contains (prop.Position, VillageArt.StonePath), "lanterns must stand on the bank path") + | _ -> () [<TestMethod>] member _.WaterRippleFramesAreDeterministicAndTwoPhase () = diff --git a/src/LivingVillage.Desktop/Assets/jiangnan-world.png b/src/LivingVillage.Desktop/Assets/jiangnan-world.png Binary files differindex ca4a521..23e02cd 100644 --- a/src/LivingVillage.Desktop/Assets/jiangnan-world.png +++ b/src/LivingVillage.Desktop/Assets/jiangnan-world.png diff --git a/src/LivingVillage.Desktop/VillageArt.fs b/src/LivingVillage.Desktop/VillageArt.fs index a66474b..151c73a 100644 --- a/src/LivingVillage.Desktop/VillageArt.fs +++ b/src/LivingVillage.Desktop/VillageArt.fs @@ -38,6 +38,12 @@ module VillageArt = | Bridge | BambooGrove | VegetablePatch + | WillowTree + | ShrubPatch + | FlowerBush + | ReedCluster + | StoneBench + | RiverLantern type RenderProp = { Kind: RenderPropKind @@ -103,6 +109,13 @@ module VillageArt = | WallWindowSprite | DoorSprite | WaterSpriteB + | WillowTopSprite + | WillowBaseSprite + | ShrubSprite + | FlowerSprite + | ReedSprite + | BenchSprite + | LanternSprite type private XnaColor = Microsoft.Xna.Framework.Color type private XnaRectangle = Microsoft.Xna.Framework.Rectangle @@ -175,7 +188,18 @@ module VillageArt = [ renderProp House 6 5 renderProp Bridge 6 11 renderProp BambooGrove 3 2 - renderProp VegetablePatch 8 7 ] + renderProp VegetablePatch 8 7 + renderProp WillowTree 1 9 + renderProp WillowTree 13 9 + renderProp ShrubPatch 1 7 + renderProp ShrubPatch 10 7 + renderProp FlowerBush 3 8 + renderProp FlowerBush 12 8 + renderProp ReedCluster 3 12 + renderProp ReedCluster 15 13 + renderProp StoneBench 9 11 + renderProp RiverLantern 5 11 + renderProp RiverLantern 7 14 ] Interior = { Elements = [ interiorElement Window 2 2 @@ -201,7 +225,13 @@ module VillageArt = yield position (x + dx) (y + dy) ] | Bridge -> [ position x y; position x (y + 1); position x (y + 2) ] | BambooGrove -> [ position x y; position x (y + 1); position (x + 1) y ] - | VegetablePatch -> [ position x y; position (x + 1) y ]) + | VegetablePatch -> [ position x y; position (x + 1) y ] + | WillowTree -> [ position x y; position x (y + 1) ] + | ShrubPatch + | FlowerBush + | ReedCluster + | StoneBench + | RiverLantern -> [ position x y ]) |> many let private positiveModulo divisor value = @@ -284,7 +314,7 @@ module VillageArt = let characterAtlasRelativePath = "Assets/jiangnan-characters.png" let interiorAtlasRelativePath = "Assets/jiangnan-interior.png" - let worldTileCount = 14 + let worldTileCount = 21 let worldAtlasWidth = Sim.tilePixels * worldTileCount let worldAtlasHeight = Sim.tilePixels let characterAtlasWidth = 32 * 4 * 4 * 2 @@ -327,6 +357,13 @@ module VillageArt = | WallWindowSprite -> 11 | DoorSprite -> 12 | WaterSpriteB -> 13 + | WillowTopSprite -> 14 + | WillowBaseSprite -> 15 + | ShrubSprite -> 16 + | FlowerSprite -> 17 + | ReedSprite -> 18 + | BenchSprite -> 19 + | LanternSprite -> 20 XnaRectangle(index * Sim.tilePixels, 0, Sim.tilePixels, Sim.tilePixels) let private tilePosition x y : TilePosition = @@ -413,12 +450,26 @@ module VillageArt = draw position GardenSprite draw (offsetTile position 1 0) GardenSprite + let drawWillow localPosition = + let position = worldTile plan localPosition + draw position WillowTopSprite + draw (offsetTile position 0 1) WillowBaseSprite + + let drawSingleTile localPosition sprite = + draw (worldTile plan localPosition) sprite + for prop in plan.Props do match prop.Kind with | House -> drawHouse prop.Position | Bridge -> drawBridge prop.Position | BambooGrove -> drawBamboo prop.Position | VegetablePatch -> drawGarden prop.Position + | WillowTree -> drawWillow prop.Position + | ShrubPatch -> drawSingleTile prop.Position ShrubSprite + | FlowerBush -> drawSingleTile prop.Position FlowerSprite + | ReedCluster -> drawSingleTile prop.Position ReedSprite + | StoneBench -> drawSingleTile prop.Position BenchSprite + | RiverLantern -> drawSingleTile prop.Position LanternSprite let drawCharacter (spriteBatch: SpriteBatch) |
