①图鉴隐藏测试模式:连点标题'军'字10次开关,纯内存标志位全部军械可见可演示,不动SAVE.codex(persist/submitRun读它,动了会污染后端),刷新失效 ②24件主武器说明文案全部替换(用户提供梗文案);演示页介绍单行显示、去掉类别前缀、删'实战模拟·自动驾驶'提示行 ③新主武器'巡飞弹'(loiter):慢攻速追踪导弹(制导引擎新增maxSpd上限5保持中速),命中留火场(zones新kind:fire纯粒子火舌,无圆形区域)+分裂4枚小炸弹快爆;新精灵loms;CODEX_KEYS同步63 ④存档云同步:星币/机体/选中机体/永久强化/本地纪录上服务端(player_state表+POST /api/state+vState白名单清洗);login/me/register响应带state;客户端persist()钩子防抖600ms自动上传;服务端整体替换本地防串号,登出全清;老账号首登自动上传本地存档迁移 ⑤机体选择/购买从出击准备并入机库强化(buildShips);出击默认无尽模式 服务端已重启生效;/api/state全链路在独立测试库curl验证通过(合法推送/回读/非法拦截清洗/401)
372 lines
18 KiB
JavaScript
372 lines
18 KiB
JavaScript
"use strict";
|
||
/* ================= 界面流转 ================= */
|
||
const OVLS=["o-auth","o-home","o-title","o-setup","o-hangar","o-cards","o-pause","o-over","o-codex","o-demo","o-rush","o-slot"];
|
||
function show(id){for(const o of OVLS)$(o).classList.toggle("hide",o!==id);}
|
||
const SETUP={mode:"endless",ship:"falcon",diff:0}; // 出击默认无尽模式
|
||
function bestLine(){
|
||
const b=SAVE.best;
|
||
$("best-line").textContent=`无尽纪录 ${Math.max(b.e0,b.e1)} · 闯关进度 ${b.stage}/6`;
|
||
}
|
||
$("b-go").onclick=()=>{AU.init();show("o-setup");};
|
||
$("b-back1").onclick=()=>show("o-title");
|
||
$("b-back2").onclick=()=>show("o-title");
|
||
$("b-hangar").onclick=()=>{AU.init();buildShop();show("o-hangar");};
|
||
$("b-codex").onclick=()=>{AU.init();buildCodex();show("o-codex");};
|
||
$("b-rush").onclick=()=>{AU.init();buildRush();show("o-rush");};
|
||
$("b-back6").onclick=()=>{UI="title";show("o-title");};
|
||
$("b-back4").onclick=()=>{UI="title";bestLine();show("o-title");};
|
||
$("b-back5").onclick=exitDemoSim;
|
||
/* 图鉴测试模式(隐藏入口):连点图鉴标题“军”字10次开关。
|
||
纯内存开关——不动 SAVE.codex(persist/结算上报都读它,动了会污染后端),刷新页面即失效 */
|
||
let codexTestAll=false,cxClicks=0,cxLastClick=0;
|
||
$("cx-secret").onclick=()=>{
|
||
const now=Date.now();
|
||
if(now-cxLastClick>2000)cxClicks=0; // 间隔超2秒重新计数
|
||
cxLastClick=now;
|
||
if(++cxClicks<10)return;
|
||
cxClicks=0;codexTestAll=!codexTestAll;
|
||
SFX.pick();buildCodex();
|
||
};
|
||
$("b-start").onclick=()=>startGame();
|
||
if("ontouchstart" in window){const pb=$("tb-pause");pb.style.display="block";pb.onclick=()=>togglePause();}
|
||
$("b-resume").onclick=()=>togglePause();
|
||
$("b-restart").onclick=()=>{show("");restartSameRun();};
|
||
$("b-quit").onclick=()=>{bank();G=null;MUS.stop();UI="title";bestLine();show("o-title");};
|
||
/* 再来一局:按刚结束那一局自己的 mode/ship/diff 重开(修复 SETUP 漂移导致进错模式,如 rush/闯关死后进了无尽) */
|
||
function restartSameRun(){
|
||
if(!G){startGame();return;}
|
||
if(G.mode==="rush"){startRush(G.rushIdx);return;}
|
||
SETUP.mode=G.mode;SETUP.ship=G.ship;SETUP.diff=G.diff;
|
||
startGame();
|
||
}
|
||
$("b-again").onclick=()=>restartSameRun();
|
||
$("b-home").onclick=()=>{G=null;UI="title";bestLine();show("o-title");};
|
||
$("mode-pick").addEventListener("click",e=>{
|
||
const c=e.target.closest(".pcard");if(!c)return;
|
||
SETUP.mode=c.dataset.m;
|
||
for(const el of $("mode-pick").children)el.classList.toggle("sel",el===c);
|
||
});
|
||
$("diff-pick").addEventListener("click",e=>{
|
||
const b=e.target.closest("button");if(!b)return;
|
||
SETUP.diff=+b.dataset.d;
|
||
for(const el of $("diff-pick").children)el.classList.toggle("primary",el===b);
|
||
});
|
||
/* 机体选择/购买(机库内):购买后整店刷新(星币/按钮态变化),选择只刷新机体卡 */
|
||
function buildShips(){
|
||
const box=$("ship-pick");box.innerHTML="";
|
||
for(const k in SHIPS){
|
||
const s=SHIPS[k],owned=SAVE.ships.includes(k);
|
||
const d=document.createElement("div");
|
||
d.className="pcard"+(owned?"":" lock")+(SETUP.ship===k?" sel":"");
|
||
const c=document.createElement("canvas");c.width=52;c.height=64;
|
||
const g=c.getContext("2d");g.imageSmoothingEnabled=false;
|
||
const spr=SPR[s.spr];g.drawImage(spr,(52-spr.width*3)/2,(64-spr.height*3)/2,spr.width*3,spr.height*3);
|
||
d.appendChild(c);
|
||
d.insertAdjacentHTML("beforeend",`<div class="nm">${s.name}</div><div class="ds">${s.trait}</div>`+(owned?"":`<div class="cost">✦ ${s.cost} 解锁</div>`));
|
||
d.onclick=()=>{
|
||
if(!SAVE.ships.includes(k)){
|
||
if(SAVE.coins>=s.cost){SAVE.coins-=s.cost;SAVE.ships.push(k);persist();SFX.level();buildShop();}
|
||
else SFX.hurt();
|
||
return;
|
||
}
|
||
SETUP.ship=k;persist();buildShips(); // persist 触发云端同步选中机体
|
||
};
|
||
box.appendChild(d);
|
||
}
|
||
}
|
||
/* ================= 机库商店 ================= */
|
||
const META=[
|
||
{key:"weapon",name:"主武器校准",ds:"出击时主武器等级 +1",max:2,costs:[300,800]},
|
||
{key:"armor",name:"强化装甲",ds:"出击时生命上限 +1",max:2,costs:[400,1000]},
|
||
{key:"hpup",name:"结构强化",ds:"出击时结构值 +15",max:2,costs:[250,700]},
|
||
{key:"luck",name:"幸运币",ds:"高品质强化概率提升",max:2,costs:[350,900]},
|
||
{key:"wallet",name:"收藏家协议",ds:"星币获取 +15%",max:3,costs:[300,800,2000]},
|
||
{key:"mag2",name:"引力透镜",ds:"拾取范围 +15%",max:2,costs:[200,600]},
|
||
{key:"odstart",name:"超频预载",ds:"出击时超频已充能 50%",max:1,costs:[500]}
|
||
];
|
||
function buildShop(){
|
||
$("wallet").textContent=SAVE.coins;
|
||
buildShips();
|
||
const box=$("shop");box.innerHTML="";
|
||
for(const m of META){
|
||
const lv=SAVE.m[m.key]||0,maxed=lv>=m.max,cost=maxed?0:m.costs[lv];
|
||
const d=document.createElement("div");d.className="sitem";
|
||
d.innerHTML=`<div class="inf"><div class="nm">${m.name}</div><div class="ds">${m.ds}</div>
|
||
<div class="pips">${"■".repeat(lv)}${"□".repeat(m.max-lv)}</div></div>
|
||
<button ${maxed||SAVE.coins<cost?"disabled":""}>${maxed?"MAX":"✦ "+cost}</button>`;
|
||
if(!maxed)d.querySelector("button").onclick=()=>{
|
||
if(SAVE.coins<cost)return;
|
||
SAVE.coins-=cost;SAVE.m[m.key]=lv+1;persist();SFX.coin();buildShop();
|
||
};
|
||
box.appendChild(d);
|
||
}
|
||
}
|
||
/* ================= 开局 / 暂停 / 结算 ================= */
|
||
function startGame(){
|
||
AU.init();if(AU.ctx&&AU.ctx.state==="suspended")AU.ctx.resume();
|
||
newRun(SETUP.mode,SETUP.ship,SETUP.diff);
|
||
UI="play";show("");acc=0;
|
||
MUS.start(false);
|
||
banner(SETUP.mode==="stage"?"STAGE 1 — "+STAGE_NAMES[0]:"无尽模式 — 生存到底!","#41e6ff",130);
|
||
startWave();
|
||
}
|
||
function togglePause(){
|
||
if(!G)return;
|
||
if(UI==="play"){UI="pause";buildLoadoutUI();show("o-pause");}
|
||
else if(UI==="pause"){UI="play";show("");}
|
||
}
|
||
/* ================= 换装面板 ================= */
|
||
function buildLoadoutUI(){
|
||
const box=$("loadout");if(!box||!G)return;box.innerHTML="";
|
||
const mk=(title,dict,owned,active,max)=>{
|
||
const sec=document.createElement("div");
|
||
sec.innerHTML=`<div class="h3" style="margin:6px 0 2px">${title}(${active.length}/${max})· 点击切换装备</div>`;
|
||
const row=document.createElement("div");
|
||
for(const k in owned){
|
||
const d=document.createElement("span");
|
||
const on=active.includes(k);
|
||
d.className="litem"+(on?" on":"");
|
||
d.textContent=`${dict[k].name} Lv${owned[k]}`;
|
||
d.onclick=()=>{
|
||
const i=active.indexOf(k);
|
||
if(i>=0){if(active===G.loadout&&active.length===1){SFX.hurt();return;}active.splice(i,1);}
|
||
else{if(active.length>=max){SFX.hurt();return;}active.push(k);}
|
||
SFX.pick();buildLoadoutUI();
|
||
};
|
||
row.appendChild(d);
|
||
}
|
||
if(Object.keys(owned).length===0)row.innerHTML='<span class="small">尚未获得</span>';
|
||
sec.appendChild(row);box.appendChild(sec);
|
||
};
|
||
mk("主武器",WEAPONS,G.weapons,G.loadout,5);
|
||
mk("副武器",SUBS,G.subs,G.subLoadout,5);
|
||
mk("僚机",DRONES,G.drones,G.droneLoadout,3);
|
||
}
|
||
/* ================= 图鉴 ================= */
|
||
function iconCanvas(img,size,sil){
|
||
const c=document.createElement("canvas");c.width=c.height=size;
|
||
const g=c.getContext("2d");g.imageSmoothingEnabled=false;
|
||
const sc=Math.min(size/img.width,size/img.height);
|
||
g.drawImage(img,(size-img.width*sc)/2,(size-img.height*sc)/2,img.width*sc,img.height*sc);
|
||
if(sil){g.globalCompositeOperation="source-atop";g.fillStyle="#232f52";g.fillRect(0,0,size,size);g.globalCompositeOperation="source-over";}
|
||
return c;
|
||
}
|
||
function buildCodex(){
|
||
const box=$("codex");box.innerHTML="";
|
||
const total=MAINS.length+SUB_KEYS.length+DRONE_KEYS.length+PAS_KEYS.length;
|
||
$("codex-t").textContent=total;
|
||
$("codex-n").textContent=codexCount(); // 计数永远显示真实收集数,测试模式不影响
|
||
$("codex-test").style.display=codexTestAll?"":"none";
|
||
$("cx-secret").style.color=codexTestAll?"var(--gold)":""; // 开启后“军”字变金
|
||
const mk=(title,keys,dict,kind)=>{
|
||
const h=document.createElement("div");h.className="cx-sec";h.textContent=title;box.appendChild(h);
|
||
const grid=document.createElement("div");grid.className="cx-grid";
|
||
for(const k of keys){
|
||
const def=dict[k],owned=!!SAVE.codex[k]||codexTestAll; // 测试模式:全部可见可演示
|
||
const d=document.createElement("div");
|
||
d.className="cx-item"+(owned?"":" lock"); // 未收集:剪影+???,不可演示
|
||
const img=def.icon?ICONS[def.icon]:SPR[def.spr];
|
||
d.appendChild(iconCanvas(img,28,!owned));
|
||
d.insertAdjacentHTML("beforeend",`<div class="nm">${owned?def.name:"???"}</div>`);
|
||
if(owned)d.onclick=()=>{SFX.pick();startDemoSim(kind,k);};
|
||
grid.appendChild(d);
|
||
}
|
||
box.appendChild(grid);
|
||
};
|
||
mk("主武器",MAINS,WEAPONS,"m");
|
||
mk("副武器",SUB_KEYS,SUBS,"s");
|
||
mk("僚机",DRONE_KEYS,DRONES,"d");
|
||
mk("被动强化",PAS_KEYS,PASSIVES,"p");
|
||
}
|
||
document.addEventListener("visibilitychange",()=>{if(document.hidden&&UI==="play")togglePause();});
|
||
function bank(){
|
||
if(!G||G.banked)return;G.banked=true;
|
||
SAVE.coins+=G.coins;
|
||
if(G.mode!=="rush"){const bk=(G.mode==="endless"?"e":"s")+G.diff;
|
||
SAVE.best[bk]=Math.max(SAVE.best[bk]||0,G.score);}
|
||
persist();
|
||
}
|
||
/* ---- Boss 挑战模式 ---- */
|
||
function buildRush(){
|
||
G=null; // 清掉上一局残留(rush胜利后G仍在),防背景卡残影
|
||
SAVE.rush=SAVE.rush||{};
|
||
const ra=$("rush-anim"); // 老虎机开关(持久化)
|
||
if(ra){ra.checked=!!SAVE.rushAnim;ra.onchange=()=>{SAVE.rushAnim=ra.checked?1:0;persist();};}
|
||
const el=$("rush-pick");el.innerHTML="";
|
||
BOSSES.forEach((b,i)=>{
|
||
const best=SAVE.rush[i];
|
||
const d=document.createElement("div");d.className="pcard";
|
||
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>`;
|
||
d.onclick=()=>startRush(i);
|
||
el.appendChild(d);
|
||
});
|
||
}
|
||
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;};
|
||
return {m:sh(Object.keys(WEAPONS)).slice(0,3),s:sh(Object.keys(SUBS)).slice(0,3),d:sh(Object.keys(DRONES)).slice(0,2)};
|
||
}
|
||
function startRush(i){
|
||
AU.init();if(AU.ctx&&AU.ctx.state==="suspended")AU.ctx.resume();
|
||
const build=rollRushBuild();
|
||
if(SAVE.rushAnim)playSlot(build,()=>startRushGo(i,build)); // 老虎机动画抽取
|
||
else startRushGo(i,build); // 直接随机
|
||
}
|
||
function startRushGo(i,build){
|
||
newRun("rush","falcon",0);
|
||
G.weapons={};build.m.forEach(k=>G.weapons[k]=3);G.loadout=build.m.slice();
|
||
G.subs={};build.s.forEach(k=>G.subs[k]=3);G.subLoadout=build.s.slice();
|
||
G.drones={};build.d.forEach(k=>G.drones[k]=2);G.droneLoadout=build.d.slice();
|
||
G.xpNeed=1e9; // 挑战模式无升级打断
|
||
G.rushIdx=i;G.rushT0=0;
|
||
UI="play";show("");acc=0;
|
||
MUS.start(true);
|
||
banner("BOSS 挑战 — "+BOSSES[i].name,"#ff5566",130);
|
||
G.rushT0=G.time;
|
||
startBoss();
|
||
}
|
||
/* ---- 老虎机抽奖动画 ---- */
|
||
let slotTimers=[];
|
||
function playSlot(build,cb){
|
||
UI="slot";show("o-slot");
|
||
const row=$("slot-row");row.innerHTML="";
|
||
$("slot-hint").textContent=" ";
|
||
const items=[
|
||
...build.m.map(k=>({cat:"主武器",pool:WEAPONS,key:k})),
|
||
...build.s.map(k=>({cat:"副武器",pool:SUBS,key:k})),
|
||
...build.d.map(k=>({cat:"僚机",pool:DRONES,key:k}))];
|
||
let done=0;
|
||
items.forEach((it,idx)=>{
|
||
const d=document.createElement("div");d.className="slotbox";
|
||
d.innerHTML=`<div class="sc">${it.cat}</div><div class="sn">???</div>`;
|
||
row.appendChild(d);
|
||
const sn=d.querySelector(".sn"),names=Object.values(it.pool).map(x=>x.name);
|
||
const iv=setInterval(()=>{sn.textContent=names[rndi(0,names.length-1)];},60);
|
||
slotTimers.push(iv);
|
||
setTimeout(()=>{clearInterval(iv);
|
||
sn.textContent=it.pool[it.key].name;sn.classList.add("hit");
|
||
SFX.graze();done++;
|
||
if(done>=items.length){$("slot-hint").textContent="— 出击 —";setTimeout(cb,700);}
|
||
},700+idx*280);
|
||
});
|
||
}
|
||
/* 结算上报服务端(每局一次,含rush胜利;图鉴演示除外) */
|
||
function submitRun(win){
|
||
if(!G||G.submitted||G.demoSim)return;
|
||
G.submitted=true;
|
||
if(typeof NET==="undefined"||!NET.user)return;
|
||
NET.submitRun({mode:G.mode,diff:G.diff,score:G.score,wave:G.wave,stage:G.stage,
|
||
timeSec:Math.round(G.time/60),bossKills:G.bossKills||0,win:!!win,
|
||
codex:Object.keys(SAVE.codex)}); // 注意:不上报 localStorage 历史最高分(SAVE.best 是浏览器级共享,会串号)
|
||
}
|
||
function endRun(win){
|
||
if(UI==="over")return;
|
||
bank();MUS.stop();
|
||
submitRun(win);
|
||
UI="over";
|
||
$("over-title").textContent=win?"★ 任务完成 ★":"任务失败";
|
||
$("over-title").style.color=win?"#ffcf5c":"#ff5566";
|
||
const mins=Math.floor(G.time/3600),secs=Math.floor(G.time/60)%60;
|
||
$("over-stats").innerHTML=
|
||
`<span>最终得分</span><b>${G.score}</b>
|
||
<span>${G.mode==="endless"?"抵达波次":G.mode==="rush"?"挑战目标":"抵达关卡"}</span><b>${G.mode==="endless"?G.wave:G.mode==="rush"?BOSSES[G.rushIdx].name:G.stage+"-"+G.wave}</b>
|
||
<span>击坠数</span><b>${G.kills}</b>
|
||
<span>强化等级</span><b>Lv.${G.level}</b>
|
||
<span>存活时间</span><b>${mins}分${secs}秒</b>
|
||
<span>持有军械</span><b>${Object.keys(G.weapons).length}主/${Object.keys(G.subs).length}副/${Object.keys(G.drones).length}机</b>`;
|
||
$("over-coins").textContent=G.coins;
|
||
setTimeout(()=>show("o-over"),win?400:900);
|
||
}
|
||
/* ================= 三选一卡片 ================= */
|
||
function offerCards(bossRare){
|
||
if(!G||G.p.dead>0&&!bossRare)return;
|
||
G.bossCard=!!bossRare;
|
||
const opts=rollCards(bossRare);
|
||
G.cardOpts=opts;
|
||
const box=$("cards");box.innerHTML="";
|
||
opts.forEach((c,i)=>{
|
||
let nm,ds,rare,img;
|
||
if(c.kind==="wlvl"){const w=WEAPONS[c.key],lv=G.weapons[c.key];nm=w.name;rare=w.rare;ds=`主武器强化 Lv.${lv} → Lv.${lv+1}`;img=w.icon?ICONS[w.icon]:SPR[w.spr];}
|
||
else if(c.kind==="weapon"){const w=WEAPONS[c.key];nm="主武器·"+w.name;rare=w.rare;ds=w.ds;img=w.icon?ICONS[w.icon]:SPR[w.spr];}
|
||
else if(c.kind==="slvl"){const u=SUBS[c.key],lv=G.subs[c.key];nm=u.name;rare=u.rare;ds=`副武器强化 Lv.${lv} → Lv.${lv+1}`;img=u.icon?ICONS[u.icon]:SPR[u.spr];}
|
||
else if(c.kind==="sub"){const u=SUBS[c.key];nm="副武器·"+u.name;rare=u.rare;ds=typeof u.ds==="function"?u.ds(0):u.ds;img=u.icon?ICONS[u.icon]:SPR[u.spr];}
|
||
else if(c.kind==="dlvl"){const u=DRONES[c.key],lv=G.drones[c.key];nm=u.name;rare=u.rare;ds=`僚机强化 Lv.${lv} → Lv.${lv+1}`;img=SPR[u.spr];}
|
||
else if(c.kind==="drone"){const u=DRONES[c.key];nm="僚机·"+u.name;rare=u.rare;ds=typeof u.ds==="function"?u.ds(0):u.ds;img=SPR[u.spr];}
|
||
else if(c.kind==="upg"){const u=PASSIVES[c.key];nm=u.name;rare=u.rare;ds=u.ds(G.u[c.key]);img=u.icon?ICONS[u.icon]:SPR[u.spr];}
|
||
else{rare=0;
|
||
if(c.key==="heal"){nm="紧急修复";ds="生命 +1";img=SPR.heart;}
|
||
else if(c.key==="coins"){nm="星币补给";ds="星币 +50";img=SPR.coin;}
|
||
else{nm="超频充能";ds="超频立即充满";img=SPR.odc;}}
|
||
const d=document.createElement("div");
|
||
d.className="ucard r"+rare;
|
||
const cv2=document.createElement("canvas");cv2.width=40;cv2.height=40;
|
||
const g=cv2.getContext("2d");g.imageSmoothingEnabled=false;
|
||
const sc=Math.min(40/img.width,40/img.height);
|
||
g.drawImage(img,(40-img.width*sc)/2,(40-img.height*sc)/2,img.width*sc,img.height*sc);
|
||
d.appendChild(cv2);
|
||
d.insertAdjacentHTML("beforeend",
|
||
`<div class="key">${i+1}</div><div class="nm">${nm}</div>
|
||
<div class="rtag">${["普通","稀有","史诗"][rare]}</div><div class="ds">${ds}</div>`);
|
||
d.onclick=()=>pickCard(i);
|
||
box.appendChild(d);
|
||
});
|
||
UI="cards";show("o-cards");SFX.level();
|
||
}
|
||
function pickCard(i){
|
||
if(UI!=="cards"||!G.cardOpts||!G.cardOpts[i])return;
|
||
const c=G.cardOpts[i];G.cardOpts=null;
|
||
if(c.kind==="wlvl")G.weapons[c.key]++;
|
||
else if(c.kind==="weapon"){G.weapons[c.key]=1;if(G.loadout.length<5)G.loadout.push(c.key);banner("获得 "+WEAPONS[c.key].name,"#b98cff",80);}
|
||
else if(c.kind==="slvl")G.subs[c.key]++;
|
||
else if(c.kind==="sub"){G.subs[c.key]=1;if(G.subLoadout.length<5)G.subLoadout.push(c.key);banner("获得 "+SUBS[c.key].name,"#b98cff",80);}
|
||
else if(c.kind==="dlvl")G.drones[c.key]++;
|
||
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"){
|
||
G.u[c.key]++;
|
||
if(c.key==="nano"){G.maxHp+=25;G.hp=Math.min(G.maxHp,G.hp+40);}
|
||
if(c.key==="shield")G.shield=Math.min(shieldCap(),G.shield+1);
|
||
}else{
|
||
if(c.key==="heal")G.lives=Math.min(G.stat.maxLives,G.lives+1);
|
||
if(c.key==="coins")addCoins(50);
|
||
if(c.key==="od")G.od=100;
|
||
}
|
||
if(c.key&&c.kind!=="fill"){SAVE.codex[c.key]=true;persist();} // 图鉴收录
|
||
SFX.pick();
|
||
if(G.bossCard){G.bossCard=false;UI="play";show("");}
|
||
else{G.cardQ=(G.cardQ||1)-1;
|
||
if(G.cardQ>0)offerCards(false);
|
||
else{UI="play";show("");}}
|
||
}
|
||
/* ================= 图鉴:全屏实战模拟 =================
|
||
复用真实战斗引擎:只装备选定军械,杂兵+射手+精英持续来袭 */
|
||
function startDemoSim(kind,key){
|
||
const def=kind==="m"?WEAPONS[key]:kind==="s"?SUBS[key]:kind==="p"?PASSIVES[key]:DRONES[key];
|
||
newRun("stage","falcon",0);
|
||
G.p.inv=0; // 图鉴演示:取消出生保护闪烁
|
||
const lv=3;
|
||
G.demoSim={kind,key};
|
||
if(kind==="m"){G.weapons={};G.weapons[key]=lv;G.loadout=[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{G.weapons={vulcan:2};G.loadout=["vulcan"];G.u[key]=3;
|
||
if(key==="nano"){G.maxHp+=75;G.hp=G.maxHp;}
|
||
if(key==="shield")G.shield=shieldCap();}
|
||
$("demo-name").textContent=def.name;
|
||
const ds=typeof def.ds==="function"?def.ds(lv):def.ds;
|
||
$("demo-ds").textContent=ds; // 只显示介绍本身,单行不折行(nowrap 在 index.html 上)
|
||
UI="demoSim";show("o-demo");acc=0;
|
||
banner(def.name,"#41e6ff",90);
|
||
}
|
||
function exitDemoSim(){G=null;UI="codex";buildCodex();show("o-codex");}
|
||
function demoSpawn(){
|
||
const D=G.demoSim;
|
||
D.t=(D.t||0)-1;
|
||
if(D.t>0||G.enemies.length>=10)return;
|
||
D.t=Math.max(30,52-Math.floor(G.time/900)); // 渐进增密
|
||
const r=Math.random();
|
||
if(r<0.5)spawnEnemy(pick(["scout","scout","weaver","kami"]),rnd(20,W-20),-12,false); // 杂兵为主
|
||
else if(r<0.84)spawnEnemy(pick(["gunship","splitter","sniper","mine"]),rnd(20,W-20),-12,false); // 射手少数
|
||
else spawnEnemy(pick(["gunship","weaver","splitter"]),rnd(20,W-20),-12,true); // 精英
|
||
}
|
||
bestLine();
|