From b776149ff079aa111d2cf145ab36297d4cb75e61 Mon Sep 17 00:00:00 2001 From: JOJO <1498581755@qq.com> Date: Thu, 6 Aug 2026 03:25:51 +0800 Subject: [PATCH] =?UTF-8?q?=E6=BF=80=E5=85=89=E9=92=A2=E7=90=B4=E9=87=8D?= =?UTF-8?q?=E8=AE=BE=E8=AE=A1=EF=BC=9A=E7=BE=8E=E6=9C=AF=E5=A4=A7=E6=94=B9?= =?UTF-8?q?=3D4=E5=B1=82=E5=90=8C=E5=BF=83=E8=A3=85=E7=94=B2=E7=8E=AF?= =?UTF-8?q?=E5=B8=A6=E7=A9=B9=E9=A1=B6=E8=A6=81=E5=A1=9E+=E5=83=8F?= =?UTF-8?q?=E7=B4=A0=E7=90=B4=E9=94=AE=EF=BC=88=E9=BB=91=E9=94=AE=E7=BA=B9?= =?UTF-8?q?=E6=A0=B7=EF=BC=89+6=E5=B0=8F=E7=82=AE=E5=A1=94+=E5=B7=A6?= =?UTF-8?q?=E5=8F=B3=E5=A4=A7=E7=82=AE=E5=A1=94=EF=BC=88=E5=B7=A6=E7=B4=AB?= =?UTF-8?q?=E5=8F=B3=E8=93=9D=EF=BC=89+=E9=9B=B7=E8=BE=BE+=E5=8F=91?= =?UTF-8?q?=E5=85=89=E7=BA=B9=E8=B7=AF=E8=93=84=E5=8A=9B=E8=94=93=E5=BB=B6?= =?UTF-8?q?+=E8=A3=82=E5=A3=B3=E9=9C=B2=E7=BA=A2=E8=89=B2=E7=AE=A1?= =?UTF-8?q?=E7=BA=BF=E7=BD=91=E7=BA=A2=E6=A0=B8=EF=BC=88=E5=86=85=E5=B1=82?= =?UTF-8?q?=E6=BB=91=E6=9B=B4=E8=BF=9C=E9=98=B6=E6=A2=AF=E8=A3=82=E5=8F=A3?= =?UTF-8?q?=EF=BC=89=EF=BC=9B=E6=94=BB=E5=87=BB=E5=B0=8F=E6=94=B9=3D?= =?UTF-8?q?=E6=96=B0=E5=A2=9E=E7=AC=AC5=E6=A8=A1=E5=BC=8F=E6=BB=91?= =?UTF-8?q?=E5=A5=8F=EF=BC=88=E9=A1=BA=E6=89=AB=E7=AB=8B=E5=88=BB=E5=8F=8D?= =?UTF-8?q?=E6=89=AB=EF=BC=89/P2=E4=B9=B1=E5=BA=8F=E5=8F=8C=E9=94=AE?= =?UTF-8?q?=E5=92=8C=E9=9F=B3/topRain=E6=94=B9=E7=82=AE=E5=A1=94=E8=BD=AE?= =?UTF-8?q?=E6=B5=81=E5=BC=80=E7=81=AB/ring=E5=88=A0=E6=94=B9pedal?= =?UTF-8?q?=E8=B8=8F=E6=9D=BF=E9=87=8D=E9=9F=B3=E6=85=A2=E9=80=9F=E5=A4=A7?= =?UTF-8?q?=E5=BC=B9=E3=80=82=E7=BB=98=E5=88=B6=E8=BF=81=E5=85=A5=E6=96=B0?= =?UTF-8?q?=E6=96=87=E4=BB=B6js/boss=5Fart.js=E3=80=82=E4=BB=85=E8=AF=AD?= =?UTF-8?q?=E6=B3=95=E6=A3=80=E6=9F=A5=E9=80=9A=E8=BF=87=EF=BC=8C=E6=9C=AA?= =?UTF-8?q?=E8=BF=90=E8=A1=8C=E9=AA=8C=E8=AF=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- star-raid/index.html | 1 + star-raid/js/boss_art.js | 78 ++++++++++++++++++++++++++++++++++++++++ star-raid/js/enemies.js | 26 +++++++++----- star-raid/js/render.js | 37 ------------------- 4 files changed, 97 insertions(+), 45 deletions(-) create mode 100644 star-raid/js/boss_art.js diff --git a/star-raid/index.html b/star-raid/index.html index 0e9f38e..0bcf207 100644 --- a/star-raid/index.html +++ b/star-raid/index.html @@ -132,6 +132,7 @@ + diff --git a/star-raid/js/boss_art.js b/star-raid/js/boss_art.js new file mode 100644 index 0000000..35e5e17 --- /dev/null +++ b/star-raid/js/boss_art.js @@ -0,0 +1,78 @@ +"use strict"; +/* ================= Boss 美术库(大改重设计) ================= + 激光钢琴 drawPiano / 夺权者X drawUsurper / 暗黑魔碟 drawSaucer + 设计标准见 .astrion/memory/boss_design_standard.md: + 大剪影+3~4层配色递进+多层结构+受光逻辑+发光焦点+微动效;变形=同一几何体连续演化 */ +/* ---- 激光钢琴:穹顶琴键要塞(split=裂壳进度0~1) ---- + 4层同心装甲环带+像素琴键炮口带;变形=环带沿中线阶梯滑开(内层滑更远)露红色管线网+红核 */ +function drawPiano(b,t,fl,split){ + const ks=b.keys,n=ks?ks.n:21; + // —— 红色管线网(壳下常存,裂壳才见;锯齿线非圆) —— + if(split>0){ctx.globalAlpha=split*0.85; + for(let k=0;k<5;k++){const lx=-40+k*20; + px(lx,-14,1,10,"#ff3355");px(lx,-5,4,1,"#ff3355");px(lx+3,-4,1,8,"#ff3355");} + ctx.globalAlpha=1;} + // —— 红色核心(裂壳显现,搏动;收在穹顶基线之上) —— + if(split>0){ctx.globalAlpha=split; + const cr=8+Math.sin(t*0.12)*1.5; + circ(0,-8,cr+4,"#7a0e1e");circ(0,-8,cr,fl||"#ff3355");circ(0,-8,cr*0.45,"#ffdfe5"); + ctx.globalAlpha=1;} + if(split<1){ctx.globalAlpha=1-split; // 一阶段青色核心窗 + px(-7,-14,14,9,fl||"#43e8ff");px(-4,-12,8,5,"#bfefff"); + ctx.globalAlpha=1;} + // —— 穹顶4层装甲环带(沿中线剖半;内层滑得更远=阶梯裂口) —— + const BANDS=[[118,34,"#10151f"],[98,28,"#1b2434"],[74,21,"#33436a"],[50,14,"#55648a"]]; + for(let bi=0;bi<4;bi++){const off=split*(16+bi*6),rx=BANDS[bi][0],ry=BANDS[bi][1]; + for(const sd of[-1,1]){ctx.save();ctx.translate(sd*off,0); + const q0=sd<0?Math.PI:Math.PI*1.5,q1=sd<0?Math.PI*1.5:Math.PI*2; + ctx.fillStyle=BANDS[bi][2];ctx.beginPath();ctx.ellipse(0,6,rx,ry,0,q0,q1);ctx.lineTo(0,6);ctx.closePath();ctx.fill(); + ctx.restore();}} + // —— 外环窗火+壳沿铆钉(随最外半壳滑开) —— + for(const sd of[-1,1]){ctx.save();ctx.translate(sd*split*16,0); + for(let k=0;k<4;k++)px(sd*(20+k*22)-2,-2,4,3,t%40<20?"#3d5a73":"#24304a"); // 窗火明暗 + for(let i=0;i<7;i++){const a=Math.PI+(i+0.5)/7*Math.PI; // 壳沿铆钉 + if((sd<0)!==(a0&&b.turrI===ti?"#e8fbff":"#1b2434");}} + // —— 左右大炮塔(踏板重音炮位;左紫右蓝;P2随最外环带滑开16) —— + {const off0=split*16; + for(const sd of[-1,1]){const px2=sd*(112+off0); + circ(px2,2,5.5,"#33436a");circ(px2-1.5,0,2.5,"#55648a"); + px(px2-1.5,4,3,8,"#1b2434"); + px(px2-1,10,2,3,b.pedT>0?"#ffffff":(sd<0?"#b98cff":"#43e8ff"));}} + // —— 舰桥+雷达(随半壳滑开) —— + for(const sd of[-1,1]){ctx.save();ctx.translate(sd*split*30,0); + px(sd<0?-10:0,-32,10,10,"#1b2434"); + px(sd<0?-6:0,-37,6,5,"#33436a"); + ctx.restore();} + ctx.save();ctx.translate(-split*30,0); // 雷达随左半壳 + circ(0,-34,4,"#33436a"); + drawSeg(0,-34,Math.cos(t*0.05)*8,-34+Math.sin(t*0.05)*8,1,"#43e8ff",0.55); // 雷达扫面 + px(-1,-38,2,2,t%30<15?"#ff3355":"#7a1622"); + ctx.restore(); + // —— 发光纹路(暗纹常存,蓄力时向键带点亮蔓延) —— + {const pr=(ks&&ks.hotT>0&&ks.hotT0)?1-ks.hotT/ks.hotT0:0; + for(let k=0;k<5;k++){const tx2=-60+k*30; + px(tx2,-8,1,12,"#134152"); + if(pr>0){ctx.globalAlpha=pr*0.7;px(tx2,-8,1,12,"#43e8ff");ctx.globalAlpha=1;}}} + // —— 琴键炮口带(21键像素钢琴:白键+黑键+键缝;键位公式 -(n-1)*5+k*10 与enemies.js同步) —— + px(-(n-1)*5-5,6,(n-1)*10+10,19,"#0a0e16"); // 键槽 + for(let i=0;i=0)); + px(kx-4,8,8,14,hot?(t%6<3?"#e8fbff":"#43e8ff"):"#d8e4f0"); + px(kx-4,20,8,2,hot?"#43e8ff":"#8a9ab8"); + if(i=0)px(kx+3,8,4,7,"#101318");} // 黑键(钢琴纹样) + if(ks&&ks.hotT>0&&ks.hotT0){const pr=1-ks.hotT/ks.hotT0; // 蓄力前摇:即将发射的键下方圆形蓄力(批准的例外) + const list=ks.hotList||(ks.hot>=0?[ks.hot]:[]); + for(const kh of list){const kx=-(n-1)*5+kh*10; + ctx.globalAlpha=0.3+0.55*pr;ctx.strokeStyle="#43e8ff";ctx.lineWidth=1.5; + ctx.beginPath();ctx.arc(kx,28,2+3.5*pr,0,TAU);ctx.stroke(); + ctx.globalAlpha=0.22*pr;ctx.fillStyle="#43e8ff"; + ctx.beginPath();ctx.arc(kx,28,2+3.5*pr,0,TAU);ctx.fill(); + ctx.globalAlpha=1;}} +} diff --git a/star-raid/js/enemies.js b/star-raid/js/enemies.js index f959ae6..75ea5ac 100644 --- a/star-raid/js/enemies.js +++ b/star-raid/js/enemies.js @@ -192,7 +192,7 @@ const BOSSES=[ {name:"太空原种",sub:"SPACE SPAWN",hp:1350,col:"#ff8fc8",r:20,rr:[20,34],ryy:[26,18],wide:1,main:"spawn",minors:["wiggle","spikeSnipe","sporeArc","tentBeam","mawSnipe","wingRain"],phases:2}, {name:"歼灭者",sub:"ANNIHILATOR",hp:1700,col:"#b05ce8",r:22,main:"annihilate",minors:["aimSpread","spawnMine"],phases:3}, {name:"暗黑突击",sub:"DARK ASSAULT",hp:2100,col:"#ff3355",r:9,rr:[9,10,11],ryy:[5,6,7],main:"assault",minors:["rain","aimSpread"],phases:3}, - {name:"激光钢琴",sub:"LASER PIANO",hp:2500,col:"#43e8ff",r:26,rr:[100,100],ryy:[30,30],main:"piano",minors:["topRain","ring"],phases:2,static:1}, + {name:"激光钢琴",sub:"LASER PIANO",hp:2500,col:"#43e8ff",r:26,rr:[100,100],ryy:[30,30],main:"piano",minors:["topRain","pedal"],phases:2,static:1}, {name:"母巢核心",sub:"HIVE CORE",hp:2800,col:"#ff9a3d",r:22,main:"hive",minors:["spawnMini","aimSpread","ring"],phases:3}, {name:"夺权者X",sub:"USURPER X",hp:3700,col:"#e8f4ff",r:24,rr:[52,26],ryy:[34,40],wide:1,main:"usurper",minors:["chain","pillar","aimSpread"],phases:2}, {name:"暗黑魔碟",sub:"DARK SAUCER",hp:4500,col:"#8a5cff",r:24,rr:[30,32,34],main:"saucer",minors:["ringBig","coneFan","crossEye","dartSpiral"],phases:3}, @@ -302,7 +302,7 @@ function updateBoss(){ else{b.x=W/2+Math.sin(b.mt*0.013)*(def.wide?44:70);b.y=b.ty+Math.sin(b.mt*0.011)*(def.wide?8:12);} // 通用游走提速+上下浮动(钢琴站桩除外) b.mt++; const fm=b.fireM*(1+0.22*b.phase); - if(b.fnT>0)b.fnT--;if(b.plT>0)b.plT--;if(b.eyeT>0)b.eyeT--;if(b.hornT>0)b.hornT--;if(b.laserT>0)b.laserT--;if(b.eyeGlow>0)b.eyeGlow--;if(b.rainT>0)b.rainT--; // 帧级技能计时器 + if(b.fnT>0)b.fnT--;if(b.plT>0)b.plT--;if(b.eyeT>0)b.eyeT--;if(b.hornT>0)b.hornT--;if(b.laserT>0)b.laserT--;if(b.eyeGlow>0)b.eyeGlow--;if(b.rainT>0)b.rainT--;if(b.turrT>0)b.turrT--;if(b.pedT>0)b.pedT--; // 帧级技能计时器 if(b.idx===1&&b.rainT>0&&b.rainT<94&&b.rainT%3===0){ // 原种红雨:前摇16帧双翼高频抖动后,翼展范围内红光点随机洒落 const span=46*(0.22+0.78*(b.phase>=1?1:0.62)); ebul(b.x+rnd(-span,span),b.y+rnd(-10,2),Math.PI/2+rnd(-0.08,0.08),rnd(2.2,3.4),"orbR",12);} @@ -385,17 +385,18 @@ function bossMain(b,def,fm){ ks.hotList=null;ks.hot=-1;SFX.eshoot();}} ks.t--; if(ks.t<=0&&ks.hotT<=0){ - if(ks.i>=ks.seq.length){ // 布下一局:轮换模式 - ks.mode=(ks.mode+1)%4; + if(ks.i>=ks.seq.length){ // 布下一局:轮换模式(5种) + ks.mode=(ks.mode+1)%5; if(ks.mode===0){ks.seq=[];for(let i=0;i=3;i--)ks.seq.push(i);} // 反扫止于3号:最左3键永远安全 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=齐射标记 + else if(ks.mode===3){ks.seq=[];for(let i=0;i<6+ph*2;i++)ks.seq.push(-1);} // -1=齐射标记 + else{ks.seq=[];for(let i=0;i=0;i--)ks.seq.push(i);} // 滑奏:顺扫到尾立刻反扫回来 ks.i=0;ks.t=Math.round((ph?14:22)/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=9;ks.hotT0=9;ks.t=Math.round((ph?21:30)/fm);} // 齐射:慢50% - else if(ks.mode<=1){ks.hot=cur;ks.hotT=1;ks.hotT0=1;ks.t=Math.round((ph?2:3)/fm);} // 顺/反扫:前摇快50% - else{ks.hot=cur;ks.hotT=3;ks.hotT0=3;ks.t=Math.round((ph?3:5)/fm);} // 乱序:慢50% + else if(ks.mode<=1||ks.mode===4){ks.hot=cur;ks.hotT=1;ks.hotT0=1;ks.t=Math.round((ph?2:3)/fm);} // 顺/反扫/滑奏:前摇快50% + else{if(ph>=1){ks.hotList=[cur,ks.i=1?28:0,ti=rndi(0,5),tx=b.x+(-75+ti*30)+(ti<3?-1:1)*off2; + b.turrI=ti;b.turrT=8; + for(let i=0;i<3;i++)ebul(tx+rnd(-3,3),b.y-2,Math.PI/2+rnd(-0.12,0.12),2.0,"orbG",13); + b.minorT=Math.round(150/fm);break;} + case "pedal": { // 钢琴:踏板重音——左右大炮塔慢速大弹(左紫右蓝;P2随外环滑开16;炮位与drawPiano同源) + const off0=b.phase>=1?16:0; + for(const sd of[-1,1]){const bb=ebul(b.x+sd*(112+off0),b.y+10,Math.PI/2+sd*0.08,1.1,sd<0?"orbP":"dartB",18); + bb.scale=2.4;bb.r=6;} + b.pedT=8;b.minorT=Math.round(280/fm);break;} case "chain": for(let arm=0;arm<2;arm++)for(let i=0;i<4;i++)ebul(b.x,b.y+8,b.t*0.09+arm*Math.PI+i*0.11,1.7,"orbO",14);b.minorT=Math.round(44/fm);break; case "pillar": {const px2=rnd(40,W-40);bossBeam(px2,-6,px2,H+8,4,"#b98cff",26,26,18);b.minorT=Math.round(220/fm);break;} case "eyeBeam": bossBeam(b.x,b.y+6,ap2.x,H+8,4,"#ff3355",22,24,20);b.minorT=Math.round(180/fm);break; diff --git a/star-raid/js/render.js b/star-raid/js/render.js index 8579de8..e37166e 100644 --- a/star-raid/js/render.js +++ b/star-raid/js/render.js @@ -337,43 +337,6 @@ function drawAssault(t,fl,swp,ph,mp){ // 暗黑突击(swp=翼展角;炮管/ if(ph>=1){poly([-52,-14,-24,-3,-22,-1,-50,-11],"#ffdfe5");poly([52,-14,24,-3,22,-1,50,-11],"#ffdfe5");} if(ph>=2){px(-4,-16,8,12,fl||"#ff3355");px(-2,-14,4,8,"#ffdfe5");} } -function drawPiano(b,t,fl,split){ // 激光钢琴(split=裂壳进度0~1) - const ks=b.keys,n=ks?ks.n:21; - px(-(n-1)*5-5,6,(n-1)*10+10,19,"#141821"); // 键槽(键距10统一,不再因非整数取整成对) - for(let i=0;i=0)); - px(kx-4,8,8,14,hot?(t%6<3?"#e8fbff":"#43e8ff"):"#d8e4f0"); - px(kx-4,20,8,2,hot?"#43e8ff":"#8a9ab8");} - if(ks&&ks.hotT>0&&ks.hotT0){const pr=1-ks.hotT/ks.hotT0; // 蓄力前摇:即将发射的键下方圆形蓄力 - const list=ks.hotList||(ks.hot>=0?[ks.hot]:[]); - for(const kh of list){const kx=-(n-1)*5+kh*10; - ctx.globalAlpha=0.3+0.55*pr;ctx.strokeStyle="#43e8ff";ctx.lineWidth=1.5; - ctx.beginPath();ctx.arc(kx,28,2+3.5*pr,0,TAU);ctx.stroke(); - ctx.globalAlpha=0.22*pr;ctx.fillStyle="#43e8ff"; - ctx.beginPath();ctx.arc(kx,28,2+3.5*pr,0,TAU);ctx.fill(); - ctx.globalAlpha=1;}} - const off=split*30; // 两半穹顶向左右裂开 - for(const sd of[-1,1]){ctx.save();ctx.translate(sd*off,0); - const q0=sd<0?Math.PI:Math.PI*1.5,q1=sd<0?Math.PI*1.5:Math.PI*2; - ctx.fillStyle="#3a4460";ctx.beginPath();ctx.ellipse(0,6,118,34,0,q0,q1);ctx.lineTo(0,6);ctx.closePath();ctx.fill(); - ctx.fillStyle="#55648a";ctx.beginPath();ctx.ellipse(0,6,92,26,0,q0,q1);ctx.lineTo(0,6);ctx.closePath();ctx.fill(); - ctx.fillStyle="#6e7f9e";ctx.beginPath();ctx.ellipse(0,6,60,17,0,q0,q1);ctx.lineTo(0,6);ctx.closePath();ctx.fill(); - ctx.restore();} - if(split>0){ctx.globalAlpha=split; // 红色核心 - const cr=10+Math.sin(t*0.12)*2; - circ(0,-4,cr+4,"#7a0e1e");circ(0,-4,cr,fl||"#ff3355");circ(0,-4,cr*0.45,"#ffdfe5"); - ctx.globalAlpha=1;} - if(split<1){ctx.globalAlpha=1-split; // 一阶段青色核心窗 - px(-7,-14,14,9,fl||"#43e8ff");px(-4,-12,8,5,"#bfefff"); - ctx.globalAlpha=1;} - for(const sd of[-1,1]){ctx.save();ctx.translate(sd*off,0); // 舰桥从中线剖开,各随各的半壳 - px(sd<0?-10:0,-32,10,10,"#3a4460"); - px(sd<0?-6:0,-37,6,5,"#55648a"); - for(let i=0;i<7;i++){const a=Math.PI+(i+0.5)/7*Math.PI; // 壳沿铆钉也随半壳 - if((sd<0)!==(a