@v12-engine/engine 0.1.1 → 0.4.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.
- package/dist/abilities/amounts.d.ts +28 -0
- package/dist/abilities/amounts.js +74 -0
- package/dist/abilities/amounts.js.map +1 -0
- package/dist/abilities/buffs.d.ts +106 -6
- package/dist/abilities/buffs.js +443 -0
- package/dist/abilities/buffs.js.map +1 -0
- package/dist/abilities/castTold.d.ts +2 -0
- package/dist/abilities/castTold.js +66 -0
- package/dist/abilities/castTold.js.map +1 -0
- package/dist/abilities/chance.d.ts +38 -0
- package/dist/abilities/chance.js +77 -0
- package/dist/abilities/chance.js.map +1 -0
- package/dist/abilities/charges.d.ts +18 -0
- package/dist/abilities/charges.js +101 -0
- package/dist/abilities/charges.js.map +1 -0
- package/dist/abilities/control.d.ts +28 -0
- package/dist/abilities/control.js +113 -0
- package/dist/abilities/control.js.map +1 -0
- package/dist/abilities/dispel.js +51 -0
- package/dist/abilities/dispel.js.map +1 -0
- package/dist/abilities/effectFields.d.ts +1 -1
- package/dist/abilities/effectFields.js +676 -0
- package/dist/abilities/effectFields.js.map +1 -0
- package/dist/abilities/effectIndex.d.ts +27 -0
- package/dist/abilities/effectIndex.js +64 -0
- package/dist/abilities/effectIndex.js.map +1 -0
- package/dist/abilities/effects/buff.d.ts +9 -3
- package/dist/abilities/effects/buff.js +84 -0
- package/dist/abilities/effects/buff.js.map +1 -0
- package/dist/abilities/effects/conditions.d.ts +2 -0
- package/dist/abilities/effects/conditions.js +41 -0
- package/dist/abilities/effects/conditions.js.map +1 -0
- package/dist/abilities/effects/core.js +54 -0
- package/dist/abilities/effects/core.js.map +1 -0
- package/dist/abilities/effects/damage.d.ts +1 -1
- package/dist/abilities/effects/damage.js +43 -0
- package/dist/abilities/effects/damage.js.map +1 -0
- package/dist/abilities/effects/heal.d.ts +2 -2
- package/dist/abilities/effects/heal.js +51 -0
- package/dist/abilities/effects/heal.js.map +1 -0
- package/dist/abilities/effects/missile.d.ts +13 -1
- package/dist/abilities/effects/missile.js +104 -0
- package/dist/abilities/effects/missile.js.map +1 -0
- package/dist/abilities/effects/morph.d.ts +5 -0
- package/dist/abilities/effects/morph.js +43 -0
- package/dist/abilities/effects/morph.js.map +1 -0
- package/dist/abilities/effects/moves.d.ts +16 -4
- package/dist/abilities/effects/moves.js +212 -0
- package/dist/abilities/effects/moves.js.map +1 -0
- package/dist/abilities/effects/passives.d.ts +19 -9
- package/dist/abilities/effects/passives.js +131 -0
- package/dist/abilities/effects/passives.js.map +1 -0
- package/dist/abilities/effects/proxy.d.ts +5 -0
- package/dist/abilities/effects/proxy.js +60 -0
- package/dist/abilities/effects/proxy.js.map +1 -0
- package/dist/abilities/effects/sight.d.ts +2 -2
- package/dist/abilities/effects/sight.js +39 -0
- package/dist/abilities/effects/sight.js.map +1 -0
- package/dist/abilities/effects/spawn.d.ts +5 -2
- package/dist/abilities/effects/spawn.js +40 -0
- package/dist/abilities/effects/spawn.js.map +1 -0
- package/dist/abilities/effects/timed.d.ts +2 -2
- package/dist/abilities/effects/timed.js +96 -0
- package/dist/abilities/effects/timed.js.map +1 -0
- package/dist/abilities/effects/trade.d.ts +1 -1
- package/dist/abilities/effects/trade.js +25 -0
- package/dist/abilities/effects/trade.js.map +1 -0
- package/dist/abilities/effects.d.ts +97 -23
- package/dist/abilities/effects.js +860 -0
- package/dist/abilities/effects.js.map +1 -0
- package/dist/abilities/land.d.ts +38 -4
- package/dist/abilities/land.js +189 -0
- package/dist/abilities/land.js.map +1 -0
- package/dist/abilities/recast.d.ts +45 -0
- package/dist/abilities/recast.js +118 -0
- package/dist/abilities/recast.js.map +1 -0
- package/dist/abilities/scaled.js +40 -0
- package/dist/abilities/scaled.js.map +1 -0
- package/dist/abilities/screen.d.ts +6 -0
- package/dist/abilities/screen.js +83 -0
- package/dist/abilities/screen.js.map +1 -0
- package/dist/abilities/shapes.d.ts +66 -0
- package/dist/abilities/shapes.js +122 -0
- package/dist/abilities/shapes.js.map +1 -0
- package/dist/abilities/text.d.ts +14 -2
- package/dist/abilities/text.js +204 -0
- package/dist/abilities/text.js.map +1 -0
- package/dist/abilities/triggers.d.ts +98 -0
- package/dist/abilities/triggers.js +351 -0
- package/dist/abilities/triggers.js.map +1 -0
- package/dist/abilityChanges.d.ts +131 -0
- package/dist/abilityChanges.js +345 -0
- package/dist/abilityChanges.js.map +1 -0
- package/dist/ai/area.d.ts +4 -1
- package/dist/ai/brain.d.ts +4 -1
- package/dist/ai/brain.js +844 -0
- package/dist/ai/brain.js.map +1 -0
- package/dist/ai/budget.js +74 -0
- package/dist/ai/budget.js.map +1 -0
- package/dist/ai/checks.d.ts +9 -1
- package/dist/ai/checks.js +242 -0
- package/dist/ai/checks.js.map +1 -0
- package/dist/ai/computer.d.ts +11 -4
- package/dist/ai/computer.js +251 -0
- package/dist/ai/computer.js.map +1 -0
- package/dist/ai/defence.d.ts +4 -0
- package/dist/ai/defence.js +430 -0
- package/dist/ai/defence.js.map +1 -0
- package/dist/ai/define.d.ts +3 -2
- package/dist/ai/define.js +31 -0
- package/dist/ai/define.js.map +1 -0
- package/dist/ai/economy.d.ts +10 -7
- package/dist/ai/economy.js +780 -0
- package/dist/ai/economy.js.map +1 -0
- package/dist/ai/geometry.js +38 -0
- package/dist/ai/geometry.js.map +1 -0
- package/dist/ai/goals.d.ts +1 -0
- package/dist/ai/goals.js +560 -0
- package/dist/ai/goals.js.map +1 -0
- package/dist/ai/knowledge.d.ts +72 -7
- package/dist/ai/knowledge.js +804 -0
- package/dist/ai/knowledge.js.map +1 -0
- package/dist/ai/lanes.d.ts +10 -2
- package/dist/ai/lanes.js +715 -0
- package/dist/ai/lanes.js.map +1 -0
- package/dist/ai/melee/build.js +264 -0
- package/dist/ai/melee/build.js.map +1 -0
- package/dist/ai/melee/goals.d.ts +7 -0
- package/dist/ai/melee/goals.js +720 -0
- package/dist/ai/melee/goals.js.map +1 -0
- package/dist/ai/melee/heroes.js +68 -0
- package/dist/ai/melee/heroes.js.map +1 -0
- package/dist/ai/melee/index.d.ts +1 -1
- package/dist/ai/melee/index.js +51 -0
- package/dist/ai/melee/index.js.map +1 -0
- package/dist/ai/melee/production.js +29 -0
- package/dist/ai/melee/production.js.map +1 -0
- package/dist/ai/melee/surrender.js +64 -0
- package/dist/ai/melee/surrender.js.map +1 -0
- package/dist/ai/micro.d.ts +9 -0
- package/dist/ai/micro.js +1316 -0
- package/dist/ai/micro.js.map +1 -0
- package/dist/ai/mission.d.ts +9 -2
- package/dist/ai/mission.js +538 -0
- package/dist/ai/mission.js.map +1 -0
- package/dist/ai/missionRules.d.ts +1 -1
- package/dist/ai/missionRules.js +26 -0
- package/dist/ai/missionRules.js.map +1 -0
- package/dist/ai/module.d.ts +6 -2
- package/dist/ai/module.js +37 -0
- package/dist/ai/module.js.map +1 -0
- package/dist/ai/options.d.ts +3 -3
- package/dist/ai/plans/build.d.ts +20 -1
- package/dist/ai/plans/build.js +608 -0
- package/dist/ai/plans/build.js.map +1 -0
- package/dist/ai/plans/placement.d.ts +4 -1
- package/dist/ai/plans/placement.js +295 -0
- package/dist/ai/plans/placement.js.map +1 -0
- package/dist/ai/plans/plans.d.ts +9 -0
- package/dist/ai/plans/plans.js +110 -0
- package/dist/ai/plans/plans.js.map +1 -0
- package/dist/ai/plans/production.d.ts +14 -2
- package/dist/ai/plans/production.js +434 -0
- package/dist/ai/plans/production.js.map +1 -0
- package/dist/ai/plans/purse.d.ts +6 -3
- package/dist/ai/plans/purse.js +139 -0
- package/dist/ai/plans/purse.js.map +1 -0
- package/dist/ai/plans/tech.d.ts +6 -0
- package/dist/ai/plans/tech.js +122 -0
- package/dist/ai/plans/tech.js.map +1 -0
- package/dist/ai/profiles.d.ts +4 -4
- package/dist/{profiles-B3NS9jwk.js → ai/profiles.js} +3 -98
- package/dist/ai/profiles.js.map +1 -0
- package/dist/ai/reach.js +54 -0
- package/dist/ai/reach.js.map +1 -0
- package/dist/ai/report.d.ts +8 -0
- package/dist/ai/report.js +87 -0
- package/dist/ai/report.js.map +1 -0
- package/dist/ai/roles.d.ts +4 -0
- package/dist/ai/roles.js +207 -0
- package/dist/ai/roles.js.map +1 -0
- package/dist/ai/rules.d.ts +8 -2
- package/dist/ai/rules.js +416 -0
- package/dist/ai/rules.js.map +1 -0
- package/dist/ai/scouting.d.ts +6 -0
- package/dist/ai/scouting.js +324 -0
- package/dist/ai/scouting.js.map +1 -0
- package/dist/ai/settings.js +99 -0
- package/dist/ai/settings.js.map +1 -0
- package/dist/ai/squads.d.ts +21 -2
- package/dist/ai/squads.js +916 -0
- package/dist/ai/squads.js.map +1 -0
- package/dist/ai/strength.js +320 -0
- package/dist/ai/strength.js.map +1 -0
- package/dist/ai/task.d.ts +3 -1
- package/dist/ai/task.js +17 -0
- package/dist/ai/task.js.map +1 -0
- package/dist/ai/tasks/attack.js +24 -0
- package/dist/ai/tasks/attack.js.map +1 -0
- package/dist/ai/tasks/capture.d.ts +3 -3
- package/dist/ai/tasks/capture.js +37 -0
- package/dist/ai/tasks/capture.js.map +1 -0
- package/dist/ai/tasks/creep.js +29 -0
- package/dist/ai/tasks/creep.js.map +1 -0
- package/dist/ai/tasks/defend.js +61 -0
- package/dist/ai/tasks/defend.js.map +1 -0
- package/dist/ai/tasks/drop.js +81 -0
- package/dist/ai/tasks/drop.js.map +1 -0
- package/dist/ai/tasks/escort.js +59 -0
- package/dist/ai/tasks/escort.js.map +1 -0
- package/dist/ai/tasks/gather.js +48 -0
- package/dist/ai/tasks/gather.js.map +1 -0
- package/dist/ai/tasks/harass.js +39 -0
- package/dist/ai/tasks/harass.js.map +1 -0
- package/dist/ai/tasks/hold.js +32 -0
- package/dist/ai/tasks/hold.js.map +1 -0
- package/dist/ai/tasks/index.js +34 -0
- package/dist/ai/tasks/index.js.map +1 -0
- package/dist/ai/tasks/move.js +26 -0
- package/dist/ai/tasks/move.js.map +1 -0
- package/dist/ai/tasks/patrol.js +59 -0
- package/dist/ai/tasks/patrol.js.map +1 -0
- package/dist/ai/tasks/retreat.js +28 -0
- package/dist/ai/tasks/retreat.js.map +1 -0
- package/dist/ai/tasks/scout.js +73 -0
- package/dist/ai/tasks/scout.js.map +1 -0
- package/dist/ai/tasks/shared.d.ts +1 -1
- package/dist/ai/tasks/shared.js +137 -0
- package/dist/ai/tasks/shared.js.map +1 -0
- package/dist/ai/team.d.ts +18 -1
- package/dist/ai/team.js +492 -0
- package/dist/ai/team.js.map +1 -0
- package/dist/ai/threat.d.ts +6 -0
- package/dist/ai/threat.js +206 -0
- package/dist/ai/threat.js.map +1 -0
- package/dist/ai/view.d.ts +21 -7
- package/dist/ai/view.js +677 -0
- package/dist/ai/view.js.map +1 -0
- package/dist/ai/waves.d.ts +6 -0
- package/dist/ai/waves.js +410 -0
- package/dist/ai/waves.js.map +1 -0
- package/dist/ai.js +54 -6239
- package/dist/aiTesting.d.ts +6 -3
- package/dist/aiTesting.js +229 -0
- package/dist/aiTesting.js.map +1 -0
- package/dist/areas.d.ts +4 -1
- package/dist/areas.js +295 -0
- package/dist/areas.js.map +1 -0
- package/dist/attacks.d.ts +3 -3
- package/dist/attacks.js +127 -0
- package/dist/attacks.js.map +1 -0
- package/dist/burning.d.ts +267 -0
- package/dist/burning.js +795 -0
- package/dist/burning.js.map +1 -0
- package/dist/bytes.js +21 -0
- package/dist/bytes.js.map +1 -0
- package/dist/callings.d.ts +71 -0
- package/dist/callings.js +157 -0
- package/dist/callings.js.map +1 -0
- package/dist/cameraOrders.d.ts +78 -0
- package/dist/cameraOrders.js +114 -0
- package/dist/cameraOrders.js.map +1 -0
- package/dist/camps.d.ts +70 -4
- package/dist/camps.js +501 -0
- package/dist/camps.js.map +1 -0
- package/dist/capture.d.ts +15 -3
- package/dist/capture.js +304 -0
- package/dist/capture.js.map +1 -0
- package/dist/cellLayers.d.ts +78 -13
- package/dist/cellLayers.js +275 -0
- package/dist/cellLayers.js.map +1 -0
- package/dist/cellPatches.d.ts +24 -0
- package/dist/cellPatches.js +73 -0
- package/dist/cellPatches.js.map +1 -0
- package/dist/cinematics.d.ts +10 -5
- package/dist/cinematics.js +161 -0
- package/dist/cinematics.js.map +1 -0
- package/dist/clock.d.ts +2 -2
- package/dist/clock.js +78 -0
- package/dist/clock.js.map +1 -0
- package/dist/commands.d.ts +36 -4
- package/dist/commands.js +636 -0
- package/dist/commands.js.map +1 -0
- package/dist/components.d.ts +31 -10
- package/dist/components.js +74 -0
- package/dist/components.js.map +1 -0
- package/dist/computers.d.ts +4 -4
- package/dist/computers.js +194 -0
- package/dist/computers.js.map +1 -0
- package/dist/concealment.d.ts +25 -2
- package/dist/concealment.js +409 -0
- package/dist/concealment.js.map +1 -0
- package/dist/coreComponents.js +130 -0
- package/dist/coreComponents.js.map +1 -0
- package/dist/createGame.d.ts +306 -0
- package/dist/createGame.js +405 -0
- package/dist/createGame.js.map +1 -0
- package/dist/dashing.d.ts +18 -2
- package/dist/dashing.js +41 -0
- package/dist/dashing.js.map +1 -0
- package/dist/debug.d.ts +8 -0
- package/dist/debug.js +5 -3
- package/dist/debug2.js +76 -0
- package/dist/debug2.js.map +1 -0
- package/dist/debugCommands.d.ts +6 -6
- package/dist/debugCommands.js +537 -0
- package/dist/debugCommands.js.map +1 -0
- package/dist/debugging.d.ts +1 -0
- package/dist/defaultSets.d.ts +183 -0
- package/dist/defaultSets.js +216 -0
- package/dist/defaultSets.js.map +1 -0
- package/dist/definitions.d.ts +6 -0
- package/dist/definitions.js +78 -0
- package/dist/definitions.js.map +1 -0
- package/dist/defs.d.ts +1895 -165
- package/dist/defs.js +380 -0
- package/dist/defs.js.map +1 -0
- package/dist/denying.d.ts +4 -4
- package/dist/denying.js +106 -0
- package/dist/denying.js.map +1 -0
- package/dist/destructibles.d.ts +2 -2
- package/dist/destructibles.js +36 -0
- package/dist/destructibles.js.map +1 -0
- package/dist/determinism.d.ts +4 -4
- package/dist/determinism.js +247 -0
- package/dist/determinism.js.map +1 -0
- package/dist/dialog.d.ts +17 -2
- package/dist/dialog.js +108 -0
- package/dist/dialog.js.map +1 -0
- package/dist/effectRefs.js +28 -0
- package/dist/effectRefs.js.map +1 -0
- package/dist/effects.d.ts +27 -11
- package/dist/effects.js +110 -0
- package/dist/effects.js.map +1 -0
- package/dist/endings.d.ts +1 -1
- package/dist/endings.js +78 -0
- package/dist/endings.js.map +1 -0
- package/dist/engagement.d.ts +40 -6
- package/dist/engagement.js +530 -0
- package/dist/engagement.js.map +1 -0
- package/dist/engineEvents.d.ts +72 -9
- package/dist/engineEvents.js +520 -0
- package/dist/engineEvents.js.map +1 -0
- package/dist/entity.d.ts +275 -26
- package/dist/entity.js +441 -0
- package/dist/entity.js.map +1 -0
- package/dist/entityIndex.d.ts +10 -3
- package/dist/entityIndex.js +149 -0
- package/dist/entityIndex.js.map +1 -0
- package/dist/errors.d.ts +6 -2
- package/dist/errors.js +59 -0
- package/dist/errors.js.map +1 -0
- package/dist/events.d.ts +11 -5
- package/dist/events.js +158 -0
- package/dist/events.js.map +1 -0
- package/dist/exactMath.js +273 -0
- package/dist/exactMath.js.map +1 -0
- package/dist/features.d.ts +1 -1
- package/dist/features.js +127 -0
- package/dist/features.js.map +1 -0
- package/dist/fields.d.ts +25 -5
- package/dist/fields.js +140 -0
- package/dist/fields.js.map +1 -0
- package/dist/fixture.d.ts +3 -3
- package/dist/fixture.js +512 -0
- package/dist/fixture.js.map +1 -0
- package/dist/fog.d.ts +26 -3
- package/dist/fog.js +333 -0
- package/dist/fog.js.map +1 -0
- package/dist/foliage.d.ts +476 -0
- package/dist/foliage.js +1478 -0
- package/dist/foliage.js.map +1 -0
- package/dist/foliageDebug.d.ts +57 -0
- package/dist/foliageDebug.js +172 -0
- package/dist/foliageDebug.js.map +1 -0
- package/dist/foliageRegrowth.d.ts +20 -0
- package/dist/foliageRegrowth.js +332 -0
- package/dist/foliageRegrowth.js.map +1 -0
- package/dist/formState.d.ts +67 -0
- package/dist/formState.js +65 -0
- package/dist/formState.js.map +1 -0
- package/dist/formation.d.ts +46 -8
- package/dist/formation.js +297 -0
- package/dist/formation.js.map +1 -0
- package/dist/forms.d.ts +58 -0
- package/dist/forms.js +146 -0
- package/dist/forms.js.map +1 -0
- package/dist/friendlyFire.d.ts +2 -2
- package/dist/friendlyFire.js +37 -0
- package/dist/friendlyFire.js.map +1 -0
- package/dist/game.d.ts +141 -48
- package/dist/game.js +834 -0
- package/dist/game.js.map +1 -0
- package/dist/gameData.d.ts +96 -22
- package/dist/gameData.js +1690 -0
- package/dist/gameData.js.map +1 -0
- package/dist/gameFiles.d.ts +3 -3
- package/dist/gameFiles.js +14 -0
- package/dist/gameFiles.js.map +1 -0
- package/dist/gates.js +92 -0
- package/dist/gates.js.map +1 -0
- package/dist/generate.d.ts +28 -1
- package/dist/generate.js +1062 -0
- package/dist/generate.js.map +1 -0
- package/dist/generateWorker.d.ts +7 -3
- package/dist/generateWorker.js +86 -0
- package/dist/generateWorker.js.map +1 -0
- package/dist/grid.d.ts +9 -0
- package/dist/grid.js +102 -0
- package/dist/grid.js.map +1 -0
- package/dist/ground.d.ts +17 -1
- package/dist/ground.js +375 -0
- package/dist/ground.js.map +1 -0
- package/dist/groundObjects.d.ts +70 -0
- package/dist/groundObjects.js +371 -0
- package/dist/groundObjects.js.map +1 -0
- package/dist/groundSpeed.d.ts +20 -3
- package/dist/groundSpeed.js +117 -0
- package/dist/groundSpeed.js.map +1 -0
- package/dist/hero.d.ts +126 -10
- package/dist/hero.js +432 -0
- package/dist/hero.js.map +1 -0
- package/dist/holds.d.ts +94 -0
- package/dist/holds.js +305 -0
- package/dist/holds.js.map +1 -0
- package/dist/home.d.ts +1 -1
- package/dist/home.js +30 -0
- package/dist/home.js.map +1 -0
- package/dist/hooks.d.ts +339 -57
- package/dist/hooks.js +105 -0
- package/dist/hooks.js.map +1 -0
- package/dist/hotReload.d.ts +6 -5
- package/dist/hotReload.js +145 -0
- package/dist/hotReload.js.map +1 -0
- package/dist/ids.d.ts +3 -3
- package/dist/ids.js +30 -0
- package/dist/ids.js.map +1 -0
- package/dist/idsFile.d.ts +36 -0
- package/dist/idsFile.js +134 -0
- package/dist/idsFile.js.map +1 -0
- package/dist/index.d.ts +105 -68
- package/dist/index.js +194 -4701
- package/dist/inspect.d.ts +29 -2
- package/dist/{inspect-D9dWHmZ0.js → inspect.js} +21 -12
- package/dist/inspect.js.map +1 -0
- package/dist/inventory.d.ts +14 -1
- package/dist/inventory.js +84 -0
- package/dist/inventory.js.map +1 -0
- package/dist/items.d.ts +136 -8
- package/dist/items.js +585 -0
- package/dist/items.js.map +1 -0
- package/dist/lanes.d.ts +12 -3
- package/dist/lanes.js +496 -0
- package/dist/lanes.js.map +1 -0
- package/dist/life.d.ts +6 -2
- package/dist/life.js +287 -0
- package/dist/life.js.map +1 -0
- package/dist/log.d.ts +26 -3
- package/dist/log.js +192 -0
- package/dist/log.js.map +1 -0
- package/dist/manaBurn.d.ts +2 -2
- package/dist/manaBurn.js +75 -0
- package/dist/manaBurn.js.map +1 -0
- package/dist/map/file.d.ts +56 -3
- package/dist/map/file.js +264 -0
- package/dist/map/file.js.map +1 -0
- package/dist/map/images.js +61 -0
- package/dist/map/images.js.map +1 -0
- package/dist/map/look.d.ts +1 -1
- package/dist/map/look.js +30 -0
- package/dist/map/look.js.map +1 -0
- package/dist/map/pathProfile.js +183 -0
- package/dist/map/pathProfile.js.map +1 -0
- package/dist/map/resize.d.ts +4 -0
- package/dist/map/resize.js +136 -0
- package/dist/map/resize.js.map +1 -0
- package/dist/map/schema.js +257 -0
- package/dist/map/schema.js.map +1 -0
- package/dist/map/terrain.js +214 -0
- package/dist/map/terrain.js.map +1 -0
- package/dist/map/types.d.ts +92 -13
- package/dist/map/types.js +251 -0
- package/dist/map/types.js.map +1 -0
- package/dist/mapCheck/cells.js +94 -0
- package/dist/mapCheck/cells.js.map +1 -0
- package/dist/mapCheck/rules/foliage.d.ts +9 -0
- package/dist/mapCheck/rules/foliage.js +83 -0
- package/dist/mapCheck/rules/foliage.js.map +1 -0
- package/dist/mapCheck/rules/names.js +39 -0
- package/dist/mapCheck/rules/names.js.map +1 -0
- package/dist/mapCheck/rules/objects.d.ts +1 -1
- package/dist/mapCheck/rules/objects.js +56 -0
- package/dist/mapCheck/rules/objects.js.map +1 -0
- package/dist/mapCheck/rules/paths.js +32 -0
- package/dist/mapCheck/rules/paths.js.map +1 -0
- package/dist/mapCheck/rules/players.js +20 -0
- package/dist/mapCheck/rules/players.js.map +1 -0
- package/dist/mapCheck/rules/races.js +56 -0
- package/dist/mapCheck/rules/races.js.map +1 -0
- package/dist/mapCheck/rules/random.js +47 -0
- package/dist/mapCheck/rules/random.js.map +1 -0
- package/dist/mapCheck/rules/regions.js +30 -0
- package/dist/mapCheck/rules/regions.js.map +1 -0
- package/dist/mapCheck/rules/targets.js +31 -0
- package/dist/mapCheck/rules/targets.js.map +1 -0
- package/dist/mapCheck/rules/walking.js +45 -0
- package/dist/mapCheck/rules/walking.js.map +1 -0
- package/dist/mapCheck.d.ts +2 -2
- package/dist/mapCheck.js +63 -0
- package/dist/mapCheck.js.map +1 -0
- package/dist/mapFields.d.ts +25 -2
- package/dist/mapFields.js +134 -0
- package/dist/mapFields.js.map +1 -0
- package/dist/mapFile.js +176 -0
- package/dist/mapFile.js.map +1 -0
- package/dist/mapMeasure.d.ts +8 -0
- package/dist/mapMeasure.js +283 -0
- package/dist/mapMeasure.js.map +1 -0
- package/dist/mapMerge.d.ts +1 -0
- package/dist/mapMerge.js +172 -0
- package/dist/mapMerge.js.map +1 -0
- package/dist/mapObjectives.js +138 -0
- package/dist/mapObjectives.js.map +1 -0
- package/dist/mapObjects.js +80 -0
- package/dist/mapObjects.js.map +1 -0
- package/dist/math.d.ts +37 -7
- package/dist/math.js +155 -0
- package/dist/math.js.map +1 -0
- package/dist/messages.d.ts +71 -4
- package/dist/messages.js +29 -0
- package/dist/messages.js.map +1 -0
- package/dist/missionRules.d.ts +10 -2
- package/dist/{missionRules-C-bGxm0Q.js → missionRules.js} +30 -150
- package/dist/missionRules.js.map +1 -0
- package/dist/mods.d.ts +39 -20
- package/dist/mods.js +271 -0
- package/dist/mods.js.map +1 -0
- package/dist/moves.d.ts +87 -0
- package/dist/moves.js +539 -0
- package/dist/moves.js.map +1 -0
- package/dist/names.d.ts +7 -0
- package/dist/names.js +122 -0
- package/dist/names.js.map +1 -0
- package/dist/navigation/agentParams.js +14 -0
- package/dist/navigation/agentParams.js.map +1 -0
- package/dist/navigation/air.d.ts +7 -3
- package/dist/navigation/air.js +401 -0
- package/dist/navigation/air.js.map +1 -0
- package/dist/navigation/builds.d.ts +34 -2
- package/dist/navigation/builds.js +287 -0
- package/dist/navigation/builds.js.map +1 -0
- package/dist/navigation/groundAreas.d.ts +59 -1
- package/dist/navigation/groundAreas.js +407 -0
- package/dist/navigation/groundAreas.js.map +1 -0
- package/dist/navigation/navigation.d.ts +98 -9
- package/dist/navigation/recast.d.ts +194 -18
- package/dist/navigation/recast.js +1379 -0
- package/dist/navigation/recast.js.map +1 -0
- package/dist/navigation/sidePaths.d.ts +8 -3
- package/dist/navigation/sidePaths.js +241 -0
- package/dist/navigation/sidePaths.js.map +1 -0
- package/dist/navigation/sized.js +243 -0
- package/dist/navigation/sized.js.map +1 -0
- package/dist/nearest.js +32 -0
- package/dist/nearest.js.map +1 -0
- package/dist/noise.js +59 -0
- package/dist/noise.js.map +1 -0
- package/dist/objectives.d.ts +12 -3
- package/dist/objectives.js +144 -0
- package/dist/objectives.js.map +1 -0
- package/dist/orders/basic.d.ts +5 -5
- package/dist/orders/basic.js +417 -0
- package/dist/orders/basic.js.map +1 -0
- package/dist/orders/build.js +278 -0
- package/dist/orders/build.js.map +1 -0
- package/dist/orders/cast.d.ts +13 -3
- package/dist/orders/cast.js +558 -0
- package/dist/orders/cast.js.map +1 -0
- package/dist/orders/facts.js +16 -0
- package/dist/orders/facts.js.map +1 -0
- package/dist/orders/harvest.d.ts +21 -5
- package/dist/orders/harvest.js +540 -0
- package/dist/orders/harvest.js.map +1 -0
- package/dist/orders/idle.d.ts +3 -1
- package/dist/orders/idle.js +234 -0
- package/dist/orders/idle.js.map +1 -0
- package/dist/orders/items.js +92 -0
- package/dist/orders/items.js.map +1 -0
- package/dist/orders/queue.js +108 -0
- package/dist/orders/queue.js.map +1 -0
- package/dist/orders/waiting.d.ts +39 -0
- package/dist/orders/waiting.js +149 -0
- package/dist/orders/waiting.js.map +1 -0
- package/dist/pack.schema.js +13 -0
- package/dist/pack.schema.js.map +1 -0
- package/dist/pack.schema.json +7622 -841
- package/dist/packs/director/index.d.ts +13 -5
- package/dist/packs/director/index.js +204 -0
- package/dist/packs/director/index.js.map +1 -0
- package/dist/packs/heroes/campaign.d.ts +3 -2
- package/dist/packs/heroes/campaign.js +241 -0
- package/dist/packs/heroes/campaign.js.map +1 -0
- package/dist/packs/heroes/carry.d.ts +6 -0
- package/dist/packs/heroes/carry.js +112 -0
- package/dist/packs/heroes/carry.js.map +1 -0
- package/dist/packs/heroes/commands.js +115 -0
- package/dist/packs/heroes/commands.js.map +1 -0
- package/dist/packs/heroes/fallen.d.ts +43 -0
- package/dist/packs/heroes/fallen.js +61 -0
- package/dist/packs/heroes/fallen.js.map +1 -0
- package/dist/packs/heroes/hire.js +60 -0
- package/dist/packs/heroes/hire.js.map +1 -0
- package/dist/packs/heroes/index.d.ts +7 -6
- package/dist/packs/heroes/index.js +130 -0
- package/dist/packs/heroes/index.js.map +1 -0
- package/dist/packs/heroes/loop.d.ts +95 -0
- package/dist/packs/heroes/loop.js +250 -0
- package/dist/packs/heroes/loop.js.map +1 -0
- package/dist/packs/heroes/making.d.ts +6 -3
- package/dist/packs/heroes/making.js +55 -0
- package/dist/packs/heroes/making.js.map +1 -0
- package/dist/packs/heroes/respawn.d.ts +3 -3
- package/dist/packs/heroes/respawn.js +87 -0
- package/dist/packs/heroes/respawn.js.map +1 -0
- package/dist/packs/heroes/sets.d.ts +32 -0
- package/dist/packs/heroes/sets.js +51 -0
- package/dist/packs/heroes/sets.js.map +1 -0
- package/dist/packs/heroes/spells.d.ts +16 -0
- package/dist/packs/heroes/spells.js +269 -0
- package/dist/packs/heroes/spells.js.map +1 -0
- package/dist/packs/items/defaults.d.ts +43 -0
- package/dist/packs/items/defaults.js +1504 -0
- package/dist/packs/items/defaults.js.map +1 -0
- package/dist/packs/items/index.d.ts +27 -0
- package/dist/packs/items/index.js +214 -0
- package/dist/packs/items/index.js.map +1 -0
- package/dist/packs/moba/index.d.ts +3 -3
- package/dist/packs/moba/index.js +174 -0
- package/dist/packs/moba/index.js.map +1 -0
- package/dist/packs/pack.d.ts +15 -15
- package/dist/packs/pack.js +14 -0
- package/dist/packs/pack.js.map +1 -0
- package/dist/packs.d.ts +15 -7
- package/dist/packs.js +222 -0
- package/dist/packs.js.map +1 -0
- package/dist/paths.d.ts +2 -0
- package/dist/paths.js +109 -0
- package/dist/paths.js.map +1 -0
- package/dist/plain.js +57 -0
- package/dist/plain.js.map +1 -0
- package/dist/player.d.ts +33 -10
- package/dist/player.js +159 -0
- package/dist/player.js.map +1 -0
- package/dist/portals.d.ts +1 -1
- package/dist/portals.js +194 -0
- package/dist/portals.js.map +1 -0
- package/dist/prefabs.d.ts +25 -2
- package/dist/prefabs.js +239 -0
- package/dist/prefabs.js.map +1 -0
- package/dist/presetBuildings.js +58 -0
- package/dist/presetBuildings.js.map +1 -0
- package/dist/presetFoliage.d.ts +27 -0
- package/dist/presetFoliage.js +98 -0
- package/dist/presetFoliage.js.map +1 -0
- package/dist/presetGround.js +65 -0
- package/dist/presetGround.js.map +1 -0
- package/dist/proxies.d.ts +12 -0
- package/dist/proxies.js +79 -0
- package/dist/proxies.js.map +1 -0
- package/dist/query.d.ts +56 -0
- package/dist/query.js +88 -0
- package/dist/query.js.map +1 -0
- package/dist/races.d.ts +15 -14
- package/dist/races.js +172 -0
- package/dist/races.js.map +1 -0
- package/dist/randomGroups.js +91 -0
- package/dist/randomGroups.js.map +1 -0
- package/dist/registry.d.ts +117 -53
- package/dist/registry.js +323 -0
- package/dist/registry.js.map +1 -0
- package/dist/remains.d.ts +11 -9
- package/dist/remains.js +188 -0
- package/dist/remains.js.map +1 -0
- package/dist/replication.d.ts +12 -7
- package/dist/replication.js +100 -0
- package/dist/replication.js.map +1 -0
- package/dist/rng.d.ts +2 -0
- package/dist/rng.js +40 -0
- package/dist/rng.js.map +1 -0
- package/dist/rules.d.ts +186 -99
- package/dist/rules.js +179 -0
- package/dist/rules.js.map +1 -0
- package/dist/rulesCheck.d.ts +5 -4
- package/dist/rulesCheck.js +693 -0
- package/dist/rulesCheck.js.map +1 -0
- package/dist/runes.d.ts +2 -2
- package/dist/runes.js +80 -0
- package/dist/runes.js.map +1 -0
- package/dist/save.d.ts +37 -12
- package/dist/save.js +532 -0
- package/dist/save.js.map +1 -0
- package/dist/scenario.d.ts +2 -2
- package/dist/scenario.js +99 -0
- package/dist/scenario.js.map +1 -0
- package/dist/scheduler.d.ts +3 -0
- package/dist/scheduler.js +156 -0
- package/dist/scheduler.js.map +1 -0
- package/dist/score.d.ts +19 -19
- package/dist/score.js +709 -0
- package/dist/score.js.map +1 -0
- package/dist/sequences.d.ts +10 -6
- package/dist/sequences.js +206 -0
- package/dist/sequences.js.map +1 -0
- package/dist/server/doorman.js +154 -0
- package/dist/server/doorman.js.map +1 -0
- package/dist/server/gameServer.d.ts +16 -1
- package/dist/server/gameServer.js +643 -0
- package/dist/server/gameServer.js.map +1 -0
- package/dist/server/index.d.ts +1 -1
- package/dist/server/lobby.js +199 -0
- package/dist/server/lobby.js.map +1 -0
- package/dist/server/log.js +18 -0
- package/dist/server/log.js.map +1 -0
- package/dist/server/match.js +267 -0
- package/dist/server/match.js.map +1 -0
- package/dist/server/mode.d.ts +18 -9
- package/dist/server/mode.js +220 -0
- package/dist/server/mode.js.map +1 -0
- package/dist/{navigation-worker.js → server/navigation-worker.js} +1 -1
- package/dist/server/navigation-worker.js.map +1 -0
- package/dist/server/navigation.js +39 -0
- package/dist/server/navigation.js.map +1 -0
- package/dist/server/ops.d.ts +4 -0
- package/dist/server/ops.js +137 -0
- package/dist/server/ops.js.map +1 -0
- package/dist/server/packs.d.ts +3 -3
- package/dist/server/packs.js +32 -0
- package/dist/server/packs.js.map +1 -0
- package/dist/server/ratings.d.ts +5 -0
- package/dist/server/ratings.js +104 -0
- package/dist/server/ratings.js.map +1 -0
- package/dist/server/relay.d.ts +12 -2
- package/dist/server/relay.js +144 -0
- package/dist/server/relay.js.map +1 -0
- package/dist/server/rooms.d.ts +9 -2
- package/dist/server/rooms.js +442 -0
- package/dist/server/rooms.js.map +1 -0
- package/dist/server/seat.js +116 -0
- package/dist/server/seat.js.map +1 -0
- package/dist/server/seatSettings.d.ts +3 -3
- package/dist/server/seatSettings.js +63 -0
- package/dist/server/seatSettings.js.map +1 -0
- package/dist/server/secret.js +13 -0
- package/dist/server/secret.js.map +1 -0
- package/dist/server/start.d.ts +13 -6
- package/dist/server/start.js +76 -0
- package/dist/server/start.js.map +1 -0
- package/dist/server/websocket.d.ts +9 -1
- package/dist/server/websocket.js +306 -0
- package/dist/server/websocket.js.map +1 -0
- package/dist/server.js +15 -2861
- package/dist/session/can.js +26 -0
- package/dist/session/can.js.map +1 -0
- package/dist/session/commandReplay.d.ts +8 -2
- package/dist/session/commandReplay.js +293 -0
- package/dist/session/commandReplay.js.map +1 -0
- package/dist/session/debug.js +151 -0
- package/dist/session/debug.js.map +1 -0
- package/dist/session/delay.js +62 -0
- package/dist/session/delay.js.map +1 -0
- package/dist/session/delta.d.ts +14 -1
- package/dist/session/delta.js +407 -0
- package/dist/session/delta.js.map +1 -0
- package/dist/session/entityState.js +415 -0
- package/dist/session/entityState.js.map +1 -0
- package/dist/session/local.d.ts +47 -5
- package/dist/session/local.js +670 -0
- package/dist/session/local.js.map +1 -0
- package/dist/session/memory.d.ts +20 -2
- package/dist/session/memory.js +131 -0
- package/dist/session/memory.js.map +1 -0
- package/dist/session/protocol.d.ts +325 -31
- package/dist/session/protocol.js +55 -0
- package/dist/session/protocol.js.map +1 -0
- package/dist/session/remote.d.ts +74 -12
- package/dist/session/remote.js +1394 -0
- package/dist/session/remote.js.map +1 -0
- package/dist/session/replay.d.ts +11 -2
- package/dist/session/replay.js +160 -0
- package/dist/session/replay.js.map +1 -0
- package/dist/session/seed.js +12 -0
- package/dist/session/seed.js.map +1 -0
- package/dist/session/share.d.ts +5 -1
- package/dist/session/share.js +94 -0
- package/dist/session/share.js.map +1 -0
- package/dist/session/socket.d.ts +1 -1
- package/dist/session/socket.js +29 -0
- package/dist/session/socket.js.map +1 -0
- package/dist/session/wire.d.ts +5 -0
- package/dist/session/wire.js +84 -0
- package/dist/session/wire.js.map +1 -0
- package/dist/session/worker.d.ts +77 -12
- package/dist/session/worker.js +499 -0
- package/dist/session/worker.js.map +1 -0
- package/dist/shared.d.ts +31 -5
- package/dist/shared.js +166 -0
- package/dist/shared.js.map +1 -0
- package/dist/sides.d.ts +9 -3
- package/dist/sides.js +149 -0
- package/dist/sides.js.map +1 -0
- package/dist/sight.js +85 -0
- package/dist/sight.js.map +1 -0
- package/dist/simulate-worker.js +1 -1
- package/dist/simulate.d.ts +12 -8
- package/dist/{simulate-oXBiw1Ff.js → simulate.js} +10 -11
- package/dist/simulate.js.map +1 -0
- package/dist/smart.js +77 -0
- package/dist/smart.js.map +1 -0
- package/dist/spatial.d.ts +32 -16
- package/dist/spatial.js +234 -0
- package/dist/spatial.js.map +1 -0
- package/dist/spawning.d.ts +1 -0
- package/dist/spawning.js +224 -0
- package/dist/spawning.js.map +1 -0
- package/dist/stances.d.ts +6 -3
- package/dist/stances.js +169 -0
- package/dist/stances.js.map +1 -0
- package/dist/start.d.ts +10 -10
- package/dist/start.js +75 -0
- package/dist/start.js.map +1 -0
- package/dist/stats.d.ts +42 -16
- package/dist/stats.js +290 -0
- package/dist/stats.js.map +1 -0
- package/dist/summoned.d.ts +52 -0
- package/dist/summoned.js +38 -0
- package/dist/summoned.js.map +1 -0
- package/dist/summons.d.ts +10 -13
- package/dist/summons.js +271 -0
- package/dist/summons.js.map +1 -0
- package/dist/systems/abilities.js +224 -0
- package/dist/systems/abilities.js.map +1 -0
- package/dist/systems/combat.d.ts +35 -1
- package/dist/systems/combat.js +677 -0
- package/dist/systems/combat.js.map +1 -0
- package/dist/systems/combatTold.d.ts +3 -1
- package/dist/systems/combatTold.js +85 -0
- package/dist/systems/combatTold.js.map +1 -0
- package/dist/systems/construction.js +80 -0
- package/dist/systems/construction.js.map +1 -0
- package/dist/systems/economy.d.ts +0 -6
- package/dist/systems/economy.js +128 -0
- package/dist/systems/economy.js.map +1 -0
- package/dist/systems/fog.d.ts +1 -1
- package/dist/systems/fog.js +120 -0
- package/dist/systems/fog.js.map +1 -0
- package/dist/systems/life.js +32 -0
- package/dist/systems/life.js.map +1 -0
- package/dist/systems/movement.d.ts +24 -12
- package/dist/systems/movement.js +1247 -0
- package/dist/systems/movement.js.map +1 -0
- package/dist/systems/orders.js +64 -0
- package/dist/systems/orders.js.map +1 -0
- package/dist/systems/production.js +322 -0
- package/dist/systems/production.js.map +1 -0
- package/dist/systems/productionQueue.js +120 -0
- package/dist/systems/productionQueue.js.map +1 -0
- package/dist/systems/victory.d.ts +1 -1
- package/dist/systems/victory.js +56 -0
- package/dist/systems/victory.js.map +1 -0
- package/dist/talk.d.ts +6 -6
- package/dist/talk.js +153 -0
- package/dist/talk.js.map +1 -0
- package/dist/targeting.d.ts +142 -0
- package/dist/targeting.js +49 -0
- package/dist/targeting.js.map +1 -0
- package/dist/tech.d.ts +20 -3
- package/dist/tech.js +239 -0
- package/dist/tech.js.map +1 -0
- package/dist/techTree.d.ts +7 -0
- package/dist/techTree.js +90 -0
- package/dist/techTree.js.map +1 -0
- package/dist/terrain.d.ts +59 -7
- package/dist/terrain.js +389 -0
- package/dist/terrain.js.map +1 -0
- package/dist/terrainSave.d.ts +6 -0
- package/dist/terrainSave.js +74 -0
- package/dist/terrainSave.js.map +1 -0
- package/dist/testing.d.ts +8 -7
- package/dist/testing.js +13 -739
- package/dist/testing.js.map +1 -1
- package/dist/tiles.d.ts +1 -1
- package/dist/tiles.js +43 -0
- package/dist/tiles.js.map +1 -0
- package/dist/timers.d.ts +29 -4
- package/dist/timers.js +242 -0
- package/dist/timers.js.map +1 -0
- package/dist/tomes.d.ts +4 -4
- package/dist/tomes.js +94 -0
- package/dist/tomes.js.map +1 -0
- package/dist/trade.js +103 -0
- package/dist/trade.js.map +1 -0
- package/dist/transport.d.ts +6 -1
- package/dist/transport.js +281 -0
- package/dist/transport.js.map +1 -0
- package/dist/tunables.d.ts +10 -3
- package/dist/tunables.js +56 -0
- package/dist/tunables.js.map +1 -0
- package/dist/unitResources.d.ts +67 -0
- package/dist/unitResources.js +266 -0
- package/dist/unitResources.js.map +1 -0
- package/dist/upkeep.d.ts +6 -1
- package/dist/upkeep.js +26 -0
- package/dist/upkeep.js.map +1 -0
- package/dist/veterancy.d.ts +8 -3
- package/dist/veterancy.js +131 -0
- package/dist/veterancy.js.map +1 -0
- package/dist/vision.d.ts +36 -0
- package/dist/vision.js +133 -0
- package/dist/vision.js.map +1 -0
- package/dist/walls.d.ts +1 -1
- package/dist/walls.js +278 -0
- package/dist/walls.js.map +1 -0
- package/dist/world.d.ts +606 -88
- package/dist/world.js +1263 -0
- package/dist/world.js.map +1 -0
- package/package.json +2 -2
- package/dist/ai.js.map +0 -1
- package/dist/attributes.d.ts +0 -66
- package/dist/brain-BvVdUhX8.js +0 -7659
- package/dist/brain-BvVdUhX8.js.map +0 -1
- package/dist/builds-C6LfgvEU.js +0 -460
- package/dist/builds-C6LfgvEU.js.map +0 -1
- package/dist/coreComponents-syuh2fPG.js +0 -5149
- package/dist/coreComponents-syuh2fPG.js.map +0 -1
- package/dist/debugCommands-CzjrSm66.js +0 -14739
- package/dist/debugCommands-CzjrSm66.js.map +0 -1
- package/dist/determinism-BCymQwiE.js +0 -518
- package/dist/determinism-BCymQwiE.js.map +0 -1
- package/dist/game-DiJEN-Ks.js +0 -5800
- package/dist/game-DiJEN-Ks.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/inspect-D9dWHmZ0.js.map +0 -1
- package/dist/local-Dkovq42k.js +0 -1379
- package/dist/local-Dkovq42k.js.map +0 -1
- package/dist/missionRules-C-bGxm0Q.js.map +0 -1
- package/dist/navigation-worker.js.map +0 -1
- package/dist/packs-g5thvmGn.js +0 -3199
- package/dist/packs-g5thvmGn.js.map +0 -1
- package/dist/profiles-B3NS9jwk.js.map +0 -1
- package/dist/server.js.map +0 -1
- package/dist/simulate-oXBiw1Ff.js.map +0 -1
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import { hashText } from "./math.js";
|
|
2
|
+
import { teamMemory } from "./session/memory.js";
|
|
3
|
+
import { fighting } from "./entity.js";
|
|
4
|
+
import { decodeSave, encodeSave, saveWorld } from "./save.js";
|
|
5
|
+
import { loadWorld } from "./game.js";
|
|
6
|
+
//#region src/determinism.ts
|
|
7
|
+
/**
|
|
8
|
+
* Whether a game comes out the same (determinism plan D1): the simulation's state as data and as a hash, and a check
|
|
9
|
+
* that runs a world twice, and on from a save, and says the first tick and the first thing they differ at. What a
|
|
10
|
+
* replay of commands, a save that goes on exactly and a game the same in every browser stand on
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* The simulation's state as plain data: its save, which holds all of it, but what each team's screens remember of the
|
|
14
|
+
* map, which is the watchers' (`teamMemory`)
|
|
15
|
+
*
|
|
16
|
+
* @beta
|
|
17
|
+
*/
|
|
18
|
+
function worldState(world) {
|
|
19
|
+
return decided(saveWorld(world, { reseat: false }), world.registry);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* A save as what decides the game: without what each team's screens remember of the map (`teamMemory`: kept in the
|
|
23
|
+
* world for who comes back, written by whoever watches it, and read by nothing that decides), and, with the rules, the
|
|
24
|
+
* components nobody would have made had nobody asked for them (`unasked`)
|
|
25
|
+
*/
|
|
26
|
+
function decided(save, registry) {
|
|
27
|
+
const { [teamMemory.id]: _memory, ...state } = save.state;
|
|
28
|
+
const kept = (components) => registry ? unasked(registry, components) : components;
|
|
29
|
+
return {
|
|
30
|
+
...save,
|
|
31
|
+
state: kept(state),
|
|
32
|
+
players: save.players.map((player) => ({
|
|
33
|
+
...player,
|
|
34
|
+
state: kept(player.state)
|
|
35
|
+
})),
|
|
36
|
+
entities: save.entities.map((entity) => entity.components ? {
|
|
37
|
+
...entity,
|
|
38
|
+
components: kept(entity.components)
|
|
39
|
+
} : entity)
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Where two saves of a game first differ, as `checkDeterminism` looks (what decides first: the tick, the random state,
|
|
44
|
+
* the players, the entities): the path to it (`entities[id 11].pos.x`) and what each holds there, or nothing when they
|
|
45
|
+
* are the same. What the screens remember of the map, and the hash of the rules they were made with, are left out;
|
|
46
|
+
* with the game's `registry`, so are components that hold what they are made with (a screen asking for one makes it).
|
|
47
|
+
* What a replay that came apart says at its checkpoints (`runReplay`), and `v12 save diff`
|
|
48
|
+
*
|
|
49
|
+
* @beta
|
|
50
|
+
*/
|
|
51
|
+
function saveDifference(a, b, registry) {
|
|
52
|
+
const ordered = (save) => {
|
|
53
|
+
const { hash: _rules, ...state } = decided(save, registry);
|
|
54
|
+
return {
|
|
55
|
+
...Object.fromEntries(FIRST.map((key) => [key, state[key]])),
|
|
56
|
+
...state
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
return firstDifference(ordered(a), ordered(b));
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Components as they would be had nobody asked for one that was not there: one that holds what its component makes
|
|
63
|
+
* when asked for (its `default`) is left out. Asking makes one, and a watcher's screen asks for what it shows (a
|
|
64
|
+
* player's objectives), so a world watched and the same world unwatched differ in that alone
|
|
65
|
+
*/
|
|
66
|
+
function unasked(registry, components) {
|
|
67
|
+
const kept = {};
|
|
68
|
+
for (const [id, value] of Object.entries(components)) {
|
|
69
|
+
const made = registry.component(id)?.default?.();
|
|
70
|
+
if (made !== void 0 && JSON.stringify(made) === JSON.stringify(value)) continue;
|
|
71
|
+
kept[id] = value;
|
|
72
|
+
}
|
|
73
|
+
return kept;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* The simulation's state as a hash: two worlds with the same hash are, to all that is saved of them, the same. Fourteen
|
|
77
|
+
* hex digits (cyrb53, of the save as JSON, but the hash of the rules, which another build of the same code writes
|
|
78
|
+
* otherwise): enough to tell drift, not to stand against someone forging a state
|
|
79
|
+
*
|
|
80
|
+
* @beta
|
|
81
|
+
*/
|
|
82
|
+
var worldHash = (world) => {
|
|
83
|
+
const { hash: _rules, ...state } = worldState(world);
|
|
84
|
+
return hashText(canonical(state));
|
|
85
|
+
};
|
|
86
|
+
/**
|
|
87
|
+
* JSON with the keys of every object in order: the same state as text however it was put together (a component a
|
|
88
|
+
* watcher made early comes first in its object, and later where nobody watched)
|
|
89
|
+
*/
|
|
90
|
+
function canonical(value) {
|
|
91
|
+
return JSON.stringify(value, (_key, each) => {
|
|
92
|
+
if (!each || typeof each !== "object" || Array.isArray(each)) return each;
|
|
93
|
+
const sorted = {};
|
|
94
|
+
for (const key of Object.keys(each).sort()) sorted[key] = each[key];
|
|
95
|
+
return sorted;
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* A quick hash of what a game that came apart differs in first: the tick, the random state, each player's resources,
|
|
100
|
+
* upgrades and supply, and each entity's id, type, owner, place, facing, health, mana, whether it lives, its orders (how
|
|
101
|
+
* many, and the first's kind and target), its buffs (which, and the time left) and its attack's cooldown and swing. Read
|
|
102
|
+
* off the world as it is, with no save: 0.4 ms at 1000 units a side, where `worldHash` takes 66. What a
|
|
103
|
+
* replay of commands keeps every few seconds and checks as it plays (`recordCommands`), and what `checkDeterminism`
|
|
104
|
+
* compares every tick, for the tick a game came apart at; what it leaves out (components, areas, the fog) the whole
|
|
105
|
+
* state, compared every so often, has
|
|
106
|
+
*
|
|
107
|
+
* @beta
|
|
108
|
+
*/
|
|
109
|
+
function worldFingerprint(world) {
|
|
110
|
+
const values = [world.tick, world.rng.save()];
|
|
111
|
+
for (const player of world.players.values()) {
|
|
112
|
+
values.push(player.id, player.supplyUsed, player.supplyCap);
|
|
113
|
+
for (const key of Object.keys(player.resources).sort()) values.push(player.resources[key] ?? 0);
|
|
114
|
+
for (const [upgrade, level] of player.upgrades) values.push(wordOf(upgrade), level);
|
|
115
|
+
}
|
|
116
|
+
for (const entity of world.entities.values()) {
|
|
117
|
+
values.push(entity.id, wordOf(entity.def.id), entity.owner, entity.pos.x, entity.pos.z, entity.facing, entity.health, entity.mana, entity.alive ? 1 : 0);
|
|
118
|
+
const order = entity.orders[0];
|
|
119
|
+
values.push(entity.orders.length, order ? wordOf(order.type) : 0, order?.init.target ?? 0);
|
|
120
|
+
values.push(entity.buffs.length);
|
|
121
|
+
for (const buff of entity.buffs) values.push(wordOf(buff.id), buff.remaining);
|
|
122
|
+
if (entity.get(fighting)) values.push(entity.get(fighting).cooldown, entity.get(fighting).swing);
|
|
123
|
+
}
|
|
124
|
+
return hashWords(new Uint32Array(Float64Array.from(values).buffer));
|
|
125
|
+
}
|
|
126
|
+
/** A short text as a number, for the fingerprint: FNV-1a of its characters */
|
|
127
|
+
function wordOf(text) {
|
|
128
|
+
let h = 2166136261;
|
|
129
|
+
for (let i = 0; i < text.length; i++) h = Math.imul(h ^ text.charCodeAt(i), 16777619);
|
|
130
|
+
return h >>> 0;
|
|
131
|
+
}
|
|
132
|
+
/** cyrb53 of 32-bit words: the bits of numbers, hashed as they are */
|
|
133
|
+
function hashWords(words) {
|
|
134
|
+
let h1 = 3735928559;
|
|
135
|
+
let h2 = 1103547991;
|
|
136
|
+
for (const word of words) {
|
|
137
|
+
h1 = Math.imul(h1 ^ word, 2654435761);
|
|
138
|
+
h2 = Math.imul(h2 ^ word, 1597334677);
|
|
139
|
+
}
|
|
140
|
+
h1 = Math.imul(h1 ^ h1 >>> 16, 2246822507) ^ Math.imul(h2 ^ h2 >>> 13, 3266489909);
|
|
141
|
+
h2 = Math.imul(h2 ^ h2 >>> 16, 2246822507) ^ Math.imul(h1 ^ h1 >>> 13, 3266489909);
|
|
142
|
+
return (4294967296 * (2097151 & h2) + (h1 >>> 0)).toString(16).padStart(14, "0");
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Where two states first differ: the path to it (`entities[id 11].pos.x`) and what each holds there. Nothing when they
|
|
146
|
+
* are the same. Lists of things with an `id` are named by it, the rest by their place
|
|
147
|
+
*
|
|
148
|
+
* @beta
|
|
149
|
+
*/
|
|
150
|
+
function firstDifference(a, b, path = "") {
|
|
151
|
+
if (Object.is(a, b)) return void 0;
|
|
152
|
+
if (typeof a !== "object" || typeof b !== "object" || a === null || b === null || Array.isArray(a) !== Array.isArray(b)) return {
|
|
153
|
+
path,
|
|
154
|
+
a,
|
|
155
|
+
b
|
|
156
|
+
};
|
|
157
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
158
|
+
for (let i = 0; i < Math.max(a.length, b.length); i++) {
|
|
159
|
+
const each = a[i] ?? b[i];
|
|
160
|
+
const named = each && typeof each === "object" && "id" in each ? `[id ${String(each.id)}]` : `[${i}]`;
|
|
161
|
+
const found = firstDifference(a[i], b[i], `${path}${named}`);
|
|
162
|
+
if (found) return found;
|
|
163
|
+
}
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
const keys = /* @__PURE__ */ new Set([...Object.keys(a), ...Object.keys(b)]);
|
|
167
|
+
for (const key of keys) {
|
|
168
|
+
const found = firstDifference(a[key], b[key], path ? `${path}.${key}` : key);
|
|
169
|
+
if (found) return found;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Makes a world twice with `make` (the same seed, the same commands given in it, as a replay would) and runs them side
|
|
174
|
+
* by side, comparing their fingerprints every tick and their states (and the order each player's entities are filed
|
|
175
|
+
* in) every `every` ticks; with `from`, a third is saved at that tick and a fourth loaded
|
|
176
|
+
* from the save, and the two run on side by side. Says, for each, the first tick and thing it differs at, or that it does not. A test of a game's
|
|
177
|
+
* own systems: one that reads `Math.random`, the clock, or the order of something unordered is found here
|
|
178
|
+
*
|
|
179
|
+
* const report = await checkDeterminism(() => makeMyGame(1), { ticks: 1800, from: 300, rules })
|
|
180
|
+
* expect(report).toEqual({ twice: null, fromSave: null })
|
|
181
|
+
*
|
|
182
|
+
* @public
|
|
183
|
+
*/
|
|
184
|
+
async function checkDeterminism(make, options) {
|
|
185
|
+
const { ticks, every = 30, from } = options;
|
|
186
|
+
if (from !== void 0 && !options.rules) throw new Error("checkDeterminism: a check from a save needs the game to load it with (game)");
|
|
187
|
+
const first = await make();
|
|
188
|
+
const second = await make();
|
|
189
|
+
const saved = from === void 0 ? void 0 : await make();
|
|
190
|
+
let loaded;
|
|
191
|
+
const report = {
|
|
192
|
+
twice: null,
|
|
193
|
+
...from !== void 0 && { fromSave: null }
|
|
194
|
+
};
|
|
195
|
+
try {
|
|
196
|
+
for (let tick = 1; tick <= ticks; tick++) {
|
|
197
|
+
first.step();
|
|
198
|
+
if (!report.twice) second.step();
|
|
199
|
+
if (saved && !report.fromSave) {
|
|
200
|
+
saved.step();
|
|
201
|
+
loaded?.step();
|
|
202
|
+
if (saved.tick === from) loaded = await loadWorld(decodeSave(encodeSave(saveWorld(saved))), { rules: options.rules });
|
|
203
|
+
}
|
|
204
|
+
const whole = tick % every === 0;
|
|
205
|
+
if (!report.twice && (whole || worldFingerprint(first) !== worldFingerprint(second))) report.twice = apartAt(first.tick, compared(first), compared(second));
|
|
206
|
+
if (loaded && !report.fromSave && saved.tick > from && (whole || worldFingerprint(saved) !== worldFingerprint(loaded))) report.fromSave = apartAt(saved.tick, compared(saved), compared(loaded));
|
|
207
|
+
if (report.twice && (from === void 0 || report.fromSave)) break;
|
|
208
|
+
}
|
|
209
|
+
} finally {
|
|
210
|
+
first.dispose();
|
|
211
|
+
second.dispose();
|
|
212
|
+
saved?.dispose();
|
|
213
|
+
loaded?.dispose();
|
|
214
|
+
}
|
|
215
|
+
return report;
|
|
216
|
+
}
|
|
217
|
+
var compared = (world) => ({
|
|
218
|
+
...worldState(world),
|
|
219
|
+
owned: Object.fromEntries([...world.players.keys()].map((player) => [player, [...world.index.ownedBy(player)].map((entity) => entity.id)]))
|
|
220
|
+
});
|
|
221
|
+
/**
|
|
222
|
+
* What is looked at first: what is decided (the random state, the players, the entities, the order they are filed in)
|
|
223
|
+
* before what follows from it (the fog, which units see), so that what is said is where it began
|
|
224
|
+
*/
|
|
225
|
+
var FIRST = [
|
|
226
|
+
"tick",
|
|
227
|
+
"rng",
|
|
228
|
+
"players",
|
|
229
|
+
"entities",
|
|
230
|
+
"owned",
|
|
231
|
+
"units"
|
|
232
|
+
];
|
|
233
|
+
var apartAt = (tick, a, b) => {
|
|
234
|
+
const ordered = (state) => ({
|
|
235
|
+
...Object.fromEntries(FIRST.map((key) => [key, state[key]])),
|
|
236
|
+
...state
|
|
237
|
+
});
|
|
238
|
+
const found = firstDifference(ordered(a), ordered(b));
|
|
239
|
+
return found ? {
|
|
240
|
+
tick,
|
|
241
|
+
...found
|
|
242
|
+
} : null;
|
|
243
|
+
};
|
|
244
|
+
//#endregion
|
|
245
|
+
export { checkDeterminism, firstDifference, saveDifference, worldFingerprint, worldHash, worldState };
|
|
246
|
+
|
|
247
|
+
//# sourceMappingURL=determinism.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"determinism.js","names":[],"sources":["../src/determinism.ts"],"sourcesContent":["/**\n * Whether a game comes out the same (determinism plan D1): the simulation's state as data and as a hash, and a check\n * that runs a world twice, and on from a save, and says the first tick and the first thing they differ at. What a\n * replay of commands, a save that goes on exactly and a game the same in every browser stand on\n */\nimport { type Rules, loadWorld } from './game'\nimport { decodeSave, encodeSave, saveWorld, type WorldSave } from './save'\nimport type { Registry } from './registry'\nimport { teamMemory } from './session/memory'\nimport type { World } from './world'\nimport { fighting } from './entity'\nimport { hashText } from './math'\n\n/**\n * The simulation's state as plain data: its save, which holds all of it, but what each team's screens remember of the\n * map, which is the watchers' (`teamMemory`)\n *\n * @beta\n */\nexport function worldState(world: World): WorldSave {\n\treturn decided(saveWorld(world, { reseat: false }), world.registry)\n}\n\n/**\n * A save as what decides the game: without what each team's screens remember of the map (`teamMemory`: kept in the\n * world for who comes back, written by whoever watches it, and read by nothing that decides), and, with the rules, the\n * components nobody would have made had nobody asked for them (`unasked`)\n */\nfunction decided(save: WorldSave, registry?: Registry): WorldSave {\n\tconst { [teamMemory.id]: _memory, ...state } = save.state\n\tconst kept = (components: Record<string, unknown>) => (registry ? unasked(registry, components) : components)\n\treturn {\n\t\t...save,\n\t\tstate: kept(state),\n\t\tplayers: save.players.map((player) => ({ ...player, state: kept(player.state) })),\n\t\tentities: save.entities.map((entity) =>\n\t\t\tentity.components ? { ...entity, components: kept(entity.components as Record<string, unknown>) } : entity\n\t\t)\n\t}\n}\n\n/**\n * Where two saves of a game first differ, as `checkDeterminism` looks (what decides first: the tick, the random state,\n * the players, the entities): the path to it (`entities[id 11].pos.x`) and what each holds there, or nothing when they\n * are the same. What the screens remember of the map, and the hash of the rules they were made with, are left out;\n * with the game's `registry`, so are components that hold what they are made with (a screen asking for one makes it).\n * What a replay that came apart says at its checkpoints (`runReplay`), and `v12 save diff`\n *\n * @beta\n */\nexport function saveDifference(a: WorldSave, b: WorldSave, registry?: Registry): Difference | undefined {\n\tconst ordered = (save: WorldSave) => {\n\t\tconst { hash: _rules, ...state } = decided(save, registry)\n\t\treturn { ...Object.fromEntries(FIRST.map((key) => [key, (state as Partial<Compared>)[key]])), ...state }\n\t}\n\treturn firstDifference(ordered(a), ordered(b))\n}\n\n/**\n * Components as they would be had nobody asked for one that was not there: one that holds what its component makes\n * when asked for (its `default`) is left out. Asking makes one, and a watcher's screen asks for what it shows (a\n * player's objectives), so a world watched and the same world unwatched differ in that alone\n */\nfunction unasked(registry: Registry, components: Record<string, unknown>): Record<string, unknown> {\n\tconst kept: Record<string, unknown> = {}\n\tfor (const [id, value] of Object.entries(components)) {\n\t\tconst made = registry.component(id)?.default?.()\n\t\tif (made !== undefined && JSON.stringify(made) === JSON.stringify(value)) continue\n\t\tkept[id] = value\n\t}\n\treturn kept\n}\n\n/**\n * The simulation's state as a hash: two worlds with the same hash are, to all that is saved of them, the same. Fourteen\n * hex digits (cyrb53, of the save as JSON, but the hash of the rules, which another build of the same code writes\n * otherwise): enough to tell drift, not to stand against someone forging a state\n *\n * @beta\n */\nexport const worldHash = (world: World): string => {\n\t// the rules' own hash names the code they were built from, which another build of the same game writes otherwise\n\tconst { hash: _rules, ...state } = worldState(world)\n\treturn hashText(canonical(state))\n}\n\n/**\n * JSON with the keys of every object in order: the same state as text however it was put together (a component a\n * watcher made early comes first in its object, and later where nobody watched)\n */\nfunction canonical(value: unknown): string {\n\treturn JSON.stringify(value, (_key, each: unknown) => {\n\t\tif (!each || typeof each !== 'object' || Array.isArray(each)) return each\n\t\tconst sorted: Record<string, unknown> = {}\n\t\tfor (const key of Object.keys(each).sort()) sorted[key] = (each as Record<string, unknown>)[key]\n\t\treturn sorted\n\t})\n}\n\n/**\n * A quick hash of what a game that came apart differs in first: the tick, the random state, each player's resources,\n * upgrades and supply, and each entity's id, type, owner, place, facing, health, mana, whether it lives, its orders (how\n * many, and the first's kind and target), its buffs (which, and the time left) and its attack's cooldown and swing. Read\n * off the world as it is, with no save: 0.4 ms at 1000 units a side, where `worldHash` takes 66. What a\n * replay of commands keeps every few seconds and checks as it plays (`recordCommands`), and what `checkDeterminism`\n * compares every tick, for the tick a game came apart at; what it leaves out (components, areas, the fog) the whole\n * state, compared every so often, has\n *\n * @beta\n */\nexport function worldFingerprint(world: World): string {\n\tconst values: number[] = [world.tick, world.rng.save()]\n\tfor (const player of world.players.values()) {\n\t\tvalues.push(player.id, player.supplyUsed, player.supplyCap)\n\t\tfor (const key of Object.keys(player.resources).sort()) values.push(player.resources[key] ?? 0)\n\t\tfor (const [upgrade, level] of player.upgrades) values.push(wordOf(upgrade), level)\n\t}\n\tfor (const entity of world.entities.values()) {\n\t\tvalues.push(\n\t\t\tentity.id,\n\t\t\twordOf(entity.def.id),\n\t\t\tentity.owner,\n\t\t\tentity.pos.x,\n\t\t\tentity.pos.z,\n\t\t\tentity.facing,\n\t\t\tentity.health,\n\t\t\tentity.mana,\n\t\t\tentity.alive ? 1 : 0\n\t\t)\n\t\tconst order = entity.orders[0]\n\t\tvalues.push(entity.orders.length, order ? wordOf(order.type) : 0, order?.init.target ?? 0)\n\t\tvalues.push(entity.buffs.length)\n\t\tfor (const buff of entity.buffs) values.push(wordOf(buff.id), buff.remaining)\n\t\tif (entity.get(fighting)) values.push(entity.get(fighting)!.cooldown, entity.get(fighting)!.swing)\n\t}\n\treturn hashWords(new Uint32Array(Float64Array.from(values).buffer))\n}\n\n/** A short text as a number, for the fingerprint: FNV-1a of its characters */\nfunction wordOf(text: string): number {\n\tlet h = 0x811c9dc5\n\tfor (let i = 0; i < text.length; i++) h = Math.imul(h ^ text.charCodeAt(i), 16777619)\n\treturn h >>> 0\n}\n\n/** cyrb53 of 32-bit words: the bits of numbers, hashed as they are */\nfunction hashWords(words: Uint32Array): string {\n\tlet h1 = 0xdeadbeef\n\tlet h2 = 0x41c6ce57\n\tfor (const word of words) {\n\t\th1 = Math.imul(h1 ^ word, 2654435761)\n\t\th2 = Math.imul(h2 ^ word, 1597334677)\n\t}\n\th1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507) ^ Math.imul(h2 ^ (h2 >>> 13), 3266489909)\n\th2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507) ^ Math.imul(h1 ^ (h1 >>> 13), 3266489909)\n\treturn (4294967296 * (2097151 & h2) + (h1 >>> 0)).toString(16).padStart(14, '0')\n}\n\n/**\n * Where two states first differ: the path to it (`entities[id 11].pos.x`) and what each holds there. Nothing when they\n * are the same. Lists of things with an `id` are named by it, the rest by their place\n *\n * @beta\n */\nexport function firstDifference(a: unknown, b: unknown, path = ''): Difference | undefined {\n\tif (Object.is(a, b)) return undefined\n\tif (typeof a !== 'object' || typeof b !== 'object' || a === null || b === null || Array.isArray(a) !== Array.isArray(b)) return { path, a, b }\n\tif (Array.isArray(a) && Array.isArray(b)) {\n\t\tfor (let i = 0; i < Math.max(a.length, b.length); i++) {\n\t\t\tconst each = a[i] ?? b[i]\n\t\t\tconst named = each && typeof each === 'object' && 'id' in each ? `[id ${String((each as { id: unknown }).id)}]` : `[${i}]`\n\t\t\tconst found = firstDifference(a[i], b[i], `${path}${named}`)\n\t\t\tif (found) return found\n\t\t}\n\t\treturn undefined\n\t}\n\tconst keys = new Set([...Object.keys(a), ...Object.keys(b)])\n\tfor (const key of keys) {\n\t\tconst found = firstDifference((a as Record<string, unknown>)[key], (b as Record<string, unknown>)[key], path ? `${path}.${key}` : key)\n\t\tif (found) return found\n\t}\n\treturn undefined\n}\n\n/**\n * Where two states differ (`firstDifference`)\n *\n * @beta\n */\nexport interface Difference {\n\t/** The path to it in the state: `entities[id 11].pos.x` */\n\tpath: string\n\t/** What the first holds there */\n\ta: unknown\n\t/** What the second holds there */\n\tb: unknown\n}\n\n/**\n * Where two runs of a game came apart: the first tick they were looked at and differed, and what differed first\n *\n * @beta\n */\nexport interface Apart extends Difference {\n\t/** The tick they were looked at and differed */\n\ttick: number\n}\n\n/**\n * How a game is checked for coming out the same (`checkDeterminism`)\n *\n * @beta\n */\nexport interface DeterminismOptions {\n\t/** How many ticks each world runs */\n\tticks: number\n\t/**\n\t * How often their whole state is compared, in ticks: every 30 when left out. Their fingerprints\n\t * (`worldFingerprint`) are compared every tick, and the whole state at once where those differ, so that the tick\n\t * said is the one they came apart at\n\t */\n\tevery?: number\n\t/**\n\t * A tick to save a third world at, and load a fourth from that save (`loadWorld`, with `game`): the game saved and\n\t * the game loaded are run on side by side and compared. Not done when left out\n\t */\n\tfrom?: number\n\t/** The game the save is loaded with, for `from` */\n\trules?: Rules\n}\n\n/**\n * What the check found: for each run, where it came apart from the first, or `null` for the same to the last tick\n *\n * @beta\n */\nexport interface DeterminismReport {\n\t/** A second world made the same way and run the same */\n\ttwice: Apart | null\n\t/** A world loaded from a save taken at `from`, beside the world the save was taken of: none when `from` was not said */\n\tfromSave?: Apart | null\n}\n\n/**\n * Makes a world twice with `make` (the same seed, the same commands given in it, as a replay would) and runs them side\n * by side, comparing their fingerprints every tick and their states (and the order each player's entities are filed\n * in) every `every` ticks; with `from`, a third is saved at that tick and a fourth loaded\n * from the save, and the two run on side by side. Says, for each, the first tick and thing it differs at, or that it does not. A test of a game's\n * own systems: one that reads `Math.random`, the clock, or the order of something unordered is found here\n *\n * const report = await checkDeterminism(() => makeMyGame(1), { ticks: 1800, from: 300, rules })\n * expect(report).toEqual({ twice: null, fromSave: null })\n *\n * @public\n */\nexport async function checkDeterminism(make: () => World | Promise<World>, options: DeterminismOptions): Promise<DeterminismReport> {\n\tconst { ticks, every = 30, from } = options\n\tif (from !== undefined && !options.rules) throw new Error('checkDeterminism: a check from a save needs the game to load it with (game)')\n\tconst first = await make()\n\tconst second = await make()\n\t// the game that is saved at `from` and goes on, and the game loaded from that save beside it\n\tconst saved = from === undefined ? undefined : await make()\n\tlet loaded: World | undefined\n\tconst report: DeterminismReport = { twice: null, ...(from !== undefined && { fromSave: null }) }\n\ttry {\n\t\tfor (let tick = 1; tick <= ticks; tick++) {\n\t\t\tfirst.step()\n\t\t\tif (!report.twice) second.step()\n\t\t\tif (saved && !report.fromSave) {\n\t\t\t\tsaved.step()\n\t\t\t\tloaded?.step()\n\t\t\t\tif (saved.tick === from) {\n\t\t\t\t\t// as a file keeps it (`encodeSave`): what a save cannot hold is lost here as it would be there\n\t\t\t\t\tloaded = await loadWorld(decodeSave(encodeSave(saveWorld(saved))), { rules: options.rules! })\n\t\t\t\t}\n\t\t\t}\n\t\t\t// the fingerprint every tick, for the tick they came apart at; the whole state every `every`, and at once where\n\t\t\t// the fingerprints differ, for what it is\n\t\t\tconst whole = tick % every === 0\n\t\t\tif (!report.twice && (whole || worldFingerprint(first) !== worldFingerprint(second)))\n\t\t\t\treport.twice = apartAt(first.tick, compared(first), compared(second))\n\t\t\tconst going = loaded && !report.fromSave && saved!.tick > from!\n\t\t\tif (going && (whole || worldFingerprint(saved!) !== worldFingerprint(loaded!)))\n\t\t\t\treport.fromSave = apartAt(saved!.tick, compared(saved!), compared(loaded!))\n\t\t\tif (report.twice && (from === undefined || report.fromSave)) break\n\t\t}\n\t} finally {\n\t\tfirst.dispose()\n\t\tsecond.dispose()\n\t\tsaved?.dispose()\n\t\tloaded?.dispose()\n\t}\n\treturn report\n}\n\n/**\n * What is compared of a world: its state (`worldState`), and the order its index gives each player's entities in\n * (`world.index.ownedBy`), which a save does not hold and which decides what is picked first among equals (the closest\n * drop-off, a shopper, who shares the experience): a world loaded from a save has to give them in the same order\n */\ntype Compared = WorldSave & { owned: Record<number, number[]> }\n\nconst compared = (world: World): Compared => ({\n\t...worldState(world),\n\towned: Object.fromEntries([...world.players.keys()].map((player) => [player, [...world.index.ownedBy(player)].map((entity) => entity.id)]))\n})\n\n/**\n * What is looked at first: what is decided (the random state, the players, the entities, the order they are filed in)\n * before what follows from it (the fog, which units see), so that what is said is where it began\n */\nconst FIRST: (keyof Compared)[] = ['tick', 'rng', 'players', 'entities', 'owned', 'units']\n\nconst apartAt = (tick: number, a: Compared, b: Compared): Apart | null => {\n\tconst ordered = (state: Compared) => ({ ...Object.fromEntries(FIRST.map((key) => [key, state[key]])), ...state })\n\tconst found = firstDifference(ordered(a), ordered(b))\n\treturn found ? { tick, ...found } : null\n}\n"],"mappings":"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"}
|
package/dist/dialog.d.ts
CHANGED
|
@@ -17,6 +17,13 @@ export interface LineDef {
|
|
|
17
17
|
text: MessageRef;
|
|
18
18
|
/** How long it stays, in seconds. `LINE_SECONDS` when left out: the rules know the key of the words, not how long they are */
|
|
19
19
|
seconds?: number;
|
|
20
|
+
/**
|
|
21
|
+
* The unit that says it, by its entity id or its name on the map: the screen shows its face talking (the HUD's
|
|
22
|
+
* portrait box, or the dialog box's) over the speaker's portrait. The speaker's look's portrait when left out
|
|
23
|
+
*/
|
|
24
|
+
unit?: number | string;
|
|
25
|
+
/** How it is said (`angry`, `whisper`): the speaker's face plays its clip for the mood, the box has it for its styles */
|
|
26
|
+
emotion?: string;
|
|
20
27
|
}
|
|
21
28
|
/**
|
|
22
29
|
* Whom a line is said to: a player, some, or everyone
|
|
@@ -30,12 +37,20 @@ export type Audience = PlayerId | PlayerId[] | 'everyone';
|
|
|
30
37
|
* @beta
|
|
31
38
|
*/
|
|
32
39
|
export interface SaidLine {
|
|
40
|
+
/** The line's number, which a client knows it by */
|
|
33
41
|
id: number;
|
|
42
|
+
/** Who says it, the name shown with the line */
|
|
34
43
|
speaker: string;
|
|
44
|
+
/** What is said */
|
|
35
45
|
text: Message;
|
|
46
|
+
/** How long the line is shown */
|
|
36
47
|
seconds: number;
|
|
37
48
|
/** Who hears it. Everyone when left out */
|
|
38
49
|
to?: PlayerId[];
|
|
50
|
+
/** The unit that says it, by its entity id or its name on the map (`LineDef.unit`) */
|
|
51
|
+
unit?: number | string;
|
|
52
|
+
/** How it is said (`LineDef.emotion`) */
|
|
53
|
+
emotion?: string;
|
|
39
54
|
}
|
|
40
55
|
/**
|
|
41
56
|
* The line each player hears now, the sequence waiting on it, and the tick it is over at, for skipping. Kept in the
|
|
@@ -83,9 +98,9 @@ export declare class Dialog {
|
|
|
83
98
|
* The steps of a conversation, for a sequence: each line is said and stays for as long as it lasts, then the next.
|
|
84
99
|
* Skipping a line (the `skipLine` command) goes on to the next at once:
|
|
85
100
|
*
|
|
86
|
-
* defineSequence('td.beforeTheBoss', [...talk(DEFENDER, [{ speaker: 'td.commander', text: 'bossComing' }, ...])])
|
|
101
|
+
* defineSequence({ id: 'td.beforeTheBoss', steps: [...talk(DEFENDER, [{ speaker: 'td.commander', text: 'bossComing' }, ...])] })
|
|
87
102
|
*
|
|
88
|
-
* @
|
|
103
|
+
* @public
|
|
89
104
|
*/
|
|
90
105
|
export declare function talk<D = undefined>(to: Audience, lines: LineDef[]): Step<D>[];
|
|
91
106
|
/** A line said, to whom it is for: everyone when it names nobody */
|
package/dist/dialog.js
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { defineComponent } from "./components.js";
|
|
2
|
+
import { lineEnded, lineSaid } from "./engineEvents.js";
|
|
3
|
+
import { replicateEvent } from "./replication.js";
|
|
4
|
+
import { toMessage } from "./messages.js";
|
|
5
|
+
import { wait } from "./sequences.js";
|
|
6
|
+
//#region src/dialog.ts
|
|
7
|
+
/**
|
|
8
|
+
* How long a line stays when it does not say: long enough to read a sentence
|
|
9
|
+
*
|
|
10
|
+
* @internal
|
|
11
|
+
*/
|
|
12
|
+
var LINE_SECONDS = 4;
|
|
13
|
+
/**
|
|
14
|
+
* The line each player hears now, the sequence waiting on it, and the tick it is over at, for skipping. Kept in the
|
|
15
|
+
* world: a save has it
|
|
16
|
+
*/
|
|
17
|
+
var current = defineComponent({
|
|
18
|
+
id: "core.dialog",
|
|
19
|
+
default: () => ({
|
|
20
|
+
next: 1,
|
|
21
|
+
lines: {}
|
|
22
|
+
})
|
|
23
|
+
});
|
|
24
|
+
/**
|
|
25
|
+
* What is said in a world
|
|
26
|
+
*
|
|
27
|
+
* @beta
|
|
28
|
+
*/
|
|
29
|
+
var Dialog = class {
|
|
30
|
+
world;
|
|
31
|
+
constructor(world) {
|
|
32
|
+
this.world = world;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Says a line, now. On its own it waits for nothing: in a sequence, `talk` says lines one after the other, each
|
|
36
|
+
* for as long as it lasts. `run` is the sequence that waits on it, which skipping the line hurries along
|
|
37
|
+
*/
|
|
38
|
+
say(to, line, run) {
|
|
39
|
+
const state = this.world.state(current);
|
|
40
|
+
const text = toMessage(line.text);
|
|
41
|
+
const players = to === "everyone" ? void 0 : Array.isArray(to) ? to : [to];
|
|
42
|
+
const said = {
|
|
43
|
+
id: state.next++,
|
|
44
|
+
speaker: line.speaker,
|
|
45
|
+
text,
|
|
46
|
+
seconds: line.seconds ?? 4,
|
|
47
|
+
to: players,
|
|
48
|
+
...line.unit !== void 0 && { unit: line.unit },
|
|
49
|
+
...line.emotion !== void 0 && { emotion: line.emotion }
|
|
50
|
+
};
|
|
51
|
+
const until = Number.isFinite(said.seconds) ? this.world.tick + Math.ceil(said.seconds * this.world.tickRate) : void 0;
|
|
52
|
+
for (const player of players ?? this.listeners()) state.lines[player] = {
|
|
53
|
+
line: said.id,
|
|
54
|
+
run,
|
|
55
|
+
to: players,
|
|
56
|
+
...until !== void 0 && { until }
|
|
57
|
+
};
|
|
58
|
+
this.world.events.emit(lineSaid, said);
|
|
59
|
+
return said;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* A player asks to skip the line they hear: it ends, and a conversation goes on to its next line. Only a line
|
|
63
|
+
* said to them alone can be: what others hear too goes on for them
|
|
64
|
+
*/
|
|
65
|
+
skip(player) {
|
|
66
|
+
const state = this.world.state(current);
|
|
67
|
+
const heard = state.lines[player];
|
|
68
|
+
if (!heard || heard.to?.length !== 1 || heard.to[0] !== player) return;
|
|
69
|
+
delete state.lines[player];
|
|
70
|
+
if (heard.until !== void 0 && this.world.tick >= heard.until) return;
|
|
71
|
+
this.world.events.emit(lineEnded, {
|
|
72
|
+
id: heard.line,
|
|
73
|
+
to: [player]
|
|
74
|
+
});
|
|
75
|
+
if (heard.run !== void 0) this.world.sequences.hurry(heard.run);
|
|
76
|
+
}
|
|
77
|
+
/** The players a line said to everyone is heard by: everyone who plays */
|
|
78
|
+
listeners() {
|
|
79
|
+
const players = [];
|
|
80
|
+
for (const player of this.world.players.values()) if (!player.neutral) players.push(player.id);
|
|
81
|
+
return players;
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
/**
|
|
85
|
+
* The steps of a conversation, for a sequence: each line is said and stays for as long as it lasts, then the next.
|
|
86
|
+
* Skipping a line (the `skipLine` command) goes on to the next at once:
|
|
87
|
+
*
|
|
88
|
+
* defineSequence({ id: 'td.beforeTheBoss', steps: [...talk(DEFENDER, [{ speaker: 'td.commander', text: 'bossComing' }, ...])] })
|
|
89
|
+
*
|
|
90
|
+
* @public
|
|
91
|
+
*/
|
|
92
|
+
function talk(to, lines) {
|
|
93
|
+
return lines.flatMap((line) => [(world, _data, run) => void world.dialog.say(to, line, run), wait(line.seconds ?? 4)]);
|
|
94
|
+
}
|
|
95
|
+
/** A line said, to whom it is for: everyone when it names nobody */
|
|
96
|
+
var toldLine = replicateEvent(lineSaid, "line", (line, view) => !line.to || line.to.includes(view.player) ? {
|
|
97
|
+
type: "line",
|
|
98
|
+
line: structuredClone(line)
|
|
99
|
+
} : void 0);
|
|
100
|
+
/** A line over, to whom it was for */
|
|
101
|
+
var toldLineEnded = replicateEvent(lineEnded, "lineEnded", ({ id, to }, view) => to.includes(view.player) ? {
|
|
102
|
+
type: "lineEnded",
|
|
103
|
+
id
|
|
104
|
+
} : void 0);
|
|
105
|
+
//#endregion
|
|
106
|
+
export { Dialog, LINE_SECONDS, current, talk, toldLine, toldLineEnded };
|
|
107
|
+
|
|
108
|
+
//# sourceMappingURL=dialog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dialog.js","names":["lineSaidEvent"],"sources":["../src/dialog.ts"],"sourcesContent":["import { lineEnded, lineSaid as lineSaidEvent } from '#engine/engineEvents'\nimport { defineComponent } from './components'\nimport type { PlayerId } from './entity'\nimport { type Message, type MessageRef, toMessage } from './messages'\nimport { type Step, wait } from './sequences'\nimport type { World } from './world'\nimport { replicateEvent } from './replication'\n\n/**\n * Something someone says: a commander between waves, a hero in a cutscene. The rules say a line is said, so that\n * every player hears it at the same moment and a save knows where a conversation was; the view shows it (a box with\n * a portrait, subtitles) and plays its voice. Who the speaker is on the screen, their name and portrait, is the\n * view's (`GameView.cinematics.speakers`)\n *\n * @beta\n */\nexport interface LineDef {\n\t/** Who says it, by an id of the game's (`'td.commander'`) */\n\tspeaker: string\n\t/** What is said, in the words of the game: a message key, with params */\n\ttext: MessageRef\n\t/** How long it stays, in seconds. `LINE_SECONDS` when left out: the rules know the key of the words, not how long they are */\n\tseconds?: number\n\t/**\n\t * The unit that says it, by its entity id or its name on the map: the screen shows its face talking (the HUD's\n\t * portrait box, or the dialog box's) over the speaker's portrait. The speaker's look's portrait when left out\n\t */\n\tunit?: number | string\n\t/** How it is said (`angry`, `whisper`): the speaker's face plays its clip for the mood, the box has it for its styles */\n\temotion?: string\n}\n\n/**\n * Whom a line is said to: a player, some, or everyone\n *\n * @beta\n */\nexport type Audience = PlayerId | PlayerId[] | 'everyone'\n\n/**\n * A line as it is said: what the engine's `lineSaid` event carries, and what goes to the screen\n *\n * @beta\n */\nexport interface SaidLine {\n\t/** The line's number, which a client knows it by */\n\tid: number\n\t/** Who says it, the name shown with the line */\n\tspeaker: string\n\t/** What is said */\n\ttext: Message\n\t/** How long the line is shown */\n\tseconds: number\n\t/** Who hears it. Everyone when left out */\n\tto?: PlayerId[]\n\t/** The unit that says it, by its entity id or its name on the map (`LineDef.unit`) */\n\tunit?: number | string\n\t/** How it is said (`LineDef.emotion`) */\n\temotion?: string\n}\n\n/**\n * How long a line stays when it does not say: long enough to read a sentence\n *\n * @internal\n */\nexport const LINE_SECONDS = 4\n\n/**\n * The line each player hears now, the sequence waiting on it, and the tick it is over at, for skipping. Kept in the\n * world: a save has it\n */\nexport const current = defineComponent<{ next: number; lines: Record<number, { line: number; run?: number; to?: PlayerId[]; until?: number }> }>({ id: 'core.dialog', default: () => ({ next: 1, lines: {} }) })\n\n/**\n * What is said in a world\n *\n * @beta\n */\nexport class Dialog {\n\tconstructor(private readonly world: World) {}\n\n\t/**\n\t * Says a line, now. On its own it waits for nothing: in a sequence, `talk` says lines one after the other, each\n\t * for as long as it lasts. `run` is the sequence that waits on it, which skipping the line hurries along\n\t */\n\tsay(to: Audience, line: LineDef, run?: number): SaidLine {\n\t\tconst state = this.world.state(current)\n\t\tconst text = toMessage(line.text)\n\t\tconst players = to === 'everyone' ? undefined : Array.isArray(to) ? to : [to]\n\t\tconst said: SaidLine = {\n\t\t\tid: state.next++,\n\t\t\tspeaker: line.speaker,\n\t\t\ttext,\n\t\t\tseconds: line.seconds ?? LINE_SECONDS,\n\t\t\tto: players,\n\t\t\t...(line.unit !== undefined && { unit: line.unit }),\n\t\t\t...(line.emotion !== undefined && { emotion: line.emotion })\n\t\t}\n\t\t// a line that waits to be moved on lasts until it is\n\t\tconst until = Number.isFinite(said.seconds) ? this.world.tick + Math.ceil(said.seconds * this.world.tickRate) : undefined\n\t\tfor (const player of players ?? this.listeners()) state.lines[player] = { line: said.id, run, to: players, ...(until !== undefined && { until }) }\n\t\tthis.world.events.emit(lineSaidEvent, said)\n\t\treturn said\n\t}\n\n\t/**\n\t * A player asks to skip the line they hear: it ends, and a conversation goes on to its next line. Only a line\n\t * said to them alone can be: what others hear too goes on for them\n\t */\n\tskip(player: PlayerId): void {\n\t\tconst state = this.world.state(current)\n\t\tconst heard = state.lines[player]\n\t\tif (!heard || heard.to?.length !== 1 || heard.to[0] !== player) return\n\t\tdelete state.lines[player]\n\t\t// a line that is over is skipped no more: the sequence that said it waits on something else by now (a minute\n\t\t// before the next wave), which skipping it once cut short\n\t\tif (heard.until !== undefined && this.world.tick >= heard.until) return\n\t\tthis.world.events.emit(lineEnded, { id: heard.line, to: [player] })\n\t\tif (heard.run !== undefined) this.world.sequences.hurry(heard.run)\n\t}\n\n\t/** The players a line said to everyone is heard by: everyone who plays */\n\tprivate listeners(): PlayerId[] {\n\t\tconst players: PlayerId[] = []\n\t\tfor (const player of this.world.players.values()) if (!player.neutral) players.push(player.id)\n\t\treturn players\n\t}\n}\n\n/**\n * The steps of a conversation, for a sequence: each line is said and stays for as long as it lasts, then the next.\n * Skipping a line (the `skipLine` command) goes on to the next at once:\n *\n * defineSequence({ id: 'td.beforeTheBoss', steps: [...talk(DEFENDER, [{ speaker: 'td.commander', text: 'bossComing' }, ...])] })\n *\n * @public\n */\nexport function talk<D = undefined>(to: Audience, lines: LineDef[]): Step<D>[] {\n\treturn lines.flatMap((line): Step<D>[] => [(world, _data, run) => void world.dialog.say(to, line, run), wait(line.seconds ?? LINE_SECONDS)])\n}\n\n/** A line said, to whom it is for: everyone when it names nobody */\nexport const toldLine = replicateEvent(lineSaidEvent, 'line', (line, view) =>\n\t!line.to || line.to.includes(view.player) ? { type: 'line', line: structuredClone(line) } : undefined\n)\n\n/** A line over, to whom it was for */\nexport const toldLineEnded = replicateEvent(lineEnded, 'lineEnded', ({ id, to }, view) =>\n\tto.includes(view.player) ? { type: 'lineEnded', id } : undefined\n)\n"],"mappings":";;;;;;;;;;;AAkEA,IAAa,eAAe;;;;;AAM5B,IAAa,UAAU,gBAA0H;CAAE,IAAI;CAAe,gBAAgB;EAAE,MAAM;EAAG,OAAO,CAAC;CAAE;AAAG,CAAC;;;;;;AAO/M,IAAa,SAAb,MAAoB;CACU;CAA7B,YAAY,OAA+B;EAAd,KAAA,QAAA;CAAe;;;;;CAM5C,IAAI,IAAc,MAAe,KAAwB;EACxD,MAAM,QAAQ,KAAK,MAAM,MAAM,OAAO;EACtC,MAAM,OAAO,UAAU,KAAK,IAAI;EAChC,MAAM,UAAU,OAAO,aAAa,KAAA,IAAY,MAAM,QAAQ,EAAE,IAAI,KAAK,CAAC,EAAE;EAC5E,MAAM,OAAiB;GACtB,IAAI,MAAM;GACV,SAAS,KAAK;GACd;GACA,SAAS,KAAK,WAAA;GACd,IAAI;GACJ,GAAI,KAAK,SAAS,KAAA,KAAa,EAAE,MAAM,KAAK,KAAK;GACjD,GAAI,KAAK,YAAY,KAAA,KAAa,EAAE,SAAS,KAAK,QAAQ;EAC3D;EAEA,MAAM,QAAQ,OAAO,SAAS,KAAK,OAAO,IAAI,KAAK,MAAM,OAAO,KAAK,KAAK,KAAK,UAAU,KAAK,MAAM,QAAQ,IAAI,KAAA;EAChH,KAAK,MAAM,UAAU,WAAW,KAAK,UAAU,GAAG,MAAM,MAAM,UAAU;GAAE,MAAM,KAAK;GAAI;GAAK,IAAI;GAAS,GAAI,UAAU,KAAA,KAAa,EAAE,MAAM;EAAG;EACjJ,KAAK,MAAM,OAAO,KAAKA,UAAe,IAAI;EAC1C,OAAO;CACR;;;;;CAMA,KAAK,QAAwB;EAC5B,MAAM,QAAQ,KAAK,MAAM,MAAM,OAAO;EACtC,MAAM,QAAQ,MAAM,MAAM;EAC1B,IAAI,CAAC,SAAS,MAAM,IAAI,WAAW,KAAK,MAAM,GAAG,OAAO,QAAQ;EAChE,OAAO,MAAM,MAAM;EAGnB,IAAI,MAAM,UAAU,KAAA,KAAa,KAAK,MAAM,QAAQ,MAAM,OAAO;EACjE,KAAK,MAAM,OAAO,KAAK,WAAW;GAAE,IAAI,MAAM;GAAM,IAAI,CAAC,MAAM;EAAE,CAAC;EAClE,IAAI,MAAM,QAAQ,KAAA,GAAW,KAAK,MAAM,UAAU,MAAM,MAAM,GAAG;CAClE;;CAGA,YAAgC;EAC/B,MAAM,UAAsB,CAAC;EAC7B,KAAK,MAAM,UAAU,KAAK,MAAM,QAAQ,OAAO,GAAG,IAAI,CAAC,OAAO,SAAS,QAAQ,KAAK,OAAO,EAAE;EAC7F,OAAO;CACR;AACD;;;;;;;;;AAUA,SAAgB,KAAoB,IAAc,OAA6B;CAC9E,OAAO,MAAM,SAAS,SAAoB,EAAE,OAAO,OAAO,QAAQ,KAAK,MAAM,OAAO,IAAI,IAAI,MAAM,GAAG,GAAG,KAAK,KAAK,WAAA,CAAuB,CAAC,CAAC;AAC5I;;AAGA,IAAa,WAAW,eAAeA,UAAe,SAAS,MAAM,SACpE,CAAC,KAAK,MAAM,KAAK,GAAG,SAAS,KAAK,MAAM,IAAI;CAAE,MAAM;CAAQ,MAAM,gBAAgB,IAAI;AAAE,IAAI,KAAA,CAC7F;;AAGA,IAAa,gBAAgB,eAAe,WAAW,cAAc,EAAE,IAAI,MAAM,SAChF,GAAG,SAAS,KAAK,MAAM,IAAI;CAAE,MAAM;CAAa;AAAG,IAAI,KAAA,CACxD"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { plain } from "./plain.js";
|
|
2
|
+
//#region src/effectRefs.ts
|
|
3
|
+
/** Where an effect is in the definition of an ability, found by identity; a copy of it when it is not in there */
|
|
4
|
+
function effectRef(registry, ability, effect) {
|
|
5
|
+
const path = registry.abilities.has(ability) ? find(registry.ability(ability), effect, []) : void 0;
|
|
6
|
+
return path ? { path } : { value: plain(effect, `effect of ${ability}`, "Save", "keep ids and plain objects in game state") };
|
|
7
|
+
}
|
|
8
|
+
function find(within, wanted, path) {
|
|
9
|
+
if (within === wanted) return path;
|
|
10
|
+
if (typeof within !== "object" || within === null) return void 0;
|
|
11
|
+
for (const [key, value] of Object.entries(within)) {
|
|
12
|
+
if (typeof value !== "object" || value === null) continue;
|
|
13
|
+
const found = find(value, wanted, [...path, Array.isArray(within) ? Number(key) : key]);
|
|
14
|
+
if (found) return found;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/** The effect a save kept (`effectRef`): found in the ability again, or the copy */
|
|
18
|
+
function resolveEffect(registry, ability, ref) {
|
|
19
|
+
if ("value" in ref) return structuredClone(ref.value);
|
|
20
|
+
let at = registry.ability(ability);
|
|
21
|
+
for (const key of ref.path) at = at?.[key];
|
|
22
|
+
if (at === void 0) throw new Error(`Save: an effect of "${ability}" is not where it was. Was the ability changed since?`);
|
|
23
|
+
return at;
|
|
24
|
+
}
|
|
25
|
+
//#endregion
|
|
26
|
+
export { effectRef, resolveEffect };
|
|
27
|
+
|
|
28
|
+
//# sourceMappingURL=effectRefs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"effectRefs.js","names":[],"sources":["../src/effectRefs.ts"],"sourcesContent":["/**\n * An effect of an ability as a save keeps it (`EffectRef`): where it is in the definition of the ability, found again\n * as a save is loaded, or a copy of one made up on the spot. What saves of missiles, zones and dashes are made with\n */\nimport type { EffectDef } from './defs'\nimport { plain } from './plain'\nimport type { Registry } from './registry'\n\n/** An effect of an ability: where it is in the definition of the ability, or a copy for one made up on the spot */\nexport type EffectRef = { path: (string | number)[] } | { value: unknown }\n\n/** Where an effect is in the definition of an ability, found by identity; a copy of it when it is not in there */\nexport function effectRef(registry: Registry, ability: string, effect: EffectDef | EffectDef[]): EffectRef {\n\tconst path = registry.abilities.has(ability) ? find(registry.ability(ability), effect, []) : undefined\n\treturn path ? { path } : { value: plain(effect, `effect of ${ability}`, 'Save', 'keep ids and plain objects in game state') }\n}\n\nfunction find(within: unknown, wanted: unknown, path: (string | number)[]): (string | number)[] | undefined {\n\tif (within === wanted) return path\n\tif (typeof within !== 'object' || within === null) return undefined\n\tfor (const [key, value] of Object.entries(within)) {\n\t\tif (typeof value !== 'object' || value === null) continue\n\t\tconst found = find(value, wanted, [...path, Array.isArray(within) ? Number(key) : key])\n\t\tif (found) return found\n\t}\n\treturn undefined\n}\n\n/** The effect a save kept (`effectRef`): found in the ability again, or the copy */\nexport function resolveEffect(registry: Registry, ability: string, ref: EffectRef): never {\n\tif ('value' in ref) return structuredClone(ref.value) as never\n\tlet at: unknown = registry.ability(ability)\n\tfor (const key of ref.path) at = (at as Record<string | number, unknown>)?.[key]\n\tif (at === undefined) throw new Error(`Save: an effect of \"${ability}\" is not where it was. Was the ability changed since?`)\n\treturn at as never\n}\n"],"mappings":";;;AAYA,SAAgB,UAAU,UAAoB,SAAiB,QAA4C;CAC1G,MAAM,OAAO,SAAS,UAAU,IAAI,OAAO,IAAI,KAAK,SAAS,QAAQ,OAAO,GAAG,QAAQ,CAAC,CAAC,IAAI,KAAA;CAC7F,OAAO,OAAO,EAAE,KAAK,IAAI,EAAE,OAAO,MAAM,QAAQ,aAAa,WAAW,QAAQ,0CAA0C,EAAE;AAC7H;AAEA,SAAS,KAAK,QAAiB,QAAiB,MAA4D;CAC3G,IAAI,WAAW,QAAQ,OAAO;CAC9B,IAAI,OAAO,WAAW,YAAY,WAAW,MAAM,OAAO,KAAA;CAC1D,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,MAAM,GAAG;EAClD,IAAI,OAAO,UAAU,YAAY,UAAU,MAAM;EACjD,MAAM,QAAQ,KAAK,OAAO,QAAQ,CAAC,GAAG,MAAM,MAAM,QAAQ,MAAM,IAAI,OAAO,GAAG,IAAI,GAAG,CAAC;EACtF,IAAI,OAAO,OAAO;CACnB;AAED;;AAGA,SAAgB,cAAc,UAAoB,SAAiB,KAAuB;CACzF,IAAI,WAAW,KAAK,OAAO,gBAAgB,IAAI,KAAK;CACpD,IAAI,KAAc,SAAS,QAAQ,OAAO;CAC1C,KAAK,MAAM,OAAO,IAAI,MAAM,KAAM,KAA0C;CAC5E,IAAI,OAAO,KAAA,GAAW,MAAM,IAAI,MAAM,uBAAuB,QAAQ,sDAAsD;CAC3H,OAAO;AACR"}
|
package/dist/effects.d.ts
CHANGED
|
@@ -3,21 +3,24 @@ import type { Entity, PlayerId } from './entity';
|
|
|
3
3
|
import type { Vec2 } from './math';
|
|
4
4
|
import type { World } from './world';
|
|
5
5
|
/**
|
|
6
|
-
* An effect the rules play on the screens, as the engine's `effect` event carries it (`world.
|
|
6
|
+
* An effect the rules play on the screens, as the engine's `effect` event carries it (`world.fx`)
|
|
7
7
|
*
|
|
8
8
|
* @beta
|
|
9
9
|
*/
|
|
10
|
-
export interface
|
|
11
|
-
/** The rules' handle on it, for `world.
|
|
10
|
+
export interface FxCue {
|
|
11
|
+
/** The rules' handle on it, for `world.stopFx` */
|
|
12
12
|
id: number;
|
|
13
13
|
/** Which of the view's effects, by name (`GameView.fx.defs`) */
|
|
14
14
|
effect: string;
|
|
15
15
|
/** Where: a place, at a height or on the ground */
|
|
16
16
|
x?: number;
|
|
17
|
+
/** The height it is at; on the ground when left out */
|
|
17
18
|
y?: number;
|
|
19
|
+
/** The north-south place, in cells */
|
|
18
20
|
z?: number;
|
|
19
21
|
/** Or on an entity, by its id: it follows it on the screen, at a point of its model */
|
|
20
22
|
on?: number;
|
|
23
|
+
/** The point of the entity's model it follows, by name; the entity's middle when left out */
|
|
21
24
|
point?: string;
|
|
22
25
|
/** Its knobs, over the effect's own: plain data, as it goes to every screen */
|
|
23
26
|
params?: Record<string, unknown>;
|
|
@@ -27,13 +30,19 @@ export interface EffectCue {
|
|
|
27
30
|
to?: PlayerId[];
|
|
28
31
|
/** Seen over the fog of war, by whom it is for: what is there to be noticed */
|
|
29
32
|
overFog?: boolean;
|
|
33
|
+
/** The other end of one drawn between two (`FxOptions.toward`): an entity by its id */
|
|
34
|
+
toward?: number;
|
|
35
|
+
/** Or a place, east-west */
|
|
36
|
+
tx?: number;
|
|
37
|
+
/** And north-south */
|
|
38
|
+
tz?: number;
|
|
30
39
|
}
|
|
31
40
|
/**
|
|
32
|
-
* How the rules play an effect (`world.
|
|
41
|
+
* How the rules play an effect (`world.fx`)
|
|
33
42
|
*
|
|
34
43
|
* @beta
|
|
35
44
|
*/
|
|
36
|
-
export interface
|
|
45
|
+
export interface FxOptions {
|
|
37
46
|
/** A place: on the ground (`{ x, z }`) or at a height (`{ x, y, z }`) */
|
|
38
47
|
at?: Vec2 | {
|
|
39
48
|
x: number;
|
|
@@ -52,6 +61,13 @@ export interface EffectOptions {
|
|
|
52
61
|
to?: Audience;
|
|
53
62
|
/** Seen over the fog of war by whom it is for, as what is there to be noticed. No when left out */
|
|
54
63
|
overFog?: boolean;
|
|
64
|
+
/**
|
|
65
|
+
* The other end, for an effect drawn between two (a beam, a tether, a link of a chain: the client's `linkFx`): an
|
|
66
|
+
* entity it follows on the screen, or a place. It starts at `at` or `on`; seen by whoever sees either end
|
|
67
|
+
*
|
|
68
|
+
* @beta
|
|
69
|
+
*/
|
|
70
|
+
toward?: Entity | number | Vec2;
|
|
55
71
|
}
|
|
56
72
|
/**
|
|
57
73
|
* How many effects a world played, for their ids and their seeds: the world's, and saved with it, so that the game
|
|
@@ -60,21 +76,21 @@ export interface EffectOptions {
|
|
|
60
76
|
export declare const played: import("./components").Component<number, number>;
|
|
61
77
|
/**
|
|
62
78
|
* Plays one of the view's effects on the screens of the players who see where it is (or those it is for), now. What it
|
|
63
|
-
* gives back stops it (`
|
|
79
|
+
* gives back stops it (`stopFx`). Not in the world's state but for its count: a save, and a player who joins later,
|
|
64
80
|
* know nothing of the effect itself
|
|
65
81
|
*
|
|
66
82
|
* @beta
|
|
67
83
|
*/
|
|
68
|
-
export declare function
|
|
84
|
+
export declare function playFx(world: World, effect: string, options?: FxOptions): number;
|
|
69
85
|
/**
|
|
70
|
-
* Stops an effect the rules played (`
|
|
86
|
+
* Stops an effect the rules played (`playFx`), on every screen: as its own `stop` says, over `fade` seconds
|
|
71
87
|
*
|
|
72
88
|
* @beta
|
|
73
89
|
*/
|
|
74
|
-
export declare function
|
|
90
|
+
export declare function stopFx(world: World, id: number, options?: {
|
|
75
91
|
fade?: number;
|
|
76
92
|
}): void;
|
|
77
93
|
/** An effect the rules play: to whom it is for, who sees where it is (what is there to be noticed, over the fog) */
|
|
78
|
-
export declare const
|
|
94
|
+
export declare const toldFx: import("./replication").ReplicatedEvent;
|
|
79
95
|
/** An effect stopped: to whom saw it start */
|
|
80
|
-
export declare const
|
|
96
|
+
export declare const toldFxStopped: import("./replication").ReplicatedEvent;
|