Boss批量调整:血条去切段刻痕;魔碟P2激光间隔翻倍/去间歇期圆圈弹幕;钢琴扫射模式全随机;突击冲撞4-15秒随机+P3速度150%+冲锋红残影+P3移动更快惯性更小;夺权者X/黑石大激光与泰伦触手伤害99999;rush按难度分级分组

This commit is contained in:
JOJO 2026-08-06 13:29:00 +08:00
parent adc4fb36d3
commit 0261f44faa
4 changed files with 59 additions and 35 deletions

View File

@ -52,6 +52,9 @@ h1.logo{font-size:clamp(20px,5.4vw,44px);color:#fff;letter-spacing:4px;line-heig
.stat-grid b{color:var(--gold);text-align:right} .stat-grid b{color:var(--gold);text-align:right}
/* ship / mode select cards */ /* ship / mode select cards */
.pick{display:flex;gap:10px;flex-wrap:wrap;justify-content:center} .pick{display:flex;gap:10px;flex-wrap:wrap;justify-content:center}
.rush-grp{width:100%;margin:2px 0 10px}
.rush-grp .h3{font-size:clamp(9px,1.6vw,12px);letter-spacing:3px;margin-bottom:5px}
.rush-row{display:flex;gap:10px;flex-wrap:wrap;justify-content:center}
.pcard{width:min(28vw,132px);background:var(--panel);border:3px solid var(--line);padding:10px 6px;cursor:pointer;transition:all .12s} .pcard{width:min(28vw,132px);background:var(--panel);border:3px solid var(--line);padding:10px 6px;cursor:pointer;transition:all .12s}
.pcard:hover{transform:translateY(-3px)} .pcard:hover{transform:translateY(-3px)}
.pcard.sel{border-color:var(--cyan);box-shadow:0 0 16px rgba(65,230,255,.35)} .pcard.sel{border-color:var(--cyan);box-shadow:0 0 16px rgba(65,230,255,.35)}

View File

@ -188,17 +188,18 @@ function updateSpawner(){
} }
/* ================= Boss 战 ================= */ /* ================= Boss 战 ================= */
/* 8大Boss1种主攻击模式 + 同时1~2种随机小攻击血量阈值触发二/三阶段变形 */ /* 8大Boss1种主攻击模式 + 同时1~2种随机小攻击血量阈值触发二/三阶段变形 */
/* 难度tier0简单/1中等/2困难/3地狱rush界面分组用户2026-08-06分级 */
const BOSSES=[ const BOSSES=[
{name:"巡逻巡洋舰",sub:"PATROL CRUISER",hp:950,col:"#e0393e",r:22,main:"cruiser",minors:["snipe","ring"],phases:2}, {name:"巡逻巡洋舰",sub:"PATROL CRUISER",hp:950,col:"#e0393e",r:22,main:"cruiser",minors:["snipe","ring"],phases:2,tier:0},
{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:"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,tier:1},
{name:"歼灭者",sub:"ANNIHILATOR",hp:1700,col:"#b05ce8",r:22,main:"annihilate",minors:["aimSpread","spawnMine"],phases:3}, {name:"歼灭者",sub:"ANNIHILATOR",hp:1700,col:"#b05ce8",r:22,main:"annihilate",minors:["aimSpread","spawnMine"],phases:3,tier:0},
{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:"DARK ASSAULT",hp:2100,col:"#ff3355",r:9,rr:[9,10,11],ryy:[5,6,7],main:"assault",minors:["rain","aimSpread"],phases:3,tier:2},
{name:"激光钢琴",sub:"LASER PIANO",hp:5000,col:"#43e8ff",r:26,rr:[100,100],ryy:[30,30],main:"piano",minors:["topRain","pedal"],phases:2,static:1}, {name:"激光钢琴",sub:"LASER PIANO",hp:5000,col:"#43e8ff",r:26,rr:[100,100],ryy:[30,30],main:"piano",minors:["topRain","pedal"],phases:2,static:1,tier:2},
{name:"母巢核心",sub:"HIVE CORE",hp:2800,col:"#ff9a3d",r:22,main:"hive",minors:["spawnMini","aimSpread","ring"],phases:3}, {name:"母巢核心",sub:"HIVE CORE",hp:2800,col:"#ff9a3d",r:22,main:"hive",minors:["spawnMini","aimSpread","ring"],phases:3,tier:0},
{name:"夺权者X",sub:"USURPER X",hp:3700,col:"#e8f4ff",r:24,rr:[58,26],ryy:[34,40],wide:1,main:"usurper",minors:["deckVolley","pillar","aimSpread"],phases:2}, {name:"夺权者X",sub:"USURPER X",hp:3700,col:"#e8f4ff",r:24,rr:[58,26],ryy:[34,40],wide:1,main:"usurper",minors:["deckVolley","pillar","aimSpread"],phases:2,tier:2},
{name:"暗黑魔碟",sub:"DARK SAUCER",hp:4500,col:"#8a5cff",r:24,rr:[30,32,34],main:"saucer",minors:["ringBig","coneFan","crossEye","dartSpiral"],phases:3}, {name:"暗黑魔碟",sub:"DARK SAUCER",hp:4500,col:"#8a5cff",r:24,rr:[30,32,34],main:"saucer",minors:["coneFan","crossEye","dartSpiral"],phases:3,tier:3}, // 间歇期无圆圈弹幕去ringBigP3主攻击仍两层
{name:"黑石要塞",sub:"BLACKSTONE",hp:3400,col:"#5cff9d",r:30,rr:[34,37],ryy:[26,28],wide:1,main:"blackstone",minors:["deckGuns","crackRift","starLine"],phases:2}, {name:"黑石要塞",sub:"BLACKSTONE",hp:3400,col:"#5cff9d",r:30,rr:[34,37],ryy:[26,28],wide:1,main:"blackstone",minors:["deckGuns","crackRift","starLine"],phases:2,tier:2},
{name:"泰伦虫族母舰",sub:"TYRANID HIVESHIP",hp:3200,col:"#7dff8a",r:26,rr:[26,29,32],ryy:[36,38,40],main:"hivespawn",minors:["sporeBloom","tentSweep","acidGas"],phases:3} {name:"泰伦虫族母舰",sub:"TYRANID HIVESHIP",hp:3200,col:"#7dff8a",r:26,rr:[26,29,32],ryy:[36,38,40],main:"hivespawn",minors:["sporeBloom","tentSweep","acidGas"],phases:3,tier:1}
]; ];
/* Boss命中判定支持椭圆宽扁机型rr=x半径ry=y半径 */ /* Boss命中判定支持椭圆宽扁机型rr=x半径ry=y半径 */
function bossHit(x,y,r){ function bossHit(x,y,r){
@ -265,6 +266,7 @@ function startBoss(){
col:b.col,name:b.name,sub:b.sub,fireM:BAL.fireMul(n)*(G.diff===1?BAL.hard.fire:1),dead:false, 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}; mainT:100,minorT:9999,minorSet:[],rerollT:340,ax:W/2,ay:64,mt:0,landed:false};
if(b.main==="piano")G.boss.mainT=24; // 激光钢琴:缩短开局空窗 if(b.main==="piano")G.boss.mainT=24; // 激光钢琴:缩短开局空窗
else if(b.main==="assault")G.boss.mainT=rndi(240,900); // 暗黑突击首次冲撞4~15秒随机
G.bossN++;SFX.warn();MUS.start(true); G.bossN++;SFX.warn();MUS.start(true);
banner("WARNING — "+b.name,"#ff5566",150); banner("WARNING — "+b.name,"#ff5566",150);
} }
@ -281,26 +283,31 @@ function updateBoss(){
G.eb.length=0;G.bbeams.length=0;b.keys=null;SFX.warn();G.shake=Math.max(G.shake,5); 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;b.form=null;if(np===1){spawnFunnels(b,4);b.plT=Math.round(300/b.fireM);}} // 夺权者X二阶段增至4架僚机 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);} banner(b.name+" — 第"+["一","二","三"][np]+"形态","#ff5566",90);}
if(b.transT>0){b.transT--; if(b.transT>0){b.transT--;b.dashTrail=null; // 变形期间清冲撞残影
if(b.transT===0){explodeFx(b.x,b.y,"dark",1.3);G.shake=Math.max(G.shake,6); if(b.transT===0){explodeFx(b.x,b.y,"dark",1.3);G.shake=Math.max(G.shake,6);
for(let i=0;i<10;i++)ebul(b.x,b.y,i/10*TAU,1.5,"orbP",12);} // 变形完成爆发环弹 for(let i=0;i<10;i++)ebul(b.x,b.y,i/10*TAU,1.5,"orbP",12);} // 变形完成爆发环弹
updateFunnels();return;} // 变形期间不攻击 updateFunnels();return;} // 变形期间不攻击
// —— 冲撞状态机(暗黑突击) —— // —— 冲撞状态机(暗黑突击) ——
if(b.dashSt){ if(b.dashSt){
const ds=b.dashSt; const ds=b.dashSt;
if(ds.ph==="tele"){ds.t--; if(ds.ph==="tele"){b.dashTrail=null;ds.t--;
if(ds.t<=0){const ap3=aimPt(),a=Math.atan2(ap3.y-b.y,ap3.x-b.x); if(ds.t<=0){const ap3=aimPt(),a=Math.atan2(ap3.y-b.y,ap3.x-b.x);
ds.ph="go";ds.t=85;ds.vx=Math.cos(a)*7.2;ds.vy=Math.sin(a)*7.2;SFX.warn();}} ds.ph="go";ds.t=85;ds.vx=Math.cos(a)*(b.phase>=2?10.8:7.2);ds.vy=Math.sin(a)*(b.phase>=2?10.8:7.2);SFX.warn();}} // 三阶段冲撞速度150%
else if(ds.ph==="go"){b.x+=ds.vx;b.y+=ds.vy;ds.t--; else if(ds.ph==="go"){b.x+=ds.vx;b.y+=ds.vy;ds.t--;
if(b.phase>=1){b.dashTrail=b.dashTrail||[];b.dashTrail.push({x:b.x,y:b.y});if(b.dashTrail.length>7)b.dashTrail.shift();} // 二三阶段:冲锋红色残影
if(G.time%9===0){const a5=Math.atan2(aimPt().y-b.y,aimPt().x-b.x);ebul(b.x,b.y,a5,2.7,"orbR",13);} // 冲撞途中瞄准弹 if(G.time%9===0){const a5=Math.atan2(aimPt().y-b.y,aimPt().x-b.x);ebul(b.x,b.y,a5,2.7,"orbR",13);} // 冲撞途中瞄准弹
if(ds.t<=0||b.y>H+26||b.y<-46||b.x<-26||b.x>W+26){ds.ph="back";ds.t=160;}} if(ds.t<=0||b.y>H+26||b.y<-46||b.x<-26||b.x>W+26){ds.ph="back";ds.t=b.phase>=2?90:160;}}
else{b.x+=(b.ax-b.x)*0.045;b.y+=(b.ay-b.y)*0.045;ds.t--; else{b.dashTrail=null;
if(ds.t<=0||(Math.abs(b.x-b.ax)<5&&Math.abs(b.y-b.ay)<5)){b.dashSt=null;b.mainT=Math.round(105/b.fireM);}} // 冲撞间隔+50% const k2=b.phase>=2?0.12:0.045; // 三阶段回位更快=惯性更小
b.x+=(b.ax-b.x)*k2;b.y+=(b.ay-b.y)*k2;ds.t--;
if(ds.t<=0||(Math.abs(b.x-b.ax)<5&&Math.abs(b.y-b.ay)<5)){b.dashSt=null;b.mainT=rndi(240,900);}} // 冲撞间隔4~15秒随机
updateFunnels();return; updateFunnels();return;
} }
// —— 常规移动 —— // —— 常规移动 ——
if(def.static){b.x+=(W/2-b.x)*0.05;b.y+=(52-b.y)*0.02;} if(def.static){b.x+=(W/2-b.x)*0.05;b.y+=(52-b.y)*0.02;}
else if(b.idx===3){b.x=W/2+Math.sin(b.mt*0.034)*62;b.y=b.ty+Math.sin(b.mt*0.021)*18;} // 暗黑突击:小体型高机动(空闲大幅提速) else if(b.idx===3){ // 暗黑突击:小体型高机动;三阶段更快+上下范围更大+惯性更小(速度积分,转折锐利)
if(b.phase>=2){b.x=clamp(b.x+62*0.052*Math.cos(b.mt*0.052),26,W-26);b.y=b.ty+Math.sin(b.mt*0.03)*27;}
else{b.x=W/2+Math.sin(b.mt*0.034)*62;b.y=b.ty+Math.sin(b.mt*0.021)*18;}}
else if(b.idx===6){const usp=b.phase>=1?0.028:0.008,amp=b.phase>=1?70:44; else if(b.idx===6){const usp=b.phase>=1?0.028:0.008,amp=b.phase>=1?70:44;
b.x=clamp(b.x+amp*usp*Math.cos(b.mt*usp),30,W-30); // 速度积分定位:换阶段参数突变也不瞬移 b.x=clamp(b.x+amp*usp*Math.cos(b.mt*usp),30,W-30); // 速度积分定位:换阶段参数突变也不瞬移
b.yA=(b.yA||8)+((b.phase>=1?12:8)-(b.yA||8))*0.05;b.y=b.ty+Math.sin(b.mt*0.011)*b.yA;} // 夺权者XP1巨大笨重→P2紧凑灵活 b.yA=(b.yA||8)+((b.phase>=1?12:8)-(b.yA||8))*0.05;b.y=b.ty+Math.sin(b.mt*0.011)*b.yA;} // 夺权者XP1巨大笨重→P2紧凑灵活
@ -318,7 +325,7 @@ function updateBoss(){
else{ else{
if(b.plT===142){b.laserT=142;b.warpChg=0.001;SFX.warn();} if(b.plT===142){b.laserT=142;b.warpChg=0.001;SFX.warn();}
if(b.plT<=142&&b.plT>90)b.warpChg=Math.min(1,(142-b.plT)/52); if(b.plT<=142&&b.plT>90)b.warpChg=Math.min(1,(142-b.plT)/52);
if(b.plT===90){b.warpChg=0;bossBeam(b.x,b.y+6,b.x,H+8,b.phase>=1?24:20,"#5cff9d",10,80,30,{b,ox:0,oy:6,followX2:true,sweep:1,amp:b.phase>=1?70:50});} if(b.plT===90){b.warpChg=0;bossBeam(b.x,b.y+6,b.x,H+8,b.phase>=1?24:20,"#5cff9d",10,80,99999,{b,ox:0,oy:6,followX2:true,sweep:1,amp:b.phase>=1?70:50});} // 亚空间主炮99999伤害
} }
} }
if(b.idx===9){ // 泰伦触手扫击9秒一次左右随机 + 玩家靠近立即触发对应侧 if(b.idx===9){ // 泰伦触手扫击9秒一次左右随机 + 玩家靠近立即触发对应侧
@ -334,7 +341,7 @@ function updateBoss(){
for(let i=1;i<7;i++){const f=i/6,sr=3.2*(1-f*0.45)+G.stat.hitR+1; for(let i=1;i<7;i++){const f=i/6,sr=3.2*(1-f*0.45)+G.stat.hitR+1;
const ax=pts[i-1].x,ay=pts[i-1].y,dx=pts[i].x-ax,dy=pts[i].y-ay,L2=dx*dx+dy*dy||1; const ax=pts[i-1].x,ay=pts[i-1].y,dx=pts[i].x-ax,dy=pts[i].y-ay,L2=dx*dx+dy*dy||1;
const tt=clamp(((lx-ax)*dx+(ly-ay)*dy)/L2,0,1),cx=ax+dx*tt,cy=ay+dy*tt; const tt=clamp(((lx-ax)*dx+(ly-ay)*dy)/L2,0,1),cx=ax+dx*tt,cy=ay+dy*tt;
if(dist2(lx,ly,cx,cy)<sr*sr){tn.hitDone=true;hitPlayer(28);break;}}} if(dist2(lx,ly,cx,cy)<sr*sr){tn.hitDone=true;hitPlayer(99999);break;}}} // 触手抽打99999伤害
} }
if(tn.act<=0)tn.rec=40; if(tn.act<=0)tn.rec=40;
} }
@ -382,7 +389,7 @@ function bossMain(b,def,fm){
b.mainT=Math.round(10/fm);break;} b.mainT=Math.round(10/fm);break;}
case "assault": // 暗黑突击:启动冲撞(锚点记当前位置,冲撞归来后正弦从此继续) case "assault": // 暗黑突击:启动冲撞(锚点记当前位置,冲撞归来后正弦从此继续)
b.dashSt={ph:"tele",t:52};b.ax=b.x;b.ay=b.y;b.mainT=9999;break; b.dashSt={ph:"tele",t:52};b.ax=b.x;b.ay=b.y;b.mainT=9999;break;
case "piano": { // 激光钢琴21键四种模式轮换——顺扫(逼走位)/反扫/乱序/齐射 case "piano": { // 激光钢琴21键五种模式随机——顺扫/反扫/乱序/齐射/滑奏(每次重抽,不固定轮换)
if(!b.keys)b.keys={n:21,seq:[],i:0,t:2,hot:-1,hotT:0,hotList:null,mode:3}; if(!b.keys)b.keys={n:21,seq:[],i:0,t:2,hot:-1,hotT:0,hotList:null,mode:3};
const ks=b.keys,n=ks.n; const ks=b.keys,n=ks.n;
if(ks.hotT>0){ks.hotT--; if(ks.hotT>0){ks.hotT--;
@ -392,8 +399,8 @@ function bossMain(b,def,fm){
ks.hotList=null;ks.hot=-1;SFX.eshoot();}} ks.hotList=null;ks.hot=-1;SFX.eshoot();}}
ks.t--; ks.t--;
if(ks.t<=0&&ks.hotT<=0){ if(ks.t<=0&&ks.hotT<=0){
if(ks.i>=ks.seq.length){ // 布下一局:轮换模式5种 if(ks.i>=ks.seq.length){ // 布下一局:随机抽模式5种
ks.mode=(ks.mode+1)%5; ks.mode=rndi(0,5);
if(ks.mode===0){ks.seq=[];for(let i=0;i<n-3;i++)ks.seq.push(i);} // 顺扫止于18号最右3键永远安全 if(ks.mode===0){ks.seq=[];for(let i=0;i<n-3;i++)ks.seq.push(i);} // 顺扫止于18号最右3键永远安全
else if(ks.mode===1){ks.seq=[];for(let i=n-1;i>=3;i--)ks.seq.push(i);} // 反扫止于3号最左3键永远安全 else if(ks.mode===1){ks.seq=[];for(let i=n-1;i>=3;i--)ks.seq.push(i);} // 反扫止于3号最左3键永远安全
else if(ks.mode===2){ks.seq=shuffled(n);} else if(ks.mode===2){ks.seq=shuffled(n);}
@ -439,7 +446,7 @@ function bossMain(b,def,fm){
ebul(px2,py2,a6,4.4,"dartS",15);} // 发射点=最下甲板炮口与drawUsurper同源 ebul(px2,py2,a6,4.4,"dartS",15);} // 发射点=最下甲板炮口与drawUsurper同源
b.mainT=Math.round(46/fm); b.mainT=Math.round(46/fm);
}else{ }else{
if(!b.plT||b.plT<=0){bossBeam(b.x,b.y+20,b.x,H+8,22,"#7ce9ff",46,80,30,{b,ox:0,oy:20,followX2:true}); if(!b.plT||b.plT<=0){bossBeam(b.x,b.y+20,b.x,H+8,22,"#7ce9ff",46,80,99999,{b,ox:0,oy:20,followX2:true}); // 巨宽核心激光99999伤害
b.laserT=130;b.plT=Math.round(420/fm);b.mainT=40;SFX.warn();} b.laserT=130;b.plT=Math.round(420/fm);b.mainT=40;SFX.warn();}
else{for(let k=-1;k<=1;k++)ebul(b.x,b.y+30,a0+k*0.14,4.4,"dartS",16); else{for(let k=-1;k<=1;k++)ebul(b.x,b.y+30,a0+k*0.14,4.4,"dartS",16);
b.mainT=Math.round(88/fm);} b.mainT=Math.round(88/fm);}
@ -456,7 +463,7 @@ function bossMain(b,def,fm){
b.mainT=Math.round((ph>=2?24:ph===1?30:36)/fm); b.mainT=Math.round((ph>=2?24:ph===1?30:36)/fm);
if(ph>=1&&(!b.eyeT||b.eyeT<=0)){const ex=ap2.x; if(ph>=1&&(!b.eyeT||b.eyeT<=0)){const ex=ap2.x;
for(const sd of[-1,1])bossBeam(b.x+sd*9,b.y-3,ex+sd*6,H+8,4,"#ff3355",22,26,20,{b,ox:sd*9,oy:-3}); for(const sd of[-1,1])bossBeam(b.x+sd*9,b.y-3,ex+sd*6,H+8,4,"#ff3355",22,26,20,{b,ox:sd*9,oy:-3});
b.eyeT=Math.round(150/fm);} b.eyeT=Math.round(300/fm);} // 二阶段激光间隔翻倍150→300三阶段不变
if(ph>=2&&b.hornT<=0){const sd=(b.hornSide=(b.hornSide||1)*-1); if(ph>=2&&b.hornT<=0){const sd=(b.hornSide=(b.hornSide||1)*-1);
const hx=b.x+sd*41,hy=b.y-33,ha=Math.atan2(ap2.y-hy,ap2.x-hx); const hx=b.x+sd*41,hy=b.y-33,ha=Math.atan2(ap2.y-hy,ap2.x-hx);
for(let k=-1;k<=1;k++)ebul(hx,hy,ha+k*0.16,2.8,"orbR",15); for(let k=-1;k<=1;k++)ebul(hx,hy,ha+k*0.16,2.8,"orbR",15);

View File

@ -36,6 +36,12 @@ function poly(pts,col){ctx.fillStyle=col;ctx.beginPath();ctx.moveTo(pts[0],pts[1
function circ(x,y,r,col){ctx.fillStyle=col;ctx.beginPath();ctx.arc(x,y,r,0,TAU);ctx.fill();} function circ(x,y,r,col){ctx.fillStyle=col;ctx.beginPath();ctx.arc(x,y,r,0,TAU);ctx.fill();}
function drawBoss(){ function drawBoss(){
const b=G.boss;if(!b)return; const b=G.boss;if(!b)return;
// 暗黑突击冲撞红色残影(二三阶段冲刺时记录位置,旧→新淡出;先画在机体下方)
if(b.idx===3&&b.dashTrail&&b.dashTrail.length){
for(let i=0;i<b.dashTrail.length;i++){const tp=b.dashTrail[i];
ctx.globalAlpha=(i+1)/b.dashTrail.length*0.3;
poly([tp.x,tp.y-9,tp.x+5,tp.y+1,tp.x-5,tp.y+1],"#ff3344");}
ctx.globalAlpha=1;}
// 变形动画五个新Boss播专属变身mp=进度);其余通用收缩展开 + 白光衰减 // 变形动画五个新Boss播专属变身mp=进度);其余通用收缩展开 + 白光衰减
let ph2=b.phase,sc=1,ox=0,oy=0,flashA=0,mp=null; let ph2=b.phase,sc=1,ox=0,oy=0,flashA=0,mp=null;
if(b.transT>0){const prog=1-b.transT/70; if(b.transT>0){const prog=1-b.transT/70;
@ -184,7 +190,7 @@ function drawBoss(){
if(ctx.setLineDash)ctx.setLineDash([4,3]); if(ctx.setLineDash)ctx.setLineDash([4,3]);
ctx.beginPath();ctx.moveTo(b.x,b.y);ctx.lineTo(G.p.x,G.p.y);ctx.stroke(); ctx.beginPath();ctx.moveTo(b.x,b.y);ctx.lineTo(G.p.x,G.p.y);ctx.stroke();
if(ctx.setLineDash)ctx.setLineDash([]);ctx.globalAlpha=1;} if(ctx.setLineDash)ctx.setLineDash([]);ctx.globalAlpha=1;}
// 多管血条:一管蓝/二管红/三管橙,当前管铺在下一管之上,见底锁血转场 // 多管血条:一管蓝/二管红/三管橙,当前管铺在下一管之上,见底锁血转场(不画管界刻痕=整管连续无切段)
const w=160,def2=BOSSES[b.idx],thr=def2.phases===2?[0.5]:[0.66,0.33]; const w=160,def2=BOSSES[b.idx],thr=def2.phases===2?[0.5]:[0.66,0.33];
const TUBEC=["#43b8ff","#e0393e","#ff9a3d"]; const TUBEC=["#43b8ff","#e0393e","#ff9a3d"];
const ratio=clamp(b.hp/b.maxhp,0,1),bx=(W-w)/2; const ratio=clamp(b.hp/b.maxhp,0,1),bx=(W-w)/2;
@ -192,8 +198,7 @@ function drawBoss(){
px(bx,10,w,4,b.phase<thr.length?(TUBEC[b.phase+1]||"#3a1020"):"#3a1020"); // 底:下一管或暗底 px(bx,10,w,4,b.phase<thr.length?(TUBEC[b.phase+1]||"#3a1020"):"#3a1020"); // 底:下一管或暗底
const hi=b.phase===0?1:thr[b.phase-1],lo=b.phase<thr.length?thr[b.phase]:0; const hi=b.phase===0?1:thr[b.phase-1],lo=b.phase<thr.length?thr[b.phase]:0;
const pc=clamp((ratio-lo)/(hi-lo),0,1); const pc=clamp((ratio-lo)/(hi-lo),0,1);
px(bx,10,w*pc,4,TUBEC[b.phase]||b.col); // 当前管 px(bx,10,w*pc,4,TUBEC[b.phase]||b.col); // 当前管(整管连续)
for(const tt of thr)px(bx+Math.round(w*tt)-1,9,2,6,"#0d1226"); // 管界刻痕
if(b.transT>0&&G.time%8<4){ctx.strokeStyle="#fff";ctx.lineWidth=1;ctx.strokeRect(bx-2.5,7.5,w+5,9);} if(b.transT>0&&G.time%8<4){ctx.strokeStyle="#fff";ctx.lineWidth=1;ctx.strokeRect(bx-2.5,7.5,w+5,9);}
ctx.font="6px 'Press Start 2P',monospace";ctx.fillStyle="#fff";ctx.textAlign="center"; ctx.font="6px 'Press Start 2P',monospace";ctx.fillStyle="#fff";ctx.textAlign="center";
ctx.fillText(b.transT>0?b.name:(b.name+" "+Math.ceil(pc*100)+"%"),W/2,22); ctx.fillText(b.transT>0?b.name:(b.name+" "+Math.ceil(pc*100)+"%"),W/2,22);

View File

@ -340,15 +340,24 @@ function buildRush(){
SAVE.rush=SAVE.rush||{}; SAVE.rush=SAVE.rush||{};
const ra=$("rush-anim"); // 老虎机开关(持久化) const ra=$("rush-anim"); // 老虎机开关(持久化)
if(ra){ra.checked=!!SAVE.rushAnim;ra.onchange=()=>{SAVE.rushAnim=ra.checked?1:0;persist();};} if(ra){ra.checked=!!SAVE.rushAnim;ra.onchange=()=>{SAVE.rushAnim=ra.checked?1:0;persist();};}
// 按难度分级分组展示BOSSES[i].tier0简单/1中等/2困难/3地狱
const TIERS=[["简单","#5cff9d"],["中等","#ffcf5c"],["困难","#ff9a3d"],["地狱","#ff3355"]];
const el=$("rush-pick");el.innerHTML=""; const el=$("rush-pick");el.innerHTML="";
for(let ti=0;ti<4;ti++){
const grp=document.createElement("div");grp.className="rush-grp";
grp.innerHTML=`<div class="h3" style="color:${TIERS[ti][1]}">— ${TIERS[ti][0]} —</div>`;
const row=document.createElement("div");row.className="rush-row";
BOSSES.forEach((b,i)=>{ BOSSES.forEach((b,i)=>{
if(b.tier!==ti)return;
const best=SAVE.rush[i]; const best=SAVE.rush[i];
const d=document.createElement("div");d.className="pcard"; const d=document.createElement("div");d.className="pcard";
d.innerHTML=`<div class="nm" style="margin-top:2px;color:${b.col}">${b.name}${best?" ✓":""}</div> d.innerHTML=`<div class="nm" style="margin-top:2px;color:${b.col}">${b.name}${best?" ✓":""}</div>
<div class="ds">${b.sub}<br>${best?("最快 "+Math.floor(best/60)+":"+("0"+best%60).slice(-2)):"未击破"}</div>`; <div class="ds">${b.sub}<br>${best?("最快 "+Math.floor(best/60)+":"+("0"+best%60).slice(-2)):"未击破"}</div>`;
d.onclick=()=>startRush(i); d.onclick=()=>startRush(i);
el.appendChild(d); row.appendChild(d);
}); });
if(row.children.length){grp.appendChild(row);el.appendChild(grp);}
}
} }
function rollRushBuild(){ // 随机武装3主武Lv3 + 3副武Lv3 + 2僚机Lv2 function rollRushBuild(){ // 随机武装3主武Lv3 + 3副武Lv3 + 2僚机Lv2
const sh=a=>{const x=a.slice();for(let i=x.length-1;i>0;i--){const j=rndi(0,i),t2=x[i];x[i]=x[j];x[j]=t2;}return x;}; const sh=a=>{const x=a.slice();for(let i=x.length-1;i>0;i--){const j=rndi(0,i),t2=x[i];x[i]=x[j];x[j]=t2;}return x;};