diff --git a/js/enemies.js b/js/enemies.js index b420432..1d6e814 100644 --- a/js/enemies.js +++ b/js/enemies.js @@ -166,24 +166,46 @@ function shuffled(n){const a=[];for(let i=0;i0; function bossBeam(x1,y1,x2,y2,w,col,tele,dur,dmg,src){ // Boss光束:tele帧预警→dur帧伤害;src={b,ox,oy,followX2}时发射点跟随Boss G.bbeams.push({x1,y1,x2,y2,w,col,tele,life:dur,dmg:dmg||20,cd:0,src:src||null}); } -function spawnFunnels(b,n){ // 夺权者X:银钉无人机(环绕僚机,瞄准/散射轮换) +function spawnFunnels(b,n){ // 夺权者X:蓝白长钉无人机(环绕僚机;定期脱离环绕摆阵型齐射) for(let i=0;i170)b.form=null;} // 阵型演出:40帧入位→4轮齐射→归位 + else if((b.laserT||0)<=0&&--b.formT<=0){ // 巨宽激光期间不排阵 + b.form={pat:rndi(0,2),t:0}; + G.funnels.forEach((f,i)=>{f.slot=i;}); + b.formT=rndi(480,660); + } + } for(const f of G.funnels){ f.t++;if(f.flashT>0)f.flashT--; - if(b&&!b.dead){f.sa+=0.014; - f.x+=(b.x+Math.cos(f.sa)*46-f.x)*0.09; - f.y+=(b.y+16+Math.sin(f.sa)*20-f.y)*0.09;} - if(b&&b.laserT>0)continue; // 巨宽核心激光期间僚机停火(仍环绕) - f.fireT--; - if(f.fireT<=0){ // 银色高弹速长钉:瞄准单发 / 三发散射 轮换 - const ap=aimPt(),a=Math.atan2(ap.y-f.y,ap.x-f.x); - if(f.mode==="aim"){ebul(f.x,f.y,a,4.8,"dartS",15);f.mode="scatter";f.fireT=rndi(64,96);} - else{for(let k=-1;k<=1;k++)ebul(f.x,f.y,a+k*0.24,3.9,"dartS",13);f.mode="aim";f.fireT=rndi(100,140);} - f.flashT=5;} + if(b&&!b.dead){ + f.sa+=0.014; + const ringX=b.x+Math.cos(f.sa)*46,ringY=b.y+16+Math.sin(f.sa)*20; + if(b.form){ // 脱离环绕摆阵型:横队垂直向下 / 两翼外张扇形 / 集火瞄准 + const fm2=b.form,i=f.slot||0,nn=Math.max(1,n-1); + let tx,ty,fa; + if(fm2.pat===0){tx=b.x+(i-nn/2)*26;ty=b.y-30;fa=Math.PI/2;} + else if(fm2.pat===1){const sd=i%2?1:-1;tx=b.x+sd*(36+Math.floor(i/2)*16);ty=b.y+6+Math.floor(i/2)*8;fa=Math.PI/2+sd*0.55;} + else{tx=b.x+(i-nn/2)*18;ty=b.y+26;fa=null;} + if(fm2.t<150){f.x+=(tx-f.x)*0.11;f.y+=(ty-f.y)*0.11;} + else{f.x+=(ringX-f.x)*0.09;f.y+=(ringY-f.y)*0.09;} // 归位回环绕 + if((b.laserT||0)>0)continue; + if(fm2.t>=40&&fm2.t<140&&fm2.t%26===0){ + const a2=fa===null?Math.atan2(aimPt().y-f.y,aimPt().x-f.x):fa; + ebul(f.x,f.y,a2,4.6,"dartS",14);f.flashT=5;} + }else{ + f.x+=(ringX-f.x)*0.09;f.y+=(ringY-f.y)*0.09; + if((b.laserT||0)>0)continue; // 巨宽核心激光期间停火(仍环绕) + f.fireT--; + if(f.fireT<=0){const a=Math.atan2(aimPt().y-f.y,aimPt().x-f.x); + ebul(f.x,f.y,a,4.8,"dartS",15);f.fireT=rndi(70,120);f.flashT=5;} + } + } } G.funnels=G.funnels.filter(f=>!f.dead); } @@ -196,6 +218,7 @@ function startBoss(){ G.boss={idx,x:W/2,y:-60,ty:64,hp:b.hp*hpM,maxhp:b.hp*hpM,r:(b.rr?b.rr[0]:b.r),ry:(b.ryy?b.ryy[0]:undefined),t:0,phase:0,transT:0, col:b.col,name:b.name,sub:b.sub,fireM:BAL.fireMul(n)*(G.diff===1?BAL.hard.fire:1),dead:false, mainT:100,minorT:9999,minorSet:[],rerollT:340,ax:W/2,ay:64,mt:0,landed:false}; + if(b.main==="piano")G.boss.mainT=36; // 激光钢琴:缩短开局空窗 G.bossN++;SFX.warn();MUS.start(true); banner("WARNING — "+b.name,"#ff5566",150); } @@ -210,7 +233,7 @@ function updateBoss(){ let np=0;for(let i=0;ib.phase){b.phase=np;b.transT=70;b.r=(def.rr&&def.rr[np])||b.r;b.ry=(def.ryy&&def.ryy[np])||undefined; G.eb.length=0;G.bbeams.length=0;b.keys=null;SFX.warn();G.shake=Math.max(G.shake,5); - if(b.idx===6){G.funnels.length=0;if(np===1){spawnFunnels(b,2);b.plT=Math.round(300/b.fireM);}} // 夺权者X二阶段:换为2架僚机 + if(b.idx===6){G.funnels.length=0;b.form=null;if(np===1){spawnFunnels(b,4);b.plT=Math.round(300/b.fireM);}} // 夺权者X二阶段:增至4架僚机 banner(b.name+" — 第"+["一","二","三"][np]+"形态","#ff5566",90);} if(b.transT>0){b.transT--; if(b.transT===0){explodeFx(b.x,b.y,"dark",1.3);G.shake=Math.max(G.shake,6); @@ -275,12 +298,12 @@ function bossMain(b,def,fm){ case "assault": // 暗黑突击:启动冲撞(锚点记当前位置,冲撞归来后正弦从此继续) b.dashSt={ph:"tele",t:52};b.ax=b.x;b.ay=b.y;b.mainT=9999;break; case "piano": { // 激光钢琴:21键,四种模式轮换——顺扫(逼走位)/反扫/乱序/齐射 - if(!b.keys)b.keys={n:21,seq:[],i:0,t:40,hot:-1,hotT:0,hotList:null,mode:3}; + if(!b.keys)b.keys={n:21,seq:[],i:0,t:8,hot:-1,hotT:0,hotList:null,mode:3}; const ks=b.keys,n=ks.n; if(ks.hotT>0){ks.hotT--; if(ks.hotT===0){const list=ks.hotList||[ks.hot]; for(const kh of list){if(kh<0)continue;const kx=-(n-1)*4.75+kh*9.5; - bossBeam(b.x+kx,b.y+18,b.x+kx,H+8,4,"#43e8ff",12,20,18,{b,ox:kx,oy:18,followX2:true});} + bossBeam(b.x+kx,b.y+18,b.x+kx,H+8,4,"#43e8ff",7,22,18,{b,ox:kx,oy:18,followX2:true});} ks.hotList=null;ks.hot=-1;SFX.eshoot();}} ks.t--; if(ks.t<=0&&ks.hotT<=0){ @@ -290,10 +313,10 @@ function bossMain(b,def,fm){ else if(ks.mode===1){ks.seq=[];for(let i=n-1;i>=0;i--)ks.seq.push(i);} else if(ks.mode===2){ks.seq=shuffled(n);} else{ks.seq=[];for(let i=0;i<6+ph*2;i++)ks.seq.push(-1);} // -1=齐射标记 - ks.i=0;ks.t=Math.round((ph?36:56)/fm); + ks.i=0;ks.t=Math.round((ph?24:40)/fm); }else{const cur=ks.seq[ks.i++]; - if(cur===-1){const pool=shuffled(n);ks.hotList=pool.slice(0,4+ph*2);ks.hotT=16;ks.t=Math.round((ph?34:50)/fm);} - else{ks.hot=cur;ks.hotT=7;ks.t=Math.round((ph?3:5)/fm);} // 扫射模式:快速连发 + if(cur===-1){const pool=shuffled(n);ks.hotList=pool.slice(0,4+ph*2);ks.hotT=9;ks.t=Math.round((ph?22:34)/fm);} + else{ks.hot=cur;ks.hotT=3;ks.t=Math.round((ph?2:3)/fm);} // 扫射模式:快速连发 } } b.mainT=6;break;} @@ -301,10 +324,10 @@ function bossMain(b,def,fm){ for(let i=0;i<10+ph*4;i++)ebul(b.x,b.y,i/(10+ph*4)*TAU+b.t*0.03,1.6+0.2*ph,"orbO"); if(b.t%240<3)for(let i=0;i<1+ph;i++)spawnEnemy(ph>=2?"kami":"mini",b.x+rnd(-30,30),b.y+8,false); b.mainT=Math.round(Math.max(34,70-8*ph)/fm);break; - case "usurper": // 夺权者X:银钉无人机(P1四架/P2两架僚机);P2周期性巨宽核心激光(压制其他攻击) + case "usurper": // 夺权者X:蓝白长钉无人机(P1两架/P2四架僚机);P2周期性巨宽核心激光(压制其他攻击) if(ph===0){ - if(!b.fn0){b.fn0=1;spawnFunnels(b,4);} - for(const sd of[-1,1]){const px2=b.x+sd*55,py2=b.y-4,a6=Math.atan2(ap2.y-py2,ap2.x-px2); + if(!b.fn0){b.fn0=1;spawnFunnels(b,2);} + for(const sd of[-1,1]){const px2=b.x+sd*37,py2=b.y-4,a6=Math.atan2(ap2.y-py2,ap2.x-px2); ebul(px2,py2,a6,4.4,"dartS",15);} b.mainT=Math.round(46/fm); }else{ @@ -350,7 +373,7 @@ function bossMinor(b,mk,fm){ } } function hurtBoss(dmg){ - const b=G.boss;if(!b||b.dead||b.transT>0||b.y0||!b.landed)return; // 入场未到位/变形转场期间不掉血(landed锁:防站桩Boss爬升后永远满足 y0.6?"#c86bff":"#2a1a3a"); @@ -145,9 +146,10 @@ function drawBoss(){ px(-2.5,-3,5,4,"#2a1a3a"); px(-1.5,-3-bl2,3,bl2,fo>0.4?(fl||"#ff3355"):"#3a2248"); ctx.restore();} - for(let k=0;k<2;k++){const aa2=a0+(0.32+k*0.36)*(a1-a0)+Math.sin(t*0.012+k*2)*0.05; // 环面光点 + for(let k=0;k<2;k++){const aa2=a0+(0.35+k*0.3)*(a1-a0)+Math.sin(t*0.012+k*2)*0.05; // 环面光点 px(Math.cos(aa2)*R-2,Math.sin(aa2)*R-2,4,4,"#c86bff");} - } + ctx.restore();} + circ(0,-R,4,"#6e4490"); // 顶部铰链节 } if(flashA>0){ctx.globalAlpha=flashA;ctx.strokeStyle="#fff"; ctx.beginPath();ctx.arc(0,0,b.r+6,0,TAU);ctx.stroke();ctx.globalAlpha=1;} @@ -177,8 +179,9 @@ function drawBoss(){ function drawProgenitor(t,fl,tf){ // 太空原种:同一躯体连续演化(tf 0=虫蛹→1=完全体),无部件凭空出现 ctx.rotate(Math.sin(t*0.05)*0.05); const wE=Math.min(1,tf*1.2); // 展翅进度 - // —— 翅膀:折叠时收在躯体正后方(被身体完全遮住),自背后向两侧展开 —— + // —— 翅膀:折叠时收在躯体正后方偏下(被身体完全遮住),自背后向两侧展开 —— ctx.save(); + ctx.translate(0,10); ctx.scale(0.05+0.95*wE,(0.55+0.45*wE)*(1+Math.sin(t*0.07)*0.04*wE)); poly([0,-20,-26,-14,-52,-4,-76,6,-58,12,-34,10,-16,16,0,20],"#c86aa8"); poly([0,-20,26,-14,52,-4,76,6,58,12,34,10,16,16,0,20],"#c86aa8"); @@ -249,12 +252,16 @@ function drawPiano(b,t,fl,split){ // 激光钢琴(split=裂壳进度0~1) px(Math.cos(a)*110-1.5,6+Math.sin(a)*30-1.5,3,3,"#9fb4d8");} } /* ---- Boss光束与浮游炮渲染 ---- */ +function drawFunnel(f){ // 蓝白长钉无人机(单架) + poly([f.x,f.y-5,f.x+4,f.y+3,f.x-4,f.y+3],"#cfe4ff"); + px(f.x-1,f.y-1,2,3,"#43b8ff"); + if(f.flashT>0){ctx.globalAlpha=0.6;px(f.x-3,f.y+3,6,3,"#ffffff");ctx.globalAlpha=1;} +} +function drawFunnelsBack(){ // 转到Boss上半部(背后)的无人机先画,被机体遮挡 + for(const f of G.funnels)if(Math.sin(f.sa)<-0.15)drawFunnel(f); +} function drawBossFx(){ - for(const f of G.funnels){ // 银钉无人机 - poly([f.x,f.y-5,f.x+4,f.y+3,f.x-4,f.y+3],"#cfe4ff"); - px(f.x-1,f.y-1,2,3,"#43b8ff"); - if(f.flashT>0){ctx.globalAlpha=0.6;px(f.x-3,f.y+3,6,3,"#ffffff");ctx.globalAlpha=1;} - } + for(const f of G.funnels)if(Math.sin(f.sa)>=-0.15)drawFunnel(f); // 下半部(身前)后画 for(const bm of G.bbeams){ if(bm.tele>0){if(G.time%4<2){ctx.globalAlpha=0.5;ctx.strokeStyle=bm.col;ctx.lineWidth=1; ctx.beginPath();ctx.moveTo(bm.x1,bm.y1);ctx.lineTo(bm.x2,bm.y2);ctx.stroke();ctx.globalAlpha=1;}} @@ -312,7 +319,7 @@ function render(){ px(e.x-w2/2,e.y-e.dh/2-3,w2,2,"#3a1020"); px(e.x-w2/2,e.y-e.dh/2-3,w2*fr,2,fr>0.4?"#5cff9d":"#ff5566");} } - drawBoss();drawBossFx(); + drawFunnelsBack();drawBoss();drawBossFx(); // 敌弹(圆形光晕烘焙在精灵内) for(const b of G.eb){ if(b.scale&&b.scale!==1){const sp=SPR[b.spr]; // 成长弹(花弹) diff --git a/js/sprites.js b/js/sprites.js index 7730f34..d32ce03 100644 --- a/js/sprites.js +++ b/js/sprites.js @@ -99,14 +99,14 @@ function orb(c1,c2){ ".HHCCCHH.", "...HHH..."],{H:halo,W:"#ffffff",C:c2}); } -const BHALO={orbR:"#ff5566",orbP:"#b05ce8",orbG:"#5cff9d",orbO:"#ff9a3d",dartB:"#3a8cd8",flowr:"#ff8fc8",dartS:"#cfe4ff",dartP:"#b98cff"}; +const BHALO={orbR:"#ff5566",orbP:"#b05ce8",orbG:"#5cff9d",orbO:"#ff9a3d",dartB:"#3a8cd8",flowr:"#ff8fc8",dartS:"#5ec4ff",dartP:"#b98cff"}; SPR.dartB=bake(["KCCCK",".CCC.",".CWC.","..C.."],{K:"#0d2433",C:"#3a8cd8",W:"#cfeaff"}); // 蓝色锥形敌弹(Boss弹幕) SPR.flowr=bake([ "....P....",".P..P..P.","..PPPPP..",".PPWWWPP.","PPPWWWPPP", ".PPWWWPP.","..PPPPP..",".P..P..P.","....P...."], {P:"#ff8fc8",W:"#ffffff"}); // 太空原种:花弹(圆形八刺,会逐渐变大) -SPR.dartS=bake(["SWS","SWS","SWS","SWS","SWS","SWS",".S.",".W."], -{S:"#cfe4ff",W:"#ffffff"}); // 夺权者X:银色高弹速长钉 +SPR.dartS=bake(["WW","WW","CC","CC","CC","CC","CC","CC","CC","CC","CC","CC"], +{W:"#f2fbff",C:"#5ec4ff"}); // 夺权者X:蓝白细长钉(2×12) SPR.dartP=bake(["KPPPK",".PPP.",".PWP.","..P.."],{K:"#2a1040",P:"#b98cff",W:"#ead9ff"}); // 暗黑魔碟:紫锥弹 SPR.orbR=orb("#ffe9e9","#ff5566");SPR.orbP=orb("#f4e9ff","#b05ce8"); SPR.orbG=orb("#e9ffe9","#5cff9d");SPR.orbO=orb("#fff4d9","#ff9a3d");