迷你三角洲‑V5
by SuperFlare63521 lines24.9 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, user‑scalable=no">
<title>迷你三角洲‑V5</title>
<style>
*{margin:0;padding:0;box‑sizing:border‑box}
body{background:#0b0b0b;overflow:hidden;font‑family:sans‑webkit,sans‑serif;color:white}
canvas{display:block;width:100vw;height:100vh;touch‑action:none}
#ui{position:fixed;inset:0;pointer‑events:none}
.btn{pointer‑events:auto;background:rgba(30,120,220,.5);border:2px solid #7af;color:#fff;font‑size:14px;border‑radius:10px;padding:8px}
#joystick{position:absolute;left:30px;bottom:40px;width:120px;height:120px;border‑radius:50%;background:rgba(255,255,255,.15);border:2px solid rgba(255,255,255,.3);pointer‑events:auto}
#stick{width:50px;height:50px;border‑radius:50%;background:rgba(80,180,255,.6);position:absolute;left:35px;top:35px}
#fireBtn{position:absolute;right:30px;bottom:70px;width:90px;height:90px;border‑radius:50%;background:rgba(220,40,40,.5);border:2px solid #f66;color:white;font‑size:18px;pointer‑events:auto}
#switchGun{position:absolute;right:140px;bottom:90px;width:70px;height:70px;border‑radius:12px;background:rgba(40,120,220,.5);border:2px solid #77bbff;color:#fff;font‑size:13px;pointer‑events:auto}
#healBtn{position:absolute;right:140px;bottom:175px;width:70px;height:70px;border‑radius:12px;background:rgba(255,255,255,.35);border:2px solid #fff;color:#fff;font‑size:13px;pointer‑events:auto}
#minimap{position:absolute;top:15px;right:15px;width:130px;height:130px;border:2px solid rgba(255,255,255,.4);background:rgba(0,0,0,.4);pointer‑events:auto}
#hp{position:absolute;top:15px;left:15px;font‑size:18px;text‑shadow:1px 1px #000}
#armor{position:absolute;top:38px;left:15px;font‑size:17px;text‑shadow:1px 1px #000}
#gunName{position:absolute;top:60px;left:15px;font‑size:16px;text‑shadow:1px 1px #000}
#kill{position:absolute;top:82px;left:15px;font‑size:15px;text‑shadow:1px 1px #000}
#extractText{position:absolute;top:104px;left:15px;font‑size:15px;color:#7f7;text‑shadow:1px 1px #000}
#medText{position:absolute;top:126px;left:15px;font‑size:15px;color:#f9c;text‑shadow:1px 1px #000}
#missionText{position:absolute;top:148px;left:15px;font‑size:14px;color:#ffd;text‑shadow:1px 1px #000;max‑width:220px}
#timerText{position:absolute;top:170px;left:15px;font‑size:15px;color:#f55;text‑shadow:1px 1px #000}
#menu{position:fixed;inset:0;background:#131822;display:flex;flex‑direction:column;justify‑content:flex‑end;padding:30px;pointer‑events:none}
#menu button{pointer‑events:auto;width:130px;height:60px;font‑size:16px;margin:8px;border‑radius:10px;border:none;color:white}
#btnStore{background:#287}
#btnStash{background:#269}
#btnStart{background:#c33;margin‑left:auto}
#overlay{position:fixed;inset:0;background:rgba(0,0,0,.85);display:none;padding:16px;overflow‑y:auto;pointer‑events:auto}
#overlay h2{margin‑bottom:12px}
.item{padding:10px;border‑bottom:1px solid #444;display:flex;justify‑content:space‑between;align‑items:center}
.hidden{display:none !important}
</style>
</head>
<body>
<canvas id="game"></canvas>
<canvas id="minimap"></canvas>
<div id="ui">
<div id="joystick"><div id="stick"></div></div>
<button id="fireBtn">开火</button>
<button id="switchGun">换枪</button>
<button id="healBtn">药品</button>
<div id="hp">生命:100</div>
<div id="armor">护甲:0</div>
<div id="gunName">武器:步枪</div>
<div id="kill">击杀:0</div>
<div id="extractText">等待撤离开启</div>
<div id="medText">药品:0</div>
<div id="missionText">任务:无</div>
<div id="timerText">撤离倒计时:‑‑</div>
</div>
<div id="menu">
<button id="btnStash">仓库</button>
<button id="btnStore">商店</button>
<button id="btnStart">开始行动</button>
</div>
<div id="overlay">
<h2 id="ovTitle">仓库</h2>
<div id="ovList"></div>
<button onclick="closeOverlay()" style="margin‑top:16px;padding:10px;width:100%">关闭</button>
</div>
<script>
const canvas=document.getElementById("game");
const ctx=canvas.getContext("2d");
const mm=document.getElementById("minimap");
const mmCtx=mm.getContext("2d");
let W,H;
function resize(){W=canvas.width=innerWidth;H=canvas.height=innerHeight;mm.width=130;mm.height=130}
resize();addEventListener("resize",resize);
// =====全局数据=====
const WORLD={w:3200,h:3200};
let camera={x:0,y:0};
let inGame=false;
let minimapZoom=false;
//仓库存档
let save=JSON.parse(localStorage.getItem("deltaSave"))||{
money:800,
stashWeapons:[{id:"rifle",mods:{muzzle:null,mag:null,sight:null}}],
stashArmors:[],
meds:3,
stashMods:{muzzle:0,mag:0,sight:0}
};
function saveData(){localStorage.setItem("deltaSave",JSON.stringify(save))}
//武器定义,新增霰弹枪、机枪
const gunDef={
rifle:{name:"步枪",damage:12,speed:9,rate:12,spread:0.06,price:100,bullets:1},
smg:{name:"冲锋枪",damage:7,speed:11,rate:5,spread:0.11,price:120,bullets:1},
sniper:{name:"狙击枪",damage:45,speed:16,rate:45,spread:0.015,price:260,bullets:1},
shotgun:{name:"霰弹枪",damage:9,speed:8,rate:32,spread:0.22,price:220,bullets:6},
lmg:{name:"轻机枪",damage:10,speed:9,rate:7,spread:0.09,price:300,bullets:1}
};
//配件
const modDef={
muzzle:{name:"消焰器",spread:‑0.02,price:80},
mag:{name:"扩容弹匣",rate:‑2,price:70},
sight:{name:"高倍瞄具",spread:‑0.015,price:90}
};
//敌人类型
const enemyTypes={
normal:{hp:30,speed:1.2,range:340,dmg:8,color:"#882222"},
rush:{hp:45,speed:2.4,range:400,dmg:14,color:"#dd5500"},
sniper:{hp:22,speed:0.7,range:520,dmg:22,color:"#6622aa"},
boss:{hp:400,speed:1.0,range:460,dmg:18,color:"#aa1111",r:28}
};
//任务库
const missionPool=[
{desc:"击杀10名敌人",target:10,type:"kill"},
{desc:"击杀20名敌人",target:20,type:"kill"},
{desc:"成功撤离一次",target:1,type:"extract"}
];
let currentMission=null,missionProgress=0;
//音效
let audioCtx;
function initAudio(){if(!audioCtx)audioCtx=new (window.AudioContext||window.webkitAudioContext)()}
function playGunSound(){
if(!audioCtx)return;const o=audioCtx.createOscillator(),g=audioCtx.createGain();
o.type="square";o.frequency.setValueAtTime(220,audioCtx.currentTime);
o.frequency.exponentialRampToValueAtTime(60,audioCtx.currentTime+0.08);
g.gain.setValueAtTime(0.18,audioCtx.currentTime);g.gain.exponentialRampToValueAtTime(0.001,audioCtx.currentTime+0.09);
o.connect(g);g.connect(audioCtx.destination);o.start();o.stop(audioCtx.currentTime+0.09);
}
function playFootstep(){
if(!audioCtx)return;const o=audioCtx.createOscillator(),g=audioCtx.createGain();
o.type="sine";o.frequency.setValueAtTime(90,audioCtx.currentTime);
g.gain.setValueAtTime(0.06,audioCtx.currentTime);g.gain.exponentialRampToValueAtTime(0.001,audioCtx.currentTime+0.07);
o.connect(g);g.connect(audioCtx.destination);o.start();o.stop(audioCtx.currentTime+0.07);
}
let player,bullets=[],enemies=[],enemyBullets=[],airDrops=[],cover=[],groundMods=[];
let extractPoint,extractActive=false,extractTimer=0,extractCountdown=0,extractDuration=60*20;
let currentGunIdx=0,fireCooldown=0,recoil=0;
let spawnTimer=0,stepTimer=0;
//摇杆
const joy=document.getElementById("joystick");
const stick=document.getElementById("stick");
let jx=0,jy=0;
joy.addEventListener("touchstart",e=>{initAudio();e.preventDefault()});
joy.addEventListener("touchmove",e=>{
const t=e.touches[0];const r=joy.getBoundingClientRect();
let dx=t.clientX‑(r.left+r.width/2),dy=t.clientY‑(r.top+r.height/2);
const dist=Math.hypot(dx,dy),max=40;
if(dist>max){dx=dx/dist*max;dy=dy/dist*max}
stick.style.left=(35+dx)+"px";stick.style.top=(35+dy)+"px";
jx=dx/max;jy=dy/max;e.preventDefault();
});
joy.addEventListener("touchend",()=>{jx=0;jy=0;stick.style.left="35px";stick.style.top="35px"});
const fireBtn=document.getElementById("fireBtn");let shooting=false;
fireBtn.addEventListener("touchstart",e=>{initAudio();shooting=true;e.preventDefault()});
fireBtn.addEventListener("touchend",()=>shooting=false);
document.getElementById("switchGun").addEventListener("touchstart",e=>{
if(!inGame)return;currentGunIdx=(currentGunIdx+1)%save.stashWeapons.length;e.preventDefault();
});
document.getElementById("healBtn").addEventListener("touchstart",e=>{
if(!inGame||player.meds<=0||player.hp>=100)return;
player.meds‑‑;player.hp=Math.min(100,player.hp+35);e.preventDefault();
});
//小地图点击放大
mm.addEventListener("touchstart",()=>{minimapZoom=!minimapZoom});
//主菜单按钮
document.getElementById("btnStart").onclick=startGame;
document.getElementById("btnStore").onclick=openStore;
document.getElementById("btnStash").onclick=openStash;
const overlay=document.getElementById("overlay"),ovTitle=document.getElementById("ovTitle"),ovList=document.getElementById("ovList");
function closeOverlay(){overlay.style.display="none"}
function openStash(){
ovTitle.innerText="仓库";ovList.innerHTML="";
ovList.innerHTML+="<div class='item'><span>金币:"+save.money+"</span></div>";
ovList.innerHTML+="<div class='item'><span>药品:"+save.meds+"</span></div>";
ovList.innerHTML+="<div class='item'><span>消焰器:"+save.stashMods.muzzle+"</span></div>";
ovList.innerHTML+="<div class='item'><span>扩容弹匣:"+save.stashMods.mag+"</span></div>";
ovList.innerHTML+="<div class='item'><span>高倍瞄具:"+save.stashMods.sight+"</span></div>";
save.stashWeapons.forEach((w,i)=>{
const d=gunDef[w.id];
ovList.innerHTML+=`<div class="item"><span>武器‑${d.name} 消焰:${w.mods.muzzle?'是':'否'} 弹匣:${w.mods.mag?'是':'否'} 瞄具:${w.mods.sight?'是':'否'}</span><button onclick="addMod(${i})">加装配件</button></div>`;
});
overlay.style.display="block";
}
function addMod(idx){
const w=save.stashWeapons[idx];
let added=false;
for(let k in modDef){
if(!w.mods[k] && save.stashMods[k]>0){
save.stashMods[k]‑=1;
w.mods[k]=true;
added=true;
break;
}
}
if(!added){
for(let k in modDef){
if(!w.mods[k]){
if(save.money>=modDef[k].price){
save.money‑=modDef[k].price;w.mods[k]=true;saveData();openStash();return;
}
}
}
alert("配件已满或者金币、库存配件不足");
return;
}
saveData();openStash();
}
function openStore(){
ovTitle.innerText="商店";ovList.innerHTML="";
ovList.innerHTML+="<div class='item'><span>金币:"+save.money+"</span></div>";
for(let gid in gunDef){
const g=gunDef[gid];
ovList.innerHTML+=`<div class="item"><span>${g.name} ${g.price}金币</span><button onclick="buyGun('${gid}')">购买</button></div>`;
}
ovList.innerHTML+=`<div class="item"><span>药品x1‑30金币</span><button onclick="buyMed()">购买</button></div>`;
ovList.innerHTML+=`<div class="item"><span>护甲‑80金币</span><button onclick="buyArmor()">购买</button></div>`;
ovList.innerHTML+=`<div class="item"><span>消焰器‑80金币</span><button onclick="buyMod('muzzle')">购买</button></div>`;
ovList.innerHTML+=`<div class="item"><span>扩容弹匣‑70金币</span><button onclick="buyMod('mag')">购买</button></div>`;
ovList.innerHTML+=`<div class="item"><span>高倍瞄具‑90金币</span><button onclick="buyMod('sight')">购买</button></div>`;
overlay.style.display="block";
}
window.buyGun=function(id){
if(save.money>=gunDef[id].price){
save.money‑=gunDef[id].price;
save.stashWeapons.push({id:id,mods:{muzzle:null,mag:null,sight:null}});
saveData();openStore();
}else alert("金币不足");
};
window.buyMed=function(){
if(save.money>=30){save.money‑=30;save.meds++;saveData();openStore()}else alert("金币不足")
};
window.buyArmor=function(){
if(save.money>=80){save.money‑=80;save.stashArmors.push(50);saveData();openStore()}else alert("金币不足")
};
window.buyMod=function(k){
if(save.money>=modDef[k].price){
save.money‑=modDef[k].price;
save.stashMods[k]+=1;
saveData();openStore();
}else alert("金币不足");
};
//生成掩体
function buildCovers(){
cover=[];
for(let i=0;i<35;i++){
const x=150+Math.random()*(WORLD.w‑300),y=150+Math.random()*(WORLD.h‑300);
const w=30+Math.random()*70,h=30+Math.random()*70;
cover.push({x,y,w,h,type:Math.random()<0.5?"rock":"building"});
}
}
//地面配件生成
function spawnGroundMods(){
groundMods=[];
const keys=["muzzle","mag","sight"];
for(let i=0;i<18;i++){
const k=keys[Math.floor(Math.random()*keys.length)];
groundMods.push({
x:120+Math.random()*(WORLD.w‑240),
y:120+Math.random()*(WORLD.h‑240),
r:14,
kind:k
});
}
}
function startGame(){
document.getElementById("menu").classList.add("hidden");
inGame=true;bullets=[];enemies=[];enemyBullets=[];airDrops=[];
extractTimer=0;extractActive=false;extractCountdown=0;
player={x:WORLD.w/2,y:WORLD.h/2,r:16,speed:3,hp:100,armor:save.stashArmors.pop()||0,kill:0,angle:0,meds:save.meds};
save.meds=0;saveData();
extractPoint={x:WORLD.w*0.8,y:WORLD.h*0.2,r:60};
buildCovers();
spawnGroundMods();
currentGunIdx=0;fireCooldown=0;recoil=0;
//随机本局任务
currentMission=missionPool[Math.floor(Math.random()*missionPool.length)];
missionProgress=0;
}
function endGame(victory){
inGame=false;
save.meds=player.meds;
save.stashArmors.push(player.armor);
save.money+=player.kill*15;
//任务奖励
if(currentMission){
let done=false;
if(currentMission.type==="kill" && missionProgress>=currentMission.target) done=true;
if(currentMission.type==="extract" && victory) done=true;
if(done) save.money+=250;
}
saveData();
document.getElementById("menu").classList.remove("hidden");
}
function spawnEnemy(){
const side=Math.floor(Math.random()*4);let ex,ey;
if(side===0){ex=Math.random()*WORLD.w;ey=‑20}
else if(side===1){ex=WORLD.w+20;ey=Math.random()*WORLD.h}
else if(side===2){ex=Math.random()*WORLD.w;ey=WORLD.h+20}
else {ex=‑20;ey=Math.random()*WORLD.h}
let type="normal";
const r=Math.random();
if(player.kill>=12&&r<0.06)type="boss";
else if(r<0.24)type="rush";
else if(r<0.42)type="sniper";
const t=enemyTypes[type];
enemies.push({x:ex,y:ey,r:t.r||15,hp:t.hp,speed:t.speed,shootCd:Math.random()*60,type:type,range:t.range,dmg:t.dmg});
}
function spawnAirdrop(){
const r=Math.random();let loot;
if(r<0.35)loot="armor";else if(r<0.65)loot="med";else loot="gun";
airDrops.push({x:80+Math.random()*(WORLD.w‑160),y:80+Math.random()*(WORLD.h‑160),r:22,lootType:loot});
}
function rectCircleHit(rect,cx,cy,cr){
const nx=Math.max(rect.x,Math.min(cx,rect.x+rect.w));
const ny=Math.max(rect.y,Math.min(cy,rect.y+rect.h));
return (cx‑nx)*(cx‑nx)+(cy‑ny)*(cy‑ny)<cr*cr;
}
function loop(){
requestAnimationFrame(loop);
ctx.fillStyle="#000";ctx.fillRect(0,0,W,H);
if(!inGame){return}
camera.x=player.x‑W/2;camera.y=player.y‑H/2;
//世界地面
ctx.save();
ctx.translate(‑camera.x,‑camera.y);
ctx.fillStyle="#3a4a32";ctx.fillRect(0,0,WORLD.w,WORLD.h);
ctx.fillStyle="#2e3d27";
for(let i=0;i<400;i++){ctx.fillRect((i*97)%WORLD.w,(i*53)%WORLD.h,3,3)}
//围墙
ctx.strokeStyle="#553322";ctx.lineWidth=16;ctx.strokeRect(0,0,WORLD.w,WORLD.h);
//掩体
for(let c of cover){
ctx.fillStyle=c.type==="rock"?"#777":"#543";
ctx.fillRect(c.x,c.y,c.w,c.h);
}
//地面配件
for(let i=groundMods.length‑1;i>=0;i‑‑){
const m=groundMods[i];
ctx.fillStyle=m.kind==="muzzle"?"#ff8":m.kind==="mag"?"#8ff":"#f8f";
ctx.beginPath();ctx.arc(m.x,m.y,m.r,0,Math.PI*2);ctx.fill();
if(Math.hypot(player.x‑m.x,player.y‑m.y)<player.r+m.r){
save.stashMods[m.kind]+=1;
saveData();
groundMods.splice(i,1);
}
}
//玩家移动
const vx=jx*player.speed,vy=jy*player.speed;
player.x+=vx;player.y+=vy;
player.x=Math.max(player.r,Math.min(WORLD.w‑player.r,player.x));
player.y=Math.max(player.r,Math.min(WORLD.h‑player.r,player.y));
if(Math.abs(vx)+Math.abs(vy)>0.1){stepTimer++;if(stepTimer>22){playFootstep();stepTimer=0}}
//瞄准最近敌人
let near=null,nd=9999;
for(let e of enemies){const d=Math.hypot(e.x‑player.x,e.y‑player.y);if(d<nd){nd=d;near=e}}
if(near)player.angle=Math.atan2(near.y‑player.y,near.x‑player.x);
//后坐力衰减
recoil*=0.82;
fireCooldown‑‑;
const weapon=save.stashWeapons[currentGunIdx];
const base=gunDef[weapon.id];
let spread=base.spread,reloadRate=base.rate;
if(weapon.mods.muzzle)spread+=modDef.muzzle.spread;
if(weapon.mods.sight)spread+=modDef.sight.spread;
if(weapon.mods.mag)reloadRate+=modDef.mag.rate;
if(shooting&&fireCooldown<=0){
for(let b=0;b<base.bullets;b++){
const spray=player.angle+(Math.random()‑0.5)*spread+recoil*0.025;
bullets.push({
x:player.x+Math.cos(spray)*18,y:player.y+Math.sin(spray)*18,
vx:Math.cos(spray)*base.speed,vy:Math.sin(spray)*base.speed,
damage:base.damage,life:180
});
}
fireCooldown=reloadRate;recoil+=1;playGunSound();
}
//玩家子弹
for(let i=bullets.length‑1;i>=0;i‑‑){
const b=bullets[i];b.x+=b.vx;b.y+=b.vy;b.life‑‑;
let dead=false;
if(b.life<=0||b.x<0||b.y<0||b.x>WORLD.w||b.y>WORLD.h)dead=true;
for(let c of cover){if(rectCircleHit(c,b.x,b.y,3))dead=true;break}
if(dead){bullets.splice(i,1);continue}
ctx.fillStyle="#ffdd33";ctx.beginPath();ctx.arc(b.x,b.y,4,0,Math.PI*2);ctx.fill();
}
//敌人
for(let i=enemies.length‑1;i>=0;i‑‑){
const e=enemies[i];
const dx=player.x‑e.x,dy=player.y‑e.y,d=Math.hypot(dx,dy);
if(e.type==="rush"||e.type==="boss"){e.x+=(dx/d)*e.speed;e.y+=(dy/d)*e.speed}
else{if(d>130){e.x+=(dx/d)*e.speed;e.y+=(dy/d)*e.speed}}
e.shootCd‑‑;
if(e.shootCd<=0&&d<e.range){
enemyBullets.push({x:e.x,y:e.y,vx:(dx/d)*5,vy:(dy/d)*5,life:200,dmg:e.dmg});
e.shootCd=(e.type==="sniper")?140:90;
}
for(let j=bullets.length‑1;j>=0;j‑‑){
const b=bullets[j];
if(Math.hypot(b.x‑e.x,b.y‑e.y)<e.r){e.hp‑=b.damage;bullets.splice(j,1);break}
}
if(e.hp<=0){
enemies.splice(i,1);player.kill++;
missionProgress=player.kill;
if(Math.random()<0.18)player.meds++;
continue;
}
ctx.fillStyle=enemyTypes[e.type].color;
ctx.beginPath();ctx.arc(e.x,e.y,e.r,0,Math.PI*2);ctx.fill();
}
//敌方子弹
for(let i=enemyBullets.length‑1;i>=0;i‑‑){
const b=enemyBullets[i];b.x+=b.vx;b.y+=b.vy;b.life‑‑;
let dead=false;
if(b.life<=0||b.x<0||b.y<0||b.x>WORLD.w||b.y>WORLD.h)dead=true;
for(let c of cover){if(rectCircleHit(c,b.x,b.y,5)){dead=true;break}}
if(dead){enemyBullets.splice(i,1);continue}
if(Math.hypot(b.x‑player.x,b.y‑player.y)<player.r){
let dmg=b.dmg;
if(player.armor>0){const absorb=Math.min(player.armor,dmg);player.armor‑=absorb;dmg‑=absorb}
player.hp‑=dmg;enemyBullets.splice(i,1);continue;
}
ctx.fillStyle="#ff5555";ctx.beginPath();ctx.arc(b.x,b.y,5,0,Math.PI*2);ctx.fill();
}
//空投
for(let i=airDrops.length‑1;i>=0;i‑‑){
const a=airDrops[i];
ctx.fillStyle="#cc8822";ctx.fillRect(a.x‑a.r,a.y‑a.r,a.r*2,a.r*2);
ctx.strokeStyle="#fff";ctx.lineWidth=2;ctx.strokeRect(a.x‑a.r,a.y‑a.r,a.r*2,a.r*2);
if(Math.hypot(player.x‑a.x,player.y‑a.y)<a.r+player.r){
if(a.lootType==="armor")player.armor=Math.min(100,player.armor+50);
else if(a.lootType==="med")player.meds+=2;
else {
const keys=Object.keys(gunDef);
const rid=keys[Math.floor(Math.random()*keys.length)];
save.stashWeapons.push({id:rid,mods:{muzzle:null,mag:null,sight:null}});
saveData();
}
airDrops.splice(i,1);
}
}
//撤离
extractTimer++;
if(extractTimer>1200&&!extractActive){
extractActive=true;
extractCountdown=extractDuration;
}
if(extractActive){
extractCountdown‑‑;
ctx.strokeStyle="#33ff55";ctx.lineWidth=3;
ctx.beginPath();ctx.arc(extractPoint.x,extractPoint.y,extractPoint.r,0,Math.PI*2);ctx.stroke();
ctx.fillStyle="#33ff55";ctx.font="18px sans‑serif";ctx.textAlign="center";
ctx.fillText("撤离点",extractPoint.x,extractPoint.y);
if(Math.hypot(player.x‑extractPoint.x,player.y‑extractPoint.y)<extractPoint.r){
ctx.fillStyle="rgba(0,0,0,.7)";ctx.fillRect(camera.x,camera.y,W,H);
ctx.fillStyle="#55ff77";ctx.font="38px sans‑serif";ctx.textAlign="center";
ctx.fillText("成功撤离!任务胜利",camera.x+W/2,camera.y+H/2‑25);
ctx.fillStyle="#fff";ctx.font="24px sans‑serif";
ctx.fillText("总击杀:"+player.kill,camera.x+W/2,camera.y+H/2+20);
ctx.restore();
setTimeout(()=>endGame(true),1800);
return;
}
//倒计时归零撤离关闭
if(extractCountdown<=0){
ctx.fillStyle="rgba(0,0,0,.7)";ctx.fillRect(camera.x,camera.y,W,H);
ctx.fillStyle="#ff5555";ctx.font="36px sans‑serif";ctx.textAlign="center";
ctx.fillText("撤离时间结束,撤离失败",camera.x+W/2,camera.y+H/2);
ctx.restore();
setTimeout(()=>endGame(false),1800);
return;
}
}
//绘制玩家
ctx.save();ctx.translate(player.x,player.y);ctx.rotate(player.angle);
ctx.fillStyle="#3377cc";ctx.beginPath();ctx.arc(0,0,player.r,0,Math.PI*2);ctx.fill();
ctx.fillStyle="#222";ctx.fillRect(8,‑3,22,6);ctx.restore();
ctx.restore();
//生成敌人空投
spawnTimer++;
if(spawnTimer>120&&enemies.length<16){spawnEnemy();spawnTimer=0}
if(player.kill>0&&player.kill%8===0&&airDrops.length<2)spawnAirdrop();
//小地图
let mmS=minimapZoom?260:130;mm.width=mmS;mm.height=mmS;
mmCtx.clearRect(0,0,mmS,mmS);
mmCtx.fillStyle="#2d3b26";mmCtx.fillRect(0,0,mmS,mmS);
const sx=mmS/WORLD.w,sy=mmS/WORLD.h;
if(extractActive){mmCtx.fillStyle="#3f5";mmCtx.beginPath();mmCtx.arc(extractPoint.x*sx,extractPoint.y*sy,5,0,Math.PI*2);mmCtx.fill()}
mmCtx.fillStyle="#c82";for(let a of airDrops)mmCtx.fillRect(a.x*sx‑2,a.y*sy‑2,4,4);
for(let m of groundMods){
mmCtx.fillStyle="#ff00ff";
mmCtx.fillRect(m.x*sx‑1,m.y*sy‑1,3,3);
}
for(let e of enemies){
if(e.type==="rush")mmCtx.fillStyle="#f72";else if(e.type==="sniper")mmCtx.fillStyle="#a4f";else if(e.type==="boss")mmCtx.fillStyle="#f11";else mmCtx.fillStyle="#f33";
mmCtx.fillRect(e.x*sx‑2,e.y*sy‑2,4,4);
}
mmCtx.fillStyle="#4af";mmCtx.beginPath();mmCtx.arc(player.x*sx,player.y*sy,4,0,Math.PI*2);mmCtx.fill();
//UI
const g=gunDef[save.stashWeapons[currentGunIdx].id];
document.getElementById("hp").innerText="生命:"+Math.max(0,player.hp);
document.getElementById("armor").innerText="护甲:"+player.armor;
document.getElementById("gunName").innerText="武器:"+g.name;
document.getElementById("kill").innerText="击杀:"+player.kill;
document.getElementById("extractText").innerText=extractActive?"撤离点已开启!前往绿色圆圈撤离":"等待撤离开启";
document.getElementById("medText").innerText="药品:"+player.meds;
if(currentMission){
document.getElementById("missionText").innerText="任务:"+currentMission.desc+" ("+missionProgress+"/"+currentMission.target+")";
}
if(extractActive){
const sec=Math.floor(extractCountdown/20);
document.getElementById("timerText").innerText="撤离倒计时:"+sec+"秒";
}else{
document.getElementById("timerText").innerText="撤离倒计时:‑‑";
}
if(player.hp<=0){
ctx.fillStyle="rgba(0,0,0,.65)";ctx.fillRect(0,0,W,H);
ctx.fillStyle="#fff";ctx.font="36px sans‑serif";ctx.textAlign="center";
ctx.fillText("任务失败!击杀:"+player.kill,W/2,H/2);
setTimeout(()=>endGame(false),1800);
}
}
loop();
</script>
</body>
</html>Game Source: 迷你三角洲‑V5
Creator: SuperFlare63
Libraries: none
Complexity: complex (521 lines, 24.9 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: v5-superflare63" to link back to the original. Then publish at arcadelab.ai/publish.