LiteChat Model Race Result (RUNJS)
```runjs
(async () => {
// Clear previous content
litechat.target.replaceChildren();
try {
// Load Three.js
const { THREE } = await litechat.utils.loadModules([
{
url: 'https://unpkg.com/three@0.170.0/build/three.module.js',
name: 'THREE',
globalKey: 'THREE'
}
]);
// Create main container
const container = document.createElement('div');
container.className = 'relative w-[800px] h-[600px] mx-auto my-4 bg-pink-50 rounded-lg overflow-hidden shadow-lg';
litechat.target.appendChild(container);
// Create canvas
const canvas = document.createElement('canvas');
canvas.width = 800;
canvas.height = 600;
canvas.className = 'block';
container.appendChild(canvas);
// HUD overlay
const hud = document.createElement('div');
hud.className = 'absolute top-0 left-0 p-2 space-y-1 text-gray-800 text-sm bg-white bg-opacity-60 rounded-bl-lg';
container.appendChild(hud);
// Instructions
const instr = document.createElement('div');
instr.textContent = '←↑↓→: Move Space: Shoot';
instr.className = 'text-xs font-semibold';
hud.appendChild(instr);
// HUD fields
const livesSpan = document.createElement('div');
const scoreSpan = document.createElement('div');
const fireRateSpan = document.createElement('div');
const bounceSpan = document.createElement('div');
const gunModeSpan = document.createElement('div');
[livesSpan, scoreSpan, fireRateSpan, bounceSpan, gunModeSpan].forEach(el => hud.appendChild(el));
// Three.js scene setup
const scene = new THREE.Scene();
scene.background = new THREE.Color(0xfff1e8); // soft pastel
const camera = new THREE.PerspectiveCamera(75, 800 / 600, 0.1, 1000);
camera.position.set(0, 0, 10);
const renderer = new THREE.WebGLRenderer({ canvas, antialias: true });
renderer.setSize(800, 600);
renderer.shadowMap.enabled = true;
const ambient = new THREE.AmbientLight(0xffffff, 0.6);
scene.add(ambient);
const directional = new THREE.DirectionalLight(0xffffff, 0.8);
directional.position.set(5, 10, 7.5);
scene.add(directional);
// Player ship
const playerGeo = new THREE.ConeGeometry(0.5, 1, 8);
playerGeo.rotateX(-Math.PI / 2);
const playerMat = new THREE.MeshLambertMaterial({ color: 0x4a4e69 });
const player = new THREE.Mesh(playerGeo, playerMat);
player.position.set(0, 0, 0);
scene.add(player);
// Game state
let lives = 3;
let score = 0;
const baseFireRate = 2; // shots/sec
let fireRateMultiplier = 1;
let bouncePower = 0;
let gunMode = 1;
let lastFireTime = 0;
let bullets = [];
let enemies = [];
let bonuses = [];
let lastEnemySpawn = 0;
let enemySpawnInterval = 1.5;
let killCount = 0;
const bonusChance = 0.2;
// Input handling
const keys = { left: false, right: false, up: false, down: false, space: false };
window.addEventListener('keydown', e => {
if (e.code === 'ArrowLeft') keys.left = true;
if (e.code === 'ArrowRight') keys.right = true;
if (e.code === 'ArrowUp') keys.up = true;
if (e.code === 'ArrowDown') keys.down = true;
if (e.code === 'Space') keys.space = true;
});
window.addEventListener('keyup', e => {
if (e.code === 'ArrowLeft') keys.left = false;
if (e.code === 'ArrowRight') keys.right = false;
if (e.code === 'ArrowUp') keys.up = false;
if (e.code === 'ArrowDown') keys.down = false;
if (e.code === 'Space') keys.space = false;
});
// Update HUD display
function updateHUD() {
livesSpan.textContent = `Lives: ${lives}`;
scoreSpan.textContent = `Score: ${score}`;
fireRateSpan.textContent = `Fire Rate: ${(baseFireRate * fireRateMultiplier).toFixed(1)} /s`;
bounceSpan.textContent = `Bounce: ${bouncePower}`;
gunModeSpan.textContent = `Gun Mode: ${gunMode}`;
}
updateHUD();
// Spawn a new enemy
function spawnEnemy() {
const geo = new THREE.SphereGeometry(0.4, 8, 8);
const isStrong = Math.random() < 0.2;
const mat = new THREE.MeshLambertMaterial({ color: isStrong ? 0xff6b6b : 0x556270 });
const mesh = new THREE.Mesh(geo, mat);
const x = (Math.random() * 2 - 1) * 7;
const y = (Math.random() * 2 - 1) * 5;
mesh.position.set(x, y, -50);
scene.add(mesh);
const speed = (0.5 + killCount * 0.005) * (isStrong ? 1.5 : 1);
enemies.push({ mesh, hp: isStrong ? 2 : 1, velocity: new THREE.Vector3(0, 0, speed) });
}
// Spawn a bonus item at given position
function spawnBonus(type, position) {
const colors = [0xffc75f, 0x3bfdd4, 0xf15bb5, 0x7ef5a3];
const geo = new THREE.SphereGeometry(0.3, 8, 8);
const mat = new THREE.MeshLambertMaterial({ color: colors[type] });
const mesh = new THREE.Mesh(geo, mat);
mesh.position.copy(position);
scene.add(mesh);
bonuses.push({ mesh, type, velocity: new THREE.Vector3(0, 0, 1) });
}
// Fire bullets based on gate rate and gun mode
let fireToggle = false;
function fire() {
const now = performance.now() / 1000;
const rate = baseFireRate * fireRateMultiplier;
if (now - lastFireTime < 1 / rate) return;
lastFireTime = now;
const bulletGeo = new THREE.SphereGeometry(0.15, 6, 6);
const bulletMat = new THREE.MeshLambertMaterial({ color: 0xffffff });
const offsets = [];
if (gunMode === 1) {
offsets.push(new THREE.Vector3(0, 0, 0));
} else if (gunMode === 2) {
const off = fireToggle ? 0.3 : -0.3;
offsets.push(new THREE.Vector3(off, 0, 0));
fireToggle = !fireToggle;
} else if (gunMode === 3) {
offsets.push(new THREE.Vector3(-0.5, 0, 0), new THREE.Vector3(0, 0, 0), new THREE.Vector3(0.5, 0, 0));
}
offsets.forEach(off => {
const mesh = new THREE.Mesh(bulletGeo, bulletMat);
mesh.position.copy(player.position).add(off);
scene.add(mesh);
const vel = new THREE.Vector3(0, 0, -1).multiplyScalar(20);
bullets.push({ mesh, velocity: vel, bounceRemaining: bouncePower });
});
}
// Main game loop
let lastTime = performance.now();
function animate() {
const now = performance.now();
const delta = (now - lastTime) / 1000;
lastTime = now;
// Move player
const moveSpeed = 10;
if (keys.left) player.position.x -= moveSpeed * delta;
if (keys.right) player.position.x += moveSpeed * delta;
if (keys.up) player.position.y += moveSpeed * delta;
if (keys.down) player.position.y -= moveSpeed * delta;
// Clamp inside bounds
player.position.x = Math.max(-8, Math.min(8, player.position.x));
player.position.y = Math.max(-6, Math.min(6, player.position.y));
// Handle firing
if (keys.space) fire();
// Spawn enemies over time
if (now / 1000 - lastEnemySpawn > enemySpawnInterval) {
spawnEnemy();
lastEnemySpawn = now / 1000;
enemySpawnInterval = Math.max(0.5, enemySpawnInterval * 0.995);
}
// Update bullets
bullets = bullets.filter(b => {
b.mesh.position.addScaledVector(b.velocity, delta);
const oob = b.mesh.position.z < -100 || b.mesh.position.z > 50;
if (oob) {
scene.remove(b.mesh);
return false;
}
return true;
});
// Update enemies
enemies = enemies.filter(e => {
e.mesh.position.addScaledVector(e.velocity, delta);
// If enemy reaches player plane
if (e.mesh.position.z > 0) {
scene.remove(e.mesh);
lives--;
updateHUD();
if (lives <= 0) {
// Game Over
const goDiv = document.createElement('div');
goDiv.className = 'absolute inset-0 flex items-center justify-center bg-black bg-opacity-50 text-white text-3xl';
goDiv.textContent = 'Game Over';
container.appendChild(goDiv);
}
return lives > 0;
}
return true;
});
// Update bonuses
bonuses = bonuses.filter(bn => {
bn.mesh.position.addScaledVector(bn.velocity, delta);
if (bn.mesh.position.z > 0) {
const d = bn.mesh.position.distanceTo(player.position);
if (d < 1) {
// Apply bonus
if (bn.type === 0) fireRateMultiplier *= 1.1;
if (bn.type === 1) bouncePower++;
if (bn.type === 2) gunMode = Math.min(3, gunMode + 1);
if (bn.type === 3) lives++;
updateHUD();
}
scene.remove(bn.mesh);
return false;
}
return true;
});
// Bullet–enemy collision
bullets.forEach(b => {
enemies.forEach((e, i) => {
if (b.mesh.position.distanceTo(e.mesh.position) < 0.5) {
e.hp--;
if (e.hp <= 0) {
// Kill enemy
scene.remove(e.mesh);
enemies.splice(i, 1);
score++;
killCount++;
updateHUD();
if (Math.random() < bonusChance) {
spawnBonus(Math.floor(Math.random() * 4), e.mesh.position.clone());
}
}
// Handle bounce
if (b.bounceRemaining > 0) {
b.bounceRemaining--;
} else {
scene.remove(b.mesh);
b._destroy = true;
}
}
});
});
bullets = bullets.filter(b => !b._destroy);
// Render
renderer.render(scene, camera);
if (lives > 0) requestAnimationFrame(animate);
}
// Start animation
animate();
} catch (error) {
litechat.utils.log('error', 'Game initialization failed:', error);
const errDiv = document.createElement('div');
errDiv.className = 'p-4 text-red-600 bg-red-100 rounded-md';
errDiv.textContent = `Error: ${error.message}`;
litechat.target.appendChild(errDiv);
}
})();
```