feat: add ore and blacksmith
This commit is contained in:
@@ -6,9 +6,12 @@ import type { IncomingMessage, ServerResponse } from 'node:http';
|
||||
import sirv from 'sirv';
|
||||
import {
|
||||
PROTOCOL_VERSION,
|
||||
PROPS,
|
||||
SKILL_TITLES,
|
||||
WORLD,
|
||||
ZONES,
|
||||
colorForName,
|
||||
levelForTotalXp,
|
||||
type DeltaMsg,
|
||||
type GameEvent,
|
||||
type SnapshotMsg,
|
||||
@@ -80,7 +83,13 @@ const world: WorldSource = {
|
||||
v: PROTOCOL_VERSION,
|
||||
tickMs: cfg.tickMs,
|
||||
serverNow: Date.now(),
|
||||
world: { w: WORLD.w, h: WORLD.h, spawn: { ...WORLD.spawn }, zones: ZONES },
|
||||
world: {
|
||||
w: WORLD.w,
|
||||
h: WORLD.h,
|
||||
spawn: { ...WORLD.spawn },
|
||||
zones: ZONES,
|
||||
props: PROPS.map((p) => ({ ...p })),
|
||||
},
|
||||
};
|
||||
},
|
||||
snapshot(): SnapshotMsg {
|
||||
@@ -90,7 +99,7 @@ const world: WorldSource = {
|
||||
serverNow: Date.now(),
|
||||
camera: camera.state,
|
||||
avatars: [...sim.avatars.values()].map((av) => sim.serializeAvatar(av)),
|
||||
nodes: sim.serializeTrees(),
|
||||
nodes: sim.serializeNodes(),
|
||||
stats: sim.stats(),
|
||||
};
|
||||
},
|
||||
@@ -102,7 +111,7 @@ const gateway = new WsGateway(world);
|
||||
|
||||
function buildDelta(events: GameEvent[], camChanged: boolean): DeltaMsg | null {
|
||||
const avatars = sim.takeDirtyAvatars();
|
||||
const nodes = sim.takeDirtyTrees();
|
||||
const nodes = sim.takeDirtyNodes();
|
||||
const stats = sim.takeStatsIfDirty();
|
||||
if (avatars.length === 0 && nodes.length === 0 && !stats && events.length === 0 && !camChanged) {
|
||||
return null;
|
||||
@@ -119,13 +128,13 @@ function buildDelta(events: GameEvent[], camChanged: boolean): DeltaMsg | null {
|
||||
setInterval(() => {
|
||||
const now = Date.now();
|
||||
const events = sim.tick(now);
|
||||
const camChanged = camera.tick(now);
|
||||
const camChanged = camera.tick(now, sim.zoneActivity());
|
||||
|
||||
for (const rec of sim.takeDirtyViewers()) db.saveViewer(cfg.channelId, rec);
|
||||
|
||||
for (const ev of events) {
|
||||
if (ev.k === 'levelup' && cfg.chatAnnounceLevelUp) {
|
||||
announce(`🌲 ${ev.name} вырос(ла) до уровня ${ev.level} в рубке леса!`);
|
||||
announce(`⭐ ${ev.name} вырос(ла) до ${ev.level}-го уровня: ${SKILL_TITLES[ev.skill] ?? ev.skill}!`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,6 +210,58 @@ function handleHttp(req: IncomingMessage, res: ServerResponse): void {
|
||||
return;
|
||||
}
|
||||
|
||||
if (cfg.devHttp && p === '/dev/give' && req.method === 'POST') {
|
||||
readBody(req, (body) => {
|
||||
try {
|
||||
const parsed = JSON.parse(body || '{}') as { name?: string; item?: string; qty?: number };
|
||||
const name = (parsed.name ?? '').trim();
|
||||
const item = (parsed.item ?? '').trim();
|
||||
const qty = Math.max(1, Math.min(999, Number(parsed.qty ?? 1)));
|
||||
if (!name || !item) {
|
||||
json(res, 400, { error: 'нужны name и item' });
|
||||
return;
|
||||
}
|
||||
const ok = sim.giveItem(`dev:${name}`, item, qty);
|
||||
json(res, ok ? 200 : 404, ok ? { ok: true } : { error: 'зритель не в мире' });
|
||||
} catch (e) {
|
||||
json(res, 400, { error: String(e) });
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (cfg.devHttp && p === '/dev/tp' && req.method === 'POST') {
|
||||
readBody(req, (body) => {
|
||||
try {
|
||||
const parsed = JSON.parse(body || '{}') as { name?: string; x?: number; y?: number };
|
||||
const name = (parsed.name ?? '').trim();
|
||||
if (!name || !Number.isFinite(parsed.x) || !Number.isFinite(parsed.y)) {
|
||||
json(res, 400, { error: 'нужны name, x, y' });
|
||||
return;
|
||||
}
|
||||
const ok = sim.teleport(`dev:${name}`, Number(parsed.x), Number(parsed.y));
|
||||
json(res, ok ? 200 : 404, ok ? { ok: true } : { error: 'зритель не в мире' });
|
||||
} catch (e) {
|
||||
json(res, 400, { error: String(e) });
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (p === '/api/inventory' && req.method === 'GET') {
|
||||
const name = (url.searchParams.get('name') ?? '').trim();
|
||||
const found = name ? sim.lookupViewer(name) : undefined;
|
||||
if (!found) {
|
||||
json(res, 200, { found: false });
|
||||
return;
|
||||
}
|
||||
const skills = Object.fromEntries(
|
||||
Object.entries(found.skills).map(([id, s]) => [id, { xp: s.xp, level: s.level ?? levelForTotalXp(s.xp) }]),
|
||||
);
|
||||
json(res, 200, { found: true, name: found.name, color: found.color, skills, items: found.items });
|
||||
return;
|
||||
}
|
||||
|
||||
for (const m of staticMounts) {
|
||||
if (!m.middleware) continue;
|
||||
const base = m.prefix.slice(0, -1);
|
||||
|
||||
Reference in New Issue
Block a user