# -*- coding: utf-8 -*- """ 主角站姿像素画样例生成器 — Dungeon Sweeper 依据 design/art/art-bible.md: - 小人蓝 #2E6E8E 主色(§4.1) / 亮色描边+落影底盘轮廓通道(§3.1) - 3 头身候选档(§3.1 两档之一;终稿归 §5 角色批) - 窄/直立/对称/凹凸≤2,剑收于剪影内(§3.1/§3.4) - 不用红(危险专属)/金(收益专属)/紫赤 300-345° 禁入带(§4.2) 输出: hero-standing-pose.png (1x) + hero-standing-pose-preview.png (8x 四联测试条) """ from PIL import Image, ImageOps PAL = { 'O': (255, 246, 224, 255), # 亮色描边(暖白) 'b': (85, 144, 175, 255), # 蓝高光(小人蓝同族) 'B': (46, 110, 142, 255), # 小人蓝 §4.1 'd': (31, 74, 96, 255), # 蓝暗部(同族) 'S': (242, 199, 156, 255), # 肤色(暖) 's': (214, 159, 108, 255), # 肤暗部 'E': (51, 38, 27, 255), # 数字墨(眼) §4.1 'T': (138, 90, 52, 255), # 皮革(木色族 §4.4) 't': (92, 58, 30, 255), # 皮革暗部 'W': (183, 195, 203, 255), # 剑-钢 'w': (132, 147, 157, 255), # 剑-钢暗部 'G': (110, 91, 62, 255), # 落影底盘(暖深) } # 左半 16 列(col 0..15),右半水平镜像;-figure 对称剪影 LEFT = [ "................", # 0 "................", # 1 "................", # 2 ".............OOO", # 3 兜帽顶 "............OBBB", # 4 "...........Obbbb", # 5 "...........ObbBB", # 6 "...........ObBBB", # 7 "...........OSSSS", # 8 额/脸顶(加宽脸窗) "...........OSSES", # 9 "...........OSSES", # 10 眼(下移一档更亲和) "...........OSSSS", # 11 "...........OSsss", # 12 下巴暗 "...........Odddd", # 13 兜帽收底 "..........Oddddd", # 14 肩起 "..........OdBBBB", # 15 肩 "..........OdBbbB", # 16 胸(中高光) ".........OBBBbbB", # 17 臂现 ".........OdTBbbB", # 18 臂+背带 ".........OdTBbbB", # 19 ".........OdTBbbB", # 20 ".........OdTBbbB", # 21 ".........OdTBbbB", # 22 ".........OdTBbbB", # 23 ".........OdTttTW", # 24 腰带+扣(2px 居中) ".........OdTttTW", # 25 ".........OdTTTTt", # 26 双手套握柄 ".........OddBBBt", # 27 柄下 ".........OBBOWWW", # 28 剑格 ".........OBBBBBW", # 29 髋/剑身起 "..........OdBddW", # 30 腿+剑 "..........OdBddW", # 31 "..........OdBddW", # 32 "..........OdBddW", # 33 "..........OdBddW", # 34 "..........OdBddW", # 35 "..........OdBddW", # 36 "..........OTTttW", # 37 靴 "..........OTTttW", # 38 "..........OTTttW", # 39 "..........OTTttW", # 40 "..........OttttW", # 41 靴底 "..............OW", # 42 剑尖 "................", # 43 ".........GGGGGGG", # 44 落影底盘 "...........GGGGG", # 45 "................", # 46 "................", # 47 ] W, H = 32, 48 grid = [['.'] * W for _ in range(H)] for y, lft in enumerate(LEFT): for x, ch in enumerate(lft): grid[y][x] = ch grid[y][W - 1 - x] = ch # 剑身右侧 1px 暗部(col 16),避免完全对称的死平 for y in range(29, 42): if grid[y][15] == 'W': grid[y][16] = 'w' def render(scale=1): img = Image.new('RGBA', (W, H), (0, 0, 0, 0)) px = img.load() for y in range(H): for x in range(W): c = PAL.get(grid[y][x]) if c: px[x, y] = c return img.resize((W * scale, H * scale), Image.NEAREST) def on_bg(img, color): bg = Image.new('RGBA', img.size, color + (255,)) bg.alpha_composite(img) return bg one_x = render(1) one_x.save('hero-standing-pose.png') # 测试条: 掩尘底 / 裸土底 / 暗底 / 灰度(轮廓通道验证) x8 = render(8) tiles = [ on_bg(x8, (201, 175, 131)), # 掩尘 #C9AF83 on_bg(x8, (179, 154, 120)), # 裸土 #B39A78 on_bg(x8, (46, 42, 38)), # 暗底对照 ImageOps.grayscale(on_bg(x8, (201, 175, 131))).convert('RGBA'), ] GAP = 24 strip = Image.new('RGBA', (len(tiles) * x8.width + (len(tiles) + 1) * GAP, x8.height + 2 * GAP), (250, 244, 230, 255)) for i, t in enumerate(tiles): strip.alpha_composite(t, (GAP + i * (x8.width + GAP), GAP)) strip.save('hero-standing-pose-preview.png') print('done:', one_x.size, '->', strip.size)