@cs2dak/maps 0.2.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/LICENSE +7 -0
  2. package/README.md +33 -2
  3. package/callout-grid/de_ancient.json +1 -0
  4. package/callout-grid/de_anubis.json +1 -0
  5. package/callout-grid/de_dust2.json +1 -0
  6. package/callout-grid/de_inferno.json +1 -0
  7. package/callout-grid/de_mirage.json +1 -0
  8. package/callout-grid/de_nuke.json +1 -0
  9. package/callout-grid/de_overpass.json +1 -0
  10. package/map-nav/de_ancient.nav.json +1 -0
  11. package/map-nav/de_anubis.nav.json +1 -0
  12. package/map-nav/de_dust2.nav.json +1 -0
  13. package/map-nav/de_inferno.nav.json +1 -0
  14. package/map-nav/de_mirage.nav.json +1 -0
  15. package/map-nav/de_nuke.nav.json +1 -0
  16. package/map-nav/de_overpass.nav.json +1 -0
  17. package/map-routes/de_ancient.json +231 -0
  18. package/map-routes/de_anubis.json +204 -0
  19. package/map-routes/de_dust2.json +153 -0
  20. package/map-routes/de_inferno.json +207 -0
  21. package/map-routes/de_mirage.json +156 -0
  22. package/map-routes/de_nuke.json +63 -0
  23. package/map-routes/de_overpass.json +87 -0
  24. package/package.json +9 -3
  25. package/src/callout-grid-browser.test.ts +36 -0
  26. package/src/callout-grid-browser.ts +31 -0
  27. package/src/callout-grid-node.test.ts +27 -0
  28. package/src/callout-grid-node.ts +28 -0
  29. package/src/callout-grid.test.ts +73 -0
  30. package/src/callout-grid.ts +168 -0
  31. package/src/callout-names.ts +238 -0
  32. package/src/default-positions.test.ts +109 -0
  33. package/src/default-positions.ts +266 -0
  34. package/src/geometry-assets.test.ts +22 -0
  35. package/src/geometry-assets.ts +57 -0
  36. package/src/index.ts +41 -0
  37. package/src/lineups.test.ts +95 -0
  38. package/src/lineups.ts +336 -0
  39. package/src/nav.test.ts +68 -0
  40. package/src/nav.ts +228 -0
  41. package/src/position-candidates.test.ts +62 -0
  42. package/src/position-candidates.ts +183 -0
  43. package/src/radar-grid.ts +66 -0
  44. package/src/route-assets.test.ts +80 -0
  45. package/src/route-assets.ts +23 -0
  46. package/src/routes.test.ts +46 -0
  47. package/src/routes.ts +93 -0
  48. package/src/site-entry-chokes.ts +234 -0
  49. package/src/site-entry-lexicon.ts +131 -0
  50. package/src/tri-assets.ts +72 -0
  51. package/src/visibility.test.ts +36 -0
  52. package/src/visibility.ts +237 -0
  53. package/src/zone-assets.ts +23 -0
  54. package/src/zones.ts +7 -7
