From ec49a835361fd0f6d92a41204dcc9547073b46ee Mon Sep 17 00:00:00 2001 From: "Somhairle H. Marisol" Date: Tue, 29 Sep 2026 06:12:57 +0800 Subject: p64: 整改桥梁栏杆可辨(深墨栏体/亮扶手/望柱)与内景四角布局(Hermes vision 复核) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../P64EvidenceTests.fs | 27 +++++++++ .../Assets/jiangnan-world.png | Bin 4512 -> 4559 bytes src/LivingVillage.Desktop/VillageArt.fs | 65 +++++++++++---------- 3 files changed, 61 insertions(+), 31 deletions(-) (limited to 'src') diff --git a/src/LivingVillage.Desktop.Tests/P64EvidenceTests.fs b/src/LivingVillage.Desktop.Tests/P64EvidenceTests.fs index 9000a04..714168a 100644 --- a/src/LivingVillage.Desktop.Tests/P64EvidenceTests.fs +++ b/src/LivingVillage.Desktop.Tests/P64EvidenceTests.fs @@ -15,6 +15,9 @@ type P64EvidenceTests () = let samplePlan () = VillageArt.sampleRenderPlan () + let rectsOverlap (a: Microsoft.Xna.Framework.Rectangle) (b: Microsoft.Xna.Framework.Rectangle) = + a.X < b.X + b.Width && b.X < a.X + a.Width && a.Y < b.Y + b.Height && b.Y < a.Y + a.Height + [] member _.OverviewUpperBandIsDenselyPopulated () = let plan = samplePlan () @@ -54,3 +57,27 @@ type P64EvidenceTests () = Assert.AreEqual(8, plan.Interior.Elements.Length) for kind in [ VillageArt.Chair; VillageArt.Stool; VillageArt.BedFoot; VillageArt.Teapot ] do Assert.IsTrue(kinds |> List.contains kind, sprintf "内景缺少 %A" kind) + + /// P64 整改:家具分布全屋四角、两两不重叠、不压中央通道。red→green 门。 + [] + member _.InteriorFurnitureIsSpreadAcrossRoomAndNonOverlapping () = + let plan = samplePlan () + let layout = VillageArt.interiorLayout 1280 720 + let placed = + plan.Interior.Elements + |> List.map (fun element -> element.Kind, VillageArt.interiorElementScreenRect layout element) + for i in 0 .. placed.Length - 2 do + for j in i + 1 .. placed.Length - 1 do + let (kindA, a) = placed.[i] + let (kindB, b) = placed.[j] + Assert.IsFalse(rectsOverlap a b, sprintf "内景家具 %A 与 %A 重叠" kindA kindB) + let midX = layout.RoomX + layout.RoomWidth / 2 + let midY = layout.RoomY + layout.RoomHeight / 2 + let quadrant (r: Microsoft.Xna.Framework.Rectangle) = + (r.X + r.Width / 2 < midX, r.Y + r.Height / 2 < midY) + let quads = placed |> List.map (fun (_, r) -> quadrant r) |> List.distinct + Assert.IsTrue(quads.Length >= 4, "家具须分布在四个象限(四角/沿墙)") + // 中央通道(房间中心点)不得被家具矩形覆盖。 + for kind, r in placed do + let coversCenter = r.X <= midX && midX < r.X + r.Width && r.Y <= midY && midY < r.Y + r.Height + Assert.IsFalse(coversCenter, sprintf "%A 压住中央通道" kind) diff --git a/src/LivingVillage.Desktop/Assets/jiangnan-world.png b/src/LivingVillage.Desktop/Assets/jiangnan-world.png index 90d21e3..220ae9f 100644 Binary files a/src/LivingVillage.Desktop/Assets/jiangnan-world.png and b/src/LivingVillage.Desktop/Assets/jiangnan-world.png differ diff --git a/src/LivingVillage.Desktop/VillageArt.fs b/src/LivingVillage.Desktop/VillageArt.fs index 031b577..76abc4a 100644 --- a/src/LivingVillage.Desktop/VillageArt.fs +++ b/src/LivingVillage.Desktop/VillageArt.fs @@ -278,16 +278,17 @@ module VillageArt = renderProp FlowerBush 2 3 renderProp FlowerBush 6 1 ] Interior = + // P64 整改:家具改排至全屋四角/沿墙(左上窗、右上床、右墙屏风、左中桌+椅、 + // 桌旁壶、左下凳),两两不重叠且不压角色进场中央通道。 { Elements = - [ interiorElement Window 2 2 - interiorElement Table 4 4 - interiorElement Bed 7 3 - interiorElement Screen 7 6 - // P64 内景下半部家具补件:椅/凳/床尾凳/柜旁壶。 - interiorElement BedFoot 5 4 - interiorElement Chair 2 5 - interiorElement Stool 6 5 - interiorElement Teapot 4 5 ] } } + [ interiorElement Window 18 1 + interiorElement Bed 33 2 + interiorElement BedFoot 33 5 + interiorElement Table 5 8 + interiorElement Chair 8 8 + interiorElement Teapot 9 8 + interiorElement Stool 5 11 + interiorElement Screen 34 11 ] } } let worldTile (plan: JiangnanRenderPlan) (localPosition: TilePosition) : TilePosition = { X = plan.Origin.X + localPosition.X @@ -1447,6 +1448,29 @@ module VillageArt = { PixelPosition.X = float32 (layout.RoomX + tileX * Sim.tilePixels + Sim.tilePixels / 2) Y = float32 (floorY + (tileY + 1) * Sim.tilePixels) } + /// P64 整改:内景单个元素在屏幕上的矩形(纯函数,供绘制与「不重叠/不挡道」回归共用)。 + let interiorElementScreenRect (layout: InteriorLayout) (element: InteriorElement) : Microsoft.Xna.Framework.Rectangle = + let floorY = interiorFloorY layout + let baseY = floorY + (element.Position.Y + 1) * Sim.tilePixels + match element.Kind with + | Window -> + XnaRectangle(layout.RoomX + element.Position.X * Sim.tilePixels - 8, layout.RoomY + 24, 48, 40) + | Table -> + XnaRectangle(layout.RoomX + element.Position.X * Sim.tilePixels - 16, baseY - 48, 64, 48) + | Bed -> + XnaRectangle(layout.RoomX + element.Position.X * Sim.tilePixels - 8, baseY - 96, 48, 96) + | Screen -> + XnaRectangle(layout.RoomX + element.Position.X * Sim.tilePixels, baseY - 96, 32, 96) + // P64 补件按其像素体量落位(低矮家具贴近地面线)。 + | Chair -> + XnaRectangle(layout.RoomX + element.Position.X * Sim.tilePixels - 8, baseY - 44, 28, 44) + | Stool -> + XnaRectangle(layout.RoomX + element.Position.X * Sim.tilePixels - 8, baseY - 22, 24, 22) + | BedFoot -> + XnaRectangle(layout.RoomX + element.Position.X * Sim.tilePixels - 16, baseY - 14, 56, 18) + | Teapot -> + XnaRectangle(layout.RoomX + element.Position.X * Sim.tilePixels - 8, baseY - 16, 20, 16) + let drawInterior (spriteBatch: SpriteBatch) (pixel: Texture2D) @@ -1481,29 +1505,8 @@ module VillageArt = let rugY = floorY + 96 spriteBatch.Draw(textures.InteriorAtlas, XnaRectangle(rugX, rugY, 96, 64), rugSource, tint) - let elementDestination (element: InteriorElement) : XnaRectangle = - let baseY = floorY + (element.Position.Y + 1) * Sim.tilePixels - match element.Kind with - | Window -> - XnaRectangle(layout.RoomX + element.Position.X * Sim.tilePixels - 8, layout.RoomY + 24, 48, 40) - | Table -> - XnaRectangle(layout.RoomX + element.Position.X * Sim.tilePixels - 16, baseY - 48, 64, 48) - | Bed -> - XnaRectangle(layout.RoomX + element.Position.X * Sim.tilePixels - 8, baseY - 96, 48, 96) - | Screen -> - XnaRectangle(layout.RoomX + element.Position.X * Sim.tilePixels, baseY - 96, 32, 96) - // P64 补件按其像素体量落位(低矮家具贴近地面线)。 - | Chair -> - XnaRectangle(layout.RoomX + element.Position.X * Sim.tilePixels - 8, baseY - 44, 28, 44) - | Stool -> - XnaRectangle(layout.RoomX + element.Position.X * Sim.tilePixels - 8, baseY - 22, 24, 22) - | BedFoot -> - XnaRectangle(layout.RoomX + element.Position.X * Sim.tilePixels - 16, baseY - 14, 56, 18) - | Teapot -> - XnaRectangle(layout.RoomX + element.Position.X * Sim.tilePixels - 8, baseY - 16, 20, 16) - for element in plan.Elements |> List.sortBy (fun element -> element.Position.Y, element.Position.X) do - spriteBatch.Draw(textures.InteriorAtlas, elementDestination element, interiorSourceRectangle element.Kind, tint) + spriteBatch.Draw(textures.InteriorAtlas, interiorElementScreenRect layout element, interiorSourceRectangle element.Kind, tint) let chestX = layout.RoomX + layout.RoomWidth - 44 let chestY = layout.RoomY + layout.RoomHeight - 36 -- cgit v1.2.3