@v12-engine/engine 0.0.0-stage → 0.1.1
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/LICENSE +21 -0
- package/dist/abilities/buffs.d.ts +83 -0
- package/dist/abilities/castTold.d.ts +12 -0
- package/dist/abilities/charges.d.ts +28 -0
- package/dist/abilities/dispel.d.ts +9 -0
- package/dist/abilities/effectFields.d.ts +10 -0
- package/dist/abilities/effects/buff.d.ts +8 -0
- package/dist/abilities/effects/core.d.ts +1 -0
- package/dist/abilities/effects/damage.d.ts +1 -0
- package/dist/abilities/effects/heal.d.ts +2 -0
- package/dist/abilities/effects/missile.d.ts +1 -0
- package/dist/abilities/effects/moves.d.ts +11 -0
- package/dist/abilities/effects/passives.d.ts +15 -0
- package/dist/abilities/effects/sight.d.ts +4 -0
- package/dist/abilities/effects/spawn.d.ts +2 -0
- package/dist/abilities/effects/timed.d.ts +4 -0
- package/dist/abilities/effects/trade.d.ts +1 -0
- package/dist/abilities/effects.d.ts +122 -0
- package/dist/abilities/land.d.ts +21 -0
- package/dist/abilities/scaled.d.ts +21 -0
- package/dist/abilities/text.d.ts +113 -0
- package/dist/ai/area.d.ts +53 -0
- package/dist/ai/brain.d.ts +528 -0
- package/dist/ai/budget.d.ts +67 -0
- package/dist/ai/checks.d.ts +115 -0
- package/dist/ai/computer.d.ts +166 -0
- package/dist/ai/defence.d.ts +108 -0
- package/dist/ai/define.d.ts +21 -0
- package/dist/ai/economy.d.ts +129 -0
- package/dist/ai/geometry.d.ts +35 -0
- package/dist/ai/goals.d.ts +360 -0
- package/dist/ai/index.d.ts +38 -0
- package/dist/ai/knowledge.d.ts +418 -0
- package/dist/ai/lanes.d.ts +232 -0
- package/dist/ai/melee/build.d.ts +61 -0
- package/dist/ai/melee/goals.d.ts +299 -0
- package/dist/ai/melee/heroes.d.ts +27 -0
- package/dist/ai/melee/index.d.ts +14 -0
- package/dist/ai/melee/production.d.ts +8 -0
- package/dist/ai/melee/surrender.d.ts +24 -0
- package/dist/ai/micro.d.ts +278 -0
- package/dist/ai/mission.d.ts +195 -0
- package/dist/ai/missionRules.d.ts +18 -0
- package/dist/ai/module.d.ts +116 -0
- package/dist/ai/options.d.ts +61 -0
- package/dist/ai/plans/build.d.ts +280 -0
- package/dist/ai/plans/placement.d.ts +82 -0
- package/dist/ai/plans/plans.d.ts +105 -0
- package/dist/ai/plans/production.d.ts +210 -0
- package/dist/ai/plans/purse.d.ts +73 -0
- package/dist/ai/plans/tech.d.ts +37 -0
- package/dist/ai/profiles.d.ts +204 -0
- package/dist/ai/reach.d.ts +4 -0
- package/dist/ai/report.d.ts +203 -0
- package/dist/ai/roles.d.ts +133 -0
- package/dist/ai/rules.d.ts +288 -0
- package/dist/ai/scouting.d.ts +98 -0
- package/dist/ai/settings.d.ts +73 -0
- package/dist/ai/squads.d.ts +429 -0
- package/dist/ai/strength.d.ts +167 -0
- package/dist/ai/task.d.ts +118 -0
- package/dist/ai/tasks/attack.d.ts +7 -0
- package/dist/ai/tasks/capture.d.ts +21 -0
- package/dist/ai/tasks/creep.d.ts +16 -0
- package/dist/ai/tasks/defend.d.ts +29 -0
- package/dist/ai/tasks/drop.d.ts +34 -0
- package/dist/ai/tasks/escort.d.ts +16 -0
- package/dist/ai/tasks/gather.d.ts +23 -0
- package/dist/ai/tasks/harass.d.ts +7 -0
- package/dist/ai/tasks/hold.d.ts +20 -0
- package/dist/ai/tasks/index.d.ts +32 -0
- package/dist/ai/tasks/move.d.ts +19 -0
- package/dist/ai/tasks/patrol.d.ts +8 -0
- package/dist/ai/tasks/retreat.d.ts +20 -0
- package/dist/ai/tasks/scout.d.ts +29 -0
- package/dist/ai/tasks/shared.d.ts +74 -0
- package/dist/ai/team.d.ts +155 -0
- package/dist/ai/threat.d.ts +91 -0
- package/dist/ai/view.d.ts +366 -0
- package/dist/ai/waves.d.ts +126 -0
- package/dist/ai.js +6239 -0
- package/dist/ai.js.map +1 -0
- package/dist/aiTesting.d.ts +179 -0
- package/dist/areas.d.ts +149 -0
- package/dist/attacks.d.ts +66 -0
- package/dist/attributes.d.ts +66 -0
- package/dist/brain-BvVdUhX8.js +7659 -0
- package/dist/brain-BvVdUhX8.js.map +1 -0
- package/dist/builds-C6LfgvEU.js +460 -0
- package/dist/builds-C6LfgvEU.js.map +1 -0
- package/dist/bytes.d.ts +6 -0
- package/dist/camps.d.ts +95 -0
- package/dist/capture.d.ts +131 -0
- package/dist/cellLayers.d.ts +75 -0
- package/dist/cinematics.d.ts +194 -0
- package/dist/clock.d.ts +32 -0
- package/dist/commands.d.ts +235 -0
- package/dist/components.d.ts +184 -0
- package/dist/computers.d.ts +108 -0
- package/dist/concealment.d.ts +61 -0
- package/dist/coreComponents-syuh2fPG.js +5149 -0
- package/dist/coreComponents-syuh2fPG.js.map +1 -0
- package/dist/coreComponents.d.ts +1 -0
- package/dist/dashing.d.ts +8 -0
- package/dist/debug.d.ts +36 -0
- package/dist/debug.js +3 -0
- package/dist/debugCommands-CzjrSm66.js +14739 -0
- package/dist/debugCommands-CzjrSm66.js.map +1 -0
- package/dist/debugCommands.d.ts +213 -0
- package/dist/debugging.d.ts +10 -0
- package/dist/definitions.d.ts +43 -0
- package/dist/defs.d.ts +1162 -0
- package/dist/denying.d.ts +81 -0
- package/dist/destructibles.d.ts +27 -0
- package/dist/determinism-BCymQwiE.js +518 -0
- package/dist/determinism-BCymQwiE.js.map +1 -0
- package/dist/determinism.d.ts +121 -0
- package/dist/dialog.d.ts +94 -0
- package/dist/effectRefs.d.ts +16 -0
- package/dist/effects.d.ts +80 -0
- package/dist/endings.d.ts +22 -0
- package/dist/engagement.d.ts +108 -0
- package/dist/engineEvents.d.ts +710 -0
- package/dist/entity.d.ts +409 -0
- package/dist/entityIndex.d.ts +50 -0
- package/dist/errors.d.ts +85 -0
- package/dist/events.d.ts +97 -0
- package/dist/exactMath.d.ts +63 -0
- package/dist/features.d.ts +10 -0
- package/dist/fields.d.ts +114 -0
- package/dist/fixture.d.ts +17 -0
- package/dist/fog.d.ts +110 -0
- package/dist/formation.d.ts +111 -0
- package/dist/friendlyFire.d.ts +26 -0
- package/dist/game-DiJEN-Ks.js +5800 -0
- package/dist/game-DiJEN-Ks.js.map +1 -0
- package/dist/game.d.ts +501 -0
- package/dist/gameData.d.ts +325 -0
- package/dist/gameFiles.d.ts +26 -0
- package/dist/gates.d.ts +28 -0
- package/dist/generate.d.ts +403 -0
- package/dist/generateWorker.d.ts +39 -0
- package/dist/grid.d.ts +53 -0
- package/dist/ground.d.ts +127 -0
- package/dist/groundSpeed.d.ts +22 -0
- package/dist/hero.d.ts +177 -0
- package/dist/home.d.ts +14 -0
- package/dist/hooks.d.ts +780 -0
- package/dist/hotReload.d.ts +76 -0
- package/dist/ids.d.ts +26 -0
- package/dist/index.d.ts +227 -0
- package/dist/index.js +4701 -0
- package/dist/index.js.map +1 -0
- package/dist/inspect-D9dWHmZ0.js +491 -0
- package/dist/inspect-D9dWHmZ0.js.map +1 -0
- package/dist/inspect.d.ts +176 -0
- package/dist/inventory.d.ts +18 -0
- package/dist/items.d.ts +91 -0
- package/dist/lanes.d.ts +213 -0
- package/dist/life.d.ts +53 -0
- package/dist/local-Dkovq42k.js +1379 -0
- package/dist/local-Dkovq42k.js.map +1 -0
- package/dist/log.d.ts +140 -0
- package/dist/manaBurn.d.ts +39 -0
- package/dist/map/file.d.ts +114 -0
- package/dist/map/images.d.ts +18 -0
- package/dist/map/look.d.ts +47 -0
- package/dist/map/pathProfile.d.ts +13 -0
- package/dist/map/resize.d.ts +54 -0
- package/dist/map/schema.d.ts +10 -0
- package/dist/map/terrain.d.ts +32 -0
- package/dist/map/types.d.ts +613 -0
- package/dist/map.d.ts +7 -0
- package/dist/mapCheck/cells.d.ts +19 -0
- package/dist/mapCheck/rule.d.ts +30 -0
- package/dist/mapCheck/rules/names.d.ts +3 -0
- package/dist/mapCheck/rules/objects.d.ts +3 -0
- package/dist/mapCheck/rules/paths.d.ts +3 -0
- package/dist/mapCheck/rules/players.d.ts +3 -0
- package/dist/mapCheck/rules/races.d.ts +3 -0
- package/dist/mapCheck/rules/random.d.ts +3 -0
- package/dist/mapCheck/rules/regions.d.ts +3 -0
- package/dist/mapCheck/rules/targets.d.ts +3 -0
- package/dist/mapCheck/rules/walking.d.ts +3 -0
- package/dist/mapCheck.d.ts +23 -0
- package/dist/mapFields.d.ts +364 -0
- package/dist/mapFile.d.ts +23 -0
- package/dist/mapMeasure.d.ts +95 -0
- package/dist/mapMerge.d.ts +19 -0
- package/dist/mapObjectives.d.ts +82 -0
- package/dist/mapObjects.d.ts +1 -0
- package/dist/math.d.ts +44 -0
- package/dist/messages.d.ts +150 -0
- package/dist/missionRules-C-bGxm0Q.js +523 -0
- package/dist/missionRules-C-bGxm0Q.js.map +1 -0
- package/dist/missionRules.d.ts +179 -0
- package/dist/mods.d.ts +146 -0
- package/dist/names.d.ts +93 -0
- package/dist/navigation/agentParams.d.ts +4 -0
- package/dist/navigation/air.d.ts +89 -0
- package/dist/navigation/builds.d.ts +140 -0
- package/dist/navigation/groundAreas.d.ts +23 -0
- package/dist/navigation/navigation.d.ts +127 -0
- package/dist/navigation/recast.d.ts +214 -0
- package/dist/navigation/sidePaths.d.ts +69 -0
- package/dist/navigation/sized.d.ts +1 -0
- package/dist/navigation-worker.js +11 -0
- package/dist/navigation-worker.js.map +1 -0
- package/dist/nearest.d.ts +8 -0
- package/dist/noise.d.ts +12 -0
- package/dist/objectives.d.ts +93 -0
- package/dist/orders/basic.d.ts +96 -0
- package/dist/orders/build.d.ts +91 -0
- package/dist/orders/cast.d.ts +105 -0
- package/dist/orders/facts.d.ts +14 -0
- package/dist/orders/harvest.d.ts +94 -0
- package/dist/orders/idle.d.ts +7 -0
- package/dist/orders/items.d.ts +14 -0
- package/dist/orders/queue.d.ts +24 -0
- package/dist/pack.schema.json +6621 -0
- package/dist/packs/director/index.d.ts +81 -0
- package/dist/packs/heroes/campaign.d.ts +137 -0
- package/dist/packs/heroes/carry.d.ts +59 -0
- package/dist/packs/heroes/commands.d.ts +6 -0
- package/dist/packs/heroes/hire.d.ts +9 -0
- package/dist/packs/heroes/index.d.ts +12 -0
- package/dist/packs/heroes/making.d.ts +16 -0
- package/dist/packs/heroes/respawn.d.ts +42 -0
- package/dist/packs/moba/index.d.ts +34 -0
- package/dist/packs/pack.d.ts +84 -0
- package/dist/packs-g5thvmGn.js +3199 -0
- package/dist/packs-g5thvmGn.js.map +1 -0
- package/dist/packs.d.ts +84 -0
- package/dist/paths.d.ts +51 -0
- package/dist/plain.d.ts +17 -0
- package/dist/player.d.ts +117 -0
- package/dist/portals.d.ts +66 -0
- package/dist/prefabs.d.ts +139 -0
- package/dist/presetBuildings.d.ts +52 -0
- package/dist/presetGround.d.ts +22 -0
- package/dist/profiles-B3NS9jwk.js +335 -0
- package/dist/profiles-B3NS9jwk.js.map +1 -0
- package/dist/races.d.ts +136 -0
- package/dist/randomGroups.d.ts +2 -0
- package/dist/registry.d.ts +464 -0
- package/dist/remains.d.ts +119 -0
- package/dist/replication.d.ts +103 -0
- package/dist/rng.d.ts +17 -0
- package/dist/rules.d.ts +395 -0
- package/dist/rulesCheck.d.ts +37 -0
- package/dist/runes.d.ts +41 -0
- package/dist/save.d.ts +277 -0
- package/dist/scenario.d.ts +56 -0
- package/dist/scheduler.d.ts +67 -0
- package/dist/score.d.ts +306 -0
- package/dist/sequences.d.ts +132 -0
- package/dist/server/doorman.d.ts +21 -0
- package/dist/server/gameServer.d.ts +346 -0
- package/dist/server/index.d.ts +24 -0
- package/dist/server/lobby.d.ts +1 -0
- package/dist/server/log.d.ts +13 -0
- package/dist/server/match.d.ts +24 -0
- package/dist/server/mode.d.ts +168 -0
- package/dist/server/navigation-worker.d.ts +1 -0
- package/dist/server/navigation.d.ts +11 -0
- package/dist/server/ops.d.ts +78 -0
- package/dist/server/packs.d.ts +15 -0
- package/dist/server/ratings.d.ts +62 -0
- package/dist/server/relay.d.ts +34 -0
- package/dist/server/rooms.d.ts +162 -0
- package/dist/server/seat.d.ts +2 -0
- package/dist/server/seatSettings.d.ts +15 -0
- package/dist/server/secret.d.ts +5 -0
- package/dist/server/start.d.ts +54 -0
- package/dist/server/websocket.d.ts +74 -0
- package/dist/server.js +2861 -0
- package/dist/server.js.map +1 -0
- package/dist/session/can.d.ts +20 -0
- package/dist/session/commandReplay.d.ts +92 -0
- package/dist/session/debug.d.ts +65 -0
- package/dist/session/delay.d.ts +15 -0
- package/dist/session/delta.d.ts +98 -0
- package/dist/session/entityState.d.ts +1 -0
- package/dist/session/local.d.ts +214 -0
- package/dist/session/memory.d.ts +27 -0
- package/dist/session/protocol.d.ts +1155 -0
- package/dist/session/remote.d.ts +530 -0
- package/dist/session/replay.d.ts +114 -0
- package/dist/session/seed.d.ts +7 -0
- package/dist/session/share.d.ts +78 -0
- package/dist/session/socket.d.ts +8 -0
- package/dist/session/wire.d.ts +153 -0
- package/dist/session/worker.d.ts +227 -0
- package/dist/shared.d.ts +171 -0
- package/dist/sides.d.ts +75 -0
- package/dist/sight.d.ts +27 -0
- package/dist/simulate-oXBiw1Ff.js +167 -0
- package/dist/simulate-oXBiw1Ff.js.map +1 -0
- package/dist/simulate-worker.d.ts +1 -0
- package/dist/simulate-worker.js +28 -0
- package/dist/simulate-worker.js.map +1 -0
- package/dist/simulate.d.ts +154 -0
- package/dist/smart.d.ts +9 -0
- package/dist/spatial.d.ts +62 -0
- package/dist/spawning.d.ts +33 -0
- package/dist/stances.d.ts +76 -0
- package/dist/start.d.ts +71 -0
- package/dist/stats.d.ts +122 -0
- package/dist/summons.d.ts +63 -0
- package/dist/systems/abilities.d.ts +3 -0
- package/dist/systems/combat.d.ts +71 -0
- package/dist/systems/combatTold.d.ts +14 -0
- package/dist/systems/construction.d.ts +34 -0
- package/dist/systems/economy.d.ts +21 -0
- package/dist/systems/fog.d.ts +19 -0
- package/dist/systems/life.d.ts +3 -0
- package/dist/systems/movement.d.ts +105 -0
- package/dist/systems/orders.d.ts +7 -0
- package/dist/systems/production.d.ts +49 -0
- package/dist/systems/productionQueue.d.ts +40 -0
- package/dist/systems/victory.d.ts +14 -0
- package/dist/talk.d.ts +65 -0
- package/dist/tech.d.ts +62 -0
- package/dist/techTree.d.ts +38 -0
- package/dist/terrain.d.ts +173 -0
- package/dist/terrainSave.d.ts +29 -0
- package/dist/testing.d.ts +58 -0
- package/dist/testing.js +822 -0
- package/dist/testing.js.map +1 -0
- package/dist/tiles.d.ts +34 -0
- package/dist/timers.d.ts +146 -0
- package/dist/tomes.d.ts +48 -0
- package/dist/trade.d.ts +28 -0
- package/dist/transport.d.ts +76 -0
- package/dist/tunables.d.ts +45 -0
- package/dist/upkeep.d.ts +16 -0
- package/dist/veterancy.d.ts +80 -0
- package/dist/walls.d.ts +86 -0
- package/dist/world.d.ts +1118 -0
- package/package.json +80 -5
- package/README.md +0 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"determinism-BCymQwiE.js","names":[],"sources":["../src/packs/heroes/hire.ts","../src/packs/heroes/commands.ts","../src/packs/heroes/making.ts","../src/packs/heroes/index.ts","../src/determinism.ts"],"sourcesContent":["import { unitHired } from '#engine/engineEvents'\nimport type { Entity, PlayerId } from '#engine/entity'\nimport { heroesOf } from '#engine/hero'\nimport { atShop } from '#engine/items'\nimport { engineMessage } from '#engine/messages'\nimport { shortOf, spend } from '#engine/player'\nimport type { World } from '#engine/world'\n\n/**\n * Whether someone of a player's stands at a place that hires (`UnitTypeDef.hires`) to hire there: any unit of theirs\n * close enough, as at a shop. The first one found\n *\n * @internal\n */\nexport function hirerAt(world: World, shop: Entity, player: PlayerId): Entity | undefined {\n\tfor (const entity of world.index.ownedBy(player))\n\t\tif (entity.alive && !entity.hidden && entity.def.kind === 'unit' && atShop(entity, shop)) return entity\n\treturn undefined\n}\n\n/**\n * Why a player cannot hire a unit at a place now, as the message the player is told: none of theirs there, a hero\n * they have already (a hero is hired once), what it costs. Nothing when they can\n *\n * @internal\n */\nexport function hireRefusal(world: World, shop: Entity, unit: string, player: PlayerId) {\n\tif (!shop.def.hires?.includes(unit) || !world.registry.units.has(unit)) return engineMessage('cannotDoThat')\n\tif (!shop.alive || !shop.operational || world.areEnemies(shop.owner, player)) return engineMessage('cannotDoThat')\n\tconst def = world.registry.unit(unit)\n\tif (def.hero && heroesOf(world, player).some((hero) => hero.def.id === unit)) return engineMessage('alreadyHired', { unit })\n\tif (!hirerAt(world, shop, player)) return engineMessage('unitAtShop')\n\tconst short = shortOf(world.player(player).resources, def.cost)\n\tif (short) return engineMessage('notEnough', { resource: short })\n\treturn undefined\n}\n\n/**\n * Hires a unit at a place that hires for a player: a tavern's hero, a dock's boat. Paid for, and made next to the\n * place, a boat on the water nearest it. The unit, or nothing with the player told why\n *\n * @beta\n */\nexport function hire(world: World, shop: Entity, unit: string, player: PlayerId): Entity | undefined {\n\tconst refusal = hireRefusal(world, shop, unit, player)\n\tif (refusal) {\n\t\tworld.message(player, refusal)\n\t\treturn undefined\n\t}\n\tconst def = world.registry.unit(unit)\n\tspend(world, player, def.cost, 'hire', unit)\n\tconst hired = world.spawn(unit, player, { x: shop.pos.x, z: shop.pos.z + shop.def.radius + def.radius + 0.5 })\n\tworld.events.emit(unitHired, { shop, unit: hired, player })\n\treturn hired\n}\n","/**\n * The heroes pack's commands: buying and selling at a shop, hiring, putting a point into an ability, bringing a hero\n * back at an altar. Taken by a world whose game has the pack on (`withHeroes`); a game without it refuses them as it\n * does any command it does not know. Their type is among `Command`s, as the pack adds it (`Commands`)\n */\nimport { defineCommand, known, leastBusy, owned, producers } from '#engine/commands'\nimport { id, index, optional, string } from '#engine/fields'\nimport { learn } from '#engine/hero'\nimport { buy, sell } from '#engine/items'\nimport { enqueueRevive } from '#engine/systems/production'\nimport { hire } from './hire'\n\n/**\n * Buys from a shop, for the hero that is named, or else the one of the player that stands closest to it\n *\n * @internal\n */\nexport const buyCommand = defineCommand('buy', { shop: id, item: string, hero: optional(id) }, (world, player, command) => {\n\t// shops sell to whoever comes by, not only to their owner\n\tconst shop = known(world, player, command.shop)\n\tconst [buyer] = command.hero === undefined ? [] : owned(world, player, [command.hero])\n\tif (shop && !world.areEnemies(shop.owner, player)) buy(world, shop, command.item, player, buyer)\n})\n\n/**\n * Hires a unit at a place that hires, a tavern or a dock: someone of the player's has to be there\n *\n * @internal\n */\nexport const hireCommand = defineCommand('hire', { shop: id, unit: string }, (world, player, command) => {\n\tconst shop = known(world, player, command.shop)\n\tif (shop) hire(world, shop, command.unit, player)\n})\n\n/**\n * Gives something a hero carries back at a shop nearby, for a part of its price\n *\n * @internal\n */\nexport const sellCommand = defineCommand('sell', { entity: id, item: index }, (world, player, command) => {\n\tconst [seller] = owned(world, player, [command.entity])\n\tif (seller) sell(world, seller, command.item)\n})\n\n/**\n * Puts a point a hero has to spend into an ability\n *\n * @internal\n */\nexport const learnCommand = defineCommand('learn', { entity: id, ability: string }, (world, player, command) => {\n\tconst [hero] = owned(world, player, [command.entity])\n\tif (hero) learn(world, hero, command.ability)\n})\n\n/** @internal */\nexport const reviveCommand = defineCommand('revive', { ...producers, hero: id }, (world, player, command) => {\n\tconst building = leastBusy(world, player, command, (entity) => !!entity.def.revives)\n\tif (building) enqueueRevive(world, building, command.hero)\n})\n\n/**\n * The commands of the heroes pack (`withHeroes`): buying, hiring, selling, learning, bringing a hero back. A game\n * without the pack refuses them as it does any command it does not know\n *\n * @internal\n */\nexport const heroCommands = [buyCommand, hireCommand, sellCommand, learnCommand, reviveCommand] as const\n\ndeclare module '#engine/commands' {\n\tinterface Commands {\n\t\theroes: (typeof heroCommands)[number]\n\t}\n}\n","/**\n * What the heroes pack does as heroes are made and as things die: a hero of a hero's type made one, and the\n * experience of a kill shared out (the pack's `made` and `death` handlers)\n */\nimport type { Entity } from '#engine/entity'\nimport { grantXp, heroOf, heroState, maxLevelOf, xpWorth } from '#engine/hero'\nimport { distSq } from '#engine/math'\nimport type { World } from '#engine/world'\n\n/**\n * Makes a unit just made a hero, as its type says (`UnitTypeDef.hero`): level 1, a point to spend, the abilities it can\n * learn (none learned) and the spells it starts with, those it has not got already. What `World.spawn` does for a\n * hero's type, and `morph` for what becomes one\n *\n * @beta\n */\nexport function makeHero(world: World, entity: Entity): void {\n\tconst def = entity.def.hero\n\tif (!def) return\n\tentity.set(heroState, { level: 1, xp: 0, points: 1 })\n\t// what it has already (a unit turned into a hero, `morph`) as it was\n\tconst add = (id: string, level: number) => {\n\t\tif (!entity.abilities.some((state) => state.id === id))\n\t\t\tentity.addAbility({ id: world.registry.ability(id).id, level, cooldown: 0, autocast: false })\n\t}\n\tfor (const id of def.learns) add(id, 0)\n\tfor (const id of def.spells ?? []) add(id, 1)\n}\n\n/** Shares the experience for a kill between the heroes of the killer's side that were around */\nexport function awardKill(world: World, dead: Entity, killer: Entity | undefined): void {\n\tif (!killer || !world.areEnemies(killer.owner, dead.owner)) return\n\tconst worth = xpWorth(world, dead)\n\tif (worth <= 0) return\n\tconst heroes: Entity[] = []\n\tconst { xpRange } = world.rules.heroes\n\tfor (const player of world.players.values()) {\n\t\tif (!world.areAllies(player.id, killer.owner)) continue\n\t\tfor (const entity of world.index.with(heroState, player.id)) {\n\t\t\tif (!entity.alive || heroOf(entity)!.level >= maxLevelOf(world, entity)) continue\n\t\t\tif (distSq(entity.pos, dead.pos) <= xpRange * xpRange) heroes.push(entity)\n\t\t}\n\t}\n\tfor (const hero of heroes) grantXp(world, hero, worth / heroes.length, dead)\n}\n","import type { CommandDef } from '#engine/commands'\nimport { heroCommands } from './commands'\nimport { campHooks, campRespawnSystem } from '#engine/camps'\nimport { defineHook, type HookDef } from '#engine/hooks'\nimport { defaultHeroRules, HEROES, type HeroesPack, type HeroRules, heroOf, heroState } from '#engine/hero'\nimport { awardKill, makeHero } from './making'\nimport { shopStock } from '#engine/items'\nimport { corpseSystem, worthless } from '#engine/summons'\nimport type { System } from '#engine/registry'\nimport type { StatPart } from '#engine/stats'\nimport type { Entity } from '#engine/entity'\nimport type { World } from '#engine/world'\n\n/** A shop's wares, each as many as it stocks; what it stocked already as it was, where it sells it still */\nfunction stockShop(world: World, entity: Entity): void {\n\tconst sells = entity.def.sells ?? []\n\tconst had = entity.get(shopStock)\n\tif (!sells.length) {\n\t\tif (had) entity.remove(shopStock)\n\t\treturn\n\t}\n\tentity.set(\n\t\tshopStock,\n\t\tsells.map((item) => {\n\t\t\tconst kept = had?.find((stock) => stock.item === item)\n\t\t\tif (kept) return kept\n\t\t\tconst stock = world.registry.item(item).stock\n\t\t\treturn { item, count: stock?.max ?? 1, restock: stock?.restock ?? 60 }\n\t\t})\n\t)\n}\n\n/** What the pack does as things are made, change type, die and fall: heroes, their experience, shops' wares */\nconst heroHooks: HookDef[] = [\n\tdefineHook({\n\t\thook: 'made',\n\t\tid: 'hero',\n\t\trun(world, entity) {\n\t\t\t// a hero of a hero's type: level 1, a point to spend, what it learns\n\t\t\tif (entity.def.hero) makeHero(world, entity)\n\t\t\tstockShop(world, entity)\n\t\t}\n\t}),\n\tdefineHook({\n\t\thook: 'morphed',\n\t\tid: 'hero',\n\t\trun(world, { entity }) {\n\t\t\t// what became a hero's type is a hero from level 1; a hero that stays one keeps its level and what it learned\n\t\t\tif (entity.def.hero && !entity.has(heroState)) makeHero(world, entity)\n\t\t\t// what became a shop stocks its wares; one that sells no more has none\n\t\t\tif (entity.def.sells?.length || entity.has(shopStock)) stockShop(world, entity)\n\t\t}\n\t}),\n\tdefineHook({\n\t\thook: 'death',\n\t\tid: 'experience',\n\t\trun(world, { entity, killer }) {\n\t\t\t// an illusion gains its killer nothing\n\t\t\tif (worthless(entity)) return\n\t\t\tawardKill(world, entity, killer)\n\t\t}\n\t}),\n\tdefineHook({\n\t\thook: 'fallenHero',\n\t\tid: 'heroesStay',\n\t\trun(_world, event) {\n\t\t\t// a fallen hero waits out of the world to be brought back: at an altar, by its own time (`withRespawn`)\n\t\t\tif (event.entity.has(heroState)) event.stays = true\n\t\t}\n\t}),\n\t...campHooks\n]\n\n/**\n * A hero's stats grow with its level, as its type says (`UnitTypeDef.hero.growth`): stamped with the level past the\n * first, so that a level set however it is set is added up again\n */\nconst heroGrowth: StatPart = {\n\tid: 'heroGrowth',\n\tstamp(entity) {\n\t\tif (!entity.def.hero) return 0\n\t\tconst level = heroOf(entity)?.level ?? 1\n\t\treturn level > 1 ? level : 0\n\t},\n\tadd(_world, entity, sum) {\n\t\tconst growth = entity.def.hero?.growth\n\t\tconst hero = growth && heroOf(entity)\n\t\tif (hero && hero.level > 1) sum.add(growth, hero.level - 1)\n\t}\n}\n\n/** Camps come back as the engine's own did: in the clean-up, before the corpses go */\nconst campsComeBack: System = { ...campRespawnSystem, before: [corpseSystem.name] }\n\n/**\n * The heroes pack: what makes a game a classic hero RTS, opted into (`GameModule.packs: [withHeroes()]`). Heroes, with\n * their experience, levels, points and abilities to learn, brought back at an altar (`revive`); creeps' camps that guard\n * their ground, sleep at night and come back (`Camp.respawn`); shops that sell (`buy`, `sell`) and places that hire\n * (`hire`). `rules` change its numbers (`HeroRules`: how many heroes, the experience a level takes), the classic RTS numbers for\n * those left out. A game without it pays nothing for any of it: no hero is made of a hero's type, no command of these\n * is taken, no camp is looked at\n *\n * @beta\n */\nexport function withHeroes(rules: Partial<HeroRules> = {}): HeroesPack {\n\tfor (const key of Object.keys(rules)) if (!(key in defaultHeroRules)) throw new Error(`\"${key}\" is not a rule of the heroes pack`)\n\treturn {\n\t\tid: HEROES,\n\t\toptions: rules,\n\t\trules: () => ({ heroes: rules }),\n\t\tcommands: [...heroCommands] as unknown as CommandDef[],\n\t\tsystems: [campsComeBack],\n\t\thooks: heroHooks,\n\t\tstatParts: [heroGrowth]\n\t}\n}\n","/**\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 GameModule, 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.hp,\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\tgame?: GameModule\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, game: rules })\n * expect(report).toEqual({ twice: null, fromSave: null })\n *\n * @beta\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.game) 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))), { game: options.game! })\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', 'actors']\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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"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether a game comes out the same (determinism plan D1): the simulation's state as data and as a hash, and a check
|
|
3
|
+
* that runs a world twice, and on from a save, and says the first tick and the first thing they differ at. What a
|
|
4
|
+
* replay of commands, a save that goes on exactly and a game the same in every browser stand on
|
|
5
|
+
*/
|
|
6
|
+
import { type GameModule } from './game';
|
|
7
|
+
import { type WorldSave } from './save';
|
|
8
|
+
import type { Registry } from './registry';
|
|
9
|
+
import type { World } from './world';
|
|
10
|
+
/**
|
|
11
|
+
* The simulation's state as plain data: its save, which holds all of it, but what each team's screens remember of the
|
|
12
|
+
* map, which is the watchers' (`teamMemory`)
|
|
13
|
+
*
|
|
14
|
+
* @beta
|
|
15
|
+
*/
|
|
16
|
+
export declare function worldState(world: World): WorldSave;
|
|
17
|
+
/**
|
|
18
|
+
* Where two saves of a game first differ, as `checkDeterminism` looks (what decides first: the tick, the random state,
|
|
19
|
+
* the players, the entities): the path to it (`entities[id 11].pos.x`) and what each holds there, or nothing when they
|
|
20
|
+
* are the same. What the screens remember of the map, and the hash of the rules they were made with, are left out;
|
|
21
|
+
* with the game's `registry`, so are components that hold what they are made with (a screen asking for one makes it).
|
|
22
|
+
* What a replay that came apart says at its checkpoints (`runReplay`), and `v12 save diff`
|
|
23
|
+
*
|
|
24
|
+
* @beta
|
|
25
|
+
*/
|
|
26
|
+
export declare function saveDifference(a: WorldSave, b: WorldSave, registry?: Registry): Difference | undefined;
|
|
27
|
+
/**
|
|
28
|
+
* The simulation's state as a hash: two worlds with the same hash are, to all that is saved of them, the same. Fourteen
|
|
29
|
+
* hex digits (cyrb53, of the save as JSON, but the hash of the rules, which another build of the same code writes
|
|
30
|
+
* otherwise): enough to tell drift, not to stand against someone forging a state
|
|
31
|
+
*
|
|
32
|
+
* @beta
|
|
33
|
+
*/
|
|
34
|
+
export declare const worldHash: (world: World) => string;
|
|
35
|
+
/**
|
|
36
|
+
* A quick hash of what a game that came apart differs in first: the tick, the random state, each player's resources,
|
|
37
|
+
* upgrades and supply, and each entity's id, type, owner, place, facing, health, mana, whether it lives, its orders (how
|
|
38
|
+
* many, and the first's kind and target), its buffs (which, and the time left) and its attack's cooldown and swing. Read
|
|
39
|
+
* off the world as it is, with no save: 0.4 ms at 1000 units a side, where `worldHash` takes 66. What a
|
|
40
|
+
* replay of commands keeps every few seconds and checks as it plays (`recordCommands`), and what `checkDeterminism`
|
|
41
|
+
* compares every tick, for the tick a game came apart at; what it leaves out (components, areas, the fog) the whole
|
|
42
|
+
* state, compared every so often, has
|
|
43
|
+
*
|
|
44
|
+
* @beta
|
|
45
|
+
*/
|
|
46
|
+
export declare function worldFingerprint(world: World): string;
|
|
47
|
+
/**
|
|
48
|
+
* Where two states first differ: the path to it (`entities[id 11].pos.x`) and what each holds there. Nothing when they
|
|
49
|
+
* are the same. Lists of things with an `id` are named by it, the rest by their place
|
|
50
|
+
*
|
|
51
|
+
* @beta
|
|
52
|
+
*/
|
|
53
|
+
export declare function firstDifference(a: unknown, b: unknown, path?: string): Difference | undefined;
|
|
54
|
+
/**
|
|
55
|
+
* Where two states differ (`firstDifference`)
|
|
56
|
+
*
|
|
57
|
+
* @beta
|
|
58
|
+
*/
|
|
59
|
+
export interface Difference {
|
|
60
|
+
/** The path to it in the state: `entities[id 11].pos.x` */
|
|
61
|
+
path: string;
|
|
62
|
+
/** What the first holds there */
|
|
63
|
+
a: unknown;
|
|
64
|
+
/** What the second holds there */
|
|
65
|
+
b: unknown;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Where two runs of a game came apart: the first tick they were looked at and differed, and what differed first
|
|
69
|
+
*
|
|
70
|
+
* @beta
|
|
71
|
+
*/
|
|
72
|
+
export interface Apart extends Difference {
|
|
73
|
+
/** The tick they were looked at and differed */
|
|
74
|
+
tick: number;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* How a game is checked for coming out the same (`checkDeterminism`)
|
|
78
|
+
*
|
|
79
|
+
* @beta
|
|
80
|
+
*/
|
|
81
|
+
export interface DeterminismOptions {
|
|
82
|
+
/** How many ticks each world runs */
|
|
83
|
+
ticks: number;
|
|
84
|
+
/**
|
|
85
|
+
* How often their whole state is compared, in ticks: every 30 when left out. Their fingerprints
|
|
86
|
+
* (`worldFingerprint`) are compared every tick, and the whole state at once where those differ, so that the tick
|
|
87
|
+
* said is the one they came apart at
|
|
88
|
+
*/
|
|
89
|
+
every?: number;
|
|
90
|
+
/**
|
|
91
|
+
* A tick to save a third world at, and load a fourth from that save (`loadWorld`, with `game`): the game saved and
|
|
92
|
+
* the game loaded are run on side by side and compared. Not done when left out
|
|
93
|
+
*/
|
|
94
|
+
from?: number;
|
|
95
|
+
/** The game the save is loaded with, for `from` */
|
|
96
|
+
game?: GameModule;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* What the check found: for each run, where it came apart from the first, or `null` for the same to the last tick
|
|
100
|
+
*
|
|
101
|
+
* @beta
|
|
102
|
+
*/
|
|
103
|
+
export interface DeterminismReport {
|
|
104
|
+
/** A second world made the same way and run the same */
|
|
105
|
+
twice: Apart | null;
|
|
106
|
+
/** A world loaded from a save taken at `from`, beside the world the save was taken of: none when `from` was not said */
|
|
107
|
+
fromSave?: Apart | null;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Makes a world twice with `make` (the same seed, the same commands given in it, as a replay would) and runs them side
|
|
111
|
+
* by side, comparing their fingerprints every tick and their states (and the order each player's entities are filed
|
|
112
|
+
* in) every `every` ticks; with `from`, a third is saved at that tick and a fourth loaded
|
|
113
|
+
* 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
|
|
114
|
+
* own systems: one that reads `Math.random`, the clock, or the order of something unordered is found here
|
|
115
|
+
*
|
|
116
|
+
* const report = await checkDeterminism(() => makeMyGame(1), { ticks: 1800, from: 300, game: rules })
|
|
117
|
+
* expect(report).toEqual({ twice: null, fromSave: null })
|
|
118
|
+
*
|
|
119
|
+
* @beta
|
|
120
|
+
*/
|
|
121
|
+
export declare function checkDeterminism(make: () => World | Promise<World>, options: DeterminismOptions): Promise<DeterminismReport>;
|
package/dist/dialog.d.ts
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import type { PlayerId } from './entity';
|
|
2
|
+
import { type Message, type MessageRef } from './messages';
|
|
3
|
+
import { type Step } from './sequences';
|
|
4
|
+
import type { World } from './world';
|
|
5
|
+
/**
|
|
6
|
+
* Something someone says: a commander between waves, a hero in a cutscene. The rules say a line is said, so that
|
|
7
|
+
* every player hears it at the same moment and a save knows where a conversation was; the view shows it (a box with
|
|
8
|
+
* a portrait, subtitles) and plays its voice. Who the speaker is on the screen, their name and portrait, is the
|
|
9
|
+
* view's (`GameView.cinematics.speakers`)
|
|
10
|
+
*
|
|
11
|
+
* @beta
|
|
12
|
+
*/
|
|
13
|
+
export interface LineDef {
|
|
14
|
+
/** Who says it, by an id of the game's (`'td.commander'`) */
|
|
15
|
+
speaker: string;
|
|
16
|
+
/** What is said, in the words of the game: a message key, with params */
|
|
17
|
+
text: MessageRef;
|
|
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
|
+
seconds?: number;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Whom a line is said to: a player, some, or everyone
|
|
23
|
+
*
|
|
24
|
+
* @beta
|
|
25
|
+
*/
|
|
26
|
+
export type Audience = PlayerId | PlayerId[] | 'everyone';
|
|
27
|
+
/**
|
|
28
|
+
* A line as it is said: what the engine's `lineSaid` event carries, and what goes to the screen
|
|
29
|
+
*
|
|
30
|
+
* @beta
|
|
31
|
+
*/
|
|
32
|
+
export interface SaidLine {
|
|
33
|
+
id: number;
|
|
34
|
+
speaker: string;
|
|
35
|
+
text: Message;
|
|
36
|
+
seconds: number;
|
|
37
|
+
/** Who hears it. Everyone when left out */
|
|
38
|
+
to?: PlayerId[];
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The line each player hears now, the sequence waiting on it, and the tick it is over at, for skipping. Kept in the
|
|
42
|
+
* world: a save has it
|
|
43
|
+
*/
|
|
44
|
+
export declare const current: import("./components").Component<{
|
|
45
|
+
next: number;
|
|
46
|
+
lines: Record<number, {
|
|
47
|
+
line: number;
|
|
48
|
+
run?: number;
|
|
49
|
+
to?: PlayerId[];
|
|
50
|
+
until?: number;
|
|
51
|
+
}>;
|
|
52
|
+
}, {
|
|
53
|
+
next: number;
|
|
54
|
+
lines: Record<number, {
|
|
55
|
+
line: number;
|
|
56
|
+
run?: number;
|
|
57
|
+
to?: PlayerId[];
|
|
58
|
+
until?: number;
|
|
59
|
+
}>;
|
|
60
|
+
}>;
|
|
61
|
+
/**
|
|
62
|
+
* What is said in a world
|
|
63
|
+
*
|
|
64
|
+
* @beta
|
|
65
|
+
*/
|
|
66
|
+
export declare class Dialog {
|
|
67
|
+
private readonly world;
|
|
68
|
+
constructor(world: World);
|
|
69
|
+
/**
|
|
70
|
+
* Says a line, now. On its own it waits for nothing: in a sequence, `talk` says lines one after the other, each
|
|
71
|
+
* for as long as it lasts. `run` is the sequence that waits on it, which skipping the line hurries along
|
|
72
|
+
*/
|
|
73
|
+
say(to: Audience, line: LineDef, run?: number): SaidLine;
|
|
74
|
+
/**
|
|
75
|
+
* A player asks to skip the line they hear: it ends, and a conversation goes on to its next line. Only a line
|
|
76
|
+
* said to them alone can be: what others hear too goes on for them
|
|
77
|
+
*/
|
|
78
|
+
skip(player: PlayerId): void;
|
|
79
|
+
/** The players a line said to everyone is heard by: everyone who plays */
|
|
80
|
+
private listeners;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* The steps of a conversation, for a sequence: each line is said and stays for as long as it lasts, then the next.
|
|
84
|
+
* Skipping a line (the `skipLine` command) goes on to the next at once:
|
|
85
|
+
*
|
|
86
|
+
* defineSequence('td.beforeTheBoss', [...talk(DEFENDER, [{ speaker: 'td.commander', text: 'bossComing' }, ...])])
|
|
87
|
+
*
|
|
88
|
+
* @beta
|
|
89
|
+
*/
|
|
90
|
+
export declare function talk<D = undefined>(to: Audience, lines: LineDef[]): Step<D>[];
|
|
91
|
+
/** A line said, to whom it is for: everyone when it names nobody */
|
|
92
|
+
export declare const toldLine: import("./replication").ReplicatedEvent;
|
|
93
|
+
/** A line over, to whom it was for */
|
|
94
|
+
export declare const toldLineEnded: import("./replication").ReplicatedEvent;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* An effect of an ability as a save keeps it (`EffectRef`): where it is in the definition of the ability, found again
|
|
3
|
+
* 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
|
|
4
|
+
*/
|
|
5
|
+
import type { EffectDef } from './defs';
|
|
6
|
+
import type { Registry } from './registry';
|
|
7
|
+
/** An effect of an ability: where it is in the definition of the ability, or a copy for one made up on the spot */
|
|
8
|
+
export type EffectRef = {
|
|
9
|
+
path: (string | number)[];
|
|
10
|
+
} | {
|
|
11
|
+
value: unknown;
|
|
12
|
+
};
|
|
13
|
+
/** Where an effect is in the definition of an ability, found by identity; a copy of it when it is not in there */
|
|
14
|
+
export declare function effectRef(registry: Registry, ability: string, effect: EffectDef | EffectDef[]): EffectRef;
|
|
15
|
+
/** The effect a save kept (`effectRef`): found in the ability again, or the copy */
|
|
16
|
+
export declare function resolveEffect(registry: Registry, ability: string, ref: EffectRef): never;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import type { Audience } from './dialog';
|
|
2
|
+
import type { Entity, PlayerId } from './entity';
|
|
3
|
+
import type { Vec2 } from './math';
|
|
4
|
+
import type { World } from './world';
|
|
5
|
+
/**
|
|
6
|
+
* An effect the rules play on the screens, as the engine's `effect` event carries it (`world.effect`)
|
|
7
|
+
*
|
|
8
|
+
* @beta
|
|
9
|
+
*/
|
|
10
|
+
export interface EffectCue {
|
|
11
|
+
/** The rules' handle on it, for `world.stopEffect` */
|
|
12
|
+
id: number;
|
|
13
|
+
/** Which of the view's effects, by name (`GameView.fx.defs`) */
|
|
14
|
+
effect: string;
|
|
15
|
+
/** Where: a place, at a height or on the ground */
|
|
16
|
+
x?: number;
|
|
17
|
+
y?: number;
|
|
18
|
+
z?: number;
|
|
19
|
+
/** Or on an entity, by its id: it follows it on the screen, at a point of its model */
|
|
20
|
+
on?: number;
|
|
21
|
+
point?: string;
|
|
22
|
+
/** Its knobs, over the effect's own: plain data, as it goes to every screen */
|
|
23
|
+
params?: Record<string, unknown>;
|
|
24
|
+
/** What its random parts go by: the same on every screen */
|
|
25
|
+
seed: number;
|
|
26
|
+
/** Who sees it: everyone who sees the place when left out */
|
|
27
|
+
to?: PlayerId[];
|
|
28
|
+
/** Seen over the fog of war, by whom it is for: what is there to be noticed */
|
|
29
|
+
overFog?: boolean;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* How the rules play an effect (`world.effect`)
|
|
33
|
+
*
|
|
34
|
+
* @beta
|
|
35
|
+
*/
|
|
36
|
+
export interface EffectOptions {
|
|
37
|
+
/** A place: on the ground (`{ x, z }`) or at a height (`{ x, y, z }`) */
|
|
38
|
+
at?: Vec2 | {
|
|
39
|
+
x: number;
|
|
40
|
+
y: number;
|
|
41
|
+
z: number;
|
|
42
|
+
};
|
|
43
|
+
/** Or an entity: on the screen it follows it, and ends with it */
|
|
44
|
+
on?: Entity | number;
|
|
45
|
+
/** A point of the entity's model (`ModelDef.points`) */
|
|
46
|
+
point?: string;
|
|
47
|
+
/** Its knobs, over the effect's own (`EffectDef.params`): plain data */
|
|
48
|
+
params?: Record<string, unknown>;
|
|
49
|
+
/** What its random parts go by: one made from the tick when left out, the same on every screen all the same */
|
|
50
|
+
seed?: number;
|
|
51
|
+
/** Who sees it: a player, some, or everyone who sees the place (when left out) */
|
|
52
|
+
to?: Audience;
|
|
53
|
+
/** Seen over the fog of war by whom it is for, as what is there to be noticed. No when left out */
|
|
54
|
+
overFog?: boolean;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* How many effects a world played, for their ids and their seeds: the world's, and saved with it, so that the game
|
|
58
|
+
* loaded from a save gives its next effect the id and the seed the game that went on does (and a stop finds its start)
|
|
59
|
+
*/
|
|
60
|
+
export declare const played: import("./components").Component<number, number>;
|
|
61
|
+
/**
|
|
62
|
+
* 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 (`stopEffect`). Not in the world's state but for its count: a save, and a player who joins later,
|
|
64
|
+
* know nothing of the effect itself
|
|
65
|
+
*
|
|
66
|
+
* @beta
|
|
67
|
+
*/
|
|
68
|
+
export declare function playEffect(world: World, effect: string, options?: EffectOptions): number;
|
|
69
|
+
/**
|
|
70
|
+
* Stops an effect the rules played (`playEffect`), on every screen: as its own `stop` says, over `fade` seconds
|
|
71
|
+
*
|
|
72
|
+
* @beta
|
|
73
|
+
*/
|
|
74
|
+
export declare function stopEffect(world: World, id: number, options?: {
|
|
75
|
+
fade?: number;
|
|
76
|
+
}): void;
|
|
77
|
+
/** 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 toldEffect: import("./replication").ReplicatedEvent;
|
|
79
|
+
/** An effect stopped: to whom saw it start */
|
|
80
|
+
export declare const toldEffectStopped: import("./replication").ReplicatedEvent;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { PlayerId } from './entity';
|
|
2
|
+
import type { World } from './world';
|
|
3
|
+
/** How many pauses each player has used, by player */
|
|
4
|
+
export declare const pauses: import("./components").Component<{
|
|
5
|
+
used: Record<number, number>;
|
|
6
|
+
}, {
|
|
7
|
+
used: Record<number, number>;
|
|
8
|
+
}>;
|
|
9
|
+
/**
|
|
10
|
+
* How many pauses a player has left (`GameRules.talk.pauses.each`, less what they used)
|
|
11
|
+
*
|
|
12
|
+
* @beta
|
|
13
|
+
*/
|
|
14
|
+
export declare function pausesLeft(world: World, player: PlayerId): number;
|
|
15
|
+
/**
|
|
16
|
+
* A player gives up: out of the game as if beaten, once the game's `beforeSurrender` lets them. The rest of their
|
|
17
|
+
* side plays on; a side with nobody left loses, as the game's own way to win says (`lastTeamStanding` counts one who
|
|
18
|
+
* gave up as one who was in the game)
|
|
19
|
+
*
|
|
20
|
+
* @beta
|
|
21
|
+
*/
|
|
22
|
+
export declare function surrender(world: World, player: PlayerId): void;
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import type { Entity } from './entity';
|
|
2
|
+
import type { Vec2 } from './math';
|
|
3
|
+
import type { World } from './world';
|
|
4
|
+
/**
|
|
5
|
+
* Where an attacker fights from: `ground`, `air` and `water` for the engine, any other name for a game's own
|
|
6
|
+
*
|
|
7
|
+
* @beta
|
|
8
|
+
*/
|
|
9
|
+
export type EngageLayer = 'ground' | 'air' | 'water' | (string & {});
|
|
10
|
+
/**
|
|
11
|
+
* Seconds a target that could not be got at is left alone (see `shun`)
|
|
12
|
+
*
|
|
13
|
+
* @beta
|
|
14
|
+
*/
|
|
15
|
+
export declare const SHUN_TIME = 4;
|
|
16
|
+
/**
|
|
17
|
+
* The ring an attacker fights from, by its reach: 0 for the melee
|
|
18
|
+
*
|
|
19
|
+
* @beta
|
|
20
|
+
*/
|
|
21
|
+
export declare function bandOf(world: World, attacker: Entity): number;
|
|
22
|
+
/**
|
|
23
|
+
* The layer an attacker fights from, as the game's rules say (`GameRules.combat.engageLayer`)
|
|
24
|
+
*
|
|
25
|
+
* @beta
|
|
26
|
+
*/
|
|
27
|
+
export declare function engageLayer(world: World, attacker: Entity): EngageLayer;
|
|
28
|
+
/**
|
|
29
|
+
* The engine's: from the air for what flies, from the water for boats, from the ground for the rest
|
|
30
|
+
*
|
|
31
|
+
* @beta
|
|
32
|
+
*/
|
|
33
|
+
export declare function defaultEngageLayer(_world: World, attacker: Entity): EngageLayer;
|
|
34
|
+
/**
|
|
35
|
+
* The engine's room around a target for attackers like this one: as many as stand side by side around it, on the ring
|
|
36
|
+
* they fight from. Few for the melee around a footman, many around a town hall, all of them for long guns
|
|
37
|
+
*
|
|
38
|
+
* @beta
|
|
39
|
+
*/
|
|
40
|
+
export declare function defaultEngageRoom(world: World, target: Entity, attacker: Entity): number;
|
|
41
|
+
/**
|
|
42
|
+
* How many attackers like this one may fight the target at once, as the game's rules say (`GameRules.combat.engageRoom`)
|
|
43
|
+
*
|
|
44
|
+
* @beta
|
|
45
|
+
*/
|
|
46
|
+
export declare function roomFor(world: World, target: Entity, attacker: Entity): number;
|
|
47
|
+
/**
|
|
48
|
+
* Everyone fighting the target or closing in on it: of one layer only, when one is named
|
|
49
|
+
*
|
|
50
|
+
* @beta
|
|
51
|
+
*/
|
|
52
|
+
export declare function attackersOf(world: World, target: Entity, layer?: EngageLayer): Entity[];
|
|
53
|
+
/**
|
|
54
|
+
* How many attackers like this one (the same layer, the same ring) the target has already, the attacker left out
|
|
55
|
+
*
|
|
56
|
+
* @beta
|
|
57
|
+
*/
|
|
58
|
+
export declare function engagedLike(world: World, target: Entity, attacker: Entity): number;
|
|
59
|
+
/**
|
|
60
|
+
* Whether the target has no room left for another attacker like this one
|
|
61
|
+
*
|
|
62
|
+
* @beta
|
|
63
|
+
*/
|
|
64
|
+
export declare function isCrowded(world: World, target: Entity, attacker: Entity): boolean;
|
|
65
|
+
/**
|
|
66
|
+
* What the attacker is fighting or closing in on, if anything
|
|
67
|
+
*
|
|
68
|
+
* @beta
|
|
69
|
+
*/
|
|
70
|
+
export declare function engagedWith(world: World, attacker: Entity): Entity | undefined;
|
|
71
|
+
/**
|
|
72
|
+
* Marks the attacker engaged with the target, giving it a spot around it when it fights from close by (and has none
|
|
73
|
+
* there yet). The engine does this as a unit fights or closes in; a game's own fighting can too
|
|
74
|
+
*
|
|
75
|
+
* @beta
|
|
76
|
+
*/
|
|
77
|
+
export declare function engageWith(world: World, attacker: Entity, target: Entity): void;
|
|
78
|
+
/**
|
|
79
|
+
* Lets go of whatever the attacker was engaged with, and of its spot there
|
|
80
|
+
*
|
|
81
|
+
* @beta
|
|
82
|
+
*/
|
|
83
|
+
export declare function release(attacker: Entity, world?: World): void;
|
|
84
|
+
/**
|
|
85
|
+
* Where the attacker goes to fight the target from: its spot around it, out where its weapon reaches, or the target
|
|
86
|
+
* itself without one. The game's `approach` hook has the last word
|
|
87
|
+
*
|
|
88
|
+
* @beta
|
|
89
|
+
*/
|
|
90
|
+
export declare function spotFor(world: World, attacker: Entity, target: Entity): Vec2;
|
|
91
|
+
/**
|
|
92
|
+
* Whether the attacker has been closing in on the target for longer than the game lets it (`GameRules.combat.giveUpChase`) without getting closer
|
|
93
|
+
*
|
|
94
|
+
* @beta
|
|
95
|
+
*/
|
|
96
|
+
export declare function isBlocked(world: World, attacker: Entity, target: Entity): boolean;
|
|
97
|
+
/**
|
|
98
|
+
* Has the attacker leave the target alone for a while: not picked by itself again until then
|
|
99
|
+
*
|
|
100
|
+
* @beta
|
|
101
|
+
*/
|
|
102
|
+
export declare function shun(world: World, attacker: Entity, target: Entity, seconds?: number): void;
|
|
103
|
+
/**
|
|
104
|
+
* Whether the attacker leaves the target alone for now (see `shun`)
|
|
105
|
+
*
|
|
106
|
+
* @beta
|
|
107
|
+
*/
|
|
108
|
+
export declare function isShunned(world: World, attacker: Entity, target: Entity): boolean;
|