diff --git a/src/AcDream.Core/World/SceneryGenerator.cs b/src/AcDream.Core/World/SceneryGenerator.cs index af0be17..8e13667 100644 --- a/src/AcDream.Core/World/SceneryGenerator.cs +++ b/src/AcDream.Core/World/SceneryGenerator.cs @@ -100,10 +100,14 @@ public static class SceneryGenerator uint terrainType = (uint)((raw >> 2) & 0x1F); // bits 2-6 uint sceneType = (uint)((raw >> 11) & 0x1F); // bits 11-15 - // NOTE: retail does NOT skip based on this vertex's road bit. - // The road test happens AFTER displacement via the 4-corner - // polygonal OnRoad check (see below). Removing the - // pre-displacement early-exit restores retail behavior. + // ACME-conformant per-vertex road check (GameScene.cs:1074). + // If this vertex itself is a road vertex, skip ALL scenery + // generation for it. This is retail behavior — the earlier + // claim that retail doesn't have this check (commit 833d167) + // was wrong. The post-displacement OnRoad check below is + // independent and still applies for non-road vertices whose + // displaced position lands on the road ribbon. + if ((raw & 0x3) != 0) continue; if (terrainType >= region.TerrainInfo.TerrainTypes.Count) continue; var sceneTypeList = region.TerrainInfo.TerrainTypes[(int)terrainType].SceneTypes; @@ -300,6 +304,10 @@ public static class SceneryGenerator int i = x * VerticesPerSide + y; ushort raw = block.Terrain[i]; + // ACME-conformant per-vertex road check (GameScene.cs:1074). + // Skip the entire vertex if its road bit is set. + if ((raw & 0x3) != 0) continue; + uint terrainType = (uint)((raw >> 2) & 0x1F); uint sceneType = (uint)((raw >> 11) & 0x1F);