// Seeds the system account, catalog and the pre-made official games. // Worlds are generated procedurally — no asset files anywhere. const crypto = require('crypto'); const { get, saveNow, uid } = require('./db'); const CATALOG = require('./catalog'); let nextPart = 1; function P(type, x, y, z, sx, sy, sz, color, extra) { return Object.assign( { id: 'p' + nextPart++, type, x, y, z, sx, sy, sz, color }, extra || {} ); } function baseplate(parts, size, color) { parts.push(P('block', 0, -0.5, 0, size, 1, size, color || '#3aa14f')); } // ---------- Game 1: Tower of Bother (obby) ---------- function buildObby() { const parts = []; nextPart = 1; parts.push(P('block', 0, -0.5, 0, 24, 1, 24, '#3aa14f')); parts.push(P('spawn', 0, 0.25, 8, 6, 0.5, 6, '#cfd8dc')); const palette = ['#e54b4b', '#f0a020', '#ffd23f', '#3aa14f', '#2a7de1', '#8a4fff']; let z = -2, y = 0, stage = 0; const rnd = (() => { let s = 1234567; return () => (s = (s * 16807) % 2147483647) / 2147483647; })(); for (let i = 0; i < 38; i++) { const c = palette[i % palette.length]; const w = 4 - Math.min(2, Math.floor(i / 12)); const gap = 4 + Math.floor(i / 10); z -= gap + w / 2; y += 0.8 + rnd() * 1.4; const x = Math.round((rnd() - 0.5) * 8); if (i % 7 === 3) { // moving platform hop parts.push(P('mover', x, y, z, w, 1, w, '#9b59b6', { dx: 6, dy: 0, dz: 0, period: 3.5 })); } else if (i % 7 === 5) { // kill brick flanked jump parts.push(P('block', x, y, z, w + 4, 1, w, c)); parts.push(P('kill', x, y + 0.55, z, w + 4, 0.2, w / 2, '#d40000')); parts.push(P('block', x, y + 0.55, z - w / 2 + 0.4, w + 4, 0.2, 0.8, c)); } else if (i % 11 === 9) { parts.push(P('bounce', x, y, z, w, 1, w, '#ffe14d')); y += 4; } else { parts.push(P('block', x, y, z, w, 1, w, c)); } if (i > 0 && i % 6 === 0) { stage++; parts.push(P('checkpoint', x, y + 0.75, z, 2.5, 0.5, 2.5, '#19c2ff', { stage })); } if (i % 5 === 2) { parts.push(P('coin', x, y + 2, z, 1, 1, 1, '#ffd700')); } } // summit z -= 8; y += 2; parts.push(P('block', 0, y, z, 10, 1, 10, '#ffffff')); parts.push(P('goal', 0, y + 1, z, 4, 2, 4, '#19ff8c')); parts.push(P('deco', 0, y + 6, z, 1, 8, 1, '#e54b4b')); return parts; } // ---------- Game 2: Glimmer Rush (coin collect) ---------- function buildCollect() { const parts = []; nextPart = 1; baseplate(parts, 80, '#3d9650'); parts.push(P('spawn', 0, 0.25, 0, 8, 0.5, 8, '#cfd8dc')); const rnd = (() => { let s = 424242; return () => (s = (s * 16807) % 2147483647) / 2147483647; })(); // scattered terrain blocks + ramps of stacked slabs for (let i = 0; i < 26; i++) { const x = Math.round((rnd() - 0.5) * 64); const z = Math.round((rnd() - 0.5) * 64); if (Math.abs(x) < 8 && Math.abs(z) < 8) continue; const h = 1 + Math.floor(rnd() * 4); const w = 3 + Math.floor(rnd() * 6); const cols = ['#7a5230', '#8d6e63', '#6d4c41', '#5d8a3c']; parts.push(P('block', x, h / 2, z, w, h, w, cols[i % 4])); if (rnd() > 0.4) parts.push(P('coin', x, h + 1.2, z, 1, 1, 1, '#ffd700')); } // floating coin rings for (let r = 0; r < 3; r++) { const cy = 6 + r * 5; for (let a = 0; a < 8; a++) { const ang = (a / 8) * Math.PI * 2; parts.push(P('coin', Math.cos(ang) * (14 + r * 8), cy, Math.sin(ang) * (14 + r * 8), 1, 1, 1, '#ffd700')); } parts.push(P('mover', 14 + r * 8, cy - 1.5, 0, 4, 1, 4, '#9b59b6', { dx: 0, dy: 4, dz: 0, period: 4 + r })); } // bounce pads to reach rings parts.push(P('bounce', 10, 0.55, 10, 4, 1, 4, '#ffe14d')); parts.push(P('bounce', -10, 0.55, -10, 4, 1, 4, '#ffe14d')); parts.push(P('speed', 0, 0.05, 20, 6, 0.1, 12, '#19c2ff')); // central lake parts.push(P('water', 28, 0.5, -28, 20, 3, 20, '#2a7de1')); for (let a = 0; a < 5; a++) parts.push(P('coin', 24 + a * 2, 1.5, -24 - a * 2, 1, 1, 1, '#ffd700')); return parts; } // ---------- Game 3: Zap Tag (tag arena) ---------- function buildTag() { const parts = []; nextPart = 1; baseplate(parts, 56, '#4a6b8a'); parts.push(P('spawn', 0, 0.25, 0, 10, 0.5, 10, '#cfd8dc')); // arena walls const W = 56; parts.push(P('block', 0, 2, -W / 2, W, 4, 1, '#37474f')); parts.push(P('block', 0, 2, W / 2, W, 4, 1, '#37474f')); parts.push(P('block', -W / 2, 2, 0, 1, 4, W, '#37474f')); parts.push(P('block', W / 2, 2, 0, 1, 4, W, '#37474f')); // cover pillars + catwalks const spots = [[-16, -16], [16, -16], [-16, 16], [16, 16], [0, -20], [0, 20], [-20, 0], [20, 0]]; for (const [x, z] of spots) { parts.push(P('block', x, 2.5, z, 4, 5, 4, '#ff8f3c')); } parts.push(P('block', 0, 5.5, -16, 36, 1, 4, '#cfd8dc')); parts.push(P('block', 0, 5.5, 16, 36, 1, 4, '#cfd8dc')); parts.push(P('block', -16, 5.5, 0, 4, 1, 28, '#cfd8dc')); parts.push(P('block', 16, 5.5, 0, 4, 1, 28, '#cfd8dc')); parts.push(P('bounce', 0, 0.55, -8, 3, 1, 3, '#ffe14d')); parts.push(P('bounce', 0, 0.55, 8, 3, 1, 3, '#ffe14d')); parts.push(P('speed', -24, 0.05, 0, 4, 0.1, 40, '#19c2ff')); parts.push(P('speed', 24, 0.05, 0, 4, 0.1, 40, '#19c2ff')); // teleporter pair for escapes parts.push(P('tele', -24, 0.3, -24, 3, 0.6, 3, '#8a4fff', { channel: 1 })); parts.push(P('tele', 24, 0.3, 24, 3, 0.6, 3, '#8a4fff', { channel: 1 })); return parts; } // ---------- Game 4: Blockworks Plaza (social sandbox / showcase) ---------- function buildPlaza() { const parts = []; nextPart = 1; baseplate(parts, 72, '#9e9e9e'); parts.push(P('spawn', 0, 0.25, 24, 8, 0.5, 8, '#cfd8dc')); // fountain parts.push(P('block', 0, 0.5, 0, 12, 1, 12, '#b0bec5')); parts.push(P('water', 0, 1.4, 0, 10, 0.8, 10, '#2a7de1')); parts.push(P('deco', 0, 3, 0, 2, 4, 2, '#b0bec5')); parts.push(P('mover', 0, 6, 0, 1.5, 1.5, 1.5, '#19c2ff', { dx: 0, dy: 2, dz: 0, period: 2 })); // four little shops around plaza const shops = [ [-24, -24, '#e54b4b'], [24, -24, '#2a7de1'], [-24, 24, '#f0a020'], [24, 24, '#8a4fff'], ]; for (const [x, z, c] of shops) { parts.push(P('block', x, 2.5, z, 10, 5, 10, c)); // walls parts.push(P('block', x, 5.4, z, 12, 0.8, 12, '#37474f')); // roof parts.push(P('block', x, 1, z + 5.2, 3, 2, 0.4, '#00000000' === c ? c : '#262626')); // doorway mark } // park strip with trees (trunk + leaf cubes) for (let i = 0; i < 6; i++) { const x = -30 + i * 12, z = -8; parts.push(P('deco', x, 1.5, z, 1, 3, 1, '#6d4c41')); parts.push(P('deco', x, 4, z, 3.5, 3, 3.5, '#2e7d32')); } // dance floor of colored speed pads for (let i = 0; i < 4; i++) for (let j = 0; j < 4; j++) parts.push(P('block', -6 + i * 4, 0.05, -26 + j * 4, 4, 0.1, 4, ['#e54b4b', '#ffd23f', '#19c2ff', '#19ff8c'][(i + j) % 4])); // observation tower with teleporter up parts.push(P('block', 30, 7, 0, 6, 14, 6, '#78909c')); parts.push(P('block', 30, 14.5, 0, 10, 1, 10, '#cfd8dc')); parts.push(P('tele', 30, 0.3, 6, 3, 0.6, 3, '#8a4fff', { channel: 1 })); parts.push(P('tele', 30, 15.3, 0, 3, 0.6, 3, '#8a4fff', { channel: 1 })); parts.push(P('coin', 30, 16.5, 0, 1, 1, 1, '#ffd700')); return parts; } function makeGame(db, owner, def) { const id = uid(10); db.games[id] = { id, name: def.name, desc: def.desc, genre: def.genre, ownerId: owner.id, collaborators: [], public: true, official: true, created: Date.now(), updated: Date.now(), visits: 0, likes: [], favorites: [], config: Object.assign( { maxPlayers: 12, mode: 'sandbox', gravity: 50, walkSpeed: 12, jumpPower: 20, respawnTime: 2 }, def.config ), world: { parts: def.parts }, }; return db.games[id]; } function seed() { const db = get(); db.catalog = CATALOG; if (!Object.values(db.users).some((u) => u.system)) { const id = uid(10); db.users[id] = { id, username: 'Glint', system: true, // random unguessable password — nobody logs in as the system user passhash: crypto.randomBytes(32).toString('hex'), salt: crypto.randomBytes(16).toString('hex'), created: Date.now(), glimmers: 0, avatar: { bodyColors: { head: '#ffd23f', torso: '#19c2ff', leftArm: '#ffd23f', rightArm: '#ffd23f', leftLeg: '#37474f', rightLeg: '#37474f' }, hat: 'hat_crown', face: 'face_cool', }, inventory: CATALOG.map((c) => c.id), friends: [], friendRequests: [], stats: { visitsReceived: 0, joins: 0 }, }; } const sys = Object.values(db.users).find((u) => u.system); if (!Object.values(db.games).some((g) => g.official)) { makeGame(db, sys, { name: 'Tower of Bother', desc: 'The classic climb. 38 platforms of pure spite. Checkpoints are your friends; the red bricks are not. First to the green summit pad wins glory.', genre: 'Obby', config: { mode: 'obby', maxPlayers: 16 }, parts: buildObby(), }); makeGame(db, sys, { name: 'Glimmer Rush', desc: 'Coins everywhere — on hills, in rings, over the lake. Grab the most before everyone else does. Coins respawn, greed is forever.', genre: 'Collecting', config: { mode: 'collect', maxPlayers: 12 }, parts: buildCollect(), }); makeGame(db, sys, { name: 'Zap Tag', desc: 'One player is Zapped. Touch someone to pass it on. Teleporters, speed lanes and bounce pads keep the chase spicy. Don\'t be holding the zap.', genre: 'Tag', config: { mode: 'tag', maxPlayers: 10, walkSpeed: 14 }, parts: buildTag(), }); makeGame(db, sys, { name: 'Blockworks Plaza', desc: 'Hang out, dance on the light floor, ride the fountain, take the teleporter up the tower. A chill social plaza — bring friends.', genre: 'Social', config: { mode: 'sandbox', maxPlayers: 20 }, parts: buildPlaza(), }); } saveNow(); } module.exports = { seed };