codepet 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +9 -2
- package/scripts/animate.py +0 -207
- package/scripts/crop_all.py +0 -46
- package/scripts/crop_expressions.py +0 -73
- package/scripts/gen_expressions.py +0 -95
- package/scripts/gen_variants.py +0 -107
- package/scripts/render_sprite.py +0 -558
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codepet",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "在编程软件里养电子宠物 — 支持 Claude Code / Codex / Cursor / VS Code 等 10+ 平台",
|
|
5
5
|
"main": "core/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -33,7 +33,14 @@
|
|
|
33
33
|
"bin/",
|
|
34
34
|
"core/",
|
|
35
35
|
"scripts/*.js",
|
|
36
|
-
"scripts
|
|
36
|
+
"scripts/img2terminal.py",
|
|
37
|
+
"scripts/img2ascii.py",
|
|
38
|
+
"scripts/img2emoji.py",
|
|
39
|
+
"scripts/polaroid.py",
|
|
40
|
+
"scripts/pet_card.py",
|
|
41
|
+
"scripts/render_to_image.py",
|
|
42
|
+
"scripts/show_all.py",
|
|
43
|
+
"scripts/show_all_expressions.py",
|
|
37
44
|
"scripts/*.sh",
|
|
38
45
|
"sprites/*.png",
|
|
39
46
|
"sprites/*/",
|
package/scripts/animate.py
DELETED
|
@@ -1,207 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""CodePet 终端动画 — 眨眼、动嘴、摆手"""
|
|
3
|
-
|
|
4
|
-
import os, sys, time, copy
|
|
5
|
-
|
|
6
|
-
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
|
|
7
|
-
sys.path.insert(0, SCRIPT_DIR)
|
|
8
|
-
|
|
9
|
-
from img2terminal import render_image
|
|
10
|
-
from PIL import Image, ImageDraw
|
|
11
|
-
|
|
12
|
-
SPRITE_DIR = os.path.join(SCRIPT_DIR, "..", "sprites")
|
|
13
|
-
RESET = "\033[0m"
|
|
14
|
-
BOLD = "\033[1m"
|
|
15
|
-
HIDE_CURSOR = "\033[?25l"
|
|
16
|
-
SHOW_CURSOR = "\033[?25h"
|
|
17
|
-
|
|
18
|
-
# ── 每个角色的关键区域(基于裁切后的图片坐标)──
|
|
19
|
-
# 格式: { 'eyes': (x1,y1,x2,y2), 'mouth': (x1,y1,x2,y2), 'arm': (x1,y1,x2,y2) }
|
|
20
|
-
# 用 None 表示没有该部位动画
|
|
21
|
-
|
|
22
|
-
REGIONS = {
|
|
23
|
-
'bibilabu': {
|
|
24
|
-
'eyes': (105, 90, 160, 110), # 猫眼睛区域
|
|
25
|
-
'mouth': (115, 110, 150, 130), # 猫嘴区域
|
|
26
|
-
},
|
|
27
|
-
'bagayalu': {
|
|
28
|
-
'eyes': (100, 100, 200, 130), # 水豚眼
|
|
29
|
-
'mouth': (120, 140, 200, 175), # 水豚嘴
|
|
30
|
-
},
|
|
31
|
-
'wodedaodun': {
|
|
32
|
-
'eyes': (60, 80, 140, 110), # 柴犬眼
|
|
33
|
-
'mouth': (80, 115, 150, 145), # 柴犬嘴
|
|
34
|
-
},
|
|
35
|
-
'bababoyi': {
|
|
36
|
-
'eyes': (60, 80, 220, 130), # 猫球大眼
|
|
37
|
-
'mouth': (110, 130, 170, 155), # 猫球嘴
|
|
38
|
-
},
|
|
39
|
-
'waibibabu': {
|
|
40
|
-
'eyes': (100, 75, 190, 100), # 壮汉眼
|
|
41
|
-
'mouth': (95, 115, 195, 155), # 壮汉嘴/胡子
|
|
42
|
-
'arm': (50, 170, 250, 250), # 壮汉手臂
|
|
43
|
-
},
|
|
44
|
-
'gugugaga': {
|
|
45
|
-
'eyes': (80, 120, 210, 155), # 刘海猫眼
|
|
46
|
-
'mouth': (120, 160, 185, 185), # 刘海猫嘴
|
|
47
|
-
},
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
def make_blink_frame(img, eye_region):
|
|
51
|
-
"""眨眼:用眼睛区域的平均肤色画一条横线盖住眼睛"""
|
|
52
|
-
frame = img.copy()
|
|
53
|
-
x1, y1, x2, y2 = eye_region
|
|
54
|
-
# 取眼睛周围的肤色(眼睛上方几像素)
|
|
55
|
-
sample_y = max(0, y1 - 5)
|
|
56
|
-
colors = []
|
|
57
|
-
for x in range(x1, min(x2, frame.width)):
|
|
58
|
-
px = frame.getpixel((x, sample_y))
|
|
59
|
-
if len(px) >= 3:
|
|
60
|
-
colors.append(px[:3])
|
|
61
|
-
if not colors:
|
|
62
|
-
return frame
|
|
63
|
-
avg = tuple(sum(c[i] for c in colors) // len(colors) for i in range(3))
|
|
64
|
-
|
|
65
|
-
draw = ImageDraw.Draw(frame)
|
|
66
|
-
# 画一条略粗的线代表闭眼
|
|
67
|
-
mid_y = (y1 + y2) // 2
|
|
68
|
-
for dy in range(-1, 2):
|
|
69
|
-
draw.line([(x1 + 3, mid_y + dy), (x2 - 3, mid_y + dy)], fill=avg + (255,), width=1)
|
|
70
|
-
return frame
|
|
71
|
-
|
|
72
|
-
def make_mouth_open(img, mouth_region):
|
|
73
|
-
"""张嘴:把嘴巴区域往下移 2px,露出深色缝隙"""
|
|
74
|
-
frame = img.copy()
|
|
75
|
-
x1, y1, x2, y2 = mouth_region
|
|
76
|
-
# 拷贝嘴巴区域
|
|
77
|
-
mouth = frame.crop((x1, y1, x2, y2))
|
|
78
|
-
# 用嘴巴上方的颜色填充原位置(模拟张开)
|
|
79
|
-
sample_y = max(0, y1 - 2)
|
|
80
|
-
for x in range(x1, min(x2, frame.width)):
|
|
81
|
-
px = frame.getpixel((x, sample_y))
|
|
82
|
-
for y in range(y1, min(y1 + 3, y2)):
|
|
83
|
-
frame.putpixel((x, y), px)
|
|
84
|
-
# 嘴巴下移
|
|
85
|
-
frame.paste(mouth, (x1, y1 + 2))
|
|
86
|
-
return frame
|
|
87
|
-
|
|
88
|
-
def make_arm_move(img, arm_region):
|
|
89
|
-
"""摆手:把手臂区域往左/右移 3px"""
|
|
90
|
-
frame = img.copy()
|
|
91
|
-
x1, y1, x2, y2 = arm_region
|
|
92
|
-
arm = frame.crop((x1, y1, x2, y2))
|
|
93
|
-
# 用背景色填原位
|
|
94
|
-
bg_color = (255, 255, 255, 0)
|
|
95
|
-
draw = ImageDraw.Draw(frame)
|
|
96
|
-
draw.rectangle([x1, y1, x2, y2], fill=bg_color)
|
|
97
|
-
# 手臂左移
|
|
98
|
-
frame.paste(arm, (x1 - 3, y1))
|
|
99
|
-
return frame
|
|
100
|
-
|
|
101
|
-
def generate_character_frames(name, img):
|
|
102
|
-
"""为每个角色生成动画序列"""
|
|
103
|
-
regions = REGIONS.get(name, {})
|
|
104
|
-
frames = [img] # 帧0: 原图
|
|
105
|
-
|
|
106
|
-
eye_r = regions.get('eyes')
|
|
107
|
-
mouth_r = regions.get('mouth')
|
|
108
|
-
arm_r = regions.get('arm')
|
|
109
|
-
|
|
110
|
-
if name == 'waibibabu':
|
|
111
|
-
# 歪比巴卜: 原图 → 摆手 → 原图 → 眨眼张嘴
|
|
112
|
-
if arm_r:
|
|
113
|
-
frames.append(make_arm_move(img, arm_r))
|
|
114
|
-
frames.append(img.copy())
|
|
115
|
-
if eye_r and mouth_r:
|
|
116
|
-
f = make_blink_frame(img, eye_r)
|
|
117
|
-
f = make_mouth_open(f, mouth_r)
|
|
118
|
-
frames.append(f)
|
|
119
|
-
elif name == 'bababoyi':
|
|
120
|
-
# 巴巴博一: 原图 → 原图 → 原图 → 眨眼(猫头鹰眨眼慢)
|
|
121
|
-
frames.append(img.copy())
|
|
122
|
-
frames.append(img.copy())
|
|
123
|
-
if eye_r:
|
|
124
|
-
frames.append(make_blink_frame(img, eye_r))
|
|
125
|
-
elif name == 'wodedaodun':
|
|
126
|
-
# 我的刀盾: 基本不动,偶尔张嘴(打哈欠)
|
|
127
|
-
frames.append(img.copy())
|
|
128
|
-
frames.append(img.copy())
|
|
129
|
-
if mouth_r:
|
|
130
|
-
frames.append(make_mouth_open(img, mouth_r))
|
|
131
|
-
else:
|
|
132
|
-
# 其他角色: 原图 → 原图 → 眨眼 → 张嘴
|
|
133
|
-
frames.append(img.copy())
|
|
134
|
-
if eye_r:
|
|
135
|
-
frames.append(make_blink_frame(img, eye_r))
|
|
136
|
-
if mouth_r:
|
|
137
|
-
frames.append(make_mouth_open(img, mouth_r))
|
|
138
|
-
|
|
139
|
-
return frames
|
|
140
|
-
|
|
141
|
-
def animate(name, width=45, fps=2, duration=0):
|
|
142
|
-
img_path = os.path.join(SPRITE_DIR, f"{name}.png")
|
|
143
|
-
if not os.path.exists(img_path):
|
|
144
|
-
print(f"图片不存在: {img_path}")
|
|
145
|
-
return
|
|
146
|
-
|
|
147
|
-
names_zh = {
|
|
148
|
-
'bibilabu': '比比拉布', 'bagayalu': '八嘎呀路',
|
|
149
|
-
'wodedaodun': '我的刀盾', 'bababoyi': '巴巴博一',
|
|
150
|
-
'waibibabu': '歪比巴卜', 'gugugaga': '咕咕嘎嘎',
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
img = Image.open(img_path).convert("RGBA")
|
|
154
|
-
|
|
155
|
-
# 生成动画帧(图片级别)
|
|
156
|
-
img_frames = generate_character_frames(name, img)
|
|
157
|
-
|
|
158
|
-
# 预渲染所有帧为终端字符串
|
|
159
|
-
rendered = []
|
|
160
|
-
for f in img_frames:
|
|
161
|
-
rendered.append(render_image(f, width))
|
|
162
|
-
|
|
163
|
-
delay = 1.0 / fps
|
|
164
|
-
frame_count = len(rendered)
|
|
165
|
-
zh = names_zh.get(name, name)
|
|
166
|
-
line_count = len(rendered[0].splitlines())
|
|
167
|
-
|
|
168
|
-
print(HIDE_CURSOR, end="")
|
|
169
|
-
print(f"\n {BOLD}{zh}{RESET}(Ctrl+C 退出)\n")
|
|
170
|
-
|
|
171
|
-
try:
|
|
172
|
-
i = 0
|
|
173
|
-
while True:
|
|
174
|
-
frame = rendered[i % frame_count]
|
|
175
|
-
for line in frame.splitlines():
|
|
176
|
-
print(f" {line}")
|
|
177
|
-
sys.stdout.flush()
|
|
178
|
-
time.sleep(delay)
|
|
179
|
-
print(f"\033[{line_count}A", end="")
|
|
180
|
-
i += 1
|
|
181
|
-
if duration > 0 and i > duration * fps:
|
|
182
|
-
break
|
|
183
|
-
except KeyboardInterrupt:
|
|
184
|
-
pass
|
|
185
|
-
finally:
|
|
186
|
-
for line in rendered[0].splitlines():
|
|
187
|
-
print(f" {line}")
|
|
188
|
-
print(SHOW_CURSOR)
|
|
189
|
-
print()
|
|
190
|
-
|
|
191
|
-
if __name__ == "__main__":
|
|
192
|
-
name_map = {
|
|
193
|
-
'比比拉布': 'bibilabu', '八嘎呀路': 'bagayalu',
|
|
194
|
-
'我的刀盾': 'wodedaodun', '巴巴博一': 'bababoyi',
|
|
195
|
-
'歪比巴卜': 'waibibabu', '咕咕嘎嘎': 'gugugaga',
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
if len(sys.argv) < 2:
|
|
199
|
-
print("用法: python3 animate.py <角色名> [宽度] [fps]")
|
|
200
|
-
print("例: python3 animate.py 歪比巴卜 45 2")
|
|
201
|
-
sys.exit(1)
|
|
202
|
-
|
|
203
|
-
name = name_map.get(sys.argv[1], sys.argv[1])
|
|
204
|
-
width = int(sys.argv[2]) if len(sys.argv) > 2 else 45
|
|
205
|
-
fps = float(sys.argv[3]) if len(sys.argv) > 3 else 1.5
|
|
206
|
-
|
|
207
|
-
animate(name, width, fps)
|
package/scripts/crop_all.py
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""从原图裁切6个角色并转为终端像素画,同时保存单独的PNG"""
|
|
3
|
-
|
|
4
|
-
import os
|
|
5
|
-
from PIL import Image
|
|
6
|
-
|
|
7
|
-
SRC = "/Users/akane/Downloads/20260401160756.jpg"
|
|
8
|
-
OUT_DIR = "/Users/akane/Projects/codepet/sprites"
|
|
9
|
-
os.makedirs(OUT_DIR, exist_ok=True)
|
|
10
|
-
|
|
11
|
-
img = Image.open(SRC)
|
|
12
|
-
W, H = img.size # 1056 x 840
|
|
13
|
-
|
|
14
|
-
# 原图布局: 2行3列 + 底部有文字
|
|
15
|
-
# 第一行: 比比拉布, 八嘎呀路, 我的刀盾 (y: 0~400)
|
|
16
|
-
# 第二行: 巴巴博一, 歪比巴卜, 咕咕嘎嘎 (y: 400~800)
|
|
17
|
-
# 每列约 352px 宽
|
|
18
|
-
|
|
19
|
-
crops = {
|
|
20
|
-
'bibilabu': (30, 10, 290, 310), # 比比拉布 — 去掉底部文字
|
|
21
|
-
'bagayalu': (350, 10, 680, 320), # 八嘎呀路
|
|
22
|
-
'wodedaodun': (720, 50, 1040, 300), # 我的刀盾
|
|
23
|
-
'bababoyi': (20, 410, 300, 670), # 巴巴博一
|
|
24
|
-
'waibibabu': (360, 400, 660, 700), # 歪比巴卜
|
|
25
|
-
'gugugaga': (720, 410, 1040, 690), # 咕咕嘎嘎
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
names_zh = {
|
|
29
|
-
'bibilabu': '比比拉布',
|
|
30
|
-
'bagayalu': '八嘎呀路',
|
|
31
|
-
'wodedaodun': '我的刀盾',
|
|
32
|
-
'bababoyi': '巴巴博一',
|
|
33
|
-
'waibibabu': '歪比巴卜',
|
|
34
|
-
'gugugaga': '咕咕嘎嘎',
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
for name, (x1, y1, x2, y2) in crops.items():
|
|
38
|
-
cropped = img.crop((x1, y1, x2, y2))
|
|
39
|
-
# 保存 PNG
|
|
40
|
-
out_path = os.path.join(OUT_DIR, f"{name}.png")
|
|
41
|
-
cropped.save(out_path)
|
|
42
|
-
print(f"[OK] {names_zh[name]} -> {out_path} ({cropped.size[0]}x{cropped.size[1]})")
|
|
43
|
-
|
|
44
|
-
print(f"\n全部保存到 {OUT_DIR}/")
|
|
45
|
-
print("接下来运行:")
|
|
46
|
-
print(" python3 scripts/img2terminal.py sprites/bibilabu.png 25")
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""从 6 张表情表裁切出 36 张单图,避开方格线和文字"""
|
|
3
|
-
|
|
4
|
-
import os
|
|
5
|
-
from PIL import Image
|
|
6
|
-
|
|
7
|
-
SRC_DIR = "/Users/akane/Projects/codepet/sprites/6"
|
|
8
|
-
OUT_DIR = "/Users/akane/Projects/codepet/sprites"
|
|
9
|
-
|
|
10
|
-
# 文件 → 角色映射
|
|
11
|
-
FILES = {
|
|
12
|
-
"1.png": "bibilabu",
|
|
13
|
-
"2.png": "bagayalu",
|
|
14
|
-
"3.png": "wodedaodun",
|
|
15
|
-
"4.png": "bababoyi",
|
|
16
|
-
"5.png": "waibibabu",
|
|
17
|
-
"6.png": "gugugaga",
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
# 表情顺序:2行3列
|
|
21
|
-
EXPRESSIONS = [
|
|
22
|
-
["normal", "happy", "sleep"],
|
|
23
|
-
["eat", "pet", "worry"],
|
|
24
|
-
]
|
|
25
|
-
|
|
26
|
-
def crop_sheet(src_path, char_id):
|
|
27
|
-
"""从一张 2x3 表情表裁切出 6 张单图"""
|
|
28
|
-
img = Image.open(src_path)
|
|
29
|
-
W, H = img.size
|
|
30
|
-
|
|
31
|
-
# 每个格子的大小
|
|
32
|
-
col_w = W // 3
|
|
33
|
-
row_h = H // 2
|
|
34
|
-
|
|
35
|
-
# 大幅内缩:避开边框线和底部英文文字
|
|
36
|
-
# 顶部缩 5%,底部缩 28%(文字+边框),左右各缩 6%
|
|
37
|
-
pad_top = int(row_h * 0.05)
|
|
38
|
-
pad_bottom = int(row_h * 0.28)
|
|
39
|
-
pad_left = int(col_w * 0.06)
|
|
40
|
-
pad_right = int(col_w * 0.06)
|
|
41
|
-
|
|
42
|
-
char_dir = os.path.join(OUT_DIR, char_id)
|
|
43
|
-
os.makedirs(char_dir, exist_ok=True)
|
|
44
|
-
|
|
45
|
-
for row_idx, row_exprs in enumerate(EXPRESSIONS):
|
|
46
|
-
for col_idx, expr in enumerate(row_exprs):
|
|
47
|
-
x1 = col_idx * col_w + pad_left
|
|
48
|
-
y1 = row_idx * row_h + pad_top
|
|
49
|
-
x2 = (col_idx + 1) * col_w - pad_right
|
|
50
|
-
y2 = (row_idx + 1) * row_h - pad_bottom
|
|
51
|
-
|
|
52
|
-
cropped = img.crop((x1, y1, x2, y2))
|
|
53
|
-
out_path = os.path.join(char_dir, f"{expr}.png")
|
|
54
|
-
cropped.save(out_path)
|
|
55
|
-
print(f" ✓ {char_id}/{expr}.png ({cropped.size[0]}x{cropped.size[1]})")
|
|
56
|
-
|
|
57
|
-
print()
|
|
58
|
-
|
|
59
|
-
def main():
|
|
60
|
-
print("\n 🔪 裁切表情表...\n")
|
|
61
|
-
for filename, char_id in FILES.items():
|
|
62
|
-
src = os.path.join(SRC_DIR, filename)
|
|
63
|
-
if not os.path.exists(src):
|
|
64
|
-
print(f" ✗ {filename} 不存在,跳过")
|
|
65
|
-
continue
|
|
66
|
-
print(f" [{char_id}] ← {filename}")
|
|
67
|
-
crop_sheet(src, char_id)
|
|
68
|
-
|
|
69
|
-
print(" ✅ 36 张裁切完成!")
|
|
70
|
-
print(" 查看: ! open ~/Projects/codepet/sprites/\n")
|
|
71
|
-
|
|
72
|
-
if __name__ == "__main__":
|
|
73
|
-
main()
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""用豆包 Seedream API 批量生成 6 角色 × 6 表情 = 36 张图"""
|
|
3
|
-
|
|
4
|
-
import os, json, time, urllib.request, urllib.error
|
|
5
|
-
|
|
6
|
-
ARK_API_KEY = os.environ.get("ARK_API_KEY", "YOUR_API_KEY_HERE")
|
|
7
|
-
API_URL = "https://ark.cn-beijing.volces.com/api/v3/images/generations"
|
|
8
|
-
MODEL = "doubao-seedream-5-0-260128"
|
|
9
|
-
OUT_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "sprites")
|
|
10
|
-
|
|
11
|
-
CHARACTERS = {
|
|
12
|
-
"bibilabu": "比比拉布, a cute cat wearing a yellow banana costume, cat face peeking out from banana peel, small grey paws, meme style",
|
|
13
|
-
"bagayalu": "八嘎呀路, a shiba inu dog sitting upright, golden brown fur, melancholic zen expression, meme painting style",
|
|
14
|
-
"wodedaodun": "我的刀盾, a shiba inu dog lying completely flat on belly like a loaf, brown fur, side view, very flat, meme painting style",
|
|
15
|
-
"bababoyi": "巴巴博一, an extremely round fat cat shaped like a ball, huge dark owl-like eyes, pink nose, grey and cream colored, meme style",
|
|
16
|
-
"waibibabu": "歪比巴卜, a cartoon muscular man with grey baseball cap, thick brown beard, white polo shirt, cartoon illustration style",
|
|
17
|
-
"gugugaga": "咕咕嘎嘎, a round chubby cat with thick dark grey bangs hair covering forehead, white face below bangs, small pink mouth, meme style",
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
EXPRESSIONS = {
|
|
21
|
-
"normal": "calm default expression, looking forward",
|
|
22
|
-
"happy": "very happy, big smile, eyes squinting with joy, sparkles around",
|
|
23
|
-
"sleep": "eyes peacefully closed, sleeping, zzZ floating above head, peaceful",
|
|
24
|
-
"eat": "mouth wide open eating food, cheeks puffed, holding food, enjoying",
|
|
25
|
-
"pet": "being gently petted on head, eyes half-closed in pleasure, blushing cheeks, content",
|
|
26
|
-
"worry": "anxious worried expression, sweat drop on forehead, nervous wide eyes, stressed",
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
def generate_image(prompt, output_path):
|
|
30
|
-
"""调用豆包 API 生成图片"""
|
|
31
|
-
data = json.dumps({
|
|
32
|
-
"model": MODEL,
|
|
33
|
-
"prompt": prompt + ", white background, high quality, detailed",
|
|
34
|
-
"response_format": "url",
|
|
35
|
-
"size": "2K",
|
|
36
|
-
"stream": False,
|
|
37
|
-
}).encode('utf-8')
|
|
38
|
-
|
|
39
|
-
req = urllib.request.Request(API_URL, data=data, headers={
|
|
40
|
-
"Content-Type": "application/json",
|
|
41
|
-
"Authorization": f"Bearer {ARK_API_KEY}",
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
try:
|
|
45
|
-
with urllib.request.urlopen(req, timeout=120) as resp:
|
|
46
|
-
result = json.loads(resp.read())
|
|
47
|
-
url = result["data"][0]["url"]
|
|
48
|
-
# 下载图片
|
|
49
|
-
urllib.request.urlretrieve(url, output_path)
|
|
50
|
-
return True
|
|
51
|
-
except Exception as e:
|
|
52
|
-
print(f" ✗ 失败: {e}")
|
|
53
|
-
return False
|
|
54
|
-
|
|
55
|
-
def main():
|
|
56
|
-
total = 0
|
|
57
|
-
success = 0
|
|
58
|
-
failed = []
|
|
59
|
-
|
|
60
|
-
print("\n 🎨 开始生成 36 张角色表情图...\n")
|
|
61
|
-
|
|
62
|
-
for char_id, char_desc in CHARACTERS.items():
|
|
63
|
-
char_dir = os.path.join(OUT_DIR, char_id)
|
|
64
|
-
os.makedirs(char_dir, exist_ok=True)
|
|
65
|
-
|
|
66
|
-
print(f" [{char_id}]")
|
|
67
|
-
for expr_id, expr_desc in EXPRESSIONS.items():
|
|
68
|
-
total += 1
|
|
69
|
-
output_path = os.path.join(char_dir, f"{expr_id}.png")
|
|
70
|
-
|
|
71
|
-
prompt = f"{char_desc}, {expr_desc}"
|
|
72
|
-
print(f" {expr_id}...", end=" ", flush=True)
|
|
73
|
-
|
|
74
|
-
if generate_image(prompt, output_path):
|
|
75
|
-
size_kb = os.path.getsize(output_path) // 1024
|
|
76
|
-
print(f"✓ ({size_kb}KB)")
|
|
77
|
-
success += 1
|
|
78
|
-
else:
|
|
79
|
-
failed.append(f"{char_id}/{expr_id}")
|
|
80
|
-
|
|
81
|
-
# 间隔避免限流
|
|
82
|
-
time.sleep(2)
|
|
83
|
-
|
|
84
|
-
print()
|
|
85
|
-
|
|
86
|
-
print(f" ──────────────────────")
|
|
87
|
-
print(f" 完成: {success}/{total} 成功")
|
|
88
|
-
if failed:
|
|
89
|
-
print(f" 失败: {', '.join(failed)}")
|
|
90
|
-
print(f"\n 查看结果:")
|
|
91
|
-
print(f" ! open {OUT_DIR}")
|
|
92
|
-
print()
|
|
93
|
-
|
|
94
|
-
if __name__ == "__main__":
|
|
95
|
-
main()
|
package/scripts/gen_variants.py
DELETED
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""从基础图片自动生成 6 个场景变体"""
|
|
3
|
-
|
|
4
|
-
import os, sys
|
|
5
|
-
from PIL import Image, ImageEnhance, ImageFilter, ImageDraw, ImageFont
|
|
6
|
-
|
|
7
|
-
SPRITE_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "sprites")
|
|
8
|
-
|
|
9
|
-
CHARACTERS = ['bibilabu', 'bagayalu', 'wodedaodun', 'bababoyi', 'waibibabu', 'gugugaga']
|
|
10
|
-
|
|
11
|
-
# 6 个场景
|
|
12
|
-
VARIANTS = {
|
|
13
|
-
'normal': '普通',
|
|
14
|
-
'happy': '开心',
|
|
15
|
-
'sleep': '睡觉',
|
|
16
|
-
'eat': '吃东西',
|
|
17
|
-
'pet': '被撸',
|
|
18
|
-
'worry': '焦虑',
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
def make_happy(img):
|
|
22
|
-
"""开心:提亮 + 暖色调"""
|
|
23
|
-
enhancer = ImageEnhance.Brightness(img)
|
|
24
|
-
bright = enhancer.enhance(1.15)
|
|
25
|
-
# 暖色:增加红黄
|
|
26
|
-
r, g, b, a = bright.split()
|
|
27
|
-
r = r.point(lambda x: min(255, int(x * 1.05)))
|
|
28
|
-
g = g.point(lambda x: min(255, int(x * 1.02)))
|
|
29
|
-
return Image.merge('RGBA', (r, g, b, a))
|
|
30
|
-
|
|
31
|
-
def make_sleep(img):
|
|
32
|
-
"""睡觉:变暗 + 轻微模糊"""
|
|
33
|
-
enhancer = ImageEnhance.Brightness(img)
|
|
34
|
-
dark = enhancer.enhance(0.8)
|
|
35
|
-
enhancer2 = ImageEnhance.Color(dark)
|
|
36
|
-
desat = enhancer2.enhance(0.7)
|
|
37
|
-
return desat.filter(ImageFilter.GaussianBlur(radius=0.5))
|
|
38
|
-
|
|
39
|
-
def make_eat(img):
|
|
40
|
-
"""吃东西:微微歪头(旋转 3 度)"""
|
|
41
|
-
rotated = img.rotate(-3, expand=False, fillcolor=(255, 255, 255, 0))
|
|
42
|
-
# 稍微提亮(吃东西开心)
|
|
43
|
-
enhancer = ImageEnhance.Brightness(rotated)
|
|
44
|
-
return enhancer.enhance(1.08)
|
|
45
|
-
|
|
46
|
-
def make_pet(img):
|
|
47
|
-
"""被撸:温暖色调 + 轻微放大"""
|
|
48
|
-
w, h = img.size
|
|
49
|
-
# 轻微放大 5%
|
|
50
|
-
new_w, new_h = int(w * 1.05), int(h * 1.05)
|
|
51
|
-
big = img.resize((new_w, new_h), Image.LANCZOS)
|
|
52
|
-
# 裁回原尺寸(居中)
|
|
53
|
-
left = (new_w - w) // 2
|
|
54
|
-
top = (new_h - h) // 2
|
|
55
|
-
cropped = big.crop((left, top, left + w, top + h))
|
|
56
|
-
# 暖色
|
|
57
|
-
enhancer = ImageEnhance.Color(cropped)
|
|
58
|
-
warm = enhancer.enhance(1.1)
|
|
59
|
-
enhancer2 = ImageEnhance.Brightness(warm)
|
|
60
|
-
return enhancer2.enhance(1.1)
|
|
61
|
-
|
|
62
|
-
def make_worry(img):
|
|
63
|
-
"""焦虑:冷色调 + 轻微缩小"""
|
|
64
|
-
# 冷色:增蓝减红
|
|
65
|
-
r, g, b, a = img.split()
|
|
66
|
-
r = r.point(lambda x: int(x * 0.92))
|
|
67
|
-
b = b.point(lambda x: min(255, int(x * 1.08)))
|
|
68
|
-
cold = Image.merge('RGBA', (r, g, b, a))
|
|
69
|
-
# 轻微降低饱和
|
|
70
|
-
enhancer = ImageEnhance.Color(cold)
|
|
71
|
-
return enhancer.enhance(0.85)
|
|
72
|
-
|
|
73
|
-
GENERATORS = {
|
|
74
|
-
'normal': lambda img: img.copy(),
|
|
75
|
-
'happy': make_happy,
|
|
76
|
-
'sleep': make_sleep,
|
|
77
|
-
'eat': make_eat,
|
|
78
|
-
'pet': make_pet,
|
|
79
|
-
'worry': make_worry,
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
def generate_all():
|
|
83
|
-
total = 0
|
|
84
|
-
for char in CHARACTERS:
|
|
85
|
-
base_path = os.path.join(SPRITE_DIR, f"{char}.png")
|
|
86
|
-
if not os.path.exists(base_path):
|
|
87
|
-
print(f" [!] 跳过 {char}:基础图片不存在")
|
|
88
|
-
continue
|
|
89
|
-
|
|
90
|
-
base = Image.open(base_path).convert("RGBA")
|
|
91
|
-
var_dir = os.path.join(SPRITE_DIR, char)
|
|
92
|
-
os.makedirs(var_dir, exist_ok=True)
|
|
93
|
-
|
|
94
|
-
for variant, zh_name in VARIANTS.items():
|
|
95
|
-
gen = GENERATORS[variant]
|
|
96
|
-
result = gen(base)
|
|
97
|
-
out_path = os.path.join(var_dir, f"{variant}.png")
|
|
98
|
-
result.save(out_path)
|
|
99
|
-
total += 1
|
|
100
|
-
|
|
101
|
-
print(f" ✓ {char} — {len(VARIANTS)} 个变体")
|
|
102
|
-
|
|
103
|
-
print(f"\n 共生成 {total} 张图片,保存到 {SPRITE_DIR}/[角色名]/")
|
|
104
|
-
|
|
105
|
-
if __name__ == '__main__':
|
|
106
|
-
print("\n 🎨 生成角色场景变体...\n")
|
|
107
|
-
generate_all()
|
package/scripts/render_sprite.py
DELETED
|
@@ -1,558 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""CodePet 像素精灵渲染器 v2 — 基于真实形象"""
|
|
3
|
-
|
|
4
|
-
import sys
|
|
5
|
-
|
|
6
|
-
# ── 调色板(从真实图片取色)──
|
|
7
|
-
C = {
|
|
8
|
-
'.': None,
|
|
9
|
-
# 香蕉/植物
|
|
10
|
-
'Y': (215, 195, 55), # 香蕉黄
|
|
11
|
-
'y': (185, 165, 45), # 香蕉暗黄
|
|
12
|
-
'G': (95, 120, 45), # 蒂绿
|
|
13
|
-
# 灰猫(比比拉布/巴巴博一/咕咕嘎嘎)
|
|
14
|
-
'S': (185, 180, 175), # 银灰猫毛
|
|
15
|
-
's': (155, 150, 145), # 深银灰
|
|
16
|
-
'A': (205, 200, 195), # 亮灰
|
|
17
|
-
'a': (130, 125, 120), # 暗灰
|
|
18
|
-
# 水豚(八嘎呀路/我的刀盾)
|
|
19
|
-
'B': (195, 145, 75), # 金棕
|
|
20
|
-
'b': (165, 120, 60), # 深棕
|
|
21
|
-
'L': (225, 200, 155), # 浅棕/肚
|
|
22
|
-
'l': (240, 220, 180), # 亮浅棕
|
|
23
|
-
'M': (210, 170, 110), # 中棕
|
|
24
|
-
# 巴巴博一(猫球)
|
|
25
|
-
'C': (215, 200, 180), # 奶油色身体
|
|
26
|
-
'c': (195, 175, 155), # 深奶油
|
|
27
|
-
# 歪比巴卜(卡通人)
|
|
28
|
-
'F': (225, 185, 145), # 肤色
|
|
29
|
-
'f': (200, 160, 120), # 深肤色
|
|
30
|
-
'H': (165, 105, 55), # 胡子棕
|
|
31
|
-
'h': (135, 80, 40), # 深胡子
|
|
32
|
-
'K': (165, 170, 175), # 帽灰
|
|
33
|
-
'k': (140, 145, 150), # 帽深灰
|
|
34
|
-
'T': (242, 242, 238), # 白衣
|
|
35
|
-
't': (220, 220, 216), # 衣影
|
|
36
|
-
# 咕咕嘎嘎(黑发)
|
|
37
|
-
'D': (65, 65, 70), # 黑发
|
|
38
|
-
'd': (90, 90, 95), # 深灰发
|
|
39
|
-
# 通用
|
|
40
|
-
'W': (240, 235, 225), # 白
|
|
41
|
-
'w': (250, 248, 242), # 亮白
|
|
42
|
-
'X': (30, 30, 30), # 黑(眼)
|
|
43
|
-
'x': (60, 55, 50), # 深灰(眼)
|
|
44
|
-
'P': (205, 155, 145), # 粉鼻
|
|
45
|
-
'p': (185, 130, 120), # 深粉
|
|
46
|
-
'R': (195, 105, 100), # 红嘴
|
|
47
|
-
'J': (80, 95, 130), # 牛仔蓝
|
|
48
|
-
'j': (60, 75, 105), # 深蓝
|
|
49
|
-
'n': (170, 130, 90), # 鼻棕
|
|
50
|
-
'E': (50, 80, 50), # 深绿
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
SPRITES = {}
|
|
54
|
-
|
|
55
|
-
# ═══════════════════════════════════════
|
|
56
|
-
# 1. 比比拉布(香蕉猫)
|
|
57
|
-
# 灰猫脸从香蕉里探出,绿蒂,灰爪
|
|
58
|
-
# 20w × 26h
|
|
59
|
-
# ═══════════════════════════════════════
|
|
60
|
-
SPRITES['bibilabu'] = {
|
|
61
|
-
'normal': [
|
|
62
|
-
".........EE.........",
|
|
63
|
-
"........EGGE........",
|
|
64
|
-
".......EGGGE........",
|
|
65
|
-
"........YYY.........",
|
|
66
|
-
".......YYYYY........",
|
|
67
|
-
"......YYYYYY........",
|
|
68
|
-
".....YYsSSsYY.......",
|
|
69
|
-
".....YsSSSSsY.......",
|
|
70
|
-
"....YYSXSSXSyY......",
|
|
71
|
-
"....YYSSPSSsyY......",
|
|
72
|
-
"....YYsSSSSYYY......",
|
|
73
|
-
".....YYYYYYYY.......",
|
|
74
|
-
".....YYYYYYYY.......",
|
|
75
|
-
".....yYYYYYYy.......",
|
|
76
|
-
".....yyyYYYYy.......",
|
|
77
|
-
".....yyyYYYy........",
|
|
78
|
-
"......yyYYy.........",
|
|
79
|
-
"......yyyYy.........",
|
|
80
|
-
".......yyyy.........",
|
|
81
|
-
"......yyy...........",
|
|
82
|
-
".......SS.SS........",
|
|
83
|
-
"......SS...SS.......",
|
|
84
|
-
],
|
|
85
|
-
'happy': [
|
|
86
|
-
".........EE.........",
|
|
87
|
-
"........EGGE........",
|
|
88
|
-
".......EGGGE........",
|
|
89
|
-
"........YYY.........",
|
|
90
|
-
".......YYYYY........",
|
|
91
|
-
"......YYYYYY........",
|
|
92
|
-
".....YYsSSsYY.......",
|
|
93
|
-
".....YsSSSSsY.......",
|
|
94
|
-
"....YYSaSSaSyY......",
|
|
95
|
-
"....YYSSPSSsyY......",
|
|
96
|
-
"....YYsSSSSYYY......",
|
|
97
|
-
".....YYYYYYYY.......",
|
|
98
|
-
".....YYYYYYYY.......",
|
|
99
|
-
".....yYYYYYYy.......",
|
|
100
|
-
".....yyyYYYYy.......",
|
|
101
|
-
".....yyyYYYy........",
|
|
102
|
-
"......yyYYy.........",
|
|
103
|
-
"......yyyYy.........",
|
|
104
|
-
".......yyyy.........",
|
|
105
|
-
"......yyy...........",
|
|
106
|
-
".......SS.SS........",
|
|
107
|
-
"......SS...SS.......",
|
|
108
|
-
],
|
|
109
|
-
'sleep': [
|
|
110
|
-
".........EE.........",
|
|
111
|
-
"........EGGE........",
|
|
112
|
-
".......EGGGE........",
|
|
113
|
-
"........YYY.........",
|
|
114
|
-
".......YYYYY........",
|
|
115
|
-
"......YYYYYY........",
|
|
116
|
-
".....YYsSSsYY.......",
|
|
117
|
-
".....YsSSSSsY.......",
|
|
118
|
-
"....YYSsSssSyY......",
|
|
119
|
-
"....YYSSPSSsyY......",
|
|
120
|
-
"....YYsSSSSYYY......",
|
|
121
|
-
".....YYYYYYYY.......",
|
|
122
|
-
".....YYYYYYYY.......",
|
|
123
|
-
".....yYYYYYYy.......",
|
|
124
|
-
".....yyyYYYYy.......",
|
|
125
|
-
".....yyyYYYy........",
|
|
126
|
-
"......yyYYy.........",
|
|
127
|
-
"......yyyYy.........",
|
|
128
|
-
".......yyyy.........",
|
|
129
|
-
"......yyy...........",
|
|
130
|
-
".......SS.SS........",
|
|
131
|
-
"......SS...SS.......",
|
|
132
|
-
],
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
# ═══════════════════════════════════════
|
|
136
|
-
# 2. 八嘎呀路(坐姿水豚)
|
|
137
|
-
# 金棕色,3/4侧面坐,闭眼忧郁,浅肚
|
|
138
|
-
# 20w × 22h
|
|
139
|
-
# ═══════════════════════════════════════
|
|
140
|
-
SPRITES['bagayalu'] = {
|
|
141
|
-
'normal': [
|
|
142
|
-
"..........bb........",
|
|
143
|
-
".........bBBb.......",
|
|
144
|
-
"........bBBBBb......",
|
|
145
|
-
".......bBBBBBBb.....",
|
|
146
|
-
"......bBBBBBBBBb....",
|
|
147
|
-
".....bBbBBbBBBBBb...",
|
|
148
|
-
".....bBBnnBBBBBBb...",
|
|
149
|
-
".....bBBBBBBBBBBb...",
|
|
150
|
-
"....bBBBBBBBBBBBb...",
|
|
151
|
-
"...bBBBBBBBBBBBBb...",
|
|
152
|
-
"..bBBBBBBBBBBBBBb...",
|
|
153
|
-
"..bBBBLLLLLBBBBb....",
|
|
154
|
-
"..bBBLLLLLLLBBBb....",
|
|
155
|
-
"..bBBLLLLLLLBBb.....",
|
|
156
|
-
"...bBBLLLLLBBb......",
|
|
157
|
-
"...bBBBBBBBBBb......",
|
|
158
|
-
"....bbBBBBBbb.......",
|
|
159
|
-
"....bBb..bBBb.......",
|
|
160
|
-
"....bb....bbb.......",
|
|
161
|
-
],
|
|
162
|
-
'happy': [
|
|
163
|
-
"..........bb........",
|
|
164
|
-
".........bBBb.......",
|
|
165
|
-
"........bBBBBb......",
|
|
166
|
-
".......bBBBBBBb.....",
|
|
167
|
-
"......bBBBBBBBBb....",
|
|
168
|
-
".....bBMBBMBBBBBb...",
|
|
169
|
-
".....bBBnnBBBBBBb...",
|
|
170
|
-
".....bBBLLBBBBBBb...",
|
|
171
|
-
"....bBBBBBBBBBBBb...",
|
|
172
|
-
"...bBBBBBBBBBBBBb...",
|
|
173
|
-
"..bBBBBBBBBBBBBBb...",
|
|
174
|
-
"..bBBBLLLLLBBBBb....",
|
|
175
|
-
"..bBBLLLLLLLBBBb....",
|
|
176
|
-
"..bBBLLLLLLLBBb.....",
|
|
177
|
-
"...bBBLLLLLBBb......",
|
|
178
|
-
"...bBBBBBBBBBb......",
|
|
179
|
-
"....bbBBBBBbb.......",
|
|
180
|
-
"....bBb..bBBb.......",
|
|
181
|
-
"....bb....bbb.......",
|
|
182
|
-
],
|
|
183
|
-
'sleep': [
|
|
184
|
-
"..........bb........",
|
|
185
|
-
".........bBBb.......",
|
|
186
|
-
"........bBBBBb......",
|
|
187
|
-
".......bBBBBBBb.....",
|
|
188
|
-
"......bBBBBBBBBb....",
|
|
189
|
-
".....bBbBBbBBBBBb...",
|
|
190
|
-
".....bBBnnBBBBBBb...",
|
|
191
|
-
".....bBBBBBBBBBBb...",
|
|
192
|
-
"....bBBBBBBBBBBBb...",
|
|
193
|
-
"...bBBBBBBBBBBBBb...",
|
|
194
|
-
"..bBBBBBBBBBBBBBb...",
|
|
195
|
-
"..bBBBLLLLLBBBBb....",
|
|
196
|
-
"..bBBLLLLLLLBBBb....",
|
|
197
|
-
"..bBBLLLLLLLBBb.....",
|
|
198
|
-
"...bBBLLLLLBBb......",
|
|
199
|
-
"...bBBBBBBBBBb......",
|
|
200
|
-
"....bbBBBBBbb.......",
|
|
201
|
-
"....bBb..bBBb.......",
|
|
202
|
-
"....bb....bbb.......",
|
|
203
|
-
],
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
# ═══════════════════════════════════════
|
|
207
|
-
# 3. 我的刀盾(趴姿水豚)
|
|
208
|
-
# 完全趴平像面包,侧面,微笑
|
|
209
|
-
# 22w × 12h
|
|
210
|
-
# ═══════════════════════════════════════
|
|
211
|
-
SPRITES['wodedaodun'] = {
|
|
212
|
-
'normal': [
|
|
213
|
-
".......bbbbb........",
|
|
214
|
-
"....bbbBBBBBbbb.....",
|
|
215
|
-
"...bBBBBBBBBBBBbb...",
|
|
216
|
-
"..bBbBbBBBBBBBBBBb..",
|
|
217
|
-
"..bBBnBBBBBBBBBBBBb.",
|
|
218
|
-
"..bBBBLLBBBBBBBBBBb.",
|
|
219
|
-
"...bBBBBBBBBBBBBBb..",
|
|
220
|
-
"...bbLBBBBBBBBLbb...",
|
|
221
|
-
"....bb.........bb...",
|
|
222
|
-
],
|
|
223
|
-
'happy': [
|
|
224
|
-
".......bbbbb........",
|
|
225
|
-
"....bbbBBBBBbbb.....",
|
|
226
|
-
"...bBBBBBBBBBBBbb...",
|
|
227
|
-
"..bBMBMBBBBBBBBBBb..",
|
|
228
|
-
"..bBBnBBBBBBBBBBBBb.",
|
|
229
|
-
"..bBBBLLBBBBBBBBBBb.",
|
|
230
|
-
"...bBBBBBBBBBBBBBb..",
|
|
231
|
-
"...bbLBBBBBBBBLbb...",
|
|
232
|
-
"....bb.........bb...",
|
|
233
|
-
],
|
|
234
|
-
'sleep': [
|
|
235
|
-
".......bbbbb........",
|
|
236
|
-
"....bbbBBBBBbbb.....",
|
|
237
|
-
"...bBBBBBBBBBBBbb...",
|
|
238
|
-
"..bBbBbBBBBBBBBBBb..",
|
|
239
|
-
"..bBBnBBBBBBBBBBBBb.",
|
|
240
|
-
"..bBBBLLBBBBBBBBBBb.",
|
|
241
|
-
"...bBBBBBBBBBBBBBb..",
|
|
242
|
-
"...bbLBBBBBBBBLbb...",
|
|
243
|
-
"....bb.........bb...",
|
|
244
|
-
],
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
# ═══════════════════════════════════════
|
|
248
|
-
# 4. 巴巴博一(圆猫球)
|
|
249
|
-
# 极度圆,巨大黑眼,粉鼻,灰+奶油色
|
|
250
|
-
# 20w × 20h
|
|
251
|
-
# ═══════════════════════════════════════
|
|
252
|
-
SPRITES['bababoyi'] = {
|
|
253
|
-
'normal': [
|
|
254
|
-
".......ssss.........",
|
|
255
|
-
"......sSSSSs........",
|
|
256
|
-
".....sSSSSSSSs......",
|
|
257
|
-
"....sSSSSSSSSSs.....",
|
|
258
|
-
"...sSSSSSSSSSSSs....",
|
|
259
|
-
"..sSxXxSSSSxXxSSs...",
|
|
260
|
-
"..sSXXxSSSSxXXSSs...",
|
|
261
|
-
"..sSSSSSSSSSSSSSSs..",
|
|
262
|
-
"..sSSSSSPPSSSSSSSs..",
|
|
263
|
-
"..sCCCCCCCCCCCCCs...",
|
|
264
|
-
"..CCCCCCCCCCCCCCCC..",
|
|
265
|
-
".CCCCCCCCCCCCCCCCCC.",
|
|
266
|
-
".CCCCCCCCCCCCCCCCCC.",
|
|
267
|
-
".CCCCCCCCCCCCCCCCCC.",
|
|
268
|
-
"..CCCCCCCCCCCCCCCC..",
|
|
269
|
-
"..cCCCCCCCCCCCCCc...",
|
|
270
|
-
"...ccCCCCCCCCcc.....",
|
|
271
|
-
"....cccccccccc......",
|
|
272
|
-
".....ss....ss.......",
|
|
273
|
-
],
|
|
274
|
-
'happy': [
|
|
275
|
-
".......ssss.........",
|
|
276
|
-
"......sSSSSs........",
|
|
277
|
-
".....sSSSSSSSs......",
|
|
278
|
-
"....sSSSSSSSSSs.....",
|
|
279
|
-
"...sSSSSSSSSSSSs....",
|
|
280
|
-
"..sSaxaSSSSaxaSSs...",
|
|
281
|
-
"..sSaaaSSSsaaaSs....",
|
|
282
|
-
"..sSSSSSSSSSSSSSSs..",
|
|
283
|
-
"..sSSSSSPPSSSSSSSs..",
|
|
284
|
-
"..sCCCCCCCCCCCCCs...",
|
|
285
|
-
"..CCCCCCCCCCCCCCCC..",
|
|
286
|
-
".CCCCCCCCCCCCCCCCCC.",
|
|
287
|
-
".CCCCCCCCCCCCCCCCCC.",
|
|
288
|
-
".CCCCCCCCCCCCCCCCCC.",
|
|
289
|
-
"..CCCCCCCCCCCCCCCC..",
|
|
290
|
-
"..cCCCCCCCCCCCCCc...",
|
|
291
|
-
"...ccCCCCCCCCcc.....",
|
|
292
|
-
"....cccccccccc......",
|
|
293
|
-
".....ss....ss.......",
|
|
294
|
-
],
|
|
295
|
-
'sleep': [
|
|
296
|
-
".......ssss.........",
|
|
297
|
-
"......sSSSSs........",
|
|
298
|
-
".....sSSSSSSSs......",
|
|
299
|
-
"....sSSSSSSSSSs.....",
|
|
300
|
-
"...sSSSSSSSSSSSs....",
|
|
301
|
-
"..sSSsSSSSSsSSSSSs..",
|
|
302
|
-
"..sSssSSSSssSSSSSs..",
|
|
303
|
-
"..sSSSSSSSSSSSSSSs..",
|
|
304
|
-
"..sSSSSSPPSSSSSSSs..",
|
|
305
|
-
"..sCCCCCCCCCCCCCs...",
|
|
306
|
-
"..CCCCCCCCCCCCCCCC..",
|
|
307
|
-
".CCCCCCCCCCCCCCCCCC.",
|
|
308
|
-
".CCCCCCCCCCCCCCCCCC.",
|
|
309
|
-
".CCCCCCCCCCCCCCCCCC.",
|
|
310
|
-
"..CCCCCCCCCCCCCCCC..",
|
|
311
|
-
"..cCCCCCCCCCCCCCc...",
|
|
312
|
-
"...ccCCCCCCCCcc.....",
|
|
313
|
-
"....cccccccccc......",
|
|
314
|
-
".....ss....ss.......",
|
|
315
|
-
],
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
# ═══════════════════════════════════════
|
|
319
|
-
# 5. 歪比巴卜(大胡子壮汉)
|
|
320
|
-
# 卡通风,灰帽,肤色脸,棕胡子,白Polo衫
|
|
321
|
-
# 18w × 26h
|
|
322
|
-
# ═══════════════════════════════════════
|
|
323
|
-
SPRITES['waibibabu'] = {
|
|
324
|
-
'normal': [
|
|
325
|
-
".......kkkk.........",
|
|
326
|
-
"......kKKKKk........",
|
|
327
|
-
".....kKKKKKKk.......",
|
|
328
|
-
".....KKKKKKKKK......",
|
|
329
|
-
"....FFFFFFFFFFf.....",
|
|
330
|
-
"....FxFFFFxFFFF.....",
|
|
331
|
-
"....FFFFFFFFFFFf....",
|
|
332
|
-
"....FFFnnFFFFFf.....",
|
|
333
|
-
"....fHHHHHHHHf......",
|
|
334
|
-
".....fHHHHHHf.......",
|
|
335
|
-
"......ffffff........",
|
|
336
|
-
".....TTTTTTTTT......",
|
|
337
|
-
"....TTTTtTTTTTT.....",
|
|
338
|
-
"...FTTTTtTTTTTF.....",
|
|
339
|
-
"...FFTTTtTTTTFF.....",
|
|
340
|
-
"...FFFTTtTTFFF......",
|
|
341
|
-
"....FFTTtTTFF.......",
|
|
342
|
-
".....TTTTTTT........",
|
|
343
|
-
".....TTTTTTT........",
|
|
344
|
-
"......TTTTT.........",
|
|
345
|
-
"......JJ.JJ.........",
|
|
346
|
-
".....JJJ.JJJ........",
|
|
347
|
-
".....JJ...JJ........",
|
|
348
|
-
"....jjj...jjj.......",
|
|
349
|
-
],
|
|
350
|
-
'happy': [
|
|
351
|
-
".......kkkk.........",
|
|
352
|
-
"......kKKKKk........",
|
|
353
|
-
".....kKKKKKKk.......",
|
|
354
|
-
".....KKKKKKKKK......",
|
|
355
|
-
"....FFFFFFFFFFf.....",
|
|
356
|
-
"....FaFFFFaFFFF.....",
|
|
357
|
-
"....FFFFFFFFFFFf....",
|
|
358
|
-
"....FFFnnFFFFFf.....",
|
|
359
|
-
"....fHHHLHHHHf......",
|
|
360
|
-
".....fHHHHHHf.......",
|
|
361
|
-
"......ffffff........",
|
|
362
|
-
".....TTTTTTTTT......",
|
|
363
|
-
"....TTTTtTTTTTT.....",
|
|
364
|
-
"...FTTTTtTTTTTF.....",
|
|
365
|
-
"...FFTTTtTTTTFF.....",
|
|
366
|
-
"...FFFTTtTTFFF......",
|
|
367
|
-
"....FFTTtTTFF.......",
|
|
368
|
-
".....TTTTTTT........",
|
|
369
|
-
".....TTTTTTT........",
|
|
370
|
-
"......TTTTT.........",
|
|
371
|
-
"......JJ.JJ.........",
|
|
372
|
-
".....JJJ.JJJ........",
|
|
373
|
-
".....JJ...JJ........",
|
|
374
|
-
"....jjj...jjj.......",
|
|
375
|
-
],
|
|
376
|
-
'sleep': [
|
|
377
|
-
".......kkkk.........",
|
|
378
|
-
"......kKKKKk........",
|
|
379
|
-
".....kKKKKKKk.......",
|
|
380
|
-
".....KKKKKKKKK......",
|
|
381
|
-
"....FFFFFFFFFFf.....",
|
|
382
|
-
"....FfFFFFfFFFF.....",
|
|
383
|
-
"....FFFFFFFFFFFf....",
|
|
384
|
-
"....FFFnnFFFFFf.....",
|
|
385
|
-
"....fHHHHHHHHf......",
|
|
386
|
-
".....fHHHHHHf.......",
|
|
387
|
-
"......ffffff........",
|
|
388
|
-
".....TTTTTTTTT......",
|
|
389
|
-
"....TTTTtTTTTTT.....",
|
|
390
|
-
"...FTTTTtTTTTTF.....",
|
|
391
|
-
"...FFTTTtTTTTFF.....",
|
|
392
|
-
"...FFFTTtTTFFF......",
|
|
393
|
-
"....FFTTtTTFF.......",
|
|
394
|
-
".....TTTTTTT........",
|
|
395
|
-
".....TTTTTTT........",
|
|
396
|
-
"......TTTTT.........",
|
|
397
|
-
"......JJ.JJ.........",
|
|
398
|
-
".....JJJ.JJJ........",
|
|
399
|
-
".....JJ...JJ........",
|
|
400
|
-
"....jjj...jjj.......",
|
|
401
|
-
],
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
# ═══════════════════════════════════════
|
|
405
|
-
# 6. 咕咕嘎嘎(黑刘海圆脸猫)
|
|
406
|
-
# 厚黑灰色刘海,白/灰圆脸,小嘴
|
|
407
|
-
# 20w × 18h
|
|
408
|
-
# ═══════════════════════════════════════
|
|
409
|
-
SPRITES['gugugaga'] = {
|
|
410
|
-
'normal': [
|
|
411
|
-
"......ddddddd.......",
|
|
412
|
-
".....dDDDDDDDd......",
|
|
413
|
-
"....dDDDDDDDDDd.....",
|
|
414
|
-
"...dDDDDDDDDDDDd....",
|
|
415
|
-
"..dDDDDDDDDDDDDDd...",
|
|
416
|
-
"..dDDDDDDDDDDDDDd...",
|
|
417
|
-
"..dDxDDDDDDxDDDDd...",
|
|
418
|
-
"..SSSSSSSSSSSSSSss..",
|
|
419
|
-
"..SSSSSSSSSSSSSSSs..",
|
|
420
|
-
"..SSSSSSSSSSSSSSss..",
|
|
421
|
-
"..sSSSSSRSSSSSSSs...",
|
|
422
|
-
"...sSSSSSSSSSSSs....",
|
|
423
|
-
"...sSSSSSSSSSSs.....",
|
|
424
|
-
"....sSWWWWWWSs......",
|
|
425
|
-
"....sWWWWWWWWs......",
|
|
426
|
-
".....sWWWWWWs.......",
|
|
427
|
-
"......ssssss........",
|
|
428
|
-
".......ss.ss........",
|
|
429
|
-
],
|
|
430
|
-
'happy': [
|
|
431
|
-
"......ddddddd.......",
|
|
432
|
-
".....dDDDDDDDd......",
|
|
433
|
-
"....dDDDDDDDDDd.....",
|
|
434
|
-
"...dDDDDDDDDDDDd....",
|
|
435
|
-
"..dDDDDDDDDDDDDDd...",
|
|
436
|
-
"..dDDDDDDDDDDDDDd...",
|
|
437
|
-
"..dDaDDDDDDaDDDDd...",
|
|
438
|
-
"..SSSSSSSSSSSSSSss..",
|
|
439
|
-
"..SSSSSSSSSSSSSSSs..",
|
|
440
|
-
"..SSSSSSSSSSSSSSss..",
|
|
441
|
-
"..sSSSSSLSSSSSSSs...",
|
|
442
|
-
"...sSSSSSSSSSSSs....",
|
|
443
|
-
"...sSSSSSSSSSSs.....",
|
|
444
|
-
"....sSWWWWWWSs......",
|
|
445
|
-
"....sWWWWWWWWs......",
|
|
446
|
-
".....sWWWWWWs.......",
|
|
447
|
-
"......ssssss........",
|
|
448
|
-
".......ss.ss........",
|
|
449
|
-
],
|
|
450
|
-
'sleep': [
|
|
451
|
-
"......ddddddd.......",
|
|
452
|
-
".....dDDDDDDDd......",
|
|
453
|
-
"....dDDDDDDDDDd.....",
|
|
454
|
-
"...dDDDDDDDDDDDd....",
|
|
455
|
-
"..dDDDDDDDDDDDDDd...",
|
|
456
|
-
"..dDDDDDDDDDDDDDd...",
|
|
457
|
-
"..dDsDDDDDDsDDDDd...",
|
|
458
|
-
"..SSSSSSSSSSSSSSss..",
|
|
459
|
-
"..SSSSSSSSSSSSSSSs..",
|
|
460
|
-
"..SSSSSSSSSSSSSSss..",
|
|
461
|
-
"..sSSSSSSSSSSSSSs...",
|
|
462
|
-
"...sSSSSSSSSSSSs....",
|
|
463
|
-
"...sSSSSSSSSSSs.....",
|
|
464
|
-
"....sSWWWWWWSs......",
|
|
465
|
-
"....sWWWWWWWWs......",
|
|
466
|
-
".....sWWWWWWs.......",
|
|
467
|
-
"......ssssss........",
|
|
468
|
-
".......ss.ss........",
|
|
469
|
-
],
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
# ── 渲染引擎 ──
|
|
473
|
-
|
|
474
|
-
def fg(r, g, b):
|
|
475
|
-
return f"\033[38;2;{r};{g};{b}m"
|
|
476
|
-
|
|
477
|
-
def bg(r, g, b):
|
|
478
|
-
return f"\033[48;2;{r};{g};{b}m"
|
|
479
|
-
|
|
480
|
-
RESET = "\033[0m"
|
|
481
|
-
BOLD = "\033[1m"
|
|
482
|
-
|
|
483
|
-
def render_pixel_pair(top_char, bot_char):
|
|
484
|
-
top_color = C.get(top_char)
|
|
485
|
-
bot_color = C.get(bot_char)
|
|
486
|
-
if top_color and bot_color:
|
|
487
|
-
if top_color == bot_color:
|
|
488
|
-
return f"{fg(*top_color)}█{RESET}"
|
|
489
|
-
else:
|
|
490
|
-
return f"{fg(*top_color)}{bg(*bot_color)}▀{RESET}"
|
|
491
|
-
elif top_color:
|
|
492
|
-
return f"{fg(*top_color)}▀{RESET}"
|
|
493
|
-
elif bot_color:
|
|
494
|
-
return f"{fg(*bot_color)}▄{RESET}"
|
|
495
|
-
else:
|
|
496
|
-
return " "
|
|
497
|
-
|
|
498
|
-
def render_sprite(name, state='normal', indent=4):
|
|
499
|
-
data = SPRITES.get(name, {}).get(state)
|
|
500
|
-
if not data:
|
|
501
|
-
print(f"未找到: {name}/{state}")
|
|
502
|
-
return
|
|
503
|
-
rows = list(data)
|
|
504
|
-
if len(rows) % 2 == 1:
|
|
505
|
-
max_len = max(len(r) for r in rows)
|
|
506
|
-
rows.append('.' * max_len)
|
|
507
|
-
max_len = max(len(r) for r in rows)
|
|
508
|
-
rows = [r.ljust(max_len, '.') for r in rows]
|
|
509
|
-
for i in range(0, len(rows), 2):
|
|
510
|
-
top_row = rows[i]
|
|
511
|
-
bot_row = rows[i + 1]
|
|
512
|
-
line = ""
|
|
513
|
-
for j in range(max_len):
|
|
514
|
-
tc = top_row[j] if j < len(top_row) else '.'
|
|
515
|
-
bc = bot_row[j] if j < len(bot_row) else '.'
|
|
516
|
-
line += render_pixel_pair(tc, bc)
|
|
517
|
-
print(f"{' ' * indent}{line}")
|
|
518
|
-
|
|
519
|
-
def render_card(name, state='normal'):
|
|
520
|
-
"""渲染单个角色带标题"""
|
|
521
|
-
names_zh = {
|
|
522
|
-
'bibilabu': '比比拉布(香蕉猫)',
|
|
523
|
-
'bagayalu': '八嘎呀路(坐姿水豚)',
|
|
524
|
-
'wodedaodun': '我的刀盾(趴姿水豚)',
|
|
525
|
-
'bababoyi': '巴巴博一(圆猫球)',
|
|
526
|
-
'waibibabu': '歪比巴卜(大胡子壮汉)',
|
|
527
|
-
'gugugaga': '咕咕嘎嘎(黑刘海猫)',
|
|
528
|
-
}
|
|
529
|
-
states_zh = {'normal': '普通', 'happy': '开心', 'sleep': '睡觉'}
|
|
530
|
-
title = names_zh.get(name, name)
|
|
531
|
-
st = states_zh.get(state, state)
|
|
532
|
-
print(f"\n {BOLD}{title}{RESET} · {st}")
|
|
533
|
-
render_sprite(name, state)
|
|
534
|
-
|
|
535
|
-
def render_all():
|
|
536
|
-
print()
|
|
537
|
-
print(" ═══════════════════════════════════")
|
|
538
|
-
print(f" 🐾 {BOLD}CodePet 像素角色画廊{RESET}")
|
|
539
|
-
print(" ═══════════════════════════════════")
|
|
540
|
-
for name in ['bibilabu', 'bagayalu', 'wodedaodun', 'bababoyi', 'waibibabu', 'gugugaga']:
|
|
541
|
-
for state in ['normal', 'happy', 'sleep']:
|
|
542
|
-
render_card(name, state)
|
|
543
|
-
print(" ───────────────────────────────────")
|
|
544
|
-
print()
|
|
545
|
-
|
|
546
|
-
# ── 入口 ──
|
|
547
|
-
if __name__ == '__main__':
|
|
548
|
-
name_map = {
|
|
549
|
-
'比比拉布': 'bibilabu', '八嘎呀路': 'bagayalu',
|
|
550
|
-
'我的刀盾': 'wodedaodun', '巴巴博一': 'bababoyi',
|
|
551
|
-
'歪比巴卜': 'waibibabu', '咕咕嘎嘎': 'gugugaga',
|
|
552
|
-
}
|
|
553
|
-
if len(sys.argv) < 2 or sys.argv[1] == 'all':
|
|
554
|
-
render_all()
|
|
555
|
-
else:
|
|
556
|
-
name = name_map.get(sys.argv[1], sys.argv[1])
|
|
557
|
-
state = sys.argv[2] if len(sys.argv) > 2 else 'normal'
|
|
558
|
-
render_card(name, state)
|