@vibemancer/core 0.1.3 → 0.1.5
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 +2 -2
- package/dist/chunk-W7HWJFQ4.js +10599 -0
- package/dist/chunk-W7HWJFQ4.js.map +1 -0
- package/dist/index-browser.d.ts +1300 -1051
- package/dist/index-browser.js +55 -17
- package/dist/index.d.ts +53 -3
- package/dist/index.js +200 -54
- package/dist/index.js.map +1 -1
- package/package.json +21 -15
- package/src/bots/Hero.ts +867 -0
- package/src/bots/berserker/01_Stormchaser.ts +162 -120
- package/src/bots/berserker/02_Stormcaller.ts +160 -116
- package/src/bots/berserker/03_Stormforger.ts +162 -129
- package/src/bots/caster/01_Flamecaller.ts +126 -84
- package/src/bots/caster/02_Pyromancer.ts +148 -101
- package/src/bots/caster/03_Infernalist.ts +180 -160
- package/src/bots/defensive/01_Turtle.ts +48 -44
- package/src/bots/defensive/02_Sentinel.ts +57 -53
- package/src/bots/defensive/03_Golem.ts +85 -97
- package/src/bots/duelist/01_Battlemage.ts +157 -122
- package/src/bots/duelist/02_Warmage.ts +166 -121
- package/src/bots/duelist/03_Archmage.ts +198 -178
- package/src/bots/homing/01_Bonemancer.ts +20 -32
- package/src/bots/homing/02_Lich.ts +145 -93
- package/src/bots/homing/03_Archlich.ts +129 -60
- package/src/bots/kiter/01_Spellspinner.ts +145 -107
- package/src/bots/kiter/02_Spellweaver.ts +153 -105
- package/src/bots/kiter/03_Spellbinder.ts +168 -123
- package/src/bots/melee/01_Shadowblade.ts +131 -75
- package/src/bots/melee/02_Nightblade.ts +174 -130
- package/src/bots/melee/03_Voidblade.ts +266 -168
- package/src/bots/registry.ts +44 -37
- package/src/bots/shared.ts +185 -25
- package/src/bots/sniper/01_Spellshot.ts +151 -98
- package/src/bots/sniper/02_Spelltracer.ts +173 -128
- package/src/bots/sniper/03_Spellseeker.ts +177 -152
- package/src/bots/standalone/Critter.ts +46 -52
- package/src/bots/standalone/Doombringer.ts +36 -40
- package/src/bots/standalone/Hogger.ts +120 -89
- package/src/bots/standalone/Rookie.ts +21 -23
- package/src/bots/standalone/TargetDummy.ts +5 -6
- package/src/bots/test/cheater.ts +91 -85
- package/src/bots/test/crasher.ts +26 -28
- package/src/engine/bundle-fight.ts +242 -0
- package/src/engine/hooks-runtime.ts +58 -18
- package/src/engine/manual-match.ts +33 -25
- package/src/engine/missile-templates.ts +25 -43
- package/src/engine/optimizer.ts +7 -7
- package/src/engine/params-runtime.ts +3 -3
- package/src/engine/physics.ts +33 -23
- package/src/engine/sandbox-browser.ts +8 -7
- package/src/engine/sandbox-compile.ts +1 -1
- package/src/engine/sandbox-harness.ts +299 -367
- package/src/engine/sandbox.ts +3 -3
- package/src/engine/simulation.ts +291 -76
- package/src/engine/spells.ts +9 -12
- package/src/engine-version.ts +1 -1
- package/src/hooks/action-builders.ts +76 -21
- package/src/hooks/index.ts +17 -9
- package/src/hooks/state-hooks.ts +61 -25
- package/src/hooks/threat-analysis.ts +44 -20
- package/src/hooks/types.ts +62 -5
- package/src/index.ts +2 -0
- package/src/rules.ts +209 -63
- package/src/stats.ts +2 -2
- package/src/testing.ts +6 -30
- package/src/trace.ts +63 -3
- package/src/types.ts +127 -14
- package/src/utils/angles.ts +1 -0
- package/src/utils/combat.ts +98 -6
- package/src/utils/spatial.ts +3 -3
- package/dist/chunk-B7YYYBEC.js +0 -9140
- package/dist/chunk-B7YYYBEC.js.map +0 -1
- package/src/hooks/bot-wrapper.ts +0 -84
|
@@ -1,18 +1,21 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type {FinalAction} from '../../hooks/index.js';
|
|
2
|
+
import {usePosition, useEnemy, useStatus, useBlinkCooldown, useTick, useThreats, useProjectiles, idle, move as moveAction, missile, shield, blink, cancel, flyStraight} from '../../hooks/index.js';
|
|
2
3
|
import {distanceTo} from '../../utils/distance.js';
|
|
3
|
-
import {
|
|
4
|
+
import {RULES, ARENA_MIN, ARENA_MAX} from '../../rules.js';
|
|
4
5
|
import {
|
|
5
|
-
getThreats,
|
|
6
6
|
getMostUrgentThreat,
|
|
7
7
|
getDodgeDirectionForMovement,
|
|
8
8
|
getBlinkToDecreaseDistance,
|
|
9
9
|
shouldForceShield,
|
|
10
|
+
keepOffWalls,
|
|
11
|
+
getPreCastReposition,
|
|
10
12
|
} from '../shared.js';
|
|
11
13
|
import {useParam} from '../../engine/params-runtime.js';
|
|
12
14
|
const WALL_BUFFER = 50;
|
|
13
15
|
|
|
14
16
|
// Melee stab: 20 damage, speed 8, 3-hit kill.
|
|
15
|
-
const
|
|
17
|
+
const STAB_SPEED = 8;
|
|
18
|
+
const STAB_DURATION = 25;
|
|
16
19
|
|
|
17
20
|
/**
|
|
18
21
|
* Aggressive movement: dodge missiles using threat analysis, close distance, light strafe.
|
|
@@ -26,12 +29,22 @@ function aggressiveMove(
|
|
|
26
29
|
strafeCyclePeriod: number,
|
|
27
30
|
strafeIntensity: number,
|
|
28
31
|
approachSpeed: number,
|
|
29
|
-
|
|
32
|
+
centerBias: number,
|
|
33
|
+
): {x: number; y: number}
|
|
30
34
|
{
|
|
31
35
|
const deltaX = enemy.position.x - position.x;
|
|
32
36
|
const deltaY = enemy.position.y - position.y;
|
|
33
37
|
const normalizedDistance = currentDistance > 0 ? currentDistance : 1;
|
|
34
38
|
|
|
39
|
+
// Approach the center-side of the enemy so knockback pushes toward center, not lava.
|
|
40
|
+
const center = (ARENA_MIN + ARENA_MAX) / 2;
|
|
41
|
+
const toCenterX = center - enemy.position.x;
|
|
42
|
+
const toCenterY = center - enemy.position.y;
|
|
43
|
+
const toCenterDist = Math.sqrt(toCenterX * toCenterX + toCenterY * toCenterY) || 1;
|
|
44
|
+
const approachDirX = deltaX / normalizedDistance + (toCenterX / toCenterDist) * centerBias;
|
|
45
|
+
const approachDirY = deltaY / normalizedDistance + (toCenterY / toCenterDist) * centerBias;
|
|
46
|
+
const approachMag = Math.sqrt(approachDirX * approachDirX + approachDirY * approachDirY) || 1;
|
|
47
|
+
|
|
35
48
|
const strafeClockwise = {
|
|
36
49
|
x: -deltaY / normalizedDistance,
|
|
37
50
|
y: deltaX / normalizedDistance,
|
|
@@ -48,10 +61,10 @@ function aggressiveMove(
|
|
|
48
61
|
strafeDirection = dodgeDirection;
|
|
49
62
|
}
|
|
50
63
|
else if (
|
|
51
|
-
position.x < WALL_BUFFER ||
|
|
52
|
-
position.x >
|
|
53
|
-
position.y < WALL_BUFFER ||
|
|
54
|
-
position.y >
|
|
64
|
+
position.x < ARENA_MIN + WALL_BUFFER ||
|
|
65
|
+
position.x > ARENA_MAX - WALL_BUFFER ||
|
|
66
|
+
position.y < ARENA_MIN + WALL_BUFFER ||
|
|
67
|
+
position.y > ARENA_MAX - WALL_BUFFER
|
|
55
68
|
)
|
|
56
69
|
{
|
|
57
70
|
const futureClockwise = {
|
|
@@ -63,16 +76,16 @@ function aggressiveMove(
|
|
|
63
76
|
y: position.y + strafeCounterClockwise.y * 100,
|
|
64
77
|
};
|
|
65
78
|
const scoreClockwise = Math.min(
|
|
66
|
-
futureClockwise.x,
|
|
67
|
-
|
|
68
|
-
futureClockwise.y,
|
|
69
|
-
|
|
79
|
+
futureClockwise.x - ARENA_MIN,
|
|
80
|
+
ARENA_MAX - futureClockwise.x,
|
|
81
|
+
futureClockwise.y - ARENA_MIN,
|
|
82
|
+
ARENA_MAX - futureClockwise.y,
|
|
70
83
|
);
|
|
71
84
|
const scoreCounterClockwise = Math.min(
|
|
72
|
-
futureCounterClockwise.x,
|
|
73
|
-
|
|
74
|
-
futureCounterClockwise.y,
|
|
75
|
-
|
|
85
|
+
futureCounterClockwise.x - ARENA_MIN,
|
|
86
|
+
ARENA_MAX - futureCounterClockwise.x,
|
|
87
|
+
futureCounterClockwise.y - ARENA_MIN,
|
|
88
|
+
ARENA_MAX - futureCounterClockwise.y,
|
|
76
89
|
);
|
|
77
90
|
strafeDirection =
|
|
78
91
|
scoreClockwise > scoreCounterClockwise
|
|
@@ -88,7 +101,7 @@ function aggressiveMove(
|
|
|
88
101
|
// When actively dodging, commit fully
|
|
89
102
|
if (dodgeDirection)
|
|
90
103
|
{
|
|
91
|
-
return {x: strafeDirection.x
|
|
104
|
+
return {x: strafeDirection.x, y: strafeDirection.y};
|
|
92
105
|
}
|
|
93
106
|
|
|
94
107
|
// Light strafe + aggressive closing
|
|
@@ -96,14 +109,14 @@ function aggressiveMove(
|
|
|
96
109
|
let moveY = strafeDirection.y * strafeIntensity;
|
|
97
110
|
|
|
98
111
|
// Always close distance aggressively (melee wants to be in the enemy's face)
|
|
99
|
-
moveX += (
|
|
100
|
-
moveY += (
|
|
112
|
+
moveX += (approachDirX / approachMag) * approachSpeed;
|
|
113
|
+
moveY += (approachDirY / approachMag) * approachSpeed;
|
|
101
114
|
|
|
102
115
|
const magnitude = Math.sqrt(moveX * moveX + moveY * moveY);
|
|
103
|
-
if (magnitude >
|
|
116
|
+
if (magnitude > 1)
|
|
104
117
|
{
|
|
105
|
-
moveX =
|
|
106
|
-
moveY =
|
|
118
|
+
moveX = moveX / magnitude;
|
|
119
|
+
moveY = moveY / magnitude;
|
|
107
120
|
}
|
|
108
121
|
|
|
109
122
|
return {x: moveX, y: moveY};
|
|
@@ -124,29 +137,38 @@ function aggressiveMove(
|
|
|
124
137
|
*
|
|
125
138
|
* TIER: 1 (base)
|
|
126
139
|
*/
|
|
127
|
-
export
|
|
140
|
+
export function Shadowblade(): FinalAction
|
|
128
141
|
{
|
|
129
|
-
const
|
|
142
|
+
const position = usePosition();
|
|
143
|
+
const enemy = useEnemy();
|
|
144
|
+
const status = useStatus();
|
|
145
|
+
const blinkCooldown = useBlinkCooldown();
|
|
146
|
+
const tick = useTick();
|
|
147
|
+
const threats = useThreats();
|
|
148
|
+
const projectiles = useProjectiles();
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
const stabRange = useParam('stabRange', 80, {
|
|
130
152
|
range: 30,
|
|
131
153
|
min: 20,
|
|
132
154
|
max: 80,
|
|
133
155
|
steps: 5,
|
|
134
156
|
});
|
|
135
|
-
const strafeCyclePeriod = useParam('strafeCyclePeriod',
|
|
157
|
+
const strafeCyclePeriod = useParam('strafeCyclePeriod', 282, {
|
|
136
158
|
range: 75,
|
|
137
159
|
min: 80,
|
|
138
160
|
max: 300,
|
|
139
161
|
});
|
|
140
|
-
const strafeIntensity = useParam('strafeIntensity',
|
|
141
|
-
range:
|
|
142
|
-
min:
|
|
143
|
-
max:
|
|
162
|
+
const strafeIntensity = useParam('strafeIntensity', 0.15, {
|
|
163
|
+
range: 0.3,
|
|
164
|
+
min: 0.1,
|
|
165
|
+
max: 0.8,
|
|
144
166
|
steps: 7,
|
|
145
167
|
});
|
|
146
|
-
const approachSpeed = useParam('approachSpeed',
|
|
147
|
-
range:
|
|
148
|
-
min:
|
|
149
|
-
max:
|
|
168
|
+
const approachSpeed = useParam('approachSpeed', 0.87, {
|
|
169
|
+
range: 0.2,
|
|
170
|
+
min: 0.5,
|
|
171
|
+
max: 1,
|
|
150
172
|
steps: 7,
|
|
151
173
|
});
|
|
152
174
|
const shieldCancelThreshold = useParam('shieldCancelThreshold', 150, {
|
|
@@ -154,35 +176,56 @@ export const Shadowblade: WizardFunction = ({state}) =>
|
|
|
154
176
|
min: 50,
|
|
155
177
|
max: 300,
|
|
156
178
|
});
|
|
157
|
-
const blinkWindowMax = useParam('blinkWindowMax',
|
|
179
|
+
const blinkWindowMax = useParam('blinkWindowMax', 51, {
|
|
158
180
|
range: 30,
|
|
159
181
|
min: 15,
|
|
160
182
|
max: 80,
|
|
161
183
|
steps: 7,
|
|
162
184
|
});
|
|
185
|
+
const stabDamage = Math.round(useParam('stabDamage', 25, {
|
|
186
|
+
range: 10,
|
|
187
|
+
min: 5,
|
|
188
|
+
max: 25,
|
|
189
|
+
steps: 7,
|
|
190
|
+
}));
|
|
191
|
+
const centerBias = useParam('centerBias', 0.8, {
|
|
192
|
+
range: 0.4,
|
|
193
|
+
min: 0,
|
|
194
|
+
max: 0.8,
|
|
195
|
+
steps: 5,
|
|
196
|
+
});
|
|
197
|
+
const maxBlinkDistance = useParam('maxBlinkDistance', 269, {
|
|
198
|
+
range: 100,
|
|
199
|
+
min: 50,
|
|
200
|
+
max: 300,
|
|
201
|
+
steps: 7,
|
|
202
|
+
});
|
|
163
203
|
|
|
164
|
-
const
|
|
165
|
-
|
|
204
|
+
const distance = distanceTo(position, enemy.position);
|
|
205
|
+
|
|
206
|
+
function capBlinkDistance(target: {x: number; y: number}): {x: number; y: number}
|
|
166
207
|
{
|
|
167
|
-
|
|
208
|
+
const dx = target.x - position.x;
|
|
209
|
+
const dy = target.y - position.y;
|
|
210
|
+
const dist = Math.sqrt(dx * dx + dy * dy);
|
|
211
|
+
if (dist <= maxBlinkDistance) return target;
|
|
212
|
+
const scale = maxBlinkDistance / dist;
|
|
213
|
+
return {x: position.x + dx * scale, y: position.y + dy * scale};
|
|
168
214
|
}
|
|
169
215
|
|
|
170
|
-
const distance = distanceTo(state.position, enemy.position);
|
|
171
|
-
|
|
172
|
-
const threats = getThreats(state);
|
|
173
216
|
const urgentThreat = getMostUrgentThreat(threats);
|
|
174
217
|
|
|
175
218
|
// Cancel shield if no longer needed
|
|
176
|
-
if (
|
|
219
|
+
if (status === 'channeling')
|
|
177
220
|
{
|
|
178
221
|
if (!urgentThreat || urgentThreat.ticksToImpact > shieldCancelThreshold)
|
|
179
222
|
{
|
|
180
|
-
return
|
|
223
|
+
return cancel();
|
|
181
224
|
}
|
|
182
|
-
return
|
|
225
|
+
return idle();
|
|
183
226
|
}
|
|
184
227
|
|
|
185
|
-
if (
|
|
228
|
+
if (status === 'idle')
|
|
186
229
|
{
|
|
187
230
|
// === BLINK: Dodge missile + close gap simultaneously (first priority) ===
|
|
188
231
|
// Wait until the missile is close (<=40 ticks) so the blink actually dodges it.
|
|
@@ -190,22 +233,20 @@ export const Shadowblade: WizardFunction = ({state}) =>
|
|
|
190
233
|
// blink cast won't complete in time.
|
|
191
234
|
if (
|
|
192
235
|
urgentThreat &&
|
|
193
|
-
|
|
236
|
+
blinkCooldown === 0 &&
|
|
194
237
|
distance > stabRange &&
|
|
195
238
|
urgentThreat.ticksToImpact >= 10 &&
|
|
196
239
|
urgentThreat.ticksToImpact <= blinkWindowMax
|
|
197
240
|
)
|
|
198
241
|
{
|
|
199
|
-
const
|
|
242
|
+
const rawBlinkTarget =
|
|
200
243
|
getBlinkToDecreaseDistance(
|
|
201
|
-
|
|
244
|
+
position,
|
|
202
245
|
enemy.position,
|
|
203
|
-
|
|
246
|
+
projectiles,
|
|
204
247
|
) ?? enemy.position;
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
startCast: {spell: 'blink', target: blinkTarget},
|
|
208
|
-
};
|
|
248
|
+
const blinkTarget = capBlinkDistance(rawBlinkTarget);
|
|
249
|
+
return blink(blinkTarget.x, blinkTarget.y);
|
|
209
250
|
}
|
|
210
251
|
|
|
211
252
|
// Pre-stab shield: already at melee range, survival beats landing another stab.
|
|
@@ -218,23 +259,18 @@ export const Shadowblade: WizardFunction = ({state}) =>
|
|
|
218
259
|
urgentThreat.ticksToStartShield <= 3
|
|
219
260
|
)
|
|
220
261
|
{
|
|
221
|
-
return
|
|
222
|
-
move: {x: 0, y: 0},
|
|
223
|
-
startCast: {spell: 'shield'},
|
|
224
|
-
};
|
|
262
|
+
return shield();
|
|
225
263
|
}
|
|
226
264
|
|
|
265
|
+
// Pre-cast wall discipline: don't commit to a stab we can't react out of while
|
|
266
|
+
// pinned near a wall — step toward center first so knockback can't reach lava.
|
|
267
|
+
const reposition = getPreCastReposition(position, enemy.position);
|
|
268
|
+
if (reposition) return moveAction(reposition.x, reposition.y);
|
|
269
|
+
|
|
227
270
|
// Melee stab when close enough (offense over defense at melee range)
|
|
228
271
|
if (distance < stabRange)
|
|
229
272
|
{
|
|
230
|
-
return {
|
|
231
|
-
move: {x: 0, y: 0},
|
|
232
|
-
startCast: {
|
|
233
|
-
spell: 'missile',
|
|
234
|
-
config: STAB_CONFIG,
|
|
235
|
-
missileAI: () => ({}), // Non-homing, point blank
|
|
236
|
-
},
|
|
237
|
-
};
|
|
273
|
+
return missile({damage: stabDamage, speed: STAB_SPEED, turnRate: 0, duration: STAB_DURATION}, () => flyStraight(), 0).move(0, 0);
|
|
238
274
|
}
|
|
239
275
|
|
|
240
276
|
// === DEFENSE: Shield undodgeable threats (only when blink can't handle it) ===
|
|
@@ -244,27 +280,47 @@ export const Shadowblade: WizardFunction = ({state}) =>
|
|
|
244
280
|
(!urgentThreat.bestDodgeDirection || shouldForceShield(urgentThreat)) &&
|
|
245
281
|
urgentThreat.canBlockInTime &&
|
|
246
282
|
urgentThreat.ticksToStartShield <= 3 &&
|
|
247
|
-
(
|
|
283
|
+
(blinkCooldown > 0 || urgentThreat.ticksToImpact < 10)
|
|
284
|
+
)
|
|
285
|
+
{
|
|
286
|
+
return shield();
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// === Preemptive shield — shield when enemy is casting a missile at close range ===
|
|
291
|
+
if (
|
|
292
|
+
status === 'idle' &&
|
|
293
|
+
distance < 300 &&
|
|
294
|
+
enemy.status === 'casting' &&
|
|
295
|
+
enemy.castingSpell === 'missile'
|
|
296
|
+
)
|
|
297
|
+
{
|
|
298
|
+
const remainingCast = (enemy.castDuration ?? 0) - (enemy.castProgress ?? 0);
|
|
299
|
+
const estimatedFlightTicks = distance / 8;
|
|
300
|
+
const ticksUntilHit = remainingCast + estimatedFlightTicks;
|
|
301
|
+
|
|
302
|
+
if (
|
|
303
|
+
ticksUntilHit >= RULES.SHIELD_CAST_TIME &&
|
|
304
|
+
ticksUntilHit < RULES.SHIELD_CAST_TIME + 15
|
|
248
305
|
)
|
|
249
306
|
{
|
|
250
|
-
return
|
|
251
|
-
move: {x: 0, y: 0},
|
|
252
|
-
startCast: {spell: 'shield'},
|
|
253
|
-
};
|
|
307
|
+
return shield();
|
|
254
308
|
}
|
|
255
309
|
}
|
|
256
310
|
|
|
257
311
|
// Aggressive movement — always close distance
|
|
258
|
-
const dodgeDirection = getDodgeDirectionForMovement(threats,
|
|
312
|
+
const dodgeDirection = getDodgeDirectionForMovement(threats, position);
|
|
259
313
|
const move = aggressiveMove(
|
|
260
|
-
|
|
314
|
+
position,
|
|
261
315
|
enemy,
|
|
262
316
|
distance,
|
|
263
317
|
dodgeDirection,
|
|
264
|
-
|
|
318
|
+
tick,
|
|
265
319
|
strafeCyclePeriod,
|
|
266
320
|
strafeIntensity,
|
|
267
321
|
approachSpeed,
|
|
322
|
+
centerBias,
|
|
268
323
|
);
|
|
269
|
-
|
|
270
|
-
|
|
324
|
+
const safeMove = keepOffWalls(position, move);
|
|
325
|
+
return moveAction(safeMove.x, safeMove.y);
|
|
326
|
+
}
|