🎮ArcadeLab

摸鱼反应堆 加强版

by CrystalPenguin76
754 lines24.1 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>
*{margin:0;padding:0;box-sizing:border-box;-webkit-tap-highlight-color:transparent;}
body{background:#000;overflow:hidden;height:100vh;width:100vw;font-family:system-ui,sans-serif;}
canvas{display:block;width:100vw;height:100vh;}
.popup{
    position:fixed;inset:0;background:rgba(0,0,0,0.88);
    display:flex;align-items:center;justify-content:center;
    z-index:99;
}
.box{
    background:#1e1e28;color:#fff;padding:32px 24px;border-radius:18px;
    width:min(88vw,380px);text-align:center;
    border:1px solid #334;
}
.box h2{margin-bottom:16px;color:#0cf;font-size:24px;}
.box p{line-height:1.7;margin:10px 0;color:#ccc;font-size:15px;}
.btn{
    margin-top:20px;padding:13px 24px;border:none;border-radius:10px;
    background:#00e0ee;color:#000;font-size:17px;font-weight:bold;
    width:100%;transition:0.15s;
}
.btn:active{opacity:0.7;scale:0.97}
.btn-sec{
    margin-top:12px;background:#333;color:#fff;
}
.hidden{display:none !important;}
</style>
</head>
<body>
<canvas id="game"></canvas>

<!-- 开局引导弹窗 -->
<div id="guidePopup" class="popup">
    <div class="box">
        <h2>👔摸鱼反应堆 进阶版</h2>
        <p>【判断老板视线】黑色虚线指向中间=盯你,千万别点!</p>
        <p>虚线指向左右墙壁=安全,连续点击触发连击加分</p>
        <p>【道具】随机掉落隐身/双倍分/暂停老板,触碰拾取</p>
        <p>【随机事件】黑屏、极速巡查、双倍扣分,注意规避</p>
        <p>⚠️紧急会议全屏变红,连点刷高额分数!</p>
        <button class="btn" id="startBtn">开始60秒挑战</button>
    </div>
</div>

<!-- 结算弹窗 -->
<div id="endPopup" class="popup hidden">
    <div class="box">
        <h2>本局结算</h2>
        <p>最终能量:<span id="finalEnergy" style="color:#0cf;font-size:22px">0</span></p>
        <p>被抓包次数:<span id="catchCount" style="color:#f55">0</span></p>
        <p>紧急会议触发:<span id="meetingCount" style="color:#ff0">0</span></p>
        <p>最高连击:<span id="maxCombo" style="color:#3f3">0</span></p>
        <p>历史最高纪录:<span id="highScore" style="color:#0cf">0</span></p>
        <p id="rankText" style="font-size:20px;margin:12px 0;font-weight:bold">评级:F</p>
        <button class="btn" id="restartBtn">再来一局</button>
        <button class="btn btn-sec" id="shareBtn">复制分享文案</button>
    </div>
</div>

<script>
// 画布初始化
const canvas = document.getElementById('game');
const ctx = canvas.getContext('2d');
function resizeCanvas(){
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;
}
resizeCanvas();
window.addEventListener('resize',resizeCanvas);

// 音频工具
let audioCtx = null;
function initAudio(){if(!audioCtx)audioCtx = new (window.AudioContext || window.webkitAudioContext)();}
function playTone(freq,dur,type='sine',vol=0.2){
    if(!audioCtx)return;
    const osc = audioCtx.createOscillator();
    const gain = audioCtx.createGain();
    osc.connect(gain);gain.connect(audioCtx.destination);
    osc.type = type;osc.frequency.value = freq;
    gain.gain.setValueAtTime(vol,audioCtx.currentTime);
    gain.gain.exponentialRampToValueAtTime(0.001,audioCtx.currentTime+dur);
    osc.start();osc.stop(audioCtx.currentTime+dur);
}
const audio = {
    success:()=>{playTone(880,0.12,'sine',0.25);playTone(1200,0.08,'sine',0.15)},
    catch:()=>playTone(90,0.35,'triangle',0.35),
    meeting:()=>playTone(1300,0.05,'square',0.18),
    item:()=>playTone(1000,0.18,'sine',0.22),
    warn:()=>playTone(220,0.2,'sawtooth',0.2)
}

// 粒子类(星光/爆炸/火花特效)
class Particle{
    constructor(x,y,color,spdX,spdY,life,size){
        this.x=x;this.y=y;this.color=color;
        this.spdX=spdX;this.spdY=spdY;
        this.life=life;this.maxLife=life;this.size=size;
    }
    update(){
        this.x+=this.spdX;
        this.y+=this.spdY;
        this.life--;
        this.spdY+=0.02;
    }
    draw(){
        const a=this.life/this.maxLife;
        ctx.globalAlpha=a;
        ctx.fillStyle=this.color;
        ctx.beginPath();
        ctx.arc(this.x,this.y,this.size*a,0,Math.PI*2);
        ctx.fill();
        ctx.globalAlpha=1;
    }
}
let particles=[];
function spawnParticles(x,y,color,count){
    for(let i=0;i<count;i++){
        const spdX=(Math.random()-0.5)*4;
        const spdY=(Math.random()-0.5)*4-2;
        const life=30+Math.random()*40;
        const size=2+Math.random()*4;
        particles.push(new Particle(x,y,color,spdX,spdY,life,size));
    }
}

// 道具类
class Item{
    constructor(){
        this.types=['hide','shield','double','pause'];
        this.type=this.types[Math.floor(Math.random()*4)];
        this.x=80+Math.random()*(canvas.width-160);
        this.y=canvas.height*0.35+Math.random()*canvas.height*0.3;
        this.r=22;
        this.life=600;
        const map={
            hide:{name:'隐身',col:'#8cf'},
            shield:{name:'护盾',col:'#4f4'},
            double:{name:'双倍分',col:'#ff0'},
            pause:{name:'暂停老板',col:'#f8f'}
        }
        this.name=map[this.type].name;
        this.col=map[this.type].col;
    }
    draw(){
        ctx.fillStyle=this.col;
        ctx.shadowColor=this.col;
        ctx.shadowBlur=12;
        ctx.beginPath();
        ctx.arc(this.x,this.y,this.r,0,Math.PI*2);
        ctx.fill();
        ctx.shadowBlur=0;
        ctx.fillStyle='#000';
        ctx.font='bold 13px system-ui';
        ctx.textAlign='center';
        ctx.fillText(this.name,this.x,this.y+5);
    }
}
let dropItems=[];

// 游戏全局状态
const game = {
    running:false,timeLeft:60,energy:0,maxEnergy:100,
    catchTimes:0,meetingTimes:0,highScore:parseInt(localStorage.getItem('fishHigh'))||0,
    combo:0,maxCombo:0,stage:1,//阶段1/2/3 提升难度
    reactor:{x:0,y:0,baseR:70,pulse:0,pulseSpeed:0.06,meetingShake:0,level:1},
    boss:{x:0,y:60,w:80,speed:0.6,baseSpeed:0.6,dir:1,
        lookDir:'front',lookTransition:0.3,lookTimer:0,expression:'normal'
    },
    emergency:{active:false,duration:4000,timer:0,nextTimer:0,nextDelay:0},
    floatTexts:[],flashRed:0,
    //道具buff
    buff:{hide:0,shield:0,double:0,pauseBoss:0},
    //随机全局事件
    event:{type:null,timer:0},
    deskItems:[]//办公桌装饰
};

// 弹窗DOM
const guidePopup = document.getElementById('guidePopup');
const endPopup = document.getElementById('endPopup');
const startBtn = document.getElementById('startBtn');
const restartBtn = document.getElementById('restartBtn');
const shareBtn = document.getElementById('shareBtn');
const finalEnergyEl = document.getElementById('finalEnergy');
const catchCountEl = document.getElementById('catchCount');
const meetingCountEl = document.getElementById('meetingCount');
const maxComboEl = document.getElementById('maxCombo');
const highScoreEl = document.getElementById('highScore');
const rankText = document.getElementById('rankText');

// 生成办公桌静态装饰
function createDeskItems(){
    game.deskItems=[
        {type:'paper',x:60,y:canvas.height-120,w:50,h:65},
        {type:'cup',x:canvas.width-90,y:canvas.height-100,r:18},
        {type:'mouse',x:canvas.width/2+120,y:canvas.height-80,w:30,h:20},
        {type:'clock',x:canvas.width-70,y:110,r:24}
    ]
}

// 重置游戏数据
function resetGame(){
    game.running = true;
    game.timeLeft = 60;
    game.energy = 0;
    game.catchTimes = 0;
    game.meetingTimes = 0;
    game.combo = 0;
    game.maxCombo = 0;
    game.stage = 1;
    game.floatTexts = [];
    game.flashRed = 0;
    particles=[];
    dropItems=[];
    game.emergency.active = false;
    game.reactor.x = canvas.width/2;
    game.reactor.y = canvas.height/2;
    game.reactor.pulse = 0;
    game.reactor.level = 1;
    game.boss.x = game.boss.w;
    game.boss.dir = 1;
    game.boss.lookDir = 'front';
    game.boss.lookTimer = 0;
    game.boss.speed=game.boss.baseSpeed;
    game.buff={hide:0,shield:0,double:0,pauseBoss:0};
    game.event={type:null,timer:0};
    createDeskItems();
    // 随机8-12秒触发第一次紧急会议
    game.emergency.nextDelay = 8000 + Math.random()*4000;
    game.emergency.nextTimer = 0;
}

// 开始游戏
startBtn.addEventListener('click',()=>{
    initAudio();
    guidePopup.classList.add('hidden');
    resetGame();
    requestAnimationFrame(gameLoop);
});
restartBtn.addEventListener('click',()=>{
    endPopup.classList.add('hidden');
    resetGame();
    requestAnimationFrame(gameLoop);
});
shareBtn.addEventListener('click',async ()=>{
    const text = `我在摸鱼反应堆里攒了${game.energy}点能量,最高连击${game.maxCombo},你敢挑战吗?`;
    await navigator.clipboard.writeText(text);
    alert('分享文案已复制!');
});

// 绘制办公桌装饰
function drawDesk(){
    game.deskItems.forEach(d=>{
        ctx.save();
        if(d.type==='paper'){
            ctx.fillStyle='#333444';
            ctx.fillRect(d.x,d.y,d.w,d.h);
            ctx.strokeStyle='#555';
            ctx.lineWidth=1;
            ctx.strokeRect(d.x,d.y,d.w,d.h);
        }else if(d.type==='cup'){
            ctx.fillStyle='#224';
            ctx.beginPath();
            ctx.arc(d.x,d.y,d.r,0,Math.PI*2);
            ctx.fill();
        }else if(d.type==='mouse'){
            ctx.fillStyle='#222';
            ctx.fillRect(d.x,d.y,d.w,d.h);
        }else if(d.type==='clock'){
            ctx.fillStyle='#222';
            ctx.beginPath();
            ctx.arc(d.x,d.y,d.r,0,Math.PI*2);
            ctx.fill();
            ctx.fillStyle='#fff';
            ctx.font='bold 14px sans-serif';
            ctx.textAlign='center';
            ctx.fillText('60',d.x,d.y+5);
        }
        ctx.restore();
    })
}

// 优化版老板绘制:表情+旋转头部+视线射线+红光警告
function drawBoss(){
    const b = game.boss;
    if(game.buff.pauseBoss>0) ctx.globalAlpha=0.4;
    ctx.save();
    ctx.translate(b.x,b.y);
    const size = b.w;
    let rotateAngle = 0;
    let rayEndX = 0, rayEndY = -size * 4;
    // 表情匹配视线
    if(b.lookDir==='front') b.expression='angry';
    else b.expression='relax';

    if(b.lookDir === "left"){
        rotateAngle = -Math.PI / 6;
        rayEndX = -canvas.width * 0.6;
    }else if(b.lookDir === "right"){
        rotateAngle = Math.PI / 6;
        rayEndX = canvas.width * 0.6;
    }else{
        rayEndX = canvas.width/2 - b.x;
        rayEndY = canvas.height/2 - b.y;
    }

    // 正视愤怒红光高亮
    if(b.lookDir === "front"){
        ctx.shadowColor = "#ff2222";
        ctx.shadowBlur = 22;
    }

    // 西装身体
    ctx.fillStyle='#223';
    ctx.fillRect(-size/2,-size/2,size,size*0.8);

    // 头部旋转容器
    ctx.save();
    ctx.rotate(rotateAngle);
    // 脸部底色
    ctx.fillStyle='#ffe0cc';
    ctx.beginPath();ctx.arc(0,-size*0.35,size*0.22,0,Math.PI*2);ctx.fill();
    // 领带
    ctx.fillStyle='#d22';
    ctx.beginPath();
    ctx.moveTo(0,-size*0.1);
    ctx.lineTo(-size*0.08,size*0.3);
    ctx.lineTo(size*0.08,size*0.3);
    ctx.closePath();ctx.fill();
    // 眼睛&表情
    ctx.fillStyle='#000';
    const eyeY = -size*0.38;
    const eyeBaseOffset = size*0.08;
    if(b.lookDir === 'left'){
        ctx.beginPath();
        ctx.arc(-eyeBaseOffset - 8, eyeY, 4, 0, Math.PI*2);
        ctx.arc(eyeBaseOffset - 12, eyeY, 4, 0, Math.PI*2);
        ctx.fill();
    }else if(b.lookDir === 'right'){
        ctx.beginPath();
        ctx.arc(-eyeBaseOffset + 12, eyeY, 4, 0, Math.PI*2);
        ctx.arc(eyeBaseOffset + 8, eyeY, 4, 0, Math.PI*2);
        ctx.fill();
    }else{
        ctx.beginPath();
        ctx.arc(-eyeBaseOffset, eyeY, 4, 0, Math.PI*2);
        ctx.arc(eyeBaseOffset, eyeY, 4, 0, Math.PI*2);
        ctx.fill();
        // 愤怒眉毛
        ctx.strokeStyle='#000';
        ctx.lineWidth=2;
        ctx.beginPath();
        ctx.moveTo(-eyeBaseOffset-6,eyeY-8);
        ctx.lineTo(-eyeBaseOffset+6,eyeY-5);
        ctx.moveTo(eyeBaseOffset+6,eyeY-8);
        ctx.lineTo(eyeBaseOffset-6,eyeY-5);
        ctx.stroke();
    }
    // 嘴巴区分表情
    ctx.strokeStyle='#111';
    ctx.lineWidth=2;
    ctx.beginPath();
    if(b.expression==='angry') ctx.arc(0,-size*0.22,6,0,Math.PI);
    else ctx.arc(0,-size*0.22,5,Math.PI,Math.PI*2);
    ctx.stroke();
    ctx.restore();

    // 视线虚线
    ctx.strokeStyle = "rgba(0,0,0,0.75)";
    ctx.lineWidth = 3;
    ctx.setLineDash([8,6]);
    ctx.beginPath();
    ctx.moveTo(0, -size*0.35);
    ctx.lineTo(rayEndX, rayEndY);
    ctx.stroke();
    ctx.setLineDash([]);

    ctx.shadowBlur = 0;
    ctx.restore();
    ctx.globalAlpha=1;
}

// 反应堆分层绘制,随等级升级外观
function drawReactor(){
    const r = game.reactor;
    let pulseScale = 1 + Math.sin(r.pulse)*0.12;
    if(game.emergency.active) pulseScale = 1 + Math.sin(r.pulse*3)*0.25;
    const radius = r.baseR * pulseScale;
    const shakeX = game.emergency.active ? (Math.random()-0.5)*r.meetingShake : 0;
    const shakeY = game.emergency.active ? (Math.random()-0.5)*r.meetingShake : 0;
    const cx = r.x + shakeX;
    const cy = r.y + shakeY;

    // 渐变分层升级
    let grad;
    if(game.emergency.active){
        grad = ctx.createRadialGradient(cx,cy,0,cx,cy,radius);
        grad.addColorStop(0,'#ffdd00');
        grad.addColorStop(1,'#ff4400');
    }else if(r.level>=3){
        grad = ctx.createRadialGradient(cx,cy,0,cx,cy,radius);
        grad.addColorStop(0,'#0ff');
        grad.addColorStop(0.6,'#08f');
        grad.addColorStop(1,'#003');
    }else if(r.level>=2){
        grad = ctx.createRadialGradient(cx,cy,0,cx,cy,radius);
        grad.addColorStop(0,'#0cf');
        grad.addColorStop(1,'#004');
    }else{
        grad = ctx.createRadialGradient(cx,cy,0,cx,cy,radius);
        grad.addColorStop(0,'#00f0ff');
        grad.addColorStop(1,'#002266');
    }
    ctx.beginPath();
    ctx.arc(cx,cy,radius,0,Math.PI*2);
    ctx.fillStyle = grad;
    ctx.fill();

    // 多层发光
    const glowSize=15+r.level*8;
    ctx.shadowColor = game.emergency.active ? '#ff7700' : '#0cf';
    ctx.shadowBlur = glowSize;
    ctx.strokeStyle = '#fff';
    ctx.lineWidth = 2;
    ctx.stroke();
    ctx.shadowBlur = 0;

    // 能量文字+连击
    let energyColor;
    if(game.energy < 30) energyColor = '#f33';
    else if(game.energy < 70) energyColor = '#fc0';
    else energyColor = '#3f3';
    ctx.fillStyle = energyColor;
    ctx.font = 'bold 32px system-ui';
    ctx.textAlign = 'center';
    ctx.fillText(game.energy + '/' + game.maxEnergy, cx, cy - radius - 16);
    if(game.combo>1){
        ctx.fillStyle='#ff0';
        ctx.font='bold 18px sans-serif';
        ctx.fillText(`连击×${game.combo}`,cx,cy-radius-42);
    }
}

// 浮动分数文字
function drawFloatText(){
    game.floatTexts.forEach((t,i)=>{
        ctx.fillStyle = t.color;
        ctx.font = 'bold 28px system-ui';
        ctx.textAlign = 'center';
        ctx.globalAlpha = t.alpha;
        ctx.fillText(t.text,t.x,t.y);
        ctx.globalAlpha = 1;
        t.y -= 1.4;
        t.alpha -= 0.02;
        if(t.alpha <= 0) game.floatTexts.splice(i,1);
    })
}

// 绘制掉落道具
function drawItems(){
    dropItems.forEach((item,idx)=>{
        item.life--;
        if(item.life<=0){dropItems.splice(idx,1);return;}
        item.draw();
        // 检测反应堆拾取碰撞
        const d=Math.hypot(item.x-game.reactor.x,item.y-game.reactor.y);
        if(d<game.reactor.baseR+item.r){
            // 触发buff
            audio.item();
            switch(item.type){
                case 'hide':game.buff.hide=300;break;
                case 'shield':game.buff.shield=1;break;
                case 'double':game.buff.double=240;break;
                case 'pause':game.buff.pauseBoss=200;break;
            }
            spawnParticles(item.x,item.y,item.col,18);
            dropItems.splice(idx,1);
        }
    })
}

// UI面板
function drawUI(){
    // 剩余时间 左上
    ctx.fillStyle='#fff';
    ctx.font='bold 22px system-ui';
    ctx.textAlign='left';
    ctx.fillText(`剩余:${Math.ceil(game.timeLeft)}s`,20,36);
    // 当前能量 右上
    ctx.textAlign='right';
    ctx.fillText(`能量:${game.energy}/${game.maxEnergy}`,canvas.width-20,36);
    // Buff提示
    let buffY=68;
    ctx.font='16px sans-serif';
    ctx.textAlign='left';
    if(game.buff.hide>0){ctx.fillStyle='#8cf';ctx.fillText(`隐身 ${Math.ceil(game.buff.hide/60)}s`,20,buffY);buffY+=24;}
    if(game.buff.double>0){ctx.fillStyle='#ff0';ctx.fillText(`双倍得分 ${Math.ceil(game.buff.double/60)}s`,20,buffY);buffY+=24;}
    if(game.buff.pauseBoss>0){ctx.fillStyle='#f8f';ctx.fillText(`老板暂停 ${Math.ceil(game.buff.pauseBoss/60)}s`,20,buffY);buffY+=24;}
    if(game.buff.shield>0){ctx.fillStyle='#4f4';ctx.fillText(`护盾生效`,20,buffY);buffY+=24;}
}

// 背景绘制+随机事件遮罩
function drawBg(){
    // 主桌面底色
    const gradBg=ctx.createLinearGradient(0,0,0,canvas.height);
    gradBg.addColorStop(0,'#14141a');
    gradBg.addColorStop(1,'#1a1a24');
    ctx.fillStyle=gradBg;
    ctx.fillRect(0,0,canvas.width,canvas.height);
    // 磨砂噪点
    ctx.fillStyle='rgba(255,255,255,0.02)';
    for(let i=0;i<150;i++){
        const x = Math.random()*canvas.width;
        const y = Math.random()*canvas.height;
        ctx.fillRect(x,y,2,2);
    }
    // 随机事件遮罩
    if(game.event.type==='blackout'){
        ctx.fillStyle=`rgba(0,0,0,${game.event.timer/120*0.85})`;
        ctx.fillRect(0,0,canvas.width,canvas.height);
    }
    // 被抓红闪
    if(game.flashRed>0){
        ctx.fillStyle=`rgba(255,40,40,${game.flashRed*0.35})`;
        ctx.fillRect(0,0,canvas.width,canvas.height);
        game.flashRed -= 0.04;
    }
    // 紧急会议红边框
    if(game.emergency.active){
        ctx.strokeStyle='#f22';
        ctx.lineWidth=10;
        ctx.strokeRect(4,4,canvas.width-8,canvas.height-8);
    }
}

// 点击交互
function handleTap(touchX,touchY){
    if(!game.running)return;
    const r = game.reactor;
    const dist = Math.hypot(touchX - r.x, touchY - r.y);
    const hitRadius = r.baseR + 50;
    if(dist > hitRadius) return;

    // 黑屏事件屏蔽点击
    if(game.event.type==='blackout') return;

    if(game.emergency.active){
        let add=5;
        if(game.buff.double>0) add*=2;
        game.energy = Math.min(game.energy+add,game.maxEnergy);
        game.floatTexts.push({
            text:`+${add}`,x:r.x,y:r.y-40,color:'#ff0',alpha:1
        });
        spawnParticles(r.x,r.y,'#ff0',12);
        audio.meeting();
        return;
    }

    const bossLook = game.boss.lookDir;
    if(bossLook === 'front' && game.buff.hide<=0){
        // 被抓包,护盾抵消一次
        if(game.buff.shield>0){
            game.buff.shield=0;
            game.floatTexts.push({text:'护盾抵挡!',x:r.x,y:r.y-40,color:'#4f4',alpha:1});
            spawnParticles(r.x,r.y,'#4f4',15);
            return;
        }
        game.energy = Math.max(game.energy-2,0);
        game.catchTimes++;
        game.combo=0;
        game.flashRed = 1;
        game.floatTexts.push({
            text:'-2',x:r.x,y:r.y-40,color:'#f33',alpha:1
        });
        spawnParticles(r.x,r.y,'#f33',22);
        audio.catch();
        // 双倍扣分事件
        if(game.event.type==='doublePenalty'){
            game.energy=Math.max(game.energy-2,0);
            game.floatTexts.push({text:'双倍扣分!',x:r.x,y:r.y-70,color:'#f00',alpha:1});
        }
    }else{
        // 摸鱼成功连击
        game.combo++;
        if(game.combo>game.maxCombo) game.maxCombo=game.combo;
        let add=2;
        if(game.combo>=5) add+=1;
        if(game.buff.double>0) add*=2;
        game.energy = Math.min(game.energy+add,game.maxEnergy);
        game.floatTexts.push({
            text:`+${add}`,x:r.x,y:r.y-40,color:'#3f3',alpha:1
        });
        spawnParticles(r.x,r.y,'#0ff',10);
        audio.success();
        // 反应堆升级
        if(game.energy>=30&&game.reactor.level<2) game.reactor.level=2;
        if(game.energy>=70&&game.reactor.level<3) game.reactor.level=3;
    }
}

// 触摸绑定
canvas.addEventListener('touchstart',e=>{
    e.preventDefault();
    const t = e.touches[0];
    handleTap(t.clientX,t.clientY);
})
canvas.addEventListener('mousedown',e=>handleTap(e.clientX,e.clientY));

// 随机全局事件生成
function spawnRandomEvent(){
    const evts=['blackout','fastBoss','doublePenalty'];
    const sel=evts[Math.floor(Math.random()*3)];
    game.event.type=sel;
    game.event.timer=180;
    audio.warn();
}

// 主游戏循环
let lastTime = 0;
let itemSpawnTimer=0;
let eventSpawnTimer=0;
function gameLoop(ts){
    const dt = (ts - lastTime)/1000;
    lastTime = ts;
    if(!game.running)return;

    // 倒计时
    game.timeLeft -= dt;
    if(game.timeLeft <= 0){
        game.running = false;
        if(game.energy > game.highScore){
            game.highScore = game.energy;
            localStorage.setItem('fishHigh',game.highScore);
        }
        // 结算评级
        let rank='F';
        if(game.energy>=90)rank='SS';
        else if(game.energy>=70)rank='S';
        else if(game.energy>=50)rank='A';
        else if(game.energy>=30)rank='B';
        else if(game.energy>=10)rank='C';
        // 赋值弹窗
        finalEnergyEl.textContent = game.energy;
        catchCountEl.textContent = game.catchTimes;
        meetingCountEl.textContent = game.meetingTimes;
        maxComboEl.textContent = game.maxCombo;
        highScoreEl.textContent = game.highScore;
        rankText.textContent=`评级:${rank}`;
        endPopup.classList.remove('hidden');
        return;
    }

    // 阶段难度升级
    if(game.timeLeft<40&&game.stage===1){game.stage=2;game.boss.speed=game.boss.baseSpeed*1.4;}
    if(game.timeLeft<20&&game.stage===2){game.stage=3;game.boss.speed=game.boss.baseSpeed*1.8;}

    // Buff倒计时
    if(game.buff.hide>0)game.buff.hide--;
    if(game.buff.double>0)game.buff.double--;
    if(game.buff.pauseBoss>0)game.buff.pauseBoss--;

    // 随机事件计时
    if(game.event.timer>0){
        game.event.timer--;
        if(game.event.timer<=0) game.event.type=null;
    }
    eventSpawnTimer+=dt;
    if(eventSpawnTimer>12&&!game.event.type&&!game.emergency.active){
        spawnRandomEvent();
        eventSpawnTimer=0;
    }

    // 道具生成
    itemSpawnTimer+=dt;
    if(itemSpawnTimer>7&&dropItems.length<3){
        dropItems.push(new Item());
        itemSpawnTimer=0;
    }

    // 反应堆脉动
    game.reactor.pulse += game.reactor.pulseSpeed;

    // 老板移动逻辑(暂停buff阻断)
    const b = game.boss;
    if(game.buff.pauseBoss<=0){
        b.x += b.speed * b.dir;
        const boundRight = canvas.width - b.w/2;
        const boundLeft = b.w/2;
        if(b.x >= boundRight){
            b.dir = -1;
            b.lookDir = 'right';
        }else if(b.x <= boundLeft){
            b.dir = 1;
            b.lookDir = 'left';
        }else{
            b.lookTimer += dt;
            let switchGap;
            if(game.stage===1) switchGap=2.2+Math.random()*1.8;
            else if(game.stage===2) switchGap=1.4+Math.random()*1.2;
            else switchGap=0.8+Math.random()*0.8;
            if(b.lookTimer > switchGap){
                b.lookDir = 'front';
                setTimeout(()=>{
                    if(b.dir>0)b.lookDir='left';
                    else b.lookDir='right';
                },b.lookTransition*1000);
                b.lookTimer = 0;
            }
        }
    }

    // 紧急会议计时器
    game.emergency.nextTimer += dt*1000;
    if(!game.emergency.active && game.emergency.nextTimer >= game.emergency.nextDelay){
        game.emergency.active = true;
        game.emergency.timer = game.emergency.duration;
        game.meetingTimes++;
    }
    if(game.emergency.active){
        game.emergency.timer -= dt*1000;
        if(game.emergency.timer <= 0){
            game.emergency.active = false;
            game.emergency.nextTimer = 0;
            game.emergency.nextDelay = 8000 + Math.random()*4000;
        }
    }

    // 更新粒子
    particles.forEach((p,i)=>{
        p.update();
        if(p.life<=0) particles.splice(i,1);
    })

    // 渲染顺序
    drawBg();
    drawDesk();
    drawReactor();
    if(!game.emergency.active) drawBoss();
    drawItems();
    particles.forEach(p=>p.draw());
    drawFloatText();
    drawUI();

    requestAnimationFrame(gameLoop);
}
</script>
</body>
</html>

Game Source: 摸鱼反应堆 加强版

Creator: CrystalPenguin76

Libraries: none

Complexity: complex (754 lines, 24.1 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-crystalpenguin76-mr1i0krp" to link back to the original. Then publish at arcadelab.ai/publish.