重制主武器/僚机/治疗反馈

- 新增追踪主武器:蜜蜂手雷、叶绿弹
- 蜂针刺弹/月牙斩/落雷术/火雨/电弧手里剑改为追踪或偏转
- 主武器数值 buff:蜂针x3、圣光x5、月牙攻速x2、泡泡x5等
- 副武器/僚机合并:移除 beamdrone/repair/frostd/teslaD/sweeper
- 僚机重做:机枪高速连射、狙击自瞄穿透、导弹追踪爆炸、全体伤害加强
- 治疗反馈:绿十字粒子+血条绿闪+数字
- 同步 CODEX_KEYS 白名单
This commit is contained in:
JOJO 2026-08-06 20:40:23 +08:00
parent 18cf37aa7a
commit 676f4344ed
8 changed files with 173 additions and 100 deletions

View File

@ -114,8 +114,13 @@ function updateBullets(){
} }
if(b.trailCol&&G.time%2===0)G.parts.push({x:b.x,y:b.y,vx:rnd(-0.2,0.2),vy:0.4,life:12,col:b.trailCol,sz:1}); if(b.trailCol&&G.time%2===0)G.parts.push({x:b.x,y:b.y,vx:rnd(-0.2,0.2),vy:0.4,life:12,col:b.trailCol,sz:1});
if(b.t==="nb"&&G.time%4===0)G.parts.push({x:b.x+rnd(-4,4),y:b.y+rnd(-4,4),vx:rnd(-0.3,0.3),vy:rnd(-0.3,0.3),life:14,col:pick(["#b98cff","#e0b3ff"]),sz:1}); if(b.t==="nb"&&G.time%4===0)G.parts.push({x:b.x+rnd(-4,4),y:b.y+rnd(-4,4),vx:rnd(-0.3,0.3),vy:rnd(-0.3,0.3),life:14,col:pick(["#b98cff","#e0b3ff"]),sz:1});
// 电弧手里剑:一路前飞不停留,全程周期性放电 // 电弧手里剑:引力偏转靠向敌人,全程周期性放电
if(b.t==="shk"&&!b.dead){ if(b.t==="shk"&&!b.dead){
const tg2=nearestEnemy(b.x,b.y,130);
if(tg2){const a=Math.atan2(tg2.y-b.y,tg2.x-b.x),c=Math.atan2(b.vy,b.vx);
let d4=a-c;while(d4>Math.PI)d4-=TAU;while(d4<-Math.PI)d4+=TAU;
const na=c+clamp(d4,-(b.deflect||0.04),b.deflect||0.04),s=Math.hypot(b.vx,b.vy);
b.vx=Math.cos(na)*s;b.vy=Math.sin(na)*s;}
b.pt2=(b.pt2||0)-1; b.pt2=(b.pt2||0)-1;
if(b.pt2<=0){b.pt2=b.pulse||20; if(b.pt2<=0){b.pt2=b.pulse||20;
const tg=nearEnemies(b.x,b.y,55,3); const tg=nearEnemies(b.x,b.y,55,3);
@ -126,11 +131,11 @@ function updateBullets(){
let pz=!!nearestEnemy(b.x,b.y,26); let pz=!!nearestEnemy(b.x,b.y,26);
if(!pz&&bossHit(b.x,b.y,26))pz=true; if(!pz&&bossHit(b.x,b.y,26))pz=true;
if(pz){b.dead=true;explodeOnce(b);}} if(pz){b.dead=true;explodeOnce(b);}}
if(b.t==="c"&&!b.dead&&b.y<H*0.5){ // 月牙斩:进入上半屏后弧线向中线聚拢 if(b.t==="bg"&&!b.dead){ // 蜜蜂手雷:近炸引信
const ca=Math.atan2(b.vy,b.vx),ta=Math.atan2(-60-b.y,(W/2)-b.x); const tg=nearestEnemy(b.x,b.y,38);
let d4=ta-ca;while(d4>Math.PI)d4-=TAU;while(d4<-Math.PI)d4+=TAU; if(tg){b.dead=true;explodeOnce(b);}
const na=ca+clamp(d4,-0.035,0.035),ss=Math.hypot(b.vx,b.vy); else{b.fuse=(b.fuse||0)-1;if(b.fuse<=0){b.dead=true;explodeOnce(b);}}
b.vx=Math.cos(na)*ss;b.vy=Math.sin(na)*ss;} }
if(!b.back&&b.bt!==undefined){b.bt--;if(b.bt<=0&&!b.dead){b.dead=true;explodeOnce(b);}} if(!b.back&&b.bt!==undefined){b.bt--;if(b.bt<=0&&!b.dead){b.dead=true;explodeOnce(b);}}
if(b.t==="mt"&&b.ty2!==undefined&&b.y<=b.ty2&&!b.dead){b.dead=true;explodeOnce(b);} if(b.t==="mt"&&b.ty2!==undefined&&b.y<=b.ty2&&!b.dead){b.dead=true;explodeOnce(b);}
if(b.t==="s"&&!b.dead){ // 分裂弹:飞行一段距离后自动分裂 if(b.t==="s"&&!b.dead){ // 分裂弹:飞行一段距离后自动分裂
@ -211,7 +216,8 @@ function collide(){
} }
if(b.pierce){if(!b.hits)b.hits={};const u2=e.uid||(e.uid=_uid++); if(b.pierce){if(!b.hits)b.hits={};const u2=e.uid||(e.uid=_uid++);
if(b.hits[u2]!==undefined)continue; if(b.hits[u2]!==undefined)continue;
b.hits[u2]=1;} b.hits[u2]=1;b.phc=(b.phc||0)+1;
if(b.hitsMax&&b.phc>=b.hitsMax){b.dead=true;}}
else b.dead=true; else b.dead=true;
if(b.slow)e.slow=Math.max(e.slow||0,b.slow); if(b.slow)e.slow=Math.max(e.slow||0,b.slow);
if(b.freeze){e.frozen=Math.max(e.frozen||0,b.freeze);e.slow=0;explodeFx(b.x,b.y,"ice",0.5);} if(b.freeze){e.frozen=Math.max(e.frozen||0,b.freeze);e.slow=0;explodeFx(b.x,b.y,"ice",0.5);}
@ -238,7 +244,7 @@ function collide(){
if(!b.dead&&G.boss){ if(!b.dead&&G.boss){
const B=G.boss; const B=G.boss;
if(bossHit(b.x,b.y,b.r)){ if(bossHit(b.x,b.y,b.r)){
if(b.pierce){if(!b.hits)b.hits={};if(!b.hits.boss){b.hits.boss=1;hurtBoss(b.dmg);}} if(b.pierce){if(!b.hits)b.hits={};if(!b.hits.boss){b.hits.boss=1;hurtBoss(b.dmg);b.phc=(b.phc||0)+1;if(b.hitsMax&&b.phc>=b.hitsMax)b.dead=true;}}
else{b.dead=true; else{b.dead=true;
if(b.t==="sg"||b.t==="ho"||b.t==="bb"||b.t==="am"||b.t==="tb"||b.t==="tb2"||b.t==="dw"||b.t==="ph"||b.t==="nb"||b.t==="ex"||b.t==="lm"||b.t==="lb")explodeOnce(b); if(b.t==="sg"||b.t==="ho"||b.t==="bb"||b.t==="am"||b.t==="tb"||b.t==="tb2"||b.t==="dw"||b.t==="ph"||b.t==="nb"||b.t==="ex"||b.t==="lm"||b.t==="lb")explodeOnce(b);
else hurtBoss(b.dmg);} else hurtBoss(b.dmg);}
@ -294,7 +300,10 @@ function collide(){
k.dead=true; k.dead=true;
if(k.kind==="gem"){addXp(G.mutator==="ARMOR"?1.3:1);SFX.pick();} if(k.kind==="gem"){addXp(G.mutator==="ARMOR"?1.3:1);SFX.pick();}
else if(k.kind==="coin"){addCoins(1);SFX.coin();} else if(k.kind==="coin"){addCoins(1);SFX.coin();}
else if(k.kind==="heart"){if(G.hp<G.maxHp){G.hp=Math.min(G.maxHp,G.hp+35);banner("结构修复 +35","#5cff9d",60);}else if(G.lives<G.stat.maxLives){G.lives++;banner("备用机体 +1","#5cff9d",60);}else addScore(500,p.x,p.y);SFX.pick();} else if(k.kind==="heart"){
if(G.hp<G.maxHp){healPlayer(35);}
else if(G.lives<G.stat.maxLives){G.lives++;banner("备用机体 +1","#5cff9d",60);SFX.pick();}
else{addScore(500,p.x,p.y);SFX.pick();}}
else if(k.kind==="odc"){G.od=Math.min(100,G.od+25);SFX.pick();} else if(k.kind==="odc"){G.od=Math.min(100,G.od+25);SFX.pick();}
} }
if(k.y>H+10)k.dead=true; if(k.y>H+10)k.dead=true;

View File

@ -25,6 +25,7 @@ function newRun(mode,shipKey,diff){
luck:(meta.luck||0),mutator:null,mutList:[], luck:(meta.luck||0),mutator:null,mutList:[],
time:0,boss:null,bossN:0,bossKills:0,shake:0,hitstop:0,flash:0,slowmo:0, time:0,boss:null,bossN:0,bossKills:0,shake:0,hitstop:0,flash:0,slowmo:0,
weapons:{vulcan:1},loadout:["vulcan"],subs:{},subLoadout:[],drones:{},droneLoadout:[], weapons:{vulcan:1},loadout:["vulcan"],subs:{},subLoadout:[],drones:{},droneLoadout:[],
healFlash:0,healAmt:0,
wt:{},wst:{},sst:{},dst:{},zones:[],zaps:[],novas:[],flares:[],holies:[],shocks:[],bbeams:[],funnels:[], wt:{},wst:{},sst:{},dst:{},zones:[],zaps:[],novas:[],flares:[],holies:[],shocks:[],bbeams:[],funnels:[],
u:{fire:0,crit:0,shield:0,nano:0,mag:0,bounty:0,odcore:0,ghost:0}, u:{fire:0,crit:0,shield:0,nano:0,mag:0,bounty:0,odcore:0,ghost:0},
enemies:[],pb:[],eb:[],parts:[],picks:[],texts:[],lasers:[], enemies:[],pb:[],eb:[],parts:[],picks:[],texts:[],lasers:[],
@ -132,6 +133,24 @@ function pbul(x,y,vx,vy,dmg,o){
if(!isFinite(x)||!isFinite(y)||!isFinite(vx)||!isFinite(vy))return; // NaN 防护 if(!isFinite(x)||!isFinite(y)||!isFinite(vx)||!isFinite(vy))return; // NaN 防护
G.pb.push(Object.assign({x,y,vx,vy,dmg,r:3,t:"v",life:400,pierce:false,hit:null},o||{})); G.pb.push(Object.assign({x,y,vx,vy,dmg,r:3,t:"v",life:400,pierce:false,hit:null},o||{}));
} }
/* ================= 治疗反馈(绿十字+血条绿闪+数字) ================= */
function healPlayer(amount,quiet){
if(!amount||amount<=0)return;
const old=G.hp;
G.hp=Math.min(G.maxHp,G.hp+amount);
const actual=G.hp-old;
if(actual<=0)return;
G.healFlash=40;
G.healAmt=(G.healAmt||0)+actual;
if(!quiet){
G.texts.push({x:G.p.x,y:G.p.y-14,t:"+"+Math.round(G.healAmt),col:"#5cff9d",life:40});
for(let i=0;i<5;i++)G.parts.push({x:G.p.x+rnd(-6,6),y:G.p.y+rnd(-6,6),vx:rnd(-0.8,0.8),vy:rnd(-2,-0.6),life:28+rndi(0,14),col:"#5cff9d",sz:2,cross:true});
G.healAmt=0;
SFX.pick();
}else if(G.time%4===0){
G.parts.push({x:G.p.x+rnd(-6,6),y:G.p.y+rnd(-6,6),vx:rnd(-0.8,0.8),vy:rnd(-2,-0.6),life:28+rndi(0,14),col:"#5cff9d",sz:2,cross:true});
}
}
/* ================= 武器引擎 ================= */ /* ================= 武器引擎 ================= */
function hurtT(t,dmg,crit){if(G.boss&&t===G.boss)hurtBoss(dmg);else hurtEnemy(t,dmg,crit);} function hurtT(t,dmg,crit){if(G.boss&&t===G.boss)hurtBoss(dmg);else hurtEnemy(t,dmg,crit);}
function nearEnemies(x,y,r,n){ function nearEnemies(x,y,r,n){
@ -307,6 +326,13 @@ function explodeBullet(b){
for(let i=0;i<ns;i++){const a=i/ns*TAU+rnd(-0.15,0.15); for(let i=0;i<ns;i++){const a=i/ns*TAU+rnd(-0.15,0.15);
pbul(b.x,b.y,Math.cos(a)*2.6,Math.sin(a)*2.6,b.dmg*0.3,{t:"st",spr:"star4",r:2,life:44});} pbul(b.x,b.y,Math.cos(a)*2.6,Math.sin(a)*2.6,b.dmg*0.3,{t:"st",spr:"star4",r:2,life:44});}
explodeFx(b.x,b.y,"nebulafx",1);SFX.boom(false); explodeFx(b.x,b.y,"nebulafx",1);SFX.boom(false);
}else if(b.t==="bg"){ // 蜜蜂手雷:爆炸分裂追踪蜜蜂
areaDamage(b.x,b.y,b.aoe||16,b.dmg);
explodeFx(b.x,b.y,"boom",0.6);
const bees=b.bees||5;
for(let i=0;i<bees;i++){const a=i/bees*TAU+rnd(-0.3,0.3);
pbul(b.x,b.y,Math.cos(a)*2.8,Math.sin(a)*2.8,b.beeDmg||b.dmg*0.4,{t:"bee",spr:"bee",r:2,homing:true,maxSpd:4.5,life:90,trailCol:"#ffd23d"});}
SFX.boom(false);
} }
} }
function aimShot(x,y,dmg,spd,spr,slow){ function aimShot(x,y,dmg,spd,spr,slow){
@ -330,6 +356,12 @@ function fireWeapons(){
pbul(p.x+off*(st.gap||6),p.y-8,Math.cos(a)*st.spd,Math.sin(a)*st.spd,st.dmg*wdm,{r:3,spr:w.spr,poison:st.poison,slow:st.slow});} pbul(p.x+off*(st.gap||6),p.y-8,Math.cos(a)*st.spd,Math.sin(a)*st.spd,st.dmg*wdm,{r:3,spr:w.spr,poison:st.poison,slow:st.slow});}
SFX.shoot();} SFX.shoot();}
break; break;
case "needle": // 蜂针刺弹:追踪剧毒飞刀
if(G.wt[wk]<=0){G.wt[wk]=Math.max(2,st.int/fm);
for(let i=0;i<st.n;i++){const off=i-(st.n-1)/2;
pbul(p.x+off*(st.gap||4),p.y-8,0,-st.spd,st.dmg*wdm,{t:"nd",spr:"ndl",r:3,homing:true,maxSpd:st.spd,poison:st.poison});}
SFX.shoot();}
break;
case "flame": // 周期性宽幅喷吐:喷射 burst 帧 → 停歇 cd 帧 case "flame": // 周期性宽幅喷吐:喷射 burst 帧 → 停歇 cd 帧
if(S.on>0){S.on--; if(S.on>0){S.on--;
for(let i=0;i<st.n;i++)pbul(p.x+rnd(-5,5),p.y-10,rnd(-2.1,2.1),-rnd(2.4,3.4),st.dmg*wdm,{t:"f",life:Math.round(st.range/3),pierce:true,r:3}); for(let i=0;i<st.n;i++)pbul(p.x+rnd(-5,5),p.y-10,rnd(-2.1,2.1),-rnd(2.4,3.4),st.dmg*wdm,{t:"f",life:Math.round(st.range/3),pierce:true,r:3});
@ -361,10 +393,10 @@ function fireWeapons(){
if(t2)G.lasers.push({x1:p.x,y1:p.y-8,x2:t2.x,y2:t2.y,w:1,life:2,col:"#7a2a3a"});} if(t2)G.lasers.push({x1:p.x,y1:p.y-8,x2:t2.x,y2:t2.y,w:1,life:2,col:"#7a2a3a"});}
if(S.c>=st.charge){S.c=0;S.beam=st.beam;SFX.od();}} if(S.c>=st.charge){S.c=0;S.beam=st.beam;SFX.od();}}
break; break;
case "crescent": // 巨型旋刃仅前方±30°两个方向自身旋转贯穿 case "crescent": // 月牙斩追踪旋刃命中3次后消失
if(G.wt[wk]<=0){G.wt[wk]=Math.max(20,st.int/fm); if(G.wt[wk]<=0){G.wt[wk]=Math.max(20,st.int/fm);
for(const sd of[-1,1]){const ca=-Math.PI/2+sd*Math.PI/6; for(const sd of[-1,1]){const ca=-Math.PI/2+sd*Math.PI/6;
pbul(p.x+sd*6,p.y-8,Math.cos(ca)*st.spd,Math.sin(ca)*st.spd,st.dmg*wdm,{t:"c",spr:"cres",pierce:true,r:6,spin:true,scale:1.8});} pbul(p.x+sd*6,p.y-8,Math.cos(ca)*st.spd,Math.sin(ca)*st.spd,st.dmg*wdm,{t:"c",spr:"cres",pierce:true,r:6,spin:true,scale:1.8,homing:true,maxSpd:st.spd,hitsMax:3});}
SFX.shoot();} SFX.shoot();}
break; break;
case "fission": case "fission":
@ -391,9 +423,12 @@ function fireWeapons(){
if(G.wt[wk]<=0){G.wt[wk]=Math.max(6,st.int/fm); if(G.wt[wk]<=0){G.wt[wk]=Math.max(6,st.int/fm);
pbul(p.x,p.y-10,rnd(-0.3,0.3),-st.spd,st.dmg*wdm,{t:"bb",spr:"bub",aoe:st.aoe,pierce:true,life:150,r:6});} pbul(p.x,p.y-10,rnd(-0.3,0.3),-st.spd,st.dmg*wdm,{t:"bb",spr:"bub",aoe:st.aoe,pierce:true,life:150,r:6});}
break; break;
case "starfall": // 流星自屏幕底部逆冲而起 case "starfall": // 流星火雨:瞄准敌人位置从天而降
if(G.wt[wk]<=0){G.wt[wk]=Math.max(70,st.int/fm); if(G.wt[wk]<=0){G.wt[wk]=Math.max(70,st.int/fm);
for(let i=0;i<st.n;i++){const mx=clamp(p.x+rnd(-70,70),10,W-10),my=clamp(p.y-rnd(60,160),20,H-20); for(let i=0;i<st.n;i++){
const t=nearestEnemy(p.x,p.y,260);
const mx=t?clamp(t.x+rnd(-20,20),10,W-10):clamp(p.x+rnd(-70,70),10,W-10);
const my=t?clamp(t.y-rnd(20,60),20,H-40):clamp(p.y-rnd(60,160),20,H-20);
pbul(mx,H+8,rnd(-0.2,0.2),-rnd(4.5,6),st.dmg*wdm,{t:"mt",spr:"met",aoe:st.aoe,ty2:my,r:4,trailCol:"#ff9a3d",rot:true});} pbul(mx,H+8,rnd(-0.2,0.2),-rnd(4.5,6),st.dmg*wdm,{t:"mt",spr:"met",aoe:st.aoe,ty2:my,r:4,trailCol:"#ff9a3d",rot:true});}
SFX.boom(false);} SFX.boom(false);}
break; break;
@ -420,17 +455,20 @@ function fireWeapons(){
if(G.wt[wk]<=0){G.wt[wk]=st.int; if(G.wt[wk]<=0){G.wt[wk]=st.int;
pbul(p.x,p.y-10,0,-st.spd,st.dmg*wdm,{t:"ho",spr:"holyo",len:st.len,life:90,r:4});} pbul(p.x,p.y-10,0,-st.spd,st.dmg*wdm,{t:"ho",spr:"holyo",len:st.len,life:90,r:4});}
break; break;
case "thunder": case "thunder": // 落雷术:瞄准敌人头顶劈落
if(G.wt[wk]<=0){G.wt[wk]=Math.max(30,st.int/fm); if(G.wt[wk]<=0){G.wt[wk]=Math.max(30,st.int/fm);
for(let i=0;i<st.n;i++){const sx=clamp(p.x+rnd(-70,70),10,W-10),sy=rnd(20,p.y-40); for(let i=0;i<st.n;i++){
const t=nearestEnemy(p.x,p.y,260);
const sx=t?clamp(t.x+rnd(-10,10),10,W-10):clamp(p.x+rnd(-70,70),10,W-10);
const sy=t?clamp(t.y,10,H-40):rnd(20,p.y-40);
zapVisual(sx,-4,sx,sy,"#ffe95c");zapVisual(sx+rnd(-10,10),-4,sx,sy,"#43e8ff"); zapVisual(sx,-4,sx,sy,"#ffe95c");zapVisual(sx+rnd(-10,10),-4,sx,sy,"#43e8ff");
areaDamage(sx,sy,st.aoe,st.dmg*wdm); areaDamage(sx,sy,st.aoe,st.dmg*wdm);
explodeFx(sx,sy,"elec",0.7);} explodeFx(sx,sy,"elec",0.7);}
G.shake=Math.max(G.shake,3);SFX.boom(false);} G.shake=Math.max(G.shake,3);SFX.boom(false);}
break; break;
case "shuriken": // 电弧手里剑:不停留,一路向前飞行放电 case "shuriken": // 电弧手里剑:偏转靠向敌人,一路放电
if(G.wt[wk]<=0){G.wt[wk]=Math.max(40,st.int/fm); if(G.wt[wk]<=0){G.wt[wk]=Math.max(40,st.int/fm);
pbul(p.x,p.y-10,0,-st.spd,st.dmg*wdm,{t:"shk",spr:"shuri",pierce:true,r:5,spin:true,life:400,pulse:st.pulse,zapDmg:st.zapDmg*wdm}); pbul(p.x,p.y-10,0,-st.spd,st.dmg*wdm,{t:"shk",spr:"shuri",pierce:true,r:5,spin:true,life:400,pulse:st.pulse,zapDmg:st.zapDmg*wdm,deflect:st.deflect||0.04});
SFX.shoot();} SFX.shoot();}
break; break;
case "volt": // 电能机炮:高频连发,命中连锁电弧(连锁在 collide 结算) case "volt": // 电能机炮:高频连发,命中连锁电弧(连锁在 collide 结算)
@ -467,6 +505,18 @@ function fireWeapons(){
pbul(p.x,p.y-10,0,-st.spd,st.dmg*wdm,{t:"nb",spr:"neb",r:6,softH:true,stars:st.stars,trailCol:"#b98cff"}); pbul(p.x,p.y-10,0,-st.spd,st.dmg*wdm,{t:"nb",spr:"neb",r:6,softH:true,stars:st.stars,trailCol:"#b98cff"});
SFX.shoot();} SFX.shoot();}
break; break;
case "beegrenade": // 蜜蜂手雷向前两发近炸分裂5只蜜蜂
if(G.wt[wk]<=0){G.wt[wk]=Math.max(30,st.int/fm);
for(let i=0;i<st.n;i++){const off=i-(st.n-1)/2;
pbul(p.x+off*10,p.y-8,off*0.4,-st.spd,st.dmg*wdm,{t:"bg",spr:"beegrenade",r:4,aoe:st.aoe,fuse:st.fuse,bees:st.bees,beeDmg:st.beeDmg*wdm,trailCol:"#ffd23d"});}
SFX.shoot();}
break;
case "chlorophyte": // 叶绿弹:高射速追踪,带绿色轨迹
if(G.wt[wk]<=0){G.wt[wk]=Math.max(3,st.int/fm);
for(let i=0;i<st.n;i++){const off=i-(st.n-1)/2;
pbul(p.x+off*(st.gap||5),p.y-8,off*0.3,-st.spd,st.dmg*wdm,{t:"cl",spr:"chloro",r:3,homing:true,trailCol:"#5cff9d"});}
SFX.shoot();}
break;
} }
} }
/* ---------- 副武器 ---------- */ /* ---------- 副武器 ---------- */
@ -535,14 +585,6 @@ function fireWeapons(){
pbul(S.x,S.y,Math.cos(a)*5,Math.sin(a)*5,(4+2*lv)*dm,{r:2});}} pbul(S.x,S.y,Math.cos(a)*5,Math.sin(a)*5,(4+2*lv)*dm,{r:2});}}
else{S.x=lerp(S.x,p.x,0.05);S.y=lerp(S.y,p.y-26,0.05);} else{S.x=lerp(S.x,p.x,0.05);S.y=lerp(S.y,p.y-26,0.05);}
break;} break;}
case "beamdrone": {
const a=G.time*0.03;S.x=p.x+Math.cos(a)*30;S.y=p.y+Math.sin(a)*30;
if(S.on>0){S.on--;
const t=nearestEnemy(S.x,S.y,220);
if(t){G.lasers.push({x1:S.x,y1:S.y,x2:t.x,y2:t.y,w:3,life:2,col:"#ff6b9d"});
if(G.time%3===0)hurtT(t,(6+3*lv)*dm*0.34,false);}}
else{S.t=(S.t||0)-1;if(S.t<=0){S.on=22;S.t=(140-15*lv)/fm;}}
break;}
case "frostnova": S.t=(S.t||0)-1; case "frostnova": S.t=(S.t||0)-1;
if(S.t<=0){S.t=(260-30*lv)/fm; if(S.t<=0){S.t=(260-30*lv)/fm;
const r=50+10*lv; const r=50+10*lv;
@ -574,11 +616,6 @@ function fireWeapons(){
pbul(p.x+sd*8,p.y-2,sd*2.5,0,(16+5*lv)*dm,{t:"am",spr:"amsl",phase:1,pt:0,zone:16,r:3,trailCol:"#9aa4b8",rot:true,pierce:true}); pbul(p.x+sd*8,p.y-2,sd*2.5,0,(16+5*lv)*dm,{t:"am",spr:"amsl",phase:1,pt:0,zone:16,r:3,trailCol:"#9aa4b8",rot:true,pierce:true});
SFX.shoot();} SFX.shoot();}
break; break;
case "repair": S.t=(S.t||0)-1;
if(S.t<=0){S.t=(30-4*lv)*60;
if(G.hp<G.maxHp){G.hp=Math.min(G.maxHp,G.hp+1);
G.texts.push({x:p.x,y:p.y-12,t:"+1",col:"#5cff9d",life:30});}}
break;
case "thorns": break; // 被动反伤,在碰撞中结算 case "thorns": break; // 被动反伤,在碰撞中结算
} }
} }
@ -591,38 +628,50 @@ function fireWeapons(){
S.t=(S.t||0)-1; S.t=(S.t||0)-1;
const db=G.stat.droneBoost; const db=G.stat.droneBoost;
switch(dd.beh){ switch(dd.beh){
case "aim": if(S.t<=0){S.t=Math.max(18,(34-2*lv)/fm);aimShot(S.x,S.y,(4+2*lv)*dm*db,5);}break; case "burst": { // 机枪僚机:高速连射+随机散布
case "aim2": if(S.t<=0){S.t=Math.max(20,(40-2*lv)/fm);aimShot(S.x-3,S.y,(3+2*lv)*dm*db,5);aimShot(S.x+3,S.y,(3+2*lv)*dm*db,5);}break; if(S.t<=0){S.burst=(S.burst||0)-1;
case "snipe": if(S.t<=0){S.t=Math.max(40,(70-4*lv)/fm);aimShot(S.x,S.y,(15+8*lv)*dm*db,7);}break; if(S.burst>0){const spread=0.28,a=-Math.PI/2+rnd(-spread,spread);
pbul(S.x,S.y,Math.cos(a)*4.6,Math.sin(a)*4.6,(5+3*lv)*dm*db,{r:2,spr:"swep"});
S.t=Math.max(3,5/fm);}
else{S.burst=4+lv;S.t=Math.max(10,(28-3*lv)/fm);}}
break;}
case "burst2": { // 双翼机枪手:双管高速连射
if(S.t<=0){S.burst=(S.burst||0)-1;
if(S.burst>0){const spread=0.24,a=-Math.PI/2+rnd(-spread,spread);
pbul(S.x-3,S.y,Math.cos(a)*4.6,Math.sin(a)*4.6,(4+2.5*lv)*dm*db,{r:2,spr:"swep"});
pbul(S.x+3,S.y,Math.cos(a)*4.6,Math.sin(a)*4.6,(4+2.5*lv)*dm*db,{r:2,spr:"swep"});
S.t=Math.max(3,5/fm);}
else{S.burst=4+lv;S.t=Math.max(12,(32-3*lv)/fm);}}
break;}
case "snipe": { // 狙击僚机:自瞄高伤穿透
if(S.t<=0){S.t=Math.max(45,(80-8*lv)/fm);
const t=nearestEnemy(S.x,S.y,280);
if(t){const d=Math.hypot(t.x-S.x,t.y-S.y)||1;
const vx=(t.x-S.x)/d*7.5,vy=(t.y-S.y)/d*7.5;
pbul(S.x,S.y,vx,vy,(35+22*lv)*dm*db,{r:3,spr:"dSnip",pierce:true,hitsMax:4,trailCol:"#43e8ff"});}}
break;}
case "beam": case "beam":
if(S.on>0){S.on--;const t=nearestEnemy(S.x,S.y,200); if(S.on>0){S.on--;const t=nearestEnemy(S.x,S.y,220);
if(t){G.lasers.push({x1:S.x,y1:S.y,x2:t.x,y2:t.y,w:3,life:2,col:"#ff6b9d"}); if(t){G.lasers.push({x1:S.x,y1:S.y,x2:t.x,y2:t.y,w:3,life:2,col:"#ff6b9d"});
if(G.time%3===0)hurtT(t,(5+3*lv)*dm*0.34,false);}} if(G.time%3===0)hurtT(t,(10+5*lv)*dm*db*0.34,false);}}
else if(S.t<=0){S.on=20;S.t=(140-15*lv)/fm;} else if(S.t<=0){S.on=25;S.t=(120-12*lv)/fm;}
break; break;
case "bomb": if(S.t<=0){S.t=(150-12*lv)/fm;pbul(S.x,S.y,0,-2.5,(12+6*lv)*dm*db,{t:"tb2",spr:"bombp",bt:40,aoe:24,r:3});}break; case "bomb": if(S.t<=0){S.t=(110-10*lv)/fm;
case "frostD": if(S.t<=0){S.t=Math.max(24,(44-3*lv)/fm);aimShot(S.x,S.y,(3+2*lv)*dm*db,5,"frost",60+20*lv);}break; pbul(S.x,S.y,0,-2.5,(22+13*lv)*dm*db,{t:"tb2",spr:"bombp",bt:40,aoe:32+5*lv,r:3});}break;
case "teslaD": if(S.t<=0){S.t=Math.max(30,(55-4*lv)/fm);
const t=nearestEnemy(S.x,S.y,85);
if(t){zapVisual(S.x,S.y,t.x,t.y,"#ffe95c");hurtT(t,(6+3*lv)*dm*db,true);}}break;
case "melee": { // 冲撞僚机:加大环绕半径与判定 case "melee": { // 冲撞僚机:加大环绕半径与判定
const a2=G.time*0.07+i*(TAU/Math.max(1,dn)); const a2=G.time*0.07+i*(TAU/Math.max(1,dn));
S.x=p.x+Math.cos(a2)*38;S.y=p.y+Math.sin(a2)*38; S.x=p.x+Math.cos(a2)*38;S.y=p.y+Math.sin(a2)*38;
for(const e of G.enemies)if(!e.dead&&dist2(S.x,S.y,e.x,e.y)<(e.r+10)*(e.r+10)&&G.time-(e.ramT||0)>22){e.ramT=G.time;hurtEnemy(e,(8+4*lv)*dm*db,true);} for(const e of G.enemies)if(!e.dead&&dist2(S.x,S.y,e.x,e.y)<(e.r+12)*(e.r+12)&&G.time-(e.ramT||0)>18){e.ramT=G.time;hurtEnemy(e,(20+12*lv)*dm*db,true);}
if(G.boss&&bossHit(S.x,S.y,10)&&G.time-(G.boss.ramT||0)>22){G.boss.ramT=G.time;hurtBoss((8+4*lv)*dm*db);} if(G.boss&&bossHit(S.x,S.y,12)&&G.time-(G.boss.ramT||0)>18){G.boss.ramT=G.time;hurtBoss((20+12*lv)*dm*db);}
break;} break;}
case "heal": if(S.t<=0){S.t=(40-6*lv)*60; case "heal": if(S.t<=0){S.t=(8-2*lv)*60;
if(G.hp<G.maxHp){G.hp=Math.min(G.maxHp,G.hp+2);G.texts.push({x:p.x,y:p.y-12,t:"+2",col:"#5cff9d",life:30});}}break; healPlayer(8+2*lv);}break;
case "block": for(const b of G.eb){if(b.dead)continue; case "block": for(const b of G.eb){if(b.dead)continue;
if(dist2(S.x,S.y,b.x,b.y)<110){b.dead=true;spawnPart(b.x,b.y,"#43e8ff",2);}}break; if(dist2(S.x,S.y,b.x,b.y)<130){b.dead=true;spawnPart(b.x,b.y,"#43e8ff",2);}}break;
case "missile": if(S.t<=0){S.t=(90-8*lv)/fm;pbul(S.x,S.y,rnd(-1,1),-2.5,(10+5*lv)*dm*db,{t:"m",spr:"msl",homing:true,r:3});}break; case "missile": if(S.t<=0){S.t=(80-10*lv)/fm;
case "bladeD": if(S.t<=0){S.t=(80-6*lv)/fm;pbul(S.x,S.y,0,-4,(8+4*lv)*dm*db,{t:"b",spr:"blade",pierce:true,back:true,r:4,bt:34});}break; pbul(S.x,S.y,rnd(-0.6,0.6),-2.8,(18+10*lv)*dm*db,{t:"m",spr:"dMsl",homing:true,r:4,aoe:18+6*lv,trailCol:"#ffb84d"});}break;
case "sweep": // 扫射僚机:周期性一串弹从左到右/从右到左扫过前方扇区 case "bladeD": if(S.t<=0){S.t=(55-5*lv)/fm;
if(S.sw>0){S.sw--; pbul(S.x,S.y,0,-4,(16+9*lv)*dm*db,{t:"b",spr:"blade",pierce:true,back:true,r:4,bt:34});}break;
if(S.sw%2===0){const pr=1-S.sw/14,sa=-Math.PI/2+(pr-0.5)*1.5*(S.dir||1);
pbul(S.x,S.y,Math.cos(sa)*5.2,Math.sin(sa)*5.2,(3+1.5*lv)*dm*db,{r:2,spr:"swep"});}
}else if(S.t<=0){S.dir=S.dir===1?-1:1;S.sw=14;S.t=Math.max(50,(110-8*lv)/fm);}
break;
} }
} }
} }

