CompactAI's picture
Upload 126 files
f6b8770 verified
Raw
History Blame Contribute Delete
10 kB
// 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 };