diff options
| author | Somhairle H. Marisol <[email protected]> | 2026-09-28 08:42:59 +0800 |
|---|---|---|
| committer | Somhairle H. Marisol <[email protected]> | 2026-09-28 08:42:59 +0800 |
| commit | fd429fffb9328515beb120da2807faf5305a6e0c (patch) | |
| tree | 3af2346a538fc4a4c05afce602c2030615b88e6c /src/LivingVillage.Desktop/VillageArt.fs | |
| parent | 39f5c7d9951fb4c3ab8cb151665233ac1b6df76a (diff) | |
| download | living-village-fd429fffb9328515beb120da2807faf5305a6e0c.tar.gz | |
feat(art): P34 素材扩展切片(水面/屋顶/墙体/栏杆)
- cc0-tiles.png 由 4 格扩到 12 格:0-3 仍 Kenney 地面,4-11 为自绘构件
(4 水A/5 水B/6 屋脊/7 屋檐/8 木构墙/9 木格窗/10 栏杆/11 门)
- 自绘 2 相位水面 cc0WaterTileIndexAt=(tick/32+x+2y)%2,单帧覆盖双相位;
河道与水田/水网共用;江南 fallback 不变
- drawWorld 接入:cc0 模式下民居屋顶/墙/窗/门、桥栏杆、水面走真素材,其余道具仍江南
- AssetPackTests 更新图集尺寸(12 格)+整数缩放,新增构件索引与双水相位用例
- docs/素材来源.md 记录自绘清单;同步 assets-and-licenses 与维护说明 P34 小节
- docs/evidence/p34-*.png 昼/夜/2x 特写/水波/对照(check-no-white-blocks 0 命中)
Diffstat (limited to 'src/LivingVillage.Desktop/VillageArt.fs')
| -rw-r--r-- | src/LivingVillage.Desktop/VillageArt.fs | 95 |
1 files changed, 81 insertions, 14 deletions
diff --git a/src/LivingVillage.Desktop/VillageArt.fs b/src/LivingVillage.Desktop/VillageArt.fs index 9a034aa..80eed07 100644 --- a/src/LivingVillage.Desktop/VillageArt.fs +++ b/src/LivingVillage.Desktop/VillageArt.fs @@ -553,7 +553,20 @@ module VillageArt = let cc0TileAtlasRelativePath = "Assets/cc0-tiles.png" // Source ground tiles are 16x16, upscaled exactly 2x to one Sim.tilePixels cell. let cc0TileSourcePixels = 16 - let cc0TileAtlasWidth = Sim.tilePixels * 4 + // P34 structure slice: indices into cc0-tiles.png. Kenney Tiny Farm ships no + // water / roof / wall / railing / door art, so indices 4-11 are self-drawn + // (provenance in docs/素材来源.md), laid out by this fixed contract. + let cc0TileCount = 12 + let cc0TileIndexWaterA = 4 + let cc0TileIndexWaterB = 5 + let cc0TileIndexRoofRidge = 6 + let cc0TileIndexRoofEave = 7 + let cc0TileIndexWallWood = 8 + let cc0TileIndexWallWindow = 9 + let cc0TileIndexRailing = 10 + let cc0TileIndexDoor = 11 + let cc0WaterFrameCount = 2 + let cc0TileAtlasWidth = Sim.tilePixels * cc0TileCount let cc0TileAtlasHeight = Sim.tilePixels let cc0CharacterAtlasRelativePath = "Assets/cc0-characters.png" // Source Puny band is 16x24, upscaled exactly 2x to a 32x48 cell; atlas packs @@ -568,6 +581,16 @@ module VillageArt = let cc0CharacterAtlasWidth = cc0CharacterCellWidth * cc0CharacterVariantCount * cc0CharacterDirectionCount * cc0CharacterFrameCount + /// Deterministic 2-phase water per tile: `(tick/32 + x + 2y) mod 2`, so one frame + /// shows both ripple phases across the river. Mirrors the 3-phase Jiangnan + /// `waterFrameTickAt`, but only two self-drawn CC0 frames exist. + let cc0WaterTileIndexAt (tick: int64) (tileX: int) (tileY: int) : int = + let phase = int ((tick / 32L + int64 (tileX + 2 * tileY)) % 2L) + if phase = 0 then cc0TileIndexWaterA else cc0TileIndexWaterB + + let cc0TileRect (index: int) : XnaRectangle = + XnaRectangle(index * Sim.tilePixels, 0, Sim.tilePixels, Sim.tilePixels) + /// Ground atlas selection: `Some cc0-tiles.png` when the pack is on, else `None` /// so the renderer keeps its procedural Jiangnan ground as the fallback. let selectedGroundAtlasRelativePath () : string option = @@ -792,6 +815,20 @@ module VillageArt = let draw position sprite = drawWorldTile spriteBatch textures camera viewportWidth viewportHeight position sprite tint + // P34: with the CC0 pack on, individual sample structures draw from the + // real/self-drawn cc0-tiles atlas; otherwise every draw falls back to jiangnan. + let drawCc0At (position: TilePosition) (index: int) = + match textures.Cc0TileAtlas with + | Some cc0 -> + drawCc0Tile + spriteBatch + cc0 + tint + (cc0TileRect index) + (position.X * Sim.tilePixels - int camera.X) + (position.Y * Sim.tilePixels - int camera.Y) + | None -> () + // Each water tile picks its phase from its own world coordinates, so a single // frame shows the three ripple phases across the river instead of one global frame. let waterSpriteAt (position: TilePosition) = @@ -800,15 +837,41 @@ module VillageArt = | 1 -> WaterSpriteB | _ -> WaterSpriteC + // P34 water: the self-drawn CC0 atlas only has two frames, so it picks the + // 2-phase index; the jiangnan atlas keeps its 3-phase ripple. + let drawWater (position: TilePosition) = + match textures.Cc0TileAtlas with + | Some _ -> drawCc0At position (cc0WaterTileIndexAt tick position.X position.Y) + | None -> draw position (waterSpriteAt position) + + // P34 structures: real/self-drawn roof, wall, window and door cells replace + // the jiangnan house tiles only when the CC0 pack is on. + let houseSpriteAt (position: TilePosition) (sprite: TileSprite) = + let cc0Index = + match textures.Cc0TileAtlas with + | Some _ -> + match sprite with + | RoofRidgeSprite -> Some cc0TileIndexRoofRidge + | RoofEaveSprite -> Some cc0TileIndexRoofEave + | WallWoodSprite -> Some cc0TileIndexWallWood + | WallWindowSprite -> Some cc0TileIndexWallWindow + | DoorSprite -> Some cc0TileIndexDoor + | _ -> None + | None -> None + match cc0Index with + | Some index -> drawCc0At position index + | None -> draw position sprite + // Procedural terrain for the 512x384 large world: water/pebble/peat codes map // onto the jiangnan atlas sprites with deterministic ripple continuity. // 生成器世界:1 水 / 2 石板路 / 3 泥炭 / 0 草地。泥炭按草地渲染,路走石板贴图。 let drawTerrainTile (position: TilePosition) (code: int) = match code with - | 1 -> draw position (waterSpriteAt position) + | 1 -> drawWater position | 2 -> draw position StonePathSprite - // P30 乡村空区:水田(4)走静态水面素材、菜畦(5)走菜地素材;均为成熟 tile。 - | 4 -> draw position WaterSprite + // P30 乡村空区:水田(4)走水波动画、菜畦(5)走菜地素材;均为成熟 tile。 + // P34:水田/水网在 CC0 包下同样走自绘 2 相位水面。 + | 4 -> drawWater position | 5 -> draw position GardenSprite | _ -> draw position GrassSprite @@ -851,7 +914,7 @@ module VillageArt = for localPosition, kind in planGrounds do let position = worldTile plan localPosition match kind with - | Water -> draw position (waterSpriteAt position) + | Water -> drawWater position | StonePath -> match textures.Cc0TileAtlas with | Some cc0 -> drawCc0Tile spriteBatch cc0 tint cc0PathRect (position.X * Sim.tilePixels - int camera.X) (position.Y * Sim.tilePixels - int camera.Y) @@ -860,18 +923,22 @@ module VillageArt = let drawHouse doorTile = let position = worldTile plan doorTile for dx in -2 .. 1 do - draw (offsetTile position dx -2) RoofRidgeSprite - draw (offsetTile position dx -1) RoofEaveSprite - draw (offsetTile position -2 0) WallWoodSprite - draw (offsetTile position -1 0) WallWindowSprite - draw (offsetTile position 0 0) DoorSprite - draw (offsetTile position 1 0) WallWoodSprite + houseSpriteAt (offsetTile position dx -2) RoofRidgeSprite + houseSpriteAt (offsetTile position dx -1) RoofEaveSprite + houseSpriteAt (offsetTile position -2 0) WallWoodSprite + houseSpriteAt (offsetTile position -1 0) WallWindowSprite + houseSpriteAt (offsetTile position 0 0) DoorSprite + houseSpriteAt (offsetTile position 1 0) WallWoodSprite let drawBridge localPosition = let position = worldTile plan localPosition - draw position BridgeSprite - draw (offsetTile position 0 1) BridgeSprite - draw (offsetTile position 0 2) BridgeSprite + let bridgeTile p = + match textures.Cc0TileAtlas with + | Some _ -> drawCc0At p cc0TileIndexRailing + | None -> draw p BridgeSprite + bridgeTile position + bridgeTile (offsetTile position 0 1) + bridgeTile (offsetTile position 0 2) let drawBamboo localPosition = let position = worldTile plan localPosition |
