@dreadwood-types/types 0.1.30 → 0.1.43
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/adventure.d.ts +49 -0
- package/dist/adventure.d.ts.map +1 -0
- package/dist/adventure.js +10 -0
- package/dist/adventure.js.map +1 -0
- package/dist/areaTransitions.d.ts +34 -0
- package/dist/areaTransitions.d.ts.map +1 -0
- package/dist/areaTransitions.js +23 -0
- package/dist/areaTransitions.js.map +1 -0
- package/dist/blacksmithing.d.ts +32 -0
- package/dist/blacksmithing.d.ts.map +1 -0
- package/dist/blacksmithing.js +207 -0
- package/dist/blacksmithing.js.map +1 -0
- package/dist/clientConfig.js +1 -1
- package/dist/clientConfig.js.map +1 -1
- package/dist/constants.d.ts +129 -4
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +254 -5
- package/dist/constants.js.map +1 -1
- package/dist/cooking.d.ts.map +1 -1
- package/dist/cooking.js +5 -0
- package/dist/cooking.js.map +1 -1
- package/dist/dreadwoodsZones.d.ts +74 -0
- package/dist/dreadwoodsZones.d.ts.map +1 -0
- package/dist/dreadwoodsZones.js +101 -0
- package/dist/dreadwoodsZones.js.map +1 -0
- package/dist/equipment.d.ts +3 -0
- package/dist/equipment.d.ts.map +1 -1
- package/dist/equipment.js +24 -0
- package/dist/equipment.js.map +1 -1
- package/dist/groundItems.d.ts.map +1 -1
- package/dist/groundItems.js +12 -0
- package/dist/groundItems.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/landmarks.d.ts +395 -19
- package/dist/landmarks.d.ts.map +1 -1
- package/dist/landmarks.js +548 -80
- package/dist/landmarks.js.map +1 -1
- package/dist/messages.d.ts +72 -6
- package/dist/messages.d.ts.map +1 -1
- package/dist/messages.js +8 -0
- package/dist/messages.js.map +1 -1
- package/dist/mining.d.ts +16 -11
- package/dist/mining.d.ts.map +1 -1
- package/dist/mining.js +54 -15
- package/dist/mining.js.map +1 -1
- package/dist/quests.d.ts +2 -2
- package/dist/quests.d.ts.map +1 -1
- package/dist/quests.js +30 -2
- package/dist/quests.js.map +1 -1
- package/dist/spiderCave.d.ts +17 -3
- package/dist/spiderCave.d.ts.map +1 -1
- package/dist/spiderCave.js +25 -3
- package/dist/spiderCave.js.map +1 -1
- package/dist/woodwork.d.ts +118 -0
- package/dist/woodwork.d.ts.map +1 -1
- package/dist/woodwork.js +178 -4
- package/dist/woodwork.js.map +1 -1
- package/dist/zones.d.ts.map +1 -1
- package/dist/zones.js +9 -2
- package/dist/zones.js.map +1 -1
- package/package.json +3 -2
package/dist/landmarks.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { BEACH_GATE_X, BEACH_SAND_START_Z, BEACH_START_Z, CHUNK_SIZE, OCEAN_START_Z, STONE_HOUSE_LODGE_RADIUS_ACROSS, STONE_HOUSE_RADIUS, UNDERGROUND_ORIGIN_X, WORLD_NORTH_PAD, WORLD_WIDTH, toWorldZ, } from './constants.js';
|
|
2
|
-
import { DEADLANDS_DEFENSE_DEPTH, DEADLANDS_MAX_X, DEADLANDS_MAX_Z, DEADLANDS_MIN_X, } from './zones.js';
|
|
1
|
+
import { BEACH_GATE_X, BEACH_SAND_START_Z, BEACH_START_Z, CHUNK_SIZE, OCEAN_START_Z, STONE_HOUSE_LODGE_RADIUS_ACROSS, STONE_HOUSE_RADIUS, UNDERGROUND_ORIGIN_X, WORLD_NORTH_PAD, WORLD_WIDTH, toLocalZ, toWorldZ, } from './constants.js';
|
|
2
|
+
import { DEADLANDS_DEFENSE_DEPTH, DEADLANDS_GATE_X, DEADLANDS_MAX_X, DEADLANDS_MAX_Z, DEADLANDS_MIN_X, } from './zones.js';
|
|
3
3
|
import { SNOW_DEEP_Z, SNOW_EDGE_Z } from './snow.js';
|
|
4
|
+
import { dreadwoodsNpcZone } from './dreadwoodsZones.js';
|
|
4
5
|
export const CROSSROADS_WORLD_X = BEACH_GATE_X;
|
|
5
6
|
export const CROSSROADS_LOCAL_Z = 17;
|
|
6
7
|
/**
|
|
7
8
|
* Bounty Board and Bruce - on the south side of the Crossroads, the first thing a
|
|
8
|
-
* player meets coming up the road from
|
|
9
|
+
* player meets coming up the road from Hope Beach. Inside the existing `crossroads`
|
|
9
10
|
* discovery zone; no new zone.
|
|
10
11
|
*/
|
|
11
12
|
export const BOUNTY_BOARD_X = CROSSROADS_WORLD_X + 3;
|
|
@@ -13,6 +14,129 @@ export const BOUNTY_BOARD_LOCAL_Z = CROSSROADS_LOCAL_Z + 3;
|
|
|
13
14
|
/** Bruce stands two tiles west of the board, clear of its posts rather than tucked against them. */
|
|
14
15
|
export const BOUNTY_BRUCE_X = CROSSROADS_WORLD_X + 1;
|
|
15
16
|
export const BOUNTY_BRUCE_LOCAL_Z = CROSSROADS_LOCAL_Z + 3;
|
|
17
|
+
/* --- The Long Pier, Hope Beach -------------------------------------------
|
|
18
|
+
*
|
|
19
|
+
* A single `brokenpier` prop used to stand on the sand here: three tiles of
|
|
20
|
+
* weathered planking that read as scenery and nothing else. The pier is now a
|
|
21
|
+
* tiled deck like the battlefield bridge, running the full depth of the ocean
|
|
22
|
+
* strip, with two smashed spans in it that a player planks over one at a time.
|
|
23
|
+
*
|
|
24
|
+
* All world coordinates. The deck is Path at height 1 over Water, so it stands
|
|
25
|
+
* proud of the sea the same way the bridge stands proud of the lake.
|
|
26
|
+
*/
|
|
27
|
+
export const HOPE_PIER_X = BEACH_GATE_X + 28;
|
|
28
|
+
/** Tiles either side of HOPE_PIER_X. Three wide, one wider than the bridge. */
|
|
29
|
+
export const HOPE_PIER_HALF_W = 1;
|
|
30
|
+
/** Sand ramp where the deck leaves the beach. */
|
|
31
|
+
export const HOPE_PIER_SHORE_Z = OCEAN_START_Z - 3;
|
|
32
|
+
/**
|
|
33
|
+
* Seaward end. Pinned to the shore rather than to the map edge: the south sea
|
|
34
|
+
* pad exists to put open water BEYOND the pier head (see WORLD_SOUTH_CHUNKS),
|
|
35
|
+
* and tying the end to WORLD_HEIGHT would spend that padding on more decking
|
|
36
|
+
* and leave the boundary visible from the last plank.
|
|
37
|
+
*/
|
|
38
|
+
export const HOPE_PIER_END_Z = OCEAN_START_Z + 87;
|
|
39
|
+
/**
|
|
40
|
+
* The east branch: a spur off the main walk to the moored ship, deliberately
|
|
41
|
+
* SHORE-SIDE of both smashed spans so the ship is open to everybody, repaired
|
|
42
|
+
* pier or not.
|
|
43
|
+
*/
|
|
44
|
+
export const HOPE_PIER_BRANCH_Z = OCEAN_START_Z + 14;
|
|
45
|
+
export const HOPE_PIER_BRANCH_HALF_W = 1;
|
|
46
|
+
export const HOPE_PIER_BRANCH_MIN_X = HOPE_PIER_X + HOPE_PIER_HALF_W + 1;
|
|
47
|
+
export const HOPE_PIER_BRANCH_MAX_X = HOPE_PIER_X + 12;
|
|
48
|
+
/**
|
|
49
|
+
* First smashed span, about a third of the way out - far enough that the walk
|
|
50
|
+
* to it is a walk, close enough to see the break from the sand.
|
|
51
|
+
*/
|
|
52
|
+
export const HOPE_PIER_GAP1_MIN_Z = OCEAN_START_Z + 28;
|
|
53
|
+
export const HOPE_PIER_GAP1_MAX_Z = OCEAN_START_Z + 29;
|
|
54
|
+
/** Second smashed span, out past the middle deck at about two thirds. */
|
|
55
|
+
export const HOPE_PIER_GAP2_MIN_Z = OCEAN_START_Z + 58;
|
|
56
|
+
export const HOPE_PIER_GAP2_MAX_Z = OCEAN_START_Z + 59;
|
|
57
|
+
/**
|
|
58
|
+
* The pier head: the last four rows widen into a platform, so the end of a long
|
|
59
|
+
* walk is somewhere to stand rather than the last plank of a corridor.
|
|
60
|
+
*/
|
|
61
|
+
export const HOPE_PIER_HEAD_MIN_Z = HOPE_PIER_END_Z - 3;
|
|
62
|
+
/** The middle deck, a passing place between the two smashed spans. */
|
|
63
|
+
export const HOPE_PIER_MID_MIN_Z = OCEAN_START_Z + 42;
|
|
64
|
+
export const HOPE_PIER_MID_MAX_Z = OCEAN_START_Z + 44;
|
|
65
|
+
/** Both widenings are five tiles across against the walk's three. */
|
|
66
|
+
export const HOPE_PIER_WIDE_HALF_W = 2;
|
|
67
|
+
/** The moored ship, off the seaward corner of the branch. */
|
|
68
|
+
export const HOPE_PIER_SHIP_X = HOPE_PIER_BRANCH_MAX_X + 4;
|
|
69
|
+
export const HOPE_PIER_SHIP_Z = HOPE_PIER_BRANCH_Z;
|
|
70
|
+
/**
|
|
71
|
+
* Saul's tackle pitch: on the sand beside the pier ramp, west of the lane so
|
|
72
|
+
* he never stands in the way of the walk out. He faces the pier.
|
|
73
|
+
*/
|
|
74
|
+
export const SAUL_SPAWN_X = HOPE_PIER_X - HOPE_PIER_HALF_W - 2;
|
|
75
|
+
/**
|
|
76
|
+
* Well up the dry sand, not on the last row before the ocean. The tile one row
|
|
77
|
+
* off the ramp is beach by the grid but the rendered shoreline laps across it,
|
|
78
|
+
* so he read as standing in the sea.
|
|
79
|
+
*/
|
|
80
|
+
export const SAUL_SPAWN_Z = HOPE_PIER_SHORE_Z - 3;
|
|
81
|
+
/**
|
|
82
|
+
* Smelt water beside the Long Pier: past the first smashed span and short of
|
|
83
|
+
* the sea bass, which hold past the second. Two off the middle deck, one off
|
|
84
|
+
* the walk either side of it. Each sits two tiles from the nearest plank, the
|
|
85
|
+
* ordinary fishing range, so the cast is made from the deck.
|
|
86
|
+
*/
|
|
87
|
+
export const HOPE_PIER_SMELT_SPOTS = [
|
|
88
|
+
{ x: HOPE_PIER_X - 4, z: OCEAN_START_Z + 43 },
|
|
89
|
+
{ x: HOPE_PIER_X + 4, z: OCEAN_START_Z + 43 },
|
|
90
|
+
{ x: HOPE_PIER_X - 3, z: OCEAN_START_Z + 36 },
|
|
91
|
+
{ x: HOPE_PIER_X + 3, z: OCEAN_START_Z + 50 },
|
|
92
|
+
];
|
|
93
|
+
/** Tiles either side of the centre line at this row. Wider over the two decks. */
|
|
94
|
+
export function hopePierHalfWidthAt(z) {
|
|
95
|
+
if (z >= HOPE_PIER_HEAD_MIN_Z)
|
|
96
|
+
return HOPE_PIER_WIDE_HALF_W;
|
|
97
|
+
if (z >= HOPE_PIER_MID_MIN_Z && z <= HOPE_PIER_MID_MAX_Z)
|
|
98
|
+
return HOPE_PIER_WIDE_HALF_W;
|
|
99
|
+
return HOPE_PIER_HALF_W;
|
|
100
|
+
}
|
|
101
|
+
/** The east spur out to the moored ship. */
|
|
102
|
+
export function onHopePierBranch(x, z) {
|
|
103
|
+
return (Math.abs(z - HOPE_PIER_BRANCH_Z) <= HOPE_PIER_BRANCH_HALF_W &&
|
|
104
|
+
x >= HOPE_PIER_BRANCH_MIN_X &&
|
|
105
|
+
x <= HOPE_PIER_BRANCH_MAX_X);
|
|
106
|
+
}
|
|
107
|
+
export function onHopePierLane(x, z) {
|
|
108
|
+
if (onHopePierBranch(x, z))
|
|
109
|
+
return true;
|
|
110
|
+
return (z >= HOPE_PIER_SHORE_Z &&
|
|
111
|
+
z <= HOPE_PIER_END_Z &&
|
|
112
|
+
Math.abs(x - HOPE_PIER_X) <= hopePierHalfWidthAt(z));
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* The one tile of a smashed span that carries the work, and once planked the
|
|
116
|
+
* crossing icon. Centre of the break's landward row - the same discipline as
|
|
117
|
+
* isBattlefieldBridgeTravelIconTile, so the hammer and the portal share a tile.
|
|
118
|
+
*/
|
|
119
|
+
export function isHopePierTravelIconTile(x, z) {
|
|
120
|
+
if (x !== HOPE_PIER_X)
|
|
121
|
+
return false;
|
|
122
|
+
return z === HOPE_PIER_GAP1_MIN_Z || z === HOPE_PIER_GAP2_MIN_Z;
|
|
123
|
+
}
|
|
124
|
+
/** Where a player lands after using a planked span's crossing icon. */
|
|
125
|
+
export function hopePierCrossingExitZ(gap, fromZ) {
|
|
126
|
+
const minZ = gap === 1 ? HOPE_PIER_GAP1_MIN_Z : HOPE_PIER_GAP2_MIN_Z;
|
|
127
|
+
const maxZ = gap === 1 ? HOPE_PIER_GAP1_MAX_Z : HOPE_PIER_GAP2_MAX_Z;
|
|
128
|
+
return fromZ <= minZ ? maxZ + 2 : minZ - 2;
|
|
129
|
+
}
|
|
130
|
+
/** Which smashed span a tile belongs to, or null for sound deck / open sea. */
|
|
131
|
+
export function hopePierGapAt(x, z) {
|
|
132
|
+
if (Math.abs(x - HOPE_PIER_X) > HOPE_PIER_HALF_W)
|
|
133
|
+
return null;
|
|
134
|
+
if (z >= HOPE_PIER_GAP1_MIN_Z && z <= HOPE_PIER_GAP1_MAX_Z)
|
|
135
|
+
return 1;
|
|
136
|
+
if (z >= HOPE_PIER_GAP2_MIN_Z && z <= HOPE_PIER_GAP2_MAX_Z)
|
|
137
|
+
return 2;
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
16
140
|
export const EAST_ROAD_START_X = CROSSROADS_WORLD_X + 7;
|
|
17
141
|
export const HIJACK_SITE_X = 402;
|
|
18
142
|
export const HIJACK_SITE_LOCAL_Z = 22;
|
|
@@ -36,6 +160,20 @@ export const BASECAMP_SLOPE_MIN_X = 605;
|
|
|
36
160
|
export const BASECAMP_SLOPE_MAX_X = 626;
|
|
37
161
|
export const BASECAMP_CAVE_X = BASECAMP_CENTER_X + Math.round(BASECAMP_RX * 0.76);
|
|
38
162
|
export const BASECAMP_CAVE_LOCAL_Z = BASECAMP_CENTER_LOCAL_Z;
|
|
163
|
+
/**
|
|
164
|
+
* The Suspicious hole gets its own style so it can keep its own entrance - the
|
|
165
|
+
* carved stone gate in the camp's rock face. It used to render whatever the
|
|
166
|
+
* shared default was, so when that default became the rat-pit mound on
|
|
167
|
+
* 2026-09-06 the gate went with it. Any mouth left on a bare compass style
|
|
168
|
+
* (the far-side door) keeps the mound; this one does not.
|
|
169
|
+
*
|
|
170
|
+
* The compass suffix still carries the facing: `caveEntranceFacing` and the
|
|
171
|
+
* client's FACING_YAW both read the direction out of the style string.
|
|
172
|
+
*/
|
|
173
|
+
export const BASECAMP_CAVE_MOUTH_STYLE = 'basecamp-west';
|
|
174
|
+
export function isBaseCampCaveMouthStyle(style) {
|
|
175
|
+
return typeof style === 'string' && style.startsWith('basecamp-');
|
|
176
|
+
}
|
|
39
177
|
/** Small lookout beyond the Base Camp tunnel, reached only after clearing Livonia. */
|
|
40
178
|
export const BASECAMP_FAR_SIDE_X = BASECAMP_CAVE_X + 30;
|
|
41
179
|
export const BASECAMP_FAR_SIDE_LOCAL_Z = BASECAMP_CAVE_LOCAL_Z;
|
|
@@ -179,6 +317,12 @@ export function inDreadwoodsEastGate(x, z) {
|
|
|
179
317
|
return (x === DREADWOODS_EAST_GATE_X &&
|
|
180
318
|
Math.abs(z - DREADWOODS_EAST_GATE_Z) <= DREADWOODS_EAST_GATE_HALF_W);
|
|
181
319
|
}
|
|
320
|
+
export const DREADWOODS_SOUTH_GATE_HALF_W = 2;
|
|
321
|
+
/** The south gate opening itself - the tiles cut out of the south wall column. */
|
|
322
|
+
export function inDreadwoodsSouthGate(x, z) {
|
|
323
|
+
return (z === DEADLANDS_MAX_Z &&
|
|
324
|
+
Math.abs(x - DEADLANDS_GATE_X) <= DREADWOODS_SOUTH_GATE_HALF_W);
|
|
325
|
+
}
|
|
182
326
|
/** The gate road plus a margin, for keeping den mouths and wild scatter off it. */
|
|
183
327
|
export function nearDreadwoodsEastRoad(x, z, margin = 6) {
|
|
184
328
|
return (x >= DREADWOODS_EAST_ROAD_MIN_X - margin &&
|
|
@@ -484,14 +628,15 @@ export function inDungeonZone(x, z) {
|
|
|
484
628
|
*/
|
|
485
629
|
export const EXPLORER_IDENTIFY_XP = 3;
|
|
486
630
|
export const EXPLORER_DISCOVERY_XP = 33;
|
|
487
|
-
/**
|
|
631
|
+
/** Hope Beach is "discovered" the instant you log in, so it pays a token amount. */
|
|
488
632
|
export const SPAWN_ZONE_DISCOVERY_XP = 3;
|
|
489
|
-
/**
|
|
490
|
-
export const FOREST_DEN_RX =
|
|
491
|
-
export const FOREST_DEN_RZ =
|
|
633
|
+
/** Rat warren with a central nest and two smaller feeding alcoves. */
|
|
634
|
+
export const FOREST_DEN_RX = 8;
|
|
635
|
+
export const FOREST_DEN_RZ = 7;
|
|
492
636
|
export const FOREST_DEN_WALL = 2;
|
|
493
637
|
export const FOREST_DEN_EXPLORER_XP = 33;
|
|
494
|
-
|
|
638
|
+
// Keep the original centres so existing characters remain in their own den.
|
|
639
|
+
const FOREST_DEN_CAVERN_X = UNDERGROUND_ORIGIN_X + 10;
|
|
495
640
|
const FOREST_DEN_CAVERN_Z0 = 108;
|
|
496
641
|
const FOREST_DEN_CAVERN_GAP = 20;
|
|
497
642
|
function forestDenSite(id, entranceX, entranceLocalZ, facing, cavernIndex) {
|
|
@@ -567,6 +712,9 @@ export function forestDenByCavern(worldX, worldZ) {
|
|
|
567
712
|
export function inForestDenCavern(x, z) {
|
|
568
713
|
return forestDenByCavern(x, z) !== null;
|
|
569
714
|
}
|
|
715
|
+
/** Wolf hollows retain their original compact footprint. */
|
|
716
|
+
export const WOLF_DEN_RX = 4;
|
|
717
|
+
export const WOLF_DEN_RZ = 3;
|
|
570
718
|
const WOLF_DEN_CAVERN_X = UNDERGROUND_ORIGIN_X + 70;
|
|
571
719
|
const WOLF_DEN_CAVERN_Z0 = 40;
|
|
572
720
|
const WOLF_DEN_CAVERN_GAP = 18;
|
|
@@ -636,8 +784,8 @@ export function wolfDenByEntrance(worldX, worldZ) {
|
|
|
636
784
|
}
|
|
637
785
|
export function wolfDenByCavern(worldX, worldZ) {
|
|
638
786
|
for (const den of WOLF_DEN_SITES) {
|
|
639
|
-
if (Math.abs(worldX - den.cavernX) <=
|
|
640
|
-
Math.abs(worldZ - den.cavernZ) <=
|
|
787
|
+
if (Math.abs(worldX - den.cavernX) <= WOLF_DEN_RX + FOREST_DEN_WALL &&
|
|
788
|
+
Math.abs(worldZ - den.cavernZ) <= WOLF_DEN_RZ + FOREST_DEN_WALL) {
|
|
641
789
|
return den;
|
|
642
790
|
}
|
|
643
791
|
}
|
|
@@ -677,40 +825,58 @@ function giantRatPitSpawnSpots() {
|
|
|
677
825
|
];
|
|
678
826
|
return offsets.map(([dx, dz]) => ({ x: cx + dx, z: pz + dz }));
|
|
679
827
|
}
|
|
680
|
-
/**
|
|
828
|
+
/** Swarming horde of tunnel vermin across the sunken rat pit (room 1). */
|
|
681
829
|
function room1RatSpawnSpots() {
|
|
682
830
|
const cx = DUNGEON_CENTER_X;
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
831
|
+
// [dx, z] - well distributed across north nooks, 4 corner alcoves,
|
|
832
|
+
// mud puddles, center feeding mound, and perimeter walkways.
|
|
833
|
+
const offsets = [
|
|
834
|
+
// North nooks and throat (z=63-64)
|
|
835
|
+
[-10, 64], [-8, 63], [-6, 64], [-4, 63], [4, 63], [6, 64], [8, 63], [10, 64],
|
|
836
|
+
// NW Alcove and rat nest (z=65-67)
|
|
837
|
+
[-14, 65], [-14, 67], [-12, 66], [-11, 65],
|
|
838
|
+
// NE Alcove and rat nest (z=65-67)
|
|
839
|
+
[14, 65], [14, 67], [12, 66], [11, 65],
|
|
840
|
+
// Upper mid and arrival flank (z=65-68)
|
|
841
|
+
[-9, 66], [-5, 65], [5, 65], [9, 66],
|
|
842
|
+
[-7, 68], [-3, 67], [3, 67], [7, 68],
|
|
843
|
+
// Center feast and sunken pit ring (z=69-73)
|
|
844
|
+
[-10, 70], [-6, 69], [-2, 69], [2, 69], [6, 69], [10, 70],
|
|
845
|
+
[-9, 72], [-5, 71], [-1, 72], [1, 71], [5, 72], [9, 71],
|
|
846
|
+
[-7, 73], [-3, 73], [3, 73], [7, 73],
|
|
847
|
+
// SW Alcove and mud puddle skeleton (z=75-77)
|
|
848
|
+
[-14, 75], [-14, 77], [-12, 75], [-11, 77],
|
|
849
|
+
// SE Alcove and trophy caches (z=75-77)
|
|
850
|
+
[14, 75], [14, 77], [12, 75], [11, 77],
|
|
851
|
+
// Lower pit and south mud puddle (z=75-78)
|
|
852
|
+
[-8, 75], [-4, 75], [-2, 77], [2, 76], [6, 76], [8, 75],
|
|
853
|
+
[-9, 78], [-5, 78], [5, 78], [9, 78],
|
|
854
|
+
];
|
|
855
|
+
return offsets.map(([dx, z]) => ({ x: cx + dx, z }));
|
|
856
|
+
}
|
|
857
|
+
/** Denser garrison for the Amazonian Cave in Room 3 of the Rat Tunnel. */
|
|
858
|
+
function amazonHallSpawnSpots() {
|
|
859
|
+
const cx = DUNGEON_CENTER_X;
|
|
686
860
|
const offsets = [
|
|
687
|
-
|
|
688
|
-
[
|
|
689
|
-
[9,
|
|
690
|
-
[
|
|
691
|
-
|
|
692
|
-
[-
|
|
693
|
-
[
|
|
694
|
-
|
|
695
|
-
[-
|
|
696
|
-
[
|
|
697
|
-
[
|
|
698
|
-
[0, 0.62],
|
|
699
|
-
[9, 0.62],
|
|
700
|
-
[-5, 0.72],
|
|
701
|
-
[5, 0.72],
|
|
702
|
-
[0, 0.82],
|
|
861
|
+
// North hall approach
|
|
862
|
+
[-4, 146], [3, 146],
|
|
863
|
+
[-9, 149], [8, 149],
|
|
864
|
+
[0, 150],
|
|
865
|
+
// Mid hall flanks
|
|
866
|
+
[-4, 153], [3, 153],
|
|
867
|
+
[-8, 158], [8, 158],
|
|
868
|
+
// South camp clearing
|
|
869
|
+
[-4, 162], [4, 162],
|
|
870
|
+
[-8, 165], [8, 165],
|
|
871
|
+
[0, 166],
|
|
703
872
|
];
|
|
704
|
-
return offsets.map(([dx,
|
|
705
|
-
x: cx + dx,
|
|
706
|
-
z: Math.round(minZ + (maxZ - minZ) * t),
|
|
707
|
-
}));
|
|
873
|
+
return offsets.map(([dx, z]) => ({ x: cx + dx, z }));
|
|
708
874
|
}
|
|
709
875
|
/** Fixed, deterministic spawn spots for each dungeon room's enemy group. */
|
|
710
876
|
export const DUNGEON_ROOM_SPAWNS = {
|
|
711
877
|
1: room1RatSpawnSpots(),
|
|
712
878
|
2: giantRatPitSpawnSpots(),
|
|
713
|
-
3:
|
|
879
|
+
3: amazonHallSpawnSpots(),
|
|
714
880
|
4: roomSpawnSpots(DUNGEON_ROOM4_MIN_Z, DUNGEON_ROOM4_MAX_Z, 1),
|
|
715
881
|
};
|
|
716
882
|
/**
|
|
@@ -719,11 +885,31 @@ export const DUNGEON_ROOM_SPAWNS = {
|
|
|
719
885
|
* East edge meets the Deadlands west border. The lake continues east of this pad
|
|
720
886
|
* (see BATTLEFIELD_LAKE_EAST_X).
|
|
721
887
|
*/
|
|
888
|
+
/**
|
|
889
|
+
* How far north the whole front line sits from the farm.
|
|
890
|
+
*
|
|
891
|
+
* The south palisade used to stand one tile off No Harm Farm's own north
|
|
892
|
+
* fence, so a player walked out of the farm gate straight into a war. Every
|
|
893
|
+
* absolute Z on the pad is written as its original survey number put through
|
|
894
|
+
* `bfZ`, which keeps the internal layout - lake, camps, cabbage rows, the
|
|
895
|
+
* bridge span, the Captain's parade ground - exactly as it was designed and
|
|
896
|
+
* moves the whole thing as one piece.
|
|
897
|
+
*
|
|
898
|
+
* The ceiling on this number is the lake, not the farm. The water sheet runs
|
|
899
|
+
* on east through the corridor south of the Dreadwoods, whose own south edge
|
|
900
|
+
* is DEADLANDS_MAX_Z (639); the wall returns need a band between the two. At
|
|
901
|
+
* 18 the lake's north bank lands at 648, leaving that corridor 9 tiles deep
|
|
902
|
+
* with the returns still reaching it. Push much past this and the lake starts
|
|
903
|
+
* eating the Dreadwoods' south wall.
|
|
904
|
+
*/
|
|
905
|
+
export const BATTLEFIELD_NORTH_SHIFT = 18;
|
|
906
|
+
/** An original survey Z, moved north by BATTLEFIELD_NORTH_SHIFT. */
|
|
907
|
+
const bfZ = (surveyZ) => surveyZ - BATTLEFIELD_NORTH_SHIFT;
|
|
722
908
|
export const BATTLEFIELD_MIN_X = 10;
|
|
723
909
|
export const BATTLEFIELD_MAX_X = DEADLANDS_MIN_X - 1;
|
|
724
|
-
export const BATTLEFIELD_MIN_WORLD_Z = 648;
|
|
725
|
-
// Includes the Captain's command camp
|
|
726
|
-
export const BATTLEFIELD_MAX_WORLD_Z = 748;
|
|
910
|
+
export const BATTLEFIELD_MIN_WORLD_Z = bfZ(648);
|
|
911
|
+
// Includes the Captain's command camp and its broad parade ground.
|
|
912
|
+
export const BATTLEFIELD_MAX_WORLD_Z = bfZ(748);
|
|
727
913
|
export const BATTLEFIELD_MIN_LOCAL_Z = BATTLEFIELD_MIN_WORLD_Z - WORLD_NORTH_PAD;
|
|
728
914
|
export const BATTLEFIELD_MAX_LOCAL_Z = BATTLEFIELD_MAX_WORLD_Z - WORLD_NORTH_PAD;
|
|
729
915
|
/**
|
|
@@ -733,8 +919,8 @@ export const BATTLEFIELD_MAX_LOCAL_Z = BATTLEFIELD_MAX_WORLD_Z - WORLD_NORTH_PAD
|
|
|
733
919
|
* corridor south of the Deadlands, past the west-face enclave, until it meets
|
|
734
920
|
* the SE high-rock row.
|
|
735
921
|
*/
|
|
736
|
-
export const BATTLEFIELD_LAKE_MIN_Z = 666;
|
|
737
|
-
export const BATTLEFIELD_LAKE_MAX_Z = 698;
|
|
922
|
+
export const BATTLEFIELD_LAKE_MIN_Z = bfZ(666);
|
|
923
|
+
export const BATTLEFIELD_LAKE_MAX_Z = bfZ(698);
|
|
738
924
|
/** Solid southward wall returns join both Dreadwoods corners to the lake bank. */
|
|
739
925
|
export const DEADLANDS_SOUTH_WALL_XS = [DEADLANDS_MIN_X, DEADLANDS_MAX_X];
|
|
740
926
|
export const DEADLANDS_SOUTH_WALL_END_Z = BATTLEFIELD_LAKE_MIN_Z - 1;
|
|
@@ -777,12 +963,195 @@ export const BATTLEFIELD_CONTESTED_MID_Z = BATTLEFIELD_LAKE_MID_Z;
|
|
|
777
963
|
* lengthens the water sheet without sliding the crossing.
|
|
778
964
|
*/
|
|
779
965
|
export const BATTLEFIELD_BRIDGE_X = 91;
|
|
780
|
-
/**
|
|
781
|
-
|
|
966
|
+
/**
|
|
967
|
+
* Snowy frontier above the goblin camps, sealed west to the coastal cliff.
|
|
968
|
+
* Sits north of SNOW_DEEP_Z so the gate and approach stay in solid snow rather
|
|
969
|
+
* than the green fleck band at the climate edge.
|
|
970
|
+
*/
|
|
971
|
+
export const NORTHERN_WALL_Z = SNOW_DEEP_Z - 25;
|
|
782
972
|
export const NORTHERN_WALL_MIN_X = 4;
|
|
783
973
|
export const NORTHERN_WALL_MAX_X = DEADLANDS_MIN_X - 1;
|
|
784
974
|
export const NORTHERN_GATE_X = BATTLEFIELD_BRIDGE_X;
|
|
785
975
|
export const NORTHERN_GATE_HALF_WIDTH = 2;
|
|
976
|
+
/**
|
|
977
|
+
* The garrison camp on the north side of the gate: tents, a fire, stores, and
|
|
978
|
+
* the quartermaster. It is the only settled ground in the snow and the reason
|
|
979
|
+
* to walk through the gate at all. A shared clearing east of the road holds
|
|
980
|
+
* the tents and hearth; NORTHERN_GARRISON_X/Z is the quartermaster's counter.
|
|
981
|
+
*/
|
|
982
|
+
export const NORTHERN_CAMP_CENTER = { x: NORTHERN_GATE_X + 23, z: NORTHERN_WALL_Z - 24 };
|
|
983
|
+
export const NORTHERN_GARRISON_X = NORTHERN_CAMP_CENTER.x - 9;
|
|
984
|
+
export const NORTHERN_GARRISON_Z = NORTHERN_CAMP_CENTER.z - 4;
|
|
985
|
+
/** Open approaches to the services around one communal camp. */
|
|
986
|
+
export const NORTHERN_CAMP_STATIONS = {
|
|
987
|
+
trading: { x: NORTHERN_CAMP_CENTER.x - 8, z: NORTHERN_CAMP_CENTER.z - 4, radius: 5 },
|
|
988
|
+
hearth: { x: NORTHERN_CAMP_CENTER.x, z: NORTHERN_CAMP_CENTER.z + 2, radius: 5 },
|
|
989
|
+
hunting: { x: NORTHERN_CAMP_CENTER.x + 6, z: NORTHERN_CAMP_CENTER.z - 4, radius: 5 },
|
|
990
|
+
guards: { x: NORTHERN_CAMP_CENTER.x + 9, z: NORTHERN_CAMP_CENTER.z + 5, radius: 5 },
|
|
991
|
+
travellers: { x: NORTHERN_CAMP_CENTER.x - 7, z: NORTHERN_CAMP_CENTER.z + 6, radius: 5 },
|
|
992
|
+
};
|
|
993
|
+
export const NORTHERN_CAMP_BOARD = { x: NORTHERN_CAMP_CENTER.x - 14, z: NORTHERN_CAMP_CENTER.z - 2 };
|
|
994
|
+
export const NORTHERN_CAMP_DWARVES = {
|
|
995
|
+
rolo: { x: NORTHERN_CAMP_CENTER.x - 3, z: NORTHERN_CAMP_CENTER.z + 1 },
|
|
996
|
+
bram: { x: NORTHERN_CAMP_CENTER.x + 5, z: NORTHERN_CAMP_CENTER.z - 5 },
|
|
997
|
+
tova: { x: NORTHERN_CAMP_CENTER.x + 2, z: NORTHERN_CAMP_CENTER.z + 3 },
|
|
998
|
+
};
|
|
999
|
+
export const NORTHERN_CAMP_ROAD_MIN_Z = NORTHERN_WALL_Z - 66;
|
|
1000
|
+
/** Camp bounds include its entrance lane; the public north road stays open. */
|
|
1001
|
+
export const NORTHERN_GARRISON_MIN_X = NORTHERN_GATE_X - 2;
|
|
1002
|
+
export const NORTHERN_GARRISON_MAX_X = NORTHERN_CAMP_CENTER.x + 18;
|
|
1003
|
+
export const NORTHERN_GARRISON_MIN_Z = NORTHERN_CAMP_CENTER.z - 15;
|
|
1004
|
+
export const NORTHERN_GARRISON_MAX_Z = NORTHERN_CAMP_CENTER.z + 15;
|
|
1005
|
+
/** Distance outside the central clearing and its five-tile entrance lane. */
|
|
1006
|
+
export function northernCampEdgeDistance(x, z) {
|
|
1007
|
+
const clearing = (Math.hypot((x - NORTHERN_CAMP_CENTER.x) / 17, (z - NORTHERN_CAMP_CENTER.z) / 14) - 1) * 14;
|
|
1008
|
+
const nearestX = Math.max(NORTHERN_GATE_X, Math.min(NORTHERN_CAMP_CENTER.x, x));
|
|
1009
|
+
return Math.min(clearing, Math.hypot(x - nearestX, z - NORTHERN_CAMP_CENTER.z) - 2);
|
|
1010
|
+
}
|
|
1011
|
+
export function inNorthernGarrison(x, z) {
|
|
1012
|
+
return (x >= NORTHERN_GARRISON_MIN_X &&
|
|
1013
|
+
x <= NORTHERN_GARRISON_MAX_X &&
|
|
1014
|
+
z >= NORTHERN_GARRISON_MIN_Z &&
|
|
1015
|
+
z <= NORTHERN_GARRISON_MAX_Z &&
|
|
1016
|
+
northernCampEdgeDistance(x, z) <= 0);
|
|
1017
|
+
}
|
|
1018
|
+
/**
|
|
1019
|
+
* The frozen lake north-west of the War Camp, out on the flat lowland short of
|
|
1020
|
+
* the Frozen Shore: holes cut through the ice, two fishers on their stools, a
|
|
1021
|
+
* jetty iced in at the east bank and a small shelter where the snowfields
|
|
1022
|
+
* approach meets the shore. Nothing is fished here yet; it is the place first.
|
|
1023
|
+
* World coordinates - z already includes WORLD_NORTH_PAD.
|
|
1024
|
+
*/
|
|
1025
|
+
export const FROZEN_LAKE_CENTER = { x: 32, z: 236 };
|
|
1026
|
+
export const FROZEN_LAKE_RX = 14;
|
|
1027
|
+
export const FROZEN_LAKE_RZ = 12;
|
|
1028
|
+
/** The level the ice is frozen at; the lowland around it already lies near it. */
|
|
1029
|
+
export const FROZEN_LAKE_ICE_HEIGHT = 0;
|
|
1030
|
+
/** Holes cut through the ice. The first two have a fisher sitting beside them. */
|
|
1031
|
+
export const FROZEN_LAKE_HOLES = [
|
|
1032
|
+
{ x: 26, z: 232 },
|
|
1033
|
+
{ x: 36, z: 240 },
|
|
1034
|
+
{ x: 22, z: 238 },
|
|
1035
|
+
{ x: 30, z: 244 },
|
|
1036
|
+
{ x: 40, z: 232 },
|
|
1037
|
+
{ x: 34, z: 229 },
|
|
1038
|
+
];
|
|
1039
|
+
/** Where each fisher sits, and the hole they face. */
|
|
1040
|
+
export const FROZEN_LAKE_FISHERS = [
|
|
1041
|
+
{ name: 'Halvard', x: 27, z: 232, faceX: 26, faceZ: 232 },
|
|
1042
|
+
{ name: 'Ingrid', x: 36, z: 241, faceX: 36, faceZ: 240 },
|
|
1043
|
+
];
|
|
1044
|
+
/** Where the jetty leaves the east bank, and how many tiles it reaches west. */
|
|
1045
|
+
export const FROZEN_LAKE_JETTY = { x: 45, z: 236, length: 4 };
|
|
1046
|
+
/**
|
|
1047
|
+
* The bait table on the east bank, between the supply sled and the cooking
|
|
1048
|
+
* fire, and the bait master who works it: he stands at its east end, facing
|
|
1049
|
+
* the table and the ice beyond it.
|
|
1050
|
+
*/
|
|
1051
|
+
export const FROZEN_LAKE_BAIT_TABLE = { x: 46, z: 243 };
|
|
1052
|
+
export const FROZEN_LAKE_BAIT_MASTER = { x: 47, z: 243, faceX: 46, faceZ: 243 };
|
|
1053
|
+
/** Signed distance to the ice edge in tiles: at or below zero is on the ice. */
|
|
1054
|
+
export function frozenLakeEdgeDistance(x, z) {
|
|
1055
|
+
const d = Math.hypot((x - FROZEN_LAKE_CENTER.x) / FROZEN_LAKE_RX, (z - FROZEN_LAKE_CENTER.z) / FROZEN_LAKE_RZ);
|
|
1056
|
+
return (d - 1) * Math.min(FROZEN_LAKE_RX, FROZEN_LAKE_RZ);
|
|
1057
|
+
}
|
|
1058
|
+
export function onFrozenLakeIce(x, z) {
|
|
1059
|
+
return frozenLakeEdgeDistance(x, z) <= 0;
|
|
1060
|
+
}
|
|
1061
|
+
/**
|
|
1062
|
+
* The far north-west corner, beyond the lake: the lowland rises to a snow
|
|
1063
|
+
* plateau, the Crystal Gorge cuts across it with a rope bridge over the drop,
|
|
1064
|
+
* and the Frozen Hallway runs north from a rune arch between walls of dark
|
|
1065
|
+
* rock and ice to an iron gate that leads deeper inside (nowhere, yet).
|
|
1066
|
+
* World coordinates - z already includes WORLD_NORTH_PAD.
|
|
1067
|
+
*/
|
|
1068
|
+
export const FROZEN_PLATEAU_H = 5;
|
|
1069
|
+
/** Where the lowland starts climbing to the plateau, and where it is level. */
|
|
1070
|
+
export const FROZEN_PLATEAU_RISE_Z = 214;
|
|
1071
|
+
export const FROZEN_PLATEAU_TOP_Z = 198;
|
|
1072
|
+
/** The hills east of here are left as they are. */
|
|
1073
|
+
export const FROZEN_PLATEAU_MAX_X = 46;
|
|
1074
|
+
export const CRYSTAL_GORGE_MIN_Z = 158;
|
|
1075
|
+
export const CRYSTAL_GORGE_MAX_Z = 166;
|
|
1076
|
+
export const CRYSTAL_GORGE_MAX_X = 44;
|
|
1077
|
+
/** The bridge column and the deck's half-width in tiles. */
|
|
1078
|
+
export const CRYSTAL_BRIDGE_X = 21;
|
|
1079
|
+
export const CRYSTAL_BRIDGE_HALF_W = 1;
|
|
1080
|
+
export const FROZEN_HALLWAY_X = 21;
|
|
1081
|
+
export const FROZEN_HALLWAY_HALF_W = 4;
|
|
1082
|
+
export const FROZEN_HALLWAY_WALL_T = 5;
|
|
1083
|
+
export const FROZEN_HALLWAY_WALL_H = 10;
|
|
1084
|
+
/** The rune arch at the mouth and the iron gate at the far end. */
|
|
1085
|
+
export const FROZEN_HALLWAY_MOUTH_Z = 105;
|
|
1086
|
+
export const FROZEN_HALLWAY_GATE_Z = 26;
|
|
1087
|
+
export const FROZEN_HALLWAY_SEAL_Z = 6;
|
|
1088
|
+
/**
|
|
1089
|
+
* The Ice Prison: the rooms behind the hallway's iron gate, carved in the
|
|
1090
|
+
* off-map strip like the other interiors. You arrive in the Frozen Cells,
|
|
1091
|
+
* the prison's gatehouse; a flight of steps at its north end leads down a
|
|
1092
|
+
* short corridor to the Frozen Shrine, where the Warden's door is sealed and
|
|
1093
|
+
* the west and east passages are still choked. World coordinates.
|
|
1094
|
+
*/
|
|
1095
|
+
export const ICE_PRISON_CX = UNDERGROUND_ORIGIN_X + 208;
|
|
1096
|
+
export const ICE_PRISON_CELLS = { cx: ICE_PRISON_CX, minZ: 150, maxZ: 172, halfW: 12 };
|
|
1097
|
+
export const ICE_PRISON_CORRIDOR = { cx: ICE_PRISON_CX, minZ: 141, maxZ: 149, halfW: 1 };
|
|
1098
|
+
export const ICE_PRISON_SHRINE = { cx: ICE_PRISON_CX, minZ: 110, maxZ: 140, halfW: 16 };
|
|
1099
|
+
/** Where the gate back out stands (the south wall) and where you arrive. */
|
|
1100
|
+
export const ICE_PRISON_GATE = { x: ICE_PRISON_CX, z: ICE_PRISON_CELLS.maxZ + 1 };
|
|
1101
|
+
export const ICE_PRISON_ENTRY = { x: ICE_PRISON_CX, z: ICE_PRISON_CELLS.maxZ - 2 };
|
|
1102
|
+
/** Gate styles carry the facing letter first; this tag picks the destination. */
|
|
1103
|
+
export const ICE_PRISON_GATE_IN_STYLE = 'n-iceprison';
|
|
1104
|
+
export const ICE_PRISON_GATE_OUT_STYLE = 's-iceprison-out';
|
|
1105
|
+
/** Where the way out drops you: just south of the hallway's gate. */
|
|
1106
|
+
export const ICE_PRISON_OUTSIDE = { x: FROZEN_HALLWAY_X, z: FROZEN_HALLWAY_GATE_Z + 3 };
|
|
1107
|
+
/** The seal on the shrine's platform; rings step up around it. */
|
|
1108
|
+
export const ICE_PRISON_SEAL = { x: ICE_PRISON_CX, z: (ICE_PRISON_SHRINE.minZ + ICE_PRISON_SHRINE.maxZ) / 2 };
|
|
1109
|
+
/**
|
|
1110
|
+
* The frost wolf pack in the Frozen Cells: four tiles across the north and
|
|
1111
|
+
* west of the room, clear of the arrival tile and the way up to the gate, so
|
|
1112
|
+
* a player gets a few steps in before the pack notices. All four stand on
|
|
1113
|
+
* the open floor at height 0 (the drop and its walkway are further east).
|
|
1114
|
+
*/
|
|
1115
|
+
export const ICE_PRISON_WOLF_SPAWNS = [
|
|
1116
|
+
{ x: ICE_PRISON_CX - 6, z: ICE_PRISON_CELLS.minZ + 6 },
|
|
1117
|
+
{ x: ICE_PRISON_CX + 2, z: ICE_PRISON_CELLS.minZ + 4 },
|
|
1118
|
+
{ x: ICE_PRISON_CX - 9, z: ICE_PRISON_CELLS.minZ + 13 },
|
|
1119
|
+
{ x: ICE_PRISON_CX - 2, z: ICE_PRISON_CELLS.minZ + 10 },
|
|
1120
|
+
];
|
|
1121
|
+
/** Wall margin included, so lighting and colouring cover the rock around the rooms. */
|
|
1122
|
+
export const ICE_PRISON_BOUNDS = {
|
|
1123
|
+
minX: ICE_PRISON_CX - ICE_PRISON_SHRINE.halfW - 8,
|
|
1124
|
+
maxX: ICE_PRISON_CX + ICE_PRISON_SHRINE.halfW + 8,
|
|
1125
|
+
minZ: ICE_PRISON_SHRINE.minZ - 8,
|
|
1126
|
+
maxZ: ICE_PRISON_CELLS.maxZ + 8,
|
|
1127
|
+
};
|
|
1128
|
+
export function inIcePrison(x, z) {
|
|
1129
|
+
return (x >= ICE_PRISON_BOUNDS.minX && x <= ICE_PRISON_BOUNDS.maxX &&
|
|
1130
|
+
z >= ICE_PRISON_BOUNDS.minZ && z <= ICE_PRISON_BOUNDS.maxZ);
|
|
1131
|
+
}
|
|
1132
|
+
/** How far the hallway's centre line swings either side of FROZEN_HALLWAY_X. */
|
|
1133
|
+
export const FROZEN_HALLWAY_SWING = 7;
|
|
1134
|
+
/** 0 at the gate, 1 at the mouth. */
|
|
1135
|
+
function hallwayT(z) {
|
|
1136
|
+
return (z - FROZEN_HALLWAY_GATE_Z) / (FROZEN_HALLWAY_MOUTH_Z - FROZEN_HALLWAY_GATE_Z);
|
|
1137
|
+
}
|
|
1138
|
+
/**
|
|
1139
|
+
* The hallway is not a straight cut: its centre line swings one full S from
|
|
1140
|
+
* the mouth to the gate, so neither end is in sight of the other, and it
|
|
1141
|
+
* breathes as it goes - two chambers where it widens to thirteen tiles and a
|
|
1142
|
+
* throat between them where it closes to five. Both are functions of z so
|
|
1143
|
+
* the builder, the corridor test and anything dressing the walls agree.
|
|
1144
|
+
*/
|
|
1145
|
+
export function frozenHallwayCenterAt(z) {
|
|
1146
|
+
return FROZEN_HALLWAY_X + Math.round(FROZEN_HALLWAY_SWING * Math.sin(2 * Math.PI * hallwayT(z)));
|
|
1147
|
+
}
|
|
1148
|
+
export function frozenHallwayHalfWidthAt(z) {
|
|
1149
|
+
return Math.round(FROZEN_HALLWAY_HALF_W + 2 * Math.sin(3 * Math.PI * hallwayT(z)));
|
|
1150
|
+
}
|
|
1151
|
+
export function inFrozenHallwayCorridor(x, z) {
|
|
1152
|
+
return (z >= FROZEN_HALLWAY_GATE_Z && z <= FROZEN_HALLWAY_MOUTH_Z &&
|
|
1153
|
+
Math.abs(x - frozenHallwayCenterAt(z)) <= frozenHallwayHalfWidthAt(z));
|
|
1154
|
+
}
|
|
786
1155
|
/** Half-width of the walkable bridge deck (tiles either side of bridge X). */
|
|
787
1156
|
export const BATTLEFIELD_BRIDGE_HALF_W = 1;
|
|
788
1157
|
/**
|
|
@@ -843,27 +1212,29 @@ export const BATTLEFIELD_GOBLINS_PER_CAMP = 6;
|
|
|
843
1212
|
export const BATTLEFIELD_CAMP_CHEST_LIFETIME_MS = 60_000;
|
|
844
1213
|
/** Soldier camps - closer to the farm (higher world Z), south of the lake. */
|
|
845
1214
|
export const BATTLEFIELD_SOLDIER_CAMPS = [
|
|
846
|
-
{ x: 22, z: 720 },
|
|
847
|
-
{ x: 48, z: 714 },
|
|
848
|
-
{ x: 74, z: 722 },
|
|
849
|
-
{ x: 100, z: 712 },
|
|
850
|
-
{ x: 126, z: 718 },
|
|
851
|
-
{ x: 152, z: 714 },
|
|
852
|
-
{ x: 34, z: 710 },
|
|
853
|
-
{ x: 60, z: 716 },
|
|
854
|
-
{ x: 86, z: 708 },
|
|
855
|
-
{ x: 112, z: 714 },
|
|
856
|
-
{ x: 138, z: 712 },
|
|
857
|
-
{ x: 164, z: 710 },
|
|
858
|
-
{ x: 18, z: 716 },
|
|
859
|
-
{ x: 168, z: 720 },
|
|
1215
|
+
{ x: 22, z: bfZ(720) },
|
|
1216
|
+
{ x: 48, z: bfZ(714) },
|
|
1217
|
+
{ x: 74, z: bfZ(722) },
|
|
1218
|
+
{ x: 100, z: bfZ(712) },
|
|
1219
|
+
{ x: 126, z: bfZ(718) },
|
|
1220
|
+
{ x: 152, z: bfZ(714) },
|
|
1221
|
+
{ x: 34, z: bfZ(710) },
|
|
1222
|
+
{ x: 60, z: bfZ(716) },
|
|
1223
|
+
{ x: 86, z: bfZ(708) },
|
|
1224
|
+
{ x: 112, z: bfZ(714) },
|
|
1225
|
+
{ x: 138, z: bfZ(712) },
|
|
1226
|
+
{ x: 164, z: bfZ(710) },
|
|
1227
|
+
{ x: 18, z: bfZ(716) },
|
|
1228
|
+
{ x: 168, z: bfZ(720) },
|
|
860
1229
|
];
|
|
861
1230
|
/** The large central soldier camp is commanded by the Battlefield Captain. */
|
|
862
|
-
export const BATTLEFIELD_CAPTAIN_CAMP = { x: 119, z: 734 };
|
|
1231
|
+
export const BATTLEFIELD_CAPTAIN_CAMP = { x: 119, z: bfZ(734) };
|
|
863
1232
|
/**
|
|
864
|
-
* Wooden palisade that seals the soldier shore.
|
|
865
|
-
*
|
|
866
|
-
*
|
|
1233
|
+
* Wooden palisade that seals the soldier shore. The south run used to sit one
|
|
1234
|
+
* tile off the farm's own north fence (farm min local Z 12 = world 748);
|
|
1235
|
+
* BATTLEFIELD_NORTH_SHIFT now leaves open country between the two. Side
|
|
1236
|
+
* returns run from that line to the muddy lake shore so nobody walks around
|
|
1237
|
+
* the ends.
|
|
867
1238
|
*/
|
|
868
1239
|
export const BATTLEFIELD_FENCE_SOUTH_Z = BATTLEFIELD_MAX_WORLD_Z - 1;
|
|
869
1240
|
export const BATTLEFIELD_FENCE_WEST_X = BATTLEFIELD_MIN_X;
|
|
@@ -888,27 +1259,27 @@ export function isBattlefieldCaptainDoorTile(x, z) {
|
|
|
888
1259
|
}
|
|
889
1260
|
/** Goblin camps - farther wilderness (lower world Z), north of the lake. */
|
|
890
1261
|
export const BATTLEFIELD_GOBLIN_CAMPS = [
|
|
891
|
-
{ x: 30, z: 654 },
|
|
892
|
-
{ x: 58, z: 648 },
|
|
893
|
-
{ x: 86, z: 658 },
|
|
894
|
-
{ x: 114, z: 650 },
|
|
895
|
-
{ x: 142, z: 654 },
|
|
896
|
-
{ x: 44, z: 650 },
|
|
897
|
-
{ x: 100, z: 648 },
|
|
898
|
-
{ x: 160, z: 652 },
|
|
899
|
-
{ x: 72, z: 650 },
|
|
900
|
-
{ x: 128, z: 656 },
|
|
1262
|
+
{ x: 30, z: bfZ(654) },
|
|
1263
|
+
{ x: 58, z: bfZ(648) },
|
|
1264
|
+
{ x: 86, z: bfZ(658) },
|
|
1265
|
+
{ x: 114, z: bfZ(650) },
|
|
1266
|
+
{ x: 142, z: bfZ(654) },
|
|
1267
|
+
{ x: 44, z: bfZ(650) },
|
|
1268
|
+
{ x: 100, z: bfZ(648) },
|
|
1269
|
+
{ x: 160, z: bfZ(652) },
|
|
1270
|
+
{ x: 72, z: bfZ(650) },
|
|
1271
|
+
{ x: 128, z: bfZ(656) },
|
|
901
1272
|
];
|
|
902
1273
|
/**
|
|
903
1274
|
* Cultivated cabbage plots on the soldier shore - between lake and camps.
|
|
904
1275
|
* Filled as neat soil rows (not sparse scatter).
|
|
905
1276
|
*/
|
|
906
1277
|
export const BATTLEFIELD_CABBAGE_PLOTS = [
|
|
907
|
-
{ minX: 28, maxX: 38, minZ: 700, maxZ: 706 },
|
|
908
|
-
{ minX: 52, maxX: 62, minZ: 701, maxZ: 707 },
|
|
909
|
-
{ minX: 108, maxX: 118, minZ: 701, maxZ: 707 },
|
|
910
|
-
{ minX: 132, maxX: 142, minZ: 700, maxZ: 706 },
|
|
911
|
-
{ minX: 154, maxX: 164, minZ: 702, maxZ: 707 },
|
|
1278
|
+
{ minX: 28, maxX: 38, minZ: bfZ(700), maxZ: bfZ(706) },
|
|
1279
|
+
{ minX: 52, maxX: 62, minZ: bfZ(701), maxZ: bfZ(707) },
|
|
1280
|
+
{ minX: 108, maxX: 118, minZ: bfZ(701), maxZ: bfZ(707) },
|
|
1281
|
+
{ minX: 132, maxX: 142, minZ: bfZ(700), maxZ: bfZ(706) },
|
|
1282
|
+
{ minX: 154, maxX: 164, minZ: bfZ(702), maxZ: bfZ(707) },
|
|
912
1283
|
];
|
|
913
1284
|
export const DISCOVERY_ZONES = [
|
|
914
1285
|
{
|
|
@@ -928,18 +1299,38 @@ export const DISCOVERY_ZONES = [
|
|
|
928
1299
|
maxZ: CROSSROADS_LOCAL_Z + 5,
|
|
929
1300
|
},
|
|
930
1301
|
{
|
|
1302
|
+
// The id stays 'lil-beach' on purpose. Discovery is persisted per player
|
|
1303
|
+
// as a set of these ids, so renaming it would un-discover the starting
|
|
1304
|
+
// zone for every existing character and pay the XP out a second time.
|
|
1305
|
+
// `SPAWN_ZONE_ID` and the 'lil-beach-safety' area effect match on it too.
|
|
1306
|
+
// Only the label is player-facing, and only the label changed.
|
|
931
1307
|
id: 'lil-beach',
|
|
932
|
-
label: '
|
|
1308
|
+
label: 'Hope Beach',
|
|
933
1309
|
minX: BEACH_GATE_X - 48,
|
|
934
1310
|
maxX: BEACH_GATE_X + 48,
|
|
935
1311
|
minZ: 56,
|
|
936
|
-
|
|
1312
|
+
// Out along the Long Pier as far as its second smashed span. Past that is
|
|
1313
|
+
// its own zone - see 'long-pier'.
|
|
1314
|
+
maxZ: toLocalZ(HOPE_PIER_GAP2_MAX_Z),
|
|
1315
|
+
},
|
|
1316
|
+
{
|
|
1317
|
+
/*
|
|
1318
|
+
* The seaward half of the pier, which cannot be reached until both spans
|
|
1319
|
+
* are planked. Its own zone rather than more Hope Beach, so getting out
|
|
1320
|
+
* there pays the discovery it deserves.
|
|
1321
|
+
*/
|
|
1322
|
+
id: 'long-pier',
|
|
1323
|
+
label: 'The Long Pier',
|
|
1324
|
+
minX: HOPE_PIER_X - 4,
|
|
1325
|
+
maxX: HOPE_PIER_X + 4,
|
|
1326
|
+
minZ: toLocalZ(HOPE_PIER_GAP2_MAX_Z + 1),
|
|
1327
|
+
maxZ: toLocalZ(HOPE_PIER_END_Z),
|
|
937
1328
|
},
|
|
938
1329
|
{
|
|
939
1330
|
id: 'henson-pond',
|
|
940
1331
|
label: 'Henson Pond',
|
|
941
|
-
minX:
|
|
942
|
-
maxX:
|
|
1332
|
+
minX: 294,
|
|
1333
|
+
maxX: 336,
|
|
943
1334
|
minZ: 24,
|
|
944
1335
|
maxZ: 50,
|
|
945
1336
|
},
|
|
@@ -996,6 +1387,16 @@ export const DISCOVERY_ZONES = [
|
|
|
996
1387
|
minZ: SPIDER_CAVE_MOUTH_Z - 6 - WORLD_NORTH_PAD,
|
|
997
1388
|
maxZ: SPIDER_CAVE_MOUTH_Z + 6 - WORLD_NORTH_PAD,
|
|
998
1389
|
},
|
|
1390
|
+
{
|
|
1391
|
+
// Bounds come from the zone descriptor so the card fires exactly at the
|
|
1392
|
+
// silk, wherever the hollow is moved to.
|
|
1393
|
+
id: 'spider-hollow',
|
|
1394
|
+
label: dreadwoodsNpcZone('spider-hollow').label,
|
|
1395
|
+
minX: dreadwoodsNpcZone('spider-hollow').cx - dreadwoodsNpcZone('spider-hollow').rx,
|
|
1396
|
+
maxX: dreadwoodsNpcZone('spider-hollow').cx + dreadwoodsNpcZone('spider-hollow').rx,
|
|
1397
|
+
minZ: dreadwoodsNpcZone('spider-hollow').cz - dreadwoodsNpcZone('spider-hollow').rz - WORLD_NORTH_PAD,
|
|
1398
|
+
maxZ: dreadwoodsNpcZone('spider-hollow').cz + dreadwoodsNpcZone('spider-hollow').rz - WORLD_NORTH_PAD,
|
|
1399
|
+
},
|
|
999
1400
|
{
|
|
1000
1401
|
id: 'trout-fishery',
|
|
1001
1402
|
label: 'Trout Fishery',
|
|
@@ -1004,6 +1405,64 @@ export const DISCOVERY_ZONES = [
|
|
|
1004
1405
|
minZ: FISHERY_NORTH_Z - 3 - WORLD_NORTH_PAD,
|
|
1005
1406
|
maxZ: FISHERY_SOUTH_Z + 5 - WORLD_NORTH_PAD,
|
|
1006
1407
|
},
|
|
1408
|
+
// The snow. Until 2026-09-09 nothing north of the goblin camps was a place:
|
|
1409
|
+
// a hundred thousand walkable tiles that paid no Explorer XP and drew no
|
|
1410
|
+
// label on the map. discoveryZoneAt takes the first match, so the two small
|
|
1411
|
+
// zones come before the field that surrounds them. Z is playable-local,
|
|
1412
|
+
// hence the pad subtraction (see Calverton above).
|
|
1413
|
+
{
|
|
1414
|
+
id: 'northern-gate',
|
|
1415
|
+
label: 'Northern Gate',
|
|
1416
|
+
minX: NORTHERN_GARRISON_MIN_X - 2,
|
|
1417
|
+
maxX: NORTHERN_GARRISON_MAX_X + 2,
|
|
1418
|
+
minZ: NORTHERN_GARRISON_MIN_Z - 2 - WORLD_NORTH_PAD,
|
|
1419
|
+
maxZ: NORTHERN_WALL_Z + 22 - WORLD_NORTH_PAD,
|
|
1420
|
+
},
|
|
1421
|
+
{
|
|
1422
|
+
// The west edge of the snow, where the boats and jetties are iced in.
|
|
1423
|
+
id: 'frozen-shore',
|
|
1424
|
+
label: 'The Frozen Shore',
|
|
1425
|
+
minX: 0,
|
|
1426
|
+
maxX: 12,
|
|
1427
|
+
minZ: 0 - WORLD_NORTH_PAD,
|
|
1428
|
+
maxZ: 345 - WORLD_NORTH_PAD,
|
|
1429
|
+
},
|
|
1430
|
+
{
|
|
1431
|
+
// The frozen lake and the fishers' ground on its banks. Listed before the
|
|
1432
|
+
// snowfields, which cover the whole north and would otherwise claim it.
|
|
1433
|
+
id: 'frozen-lake',
|
|
1434
|
+
label: 'The Frozen Lake',
|
|
1435
|
+
minX: FROZEN_LAKE_CENTER.x - FROZEN_LAKE_RX - 4,
|
|
1436
|
+
maxX: FROZEN_LAKE_CENTER.x + FROZEN_LAKE_RX + 10,
|
|
1437
|
+
minZ: FROZEN_LAKE_CENTER.z - FROZEN_LAKE_RZ - 8 - WORLD_NORTH_PAD,
|
|
1438
|
+
maxZ: FROZEN_LAKE_CENTER.z + FROZEN_LAKE_RZ + 10 - WORLD_NORTH_PAD,
|
|
1439
|
+
},
|
|
1440
|
+
{
|
|
1441
|
+
// The plateau either side of the gorge, bridge included.
|
|
1442
|
+
id: 'crystal-gorge',
|
|
1443
|
+
label: 'The Crystal Gorge',
|
|
1444
|
+
minX: 3,
|
|
1445
|
+
maxX: FROZEN_PLATEAU_MAX_X + 2,
|
|
1446
|
+
minZ: FROZEN_HALLWAY_MOUTH_Z + 8 - WORLD_NORTH_PAD,
|
|
1447
|
+
maxZ: FROZEN_PLATEAU_RISE_Z - 2 - WORLD_NORTH_PAD,
|
|
1448
|
+
},
|
|
1449
|
+
{
|
|
1450
|
+
// The walled canyon from the rune arch to the iron gate.
|
|
1451
|
+
id: 'frozen-hallway',
|
|
1452
|
+
label: 'The Frozen Hallway',
|
|
1453
|
+
minX: FROZEN_HALLWAY_X - FROZEN_HALLWAY_SWING - FROZEN_HALLWAY_HALF_W - FROZEN_HALLWAY_WALL_T - 4,
|
|
1454
|
+
maxX: FROZEN_HALLWAY_X + FROZEN_HALLWAY_SWING + FROZEN_HALLWAY_HALF_W + FROZEN_HALLWAY_WALL_T + 4,
|
|
1455
|
+
minZ: FROZEN_HALLWAY_SEAL_Z - 2 - WORLD_NORTH_PAD,
|
|
1456
|
+
maxZ: FROZEN_HALLWAY_MOUTH_Z + 7 - WORLD_NORTH_PAD,
|
|
1457
|
+
},
|
|
1458
|
+
{
|
|
1459
|
+
id: 'northern-snowfields',
|
|
1460
|
+
label: 'The Snowfields',
|
|
1461
|
+
minX: 0,
|
|
1462
|
+
maxX: NORTHERN_WALL_MAX_X,
|
|
1463
|
+
minZ: 0 - WORLD_NORTH_PAD,
|
|
1464
|
+
maxZ: NORTHERN_WALL_Z - 25 - WORLD_NORTH_PAD,
|
|
1465
|
+
},
|
|
1007
1466
|
];
|
|
1008
1467
|
export function discoveryZoneAt(worldX, worldZ) {
|
|
1009
1468
|
for (const zone of DISCOVERY_ZONES) {
|
|
@@ -1039,15 +1498,21 @@ export function inMineYard(x, z, margin = 0) {
|
|
|
1039
1498
|
*/
|
|
1040
1499
|
export const WORLD_MAP_LANDMARKS = [
|
|
1041
1500
|
{ x: NORTHERN_GATE_X, z: NORTHERN_WALL_Z, label: 'Northern Gate' },
|
|
1501
|
+
{ x: NORTHERN_CAMP_CENTER.x, z: NORTHERN_CAMP_CENTER.z, label: 'War Camp', labelSide: 'right' },
|
|
1502
|
+
{ x: 8, z: 200, label: 'Frozen Shore', labelSide: 'right' },
|
|
1503
|
+
{ x: FROZEN_LAKE_CENTER.x, z: FROZEN_LAKE_CENTER.z, label: 'Frozen Lake', labelSide: 'right' },
|
|
1504
|
+
{ x: CRYSTAL_BRIDGE_X, z: (CRYSTAL_GORGE_MIN_Z + CRYSTAL_GORGE_MAX_Z) / 2, label: 'Crystal Bridge', labelSide: 'right' },
|
|
1505
|
+
{ x: FROZEN_HALLWAY_X, z: (FROZEN_HALLWAY_GATE_Z + FROZEN_HALLWAY_MOUTH_Z) / 2, label: 'Frozen Hallway', labelSide: 'right' },
|
|
1042
1506
|
{ ...discoveryZoneCenter('no-harm-farm'), label: 'Farm' },
|
|
1043
1507
|
{ x: CROSSROADS_WORLD_X, z: toWorldZ(CROSSROADS_LOCAL_Z), label: 'Crossroads' },
|
|
1044
1508
|
{ ...discoveryZoneCenter('henson-pond'), label: 'Pond' },
|
|
1045
1509
|
{
|
|
1046
1510
|
x: BEACH_GATE_X,
|
|
1047
1511
|
z: Math.round((BEACH_SAND_START_Z + OCEAN_START_Z) / 2),
|
|
1048
|
-
label: '
|
|
1512
|
+
label: 'Hope Beach',
|
|
1049
1513
|
labelSide: 'on',
|
|
1050
1514
|
},
|
|
1515
|
+
{ x: HOPE_PIER_X, z: HOPE_PIER_END_Z, label: 'The Long Pier', labelSide: 'left' },
|
|
1051
1516
|
{
|
|
1052
1517
|
x: Math.round((FISHERY_WATER_MIN_X + FISHERY_WATER_MAX_X) / 2),
|
|
1053
1518
|
z: Math.round((FISHERY_WATER_NORTH_Z + FISHERY_WATER_SOUTH_Z) / 2),
|
|
@@ -1142,5 +1607,8 @@ export const MINE_SEAMS = [
|
|
|
1142
1607
|
{ dx: -5, dz: -7, style: 'copper' },
|
|
1143
1608
|
// The single tin seam works the eastern side of the face.
|
|
1144
1609
|
{ dx: 7, dz: -5, style: 'tin' },
|
|
1610
|
+
// Iron seams along the deep northern and eastern faces.
|
|
1611
|
+
{ dx: 5, dz: -7, style: 'iron' },
|
|
1612
|
+
{ dx: 8, dz: -1, style: 'iron' },
|
|
1145
1613
|
];
|
|
1146
1614
|
//# sourceMappingURL=landmarks.js.map
|