@v12-engine/engine 0.1.0 → 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,430 @@
|
|
|
1
|
+
import { distSq } from "../math.js";
|
|
2
|
+
import { defineModule } from "./module.js";
|
|
3
|
+
import { consider, curve, defineGoal } from "./goals.js";
|
|
4
|
+
import { pair, point, where } from "./geometry.js";
|
|
5
|
+
//#region src/ai/defence.ts
|
|
6
|
+
/** The share of the army's strength a raid scores the whole point at: one as strong as the army is everything */
|
|
7
|
+
var SCORE_FROM = .6;
|
|
8
|
+
/**
|
|
9
|
+
* The id of the defence's goal: what a game weighs it by (`AiOptions.weigh`)
|
|
10
|
+
*
|
|
11
|
+
* @beta
|
|
12
|
+
*/
|
|
13
|
+
var DEFEND = "defend";
|
|
14
|
+
/** The name of the squad that answers the strongest raid; the others are `defence 2`, `defence 3` */
|
|
15
|
+
var SQUAD = "defence";
|
|
16
|
+
var squadName = (i) => i === 0 ? SQUAD : `${SQUAD} ${i + 1}`;
|
|
17
|
+
/**
|
|
18
|
+
* A brain's defence (`ai.defence`): the raids on its bases and the answer to each, through its goal (`defend`), and the
|
|
19
|
+
* towers a raided base wants. Run by the `defence()` module each think; asked by whoever wants to know (`intruders`,
|
|
20
|
+
* `raids`). Kept in the brain's memory (`BrainMemory.defence`)
|
|
21
|
+
*
|
|
22
|
+
* @internal
|
|
23
|
+
*/
|
|
24
|
+
var Defence = class Defence {
|
|
25
|
+
ai;
|
|
26
|
+
/** What finds it on a brain (`ai.area(Defence)`): made the first time a module asks for it */
|
|
27
|
+
static id = "defence";
|
|
28
|
+
/** Makes the defence of a brain; what `ai.area(Defence)` calls the first time a module asks for it */
|
|
29
|
+
static make(ai) {
|
|
30
|
+
return new Defence(ai);
|
|
31
|
+
}
|
|
32
|
+
/** Its id among the brain's areas: what its memory and its report are kept under */
|
|
33
|
+
id = "defence";
|
|
34
|
+
/** What the log says where a save's memory of it is of a shape it does not read */
|
|
35
|
+
afresh = "the save remembers the raids on the computer player in a shape this brain does not read: it starts afresh";
|
|
36
|
+
/** Whether a `defence()` module is in the brain: without one, nothing here runs, and the goal is not the brain's */
|
|
37
|
+
enabled = false;
|
|
38
|
+
memory = {
|
|
39
|
+
v: 1,
|
|
40
|
+
raids: {}
|
|
41
|
+
};
|
|
42
|
+
moduleOptions = {};
|
|
43
|
+
thoughtAt = -1;
|
|
44
|
+
/** The goal it answers raids through: added to the brain's picker by the module */
|
|
45
|
+
goal;
|
|
46
|
+
constructor(ai) {
|
|
47
|
+
this.ai = ai;
|
|
48
|
+
this.goal = this.makeGoal();
|
|
49
|
+
}
|
|
50
|
+
/** Puts the `defence()` module's options under the brain's settings, and its goal on the picker */
|
|
51
|
+
configure(options) {
|
|
52
|
+
this.moduleOptions = options;
|
|
53
|
+
if (!this.enabled) this.ai.goals.add(this.goal);
|
|
54
|
+
this.enabled = true;
|
|
55
|
+
}
|
|
56
|
+
/** How it defends: the module's options, the brain's settings over them (`ai.settings.defence`) */
|
|
57
|
+
get options() {
|
|
58
|
+
return this.ai.cache("defence.options", () => ({
|
|
59
|
+
...this.moduleOptions,
|
|
60
|
+
...this.ai.settings.defence
|
|
61
|
+
}));
|
|
62
|
+
}
|
|
63
|
+
/** How near its buildings a rival's unit is an intruder */
|
|
64
|
+
get radius() {
|
|
65
|
+
return this.options.radius ?? 16;
|
|
66
|
+
}
|
|
67
|
+
/** How much stronger than a raid the defenders sent are: from the odds asked, by the square law (two in three: √2) */
|
|
68
|
+
get margin() {
|
|
69
|
+
const odds = Math.min(.95, Math.max(.05, this.options.odds ?? 2 / 3));
|
|
70
|
+
return Math.sqrt(odds / (1 - odds));
|
|
71
|
+
}
|
|
72
|
+
/** The rivals' units at its buildings, once a think: not creeps that camp near (they are for creeping) */
|
|
73
|
+
intruders() {
|
|
74
|
+
const { ai } = this;
|
|
75
|
+
return ai.cache("defence.intruders", () => {
|
|
76
|
+
const { view } = ai;
|
|
77
|
+
const buildings = view.mine.buildings();
|
|
78
|
+
const reach = this.radius * this.radius;
|
|
79
|
+
return view.enemies.units().alive().where((enemy) => buildings.some((building) => distSq(building, enemy) < reach));
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
/** The intruders base by base (each at the base whose buildings it is nearest), strongest first, once a think */
|
|
83
|
+
raids() {
|
|
84
|
+
const { ai } = this;
|
|
85
|
+
return ai.cache("defence.raids", () => {
|
|
86
|
+
const { view } = ai;
|
|
87
|
+
const intruders = this.intruders();
|
|
88
|
+
if (!intruders.length) return [];
|
|
89
|
+
const bases = view.bases();
|
|
90
|
+
const by = /* @__PURE__ */ new Map();
|
|
91
|
+
for (const enemy of intruders) {
|
|
92
|
+
let best;
|
|
93
|
+
let bestDistance = Infinity;
|
|
94
|
+
for (const base of bases) for (const building of base.buildings) {
|
|
95
|
+
const d = distSq(building, enemy);
|
|
96
|
+
if (d < bestDistance) {
|
|
97
|
+
bestDistance = d;
|
|
98
|
+
best = base;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
if (best) (by.get(best) ?? by.set(best, []).get(best)).push(enemy);
|
|
102
|
+
}
|
|
103
|
+
const raids = [];
|
|
104
|
+
for (const [base, enemies] of by) {
|
|
105
|
+
const set = intruders.where((entity) => enemies.includes(entity));
|
|
106
|
+
raids.push({
|
|
107
|
+
base,
|
|
108
|
+
key: String(base.centre.id),
|
|
109
|
+
intruders: set,
|
|
110
|
+
strength: set.strength()
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
return raids.sort((a, b) => b.strength - a.strength || a.base.centre.id - b.base.centre.id);
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
/** Its fighters: what is gathering (a ghoul at the trees) is at work, not in the army */
|
|
117
|
+
army(units) {
|
|
118
|
+
return units.fighters().where((entity) => entity.own?.order !== "harvest");
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* What is at home to meet a raid: the fighters nothing else holds (the pool) and what the defence holds already. What
|
|
122
|
+
* a raid is weighed against for the army to come home
|
|
123
|
+
*/
|
|
124
|
+
atHome() {
|
|
125
|
+
const { ai } = this;
|
|
126
|
+
return ai.cache("defence.home", () => {
|
|
127
|
+
const { goals } = ai;
|
|
128
|
+
return this.army(ai.view.mine.where((unit) => !goals.holder(unit.id) || goals.holder(unit.id) === "defend")).strength();
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
/** Whether the army is to come home: a raid more than what is at home can meet, where it may */
|
|
132
|
+
recalling() {
|
|
133
|
+
if (this.options.recall === false) return false;
|
|
134
|
+
const strongest = this.raids()[0];
|
|
135
|
+
return !!strongest && strongest.strength * this.margin > this.atHome();
|
|
136
|
+
}
|
|
137
|
+
/** The goal: scored on the raids against the army, urgent when the army is to come home, run with a squad a raid */
|
|
138
|
+
makeGoal() {
|
|
139
|
+
return defineGoal({
|
|
140
|
+
id: DEFEND,
|
|
141
|
+
considerations: [consider("intruders at my buildings", () => this.intruders().length, curve.step(1)), consider("the raid against my army", () => this.intruders().strength() / Math.max(1e-9, this.army(this.ai.view.mine).strength()), curve.custom((x) => SCORE_FROM + .4 * Math.min(1, x)))],
|
|
142
|
+
urgent: () => this.recalling(),
|
|
143
|
+
claims: { units: (_ai, available) => this.claim(available) },
|
|
144
|
+
commitment: 3,
|
|
145
|
+
run: (_ai, claimed) => this.answer(claimed.units)
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* What the goal claims as it is picked: for each raid, strongest first, the fighters nearest it that make it as much
|
|
150
|
+
* stronger as asked, out of what it may have: those nothing holds first, then (where the army is to come home) whole
|
|
151
|
+
* squads, the nearest first
|
|
152
|
+
*/
|
|
153
|
+
claim(available) {
|
|
154
|
+
const { ai } = this;
|
|
155
|
+
const { view } = ai;
|
|
156
|
+
const chosen = [];
|
|
157
|
+
const taken = /* @__PURE__ */ new Set();
|
|
158
|
+
const fighters = this.army(available);
|
|
159
|
+
for (const raid of this.raids()) {
|
|
160
|
+
const target = raid.intruders.first();
|
|
161
|
+
const need = raid.strength * this.margin;
|
|
162
|
+
let got = 0;
|
|
163
|
+
const free = fighters.items.filter((unit) => !taken.has(unit.id) && !ai.goals.holder(unit.id));
|
|
164
|
+
free.sort((a, b) => distSq(a, target) - distSq(b, target) || a.id - b.id);
|
|
165
|
+
for (const unit of free) {
|
|
166
|
+
if (got >= need && chosen.length) break;
|
|
167
|
+
chosen.push(unit);
|
|
168
|
+
taken.add(unit.id);
|
|
169
|
+
got += view.strength.of(unit);
|
|
170
|
+
}
|
|
171
|
+
if (got >= need) continue;
|
|
172
|
+
const squads = /* @__PURE__ */ new Map();
|
|
173
|
+
for (const unit of fighters) {
|
|
174
|
+
if (taken.has(unit.id) || !ai.goals.holder(unit.id)) continue;
|
|
175
|
+
const squad = ai.squads.squadOf(unit);
|
|
176
|
+
(squads.get(squad) ?? squads.set(squad, []).get(squad)).push(unit);
|
|
177
|
+
}
|
|
178
|
+
const groups = [...squads.values()].sort((a, b) => distSq(a[0], target) - distSq(b[0], target) || a[0].id - b[0].id);
|
|
179
|
+
for (const group of groups) {
|
|
180
|
+
if (got >= need) break;
|
|
181
|
+
for (const unit of group) {
|
|
182
|
+
chosen.push(unit);
|
|
183
|
+
taken.add(unit.id);
|
|
184
|
+
got += view.strength.of(unit);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return chosen;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* The goal's run, each think it holds its claim: a squad for each raid, strongest first. A squad keeps answering the
|
|
192
|
+
* raid at the base it was sent to while that goes on; what it lacks is made up from what the goal holds and no squad
|
|
193
|
+
* has, then from what nothing holds, the nearest first; and where the army is to come home, from the squads of other
|
|
194
|
+
* goals, whole
|
|
195
|
+
*/
|
|
196
|
+
answer(held) {
|
|
197
|
+
const { ai } = this;
|
|
198
|
+
const { view } = ai;
|
|
199
|
+
const raids = this.raids();
|
|
200
|
+
const radius = this.radius;
|
|
201
|
+
const margin = this.margin;
|
|
202
|
+
const mine = ai.squads.of(DEFEND).squads.filter((squad) => squad.name === SQUAD || squad.name.startsWith(`${SQUAD} `));
|
|
203
|
+
const matched = /* @__PURE__ */ new Map();
|
|
204
|
+
for (const squad of mine) {
|
|
205
|
+
const at = squad.current?.data.at;
|
|
206
|
+
const raid = at && raids.find((each) => !matched.has(each) && each.base.buildings.some((building) => distSq(building, at) <= radius * radius * 4));
|
|
207
|
+
if (raid) matched.set(raid, squad);
|
|
208
|
+
}
|
|
209
|
+
const inSquads = new Set(mine.filter((squad) => [...matched.values()].includes(squad)).flatMap((squad) => squad.data.units));
|
|
210
|
+
const spare = this.army(held).where((unit) => !inSquads.has(unit.id));
|
|
211
|
+
const used = /* @__PURE__ */ new Set();
|
|
212
|
+
const recall = this.recalling();
|
|
213
|
+
raids.forEach((raid, i) => {
|
|
214
|
+
const target = raid.intruders.nearest(matched.get(raid)?.centre() ?? raid.base.at);
|
|
215
|
+
let squad = matched.get(raid);
|
|
216
|
+
let got = squad ? squad.strength() : 0;
|
|
217
|
+
const need = raid.strength * margin;
|
|
218
|
+
const more = [];
|
|
219
|
+
const add = (units) => {
|
|
220
|
+
for (const unit of units) {
|
|
221
|
+
if (got >= need && (squad?.length || more.length)) return;
|
|
222
|
+
if (used.has(unit.id) || inSquads.has(unit.id)) continue;
|
|
223
|
+
used.add(unit.id);
|
|
224
|
+
more.push(unit.id);
|
|
225
|
+
got += view.strength.of(unit);
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
const byDistance = (units) => [...units].sort((a, b) => distSq(a, target) - distSq(b, target) || a.id - b.id);
|
|
229
|
+
add(byDistance(spare.items));
|
|
230
|
+
if (got < need) add(byDistance(this.army(ai.squads.pool()).idle().items));
|
|
231
|
+
if (got < need && recall) {
|
|
232
|
+
const others = ai.squads.all().squads.filter((each) => each.owner !== "defend" && ai.goals.get(each.owner) && each.length).sort((a, b) => distSq(a.centre() ?? target, target) - distSq(b.centre() ?? target, target));
|
|
233
|
+
for (const other of others) {
|
|
234
|
+
if (got >= need) break;
|
|
235
|
+
const ids = other.data.units.filter((id) => ai.goals.holder(id) !== "defend" && !used.has(id));
|
|
236
|
+
if (!ids.length) continue;
|
|
237
|
+
const before = more.length;
|
|
238
|
+
add(this.army(view.mine.where((unit) => ids.includes(unit.id))).items);
|
|
239
|
+
const called = more.slice(before);
|
|
240
|
+
if (!called.length) continue;
|
|
241
|
+
ai.goals.take(called, "urgent");
|
|
242
|
+
ai.decide(`calls ${called.length === ids.length ? other.name : `${called.length} of ${other.name}`} home to meet a raid of ${raid.intruders.length} at ${where(raid.base.at)}`, {
|
|
243
|
+
kind: "defence",
|
|
244
|
+
goal: DEFEND
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
if (!squad) {
|
|
249
|
+
if (!more.length) return;
|
|
250
|
+
squad = ai.squads.make(squadName(i), more, {
|
|
251
|
+
retreatAt: false,
|
|
252
|
+
owner: DEFEND
|
|
253
|
+
});
|
|
254
|
+
squad.defend(point(target), radius);
|
|
255
|
+
ai.decide(`answers a raid of ${raid.intruders.length} at ${where(raid.base.at)} with ${more.length} (strength ${Math.round(got)} to ${Math.round(raid.strength)})`, {
|
|
256
|
+
kind: "defence",
|
|
257
|
+
goal: DEFEND
|
|
258
|
+
});
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
if (more.length) squad.add(more, "sent to meet the raid");
|
|
262
|
+
const at = squad.current?.data.at;
|
|
263
|
+
if (!at || squad.done !== void 0 || distSq(at, target) > radius * radius) squad.defend(point(target), radius);
|
|
264
|
+
});
|
|
265
|
+
for (const squad of mine) if (![...matched.values()].includes(squad) && squad.alive) ai.squads.disband(squad, "its raid is over");
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* The think, once a think: the raids counted base by base (one begins as intruders come to a base that stood clear,
|
|
269
|
+
* and is over once it stood clear `quiet` seconds again), and the towers a base raided often enough wants
|
|
270
|
+
*/
|
|
271
|
+
think() {
|
|
272
|
+
const { ai } = this;
|
|
273
|
+
if (this.thoughtAt === ai.tick) return;
|
|
274
|
+
this.thoughtAt = ai.tick;
|
|
275
|
+
const { view } = ai;
|
|
276
|
+
const quiet = (this.options.quiet ?? 15) * ai.session.tickRate;
|
|
277
|
+
const raids = this.raids();
|
|
278
|
+
const kept = this.memory.raids;
|
|
279
|
+
for (const raid of raids) {
|
|
280
|
+
const record = kept[raid.key] ??= {
|
|
281
|
+
at: point(raid.base.at),
|
|
282
|
+
count: 0,
|
|
283
|
+
last: ai.tick,
|
|
284
|
+
most: 0
|
|
285
|
+
};
|
|
286
|
+
if (record.since === void 0) {
|
|
287
|
+
record.count++;
|
|
288
|
+
record.since = ai.tick;
|
|
289
|
+
record.most = 0;
|
|
290
|
+
ai.decide(`raid ${record.count} on the base at ${where(raid.base.at)}: ${raid.intruders.length} of theirs, strength ${Math.round(raid.strength)}`, { kind: "defence" });
|
|
291
|
+
}
|
|
292
|
+
record.last = ai.tick;
|
|
293
|
+
record.most = Math.max(record.most, raid.strength);
|
|
294
|
+
}
|
|
295
|
+
const bases = new Set(view.bases().map((base) => String(base.centre.id)));
|
|
296
|
+
for (const key in kept) {
|
|
297
|
+
const record = kept[key];
|
|
298
|
+
if (record.since !== void 0 && ai.tick - record.last > quiet) delete record.since;
|
|
299
|
+
if (!bases.has(key)) {
|
|
300
|
+
delete kept[key];
|
|
301
|
+
ai.plans.build.drop(`defence.tower:${key}`);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
this.towers();
|
|
305
|
+
}
|
|
306
|
+
/** The tower it builds: the one named, or the cheapest of the `tower` role its workers build */
|
|
307
|
+
towerType() {
|
|
308
|
+
const { ai } = this;
|
|
309
|
+
const named = this.options.tower;
|
|
310
|
+
if (named) return ai.registry.units.has(named) ? named : void 0;
|
|
311
|
+
const { view, registry } = ai;
|
|
312
|
+
const builds = new Set(view.typesOf("worker").flatMap((def) => def.builds ?? []));
|
|
313
|
+
const money = registry.currency ?? registry.resources[0]?.id ?? "";
|
|
314
|
+
return [...view.typesOf("tower").filter((def) => builds.has(def.id))].sort((a, b) => (a.cost?.[money] ?? 0) - (b.cost?.[money] ?? 0))[0]?.id;
|
|
315
|
+
}
|
|
316
|
+
/** The towers a base raided often enough wants: a standing want of the build plan, by the base */
|
|
317
|
+
towers() {
|
|
318
|
+
const { ai } = this;
|
|
319
|
+
const options = this.options;
|
|
320
|
+
const wanted = options.towers ?? 1;
|
|
321
|
+
const after = options.towerAfter ?? 2;
|
|
322
|
+
const type = this.towerType();
|
|
323
|
+
if (!type || wanted <= 0) return;
|
|
324
|
+
const { view } = ai;
|
|
325
|
+
for (const base of view.bases()) {
|
|
326
|
+
const key = String(base.centre.id);
|
|
327
|
+
const record = this.memory.raids[key];
|
|
328
|
+
const id = `defence.tower:${key}`;
|
|
329
|
+
if (!record || record.count < after) continue;
|
|
330
|
+
const at = point(base.at);
|
|
331
|
+
if (!ai.plans.build.get(id)) ai.decide(`wants ${wanted === 1 ? "a tower" : `${wanted} towers`} at the base at ${where(at)}: raided ${record.count} times`, { kind: "defence" });
|
|
332
|
+
ai.plans.build.want({
|
|
333
|
+
id,
|
|
334
|
+
type,
|
|
335
|
+
count: (brain) => {
|
|
336
|
+
const all = brain.view.mine.ofType(type);
|
|
337
|
+
return all.length - all.near(at, 14).length + wanted;
|
|
338
|
+
},
|
|
339
|
+
near: at,
|
|
340
|
+
within: 12,
|
|
341
|
+
priority: 45,
|
|
342
|
+
keep: true,
|
|
343
|
+
by: "defence"
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
/** The defence as the report shows it */
|
|
348
|
+
report() {
|
|
349
|
+
const { ai } = this;
|
|
350
|
+
if (!this.enabled) return {
|
|
351
|
+
raids: [],
|
|
352
|
+
bases: [],
|
|
353
|
+
towers: []
|
|
354
|
+
};
|
|
355
|
+
const squads = ai.squads.of(DEFEND).squads;
|
|
356
|
+
return {
|
|
357
|
+
raids: this.raids().map((raid) => {
|
|
358
|
+
const squad = squads.find((each) => {
|
|
359
|
+
const at = each.current?.data.at;
|
|
360
|
+
return !!at && raid.base.buildings.some((building) => distSq(building, at) <= this.radius * this.radius * 4);
|
|
361
|
+
});
|
|
362
|
+
const record = this.memory.raids[raid.key];
|
|
363
|
+
return {
|
|
364
|
+
at: pair(raid.base.at),
|
|
365
|
+
intruders: raid.intruders.map((unit) => unit.id),
|
|
366
|
+
strength: Math.round(raid.strength),
|
|
367
|
+
...squad && { squad: squad.name },
|
|
368
|
+
defenders: squad?.length ?? 0,
|
|
369
|
+
since: record?.since ?? ai.tick,
|
|
370
|
+
count: record?.count ?? 1
|
|
371
|
+
};
|
|
372
|
+
}),
|
|
373
|
+
bases: Object.values(this.memory.raids).map((record) => ({
|
|
374
|
+
at: pair(record.at),
|
|
375
|
+
raids: record.count,
|
|
376
|
+
last: record.last
|
|
377
|
+
})),
|
|
378
|
+
towers: ai.plans.build.list().filter((want) => want.id?.startsWith("defence.tower:")).map((want) => ({
|
|
379
|
+
at: pair(want.near),
|
|
380
|
+
want: want.id,
|
|
381
|
+
status: ai.plans.build.status(want.id) ?? "new"
|
|
382
|
+
}))
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
/** What it remembers, for a save: a copy */
|
|
386
|
+
/** Whether it has anything to remember now: one that does not is left out of a save */
|
|
387
|
+
remembers() {
|
|
388
|
+
return this.enabled;
|
|
389
|
+
}
|
|
390
|
+
/** Its part of the brain's report */
|
|
391
|
+
reportTo(report) {
|
|
392
|
+
report.defence = this.report();
|
|
393
|
+
}
|
|
394
|
+
/** Gives a copy of the defence's memory, for a save of the brain */
|
|
395
|
+
save() {
|
|
396
|
+
return structuredClone(this.memory);
|
|
397
|
+
}
|
|
398
|
+
/** Takes back what `save` gave: whether it was read */
|
|
399
|
+
restore(memory) {
|
|
400
|
+
const given = memory;
|
|
401
|
+
if (!given || typeof given !== "object" || given.v !== 1) return false;
|
|
402
|
+
this.memory = {
|
|
403
|
+
v: 1,
|
|
404
|
+
raids: structuredClone(given.raids ?? {})
|
|
405
|
+
};
|
|
406
|
+
return true;
|
|
407
|
+
}
|
|
408
|
+
};
|
|
409
|
+
/**
|
|
410
|
+
* The defence module: the raids on the bases counted each think, the towers a raided base wants (`ai.defence.think`),
|
|
411
|
+
* and the `defend` goal put on the brain's picker, which answers each raid with a squad of its own. With how it defends
|
|
412
|
+
* where the brain's definition and the seat's options say nothing (`DefenceOptions`)
|
|
413
|
+
*
|
|
414
|
+
* @beta
|
|
415
|
+
*/
|
|
416
|
+
var defence = (options = {}) => defineModule({
|
|
417
|
+
id: "defence",
|
|
418
|
+
make: (ai) => {
|
|
419
|
+
ai.area(Defence).configure(options);
|
|
420
|
+
return {
|
|
421
|
+
id: "defence",
|
|
422
|
+
phase: "sense",
|
|
423
|
+
think: () => ai.area(Defence).think()
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
});
|
|
427
|
+
//#endregion
|
|
428
|
+
export { DEFEND, Defence, defence };
|
|
429
|
+
|
|
430
|
+
//# sourceMappingURL=defence.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"defence.js","names":[],"sources":["../../src/ai/defence.ts"],"sourcesContent":["import { distSq, type Vec2 } from '#engine/math'\nimport type { AiArea } from './area'\nimport type { AiReport } from './report'\nimport type { EntityState } from '#engine/session/protocol'\nimport { type Brain } from './brain'\nimport { type AiModule, type AiModuleDef, defineModule } from './module'\nimport { consider, curve, defineGoal, type GoalDef } from './goals'\nimport type { Squad } from './squads'\nimport type { Base, UnitSet } from './view'\nimport { pair, point, where } from './geometry'\n\n/*\n * Defence (AI plan, C4): what comes at the bases, and the answer to it. The old melee player sent the fighters\n * nearest any rival unit at its buildings, half again as strong, and nothing kept count: a raid at the mine every\n * minute was met the same way every minute, and the computer never thought of a tower there. This is what a player\n * does when the minimap flashes:\n *\n * - **Intruders** are the rivals' units within `radius` of one of its buildings (creeps are not: they camp, and are\n * for creeping). They are taken base by base: a **raid** is the intruders at one base, with their strength.\n * - **The answer is sized to the raid.** For each raid, strongest first, the fighters nearest it that make the odds of\n * the fight (`Strength.odds`, A4: Lanchester's square law, a number of the chance to win) at least `odds` (two in\n * three): one footman for a peasant that came to look, five for a party of four, not the whole army for a scout.\n * Each raid gets a squad of its own (`defence`, `defence 2`, …), sent to defend the place, which fights what comes\n * within the radius and goes back once it is gone. What nothing holds goes first, so that the wave out goes on.\n * - **The army comes home** (`recall`) when a raid is more than what is at home can meet: the goal is urgent then,\n * and as it scores past the wave (a raid over half the army's strength) it takes the squads out, whole.\n * - **Raids are counted** a base at a time: one is over once the base has stood clear `quiet` seconds. A base raided\n * `towerAfter` times wants `towers` towers (the build plan builds them by the base, and anew when lost).\n * - **Help** from an ally is C5's (`defendUsers`): the team's requests.\n *\n * The defence is a goal on the brain's picker (`defend`, the `defence()` module adds it), so it competes with the\n * others for units as any goal does, and the one order of who may move a unit holds. What it remembers (the raids of\n * each base) is plain data in the brain's memory (`BrainMemory.defence`).\n */\n\n/** The share of the army's strength a raid scores the whole point at: one as strong as the army is everything */\nconst SCORE_FROM = 0.6\n\n/**\n * How a brain defends its bases (`BrainDef.defence`, the seat's `AiOptions.defence` over it, the `defence()` module's\n * own under both). Data, kept in a save\n *\n * @beta\n */\nexport interface DefenceOptions {\n\t/** How near one of its buildings a rival's unit is an intruder: the seat's `defendRadius`, else 16 */\n\tradius?: number\n\t/**\n\t * The odds of the fight (`Strength.odds`) the defenders sent against a raid are to have: two in three when left out\n\t * (about four tenths stronger than the raid)\n\t */\n\todds?: number\n\t/**\n\t * Whether a raid more than what is at home can meet brings the army home (the goal is urgent: it takes squads out,\n\t * whole, once it scores past what they are on): yes when left out\n\t */\n\trecall?: boolean\n\t/** Towers a base wants once raided `towerAfter` times: 1 when left out. 0 for none */\n\ttowers?: number\n\t/** Raids at one base before it wants towers: 2 when left out */\n\ttowerAfter?: number\n\t/** The tower to build, by type id: the cheapest of the `tower` role its workers build when left out */\n\ttower?: string\n\t/** Seconds a base stands clear before a raid on it is over: 15 when left out */\n\tquiet?: number\n}\n\n/**\n * What the defence remembers between thinks, as a save keeps it: each base's raids\n *\n * @beta\n */\nexport interface DefenceMemory {\n\t/** The version of the memory's format: 1 so far */\n\tv: 1\n\t/** By base (the id of what it is around): where, how many raids, the one on now (since when), when last seen, the strongest */\n\traids: Record<string, { at: Vec2; count: number; since?: number; last: number; most: number }>\n}\n\n/**\n * The intruders at one base, as the defence sees them this think (`ai.defence.raids()`)\n *\n * @beta\n */\nexport interface Raid {\n\t/** The base raided */\n\tbase: Base\n\t/** What the defence remembers it by: the id of what the base is around */\n\tkey: string\n\t/** The rivals' units there */\n\tintruders: UnitSet\n\t/** Their strength together (`Strength.of`) */\n\tstrength: number\n}\n\n/**\n * The defence as the report shows it (`AiReport.defence`): the raids on now with what answers each, every base's raids\n * so far, the towers it wants\n *\n * @beta\n */\nexport interface DefenceReport {\n\t/** The raids on now, each with where it is, the enemy units in it, their strength, the squad that answers it and how many defend, since when and how many so far */\n\traids: { at: [number, number]; intruders: number[]; strength: number; squad?: string; defenders: number; since: number; count: number }[]\n\t/** The bases raided, each with where it is, how many raids it had and the tick of the last */\n\tbases: { at: [number, number]; raids: number; last: number }[]\n\t/** The towers it wants, each with where, the type wanted and how far the want is along */\n\ttowers: { at: [number, number]; want: string; status: string }[]\n}\n\n/**\n * The id of the defence's goal: what a game weighs it by (`AiOptions.weigh`)\n *\n * @beta\n */\nexport const DEFEND = 'defend'\n/** The name of the squad that answers the strongest raid; the others are `defence 2`, `defence 3` */\nconst SQUAD = 'defence'\nconst squadName = (i: number) => (i === 0 ? SQUAD : `${SQUAD} ${i + 1}`)\n\n/**\n * A brain's defence (`ai.defence`): the raids on its bases and the answer to each, through its goal (`defend`), and the\n * towers a raided base wants. Run by the `defence()` module each think; asked by whoever wants to know (`intruders`,\n * `raids`). Kept in the brain's memory (`BrainMemory.defence`)\n *\n * @internal\n */\nexport class Defence implements AiArea {\n\t/** What finds it on a brain (`ai.area(Defence)`): made the first time a module asks for it */\n\tstatic readonly id = 'defence'\n\t/** Makes the defence of a brain; what `ai.area(Defence)` calls the first time a module asks for it */\n\tstatic make(ai: Brain): Defence {\n\t\treturn new Defence(ai)\n\t}\n\t/** Its id among the brain's areas: what its memory and its report are kept under */\n\treadonly id = 'defence'\n\t/** What the log says where a save's memory of it is of a shape it does not read */\n\treadonly afresh = 'the save remembers the raids on the computer player in a shape this brain does not read: it starts afresh'\n\n\t/** Whether a `defence()` module is in the brain: without one, nothing here runs, and the goal is not the brain's */\n\tenabled = false\n\tprivate memory: DefenceMemory = { v: 1, raids: {} }\n\tprivate moduleOptions: DefenceOptions = {}\n\tprivate thoughtAt = -1\n\t/** The goal it answers raids through: added to the brain's picker by the module */\n\treadonly goal: GoalDef\n\n\tconstructor(\n\t\t/** The brain it defends for */\n\t\treadonly ai: Brain\n\t) {\n\t\tthis.goal = this.makeGoal()\n\t}\n\n\t/** Puts the `defence()` module's options under the brain's settings, and its goal on the picker */\n\tconfigure(options: DefenceOptions): void {\n\t\tthis.moduleOptions = options\n\t\tif (!this.enabled) this.ai.goals.add(this.goal)\n\t\tthis.enabled = true\n\t}\n\n\t/** How it defends: the module's options, the brain's settings over them (`ai.settings.defence`) */\n\tget options(): DefenceOptions {\n\t\treturn this.ai.cache('defence.options', () => ({ ...this.moduleOptions, ...this.ai.settings.defence }))\n\t}\n\n\t/** How near its buildings a rival's unit is an intruder */\n\tget radius(): number {\n\t\treturn this.options.radius ?? 16\n\t}\n\n\t/** How much stronger than a raid the defenders sent are: from the odds asked, by the square law (two in three: √2) */\n\tget margin(): number {\n\t\tconst odds = Math.min(0.95, Math.max(0.05, this.options.odds ?? 2 / 3))\n\t\treturn Math.sqrt(odds / (1 - odds))\n\t}\n\n\t/** The rivals' units at its buildings, once a think: not creeps that camp near (they are for creeping) */\n\tintruders(): UnitSet {\n\t\tconst { ai } = this\n\t\treturn ai.cache('defence.intruders', () => {\n\t\t\tconst { view } = ai\n\t\t\tconst buildings = view.mine.buildings()\n\t\t\tconst reach = this.radius * this.radius\n\t\t\treturn view.enemies\n\t\t\t\t.units()\n\t\t\t\t.alive()\n\t\t\t\t.where((enemy) => buildings.some((building) => distSq(building, enemy) < reach))\n\t\t})\n\t}\n\n\t/** The intruders base by base (each at the base whose buildings it is nearest), strongest first, once a think */\n\traids(): Raid[] {\n\t\tconst { ai } = this\n\t\treturn ai.cache('defence.raids', () => {\n\t\t\tconst { view } = ai\n\t\t\tconst intruders = this.intruders()\n\t\t\tif (!intruders.length) return []\n\t\t\tconst bases = view.bases()\n\t\t\tconst by = new Map<Base, EntityState[]>()\n\t\t\tfor (const enemy of intruders) {\n\t\t\t\tlet best: Base | undefined\n\t\t\t\tlet bestDistance = Infinity\n\t\t\t\tfor (const base of bases) {\n\t\t\t\t\tfor (const building of base.buildings) {\n\t\t\t\t\t\tconst d = distSq(building, enemy)\n\t\t\t\t\t\tif (d < bestDistance) {\n\t\t\t\t\t\t\tbestDistance = d\n\t\t\t\t\t\t\tbest = base\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (best) (by.get(best) ?? by.set(best, []).get(best)!).push(enemy)\n\t\t\t}\n\t\t\tconst raids: Raid[] = []\n\t\t\tfor (const [base, enemies] of by) {\n\t\t\t\tconst set = intruders.where((entity) => enemies.includes(entity))\n\t\t\t\traids.push({ base, key: String(base.centre.id), intruders: set, strength: set.strength() })\n\t\t\t}\n\t\t\treturn raids.sort((a, b) => b.strength - a.strength || a.base.centre.id - b.base.centre.id)\n\t\t})\n\t}\n\n\t/** Its fighters: what is gathering (a ghoul at the trees) is at work, not in the army */\n\tprivate army(units: UnitSet): UnitSet {\n\t\treturn units.fighters().where((entity) => entity.own?.order !== 'harvest')\n\t}\n\n\t/**\n\t * What is at home to meet a raid: the fighters nothing else holds (the pool) and what the defence holds already. What\n\t * a raid is weighed against for the army to come home\n\t */\n\tprivate atHome(): number {\n\t\tconst { ai } = this\n\t\treturn ai.cache('defence.home', () => {\n\t\t\tconst { goals } = ai\n\t\t\treturn this.army(ai.view.mine.where((unit) => !goals.holder(unit.id) || goals.holder(unit.id) === DEFEND)).strength()\n\t\t})\n\t}\n\n\t/** Whether the army is to come home: a raid more than what is at home can meet, where it may */\n\tprivate recalling(): boolean {\n\t\tif (this.options.recall === false) return false\n\t\tconst strongest = this.raids()[0]\n\t\treturn !!strongest && strongest.strength * this.margin > this.atHome()\n\t}\n\n\t/** The goal: scored on the raids against the army, urgent when the army is to come home, run with a squad a raid */\n\tprivate makeGoal(): GoalDef {\n\t\treturn defineGoal({\n\t\t\tid: DEFEND,\n\t\t\tconsiderations: [\n\t\t\t\tconsider('intruders at my buildings', () => this.intruders().length, curve.step(1)),\n\t\t\t\t// six tenths for any intruder, the whole point for a raid as strong as the army: a lone scout at home does not\n\t\t\t\t// call the wave back, a raid does\n\t\t\t\tconsider(\n\t\t\t\t\t'the raid against my army',\n\t\t\t\t\t() => this.intruders().strength() / Math.max(1e-9, this.army(this.ai.view.mine).strength()),\n\t\t\t\t\tcurve.custom((x) => SCORE_FROM + (1 - SCORE_FROM) * Math.min(1, x))\n\t\t\t\t)\n\t\t\t],\n\t\t\t// it may break what the army out is committed to when what is at home cannot meet the raid\n\t\t\turgent: () => this.recalling(),\n\t\t\tclaims: { units: (_ai, available) => this.claim(available) },\n\t\t\tcommitment: 3,\n\t\t\trun: (_ai, claimed) => this.answer(claimed.units)\n\t\t})\n\t}\n\n\t/**\n\t * What the goal claims as it is picked: for each raid, strongest first, the fighters nearest it that make it as much\n\t * stronger as asked, out of what it may have: those nothing holds first, then (where the army is to come home) whole\n\t * squads, the nearest first\n\t */\n\tprivate claim(available: UnitSet): EntityState[] {\n\t\tconst { ai } = this\n\t\tconst { view } = ai\n\t\tconst chosen: EntityState[] = []\n\t\tconst taken = new Set<number>()\n\t\tconst fighters = this.army(available)\n\t\tfor (const raid of this.raids()) {\n\t\t\tconst target = raid.intruders.first()!\n\t\t\tconst need = raid.strength * this.margin\n\t\t\tlet got = 0\n\t\t\tconst free = fighters.items.filter((unit) => !taken.has(unit.id) && !ai.goals.holder(unit.id))\n\t\t\tfree.sort((a, b) => distSq(a, target) - distSq(b, target) || a.id - b.id)\n\t\t\tfor (const unit of free) {\n\t\t\t\tif (got >= need && chosen.length) break\n\t\t\t\tchosen.push(unit)\n\t\t\t\ttaken.add(unit.id)\n\t\t\t\tgot += view.strength.of(unit)\n\t\t\t}\n\t\t\tif (got >= need) continue\n\t\t\t// short: what others hold and it may take, a squad at a time\n\t\t\tconst squads = new Map<Squad | undefined, EntityState[]>()\n\t\t\tfor (const unit of fighters) {\n\t\t\t\tif (taken.has(unit.id) || !ai.goals.holder(unit.id)) continue\n\t\t\t\tconst squad = ai.squads.squadOf(unit)\n\t\t\t\t;(squads.get(squad) ?? squads.set(squad, []).get(squad)!).push(unit)\n\t\t\t}\n\t\t\tconst groups = [...squads.values()].sort((a, b) => distSq(a[0], target) - distSq(b[0], target) || a[0].id - b[0].id)\n\t\t\tfor (const group of groups) {\n\t\t\t\tif (got >= need) break\n\t\t\t\tfor (const unit of group) {\n\t\t\t\t\tchosen.push(unit)\n\t\t\t\t\ttaken.add(unit.id)\n\t\t\t\t\tgot += view.strength.of(unit)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn chosen\n\t}\n\n\t/**\n\t * The goal's run, each think it holds its claim: a squad for each raid, strongest first. A squad keeps answering the\n\t * raid at the base it was sent to while that goes on; what it lacks is made up from what the goal holds and no squad\n\t * has, then from what nothing holds, the nearest first; and where the army is to come home, from the squads of other\n\t * goals, whole\n\t */\n\tprivate answer(held: UnitSet): void {\n\t\tconst { ai } = this\n\t\tconst { view } = ai\n\t\tconst raids = this.raids()\n\t\tconst radius = this.radius\n\t\tconst margin = this.margin\n\t\t// the squads answering, each matched to the raid at the base it defends; a squad whose raid is over is let go of\n\t\tconst mine = ai.squads.of(DEFEND).squads.filter((squad) => squad.name === SQUAD || squad.name.startsWith(`${SQUAD} `))\n\t\tconst matched = new Map<Raid, Squad>()\n\t\tfor (const squad of mine) {\n\t\t\tconst at = squad.current?.data.at as Vec2 | undefined\n\t\t\tconst raid =\n\t\t\t\tat && raids.find((each) => !matched.has(each) && each.base.buildings.some((building) => distSq(building, at) <= radius * radius * 4))\n\t\t\tif (raid) matched.set(raid, squad)\n\t\t}\n\t\tconst inSquads = new Set(mine.filter((squad) => [...matched.values()].includes(squad)).flatMap((squad) => squad.data.units))\n\t\tconst spare = this.army(held).where((unit) => !inSquads.has(unit.id))\n\t\tconst used = new Set<number>()\n\t\tconst recall = this.recalling()\n\t\traids.forEach((raid, i) => {\n\t\t\tconst target = raid.intruders.nearest(matched.get(raid)?.centre() ?? raid.base.at)!\n\t\t\tlet squad = matched.get(raid)\n\t\t\tlet got = squad ? squad.strength() : 0\n\t\t\tconst need = raid.strength * margin\n\t\t\tconst more: number[] = []\n\t\t\tconst add = (units: Iterable<EntityState>) => {\n\t\t\t\tfor (const unit of units) {\n\t\t\t\t\tif (got >= need && (squad?.length || more.length)) return\n\t\t\t\t\tif (used.has(unit.id) || inSquads.has(unit.id)) continue\n\t\t\t\t\tused.add(unit.id)\n\t\t\t\t\tmore.push(unit.id)\n\t\t\t\t\tgot += view.strength.of(unit)\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst byDistance = (units: readonly EntityState[]) => [...units].sort((a, b) => distSq(a, target) - distSq(b, target) || a.id - b.id)\n\t\t\tadd(byDistance(spare.items))\n\t\t\tif (got < need) add(byDistance(this.army(ai.squads.pool()).idle().items))\n\t\t\tif (got < need && recall) {\n\t\t\t\t// the army home: other goals' squads, whole, the nearest first\n\t\t\t\tconst others = ai.squads\n\t\t\t\t\t.all()\n\t\t\t\t\t.squads.filter((each) => each.owner !== DEFEND && ai.goals.get(each.owner) && each.length)\n\t\t\t\t\t.sort((a, b) => distSq(a.centre() ?? target, target) - distSq(b.centre() ?? target, target))\n\t\t\t\tfor (const other of others) {\n\t\t\t\t\tif (got >= need) break\n\t\t\t\t\t// what the goal claimed of it already is its own: only the rest is taken\n\t\t\t\t\tconst ids = other.data.units.filter((id) => ai.goals.holder(id) !== DEFEND && !used.has(id))\n\t\t\t\t\tif (!ids.length) continue\n\t\t\t\t\t// only what the raid needs of it: the rest stays with its goal (taken and left in the pool, its goal would\n\t\t\t\t\t// pick it up again, and its squad be made anew: orders sent twice)\n\t\t\t\t\tconst before = more.length\n\t\t\t\t\t// its fighters: a worker on another goal's errand (a scout) goes on with it, and is not sent to a fight (AI1)\n\t\t\t\t\tadd(this.army(view.mine.where((unit) => ids.includes(unit.id))).items)\n\t\t\t\t\tconst called = more.slice(before)\n\t\t\t\t\tif (!called.length) continue\n\t\t\t\t\tai.goals.take(called, 'urgent')\n\t\t\t\t\tai.decide(\n\t\t\t\t\t\t`calls ${called.length === ids.length ? other.name : `${called.length} of ${other.name}`} home to meet a raid of ${raid.intruders.length} at ${where(raid.base.at)}`,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tkind: 'defence',\n\t\t\t\t\t\t\tgoal: DEFEND\n\t\t\t\t\t\t}\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!squad) {\n\t\t\t\tif (!more.length) return\n\t\t\t\tsquad = ai.squads.make(squadName(i), more, { retreatAt: false, owner: DEFEND })\n\t\t\t\tsquad.defend(point(target), radius)\n\t\t\t\tai.decide(\n\t\t\t\t\t`answers a raid of ${raid.intruders.length} at ${where(raid.base.at)} with ${more.length} (strength ${Math.round(got)} to ${Math.round(raid.strength)})`,\n\t\t\t\t\t{ kind: 'defence', goal: DEFEND }\n\t\t\t\t)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (more.length) squad.add(more, 'sent to meet the raid')\n\t\t\t// a squad that holds the place the raid is at, sent on to where it went next once that is out of its reach\n\t\t\tconst at = squad.current?.data.at as Vec2 | undefined\n\t\t\tif (!at || squad.done !== undefined || distSq(at, target) > radius * radius) squad.defend(point(target), radius)\n\t\t})\n\t\t// the squads whose raid is over: let go of, their units back to the pool for the goal to give up\n\t\tfor (const squad of mine) if (![...matched.values()].includes(squad) && squad.alive) ai.squads.disband(squad, 'its raid is over')\n\t}\n\n\t/**\n\t * The think, once a think: the raids counted base by base (one begins as intruders come to a base that stood clear,\n\t * and is over once it stood clear `quiet` seconds again), and the towers a base raided often enough wants\n\t */\n\tthink(): void {\n\t\tconst { ai } = this\n\t\tif (this.thoughtAt === ai.tick) return\n\t\tthis.thoughtAt = ai.tick\n\t\tconst { view } = ai\n\t\tconst options = this.options\n\t\tconst quiet = (options.quiet ?? 15) * ai.session.tickRate\n\t\tconst raids = this.raids()\n\t\tconst kept = this.memory.raids\n\t\tfor (const raid of raids) {\n\t\t\tconst record = (kept[raid.key] ??= { at: point(raid.base.at), count: 0, last: ai.tick, most: 0 })\n\t\t\tif (record.since === undefined) {\n\t\t\t\trecord.count++\n\t\t\t\trecord.since = ai.tick\n\t\t\t\trecord.most = 0\n\t\t\t\tai.decide(\n\t\t\t\t\t`raid ${record.count} on the base at ${where(raid.base.at)}: ${raid.intruders.length} of theirs, strength ${Math.round(raid.strength)}`,\n\t\t\t\t\t{\n\t\t\t\t\t\tkind: 'defence'\n\t\t\t\t\t}\n\t\t\t\t)\n\t\t\t}\n\t\t\trecord.last = ai.tick\n\t\t\trecord.most = Math.max(record.most, raid.strength)\n\t\t}\n\t\tconst bases = new Set(view.bases().map((base) => String(base.centre.id)))\n\t\tfor (const key in kept) {\n\t\t\tconst record = kept[key]\n\t\t\tif (record.since !== undefined && ai.tick - record.last > quiet) delete record.since\n\t\t\t// a base that is no more is forgotten\n\t\t\tif (!bases.has(key)) {\n\t\t\t\tdelete kept[key]\n\t\t\t\tai.plans.build.drop(`defence.tower:${key}`)\n\t\t\t}\n\t\t}\n\t\tthis.towers()\n\t}\n\n\t/** The tower it builds: the one named, or the cheapest of the `tower` role its workers build */\n\ttowerType(): string | undefined {\n\t\tconst { ai } = this\n\t\tconst named = this.options.tower\n\t\tif (named) return ai.registry.units.has(named) ? named : undefined\n\t\tconst { view, registry } = ai\n\t\tconst builds = new Set(view.typesOf('worker').flatMap((def) => def.builds ?? []))\n\t\tconst money = registry.currency ?? registry.resources[0]?.id ?? ''\n\t\tconst towers = view.typesOf('tower').filter((def) => builds.has(def.id))\n\t\treturn [...towers].sort((a, b) => (a.cost?.[money] ?? 0) - (b.cost?.[money] ?? 0))[0]?.id\n\t}\n\n\t/** The towers a base raided often enough wants: a standing want of the build plan, by the base */\n\tprivate towers(): void {\n\t\tconst { ai } = this\n\t\tconst options = this.options\n\t\tconst wanted = options.towers ?? 1\n\t\tconst after = options.towerAfter ?? 2\n\t\tconst type = this.towerType()\n\t\tif (!type || wanted <= 0) return\n\t\tconst { view } = ai\n\t\tfor (const base of view.bases()) {\n\t\t\tconst key = String(base.centre.id)\n\t\t\tconst record = this.memory.raids[key]\n\t\t\tconst id = `defence.tower:${key}`\n\t\t\tif (!record || record.count < after) continue\n\t\t\tconst at = point(base.at)\n\t\t\tif (!ai.plans.build.get(id))\n\t\t\t\tai.decide(`wants ${wanted === 1 ? 'a tower' : `${wanted} towers`} at the base at ${where(at)}: raided ${record.count} times`, {\n\t\t\t\t\tkind: 'defence'\n\t\t\t\t})\n\t\t\tai.plans.build.want({\n\t\t\t\tid,\n\t\t\t\ttype,\n\t\t\t\t// the towers elsewhere, and so many here\n\t\t\t\tcount: (brain) => {\n\t\t\t\t\tconst all = brain.view.mine.ofType(type)\n\t\t\t\t\treturn all.length - all.near(at, 14).length + wanted\n\t\t\t\t},\n\t\t\t\tnear: at,\n\t\t\t\twithin: 12,\n\t\t\t\tpriority: 45,\n\t\t\t\tkeep: true,\n\t\t\t\tby: 'defence'\n\t\t\t})\n\t\t}\n\t}\n\n\t/** The defence as the report shows it */\n\treport(): DefenceReport {\n\t\tconst { ai } = this\n\t\tif (!this.enabled) return { raids: [], bases: [], towers: [] }\n\t\tconst squads = ai.squads.of(DEFEND).squads\n\t\treturn {\n\t\t\traids: this.raids().map((raid) => {\n\t\t\t\tconst squad = squads.find((each) => {\n\t\t\t\t\tconst at = each.current?.data.at as Vec2 | undefined\n\t\t\t\t\treturn !!at && raid.base.buildings.some((building) => distSq(building, at) <= this.radius * this.radius * 4)\n\t\t\t\t})\n\t\t\t\tconst record = this.memory.raids[raid.key]\n\t\t\t\treturn {\n\t\t\t\t\tat: pair(raid.base.at),\n\t\t\t\t\tintruders: raid.intruders.map((unit) => unit.id),\n\t\t\t\t\tstrength: Math.round(raid.strength),\n\t\t\t\t\t...(squad && { squad: squad.name }),\n\t\t\t\t\tdefenders: squad?.length ?? 0,\n\t\t\t\t\tsince: record?.since ?? ai.tick,\n\t\t\t\t\tcount: record?.count ?? 1\n\t\t\t\t}\n\t\t\t}),\n\t\t\tbases: Object.values(this.memory.raids).map((record) => ({ at: pair(record.at), raids: record.count, last: record.last })),\n\t\t\ttowers: ai.plans.build\n\t\t\t\t.list()\n\t\t\t\t.filter((want) => want.id?.startsWith('defence.tower:'))\n\t\t\t\t.map((want) => ({ at: pair(want.near as Vec2), want: want.id!, status: ai.plans.build.status(want.id!) ?? 'new' }))\n\t\t}\n\t}\n\n\t/** What it remembers, for a save: a copy */\n\t/** Whether it has anything to remember now: one that does not is left out of a save */\n\tremembers(): boolean {\n\t\treturn this.enabled\n\t}\n\n\t/** Its part of the brain's report */\n\treportTo(report: AiReport): void {\n\t\treport.defence = this.report()\n\t}\n\n\t/** Gives a copy of the defence's memory, for a save of the brain */\n\tsave(): DefenceMemory {\n\t\treturn structuredClone(this.memory)\n\t}\n\n\t/** Takes back what `save` gave: whether it was read */\n\trestore(memory: unknown): boolean {\n\t\tconst given = memory as DefenceMemory | undefined\n\t\tif (!given || typeof given !== 'object' || given.v !== 1) return false\n\t\tthis.memory = { v: 1, raids: structuredClone(given.raids ?? {}) }\n\t\treturn true\n\t}\n}\n\n/**\n * The defence module: the raids on the bases counted each think, the towers a raided base wants (`ai.defence.think`),\n * and the `defend` goal put on the brain's picker, which answers each raid with a squad of its own. With how it defends\n * where the brain's definition and the seat's options say nothing (`DefenceOptions`)\n *\n * @beta\n */\nexport const defence = (options: DefenceOptions = {}): AiModuleDef =>\n\tdefineModule({ id: 'defence', make: (ai: Brain): AiModule => {\n\t\tai.area(Defence).configure(options)\n\t\treturn {\n\t\t\tid: 'defence',\n\t\t\t// before the goals are picked: the raids are counted as the goal reads them\n\t\t\tphase: 'sense',\n\t\t\tthink: () => ai.area(Defence).think()\n\t\t}\n\t} })\n\ndeclare module './area' {\n\tinterface AreaMemories {\n\t\tdefence?: DefenceMemory\n\t}\n}\n"],"mappings":"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"}
|
package/dist/ai/define.d.ts
CHANGED
|
@@ -9,13 +9,14 @@ import type { BrainDef, BrainExtension } from './brain';
|
|
|
9
9
|
* @beta
|
|
10
10
|
*/
|
|
11
11
|
export interface DefinedBrain extends BrainDef {
|
|
12
|
+
/** Gives a brain of this one with the extension laid over it: its modules, settings and plans added to or replaced */
|
|
12
13
|
with(more: BrainExtension): DefinedBrain;
|
|
13
14
|
}
|
|
14
15
|
/**
|
|
15
16
|
* Defines a brain: what a computer player is made of, to seat at a world (`world.computers.seat(player, { ...options, brain })`,
|
|
16
|
-
* `world.computers.seat`) and to list in `
|
|
17
|
+
* `world.computers.seat`) and to list in `Rules.brains`, for a save to find it by its id. Modules that keep state
|
|
17
18
|
* are given as functions of the brain, as systems are of the world
|
|
18
19
|
*
|
|
19
|
-
* @
|
|
20
|
+
* @public
|
|
20
21
|
*/
|
|
21
22
|
export declare function defineBrain(def: BrainDef): DefinedBrain;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { resolveSettings } from "./settings.js";
|
|
2
|
+
import { createBrainSystem, mapAiOptions } from "./computer.js";
|
|
3
|
+
//#region src/ai/define.ts
|
|
4
|
+
/**
|
|
5
|
+
* Defines a brain: what a computer player is made of, to seat at a world (`world.computers.seat(player, { ...options, brain })`,
|
|
6
|
+
* `world.computers.seat`) and to list in `Rules.brains`, for a save to find it by its id. Modules that keep state
|
|
7
|
+
* are given as functions of the brain, as systems are of the world
|
|
8
|
+
*
|
|
9
|
+
* @public
|
|
10
|
+
*/
|
|
11
|
+
function defineBrain(def) {
|
|
12
|
+
if (!def.id) throw new Error("A brain needs an id");
|
|
13
|
+
if (!Array.isArray(def.modules)) throw new Error(`The brain "${def.id}" needs its modules, as a list`);
|
|
14
|
+
const brain = { ...def };
|
|
15
|
+
brain.with = (more) => defineBrain({
|
|
16
|
+
...def,
|
|
17
|
+
...more,
|
|
18
|
+
seat: more.seat,
|
|
19
|
+
modules: [...def.modules, ...more.modules ?? []],
|
|
20
|
+
goals: [...def.goals ?? [], ...more.goals ?? []],
|
|
21
|
+
...(def.settings || more.settings) && { settings: resolveSettings([def.settings, more.settings]) },
|
|
22
|
+
...(def.rules || more.rules) && { rules: [...def.rules ?? [], ...more.rules ?? []] },
|
|
23
|
+
...(def.without || more.without) && { without: [...def.without ?? [], ...more.without ?? []] }
|
|
24
|
+
});
|
|
25
|
+
brain.seat ??= (world, player, options) => createBrainSystem(world, player, brain, mapAiOptions(world, player, options));
|
|
26
|
+
return brain;
|
|
27
|
+
}
|
|
28
|
+
//#endregion
|
|
29
|
+
export { defineBrain };
|
|
30
|
+
|
|
31
|
+
//# sourceMappingURL=define.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"define.js","names":[],"sources":["../../src/ai/define.ts"],"sourcesContent":["/**\n * A brain defined (`defineBrain`): checked, given its `with` for variants, and its seat (`BrainDef.seat`), which is the\n * kit's (`createBrainSystem`). Apart from the brain itself (`brain.ts`), which the seat is made of\n */\nimport type { BrainDef, BrainExtension } from './brain'\nimport { createBrainSystem, mapAiOptions } from './computer'\nimport { resolveSettings } from './settings'\n\n/**\n * A brain as `defineBrain` gives it: with its `with`, for variants of it\n *\n * @beta\n */\nexport interface DefinedBrain extends BrainDef {\n\t/** Gives a brain of this one with the extension laid over it: its modules, settings and plans added to or replaced */\n\twith(more: BrainExtension): DefinedBrain\n}\n\n/**\n * Defines a brain: what a computer player is made of, to seat at a world (`world.computers.seat(player, { ...options, brain })`,\n * `world.computers.seat`) and to list in `Rules.brains`, for a save to find it by its id. Modules that keep state\n * are given as functions of the brain, as systems are of the world\n *\n * @public\n */\nexport function defineBrain(def: BrainDef): DefinedBrain {\n\tif (!def.id) throw new Error('A brain needs an id')\n\tif (!Array.isArray(def.modules)) throw new Error(`The brain \"${def.id}\" needs its modules, as a list`)\n\t// a brain of its own: what it was given is left as it is (AI4: a frozen literal would have thrown)\n\tconst brain = { ...def } as DefinedBrain\n\tbrain.with = (more) =>\n\t\tdefineBrain({\n\t\t\t...def,\n\t\t\t...more,\n\t\t\t// its own seat, of the variant: not the one it was made from\n\t\t\tseat: more.seat,\n\t\t\tmodules: [...def.modules, ...(more.modules ?? [])],\n\t\t\tgoals: [...(def.goals ?? []), ...(more.goals ?? [])],\n\t\t\t...((def.settings || more.settings) && { settings: resolveSettings([def.settings, more.settings]) }),\n\t\t\t...((def.rules || more.rules) && { rules: [...(def.rules ?? []), ...(more.rules ?? [])] }),\n\t\t\t...((def.without || more.without) && { without: [...(def.without ?? []), ...(more.without ?? [])] })\n\t\t})\n\t// seated as the kit seats a brain, so that the core does not have to know how (`world.computers.seat`)\n\tbrain.seat ??= (world, player, options) => createBrainSystem(world, player, brain, mapAiOptions(world, player, options))\n\treturn brain\n}\n"],"mappings":";;;;;;;;;;AAyBA,SAAgB,YAAY,KAA6B;CACxD,IAAI,CAAC,IAAI,IAAI,MAAM,IAAI,MAAM,qBAAqB;CAClD,IAAI,CAAC,MAAM,QAAQ,IAAI,OAAO,GAAG,MAAM,IAAI,MAAM,cAAc,IAAI,GAAG,+BAA+B;CAErG,MAAM,QAAQ,EAAE,GAAG,IAAI;CACvB,MAAM,QAAQ,SACb,YAAY;EACX,GAAG;EACH,GAAG;EAEH,MAAM,KAAK;EACX,SAAS,CAAC,GAAG,IAAI,SAAS,GAAI,KAAK,WAAW,CAAC,CAAE;EACjD,OAAO,CAAC,GAAI,IAAI,SAAS,CAAC,GAAI,GAAI,KAAK,SAAS,CAAC,CAAE;EACnD,IAAK,IAAI,YAAY,KAAK,aAAa,EAAE,UAAU,gBAAgB,CAAC,IAAI,UAAU,KAAK,QAAQ,CAAC,EAAE;EAClG,IAAK,IAAI,SAAS,KAAK,UAAU,EAAE,OAAO,CAAC,GAAI,IAAI,SAAS,CAAC,GAAI,GAAI,KAAK,SAAS,CAAC,CAAE,EAAE;EACxF,IAAK,IAAI,WAAW,KAAK,YAAY,EAAE,SAAS,CAAC,GAAI,IAAI,WAAW,CAAC,GAAI,GAAI,KAAK,WAAW,CAAC,CAAE,EAAE;CACnG,CAAC;CAEF,MAAM,UAAU,OAAO,QAAQ,YAAY,kBAAkB,OAAO,QAAQ,OAAO,aAAa,OAAO,QAAQ,OAAO,CAAC;CACvH,OAAO;AACR"}
|
package/dist/ai/economy.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type ResourceId } from '#engine/defs';
|
|
2
2
|
import { type Vec2 } from '#engine/math';
|
|
3
3
|
import type { EntityState } from '#engine/session/protocol';
|
|
4
4
|
import { type Brain } from './brain';
|
|
@@ -11,7 +11,7 @@ import type { Base } from './view';
|
|
|
11
11
|
*
|
|
12
12
|
* @beta
|
|
13
13
|
*/
|
|
14
|
-
export type WorkersPer = Partial<Record<
|
|
14
|
+
export type WorkersPer = Partial<Record<ResourceId, number>> | ((ai: Brain, base: Base, index: number) => Partial<Record<ResourceId, number>>);
|
|
15
15
|
/**
|
|
16
16
|
* How a brain runs its economy (`BrainDef.economy`, the seat's `AiOptions.economy` over it, the `economy()` module's
|
|
17
17
|
* own under both). Every field has a data form a save keeps; the function forms are the brain's to give again
|
|
@@ -53,6 +53,7 @@ export interface EconomyOptions {
|
|
|
53
53
|
* @beta
|
|
54
54
|
*/
|
|
55
55
|
export interface EconomyMemory {
|
|
56
|
+
/** The version of the memory's format: 1 so far */
|
|
56
57
|
v: 1;
|
|
57
58
|
/** Each worker that fled, by id: the base it fled (its middle), where to, since when, and since when its base stood clear */
|
|
58
59
|
fled: Record<string, {
|
|
@@ -83,13 +84,13 @@ export interface EconomyBase {
|
|
|
83
84
|
/** The view's base */
|
|
84
85
|
base: Base;
|
|
85
86
|
/** Its deposits it gathers from, by resource: those of the money with a hall by them (or something of its own on them), the rest near it */
|
|
86
|
-
deposits: Partial<Record<
|
|
87
|
+
deposits: Partial<Record<ResourceId, EntityState[]>>;
|
|
87
88
|
/** How many workers its deposits of each resource serve at once */
|
|
88
|
-
serves: Partial<Record<
|
|
89
|
+
serves: Partial<Record<ResourceId, number>>;
|
|
89
90
|
/** How many it wants on each resource: what it is to have, no more than what is served where it saturates */
|
|
90
|
-
wants: Partial<Record<
|
|
91
|
+
wants: Partial<Record<ResourceId, number>>;
|
|
91
92
|
/** The workers at work on each resource at it, by id */
|
|
92
|
-
working: Partial<Record<
|
|
93
|
+
working: Partial<Record<ResourceId, number[]>>;
|
|
93
94
|
}
|
|
94
95
|
/**
|
|
95
96
|
* The economy as the report shows it (`AiReport.economy.bases`, `fled`): each base with what it wants and has, the
|
|
@@ -98,11 +99,13 @@ export interface EconomyBase {
|
|
|
98
99
|
* @beta
|
|
99
100
|
*/
|
|
100
101
|
export interface EconomyReport {
|
|
102
|
+
/** The bases, each with where it is, its hall's entity id and per resource the workers working, wanted and served by it */
|
|
101
103
|
bases: {
|
|
102
104
|
at: [number, number];
|
|
103
105
|
hall: number;
|
|
104
|
-
workers: Record<
|
|
106
|
+
workers: Record<ResourceId, [working: number, wants: number, serves: number]>;
|
|
105
107
|
}[];
|
|
108
|
+
/** The workers that fled a raid, each with the unit's id, the place it fled and the tick it fled at */
|
|
106
109
|
fled: {
|
|
107
110
|
unit: number;
|
|
108
111
|
from: [number, number];
|