@jgengine/core 0.7.0 → 0.9.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 (329) hide show
  1. package/CHANGELOG.md +63 -0
  2. package/README.md +2 -0
  3. package/dist/ai/flock.d.ts +36 -0
  4. package/dist/ai/flock.js +182 -0
  5. package/dist/ai/groupAssist.d.ts +19 -0
  6. package/dist/ai/groupAssist.js +59 -0
  7. package/dist/ai/laneSelect.d.ts +21 -0
  8. package/dist/ai/laneSelect.js +37 -0
  9. package/dist/ai/mobBrain.d.ts +68 -0
  10. package/dist/ai/mobBrain.js +115 -0
  11. package/dist/ai/spawnDirector.d.ts +5 -0
  12. package/dist/ai/spawnDirector.js +14 -2
  13. package/dist/ai/threat.d.ts +2 -0
  14. package/dist/ai/threat.js +34 -0
  15. package/dist/anim/easing.d.ts +14 -0
  16. package/dist/anim/easing.js +55 -0
  17. package/dist/behaviour/behaviour.d.ts +136 -0
  18. package/dist/behaviour/behaviour.js +388 -0
  19. package/dist/cards/cardPile.d.ts +1 -0
  20. package/dist/cards/cardPile.js +12 -3
  21. package/dist/cartridge/runtime.d.ts +15 -0
  22. package/dist/cartridge/runtime.js +556 -0
  23. package/dist/cartridge/spec.d.ts +188 -0
  24. package/dist/cartridge/spec.js +19 -0
  25. package/dist/cartridge/validate.d.ts +2 -0
  26. package/dist/cartridge/validate.js +78 -0
  27. package/dist/combat/abilityKit.d.ts +22 -2
  28. package/dist/combat/abilityKit.js +72 -13
  29. package/dist/combat/castRunner.d.ts +40 -0
  30. package/dist/combat/castRunner.js +56 -0
  31. package/dist/combat/comboPoints.d.ts +14 -0
  32. package/dist/combat/comboPoints.js +52 -0
  33. package/dist/combat/defensiveWindow.d.ts +1 -1
  34. package/dist/combat/defensiveWindow.js +5 -3
  35. package/dist/combat/effects.d.ts +1 -1
  36. package/dist/combat/effects.js +9 -6
  37. package/dist/combat/projectiles.d.ts +64 -6
  38. package/dist/combat/projectiles.js +232 -74
  39. package/dist/combat/resistance.d.ts +16 -4
  40. package/dist/combat/resistance.js +43 -7
  41. package/dist/combat/resourcePool.d.ts +20 -0
  42. package/dist/combat/resourcePool.js +55 -0
  43. package/dist/combat/shotOrigin.d.ts +40 -0
  44. package/dist/combat/shotOrigin.js +57 -0
  45. package/dist/combat/telegraph.d.ts +30 -1
  46. package/dist/combat/telegraph.js +61 -0
  47. package/dist/commands/commandRegistry.d.ts +1 -1
  48. package/dist/commands/commandRegistry.js +2 -1
  49. package/dist/crafting/recipe.js +4 -1
  50. package/dist/data/dataSource.d.ts +26 -0
  51. package/dist/data/dataSource.js +93 -0
  52. package/dist/data/devProxy.d.ts +11 -0
  53. package/dist/data/devProxy.js +38 -0
  54. package/dist/data/fetchJson.d.ts +20 -0
  55. package/dist/data/fetchJson.js +43 -0
  56. package/dist/data/jsonDataSource.d.ts +4 -0
  57. package/dist/data/jsonDataSource.js +6 -0
  58. package/dist/devtools/devtools.d.ts +205 -0
  59. package/dist/devtools/devtools.js +832 -0
  60. package/dist/devtools/transformTunables.d.ts +14 -0
  61. package/dist/devtools/transformTunables.js +38 -0
  62. package/dist/devtools/tunableSchema.d.ts +140 -0
  63. package/dist/devtools/tunableSchema.js +476 -0
  64. package/dist/faction/factions.d.ts +33 -0
  65. package/dist/faction/factions.js +76 -0
  66. package/dist/faction/reputation.d.ts +32 -0
  67. package/dist/faction/reputation.js +82 -0
  68. package/dist/game/chat.d.ts +68 -0
  69. package/dist/game/chat.js +173 -0
  70. package/dist/game/chatFilter.d.ts +15 -0
  71. package/dist/game/chatFilter.js +55 -0
  72. package/dist/game/controlGate.d.ts +4 -0
  73. package/dist/game/controlGate.js +7 -0
  74. package/dist/game/defineGame.d.ts +10 -2
  75. package/dist/game/defineGame.js +2 -1
  76. package/dist/game/events.d.ts +35 -0
  77. package/dist/game/gamePhase.d.ts +20 -0
  78. package/dist/game/gamePhase.js +21 -0
  79. package/dist/game/levelSequence.d.ts +44 -0
  80. package/dist/game/levelSequence.js +78 -0
  81. package/dist/game/lootTable.d.ts +10 -1
  82. package/dist/game/lootTable.js +27 -7
  83. package/dist/game/playableGame.d.ts +286 -6
  84. package/dist/game/playableGame.js +6 -1
  85. package/dist/game/progression.d.ts +1 -0
  86. package/dist/game/progression.js +23 -2
  87. package/dist/game/quest.d.ts +7 -3
  88. package/dist/game/quest.js +17 -8
  89. package/dist/game/race.d.ts +44 -1
  90. package/dist/game/race.js +116 -11
  91. package/dist/game/recordBook.d.ts +29 -0
  92. package/dist/game/recordBook.js +86 -0
  93. package/dist/game/social.d.ts +42 -0
  94. package/dist/game/social.js +122 -0
  95. package/dist/game/spawnPoints.d.ts +19 -0
  96. package/dist/game/spawnPoints.js +23 -0
  97. package/dist/game/talents.d.ts +47 -0
  98. package/dist/game/talents.js +140 -0
  99. package/dist/game/worldItem.d.ts +3 -0
  100. package/dist/game/worldItem.js +12 -6
  101. package/dist/index.d.ts +1 -0
  102. package/dist/index.js +1 -0
  103. package/dist/input/actionBindings.d.ts +10 -0
  104. package/dist/input/actionBindings.js +14 -0
  105. package/dist/input/axisInput.d.ts +26 -3
  106. package/dist/input/axisInput.js +44 -4
  107. package/dist/input/bindingOverrides.d.ts +25 -0
  108. package/dist/input/bindingOverrides.js +95 -0
  109. package/dist/input/gestureSurface.d.ts +31 -0
  110. package/dist/input/gestureSurface.js +120 -0
  111. package/dist/input/pointer.d.ts +45 -0
  112. package/dist/input/pointer.js +48 -0
  113. package/dist/input/pointerAxis.d.ts +30 -0
  114. package/dist/input/pointerAxis.js +31 -0
  115. package/dist/input/touchScheme.d.ts +89 -0
  116. package/dist/input/touchScheme.js +153 -0
  117. package/dist/item/affix.d.ts +1 -1
  118. package/dist/item/affix.js +1 -19
  119. package/dist/meta/changelog.d.ts +1 -1
  120. package/dist/meta/changelog.js +59 -1
  121. package/dist/movement/dash.d.ts +3 -0
  122. package/dist/movement/dash.js +19 -5
  123. package/dist/movement/glideModel.d.ts +44 -0
  124. package/dist/movement/glideModel.js +54 -0
  125. package/dist/movement/grappleSwing.d.ts +34 -0
  126. package/dist/movement/grappleSwing.js +56 -0
  127. package/dist/movement/leaderTrail.d.ts +29 -0
  128. package/dist/movement/leaderTrail.js +67 -0
  129. package/dist/movement/movementModel.d.ts +36 -3
  130. package/dist/movement/movementModel.js +81 -5
  131. package/dist/movement/steering.d.ts +27 -0
  132. package/dist/movement/steering.js +40 -0
  133. package/dist/movement/voxelController.d.ts +61 -0
  134. package/dist/movement/voxelController.js +229 -0
  135. package/dist/multiplayer/chatContract.d.ts +42 -0
  136. package/dist/multiplayer/chatContract.js +57 -0
  137. package/dist/multiplayer/feedWriteGate.d.ts +10 -0
  138. package/dist/multiplayer/feedWriteGate.js +12 -0
  139. package/dist/multiplayer/identity.d.ts +13 -0
  140. package/dist/multiplayer/identity.js +20 -0
  141. package/dist/multiplayer/poseSyncGate.d.ts +1 -0
  142. package/dist/multiplayer/poseSyncGate.js +13 -1
  143. package/dist/multiplayer/presenceModel.d.ts +17 -2
  144. package/dist/multiplayer/presenceModel.js +30 -4
  145. package/dist/multiplayer/voiceContract.d.ts +44 -0
  146. package/dist/multiplayer/voiceContract.js +108 -0
  147. package/dist/nav/corridors.d.ts +30 -0
  148. package/dist/nav/corridors.js +69 -0
  149. package/dist/nav/navConstrain.d.ts +14 -0
  150. package/dist/nav/navConstrain.js +26 -0
  151. package/dist/nav/navFromEnvironment.d.ts +11 -0
  152. package/dist/nav/navFromEnvironment.js +16 -0
  153. package/dist/nav/navGrid.d.ts +10 -0
  154. package/dist/nav/navGrid.js +48 -5
  155. package/dist/nav/pathFollow.d.ts +4 -2
  156. package/dist/nav/pathFollow.js +5 -3
  157. package/dist/nav/railGraph.d.ts +66 -0
  158. package/dist/nav/railGraph.js +129 -0
  159. package/dist/nav/timetable.d.ts +41 -0
  160. package/dist/nav/timetable.js +94 -0
  161. package/dist/physics/ballisticSweep.d.ts +18 -0
  162. package/dist/physics/ballisticSweep.js +49 -0
  163. package/dist/physics/buoyancy.d.ts +6 -0
  164. package/dist/physics/buoyancy.js +14 -0
  165. package/dist/physics/flowTube.d.ts +32 -0
  166. package/dist/physics/flowTube.js +64 -0
  167. package/dist/physics/forceVolume.d.ts +27 -0
  168. package/dist/physics/forceVolume.js +43 -5
  169. package/dist/physics/kinematicVehicle.d.ts +52 -0
  170. package/dist/physics/kinematicVehicle.js +81 -0
  171. package/dist/physics/physicsWorld.d.ts +54 -8
  172. package/dist/physics/physicsWorld.js +400 -79
  173. package/dist/physics/ragdoll.js +1 -1
  174. package/dist/physics/spatialGrid.d.ts +1 -1
  175. package/dist/physics/spatialGrid.js +5 -0
  176. package/dist/physics/vehicleBody.d.ts +6 -0
  177. package/dist/physics/vehicleBody.js +18 -18
  178. package/dist/puzzle/cellGrid.d.ts +26 -0
  179. package/dist/puzzle/cellGrid.js +110 -0
  180. package/dist/puzzle/fallingPiece.d.ts +24 -0
  181. package/dist/puzzle/fallingPiece.js +51 -0
  182. package/dist/random/nameGen.d.ts +18 -0
  183. package/dist/random/nameGen.js +35 -0
  184. package/dist/random/rng.d.ts +2 -0
  185. package/dist/random/rng.js +23 -0
  186. package/dist/random/seedLink.d.ts +5 -0
  187. package/dist/random/seedLink.js +35 -0
  188. package/dist/runtime/adapter.d.ts +35 -0
  189. package/dist/runtime/adapter.js +37 -1
  190. package/dist/runtime/cameraDirector.d.ts +15 -0
  191. package/dist/runtime/cameraDirector.js +31 -0
  192. package/dist/runtime/commandRunner.d.ts +2 -2
  193. package/dist/runtime/commandRunner.js +2 -2
  194. package/dist/runtime/gameContext.d.ts +65 -2
  195. package/dist/runtime/gameContext.js +280 -9
  196. package/dist/runtime/gameRuntime.d.ts +5 -1
  197. package/dist/runtime/gameRuntime.js +13 -12
  198. package/dist/runtime/hostPersistence.d.ts +30 -1
  199. package/dist/runtime/hostPersistence.js +70 -5
  200. package/dist/runtime/inputSnapshot.d.ts +12 -0
  201. package/dist/runtime/inputSnapshot.js +17 -0
  202. package/dist/runtime/motionIntents.d.ts +33 -0
  203. package/dist/runtime/motionIntents.js +43 -0
  204. package/dist/runtime/snapshot.d.ts +1 -0
  205. package/dist/runtime/snapshot.js +1 -1
  206. package/dist/runtime/transport.d.ts +32 -0
  207. package/dist/runtime/visibility.d.ts +74 -0
  208. package/dist/runtime/visibility.js +127 -0
  209. package/dist/scene/colliders.d.ts +44 -0
  210. package/dist/scene/colliders.js +77 -0
  211. package/dist/scene/entityStore.d.ts +30 -5
  212. package/dist/scene/entityStore.js +83 -13
  213. package/dist/scene/movementSpeed.d.ts +35 -0
  214. package/dist/scene/movementSpeed.js +20 -0
  215. package/dist/scene/objectQuery.d.ts +29 -0
  216. package/dist/scene/objectQuery.js +140 -0
  217. package/dist/scene/objectStore.d.ts +13 -0
  218. package/dist/scene/objectStore.js +130 -6
  219. package/dist/scene/paintLayer.d.ts +15 -0
  220. package/dist/scene/paintLayer.js +53 -0
  221. package/dist/scene/sceneRaycast.d.ts +80 -0
  222. package/dist/scene/sceneRaycast.js +284 -0
  223. package/dist/scene/spatial.d.ts +17 -1
  224. package/dist/scene/spatial.js +140 -17
  225. package/dist/sensor/concealment.d.ts +21 -0
  226. package/dist/sensor/concealment.js +63 -0
  227. package/dist/sensor/freezeMonitor.d.ts +17 -0
  228. package/dist/sensor/freezeMonitor.js +35 -0
  229. package/dist/sensor/recordingBuffer.d.ts +6 -0
  230. package/dist/sensor/recordingBuffer.js +7 -0
  231. package/dist/sensor/replayLoop.d.ts +39 -0
  232. package/dist/sensor/replayLoop.js +64 -0
  233. package/dist/sensor/visionCone.d.ts +31 -0
  234. package/dist/sensor/visionCone.js +72 -0
  235. package/dist/session/extraction.d.ts +2 -2
  236. package/dist/session/extraction.js +13 -2
  237. package/dist/session/roles.d.ts +6 -0
  238. package/dist/session/roles.js +32 -0
  239. package/dist/session/roundState.d.ts +41 -17
  240. package/dist/session/roundState.js +67 -38
  241. package/dist/settings/settingsModel.d.ts +97 -0
  242. package/dist/settings/settingsModel.js +100 -0
  243. package/dist/stats/statModifiers.d.ts +5 -2
  244. package/dist/stats/statModifiers.js +32 -14
  245. package/dist/store/observableKeyedStore.js +8 -2
  246. package/dist/survival/decayMeter.d.ts +1 -1
  247. package/dist/survival/decayMeter.js +0 -1
  248. package/dist/tactics/fallingGrid.d.ts +62 -0
  249. package/dist/tactics/fallingGrid.js +158 -0
  250. package/dist/tactics/tacticalGrid.d.ts +11 -0
  251. package/dist/tactics/tacticalGrid.js +17 -0
  252. package/dist/time/beatClock.d.ts +18 -0
  253. package/dist/time/beatClock.js +24 -0
  254. package/dist/time/idleProgress.d.ts +29 -0
  255. package/dist/time/idleProgress.js +31 -0
  256. package/dist/time/serverTick.d.ts +7 -1
  257. package/dist/time/serverTick.js +27 -4
  258. package/dist/time/simClock.d.ts +25 -1
  259. package/dist/time/simClock.js +28 -3
  260. package/dist/time/stateSchedule.d.ts +57 -0
  261. package/dist/time/stateSchedule.js +121 -0
  262. package/dist/turn/commit.d.ts +2 -2
  263. package/dist/turn/commit.js +2 -2
  264. package/dist/turn/intent.d.ts +14 -0
  265. package/dist/turn/intent.js +29 -0
  266. package/dist/turn/turnLoop.d.ts +2 -0
  267. package/dist/turn/turnLoop.js +8 -1
  268. package/dist/ui/hudLayout.d.ts +58 -0
  269. package/dist/ui/hudLayout.js +207 -0
  270. package/dist/ui/hudScale.d.ts +44 -0
  271. package/dist/ui/hudScale.js +55 -0
  272. package/dist/visibility/assetStreaming.d.ts +66 -0
  273. package/dist/visibility/assetStreaming.js +201 -0
  274. package/dist/visibility/bounds.d.ts +66 -0
  275. package/dist/visibility/bounds.js +113 -0
  276. package/dist/visibility/camera.d.ts +21 -0
  277. package/dist/visibility/camera.js +3 -0
  278. package/dist/visibility/config.d.ts +21 -0
  279. package/dist/visibility/config.js +1 -0
  280. package/dist/visibility/diagnostics.d.ts +26 -0
  281. package/dist/visibility/diagnostics.js +36 -0
  282. package/dist/visibility/distance.d.ts +11 -0
  283. package/dist/visibility/distance.js +30 -0
  284. package/dist/visibility/frustum.d.ts +47 -0
  285. package/dist/visibility/frustum.js +186 -0
  286. package/dist/visibility/occlusion.d.ts +36 -0
  287. package/dist/visibility/occlusion.js +45 -0
  288. package/dist/visibility/settings.d.ts +75 -0
  289. package/dist/visibility/settings.js +49 -0
  290. package/dist/visibility/simulationCulling.d.ts +34 -0
  291. package/dist/visibility/simulationCulling.js +36 -0
  292. package/dist/visibility/spatialIndex.d.ts +45 -0
  293. package/dist/visibility/spatialIndex.js +198 -0
  294. package/dist/visibility/visibilitySystem.d.ts +90 -0
  295. package/dist/visibility/visibilitySystem.js +257 -0
  296. package/dist/world/buildings.d.ts +6 -0
  297. package/dist/world/buildings.js +152 -0
  298. package/dist/world/carve.d.ts +7 -2
  299. package/dist/world/carve.js +17 -7
  300. package/dist/world/cellStates.d.ts +45 -0
  301. package/dist/world/cellStates.js +103 -0
  302. package/dist/world/environmentSummary.d.ts +61 -0
  303. package/dist/world/environmentSummary.js +138 -0
  304. package/dist/world/features.d.ts +138 -9
  305. package/dist/world/features.js +73 -2
  306. package/dist/world/gridInstances.d.ts +12 -0
  307. package/dist/world/gridInstances.js +160 -0
  308. package/dist/world/lod.d.ts +17 -0
  309. package/dist/world/lod.js +66 -0
  310. package/dist/world/mapLayers.d.ts +60 -0
  311. package/dist/world/mapLayers.js +44 -0
  312. package/dist/world/massing.d.ts +45 -0
  313. package/dist/world/massing.js +581 -0
  314. package/dist/world/minimap.d.ts +13 -1
  315. package/dist/world/minimap.js +21 -2
  316. package/dist/world/placementController.d.ts +22 -1
  317. package/dist/world/placementController.js +56 -0
  318. package/dist/world/terrain.d.ts +62 -1
  319. package/dist/world/terrain.js +253 -9
  320. package/dist/world/voxelField.d.ts +54 -0
  321. package/dist/world/voxelField.js +241 -0
  322. package/dist/world/water.d.ts +7 -0
  323. package/dist/world/water.js +19 -4
  324. package/dist/world/weather.d.ts +3 -18
  325. package/dist/world/weather.js +4 -17
  326. package/dist/world/windZones.d.ts +47 -0
  327. package/dist/world/windZones.js +59 -0
  328. package/llms.txt +4983 -0
  329. package/package.json +14 -3