View File

@ -12,62 +12,67 @@ const SHIPS={
asura:{name:"修罗",spr:"ship4",cost:5000,trait:"暴击+15% · 伤害+10% · 命-1", asura:{name:"修罗",spr:"ship4",cost:5000,trait:"暴击+15% · 伤害+10% · 命-1",
mod:{spd:1,dmg:1.1,lives:-1,crit:0.15,dodge:0,hitR:0,mag:1,bombs:1,shield:0,drone:0,droneBoost:1}} mod:{spd:1,dmg:1.1,lives:-1,crit:0.15,dodge:0,hitR:0,mag:1,bombs:1,shield:0,drone:0,droneBoost:1}}
}; };
/* ================= 25 ================= /* ================= 27 =================
beh: 行为引擎类型 lvl(w): 该等级的战斗参数 */ beh: 行为引擎类型 lvl(w): 该等级的战斗参数 */
const WEAPONS={ const WEAPONS={
vulcan:{name:"火神机炮",spr:"trcr",rare:0,beh:"streams",ds:"!?神神?!", vulcan:{name:"火神机炮",spr:"trcr",rare:0,beh:"streams",ds:"!?神神?!",
lvl:w=>({n:1+Math.floor(w/2),dmg:7*(1+.22*(w-1)),int:8,spd:5.4,gap:6})}, lvl:w=>({n:1+Math.floor(w/2),dmg:9*(1+.22*(w-1)),int:8,spd:5.4,gap:6})},
shotgun:{name:"超级霰弹",spr:"pelt",rare:0,beh:"streams",ds:"“友谊颗粒”", shotgun:{name:"超级霰弹",spr:"pelt",rare:0,beh:"streams",ds:"“友谊颗粒”",
lvl:w=>({n:5+2*w,dmg:4.2*(1+.18*(w-1)),int:22,spd:5.5,fan:0.5,gap:2,skipMid:1})}, lvl:w=>({n:5+2*w,dmg:5*(1+.18*(w-1)),int:22,spd:5.5,fan:0.5,gap:2,skipMid:1})},
needle:{name:"蜂针刺弹",spr:"ndl",rare:0,beh:"streams",ds:"这可是吐了剧毒的飞刀", needle:{name:"蜂针刺弹",spr:"ndl",rare:0,beh:"needle",ds:"这可是吐了剧毒的飞刀",
lvl:w=>({n:1+Math.floor(w/3),dmg:2.6*(1+.2*(w-1)),int:3,spd:7,gap:4,poison:100})}, lvl:w=>({n:1+Math.floor(w/3),dmg:7.8*(1+.2*(w-1)),int:3,spd:7,gap:4,poison:100})},
flame:{name:"火焰喷射器",icon:"flame",rare:0,beh:"flame",ds:"让火焰净化一切", flame:{name:"火焰喷射器",icon:"flame",rare:0,beh:"flame",ds:"让火焰净化一切",
lvl:w=>({n:3+Math.floor(w/2),dmg:1.1*(1+.22*(w-1)),range:70+8*w,burst:34+4*w,cd:Math.max(46,74-5*w)})}, lvl:w=>({n:3+Math.floor(w/2),dmg:1.2*(1+.22*(w-1)),range:90+8*w,burst:44+4*w,cd:Math.max(40,74-5*w)})},
shotgun2:{name:"霜冻霰弹",spr:"shard",rare:1,beh:"streams",ds:"冷冷冷", shotgun2:{name:"霜冻霰弹",spr:"shard",rare:1,beh:"streams",ds:"冷冷冷",
lvl:w=>({n:3+w,dmg:3*(1+.18*(w-1)),int:23,spd:5.2,fan:0.42,gap:2,slow:80+25*w})}, lvl:w=>({n:3+w,dmg:3.2*(1+.18*(w-1)),int:23,spd:5.2,fan:0.42,gap:2,slow:80+25*w})},
tesla:{name:"电弧发生器",icon:"chain",rare:1,beh:"tesla",ds:"?!电电!?", tesla:{name:"电弧发生器",icon:"chain",rare:1,beh:"tesla",ds:"?!电电!?",
lvl:w=>({n:1+Math.floor(w/2),dmg:8*(1+.25*(w-1)),int:44-2*w,range:110})}, lvl:w=>({n:1+Math.floor(w/2),dmg:11*(1+.25*(w-1)),int:44-2*w,range:110})},
railgun:{name:"电磁炮",icon:"rail",rare:2,beh:"rail",ds:"带轮子的垃圾桶", railgun:{name:"电磁炮",icon:"rail",rare:2,beh:"rail",ds:"带轮子的垃圾桶",
lvl:w=>({dmg:55*(1+.3*(w-1)),charge:120-10*w,spd:11,zone:24+4*w})}, lvl:w=>({dmg:70*(1+.3*(w-1)),charge:120-10*w,spd:11,zone:24+4*w})},
deathray:{name:"死光",icon:"laser",rare:2,beh:"deathray",ds:"记得带镜子", deathray:{name:"死光",icon:"laser",rare:2,beh:"deathray",ds:"记得带镜子",
lvl:w=>({dps:0.9*(1+.28*(w-1)),charge:160-12*w,beam:55,width:5+2*w})}, lvl:w=>({dps:1.2*(1+.28*(w-1)),charge:160-12*w,beam:55,width:5+2*w})},
crescent:{name:"月牙斩",spr:"cres",rare:1,beh:"crescent",ds:"青豆?", crescent:{name:"月牙斩",spr:"cres",rare:1,beh:"crescent",ds:"青豆?",
lvl:w=>({dmg:44*(1+.24*(w-1)),int:120,spd:3.4})}, lvl:w=>({dmg:44*(1+.24*(w-1)),int:60,spd:3.4,hits:3})},
fission:{name:"分裂弹",spr:"fiss",rare:1,beh:"fission",ds:"四阶村长", fission:{name:"分裂弹",spr:"fiss",rare:1,beh:"fission",ds:"四阶村长",
lvl:w=>({dmg:6*(1+.22*(w-1)),int:20,spd:5.2,split:3+Math.floor(w/2),splitAt:26})}, lvl:w=>({dmg:7*(1+.22*(w-1)),int:20,spd:5.2,split:3+Math.floor(w/2),splitAt:26})},
frost:{name:"冰冻枪",spr:"icelance",rare:1,beh:"frost",ds:"冷冷冷冷", frost:{name:"冰冻枪",spr:"icelance",rare:1,beh:"frost",ds:"冷冷冷冷",
lvl:w=>({dmg:6*(1+.2*(w-1)),int:16,spd:6,freeze:70+25*w})}, lvl:w=>({dmg:10*(1+.2*(w-1)),int:13,spd:6,freeze:70+25*w})},
swarm:{name:"蜂巢导弹",spr:"msl2",rare:1,beh:"homing",ds:"不可用于对付肉山", swarm:{name:"蜂巢导弹",spr:"msl2",rare:1,beh:"homing",ds:"不可用于对付肉山",
lvl:w=>({n:1+w,dmg:2.2*(1+.2*(w-1)),int:22,spd:3.2,aoe:10})}, lvl:w=>({n:1+w,dmg:2.5*(1+.2*(w-1)),int:22,spd:3.2,aoe:10})},
bubble:{name:"重力泡泡",spr:"bub",rare:1,beh:"bubble",ds:"天呐!是海绵宝宝", bubble:{name:"重力泡泡",spr:"bub",rare:1,beh:"bubble",ds:"天呐!是海绵宝宝",
lvl:w=>({dmg:18*(1+.25*(w-1)),int:26,spd:1.4,aoe:30+4*w})}, lvl:w=>({dmg:90*(1+.25*(w-1)),int:26,spd:1.4,aoe:30+4*w})},
starfall:{name:"流星火雨",spr:"met",rare:2,beh:"starfall",ds:"天火", starfall:{name:"流星火雨",spr:"met",rare:2,beh:"starfall",ds:"天火",
lvl:w=>({n:3+Math.floor(w/2),dmg:16*(1+.25*(w-1)),int:150-8*w,aoe:16})}, lvl:w=>({n:3+Math.floor(w/2),dmg:16*(1+.25*(w-1)),int:150-8*w,aoe:16})},
boomerang:{name:"回旋镖",spr:"boomer",rare:0,beh:"boom",ds:"但愿不会打到自己", boomerang:{name:"回旋镖",spr:"boomer",rare:0,beh:"boom",ds:"但愿不会打到自己",
lvl:w=>({n:1+Math.floor(w/2),dmg:7*(1+.22*(w-1)),int:21,spd:4.8})}, lvl:w=>({n:1+Math.floor(w/2),dmg:9*(1+.22*(w-1)),int:21,spd:4.8})},
singularity:{name:"奇点炮",spr:"sing",rare:2,beh:"sing",ds:"雷工蛋?", singularity:{name:"奇点炮",spr:"sing",rare:2,beh:"sing",ds:"雷工蛋?",
lvl:w=>({dmg:120*(1+.3*(w-1)),int:200,pull:88,aoe:62,fuse:160})}, // 伤害×3(2026-08-06) // 牵引/爆炸范围与射程增强 lvl:w=>({dmg:120*(1+.3*(w-1)),int:200,pull:88,aoe:62,fuse:160})},
wavecannon:{name:"波动炮",spr:"waveb",rare:2,beh:"wave",ds:"炼狱无双波动炮", wavecannon:{name:"波动炮",spr:"waveb",rare:2,beh:"wave",ds:"炼狱无双波动炮",
lvl:w=>({dmg:32*(1+.3*(w-1)),charge:100-8*w,spd:5,width:14+6*w})}, lvl:w=>({dmg:32*(1+.3*(w-1)),charge:100-8*w,spd:5,width:14+6*w})},
holy:{name:"圣光弹",spr:"holyo",rare:2,beh:"holy",ds:"哇哦", holy:{name:"圣光弹",spr:"holyo",rare:2,beh:"holy",ds:"哇哦",
lvl:w=>({dmg:26*(1+.25*(w-1)),int:65,spd:3,len:16+3*w})}, // 伤害×2(2026-08-06) lvl:w=>({dmg:130*(1+.25*(w-1)),int:65,spd:3,len:16+3*w})},
thundercall:{name:"落雷术",spr:"thnd",rare:1,beh:"thunder",ds:"何方道友在此渡劫", thundercall:{name:"落雷术",spr:"thnd",rare:1,beh:"thunder",ds:"何方道友在此渡劫",
lvl:w=>({n:2+Math.floor(w/2),dmg:12*(1+.25*(w-1)),int:90-4*w,aoe:13})}, lvl:w=>({n:2+Math.floor(w/2),dmg:12*(1+.25*(w-1)),int:90-4*w,aoe:13})},
arcshu:{name:"电弧手里剑",spr:"shuri",rare:2,beh:"shuriken",ds:"这个很熟悉了吧", arcshu:{name:"电弧手里剑",spr:"shuri",rare:2,beh:"shuriken",ds:"这个很熟悉了吧",
lvl:w=>({dmg:6*(1+.22*(w-1)),int:95-4*w,spd:2.2,zapDmg:5*(1+.25*(w-1)),pulse:7})}, // 电弧释放速度×3 lvl:w=>({dmg:7*(1+.22*(w-1)),int:95-4*w,spd:2.2,zapDmg:6*(1+.25*(w-1)),pulse:7,deflect:0.04})},
volt:{name:"电能机炮",spr:"voltb",rare:1,beh:"volt",ds:"疑似电大", volt:{name:"电能机炮",spr:"voltb",rare:1,beh:"volt",ds:"疑似电大",
lvl:w=>({n:1+Math.floor(w/3),dmg:3.2*(1+.2*(w-1)),int:5,spd:6.2,gap:5,chain:2+Math.floor(w/2)})}, lvl:w=>({n:1+Math.floor(w/3),dmg:4.5*(1+.2*(w-1)),int:5,spd:6.2,gap:5,chain:2+Math.floor(w/2)})},
dawn:{name:"黎明重炮",spr:"dawnsh",rare:2,beh:"dawn",ds:"祖宗发射器", dawn:{name:"黎明重炮",spr:"dawnsh",rare:2,beh:"dawn",ds:"祖宗发射器",
lvl:w=>({dmg:70*(1+.3*(w-1)),charge:170-12*w,spd:5,aoe:30+3*w})}, lvl:w=>({dmg:70*(1+.3*(w-1)),charge:170-12*w,spd:5,aoe:30+3*w})},
proto:{name:"协议武装",spr:"protb",rare:2,beh:"protocol",ds:"搜索,歼灭", proto:{name:"协议武装",spr:"protb",rare:2,beh:"protocol",ds:"搜索,歼灭",
lvl:w=>({dmg:3*(1+.2*(w-1)),int:6,spd:6.5,heavy:120-8*w,hdmg:36*(1+.28*(w-1)),haoe:26+3*w})}, lvl:w=>({dmg:3*(1+.2*(w-1)),int:6,spd:6.5,heavy:120-8*w,hdmg:36*(1+.28*(w-1)),haoe:26+3*w})},
nebula:{name:"星云奥秘",spr:"neb",rare:2,beh:"nebula",ds:"字面意思", nebula:{name:"星云奥秘",spr:"neb",rare:2,beh:"nebula",ds:"字面意思",
lvl:w=>({dmg:12*(1+.25*(w-1)),int:100-5*w,spd:1.6,stars:10+2*w})}, lvl:w=>({dmg:14*(1+.25*(w-1)),int:100-5*w,spd:1.6,stars:10+2*w})},
loiter:{name:"巡飞弹",spr:"loms",rare:1,beh:"loiter",ds:"堵桥来", loiter:{name:"巡飞弹",spr:"loms",rare:1,beh:"loiter",ds:"堵桥来",
lvl:w=>({dmg:22*(1+.25*(w-1)),int:68-5*w,spd:4.2,zone:20+3*w,fire:95+15*w})} lvl:w=>({dmg:22*(1+.25*(w-1)),int:68-5*w,spd:4.2,zone:20+3*w,fire:95+15*w})},
beegrenade:{name:"蜜蜂手雷",spr:"beegrenade",rare:1,beh:"beegrenade",ds:"嗡嗡嗡",
lvl:w=>({n:2,dmg:10*(1+.25*(w-1)),beeDmg:4*(1+.25*(w-1)),int:55-4*w,spd:3.2,aoe:18,bees:5,fuse:40})},
chlorophyte:{name:"叶绿弹",spr:"chloro",rare:1,beh:"chlorophyte",ds:"丛林之怒",
lvl:w=>({n:1+Math.floor(w/2),dmg:4*(1+.2*(w-1)),int:10,spd:5.2,gap:5})}
}; };
const MAINS=Object.keys(WEAPONS); const MAINS=Object.keys(WEAPONS);
/* ================= 18 副武器 ================= */ /* ================= 15 =================
爆发/控场/防御分类原则一次性大范围或长CD特效 */
const SUBS={ const SUBS={
broad:{name:"侧舷火炮",spr:"broad",rare:0,max:5,beh:"broad",ds:l=>`舷炮斜向前方45°齐射Lv${l+1}`}, broad:{name:"侧舷火炮",spr:"broad",rare:0,max:5,beh:"broad",ds:l=>`舷炮斜向前方45°齐射Lv${l+1}`},
sweep:{name:"探照扫射",icon:"laser",rare:1,max:5,beh:"sweep",ds:l=>`往复摆动的扫描光束Lv${l+1}`}, sweep:{name:"探照扫射",icon:"laser",rare:1,max:5,beh:"sweep",ds:l=>`往复摆动的扫描光束Lv${l+1}`},
@ -78,31 +83,27 @@ const SUBS={
slowfield:{name:"时间力场",spr:"grv",rare:2,max:3,beh:"slowfield",ds:l=>"力场内敌人与敌弹大幅减速Lv"+(l+1)}, slowfield:{name:"时间力场",spr:"grv",rare:2,max:3,beh:"slowfield",ds:l=>"力场内敌人与敌弹大幅减速Lv"+(l+1)},
flares:{name:"热诱弹舱",spr:"flare",rare:0,max:4,beh:"flares",ds:l=>"扇形抛射4枚缓速热诱弹各诱爆一枚敌弹Lv"+(l+1)}, flares:{name:"热诱弹舱",spr:"flare",rare:0,max:4,beh:"flares",ds:l=>"扇形抛射4枚缓速热诱弹各诱爆一枚敌弹Lv"+(l+1)},
seeker:{name:"索敌浮游炮",spr:"dGun2",rare:1,max:3,beh:"seeker",ds:l=>"离机索敌的浮游炮Lv"+(l+1)}, seeker:{name:"索敌浮游炮",spr:"dGun2",rare:1,max:3,beh:"seeker",ds:l=>"离机索敌的浮游炮Lv"+(l+1)},
beamdrone:{name:"光束浮游炮",spr:"dBeam",rare:2,max:3,beh:"beamdrone",ds:l=>"环绕发射光束的浮游单元Lv"+(l+1)},
frostnova:{name:"冰霜新星",spr:"frost",rare:1,max:3,beh:"frostnova",ds:l=>"周期性冻结周围敌人Lv"+(l+1)}, frostnova:{name:"冰霜新星",spr:"frost",rare:1,max:3,beh:"frostnova",ds:l=>"周期性冻结周围敌人Lv"+(l+1)},
thorns:{name:"反伤棘甲",icon:"crit",rare:1,max:3,beh:"thorns",ds:l=>"撞击你的敌人遭到反噬Lv"+(l+1)}, thorns:{name:"反伤棘甲",icon:"crit",rare:1,max:3,beh:"thorns",ds:l=>"撞击你的敌人遭到反噬Lv"+(l+1)},
sticky:{name:"粘着炸弹",spr:"sticky",rare:0,max:3,beh:"sticky",ds:l=>"粘附敌人的定时高爆炸弹Lv"+(l+1)}, sticky:{name:"粘着炸弹",spr:"sticky",rare:0,max:3,beh:"sticky",ds:l=>"粘附敌人的定时高爆炸弹Lv"+(l+1)},
magnetar:{name:"磁暴环",icon:"mag",rare:2,max:3,beh:"magnetar",ds:l=>"周期性电磁脉冲消弹伤敌Lv"+(l+1)}, magnetar:{name:"磁暴环",icon:"mag",rare:2,max:3,beh:"magnetar",ds:l=>"周期性电磁脉冲消弹伤敌Lv"+(l+1)},
executor:{name:"斩首巡航弹",spr:"cmsl",rare:2,max:3,beh:"executor",ds:l=>"巡航导弹猎杀最强之敌Lv"+(l+1)}, executor:{name:"斩首巡航弹",spr:"cmsl",rare:2,max:3,beh:"executor",ds:l=>"巡航导弹猎杀最强之敌Lv"+(l+1)},
airstrike:{name:"空爆弹",spr:"amsl",rare:1,max:5,beh:"airstrike",ds:l=>"双弹侧掠折向直上穿透机身沿途轰炸Lv"+(l+1)}, airstrike:{name:"空爆弹",spr:"amsl",rare:1,max:5,beh:"airstrike",ds:l=>"双弹侧掠折向直上穿透机身沿途轰炸Lv"+(l+1)}
repair:{name:"修复无人机",spr:"dHeal",rare:1,max:3,beh:"repair",ds:l=>"持续修复机体结构Lv"+(l+1)}
}; };
const SUB_KEYS=Object.keys(SUBS); const SUB_KEYS=Object.keys(SUBS);
/* ================= 12 僚机 ================= */ /* ================= 10 =================
自动跟随/持续输出全部获得伤害与功能强化 */
const DRONES={ const DRONES={
gunner:{name:"机枪僚机",spr:"dGun",rare:0,max:3,beh:"aim",ds:l=>"协同瞄准射击Lv"+(l+1)}, gunner:{name:"机枪僚机",spr:"dGun",rare:0,max:3,beh:"burst",ds:l=>"高速连射+随机散布Lv"+(l+1)},
gunner2:{name:"双翼机枪手",spr:"dGun2",rare:1,max:3,beh:"aim2",ds:l=>"双管僚机Lv"+(l+1)}, gunner2:{name:"双翼机枪手",spr:"dGun2",rare:1,max:3,beh:"burst2",ds:l=>"双管高速连射Lv"+(l+1)},
sniperd:{name:"狙击僚机",spr:"dSnip",rare:1,max:3,beh:"snipe",ds:l=>"高伤点射Lv"+(l+1)}, sniperd:{name:"狙击僚机",spr:"dSnip",rare:1,max:3,beh:"snipe",ds:l=>"自瞄高伤穿透弹Lv"+(l+1)},
beamer:{name:"光束僚机",spr:"dBeam",rare:2,max:3,beh:"beam",ds:l=>"周期光束Lv"+(l+1)}, beamer:{name:"光束僚机",spr:"dBeam",rare:2,max:3,beh:"beam",ds:l=>"周期光束Lv"+(l+1)},
bomber:{name:"轰炸僚机",spr:"dBomb",rare:1,max:3,beh:"bomb",ds:l=>"投掷爆炸弹Lv"+(l+1)}, bomber:{name:"轰炸僚机",spr:"dBomb",rare:1,max:3,beh:"bomb",ds:l=>"投掷爆炸弹Lv"+(l+1)},
frostd:{name:"冰霜僚机",spr:"dFrost",rare:1,max:3,beh:"frostD",ds:l=>"减速弹Lv"+(l+1)},
teslaD:{name:"电弧僚机",spr:"dTesla",rare:2,max:3,beh:"teslaD",ds:l=>"近身电击Lv"+(l+1)},
rammer:{name:"冲撞僚机",spr:"dRam",rare:1,max:3,beh:"melee",big:1.6,ds:l=>"高速环绕撞击Lv"+(l+1)}, rammer:{name:"冲撞僚机",spr:"dRam",rare:1,max:3,beh:"melee",big:1.6,ds:l=>"高速环绕撞击Lv"+(l+1)},
healer:{name:"修复僚机",spr:"dHeal",rare:1,max:3,beh:"heal",ds:l=>"缓慢回复结构值Lv"+(l+1)}, healer:{name:"修复僚机",spr:"dHeal",rare:1,max:3,beh:"heal",ds:l=>"持续修复机体结构Lv"+(l+1)},
shieldd:{name:"护盾僚机",spr:"dShield",rare:2,max:3,beh:"block",big:1.5,ds:l=>"环绕格挡敌弹Lv"+(l+1)}, shieldd:{name:"护盾僚机",spr:"dShield",rare:2,max:3,beh:"block",big:1.5,ds:l=>"环绕格挡敌弹Lv"+(l+1)},
missileD:{name:"导弹僚机",spr:"dMsl",rare:1,max:3,beh:"missile",ds:l=>"追踪导弹Lv"+(l+1)}, missileD:{name:"导弹僚机",spr:"dMsl",rare:1,max:3,beh:"missile",ds:l=>"追踪导弹+大范围爆炸Lv"+(l+1)},
bladeD:{name:"刃舞僚机",spr:"dBlade",rare:1,max:3,beh:"bladeD",ds:l=>"投掷回旋刃Lv"+(l+1)}, bladeD:{name:"刃舞僚机",spr:"dBlade",rare:1,max:3,beh:"bladeD",ds:l=>"投掷回旋刃Lv"+(l+1)}
sweeper:{name:"扫射僚机",spr:"dSweep",rare:1,max:3,beh:"sweep",ds:l=>"扇形区域来回扫射火力覆盖Lv"+(l+1)}
}; };
const DRONE_KEYS=Object.keys(DRONES); const DRONE_KEYS=Object.keys(DRONES);
/* ================= 8 被动强化 ================= */ /* ================= 8 被动强化 ================= */

View File

@ -67,7 +67,7 @@ function updateEnemy(e){
if(e.hp<=0){killEnemy(e);return;} if(e.hp<=0){killEnemy(e);return;}
if(e.dot.t<=0)e.dot=null;} if(e.dot.t<=0)e.dot=null;}
if(e.leech){e.leech.t--;e.hp-=e.leech.dps/60; // 寄生电浆:吸血 if(e.leech){e.leech.t--;e.hp-=e.leech.dps/60; // 寄生电浆:吸血
G.hp=Math.min(G.maxHp,G.hp+e.leech.dps*0.3/60); healPlayer(e.leech.dps*0.3/60,true);
if(G.time%10===0)G.parts.push({x:e.x,y:e.y,vx:0,vy:0,life:12,col:"#b05ce8",sz:1,suck:{x:G.p.x,y:G.p.y}}); if(G.time%10===0)G.parts.push({x:e.x,y:e.y,vx:0,vy:0,life:12,col:"#b05ce8",sz:1,suck:{x:G.p.x,y:G.p.y}});
if(e.hp<=0){killEnemy(e);return;} if(e.hp<=0){killEnemy(e);return;}
if(e.leech.t<=0)e.leech=null;} if(e.leech.t<=0)e.leech=null;}

View File

@ -396,7 +396,6 @@ function render(){
if(G.subLoadout.includes("sats")){const n=1+G.subs.sats; if(G.subLoadout.includes("sats")){const n=1+G.subs.sats;
for(let i=0;i<n;i++){const a=G.p.satA+i*(TAU/n);drawSprite(SPR.sat,G.p.x+Math.cos(a)*20,G.p.y+Math.sin(a)*20);}} for(let i=0;i<n;i++){const a=G.p.satA+i*(TAU/n);drawSprite(SPR.sat,G.p.x+Math.cos(a)*20,G.p.y+Math.sin(a)*20);}}
const skS=G.sst.seeker;if(skS&&skS.x!==undefined&&G.subLoadout.includes("seeker"))drawSprite(SPR.dGun2,skS.x,skS.y); const skS=G.sst.seeker;if(skS&&skS.x!==undefined&&G.subLoadout.includes("seeker"))drawSprite(SPR.dGun2,skS.x,skS.y);
const bdS=G.sst.beamdrone;if(bdS&&bdS.x!==undefined&&G.subLoadout.includes("beamdrone"))drawSprite(SPR.dBeam,bdS.x,bdS.y);
if(G.subLoadout.includes("thorns"))for(let i=0;i<4;i++){const a=G.time*0.04+i*TAU/4; if(G.subLoadout.includes("thorns"))for(let i=0;i<4;i++){const a=G.time*0.04+i*TAU/4;
px(G.p.x+Math.cos(a)*13-1,G.p.y+Math.sin(a)*13-1,3,3,"#c8d8ff");} px(G.p.x+Math.cos(a)*13-1,G.p.y+Math.sin(a)*13-1,3,3,"#c8d8ff");}
@ -414,9 +413,10 @@ function render(){
if(p.focus){px(p.x-1,p.y-1,3,3,"#ff3355");px(p.x-0.5,p.y-0.5,2,2,"#fff"); if(p.focus){px(p.x-1,p.y-1,3,3,"#ff3355");px(p.x-0.5,p.y-0.5,2,2,"#fff");
ctx.strokeStyle="rgba(255,51,85,.5)";ctx.beginPath();ctx.arc(p.x,p.y,6,0,TAU);ctx.stroke();} ctx.strokeStyle="rgba(255,51,85,.5)";ctx.beginPath();ctx.arc(p.x,p.y,6,0,TAU);ctx.stroke();}
} }
// 粒子与浮字spark 粒子渲染为线段 // 粒子与浮字spark 粒子渲染为线段cross 渲染为绿色十字
for(const q of G.parts){ctx.globalAlpha=clamp(q.life/20,0,1); for(const q of G.parts){ctx.globalAlpha=clamp(q.life/20,0,1);
if(q.spark){ctx.strokeStyle=q.col;ctx.beginPath();ctx.moveTo(q.x,q.y);ctx.lineTo(q.x-q.vx*2.2,q.y-q.vy*2.2);ctx.stroke();} if(q.cross){const c=q.col||"#5cff9d";px(q.x-1,q.y-3,2,6,c);px(q.x-3,q.y-1,6,2,c);}
else if(q.spark){ctx.strokeStyle=q.col;ctx.beginPath();ctx.moveTo(q.x,q.y);ctx.lineTo(q.x-q.vx*2.2,q.y-q.vy*2.2);ctx.stroke();}
else px(q.x,q.y,q.sz,q.sz,q.col); else px(q.x,q.y,q.sz,q.sz,q.col);
ctx.globalAlpha=1;} ctx.globalAlpha=1;}
ctx.textAlign="center"; ctx.textAlign="center";
@ -451,6 +451,14 @@ function drawHUD(){
px(4,H-12,hpw+2,6,"#0d1226"); px(4,H-12,hpw+2,6,"#0d1226");
px(5,H-11,hpw,4,"#3a1020"); px(5,H-11,hpw,4,"#3a1020");
px(5,H-11,hpw*pc2,4,pc2>0.5?"#5cff9d":pc2>0.25?"#ffcf5c":"#ff5566"); px(5,H-11,hpw*pc2,4,pc2>0.5?"#5cff9d":pc2>0.25?"#ffcf5c":"#ff5566");
if(G.healFlash>0){G.healFlash--;
ctx.globalAlpha=0.35+0.45*(G.healFlash/40);
px(5,H-11,hpw*pc2,4,"#5cff9d");
ctx.globalAlpha=0.9+0.1*Math.sin(G.time*0.4);
ctx.fillStyle="#5cff9d";ctx.font="6px 'Press Start 2P',monospace";ctx.textAlign="left";
ctx.fillText("+"+Math.round(G.healAmt),5,H-16);
ctx.globalAlpha=1;
if(G.healFlash<=0)G.healAmt=0;}
for(let i=0;i<G.stat.maxLives;i++){ctx.globalAlpha=i<G.lives?1:0.2;ctx.drawImage(SPR.heart,5+i*9,H-21);} for(let i=0;i<G.stat.maxLives;i++){ctx.globalAlpha=i<G.lives?1:0.2;ctx.drawImage(SPR.heart,5+i*9,H-21);}
ctx.globalAlpha=1; ctx.globalAlpha=1;
// 经验条 // 经验条

View File

@ -160,6 +160,11 @@ SPR.protb=bake([".K.","KCK","KWK","KCK","KCK","KCK",".K."],{K:"#140a0a",C:"#ff4a
SPR.protoh=orb(null,"#ff5a2e"); // 协议重炮弹:黑壳橙芯大能量球 SPR.protoh=orb(null,"#ff5a2e"); // 协议重炮弹:黑壳橙芯大能量球
SPR.neb=orb(null,"#8a5cff"); // 星云奥秘:紫蓝奥术球 SPR.neb=orb(null,"#8a5cff"); // 星云奥秘:紫蓝奥术球
SPR.star4=bake(["..K..","..K..","KKBKK","..K..","..K.."],{K:"#e0b3ff",B:"#ffffff"}); // 星云绽放的小星星 SPR.star4=bake(["..K..","..K..","KKBKK","..K..","..K.."],{K:"#e0b3ff",B:"#ffffff"}); // 星云绽放的小星星
/* ---- 新增武器与治疗精灵 ---- */
SPR.beegrenade=bake(["..KK..",".KYYK.","KYBBYK","KYYBYK",".KBBK.","..KK.."],{K:"#33240a",Y:"#ffd23d",B:"#5a4a1a"}); // 蜜蜂手雷:黄黑条纹球
SPR.bee=bake([".K.","KYK","KKK",".K."],{K:"#1a1a1a",Y:"#ffd23d"}); // 追踪蜜蜂
SPR.chloro=bake(["...K...","..KLK..",".KLW LK".replace(" ",""),"..KLK..","...K..."],{K:"#0d2a0d",L:"#5cff9d",W:"#eafff0"}); // 叶绿弹:翠绿叶形
SPR.healcross=bake(["..G..",".GGG.","GGGGG",".GGG.","..G.."],{G:"#5cff9d"}); // 治疗绿十字
/* 12 种僚机 */ /* 12 种僚机 */
SPR.dGun=bake(["...K...","..KWK..",".KWWWK.","KWKWKWK","..KWK.."],{K:"#14142b",W:"#c8d8ff"}); SPR.dGun=bake(["...K...","..KWK..",".KWWWK.","KWKWKWK","..KWK.."],{K:"#14142b",W:"#c8d8ff"});
SPR.dGun2=bake(["K..K..K","KKK.KKK",".KWKWK.","..KWK..","...K..."],{K:"#14142b",W:"#9fd8ff"}); SPR.dGun2=bake(["K..K..K","KKK.KKK",".KWKWK.","..KWK..","...K..."],{K:"#14142b",W:"#9fd8ff"});

View File

@ -572,7 +572,7 @@ function pickCard(i){
else if(c.kind==="drone"){G.drones[c.key]=1;if(G.droneLoadout.length<3)G.droneLoadout.push(c.key);banner("获得 "+DRONES[c.key].name,"#b98cff",80);} else if(c.kind==="drone"){G.drones[c.key]=1;if(G.droneLoadout.length<3)G.droneLoadout.push(c.key);banner("获得 "+DRONES[c.key].name,"#b98cff",80);}
else if(c.kind==="upg"){ else if(c.kind==="upg"){
G.u[c.key]++; G.u[c.key]++;
if(c.key==="nano"){G.maxHp+=25;G.hp=Math.min(G.maxHp,G.hp+40);} if(c.key==="nano"){G.maxHp+=25;healPlayer(40);}
if(c.key==="shield")G.shield=Math.min(shieldCap(),G.shield+1); if(c.key==="shield")G.shield=Math.min(shieldCap(),G.shield+1);
}else{ }else{
if(c.key==="heal")G.lives=Math.min(G.stat.maxLives,G.lives+1); if(c.key==="heal")G.lives=Math.min(G.stat.maxLives,G.lives+1);
@ -598,7 +598,7 @@ function startDemoSim(kind,key){
else if(kind==="s"){G.weapons={vulcan:1};G.loadout=["vulcan"];G.subs={};G.subs[key]=lv;G.subLoadout=[key];} else if(kind==="s"){G.weapons={vulcan:1};G.loadout=["vulcan"];G.subs={};G.subs[key]=lv;G.subLoadout=[key];}
else if(kind==="d"){G.weapons={};G.loadout=[];G.drones={};G.drones[key]=lv;G.droneLoadout=[key];} else if(kind==="d"){G.weapons={};G.loadout=[];G.drones={};G.drones[key]=lv;G.droneLoadout=[key];}
else{G.weapons={vulcan:2};G.loadout=["vulcan"];G.u[key]=3; else{G.weapons={vulcan:2};G.loadout=["vulcan"];G.u[key]=3;
if(key==="nano"){G.maxHp+=75;G.hp=G.maxHp;} if(key==="nano"){G.maxHp+=75;healPlayer(75);}
if(key==="shield")G.shield=shieldCap();} if(key==="shield")G.shield=shieldCap();}
$("demo-name").textContent=def.name; $("demo-name").textContent=def.name;
const ds=typeof def.ds==="function"?def.ds(lv):def.ds; const ds=typeof def.ds==="function"?def.ds(lv):def.ds;

View File

@ -5,15 +5,16 @@ const db = require("./db");
const auth = require("./auth"); const auth = require("./auth");
const { vUsername, vEmail, vPassword, vInt, rateLimit, clientIp } = require("./util"); const { vUsername, vEmail, vPassword, vInt, rateLimit, clientIp } = require("./util");
/* 图鉴合法 key 白名单(与 js/data.js 保持同步25主武+17副武+13僚机+8被动=63 */ /* 图鉴合法 key 白名单(与 js/data.js 保持同步27主武+15副武+10僚机+8被动=60 */
const CODEX_KEYS = new Set([ const CODEX_KEYS = new Set([
"vulcan", "shotgun", "needle", "flame", "shotgun2", "tesla", "railgun", "deathray", "vulcan", "shotgun", "needle", "flame", "shotgun2", "tesla", "railgun", "deathray",
"crescent", "fission", "frost", "swarm", "bubble", "starfall", "boomerang", "singularity", "crescent", "fission", "frost", "swarm", "bubble", "starfall", "boomerang", "singularity",
"wavecannon", "holy", "thundercall", "arcshu", "volt", "dawn", "proto", "nebula", "loiter", "wavecannon", "holy", "thundercall", "arcshu", "volt", "dawn", "proto", "nebula", "loiter",
"beegrenade", "chlorophyte",
"broad", "sweep", "leech", "orbit", "sats", "pd", "slowfield", "flares", "seeker", "broad", "sweep", "leech", "orbit", "sats", "pd", "slowfield", "flares", "seeker",
"beamdrone", "frostnova", "thorns", "sticky", "magnetar", "executor", "airstrike", "repair", "frostnova", "thorns", "sticky", "magnetar", "executor", "airstrike",
"gunner", "gunner2", "sniperd", "beamer", "bomber", "frostd", "teslaD", "rammer", "gunner", "gunner2", "sniperd", "beamer", "bomber", "rammer",
"healer", "shieldd", "missileD", "bladeD", "sweeper", "healer", "shieldd", "missileD", "bladeD",
"fire", "crit", "shield", "nano", "mag", "bounty", "odcore", "ghost", "fire", "crit", "shield", "nano", "mag", "bounty", "odcore", "ghost",
]); ]);
const CODEX_TOTAL = CODEX_KEYS.size; const CODEX_TOTAL = CODEX_KEYS.size;