@@ -0,0 +1,207 @@
1
+ {
2
+ "mapName": "de_inferno",
3
+ "version": "de_inferno-routes-0.4",
4
+ "routes": [
5
+ {
6
+ "id": "a_mid",
7
+ "name": "中路A1强攻",
8
+ "type": "primary_entry",
9
+ "bombsite": "a",
10
+ "confidence": "high",
11
+ "zones": [
12
+ {
13
+ "id": "TSpawn"
14
+ },
15
+ {
16
+ "id": "TRamp"
17
+ },
18
+ {
19
+ "id": "LowerMid"
20
+ },
21
+ {
22
+ "id": "Middle"
23
+ },
24
+ {
25
+ "id": "TopofMid"
26
+ },
27
+ {
28
+ "id": "Quad"
29
+ },
30
+ {
31
+ "id": "BombsiteA"
32
+ }
33
+ ]
34
+ },
35
+ {
36
+ "id": "a_arch",
37
+ "name": "中路链接夹A",
38
+ "type": "secondary_entry",
39
+ "bombsite": "a",
40
+ "confidence": "high",
41
+ "zones": [
42
+ {
43
+ "id": "TSpawn"
44
+ },
45
+ {
46
+ "id": "TRamp"
47
+ },
48
+ {
49
+ "id": "LowerMid"
50
+ },
51
+ {
52
+ "id": "Middle"
53
+ },
54
+ {
55
+ "id": "TopofMid"
56
+ },
57
+ {
58
+ "id": "Arch"
59
+ },
60
+ {
61
+ "id": "BombsiteA"
62
+ }
63
+ ]
64
+ },
65
+ {
66
+ "id": "b_banana",
67
+ "name": "B点正面",
68
+ "type": "primary_entry",
69
+ "bombsite": "b",
70
+ "confidence": "high",
71
+ "zones": [
72
+ {
73
+ "id": "TSpawn"
74
+ },
75
+ {
76
+ "id": "TRamp"
77
+ },
78
+ {
79
+ "id": "LowerMid"
80
+ },
81
+ {
82
+ "id": "Banana"
83
+ },
84
+ {
85
+ "id": "BombsiteB"
86
+ }
87
+ ]
88
+ },
89
+ {
90
+ "id": "route_1780681085281",
91
+ "name": "侧道A1强攻",
92
+ "type": "primary_entry",
93
+ "bombsite": "a",
94
+ "confidence": "high",
95
+ "zones": [
96
+ {
97
+ "id": "TSpawn"
98
+ },
99
+ {
100
+ "id": "SecondMid"
101
+ },
102
+ {
103
+ "id": "TopofMid"
104
+ },
105
+ {
106
+ "id": "Quad"
107
+ },
108
+ {
109
+ "id": "BombsiteA"
110
+ }
111
+ ]
112
+ },
113
+ {
114
+ "id": "route_1780681126248",
115
+ "name": "匪2二楼控制",
116
+ "type": "secondary_entry",
117
+ "bombsite": "a",
118
+ "confidence": "high",
119
+ "zones": [
120
+ {
121
+ "id": "TSpawn"
122
+ },
123
+ {
124
+ "id": "TRamp"
125
+ },
126
+ {
127
+ "id": "Bridge"
128
+ },
129
+ {
130
+ "id": "Upstairs"
131
+ },
132
+ {
133
+ "id": "BackAlley"
134
+ },
135
+ {
136
+ "id": "Apartments"
137
+ },
138
+ {
139
+ "id": "Balcony"
140
+ },
141
+ {
142
+ "id": "BombsiteA"
143
+ }
144
+ ]
145
+ },
146
+ {
147
+ "id": "route_1780681170948",
148
+ "name": "侧道二楼控制",
149
+ "type": "secondary_entry",
150
+ "bombsite": "a",
151
+ "confidence": "high",
152
+ "zones": [
153
+ {
154
+ "id": "TSpawn"
155
+ },
156
+ {
157
+ "id": "SecondMid"
158
+ },
159
+ {
160
+ "id": "Apartments"
161
+ },
162
+ {
163
+ "id": "Balcony"
164
+ },
165
+ {
166
+ "id": "BombsiteA"
167
+ }
168
+ ]
169
+ },
170
+ {
171
+ "id": "route_1780681203714",
172
+ "name": "中路警家夹B",
173
+ "type": "secondary_entry",
174
+ "bombsite": "b",
175
+ "confidence": "high",
176
+ "zones": [
177
+ {
178
+ "id": "TSpawn"
179
+ },
180
+ {
181
+ "id": "TRamp"
182
+ },
183
+ {
184
+ "id": "LowerMid"
185
+ },
186
+ {
187
+ "id": "Middle"
188
+ },
189
+ {
190
+ "id": "TopofMid"
191
+ },
192
+ {
193
+ "id": "Arch"
194
+ },
195
+ {
196
+ "id": "CTSpawn"
197
+ },
198
+ {
199
+ "id": "Ruins"
200
+ },
201
+ {
202
+ "id": "BombsiteB"
203
+ }
204
+ ]
205
+ }
206
+ ]
207
+ }
@@ -0,0 +1,156 @@
1
+ {
2
+ "mapName": "de_mirage",
3
+ "version": "de_mirage-routes-0.4",
4
+ "routes": [
5
+ {
6
+ "id": "a_palace",
7
+ "name": "A二楼",
8
+ "type": "secondary_entry",
9
+ "bombsite": "a",
10
+ "confidence": "high",
11
+ "zones": [
12
+ {
13
+ "id": "TSpawn"
14
+ },
15
+ {
16
+ "id": "PalaceInterior"
17
+ },
18
+ {
19
+ "id": "Scaffolding"
20
+ },
21
+ {
22
+ "id": "BombsiteA"
23
+ }
24
+ ]
25
+ },
26
+ {
27
+ "id": "a_mid_connector",
28
+ "name": "中路拱门",
29
+ "type": "mid_connector",
30
+ "bombsite": "a",
31
+ "confidence": "high",
32
+ "zones": [
33
+ {
34
+ "id": "TSpawn"
35
+ },
36
+ {
37
+ "id": "SideAlley"
38
+ },
39
+ {
40
+ "id": "TopofMid"
41
+ },
42
+ {
43
+ "id": "Middle"
44
+ },
45
+ {
46
+ "id": "Connector"
47
+ },
48
+ {
49
+ "id": "Stairs"
50
+ },
51
+ {
52
+ "id": "BombsiteA"
53
+ }
54
+ ]
55
+ },
56
+ {
57
+ "id": "a_underpass_wrap",
58
+ "name": "下水道控中夹A",
59
+ "type": "lurk_lane",
60
+ "bombsite": "a",
61
+ "confidence": "medium",
62
+ "zones": [
63
+ {
64
+ "id": "TSpawn"
65
+ },
66
+ {
67
+ "id": "House"
68
+ },
69
+ {
70
+ "id": "BackAlley"
71
+ },
72
+ {
73
+ "id": "Underpass"
74
+ },
75
+ {
76
+ "id": "Middle"
77
+ },
78
+ {
79
+ "id": "Connector"
80
+ },
81
+ {
82
+ "id": "BombsiteA"
83
+ }
84
+ ]
85
+ },
86
+ {
87
+ "id": "a_scaffold",
88
+ "name": "A1",
89
+ "type": "primary_entry",
90
+ "bombsite": "a",
91
+ "confidence": "high",
92
+ "zones": [
93
+ {
94
+ "id": "TSpawn"
95
+ },
96
+ {
97
+ "id": "PalaceAlley"
98
+ },
99
+ {
100
+ "id": "TRamp"
101
+ },
102
+ {
103
+ "id": "BombsiteA"
104
+ }
105
+ ]
106
+ },
107
+ {
108
+ "id": "b_apartments",
109
+ "name": "B二楼",
110
+ "type": "primary_entry",
111
+ "bombsite": "b",
112
+ "confidence": "high",
113
+ "zones": [
114
+ {
115
+ "id": "TSpawn"
116
+ },
117
+ {
118
+ "id": "House"
119
+ },
120
+ {
121
+ "id": "BackAlley"
122
+ },
123
+ {
124
+ "id": "Apartments"
125
+ },
126
+ {
127
+ "id": "BombsiteB"
128
+ }
129
+ ]
130
+ },
131
+ {
132
+ "id": "b_catwalk",
133
+ "name": "B小",
134
+ "type": "mid_connector",
135
+ "bombsite": "b",
136
+ "confidence": "high",
137
+ "zones": [
138
+ {
139
+ "id": "TSpawn"
140
+ },
141
+ {
142
+ "id": "SideAlley"
143
+ },
144
+ {
145
+ "id": "TopofMid"
146
+ },
147
+ {
148
+ "id": "Catwalk"
149
+ },
150
+ {
151
+ "id": "BombsiteB"
152
+ }
153
+ ]
154
+ }
155
+ ]
156
+ }
@@ -0,0 +1,63 @@
1
+ {
2
+ "mapName": "de_nuke",
3
+ "version": "nuke-routes-0.3",
4
+ "routes": [
5
+ {
6
+ "id": "b_secret",
7
+ "name": "K1下B",
8
+ "type": "primary_entry",
9
+ "bombsite": "b",
10
+ "confidence": "medium",
11
+ "zones": [
12
+ {
13
+ "id": "TSpawn"
14
+ },
15
+ {
16
+ "id": "Outside"
17
+ },
18
+ {
19
+ "id": "Secret"
20
+ },
21
+ {
22
+ "id": "Tunnels"
23
+ },
24
+ {
25
+ "id": "BombsiteB"
26
+ }
27
+ ]
28
+ },
29
+ {
30
+ "id": "b_ramp",
31
+ "name": "铁板下B",
32
+ "type": "secondary_entry",
33
+ "bombsite": "b",
34
+ "confidence": "medium",
35
+ "zones": [
36
+ {
37
+ "id": "TSpawn"
38
+ },
39
+ {
40
+ "id": "Outside"
41
+ },
42
+ {
43
+ "id": "Lobby"
44
+ },
45
+ {
46
+ "id": "Vending"
47
+ },
48
+ {
49
+ "id": "Trophy"
50
+ },
51
+ {
52
+ "id": "Control"
53
+ },
54
+ {
55
+ "id": "Ramp"
56
+ },
57
+ {
58
+ "id": "BombsiteB"
59
+ }
60
+ ]
61
+ }
62
+ ]
63
+ }
@@ -0,0 +1,87 @@
1
+ {
2
+ "mapName": "de_overpass",
3
+ "version": "de_overpass-routes-0.3",
4
+ "routes": [
5
+ {
6
+ "id": "a_park",
7
+ "name": "公园进A",
8
+ "type": "primary_entry",
9
+ "bombsite": "a",
10
+ "confidence": "medium",
11
+ "zones": [
12
+ {
13
+ "id": "TSpawn"
14
+ },
15
+ {
16
+ "id": "TStairs"
17
+ },
18
+ {
19
+ "id": "Tunnels"
20
+ },
21
+ {
22
+ "id": "Fountain"
23
+ },
24
+ {
25
+ "id": "UpperPark"
26
+ },
27
+ {
28
+ "id": "LowerPark"
29
+ },
30
+ {
31
+ "id": "BombsiteA"
32
+ }
33
+ ]
34
+ },
35
+ {
36
+ "id": "b_canal",
37
+ "name": "长管进B",
38
+ "type": "primary_entry",
39
+ "bombsite": "b",
40
+ "confidence": "medium",
41
+ "zones": [
42
+ {
43
+ "id": "TSpawn"
44
+ },
45
+ {
46
+ "id": "Alley"
47
+ },
48
+ {
49
+ "id": "Canal"
50
+ },
51
+ {
52
+ "id": "BombsiteB"
53
+ }
54
+ ]
55
+ },
56
+ {
57
+ "id": "b_construction",
58
+ "name": "短管工地进B",
59
+ "type": "secondary_entry",
60
+ "bombsite": "b",
61
+ "confidence": "medium",
62
+ "zones": [
63
+ {
64
+ "id": "TSpawn"
65
+ },
66
+ {
67
+ "id": "Alley"
68
+ },
69
+ {
70
+ "id": "Canal"
71
+ },
72
+ {
73
+ "id": "Pipe"
74
+ },
75
+ {
76
+ "id": "Water"
77
+ },
78
+ {
79
+ "id": "Construction"
80
+ },
81
+ {
82
+ "id": "BombsiteB"
83
+ }
84
+ ]
85
+ }
86
+ ]
87
+ }
package/package.json CHANGED
@@ -1,13 +1,19 @@
1
1
  {
2
2
  "name": "@cs2dak/maps",
3
- "version": "0.2.1",
3
+ "version": "1.0.0",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "exports": {
7
- ".": "./src/index.ts"
7
+ ".": "./src/index.ts",
8
+ "./callout-grid-browser": "./src/callout-grid-browser.ts",
9
+ "./callout-grid-node": "./src/callout-grid-node.ts",
10
+ "./tri-assets": "./src/tri-assets.ts"
8
11
  },
9
12
  "files": [
10
- "src"
13
+ "src",
14
+ "callout-grid",
15
+ "map-routes",
16
+ "map-nav"
11
17
  ],
12
18
  "publishConfig": {
13
19
  "access": "public"
@@ -0,0 +1,36 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { clearCalloutGridBrowserCache, loadCalloutGridBrowser } from "./callout-grid-browser.js";
3
+ import type { CalloutGrid } from "./callout-grid.js";
4
+
5
+ const sampleGrid: CalloutGrid = {
6
+ mapName: "de_test",
7
+ gridSize: 10,
8
+ origin: [0, 0, 0],
9
+ maxCoord: [10, 10, 10],
10
+ dims: [1, 1, 1],
11
+ vocabulary: ["A"],
12
+ confidence: 0.51,
13
+ minSamples: 3,
14
+ cells: { "0,0,0": [0, 1, 3] },
15
+ };
16
+
17
+ describe("callout-grid browser loader", () => {
18
+ it("loads a map from explicit URL and caches the promise", async () => {
19
+ clearCalloutGridBrowserCache();
20
+ const calls: string[] = [];
21
+ const fetchImpl = async (url: string | URL | Request) => {
22
+ calls.push(String(url));
23
+ return new Response(JSON.stringify(sampleGrid), { status: 200 });
24
+ };
25
+
26
+ await expect(loadCalloutGridBrowser("de_test", { urls: { de_test: "/assets/de_test.json" }, fetchImpl })).resolves.toEqual(sampleGrid);
27
+ await expect(loadCalloutGridBrowser("de_test", { urls: { de_test: "/assets/de_test.json" }, fetchImpl })).resolves.toEqual(sampleGrid);
28
+ expect(calls).toEqual(["/assets/de_test.json"]);
29
+ });
30
+
31
+ it("returns null for missing assets", async () => {
32
+ clearCalloutGridBrowserCache();
33
+ const fetchImpl = async () => new Response("", { status: 404 });
34
+ await expect(loadCalloutGridBrowser("de_missing", { baseUrl: "/callout-grid", fetchImpl })).resolves.toBeNull();
35
+ });
36
+ });
@@ -0,0 +1,31 @@
1
+ import type { CalloutGrid } from "./callout-grid.js";
2
+
3
+ export interface CalloutGridBrowserOptions {
4
+ baseUrl?: string;
5
+ urls?: Record<string, string>;
6
+ fetchImpl?: typeof fetch;
7
+ }
8
+
9
+ const CACHE = new Map<string, Promise<CalloutGrid | null>>();
10
+
11
+ export function loadCalloutGridBrowser(
12
+ mapName: string,
13
+ options: CalloutGridBrowserOptions = {},
14
+ ): Promise<CalloutGrid | null> {
15
+ const url = options.urls?.[mapName] ?? `${options.baseUrl ?? "./callout-grid"}/${mapName}.json`;
16
+ const cacheKey = `${mapName}:${url}`;
17
+ const cached = CACHE.get(cacheKey);
18
+ if (cached) return cached;
19
+
20
+ const fetcher = options.fetchImpl ?? globalThis.fetch;
21
+ if (!fetcher) return Promise.resolve(null);
22
+ const promise = fetcher(url)
23
+ .then((response) => response.ok ? response.json() as Promise<CalloutGrid> : null)
24
+ .catch(() => null);
25
+ CACHE.set(cacheKey, promise);
26
+ return promise;
27
+ }
28
+
29
+ export function clearCalloutGridBrowserCache(): void {
30
+ CACHE.clear();
31
+ }
@@ -0,0 +1,27 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { readFileSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ import { clearCalloutGridCache, loadCalloutGrid } from "./callout-grid-node.js";
5
+ import { calloutAt } from "./callout-grid.js";
6
+
7
+ describe("callout-grid node loader", () => {
8
+ it("loads committed callout-grid JSON for package consumers", () => {
9
+ clearCalloutGridCache();
10
+ const grid = loadCalloutGrid("de_mirage");
11
+ expect(grid?.mapName).toBe("de_mirage");
12
+ expect(Object.keys(grid?.cells ?? {}).length).toBeGreaterThan(1000);
13
+ });
14
+
15
+ it("package files include callout-grid assets", () => {
16
+ const pkg = JSON.parse(readFileSync(join(process.cwd(), "packages/maps/package.json"), "utf-8")) as { files?: string[] };
17
+ expect(pkg.files).toContain("callout-grid");
18
+ });
19
+
20
+ it("real grid cells can be queried through calloutAt", () => {
21
+ const grid = loadCalloutGrid("de_mirage");
22
+ const firstKey = Object.keys(grid?.cells ?? {})[0];
23
+ expect(firstKey).toBeTruthy();
24
+ const [x, y, z] = firstKey!.split(",").map(Number);
25
+ expect(calloutAt(grid!, { x: x!, y: y!, z: z! })?.callout).toBeTruthy();
26
+ });
27
+ });
@@ -0,0 +1,28 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { join, dirname } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import type { CalloutGrid } from "./callout-grid.js";
5
+
6
+ const THIS_DIR = dirname(fileURLToPath(import.meta.url));
7
+ const MAPS_ROOT = join(THIS_DIR, "..");
8
+ const GRID_DIR = join(MAPS_ROOT, "callout-grid");
9
+ const CACHE = new Map<string, CalloutGrid | null>();
10
+
11
+ export function loadCalloutGrid(mapName: string): CalloutGrid | null {
12
+ const cached = CACHE.get(mapName);
13
+ if (cached !== undefined) return cached;
14
+
15
+ try {
16
+ const raw = readFileSync(join(GRID_DIR, `${mapName}.json`), "utf-8");
17
+ const grid = JSON.parse(raw) as CalloutGrid;
18
+ CACHE.set(mapName, grid);
19
+ return grid;
20
+ } catch {
21
+ CACHE.set(mapName, null);
22
+ return null;
23
+ }
24
+ }
25
+
26
+ export function clearCalloutGridCache(): void {
27
+ CACHE.clear();
28
+ }