@@ -80,7 +80,7 @@ export function advanceSpawnDirector(config, state, dt, ctx) {
80
80
  let done = state.done;
81
81
  const alert = Math.max(0, state.alert - alertDecay * dt);
82
82
  const trickle = config.waves[wave]?.budgetPerSecond ?? 0;
83
- const escalation = (config.escalationPerSecond ?? 0) * elapsed;
83
+ const escalation = config.escalationPerSecond ?? 0;
84
84
  const alertBonus = (config.alertBudgetPerSecond ?? 0) * alert;
85
85
  const playerBonus = (config.playerBudgetPerSecond ?? 0) * players;
86
86
  budget += (trickle + escalation + alertBonus + playerBonus) * dt;
@@ -106,13 +106,25 @@ export function advanceSpawnDirector(config, state, dt, ctx) {
106
106
  let spawnedTotal = state.spawnedTotal;
107
107
  let alive = ctx.alive;
108
108
  const entries = config.waves[wave]?.entries ?? [];
109
+ const spawnPoints = config.spawnPoints;
110
+ const playerPositions = ctx.playerPositions ?? [];
109
111
  while (spawns.length < maxSpawns && alive < maxAlive) {
110
112
  const picked = pickEntry(entries, wave, budget, rng);
111
113
  if (picked === null)
112
114
  break;
113
115
  rng = picked.rng;
114
116
  budget -= picked.entry.cost;
115
- spawns.push({ entryId: picked.entry.id, cost: picked.entry.cost, wave });
117
+ const spawn = { entryId: picked.entry.id, cost: picked.entry.cost, wave };
118
+ if (spawnPoints !== undefined && spawnPoints.length > 0) {
119
+ const [roll, nextRng] = nextRandom(rng);
120
+ rng = nextRng;
121
+ const point = pickSpawnPoint(spawnPoints, playerPositions, { roll, bias: config.spawnPointBias });
122
+ if (point !== null) {
123
+ spawn.point = point;
124
+ spawn.laneId = spawnPoints.indexOf(point);
125
+ }
126
+ }
127
+ spawns.push(spawn);
116
128
  spawnedThisWave += 1;
117
129
  spawnedTotal += 1;
118
130
  alive += 1;
@@ -18,6 +18,8 @@ export interface ThreatTable {
18
18
  decay(dt: number): void;
19
19
  highest(options?: HighestThreatOptions): string | null;
20
20
  ranked(): ThreatEntry[];
21
+ taunt(sourceId: string, durationSeconds: number): void;
22
+ forcedTarget(): string | null;
21
23
  remove(sourceId: string): void;
22
24
  clear(): void;
23
25
  size(): number;
package/dist/ai/threat.js CHANGED
@@ -4,6 +4,8 @@ export function createThreatTable(config = {}) {
4
4
  const max = config.max ?? Number.POSITIVE_INFINITY;
5
5
  const forgetBelow = config.forgetBelow ?? DEFAULT_FORGET_BELOW;
6
6
  const table = new Map();
7
+ let forcedSource = null;
8
+ let forcedRemaining = 0;
7
9
  function store(sourceId, value) {
8
10
  const clamped = Math.min(max, value);
9
11
  if (clamped <= forgetBelow) {
@@ -13,6 +15,16 @@ export function createThreatTable(config = {}) {
13
15
  table.set(sourceId, clamped);
14
16
  return clamped;
15
17
  }
18
+ function topThreat() {
19
+ let best = 0;
20
+ for (const value of table.values())
21
+ if (value > best)
22
+ best = value;
23
+ return best;
24
+ }
25
+ function forcedTarget() {
26
+ return forcedRemaining > 0 ? forcedSource : null;
27
+ }
16
28
  return {
17
29
  add(sourceId, amount) {
18
30
  return store(sourceId, (table.get(sourceId) ?? 0) + amount);
@@ -24,6 +36,13 @@ export function createThreatTable(config = {}) {
24
36
  return table.get(sourceId) ?? 0;
25
37
  },
26
38
  decay(dt) {
39
+ if (dt > 0 && forcedRemaining > 0) {
40
+ forcedRemaining -= dt;
41
+ if (forcedRemaining <= 0) {
42
+ forcedRemaining = 0;
43
+ forcedSource = null;
44
+ }
45
+ }
27
46
  if (decayPerSecond <= 0 || dt <= 0)
28
47
  return;
29
48
  const loss = decayPerSecond * dt;
@@ -31,6 +50,9 @@ export function createThreatTable(config = {}) {
31
50
  store(sourceId, value - loss);
32
51
  },
33
52
  highest(options = {}) {
53
+ const forced = forcedTarget();
54
+ if (forced !== null)
55
+ return forced;
34
56
  let bestId = null;
35
57
  let bestThreat = Number.NEGATIVE_INFINITY;
36
58
  for (const [sourceId, value] of table) {
@@ -54,11 +76,23 @@ export function createThreatTable(config = {}) {
54
76
  .map(([sourceId, threat]) => ({ sourceId, threat }))
55
77
  .sort((a, b) => b.threat - a.threat || a.sourceId.localeCompare(b.sourceId));
56
78
  },
79
+ taunt(sourceId, durationSeconds) {
80
+ store(sourceId, Math.max(table.get(sourceId) ?? 0, topThreat()));
81
+ forcedSource = sourceId;
82
+ forcedRemaining = Math.max(0, durationSeconds);
83
+ },
84
+ forcedTarget,
57
85
  remove(sourceId) {
58
86
  table.delete(sourceId);
87
+ if (forcedSource === sourceId) {
88
+ forcedSource = null;
89
+ forcedRemaining = 0;
90
+ }
59
91
  },
60
92
  clear() {
61
93
  table.clear();
94
+ forcedSource = null;
95
+ forcedRemaining = 0;
62
96
  },
63
97
  size() {
64
98
  return table.size;
@@ -0,0 +1,14 @@
1
+ export type Easing = (t: number) => number;
2
+ export declare function lerp(from: number, to: number, t: number): number;
3
+ export declare function clamp01(t: number): number;
4
+ export declare function smoothstep(t: number): number;
5
+ export declare function easeInQuad(t: number): number;
6
+ export declare function easeOutQuad(t: number): number;
7
+ export declare function easeInOutQuad(t: number): number;
8
+ export declare function easeInCubic(t: number): number;
9
+ export declare function easeOutCubic(t: number): number;
10
+ export declare function easeInOutCubic(t: number): number;
11
+ export declare function easeOutBack(t: number): number;
12
+ export declare function easeOutElastic(t: number): number;
13
+ export declare function tween(from: number, to: number, t: number, easing?: Easing): number;
14
+ export declare function timedProgress(startedAt: number, now: number, durationMs: number): number;
@@ -0,0 +1,55 @@
1
+ export function lerp(from, to, t) {
2
+ return from + (to - from) * t;
3
+ }
4
+ export function clamp01(t) {
5
+ return t < 0 ? 0 : t > 1 ? 1 : t;
6
+ }
7
+ export function smoothstep(t) {
8
+ const c = clamp01(t);
9
+ return c * c * (3 - 2 * c);
10
+ }
11
+ export function easeInQuad(t) {
12
+ const c = clamp01(t);
13
+ return c * c;
14
+ }
15
+ export function easeOutQuad(t) {
16
+ const c = clamp01(t);
17
+ return 1 - (1 - c) * (1 - c);
18
+ }
19
+ export function easeInOutQuad(t) {
20
+ const c = clamp01(t);
21
+ return c < 0.5 ? 2 * c * c : 1 - ((-2 * c + 2) ** 2) / 2;
22
+ }
23
+ export function easeInCubic(t) {
24
+ const c = clamp01(t);
25
+ return c * c * c;
26
+ }
27
+ export function easeOutCubic(t) {
28
+ const c = clamp01(t);
29
+ return 1 - (1 - c) ** 3;
30
+ }
31
+ export function easeInOutCubic(t) {
32
+ const c = clamp01(t);
33
+ return c < 0.5 ? 4 * c * c * c : 1 - ((-2 * c + 2) ** 3) / 2;
34
+ }
35
+ const BACK_OVERSHOOT = 1.70158;
36
+ export function easeOutBack(t) {
37
+ const c = clamp01(t);
38
+ const s = BACK_OVERSHOOT + 1;
39
+ return 1 + s * (c - 1) ** 3 + BACK_OVERSHOOT * (c - 1) ** 2;
40
+ }
41
+ const ELASTIC_PERIOD = (2 * Math.PI) / 3;
42
+ export function easeOutElastic(t) {
43
+ const c = clamp01(t);
44
+ if (c === 0 || c === 1)
45
+ return c;
46
+ return 2 ** (-10 * c) * Math.sin((c * 10 - 0.75) * ELASTIC_PERIOD) + 1;
47
+ }
48
+ export function tween(from, to, t, easing = smoothstep) {
49
+ return lerp(from, to, easing(clamp01(t)));
50
+ }
51
+ export function timedProgress(startedAt, now, durationMs) {
52
+ if (durationMs <= 0)
53
+ return now >= startedAt ? 1 : 0;
54
+ return clamp01((now - startedAt) / durationMs);
55
+ }
@@ -0,0 +1,136 @@
1
+ /**
2
+ * Unity-style behaviour lifecycle over a headless id-keyed node tree, ported from
3
+ * vladkrutenyuk/three-start (MIT) and adapted to run without three.js: nodes are plain
4
+ * string ids (use entity instance ids to pair with `entityStore`), and the shell binds
5
+ * render-side hooks separately.
6
+ *
7
+ * Lifecycle per behaviour: `onAwake` (once, first activation) → `onEnable` (every
8
+ * inactive→active transition) → `onStart` (once, after the first `onEnable`) →
9
+ * `onUpdate` (per `world.update` while active) → `onDisable` (every active→inactive
10
+ * transition) → `onDestroy` (once, always — even if never activated).
11
+ *
12
+ * `world.start()` bootstraps in passes: modules awake → modules start → modules join
13
+ * update dispatch → ALL behaviours on effectively-active nodes awake (even disabled
14
+ * ones) → enable/start/subscribe. Behaviours attached during the module pass are
15
+ * deferred to the behaviour passes, so module `onUpdate` always dispatches before any
16
+ * behaviour `onUpdate`.
17
+ */
18
+ /**
19
+ * Declaration-merging registry games and adapters augment for typed access to
20
+ * world modules:
21
+ *
22
+ * ```ts
23
+ * declare module "@jgengine/core/behaviour/behaviour" {
24
+ * interface JGEngineRegister {
25
+ * modules: { physics: PhysicsModule; director: DirectorModule };
26
+ * }
27
+ * }
28
+ * ```
29
+ */
30
+ export interface JGEngineRegister {
31
+ }
32
+ export type RegisterField<TKey extends string, TFallback = unknown> = TKey extends keyof JGEngineRegister ? JGEngineRegister[TKey] : TFallback;
33
+ /** Resolves to the augmented module map when `JGEngineRegister` declares one. */
34
+ export type BehaviourModules = RegisterField<"modules", Record<string, BehaviourModule>>;
35
+ interface UpdateSubscriber {
36
+ onUpdate(dt: number): void;
37
+ }
38
+ interface BehaviourWorldInternal extends BehaviourWorld {
39
+ _subscribeUpdate(subscriber: UpdateSubscriber): void;
40
+ _unsubscribeUpdate(subscriber: UpdateSubscriber): void;
41
+ _isEffectivelyActive(nodeId: string): boolean;
42
+ _detachBehaviour(behaviour: Behaviour): void;
43
+ }
44
+ /**
45
+ * Subclass and override the lifecycle hooks. A behaviour only joins the per-frame
46
+ * update dispatch if it actually overrides `onUpdate` (prototype-identity check at
47
+ * each activation), so hook-only behaviours cost nothing per frame.
48
+ */
49
+ export declare class Behaviour {
50
+ /** @internal */ _world: BehaviourWorldInternal | null;
51
+ /** @internal */ _nodeId: string | null;
52
+ /** @internal */ _awoken: boolean;
53
+ /** @internal */ _startedOnce: boolean;
54
+ /** @internal */ _isActive: boolean;
55
+ private _enabled;
56
+ private _destroyed;
57
+ /** The node this behaviour is attached to; available from `onAwake` onward, never in the constructor. */
58
+ get nodeId(): string;
59
+ get world(): BehaviourWorld;
60
+ get modules(): BehaviourModules;
61
+ /** Own enabled flag; effective activity also requires the node's hierarchy to be active. */
62
+ get enabled(): boolean;
63
+ /** Whether the lifecycle is currently in the active span (`onEnable` fired, `onDisable` not yet). */
64
+ get isActive(): boolean;
65
+ onAwake(): void;
66
+ onEnable(): void;
67
+ onStart(): void;
68
+ onUpdate(_dt: number): void;
69
+ onDisable(): void;
70
+ onDestroy(): void;
71
+ enable(): void;
72
+ disable(): void;
73
+ /** Flips the flag only when the node is hierarchy-inactive or the world hasn't started; lifecycle catches up on the next activation. */
74
+ setEnabled(enabled: boolean): void;
75
+ /** Detaches from the node and fires `onDisable` (if active) then `onDestroy`. */
76
+ destroy(): void;
77
+ /** @internal */
78
+ _activate(): void;
79
+ /** @internal */
80
+ _deactivate(): void;
81
+ /** @internal */
82
+ _destroy(): void;
83
+ }
84
+ /**
85
+ * A world-lifetime service with typed sibling access via `this.modules`. Modules awake
86
+ * and start before any behaviour during `world.start()`, subscribe to update dispatch
87
+ * first (their `onUpdate` fires before every behaviour's), and have no disable/destroy —
88
+ * they live as long as the world.
89
+ */
90
+ export declare class BehaviourModule {
91
+ /** @internal */ _world: BehaviourWorldInternal | null;
92
+ get world(): BehaviourWorld;
93
+ get modules(): BehaviourModules;
94
+ onAwake(): void;
95
+ onStart(): void;
96
+ onUpdate(_dt: number): void;
97
+ /** @internal */
98
+ _subscribe(): void;
99
+ }
100
+ export interface BehaviourWorld {
101
+ /** Creates the node if missing (parented under `parentId` when given, else a root); reparents an existing node when `parentId` differs. */
102
+ add(id: string, parentId?: string): void;
103
+ has(id: string): boolean;
104
+ parentOf(id: string): string | null;
105
+ childrenOf(id: string): readonly string[];
106
+ /** Moves `id` (and subtree) under `parentId` (`null` = root), firing enable/disable cascades when effective activity changes. Throws on cycles. */
107
+ setParent(id: string, parentId: string | null): void;
108
+ /** Attaches and immediately activates when the world has started, the node is effectively active, and the behaviour is enabled (deferred during bootstrap). */
109
+ attach<T extends Behaviour>(nodeId: string, behaviour: T): T;
110
+ behavioursOf(nodeId: string): readonly Behaviour[];
111
+ /**
112
+ * Sets the node's own flag and cascades activation/deactivation through the subtree,
113
+ * pruning at descendants whose own flag is false — so reactivating a parent restores
114
+ * exactly the per-child stored flags. A flip under an inactive ancestor changes only
115
+ * the flag.
116
+ */
117
+ setActive(id: string, active: boolean): void;
118
+ /** The node's own flag; unknown ids count active. */
119
+ isActiveSelf(id: string): boolean;
120
+ /** Own flag AND every ancestor's; unknown ids count active. */
121
+ isActive(id: string): boolean;
122
+ /** Registers modules by key. Throws on duplicate keys or after `start()`. */
123
+ addModules(modules: Partial<BehaviourModules>): void;
124
+ readonly modules: BehaviourModules;
125
+ /** Bootstraps: modules awake/start/subscribe, then all behaviours on effectively-active nodes awake, then enable+start+subscribe. Idempotent. */
126
+ start(): void;
127
+ started(): boolean;
128
+ /** Dispatches `onUpdate(dt)` to modules then behaviours, over a snapshot — subscribers added mid-dispatch first fire next update. */
129
+ update(dt: number): void;
130
+ /** Destroys every behaviour in the subtree (parent-first) and removes the nodes. */
131
+ destroyNode(id: string): boolean;
132
+ /** Destroys all nodes and behaviours and clears update dispatch. */
133
+ destroy(): void;
134
+ }
135
+ export declare function createBehaviourWorld(): BehaviourWorld;
136
+ export {};