210 lines
8.4 KiB
JavaScript
210 lines
8.4 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* Publish sample games under the "Arcade Master" account
|
|
* Run with: node scripts/publish-sample-games.js
|
|
*/
|
|
|
|
require('dotenv').config();
|
|
const { Pool } = require('pg');
|
|
const bcrypt = require('bcrypt');
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
const pool = new Pool({
|
|
host: process.env.DB_HOST,
|
|
port: process.env.DB_PORT,
|
|
database: process.env.DB_NAME,
|
|
user: process.env.DB_USER,
|
|
password: process.env.DB_PASSWORD,
|
|
});
|
|
|
|
const ARCADE_MASTER = {
|
|
username: 'ArcadeMaster',
|
|
email: 'arcademaster@gamercomp.com',
|
|
password: 'ArcadeMaster2024!',
|
|
displayName: 'Arcade Master'
|
|
};
|
|
|
|
const GAMES_DIR = path.join(__dirname, '../../games/samples');
|
|
|
|
// Game metadata (title, description, prompt for each game)
|
|
const GAME_METADATA = {
|
|
'brick-breaker.html': {
|
|
title: 'Brick Breaker',
|
|
description: 'Classic paddle and ball game! Move your paddle to bounce the ball and break all the bricks. Easy to learn, fun to play!',
|
|
prompt: 'Create a classic brick breaker game with a wide paddle, slow ball, and 5 lives. Make it easy for kids to play.'
|
|
},
|
|
'pong.html': {
|
|
title: 'Pong',
|
|
description: 'The classic 2-player game! Play against the computer and try to score 7 points first. The computer makes mistakes so you can win!',
|
|
prompt: 'Create a single-player Pong game vs AI. The AI should make mistakes sometimes so kids can win.'
|
|
},
|
|
'dino-runner.html': {
|
|
title: 'Dino Runner',
|
|
description: 'Run as far as you can! Tap or press space to jump over cacti. The game starts slow so you can learn the timing.',
|
|
prompt: 'Create a Chrome Dino style endless runner. Start very slow with obstacles far apart.'
|
|
},
|
|
'asteroids.html': {
|
|
title: 'Asteroids',
|
|
description: 'Classic space shooter! Rotate your ship and blast the asteroids. You have shields that protect you when you get hit.',
|
|
prompt: 'Create an Asteroids game with a large ship, slow asteroids, and invincibility after being hit.'
|
|
},
|
|
'whack-a-mole.html': {
|
|
title: 'Whack-a-Mole',
|
|
description: 'Tap the moles when they pop up! They stay visible for a long time so you can catch them. 60 seconds to get the high score!',
|
|
prompt: 'Create a whack-a-mole game with a 3x3 grid. Moles should stay up for 2+ seconds.'
|
|
},
|
|
'block-drop.html': {
|
|
title: 'Block Drop',
|
|
description: 'Stack falling blocks to complete lines! Pieces fall slowly so you have time to think. A ghost piece shows where blocks will land.',
|
|
prompt: 'Create a Tetris-style game with very slow falling pieces and helpful ghost piece preview.'
|
|
},
|
|
'twenty-forty-eight.html': {
|
|
title: '2048',
|
|
description: 'Swipe to combine numbers! 2+2=4, 4+4=8, and so on. No time limit - take as long as you need to plan your moves.',
|
|
prompt: 'Create the 2048 puzzle game with no time pressure and clear directional arrows.'
|
|
},
|
|
'memory-match.html': {
|
|
title: 'Memory Match',
|
|
description: 'Find all the matching pairs! Flip cards to reveal pictures and remember where they are. Only 6 pairs to start.',
|
|
prompt: 'Create a memory matching game with a small 3x4 grid (6 pairs) and cards that stay flipped for 1.5 seconds.'
|
|
},
|
|
'connect-four.html': {
|
|
title: 'Connect Four',
|
|
description: 'Drop your pieces to connect 4 in a row! Play against a friendly computer that lets you learn the game.',
|
|
prompt: 'Create Connect Four vs AI. The AI should play randomly most of the time so kids can win.'
|
|
},
|
|
'lights-out.html': {
|
|
title: 'Lights Out',
|
|
description: 'Turn off all the lights to win! Clicking a light toggles it and its neighbors. Includes a helpful tutorial!',
|
|
prompt: 'Create Lights Out puzzle with an easy 3x3 grid, interactive tutorial, and hover highlights.'
|
|
},
|
|
'geometry-runner.html': {
|
|
title: 'Geometry Runner',
|
|
description: 'Jump over obstacles in this rhythm-style runner! Starts very slow with obstacles far apart so you can learn the timing.',
|
|
prompt: 'Create a Geometry Dash style runner that starts very slow with generous jump timing.'
|
|
},
|
|
'lunar-lander.html': {
|
|
title: 'Lunar Lander',
|
|
description: 'Land your spaceship safely on the moon! You have lots of fuel and the landing pad is big. Low gravity makes it easier.',
|
|
prompt: 'Create a Lunar Lander with generous fuel, low gravity, and a large landing pad.'
|
|
},
|
|
'fruit-slicer.html': {
|
|
title: 'Fruit Slicer',
|
|
description: 'Swipe through the fruit to slice it! Fruit moves slowly and stays on screen longer. Watch out for bombs!',
|
|
prompt: 'Create a Fruit Ninja style game with big slow fruit and rare bombs at the start.'
|
|
},
|
|
'maze-chomper.html': {
|
|
title: 'Maze Chomper',
|
|
description: 'Eat all the dots while avoiding ghosts! You move much faster than the ghosts, and power pellets last a long time.',
|
|
prompt: 'Create a Pac-Man style game where the player is 3x faster than ghosts and power-ups last 10 seconds.'
|
|
},
|
|
'platformer.html': {
|
|
title: 'Platformer',
|
|
description: 'Jump and run to reach the flag! 5 lives, slow enemies, and big platforms make this adventure fun for everyone.',
|
|
prompt: 'Create a simple platformer with 5 lives, slow enemies, and generous jump mechanics.'
|
|
},
|
|
'bridge-builder.html': {
|
|
title: 'Bridge Builder',
|
|
description: 'Build a bridge to get the car across! Strong beams and a light car make building easy. Includes a helpful tutorial.',
|
|
prompt: 'Create a bridge building physics game with strong materials, light car, and visual tutorial.'
|
|
}
|
|
};
|
|
|
|
async function getOrCreateArcadeMaster() {
|
|
// Check if user exists
|
|
let result = await pool.query(
|
|
'SELECT * FROM users WHERE username = $1',
|
|
[ARCADE_MASTER.username]
|
|
);
|
|
|
|
if (result.rows.length > 0) {
|
|
console.log(`Found existing Arcade Master account (ID: ${result.rows[0].id})`);
|
|
return result.rows[0];
|
|
}
|
|
|
|
// Create new user
|
|
const passwordHash = await bcrypt.hash(ARCADE_MASTER.password, 10);
|
|
result = await pool.query(
|
|
`INSERT INTO users (username, email, password_hash, display_name, tokens_balance, role)
|
|
VALUES ($1, $2, $3, $4, 9999, 'developer')
|
|
RETURNING *`,
|
|
[ARCADE_MASTER.username, ARCADE_MASTER.email, passwordHash, ARCADE_MASTER.displayName]
|
|
);
|
|
|
|
console.log(`Created Arcade Master account (ID: ${result.rows[0].id})`);
|
|
return result.rows[0];
|
|
}
|
|
|
|
async function publishGame(creatorId, filename, gameCode) {
|
|
const metadata = GAME_METADATA[filename];
|
|
if (!metadata) {
|
|
console.log(` Skipping ${filename} - no metadata defined`);
|
|
return null;
|
|
}
|
|
|
|
// Check if game already exists
|
|
const existing = await pool.query(
|
|
'SELECT id FROM games WHERE title = $1 AND creator_id = $2',
|
|
[metadata.title, creatorId]
|
|
);
|
|
|
|
if (existing.rows.length > 0) {
|
|
// Update existing game
|
|
await pool.query(
|
|
`UPDATE games SET game_code = $1, description = $2, prompt = $3 WHERE id = $4`,
|
|
[gameCode, metadata.description, metadata.prompt, existing.rows[0].id]
|
|
);
|
|
console.log(` Updated: ${metadata.title} (ID: ${existing.rows[0].id})`);
|
|
return existing.rows[0].id;
|
|
}
|
|
|
|
// Insert new game as published
|
|
const result = await pool.query(
|
|
`INSERT INTO games (creator_id, title, description, game_code, prompt, status, published_at)
|
|
VALUES ($1, $2, $3, $4, $5, 'published', NOW())
|
|
RETURNING id`,
|
|
[creatorId, metadata.title, metadata.description, gameCode, metadata.prompt]
|
|
);
|
|
|
|
console.log(` Published: ${metadata.title} (ID: ${result.rows[0].id})`);
|
|
return result.rows[0].id;
|
|
}
|
|
|
|
async function main() {
|
|
try {
|
|
console.log('=== Publishing Sample Games ===\n');
|
|
|
|
// Get or create Arcade Master account
|
|
const arcadeMaster = await getOrCreateArcadeMaster();
|
|
console.log('');
|
|
|
|
// Read all game files
|
|
const files = fs.readdirSync(GAMES_DIR).filter(f => f.endsWith('.html'));
|
|
console.log(`Found ${files.length} game files in ${GAMES_DIR}\n`);
|
|
|
|
let published = 0;
|
|
for (const filename of files) {
|
|
const filepath = path.join(GAMES_DIR, filename);
|
|
const gameCode = fs.readFileSync(filepath, 'utf8');
|
|
|
|
const gameId = await publishGame(arcadeMaster.id, filename, gameCode);
|
|
if (gameId) published++;
|
|
}
|
|
|
|
console.log(`\n=== Complete! ===`);
|
|
console.log(`Published ${published} games under ${ARCADE_MASTER.username}`);
|
|
console.log(`\nArcade Master credentials:`);
|
|
console.log(` Username: ${ARCADE_MASTER.username}`);
|
|
console.log(` Password: ${ARCADE_MASTER.password}`);
|
|
|
|
} catch (error) {
|
|
console.error('Error:', error);
|
|
process.exit(1);
|
|
} finally {
|
|
await pool.end();
|
|
}
|
|
}
|
|
|
|
main();
|