lpc-forge 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.
- package/CREDITS.csv +22985 -0
- package/LICENSE +674 -0
- package/README.md +281 -0
- package/assets/music/.gitkeep +0 -0
- package/dist/character/batch.d.ts +17 -0
- package/dist/character/batch.js +48 -0
- package/dist/character/batch.js.map +1 -0
- package/dist/character/composer.d.ts +3 -0
- package/dist/character/composer.js +164 -0
- package/dist/character/composer.js.map +1 -0
- package/dist/character/definitions.d.ts +16 -0
- package/dist/character/definitions.js +116 -0
- package/dist/character/definitions.js.map +1 -0
- package/dist/character/presets.d.ts +6 -0
- package/dist/character/presets.js +246 -0
- package/dist/character/presets.js.map +1 -0
- package/dist/character/slicer.d.ts +8 -0
- package/dist/character/slicer.js +66 -0
- package/dist/character/slicer.js.map +1 -0
- package/dist/character/types.d.ts +48 -0
- package/dist/character/types.js +32 -0
- package/dist/character/types.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +938 -0
- package/dist/export/frames.d.ts +5 -0
- package/dist/export/frames.js +15 -0
- package/dist/export/frames.js.map +1 -0
- package/dist/export/godot.d.ts +17 -0
- package/dist/export/godot.js +464 -0
- package/dist/export/godot.js.map +1 -0
- package/dist/export/types.d.ts +11 -0
- package/dist/export/types.js +2 -0
- package/dist/export/types.js.map +1 -0
- package/dist/license.d.ts +49 -0
- package/dist/license.js +271 -0
- package/dist/map/cellular.d.ts +3 -0
- package/dist/map/cellular.js +191 -0
- package/dist/map/cellular.js.map +1 -0
- package/dist/map/dungeon.d.ts +3 -0
- package/dist/map/dungeon.js +238 -0
- package/dist/map/dungeon.js.map +1 -0
- package/dist/map/multifloor.d.ts +20 -0
- package/dist/map/multifloor.js +57 -0
- package/dist/map/multifloor.js.map +1 -0
- package/dist/map/overworld.d.ts +3 -0
- package/dist/map/overworld.js +205 -0
- package/dist/map/overworld.js.map +1 -0
- package/dist/map/town.d.ts +7 -0
- package/dist/map/town.js +181 -0
- package/dist/map/town.js.map +1 -0
- package/dist/map/types.d.ts +65 -0
- package/dist/map/types.js +16 -0
- package/dist/map/types.js.map +1 -0
- package/dist/map/wfc.d.ts +18 -0
- package/dist/map/wfc.js +192 -0
- package/dist/map/wfc.js.map +1 -0
- package/dist/tileset/atlas.d.ts +15 -0
- package/dist/tileset/atlas.js +55 -0
- package/dist/tileset/atlas.js.map +1 -0
- package/dist/tileset/registry.d.ts +12 -0
- package/dist/tileset/registry.js +71 -0
- package/dist/tileset/registry.js.map +1 -0
- package/dist/tileset/terrain.d.ts +3 -0
- package/dist/tileset/terrain.js +110 -0
- package/dist/tileset/terrain.js.map +1 -0
- package/dist/utils/credits.d.ts +11 -0
- package/dist/utils/credits.js +74 -0
- package/dist/utils/credits.js.map +1 -0
- package/dist/utils/image.d.ts +17 -0
- package/dist/utils/image.js +94 -0
- package/dist/utils/image.js.map +1 -0
- package/dist/utils/rng.d.ts +18 -0
- package/dist/utils/rng.js +48 -0
- package/dist/utils/rng.js.map +1 -0
- package/package.json +77 -0
package/dist/license.js
ADDED
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
import { readFile, writeFile, mkdir, unlink } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { homedir, hostname, platform, arch } from 'node:os';
|
|
4
|
+
import { createHmac } from 'node:crypto';
|
|
5
|
+
const LICENSE_DIR = join(homedir(), '.lpc-forge');
|
|
6
|
+
const LICENSE_FILE = join(LICENSE_DIR, 'license.json');
|
|
7
|
+
/**
|
|
8
|
+
* HMAC signing key derived from a combination of factors.
|
|
9
|
+
* This isn't meant to be uncrackable — it's a Node CLI tool, the source is
|
|
10
|
+
* open. The goal is to make casual bypass inconvenient enough that buying
|
|
11
|
+
* $10 is easier. Determined crackers will always win; we optimize for the
|
|
12
|
+
* honest middle.
|
|
13
|
+
*/
|
|
14
|
+
const SIGNING_SEED = 'lpc-forge:blueth:2026';
|
|
15
|
+
// ─── Machine Fingerprint ──────────────────────────────────────────────
|
|
16
|
+
function getMachineFingerprint() {
|
|
17
|
+
const raw = `${hostname()}:${platform()}:${arch()}:${homedir()}`;
|
|
18
|
+
return createHmac('sha256', SIGNING_SEED).update(raw).digest('hex').slice(0, 16);
|
|
19
|
+
}
|
|
20
|
+
// ─── HMAC Signing ─────────────────────────────────────────────────────
|
|
21
|
+
function signLicense(data) {
|
|
22
|
+
const payload = [
|
|
23
|
+
data.key,
|
|
24
|
+
data.email,
|
|
25
|
+
data.product,
|
|
26
|
+
data.activatedAt,
|
|
27
|
+
data.lastVerifiedAt,
|
|
28
|
+
data.machineId,
|
|
29
|
+
].join('|');
|
|
30
|
+
return createHmac('sha256', SIGNING_SEED).update(payload).digest('hex');
|
|
31
|
+
}
|
|
32
|
+
function verifySignature(license) {
|
|
33
|
+
const { signature, ...data } = license;
|
|
34
|
+
return signLicense(data) === signature;
|
|
35
|
+
}
|
|
36
|
+
// ─── License Validation ───────────────────────────────────────────────
|
|
37
|
+
/**
|
|
38
|
+
* Check if a valid premium license exists.
|
|
39
|
+
*
|
|
40
|
+
* Validates:
|
|
41
|
+
* 1. File exists and parses
|
|
42
|
+
* 2. HMAC signature is correct (not tampered)
|
|
43
|
+
* 3. Machine fingerprint matches (not copied from another machine)
|
|
44
|
+
* 4. Key format is valid
|
|
45
|
+
* 5. Activation is not absurdly far in the future
|
|
46
|
+
*
|
|
47
|
+
* Periodic re-validation:
|
|
48
|
+
* - If last online verification was > 30 days ago, attempts re-verify
|
|
49
|
+
* - If re-verify fails due to network, still valid (grace period)
|
|
50
|
+
* - If re-verify fails due to invalid key, invalidates
|
|
51
|
+
*/
|
|
52
|
+
export async function hasValidLicense() {
|
|
53
|
+
try {
|
|
54
|
+
const license = await loadLicense();
|
|
55
|
+
if (!license)
|
|
56
|
+
return false;
|
|
57
|
+
// Signature check (tamper detection)
|
|
58
|
+
if (!verifySignature(license)) {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
// Machine binding check
|
|
62
|
+
if (license.machineId !== getMachineFingerprint()) {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
// Key format sanity
|
|
66
|
+
if (!isValidKeyFormat(license.key)) {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
// Activation date sanity (not in the future)
|
|
70
|
+
const activated = new Date(license.activatedAt);
|
|
71
|
+
if (activated.getTime() > Date.now() + 86400000) {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
// Periodic re-validation (every 30 days)
|
|
75
|
+
const lastVerified = new Date(license.lastVerifiedAt);
|
|
76
|
+
const daysSinceVerify = (Date.now() - lastVerified.getTime()) / 86400000;
|
|
77
|
+
if (daysSinceVerify > 30) {
|
|
78
|
+
// Try to re-verify online, but don't block on failure
|
|
79
|
+
const revalidated = await revalidateOnline(license);
|
|
80
|
+
if (revalidated === 'invalid') {
|
|
81
|
+
// Key was explicitly rejected by Gumroad (refunded, disabled, etc.)
|
|
82
|
+
await removeLicense();
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
// 'valid' or 'network_error' — both count as OK (grace period)
|
|
86
|
+
if (revalidated === 'valid') {
|
|
87
|
+
await updateLastVerified(license);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/** Require a valid license or exit with purchase instructions */
|
|
97
|
+
export async function requireLicense(commandName) {
|
|
98
|
+
const valid = await hasValidLicense();
|
|
99
|
+
if (!valid) {
|
|
100
|
+
const chalk = (await import('chalk')).default;
|
|
101
|
+
console.log('');
|
|
102
|
+
console.log(chalk.yellow(`⚠ The "${commandName}" command requires LPC Forge Premium.`));
|
|
103
|
+
console.log('');
|
|
104
|
+
console.log(chalk.white(' Purchase for $10: ') + chalk.cyan.underline('https://launchday.gumroad.com/l/lpc-forge-premium'));
|
|
105
|
+
console.log(chalk.white(' Then activate: ') + chalk.green('lpc-forge activate <your-license-key>'));
|
|
106
|
+
console.log('');
|
|
107
|
+
console.log(chalk.gray(' Free commands: character, batch, list, map, init (without --full)'));
|
|
108
|
+
console.log('');
|
|
109
|
+
process.exit(1);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
// ─── Key Format ───────────────────────────────────────────────────────
|
|
113
|
+
function isValidKeyFormat(key) {
|
|
114
|
+
// Gumroad format: XXXXXXXX-XXXXXXXX-XXXXXXXX-XXXXXXXX (hex)
|
|
115
|
+
return /^[A-F0-9]{8}-[A-F0-9]{8}-[A-F0-9]{8}-[A-F0-9]{8}$/i.test(key);
|
|
116
|
+
}
|
|
117
|
+
// ─── Online Verification ──────────────────────────────────────────────
|
|
118
|
+
async function revalidateOnline(license) {
|
|
119
|
+
try {
|
|
120
|
+
const response = await fetch('https://api.gumroad.com/v2/licenses/verify', {
|
|
121
|
+
method: 'POST',
|
|
122
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
123
|
+
body: new URLSearchParams({
|
|
124
|
+
product_id: process.env.LPC_FORGE_PRODUCT_ID ?? '-2jxD5LR4p_tXnvxMiWU7g==',
|
|
125
|
+
license_key: license.key,
|
|
126
|
+
increment_uses_count: 'false', // Don't increment on re-validation
|
|
127
|
+
}),
|
|
128
|
+
signal: AbortSignal.timeout(10000),
|
|
129
|
+
});
|
|
130
|
+
const data = (await response.json());
|
|
131
|
+
if (data.success)
|
|
132
|
+
return 'valid';
|
|
133
|
+
// Check if explicitly rejected (refunded/disabled) vs just error
|
|
134
|
+
const msg = String(data.message ?? '').toLowerCase();
|
|
135
|
+
if (msg.includes('not found') || msg.includes('disabled') || msg.includes('refund')) {
|
|
136
|
+
return 'invalid';
|
|
137
|
+
}
|
|
138
|
+
return 'network_error';
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
return 'network_error';
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
// ─── File Operations ──────────────────────────────────────────────────
|
|
145
|
+
async function loadLicense() {
|
|
146
|
+
try {
|
|
147
|
+
const raw = await readFile(LICENSE_FILE, 'utf-8');
|
|
148
|
+
const data = JSON.parse(raw);
|
|
149
|
+
if (!data.key || !data.signature || !data.machineId)
|
|
150
|
+
return null;
|
|
151
|
+
return data;
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
async function storeLicense(license) {
|
|
158
|
+
const stored = { ...license, _v: 2 };
|
|
159
|
+
await mkdir(LICENSE_DIR, { recursive: true });
|
|
160
|
+
await writeFile(LICENSE_FILE, JSON.stringify(stored, null, 2), { mode: 0o600 });
|
|
161
|
+
}
|
|
162
|
+
async function updateLastVerified(license) {
|
|
163
|
+
const updated = {
|
|
164
|
+
...license,
|
|
165
|
+
lastVerifiedAt: new Date().toISOString(),
|
|
166
|
+
signature: '', // Will be re-signed
|
|
167
|
+
};
|
|
168
|
+
updated.signature = signLicense(updated);
|
|
169
|
+
await storeLicense(updated);
|
|
170
|
+
}
|
|
171
|
+
async function removeLicense() {
|
|
172
|
+
try {
|
|
173
|
+
await unlink(LICENSE_FILE);
|
|
174
|
+
}
|
|
175
|
+
catch {
|
|
176
|
+
// Doesn't exist, that's fine
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
// ─── Public API ───────────────────────────────────────────────────────
|
|
180
|
+
/** Get stored license info (for display only) */
|
|
181
|
+
export async function getLicenseInfo() {
|
|
182
|
+
const license = await loadLicense();
|
|
183
|
+
if (!license)
|
|
184
|
+
return null;
|
|
185
|
+
const valid = verifySignature(license) && license.machineId === getMachineFingerprint();
|
|
186
|
+
return {
|
|
187
|
+
key: license.key.slice(0, 8) + '-****-****-' + license.key.slice(-8),
|
|
188
|
+
email: license.email,
|
|
189
|
+
activatedAt: license.activatedAt,
|
|
190
|
+
lastVerifiedAt: license.lastVerifiedAt,
|
|
191
|
+
valid,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
/** Activate a license key */
|
|
195
|
+
export async function activateLicense(licenseKey) {
|
|
196
|
+
// Format check
|
|
197
|
+
if (!isValidKeyFormat(licenseKey)) {
|
|
198
|
+
return {
|
|
199
|
+
success: false,
|
|
200
|
+
message: 'Invalid key format. Expected: XXXXXXXX-XXXXXXXX-XXXXXXXX-XXXXXXXX',
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
// Try online verification first
|
|
204
|
+
try {
|
|
205
|
+
const response = await fetch('https://api.gumroad.com/v2/licenses/verify', {
|
|
206
|
+
method: 'POST',
|
|
207
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
208
|
+
body: new URLSearchParams({
|
|
209
|
+
product_id: process.env.LPC_FORGE_PRODUCT_ID ?? '-2jxD5LR4p_tXnvxMiWU7g==',
|
|
210
|
+
license_key: licenseKey,
|
|
211
|
+
increment_uses_count: 'true',
|
|
212
|
+
}),
|
|
213
|
+
signal: AbortSignal.timeout(15000),
|
|
214
|
+
});
|
|
215
|
+
const data = (await response.json());
|
|
216
|
+
if (!data.success) {
|
|
217
|
+
return {
|
|
218
|
+
success: false,
|
|
219
|
+
message: data.message ?? 'License key rejected by server.',
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
const purchase = data.purchase;
|
|
223
|
+
const now = new Date().toISOString();
|
|
224
|
+
const machineId = getMachineFingerprint();
|
|
225
|
+
const licenseData = {
|
|
226
|
+
key: licenseKey,
|
|
227
|
+
email: purchase?.email ?? 'unknown',
|
|
228
|
+
product: 'lpc-forge-premium',
|
|
229
|
+
activatedAt: now,
|
|
230
|
+
lastVerifiedAt: now,
|
|
231
|
+
machineId,
|
|
232
|
+
};
|
|
233
|
+
const license = {
|
|
234
|
+
...licenseData,
|
|
235
|
+
signature: signLicense(licenseData),
|
|
236
|
+
};
|
|
237
|
+
await storeLicense(license);
|
|
238
|
+
return {
|
|
239
|
+
success: true,
|
|
240
|
+
message: 'License activated successfully!',
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
catch (err) {
|
|
244
|
+
// Offline activation — allow but mark as unverified
|
|
245
|
+
const now = new Date().toISOString();
|
|
246
|
+
const machineId = getMachineFingerprint();
|
|
247
|
+
// Set lastVerifiedAt to epoch so it triggers online check on first use
|
|
248
|
+
const licenseData = {
|
|
249
|
+
key: licenseKey,
|
|
250
|
+
email: 'offline-activation',
|
|
251
|
+
product: 'lpc-forge-premium',
|
|
252
|
+
activatedAt: now,
|
|
253
|
+
lastVerifiedAt: new Date(0).toISOString(),
|
|
254
|
+
machineId,
|
|
255
|
+
};
|
|
256
|
+
const license = {
|
|
257
|
+
...licenseData,
|
|
258
|
+
signature: signLicense(licenseData),
|
|
259
|
+
};
|
|
260
|
+
await storeLicense(license);
|
|
261
|
+
return {
|
|
262
|
+
success: true,
|
|
263
|
+
message: 'License stored (offline — will verify online on next use).',
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
/** Deactivate the stored license */
|
|
268
|
+
export async function deactivateLicense() {
|
|
269
|
+
await removeLicense();
|
|
270
|
+
}
|
|
271
|
+
//# sourceMappingURL=license.js.map
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { SeededRNG } from '../utils/rng.js';
|
|
2
|
+
import { TileType as TT } from './types.js';
|
|
3
|
+
const DEFAULT_BIRTH = 4;
|
|
4
|
+
const DEFAULT_DEATH = 3;
|
|
5
|
+
const DEFAULT_ITERATIONS = 5;
|
|
6
|
+
const DEFAULT_DENSITY = 0.45;
|
|
7
|
+
/** Generate a cave map using cellular automata */
|
|
8
|
+
export function generateCave(config) {
|
|
9
|
+
const { width, height, seed = Date.now(), birthLimit = DEFAULT_BIRTH, deathLimit = DEFAULT_DEATH, iterations = DEFAULT_ITERATIONS, initialDensity = DEFAULT_DENSITY, } = config;
|
|
10
|
+
const rng = new SeededRNG(seed);
|
|
11
|
+
// Initialize grid randomly
|
|
12
|
+
let grid = Array.from({ length: height }, () => Array.from({ length: width }, () => rng.random() < initialDensity));
|
|
13
|
+
// Force edges to be walls
|
|
14
|
+
for (let y = 0; y < height; y++) {
|
|
15
|
+
grid[y][0] = true;
|
|
16
|
+
grid[y][width - 1] = true;
|
|
17
|
+
}
|
|
18
|
+
for (let x = 0; x < width; x++) {
|
|
19
|
+
grid[0][x] = true;
|
|
20
|
+
grid[height - 1][x] = true;
|
|
21
|
+
}
|
|
22
|
+
// Run cellular automata iterations
|
|
23
|
+
for (let i = 0; i < iterations; i++) {
|
|
24
|
+
grid = step(grid, width, height, birthLimit, deathLimit);
|
|
25
|
+
}
|
|
26
|
+
// Find connected regions using flood fill
|
|
27
|
+
const regionMap = new Int32Array(width * height).fill(-1);
|
|
28
|
+
const regions = [];
|
|
29
|
+
let regionId = 0;
|
|
30
|
+
for (let y = 1; y < height - 1; y++) {
|
|
31
|
+
for (let x = 1; x < width - 1; x++) {
|
|
32
|
+
if (!grid[y][x] && regionMap[y * width + x] === -1) {
|
|
33
|
+
const cells = floodFill(grid, regionMap, x, y, width, height, regionId);
|
|
34
|
+
regions.push({ id: regionId, cells });
|
|
35
|
+
regionId++;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
// Keep the largest region, or connect the largest ones
|
|
40
|
+
if (regions.length > 1) {
|
|
41
|
+
regions.sort((a, b) => b.cells.length - a.cells.length);
|
|
42
|
+
// Connect secondary regions to the main region via tunnels
|
|
43
|
+
const main = regions[0];
|
|
44
|
+
for (let i = 1; i < Math.min(regions.length, 5); i++) {
|
|
45
|
+
connectRegions(grid, main.cells, regions[i].cells, rng);
|
|
46
|
+
}
|
|
47
|
+
// Fill remaining small isolated regions
|
|
48
|
+
for (let i = 5; i < regions.length; i++) {
|
|
49
|
+
for (const cell of regions[i].cells) {
|
|
50
|
+
grid[cell.y][cell.x] = true;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
// Convert to tile types
|
|
55
|
+
const tiles = grid.map((row) => row.map((wall) => (wall ? TT.WALL : TT.FLOOR)));
|
|
56
|
+
// Add water pools in dead-end areas
|
|
57
|
+
addWaterPools(tiles, rng, width, height);
|
|
58
|
+
// Find spawn and exit points
|
|
59
|
+
const floorCells = [];
|
|
60
|
+
for (let y = 1; y < height - 1; y++) {
|
|
61
|
+
for (let x = 1; x < width - 1; x++) {
|
|
62
|
+
if (tiles[y][x] === TT.FLOOR) {
|
|
63
|
+
floorCells.push({ x, y });
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
const spawnPoint = floorCells.length > 0 ? floorCells[0] : undefined;
|
|
68
|
+
const exitPoint = floorCells.length > 1 ? floorCells[floorCells.length - 1] : undefined;
|
|
69
|
+
// Create pseudo-rooms from connected areas
|
|
70
|
+
const rooms = regions.slice(0, 5).map((r, i) => {
|
|
71
|
+
const minX = Math.min(...r.cells.map((c) => c.x));
|
|
72
|
+
const minY = Math.min(...r.cells.map((c) => c.y));
|
|
73
|
+
const maxX = Math.max(...r.cells.map((c) => c.x));
|
|
74
|
+
const maxY = Math.max(...r.cells.map((c) => c.y));
|
|
75
|
+
return {
|
|
76
|
+
id: i,
|
|
77
|
+
x: minX,
|
|
78
|
+
y: minY,
|
|
79
|
+
width: maxX - minX + 1,
|
|
80
|
+
height: maxY - minY + 1,
|
|
81
|
+
connections: [],
|
|
82
|
+
};
|
|
83
|
+
});
|
|
84
|
+
return { width, height, tiles, rooms, seed, pois: [], spawnPoint, exitPoint };
|
|
85
|
+
}
|
|
86
|
+
function step(grid, width, height, birthLimit, deathLimit) {
|
|
87
|
+
const next = Array.from({ length: height }, () => Array(width).fill(false));
|
|
88
|
+
for (let y = 0; y < height; y++) {
|
|
89
|
+
for (let x = 0; x < width; x++) {
|
|
90
|
+
// Edges are always walls
|
|
91
|
+
if (y === 0 || y === height - 1 || x === 0 || x === width - 1) {
|
|
92
|
+
next[y][x] = true;
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
const neighbors = countNeighbors(grid, x, y, width, height);
|
|
96
|
+
if (grid[y][x]) {
|
|
97
|
+
// Alive cell: stays alive if enough neighbors
|
|
98
|
+
next[y][x] = neighbors >= deathLimit;
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
// Dead cell: becomes alive if enough neighbors
|
|
102
|
+
next[y][x] = neighbors >= birthLimit;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return next;
|
|
107
|
+
}
|
|
108
|
+
function countNeighbors(grid, x, y, width, height) {
|
|
109
|
+
let count = 0;
|
|
110
|
+
for (let dy = -1; dy <= 1; dy++) {
|
|
111
|
+
for (let dx = -1; dx <= 1; dx++) {
|
|
112
|
+
if (dx === 0 && dy === 0)
|
|
113
|
+
continue;
|
|
114
|
+
const nx = x + dx;
|
|
115
|
+
const ny = y + dy;
|
|
116
|
+
if (nx < 0 || nx >= width || ny < 0 || ny >= height) {
|
|
117
|
+
count++; // Out of bounds counts as wall
|
|
118
|
+
}
|
|
119
|
+
else if (grid[ny][nx]) {
|
|
120
|
+
count++;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return count;
|
|
125
|
+
}
|
|
126
|
+
function floodFill(grid, regionMap, startX, startY, width, height, regionId) {
|
|
127
|
+
const cells = [];
|
|
128
|
+
const stack = [[startX, startY]];
|
|
129
|
+
while (stack.length > 0) {
|
|
130
|
+
const [x, y] = stack.pop();
|
|
131
|
+
if (x < 0 || x >= width || y < 0 || y >= height)
|
|
132
|
+
continue;
|
|
133
|
+
if (grid[y][x])
|
|
134
|
+
continue;
|
|
135
|
+
if (regionMap[y * width + x] !== -1)
|
|
136
|
+
continue;
|
|
137
|
+
regionMap[y * width + x] = regionId;
|
|
138
|
+
cells.push({ x, y });
|
|
139
|
+
stack.push([x + 1, y], [x - 1, y], [x, y + 1], [x, y - 1]);
|
|
140
|
+
}
|
|
141
|
+
return cells;
|
|
142
|
+
}
|
|
143
|
+
function connectRegions(grid, a, b, rng) {
|
|
144
|
+
// Find closest pair of cells between regions
|
|
145
|
+
const cellA = rng.pick(a);
|
|
146
|
+
let bestB = b[0];
|
|
147
|
+
let bestDist = Infinity;
|
|
148
|
+
for (const cb of b) {
|
|
149
|
+
const dist = Math.abs(cb.x - cellA.x) + Math.abs(cb.y - cellA.y);
|
|
150
|
+
if (dist < bestDist) {
|
|
151
|
+
bestDist = dist;
|
|
152
|
+
bestB = cb;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
// Carve tunnel between them
|
|
156
|
+
let x = cellA.x;
|
|
157
|
+
let y = cellA.y;
|
|
158
|
+
while (x !== bestB.x || y !== bestB.y) {
|
|
159
|
+
if (x !== bestB.x) {
|
|
160
|
+
x += x < bestB.x ? 1 : -1;
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
y += y < bestB.y ? 1 : -1;
|
|
164
|
+
}
|
|
165
|
+
if (y > 0 && y < grid.length - 1 && x > 0 && x < grid[0].length - 1) {
|
|
166
|
+
grid[y][x] = false;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
function addWaterPools(tiles, rng, width, height) {
|
|
171
|
+
const poolCount = rng.randomInt(1, 3);
|
|
172
|
+
for (let i = 0; i < poolCount; i++) {
|
|
173
|
+
const cx = rng.randomInt(5, width - 5);
|
|
174
|
+
const cy = rng.randomInt(5, height - 5);
|
|
175
|
+
const radius = rng.randomInt(2, 4);
|
|
176
|
+
for (let dy = -radius; dy <= radius; dy++) {
|
|
177
|
+
for (let dx = -radius; dx <= radius; dx++) {
|
|
178
|
+
if (dx * dx + dy * dy > radius * radius)
|
|
179
|
+
continue;
|
|
180
|
+
const ty = cy + dy;
|
|
181
|
+
const tx = cx + dx;
|
|
182
|
+
if (ty > 0 && ty < height - 1 && tx > 0 && tx < width - 1) {
|
|
183
|
+
if (tiles[ty][tx] === TT.FLOOR) {
|
|
184
|
+
tiles[ty][tx] = TT.WATER;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
//# sourceMappingURL=cellular.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cellular.js","sourceRoot":"","sources":["../../src/map/cellular.ts"],"names":[],"mappings":"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"}
|