@vibemancer/core 1.0.6 → 1.0.8

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.
@@ -9,6 +9,7 @@
9
9
  export * from './types.js';
10
10
  export * from './rules.js';
11
11
  export * from './engine-version.js';
12
+ export * from './replay-compat.js';
12
13
  export * from './engine/simulation.js';
13
14
  export * from './engine/hooks-runtime.js';
14
15
  export * from './engine/physics.js';
package/src/index.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export * from './types.js';
2
2
  export * from './rules.js';
3
3
  export * from './engine-version.js';
4
+ export * from './replay-compat.js';
4
5
  export * from './engine/simulation.js';
5
6
  export {DEFAULT_BUDGET, DEFAULT_FIGHT_BACKSTOP_MS, createBudgetState, createFightBudget, recordSpend, mayAct} from './engine/bot-compute-budget.js';
6
7
  export type {BudgetLimits, BotBudgetState, FightBudget} from './engine/bot-compute-budget.js';
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Whether a stored match can still be replayed by the running engine.
3
+ *
4
+ * Spectate re-simulates a recorded match from its seed, so it may only do that when the
5
+ * engine is the one that produced it. The check used to be `match.engineVersion ===
6
+ * ENGINE_VERSION`, which is right in spirit and slightly too strict in fact.
7
+ *
8
+ * ENGINE_VERSION is a CONTENT HASH, so it answers "is this the same source?" rather than
9
+ * "is this the same behaviour?". Those come apart when the hashing itself is corrected. On
10
+ * 2026-09-09 the comment stripper was fixed — it had been hashing most comments as if they
11
+ * were code — and the value moved from 1688330189011034 to 3884810833238487 without
12
+ * packages/core/src changing by a byte. Under an exact comparison that would have stranded
13
+ * 26,948 of the 42,896 stored matches, 63% of them, for no reason a player could act on.
14
+ *
15
+ * So the gate consults a short list of historical values known to describe the same engine.
16
+ * See scripts/engine-version-equivalents.json for what may go in it, and for why a wrong
17
+ * entry is the dangerous kind of wrong: it fails OPEN.
18
+ */
19
+
20
+ import {ENGINE_VERSION, EQUIVALENT_ENGINE_VERSIONS} from './engine-version.js';
21
+
22
+ /**
23
+ * @param matchEngineVersion the `engineVersion` field as read from a stored match.
24
+ * Deliberately typed to admit the junk a database can hand back: a document written
25
+ * before the field existed returns undefined, and a missing version is never a match.
26
+ */
27
+ export function canReplayMatch(matchEngineVersion: number | null | undefined): boolean
28
+ {
29
+ if (typeof matchEngineVersion !== 'number') return false;
30
+ // NaN would fail `===` against everything, so an exact gate happened to reject it. Being
31
+ // explicit keeps that true now that the comparison is a lookup.
32
+ if (!Number.isSafeInteger(matchEngineVersion)) return false;
33
+ if (matchEngineVersion === ENGINE_VERSION) return true;
34
+ return EQUIVALENT_ENGINE_VERSIONS.includes(matchEngineVersion);
35
+ }
package/src/rules.ts CHANGED
@@ -61,6 +61,11 @@ export const RULES = {
61
61
  MISSILE_MIN_CAST_TIME: 0.1, // seconds
62
62
  MISSILE_BASE_RADIUS: 2, // base hitbox radius in units
63
63
  MISSILE_DAMAGE_RADIUS_SCALE: 0.1, // additional radius per damage point
64
+ // The real hitbox is THREE TIMES the base+scale figure. This lived as a bare `* 3` inside
65
+ // calculateMissileRadius, outside RULES, so no document could interpolate it — and five
66
+ // of them consequently stated the unmultiplied formula, understating the hit window by
67
+ // 43-57%. It is a rule, so it lives with the rules.
68
+ MISSILE_RADIUS_MULTIPLIER: 3,
64
69
  MISSILE_BASE_CAST: 0.1,
65
70
  MISSILE_DAMAGE_SCALE: 0.226,
66
71
  MISSILE_DAMAGE_POWER: 2 / 3, // sublinear: DPS always increases with damage
@@ -133,7 +138,7 @@ export const MISSILE_DAMAGE_RADIUS_SCALE = RULES.MISSILE_DAMAGE_RADIUS_SCALE;
133
138
  */
134
139
  export function calculateMissileRadius(damage: number): number
135
140
  {
136
- return (RULES.MISSILE_BASE_RADIUS + damage * RULES.MISSILE_DAMAGE_RADIUS_SCALE) * 3;
141
+ return (RULES.MISSILE_BASE_RADIUS + damage * RULES.MISSILE_DAMAGE_RADIUS_SCALE) * RULES.MISSILE_RADIUS_MULTIPLIER;
137
142
  }
138
143
 
139
144
  // === MISSILE CAST TIME FORMULA ===