diff options
Diffstat (limited to 'src/LivingVillage.Desktop')
| -rw-r--r-- | src/LivingVillage.Desktop/Assets/jiangnan-world.png | bin | 4147 -> 4468 bytes | |||
| -rw-r--r-- | src/LivingVillage.Desktop/MapGen.fs | 91 | ||||
| -rw-r--r-- | src/LivingVillage.Desktop/VillageArt.fs | 16 |
3 files changed, 101 insertions, 6 deletions
diff --git a/src/LivingVillage.Desktop/Assets/jiangnan-world.png b/src/LivingVillage.Desktop/Assets/jiangnan-world.png Binary files differindex ea2866d..11145a8 100644 --- a/src/LivingVillage.Desktop/Assets/jiangnan-world.png +++ b/src/LivingVillage.Desktop/Assets/jiangnan-world.png diff --git a/src/LivingVillage.Desktop/MapGen.fs b/src/LivingVillage.Desktop/MapGen.fs index 63b14bc..ce9fb3a 100644 --- a/src/LivingVillage.Desktop/MapGen.fs +++ b/src/LivingVillage.Desktop/MapGen.fs @@ -34,7 +34,9 @@ module MapGen = /// P24:大图额外沿河民居数量(沿现有路网泊松散布)。默认 0,64x48/256x192 不受影响。 ExtraRiversideHouses: int /// P25:是否放置孤立装饰石块。默认 true;大图关闭,避免草地上出现游离石板 tile。 - DecorativeStones: bool } + DecorativeStones: bool + /// P26:是否沿海河岸放置河岸装饰(护岸石垒/芦苇/垂柳)。默认 false;仅大图开启。 + RiverDecorations: bool } /// P20 第二步:一条贯穿全图的东西向河道(每列恰好一段,宽度下限 RiverWidth)。 type River = @@ -50,6 +52,18 @@ module MapGen = DoorX: int DoorY: int } + /// P26 河岸装饰种类:护岸石垒 / 芦苇 / 垂柳(垂柳占上下两格)。 + type RiverDecorationKind = + | RevetmentStone + | Reeds + | Willow + + /// P26 河岸装饰:纯视觉,不写入瓦片、不影响通行;仅大图沿海河岸生成。 + type RiverDecoration = + { Kind: RiverDecorationKind + X: int + Y: int } + type Result = { Width: int Height: int @@ -61,6 +75,7 @@ module MapGen = Bridges: (int * int) list Paths: (int * int) list Buildings: Building list + Decorations: RiverDecoration list ReachableTiles: int ReachabilityOk: bool BridgeCrossingsOk: bool } @@ -76,7 +91,8 @@ module MapGen = SpawnColumns = 5 SpawnRows = 6 ExtraRiversideHouses = 0 - DecorativeStones = true } + DecorativeStones = true + RiverDecorations = false } /// P23 尺寸自适应参数:河道数量随高度增长(约每 96 行一条),其余沿用默认。 /// 64x48 / 256x192 仍得到 2 条河道,与既有输出逐字节一致;512x384 得到 4 条。 @@ -89,7 +105,8 @@ module MapGen = Height = height RiverCount = max base_.RiverCount (max 1 (height / 96)) ExtraRiversideHouses = (if large then 10 else 0) - DecorativeStones = not large } + DecorativeStones = not large + RiverDecorations = large } // ---- splitmix64 与 value noise(无外部依赖、无时钟) ---- @@ -330,6 +347,63 @@ module MapGen = addPath building.DoorX building.DoorY extraPlaced <- extraPlaced + 1 + // 5c) P26:河岸装饰(护岸石垒/芦苇/垂柳),仅大图启用。纯视觉层,不写入任何瓦片, + // 故 64x48/256x192 的 tiles checksum 与默认世界完全不受影响;装饰只落在紧邻水面的 + // 岸格,且避开路网/桥面/民居/核心区,保证不遮挡通行。 + let mutable decorations: RiverDecoration list = [] + if p.RiverDecorations then + let mutable decorRng = p.Seed ^^^ 0xD3C0DEUL + let nextDecor () = + let nextState, raw = splitmix decorRng + decorRng <- nextState + raw + let bridgeLookup = System.Collections.Generic.HashSet<int>(bridges |> List.map (fun (x, y) -> idx x y)) + let inBuildingFootprint x y = + buildings + |> List.exists (fun b -> + x >= b.Left - 1 && x <= b.Left + b.Width && y >= b.Top && y <= b.DoorY + 1) + let blocked x y = + not (inMap x y) + || tiles.[idx x y] = int GroundTile.Water + || pathSet.Contains(idx x y) + || bridgeLookup.Contains(idx x y) + || inCore x y + || inBuildingFootprint x y + let occupies (kind: RiverDecorationKind) (y: int) = + if kind = Willow then [ y; y + 1 ] else [ y ] + let tooClose (kind: RiverDecorationKind) x y = + decorations + |> List.exists (fun d -> + let dyList = occupies d.Kind d.Y + occupies kind y + |> List.exists (fun cy -> + dyList + |> List.exists (fun oy -> + let dx = d.X - x + let dy = oy - cy + dx * dx + dy * dy < 9))) + let tryPlace (kind: RiverDecorationKind) x y = + let tilesFree = + if kind = Willow then not (blocked x y) && not (blocked x (y + 1)) + else not (blocked x y) + if tilesFree && not (tooClose kind x y) then + decorations <- { Kind = kind; X = x; Y = y } :: decorations + let kindOf (roll: uint64) = + match int (roll % 3UL) with + | 1 -> Reeds + | 2 -> Willow + | _ -> RevetmentStone + for river in riverBands do + for x in 2 .. w - 3 do + let lowerRoll = nextDecor () + if lowerRoll % 3UL <> 0UL then + tryPlace (kindOf lowerRoll) x (river.CenterY + river.Width) + if nextDecor () % 4UL = 0UL then + let upperRoll = nextDecor () + let kind = kindOf upperRoll + let anchorY = if kind = Willow then river.CenterY - 2 else river.CenterY - 1 + tryPlace kind x anchorY + // 6) 30 个 spawn:核心区内的规则网格。 let sc = max 2 p.SpawnColumns let sr = max 2 p.SpawnRows @@ -385,6 +459,7 @@ module MapGen = Bridges = bridges Paths = paths Buildings = buildings + Decorations = decorations ReachableTiles = reachableTiles ReachabilityOk = spawnsOk && doorsOk && bridgesOk BridgeCrossingsOk = crossingsOk } @@ -414,6 +489,16 @@ module MapGen = .Append(building.Width).Append(':').Append(building.Height).Append(':') .Append(building.DoorX).Append(':').Append(building.DoorY).Append(';') |> ignore + builder.Append('|') |> ignore + let decorCode = function + | RevetmentStone -> 0 + | Reeds -> 1 + | Willow -> 2 + for decoration in map.Decorations do + builder + .Append(decorCode decoration.Kind).Append(':') + .Append(decoration.X).Append(':').Append(decoration.Y).Append(';') + |> ignore builder.ToString() /// 可行走判定:非水,或位于桥面上(桥面仍标记为水但可通行)。 diff --git a/src/LivingVillage.Desktop/VillageArt.fs b/src/LivingVillage.Desktop/VillageArt.fs index f303620..d2fcc1a 100644 --- a/src/LivingVillage.Desktop/VillageArt.fs +++ b/src/LivingVillage.Desktop/VillageArt.fs @@ -157,6 +157,7 @@ module VillageArt = | RoofEaveOverhangSprite | WallLatticeSprite | CourtyardDoorSprite + | RevetmentStoneSprite type private XnaColor = Microsoft.Xna.Framework.Color type private XnaRectangle = Microsoft.Xna.Framework.Rectangle @@ -499,7 +500,7 @@ module VillageArt = let characterAtlasRelativePath = "Assets/jiangnan-characters.png" let interiorAtlasRelativePath = "Assets/jiangnan-interior.png" - let worldTileCount = 31 + let worldTileCount = 32 let worldAtlasWidth = Sim.tilePixels * worldTileCount let worldAtlasHeight = Sim.tilePixels let characterAtlasWidth = 32 * 5 * 4 * 6 @@ -576,6 +577,7 @@ module VillageArt = | RoofEaveOverhangSprite -> 28 | WallLatticeSprite -> 29 | CourtyardDoorSprite -> 30 + | RevetmentStoneSprite -> 31 XnaRectangle(index * Sim.tilePixels, 0, Sim.tilePixels, Sim.tilePixels) let private tilePosition x y : TilePosition = @@ -626,13 +628,13 @@ module VillageArt = (cell: int) (map: MapGen.Result) = let bridgeSource = tileSourceRectangle BridgeSprite + let drawCell sprite x y = + spriteBatch.Draw(textures.TileAtlas, XnaRectangle(tileX x, tileY y, cell, cell), tileSourceRectangle sprite, XnaColor.White) for (x, y) in map.Bridges do spriteBatch.Draw(textures.TileAtlas, XnaRectangle(tileX x, tileY y, cell, cell), bridgeSource, XnaColor.White) for building in map.Buildings do let left = building.Left let right = building.Left + building.Width - 1 - let drawCell sprite x y = - spriteBatch.Draw(textures.TileAtlas, XnaRectangle(tileX x, tileY y, cell, cell), tileSourceRectangle sprite, XnaColor.White) match houseFacadeVariant building with | 0 -> // 变体 A(P24):黛瓦(脊/檐)+ 木墙/窗 + 门。 @@ -656,6 +658,14 @@ module VillageArt = elif (x - left) % 2 = 0 then WallLatticeSprite else WallWoodSprite drawCell wallSprite x building.DoorY + // P26 河岸装饰:护岸石垒 / 芦苇 / 垂柳(垂柳占上下两格),只在大图生成。 + for decoration in map.Decorations do + match decoration.Kind with + | MapGen.RiverDecorationKind.RevetmentStone -> drawCell RevetmentStoneSprite decoration.X decoration.Y + | MapGen.RiverDecorationKind.Reeds -> drawCell ReedSprite decoration.X decoration.Y + | MapGen.RiverDecorationKind.Willow -> + drawCell WillowTopSprite decoration.X decoration.Y + drawCell WillowBaseSprite decoration.X (decoration.Y + 1) /// 桥头灯笼瓦片:每座桥每列的最上/最下一格(桥两端),用于挂灯笼与夜间暖光。 let bridgeLanternTiles (map: MapGen.Result) : (int * int) list = |
