forked from Bitcoindefi/OpenAO
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmapNpcStorage.ts
More file actions
124 lines (105 loc) · 3.35 KB
/
Copy pathmapNpcStorage.ts
File metadata and controls
124 lines (105 loc) · 3.35 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
import { existsSync } from "fs";
import fs from "fs/promises";
import path from "path";
export type MapNpcPlacement = {
mapNum: number;
x: number;
y: number;
npcIndex: number;
movement?: number;
};
const MAP_DIR_PATTERN = /^mapa_(\d+)$/i;
function toFiniteNumber(value: unknown): number | null {
if (typeof value === "number" && Number.isFinite(value)) {
return value;
}
if (typeof value === "string" && value.trim()) {
const parsed = Number(value);
return Number.isFinite(parsed) ? parsed : null;
}
return null;
}
function normalizePlacement(
value: unknown,
fallbackMapNum?: number,
): MapNpcPlacement | null {
if (!value || typeof value !== "object") {
return null;
}
const candidate = value as Record<string, unknown>;
const mapNum = toFiniteNumber(candidate.mapNum) ?? fallbackMapNum ?? null;
const x = toFiniteNumber(candidate.x);
const y = toFiniteNumber(candidate.y);
const npcIndex = toFiniteNumber(candidate.npcIndex);
const movement = toFiniteNumber(candidate.movement);
if (
mapNum === null ||
!Number.isInteger(mapNum) ||
mapNum <= 0 ||
x === null ||
!Number.isInteger(x) ||
x <= 0 ||
y === null ||
!Number.isInteger(y) ||
y <= 0 ||
npcIndex === null ||
!Number.isInteger(npcIndex) ||
npcIndex <= 0
) {
return null;
}
return movement !== null && Number.isInteger(movement)
? { mapNum, x, y, npcIndex, movement }
: { mapNum, x, y, npcIndex };
}
function sortPlacements(placements: MapNpcPlacement[]): MapNpcPlacement[] {
return [...placements].sort(
(left, right) =>
left.mapNum - right.mapNum ||
left.y - right.y ||
left.x - right.x ||
left.npcIndex - right.npcIndex,
);
}
export async function listAvailableMapIds(
sourceDir: string,
): Promise<number[]> {
if (!existsSync(sourceDir)) {
return [];
}
const entries = await fs.readdir(sourceDir, { withFileTypes: true });
return entries
.filter((entry) => entry.isDirectory())
.map((entry) => entry.name.match(MAP_DIR_PATTERN))
.filter((match): match is RegExpMatchArray => Boolean(match))
.map((match) => Number.parseInt(match[1], 10))
.filter((mapId) => Number.isInteger(mapId) && mapId > 0)
.sort((left, right) => left - right);
}
export async function loadMapNpcPlacements(
mapsSourceDir: string,
mapNum: number,
): Promise<MapNpcPlacement[]> {
const filePath = path.join(mapsSourceDir, `mapa_${mapNum}`, "npcs.json");
if (!existsSync(filePath)) {
return [];
}
const parsed = JSON.parse(await fs.readFile(filePath, "utf8")) as unknown;
if (!Array.isArray(parsed)) {
return [];
}
return sortPlacements(
parsed
.map((entry) => normalizePlacement(entry, mapNum))
.filter((entry): entry is MapNpcPlacement => Boolean(entry)),
);
}
export async function loadAllMapNpcPlacements(
mapsSourceDir: string,
): Promise<MapNpcPlacement[]> {
const mapIds = await listAvailableMapIds(mapsSourceDir);
const placements = await Promise.all(
mapIds.map((mapId) => loadMapNpcPlacements(mapsSourceDir, mapId)),
);
return sortPlacements(placements.flat());
}