summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/LivingVillage.Desktop.Tests/PrototypeTests.fs38
-rw-r--r--src/LivingVillage.Desktop/Assets/jiangnan-world.pngbin1483 -> 1576 bytes
-rw-r--r--src/LivingVillage.Desktop/Game.fs1
-rw-r--r--src/LivingVillage.Desktop/VillageArt.fs47
4 files changed, 69 insertions, 17 deletions
diff --git a/src/LivingVillage.Desktop.Tests/PrototypeTests.fs b/src/LivingVillage.Desktop.Tests/PrototypeTests.fs
index 06b3250..7959d27 100644
--- a/src/LivingVillage.Desktop.Tests/PrototypeTests.fs
+++ b/src/LivingVillage.Desktop.Tests/PrototypeTests.fs
@@ -301,8 +301,42 @@ 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 + 2 bridge + 3 bamboo + 2 garden.
- Assert.AreEqual<int>(19, footprint.Count)
+ // 12 house tiles + 3 bridge + 3 bamboo + 2 garden.
+ Assert.AreEqual<int>(20, footprint.Count)
+
+ [<TestMethod>]
+ member _.WaterRippleFramesAreDeterministicAndTwoPhase () =
+ Assert.AreEqual<int>(0, VillageArt.waterFrameTick 0L)
+ Assert.AreEqual<int>(0, VillageArt.waterFrameTick 31L)
+ Assert.AreEqual<int>(1, VillageArt.waterFrameTick 32L)
+ Assert.AreEqual<int>(1, VillageArt.waterFrameTick 63L)
+ Assert.AreEqual<int>(0, VillageArt.waterFrameTick 64L)
+ Assert.AreEqual<int>(VillageArt.waterFrameTick 100L, VillageArt.waterFrameTick 100L)
+
+ [<TestMethod>]
+ member _.RiverBandSpansTheSceneWithBankPathsAndCrossing () =
+ let plan = VillageArt.sampleRenderPlan ()
+ let footprintOf (value: VillageArt.JiangnanRenderPlan) = VillageArt.planFootprint value
+ let waters = plan.Grounds |> List.filter (fun (_, kind) -> kind = VillageArt.Water) |> List.map fst |> Set.ofList
+ let paths = plan.Grounds |> List.filter (fun (_, kind) -> kind = VillageArt.StonePath) |> List.map fst |> Set.ofList
+ let door = scene.Homes |> List.head |> fun home -> home.Door
+
+ // The river must run the full width of the decorated scene as a readable water band.
+ Assert.AreEqual<int>(20, [ 0 .. 19 ] |> List.filter (fun x -> waters.Contains({ X = x; Y = 12 } : TilePosition)) |> List.length)
+ Assert.IsTrue(waters.Contains({ X = 6; Y = 13 } : TilePosition))
+ // Banks stay walkable stone, and the door row connects to the north bank.
+ for x in 0 .. 19 do
+ if x <> 6 && x <> 7 && x <> 8 then
+ Assert.IsTrue(paths.Contains({ X = x; Y = 11 } : TilePosition), sprintf "missing north bank x=%d" x)
+ for y in 6 .. 10 do
+ Assert.IsTrue(paths.Contains({ X = 6; Y = y } : TilePosition), sprintf "missing door connector y=%d" y)
+ // Bridge footprint crosses the full river between the banks.
+ let bridge = plan.Props |> List.find (fun prop -> prop.Kind = VillageArt.Bridge)
+ let expectedBridge : TilePosition = { X = 6; Y = 11 }
+ Assert.AreEqual<TilePosition>(expectedBridge, bridge.Position)
+ for y in 11 .. 13 do
+ let bridgeTile = VillageArt.worldTile plan { X = 6; Y = y }
+ Assert.IsTrue(footprintOf plan |> List.contains bridgeTile, sprintf "bridge tile y=%d" y)
[<TestMethod>]
member _.FacingUsesTheActualMovementOrTargetVector () =
diff --git a/src/LivingVillage.Desktop/Assets/jiangnan-world.png b/src/LivingVillage.Desktop/Assets/jiangnan-world.png
index 766405f..ca4a521 100644
--- a/src/LivingVillage.Desktop/Assets/jiangnan-world.png
+++ b/src/LivingVillage.Desktop/Assets/jiangnan-world.png
Binary files differ
diff --git a/src/LivingVillage.Desktop/Game.fs b/src/LivingVillage.Desktop/Game.fs
index a5049f3..4871e80 100644
--- a/src/LivingVillage.Desktop/Game.fs
+++ b/src/LivingVillage.Desktop/Game.fs
@@ -576,6 +576,7 @@ type LivingVillageGame() as this =
viewport.Width
viewport.Height
renderPlan
+ world.Tick
profile.WorldTint
let avatarSpec = VillageArt.avatarSpriteSpec avatarFacing world.Tick
diff --git a/src/LivingVillage.Desktop/VillageArt.fs b/src/LivingVillage.Desktop/VillageArt.fs
index 5883a97..a66474b 100644
--- a/src/LivingVillage.Desktop/VillageArt.fs
+++ b/src/LivingVillage.Desktop/VillageArt.fs
@@ -102,6 +102,7 @@ module VillageArt =
| WallWoodSprite
| WallWindowSprite
| DoorSprite
+ | WaterSpriteB
type private XnaColor = Microsoft.Xna.Framework.Color
type private XnaRectangle = Microsoft.Xna.Framework.Rectangle
@@ -143,15 +144,7 @@ module VillageArt =
{ Origin = position 25 18
Grounds =
- [ ground Water 10 2
- ground Water 10 3
- ground Water 10 4
- ground Water 10 5
- ground Water 10 6
- ground Water 10 7
- ground Water 11 4
- ground Water 11 5
- ground StonePath 3 5
+ [ ground StonePath 3 5
ground StonePath 4 5
ground StonePath 5 5
ground StonePath 6 5
@@ -161,10 +154,26 @@ module VillageArt =
ground StonePath 10 5
ground StonePath 11 5
ground StonePath 6 6
- ground StonePath 6 7 ]
+ ground StonePath 6 7
+ ground StonePath 6 8
+ ground StonePath 6 9
+ ground StonePath 6 10
+ for x in 0 .. 5 do
+ ground StonePath x 11
+ for x in 9 .. 19 do
+ ground StonePath x 11
+ for x in 1 .. 18 do
+ ground StonePath x 14
+ ground Water 6 11
+ ground Water 7 11
+ ground Water 8 11
+ for x in 0 .. 19 do
+ ground Water x 12
+ for x in 2 .. 17 do
+ ground Water x 13 ]
Props =
[ renderProp House 6 5
- renderProp Bridge 10 5
+ renderProp Bridge 6 11
renderProp BambooGrove 3 2
renderProp VegetablePatch 8 7 ]
Interior =
@@ -190,7 +199,7 @@ module VillageArt =
[ for dx in -2 .. 1 do
for dy in -2 .. 0 do
yield position (x + dx) (y + dy) ]
- | Bridge -> [ position x y; position x (y + 1) ]
+ | 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 ])
|> many
@@ -212,6 +221,10 @@ module VillageArt =
let animationFrame (tick: int64) : AnimationFrame =
if positiveModulo64 8L tick < 4L then FrameOne else FrameTwo
+ /// Water surface ripple phase: flips every 32 simulation ticks, fully deterministic.
+ let waterFrameTick (tick: int64) : int =
+ int (positiveModulo64 2L (tick / 32L))
+
let directionFromVector (vector: Vec2) : FacingDirection =
if abs vector.X < 0.001f && abs vector.Y < 0.001f then
SouthFacing
@@ -271,7 +284,7 @@ module VillageArt =
let characterAtlasRelativePath = "Assets/jiangnan-characters.png"
let interiorAtlasRelativePath = "Assets/jiangnan-interior.png"
- let worldTileCount = 13
+ let worldTileCount = 14
let worldAtlasWidth = Sim.tilePixels * worldTileCount
let worldAtlasHeight = Sim.tilePixels
let characterAtlasWidth = 32 * 4 * 4 * 2
@@ -313,6 +326,7 @@ module VillageArt =
| WallWoodSprite -> 10
| WallWindowSprite -> 11
| DoorSprite -> 12
+ | WaterSpriteB -> 13
XnaRectangle(index * Sim.tilePixels, 0, Sim.tilePixels, Sim.tilePixels)
let private tilePosition x y : TilePosition =
@@ -350,9 +364,11 @@ module VillageArt =
(viewportWidth: int)
(viewportHeight: int)
(plan: JiangnanRenderPlan)
+ (tick: int64)
(tint: XnaColor) =
let draw position sprite =
drawWorldTile spriteBatch textures camera viewportWidth viewportHeight position sprite tint
+ let rippleFrame = waterFrameTick tick
let tx0 = max 0 (int camera.X / Sim.tilePixels)
let ty0 = max 0 (int camera.Y / Sim.tilePixels)
@@ -366,7 +382,7 @@ module VillageArt =
let position = worldTile plan localPosition
let sprite =
match kind with
- | Water -> WaterSprite
+ | Water -> if rippleFrame = 0 then WaterSprite else WaterSpriteB
| StonePath -> StonePathSprite
draw position sprite
@@ -383,7 +399,8 @@ module VillageArt =
let drawBridge localPosition =
let position = worldTile plan localPosition
draw position BridgeSprite
- draw (offsetTile position 1 0) BridgeSprite
+ draw (offsetTile position 0 1) BridgeSprite
+ draw (offsetTile position 0 2) BridgeSprite
let drawBamboo localPosition =
let position = worldTile plan localPosition