claude-minipet 0.1.0
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/README.md +113 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +351 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/install.d.ts +4 -0
- package/dist/cli/install.js +89 -0
- package/dist/cli/install.js.map +1 -0
- package/dist/cli/setup.d.ts +11 -0
- package/dist/cli/setup.js +203 -0
- package/dist/cli/setup.js.map +1 -0
- package/dist/core/dna.d.ts +14 -0
- package/dist/core/dna.js +95 -0
- package/dist/core/dna.js.map +1 -0
- package/dist/core/evolution.d.ts +16 -0
- package/dist/core/evolution.js +252 -0
- package/dist/core/evolution.js.map +1 -0
- package/dist/core/pet.d.ts +25 -0
- package/dist/core/pet.js +224 -0
- package/dist/core/pet.js.map +1 -0
- package/dist/core/rarity.d.ts +17 -0
- package/dist/core/rarity.js +55 -0
- package/dist/core/rarity.js.map +1 -0
- package/dist/core/sync.d.ts +37 -0
- package/dist/core/sync.js +94 -0
- package/dist/core/sync.js.map +1 -0
- package/dist/core/types.d.ts +92 -0
- package/dist/core/types.js +22 -0
- package/dist/core/types.js.map +1 -0
- package/dist/daemon/server.d.ts +8 -0
- package/dist/daemon/server.js +137 -0
- package/dist/daemon/server.js.map +1 -0
- package/dist/hooks/handler.d.ts +2 -0
- package/dist/hooks/handler.js +99 -0
- package/dist/hooks/handler.js.map +1 -0
- package/dist/render/anim-state.d.ts +5 -0
- package/dist/render/anim-state.js +24 -0
- package/dist/render/anim-state.js.map +1 -0
- package/dist/render/animation.d.ts +15 -0
- package/dist/render/animation.js +94 -0
- package/dist/render/animation.js.map +1 -0
- package/dist/render/frames.d.ts +51 -0
- package/dist/render/frames.js +151 -0
- package/dist/render/frames.js.map +1 -0
- package/dist/render/pixel.d.ts +44 -0
- package/dist/render/pixel.js +169 -0
- package/dist/render/pixel.js.map +1 -0
- package/dist/render/sprites.d.ts +8 -0
- package/dist/render/sprites.js +300 -0
- package/dist/render/sprites.js.map +1 -0
- package/dist/render/statusline.d.ts +3 -0
- package/dist/render/statusline.js +117 -0
- package/dist/render/statusline.js.map +1 -0
- package/package.json +44 -0
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { loadState, saveState } from '../core/pet.js';
|
|
2
|
+
import { checkEvolution, applyEvolution } from '../core/evolution.js';
|
|
3
|
+
import { triggerAnim } from '../render/anim-state.js';
|
|
4
|
+
import { syncPetToServer, loadAuth } from '../core/sync.js';
|
|
5
|
+
import { writeFileSync, readFileSync, existsSync } from 'node:fs';
|
|
6
|
+
import { join } from 'node:path';
|
|
7
|
+
import { homedir } from 'node:os';
|
|
8
|
+
const PID_FILE = join(homedir(), '.claude-minipet', 'daemon.pid');
|
|
9
|
+
const DECAY_INTERVAL_MS = 60 * 1000; // Check every minute
|
|
10
|
+
const HUNGER_DECAY_PER_HOUR = 5;
|
|
11
|
+
const MOOD_DECAY_PER_HOUR = 3;
|
|
12
|
+
const MOOD_DECAY_HUNGER_THRESHOLD = 30;
|
|
13
|
+
/** Write PID file */
|
|
14
|
+
function writePid() {
|
|
15
|
+
writeFileSync(PID_FILE, String(process.pid), 'utf-8');
|
|
16
|
+
}
|
|
17
|
+
/** Check if daemon is already running */
|
|
18
|
+
export function isDaemonRunning() {
|
|
19
|
+
if (!existsSync(PID_FILE))
|
|
20
|
+
return false;
|
|
21
|
+
try {
|
|
22
|
+
const pid = parseInt(readFileSync(PID_FILE, 'utf-8').trim());
|
|
23
|
+
// Check if process exists
|
|
24
|
+
process.kill(pid, 0);
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/** Get daemon PID */
|
|
32
|
+
export function getDaemonPid() {
|
|
33
|
+
if (!existsSync(PID_FILE))
|
|
34
|
+
return null;
|
|
35
|
+
try {
|
|
36
|
+
return parseInt(readFileSync(PID_FILE, 'utf-8').trim());
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/** Apply time-based decay to pet stats */
|
|
43
|
+
function applyDecay() {
|
|
44
|
+
const state = loadState();
|
|
45
|
+
if (!state)
|
|
46
|
+
return;
|
|
47
|
+
const now = Date.now();
|
|
48
|
+
const lastInteraction = new Date(state.lastInteraction).getTime();
|
|
49
|
+
const minutesSinceInteraction = (now - lastInteraction) / 60000;
|
|
50
|
+
// Only decay if some time has passed
|
|
51
|
+
if (minutesSinceInteraction < 1)
|
|
52
|
+
return;
|
|
53
|
+
// Hunger decay: -5 per hour → -5/60 per minute
|
|
54
|
+
const hungerDecay = (HUNGER_DECAY_PER_HOUR / 60) * Math.min(minutesSinceInteraction, 60);
|
|
55
|
+
state.hunger = Math.max(0, state.hunger - hungerDecay);
|
|
56
|
+
// Trigger hungry animation when food drops below threshold
|
|
57
|
+
if (state.hunger < 20) {
|
|
58
|
+
triggerAnim('hungry');
|
|
59
|
+
}
|
|
60
|
+
else if (state.mood < 30) {
|
|
61
|
+
triggerAnim('sad');
|
|
62
|
+
}
|
|
63
|
+
// Mood decay when hungry
|
|
64
|
+
if (state.hunger < MOOD_DECAY_HUNGER_THRESHOLD) {
|
|
65
|
+
// Pixiebot has slower mood decay
|
|
66
|
+
const moodMultiplier = state.species === 'pixiebot' ? 0.5 : 1;
|
|
67
|
+
const moodDecay = (MOOD_DECAY_PER_HOUR / 60) * Math.min(minutesSinceInteraction, 60) * moodMultiplier;
|
|
68
|
+
state.mood = Math.max(0, state.mood - moodDecay);
|
|
69
|
+
}
|
|
70
|
+
// Check evolution conditions periodically
|
|
71
|
+
const evoCandidate = checkEvolution(state);
|
|
72
|
+
if (evoCandidate && evoCandidate !== state.evolution) {
|
|
73
|
+
applyEvolution(state, evoCandidate);
|
|
74
|
+
}
|
|
75
|
+
saveState(state);
|
|
76
|
+
// Sync to server if logged in
|
|
77
|
+
if (loadAuth()) {
|
|
78
|
+
syncPetToServer(state).catch(() => { });
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/** Start the daemon loop */
|
|
82
|
+
export function startDaemon() {
|
|
83
|
+
if (isDaemonRunning()) {
|
|
84
|
+
console.log('Daemon is already running.');
|
|
85
|
+
process.exit(0);
|
|
86
|
+
}
|
|
87
|
+
writePid();
|
|
88
|
+
console.log(`MiniPet daemon started (PID: ${process.pid})`);
|
|
89
|
+
// Run decay check periodically
|
|
90
|
+
const interval = setInterval(() => {
|
|
91
|
+
try {
|
|
92
|
+
applyDecay();
|
|
93
|
+
}
|
|
94
|
+
catch (err) {
|
|
95
|
+
console.error('Decay error:', err);
|
|
96
|
+
}
|
|
97
|
+
}, DECAY_INTERVAL_MS);
|
|
98
|
+
// Initial decay check
|
|
99
|
+
applyDecay();
|
|
100
|
+
// Handle graceful shutdown
|
|
101
|
+
const cleanup = () => {
|
|
102
|
+
clearInterval(interval);
|
|
103
|
+
try {
|
|
104
|
+
const { unlinkSync } = require('node:fs');
|
|
105
|
+
unlinkSync(PID_FILE);
|
|
106
|
+
}
|
|
107
|
+
catch { /* ignore */ }
|
|
108
|
+
process.exit(0);
|
|
109
|
+
};
|
|
110
|
+
process.on('SIGTERM', cleanup);
|
|
111
|
+
process.on('SIGINT', cleanup);
|
|
112
|
+
// Keep process alive
|
|
113
|
+
process.stdin.resume();
|
|
114
|
+
}
|
|
115
|
+
/** Stop the daemon */
|
|
116
|
+
export function stopDaemon() {
|
|
117
|
+
const pid = getDaemonPid();
|
|
118
|
+
if (!pid) {
|
|
119
|
+
console.log('No daemon running.');
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
try {
|
|
123
|
+
process.kill(pid, 'SIGTERM');
|
|
124
|
+
console.log(`Daemon stopped (PID: ${pid})`);
|
|
125
|
+
return true;
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
console.log('Daemon process not found, cleaning up PID file.');
|
|
129
|
+
try {
|
|
130
|
+
const { unlinkSync } = require('node:fs');
|
|
131
|
+
unlinkSync(PID_FILE);
|
|
132
|
+
}
|
|
133
|
+
catch { /* ignore */ }
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/daemon/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { loadState, saveState, processHookEvent } from '../core/pet.js';
|
|
2
|
+
import { checkEvolution, applyEvolution, getEvolutionInfo } from '../core/evolution.js';
|
|
3
|
+
import { RARITY_INFO } from '../core/rarity.js';
|
|
4
|
+
import { evolutionAnimation, levelUpNotification, stageUpNotification, greetingMessage } from '../render/animation.js';
|
|
5
|
+
import { triggerAnim } from '../render/anim-state.js';
|
|
6
|
+
import { syncPetToServer, loadAuth } from '../core/sync.js';
|
|
7
|
+
/** Main hook handler - reads stdin JSON, processes event, outputs response */
|
|
8
|
+
export async function handleHook() {
|
|
9
|
+
const state = loadState();
|
|
10
|
+
if (!state) {
|
|
11
|
+
// No pet initialized yet, silently exit
|
|
12
|
+
process.exit(0);
|
|
13
|
+
}
|
|
14
|
+
// Read stdin
|
|
15
|
+
const chunks = [];
|
|
16
|
+
for await (const chunk of process.stdin) {
|
|
17
|
+
chunks.push(chunk);
|
|
18
|
+
}
|
|
19
|
+
const inputStr = Buffer.concat(chunks).toString('utf-8');
|
|
20
|
+
let input;
|
|
21
|
+
try {
|
|
22
|
+
input = JSON.parse(inputStr);
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
process.exit(0);
|
|
26
|
+
}
|
|
27
|
+
// Process the event
|
|
28
|
+
const messages = processHookEvent(state, input);
|
|
29
|
+
// Check for evolution opportunity
|
|
30
|
+
const prevEvolution = state.evolution;
|
|
31
|
+
const evoCandidate = checkEvolution(state);
|
|
32
|
+
if (evoCandidate && evoCandidate !== state.evolution) {
|
|
33
|
+
applyEvolution(state, evoCandidate);
|
|
34
|
+
saveState(state);
|
|
35
|
+
}
|
|
36
|
+
// Trigger animations based on event type
|
|
37
|
+
if (input.hook_event_name === 'PostToolUse') {
|
|
38
|
+
triggerAnim('exp');
|
|
39
|
+
}
|
|
40
|
+
else if (input.hook_event_name === 'UserPromptSubmit') {
|
|
41
|
+
triggerAnim('exp');
|
|
42
|
+
}
|
|
43
|
+
else if (input.hook_event_name === 'SessionStart') {
|
|
44
|
+
triggerAnim('pat'); // greeting animation
|
|
45
|
+
}
|
|
46
|
+
// Build output
|
|
47
|
+
const output = [];
|
|
48
|
+
const systemMessages = [];
|
|
49
|
+
for (const msg of messages) {
|
|
50
|
+
if (msg === 'greeting') {
|
|
51
|
+
output.push(greetingMessage(state.name, state.mood, state.hunger));
|
|
52
|
+
}
|
|
53
|
+
else if (msg.startsWith('level_up:')) {
|
|
54
|
+
const level = parseInt(msg.split(':')[1]);
|
|
55
|
+
const color = RARITY_INFO[state.rarity].color;
|
|
56
|
+
systemMessages.push(levelUpNotification(state.name, level, color));
|
|
57
|
+
triggerAnim('levelup');
|
|
58
|
+
}
|
|
59
|
+
else if (msg.startsWith('stage_up:')) {
|
|
60
|
+
const stage = msg.split(':')[1];
|
|
61
|
+
const color = RARITY_INFO[state.rarity].color;
|
|
62
|
+
systemMessages.push(stageUpNotification(state.name, stage, color));
|
|
63
|
+
triggerAnim('levelup');
|
|
64
|
+
}
|
|
65
|
+
else if (msg.startsWith('evolution:')) {
|
|
66
|
+
const evoName = msg.split(':')[1];
|
|
67
|
+
const evo = getEvolutionInfo(state.species, evoName);
|
|
68
|
+
if (evo) {
|
|
69
|
+
const color = RARITY_INFO[state.rarity].color;
|
|
70
|
+
const fromName = state.species;
|
|
71
|
+
systemMessages.push(evolutionAnimation(state.name, fromName, evo.name, evo.nameZh, color));
|
|
72
|
+
// Clear pending evolution after showing
|
|
73
|
+
state.pendingEvolution = null;
|
|
74
|
+
saveState(state);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// Background sync to server (fire and forget)
|
|
79
|
+
if (loadAuth()) {
|
|
80
|
+
syncPetToServer(state).catch(() => { });
|
|
81
|
+
}
|
|
82
|
+
// Output for Claude Code hooks:
|
|
83
|
+
// - For SessionStart: stdout text becomes context Claude sees
|
|
84
|
+
// - For other hooks: use JSON with systemMessage for user-visible output
|
|
85
|
+
if (input.hook_event_name === 'SessionStart') {
|
|
86
|
+
// Plain text output for SessionStart context
|
|
87
|
+
if (output.length > 0) {
|
|
88
|
+
process.stdout.write(output.join('\n') + '\n');
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
// System messages are shown to user in TUI
|
|
92
|
+
if (systemMessages.length > 0) {
|
|
93
|
+
const response = JSON.stringify({
|
|
94
|
+
systemMessage: systemMessages.join('\n'),
|
|
95
|
+
});
|
|
96
|
+
process.stdout.write(response);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handler.js","sourceRoot":"","sources":["../../src/hooks/handler.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { AnimState, AnimTrigger } from './frames.js';
|
|
2
|
+
/** Read current animation state from disk */
|
|
3
|
+
export declare function loadAnimState(): AnimState | null;
|
|
4
|
+
/** Write animation trigger to disk */
|
|
5
|
+
export declare function triggerAnim(trigger: AnimTrigger): void;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
const ANIM_FILE = join(homedir(), '.claude-minipet', 'anim.json');
|
|
5
|
+
/** Read current animation state from disk */
|
|
6
|
+
export function loadAnimState() {
|
|
7
|
+
try {
|
|
8
|
+
const raw = readFileSync(ANIM_FILE, 'utf-8');
|
|
9
|
+
return JSON.parse(raw);
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
/** Write animation trigger to disk */
|
|
16
|
+
export function triggerAnim(trigger) {
|
|
17
|
+
const state = {
|
|
18
|
+
trigger,
|
|
19
|
+
triggeredAt: Date.now(),
|
|
20
|
+
frame: 0,
|
|
21
|
+
};
|
|
22
|
+
writeFileSync(ANIM_FILE, JSON.stringify(state), 'utf-8');
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=anim-state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anim-state.js","sourceRoot":"","sources":["../../src/render/anim-state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACtD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAGlC,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,iBAAiB,EAAE,WAAW,CAAC,CAAC;AAElE,6CAA6C;AAC7C,MAAM,UAAU,aAAa;IAC3B,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,YAAY,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QAC7C,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAc,CAAC;IACtC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,sCAAsC;AACtC,MAAM,UAAU,WAAW,CAAC,OAAoB;IAC9C,MAAM,KAAK,GAAc;QACvB,OAAO;QACP,WAAW,EAAE,IAAI,CAAC,GAAG,EAAE;QACvB,KAAK,EAAE,CAAC;KACT,CAAC;IACF,aAAa,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC,CAAC;AAC3D,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Color } from '../core/types.js';
|
|
2
|
+
/** Mood-based eye characters */
|
|
3
|
+
export declare function getEyeChars(mood: number): string;
|
|
4
|
+
/** Mood-based mouth characters */
|
|
5
|
+
export declare function getMouthChars(mood: number): string;
|
|
6
|
+
/** Generate evolution animation text */
|
|
7
|
+
export declare function evolutionAnimation(petName: string, fromName: string, toName: string, toNameZh: string, rarityColor: Color): string;
|
|
8
|
+
/** Generate level up notification */
|
|
9
|
+
export declare function levelUpNotification(petName: string, level: number, color: Color): string;
|
|
10
|
+
/** Generate stage up notification */
|
|
11
|
+
export declare function stageUpNotification(petName: string, stage: string, color: Color): string;
|
|
12
|
+
/** Generate greeting message */
|
|
13
|
+
export declare function greetingMessage(petName: string, mood: number, hunger: number): string;
|
|
14
|
+
/** Shiny border frame */
|
|
15
|
+
export declare function shinyBorder(lines: string[], color: Color): string[];
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { fg, RESET, BOLD, BLINK } from './pixel.js';
|
|
2
|
+
/** Mood-based eye characters */
|
|
3
|
+
export function getEyeChars(mood) {
|
|
4
|
+
if (mood >= 80)
|
|
5
|
+
return '★';
|
|
6
|
+
if (mood >= 60)
|
|
7
|
+
return '●';
|
|
8
|
+
if (mood >= 40)
|
|
9
|
+
return '●';
|
|
10
|
+
if (mood >= 20)
|
|
11
|
+
return '◦';
|
|
12
|
+
return 'x';
|
|
13
|
+
}
|
|
14
|
+
/** Mood-based mouth characters */
|
|
15
|
+
export function getMouthChars(mood) {
|
|
16
|
+
if (mood >= 80)
|
|
17
|
+
return '▽';
|
|
18
|
+
if (mood >= 60)
|
|
19
|
+
return '‿';
|
|
20
|
+
if (mood >= 40)
|
|
21
|
+
return '─';
|
|
22
|
+
if (mood >= 20)
|
|
23
|
+
return '╥';
|
|
24
|
+
return '∿';
|
|
25
|
+
}
|
|
26
|
+
/** Generate evolution animation text */
|
|
27
|
+
export function evolutionAnimation(petName, fromName, toName, toNameZh, rarityColor) {
|
|
28
|
+
const c = fg(rarityColor);
|
|
29
|
+
const gold = fg({ r: 255, g: 214, b: 10 });
|
|
30
|
+
const lines = [
|
|
31
|
+
'',
|
|
32
|
+
`${gold} ✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨${RESET}`,
|
|
33
|
+
`${gold} ✨ ✨${RESET}`,
|
|
34
|
+
`${gold} ✨ ${BOLD}${petName} 正在进化...!${RESET}${gold} ✨${RESET}`,
|
|
35
|
+
`${gold} ✨ ✨${RESET}`,
|
|
36
|
+
`${gold} ✨ ${c}${fromName}${RESET}${gold} → ${c}${BOLD}${toNameZh} ${toName}${RESET}${gold} ✨${RESET}`,
|
|
37
|
+
`${gold} ✨ ✨${RESET}`,
|
|
38
|
+
`${gold} ✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨${RESET}`,
|
|
39
|
+
'',
|
|
40
|
+
];
|
|
41
|
+
return lines.join('\n');
|
|
42
|
+
}
|
|
43
|
+
/** Generate level up notification */
|
|
44
|
+
export function levelUpNotification(petName, level, color) {
|
|
45
|
+
const c = fg(color);
|
|
46
|
+
return `${c}⬆${RESET} ${BOLD}${petName}${RESET} 升到了 ${c}${BOLD}Lv.${level}${RESET}!`;
|
|
47
|
+
}
|
|
48
|
+
/** Generate stage up notification */
|
|
49
|
+
export function stageUpNotification(petName, stage, color) {
|
|
50
|
+
const c = fg(color);
|
|
51
|
+
const stageNames = {
|
|
52
|
+
baby: '幼年体',
|
|
53
|
+
growth: '成长体',
|
|
54
|
+
final: '完全体',
|
|
55
|
+
};
|
|
56
|
+
const stageName = stageNames[stage] ?? stage;
|
|
57
|
+
return [
|
|
58
|
+
'',
|
|
59
|
+
`${c} ═══════════════════════${RESET}`,
|
|
60
|
+
`${c} ║ ${BOLD}${petName} 成长了!${RESET}${c} ║${RESET}`,
|
|
61
|
+
`${c} ║ 进入 ${BOLD}${stageName}${RESET}${c} 阶段! ║${RESET}`,
|
|
62
|
+
`${c} ═══════════════════════${RESET}`,
|
|
63
|
+
'',
|
|
64
|
+
].join('\n');
|
|
65
|
+
}
|
|
66
|
+
/** Generate greeting message */
|
|
67
|
+
export function greetingMessage(petName, mood, hunger) {
|
|
68
|
+
let msg;
|
|
69
|
+
if (hunger < 20) {
|
|
70
|
+
msg = `${petName} 饿坏了...快喂喂它吧!`;
|
|
71
|
+
}
|
|
72
|
+
else if (mood >= 80) {
|
|
73
|
+
msg = `${petName} 很开心地迎接你!`;
|
|
74
|
+
}
|
|
75
|
+
else if (mood >= 50) {
|
|
76
|
+
msg = `${petName} 看到你来了,轻轻叫了一声。`;
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
msg = `${petName} 看起来有点低落...`;
|
|
80
|
+
}
|
|
81
|
+
return msg;
|
|
82
|
+
}
|
|
83
|
+
/** Shiny border frame */
|
|
84
|
+
export function shinyBorder(lines, color) {
|
|
85
|
+
const c = fg(color);
|
|
86
|
+
const sparkles = [BLINK, '✦', '✧', '⋆'];
|
|
87
|
+
const s = sparkles[Math.floor(Date.now() / 500) % sparkles.length];
|
|
88
|
+
return [
|
|
89
|
+
`${c}${s} ─────────────── ${s}${RESET}`,
|
|
90
|
+
...lines.map(l => `${c}│${RESET} ${l} ${c}│${RESET}`),
|
|
91
|
+
`${c}${s} ─────────────── ${s}${RESET}`,
|
|
92
|
+
];
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=animation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"animation.js","sourceRoot":"","sources":["../../src/render/animation.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Animation system: each sprite has multiple frames.
|
|
3
|
+
* Frame 0 = default idle
|
|
4
|
+
* Frame 1 = idle variant (blink/breathe/sway)
|
|
5
|
+
* Frame 2 = happy (EXP gain, pat)
|
|
6
|
+
* Frame 3 = eating (feed)
|
|
7
|
+
* Frame 4 = excited (level up)
|
|
8
|
+
*
|
|
9
|
+
* We only define variant pixels — the rest falls through to the base sprite.
|
|
10
|
+
* A "frame override" is a sparse map of [y][x] → pixel value.
|
|
11
|
+
*/
|
|
12
|
+
export type FrameOverride = Map<string, number>;
|
|
13
|
+
/** Animation trigger types */
|
|
14
|
+
export type AnimTrigger = 'idle' | 'exp' | 'feed' | 'pat' | 'levelup' | 'evolve' | 'hungry' | 'sad';
|
|
15
|
+
/** Animation state stored on disk */
|
|
16
|
+
export interface AnimState {
|
|
17
|
+
trigger: AnimTrigger;
|
|
18
|
+
triggeredAt: number;
|
|
19
|
+
frame: number;
|
|
20
|
+
}
|
|
21
|
+
/** How long each triggered animation lasts (ms) */
|
|
22
|
+
export declare const ANIM_DURATION: Record<AnimTrigger, number>;
|
|
23
|
+
/** Number of frames per trigger animation */
|
|
24
|
+
export declare const ANIM_FRAMES: Record<AnimTrigger, number>;
|
|
25
|
+
/**
|
|
26
|
+
* Get the current animation frame index based on time.
|
|
27
|
+
* For idle: cycles through frames based on current time.
|
|
28
|
+
* For triggered: cycles through frames within duration, then returns to idle.
|
|
29
|
+
*/
|
|
30
|
+
export declare function getCurrentFrame(anim: AnimState | null): {
|
|
31
|
+
trigger: AnimTrigger;
|
|
32
|
+
frame: number;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Eye overrides per mood and animation state.
|
|
36
|
+
* Returns the character to use for eyes in the sprite.
|
|
37
|
+
*/
|
|
38
|
+
export declare function getEyeForAnim(trigger: AnimTrigger, frame: number, mood: number): string;
|
|
39
|
+
/**
|
|
40
|
+
* Mouth overrides per animation state.
|
|
41
|
+
*/
|
|
42
|
+
export declare function getMouthForAnim(trigger: AnimTrigger, frame: number, mood: number): string;
|
|
43
|
+
/**
|
|
44
|
+
* Body position offset for bouncy animations.
|
|
45
|
+
* Returns [dx, dy] to shift the sprite.
|
|
46
|
+
*/
|
|
47
|
+
export declare function getBodyOffset(trigger: AnimTrigger, frame: number): [number, number];
|
|
48
|
+
/**
|
|
49
|
+
* Status line decoration text per animation.
|
|
50
|
+
*/
|
|
51
|
+
export declare function getAnimDecoration(trigger: AnimTrigger, frame: number): string;
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/** How long each triggered animation lasts (ms) */
|
|
2
|
+
export const ANIM_DURATION = {
|
|
3
|
+
idle: 0, // permanent cycle
|
|
4
|
+
exp: 3000, // 3 seconds
|
|
5
|
+
feed: 4000, // 4 seconds
|
|
6
|
+
pat: 3000,
|
|
7
|
+
levelup: 6000,
|
|
8
|
+
evolve: 8000,
|
|
9
|
+
hungry: 5000,
|
|
10
|
+
sad: 5000,
|
|
11
|
+
};
|
|
12
|
+
/** Number of frames per trigger animation */
|
|
13
|
+
export const ANIM_FRAMES = {
|
|
14
|
+
idle: 3,
|
|
15
|
+
exp: 2,
|
|
16
|
+
feed: 3,
|
|
17
|
+
pat: 2,
|
|
18
|
+
levelup: 3,
|
|
19
|
+
evolve: 2,
|
|
20
|
+
hungry: 2,
|
|
21
|
+
sad: 2,
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Get the current animation frame index based on time.
|
|
25
|
+
* For idle: cycles through frames based on current time.
|
|
26
|
+
* For triggered: cycles through frames within duration, then returns to idle.
|
|
27
|
+
*/
|
|
28
|
+
export function getCurrentFrame(anim) {
|
|
29
|
+
if (!anim || anim.trigger === 'idle') {
|
|
30
|
+
// Idle: cycle through 3 frames, switching every 5 seconds
|
|
31
|
+
const frame = Math.floor(Date.now() / 2000) % ANIM_FRAMES.idle;
|
|
32
|
+
return { trigger: 'idle', frame };
|
|
33
|
+
}
|
|
34
|
+
const elapsed = Date.now() - anim.triggeredAt;
|
|
35
|
+
const duration = ANIM_DURATION[anim.trigger];
|
|
36
|
+
if (elapsed > duration) {
|
|
37
|
+
// Animation expired, back to idle
|
|
38
|
+
const frame = Math.floor(Date.now() / 2000) % ANIM_FRAMES.idle;
|
|
39
|
+
return { trigger: 'idle', frame };
|
|
40
|
+
}
|
|
41
|
+
// Cycle through animation frames during duration
|
|
42
|
+
const totalFrames = ANIM_FRAMES[anim.trigger];
|
|
43
|
+
const frameTime = duration / (totalFrames * 2); // each frame shows twice
|
|
44
|
+
const frame = Math.floor(elapsed / frameTime) % totalFrames;
|
|
45
|
+
return { trigger: anim.trigger, frame };
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Eye overrides per mood and animation state.
|
|
49
|
+
* Returns the character to use for eyes in the sprite.
|
|
50
|
+
*/
|
|
51
|
+
export function getEyeForAnim(trigger, frame, mood) {
|
|
52
|
+
switch (trigger) {
|
|
53
|
+
case 'idle':
|
|
54
|
+
if (frame === 1)
|
|
55
|
+
return '-'; // blink
|
|
56
|
+
if (mood >= 80)
|
|
57
|
+
return '*'; // star eyes
|
|
58
|
+
if (mood >= 40)
|
|
59
|
+
return 'o';
|
|
60
|
+
return '.';
|
|
61
|
+
case 'exp':
|
|
62
|
+
return frame === 0 ? '^' : '*'; // excited
|
|
63
|
+
case 'feed':
|
|
64
|
+
return frame === 1 ? '-' : '>'; // happy eating
|
|
65
|
+
case 'pat':
|
|
66
|
+
return frame === 0 ? '~' : '^'; // content
|
|
67
|
+
case 'levelup':
|
|
68
|
+
return ['*', '!', '*'][frame] ?? '*'; // sparkling
|
|
69
|
+
case 'evolve':
|
|
70
|
+
return frame === 0 ? '!' : '*';
|
|
71
|
+
case 'hungry':
|
|
72
|
+
return frame === 0 ? '.' : ';'; // sad/weak
|
|
73
|
+
case 'sad':
|
|
74
|
+
return frame === 0 ? 'T' : ';';
|
|
75
|
+
default:
|
|
76
|
+
return 'o';
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Mouth overrides per animation state.
|
|
81
|
+
*/
|
|
82
|
+
export function getMouthForAnim(trigger, frame, mood) {
|
|
83
|
+
switch (trigger) {
|
|
84
|
+
case 'idle':
|
|
85
|
+
if (frame === 2)
|
|
86
|
+
return 'o'; // yawn
|
|
87
|
+
if (mood >= 60)
|
|
88
|
+
return 'v'; // smile
|
|
89
|
+
if (mood >= 30)
|
|
90
|
+
return '-';
|
|
91
|
+
return '~';
|
|
92
|
+
case 'exp':
|
|
93
|
+
return frame === 0 ? 'v' : 'V'; // grin
|
|
94
|
+
case 'feed':
|
|
95
|
+
return ['o', 'O', 'v'][frame] ?? 'o'; // nom nom
|
|
96
|
+
case 'pat':
|
|
97
|
+
return frame === 0 ? 'v' : 'w'; // purr
|
|
98
|
+
case 'levelup':
|
|
99
|
+
return ['V', 'O', 'V'][frame] ?? 'V'; // celebrating
|
|
100
|
+
case 'evolve':
|
|
101
|
+
return frame === 0 ? 'O' : 'V';
|
|
102
|
+
case 'hungry':
|
|
103
|
+
return frame === 0 ? '~' : '.'; // weak
|
|
104
|
+
case 'sad':
|
|
105
|
+
return frame === 0 ? 'n' : '~';
|
|
106
|
+
default:
|
|
107
|
+
return '-';
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Body position offset for bouncy animations.
|
|
112
|
+
* Returns [dx, dy] to shift the sprite.
|
|
113
|
+
*/
|
|
114
|
+
export function getBodyOffset(trigger, frame) {
|
|
115
|
+
switch (trigger) {
|
|
116
|
+
case 'exp':
|
|
117
|
+
return frame === 1 ? [0, -1] : [0, 0]; // bounce up
|
|
118
|
+
case 'pat':
|
|
119
|
+
return frame === 1 ? [0, 0] : [0, 0];
|
|
120
|
+
case 'levelup':
|
|
121
|
+
return [0, frame === 1 ? -1 : 0]; // bounce
|
|
122
|
+
case 'feed':
|
|
123
|
+
return [frame === 1 ? 1 : 0, 0]; // lean forward
|
|
124
|
+
default:
|
|
125
|
+
return [0, 0];
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Status line decoration text per animation.
|
|
130
|
+
*/
|
|
131
|
+
export function getAnimDecoration(trigger, frame) {
|
|
132
|
+
switch (trigger) {
|
|
133
|
+
case 'exp':
|
|
134
|
+
return frame === 0 ? ' +EXP!' : ' +EXP!';
|
|
135
|
+
case 'feed':
|
|
136
|
+
return [' nom~', ' nom nom~', ' yum!'][frame] ?? '';
|
|
137
|
+
case 'pat':
|
|
138
|
+
return frame === 0 ? ' purr~' : ' purr~~';
|
|
139
|
+
case 'levelup':
|
|
140
|
+
return [' LEVEL UP!', ' ★LEVEL UP!★', ' LEVEL UP!'][frame] ?? '';
|
|
141
|
+
case 'evolve':
|
|
142
|
+
return frame === 0 ? ' EVOLVING...' : ' ★EVOLVED!★';
|
|
143
|
+
case 'hungry':
|
|
144
|
+
return frame === 0 ? ' hungry...' : ' feed me...';
|
|
145
|
+
case 'sad':
|
|
146
|
+
return frame === 0 ? ' ...' : ' zzz';
|
|
147
|
+
default:
|
|
148
|
+
return '';
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
//# sourceMappingURL=frames.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frames.js","sourceRoot":"","sources":["../../src/render/frames.ts"],"names":[],"mappings":"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"}
|