summaryrefslogtreecommitdiff
path: root/scripts
diff options
context:
space:
mode:
authorSomhairle H. Marisol <[email protected]>2026-09-28 16:33:23 +0800
committerSomhairle H. Marisol <[email protected]>2026-09-28 16:33:23 +0800
commitafefc9b6ed4f464753c1009c6594557b07b325e9 (patch)
tree73b704e3410af4f0fa65f807a4368d987258147d /scripts
parentcefe17a63d54762c2e59c62d5198984c39cdc896 (diff)
downloadliving-village-afefc9b6ed4f464753c1009c6594557b07b325e9.tar.gz
art(p40): 石板/红灯笼新格 + 重绘乌篷船与石灯笼(672x32, 21 格)
- 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
Diffstat (limited to 'scripts')
-rw-r--r--scripts/make-cc0-art.py196
1 files changed, 142 insertions, 54 deletions
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: