LiteChat Model Race Result (RUNJS)
```runjs
// Clear any previous content
litechat.target.replaceChildren();
async function createShooterGame() {
try {
litechat.utils.log('info', '🚀 Starting 2D Scroll Shooter with 3D Effects...');
// Load Three.js
const modules = await litechat.utils.loadModules([
{
url: 'https://unpkg.com/three@0.170.0/build/three.module.js',
name: 'THREE',
globalKey: 'THREE',
importMap: {
"three": "https://unpkg.com/three@0.170.0/build/three.module.js",
"three/addons/": "https://unpkg.com/three@0.170.0/examples/jsm/"
}
}
]);
const { THREE } = modules;
// Create container
const container = document.createElement('div');
container.className = 'p-4 bg-gradient-to-b from-purple-900 to-indigo-900 rounded-lg relative overflow-hidden';
container.style.width = '800px';
container.style.height = '600px';
// Create canvas
const canvas = document.createElement('canvas');
canvas.className = 'border border-purple-500 rounded';
container.appendChild(canvas);
// Add HUD overlay
const hud = document.createElement('div');
hud.className = 'absolute top-4 left-4 text-white font-bold text-lg pointer-events-none';
hud.innerHTML = `
<div id="lives">Lives: 3</div>
<div id="score">Score: 0</div>
<div id="powerups">Powerups: Fire Rate: 0 | Bouncy: 0 | Guns: 1</div>
`;
container.appendChild(hud);
// Add instructions
const info = document.createElement('div');
info.className = 'absolute bottom-4 left-4 text-white text-sm pointer-events-none';
info.textContent = 'Arrows/WASD: Move | Space: Shoot | Focus canvas to play';
container.appendChild(info);
litechat.target.appendChild(container);
// Three.js setup
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x220044); // Deep purple for comfy feel
const camera = new THREE.PerspectiveCamera(75, 800/600, 0.1, 1000);
camera.position.set(0, 0, 50); // Pulled back for 3D effect
camera.lookAt(0, 0, 0);
const renderer = new THREE.WebGLRenderer({ canvas, antialias: true, alpha: true });
renderer.setSize(800, 600);
// Add soft lighting for poppy colors
const ambientLight = new THREE.AmbientLight(0xffffff, 0.8);
scene.add(ambientLight);
const pointLight = new THREE.PointLight(0xffffff, 1, 100);
pointLight.position.set(0, 0, 60);
scene.add(pointLight);
// Particle system for background stars (comfy, poppy effect)
const starGeometry = new THREE.BufferGeometry();
const starVertices = [];
for (let i = 0; i < 1000; i++) {
starVertices.push((Math.random() - 0.5) * 200, (Math.random() - 0.5) * 200, (Math.random() - 0.5) * 50 - 50);
}
starGeometry.setAttribute('position', new THREE.Float32BufferAttribute(starVertices, 3));
const starMaterial = new THREE.PointsMaterial({ color: 0xffffff, size: 0.5, transparent: true });
const stars = new THREE.Points(starGeometry, starMaterial);
scene.add(stars);
// Game variables
let lives = 3;
let score = 0;
let fireRateBonus = 0; // +10% faster per bonus
let bouncyBonus = 0; // bounces up to this number
let gunLevel = 1; // 1: single, 2: two alt, 3: three spread
let baseFireCooldown = 300; // ms
let lastShot = 0;
let enemies = [];
let bullets = [];
let bonuses = [];
let particles = [];
let difficulty = 1; // increases over time
let lastDifficultyTime = Date.now();
let isShooting = false;
let keys = {};
// Player (simple colorful sphere with 3D rotation)
const playerGeometry = new THREE.SphereGeometry(1, 32, 32);
const playerMaterial = new THREE.MeshPhongMaterial({ color: 0x00ff00, shininess: 100 });
const player = new THREE.Mesh(playerGeometry, playerMaterial);
player.position.set(0, -25, 0); // Bottom of screen
scene.add(player);
// Input handling
const onKeyDown = (e) => {
keys[e.code] = true;
if (e.code === 'Space') isShooting = true;
};
const onKeyUp = (e) => {
keys[e.code] = false;
if (e.code === 'Space') isShooting = false;
};
// Focus management to not lock LiteChat
canvas.tabIndex = 0; // Make focusable
canvas.addEventListener('focus', () => {
document.addEventListener('keydown', onKeyDown);
document.addEventListener('keyup', onKeyUp);
});
canvas.addEventListener('blur', () => {
document.removeEventListener('keydown', onKeyDown);
document.removeEventListener('keyup', onKeyUp);
keys = {};
isShooting = false;
});
// Helper functions
function createEnemy() {
const size = 1 + Math.random() * 1;
const geometry = new THREE.DodecahedronGeometry(size);
const material = new THREE.MeshPhongMaterial({ color: Math.random() * 0xffffff, shininess: 50 });
const enemy = new THREE.Mesh(geometry, material);
enemy.position.set((Math.random() - 0.5) * 60, 30 + Math.random() * 10, (Math.random() - 0.5) * 10); // 3D depth
enemy.speed = 0.1 + difficulty * 0.05;
scene.add(enemy);
enemies.push(enemy);
}
function createBullet(pos, dir, bounces = 0) {
const geometry = new THREE.SphereGeometry(0.5, 8, 8);
const material = new THREE.MeshPhongMaterial({ color: 0xff00ff, emissive: 0xff00ff });
const bullet = new THREE.Mesh(geometry, material);
bullet.position.copy(pos);
bullet.velocity = dir; // Vector3
bullet.bouncesLeft = bounces;
scene.add(bullet);
bullets.push(bullet);
}
function createBonus(pos, type) {
const geometry = new THREE.OctahedronGeometry(1);
let color;
switch (type) {
case 'fireRate': color = 0xff0000; break;
case 'bouncy': color = 0x00ff00; break;
case 'guns': color = 0x0000ff; break;
case 'life': color = 0xffff00; break;
}
const material = new THREE.MeshPhongMaterial({ color, shininess: 100 });
const bonus = new THREE.Mesh(geometry, material);
bonus.position.copy(pos);
bonus.type = type;
bonus.speed = 0.1;
scene.add(bonus);
bonuses.push(bonus);
}
function createParticles(pos, count, color) {
for (let i = 0; i < count; i++) {
const geometry = new THREE.SphereGeometry(0.2, 4, 4);
const material = new THREE.MeshBasicMaterial({ color });
const particle = new THREE.Mesh(geometry, material);
particle.position.copy(pos);
particle.velocity = new THREE.Vector3((Math.random() - 0.5) * 0.5, (Math.random() - 0.5) * 0.5, (Math.random() - 0.5) * 0.5);
particle.life = 60; // frames
scene.add(particle);
particles.push(particle);
}
}
let altGun = false; // For gun level 2
function shoot() {
const now = Date.now();
const cooldown = baseFireCooldown * Math.pow(0.9, fireRateBonus);
if (now - lastShot < cooldown) return;
lastShot = now;
const pos = player.position.clone();
const bounces = bouncyBonus;
if (gunLevel === 1) {
createBullet(pos, new THREE.Vector3(0, 1, 0), bounces);
} else if (gunLevel === 2) {
const offset = altGun ? -1 : 1;
pos.x += offset;
createBullet(pos, new THREE.Vector3(0, 1, 0), bounces);
altGun = !altGun;
} else if (gunLevel === 3) {
createBullet(pos, new THREE.Vector3(0, 1, 0), bounces); // center
const leftPos = pos.clone(); leftPos.x -= 1;
createBullet(leftPos, new THREE.Vector3(-0.2, 1, 0).normalize(), bounces);
const rightPos = pos.clone(); rightPos.x += 1;
createBullet(rightPos, new THREE.Vector3(0.2, 1, 0).normalize(), bounces);
}
}
function updateHUD() {
document.getElementById('lives').textContent = `Lives: ${lives}`;
document.getElementById('score').textContent = `Score: ${score}`;
document.getElementById('powerups').textContent = `Powerups: Fire Rate: ${fireRateBonus} | Bouncy: ${bouncyBonus} | Guns: ${gunLevel}`;
}
function gameOver() {
// Simple game over screen
const over = document.createElement('div');
over.className = 'absolute inset-0 flex items-center justify-center text-white text-4xl font-bold bg-black bg-opacity-70';
over.textContent = `Game Over! Score: ${score}`;
container.appendChild(over);
// Stop animation? But for now, let it run in background
}
// Animation loop
let lastTime = Date.now();
function animate() {
requestAnimationFrame(animate);
const now = Date.now();
const delta = now - lastTime;
lastTime = now;
// Player movement (with bounds)
const speed = 0.3;
if (keys['ArrowLeft'] || keys['KeyA']) player.position.x = Math.max(-35, player.position.x - speed);
if (keys['ArrowRight'] || keys['KeyD']) player.position.x = Math.min(35, player.position.x + speed);
if (keys['ArrowUp'] || keys['KeyW']) player.position.y = Math.min(-15, player.position.y + speed);
if (keys['ArrowDown'] || keys['KeyS']) player.position.y = Math.max(-35, player.position.y - speed);
// 3D effect: slight rotation based on movement
player.rotation.y = player.position.x * 0.01;
player.rotation.x = (player.position.y + 25) * 0.005;
// Shooting
if (isShooting) shoot();
// Spawn enemies
if (Math.random() < 0.02 * difficulty) createEnemy();
// Update enemies
enemies = enemies.filter(enemy => {
enemy.position.y -= enemy.speed;
enemy.rotation.x += 0.01;
enemy.rotation.y += 0.01;
// Collision with player
if (player.position.distanceTo(enemy.position) < 2) {
lives--;
updateHUD();
createParticles(enemy.position, 20, enemy.material.color.getHex());
scene.remove(enemy);
if (lives <= 0) gameOver();
return false;
}
// Off screen
if (enemy.position.y < -40) {
scene.remove(enemy);
return false;
}
return true;
});
// Update bullets
bullets = bullets.filter(bullet => {
bullet.position.add(bullet.velocity.clone().multiplyScalar(0.5));
// Check collisions with enemies
for (let i = 0; i < enemies.length; i++) {
const enemy = enemies[i];
if (bullet.position.distanceTo(enemy.position) < 1.5) {
score += 10;
updateHUD();
createParticles(enemy.position, 20, enemy.material.color.getHex());
scene.remove(enemy);
enemies.splice(i, 1);
// Random bonus drop (20% chance)
if (Math.random() < 0.2) {
const types = ['fireRate', 'bouncy', 'guns', 'life'];
const type = types[Math.floor(Math.random() * types.length)];
createBonus(enemy.position, type);
}
// Bounce if able
if (bullet.bouncesLeft > 0) {
bullet.bouncesLeft--;
// Find nearest enemy to bounce to
let nearest = null;
let minDist = Infinity;
enemies.forEach(e => {
const dist = bullet.position.distanceTo(e.position);
if (dist < minDist) {
minDist = dist;
nearest = e;
}
});
if (nearest) {
bullet.velocity = nearest.position.clone().sub(bullet.position).normalize();
return true; // Continue bullet
}
}
scene.remove(bullet);
return false;
}
}
// Off screen
if (bullet.position.y > 40) {
scene.remove(bullet);
return false;
}
return true;
});
// Update bonuses
bonuses = bonuses.filter(bonus => {
bonus.position.y -= bonus.speed;
bonus.rotation.x += 0.02;
bonus.rotation.y += 0.02;
// Collect
if (player.position.distanceTo(bonus.position) < 2) {
switch (bonus.type) {
case 'fireRate': fireRateBonus++; break;
case 'bouncy': bouncyBonus++; break;
case 'guns': if (gunLevel < 3) gunLevel++; break;
case 'life': lives++; break;
}
updateHUD();
createParticles(bonus.position, 10, bonus.material.color.getHex());
scene.remove(bonus);
return false;
}
// Off screen
if (bonus.position.y < -40) {
scene.remove(bonus);
return false;
}
return true;
});
// Update particles
particles = particles.filter(p => {
p.position.add(p.velocity);
p.life--;
if (p.life <= 0) {
scene.remove(p);
return false;
}
return true;
});
// Background stars movement for scroll effect
stars.position.y -= 0.05;
if (stars.position.y < -50) stars.position.y = 50;
// Increase difficulty every 10 seconds
if (now - lastDifficultyTime > 10000) {
difficulty += 0.5;
lastDifficultyTime = now;
// Make enemies stronger: perhaps scale health, but for simplicity, just faster spawn/speed
}
renderer.render(scene, camera);
}
animate();
litechat.utils.log('info', '🎉 Game loaded! Focus the canvas and use arrows/WASD to move, space to shoot.');
} catch (error) {
litechat.utils.log('error', '❌ Failed to create game:', error.message);
const errorDiv = document.createElement('div');
errorDiv.className = 'p-4 text-red-600 bg-red-100 border border-red-200 rounded-md';
errorDiv.textContent = `Error: ${error.message}`;
litechat.target.appendChild(errorDiv);
}
}
createShooterGame();
```