@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 +62 -2
- package/docs/api/act.md +1 -1
- package/docs/api/custom.md +24 -0
- package/docs/api/events.md +2 -0
- package/docs/api/game.md +2 -2
- package/docs/api/online.md +3 -0
- package/docs/api/rules.md +2 -2
- package/docs/errors.md +1 -1
- package/docs/llms-full.txt +36 -7
- package/docs/rules/index.md +1 -1
- package/docs/rules/orcs.md +14 -14
- package/docs/rules/templars.md +21 -21
- package/docs/rules/witches.md +24 -24
- package/docs/rules.json +399 -58
- package/index.js +12 -0
- package/llms-full.txt +36 -7
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,63 @@
|
|
|
1
|
-
# Changes in
|
|
1
|
+
# Changes in 63.0.1
|
|
2
2
|
|
|
3
|
-
Compared with engine
|
|
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
|
|
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) |
|
package/docs/api/custom.md
CHANGED
|
@@ -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
|
+
```
|
package/docs/api/events.md
CHANGED
|
@@ -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
|
|
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`.
|
package/docs/api/online.md
CHANGED
|
@@ -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
|
|
package/docs/llms-full.txt
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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
|
|
package/docs/rules/index.md
CHANGED
package/docs/rules/orcs.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# orcs (engine
|
|
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
|
|
package/docs/rules/templars.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# templars (engine
|
|
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
|
|
package/docs/rules/witches.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# witches (engine
|
|
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
|
|