@pikehub/warpass-bot 62.0.1 → 63.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,63 @@
1
- # Changes in 62.0.1
1
+ # Changes in 63.0.1
2
2
 
3
- Compared with engine 62.
3
+ Compared with engine 63.
4
+
5
+ ## Buildings
6
+
7
+ - changed `templars:keep`: armor: null → {"melee":3,"pierce":6}; attack: null → {"damage":4,"range":1792,"minRange":0,"cooldown":40,"splash":0,"onlyBuildings":false}
8
+ - changed `templars:house`: armor: null → {"melee":1,"pierce":6}; attack: null → null
9
+ - changed `templars:farm`: armor: null → {"melee":0,"pierce":0}; attack: null → null
10
+ - changed `templars:granary`: armor: null → {"melee":1,"pierce":6}; attack: null → null
11
+ - changed `templars:lumberMill`: armor: null → {"melee":1,"pierce":6}; attack: null → null
12
+ - changed `templars:miningCamp`: armor: null → {"melee":1,"pierce":6}; attack: null → null
13
+ - changed `templars:barracks`: armor: null → {"melee":1,"pierce":8}; attack: null → null
14
+ - changed `templars:archeryRange`: armor: null → {"melee":1,"pierce":8}; attack: null → null
15
+ - changed `templars:blacksmith`: armor: null → {"melee":1,"pierce":8}; attack: null → null
16
+ - changed `templars:stable`: armor: null → {"melee":1,"pierce":8}; attack: null → null
17
+ - changed `templars:chapel`: armor: null → {"melee":1,"pierce":8}; attack: null → null
18
+ - changed `templars:masonsLodge`: armor: null → {"melee":2,"pierce":8}; attack: null → null
19
+ - changed `templars:siegeWorkshop`: armor: null → {"melee":1,"pierce":8}; attack: null → null
20
+ - changed `templars:powderMill`: armor: null → {"melee":1,"pierce":8}; attack: null → null
21
+ - changed `templars:palisade`: armor: null → {"melee":0,"pierce":3}; attack: null → null
22
+ - changed `templars:stoneWall`: armor: null → {"melee":6,"pierce":8}; attack: null → null
23
+ - changed `templars:gate`: armor: null → {"melee":6,"pierce":10}; attack: null → null
24
+ - changed `templars:watchtower`: armor: null → {"melee":1,"pierce":7}; attack: null → {"damage":5,"range":2048,"minRange":256,"cooldown":40,"splash":0,"onlyBuildings":false}
25
+ - changed `orcs:skullHall`: armor: null → {"melee":3,"pierce":5}; attack: null → {"damage":4,"range":1792,"minRange":0,"cooldown":40,"splash":0,"onlyBuildings":false}
26
+ - changed `orcs:burrow`: armor: null → {"melee":1,"pierce":5}; attack: null → null
27
+ - changed `orcs:mushroomBed`: armor: null → {"melee":0,"pierce":0}; attack: null → null
28
+ - changed `orcs:gutLarder`: armor: null → {"melee":1,"pierce":5}; attack: null → null
29
+ - changed `orcs:boneYard`: armor: null → {"melee":1,"pierce":5}; attack: null → null
30
+ - changed `orcs:goldPit`: armor: null → {"melee":1,"pierce":5}; attack: null → null
31
+ - changed `orcs:spawningPit`: armor: null → {"melee":1,"pierce":7}; attack: null → null
32
+ - changed `orcs:totemLodge`: armor: null → {"melee":1,"pierce":7}; attack: null → null
33
+ - changed `orcs:fleshDen`: armor: null → {"melee":1,"pierce":7}; attack: null → null
34
+ - changed `orcs:bloodAltar`: armor: null → {"melee":2,"pierce":7}; attack: null → null
35
+ - changed `orcs:spikeTower`: armor: null → {"melee":1,"pierce":7}; attack: null → {"damage":6,"range":1792,"minRange":256,"cooldown":40,"splash":0,"onlyBuildings":false}
36
+ - changed `witches:covenSpire`: armor: null → {"melee":3,"pierce":5}; attack: null → {"damage":4,"range":1792,"minRange":0,"cooldown":40,"splash":0,"onlyBuildings":false}
37
+ - changed `witches:starlitCottage`: armor: null → {"melee":1,"pierce":6}; attack: null → null
38
+ - changed `witches:moonGarden`: armor: null → {"melee":0,"pierce":0}; attack: null → null
39
+ - changed `witches:stillroom`: armor: null → {"melee":1,"pierce":6}; attack: null → null
40
+ - changed `witches:sylvanLodge`: armor: null → {"melee":1,"pierce":6}; attack: null → null
41
+ - changed `witches:geodeVault`: armor: null → {"melee":1,"pierce":6}; attack: null → null
42
+ - changed `witches:wayshrine`: armor: null → {"melee":1,"pierce":5}; attack: null → null
43
+ - changed `witches:sanctum`: armor: null → {"melee":1,"pierce":8}; attack: null → null
44
+ - changed `witches:aerie`: armor: null → {"melee":1,"pierce":8}; attack: null → null
45
+ - changed `witches:observatory`: armor: null → {"melee":1,"pierce":8}; attack: null → null
46
+ - changed `witches:atelier`: armor: null → {"melee":1,"pierce":8}; attack: null → null
47
+ - changed `witches:gargoyleRoost`: armor: null → {"melee":2,"pierce":8}; attack: null → null
48
+ - changed `witches:wardstone`: armor: null → {"melee":1,"pierce":6}; attack: null → {"damage":4,"range":1792,"minRange":256,"cooldown":32,"splash":0,"onlyBuildings":false}
49
+ - changed `witches:hexSpire`: armor: null → {"melee":1,"pierce":7}; attack: null → {"damage":5,"range":2048,"minRange":256,"cooldown":50,"splash":0,"onlyBuildings":false}
50
+ - changed `witches:starfallBeacon`: armor: null → {"melee":2,"pierce":8}; attack: null → {"damage":30,"range":2816,"minRange":1024,"cooldown":120,"splash":256,"onlyBuildings":false}
51
+ - changed `witches:leyObelisk`: armor: null → {"melee":0,"pierce":4}; attack: null → null
52
+ - changed `witches:hexCircle`: armor: null → {"melee":0,"pierce":2}; attack: null → null
53
+ - changed `witches:thornSnare`: armor: null → {"melee":0,"pierce":2}; attack: null → null
54
+ - changed `witches:briarHedge`: armor: null → {"melee":0,"pierce":3}; attack: null → null
55
+ - changed `witches:moonstoneWall`: armor: null → {"melee":5,"pierce":8}; attack: null → null
56
+ - changed `witches:moonGate`: armor: null → {"melee":5,"pierce":10}; attack: null → null
57
+ - changed `-:sawmillWeir`: armor: null → {"melee":2,"pierce":6}; attack: null → null
58
+ - changed `-:deepMine`: armor: null → {"melee":3,"pierce":8}; attack: null → null
59
+ - changed `-:quarry`: armor: null → {"melee":3,"pierce":8}; attack: null → null
60
+ - changed `-:horseRanch`: armor: null → {"melee":2,"pierce":6}; attack: null → null
61
+ - changed `-:warCamp`: armor: null → {"melee":2,"pierce":6}; attack: null → null
62
+ - changed `-:engineersYard`: armor: null → {"melee":2,"pierce":8}; attack: null → null
63
+ - changed `-:holyWell`: armor: null → {"melee":2,"pierce":6}; attack: null → null
package/docs/api/act.md CHANGED
@@ -14,7 +14,7 @@ Every order a bot can give, on `act` during a think (and on the builder passed t
14
14
  | `target(who, target, { queued })` | `target(who, target, queued=False)` | the right-click order: attack an enemy, gather a resource, repair your building |
15
15
  | `repair(who, target, { queued })` | `repair(who, target, queued=False)` | workers repair one of your buildings |
16
16
  | `gather(who, tile, { queued })` | `gather(who, tile, queued=False)` | workers gather the resource on a tile (from `nearestResource`) |
17
- | `build(who, building, at, { queued })` | `build(who, building, at, queued=False)` | workers place and build a building with its top-left at `at`; pays on placement |
17
+ | `build(who, building, at, { queued })` | `build(who, building, at, queued=False)` | workers place and build a building with its top-left at `at` (the footprint must be in view); pays on placement |
18
18
  | `buildLine(who, building, from, to, { queued })` | `build_line(who, building, start, end, queued=False)` | a line of wall segments |
19
19
  | `train(building, unit, count = 1)` | `train(building, unit, count=1)` | queue units (pays now) |
20
20
  | `research(building, upgrade)` | `research(building, upgrade)` | queue an upgrade (pays now) |
@@ -51,7 +51,31 @@ Ops after the first step still apply but make the game unreplayable.
51
51
  | `until(maxTicks, (observer, tick) => done)` | `until(max_ticks, lambda observer, tick: done)` | step until `done`; returns ticks taken or `-1` |
52
52
  | `view(slot)` | `view(slot)` | the fogged [view](view.md) of a slot |
53
53
  | `observer()` | `observer()` | the full view of everything, no fog |
54
+ | `events(slot)` | `events(slot)` | the [events](events.md) that slot got since the last call, fog-filtered exactly as in a match |
55
+ | `observerEvents()` | `observer_events()` | every event since the last call, unfiltered |
54
56
  | `game(slot)` | `game(slot)` | [queries](game.md) as that slot |
55
57
  | `map()` | `map()` | map size, starts, chokes, sites |
56
58
  | `replay()` | `replay()` | a [Replay](replay.md) of setup and orders so far |
57
59
  | `tick`, `winner` | `tick`, `winner` | current tick; winning slot once over |
60
+
61
+ The first `events` call for a slot (or `observerEvents`) starts recording for it and returns `[]`, so call it once before stepping. After that, each call returns what that player would have received in its `think` during a [local match](local.md):
62
+
63
+ ```js
64
+ import { CustomGame, rules } from "@pikehub/warpass-bot";
65
+
66
+ const r = rules();
67
+ const [a, b] = r.races;
68
+ const g = new CustomGame({ seed: 5, races: [a, b] });
69
+ const [x0, y0] = g.flattenArea(24, 12);
70
+ const fighter = (race) => r.units.find((u) => u.race === race && !u.worker && u.attack);
71
+ const ours = [0, 1, 2].map((k) => g.spawn(0, fighter(a).id, (x0 + 4) * 256, (y0 + 3 + k) * 256));
72
+ [0, 1, 2].forEach((k) => g.spawn(1, fighter(b).id, (x0 + 10) * 256, (y0 + 3 + k) * 256));
73
+ g.events(0);
74
+ g.events(1);
75
+ g.order(0, (act) => act.attackMove(ours, (x0 + 12) * 256, (y0 + 4) * 256));
76
+ g.step(20 * 30);
77
+ const mine = g.events(0);
78
+ const theirs = g.events(1);
79
+ console.log(`slot 0: ${mine.filter((e) => e.t === "hit").length} hits, ${mine.filter((e) => e.t === "death").length} deaths`);
80
+ console.log(`slot 1: ${theirs.filter((e) => e.t === "hit").length} hits`);
81
+ ```
@@ -2,6 +2,8 @@
2
2
 
3
3
  What happened to your player since the last think, in `events` (oldest first). Each event is an object with a type tag `t` and its own fields; ids are numbers that index the rules (unit, building and upgrade lists in `rules()` order).
4
4
 
5
+ In a [custom game](custom.md), `events(slot)` returns the same events for any slot.
6
+
5
7
  | `t` | When |
6
8
  |---|---|
7
9
  | `trained` | one of your units finished training |
package/docs/api/game.md CHANGED
@@ -12,7 +12,7 @@ Questions a bot can ask during a think (`game`, or `match.game` online). Queries
12
12
  | `canResearch(building, upgrade)` | `can_research(building, upgrade)` | |
13
13
  | `placeRefusal(building, at)` | `place_refusal(building, at)` | `null` if a building id fits with its top-left at tile `at`, else a reason |
14
14
  | `canPlace(building, at)` | `can_place(building, at)` | |
15
- | `findPlacement(building, near, radius = 16)` | `find_placement(building, near, radius=16)` | `[tx, ty]` where it fits closest to `near`, or `null` |
15
+ | `findPlacement(building, near, radius = 16)` | `find_placement(building, near, radius=16)` | `[tx, ty]` where it fits closest to `near` and is in view now, or `null` |
16
16
  | `nearestResource(from, resource, radius = 40)` | `nearest_resource(start, resource, radius=40)` | `[tx, ty]` of the nearest tile with `food`, `wood`, `stone` or `gold` left |
17
17
  | `reachable(from, to)` | `reachable(start, end)` | `true` if your units can walk between the two tiles |
18
18
  | `front(toward?)` | `front(toward=None)` | `[tx, ty]`: the tile where your army should gather to face tile `toward` (default: the map centre), usually a choke near your base |
@@ -23,4 +23,4 @@ Questions a bot can ask during a think (`game`, or `match.game` online). Queries
23
23
  - `building` arguments take a view object or an eid; `unit`, `upgrade` and building ids are strings from the rules.
24
24
  - Tile arguments take `[tx, ty]` or an object with `x`, `y` in tiles.
25
25
  - Training and research refusals: `notEnough` (resources), `supplyLimit`, `needHousing`, `queueFull`, `requirementsNotMet`, `limitReached`, `invalid` (wrong building, not finished, not yours).
26
- - Placement refusals: `outOfBounds`, `somethingInWay`, `cannotBuildThere`, `unevenGround`, `rampBlocked`, `buildOnSite` / `siteGround` (site buildings must stand on their site), `alreadyBuilt`, `limitReached`, `requirementsNotMet`, `unknownBuilding`.
26
+ - Placement refusals: `outOfBounds`, `notVisible` (every footprint tile must be in your current vision; send a unit to look first), `somethingInWay`, `cannotBuildThere`, `unevenGround`, `rampBlocked`, `buildOnSite` / `siteGround` (site buildings must stand on their site), `alreadyBuilt`, `limitReached`, `requirementsNotMet`, `unknownBuilding`.
@@ -9,6 +9,7 @@ Connect a bot account, queue or join a lobby by code, play a match and read the
9
9
  | `await offer.accept({ everyTicks? })` | `offer.accept(every_ticks=10)` | `OnlineMatch` once both accepted |
10
10
  | `await wp.lobby({ code, race, everyTicks? })` | `wp.lobby(code, race, every_ticks=10)` | `OnlineMatch` once the host starts: joins a player's lobby by its code, marks ready (again after the host changes the map or race) and waits |
11
11
  | `await offer.decline()` | `offer.decline()` | declines; the queue may give you a short penalty |
12
+ | `wp.close()` | `wp.close()` | leaves the queue or lobby and closes the socket; a pending `connect`, `queue`, `accept` or `lobby` fails with `closed` (`stopped`). Call it before exiting on Ctrl-C |
12
13
  | `await Warpass.join({ token, matchId, url?, everyTicks? })` | `Warpass.join(match_id, token=, url=, every_ticks=10)` | `OnlineMatch`: rejoin a match you are in after a crash |
13
14
  | `match.onThink(think, { everyTicks? })` | `match.on_think(think, every_ticks=None)` | registers your think; returns the match |
14
15
  | `await match.ended()` | `match.ended()` | `{ reason }` when the match ends |
@@ -17,8 +18,10 @@ Connect a bot account, queue or join a lobby by code, play a match and read the
17
18
  | `match.id`, `match.slot` | `match.id`, `match.slot` | the match id and your slot |
18
19
 
19
20
  - `url` defaults to `DEFAULT_URL` (`https://warpass.pike.com.br`).
21
+ - A match takes over the connection; after it ends, `queue` and `lobby` on the same `Warpass` reconnect by themselves, so one `Warpass` can play match after match.
20
22
  - `Offer.opponent` is `{ name, bot, rating, race }`.
21
23
  - `lobby` fails with `refused` (`missing`, `full`, `started`) when the code is wrong or taken, and with `cancelled` (`closed`) when the host closes the lobby.
24
+ - In Python, Ctrl-C during `connect`, `queue`, `accept` or `lobby` closes the session and raises `KeyboardInterrupt`.
22
25
  - In Python, `ended()` and `result()` run your think loop on the calling thread until the match ends; an `async def` think is awaited.
23
26
  - In JS, the think loop runs in the background after `onThink`; `ended()` and `result()` wait for it.
24
27
 
package/docs/api/rules.md CHANGED
@@ -18,11 +18,11 @@ Static facts about the bundled engine, and these docs, from code.
18
18
  ```
19
19
  { engineVersion, races, resources, terrains: { name: id }, tile, ticksPerSecond, mapSize,
20
20
  units: [{ id, race, cost, supply, hp, speed, sight, trainTicks, trainedAt, requires, abilities, worker, lane, armor: { melee, pierce }, attack }],
21
- buildings: [{ id, race, size, cost, hp, buildTicks, supply, trains, researches, requires, site }],
21
+ buildings: [{ id, race, size, cost, hp, buildTicks, supply, trains, researches, requires, site, armor: { melee, pierce }, attack }],
22
22
  upgrades: [{ id, race, cost, ticks, requires, group }] }
23
23
  ```
24
24
 
25
- `requires` is `{ buildings, anyBuildings, upgrades, anyUpgrades }` (ids). `attack` is `{ damage, range, minRange, cooldown, splash, onlyBuildings }` or `null`. Units of measure are in [../rules/index.md](../rules/index.md); the same data as tables is under `rules/`.
25
+ `requires` is `{ buildings, anyBuildings, upgrades, anyUpgrades }` (ids). `attack` (units and buildings) is `{ damage, range, minRange, cooldown, splash, onlyBuildings }` or `null`. Units of measure are in [../rules/index.md](../rules/index.md); the same data as tables is under `rules/`.
26
26
 
27
27
  ```js
28
28
  import { docs, docsIndex } from "@pikehub/warpass-bot";
package/docs/errors.md CHANGED
@@ -8,7 +8,7 @@ Every SDK call that fails throws a `WarpassError` with a `kind` and the fields b
8
8
  | `unauthenticated` | | the key is wrong, rotated, or the bot was deleted | create or rotate the key on the Developer page |
9
9
  | `refused` | `about`, `reason`, `message` | the server refused a request | see reasons below |
10
10
  | `cancelled` | `reason` | the offer fell through (`declined`, `timeout`, the rival left) or the host closed the lobby (`closed`) | queue again |
11
- | `closed` | `reason` | the socket closed and could not reconnect | connect again; `Warpass.join` resumes a running match |
11
+ | `closed` | `reason` | the socket closed and could not reconnect, or you called `close()` (`stopped`) | connect again; `Warpass.join` resumes a running match |
12
12
  | `timeout` | | no answer in time (`result` waits 120 s by default) | retry `result()`; the match may still be sealing |
13
13
  | `network` | `message` | DNS, TLS or HTTP failure | check `url` and connectivity |
14
14
 
@@ -285,6 +285,7 @@ Connect a bot account, queue or join a lobby by code, play a match and read the
285
285
  | `await offer.accept({ everyTicks? })` | `offer.accept(every_ticks=10)` | `OnlineMatch` once both accepted |
286
286
  | `await wp.lobby({ code, race, everyTicks? })` | `wp.lobby(code, race, every_ticks=10)` | `OnlineMatch` once the host starts: joins a player's lobby by its code, marks ready (again after the host changes the map or race) and waits |
287
287
  | `await offer.decline()` | `offer.decline()` | declines; the queue may give you a short penalty |
288
+ | `wp.close()` | `wp.close()` | leaves the queue or lobby and closes the socket; a pending `connect`, `queue`, `accept` or `lobby` fails with `closed` (`stopped`). Call it before exiting on Ctrl-C |
288
289
  | `await Warpass.join({ token, matchId, url?, everyTicks? })` | `Warpass.join(match_id, token=, url=, every_ticks=10)` | `OnlineMatch`: rejoin a match you are in after a crash |
289
290
  | `match.onThink(think, { everyTicks? })` | `match.on_think(think, every_ticks=None)` | registers your think; returns the match |
290
291
  | `await match.ended()` | `match.ended()` | `{ reason }` when the match ends |
@@ -293,8 +294,10 @@ Connect a bot account, queue or join a lobby by code, play a match and read the
293
294
  | `match.id`, `match.slot` | `match.id`, `match.slot` | the match id and your slot |
294
295
 
295
296
  - `url` defaults to `DEFAULT_URL` (`https://warpass.pike.com.br`).
297
+ - A match takes over the connection; after it ends, `queue` and `lobby` on the same `Warpass` reconnect by themselves, so one `Warpass` can play match after match.
296
298
  - `Offer.opponent` is `{ name, bot, rating, race }`.
297
299
  - `lobby` fails with `refused` (`missing`, `full`, `started`) when the code is wrong or taken, and with `cancelled` (`closed`) when the host closes the lobby.
300
+ - In Python, Ctrl-C during `connect`, `queue`, `accept` or `lobby` closes the session and raises `KeyboardInterrupt`.
298
301
  - In Python, `ended()` and `result()` run your think loop on the calling thread until the match ends; an `async def` think is awaited.
299
302
  - In JS, the think loop runs in the background after `onThink`; `ended()` and `result()` wait for it.
300
303
 
@@ -423,7 +426,7 @@ Questions a bot can ask during a think (`game`, or `match.game` online). Queries
423
426
  | `canResearch(building, upgrade)` | `can_research(building, upgrade)` | |
424
427
  | `placeRefusal(building, at)` | `place_refusal(building, at)` | `null` if a building id fits with its top-left at tile `at`, else a reason |
425
428
  | `canPlace(building, at)` | `can_place(building, at)` | |
426
- | `findPlacement(building, near, radius = 16)` | `find_placement(building, near, radius=16)` | `[tx, ty]` where it fits closest to `near`, or `null` |
429
+ | `findPlacement(building, near, radius = 16)` | `find_placement(building, near, radius=16)` | `[tx, ty]` where it fits closest to `near` and is in view now, or `null` |
427
430
  | `nearestResource(from, resource, radius = 40)` | `nearest_resource(start, resource, radius=40)` | `[tx, ty]` of the nearest tile with `food`, `wood`, `stone` or `gold` left |
428
431
  | `reachable(from, to)` | `reachable(start, end)` | `true` if your units can walk between the two tiles |
429
432
  | `front(toward?)` | `front(toward=None)` | `[tx, ty]`: the tile where your army should gather to face tile `toward` (default: the map centre), usually a choke near your base |
@@ -434,7 +437,7 @@ Questions a bot can ask during a think (`game`, or `match.game` online). Queries
434
437
  - `building` arguments take a view object or an eid; `unit`, `upgrade` and building ids are strings from the rules.
435
438
  - Tile arguments take `[tx, ty]` or an object with `x`, `y` in tiles.
436
439
  - Training and research refusals: `notEnough` (resources), `supplyLimit`, `needHousing`, `queueFull`, `requirementsNotMet`, `limitReached`, `invalid` (wrong building, not finished, not yours).
437
- - Placement refusals: `outOfBounds`, `somethingInWay`, `cannotBuildThere`, `unevenGround`, `rampBlocked`, `buildOnSite` / `siteGround` (site buildings must stand on their site), `alreadyBuilt`, `limitReached`, `requirementsNotMet`, `unknownBuilding`.
440
+ - Placement refusals: `outOfBounds`, `notVisible` (every footprint tile must be in your current vision; send a unit to look first), `somethingInWay`, `cannotBuildThere`, `unevenGround`, `rampBlocked`, `buildOnSite` / `siteGround` (site buildings must stand on their site), `alreadyBuilt`, `limitReached`, `requirementsNotMet`, `unknownBuilding`.
438
441
 
439
442
  <!-- api/act.md -->
440
443
 
@@ -454,7 +457,7 @@ Every order a bot can give, on `act` during a think (and on the builder passed t
454
457
  | `target(who, target, { queued })` | `target(who, target, queued=False)` | the right-click order: attack an enemy, gather a resource, repair your building |
455
458
  | `repair(who, target, { queued })` | `repair(who, target, queued=False)` | workers repair one of your buildings |
456
459
  | `gather(who, tile, { queued })` | `gather(who, tile, queued=False)` | workers gather the resource on a tile (from `nearestResource`) |
457
- | `build(who, building, at, { queued })` | `build(who, building, at, queued=False)` | workers place and build a building with its top-left at `at`; pays on placement |
460
+ | `build(who, building, at, { queued })` | `build(who, building, at, queued=False)` | workers place and build a building with its top-left at `at` (the footprint must be in view); pays on placement |
458
461
  | `buildLine(who, building, from, to, { queued })` | `build_line(who, building, start, end, queued=False)` | a line of wall segments |
459
462
  | `train(building, unit, count = 1)` | `train(building, unit, count=1)` | queue units (pays now) |
460
463
  | `research(building, upgrade)` | `research(building, upgrade)` | queue an upgrade (pays now) |
@@ -475,6 +478,8 @@ Every order a bot can give, on `act` during a think (and on the builder passed t
475
478
 
476
479
  What happened to your player since the last think, in `events` (oldest first). Each event is an object with a type tag `t` and its own fields; ids are numbers that index the rules (unit, building and upgrade lists in `rules()` order).
477
480
 
481
+ In a [custom game](custom.md), `events(slot)` returns the same events for any slot.
482
+
478
483
  | `t` | When |
479
484
  |---|---|
480
485
  | `trained` | one of your units finished training |
@@ -560,11 +565,35 @@ Ops after the first step still apply but make the game unreplayable.
560
565
  | `until(maxTicks, (observer, tick) => done)` | `until(max_ticks, lambda observer, tick: done)` | step until `done`; returns ticks taken or `-1` |
561
566
  | `view(slot)` | `view(slot)` | the fogged [view](view.md) of a slot |
562
567
  | `observer()` | `observer()` | the full view of everything, no fog |
568
+ | `events(slot)` | `events(slot)` | the [events](events.md) that slot got since the last call, fog-filtered exactly as in a match |
569
+ | `observerEvents()` | `observer_events()` | every event since the last call, unfiltered |
563
570
  | `game(slot)` | `game(slot)` | [queries](game.md) as that slot |
564
571
  | `map()` | `map()` | map size, starts, chokes, sites |
565
572
  | `replay()` | `replay()` | a [Replay](replay.md) of setup and orders so far |
566
573
  | `tick`, `winner` | `tick`, `winner` | current tick; winning slot once over |
567
574
 
575
+ The first `events` call for a slot (or `observerEvents`) starts recording for it and returns `[]`, so call it once before stepping. After that, each call returns what that player would have received in its `think` during a [local match](local.md):
576
+
577
+ ```js
578
+ import { CustomGame, rules } from "@pikehub/warpass-bot";
579
+
580
+ const r = rules();
581
+ const [a, b] = r.races;
582
+ const g = new CustomGame({ seed: 5, races: [a, b] });
583
+ const [x0, y0] = g.flattenArea(24, 12);
584
+ const fighter = (race) => r.units.find((u) => u.race === race && !u.worker && u.attack);
585
+ const ours = [0, 1, 2].map((k) => g.spawn(0, fighter(a).id, (x0 + 4) * 256, (y0 + 3 + k) * 256));
586
+ [0, 1, 2].forEach((k) => g.spawn(1, fighter(b).id, (x0 + 10) * 256, (y0 + 3 + k) * 256));
587
+ g.events(0);
588
+ g.events(1);
589
+ g.order(0, (act) => act.attackMove(ours, (x0 + 12) * 256, (y0 + 4) * 256));
590
+ g.step(20 * 30);
591
+ const mine = g.events(0);
592
+ const theirs = g.events(1);
593
+ console.log(`slot 0: ${mine.filter((e) => e.t === "hit").length} hits, ${mine.filter((e) => e.t === "death").length} deaths`);
594
+ console.log(`slot 1: ${theirs.filter((e) => e.t === "hit").length} hits`);
595
+ ```
596
+
568
597
  <!-- api/replay.md -->
569
598
 
570
599
  # Replays
@@ -618,11 +647,11 @@ Static facts about the bundled engine, and these docs, from code.
618
647
  ```
619
648
  { engineVersion, races, resources, terrains: { name: id }, tile, ticksPerSecond, mapSize,
620
649
  units: [{ id, race, cost, supply, hp, speed, sight, trainTicks, trainedAt, requires, abilities, worker, lane, armor: { melee, pierce }, attack }],
621
- buildings: [{ id, race, size, cost, hp, buildTicks, supply, trains, researches, requires, site }],
650
+ buildings: [{ id, race, size, cost, hp, buildTicks, supply, trains, researches, requires, site, armor: { melee, pierce }, attack }],
622
651
  upgrades: [{ id, race, cost, ticks, requires, group }] }
623
652
  ```
624
653
 
625
- `requires` is `{ buildings, anyBuildings, upgrades, anyUpgrades }` (ids). `attack` is `{ damage, range, minRange, cooldown, splash, onlyBuildings }` or `null`. Units of measure are in [../rules/index.md](../rules/index.md); the same data as tables is under `rules/`.
654
+ `requires` is `{ buildings, anyBuildings, upgrades, anyUpgrades }` (ids). `attack` (units and buildings) is `{ damage, range, minRange, cooldown, splash, onlyBuildings }` or `null`. Units of measure are in [../rules/index.md](../rules/index.md); the same data as tables is under `rules/`.
626
655
 
627
656
  ```js
628
657
  import { docs, docsIndex } from "@pikehub/warpass-bot";
@@ -773,7 +802,7 @@ Every SDK call that fails throws a `WarpassError` with a `kind` and the fields b
773
802
  | `unauthenticated` | | the key is wrong, rotated, or the bot was deleted | create or rotate the key on the Developer page |
774
803
  | `refused` | `about`, `reason`, `message` | the server refused a request | see reasons below |
775
804
  | `cancelled` | `reason` | the offer fell through (`declined`, `timeout`, the rival left) or the host closed the lobby (`closed`) | queue again |
776
- | `closed` | `reason` | the socket closed and could not reconnect | connect again; `Warpass.join` resumes a running match |
805
+ | `closed` | `reason` | the socket closed and could not reconnect, or you called `close()` (`stopped`) | connect again; `Warpass.join` resumes a running match |
777
806
  | `timeout` | | no answer in time (`result` waits 120 s by default) | retry `result()`; the match may still be sealing |
778
807
  | `network` | `message` | DNS, TLS or HTTP failure | check `url` and connectivity |
779
808
 
@@ -794,7 +823,7 @@ Errors from local games and custom games (unknown ids, setup ops off the map) ar
794
823
 
795
824
  <!-- rules/index.md -->
796
825
 
797
- # Rules for engine 62
826
+ # Rules for engine 63
798
827
 
799
828
  Generated from the engine this SDK bundles. Every id below is what the view, the queries and the orders use.
800
829
 
@@ -1,4 +1,4 @@
1
- # Rules for engine 62
1
+ # Rules for engine 63
2
2
 
3
3
  Generated from the engine this SDK bundles. Every id below is what the view, the queries and the orders use.
4
4
 
@@ -1,4 +1,4 @@
1
- # orcs (engine 62)
1
+ # orcs (engine 63)
2
2
 
3
3
  Generated from the bundled engine. Units of measure are in [index.md](index.md).
4
4
 
@@ -17,19 +17,19 @@ Generated from the bundled engine. Units of measure are in [index.md](index.md).
17
17
 
18
18
  ## Buildings
19
19
 
20
- | id | size | cost | hp | build | supply | trains | researches | requires |
21
- |---|---|---|---|---|---|---|---|---|
22
- | skullHall | 4×4 | 275 wood, 150 stone | 2200 | 58.6 s | 10 | peon | grubRations, slaveDrivers | - |
23
- | burrow | 2×2 | 30 wood | 450 | 15.2 s | 8 | - | - | - |
24
- | mushroomBed | 3×3 | 55 wood | 200 | 10.3 s | 0 | - | - | - |
25
- | gutLarder | 2×2 | 100 wood | 550 | 19.3 s | 0 | - | - | - |
26
- | boneYard | 2×2 | 100 wood | 550 | 19.3 s | 0 | - | boneSaws | - |
27
- | goldPit | 2×2 | 100 wood | 550 | 19.3 s | 0 | - | deepPits | - |
28
- | spawningPit | 3×3 | 175 wood | 1000 | 24.1 s | 0 | - | broodMothers, feedingFrenzy, endlessHorde | all of burrow |
29
- | totemLodge | 3×3 | 175 wood, 50 gold | 1000 | 27.6 s | 0 | shaman, warDrummer, witchDoctor | bloodRites, deepTrance, bloodFrenzy | all of spawningPit |
30
- | fleshDen | 3×3 | 175 wood, 50 gold | 1000 | 27.6 s | 0 | fleshShaper, bonecaller, venomBrewer | thickHides, grislyMending, abomination | all of spawningPit |
31
- | bloodAltar | 2×2 | 125 wood, 50 stone | 800 | 24.1 s | 0 | - | jaggedIron, serratedIron, hideArmor, bonePlate | all of spawningPit |
32
- | spikeTower | 2×2 | 90 wood, 100 stone | 650 | 22.1 s | 0 | - | - | all of spawningPit |
20
+ | id | size | cost | hp | armor m/p | attack | build | supply | trains | researches | requires |
21
+ |---|---|---|---|---|---|---|---|---|---|---|
22
+ | skullHall | 4×4 | 275 wood, 150 stone | 2200 | 3/5 | 4 dmg, range 7.0 tiles, every 2.0 s | 58.6 s | 10 | peon | grubRations, slaveDrivers | - |
23
+ | burrow | 2×2 | 30 wood | 450 | 1/5 | - | 15.2 s | 8 | - | - | - |
24
+ | mushroomBed | 3×3 | 55 wood | 200 | 0/0 | - | 10.3 s | 0 | - | - | - |
25
+ | gutLarder | 2×2 | 100 wood | 550 | 1/5 | - | 19.3 s | 0 | - | - | - |
26
+ | boneYard | 2×2 | 100 wood | 550 | 1/5 | - | 19.3 s | 0 | - | boneSaws | - |
27
+ | goldPit | 2×2 | 100 wood | 550 | 1/5 | - | 19.3 s | 0 | - | deepPits | - |
28
+ | spawningPit | 3×3 | 175 wood | 1000 | 1/7 | - | 24.1 s | 0 | - | broodMothers, feedingFrenzy, endlessHorde | all of burrow |
29
+ | totemLodge | 3×3 | 175 wood, 50 gold | 1000 | 1/7 | - | 27.6 s | 0 | shaman, warDrummer, witchDoctor | bloodRites, deepTrance, bloodFrenzy | all of spawningPit |
30
+ | fleshDen | 3×3 | 175 wood, 50 gold | 1000 | 1/7 | - | 27.6 s | 0 | fleshShaper, bonecaller, venomBrewer | thickHides, grislyMending, abomination | all of spawningPit |
31
+ | bloodAltar | 2×2 | 125 wood, 50 stone | 800 | 2/7 | - | 24.1 s | 0 | - | jaggedIron, serratedIron, hideArmor, bonePlate | all of spawningPit |
32
+ | spikeTower | 2×2 | 90 wood, 100 stone | 650 | 1/7 | 6 dmg, range 7.0 tiles, every 2.0 s | 22.1 s | 0 | - | - | all of spawningPit |
33
33
 
34
34
  ## Upgrades
35
35
 
@@ -1,4 +1,4 @@
1
- # templars (engine 62)
1
+ # templars (engine 63)
2
2
 
3
3
  Generated from the bundled engine. Units of measure are in [index.md](index.md).
4
4
 
@@ -21,26 +21,26 @@ Generated from the bundled engine. Units of measure are in [index.md](index.md).
21
21
 
22
22
  ## Buildings
23
23
 
24
- | id | size | cost | hp | build | supply | trains | researches | requires |
25
- |---|---|---|---|---|---|---|---|---|
26
- | keep | 4×4 | 300 wood, 175 stone | 2400 | 62.0 s | 10 | peasant | guildCharter, merchantGuilds, guildmasters, wheelbarrow, townWatch | - |
27
- | house | 2×2 | 30 wood | 550 | 17.2 s | 8 | - | - | - |
28
- | farm | 3×3 | 60 wood | 200 | 10.3 s | 0 | - | - | - |
29
- | granary | 2×2 | 100 wood | 600 | 20.6 s | 0 | - | horseCollar, heavyPlow | - |
30
- | lumberMill | 2×2 | 100 wood | 600 | 20.6 s | 0 | - | doubleBitAxe, bowSaw | - |
31
- | miningCamp | 2×2 | 100 wood | 600 | 20.6 s | 0 | - | goldMining, stoneMining | - |
32
- | barracks | 3×3 | 175 wood | 1200 | 27.6 s | 0 | spearman, swordsman | levyRights, drillYards, warhost | all of house |
33
- | archeryRange | 3×3 | 175 wood | 1200 | 27.6 s | 0 | archer, crossbowman | crossbowDrill, fireArrows | all of barracks |
34
- | blacksmith | 3×3 | 150 wood | 1200 | 27.6 s | 0 | - | forging, steel, mail, plate, fletching, bodkin, shieldWall, saltpeter | all of barracks |
35
- | stable | 3×3 | 175 wood | 1200 | 31.0 s | 0 | outrider, knight | squires, lanceCharge, knightsOrder | all of barracks |
36
- | chapel | 3×3 | 150 wood, 75 stone | 1000 | 27.6 s | 0 | cleric | sanctify | all of barracks |
37
- | masonsLodge | 3×3 | 150 wood, 50 stone | 1200 | 31.0 s | 0 | - | masterMasons, guardTowers, fortress | all of barracks |
38
- | siegeWorkshop | 3×3 | 200 wood | 1200 | 34.5 s | 0 | ram, mangonel, bombard | engineersCorps, siegeTrain, grandBattery | all of barracks |
39
- | powderMill | 3×3 | 150 wood, 100 stone | 1300 | 27.6 s | 0 | handgunner | volleyFire, arsenal | researched saltpeter |
40
- | palisade | 1×1 | 6 wood | 160 | 4.1 s | 0 | - | - | - |
41
- | stoneWall | 1×1 | 12 stone | 900 | 6.8 s | 0 | - | - | all of masonsLodge |
42
- | gate | 1×1 | 40 stone | 1200 | 10.3 s | 0 | - | - | all of masonsLodge |
43
- | watchtower | 2×2 | 75 wood, 125 stone | 700 | 24.1 s | 0 | - | - | all of barracks |
24
+ | id | size | cost | hp | armor m/p | attack | build | supply | trains | researches | requires |
25
+ |---|---|---|---|---|---|---|---|---|---|---|
26
+ | keep | 4×4 | 300 wood, 175 stone | 2400 | 3/6 | 4 dmg, range 7.0 tiles, every 2.0 s | 62.0 s | 10 | peasant | guildCharter, merchantGuilds, guildmasters, wheelbarrow, townWatch | - |
27
+ | house | 2×2 | 30 wood | 550 | 1/6 | - | 17.2 s | 8 | - | - | - |
28
+ | farm | 3×3 | 60 wood | 200 | 0/0 | - | 10.3 s | 0 | - | - | - |
29
+ | granary | 2×2 | 100 wood | 600 | 1/6 | - | 20.6 s | 0 | - | horseCollar, heavyPlow | - |
30
+ | lumberMill | 2×2 | 100 wood | 600 | 1/6 | - | 20.6 s | 0 | - | doubleBitAxe, bowSaw | - |
31
+ | miningCamp | 2×2 | 100 wood | 600 | 1/6 | - | 20.6 s | 0 | - | goldMining, stoneMining | - |
32
+ | barracks | 3×3 | 175 wood | 1200 | 1/8 | - | 27.6 s | 0 | spearman, swordsman | levyRights, drillYards, warhost | all of house |
33
+ | archeryRange | 3×3 | 175 wood | 1200 | 1/8 | - | 27.6 s | 0 | archer, crossbowman | crossbowDrill, fireArrows | all of barracks |
34
+ | blacksmith | 3×3 | 150 wood | 1200 | 1/8 | - | 27.6 s | 0 | - | forging, steel, mail, plate, fletching, bodkin, shieldWall, saltpeter | all of barracks |
35
+ | stable | 3×3 | 175 wood | 1200 | 1/8 | - | 31.0 s | 0 | outrider, knight | squires, lanceCharge, knightsOrder | all of barracks |
36
+ | chapel | 3×3 | 150 wood, 75 stone | 1000 | 1/8 | - | 27.6 s | 0 | cleric | sanctify | all of barracks |
37
+ | masonsLodge | 3×3 | 150 wood, 50 stone | 1200 | 2/8 | - | 31.0 s | 0 | - | masterMasons, guardTowers, fortress | all of barracks |
38
+ | siegeWorkshop | 3×3 | 200 wood | 1200 | 1/8 | - | 34.5 s | 0 | ram, mangonel, bombard | engineersCorps, siegeTrain, grandBattery | all of barracks |
39
+ | powderMill | 3×3 | 150 wood, 100 stone | 1300 | 1/8 | - | 27.6 s | 0 | handgunner | volleyFire, arsenal | researched saltpeter |
40
+ | palisade | 1×1 | 6 wood | 160 | 0/3 | - | 4.1 s | 0 | - | - | - |
41
+ | stoneWall | 1×1 | 12 stone | 900 | 6/8 | - | 6.8 s | 0 | - | - | all of masonsLodge |
42
+ | gate | 1×1 | 40 stone | 1200 | 6/10 | - | 10.3 s | 0 | - | - | all of masonsLodge |
43
+ | watchtower | 2×2 | 75 wood, 125 stone | 700 | 1/7 | 5 dmg, range 8.0 tiles, every 2.0 s | 24.1 s | 0 | - | - | all of barracks |
44
44
 
45
45
  ## Upgrades
46
46
 
@@ -1,4 +1,4 @@
1
- # witches (engine 62)
1
+ # witches (engine 63)
2
2
 
3
3
  Generated from the bundled engine. Units of measure are in [index.md](index.md).
4
4
 
@@ -16,29 +16,29 @@ Generated from the bundled engine. Units of measure are in [index.md](index.md).
16
16
 
17
17
  ## Buildings
18
18
 
19
- | id | size | cost | hp | build | supply | trains | researches | requires |
20
- |---|---|---|---|---|---|---|---|---|
21
- | covenSpire | 4×4 | 300 wood, 175 stone | 2400 | 51.7 s | 10 | acolyte | spindleCarry, starChart, swiftLooms, seedstones, moonrise | - |
22
- | starlitCottage | 2×2 | 30 wood | 500 | 15.2 s | 8 | - | - | - |
23
- | moonGarden | 3×3 | 45 wood | 200 | 10.3 s | 0 | - | - | - |
24
- | stillroom | 2×2 | 100 wood | 600 | 20.6 s | 0 | - | herbLore, deepRoots | - |
25
- | sylvanLodge | 2×2 | 100 wood | 600 | 20.6 s | 0 | - | whisperingAxe, heartwood | - |
26
- | geodeVault | 2×2 | 100 wood | 650 | 20.6 s | 0 | - | veinSight, stoneSong | - |
27
- | wayshrine | 2×2 | 90 wood, 80 stone | 400 | 17.2 s | 4 | acolyte, moonblade, hexer | - | all of sanctum |
28
- | sanctum | 3×3 | 140 wood, 40 stone | 1150 | 27.6 s | 0 | moonblade, hexer | silverGlaives, moonsteel, wardedRobes, starweave, hexcraft, deepHexes | all of starlitCottage |
29
- | aerie | 3×3 | 110 wood, 75 gold | 1100 | 27.6 s | 0 | broomrider | tailwinds | all of sanctum |
30
- | observatory | 3×3 | 100 wood, 125 stone | 1100 | 31.0 s | 0 | astromancer | astralDrill, constellations, nightOfFallingStars, leyAttunement, wellspring, grandConvergence | all of starlitCottage |
31
- | atelier | 3×3 | 100 wood, 100 stone | 1100 | 27.6 s | 0 | weaver | bitterSoil, creepingMire, blightCrown, briarcraft, hollowGround, gardenOfThorns, wardMasonry, crystalLenses | all of starlitCottage |
32
- | gargoyleRoost | 2×2 | 80 wood, 60 stone | 800 | 24.1 s | 0 | - | - | all of starlitCottage |
33
- | wardstone | 2×2 | 120 stone, 20 gold | 550 | 20.6 s | 0 | - | - | all of sanctum |
34
- | hexSpire | 2×2 | 150 stone, 75 gold | 600 | 27.6 s | 0 | - | - | all of atelier |
35
- | starfallBeacon | 3×3 | 250 stone, 150 gold | 900 | 37.9 s | 0 | - | - | all of observatory |
36
- | leyObelisk | 1×1 | 60 stone, 40 gold | 250 | 13.8 s | 0 | - | - | all of sanctum |
37
- | hexCircle | 3×3 | 50 stone, 100 gold | 300 | 17.2 s | 0 | - | - | all of atelier |
38
- | thornSnare | 1×1 | 25 wood, 15 gold | 60 | 6.8 s | 0 | - | - | all of sanctum |
39
- | briarHedge | 1×1 | 8 wood | 220 | 4.1 s | 0 | - | - | - |
40
- | moonstoneWall | 1×1 | 14 stone | 850 | 6.8 s | 0 | - | - | all of atelier |
41
- | moonGate | 1×1 | 40 stone | 1100 | 10.3 s | 0 | - | - | all of atelier |
19
+ | id | size | cost | hp | armor m/p | attack | build | supply | trains | researches | requires |
20
+ |---|---|---|---|---|---|---|---|---|---|---|
21
+ | covenSpire | 4×4 | 300 wood, 175 stone | 2400 | 3/5 | 4 dmg, range 7.0 tiles, every 2.0 s | 51.7 s | 10 | acolyte | spindleCarry, starChart, swiftLooms, seedstones, moonrise | - |
22
+ | starlitCottage | 2×2 | 30 wood | 500 | 1/6 | - | 15.2 s | 8 | - | - | - |
23
+ | moonGarden | 3×3 | 45 wood | 200 | 0/0 | - | 10.3 s | 0 | - | - | - |
24
+ | stillroom | 2×2 | 100 wood | 600 | 1/6 | - | 20.6 s | 0 | - | herbLore, deepRoots | - |
25
+ | sylvanLodge | 2×2 | 100 wood | 600 | 1/6 | - | 20.6 s | 0 | - | whisperingAxe, heartwood | - |
26
+ | geodeVault | 2×2 | 100 wood | 650 | 1/6 | - | 20.6 s | 0 | - | veinSight, stoneSong | - |
27
+ | wayshrine | 2×2 | 90 wood, 80 stone | 400 | 1/5 | - | 17.2 s | 4 | acolyte, moonblade, hexer | - | all of sanctum |
28
+ | sanctum | 3×3 | 140 wood, 40 stone | 1150 | 1/8 | - | 27.6 s | 0 | moonblade, hexer | silverGlaives, moonsteel, wardedRobes, starweave, hexcraft, deepHexes | all of starlitCottage |
29
+ | aerie | 3×3 | 110 wood, 75 gold | 1100 | 1/8 | - | 27.6 s | 0 | broomrider | tailwinds | all of sanctum |
30
+ | observatory | 3×3 | 100 wood, 125 stone | 1100 | 1/8 | - | 31.0 s | 0 | astromancer | astralDrill, constellations, nightOfFallingStars, leyAttunement, wellspring, grandConvergence | all of starlitCottage |
31
+ | atelier | 3×3 | 100 wood, 100 stone | 1100 | 1/8 | - | 27.6 s | 0 | weaver | bitterSoil, creepingMire, blightCrown, briarcraft, hollowGround, gardenOfThorns, wardMasonry, crystalLenses | all of starlitCottage |
32
+ | gargoyleRoost | 2×2 | 80 wood, 60 stone | 800 | 2/8 | - | 24.1 s | 0 | - | - | all of starlitCottage |
33
+ | wardstone | 2×2 | 120 stone, 20 gold | 550 | 1/6 | 4 dmg, range 7.0 tiles, every 1.6 s | 20.6 s | 0 | - | - | all of sanctum |
34
+ | hexSpire | 2×2 | 150 stone, 75 gold | 600 | 1/7 | 5 dmg, range 8.0 tiles, every 2.5 s | 27.6 s | 0 | - | - | all of atelier |
35
+ | starfallBeacon | 3×3 | 250 stone, 150 gold | 900 | 2/8 | 30 dmg, range 11.0 tiles, every 6.0 s | 37.9 s | 0 | - | - | all of observatory |
36
+ | leyObelisk | 1×1 | 60 stone, 40 gold | 250 | 0/4 | - | 13.8 s | 0 | - | - | all of sanctum |
37
+ | hexCircle | 3×3 | 50 stone, 100 gold | 300 | 0/2 | - | 17.2 s | 0 | - | - | all of atelier |
38
+ | thornSnare | 1×1 | 25 wood, 15 gold | 60 | 0/2 | - | 6.8 s | 0 | - | - | all of sanctum |
39
+ | briarHedge | 1×1 | 8 wood | 220 | 0/3 | - | 4.1 s | 0 | - | - | - |
40
+ | moonstoneWall | 1×1 | 14 stone | 850 | 5/8 | - | 6.8 s | 0 | - | - | all of atelier |
41
+ | moonGate | 1×1 | 40 stone | 1100 | 5/10 | - | 10.3 s | 0 | - | - | all of atelier |
42
42
 
43
43
  ## Upgrades
44
44