🎮ArcadeLab

火焰豌豆大冒险

by MysticPanda75
745 lines24.5 KB
▶ Play
<!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>
*{box-sizing:border-box;margin:0;padding:0;-webkit-tap-highlight-color:transparent;}
html,body{width:100%;height:100%;overflow:hidden;background:#111;font-family:system-ui,sans-serif;}
#gameCanvas{display:block;width:100%;height:100%;background:#529c49;}
#joystickArea{position:fixed;bottom:40px;left:40px;width:140px;height:140px;background:rgba(255,255,255,0.2);border-radius:50%;touch-action:none;z-index:10;}
#joystickKnob{position:absolute;width:60px;height:60px;background:rgba(255,255,255,0.5);border-radius:50%;left:40px;top:40px;}
#fireBtn{position:fixed;bottom:40px;right:40px;width:110px;height:110px;border-radius:50%;background:#ff6622;color:white;font-size:24px;border:none;touch-action:none;z-index:10;}
#shopBtn{position:fixed;top:20px;right:20px;padding:12px 18px;font-size:20px;border-radius:10px;border:none;background:#4285f4;color:white;z-index:10;}
#shopPanel{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);width:380px;background:#222;padding:20px;border-radius:12px;color:white;display:none;border:2px solid #888;z-index:100;}
.shop-item{padding:10px;margin:8px 0;background:#333;border-radius:6px;display:flex;justify-content:space-between;align-items:center;}
.shop-item button{padding:6px 10px;border:none;border-radius:4px;background:#28a745;color:white;}
#closeShop{width:100%;margin-top:12px;padding:8px;background:#dc3545;border:none;color:white;border-radius:6px;font-size:16px;}
#cardPanel{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);width:460px;background:#1a1a1a;padding:24px;border-radius:14px;color:white;display:none;border:2px solid #ffcc00;text-align:center;z-index:100;}
.card-choice{display:inline-block;width:125px;padding:12px 8px;margin:6px;background:#333;border-radius:8px;border:1px solid #777;cursor:pointer;vertical-align:top;}
.card-choice:hover{background:#444;border-color:#ffcc00;}
</style>
</head>
<body>
<canvas id="gameCanvas"></canvas>
<div id="joystickArea"><div id="joystickKnob"></div></div>
<button id="fireBtn">🔥追踪弹</button>
<button id="shopBtn">🏪商店</button>

<div id="shopPanel">
  <h3 style="text-align:center;margin:0 0 15px">火焰豌豆商店</h3>
  <div class="shop-item"><span>❤️恢复20血量</span><button data-type="heal">20铜币</button></div>
  <div class="shop-item"><span>⚡移动速度+1(永久)</span><button data-type="speed">50铜币</button></div>
  <div class="shop-item"><span>🔥子弹速度+2(永久)</span><button data-type="bulletSpeed">60铜币</button></div>
  <div class="shop-item"><span>💥子弹伤害+1(永久)</span><button data-type="bulletDmg">80铜币</button></div>
  <div class="shop-item"><span>🔫射速提升(永久)</span><button data-type="fireRate">90铜币</button></div>
  <div class="shop-item"><span>🛡️受伤冷却+1秒(永久)</span><button data-type="coolUp">100铜币</button></div>
  <div class="shop-item"><span>💰击杀铜币+1(永久)</span><button data-type="coinBonus">120铜币</button></div>
  <div class="shop-item"><span>💖最大血量+20(永久)</span><button data-type="maxHp">150铜币</button></div>
  <div class="shop-item"><span>🤖自动射击(永久)</span><button data-type="autoFire">180铜币</button></div>
  <div class="shop-item"><span>💣击杀爆炸(永久)</span><button data-type="explodeKill">200铜币</button></div>
  <div class="shop-item"><span>🌱三发爆发卡牌【10秒】</span><button data-type="cardTriple">100铜币</button></div>
  <div class="shop-item"><span>❄️寒冰射手卡牌</span><button data-type="cardIce">140铜币</button></div>
  <div class="shop-item"><span>🪨坚果墙卡牌</span><button data-type="cardNut">160铜币</button></div>
  <button id="closeShop">关闭商店</button>
</div>

<div id="cardPanel">
  <h3 style="margin:0 0 16px">🎴选择一张卡牌</h3>
  <div id="cardList"></div>
</div>

<script>
const canvas = document.getElementById("gameCanvas");
const ctx = canvas.getContext("2d");
function resizeCanvas(){canvas.width = window.innerWidth;canvas.height = window.innerHeight;}
resizeCanvas();
window.addEventListener("resize",resizeCanvas);

const player = {
  x: canvas.width/2,
  y: canvas.height/2,
  size: 50,
  speed: 4,
  hp: 100,
  maxHp:100
};

let gameOver = false;
let bulletDamage = 1;
let fireDelay = 800;
let lastFireTime = 0;
let hurtCoolAdd = 0;
let killCoinBonus = 0;
let autoFire = false;
let explodeKill = false;
let coin = 0;
let killCount = 0;

let slowZombieTimer = 0;
let doubleCoinTimer = 0;
let piercingTimer = 0;
let shieldTimer = 0;
let instantKillCount = 0;
let bombWaveTimer = 0;
let summonHelperTimer = 0;
let weakZombieTimer = 0;
let tripleShotTimer = 0; //三发buff持续10秒

let bullets = [];
let enemies = [];
let enemyHitCool = new Map();
let helper = null;
let plants = [];
let playerCards = []; //玩家背包卡牌

let boss = null;
const BOSS_KILL_REQUIRE = 50;

const joystickArea = document.getElementById("joystickArea");
const joystickKnob = document.getElementById("joystickKnob");
const fireBtn = document.getElementById("fireBtn");
const shopBtn = document.getElementById("shopBtn");
const shopPanel = document.getElementById("shopPanel");
const closeShop = document.getElementById("closeShop");
const shopButtons = document.querySelectorAll(".shop-item button");
const cardPanel = document.getElementById("cardPanel");
const cardList = document.getElementById("cardList");

let joyActive = false;
let joyDx = 0, joyDy = 0;

joystickArea.addEventListener("touchstart", e=>{
  e.preventDefault();
  joyActive = true;
});

joystickArea.addEventListener("touchmove", e=>{
  e.preventDefault();
  const rect = joystickArea.getBoundingClientRect();
  const cx = rect.left + rect.width / 2;
  const cy = rect.top + rect.height / 2;
  const touch = e.touches[0];
  let x = touch.clientX - cx;
  let y = touch.clientY - cy;
  const dist = Math.hypot(x, y);
  if(dist > 60){
    x = x / dist * 60;
    y = y / dist * 60;
  }
  joyDx = x / 60;
  joyDy = y / 60;
  joystickKnob.style.left = (40 + x) + "px";
  joystickKnob.style.top = (40 + y) + "px";
});

joystickArea.addEventListener("touchend", ()=>{
  joyActive = false;
  joyDx = 0;
  joyDy = 0;
  joystickKnob.style.left = "40px";
  joystickKnob.style.top = "40px";
});

function shoot(){
  if(gameOver) return;
  if(shopPanel.style.display === "block" || cardPanel.style.display === "block") return;

  const now = Date.now();
  if(now - lastFireTime < fireDelay) return;
  if(enemies.length === 0) return;

  lastFireTime = now;

  let nearest = enemies[0];
  let minD = Math.hypot(nearest.x - player.x, nearest.y - player.y);

  for(let e of enemies){
    let d = Math.hypot(e.x - player.x, e.y - player.y);
    if(d < minD){
      minD = d;
      nearest = e;
    }
  }

  //基础子弹
  bullets.push({
    x: player.x,
    y: player.y,
    target: nearest,
    speed: 9,
    size: 14,
    damage: bulletDamage,
    pierce: piercingTimer > 0,
    fromPlant: false
  });

  //三发buff生效:额外多发2颗,总共3发!持续10秒
  if(tripleShotTimer > 0){
    bullets.push({
      x: player.x,
      y: player.y,
      target: nearest,
      speed: 9,
      size: 14,
      damage: bulletDamage,
      pierce: piercingTimer > 0,
      fromPlant: false,
      angleOffset:-0.12
    });
    bullets.push({
      x: player.x,
      y: player.y,
      target: nearest,
      speed: 9,
      size: 14,
      damage: bulletDamage,
      pierce: piercingTimer > 0,
      fromPlant: false,
      angleOffset:0.12
    });
  }
}

fireBtn.addEventListener("touchstart", e=>{
  e.preventDefault();
  shoot();
});

shopBtn.onclick = () => {
  if(!gameOver) shopPanel.style.display = "block";
};

closeShop.onclick = () => {
  shopPanel.style.display = "none";
};

shopButtons.forEach(btn => {
  btn.onclick = () => {
    const type = btn.dataset.type;

    if(type === "heal"){
      if(coin >= 20 && player.hp < player.maxHp){
        coin -= 20;
        player.hp = Math.min(player.maxHp, player.hp + 20);
      }
    }else if(type === "speed"){
      if(coin >= 50){coin -= 50; player.speed += 1;}
    }else if(type === "bulletSpeed"){
      if(coin >= 60){coin -= 60; bullets.forEach(b => b.speed += 2);}
    }else if(type === "bulletDmg"){
      if(coin >= 80){coin -= 80; bulletDamage += 1;}
    }else if(type === "fireRate"){
      if(coin >= 90){coin -= 90; fireDelay -= 100;}
    }else if(type === "coolUp"){
      if(coin >= 100){coin -= 100; hurtCoolAdd += 1000;}
    }else if(type === "coinBonus"){
      if(coin >= 120){coin -= 120; killCoinBonus += 1;}
    }else if(type === "maxHp"){
      if(coin >= 150){coin -= 150; player.maxHp += 20; player.hp += 20;}
    }else if(type === "autoFire"){
      if(coin >= 180){coin -= 180; autoFire = true;}
    }else if(type === "explodeKill"){
      if(coin >= 200){coin -= 200; explodeKill = true;}
    }else if(type === "cardTriple"){
      if(coin >=100){coin -= 100; playerCards.push({name:"三发爆发",type:"triple"});}
    }else if(type === "cardIce"){
      if(coin >=140){coin -= 140; playerCards.push({name:"寒冰射手",type:"ice"});}
    }else if(type === "cardNut"){
      if(coin >=160){coin -= 160; playerCards.push({name:"坚果墙",type:"nut"});}
    }
  };
});

//使用卡牌
function useCard(cardIndex){
  if(gameOver) return;
  const card = playerCards[cardIndex];
  if(!card) return;
  
  if(card.type === "triple"){
    tripleShotTimer = 10000; //持续10秒,玩家每次射击+2发子弹
  }else{
    const ang = Math.atan2(joyDy || 0, joyDx || 1);
    const spawnX = player.x + Math.cos(ang)*70;
    const spawnY = player.y + Math.sin(ang)*70;
    if(card.type === "ice"){
      plants.push({
        x:spawnX,y:spawnY,size:44,shootCd:0,shootRate:35,plantType:"ice",hp:80,maxHp:80
      });
    }else if(card.type === "nut"){
      plants.push({
        x:spawnX,y:spawnY,size:55,shootCd:0,shootRate:0,plantType:"nut",hp:8000,maxHp:8000
      });
    }
  }
  playerCards.splice(cardIndex,1);
}

function spawnEnemy(){
  if(gameOver) return;

  const side = Math.floor(Math.random() * 4);
  let ex, ey;
  let type = 0;
  let rand = Math.random();

  if(rand < 0.7) type = 0;
  else if(rand < 0.92) type = 1;
  else type = 2;

  if(side === 0){ex = 0; ey = Math.random() * canvas.height;}
  else if(side === 1){ex = canvas.width; ey = Math.random() * canvas.height;}
  else if(side === 2){ex = Math.random() * canvas.width; ey = 0;}
  else {ex = Math.random() * canvas.width; ey = canvas.height;}

  let enemyData = {
    x: ex,
    y: ey,
    size: 46,
    speed: 0.8,
    id: Date.now() + Math.random(),
    type: type,
    hp: 1,
    maxHp: 1,
    targetPlayer:true //默认目标玩家
  };

  if(type === 1){
    enemyData.speed = 1.1;
    enemyData.size = 54;
    enemyData.hp = 3;
    enemyData.maxHp = 3;
  }else if(type === 2){
    enemyData.speed = 1.4;
    enemyData.size = 62;
    enemyData.hp = 5;
    enemyData.maxHp = 5;
  }

  enemies.push(enemyData);
}

function spawnBoss(){
  if(boss) return;

  boss = {
    x: canvas.width / 2,
    y: 120,
    size: 120,
    hp: 200,
    maxHp: 200,
    speed: 0.35,
    dir: 1,
    shootCd: 0,
    summonCd: 240
  };
}

function showCardSelect(){
  cardList.innerHTML = "";

  const cardPool = [
    {name:"❄️僵尸减速",desc:"全体僵尸速度减半,持续10秒",action:()=>{ slowZombieTimer = 10000; }},
    {name:"💰双倍铜币",desc:"10秒击杀铜币×2",action:()=>{ doubleCoinTimer = 10000; }},
    {name:"🔥穿透子弹",desc:"子弹穿透多个僵尸,持续8秒",action:()=>{ piercingTimer = 8000; }},
    {name:"🛡️临时护盾",desc:"5秒免伤",action:()=>{ shieldTimer = 5000; }},
    {name:"💀一击秒杀",desc:"接下来5发子弹秒杀",action:()=>{ instantKillCount = 5; }},
    {name:"❤️回满血量",desc:"血量直接回满",action:()=>{ player.hp = player.maxHp; }},
    {name:"🪙铜币+50",desc:"直接获得50铜币",action:()=>{ coin += 50; }},
    {name:"💥冲击波",desc:"清空场上全部僵尸",action:()=>{ bombWaveTimer = 1; }},
    {name:"🤖召唤帮手",desc:"豌豆助手打怪12秒",action:()=>{ summonHelperTimer = 12000; helper = {x: player.x, y: player.y}; }},
    {name:"🥺僵尸虚弱",desc:"僵尸受到伤害+50%持续10秒",action:()=>{ weakZombieTimer = 10000; }}
  ];

  let shuffled = [...cardPool].sort(() => Math.random() - 0.5).slice(0, 3);

  shuffled.forEach((card,idx) => {
    let div = document.createElement("div");
    div.className = "card-choice";
    div.innerHTML = `<div>${card.name}</div><div style="font-size:12px;margin-top:4px">${card.desc}</div>`;
    div.onclick = () => {
      card.action();
      cardPanel.style.display = "none";
    };
    cardList.appendChild(div);
  });

  cardPanel.style.display = "block";
}

function loop(){
  const paused = shopPanel.style.display === "block" || cardPanel.style.display === "block";

  if(!gameOver && !paused){
    player.x += joyDx * player.speed;
    player.y += joyDy * player.speed;

    player.x = Math.max(player.size, Math.min(canvas.width - player.size, player.x));
    player.y = Math.max(player.size, Math.min(canvas.height - player.size, player.y));

    if(autoFire){
      shoot();
    }

    if(killCount > 0 && killCount % 10 === 0 && cardPanel.style.display !== "block"){
      showCardSelect();
      killCount += 1;
    }

    if(killCount >= BOSS_KILL_REQUIRE){
      spawnBoss();
    }

    if(slowZombieTimer > 0) slowZombieTimer -= 16;
    if(doubleCoinTimer > 0) doubleCoinTimer -= 16;
    if(piercingTimer > 0) piercingTimer -= 16;
    if(shieldTimer > 0) shieldTimer -= 16;
    if(summonHelperTimer > 0) summonHelperTimer -= 16;
    if(weakZombieTimer > 0) weakZombieTimer -= 16;
    if(tripleShotTimer > 0) tripleShotTimer -= 16;

    if(bombWaveTimer > 0){
      bombWaveTimer = 0;
      enemies.forEach(e => {
        killCount++;
        let getCoin = 0;
        if(e.type === 0) getCoin = 1 + killCoinBonus;
        else if(e.type === 1) getCoin = 5 + killCoinBonus;
        else if(e.type === 2) getCoin = 20 + killCoinBonus;
        if(doubleCoinTimer > 0) getCoin *= 2;
        coin += getCoin;
      });
      enemies = [];
    }

    if(summonHelperTimer > 0 && helper){
      let nearestEnemy = null;
      let minD = 9999;

      enemies.forEach(e => {
        let d = Math.hypot(e.x - helper.x, e.y - helper.y);
        if(d < minD){
          minD = d;
          nearestEnemy = e;
        }
      });

      if(nearestEnemy){
        let ang = Math.atan2(nearestEnemy.y - helper.y, nearestEnemy.x - helper.x);
        helper.x += Math.cos(ang) * 3;
        helper.y += Math.sin(ang) * 3;

        if(Math.hypot(helper.x - nearestEnemy.x, helper.y - nearestEnemy.y) < 30){
          nearestEnemy.hp -= 0.08;
        }
      }
    }

    let hasIcePlant = plants.some(p=>p.plantType === "ice" && p.hp>0);

    // ===== 植物自动攻击 =====
    plants.forEach(p => {
      p.shootCd--;
      if(p.plantType === "nut") return;
      if(p.shootCd <= 0 && enemies.length > 0){
        let nearest = enemies[0];
        let minD = Math.hypot(nearest.x - p.x, nearest.y - p.y);
        for(let e of enemies){
          let d = Math.hypot(e.x - p.x, e.y - p.y);
          if(d < minD){
            minD = d;
            nearest = e;
          }
        }
        const ang = Math.atan2(nearest.y - p.y, nearest.x - p.x);
        if(p.plantType === "ice"){
          bullets.push({x:p.x,y:p.y,target:nearest,speed:7,size:12,damage:0.4,pierce:false,fromPlant:true,isIce:true});
          p.shootCd = p.shootRate;
        }
      }
    });

    for(let i = bullets.length - 1; i >= 0; i--){
      const b = bullets[i];
      if(!enemies.includes(b.target)){
        bullets.splice(i, 1);
        continue;
      }
      let ang = Math.atan2(b.target.y - b.y, b.target.x - b.x);
      //子弹角度偏移
      if(b.angleOffset) ang += b.angleOffset;
      b.x += Math.cos(ang) * b.speed;
      b.y += Math.sin(ang) * b.speed;
      if(b.x < 0 || b.x > canvas.width || b.y < 0 || b.y > canvas.height){
        bullets.splice(i, 1);
      }
    }

    for(let i = enemies.length - 1; i >= 0; i--){
      const e = enemies[i];
      //僵尸优先找最近坚果墙
      let nearestNut = null;
      let nutDist = 9999;
      plants.forEach(p=>{
        if(p.plantType === "nut" && p.hp>0){
          let d = Math.hypot(p.x - e.x,p.y - e.y);
          if(d < nutDist){
            nutDist = d;
            nearestNut = p;
          }
        }
      });
      if(nearestNut){
        e.targetPlayer = false;
        const ang = Math.atan2(nearestNut.y - e.y, nearestNut.x - e.x);
        e.x += Math.cos(ang)*e.speed;
        e.y += Math.sin(ang)*e.speed;
        //僵尸啃坚果
        if(Math.hypot(e.x-nearestNut.x,e.y-nearestNut.y)<e.size+nearestNut.size){
          nearestNut.hp -= 0.05;
          if(nearestNut.hp <=0){
            plants = plants.filter(x=>x!==nearestNut);
          }
        }
      }else{
        e.targetPlayer = true;
        let speedMult = slowZombieTimer > 0 ? 0.5 : 1;
        if(hasIcePlant) speedMult *= 0.5; //寒冰在场,全体僵尸减速
        const ang = Math.atan2(player.y - e.y, player.x - e.x);
        e.x += Math.cos(ang) * e.speed * speedMult;
        e.y += Math.sin(ang) * e.speed * speedMult;
      }

      const totalHurtCool = 3000 + hurtCoolAdd;
      const hitDist = Math.hypot(player.x - e.x, player.y - e.y);
      const now = Date.now();
      if(e.targetPlayer && hitDist < player.size + e.size){
        if(shieldTimer <= 0 && (!enemyHitCool.has(e.id) || now - enemyHitCool.get(e.id) > totalHurtCool)){
          player.hp -= 1;
          enemyHitCool.set(e.id, now);
          if(player.hp <= 0){
            player.hp = 0;
            gameOver = true;
          }
        }
      }

      for(let j = bullets.length - 1; j >= 0; j--){
        const b = bullets[j];
        const dist = Math.hypot(b.x - e.x, b.y - e.y);
        if(dist < e.size + b.size){
          let dmg = b.damage;
          if(instantKillCount > 0 && !b.fromPlant){
            dmg = 999;
            instantKillCount--;
          }
          if(weakZombieTimer > 0) dmg *= 1.5;
          e.hp -= dmg;
          if(!b.pierce){
            bullets.splice(j, 1);
          }
          if(e.hp <= 0){
            killCount += 1;
            let getCoin = 0;
            if(e.type === 0) getCoin = 1 + killCoinBonus;
            else if(e.type === 1) getCoin = 5 + killCoinBonus;
            else if(e.type === 2) getCoin = 20 + killCoinBonus;
            if(doubleCoinTimer > 0) getCoin *= 2;
            coin += getCoin;
            if(explodeKill){
              enemies.forEach(ee => {
                if(Math.hypot(ee.x - e.x, ee.y - e.y) < 70){
                  ee.hp -= 2;
                }
              });
            }
            enemies.splice(i, 1);
            enemyHitCool.delete(e.id);
          }
          break;
        }
      }
    }

    // ===== BOSS逻辑 =====
    if(boss){
      boss.x += boss.dir * boss.speed;
      if(boss.x < 160 || boss.x > canvas.width - 160){
        boss.dir *= -1;
      }
      boss.summonCd--;
      if(boss.summonCd <= 0){
        boss.summonCd = 300;
        for(let k = 0; k < 3; k++){
          setTimeout(()=>{
            if(!gameOver) spawnEnemy();
          }, k * 120);
        }
      }
      const bossHit = Math.hypot(player.x - boss.x, player.y - boss.y);
      if(bossHit < player.size + boss.size){
        if(shieldTimer <= 0){
          player.hp -= 1;
          if(player.hp <= 0){
            player.hp = 0;
            gameOver = true;
          }
        }
      }
      for(let j = bullets.length - 1; j >= 0; j--){
        const b = bullets[j];
        const dist = Math.hypot(b.x - boss.x, b.y - boss.y);
        if(dist < boss.size + b.size){
          let dmg = b.damage;
          if(weakZombieTimer > 0) dmg *= 1.5;
          boss.hp -= dmg;
          if(!b.pierce){
            bullets.splice(j, 1);
          }
          if(boss.hp <= 0){
            coin += 200;
            killCount += 10;
            boss = null;
          }
        }
      }
    }

    if(Math.random() < 0.010) spawnEnemy();
  }

  ctx.fillStyle = "#529c49";
  ctx.fillRect(0, 0, canvas.width, canvas.height);

  ctx.strokeStyle = "#407c3a";
  ctx.lineWidth = 1;
  for(let x = 0; x < canvas.width; x += 40){
    ctx.beginPath();
    ctx.moveTo(x, 0);
    ctx.lineTo(x, canvas.height);
    ctx.stroke();
  }
  for(let y = 0; y < canvas.height; y += 40){
    ctx.beginPath();
    ctx.moveTo(0, y);
    ctx.lineTo(canvas.width, y);
    ctx.stroke();
  }

  // ===== 绘制植物 =====
  plants.forEach(p => {
    ctx.font = "44px system-ui";
    let icon = "🌱";
    if(p.plantType === "ice") icon = "❄️";
    if(p.plantType === "nut") icon = "🪨";
    ctx.fillText(icon, p.x - 22, p.y + 14);
    //植物血条
    ctx.fillStyle = "#333";
    ctx.fillRect(p.x - 25, p.y - 35, 50, 6);
    ctx.fillStyle = "#2dff40";
    ctx.fillRect(p.x - 25, p.y - 35, 50*(p.hp/p.maxHp),6);
  });

  ctx.font = "40px system-ui";
  enemies.forEach(e => {
    let icon = "🧟";
    if(e.type === 1) icon = "👹";
    if(e.type === 2) icon = "👾";
    ctx.fillText(icon, e.x - e.size / 2, e.y + e.size / 3);
    const enemyHpW = 44;
    ctx.fillStyle = "#333";
    ctx.fillRect(e.x - enemyHpW / 2, e.y - e.size - 10, enemyHpW, 6);
    ctx.fillStyle = "#ff5555";
    ctx.fillRect(e.x - enemyHpW / 2, e.y - e.size - 10, enemyHpW * (e.hp / e.maxHp), 6);
  });

  ctx.font = "28px system-ui";
  bullets.forEach(b => {
    if(b.fromPlant){
      if(b.isIce){
        ctx.fillText("🔵", b.x - 10, b.y + 8);
      }else{
        ctx.fillText("🟢", b.x - 10, b.y + 8);
      }
    }else{
      ctx.fillText("🔥", b.x - 10, b.y + 8);
    }
  });

  if(summonHelperTimer > 0 && helper){
    ctx.font = "44px system-ui";
    ctx.fillText("🌱", helper.x - 18, helper.y + 12);
  }

  ctx.font = "48px system-ui";
  ctx.fillText("🔥", player.x - 20, player.y + 14);

  // ===== 绘制BOSS =====
  if(boss){
    ctx.font = "120px system-ui";
    ctx.fillText("👑", boss.x - 60, boss.y + 40);
    ctx.fillStyle = "#333";
    ctx.fillRect(canvas.width / 2 - 200, 30, 400, 18);
    ctx.fillStyle = "#ff3333";
    ctx.fillRect(canvas.width / 2 - 200, 30, 400 * (boss.hp / boss.maxHp), 18);
    ctx.fillStyle = "#fff";
    ctx.font = "16px system-ui";
    ctx.textAlign = "center";
    ctx.fillText("僵王博士", canvas.width / 2, 24);
    ctx.textAlign = "left";
  }

  ctx.fillStyle = "#333";
  ctx.fillRect(10, 10, 204, 24);
  let hpPercent = player.hp / player.maxHp;
  ctx.fillStyle = "#ff3333";
  ctx.fillRect(12, 12, 200 * hpPercent, 20);
  ctx.fillStyle = "#fff";
  ctx.font = "16px system-ui";
  ctx.fillText("HP:" + player.hp + "/" + player.maxHp, 15, 28);
  ctx.fillStyle = "#fff";
  ctx.font = "16px system-ui";
  ctx.fillText("🪙铜币:" + coin, 10, 52);
  ctx.fillText("💥伤害:" + bulletDamage, 10, 74);
  ctx.fillText("🔫射速间隔:" + fireDelay + "ms", 10, 96);
  ctx.fillText("🤖自动射击:" + (autoFire ? "开启" : "关闭"), 10, 140);
  ctx.fillText("💣击杀爆炸:" + (explodeKill ? "开启" : "关闭"), 10, 162);
  ctx.fillText("🌱植物:" + plants.length, 10, 184);
  ctx.fillText("🎴卡牌:"+playerCards.length,10,206);
  //卡牌列表
  playerCards.forEach((c,i)=>{
    ctx.fillText(`${i+1}.${c.name}`,10,230+i*24);
  })

  let statusX = 220;
  if(slowZombieTimer > 0){ctx.fillStyle = "#8cf"; ctx.fillText("❄️僵尸减速", statusX, 28); statusX += 90;}
  if(doubleCoinTimer > 0){ctx.fillStyle = "#ffc"; ctx.fillText("💰双倍铜币", statusX, 28); statusX += 90;}
  if(piercingTimer > 0){ctx.fillStyle = "#f8f"; ctx.fillText("🔥穿透子弹", statusX, 28); statusX += 90;}
  if(shieldTimer > 0){ctx.fillStyle = "#0cf"; ctx.fillText("🛡️护盾", statusX, 28); statusX += 90;}
  if(instantKillCount > 0){ctx.fillStyle = "#f44"; ctx.fillText("💀秒杀剩余:" + instantKillCount, statusX, 28); statusX += 90;}
  if(summonHelperTimer > 0){ctx.fillStyle = "#4f8"; ctx.fillText("🤖帮手", statusX, 50); statusX += 90;}
  if(weakZombieTimer > 0){ctx.fillStyle = "#fa8"; ctx.fillText("🥺僵尸虚弱", statusX, 50); statusX += 90;}
  if(hasIcePlant){ctx.fillStyle="#8cf";ctx.fillText("❄️全场减速",statusX,50);statusX+=90;}
  if(tripleShotTimer > 0){ctx.fillStyle="#ff9";ctx.fillText("🔥三发爆发",statusX,28);statusX +=90;}

  if(gameOver){
    ctx.fillStyle = "rgba(0,0,0,0.7)";
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    ctx.fillStyle = "#ff4444";
    ctx.font = "50px system-ui";
    ctx.textAlign = "center";
    ctx.fillText("游戏结束", canvas.width / 2, canvas.height / 2 - 30);
    ctx.font = "24px system-ui";
    ctx.fillText("刷新页面重新游玩", canvas.width / 2, canvas.height / 2 + 20);
    ctx.textAlign = "left";
  }

  requestAnimationFrame(loop);
}

loop();
</script>
</body>
</html>

Game Source: 火焰豌豆大冒险

Creator: MysticPanda75

Libraries: none

Complexity: complex (745 lines, 24.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-mysticpanda75-mvf75lkn" to link back to the original. Then publish at arcadelab.ai/publish.