@netforge/engine 0.0.0-stage → 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.
Files changed (244) hide show
  1. package/GAME-DESIGN.md +195 -0
  2. package/LICENSE +21 -0
  3. package/NOTICE +30 -0
  4. package/README.md +147 -2
  5. package/dist/api/appearance.d.ts +37 -0
  6. package/dist/api/appearance.js +41 -0
  7. package/dist/api/cards.d.ts +105 -0
  8. package/dist/api/cards.js +133 -0
  9. package/dist/api/collector.d.ts +114 -0
  10. package/dist/api/collector.js +204 -0
  11. package/dist/api/dungeon.d.ts +58 -0
  12. package/dist/api/dungeon.js +216 -0
  13. package/dist/api/harness.d.ts +149 -0
  14. package/dist/api/harness.js +549 -0
  15. package/dist/api/idle.d.ts +71 -0
  16. package/dist/api/idle.js +104 -0
  17. package/dist/api/race-harness.d.ts +3 -0
  18. package/dist/api/race-harness.js +148 -0
  19. package/dist/api/registry.d.ts +241 -0
  20. package/dist/api/registry.js +394 -0
  21. package/dist/api/scheduler.d.ts +56 -0
  22. package/dist/api/scheduler.js +160 -0
  23. package/dist/api/schemas.d.ts +958 -0
  24. package/dist/api/schemas.js +859 -0
  25. package/dist/api/tower.d.ts +127 -0
  26. package/dist/api/tower.js +183 -0
  27. package/dist/game/adapter.d.ts +69 -0
  28. package/dist/game/adapter.js +341 -0
  29. package/dist/game/arena-bot.d.ts +66 -0
  30. package/dist/game/arena-bot.js +188 -0
  31. package/dist/game/bot.d.ts +95 -0
  32. package/dist/game/bot.js +285 -0
  33. package/dist/game/compile.d.ts +97 -0
  34. package/dist/game/compile.js +476 -0
  35. package/dist/game/director.d.ts +42 -0
  36. package/dist/game/director.js +179 -0
  37. package/dist/game/earn-source.d.ts +122 -0
  38. package/dist/game/earn-source.js +386 -0
  39. package/dist/game/earn.d.ts +49 -0
  40. package/dist/game/earn.js +55 -0
  41. package/dist/game/index.d.ts +37 -0
  42. package/dist/game/index.js +25 -0
  43. package/dist/game/platformer.d.ts +156 -0
  44. package/dist/game/platformer.js +619 -0
  45. package/dist/game/profile.d.ts +97 -0
  46. package/dist/game/profile.js +140 -0
  47. package/dist/game/race.d.ts +177 -0
  48. package/dist/game/race.js +386 -0
  49. package/dist/game/racer.d.ts +98 -0
  50. package/dist/game/racer.js +379 -0
  51. package/dist/game/runner.d.ts +286 -0
  52. package/dist/game/runner.js +824 -0
  53. package/dist/game/threats.d.ts +23 -0
  54. package/dist/game/threats.js +49 -0
  55. package/dist/game/view.d.ts +47 -0
  56. package/dist/game/view.js +123 -0
  57. package/dist/index.d.ts +33 -0
  58. package/dist/index.js +24 -0
  59. package/dist/net/bits.d.ts +33 -0
  60. package/dist/net/bits.js +135 -0
  61. package/dist/net/client.d.ts +75 -0
  62. package/dist/net/client.js +151 -0
  63. package/dist/net/index.d.ts +23 -0
  64. package/dist/net/index.js +23 -0
  65. package/dist/net/input.d.ts +49 -0
  66. package/dist/net/input.js +112 -0
  67. package/dist/net/interpolation.d.ts +37 -0
  68. package/dist/net/interpolation.js +97 -0
  69. package/dist/net/netsim.d.ts +22 -0
  70. package/dist/net/netsim.js +52 -0
  71. package/dist/net/prediction.d.ts +31 -0
  72. package/dist/net/prediction.js +101 -0
  73. package/dist/net/profile-client.d.ts +75 -0
  74. package/dist/net/profile-client.js +173 -0
  75. package/dist/net/protocol.d.ts +208 -0
  76. package/dist/net/protocol.js +8 -0
  77. package/dist/net/room-client.d.ts +57 -0
  78. package/dist/net/room-client.js +144 -0
  79. package/dist/net/schema.d.ts +78 -0
  80. package/dist/net/schema.js +121 -0
  81. package/dist/net/sim-adapter.d.ts +134 -0
  82. package/dist/net/sim-adapter.js +22 -0
  83. package/dist/net/snapshot.d.ts +61 -0
  84. package/dist/net/snapshot.js +301 -0
  85. package/dist/rules/adapter.d.ts +53 -0
  86. package/dist/rules/adapter.js +261 -0
  87. package/dist/rules/cards.d.ts +115 -0
  88. package/dist/rules/cards.js +547 -0
  89. package/dist/rules/collector.d.ts +161 -0
  90. package/dist/rules/collector.js +732 -0
  91. package/dist/rules/genres.d.ts +13 -0
  92. package/dist/rules/genres.js +38 -0
  93. package/dist/rules/harness.d.ts +16 -0
  94. package/dist/rules/harness.js +127 -0
  95. package/dist/rules/idle.d.ts +115 -0
  96. package/dist/rules/idle.js +370 -0
  97. package/dist/rules/index.d.ts +16 -0
  98. package/dist/rules/index.js +14 -0
  99. package/dist/rules/kit.d.ts +171 -0
  100. package/dist/rules/kit.js +88 -0
  101. package/dist/rules/tower.d.ts +214 -0
  102. package/dist/rules/tower.js +655 -0
  103. package/dist/runtime/arena.d.ts +86 -0
  104. package/dist/runtime/arena.js +269 -0
  105. package/dist/runtime/assets.d.ts +26 -0
  106. package/dist/runtime/assets.js +35 -0
  107. package/dist/runtime/camera.d.ts +113 -0
  108. package/dist/runtime/camera.js +247 -0
  109. package/dist/runtime/crowd.d.ts +18 -0
  110. package/dist/runtime/crowd.js +62 -0
  111. package/dist/runtime/feel/audio.d.ts +150 -0
  112. package/dist/runtime/feel/audio.js +327 -0
  113. package/dist/runtime/feel/clock.d.ts +52 -0
  114. package/dist/runtime/feel/clock.js +80 -0
  115. package/dist/runtime/feel/flash.d.ts +28 -0
  116. package/dist/runtime/feel/flash.js +60 -0
  117. package/dist/runtime/feel/index.d.ts +27 -0
  118. package/dist/runtime/feel/index.js +16 -0
  119. package/dist/runtime/feel/kit.d.ts +159 -0
  120. package/dist/runtime/feel/kit.js +226 -0
  121. package/dist/runtime/feel/knockback.d.ts +41 -0
  122. package/dist/runtime/feel/knockback.js +70 -0
  123. package/dist/runtime/feel/noise.d.ts +6 -0
  124. package/dist/runtime/feel/noise.js +30 -0
  125. package/dist/runtime/feel/numbers.d.ts +91 -0
  126. package/dist/runtime/feel/numbers.js +172 -0
  127. package/dist/runtime/feel/rumble.d.ts +40 -0
  128. package/dist/runtime/feel/rumble.js +58 -0
  129. package/dist/runtime/feel/sound.d.ts +96 -0
  130. package/dist/runtime/feel/sound.js +176 -0
  131. package/dist/runtime/feel/spring.d.ts +42 -0
  132. package/dist/runtime/feel/spring.js +95 -0
  133. package/dist/runtime/feel/trauma.d.ts +48 -0
  134. package/dist/runtime/feel/trauma.js +76 -0
  135. package/dist/runtime/feel/tween.d.ts +66 -0
  136. package/dist/runtime/feel/tween.js +180 -0
  137. package/dist/runtime/feel/types.d.ts +30 -0
  138. package/dist/runtime/feel/types.js +5 -0
  139. package/dist/runtime/index.d.ts +28 -0
  140. package/dist/runtime/index.js +26 -0
  141. package/dist/runtime/input.d.ts +38 -0
  142. package/dist/runtime/input.js +159 -0
  143. package/dist/runtime/interpolate.d.ts +6 -0
  144. package/dist/runtime/interpolate.js +13 -0
  145. package/dist/runtime/render/animation.d.ts +63 -0
  146. package/dist/runtime/render/animation.js +128 -0
  147. package/dist/runtime/render/atmosphere.d.ts +32 -0
  148. package/dist/runtime/render/atmosphere.js +59 -0
  149. package/dist/runtime/render/crowd-motion.d.ts +68 -0
  150. package/dist/runtime/render/crowd-motion.js +163 -0
  151. package/dist/runtime/render/decals.d.ts +64 -0
  152. package/dist/runtime/render/decals.js +222 -0
  153. package/dist/runtime/render/highlight.d.ts +29 -0
  154. package/dist/runtime/render/highlight.js +110 -0
  155. package/dist/runtime/render/index.d.ts +35 -0
  156. package/dist/runtime/render/index.js +22 -0
  157. package/dist/runtime/render/lighting.d.ts +50 -0
  158. package/dist/runtime/render/lighting.js +142 -0
  159. package/dist/runtime/render/particles.d.ts +117 -0
  160. package/dist/runtime/render/particles.js +289 -0
  161. package/dist/runtime/render/post.d.ts +92 -0
  162. package/dist/runtime/render/post.js +191 -0
  163. package/dist/runtime/render/presets.d.ts +57 -0
  164. package/dist/runtime/render/presets.js +116 -0
  165. package/dist/runtime/render/quality.d.ts +113 -0
  166. package/dist/runtime/render/quality.js +180 -0
  167. package/dist/runtime/render/rig.d.ts +29 -0
  168. package/dist/runtime/render/rig.js +107 -0
  169. package/dist/runtime/render/sprite.d.ts +34 -0
  170. package/dist/runtime/render/sprite.js +58 -0
  171. package/dist/runtime/render/stack.d.ts +74 -0
  172. package/dist/runtime/render/stack.js +132 -0
  173. package/dist/runtime/render/vat.d.ts +78 -0
  174. package/dist/runtime/render/vat.js +219 -0
  175. package/dist/runtime/renderer.d.ts +19 -0
  176. package/dist/runtime/renderer.js +18 -0
  177. package/dist/runtime/stats.d.ts +11 -0
  178. package/dist/runtime/stats.js +29 -0
  179. package/dist/runtime/styles/handdrawn/ground.d.ts +11 -0
  180. package/dist/runtime/styles/handdrawn/ground.js +282 -0
  181. package/dist/runtime/styles/handdrawn/index.d.ts +4 -0
  182. package/dist/runtime/styles/handdrawn/index.js +94 -0
  183. package/dist/runtime/styles/handdrawn/ink.d.ts +31 -0
  184. package/dist/runtime/styles/handdrawn/ink.js +68 -0
  185. package/dist/runtime/styles/handdrawn/looks.d.ts +18 -0
  186. package/dist/runtime/styles/handdrawn/looks.js +42 -0
  187. package/dist/runtime/styles/handdrawn/paint.d.ts +59 -0
  188. package/dist/runtime/styles/handdrawn/paint.js +129 -0
  189. package/dist/runtime/styles/handdrawn/palette.d.ts +49 -0
  190. package/dist/runtime/styles/handdrawn/palette.js +70 -0
  191. package/dist/runtime/styles/handdrawn/textures.d.ts +27 -0
  192. package/dist/runtime/styles/handdrawn/textures.js +150 -0
  193. package/dist/runtime/styles/handdrawn/vfx.d.ts +14 -0
  194. package/dist/runtime/styles/handdrawn/vfx.js +73 -0
  195. package/dist/runtime/styles/index.d.ts +9 -0
  196. package/dist/runtime/styles/index.js +32 -0
  197. package/dist/runtime/styles/kit.d.ts +193 -0
  198. package/dist/runtime/styles/kit.js +1 -0
  199. package/dist/runtime/styles/pixel/index.d.ts +46 -0
  200. package/dist/runtime/styles/pixel/index.js +167 -0
  201. package/dist/runtime/styles/ps1/index.d.ts +29 -0
  202. package/dist/runtime/styles/ps1/index.js +41 -0
  203. package/dist/runtime/styles/ps1/lights.d.ts +47 -0
  204. package/dist/runtime/styles/ps1/lights.js +146 -0
  205. package/dist/runtime/styles/ps1/materials.d.ts +62 -0
  206. package/dist/runtime/styles/ps1/materials.js +285 -0
  207. package/dist/runtime/styles/ps1/pipeline.d.ts +52 -0
  208. package/dist/runtime/styles/ps1/pipeline.js +111 -0
  209. package/dist/runtime/styles/ps1/surfaces.d.ts +44 -0
  210. package/dist/runtime/styles/ps1/surfaces.js +228 -0
  211. package/dist/runtime/styles/toon/index.d.ts +5 -0
  212. package/dist/runtime/styles/toon/index.js +178 -0
  213. package/dist/runtime/styles/toon/ink.d.ts +39 -0
  214. package/dist/runtime/styles/toon/ink.js +73 -0
  215. package/dist/runtime/styles/toon/look.d.ts +10 -0
  216. package/dist/runtime/styles/toon/look.js +112 -0
  217. package/dist/runtime/styles/toon/material.d.ts +74 -0
  218. package/dist/runtime/styles/toon/material.js +208 -0
  219. package/dist/runtime/styles/toon/normals.d.ts +26 -0
  220. package/dist/runtime/styles/toon/normals.js +127 -0
  221. package/dist/runtime/styles/toon/shadows.d.ts +24 -0
  222. package/dist/runtime/styles/toon/shadows.js +42 -0
  223. package/dist/runtime/styles/toon/vfx.d.ts +15 -0
  224. package/dist/runtime/styles/toon/vfx.js +47 -0
  225. package/dist/runtime/styles/view.d.ts +35 -0
  226. package/dist/runtime/styles/view.js +34 -0
  227. package/dist/runtime/styles/voxel/core.d.ts +114 -0
  228. package/dist/runtime/styles/voxel/core.js +577 -0
  229. package/dist/runtime/styles/voxel/geometry.d.ts +27 -0
  230. package/dist/runtime/styles/voxel/geometry.js +40 -0
  231. package/dist/runtime/styles/voxel/index.d.ts +21 -0
  232. package/dist/runtime/styles/voxel/index.js +23 -0
  233. package/dist/runtime/styles/voxel/kit.d.ts +6 -0
  234. package/dist/runtime/styles/voxel/kit.js +11 -0
  235. package/dist/runtime/styles/voxel/look.d.ts +10 -0
  236. package/dist/runtime/styles/voxel/look.js +16 -0
  237. package/dist/runtime/styles/voxel/props.d.ts +9 -0
  238. package/dist/runtime/styles/voxel/props.js +52 -0
  239. package/dist/runtime/styles/voxel/shapes.d.ts +41 -0
  240. package/dist/runtime/styles/voxel/shapes.js +83 -0
  241. package/dist/runtime/styles/voxel/world.d.ts +49 -0
  242. package/dist/runtime/styles/voxel/world.js +422 -0
  243. package/llms.txt +183 -0
  244. package/package.json +82 -3
