From afefc9b6ed4f464753c1009c6594557b07b325e9 Mon Sep 17 00:00:00 2001 From: "Somhairle H. Marisol" Date: Mon, 28 Sep 2026 16:33:23 +0800 Subject: art(p40): 石板/红灯笼新格 + 重绘乌篷船与石灯笼(672x32, 21 格) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - make-cc0-art.py: 新增 draw_paving(19)/draw_red_lantern(20);重绘 draw_boat(14/15) 放大船体 + 醒目拱形竹篷,重绘 draw_stone_lantern(16) 三段石结构强化 - 索引 0-13、17-18 逐字节不变(git show 复核);二次生成 byte-identical - docs/素材来源.md 更新为 21 格并新增第 5 节 P40 --- scripts/make-cc0-art.py | 196 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 142 insertions(+), 54 deletions(-) (limited to 'scripts') diff --git a/scripts/make-cc0-art.py b/scripts/make-cc0-art.py index 7c5868c..351c20a 100644 --- a/scripts/make-cc0-art.py +++ b/scripts/make-cc0-art.py @@ -227,6 +227,19 @@ CARROT = (216, 126, 54, 255) CABBAGE = (120, 168, 84, 255) CLOTH = (208, 198, 172, 255) +# --- P40 palette: paving slab, Wu Peng bamboo awning, red paper lantern ------- +PAVE = (146, 144, 136, 255) +PAVE_HI = (188, 186, 176, 255) +PAVE_DARK = (98, 97, 92, 255) +PAVE_JOINT = (72, 72, 70, 255) +BAMBOO = (44, 58, 34, 255) +BAMBOO_HI = (86, 104, 62, 255) +BAMBOO_RIB = (28, 38, 22, 255) +RED_LANTERN = (208, 58, 46, 255) +RED_LANTERN_HI = (238, 104, 84, 255) +RED_LANTERN_DARK = (150, 34, 28, 255) +GOLD = (232, 196, 96, 255) + def draw_bridge_center(c, ox): """Arch bridge span: stone deck with a railing and an arched underside. @@ -269,69 +282,139 @@ def draw_bridge_end(c, ox): def draw_boat(c, ox, right_half): - """Wu Peng boat half (2 tiles wide): dark hull + curved bamboo awning. + """Wu Peng boat half (2 tiles wide): dark hull + tall curved bamboo awning. - Drawn as two halves so a 2-tile boat reads as one continuous silhouette. - The hull floats on the water: its top edge (gunwale) is 4px down, so open - water ripple stays visible above the boat on the tile. + P40: the boat was scaled up to fill the 64x32 footprint so the wu peng (拱形 + 竹篷) reads at a glance from a bridge or bank. Each half is 32px wide; the + awning is a tall elliptical bamboo arch spanning the middle of the 2-tile + boat with visible ribs, and the hull is a solid dark band with an upturned + bow/stern tip. The gunwale sits 8px down, leaving open water ripple above. """ c.fill(ox, 0, 32, 32, CLEAR) + # --- Hull: solid dark band spanning the full cell, 8px down to 30px. --- + c.fill(ox, 8, 32, 22, BOAT_HULL) + c.fill(ox, 8, 32, 2, BOAT_HULL_DARK) + c.fill(ox, 28, 32, 2, BOAT_HULL_DARK) + # --- Awning (wu peng): tall bamboo arch over the middle of the 2-tile boat. --- + # Arc rises from the outer edge up to a high ridge, so both halves join into + # one continuous arch. Left half: low at x=0, ridge near the inner (right) edge. + arc = [ + (0, 12), (2, 9), (4, 6), (6, 4), (8, 3), + (10, 2), (12, 1), (14, 1), (16, 1), (18, 1), + (20, 1), (22, 1), (24, 2), (26, 3), (28, 4), + (30, 5), (32, 6), + ] if not right_half: - # Left half: bow rising to the left, hull tapering. - for i in range(26): - half = 3 + i // 3 - c.fill(ox + 0, 10 + i, half, 1, BOAT_HULL) - c.fill(ox, 10, 3, 1, BOAT_HULL_DARK) - c.fill(ox + 12, 9, 20, 2, BOAT_HULL_DARK) - c.fill(ox + 12, 11, 20, 13, BOAT_HULL) - # Awning (wu peng) left edge, arch top. - c.fill(ox + 12, 1, 20, 8, BOAT_ROOF) - c.fill(ox + 12, 1, 20, 2, BOAT_ROOF_HI) - c.fill(ox + 12, 1, 2, 8, BOAT_ROOF_HI) - c.fill(ox + 12, 8, 20, 2, BOAT_HULL_DARK) - # Stern post at the extreme left of the visible half. - c.fill(ox, 6, 2, 6, BOAT_HULL_DARK) + # Left half: bow tip at the far left, rising; awning covers inner 20px. + for i in range(24): + half = 4 + i // 2 + c.fill(ox, 8 + i, min(32, half), 1, BOAT_HULL) + c.fill(ox, 6, 4, 3, BOAT_HULL_DARK) + c.fill(ox, 5, 2, 3, BOAT_HULL) + for (x, top) in arc: + c.fill(ox + x, top, 1, 32 - top, BAMBOO) + c.fill(ox + 12, 1, 20, 2, BAMBOO_HI) + for rib in (14, 18, 22, 26, 30): + c.fill(ox + rib, 1, 1, 30, BAMBOO_RIB) + c.fill(ox + 12, 30, 20, 2, BAMBOO_RIB) + c.fill(ox + 12, 6, 2, 24, BAMBOO_HI) else: - # Right half: stern rising to the right, mirrored continuous hull. - for i in range(26): - half = 3 + i // 3 - c.fill(ox + 32 - half, 10 + i, half, 1, BOAT_HULL) - c.fill(ox + 29, 10, 3, 1, BOAT_HULL_DARK) - c.fill(ox, 9, 20, 2, BOAT_HULL_DARK) - c.fill(ox, 11, 20, 13, BOAT_HULL) - c.fill(ox, 1, 20, 8, BOAT_ROOF) - c.fill(ox, 1, 20, 2, BOAT_ROOF_HI) - c.fill(ox + 18, 1, 2, 8, BOAT_ROOF_HI) - c.fill(ox, 8, 20, 2, BOAT_HULL_DARK) - c.fill(ox + 30, 6, 2, 6, BOAT_HULL_DARK) + # Right half: stern tip at the far right, mirrored continuous hull. + for i in range(24): + half = 4 + i // 2 + c.fill(ox + 32 - min(32, half), 8 + i, min(32, half), 1, BOAT_HULL) + c.fill(ox + 28, 6, 4, 3, BOAT_HULL_DARK) + c.fill(ox + 30, 5, 2, 3, BOAT_HULL) + for (x, top) in arc: + c.fill(ox + 31 - x, top, 1, 32 - top, BAMBOO) + c.fill(ox, 1, 20, 2, BAMBOO_HI) + for rib in (1, 5, 9, 13, 17): + c.fill(ox + rib, 1, 1, 30, BAMBOO_RIB) + c.fill(ox, 30, 20, 2, BAMBOO_RIB) + c.fill(ox + 18, 6, 2, 24, BAMBOO_HI) + + +def draw_paving(c, ox): + """P40 stone paving slab band: a coherent, walkable-looking stone tile. + + Used only where the ground follows a traffic path / doorway / bridge approach + (see VillageArt.cc0PavingTiles); the renderer drops the isolated decorative + scatter so paving reads as a continuous band instead of lone slabs. + """ + c.fill(ox, 0, 32, 32, PAVE) + # Irregular slab joints: a few horizontal seams + staggered verticals so the + # tile tiles seamlessly into a band without a repeating checker look. + for y in (8, 20, 30): + c.hline(ox, y, 32, PAVE_JOINT) + for (x, y0, y1) in [(10, 0, 8), (22, 8, 20), (6, 20, 30), (18, 20, 30), (27, 0, 8)]: + c.vline(ox + x, y0, y1 - y0, PAVE_JOINT) + # Bright top-left highlights on a couple of slabs, dark bottom-right corners. + c.fill(ox + 1, 1, 8, 1, PAVE_HI) + c.fill(ox + 13, 9, 8, 1, PAVE_HI) + c.fill(ox + 23, 21, 8, 1, PAVE_HI) + c.set(ox + 9, 7, PAVE_DARK) + c.set(ox + 21, 19, PAVE_DARK) + c.set(ox + 31, 31, PAVE_DARK) def draw_stone_lantern(c, ox): - """Ishidoro-style stone lantern with a warm lit window (night glow anchor). + """Ishidoro-style stone lantern: three clear tiers (base / chamber / roof). - The lit pane is a bright warm core (alpha opaque) sitting inside a stone - housing, so the renderer's radial glow layer adds a real halo on top. + P40: the three tiers are drawn with distinct stone bands and outlines so the + stone lantern form is unmistakable and clearly differs from the red paper + lantern (index 20). A bright warm pane anchors the renderer's existing P14 + premultiplied-alpha radial glow (no new lighting system). + """ + c.fill(ox, 0, 32, 32, CLEAR) + # Tier 1 — base: wide plinth + a narrower pedestal drum. + c.fill(ox + 6, 27, 20, 5, LANTERN_STONE) + c.fill(ox + 6, 27, 20, 1, STONE_HI) + c.fill(ox + 6, 31, 20, 1, STONE_DARK) + c.fill(ox + 10, 23, 12, 4, LANTERN_STONE) + c.fill(ox + 10, 23, 12, 1, STONE_HI) + c.fill(ox + 13, 17, 6, 6, LANTERN_STONE) + # Tier 2 — light chamber: wide stone housing with a bright warm pane. + c.fill(ox + 5, 8, 22, 10, LANTERN_STONE) + c.fill(ox + 5, 8, 22, 1, STONE_HI) + c.fill(ox + 5, 17, 22, 1, STONE_DARK) + c.fill(ox + 8, 10, 16, 7, LANTERN_GLOW) + c.fill(ox + 10, 11, 5, 5, LANTERN_GLOW_HI) + c.vline(ox + 8, 10, 7, STONE_DARK) + c.vline(ox + 23, 10, 7, STONE_DARK) + c.vline(ox + 15, 10, 7, STONE_DARK) + # Tier 3 — roof cap: flared stone eave + finial. + c.fill(ox + 3, 5, 26, 3, STONE_DARK) + c.fill(ox + 6, 3, 20, 2, LANTERN_STONE) + c.fill(ox + 6, 3, 20, 1, STONE_HI) + c.fill(ox + 14, 0, 4, 3, LANTERN_STONE) + c.set(ox + 15, 0, STONE_HI) + + +def draw_red_lantern(c, ox): + """P40 red paper lantern: base / lit chamber / roof in the same three tiers. + + A round red paper body with a golden tassel base and a dark wood cap; the + warm core reuses the same renderer glow path as the stone lantern, but the + silhouette (round red body vs grey stone housing) keeps the two distinct. """ c.fill(ox, 0, 32, 32, CLEAR) - # Base plinth. - c.fill(ox + 8, 27, 16, 5, LANTERN_STONE) - c.fill(ox + 8, 27, 16, 1, STONE_HI) - c.fill(ox + 11, 24, 10, 4, LANTERN_STONE) - # Shaft. - c.fill(ox + 14, 18, 4, 7, LANTERN_STONE) - # Light housing with a bright warm pane. - c.fill(ox + 6, 9, 20, 10, LANTERN_STONE) - c.fill(ox + 6, 9, 20, 1, STONE_HI) - c.fill(ox + 9, 11, 14, 7, LANTERN_GLOW) - c.fill(ox + 11, 12, 4, 5, LANTERN_GLOW_HI) - # Dark frame over the pane. - c.vline(ox + 9, 11, 7, STONE_DARK) - c.vline(ox + 22, 11, 7, STONE_DARK) - c.vline(ox + 15, 11, 7, STONE_DARK) - # Cap + finial. - c.fill(ox + 4, 6, 24, 4, STONE_DARK) - c.fill(ox + 6, 5, 20, 2, LANTERN_STONE) - c.fill(ox + 15, 1, 3, 4, LANTERN_STONE) + # Tier 1 — base: small wooden foot + short golden tassel (bottom rows stay + # clear so the door tile underneath is still readable when hung at a door). + c.fill(ox + 12, 22, 8, 2, WOOD_DARK) + c.fill(ox + 14, 19, 4, 3, GOLD) + c.fill(ox + 15, 17, 2, 2, GOLD) + # Tier 2 — paper body: round red lantern with vertical ribs and a lit core. + c.fill(ox + 7, 8, 18, 12, RED_LANTERN) + c.fill(ox + 6, 11, 20, 6, RED_LANTERN) + c.fill(ox + 9, 10, 14, 8, RED_LANTERN_HI) + c.fill(ox + 12, 12, 8, 5, (255, 226, 168, 255)) + for rib in (10, 14, 18, 22): + c.vline(ox + rib, 9, 10, RED_LANTERN_DARK) + c.fill(ox + 7, 19, 18, 1, RED_LANTERN_DARK) + # Tier 3 — roof: dark wooden cap + finial loop. + c.fill(ox + 9, 6, 14, 2, WOOD_DARK) + c.fill(ox + 12, 4, 8, 2, WOOD) + c.fill(ox + 15, 1, 2, 3, WOOD_DARK) def draw_stall(c, ox): @@ -393,8 +476,10 @@ def build_cc0_tile_atlas(): P34 block so every older index stays unchanged and backwards compatible: 12 bridge centre / 13 bridge end / 14 boat left / 15 boat right 16 stone lantern / 17 market stall / 18 reeds + P40 appends indices 19-20 (paving slab / red lantern): + 19 paving slab / 20 red lantern """ - c = Canvas(608, 32) + c = Canvas(672, 32) packed = Image.open(THIRD / "kenney_tiny-farm" / "Tilemap" / "tilemap_packed.png").convert("RGBA") def tile(grid_x, grid_y): @@ -420,6 +505,9 @@ def build_cc0_tile_atlas(): draw_stone_lantern(c, 16 * 32) draw_stall(c, 17 * 32) draw_reeds(c, 18 * 32) + # P40 appended block. + draw_paving(c, 19 * 32) + draw_red_lantern(c, 20 * 32) return c @@ -474,7 +562,7 @@ def build_cc0_character_atlas(): def main(): ASSETS.mkdir(parents=True, exist_ok=True) targets = [ - ("cc0-tiles.png", build_cc0_tile_atlas(), 608, 32), + ("cc0-tiles.png", build_cc0_tile_atlas(), 672, 32), ("cc0-characters.png", build_cc0_character_atlas(), 3840, 48), ] for name, canvas, w, h in targets: -- cgit v1.2.3