Repository navigation
Expand file tree
/
Copy pathwerhd-user-script.mjs
More file actions
90 lines (79 loc) · 2.87 KB
/
Copy pathwerhd-user-script.mjs
File metadata and controls
90 lines (79 loc) · 2.87 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
/**
* 用户侧示例:在游戏页里直接调 window.werhd。
* 这不是内置 AI,只是一份可粘贴 / 可 import 的参考脚本。
*
* 进局后控制台:
* const { attachWerhdPlayer } = await import('/docs/examples/werhd-user-script.mjs')
* attachWerhdPlayer(werhd)
*
* 默认用写死规则(展开基地车 → 造 E1 → 打看得见的敌人)。
* 若要问 Jev,自己传入 askJev(snapshot),不要在 onTick 里 await。
*/
export const MCV_NAMES = ['AMCV', 'SMCV', 'CMCV'];
export function snapshotFrom(werhd) {
return {
tick: werhd.tick?.() ?? 0,
me: werhd.me?.(),
units: werhd.units?.('self') ?? [],
enemies: werhd.units?.('enemy') ?? [],
available: werhd.production?.available?.() ?? [],
queues: werhd.production?.queues?.() ?? [],
};
}
export function decide(snapshot) {
const units = snapshot.units ?? [];
const enemies = snapshot.enemies ?? [];
const mcv = units.find((unit) => MCV_NAMES.includes(unit.name));
if (mcv) return { deployIds: [mcv.id] };
const infantryQueue = (snapshot.queues ?? []).find((queue) => queue.type === 2);
const queuedE1 = infantryQueue?.items?.some((item) => item.name === 'E1');
const e1s = units.filter((unit) => unit.name === 'E1');
const canMakeE1 = (snapshot.available ?? []).some((rule) => rule.name === 'E1');
if (canMakeE1 && e1s.length === 0 && !queuedE1) return { produce: 'E1' };
const idle = e1s.filter((unit) => unit.isIdle);
const enemy = enemies[0];
if (idle.length && enemy) return { attackIds: idle.map((unit) => unit.id), targetId: enemy.id };
return {};
}
export function applyDecision(werhd, decision) {
if (!decision) return;
if (decision.deployIds?.length) werhd.deploy(decision.deployIds);
if (decision.produce) werhd.produce(decision.produce);
if (decision.attackIds?.length && decision.targetId != null) {
werhd.attack(decision.attackIds, decision.targetId);
}
}
export function attachWerhdPlayer(werhd, options = {}) {
if (!werhd) throw new Error('werhd is not available');
const askJev = options.askJev;
const everyTicks = options.everyTicks ?? 10;
let pending;
let asking = false;
werhd.onTick(({ tick }) => {
if (pending) {
applyDecision(werhd, pending);
pending = undefined;
}
if (tick % everyTicks !== 0 || asking) return;
const snapshot = snapshotFrom(werhd);
if (!askJev) {
applyDecision(werhd, decide(snapshot));
return;
}
asking = true;
Promise.resolve(askJev(snapshot))
.then((answer) => {
pending = answer;
})
.catch((error) => {
console.error('werhd player askJev failed', error);
})
.finally(() => {
asking = false;
});
});
return { decide, applyDecision, snapshotFrom };
}
if (typeof globalThis !== 'undefined' && globalThis.werhd && globalThis.werhdAutoAttach !== false) {
attachWerhdPlayer(globalThis.werhd);
}