@@ -0,0 +1,179 @@
1
+ /**
2
+ * The spawn director (GAME-DESIGN.md): the engine's
3
+ * answer to "top the crowd up to N". It runs on the authoritative side next
4
+ * to the systems, on the run clock, and is fully deterministic (the sim's
5
+ * PRNG, tick counts, netId-ordered loops in the sim).
6
+ *
7
+ * - Spawns from a weighted pool at a curve-driven rate, up to a
8
+ * curve-driven number alive, scaled by players present.
9
+ * - Spawns just outside each player's screen (engine-sim edge points):
10
+ * never on it, never clamped to a wall.
11
+ * - Bodies the cap held back become "crowded" multipliers on later spawns.
12
+ * - Recycles stragglers far off screen to the edge ahead of a moving player.
13
+ */
14
+ import { edgeSpawnPoint, enemyCount, entityHeadroom, listPlayers, recycleEnemies, setEnemySpeed, simRandom, spawnEnemyScaled } from "@netforge/engine-sim";
15
+ /** Value of a curve at `t` seconds: linear between keyframes, held outside them. */
16
+ export function curveAt(c, t) {
17
+ if (typeof c === "number")
18
+ return c;
19
+ const first = c[0];
20
+ if (!first)
21
+ return 0;
22
+ if (t <= first[0])
23
+ return first[1];
24
+ for (let i = 1; i < c.length; i++) {
25
+ const b = c[i];
26
+ if (t <= b[0]) {
27
+ const a = c[i - 1];
28
+ return a[1] + ((b[1] - a[1]) * (t - a[0])) / (b[0] - a[0]);
29
+ }
30
+ }
31
+ return c[c.length - 1][1];
32
+ }
33
+ /** Ticks between straggler sweeps. */
34
+ const RECYCLE_PERIOD = 15;
35
+ export class Director {
36
+ def;
37
+ world;
38
+ hooks;
39
+ budget;
40
+ /** Bodies the cap held back, owed as toughness. */
41
+ pressure = 0;
42
+ /** Spawn-rate multiplier set by systems (events), and until when. */
43
+ boost = 1;
44
+ boostUntil = 0;
45
+ pausedUntil = 0;
46
+ /** Run tick of the next elite (-1: none planned). */
47
+ nextElite = -1;
48
+ constructor(def, world, hooks) {
49
+ this.def = def;
50
+ this.world = world;
51
+ this.hooks = hooks;
52
+ this.budget = def.start;
53
+ if (def.elites)
54
+ this.nextElite = Math.round(def.elites.from * world.tickRate);
55
+ }
56
+ /** Systems: stop spawning for `ticks` from `now` (events, set pieces). */
57
+ pause(now, ticks) {
58
+ this.pausedUntil = Math.max(this.pausedUntil, now + ticks);
59
+ }
60
+ /** Systems: multiply the spawn rate by `k` for `ticks` from `now`. */
61
+ surge(now, k, ticks) {
62
+ this.boost = k;
63
+ this.boostUntil = now + ticks;
64
+ }
65
+ /** Players present (at least 1), for the per-player scaling. */
66
+ players() {
67
+ return Math.max(1, listPlayers(this.world).length);
68
+ }
69
+ /** The living-enemy target at run tick `tick` (scaled by players). */
70
+ aliveTarget(tick) {
71
+ const t = tick / this.world.tickRate;
72
+ const mult = 1 + this.def.perPlayer * (this.players() - 1);
73
+ return Math.min(this.def.max, Math.round(curveAt(this.def.alive, t) * mult));
74
+ }
75
+ /** One run tick. */
76
+ tick(tick) {
77
+ const w = this.world;
78
+ const rate = w.tickRate;
79
+ const def = this.def;
80
+ if (def.recycle !== false && tick % RECYCLE_PERIOD === 0 && tick > 0) {
81
+ const r = def.recycle === true ? { seconds: 2.5, margin: 8 } : def.recycle;
82
+ recycleEnemies(w, RECYCLE_PERIOD, Math.round(r.seconds * rate), r.margin);
83
+ }
84
+ const t = tick / rate;
85
+ setEnemySpeed(w, curveAt(def.speed, t));
86
+ if (tick < this.pausedUntil)
87
+ return;
88
+ if (this.nextElite >= 0 && tick >= this.nextElite && def.elites) {
89
+ const e = this.pick(t);
90
+ if (e && this.spawnGroup(e.prefab, 1, t, true) > 0) {
91
+ const [lo, hi] = def.elites.every;
92
+ this.nextElite = tick + Math.round((lo + simRandom(w) * (hi - lo)) * rate);
93
+ }
94
+ }
95
+ const n = this.players();
96
+ const mult = 1 + def.perPlayer * (n - 1);
97
+ let perSecond = curveAt(def.rate, t) * mult * (this.hooks.bossAlive() ? def.whileBoss : 1);
98
+ if (tick < this.boostUntil)
99
+ perSecond *= this.boost;
100
+ this.budget = Math.min(this.budget + perSecond / rate, Math.max(this.budget, perSecond * 2 + 1));
101
+ const cap = Math.min(def.max, Math.round(curveAt(def.alive, t) * mult));
102
+ let alive = enemyCount(w);
103
+ let guard = 64;
104
+ while (this.budget >= 1 && guard-- > 0) {
105
+ if (alive >= cap) {
106
+ // The cap holds the body back; it comes back as toughness instead.
107
+ this.budget -= 1;
108
+ this.pressure = Math.min(this.pressure + 1, cap);
109
+ continue;
110
+ }
111
+ const entry = this.pick(t);
112
+ if (!entry) {
113
+ this.budget = 0;
114
+ break;
115
+ }
116
+ const g = entry.group;
117
+ const size = Array.isArray(g) ? g[0] + Math.floor(simRandom(w) * (g[1] - g[0] + 1)) : g;
118
+ const spawned = this.spawnGroup(entry.prefab, Math.min(size, cap - alive), t);
119
+ this.budget -= Math.max(1, size);
120
+ alive += spawned;
121
+ if (spawned === 0)
122
+ break;
123
+ }
124
+ }
125
+ pick(t) {
126
+ const def = this.def;
127
+ let total = 0;
128
+ const weights = [];
129
+ for (const e of def.pool) {
130
+ let wgt = 0;
131
+ if (t >= e.from && (e.until === undefined || t < e.until)) {
132
+ wgt = e.weight;
133
+ if (def.fade && t - e.from > def.fade.after)
134
+ wgt *= def.fade.weight;
135
+ }
136
+ weights.push(wgt);
137
+ total += wgt;
138
+ }
139
+ if (total <= 0)
140
+ return null;
141
+ let r = simRandom(this.world) * total;
142
+ for (let i = 0; i < def.pool.length; i++) {
143
+ r -= weights[i] ?? 0;
144
+ if (r < 0)
145
+ return def.pool[i] ?? null;
146
+ }
147
+ return def.pool[def.pool.length - 1] ?? null;
148
+ }
149
+ /** Spawn up to `count` of a prefab from one heading off one player's screen. */
150
+ spawnGroup(prefab, count, t, elite = false) {
151
+ const w = this.world;
152
+ const type = this.hooks.typeOf(prefab);
153
+ if (type === undefined || count <= 0)
154
+ return 0;
155
+ const radius = this.hooks.radiusOf(prefab);
156
+ const def = this.def;
157
+ const base = { health: curveAt(def.health, t), damage: curveAt(def.damage, t), xp: curveAt(def.xp, t) };
158
+ // A group shares a heading: one stream from one direction.
159
+ const heading = count > 1 ? simRandom(w) * Math.PI * 2 : undefined;
160
+ const ahead = heading === undefined && simRandom(w) < def.ahead;
161
+ let n = 0;
162
+ for (let i = 0; i < count; i++) {
163
+ if (entityHeadroom(w) <= 64)
164
+ break;
165
+ const at = edgeSpawnPoint(w, undefined, heading !== undefined ? { heading, spread: 0.45, radius } : ahead ? { relative: "ahead", spread: 2, radius } : { radius });
166
+ if (!at)
167
+ break;
168
+ let scale = base;
169
+ if (this.pressure > 0) {
170
+ this.pressure--;
171
+ scale = { health: (base.health ?? 1) * def.crowded.health, damage: (base.damage ?? 1) * def.crowded.damage, xp: (base.xp ?? 1) * def.crowded.xp };
172
+ }
173
+ const id = spawnEnemyScaled(w, type, at[0], at[1], elite ? { ...scale, elite: true } : scale);
174
+ this.hooks.spawned?.(prefab, id);
175
+ n++;
176
+ }
177
+ return n;
178
+ }
179
+ }
@@ -0,0 +1,122 @@
1
+ /**
2
+ * Earn rules as source text: read and rewrite the `earn.rule(...)` lines of a
3
+ * game's src/game.ts.
4
+ *
5
+ * game.ts is the single source of truth for what a game pays. The room
6
+ * server's referee pays the compiled module's `pay`, and the launch
7
+ * registers one RewardVault rule per `earn.rule`, in declaration order
8
+ * (vault rule ids 1, 2, 3...). So the
9
+ * Launch panel never keeps its own copy of a payout: an edit there is a
10
+ * rewrite of game.ts, which goes through the same gate and build as any
11
+ * other change, and what launches is what the build declares.
12
+ *
13
+ * Pure, no imports, so the Net Forge server, the Net Forge site and the
14
+ * tests can all share it.
15
+ */
16
+ export type OncePer = "run" | "day" | "lifetime";
17
+ /** One `earn.rule(id, { when, n, pay, oncePer })` declaration. */
18
+ export interface SourceEarnRule {
19
+ readonly id: string;
20
+ /** The game event it counts, e.g. "wave.cleared". */
21
+ readonly when: string;
22
+ /** Pays when the event's count reaches n in a run. */
23
+ readonly n: number;
24
+ /** Whole coins (1 coin = 1 token), up to 6 decimals. */
25
+ readonly pay: number;
26
+ readonly oncePer: OncePer;
27
+ }
28
+ /** The engine's id rule (api/schemas.ts idSchema). */
29
+ export declare const EARN_RULE_ID: RegExp;
30
+ /** The engine's event-name rule (api/schemas.ts earnRuleSchema.when). */
31
+ export declare const EARN_EVENT: RegExp;
32
+ /** Most rules one game declares (the Launch panel's limit too). */
33
+ export declare const MAX_EARN_RULES = 8;
34
+ /** Highest count a rule may wait for. */
35
+ export declare const MAX_EARN_N = 100000;
36
+ /** Highest payout one rule may make in one run, in coins. */
37
+ export declare const MAX_EARN_PAY = 1000000;
38
+ /**
39
+ * Events the sim raises by itself that may pay, per template (engine
40
+ * registry `on(...)` docs). A game's own `emit("...")` events are added from
41
+ * its source. Player-state events (downed, died, revived, levelup) are left
42
+ * out on purpose: they are cheap to farm.
43
+ */
44
+ export declare const SIM_EARN_EVENTS: Readonly<Record<string, readonly string[]>>;
45
+ /** The plain trigger a player reads, from the rule's event and count. */
46
+ export declare function earnRuleLabel(when: string, n: number): string;
47
+ export interface ReadEarnRules {
48
+ /** Every rule the panel can read, in declaration order (the vault's id order). */
49
+ readonly rules: readonly SourceEarnRule[];
50
+ /** Why some declaration cannot be read or rewritten; empty when the panel can edit them all. */
51
+ readonly problems: readonly string[];
52
+ }
53
+ /** The game's earn rules, in declaration order. */
54
+ export declare function readEarnRules(source: string): ReadEarnRules;
55
+ /** Events a game may pay on: the template's sim events and every `emit("...")` in its source. */
56
+ export declare function earnEvents(source: string): string[];
57
+ /** Every problem with a rule set, as one plain line each; empty when it can be written. */
58
+ export declare function checkEarnRules(rules: readonly SourceEarnRule[], events: readonly string[]): string[];
59
+ /** A fresh rule id for an event and count, unique among `taken`. */
60
+ export declare function newEarnRuleId(when: string, n: number, taken: readonly string[]): string;
61
+ /** The one line a rule is written as. */
62
+ export declare function earnRuleLine(r: SourceEarnRule): string;
63
+ export declare class EarnSourceError extends Error {
64
+ }
65
+ /**
66
+ * game.ts with its earn rules replaced by `rules`, in that order. The
67
+ * declarations are written as one block where the first one was, so the
68
+ * order in the file is the order given (the vault's rule ids). Throws
69
+ * EarnSourceError when an existing declaration cannot be read safely
70
+ * (nested, multi-line or computed): those games change their rules in chat.
71
+ */
72
+ export declare function writeEarnRules(source: string, rules: readonly SourceEarnRule[]): string;
73
+ /** Same rules, same order, same values (labels and launch settings aside). */
74
+ export declare function sameEarnRules(a: readonly SourceEarnRule[], b: readonly SourceEarnRule[]): boolean;
75
+ /**
76
+ * The buy-in run, `earn.stake({ min, max, multiple })` (the mandatory sink
77
+ * loop), read and rewritten the same way: one top-level
78
+ * line in game.ts, so the Launch panel shows exactly what the build pays.
79
+ */
80
+ export interface SourceEarnStake {
81
+ /** Smallest stake a run takes, in coins. */
82
+ readonly min: number;
83
+ /** Largest stake a run takes, in coins. */
84
+ readonly max: number;
85
+ /** The stake's payout ceiling is stake x multiple. */
86
+ readonly multiple: number;
87
+ }
88
+ /** PLACEHOLDER: the engine's cap on the multiple (api/schemas.ts STAKE_MAX_MULTIPLE). */
89
+ export declare const MAX_STAKE_MULTIPLE = 2;
90
+ /** The engine's cap on one stake, in coins (api/schemas.ts STAKE_MAX_COINS). */
91
+ export declare const MAX_STAKE_COINS = 1000000;
92
+ export interface ReadEarnStake {
93
+ /** The declaration, or null when there is none (or it cannot be read). */
94
+ readonly stake: SourceEarnStake | null;
95
+ /** Why the panel cannot read or rewrite it; empty when it can. */
96
+ readonly problems: readonly string[];
97
+ }
98
+ /** The game's buy-in run terms. */
99
+ export declare function readEarnStake(source: string): ReadEarnStake;
100
+ /** Every problem with stake terms, as one plain line each; empty when they can be written. */
101
+ export declare function checkEarnStake(s: SourceEarnStake): string[];
102
+ /** The one line the stake is written as. */
103
+ export declare function earnStakeLine(s: SourceEarnStake): string;
104
+ /**
105
+ * game.ts with its buy-in run set to `stake`: the declaration is rewritten
106
+ * in place, or added after the last earn rule when there is none. Throws
107
+ * EarnSourceError when an existing one cannot be read safely.
108
+ */
109
+ export declare function writeEarnStake(source: string, stake: SourceEarnStake): string;
110
+ /** Same terms. */
111
+ export declare function sameEarnStake(a: SourceEarnStake | null, b: SourceEarnStake | null): boolean;
112
+ /** A PvP pot, `earn.pot({ entry, recycle })` (optional; the arena template). */
113
+ export interface SourceEarnPot {
114
+ /** Coins each entrant pays in. */
115
+ readonly entry: number;
116
+ /** Share of every pot that stays in the reward pool. */
117
+ readonly recycle: number;
118
+ /** Pots are off by default until a legal review: true only when the line says `enabled: true`. */
119
+ readonly enabled?: boolean;
120
+ }
121
+ /** The game's PvP pot, read only (null when it has none or it is not written with plain values on one line). */
122
+ export declare function readEarnPot(source: string): SourceEarnPot | null;