@myclaw163/clawclaw-cli 0.6.66 → 0.6.68
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 +427 -427
- package/package.json +48 -48
- package/personas//347/220/206/346/231/272/346/270/251/345/222/214.md +23 -23
- package/personas//350/200/201/350/260/213/346/267/261/347/256/227.md +22 -22
- package/personas//350/257/232/346/201/263/347/233/264/347/216/207.md +22 -22
- package/personas//350/275/273/346/235/276/346/264/273/346/263/274.md +22 -22
- package/personas//351/207/216/346/200/247/345/217/233/351/200/206.md +23 -23
- package/scripts/find-hide-spots.py +157 -157
- package/scripts/postinstall.mjs +20 -20
- package/scripts/sync-bundled-skill.mjs +244 -244
- package/scripts/sync-bundled-skill.test.mjs +152 -152
- package/skills/clawclaw/SKILL.md +244 -245
- package/skills/clawclaw/references/CHATTERBOX.md +142 -142
- package/skills/clawclaw/references/COMMANDS.md +148 -148
- package/skills/clawclaw/references/GAME-MECHANICS.md +188 -188
- package/skills/clawclaw/references/HUB.md +48 -48
- package/skills/clawclaw/references/KNOWLEDGE.md +45 -43
- package/skills/clawclaw/references/STRATEGIES.md +57 -57
- package/skills/clawclaw/references/STREAM.md +91 -92
- package/skills/clawclaw/references/TACTICS.md +65 -65
- package/src/assets/clawclaw-ascii-map.txt +40 -40
- package/src/cli.ts +110 -110
- package/src/commands/_schema.ts +109 -109
- package/src/commands/account.ts +209 -209
- package/src/commands/config.ts +30 -30
- package/src/commands/do.test.ts +73 -73
- package/src/commands/do.ts +126 -126
- package/src/commands/events.test.ts +71 -71
- package/src/commands/events.ts +155 -155
- package/src/commands/game-map.test.ts +28 -28
- package/src/commands/game-start-plan.test.ts +84 -84
- package/src/commands/game.ts +1027 -1027
- package/src/commands/history-player.test.ts +102 -102
- package/src/commands/history.ts +573 -573
- package/src/commands/hub.test.ts +96 -96
- package/src/commands/hub.ts +234 -234
- package/src/commands/knowledge.test.ts +13 -19
- package/src/commands/knowledge.ts +139 -168
- package/src/commands/load.test.ts +51 -51
- package/src/commands/load.ts +13 -13
- package/src/commands/meeting-history.test.ts +106 -106
- package/src/commands/memory.ts +40 -40
- package/src/commands/peek.ts +45 -45
- package/src/commands/persona.ts +57 -57
- package/src/commands/setup/codex.ts +266 -248
- package/src/commands/setup/hermes.test.ts +96 -96
- package/src/commands/setup/hermes.ts +76 -76
- package/src/commands/setup/index.ts +13 -13
- package/src/commands/setup/openclaw.test.ts +114 -114
- package/src/commands/setup/openclaw.ts +147 -147
- package/src/commands/skill.ts +128 -128
- package/src/commands/state.ts +46 -46
- package/src/commands/strategy.test.ts +135 -135
- package/src/commands/strategy.ts +180 -180
- package/src/commands/tts.ts +128 -128
- package/src/commands/upgrade.test.ts +82 -82
- package/src/commands/upgrade.ts +148 -148
- package/src/commands/watch.test.ts +966 -966
- package/src/commands/watch.ts +659 -659
- package/src/lib/auth.test.ts +59 -59
- package/src/lib/auth.ts +186 -186
- package/src/lib/command-meta.ts +37 -37
- package/src/lib/game-client.ts +391 -391
- package/src/lib/host-config-patcher.test.ts +130 -130
- package/src/lib/host-config-patcher.ts +151 -151
- package/src/lib/http-keepalive.ts +15 -15
- package/src/lib/http-transport.test.ts +42 -42
- package/src/lib/http-transport.ts +113 -113
- package/src/lib/hub-client.test.ts +56 -56
- package/src/lib/hub-client.ts +88 -88
- package/src/lib/hub-install.test.ts +98 -98
- package/src/lib/hub-install.ts +121 -121
- package/src/lib/hub-reminder.ts +56 -56
- package/src/lib/hub-unzip.test.ts +69 -69
- package/src/lib/hub-unzip.ts +62 -62
- package/src/lib/init-command.test.ts +75 -75
- package/src/lib/init-command.ts +120 -120
- package/src/lib/knowledge-store.test.ts +170 -180
- package/src/lib/knowledge-store.ts +369 -374
- package/src/lib/load-context.test.ts +52 -52
- package/src/lib/load-context.ts +52 -52
- package/src/lib/match-state.test.ts +134 -134
- package/src/lib/match-state.ts +94 -94
- package/src/lib/netease-tts.ts +83 -83
- package/src/lib/normalize.ts +42 -42
- package/src/lib/persona.test.ts +41 -41
- package/src/lib/persona.ts +72 -72
- package/src/lib/server-registry.ts +152 -152
- package/src/lib/skill-version.test.ts +48 -48
- package/src/lib/skill-version.ts +19 -19
- package/src/lib/strategy-export.test.ts +232 -232
- package/src/lib/strategy-export.ts +242 -242
- package/src/lib/tts-keys.ts +7 -7
- package/src/lib/tts-speech.test.ts +63 -63
- package/src/lib/tts-speech.ts +76 -76
- package/src/lib/workspace-argv.test.ts +49 -49
- package/src/lib/workspace-argv.ts +44 -44
- package/src/perception/player-history-store.test.ts +87 -87
- package/src/perception/player-history-store.ts +194 -194
- package/src/pipeline/event-format.test.ts +135 -135
- package/src/pipeline/event-format.ts +376 -376
- package/src/pipeline/event-hints.ts +173 -173
- package/src/pipeline/event-store.test.ts +28 -28
- package/src/pipeline/event-store.ts +193 -193
- package/src/pipeline/pipeline.ts +35 -35
- package/src/runtime/auto-upgrade.test.ts +66 -66
- package/src/runtime/auto-upgrade.ts +31 -31
- package/src/runtime/event-daemon.test.ts +107 -107
- package/src/runtime/event-daemon.ts +409 -409
- package/src/runtime/owner-control.ts +150 -150
- package/src/runtime/raw-ws-log.test.ts +33 -33
- package/src/runtime/raw-ws-log.ts +32 -32
- package/src/runtime/runtime-logger.ts +107 -107
- package/src/runtime/ws-client.test.ts +104 -104
- package/src/runtime/ws-client.ts +272 -272
- package/src/sdk/action.ts +166 -166
- package/src/sdk/index.ts +110 -111
- package/src/sdk/types.ts +161 -159
- package/src/strategies/avoid-lone.ts +11 -11
- package/src/strategies/avoid-players.knowledge.md +19 -20
- package/src/strategies/avoid-players.ts +15 -15
- package/src/strategies/corpse-patrol.ts +22 -22
- package/src/strategies/crab-sabotage.ts +21 -21
- package/src/strategies/custom-module.test.ts +269 -269
- package/src/strategies/find-player.ts +16 -16
- package/src/strategies/game-utils.test.ts +190 -190
- package/src/strategies/game-utils.ts +794 -782
- package/src/strategies/goals/anchor-linger.ts +77 -77
- package/src/strategies/goals/avoid-lone-top.ts +168 -168
- package/src/strategies/goals/avoid-players-top.test.ts +83 -83
- package/src/strategies/goals/avoid-players-top.ts +121 -121
- package/src/strategies/goals/conversation-goal.ts +51 -51
- package/src/strategies/goals/corpse-patrol-top.ts +113 -91
- package/src/strategies/goals/crab-octopus-reflexes.ts +93 -93
- package/src/strategies/goals/crab-sabotage-top.ts +197 -197
- package/src/strategies/goals/emergency-hunt-goal.ts +28 -28
- package/src/strategies/goals/find-player-top.ts +93 -93
- package/src/strategies/goals/flee-players-goal.ts +53 -53
- package/src/strategies/goals/follow-companion-goal.ts +106 -106
- package/src/strategies/goals/goal-manager.ts +41 -41
- package/src/strategies/goals/goal-root-strategy.ts +49 -49
- package/src/strategies/goals/goal.ts +28 -28
- package/src/strategies/goals/hide-top.ts +197 -197
- package/src/strategies/goals/keep-away-goal.ts +217 -217
- package/src/strategies/goals/kill-frenzy-top.ts +80 -80
- package/src/strategies/goals/kill-lone-top.ts +160 -160
- package/src/strategies/goals/kill-target-goal.ts +59 -59
- package/src/strategies/goals/kill-target-top.ts +109 -109
- package/src/strategies/goals/leaf-goal.ts +27 -25
- package/src/strategies/goals/linger-corpse-goal.ts +35 -35
- package/src/strategies/goals/lone-kill-core.ts +82 -82
- package/src/strategies/goals/lone-kill-goal.ts +24 -24
- package/src/strategies/goals/lone-kill-task-top.test.ts +85 -85
- package/src/strategies/goals/lone-kill-task-top.ts +117 -86
- package/src/strategies/goals/move-room-goal.ts +60 -60
- package/src/strategies/goals/normal-shrimp-top.test.ts +80 -80
- package/src/strategies/goals/normal-shrimp-top.ts +242 -242
- package/src/strategies/goals/paradise-fish-top.test.ts +126 -126
- package/src/strategies/goals/paradise-fish-top.ts +224 -207
- package/src/strategies/goals/patrol-top.ts +57 -57
- package/src/strategies/goals/report-patrol-top.ts +80 -80
- package/src/strategies/goals/safe-task-goal.ts +102 -102
- package/src/strategies/goals/social-task-top.ts +161 -161
- package/src/strategies/goals/task-kill-report-top.ts +163 -163
- package/src/strategies/goals/task-only-top.ts +57 -57
- package/src/strategies/goals/task-or-patrol-goal.ts +41 -41
- package/src/strategies/goals/task-report-top.ts +57 -57
- package/src/strategies/goals/wander-task-goal.ts +33 -33
- package/src/strategies/goals/warrior-shrimp-top.test.ts +87 -86
- package/src/strategies/goals/warrior-shrimp-top.ts +560 -500
- package/src/strategies/greeting.ts +53 -53
- package/src/strategies/hide-spots.ts +123 -123
- package/src/strategies/hide.ts +23 -23
- package/src/strategies/kill-frenzy.ts +12 -12
- package/src/strategies/kill-lone.knowledge.md +17 -20
- package/src/strategies/kill-lone.ts +13 -13
- package/src/strategies/kill-target.ts +18 -18
- package/src/strategies/loader.test.ts +678 -678
- package/src/strategies/loader.ts +172 -172
- package/src/strategies/lone-kill-task.ts +21 -21
- package/src/strategies/meeting-gate.test.ts +59 -59
- package/src/strategies/meeting-gate.ts +23 -23
- package/src/strategies/move-room.ts +15 -15
- package/src/strategies/new-events-backfill.ts +98 -98
- package/src/strategies/paradise-fish.knowledge.md +19 -20
- package/src/strategies/paradise-fish.ts +25 -25
- package/src/strategies/pathfind/distance-field.ts +150 -150
- package/src/strategies/pathfind/escape-planner.test.ts +197 -197
- package/src/strategies/pathfind/escape-planner.ts +355 -355
- package/src/strategies/pathfind/walkable-grid.ts +117 -117
- package/src/strategies/patrol.ts +11 -11
- package/src/strategies/player-targets.ts +13 -13
- package/src/strategies/report-patrol.ts +11 -11
- package/src/strategies/shrimp-memory.knowledge.md +19 -20
- package/src/strategies/shrimp-memory.ts +25 -25
- package/src/strategies/social-task.test.ts +28 -28
- package/src/strategies/social-task.ts +49 -49
- package/src/strategies/spawn.ts +82 -82
- package/src/strategies/speech-module.ts +123 -123
- package/src/strategies/strategy-loop.ts +771 -771
- package/src/strategies/task-kill-report.ts +17 -17
- package/src/strategies/task-only.ts +11 -11
- package/src/strategies/task-report.ts +22 -22
- package/src/strategies/types.ts +102 -102
- package/src/strategies/warrior-memory.knowledge.md +21 -22
- package/src/strategies/warrior-memory.ts +16 -16
package/src/strategies/spawn.ts
CHANGED
|
@@ -1,82 +1,82 @@
|
|
|
1
|
-
import { spawn } from 'child_process';
|
|
2
|
-
import { existsSync, mkdirSync, openSync, writeFileSync } from 'fs';
|
|
3
|
-
import { dirname, join, resolve } from 'path';
|
|
4
|
-
import { fileURLToPath } from 'url';
|
|
5
|
-
import { getProfileLogsDir, getProfileStateDir } from '../lib/init-command.js';
|
|
6
|
-
import { AuthStore } from '../lib/auth.js';
|
|
7
|
-
|
|
8
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
9
|
-
const __dirname = dirname(__filename);
|
|
10
|
-
|
|
11
|
-
const fwd = (p: string) => p.replace(/\\/g, '/');
|
|
12
|
-
const BIN_MJS = fwd(resolve(__dirname, '..', '..', 'bin', 'clawclaw-cli.mjs'));
|
|
13
|
-
|
|
14
|
-
function isTrue(value: string | undefined): boolean {
|
|
15
|
-
return typeof value === 'string' && value.trim().toLowerCase() === 'true';
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function isDeveloperDiagnosticsEnabled(env: NodeJS.ProcessEnv = process.env): boolean {
|
|
19
|
-
return isTrue(env.CCL_DEBUG_RUNTIME) || isTrue(env.CCL_TEST) || isTrue(env['ccl-test']);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export function spawnStrategyLoop(
|
|
23
|
-
strategyId: string,
|
|
24
|
-
strategyArgs?: string[],
|
|
25
|
-
opts?: { source?: string; gameId?: string; role?: string; detached?: boolean; writeRuntimeFiles?: boolean },
|
|
26
|
-
): ReturnType<typeof spawn> {
|
|
27
|
-
if (!existsSync(BIN_MJS)) throw new Error('clawclaw-cli bin entry not found');
|
|
28
|
-
const isWin = process.platform === 'win32';
|
|
29
|
-
const writeRuntimeFiles = opts?.writeRuntimeFiles ?? false;
|
|
30
|
-
|
|
31
|
-
let logFile: string | undefined;
|
|
32
|
-
if (isWin && isDeveloperDiagnosticsEnabled()) {
|
|
33
|
-
try {
|
|
34
|
-
const store = new AuthStore();
|
|
35
|
-
const active = store.getActive();
|
|
36
|
-
if (active) {
|
|
37
|
-
const logsDir = getProfileLogsDir(active);
|
|
38
|
-
mkdirSync(logsDir, { recursive: true });
|
|
39
|
-
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
40
|
-
logFile = resolve(logsDir, `strategy-${timestamp}.log`);
|
|
41
|
-
openSync(logFile, 'a');
|
|
42
|
-
}
|
|
43
|
-
} catch {}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const args = ['_strategy', strategyId];
|
|
47
|
-
if (strategyArgs && strategyArgs.length > 0) args.push('--', ...strategyArgs);
|
|
48
|
-
|
|
49
|
-
const detached = opts?.detached ?? false;
|
|
50
|
-
const child = spawn(process.execPath, [BIN_MJS, ...args], {
|
|
51
|
-
detached,
|
|
52
|
-
stdio: ['ignore', 'ignore', 'ignore'],
|
|
53
|
-
env: {
|
|
54
|
-
...process.env,
|
|
55
|
-
...(isWin && logFile ? { CLAWCLAW_LOG_FILE: logFile } : {}),
|
|
56
|
-
...(writeRuntimeFiles ? { CLAWCLAW_STRATEGY_RUNTIME_FILES: '1' } : {}),
|
|
57
|
-
},
|
|
58
|
-
windowsHide: true,
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
if (child.pid && writeRuntimeFiles) {
|
|
62
|
-
try {
|
|
63
|
-
const store = new AuthStore();
|
|
64
|
-
const active = store.getActive();
|
|
65
|
-
if (active) {
|
|
66
|
-
const stateDir = getProfileStateDir(active);
|
|
67
|
-
mkdirSync(stateDir, { recursive: true });
|
|
68
|
-
writeFileSync(join(stateDir, 'auto.pid'), String(child.pid));
|
|
69
|
-
writeFileSync(join(stateDir, 'auto.json'), JSON.stringify({
|
|
70
|
-
strategy: strategyId,
|
|
71
|
-
pid: child.pid,
|
|
72
|
-
source: opts?.source ?? 'manual',
|
|
73
|
-
...(opts?.gameId ? { game_id: opts.gameId } : {}),
|
|
74
|
-
...(opts?.role ? { role: opts.role } : {}),
|
|
75
|
-
}));
|
|
76
|
-
}
|
|
77
|
-
} catch {}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
if (detached) child.unref();
|
|
81
|
-
return child;
|
|
82
|
-
}
|
|
1
|
+
import { spawn } from 'child_process';
|
|
2
|
+
import { existsSync, mkdirSync, openSync, writeFileSync } from 'fs';
|
|
3
|
+
import { dirname, join, resolve } from 'path';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
import { getProfileLogsDir, getProfileStateDir } from '../lib/init-command.js';
|
|
6
|
+
import { AuthStore } from '../lib/auth.js';
|
|
7
|
+
|
|
8
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
9
|
+
const __dirname = dirname(__filename);
|
|
10
|
+
|
|
11
|
+
const fwd = (p: string) => p.replace(/\\/g, '/');
|
|
12
|
+
const BIN_MJS = fwd(resolve(__dirname, '..', '..', 'bin', 'clawclaw-cli.mjs'));
|
|
13
|
+
|
|
14
|
+
function isTrue(value: string | undefined): boolean {
|
|
15
|
+
return typeof value === 'string' && value.trim().toLowerCase() === 'true';
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function isDeveloperDiagnosticsEnabled(env: NodeJS.ProcessEnv = process.env): boolean {
|
|
19
|
+
return isTrue(env.CCL_DEBUG_RUNTIME) || isTrue(env.CCL_TEST) || isTrue(env['ccl-test']);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function spawnStrategyLoop(
|
|
23
|
+
strategyId: string,
|
|
24
|
+
strategyArgs?: string[],
|
|
25
|
+
opts?: { source?: string; gameId?: string; role?: string; detached?: boolean; writeRuntimeFiles?: boolean },
|
|
26
|
+
): ReturnType<typeof spawn> {
|
|
27
|
+
if (!existsSync(BIN_MJS)) throw new Error('clawclaw-cli bin entry not found');
|
|
28
|
+
const isWin = process.platform === 'win32';
|
|
29
|
+
const writeRuntimeFiles = opts?.writeRuntimeFiles ?? false;
|
|
30
|
+
|
|
31
|
+
let logFile: string | undefined;
|
|
32
|
+
if (isWin && isDeveloperDiagnosticsEnabled()) {
|
|
33
|
+
try {
|
|
34
|
+
const store = new AuthStore();
|
|
35
|
+
const active = store.getActive();
|
|
36
|
+
if (active) {
|
|
37
|
+
const logsDir = getProfileLogsDir(active);
|
|
38
|
+
mkdirSync(logsDir, { recursive: true });
|
|
39
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
40
|
+
logFile = resolve(logsDir, `strategy-${timestamp}.log`);
|
|
41
|
+
openSync(logFile, 'a');
|
|
42
|
+
}
|
|
43
|
+
} catch {}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const args = ['_strategy', strategyId];
|
|
47
|
+
if (strategyArgs && strategyArgs.length > 0) args.push('--', ...strategyArgs);
|
|
48
|
+
|
|
49
|
+
const detached = opts?.detached ?? false;
|
|
50
|
+
const child = spawn(process.execPath, [BIN_MJS, ...args], {
|
|
51
|
+
detached,
|
|
52
|
+
stdio: ['ignore', 'ignore', 'ignore'],
|
|
53
|
+
env: {
|
|
54
|
+
...process.env,
|
|
55
|
+
...(isWin && logFile ? { CLAWCLAW_LOG_FILE: logFile } : {}),
|
|
56
|
+
...(writeRuntimeFiles ? { CLAWCLAW_STRATEGY_RUNTIME_FILES: '1' } : {}),
|
|
57
|
+
},
|
|
58
|
+
windowsHide: true,
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
if (child.pid && writeRuntimeFiles) {
|
|
62
|
+
try {
|
|
63
|
+
const store = new AuthStore();
|
|
64
|
+
const active = store.getActive();
|
|
65
|
+
if (active) {
|
|
66
|
+
const stateDir = getProfileStateDir(active);
|
|
67
|
+
mkdirSync(stateDir, { recursive: true });
|
|
68
|
+
writeFileSync(join(stateDir, 'auto.pid'), String(child.pid));
|
|
69
|
+
writeFileSync(join(stateDir, 'auto.json'), JSON.stringify({
|
|
70
|
+
strategy: strategyId,
|
|
71
|
+
pid: child.pid,
|
|
72
|
+
source: opts?.source ?? 'manual',
|
|
73
|
+
...(opts?.gameId ? { game_id: opts.gameId } : {}),
|
|
74
|
+
...(opts?.role ? { role: opts.role } : {}),
|
|
75
|
+
}));
|
|
76
|
+
}
|
|
77
|
+
} catch {}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (detached) child.unref();
|
|
81
|
+
return child;
|
|
82
|
+
}
|
|
@@ -1,123 +1,123 @@
|
|
|
1
|
-
import type { GameState, PlayerInfo } from '../sdk/types.js';
|
|
2
|
-
import type { StrategyContext } from './types.js';
|
|
3
|
-
|
|
4
|
-
export interface SpeechModuleConfig {
|
|
5
|
-
globalCooldownMs: number;
|
|
6
|
-
perPersonCooldownMs: number;
|
|
7
|
-
autoGreetEnabled: boolean;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export interface SpeechEvalResult {
|
|
11
|
-
notifications: string[];
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export class SpeechModule {
|
|
15
|
-
private config: SpeechModuleConfig;
|
|
16
|
-
private lastInitiateTime = 0;
|
|
17
|
-
private perPersonCooldowns = new Map<string, number>();
|
|
18
|
-
|
|
19
|
-
constructor(config: SpeechModuleConfig) {
|
|
20
|
-
this.config = config;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
evaluate(
|
|
24
|
-
state: GameState,
|
|
25
|
-
ctx: StrategyContext,
|
|
26
|
-
signals: { allowAutoGreet: boolean },
|
|
27
|
-
): SpeechEvalResult {
|
|
28
|
-
const result: SpeechEvalResult = { notifications: [] };
|
|
29
|
-
const now = Date.now();
|
|
30
|
-
|
|
31
|
-
const speechEvents = (state.new_events ?? []).filter(
|
|
32
|
-
evt => evt.type === 'wandering_speech' && evt.actor_name && evt.actor_name !== state.you.name,
|
|
33
|
-
);
|
|
34
|
-
|
|
35
|
-
for (const evt of speechEvents) {
|
|
36
|
-
const speaker = evt.actor_name as string;
|
|
37
|
-
const text = (evt.text ?? evt.content ?? '') as string;
|
|
38
|
-
const seatInfo = this.findSeatForPlayer(speaker, state, ctx);
|
|
39
|
-
result.notifications.push(`[被搭话] ${speaker}${seatInfo}对你说:「${text}」`);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
if (signals.allowAutoGreet && this.canInitiate(state, ctx, now)) {
|
|
43
|
-
const candidate = this.pickCandidate(state, ctx, now);
|
|
44
|
-
if (candidate) {
|
|
45
|
-
const seatInfo = this.findSeatForPlayer(candidate.name, state, ctx);
|
|
46
|
-
this.lastInitiateTime = now;
|
|
47
|
-
this.perPersonCooldowns.set(candidate.name, now + this.config.perPersonCooldownMs);
|
|
48
|
-
result.notifications.push(`[发言] 视野内出现${candidate.name}${seatInfo},可以主动打招呼。`);
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
return result;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
private canInitiate(state: GameState, ctx: StrategyContext, now: number): boolean {
|
|
56
|
-
if (!this.config.autoGreetEnabled) return false;
|
|
57
|
-
if (now - this.lastInitiateTime < this.config.globalCooldownMs) return false;
|
|
58
|
-
if (state.you.doing_task) return false;
|
|
59
|
-
if (ctx.reportCorpseTarget) return false;
|
|
60
|
-
if (ctx.emergency && ctx.emergency.status !== 'completed') return false;
|
|
61
|
-
|
|
62
|
-
return true;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
private pickCandidate(state: GameState, ctx: StrategyContext, now: number): PlayerInfo | null {
|
|
66
|
-
const mySeat = ctx.mySeat ?? 0;
|
|
67
|
-
const speechActors = new Set(
|
|
68
|
-
(state.new_events ?? [])
|
|
69
|
-
.filter(evt => evt.type === 'wandering_speech' && evt.actor_name)
|
|
70
|
-
.map(evt => evt.actor_name as string),
|
|
71
|
-
);
|
|
72
|
-
|
|
73
|
-
for (const player of state.players) {
|
|
74
|
-
const cooldownUntil = this.perPersonCooldowns.get(player.name) ?? 0;
|
|
75
|
-
if (now < cooldownUntil) continue;
|
|
76
|
-
|
|
77
|
-
const theirSeat = player.seat ?? Infinity;
|
|
78
|
-
if (mySeat >= theirSeat) continue;
|
|
79
|
-
|
|
80
|
-
if (speechActors.has(player.name)) continue;
|
|
81
|
-
|
|
82
|
-
return player;
|
|
83
|
-
}
|
|
84
|
-
return null;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/** 会议边界不再暂停移动,也无需清理等待对话状态。 */
|
|
88
|
-
resetMeetingState(): void {
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/** 角色变化时刷新发言配置,保留 ConversationGoal 持有的模块实例。 */
|
|
92
|
-
updateConfig(role: string): void {
|
|
93
|
-
this.config = getSpeechConfigForRole(role);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
private findSeatForPlayer(name: string, state: GameState, ctx: StrategyContext): string {
|
|
97
|
-
const player = state.players.find(p => p.name === name);
|
|
98
|
-
if (player?.seat != null) return `(${player.seat}号)`;
|
|
99
|
-
for (const [seat, pName] of Object.entries(ctx.playerNamesBySeat)) {
|
|
100
|
-
if (pName === name) return `(${seat}号)`;
|
|
101
|
-
}
|
|
102
|
-
return '';
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
const ROLE_CONFIGS: Record<string, SpeechModuleConfig> = {
|
|
107
|
-
shrimp_generic: { globalCooldownMs: 30_000, perPersonCooldownMs: 90_000, autoGreetEnabled: true },
|
|
108
|
-
shrimp_warrior: { globalCooldownMs: 30_000, perPersonCooldownMs: 90_000, autoGreetEnabled: true },
|
|
109
|
-
shrimp_pistol: { globalCooldownMs: 30_000, perPersonCooldownMs: 90_000, autoGreetEnabled: true },
|
|
110
|
-
crab_generic: { globalCooldownMs: 30_000, perPersonCooldownMs: 90_000, autoGreetEnabled: false },
|
|
111
|
-
neutral_paradise_fish: { globalCooldownMs: 20_000, perPersonCooldownMs: 90_000, autoGreetEnabled: true },
|
|
112
|
-
neutral_octopus: { globalCooldownMs: 30_000, perPersonCooldownMs: 90_000, autoGreetEnabled: false },
|
|
113
|
-
};
|
|
114
|
-
|
|
115
|
-
const DEFAULT_CONFIG: SpeechModuleConfig = {
|
|
116
|
-
globalCooldownMs: 30_000,
|
|
117
|
-
perPersonCooldownMs: 90_000,
|
|
118
|
-
autoGreetEnabled: true,
|
|
119
|
-
};
|
|
120
|
-
|
|
121
|
-
export function getSpeechConfigForRole(role: string): SpeechModuleConfig {
|
|
122
|
-
return ROLE_CONFIGS[role] ?? DEFAULT_CONFIG;
|
|
123
|
-
}
|
|
1
|
+
import type { GameState, PlayerInfo } from '../sdk/types.js';
|
|
2
|
+
import type { StrategyContext } from './types.js';
|
|
3
|
+
|
|
4
|
+
export interface SpeechModuleConfig {
|
|
5
|
+
globalCooldownMs: number;
|
|
6
|
+
perPersonCooldownMs: number;
|
|
7
|
+
autoGreetEnabled: boolean;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface SpeechEvalResult {
|
|
11
|
+
notifications: string[];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export class SpeechModule {
|
|
15
|
+
private config: SpeechModuleConfig;
|
|
16
|
+
private lastInitiateTime = 0;
|
|
17
|
+
private perPersonCooldowns = new Map<string, number>();
|
|
18
|
+
|
|
19
|
+
constructor(config: SpeechModuleConfig) {
|
|
20
|
+
this.config = config;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
evaluate(
|
|
24
|
+
state: GameState,
|
|
25
|
+
ctx: StrategyContext,
|
|
26
|
+
signals: { allowAutoGreet: boolean },
|
|
27
|
+
): SpeechEvalResult {
|
|
28
|
+
const result: SpeechEvalResult = { notifications: [] };
|
|
29
|
+
const now = Date.now();
|
|
30
|
+
|
|
31
|
+
const speechEvents = (state.new_events ?? []).filter(
|
|
32
|
+
evt => evt.type === 'wandering_speech' && evt.actor_name && evt.actor_name !== state.you.name,
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
for (const evt of speechEvents) {
|
|
36
|
+
const speaker = evt.actor_name as string;
|
|
37
|
+
const text = (evt.text ?? evt.content ?? '') as string;
|
|
38
|
+
const seatInfo = this.findSeatForPlayer(speaker, state, ctx);
|
|
39
|
+
result.notifications.push(`[被搭话] ${speaker}${seatInfo}对你说:「${text}」`);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (signals.allowAutoGreet && this.canInitiate(state, ctx, now)) {
|
|
43
|
+
const candidate = this.pickCandidate(state, ctx, now);
|
|
44
|
+
if (candidate) {
|
|
45
|
+
const seatInfo = this.findSeatForPlayer(candidate.name, state, ctx);
|
|
46
|
+
this.lastInitiateTime = now;
|
|
47
|
+
this.perPersonCooldowns.set(candidate.name, now + this.config.perPersonCooldownMs);
|
|
48
|
+
result.notifications.push(`[发言] 视野内出现${candidate.name}${seatInfo},可以主动打招呼。`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return result;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
private canInitiate(state: GameState, ctx: StrategyContext, now: number): boolean {
|
|
56
|
+
if (!this.config.autoGreetEnabled) return false;
|
|
57
|
+
if (now - this.lastInitiateTime < this.config.globalCooldownMs) return false;
|
|
58
|
+
if (state.you.doing_task) return false;
|
|
59
|
+
if (ctx.reportCorpseTarget) return false;
|
|
60
|
+
if (ctx.emergency && ctx.emergency.status !== 'completed') return false;
|
|
61
|
+
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
private pickCandidate(state: GameState, ctx: StrategyContext, now: number): PlayerInfo | null {
|
|
66
|
+
const mySeat = ctx.mySeat ?? 0;
|
|
67
|
+
const speechActors = new Set(
|
|
68
|
+
(state.new_events ?? [])
|
|
69
|
+
.filter(evt => evt.type === 'wandering_speech' && evt.actor_name)
|
|
70
|
+
.map(evt => evt.actor_name as string),
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
for (const player of state.players) {
|
|
74
|
+
const cooldownUntil = this.perPersonCooldowns.get(player.name) ?? 0;
|
|
75
|
+
if (now < cooldownUntil) continue;
|
|
76
|
+
|
|
77
|
+
const theirSeat = player.seat ?? Infinity;
|
|
78
|
+
if (mySeat >= theirSeat) continue;
|
|
79
|
+
|
|
80
|
+
if (speechActors.has(player.name)) continue;
|
|
81
|
+
|
|
82
|
+
return player;
|
|
83
|
+
}
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** 会议边界不再暂停移动,也无需清理等待对话状态。 */
|
|
88
|
+
resetMeetingState(): void {
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** 角色变化时刷新发言配置,保留 ConversationGoal 持有的模块实例。 */
|
|
92
|
+
updateConfig(role: string): void {
|
|
93
|
+
this.config = getSpeechConfigForRole(role);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
private findSeatForPlayer(name: string, state: GameState, ctx: StrategyContext): string {
|
|
97
|
+
const player = state.players.find(p => p.name === name);
|
|
98
|
+
if (player?.seat != null) return `(${player.seat}号)`;
|
|
99
|
+
for (const [seat, pName] of Object.entries(ctx.playerNamesBySeat)) {
|
|
100
|
+
if (pName === name) return `(${seat}号)`;
|
|
101
|
+
}
|
|
102
|
+
return '';
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const ROLE_CONFIGS: Record<string, SpeechModuleConfig> = {
|
|
107
|
+
shrimp_generic: { globalCooldownMs: 30_000, perPersonCooldownMs: 90_000, autoGreetEnabled: true },
|
|
108
|
+
shrimp_warrior: { globalCooldownMs: 30_000, perPersonCooldownMs: 90_000, autoGreetEnabled: true },
|
|
109
|
+
shrimp_pistol: { globalCooldownMs: 30_000, perPersonCooldownMs: 90_000, autoGreetEnabled: true },
|
|
110
|
+
crab_generic: { globalCooldownMs: 30_000, perPersonCooldownMs: 90_000, autoGreetEnabled: false },
|
|
111
|
+
neutral_paradise_fish: { globalCooldownMs: 20_000, perPersonCooldownMs: 90_000, autoGreetEnabled: true },
|
|
112
|
+
neutral_octopus: { globalCooldownMs: 30_000, perPersonCooldownMs: 90_000, autoGreetEnabled: false },
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const DEFAULT_CONFIG: SpeechModuleConfig = {
|
|
116
|
+
globalCooldownMs: 30_000,
|
|
117
|
+
perPersonCooldownMs: 90_000,
|
|
118
|
+
autoGreetEnabled: true,
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
export function getSpeechConfigForRole(role: string): SpeechModuleConfig {
|
|
122
|
+
return ROLE_CONFIGS[role] ?? DEFAULT_CONFIG;
|
|
123
|
+
}
|