太阳系防御战 - 舰队协同
by TurboKoala83721 lines27.5 KB
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>太阳系防御战 - 舰队协同</title>
<style>
body {
margin: 0;
overflow: hidden;
background-color: #050505;
font-family: 'Courier New', monospace;
color: #e2e8f0;
touch-action: none;
user-select: none;
-webkit-user-select: none;
}
canvas { display: block; width: 100vw; height: 100vh; }
#ui-layer {
position: absolute;
top: 0; left: 0; width: 100%; height: 100%;
pointer-events: none;
display: flex;
flex-direction: column;
justify-content: space-between;
padding: 20px;
box-sizing: border-box;
}
/* 顶部状态栏 */
.hud-top {
display: flex;
flex-direction: column;
gap: 8px;
background: rgba(15, 23, 42, 0.8);
padding: 12px;
border-radius: 8px;
border: 1px solid #1e3a5f;
backdrop-filter: blur(4px);
width: 220px;
border-left: 3px solid #22d3ee;
}
.bar-container { width: 100%; }
.bar-label { font-size: 11px; color: #94a3b8; margin-bottom: 2px; display: flex; justify-content: space-between; }
.bar-bg { width: 100%; height: 8px; background: #1e293b; border-radius: 4px; overflow: hidden; }
.bar-fill { height: 100%; transition: width 0.1s; }
#hp-fill { background: #3b82f6; width: 100%; }
#heat-fill { background: #ef4444; width: 0%; }
#heat-fill.cooling { background: #10b981; }
#heat-fill.overheat { background: #facc15; }
/* 底部控制区 */
.hud-bottom {
display: flex;
justify-content: space-between;
align-items: flex-end;
pointer-events: auto;
padding-bottom: 10px;
}
#btn-fire {
width: 90px;
height: 90px;
border-radius: 50%;
background: rgba(34, 211, 238, 0.15);
border: 2px solid #22d3ee;
color: #22d3ee;
font-size: 14px;
font-weight: bold;
pointer-events: auto;
touch-action: none;
box-shadow: 0 0 20px rgba(34, 211, 238, 0.2);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
user-select: none;
transition: all 0.1s;
}
#btn-fire:active, #btn-fire.active {
background: rgba(34, 211, 238, 0.7);
color: #fff;
box-shadow: 0 0 40px rgba(34, 211, 238, 0.8);
transform: scale(0.95);
}
#game-over {
position: absolute;
top: 50%; left: 50%;
transform: translate(-50%, -50%);
background: rgba(0,0,0,0.9);
border: 2px solid #ef4444;
padding: 30px;
border-radius: 10px;
text-align: center;
display: none;
flex-direction: column;
gap: 15px;
pointer-events: auto;
}
#game-over h1 { color: #ef4444; margin: 0; font-size: 24px; }
#restart-btn {
background: #22d3ee; color: #000; border: none; padding: 10px 20px;
font-family: inherit; font-weight: bold; cursor: pointer; border-radius: 5px;
}
</style>
</head>
<body>
<canvas id="gameCanvas"></canvas>
<div id="ui-layer">
<div class="hud-top">
<div class="bar-container">
<div class="bar-label"><span>地球防线</span><span id="hp-text">100%</span></div>
<div class="bar-bg"><div class="bar-fill" id="hp-fill"></div></div>
</div>
<div class="bar-container">
<div class="bar-label"><span>舰体热量</span><span id="heat-text">0%</span></div>
<div class="bar-bg"><div class="bar-fill" id="heat-fill"></div></div>
</div>
<div style="font-size: 12px; color: #94a3b8; display: flex; justify-content: space-between;">
<span>战绩: <span id="score" style="color:#22d3ee; font-weight:bold;">0</span></span>
<span>友军: <span id="ally-count" style="color:#10b981;">2</span></span>
</div>
</div>
<div class="hud-bottom">
<div></div>
<div id="btn-fire">主炮<br>开火</div>
</div>
</div>
<div id="game-over">
<h1>地球失守</h1>
<p>防线已被击穿</p>
<button id="restart-btn">重新部署</button>
</div>
<script>
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
let width, height;
let gameRunning = true;
let score = 0;
// --- 相机系统 (严格限制缩放极值) ---
const camera = {
x: 0, y: 0,
scale: 1.0, // 初始比例
minScale: 0.6, // 不能拉太远,保证战舰全貌可见
maxScale: 1.2 // 不能拉太近,避免战舰过大遮挡视野
};
// --- 实体数据 ---
const player = {
type: 'player',
x: 0, y: 0,
radius: 15,
hp: 100,
heat: 0,
maxHeat: 100,
isOverheated: false,
angle: -Math.PI / 2,
targetAngle: -Math.PI / 2,
cannonCooldown: 0,
interceptorCooldown: 0
};
// AI 友军:昆仑山号
const ally1 = {
type: 'ally',
name: '昆仑山号',
x: -100, y: 0,
radius: 18,
hp: 200, maxHp: 200,
angle: -Math.PI / 2,
fireCooldown: 0,
color: '#3b82f6'
};
// AI 友军:抽屉导弹舰
const ally2 = {
type: 'ally',
name: '抽屉导弹舰',
x: 100, y: 0,
radius: 16,
hp: 150, maxHp: 150,
angle: -Math.PI / 2,
fireCooldown: 0,
volleyCooldown: 0,
color: '#f97316'
};
let enemies = [];
let bullets = [];
let particles = [];
// --- 触摸/输入状态 ---
let isDragging = false;
let touchTarget = { x: 0, y: 0 };
let isFiring = false;
function resize() {
width = canvas.width = window.innerWidth;
height = canvas.height = window.innerHeight;
player.x = 0;
player.y = 100;
touchTarget.x = player.x;
touchTarget.y = player.y;
ally1.x = -120; ally1.y = 200;
ally2.x = 120; ally2.y = 250;
camera.x = 0;
camera.y = 0;
}
window.addEventListener('resize', resize);
resize();
function spawnExplosion(x, y, color) {
for (let i = 0; i < 10; i++) {
let angle = Math.random() * Math.PI * 2;
let speed = Math.random() * 4 + 1;
particles.push({
x: x, y: y,
vx: Math.cos(angle) * speed,
vy: Math.sin(angle) * speed,
life: 1.0,
color: color
});
}
}
// ==========================================
// 🚀 核心绘制:严格瘦身版祖冲之号(完全按照图)
// ==========================================
function drawPlayer() {
ctx.save();
ctx.translate(player.x, player.y);
ctx.rotate(player.angle);
// 主推进器尾焰
if (isDragging) {
ctx.fillStyle = 'rgba(251, 146, 60, 0.8)';
ctx.beginPath();
ctx.moveTo(-4, 25); ctx.lineTo(0, 40); ctx.lineTo(4, 25);
ctx.fill();
}
// 1. 两侧散热巨翼 (细长横向模块,带红色栅格)
ctx.fillStyle = '#1E293B';
ctx.strokeStyle = '#64748B';
ctx.lineWidth = 1;
// 左翼
ctx.fillRect(-40, -8, 25, 16);
ctx.strokeRect(-40, -8, 25, 16);
// 右翼
ctx.fillRect(15, -8, 25, 16);
ctx.strokeRect(15, -8, 25, 16);
// 翼上红色散热栅格 (模拟原图红线)
ctx.fillStyle = '#ef4444';
for(let i=0; i<3; i++) {
ctx.fillRect(-35 + i*8, -6, 2, 12); // 左翼
ctx.fillRect(20 + i*8, -6, 2, 12); // 右翼
}
// 2. 中央脊线 (极度收窄,修长)
// 顶部
ctx.fillStyle = '#F3F4F6';
ctx.fillRect(-5, -30, 10, 8);
// 中部白色装甲
ctx.fillRect(-5, -22, 10, 12);
// 蓝色AI核心
ctx.fillStyle = '#1E3A8A';
ctx.fillRect(-5, -10, 10, 12);
// 生活舱 (灰色)
ctx.fillStyle = '#9CA3AF';
ctx.fillRect(-5, 2, 10, 8);
// 底部装甲
ctx.fillStyle = '#F3F4F6';
ctx.fillRect(-5, 10, 10, 15);
// 3. 主炮 (位于左前侧,红底青口)
ctx.fillStyle = '#7F1D1D';
ctx.fillRect(-9, -35, 5, 20);
ctx.fillStyle = '#22D3EE'; // 青色炮口
ctx.fillRect(-9, -35, 5, 6);
// 4. 六门拦截炮 (灰色底座小圆)
ctx.fillStyle = '#6B7280';
const cannons = [[-15, -5], [15, -5], [-15, 8], [15, 8], [-8, -15], [8, -15]];
cannons.forEach(c => {
ctx.beginPath();
ctx.arc(c[0], c[1], 2, 0, Math.PI * 2);
ctx.fill();
});
ctx.restore();
}
function drawAlly(ally) {
ctx.save();
ctx.translate(ally.x, ally.y);
ctx.rotate(ally.angle);
// 友军外壳
ctx.fillStyle = '#1e3a8a';
ctx.strokeStyle = ally.color;
ctx.lineWidth = 2;
// 简单绘制友军轮廓
ctx.fillRect(-12, -18, 24, 36);
ctx.strokeRect(-12, -18, 24, 36);
// 友军武器
ctx.fillStyle = ally.color;
ctx.fillRect(-8, -25, 16, 8);
ctx.restore();
}
// --- 游戏主循环 ---
function update() {
if (!gameRunning) return;
// 1. 玩家移动 (平滑跟随手指)
if (isDragging) {
let dx = touchTarget.x - player.x;
let dy = touchTarget.y - player.y;
let dist = Math.hypot(dx, dy);
if (dist > 5) {
player.x += (dx / dist) * 5;
player.y += (dy / dist) * 5;
}
}
// 2. 热量与武器逻辑
let heatChange = 0;
if (isFiring && !player.isOverheated) {
heatChange += 2.5;
if (player.cannonCooldown <= 0) {
player.cannonCooldown = 10;
let bulletX = player.x + Math.cos(player.angle) * 40;
let bulletY = player.y + Math.sin(player.angle) * 40;
bullets.push({
x: bulletX, y: bulletY,
vx: Math.cos(player.angle) * 16,
vy: Math.sin(player.angle) * 16,
damage: 15, isPlayer: true, color: '#22D3EE'
});
}
} else {
heatChange -= 1.8;
}
// 拦截炮自动开火
if (player.interceptorCooldown <= 0 && enemies.length > 0) {
player.interceptorCooldown = 25;
heatChange += 0.3;
let nearestEnemy = null, minDist = Infinity;
enemies.forEach(e => {
let d = Math.hypot(e.x - player.x, e.y - player.y);
if (d < minDist) { minDist = d; nearestEnemy = e; }
});
if (nearestEnemy && minDist < 300) {
let angle = Math.atan2(nearestEnemy.y - player.y, nearestEnemy.x - player.x);
bullets.push({
x: player.x + Math.cos(angle) * 20, y: player.y + Math.sin(angle) * 20,
vx: Math.cos(angle) * 12, vy: Math.sin(angle) * 12,
damage: 4, isPlayer: true, color: '#A3E635'
});
}
}
player.cannonCooldown--; player.interceptorCooldown--;
// 热量结算
player.heat += heatChange;
if (player.heat >= player.maxHeat) { player.heat = player.maxHeat; player.isOverheated = true; }
else if (player.heat <= 0) player.heat = 0;
if (player.isOverheated && player.heat < 30) player.isOverheated = false;
// 3. 友军AI逻辑
[ally1, ally2].forEach(ally => {
if (ally.hp <= 0) return; // 友军阵亡则不再行动
// 跟随玩家
let targetX = player.x + (ally === ally1 ? -100 : 100);
let targetY = player.y + (ally === ally1 ? 80 : 120);
let dx = targetX - ally.x;
let dy = targetY - ally.y;
let dist = Math.hypot(dx, dy);
if (dist > 10) {
ally.x += (dx / dist) * 2;
ally.y += (dy / dist) * 2;
}
// 寻找敌人开火
ally.fireCooldown--;
if (ally.fireCooldown <= 0 && enemies.length > 0) {
let nearest = null, minD = Infinity;
enemies.forEach(e => {
let d = Math.hypot(e.x - ally.x, e.y - ally.y);
if (d < minD) { minD = d; nearest = e; }
});
if (nearest) {
ally.angle = Math.atan2(nearest.y - ally.y, nearest.x - ally.x);
ally.fireCooldown = 25;
bullets.push({
x: ally.x + Math.cos(ally.angle) * 20, y: ally.y + Math.sin(ally.angle) * 20,
vx: Math.cos(ally.angle) * 10, vy: Math.sin(ally.angle) * 10,
damage: 8, isPlayer: true, color: ally.color
});
}
}
});
// 抽屉导弹舰特殊能力 (发射大量导弹)
if (ally2.hp > 0) {
ally2.volleyCooldown--;
if (ally2.volleyCooldown <= 0 && enemies.length > 0) {
ally2.volleyCooldown = 180;
for(let i=0; i<6; i++) {
let angle = ally2.angle + (Math.random() - 0.5) * 0.8;
bullets.push({
x: ally2.x, y: ally2.y,
vx: Math.cos(angle) * 8, vy: Math.sin(angle) * 8,
damage: 6, isPlayer: true, color: '#f97316'
});
}
}
}
// 4. 自动瞄准转向
let nearest = null, nearestDist = Infinity;
enemies.forEach(e => {
let d = Math.hypot(e.x - player.x, e.y - player.y);
if (d < nearestDist) { nearestDist = d; nearest = e; }
});
if (nearest) {
player.targetAngle = Math.atan2(nearest.y - player.y, nearest.x - player.x);
let diff = player.targetAngle - player.angle;
while (diff < -Math.PI) diff += Math.PI * 2;
while (diff > Math.PI) diff -= Math.PI * 2;
player.angle += diff * 0.15;
}
// 5. 生成敌人 (攻击星)
if (Math.random() < 0.025) {
let angle = Math.random() * Math.PI * 2;
let dist = Math.max(width, height) * 1.5;
let ex = player.x + Math.cos(angle) * dist;
let ey = player.y + Math.sin(angle) * dist;
enemies.push({
x: ex, y: ey, radius: 8, hp: 20, speed: 1.2,
angle: 0, color: '#EF4444', fireCooldown: 60
});
}
// 6. 更新子弹
for (let i = bullets.length - 1; i >= 0; i--) {
let b = bullets[i];
b.x += b.vx; b.y += b.vy;
if (b.x < -3000 || b.x > 3000 || b.y < -3000 || b.y > 3000) { bullets.splice(i, 1); continue; }
if (b.isPlayer) {
for (let j = enemies.length - 1; j >= 0; j--) {
let e = enemies[j];
if (Math.hypot(b.x - e.x, b.y - e.y) < e.radius + 4) {
e.hp -= b.damage;
spawnExplosion(b.x, b.y, b.color);
bullets.splice(i, 1);
if (e.hp <= 0) { enemies.splice(j, 1); score += 10; }
break;
}
}
}
}
// 7. 更新敌人 AI
for (let i = enemies.length - 1; i >= 0; i--) {
let e = enemies[i];
// 敌人AI目标选择:优先攻击玩家,如果太远就直扑地球
let targetX = player.x;
let targetY = player.y;
let distToPlayer = Math.hypot(player.x - e.x, player.y - e.y);
if (distToPlayer > 500) {
targetX = 0; targetY = 400; // 地球防线位置
}
let angle = Math.atan2(targetY - e.y, targetX - e.x);
e.x += Math.cos(angle) * e.speed;
e.y += Math.sin(angle) * e.speed;
e.angle = angle;
// 敌人射击
e.fireCooldown--;
if (e.fireCooldown <= 0 && distToPlayer < 400) {
e.fireCooldown = 80;
let shootAngle = Math.atan2(player.y - e.y, player.x - e.x);
bullets.push({
x: e.x, y: e.y, vx: Math.cos(shootAngle) * 4, vy: Math.sin(shootAngle) * 4,
damage: 5, isPlayer: false, color: '#EF4444'
});
}
// 碰撞检测
if (Math.hypot(e.x - player.x, e.y - player.y) < e.radius + player.radius) {
player.hp -= 15; spawnExplosion(e.x, e.y, '#EF4444'); enemies.splice(i, 1);
}
[ally1, ally2].forEach(ally => {
if (ally.hp > 0 && Math.hypot(e.x - ally.x, e.y - ally.y) < e.radius + ally.radius) {
ally.hp -= 10; spawnExplosion(e.x, e.y, '#EF4444'); enemies.splice(i, 1);
}
});
}
// 8. 更新敌方子弹
for (let i = bullets.length - 1; i >= 0; i--) {
let b = bullets[i];
if (!b.isPlayer) {
b.x += b.vx; b.y += b.vy;
if (Math.hypot(b.x - player.x, b.y - player.y) < player.radius + 3) {
player.hp -= b.damage; spawnExplosion(b.x, b.y, '#EF4444'); bullets.splice(i, 1);
}
// 敌方子弹打友军
[ally1, ally2].forEach(ally => {
if (ally.hp > 0 && Math.hypot(b.x - ally.x, b.y - ally.y) < ally.radius + 3) {
ally.hp -= b.damage; spawnExplosion(b.x, b.y, '#EF4444'); bullets.splice(i, 1);
}
});
}
}
// 9. 更新粒子
for (let i = particles.length - 1; i >= 0; i--) {
let p = particles[i];
p.x += p.vx; p.y += p.vy; p.life -= 0.05;
if (p.life <= 0) particles.splice(i, 1);
}
// 相机跟随玩家 (限制视野范围,不无限拉远)
camera.x = player.x;
camera.y = player.y;
// 检查游戏结束
if (player.hp <= 0) {
gameRunning = false;
document.getElementById('game-over').style.display = 'flex';
}
// 更新UI
document.getElementById('hp-fill').style.width = Math.max(0, player.hp) + '%';
document.getElementById('hp-text').innerText = Math.max(0, Math.floor(player.hp)) + '%';
let heatPercent = (player.heat / player.maxHeat) * 100;
let heatFill = document.getElementById('heat-fill');
heatFill.style.width = heatPercent + '%';
document.getElementById('heat-text').innerText = Math.floor(heatPercent) + '%';
if (player.isOverheated) {
heatFill.classList.add('overheat');
document.getElementById('heat-text').innerText = "过热停机!";
} else {
heatFill.classList.remove('overheat');
}
document.getElementById('score').innerText = score;
document.getElementById('ally-count').innerText = (ally1.hp > 0 ? 1 : 0) + (ally2.hp > 0 ? 1 : 0);
}
function render() {
ctx.fillStyle = '#050505';
ctx.fillRect(0, 0, width, height);
// 绘制太阳系战术星图背景
ctx.save();
ctx.translate(width / 2 - camera.x * camera.scale, height / 2 - camera.y * camera.scale);
ctx.scale(camera.scale, camera.scale);
// 星空
for (let i = 0; i < 150; i++) {
let sx = (i * 137.5) % 2000 - 1000;
let sy = (i * 73.1) % 2000 - 1000;
ctx.fillStyle = `rgba(255, 255, 255, ${0.2 + (i % 3) * 0.1})`;
ctx.fillRect(sx, sy, 1.5, 1.5);
}
// 太阳系轨道线
ctx.strokeStyle = 'rgba(71, 85, 105, 0.4)';
ctx.lineWidth = 2;
ctx.beginPath(); ctx.arc(0, 0, 600, 0, Math.PI * 2); ctx.stroke();
ctx.beginPath(); ctx.arc(0, 0, 900, 0, Math.PI * 2); ctx.stroke();
// 地球防线
ctx.beginPath();
ctx.arc(0, 400, 300, Math.PI, Math.PI * 2);
let earthGrad = ctx.createRadialGradient(0, 400, 50, 0, 400, 300);
earthGrad.addColorStop(0, '#3B82F6');
earthGrad.addColorStop(1, '#1E3A8A');
ctx.fillStyle = earthGrad;
ctx.fill();
// 绘制实体
bullets.forEach(b => {
ctx.beginPath();
ctx.arc(b.x, b.y, b.isPlayer ? 3 : 4, 0, Math.PI * 2);
ctx.fillStyle = b.color;
ctx.shadowColor = b.color;
ctx.shadowBlur = 10;
ctx.fill();
ctx.shadowBlur = 0;
});
enemies.forEach(e => {
ctx.save();
ctx.translate(e.x, e.y);
ctx.rotate(e.angle);
ctx.beginPath();
ctx.moveTo(-8, -6); ctx.lineTo(8, 0); ctx.lineTo(-8, 6);
ctx.closePath();
ctx.fillStyle = e.color; ctx.fill();
ctx.strokeStyle = '#fff'; ctx.lineWidth = 1; ctx.stroke();
ctx.restore();
});
particles.forEach(p => {
ctx.globalAlpha = p.life;
ctx.fillStyle = p.color;
ctx.fillRect(p.x - 2, p.y - 2, 4, 4);
});
ctx.globalAlpha = 1;
// 绘制友军和玩家
if (gameRunning) {
if (ally1.hp > 0) drawAlly(ally1);
if (ally2.hp > 0) drawAlly(ally2);
drawPlayer();
// 主炮激光束 (视觉表现)
if (isFiring && !player.isOverheated) {
ctx.beginPath();
ctx.moveTo(player.x + Math.cos(player.angle) * 30, player.y + Math.sin(player.angle) * 30);
ctx.lineTo(player.x + Math.cos(player.angle) * 1500, player.y + Math.sin(player.angle) * 1500);
ctx.strokeStyle = 'rgba(34, 211, 238, 0.8)';
ctx.lineWidth = 6; ctx.stroke();
ctx.strokeStyle = '#ffffff'; ctx.lineWidth = 2; ctx.stroke();
}
}
ctx.restore();
}
function loop() {
update();
render();
requestAnimationFrame(loop);
}
// --- 输入控制逻辑 ---
canvas.addEventListener('touchstart', (e) => {
e.preventDefault();
if (!gameRunning) return;
let touch = e.touches[0];
isDragging = true;
// 屏幕坐标转世界坐标
touchTarget.x = (touch.clientX - width / 2) / camera.scale + camera.x;
touchTarget.y = (touch.clientY - height / 2) / camera.scale + camera.y;
}, { passive: false });
canvas.addEventListener('touchmove', (e) => {
e.preventDefault();
if (!gameRunning || !isDragging) return;
let touch = e.touches[0];
touchTarget.x = (touch.clientX - width / 2) / camera.scale + camera.x;
touchTarget.y = (touch.clientY - height / 2) / camera.scale + camera.y;
}, { passive: false });
canvas.addEventListener('touchend', (e) => { e.preventDefault(); isDragging = false; }, { passive: false });
// 桌面端鼠标兼容
canvas.addEventListener('mousedown', (e) => {
if (!gameRunning) return;
isDragging = true;
touchTarget.x = (e.clientX - width / 2) / camera.scale + camera.x;
touchTarget.y = (e.clientY - height / 2) / camera.scale + camera.y;
});
canvas.addEventListener('mousemove', (e) => {
if (isDragging) {
touchTarget.x = (e.clientX - width / 2) / camera.scale + camera.x;
touchTarget.y = (e.clientY - height / 2) / camera.scale + camera.y;
}
});
canvas.addEventListener('mouseup', () => { isDragging = false; });
// 主炮开火按钮
const fireBtn = document.getElementById('btn-fire');
fireBtn.addEventListener('touchstart', (e) => { e.preventDefault(); isFiring = true; fireBtn.classList.add('active'); }, { passive: false });
fireBtn.addEventListener('touchend', (e) => { e.preventDefault(); isFiring = false; fireBtn.classList.remove('active'); }, { passive: false });
fireBtn.addEventListener('mousedown', () => { isFiring = true; fireBtn.classList.add('active'); });
fireBtn.addEventListener('mouseup', () => { isFiring = false; fireBtn.classList.remove('active'); });
fireBtn.addEventListener('mouseleave', () => { isFiring = false; fireBtn.classList.remove('active'); });
// 重新开始
document.getElementById('restart-btn').addEventListener('click', () => {
gameRunning = true;
player.hp = 100; player.heat = 0; player.isOverheated = false;
player.x = 0; player.y = 100;
touchTarget.x = player.x; touchTarget.y = player.y;
ally1.hp = 200; ally2.hp = 150;
ally1.x = -120; ally1.y = 200; ally2.x = 120; ally2.y = 250;
enemies = []; bullets = []; particles = []; score = 0;
document.getElementById('game-over').style.display = 'none';
});
loop();
</script>
</body>
</html>Game Source: 太阳系防御战 - 舰队协同
Creator: TurboKoala83
Libraries: none
Complexity: complex (721 lines, 27.5 KB)
The full source code is displayed above on this page.
Remix Instructions
To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: game-turbokoala83-muquiosh" to link back to the original. Then publish at arcadelab.ai/publish.