@mingxy/opencode-mascot 0.7.7 → 0.7.9
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
CHANGED
|
@@ -1,19 +1,6 @@
|
|
|
1
1
|
import type { PropPack } from "../../core/types";
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
* Pad 道具 — busy 状态变体道具
|
|
5
|
-
*
|
|
6
|
-
* 尺寸: 18 宽 × 9 高(双层框+.•摄像头+底部◉Home键)
|
|
7
|
-
* 屏幕区: 12 宽 × 4 高
|
|
8
|
-
* 叙事: 月儿小人玩游戏,又菜又爱玩
|
|
9
|
-
* 贪吃蛇 6帧: 开局→走→撞墙→哭→不服→重开
|
|
10
|
-
* 俄罗斯方块 4帧: 开局→堆积→满→over
|
|
11
|
-
* 2048 4帧: 开局→合并→128→over
|
|
12
|
-
* 切换 1帧: next!
|
|
13
|
-
* 月儿小人: ☆ 呆毛 + (^-^) 圆脸 + ┃█┃ 身体(迷你版)
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
const W = 12; // 屏幕内容区宽
|
|
3
|
+
const W = 12;
|
|
17
4
|
|
|
18
5
|
const OUTER_TOP = "╭.•" + "─".repeat(14) + "╮";
|
|
19
6
|
const INNER_TOP = "│ ┌" + "─".repeat(W) + "┐ │";
|
|
@@ -25,24 +12,20 @@ const scr = (rows: string[]) =>
|
|
|
25
12
|
rows.map((r) => "│ │" + r.padEnd(W) + "│ │");
|
|
26
13
|
|
|
27
14
|
const games: string[][] = [
|
|
28
|
-
// 贪吃蛇 6帧
|
|
29
15
|
[" ☆ ᵇᵉᵍⁱⁿ ", "(^-^) ◯→ ●", " ┃█┃ ˢ:0 ", " "],
|
|
30
16
|
[" ☆ ", "(^-^) ◯◯→ ●", " ┃█┃ ˢ:1 ", " "],
|
|
31
17
|
[" ☆ ", "(×_×) ◯◯💥 ", " ┃█┃ ᵍᵍ! ", " "],
|
|
32
18
|
[" ☆ ", "(╥_╥) ᵒᵛᵉʳ ", " ┃█┃ ˢ:1 ", " "],
|
|
33
19
|
[" ☆ ", "(¬_¬) ᵃᵍᵃⁱⁿ", " ┃█┃ ", " "],
|
|
34
20
|
[" ☆ ᵍᵒ! ", "(^-^) ◯→ ●", " ┃█┃ ˢ:0 ", " "],
|
|
35
|
-
// 俄罗斯方块 4帧
|
|
36
21
|
[" ☆ ᵇᵉᵍⁱⁿ ", "(^-^) ▣ ", " ┃█┃ ", " "],
|
|
37
22
|
[" ☆ ", "(^-^) ▣▣ ", " ┃█┃ ▣▣▣▣ ", " "],
|
|
38
23
|
[" ☆ ", "(×_×) ", " ┃█┃ ▣▣▣▣▣▣", " ᶠᵘˡˡ! "],
|
|
39
24
|
[" ☆ ", "(╥_╥) ᵒᵛᵉʳ ", " ┃█┃ ", " "],
|
|
40
|
-
// 2048 4帧
|
|
41
25
|
[" ☆ ᵇᵉᵍⁱⁿ ", "(^-^) [ 2 ]", " ┃█┃ ", " "],
|
|
42
26
|
[" ☆ ", "(^-^) [ 4 ]", " ┃█┃ ᵒᵒⁿ ", " "],
|
|
43
27
|
[" ☆ ", "(⊙_⊙) [128]", " ┃█┃ ʷᵒʷ ", " "],
|
|
44
28
|
[" ☆ ", "(╥_╥) ᵒᵛᵉʳ ", " ┃█┃ ", " "],
|
|
45
|
-
// 切换
|
|
46
29
|
[" ☆ ", "(^-^) ⁿᵉˣᵗ!", " ┃█┃ ", " "],
|
|
47
30
|
];
|
|
48
31
|
|
|
@@ -157,7 +157,19 @@ export function HomeMascot(props: HomeMascotProps): JSX.Element {
|
|
|
157
157
|
onMouseUp={() => { stopDrag(); }}
|
|
158
158
|
onMouseDragEnd={() => { stopDrag(); }}
|
|
159
159
|
>
|
|
160
|
-
{renderers[currentName()]?.
|
|
160
|
+
{renderers[currentName()]?.propElement() ? (
|
|
161
|
+
<box
|
|
162
|
+
position="absolute"
|
|
163
|
+
zIndex={50}
|
|
164
|
+
left={renderers[currentName()].getPropPosition() === "side-left" ? -16 : renderers[currentName()].getPropPosition() === "side-right" ? 12 : 0}
|
|
165
|
+
top={0}
|
|
166
|
+
>
|
|
167
|
+
{renderers[currentName()].propElement()}
|
|
168
|
+
</box>
|
|
169
|
+
) : null}
|
|
170
|
+
<box zIndex={100}>
|
|
171
|
+
{renderers[currentName()]?.element() ?? null}
|
|
172
|
+
</box>
|
|
161
173
|
</box>
|
|
162
174
|
);
|
|
163
175
|
}
|
|
@@ -159,16 +159,8 @@ export function SidebarMascot(props: SidebarMascotProps): JSX.Element {
|
|
|
159
159
|
if (statusType === "busy" || statusType === "retry") {
|
|
160
160
|
if (hideSide) returnToView();
|
|
161
161
|
renderers[currentName()].setState("busy");
|
|
162
|
-
// 先显示箱子"打开"动画300ms,再切换到 busy 道具(道具从箱子里掉出来)
|
|
163
162
|
const busyProp = pickPropByTrigger("busy");
|
|
164
|
-
|
|
165
|
-
renderers[currentName()].setProp(getProp("box") ?? null);
|
|
166
|
-
setTimeout(() => {
|
|
167
|
-
renderers[currentName()].setProp(busyProp);
|
|
168
|
-
}, 300);
|
|
169
|
-
} else {
|
|
170
|
-
renderers[currentName()].setProp(null);
|
|
171
|
-
}
|
|
163
|
+
renderers[currentName()].setProp(busyProp);
|
|
172
164
|
} else {
|
|
173
165
|
setStateWithSwitch("idle");
|
|
174
166
|
renderers[currentName()].setProp(null);
|
|
@@ -209,7 +201,7 @@ export function SidebarMascot(props: SidebarMascotProps): JSX.Element {
|
|
|
209
201
|
setTimeout(() => setZBoost(false), 3500);
|
|
210
202
|
});
|
|
211
203
|
|
|
212
|
-
let scattered =
|
|
204
|
+
let scattered = false;
|
|
213
205
|
|
|
214
206
|
onScatter(() => {
|
|
215
207
|
if (scattered) return;
|
|
@@ -217,45 +209,11 @@ export function SidebarMascot(props: SidebarMascotProps): JSX.Element {
|
|
|
217
209
|
renderers[currentName()].scatterIn();
|
|
218
210
|
});
|
|
219
211
|
|
|
220
|
-
renderers[currentName()].setCharacterHidden(true);
|
|
221
|
-
renderers[currentName()].setProp(getProp("box") ?? null);
|
|
222
|
-
|
|
223
|
-
const finalY = posY();
|
|
224
|
-
const finalX = posX();
|
|
225
|
-
const fallStartY = finalY - 15;
|
|
226
|
-
const fallDuration = 500;
|
|
227
|
-
const fallStartTime = Date.now();
|
|
228
|
-
setPosY(fallStartY);
|
|
229
|
-
|
|
230
|
-
const fallInterval = setInterval(() => {
|
|
231
|
-
const elapsed = Date.now() - fallStartTime;
|
|
232
|
-
const t = Math.min(elapsed / fallDuration, 1);
|
|
233
|
-
const eased = t * t;
|
|
234
|
-
setPosY(Math.round(fallStartY + (finalY - fallStartY) * eased));
|
|
235
|
-
if (t >= 1) {
|
|
236
|
-
clearInterval(fallInterval);
|
|
237
|
-
setPosY(finalY);
|
|
238
|
-
|
|
239
|
-
setTimeout(() => {
|
|
240
|
-
const shakeSeq = [1, -1, 1, -1, 0];
|
|
241
|
-
let shakeIdx = 0;
|
|
242
|
-
const shakeInterval = setInterval(() => {
|
|
243
|
-
if (shakeIdx >= shakeSeq.length) {
|
|
244
|
-
clearInterval(shakeInterval);
|
|
245
|
-
setPosX(finalX);
|
|
246
|
-
return;
|
|
247
|
-
}
|
|
248
|
-
setPosX(finalX + shakeSeq[shakeIdx]);
|
|
249
|
-
shakeIdx++;
|
|
250
|
-
}, 60);
|
|
251
|
-
}, 2000);
|
|
252
|
-
}
|
|
253
|
-
}, 16);
|
|
254
|
-
|
|
255
212
|
setTimeout(() => {
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
213
|
+
if (scattered) return;
|
|
214
|
+
scattered = true;
|
|
215
|
+
renderers[currentName()].scatterIn();
|
|
216
|
+
}, 2000);
|
|
259
217
|
|
|
260
218
|
return (
|
|
261
219
|
<box
|
|
@@ -313,7 +271,19 @@ export function SidebarMascot(props: SidebarMascotProps): JSX.Element {
|
|
|
313
271
|
checkEdge();
|
|
314
272
|
}}
|
|
315
273
|
>
|
|
316
|
-
{renderers[currentName()]?.
|
|
274
|
+
{renderers[currentName()]?.propElement() ? (
|
|
275
|
+
<box
|
|
276
|
+
position="absolute"
|
|
277
|
+
zIndex={50}
|
|
278
|
+
left={renderers[currentName()].getPropPosition() === "side-left" ? -16 : renderers[currentName()].getPropPosition() === "side-right" ? 12 : 0}
|
|
279
|
+
top={0}
|
|
280
|
+
>
|
|
281
|
+
{renderers[currentName()].propElement()}
|
|
282
|
+
</box>
|
|
283
|
+
) : null}
|
|
284
|
+
<box zIndex={100}>
|
|
285
|
+
{renderers[currentName()]?.element() ?? null}
|
|
286
|
+
</box>
|
|
317
287
|
</box>
|
|
318
288
|
);
|
|
319
289
|
}
|
|
@@ -46,6 +46,8 @@ function getFrameLines(pack: MascotPack, frameName: string): string[] {
|
|
|
46
46
|
|
|
47
47
|
export function createAnimatedRenderer(pack: MascotPack): {
|
|
48
48
|
element: () => JSX.Element;
|
|
49
|
+
propElement: () => JSX.Element | null;
|
|
50
|
+
getPropPosition: () => PropPosition | null;
|
|
49
51
|
getState: () => MascotState;
|
|
50
52
|
setState: (s: MascotState) => void;
|
|
51
53
|
toggleWalk: () => void;
|
|
@@ -331,9 +333,6 @@ export function createAnimatedRenderer(pack: MascotPack): {
|
|
|
331
333
|
scatter();
|
|
332
334
|
bomb();
|
|
333
335
|
versionMsg();
|
|
334
|
-
activeProp();
|
|
335
|
-
propFrameIdx();
|
|
336
|
-
propPosition();
|
|
337
336
|
|
|
338
337
|
for (const [, [get]] of extraSignals) {
|
|
339
338
|
get();
|
|
@@ -368,47 +367,6 @@ export function createAnimatedRenderer(pack: MascotPack): {
|
|
|
368
367
|
lines = effects.render(lines, renderCtx);
|
|
369
368
|
}
|
|
370
369
|
|
|
371
|
-
// ─── Prop overlay ───
|
|
372
|
-
const prop = activeProp();
|
|
373
|
-
if (prop) {
|
|
374
|
-
const propFramesRaw = Array.isArray(prop.frames[0])
|
|
375
|
-
? (prop.frames as string[][])
|
|
376
|
-
: [prop.frames as string[]];
|
|
377
|
-
const propLines = propFramesRaw[propFrameIdx() % propFramesRaw.length] ?? propFramesRaw[0];
|
|
378
|
-
|
|
379
|
-
if (propLines.length > 0) {
|
|
380
|
-
const pos = propPosition();
|
|
381
|
-
if (pos === 'front') {
|
|
382
|
-
const overlayCount = Math.min(propLines.length, lines.length);
|
|
383
|
-
const startRow = Math.floor((lines.length - overlayCount) / 2);
|
|
384
|
-
for (let i = 0; i < overlayCount; i++) {
|
|
385
|
-
lines[startRow + i] = propLines[i];
|
|
386
|
-
}
|
|
387
|
-
} else {
|
|
388
|
-
const charWidth = lines[0]?.length ?? 0;
|
|
389
|
-
const propWidth = propLines[0]?.length ?? 0;
|
|
390
|
-
const charHeight = lines.length;
|
|
391
|
-
const propHeight = propLines.length;
|
|
392
|
-
const maxLines = Math.max(charHeight, propHeight);
|
|
393
|
-
const charPad = Math.floor((maxLines - charHeight) / 2);
|
|
394
|
-
const propPad = Math.floor((maxLines - propHeight) / 2);
|
|
395
|
-
const sep = " ";
|
|
396
|
-
|
|
397
|
-
const merged: string[] = [];
|
|
398
|
-
for (let i = 0; i < maxLines; i++) {
|
|
399
|
-
const cLine = (i >= charPad && i < charPad + charHeight)
|
|
400
|
-
? lines[i - charPad]
|
|
401
|
-
: " ".repeat(charWidth);
|
|
402
|
-
const pLine = (i >= propPad && i < propPad + propHeight)
|
|
403
|
-
? propLines[i - propPad]
|
|
404
|
-
: " ".repeat(propWidth);
|
|
405
|
-
merged.push(pos === 'side-right' ? cLine + sep + pLine : pLine + sep + cLine);
|
|
406
|
-
}
|
|
407
|
-
lines = merged;
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
|
|
412
370
|
const top = jumpOffset();
|
|
413
371
|
const left = offset > 0 ? offset : 0;
|
|
414
372
|
const cel = celebrate();
|
|
@@ -435,6 +393,26 @@ export function createAnimatedRenderer(pack: MascotPack): {
|
|
|
435
393
|
);
|
|
436
394
|
};
|
|
437
395
|
|
|
396
|
+
const propElement = () => {
|
|
397
|
+
activeProp();
|
|
398
|
+
propFrameIdx();
|
|
399
|
+
const prop = activeProp();
|
|
400
|
+
if (!prop) return null;
|
|
401
|
+
|
|
402
|
+
const propFramesRaw = Array.isArray(prop.frames[0])
|
|
403
|
+
? (prop.frames as string[][])
|
|
404
|
+
: [prop.frames as string[]];
|
|
405
|
+
const propLines = propFramesRaw[propFrameIdx() % propFramesRaw.length] ?? propFramesRaw[0];
|
|
406
|
+
|
|
407
|
+
return (
|
|
408
|
+
<box flexDirection="column" alignItems="flex-start">
|
|
409
|
+
{propLines.map((line: string) => (
|
|
410
|
+
<text fg={fg}>{line}</text>
|
|
411
|
+
))}
|
|
412
|
+
</box>
|
|
413
|
+
);
|
|
414
|
+
};
|
|
415
|
+
|
|
438
416
|
// ─── State control ───
|
|
439
417
|
const setState = (s: MascotState) => {
|
|
440
418
|
setCurrentState(s);
|
|
@@ -658,5 +636,5 @@ export function createAnimatedRenderer(pack: MascotPack): {
|
|
|
658
636
|
|
|
659
637
|
const getProp = () => activeProp();
|
|
660
638
|
|
|
661
|
-
return { element, getState: currentState, setState, toggleWalk, setDragging, setCharacterHidden, celebrateUpdate, bounce, showVersion, scatterIn, explode, setProp, getProp };
|
|
639
|
+
return { element, propElement, getPropPosition: () => propPosition(), getState: currentState, setState, toggleWalk, setDragging, setCharacterHidden, celebrateUpdate, bounce, showVersion, scatterIn, explode, setProp, getProp };
|
|
662
640
|
}
|