Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions webapp/src/routes/lobby.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Router } from 'express';
import { z } from 'zod';
import { lobbyStore } from '../services/lobby-store.js';
import { serializeSession } from '../services/session-serializer.js';
import { difficultyDefinitions, difficultyKeys } from '../services/difficulties.js';
import { difficultyKeys, getDifficultyDefinitions } from '../services/difficulties.js';

export const lobbyRouter = Router();

Expand All @@ -18,7 +18,8 @@ lobbyRouter.get('/lobby', (_req, res) => {
});

lobbyRouter.get('/difficulties', (_req, res) => {
res.json({ difficulties: difficultyKeys.map((key) => difficultyDefinitions[key]) });
const definitions = getDifficultyDefinitions();
res.json({ difficulties: difficultyKeys.map((key) => definitions[key]) });
});

lobbyRouter.post('/lobby/messages', (req, res, next) => {
Expand Down
2 changes: 2 additions & 0 deletions webapp/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ import { createApp } from './app.js';
import { getEnv } from './config/env.js';
import { ensureDatabaseSetup } from './db/setup.js';
import { logger } from './logger.js';
import { loadDifficultyDefinitionsFromDatabase } from './services/difficulties.js';

const env = getEnv();
const app = createApp();

const startServer = async () => {
await ensureDatabaseSetup();
await loadDifficultyDefinitionsFromDatabase();
app.listen(env.port, () => {
logger.info({ port: env.port, nodeEnv: env.nodeEnv }, 'Web app server listening');
});
Expand Down
66 changes: 65 additions & 1 deletion webapp/src/services/difficulties.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { RowDataPacket } from 'mysql2/promise';
import { pool } from '../db/pool.js';
import { logger } from '../logger.js';
import { Difficulty, DifficultyDefinition, difficultyKeys } from './types.js';

export const difficultyOrder: Difficulty[] = [...difficultyKeys];

export const difficultyDefinitions: Record<Difficulty, DifficultyDefinition> = {
const defaultDifficultyDefinitions: Record<Difficulty, DifficultyDefinition> = {
easy: {
key: 'easy',
name: 'Easy',
Expand Down Expand Up @@ -61,6 +64,67 @@ export const difficultyDefinitions: Record<Difficulty, DifficultyDefinition> = {
},
};

let difficultyDefinitions: Record<Difficulty, DifficultyDefinition> = { ...defaultDifficultyDefinitions };

type DifficultyRow = RowDataPacket & {
name: string;
width: number;
height: number;
min_floors: number;
max_floors: number;
min_rooms: number;
enemy_chance: number;
npc_count: number;
basement_chance: number;
basement_min_rooms: number;
basement_max_rooms: number;
};

const normalizeDifficultyName = (name: string): Difficulty | null => {
const normalized = name.trim().toLowerCase().replace(/\s+/g, '_');
return difficultyKeys.includes(normalized as Difficulty) ? (normalized as Difficulty) : null;
};

export const loadDifficultyDefinitionsFromDatabase = async (): Promise<Record<Difficulty, DifficultyDefinition>> => {
const [rows] = await pool.query<DifficultyRow[]>(
`SELECT name, width, height, min_floors, max_floors, min_rooms, enemy_chance, npc_count, basement_chance, basement_min_rooms, basement_max_rooms
FROM difficulties
ORDER BY difficulty_id ASC`,
);

const updated: Record<Difficulty, DifficultyDefinition> = { ...defaultDifficultyDefinitions };

rows.forEach((row) => {
const key = normalizeDifficultyName(row.name);
if (!key) {
logger.warn({ name: row.name }, 'Skipping unknown difficulty name from database');
return;
}

updated[key] = {
key,
name: row.name,
width: row.width,
height: row.height,
minFloors: row.min_floors,
maxFloors: row.max_floors,
minRooms: row.min_rooms,
enemyChance: row.enemy_chance,
npcCount: row.npc_count,
basementChance: row.basement_chance,
basementMinRooms: row.basement_min_rooms,
basementMaxRooms: row.basement_max_rooms,
};
});

difficultyDefinitions = updated;
logger.info({ count: Object.keys(updated).length }, 'Loaded difficulty definitions from database');

return difficultyDefinitions;
};

export const getDifficultyDefinitions = (): Record<Difficulty, DifficultyDefinition> => difficultyDefinitions;

export const getDifficultyDefinition = (key: Difficulty): DifficultyDefinition => difficultyDefinitions[key];

export { difficultyKeys };
4 changes: 2 additions & 2 deletions webapp/src/services/game-engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import crypto from 'crypto';
import { HttpError } from '../errors/http-error.js';
import { createSeededRng, pickFrom, randomInt } from './random.js';
import { baseRoomTypes, floorRoomRules } from './floor-room-rules.js';
import { difficultyDefinitions, getDifficultyDefinition } from './difficulties.js';
import { getDifficultyDefinition } from './difficulties.js';
import {
CreateSessionOptions,
Difficulty,
Expand Down Expand Up @@ -105,7 +105,7 @@ const pickFloorRules = (difficulty: Difficulty, floorIndex: number) => {

const sprinkleRooms = (floor: FloorState, difficulty: Difficulty) => {
const rng = createSeededRng(`${floor.start.x},${floor.start.y}:${difficulty}:${floor.index}`);
const definition = difficultyDefinitions[difficulty];
const definition = getDifficultyDefinition(difficulty);
const rules = pickFloorRules(difficulty, floor.index);
const occupied = new Set<string>([`${floor.start.x},${floor.start.y}`, `${floor.stairs.x},${floor.stairs.y}`]);
const occupy = (pos: Position) => occupied.add(`${pos.x},${pos.y}`);
Expand Down
Loading