292 lines
20 KiB
JavaScript
292 lines
20 KiB
JavaScript
"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)!==(a<Math.PI*1.5))continue;
|
||
px(Math.cos(a)*110-1.5,6+Math.sin(a)*30-1.5,3,3,"#9fb4d8");}
|
||
ctx.restore();}
|
||
// —— 6座穹顶小炮塔(topRain炮位,与enemies.js同源:x=-75+ti*30,P2随第3环带滑开28) ——
|
||
{const off2=split*28;
|
||
for(let ti=0;ti<6;ti++){const sd2=ti<3?-1:1,tx=-75+ti*30+sd2*off2;
|
||
circ(tx,-4,3.5,"#33436a");circ(tx-1,-5,1.5,"#55648a");
|
||
px(tx-1,-2,2,5,b.turrT>0&&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<n;i++){const kx=-(n-1)*5+i*10;
|
||
const hot=ks&&(ks.hot===i||(ks.hotList&&ks.hotList.indexOf(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<n-1&&[0,1,3,4,5].indexOf(i%7)>=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;}}
|
||
}
|
||
/* ---- 夺权者X:P1三层阶梯甲板母舰(巨大厚重)→ P2抛甲板露粗壮宽翼战机(紧凑但不细瘦) ----
|
||
无眼睛设计(机械反应环,非生物眼);变形=甲板整体抛离旋转飞走+机身渐显(壳下常存) */
|
||
function drawUsurper(b,t,fl,ph2,mp){
|
||
const jt=mp||0;
|
||
// —— P2粗壮宽翼战机(壳下常存,mp渐显;宽翼+厚机身+三引擎,绝不细瘦) ——
|
||
if(ph2>=1){const di=mp!==null?mp:1;
|
||
ctx.save();ctx.globalAlpha=di;const ds=(0.7+0.3*di)*1.15;ctx.scale(ds,ds);
|
||
for(const sd of[-1,1]){ // 后掠宽翼(翼展±30)
|
||
poly([sd*11,0,sd*28,-13,sd*32,-7,sd*13,8],"#c9d6e2");
|
||
poly([sd*11,0,sd*28,-13,sd*26,-10,sd*12,3],"#e8f4ff"); // 翼面受光
|
||
drawSeg(sd*12,-1,sd*27,-11,2,"#43b8ff",0.9); // 前缘蓝灯带
|
||
px(sd*28-2,-13,4,3,"#ff9a3d"); // 翼尖橙标识
|
||
px(sd*12,4,3,3,"#ff3355");} // 翼根红炮口
|
||
poly([0,32,-13,16,-11,-18,-6,-32,6,-32,11,-18,13,16],"#e8f4ff"); // 粗壮机身
|
||
poly([0,32,13,16,11,-18,6,-32,0,-32],"#9fb0c6");
|
||
px(-11,-18,22,2,"#5a6c80");px(-9,-6,18,2,"#5a6c80"); // 装甲板缝
|
||
px(-13,16,26,3,"#ffffff"); // 底缘受光(粗边框感)
|
||
// 中央反应环(机械环非眼睛:外环+旋转弧段+内芯搏动)
|
||
circ(0,-10,7,"#0d2244");
|
||
ctx.strokeStyle=fl||"#43b8ff";ctx.lineWidth=2;ctx.beginPath();ctx.arc(0,-10,7,0,TAU);ctx.stroke();
|
||
for(let k=0;k<3;k++){const ra=t*0.03+k*TAU/3;
|
||
ctx.beginPath();ctx.arc(0,-10,7,ra,ra+0.8);ctx.stroke();}
|
||
circ(0,-10,3+Math.sin(t*0.1)*0.5,"#bfefff");
|
||
if(b.laserT>0){ctx.globalAlpha=di*0.5;circ(0,-10,10,"#7ce9ff");ctx.globalAlpha=di;} // 巨宽激光期高亮
|
||
for(const sd of[-1,1]){px(sd*5-2,-34,4,3,"#5a6c80"); // 三引擎尾焰
|
||
px(sd*5-1.5,-38-(t%4),3,3+(t%4),t%6<3?"#7fd8ff":"#43b8ff");}
|
||
px(-1.5,-34,3,3,"#5a6c80");px(-1,-39-(t%4),2,2+(t%4),"#7fd8ff");
|
||
ctx.restore();ctx.globalAlpha=1;}
|
||
// —— P1巨舰(变形=甲板抛离飞走+舰体淡出;整体1.25倍放大=参考图体量) ——
|
||
if(ph2===0||mp!==null){ctx.save();ctx.scale(1.25,1.25);const hullA=1-jt;
|
||
if(hullA>0.02){ctx.save();ctx.globalAlpha=hullA;
|
||
// 主舰体:三段式宽塔(厚重霸气)
|
||
poly([0,36,-24,14,-20,-14,-12,-36,12,-36,20,-14,24,14],"#d8e4f0");
|
||
poly([0,36,24,14,20,-14,12,-36,0,-36],"#9fb0c6");
|
||
px(-24,14,48,3,"#e8f4ff"); // 底缘受光
|
||
px(-20,2,40,2,"#5a6c80");px(-16,-14,32,2,"#5a6c80");px(-12,-24,24,2,"#5a6c80"); // 层间板缝
|
||
for(let k=0;k<6;k++)px(-18+k*7,-1,2,2,"#8a9ab8"); // 装甲铆钉
|
||
// 指挥塔(顶部)+天线
|
||
px(-8,-44,16,8,"#c9d6e2");px(-5,-50,10,6,"#9fb0c6");
|
||
px(-1,-56,2,6,"#5a6c80");px(-1.5,-58,3,2,t%30<15?"#ff3355":"#7a1622");
|
||
// 中央反应环(机械环非眼睛:外环+旋转弧段+内芯搏动)
|
||
circ(0,-6,9,"#0d2244");
|
||
ctx.strokeStyle=fl||"#43b8ff";ctx.lineWidth=2;ctx.beginPath();ctx.arc(0,-6,9,0,TAU);ctx.stroke();
|
||
for(let k=0;k<3;k++){const ra=t*0.02+k*TAU/3;
|
||
ctx.globalAlpha=hullA*0.85;ctx.beginPath();ctx.arc(0,-6,9,ra,ra+0.7);ctx.stroke();ctx.globalAlpha=hullA;}
|
||
circ(0,-6,4+Math.sin(t*0.08)*0.6,"#bfefff");
|
||
if(b.laserT>0){ctx.globalAlpha=hullA*0.5;circ(0,-6,12,"#7ce9ff");ctx.globalAlpha=hullA;}
|
||
// 机腹炮列(红色炮口一排)
|
||
for(let k=-2;k<=2;k++)px(k*8-1.5,18,3,4,"#ff3355");
|
||
// 引擎列(顶部4喷口,焰长抖动)
|
||
for(let e=0;e<4;e++){const ex2=-12+e*8;
|
||
px(ex2-2,-40,4,3,"#5a6c80");
|
||
px(ex2-1.5,-44-(t+e*2)%4,3,3+(t+e*2)%4,e%2?"#7fd8ff":"#43b8ff");}
|
||
ctx.restore();ctx.globalAlpha=1;}
|
||
// —— 三层阶梯甲板(左右各3层;变形=整体抛离旋转飞走,带运动轨迹) ——
|
||
for(const sd of[-1,1])for(let L=0;L<3;L++){
|
||
const ja=(1-jt)*(1-jt*0.85);
|
||
if(ja<=0.02)continue;
|
||
ctx.save();ctx.translate(sd*jt*(70+L*30),jt*(30+L*14));ctx.rotate(sd*jt*(0.4+L*0.25));ctx.globalAlpha=ja;
|
||
const dw=34+L*9,dy=-26+L*13; // 阶梯向下外扩
|
||
poly([sd*8,dy-5,sd*(dw+8),dy,sd*(dw+4),dy+9,sd*8,dy+7],"#c9d6e2");
|
||
poly([sd*8,dy-5,sd*(dw+8),dy,sd*(dw+4),dy+3,sd*8,dy+1],"#e8f4ff"); // 甲板面受光
|
||
px(sd*(dw+2)-2,dy+2,4,2,t%50<25?"#7fd8ff":"#2a5a78"); // 板缘蓝灯带(明灭)
|
||
px(sd*(dw-8)-2,dy+4,4,3,"#ff9a3d"); // 橙色标识块
|
||
px(sd*14-1,dy+1,10,1,"#5a6c80"); // 甲板板缝
|
||
for(let g=0;g<2+L;g++){const gx=sd*(14+g*10); // 红色炮口(deckVolley前摇:最下层逐个点亮)
|
||
const glow=b.deckT>0&&L===2&&(26-b.deckT)>g*6;
|
||
px(gx-1,dy+7,3,3,glow?"#ffffff":"#ff3355");}
|
||
if(L===1){const tx3=sd*(dw+2); // 侧舷双联副炮塔(随甲板抛离)
|
||
px(tx3-2,dy-8,5,10,"#9fb0c6");
|
||
px(tx3-1,dy-12,1.5,5,"#5a6c80");px(tx3+1,dy-12,1.5,5,"#5a6c80");
|
||
px(tx3-1.5,dy-14,2,2,"#ff3355");px(tx3+0.5,dy-14,2,2,"#ff3355");}
|
||
ctx.restore();ctx.globalAlpha=1;}
|
||
ctx.restore();}
|
||
}
|
||
/* ---- 暗黑魔碟:8扇区缝合装甲碟+红能量窗/裂纹+紫红竖瞳魔眼 ----
|
||
变形沿用原机制(mp优先):P2圆环绕顶轴外旋裂开+魔眼点亮;P3爆甲壳板飞离+铆钉伸长成刺+犄角炮台 */
|
||
function drawSaucer(b,t,fl,ph2,mp){
|
||
let open,fo,eyeG;
|
||
if(ph2===0){open=0;fo=0;eyeG=0;}
|
||
else if(ph2===1){open=mp!==null?mp:1;fo=0;eyeG=mp!==null?mp:1;}
|
||
else{open=1;fo=mp!==null?mp:1;eyeG=1;}
|
||
const R=30+open*3;
|
||
// —— 碟心基盘+8装甲扇区(扇区纹饰缓转,眼不转=诡异感;扇区间红裂纹随阶段增亮) ——
|
||
circ(0,0,20,"#1a1030");circ(0,0,15,"#12081c");
|
||
ctx.save();ctx.rotate(t*0.004);
|
||
for(let k=0;k<8;k++){ctx.save();ctx.rotate(k*TAU/8);
|
||
poly([0,-19,6,-14,4,-8,-4,-8,-6,-14],k%2?"#241430":"#2a1a3a"); // 扇区装甲(明暗交替)
|
||
px(-0.5,-19,1,11,open>0?"#ff3355":"#7a1622"); // 扇区间裂纹(P2转亮红)
|
||
ctx.restore();}
|
||
for(let k=0;k<8;k++){const a=(k+0.5)*TAU/8; // 红色六边形能量窗(随扇区角度旋转,呼吸明灭)
|
||
ctx.globalAlpha=(0.3+0.3*Math.sin(t*0.08+k*1.1))*(0.5+0.5*eyeG);
|
||
ctx.save();ctx.translate(Math.sin(a)*12.5,-Math.cos(a)*12.5);ctx.rotate(a);
|
||
px(-2,-1,4,2,"#ff3355");px(-1,-2,2,4,"#ff3355");
|
||
ctx.restore();ctx.globalAlpha=1;}
|
||
ctx.restore();
|
||
// —— 眼窝(常存)+紫红魔眼(P2点亮;竖瞳追踪玩家,P3瞳孔收成竖缝) ——
|
||
px(-11,-5,7,6,"#241430");px(4,-5,7,6,"#241430");
|
||
if(eyeG>0){ctx.globalAlpha=eyeG;
|
||
px(-10,-4,5,4,"#c86bff");px(5,-4,5,4,"#c86bff");
|
||
const pu=clamp((G.p.x-b.x)*0.02,-1.5,1.5);
|
||
px(-8.5+pu,-4,ph2>=2?1:1.8,4,fl||"#ff3355");px(6.5+pu,-4,ph2>=2?1:1.8,4,fl||"#ff3355");
|
||
ctx.globalAlpha=1;}
|
||
// —— 外环:12节分节环(节间红发光条呼吸);变形=绕顶轴外旋裂开、下端伸长变犄角(同一几何体连续演化) ——
|
||
const rot=open*0.55;
|
||
for(const sd of[-1,1]){ctx.save();
|
||
ctx.translate(0,-R);ctx.rotate(-sd*rot);ctx.translate(0,R);
|
||
const a0=sd<0?Math.PI/2:-Math.PI/2,a1=sd<0?Math.PI*1.5:Math.PI/2;
|
||
for(let sg=0;sg<6;sg++){const sa0=a0+sg*(a1-a0)/6+0.04,sa1=a0+(sg+1)*(a1-a0)/6-0.04; // 6节/半环
|
||
ctx.strokeStyle="#2a1a3a";ctx.lineWidth=11;
|
||
ctx.beginPath();ctx.arc(0,0,R,sa0,sa1);ctx.stroke();
|
||
ctx.strokeStyle="#55336e";ctx.lineWidth=4.5;
|
||
ctx.beginPath();ctx.arc(0,0,R,sa0,sa1);ctx.stroke();}
|
||
for(let k=0;k<5;k++){const ag=a0+(k+1)*(a1-a0)/6; // 节间红发光条(径向对齐,呼吸明灭)
|
||
ctx.globalAlpha=0.45+0.4*Math.sin(t*0.09+k);
|
||
ctx.save();ctx.translate(Math.cos(ag)*R,Math.sin(ag)*R);ctx.rotate(ag);
|
||
px(-4,-1,8,2,"#ff3355");ctx.restore();ctx.globalAlpha=1;}
|
||
const hL=3+open*26; // 半环下端伸长变尖:圆环下半部分延续为朝下的锥刺(同一几何体)
|
||
poly([-6.5,R,6.5,R,2.5,R+hL*0.55,0,R+hL,-2.5,R+hL*0.55],"#2a1a3a");
|
||
poly([-3.5,R,3.5,R,1.2,R+hL*0.5,0,R+hL*0.8,-1.2,R+hL*0.5],"#55336e");
|
||
for(let k=0;k<3;k++){const aa=a0+(sd<0?0.35+k*0.2:0.65-k*0.2)*(a1-a0); // 左右半环镜像对称
|
||
const rx=Math.cos(aa),ry=Math.sin(aa),tx2=-ry,ty2=rx;
|
||
const bl=2+9*fo; // 铆钉→尖刺连续伸长
|
||
poly([rx*(R+5)+tx2*2.5,ry*(R+5)+ty2*2.5, rx*(R+5)-tx2*2.5,ry*(R+5)-ty2*2.5, rx*(R+5+bl),ry*(R+5+bl)],fo>0.6?(fl||"#ff3355"):"#241430");
|
||
if(fo<0.99){ctx.save(); // 外壳板:爆甲时旋转飞离(有运动轨迹,非消失)
|
||
ctx.translate(rx*(R+9+fo*22)+tx2*fo*sd*9,ry*(R+9+fo*22)+ty2*fo*sd*9);
|
||
ctx.rotate(aa+Math.PI/2+fo*sd*1.7);ctx.globalAlpha=1-fo;
|
||
px(-5,-3,10,6,"#2a1a3a");px(-5,-3,10,2,"#55336e");
|
||
ctx.restore();ctx.globalAlpha=1;}}
|
||
{const ta=a0+0.5*(a1-a0),bl2=2+6*fo; // 红色犄角炮台:基座常存,爆甲后炮管伸出点亮
|
||
ctx.save();ctx.translate(Math.cos(ta)*(R+2),Math.sin(ta)*(R+2));ctx.rotate(ta+Math.PI/2);
|
||
px(-2.5,-3,5,4,"#241430");
|
||
px(-1.5,-3-bl2,3,bl2,fo>0.4?(fl||"#ff3355"):"#3a2248");
|
||
ctx.restore();}
|
||
ctx.restore();}
|
||
circ(0,-R,4,"#55336e"); // 顶部铰链节
|
||
}
|
||
/* ---- 太空原种/暗黑突击绘制(从render.js迁入,集中管理Boss美术) ---- */
|
||
function drawProgenitor(b,t,fl,tf){ // 太空原种·装甲虫兽(tf 0=装甲态→1=血肉态):P1小翅芽→P2骨膜巨翼;无复眼(大颚+感器红缝=虫形压迫感);摆尾衔接于躯体之下(尾根被加长的身体覆住,无缝)
|
||
ctx.rotate(Math.sin(t*0.05)*0.04); // 悬停微摇
|
||
const wS=0.22+0.78*tf; // 翼展:P1小翅芽→P2全展(同一对翼连续缩放)
|
||
// —— 骨膜翼一对:3根骨刺撑起膜面(P1灰甲小翼→P2血红膜+暗红骨,同一几何体罩色) ——
|
||
for(const sd of[-1,1]){ctx.save();
|
||
const flut=b.rainT>0?Math.sin(t*0.62)*0.13:Math.sin(t*0.07)*0.03; // 红雨前摇/洒落期双翼高频抖动
|
||
ctx.translate(sd*7,-8);ctx.scale(sd*wS,1+flut); // 呼吸/抖动+镜像
|
||
poly([0,-4,14,-16,34,-16,54,-10,48,0,34,6,16,4,0,2],"#c9d6e2"); // 膜面主甲
|
||
poly([0,-4,14,-16,34,-16,54,-10,40,-8,18,-8,0,-1],"#e8f0f6"); // 上缘受光
|
||
poly([0,2,16,4,34,6,48,0,36,0,14,2],"#8498ac"); // 下缘暗面
|
||
for(const tp of[[14,-15],[34,-15],[54,-9]])drawSeg(2,-2,tp[0],tp[1],2,"#8498ac",1); // 3根骨刺(辐射)
|
||
if(tf>0){ctx.globalAlpha=tf*0.85;
|
||
poly([0,-4,14,-16,34,-16,54,-10,48,0,34,6,16,4,0,2],"#c02030"); // P2膜面转血红(同面罩色)
|
||
for(const tp of[[14,-15],[34,-15],[54,-9]])drawSeg(2,-2,tp[0],tp[1],2,"#7a1622",tf); // P2骨刺转暗红
|
||
ctx.globalAlpha=1;}
|
||
ctx.restore();}
|
||
// —— 摆尾:先画在躯体下层(尾根被加长的身体覆住,无缝衔接),5节逐节相位摆动;P2节间血红肉显露+整体伸长 ——
|
||
const segH=5.2+tf*1.6,gap=11;
|
||
for(let i=4;i>=0;i--){const sw=Math.sin(t*0.055+i*0.7)*(0.8+i),sy=gap+i*segH,hw=9-i*1.5;
|
||
if(tf>0&&i<4){ctx.globalAlpha=tf*0.85;px(sw-hw+2,sy+segH-2,(hw-2)*2,2,"#c8202e");ctx.globalAlpha=1;} // 节间血肉环
|
||
poly([sw-hw,sy,sw+hw,sy,sw+hw-2,sy+segH,sw-hw+2,sy+segH],i%2?"#b8c8d8":"#c9d6e2"); // 尾节甲片
|
||
px(sw-hw,sy,3,segH,"#e8f0f6");px(sw+hw-3,sy,3,segH,"#8498ac"); // 左亮右暗
|
||
for(const sd of[-1,1])poly([sw+sd*(hw+1),sy+1,sw+sd*(hw+4),sy+2.5,sw+sd*(hw+1),sy+4],"#e8dcc0"); // 节侧骨刺
|
||
}
|
||
const swT=Math.sin(t*0.055+2.8)*4.8,tipY=gap+4*segH; // 尾末生殖腔(花弹发射口,与enemies.js同源公式)
|
||
circ(swT,tipY+2,2.6+Math.sin(t*0.11)*0.6,fl||(tf>0.5?"#ff2a3c":"#ff5566"));
|
||
circ(swT,tipY+2,1.1,"#ffdfe5");
|
||
// —— 壳下血肉(常存于背甲之下,滑开才见;血红,非粉) ——
|
||
if(tf>0){ctx.globalAlpha=tf*0.9;
|
||
poly([0,-36,9,-29,11,-14,10,2,9,14,7,16,-7,16,-9,14,-10,2,-11,-14,-9,-29],"#c8202e");
|
||
px(-2,-30,4,45,"#e8393a");ctx.globalAlpha=1;}
|
||
// —— 背甲两瓣:绕尾端枢轴楔开(顶端外摆露血肉、尾端原地不动,衔接处不变宽;同一几何体旋转) ——
|
||
for(const sd of[-1,1]){ctx.save();ctx.translate(0,14);ctx.rotate(sd*tf*0.28);ctx.translate(0,-14);
|
||
poly([0,-40,sd*11,-37,sd*15,-28,sd*13,-12,sd*12,4,sd*11,14,0,14],sd<0?"#c9d6e2":"#9fb0c2"); // 末端平边与尾节同材质(#c9d6e2)
|
||
px(sd<0?-11:0,12,11,2,sd<0?"#e8f0f6":"#8498ac"); // 底缘左亮右暗(与尾节描边一致,接缝读作甲片缝)
|
||
poly([0,-40,sd*11,-37,sd*15,-28,sd*7,-26,sd*5,-36],"#e8f0f6"); // 受光斜面
|
||
for(let i=0;i<4;i++)px(sd*(4+i*2.8)-1,-24+i*9,2,5,"#5a6c80"); // 甲片板缝
|
||
const crk=Math.max(0,Math.min(1,(tf-0.15)/0.4))*10; // 裂纹随tf生长(延长,非新增)
|
||
if(crk>0.5)px(sd*6-1,-18,2,crk,"#8a2a3a");
|
||
ctx.restore();}
|
||
// —— 头部:圆顶甲+锯齿犄角+下弯大颚(无复眼=虫形压迫感)+一线感器红缝 ——
|
||
circ(0,-34,10,"#dde7f0");px(-9,-36,18,3,"#9fb0c2");
|
||
for(const sd of[-1,1]){poly([sd*6,-40,sd*15,-49,sd*12,-51,sd*4,-43],"#8498ac");
|
||
poly([sd*10,-44,sd*13,-48,sd*11,-43],"#5a6c80"); // 犄角锯齿棱
|
||
px(sd*9-1,-46,2,4,"#7dff8a");} // 犄角绿纹
|
||
px(-5,-33,10,1.6,t%30<15?"#ff3355":"#7a1622"); // 感器红缝(明灭)
|
||
for(const sd of[-1,1]){poly([sd*3,-26,sd*9,-22,sd*8,-14,sd*5,-13,sd*2,-20],"#e8dcc0"); // 下弯大颚(骨白)
|
||
px(sd*5,-16,2,3,"#8a6a4a");} // 颚尖暗色
|
||
if(b.eyeGlow>0){ctx.globalAlpha=b.eyeGlow/20; // 击发余辉:大颚+感器缝闪白
|
||
px(-8,-22,4,3,"#fff");px(4,-22,4,3,"#fff");px(-5,-33,10,1.6,"#fff");ctx.globalAlpha=1;}
|
||
// —— 头侧青色管囊(P2转荧绿) ——
|
||
for(const sd of[-1,1]){px(sd*15-2.5,-18,5,12,tf>0.5?"#7dff8a":(t%40<20?"#7fd8ff":"#43b8ff"));
|
||
px(sd*15-1.5,-20,3,3,"#55648a");}
|
||
}
|
||
function drawAssault(t,fl,swp,ph,mp){ // 暗黑突击(swp=翼展角;炮管/推进器随阶段增多)
|
||
const sw=swp*Math.PI/180;
|
||
for(const sd of[-1,1]){
|
||
const tx=sd*(8+52*Math.sin(sw)),ty=14-52*Math.cos(sw);
|
||
poly([sd*4,6,tx,ty,tx-sd*9,ty+11,sd*4,20],ph>=2?"#c01a2e":"#a01224");
|
||
poly([tx-sd*2,ty-6,tx+sd*3,ty-14,tx+sd*7,ty-3],"#6e0c1a");}
|
||
poly([0,38,-5,6,-3,-26,3,-26,5,6],"#d4233a");
|
||
px(-2,8,4,8,fl||"#ffd23d");
|
||
const guns=[[16,4],[-16,4],[30,-4],[-30,-4],[42,-10],[-42,-10]];
|
||
const gn=ph>=2?6:ph>=1?4:2,pgn=ph===2?4:ph===1?2:0;
|
||
for(let i=0;i<gn;i++){let gl=8;if(mp!==null&&i>=pgn)gl=Math.max(1,8*mp); // 新炮管生长
|
||
px(guns[i][0]-1.5,guns[i][1],3,gl,"#3a0a10");px(guns[i][0]-1,guns[i][1]+gl-2,2,2,"#ff9a3d");}
|
||
const eng=[[7,-26],[-7,-26],[0,-30],[14,-22]];
|
||
const en=ph>=2?4:ph>=1?3:2,pen=ph===2?3:ph===1?2:0;
|
||
for(let i=0;i<en;i++){let el=5+(t%4<2?2:0);if(mp!==null&&i>=pen)el=Math.max(1,el*mp); // 新推进器
|
||
px(eng[i][0]-2,eng[i][1],4,3,"#5a0a14");
|
||
px(eng[i][0]-1.5,eng[i][1]-3-el,3,el,t%6<3?"#ff9a3d":"#ffcf5c");}
|
||
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");}
|
||
}
|
||
/* ---- Boss光束与浮游炮渲染 ---- */
|