🎮ArcadeLab

Rogue 冒险 · 层级远征

by QuantumPenguin78
739 lines57.2 KB
▶ Play
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no,viewport-fit=cover">
<title>Rogue 冒险 · 层级远征</title>
<style>
*{box-sizing:border-box;margin:0;padding:0}html{-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}
body{font-family:-apple-system,'SF Pro Text','Segoe UI','Microsoft YaHei',sans-serif;background:linear-gradient(135deg,#0d0d1a,#1a1a2e,#16213e);min-height:100vh;min-height:100dvh;color:#e0e0e0;display:flex;flex-direction:column;align-items:center;padding:max(8px,env(safe-area-inset-top)) max(8px,env(safe-area-inset-right)) max(8px,env(safe-area-inset-bottom)) max(8px,env(safe-area-inset-left));user-select:none;-webkit-user-select:none;touch-action:manipulation}
button,.card,.ecard{touch-action:manipulation;-webkit-tap-highlight-color:transparent}
.top-bar{display:flex;gap:10px;flex-wrap:wrap;justify-content:center;width:100%;max-width:700px;margin-bottom:6px;font-size:0.9rem}
.top-bar span{background:rgba(255,255,255,0.08);padding:3px 10px;border-radius:6px}
.top-bar .floor{color:#ffd700;font-weight:700}.top-bar .lvl{color:#a8e6cf}.top-bar .gold{color:#f9ca24}
h1.title{font-size:clamp(1.1rem,4vw,1.5rem);color:#e94560;text-shadow:0 0 15px rgba(233,69,96,0.5);margin-bottom:4px}
.main{display:flex;flex-direction:column;gap:8px;width:100%;max-width:700px}
.panel{background:rgba(255,255,255,0.06);border:1px solid rgba(255,255,255,0.12);border-radius:12px;padding:12px;display:flex;flex-direction:column;gap:5px}
.panel-hdr{display:flex;align-items:center;gap:8px;margin-bottom:2px}
.av{width:36px;height:36px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:1.2rem;flex-shrink:0}
.av.p{background:linear-gradient(135deg,#3498db,#2980b9)}.av.e{background:linear-gradient(135deg,#e74c3c,#c0392b)}
.pname{font-size:0.95rem;font-weight:700;color:#fff}.psub{font-size:0.75rem;color:#8892b0}
.bars{display:flex;flex-direction:column;gap:4px}
.bar-r{display:flex;align-items:center;gap:6px}
.bar-l{font-size:0.75rem;width:22px;font-weight:600;flex-shrink:0}.bar-l.hp{color:#e74c3c}.bar-l.mp{color:#3498db}
.bar-t{flex:1;height:14px;background:rgba(255,255,255,0.1);border-radius:7px;overflow:hidden;position:relative}
.bar-f{height:100%;border-radius:7px;transition:width 0.3s}
.bar-f.hp{background:linear-gradient(90deg,#e74c3c,#ff6b6b)}.bar-f.mp{background:linear-gradient(90deg,#2984b9,#3498db)}
.bar-tx{position:absolute;right:5px;top:50%;transform:translateY(-50%);font-size:0.65rem;color:#fff;text-shadow:0 1px 2px rgba(0,0,0,0.8);font-weight:600}
.stats{display:flex;gap:6px;flex-wrap:wrap;font-size:0.82rem}
.stat{background:rgba(255,255,255,0.07);border-radius:5px;padding:2px 8px;font-weight:600}
.stat .l{color:#8892b0;font-weight:400}
.equip-row,.cal-row{display:flex;gap:6px;align-items:center;flex-wrap:wrap;font-size:0.78rem}
.wpn-badge{background:rgba(255,215,0,0.1);border:1px solid rgba(255,215,0,0.25);border-radius:6px;padding:2px 8px;font-weight:600}
.acc-badge{font-size:0.95rem;background:rgba(255,215,0,0.1);border-radius:5px;padding:1px 4px;border:1px solid rgba(255,215,0,0.2)}
.enemy-area{display:flex;gap:8px;overflow-x:auto;-webkit-overflow-scrolling:touch;scroll-snap-type:x mandatory;padding-bottom:4px;width:100%}
.enemy-area::-webkit-scrollbar{height:3px}.enemy-area::-webkit-scrollbar-thumb{background:rgba(255,255,255,0.15);border-radius:2px}
.ecard{flex:0 0 auto;min-width:170px;background:rgba(255,255,255,0.04);border:2px solid rgba(255,255,255,0.1);border-radius:10px;padding:10px;cursor:pointer;transition:border-color 0.15s,transform 0.08s;display:flex;flex-direction:column;gap:4px;min-height:48px;scroll-snap-align:start}
.ecard:active{transform:scale(0.97)}.ecard.tgt{border-color:#e94560;box-shadow:0 0 10px rgba(233,69,96,0.3)}.ecard.dead{opacity:0.25;pointer-events:none;filter:grayscale(0.8)}
.ecard .eh{display:flex;align-items:center;gap:5px}.ecard .ei{font-size:1.1rem}.ecard .en{font-size:0.85rem;font-weight:700;color:#fff}.ecard .et{font-size:0.65rem;color:#e94560;margin-left:auto;font-weight:600}
.ecard .efx{font-size:0.68rem;color:#ffa502;display:flex;gap:4px;flex-wrap:wrap}
.log{width:100%;background:rgba(0,0,0,0.3);border:1px solid rgba(255,255,255,0.06);border-radius:8px;padding:8px 10px;height:120px;overflow-y:auto;-webkit-overflow-scrolling:touch;scroll-behavior:smooth}
.log::-webkit-scrollbar{width:3px}.log::-webkit-scrollbar-thumb{background:rgba(255,255,255,0.15);border-radius:2px}
.le{font-size:0.82rem;line-height:1.5;padding:1px 0;border-bottom:1px solid rgba(255,255,255,0.03);color:#c0c8e0;animation:li 0.12s ease}
.le.pl{color:#7ec8f8}.le.en{color:#f8a0a0}.le.sys{color:#a8e6cf}.le.art{color:#ffd700}
@keyframes li{from{opacity:0;transform:translateX(-3px)}to{opacity:1;transform:none}}
.acts{display:flex;gap:6px;flex-wrap:wrap;justify-content:center;width:100%;padding-bottom:env(safe-area-inset-bottom)}
.abtn{flex:1 1 72px;min-width:72px;min-height:50px;border:none;border-radius:10px;font-size:0.92rem;font-weight:700;cursor:pointer;color:#fff;transition:transform 0.08s,opacity 0.12s;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:1px;padding:5px 6px}
.abtn:active:not(:disabled){transform:scale(0.93);opacity:0.85}.abtn:disabled{opacity:0.3;cursor:not-allowed;filter:grayscale(0.6)}
.abtn .s{font-size:0.68rem;font-weight:400;opacity:0.8}
.b-a{background:linear-gradient(135deg,#636e72,#2d3436)}.b-h{background:linear-gradient(135deg,#e17055,#d63031)}
.b-d{background:linear-gradient(135deg,#0984e3,#074b8a)}.b-m{background:linear-gradient(135deg,#6c5ce7,#4834d4)}
.ov{position:fixed;inset:0;background:rgba(0,0,0,0.75);backdrop-filter:blur(4px);display:none;align-items:center;justify-content:center;z-index:100;padding:max(12px,env(safe-area-inset-top)) max(12px,env(safe-area-inset-right)) max(12px,env(safe-area-inset-bottom)) max(12px,env(safe-area-inset-left));overflow-y:auto;-webkit-overflow-scrolling:touch}
.ov.show{display:flex}.ov.z2{z-index:110}.ov.z3{z-index:120}
.mdl{background:rgba(25,25,50,0.97);border:1px solid rgba(255,255,255,0.15);border-radius:14px;padding:20px 16px;max-width:520px;width:100%;text-align:center;animation:mi 0.2s ease;max-height:92vh;overflow-y:auto;position:relative}
@keyframes mi{from{opacity:0;transform:scale(0.92)}to{opacity:1;transform:scale(1)}}
.mdl h2{margin-bottom:4px;font-size:1.15rem}.mdl p{color:#8892b0;font-size:0.85rem;margin-bottom:12px}
.bgrid{display:grid;grid-template-columns:1fr 1fr 1fr;gap:8px}
@media(max-width:500px){.bgrid{grid-template-columns:1fr}.ecard{min-width:180px}.class-grid{grid-template-columns:1fr 1fr!important}}
.bcard{background:rgba(255,255,255,0.06);border:2px solid rgba(255,255,255,0.12);border-radius:10px;padding:14px 8px;cursor:pointer;transition:border-color 0.15s,transform 0.08s;display:flex;flex-direction:column;align-items:center;gap:5px;min-height:80px}
.bcard:active{transform:scale(0.96);border-color:#ffd700}
.bcard .bi{font-size:1.8rem}.bcard .bn{font-weight:700;font-size:0.9rem}.bcard .bd{font-size:0.75rem;color:#8892b0;line-height:1.3}
.shop-item{display:flex;align-items:center;gap:10px;background:rgba(255,255,255,0.05);border:1px solid rgba(255,255,255,0.1);border-radius:8px;padding:10px;margin-bottom:6px;cursor:pointer;transition:background 0.1s}
.shop-item:active{background:rgba(255,255,255,0.15)}.shop-item.off{opacity:0.35;pointer-events:none}
.shop-item .si-name{font-weight:700;font-size:0.88rem;flex:1}.shop-item .si-price{color:#f9ca24;font-weight:700;font-size:0.85rem}
.q1{color:#bbb}.q2{color:#2ecc71}.q3{color:#3498db}.q4{color:#9b59b6}.q5{color:#f39c12}.q6{color:#e74c3c}
.rbtn{background:linear-gradient(135deg,#e94560,#c0392b);border:none;color:#fff;font-size:1rem;font-weight:700;padding:12px 28px;border-radius:10px;cursor:pointer;min-height:50px;margin-top:10px}
.rbtn:active{transform:scale(0.95)}
.compare{display:flex;gap:10px;justify-content:center;margin:10px 0}
.cmp-box{flex:1;background:rgba(255,255,255,0.05);border:1px solid rgba(255,255,255,0.1);border-radius:8px;padding:10px;text-align:center;font-size:0.8rem}
.cmp-box .cb-n{font-weight:700;font-size:0.9rem;margin-bottom:4px}.cmp-box .cb-s{color:#8892b0;font-size:0.75rem}
.cbtn-row{display:flex;gap:8px;justify-content:center;margin-top:8px}
.cbtn{padding:10px 20px;border:none;border-radius:8px;font-weight:700;font-size:0.9rem;cursor:pointer;color:#fff;min-height:44px}
.cbtn:active{transform:scale(0.95)}.cbtn.yes{background:#27ae60}.cbtn.no{background:#636e72}
.debuff-row,.cal-row{display:flex;gap:4px;flex-wrap:wrap;font-size:0.7rem}
.debuff{background:rgba(255,100,50,0.2);border:1px solid rgba(255,100,50,0.3);border-radius:4px;padding:1px 5px;color:#ffa502}
.pause-btn{position:absolute;top:10px;right:10px;background:rgba(255,255,255,0.1);border:1px solid rgba(255,255,255,0.2);border-radius:8px;padding:6px 12px;color:#aaa;font-size:0.8rem;cursor:pointer;z-index:5;min-height:36px}
.pause-btn:active{transform:scale(0.93);background:rgba(255,255,255,0.2)}
.resume-float{position:fixed;bottom:max(20px,env(safe-area-inset-bottom));left:50%;transform:translateX(-50%);background:linear-gradient(135deg,#e94560,#c0392b);border:none;color:#fff;font-size:0.95rem;font-weight:700;padding:12px 24px;border-radius:24px;cursor:pointer;z-index:200;box-shadow:0 4px 20px rgba(233,69,96,0.5);display:none;min-height:48px;animation:floatPulse 2s ease-in-out infinite}
@keyframes floatPulse{0%,100%{box-shadow:0 4px 20px rgba(233,69,96,0.5)}50%{box-shadow:0 4px 30px rgba(233,69,96,0.8)}}
.resume-float.show{display:block}
.acc-badge,.cal-icon{position:relative;cursor:help}
.acc-tip,.cal-tip{display:none;position:absolute;bottom:calc(100% + 6px);left:50%;transform:translateX(-50%);background:rgba(20,20,40,0.97);border:1px solid rgba(255,255,255,0.2);border-radius:8px;padding:8px 12px;white-space:nowrap;font-size:0.78rem;color:#e0e0e0;z-index:50;pointer-events:none;box-shadow:0 4px 12px rgba(0,0,0,0.5);min-width:120px}
.acc-tip::after,.cal-tip::after{content:'';position:absolute;top:100%;left:50%;transform:translateX(-50%);border:5px solid transparent;border-top-color:rgba(255,255,255,0.2)}
.acc-badge:hover .acc-tip,.acc-badge:active .acc-tip,.cal-icon:hover .cal-tip,.cal-icon:active .cal-tip{display:block}
.wpn-badge{position:relative;cursor:help}.wpn-tip{display:none;position:absolute;bottom:calc(100% + 6px);left:50%;transform:translateX(-50%);background:rgba(20,20,40,0.97);border:1px solid rgba(255,255,255,0.2);border-radius:8px;padding:8px 12px;white-space:nowrap;font-size:0.78rem;color:#e0e0e0;z-index:50;pointer-events:none;box-shadow:0 4px 12px rgba(0,0,0,0.5)}
.wpn-tip::after{content:'';position:absolute;top:100%;left:50%;transform:translateX(-50%);border:5px solid transparent;border-top-color:rgba(255,255,255,0.2)}
.wpn-badge:hover .wpn-tip,.wpn-badge:active .wpn-tip{display:block}
.class-grid{display:grid;grid-template-columns:1fr 1fr 1fr;gap:10px;margin-top:12px}
.class-card{background:rgba(255,255,255,0.06);border:2px solid rgba(255,255,255,0.12);border-radius:12px;padding:14px 10px;cursor:pointer;transition:border-color 0.15s,transform 0.08s;display:flex;flex-direction:column;align-items:center;gap:6px;text-align:center}
.class-card:active{transform:scale(0.96);border-color:#ffd700}
.class-card .cc-icon{font-size:2rem}.class-card .cc-name{font-weight:900;font-size:1rem;color:#fff}
.class-card .cc-stats{font-size:0.75rem;color:#8892b0;line-height:1.5}.class-card .cc-spec{font-size:0.7rem;color:#e94560;line-height:1.3;margin-top:2px}
.cal-icon{background:rgba(255,50,50,0.15);border:1px solid rgba(255,50,50,0.3);border-radius:5px;padding:1px 5px;font-size:0.85rem}
.drop-acc{background:rgba(255,255,255,0.05);border:1px solid rgba(255,255,255,0.15);border-radius:10px;padding:12px;margin:6px 0;text-align:center}
</style>
</head>
<body>
<h1 class="title">⚔️ 层级远征</h1>
<div class="top-bar" id="topBar">
  <span class="floor" id="dFloor">第 1 / 30 层</span>
  <span class="lvl" id="dLvl">Lv.1</span>
  <span class="gold" id="dGold">💰 0</span>
</div>
<div class="main">
  <div class="panel" id="playerPanel">
    <div class="panel-hdr"><div class="av p" id="pAv">🗡️</div><div><div class="pname" id="pName">勇者</div><div class="psub" id="pSub">Lv.1 冒险者</div></div></div>
    <div class="bars">
      <div class="bar-r"><span class="bar-l hp">HP</span><div class="bar-t"><div class="bar-f hp" id="pHp" style="width:100%"></div><span class="bar-tx" id="pHpTx">30/30</span></div></div>
      <div class="bar-r"><span class="bar-l mp">MP</span><div class="bar-t"><div class="bar-f mp" id="pMp" style="width:100%"></div><span class="bar-tx" id="pMpTx">10/10</span></div></div>
    </div>
    <div class="stats"><div class="stat"><span class="l">ATK </span><span id="pAtk">5</span></div><div class="stat"><span class="l">DEF </span><span id="pDef">2</span></div></div>
    <div class="equip-row" id="equipRow"></div>
    <div class="cal-row" id="calRow"></div>
    <div class="debuff-row" id="debuffRow"></div>
  </div>
  <div class="enemy-area" id="enemyArea" style="display:none"></div>
  <div class="log" id="logBox"></div>
  <div class="acts" id="actBar" style="display:none">
    <button class="abtn b-a" id="bAtk" onclick="doAction('atk')">普通攻击<span class="s">0 MP</span></button>
    <button class="abtn b-h" id="bHvy" onclick="doAction('hvy')">强力攻击<span class="s" id="bHvyC">5 MP</span></button>
    <button class="abtn b-d" id="bDef" onclick="doAction('def')">防御<span class="s">减伤50%</span></button>
    <button class="abtn b-m" id="bMp" onclick="doAction('mp')">回蓝<span class="s">+5 MP</span></button>
  </div>
</div>
<div class="ov z3" id="classOv"><div class="mdl"><h2>⚔️ 选择职业</h2><p>你的职业将决定冒险的走向</p><div class="class-grid" id="classGrid"></div></div></div>
<div class="ov" id="branchOv"><div class="mdl"><button class="pause-btn" onclick="pauseOv('branchOv')">⏸ 暂离</button><h2>🗺️ 选择前进路线</h2><p id="branchHint">选择一条路线继续冒险</p><div class="bgrid" id="branchGrid"></div></div></div>
<div class="ov" id="eventOv"><div class="mdl" id="eventMdl"><button class="pause-btn" id="eventPauseBtn" onclick="pauseOv('eventOv')">⏸ 暂离</button></div></div>
<div class="ov z2" id="wpnOv"><div class="mdl" id="wpnMdl"></div></div>
<div class="ov z2" id="accDropOv"><div class="mdl" id="accDropMdl"></div></div>
<div class="ov" id="endOv"><div class="mdl"><div id="endTitle" style="font-size:1.5rem;font-weight:900;margin-bottom:6px"></div><p id="endMsg"></p><div id="endSummary" style="text-align:left;font-size:0.82rem;line-height:1.8;margin:10px 0"></div><button class="rbtn" onclick="restart()">🔄 重新开始</button></div></div>
<button class="resume-float" id="resumeFloat" onclick="resumeOv()">▶ 继续</button>
<script>
// ============ CONFIG ============
const TOTAL=30,ELITE=[5,15,25],BOSS=[10,20,30];
const QCOLORS=['','q1','q2','q3','q4','q5','q6'];
const QNAMES=['','普通','优秀','稀有','史诗','传说','神话'];
const BRANCH_W={enemy:40,heal:25,treasure:15,shop:15,mystery:5};
const WPN_NAMES=[[],['小木棍','树枝杖','石匕首','粗铁剑','朽木杖'],['铁剑','阔刀','长戟','双刃斧','战锤'],['精钢刀','寒铁剑','秘银刃','符文斧','月光匕'],['暗影刃','噬魂剑','风暴战斧','雷霆之锤','炎魔刀'],['龙牙剑','圣光斩刀','血月弯刀','天罚之枪','星辰之刃'],['灭世之刃','混沌神剑','虚空裂斧','命运之矛','永恒之刃']];
const WPN_EFF=['none','fire','poison','lifesteal','blood_sac','iron_wall','charge'];
const WPN_EFF_NAME={none:'无',fire:'火焰附加',poison:'毒素附加',lifesteal:'吸血',blood_sac:'血祭',iron_wall:'铁壁',charge:'蓄力'};
const ACC_POOL=[
{id:'blood_blade',name:'血刃',desc:'胜利恢复{v}%HP',base:20},
{id:'berserker',name:'狂战怒意',desc:'强攻倍率+{v}',base:0.3},
{id:'crit_ring',name:'暴击指环',desc:'{v}%暴击率',base:12},
{id:'thorn',name:'荆棘之甲',desc:'反弹{v}伤害',base:2},
{id:'sharp',name:'锐利之刃',desc:'无视防御{v}',base:2},
{id:'regen',name:'复苏之风',desc:'每回合恢复{v}HP',base:2},
{id:'energy',name:'能量过载',desc:'强攻耗蓝-{v}',base:1},
{id:'greed',name:'贪婪指环',desc:'金币+{v}%',base:30},
{id:'fang',name:'嗜血之牙',desc:'普攻恢复{v}HP',base:3},
{id:'thunder',name:'雷霆之击',desc:'{v}%闪电链5-10伤害',base:15},
{id:'weakness',name:'弱点洞悉',desc:'无视{v}%防御',base:20},
{id:'rage_blood',name:'狂战之血',desc:'每损失1%HP伤害+{v}%',base:0.3},
{id:'executioner',name:'处刑者',desc:'敌人HP<30%伤害+{v}%',base:40},
{id:'ricochet',name:'弹射之刃',desc:'击杀溢出伤害传递',base:1},
{id:'dragon_scale',name:'龙鳞甲',desc:'减伤最大生命值的{v}%',base:5},
{id:'life_pact',name:'生命契约',desc:'HP<30%恢复10HP/场',base:10},
{id:'undying',name:'不屈意志',desc:'致死时存活恢复30%HP/场',base:30},
{id:'meditation',name:'冥想之石',desc:'每回合额外恢复{v}MP',base:3},
{id:'stone_skin',name:'石肤术',desc:'防御减伤提升至{v}%',base:65},
{id:'life_fountain',name:'生命之泉',desc:'层结束恢复{v}%HP',base:15},
{id:'merchant',name:'商人之印',desc:'商店{v}折',base:80},
{id:'treasure_hunter',name:'宝箱猎手',desc:'宝箱怪概率{v}%',base:10},
{id:'lucky_coin',name:'幸运币',desc:'击杀额外+{v}金币',base:10},
{id:'collector',name:'收藏家',desc:'每饰品ATK+{v}(上限10)',base:1},
{id:'greedy_pot',name:'贪婪之壶',desc:'{v}%双倍金币',base:30},
{id:'warrior_mark',name:'勇者之印',desc:'敌人分支胜利+{v}ATK',base:1},
{id:'exp_badge',name:'经验徽章',desc:'击杀额外+{v}级',base:1},
{id:'heritage',name:'传承护符',desc:'升级额外+{v}ATK+2HP',base:1},
{id:'echo_bell',name:'回响之铃',desc:'回蓝额外恢复{v}HP',base:2},
{id:'energy_conv',name:'能量转换',desc:'每回合3HP→3MP',base:3}
];
const ENEMY_EFF=['fire','poison','lifesteal','berserk'];
const ENEMY_EFF_NAME={fire:'🔥火焰',poison:'☠️毒素',lifesteal:'🩸吸血',berserk:'💢狂暴'};
const MYSTERY_POOL=['cursed_acc','blessing','trap','gambling'];
const PAUSE_BTN='<button class="pause-btn" onclick="pauseOv(\'eventOv\')">\u23F8 \u6682\u79BB</button>';
// ============ CLASS DATA ============
const CLASSES=[
{id:'assassin',name:'刺客',icon:'🗡️',hp:25,atk:10,def:1,mp:10,growA:3,growD:1,growH:8,spec:'初始暴击率20%,暴击伤害2.2倍',specDesc:'高攻低防的致命一击者'},
{id:'berserker',name:'狂战士',icon:'🩸',hp:45,atk:4,def:1,mp:10,growA:2,growD:2,growH:8,spec:'ATK/DEF = 基本值+(最大HP-当前HP)×20%',specDesc:'血量越低越强'},
{id:'shield',name:'神盾使',icon:'🛡️',hp:35,atk:2,def:8,mp:10,growA:1,growD:4,growH:8,spec:'受击反弹DEF×0.5伤害',specDesc:'以守为攻的铁壁'},
{id:'werewolf',name:'狼人',icon:'🐺',hp:80,atk:15,def:0,mp:10,growA:2,growD:0,growH:8,spec:'DEF=0,DEF加成→HP上限;攻击吸血15%',specDesc:'永不防御的野兽'},
{id:'knight',name:'骑士',icon:'⚔️',hp:40,atk:7,def:5,mp:10,growA:3,growD:3,growH:10,spec:'全面成长的精英战士',specDesc:'攻守兼备的全能者'},
{id:'noble',name:'没落贵族',icon:'👑',hp:30,atk:5,def:3,mp:10,growA:2,growD:2,growH:8,spec:'初始150金;商店+1件;击杀金+50%;紫品+概率',specDesc:'以财富弥补实力'}
];
// ============ CALAMITY DATA ============
const CALAMITIES=[
{id:'price_up',name:'物价飞涨',icon:'💰',desc:'商店所有商品价格+50%'},
{id:'enemy_rage',name:'敌人狂怒',icon:'⚡',desc:'所有敌人攻击力+25%'},
{id:'life_decay',name:'生命凋零',icon:'☠️',desc:'每回合开始损失2点HP'},
{id:'thorn_shield',name:'荆棘之盾',icon:'🛡️',desc:'敌人20%概率减伤50%'},
{id:'fog',name:'迷雾笼罩',icon:'🌫️',desc:'分支选项不显示类型(盲选)'},
{id:'hunger',name:'饥饿诅咒',icon:'🍖',desc:'医疗恢复量减半'},
{id:'greed_punish',name:'贪婪惩罚',icon:'👹',desc:'击杀金币-30%'},
{id:'fragile',name:'脆弱躯体',icon:'💔',desc:'最大生命值-20%'},
{id:'blind',name:'失明诅咒',icon:'🕶️',desc:'战斗中隐藏敌人属性'},
{id:'breath',name:'喘息之敌',icon:'💨',desc:'敌人每回合恢复3HP'},
{id:'bad_luck',name:'厄运缠绕',icon:'🎲',desc:'暴击率减半'},
{id:'weak_touch',name:'虚弱之触',icon:'🤲',desc:'攻击力-15%'},
{id:'mp_block',name:'回蓝阻碍',icon:'🔮',desc:'每回合MP恢复-2'},
{id:'chest_trap',name:'宝箱陷阱',icon:'🎁',desc:'宝箱怪概率40%,属性×1.5'}
];
// ============ STATE ============
let P,enemies,tgtIdx,busy,logs,gameOver,endlessMode=false,calamities=[],shopItems=[];
let stats={kills:0,elites:0,bosses:0,goldTotal:0,weapons:[],accessories:[],maxFloor:0};
let bossDropQueue=[];

function showClassSelect(){
  let h='';CLASSES.forEach((c,i)=>{
    h+=`<div class="class-card" onclick="selectClass(${i})">
      <div class="cc-icon">${c.icon}</div><div class="cc-name">${c.name}</div>
      <div class="cc-stats">ATK ${c.atk} / DEF ${c.def} / HP ${c.hp}<br>${c.specDesc}</div>
      <div class="cc-spec">${c.spec}</div></div>`;
  });$('classGrid').innerHTML=h;$('classOv').classList.add('show');
}
function selectClass(i){
  let c=CLASSES[i];$('classOv').classList.remove('show');
  P={hp:c.hp,maxHp:c.hp,atk:c.atk,def:c.def,mp:c.mp||10,maxMp:10,level:1,gold:c.id==='noble'?150:0,floor:1,
    cls:c.id,growA:c.growA,growD:c.growD,growH:c.growH,
    weapon:{name:'小木棍',quality:1,atkBonus:0,mpCost:5,mult:2.0,effect:'none'},
    accessories:[],burnTurns:0,poisonTurns:0,chargeBonus:0,
    cursedTurns:0,cursedAcc:null,warriorMarks:0,lifePactUsed:false,undyingUsed:false};
  // Fragile calamity applied after calamity roll
  $('pName').textContent=c.name;$('pAv').textContent=c.icon;
  addCalamity();renderAll();showBranch();
}
function newGame(){enemies=[];tgtIdx=0;busy=false;logs=[];gameOver=false;endlessMode=false;calamities=[];bossDropQueue=[];
  stats={kills:0,elites:0,bosses:0,goldTotal:0,weapons:[],accessories:[],maxFloor:0};
  stats.weapons.push('小木棍(白)');showClassSelect();
}

// ============ PAUSE / RESUME ============
let pausedOvId=null;
function pauseOv(id){$(id).classList.remove('show');pausedOvId=id;$('resumeFloat').classList.add('show');renderAll()}
function resumeOv(){if(pausedOvId){$(pausedOvId).classList.add('show');pausedOvId=null;$('resumeFloat').classList.remove('show')}}

// ============ UTIL ============
const rng=(a,b)=>Math.floor(Math.random()*(b-a+1))+a;
const $=id=>document.getElementById(id);
function addLog(t,m){logs.push({t,m});if(logs.length>80)logs.shift();renderLog();}
function hasAcc(id){return P.accessories.some(a=>a.id===id)}
function accVal(id){let v=0;P.accessories.forEach(a=>{if(a.id===id)v+=a.value});return v}
function hasCal(id){return calamities.some(c=>c.id===id)}
function getEffAtk(){
  let a=P.atk+(P.weapon?P.weapon.atkBonus:0);let cv=accVal('collector');if(cv>0)a+=Math.min(10,P.accessories.length*Math.floor(cv));
  a+=P.warriorMarks*Math.floor(accVal('warrior_mark')||0);
  if(P.cls==='berserker'){let missing=Math.max(0,P.maxHp-P.hp);a+=Math.floor(missing*0.2)}
  if(hasCal('weak_touch'))a=Math.floor(a*0.85);
  return a;
}
function getEffDef(){
  let d=P.def;if(P.cls==='berserker'){let missing=Math.max(0,P.maxHp-P.hp);d+=Math.floor(missing*0.2)}
  if(P.cls==='werewolf')d=0;return d;
}
function getEffMult(){let m=P.weapon?P.weapon.mult:2.0;P.accessories.forEach(a=>{if(a.id==='berserker')m+=a.value});return m}
function getEffMpCost(){let c=P.weapon?P.weapon.mpCost:5;P.accessories.forEach(a=>{if(a.id==='energy')c=Math.max(0,c-Math.floor(a.value))});return c}
function getSharp(){let s=0;P.accessories.forEach(a=>{if(a.id==='sharp')s+=Math.floor(a.value)});return s}
function getThorn(){let v=0;P.accessories.forEach(a=>{if(a.id==='thorn')v+=Math.floor(a.value)});return v}
function getCrit(){let r=0,d=1.8;if(P.cls==='assassin'){r=0.2;d=2.2}P.accessories.forEach(a=>{if(a.id==='crit_ring')r+=a.value/100});if(hasCal('bad_luck'))r*=0.5;return{rate:r,dmg:d}}
function getRegen(){let v=0;P.accessories.forEach(a=>{if(a.id==='regen')v+=Math.floor(a.value)});return v}
function getBloodBlade(){let v=0;P.accessories.forEach(a=>{if(a.id==='blood_blade')v+=a.value});return v}
function getGreedMult(){let g=0;P.accessories.forEach(a=>{if(a.id==='greed')g+=a.value});let m=1+g/100;if(P.cls==='noble')m+=0.5;if(hasCal('greed_punish'))m*=0.7;return m}
function getDefRate(){return hasAcc('stone_skin')?accVal('stone_skin')/100:0.5}
function getShopDiscount(){return hasAcc('merchant')?accVal('merchant')/100:1}
function getMimicChance(){if(hasCal('chest_trap'))return 0.4;return hasAcc('treasure_hunter')?accVal('treasure_hunter')/100:0.2}
function getRageBonus(){if(!hasAcc('rage_blood'))return 0;let pct=Math.max(0,(P.maxHp-P.hp)/P.maxHp*100);return Math.min(30,Math.floor(pct*accVal('rage_blood')))}
function getExecBonus(e){if(!hasAcc('executioner'))return 1;if(e.alive&&e.hp<e.maxHp*0.3)return 1+accVal('executioner')/100;return 1}
function getDragonReduce(){if(!hasAcc('dragon_scale'))return 0;return Math.floor(P.maxHp*accVal('dragon_scale')/100)}

// ============ CALAMITY ============
function addCalamity(){
  let pool=CALAMITIES.filter(c=>!hasCal(c.id));if(!pool.length)return;
  let cal=pool[rng(0,pool.length-1)];calamities.push(cal);
  if(cal.id==='fragile'){P.maxHp=Math.max(1,Math.floor(P.maxHp*0.8));P.hp=Math.min(P.hp,P.maxHp)}
  addLog('art',`⚠️ 灾厄降临:${cal.icon} ${cal.name} — ${cal.desc}`);
}

// ============ QUALITY ============
function rollQuality(floor){
  let p=[0,0,0,0,0,0],f=floor/TOTAL;
  p[1]=Math.max(5,50-floor*2);p[2]=25+Math.floor(floor*0.5);p[3]=10+Math.floor(floor*0.8);
  p[4]=5+Math.floor(floor*0.5);p[5]=Math.max(0,2+Math.floor((floor-15)*0.4));p[6]=0;
  let total=p.reduce((a,b)=>a+b,0),r=Math.random()*total,acc=0;
  for(let i=1;i<=6;i++){acc+=p[i];if(r<acc)return i}return 1;
}

// ============ EQUIPMENT GEN ============
function genWeapon(floor,qb=0){
  let q=Math.min(6,rollQuality(floor)+qb);
  let atk=[0,0,3,6,10,15,22][q]+rng(0,Math.floor(q*0.8));
  let mpCost=Math.max(2,5-Math.floor(q/2)),mult=Math.round((1.5+q*0.25)*10)/10;
  let eff='none';if(q>=2){let pool=WPN_EFF.slice(1);eff=pool[rng(0,pool.length-1)]}
  let np=WPN_NAMES[Math.min(6,q)]||WPN_NAMES[1];let name=np[rng(0,np.length-1)];
  return{name,quality:q,atkBonus:atk,mpCost:Math.round(mpCost),mult,effect:eff};
}
function genAccessory(floor,qb=0){
  let q=Math.min(6,rollQuality(floor)+qb),pool=ACC_POOL.slice();
  let def=pool[rng(0,pool.length-1)],value=Math.round(def.base*(0.5+q*0.4)*10)/10;
  return{id:def.id,name:def.name,quality:q,desc:def.desc.replace('{v}',value),value};
}
function genCursedAccessory(floor){
  let q=Math.min(6,rollQuality(floor)+2),pool=ACC_POOL.slice();
  let def=pool[rng(0,pool.length-1)],value=Math.round(def.base*(0.5+q*0.4)*10)/10;
  return{id:def.id,name:'诅咒·'+def.name,quality:q,desc:def.desc.replace('{v}',value)+' (诅咒)',value,cursed:true};
}

// ============ ENEMY GEN ============
function genEnemy(floor,type){
  let hp=10+floor*3,atk=2+Math.floor(floor*0.8),def=1+Math.floor(floor/3),mp=Math.min(8,5+Math.floor(floor/10));
  if(floor>TOTAL){let ed=floor-TOTAL;let sc=1+ed*0.25;hp=Math.floor(hp*sc);atk=Math.floor(atk*sc);def=Math.floor(def*sc)}
  let mult=1,enm=mp,name='哥布林',icon='👹',eff=[];
  if(type==='elite'){
    mult=1.6;let en=['精英骑士','暗黑卫兵','魔化将军','血煞统领','影刃队长','诅咒斗士','深渊先锋'];
    let ei=['⚔️','🛡️','💀','👺','🗡️','🔱','⚔️'],idx=rng(0,en.length-1);name=en[idx];icon=ei[idx];
    enm=7;eff.push(ENEMY_EFF[rng(0,ENEMY_EFF.length-1)]);
  }else if(type==='boss'){
    if(floor<=30){mult=3.0;enm=12;
      if(floor===10){name='深渊领主';icon='👿';eff=['fire']}
      else if(floor===20){name='暗黑龙';icon='🐉';eff=['fire','poison']}
      else{name='深渊霸主';icon='💀';eff=['fire','poison','lifesteal']}
    }else{// endless super boss every 20 floors
      mult=3.0+Math.floor((floor-TOTAL)/20)*0.5;enm=12;name='深渊霸主';icon='💀';eff=['fire','poison','lifesteal'];
    }
  }else{
    let tier=Math.min(9,Math.floor((floor-1)/3));
    let np=[['史莱姆','绿泥怪','果冻怪'],['骷髅','骷髅弓手','不死士兵'],['兽人','半兽人','兽人战士'],['暗精灵','暗精灵刺客','影行者'],['魔像','石魔像','铁魔像'],['恶灵','怨灵','幽灵骑士'],['魔蛛','巨型毒蛛','暗影蜘蛛'],['石像鬼','翼魔','石翼兽'],['暗影刺客','幻影杀手','夜行者'],['地精','地精萨满','疯狂地精']];
    let ip=['👹','💀','👺','🧝','🗿','👻','🕷️','🦇','🥷','👾'];
    name=np[tier][rng(0,2)];icon=ip[tier];
    if(Math.random()<0.15+floor*0.012||(floor>TOTAL&&Math.random()<0.5))eff.push(ENEMY_EFF[rng(0,ENEMY_EFF.length-1)]);
  }
  let ehp=Math.floor(hp*mult),ea=Math.floor(atk*mult),ed=Math.floor(def*mult);
  if(hasCal('enemy_rage')&&type!=='boss')ea=Math.floor(ea*1.25);
  return{name,icon,hp:ehp,maxHp:ehp,atk:ea,def:ed,mp:enm,maxMp:enm,type,eff,alive:true};
}
function spawnEnemies(floor){
  enemies=[];
  if(!endlessMode&&(ELITE.includes(floor)||BOSS.includes(floor))){
    if(ELITE.includes(floor)){enemies.push(genEnemy(floor,'elite'));enemies.push(genEnemy(floor,'elite'))}
    else enemies.push(genEnemy(floor,'boss'));
  }else if(endlessMode){
    let ed=floor-TOTAL;
    if(floor%20===0)enemies.push(genEnemy(floor,'boss'));
    else{let cnt=Math.min(3,1+Math.floor(ed/5));for(let i=0;i<cnt;i++){let t=Math.random()<0.15+ed*0.02?'elite':'normal';enemies.push(genEnemy(floor,t))}}
  }else{
    let cnt=floor>=16?2:(floor>=8?rng(1,2):1);for(let i=0;i<cnt;i++)enemies.push(genEnemy(floor,'normal'));
  }
  tgtIdx=enemies.findIndex(e=>e.alive);
}

// ============ RENDER ============
function renderAll(){renderTop();renderPlayer();renderEnemies();renderLog();renderActs()}
function renderTop(){$('dFloor').textContent=endlessMode?`无尽 第${P.floor}层`:`第 ${P.floor} / ${TOTAL} 层`;$('dLvl').textContent=`Lv.${P.level}`;$('dGold').textContent=`💰 ${P.gold}`}
function renderPlayer(){
  $('pHp').style.width=(P.hp/P.maxHp*100)+'%';$('pHpTx').textContent=P.hp+'/'+P.maxHp;
  $('pMp').style.width=(P.mp/P.maxMp*100)+'%';$('pMpTx').textContent=P.mp+'/'+P.maxMp;
  $('pAtk').textContent=getEffAtk();$('pDef').textContent=getEffDef();$('pSub').textContent=`Lv.${P.level} ${CLASSES.find(c=>c.id===P.cls)?.name||'冒险者'}`;
  $('bHvyC').textContent=getEffMpCost()+' MP';
  let eq='';
  if(P.weapon){let we=P.weapon.effect;eq+=`<span class="wpn-badge ${QCOLORS[P.weapon.quality]}">${P.weapon.name} +${P.weapon.atkBonus}ATK<div class="wpn-tip"><span class="${QCOLORS[P.weapon.quality]}">[${QNAMES[P.weapon.quality]}]</span> ATK+${P.weapon.atkBonus}<br>耗蓝${P.weapon.mpCost} 倍率${P.weapon.mult}x<br>特效: ${WPN_EFF_NAME[we]}</div></span>`}
  P.accessories.forEach(a=>{eq+=`<span class="acc-badge ${QCOLORS[a.quality]}">${a.quality>=5?'✨':''}${a.name}<div class="acc-tip"><span class="${QCOLORS[a.quality]}">[${QNAMES[a.quality]}]</span> ${a.name}<br>${a.desc}</div></span>`});
  $('equipRow').innerHTML=eq;
  let cr='';calamities.forEach(c=>{cr+=`<span class="cal-icon">${c.icon}<div class="cal-tip"><strong>${c.name}</strong><br>${c.desc}</div></span>`});$('calRow').innerHTML=cr;
  let db='';
  if(P.burnTurns>0)db+=`<span class="debuff">🔥灼伤${P.burnTurns}</span>`;
  if(P.poisonTurns>0)db+=`<span class="debuff">☠️中毒${P.poisonTurns}</span>`;
  if(P.cursedTurns>0)db+=`<span class="debuff">💀诅咒${P.cursedTurns}战</span>`;
  if(P.chargeBonus>0)db+=`<span class="debuff" style="color:#7ec8f8">⚡蓄力+${P.chargeBonus}%</span>`;
  $('debuffRow').innerHTML=db;
}
function renderEnemies(){
  if(!enemies.length){$('enemyArea').style.display='none';return}
  $('enemyArea').style.display='flex';
  $('enemyArea').innerHTML=enemies.map((e,i)=>{
    let cls='ecard'+(i===tgtIdx?' tgt':'')+(e.alive?'':' dead');
    let tt=e.type==='boss'?'BOSS':e.type==='elite'?'精英':'';
    let fx=e.eff.map(f=>ENEMY_EFF_NAME[f]||'').join(' ');
    let statLine=hasCal('blind')?'<div class="efx">???</div>':`<div style="font-size:0.7rem;color:#8892b0">ATK ${e.atk} DEF ${e.def}</div>`;
    return`<div class="${cls}" onclick="setTgt(${i})"><div class="eh"><span class="ei">${e.icon}</span><span class="en">${e.name}</span>${tt?`<span class="et">${tt}</span>`:''}</div><div class="bars"><div class="bar-r"><span class="bar-l hp">HP</span><div class="bar-t"><div class="bar-f hp" style="width:${e.alive?e.hp/e.maxHp*100:0}%"></div><span class="bar-tx">${e.alive?e.hp+'/'+e.maxHp:'已阵亡'}</span></div></div></div>${statLine}${fx?`<div class="efx">${fx}</div>`:''}</div>`;
  }).join('');
}
function renderLog(){$('logBox').innerHTML=logs.map(l=>`<div class="le ${l.t}">${l.m}</div>`).join('');$('logBox').scrollTop=$('logBox').scrollHeight}
function renderActs(){let s=!gameOver&&enemies.some(e=>e.alive);$('actBar').style.display=s?'flex':'none';if(!s)return;$('bAtk').disabled=busy;$('bHvy').disabled=busy||P.mp<getEffMpCost();$('bDef').disabled=busy;$('bMp').disabled=busy}
function setTgt(i){if(enemies[i].alive){tgtIdx=i;renderEnemies()}}

// ============ BRANCH SYSTEM ============
let enemyWeightBoost=false;
function showBranch(){
  if(gameOver){showEnd(P.floor>TOTAL);return}
  if(!endlessMode&&P.floor>TOTAL){showEndlessChoice();return}
  if(!endlessMode&&(ELITE.includes(P.floor)||BOSS.includes(P.floor))){
    spawnEnemies(P.floor);addLog('sys',`⚠️ 第${P.floor}层:${BOSS.includes(P.floor)?'BOSS':'精英'}战!`);
    renderAll();$('branchOv').classList.remove('show');return;
  }
  if(endlessMode&&P.floor%10===0&&P.floor>TOTAL&&!BOSS.includes(P.floor)){
    spawnEnemies(P.floor);addLog('sys',`⚠️ 无尽第${P.floor}层:超级Boss战!`);renderAll();$('branchOv').classList.remove('show');return;
  }
  let w=Object.assign({},BRANCH_W);if(enemyWeightBoost)w.enemy+=20;
  let total=Object.values(w).reduce((a,b)=>a+b,0),cards=[],used=[];
  while(cards.length<3){let r=Math.random()*total,acc=0;
    for(let k of Object.keys(w)){acc+=w[k];if(r<acc&&!used.includes(k)){used.push(k);cards.push(k);break}}}
  let fog=hasCal('fog');
  let info={enemy:{icon:'⚔️',name:'敌人',desc:'战斗获取经验和金币'},heal:{icon:'❤️',name:'医疗',desc:'恢复生命值'},treasure:{icon:'🎁',name:'宝藏',desc:'获取武器或金币'},shop:{icon:'🏪',name:'商店',desc:'购买装备和补给'},mystery:{icon:'🔮',name:'神秘房间',desc:'未知的机遇与风险'}};
  let hintHtml='';if(hasAcc('map_frag')&&!fog){/* map_frag removed, no preview */}
  let h='';cards.forEach(c=>{let inf=info[c];
    h+=`<div class="bcard" onclick="chooseBranch('${c}')"><div class="bi">${fog?'❓':inf.icon}</div><div class="bn">${fog?'???':inf.name}</div><div class="bd">${fog?'???':inf.desc}</div></div>`;
  });
  $('branchGrid').innerHTML=h;$('branchOv').classList.add('show');enemyWeightBoost=false;renderAll();
}
function chooseBranch(type){$('branchOv').classList.remove('show');
  if(type==='enemy'){startCombat();enemyWeightBoost=false}
  else if(type==='heal')doHeal();
  else if(type==='treasure')doTreasure();
  else if(type==='shop')doShop();
  else if(type==='mystery')doMystery();
}

// ============ COMBAT ============
function startCombat(){
  spawnEnemies(P.floor);P.lifePactUsed=false;P.undyingUsed=false;
  addLog('sys',`— 第${P.floor}层 — 战斗开始!`);renderAll();
}
function doAction(act){
  if(busy||gameOver)return;
  if(!enemies[tgtIdx].alive){let a=enemies.findIndex(e=>e.alive);if(a===-1)return;tgtIdx=a}
  busy=true;renderActs();
  // Calamity: life decay
  if(hasCal('life_decay')){P.hp=Math.max(1,P.hp-2);addLog('sys','☠️ 生命凋零:-2HP')}
  let rg=getRegen();if(rg>0){P.hp=Math.min(P.maxHp,P.hp+rg);addLog('sys',`复苏之风恢复${rg}HP`)}
  let med=accVal('meditation');let medGain=Math.floor(med);if(hasCal('mp_block'))medGain=Math.max(0,medGain-2);
  if(medGain>0){P.mp=Math.min(P.maxMp,P.mp+medGain);addLog('sys',`冥想恢复${medGain}MP`)}
  if(hasAcc('energy_conv')&&P.hp>3){P.hp-=3;P.mp=Math.min(P.maxMp,P.mp+3);addLog('sys','能量转换: -3HP +3MP')}
  if(hasAcc('life_pact')&&!P.lifePactUsed&&P.hp<P.maxHp*0.3){P.hp=Math.min(P.maxHp,P.hp+10);P.lifePactUsed=true;addLog('art','生命契约触发!+10HP')}
  if(act==='atk')doPlayerAttack(false);
  else if(act==='hvy'){P.mp-=getEffMpCost();doPlayerAttack(true)}
  else if(act==='def'){addLog('pl',`选择防御,减伤${Math.floor(getDefRate()*100)}%`);P.defending=true;setTimeout(()=>enemyPhase(),300)}
  else if(act==='mp'){let mpGain=5;if(hasCal('mp_block'))mpGain=Math.max(0,mpGain-2);P.mp=Math.min(P.maxMp,P.mp+mpGain);let echoHp=accVal('echo_bell');if(echoHp>0){P.hp=Math.min(P.maxHp,P.hp+Math.floor(echoHp));addLog('pl',`回蓝+${mpGain}MP,回响恢复${Math.floor(echoHp)}HP`)}else addLog('pl',`回蓝 +${mpGain} MP`);renderAll();setTimeout(()=>enemyPhase(),300)}
}
function doPlayerAttack(heavy){
  let e=enemies[tgtIdx];if(!e.alive){busy=false;renderActs();return}
  let atk=getEffAtk(),def=e.def;
  if(hasAcc('weakness'))def=Math.floor(def*(1-accVal('weakness')/100));
  let sharp=getSharp();def=Math.max(0,def-sharp);
  let dmg,bonus='';
  if(heavy){let m=getEffMult();dmg=Math.max(1,Math.floor(atk*m)-def);bonus='(强攻)'}
  else{dmg=Math.max(1,atk-def)}
  // Charge bonus
  if(P.chargeBonus>0){dmg=Math.floor(dmg*(1+P.chargeBonus/100));bonus+=`(蓄力+${P.chargeBonus}%)`;P.chargeBonus=0}
  // Calamity: thorn shield
  if(hasCal('thorn_shield')&&Math.random()<0.2){dmg=Math.floor(dmg*0.5);addLog('en','🛡️ 荆棘之盾:敌人减伤50%!')}
  let rage=getRageBonus();if(rage>0){dmg=Math.floor(dmg*(1+rage/100));bonus+=`(怒血+${rage}%)`}
  dmg=Math.floor(dmg*getExecBonus(e));
  let crit=getCrit(),isCrit=Math.random()<crit.rate;if(isCrit){dmg=Math.floor(dmg*crit.dmg);bonus+=' 暴击!'}
  let prevHp=e.hp;e.hp=Math.max(0,e.hp-dmg);addLog('pl',`${heavy?'强力攻击':'普通攻击'}→${e.name} ${dmg}伤害${bonus}`);
  // Fang lifesteal
  if(!heavy&&hasAcc('fang')){let h=Math.floor(accVal('fang'));P.hp=Math.min(P.maxHp,P.hp+h)}
  // Werewolf lifesteal
  if(P.cls==='werewolf'){let h=Math.max(1,Math.floor(dmg*0.15));P.hp=Math.min(P.maxHp,P.hp+h)}
  if(heavy&&e.alive){let we=P.weapon.effect;
    if(we==='fire'&&Math.random()<0.35){e.eff.push('fire');addLog('pl','🔥 火焰附加!')}
    if(we==='poison'&&Math.random()<0.3){e.eff.push('poison');addLog('pl','☠️ 毒素附加!')}
    if(we==='lifesteal'){let h=Math.floor(dmg*0.15);P.hp=Math.min(P.maxHp,P.hp+h);addLog('pl',`🩸 吸血${h}HP`)}
    if(we==='blood_sac'){let cost=Math.floor(P.maxHp*0.05);P.hp-=cost;let extra=Math.floor(dmg*0.5);e.hp=Math.max(0,e.hp-extra);addLog('pl',`🩸 血祭-${cost}HP 额外${extra}伤害`)}
    if(we==='iron_wall'){P.defending=true;addLog('pl','🛡️ 铁壁防御')}
    if(we==='charge'){P.chargeBonus+=20;addLog('pl',`⚡ 蓄力+20%`)}
  }
  // Thunder chain
  if(hasAcc('thunder')&&Math.random()<accVal('thunder')/100){let tn=rng(5,10);let other=enemies.find(x=>x.alive&&x!==e);if(other){other.hp=Math.max(0,other.hp-tn);addLog('art',`⚡ 闪电链→${other.name} ${tn}伤害`);if(other.hp<=0){other.alive=false;processKill(other)}}}
  renderAll();
  if(e.hp<=0){e.alive=false;let overflow=Math.max(0,dmg-prevHp);processKill(e);
    if(hasAcc('ricochet')&&overflow>0){let nxt=enemies.find(x=>x.alive);if(nxt){nxt.hp=Math.max(0,nxt.hp-overflow);addLog('art',`弹射之刃→${nxt.name} ${overflow}`);if(nxt.hp<=0){nxt.alive=false;processKill(nxt)}}}}
  if(!enemies.some(x=>x.alive)){busy=false;combatWin();return}
  setTimeout(()=>enemyPhase(),350);
}
function processKill(e){
  let gold=rng(10,20);if(e.type==='elite')gold=rng(30,50);if(e.type==='boss')gold=rng(80,120);
  if(hasAcc('greedy_pot')&&Math.random()<accVal('greedy_pot')/100){gold*=2;addLog('art','💰 贪婪之壶触发!双倍金币!')}
  gold=Math.floor(gold*getGreedMult());gold+=Math.floor(accVal('lucky_coin')||0);
  P.gold+=gold;stats.goldTotal+=gold;
  let lvls=1,aUp=P.growA||2,dUp=P.growD||2,hUp=P.growH||8;
  if(e.type==='elite'){lvls=2;aUp=P.growA||2;dUp=P.growD||2;hUp=P.growH||8}
  else if(e.type==='boss'){lvls=3}
  let extraLv=hasAcc('exp_badge')?Math.floor(accVal('exp_badge')):0;lvls+=extraLv;
  let hAtk=hasAcc('heritage')?Math.floor(accVal('heritage'))*lvls:0;let hHp=hasAcc('heritage')?2*lvls:0;
  for(let i=0;i<lvls;i++){P.level++;
    let da=P.growA||2,dd=P.growD||2,dh=P.growH||8;
    if(e.type==='elite'){da=Math.floor(da*1.5);dd=Math.floor(dd*1.5);dh=Math.floor(dh*1.5)}
    if(e.type==='boss'){da=Math.floor(da*2);dd=Math.floor(dd*2);dh=Math.floor(dh*2)}
    P.atk+=da;
    if(P.cls==='werewolf'){P.maxHp+=dd}else{P.def+=dd}
    P.maxHp+=dh;P.hp=Math.min(P.maxHp,P.hp+dh);
  }
  P.atk+=hAtk;P.maxHp+=hHp;P.hp=Math.min(P.maxHp,P.hp+hHp);
  if(e.type==='normal')stats.kills++;else if(e.type==='elite')stats.elites++;else stats.bosses++;
  addLog('sys',`💀 ${e.name} 击败!+${gold}💰 +${lvls}级`);
  let bb=getBloodBlade();if(bb>0){let h=Math.max(1,Math.floor(P.maxHp*bb/100));P.hp=Math.min(P.maxHp,P.hp+h);addLog('art',`血刃恢复${h}HP`)}
  renderAll();
}
function enemyPhase(){
  if(gameOver)return;
  for(let e of enemies){if(!e.alive)continue;
    // Enemy effects
    if(e.eff.includes('fire')&&Math.random()<0.3&&!P.burnTurns){P.burnTurns=2;addLog('en',`🔥 ${e.name}点燃了你!`)}
    if(e.eff.includes('poison')&&Math.random()<0.25&&!P.poisonTurns){P.poisonTurns=3;addLog('en',`☠️ ${e.name}对你施毒!`)}
    let eatk=e.atk;if(e.eff.includes('berserk')&&e.hp<e.maxHp*0.5)eatk=Math.floor(eatk*1.3);
    let eDmg=Math.max(1,eatk-getEffDef());
    if(P.defending){eDmg=Math.floor(eDmg*(1-getDefRate()));P.defending=false}
    // Dragon scale flat reduce
    let dr=getDragonReduce();if(dr>0){eDmg=Math.max(0,eDmg-dr);addLog('pl',`龙鳞甲减伤${dr}`)}
    P.hp-=eDmg;addLog('en',`${e.name}攻击你 ${eDmg}伤害`);
    // Shield reflect
    if(P.cls==='shield'){let sdef=getEffDef();let ref=Math.max(1,Math.floor(sdef*0.5));e.hp=Math.max(0,e.hp-ref);addLog('pl',`🛡️ 反弹${ref}伤害`);if(e.hp<=0){e.alive=false;processKill(e)}}
    // Lifesteal
    if(e.eff.includes('lifesteal')){let h=Math.floor(eDmg*0.2);e.hp=Math.min(e.maxHp,e.hp+h)}
    // Breath heal
    if(hasCal('breath')){e.hp=Math.min(e.maxHp,e.hp+3)}
    // Thorn
    let th=getThorn();if(th>0){e.hp=Math.max(0,e.hp-th);addLog('pl',`荆棘反弹${th}伤害`);if(e.hp<=0){e.alive=false;processKill(e)}}
    if(P.hp<=0&&!P.undyingUsed&&hasAcc('undying')){P.hp=Math.floor(P.maxHp*0.3);P.undyingUsed=true;addLog('art','💪 不屈意志触发!')}
    if(P.hp<=0){gameOver=true;addLog('sys','你倒下了...');renderAll();setTimeout(()=>showEnd(false),600);return}
    renderAll();
  }
  // Check if all enemies dead (from thorn/shield reflect)
  if(!enemies.some(x=>x.alive)){busy=false;combatWin();return}
  // Turn end effects
  if(P.burnTurns>0){let bd=rng(2,4);P.hp-=bd;P.burnTurns--;addLog('sys',`🔥灼伤-${bd}HP`);if(P.hp<=0&&!P.undyingUsed&&hasAcc('undying')){P.hp=Math.floor(P.maxHp*0.3);P.undyingUsed=true;addLog('art','💪 不屈意志触发!')}if(P.hp<=0){gameOver=true;addLog('sys','你倒下了...');renderAll();setTimeout(()=>showEnd(false),600);return}}
  if(P.poisonTurns>0){let pd=rng(1,3);P.hp-=pd;P.poisonTurns--;addLog('sys',`☠️中毒-${pd}HP`);if(P.hp<=0&&!P.undyingUsed&&hasAcc('undying')){P.hp=Math.floor(P.maxHp*0.3);P.undyingUsed=true;addLog('art','💪 不屈意志触发!')}if(P.hp<=0){gameOver=true;addLog('sys','你倒下了...');renderAll();setTimeout(()=>showEnd(false),600);return}}
  busy=false;renderAll();
}
function combatWin(){
  addLog('sys','🎉 战斗胜利!');
  // Warrior mark: +1 ATK per enemy branch victory
  P.warriorMarks++;
  // Check if boss floor → 6-pick-2 accessory drop
  if(!endlessMode&&BOSS.includes(P.floor)){
    window._bossPickPool=[];for(let i=0;i<6;i++)window._bossPickPool.push(genAccessory(P.floor,2));
    window._bossPicks=0;showBossPick();return;
  }
  // Endless super boss
  if(endlessMode&&P.floor%20===0&&P.floor>TOTAL){
    window._bossPickPool=[];for(let i=0;i<6;i++)window._bossPickPool.push(genAccessory(P.floor,2));
    window._bossPicks=0;showBossPick();return;
  }
  renderAll();setTimeout(()=>nextFloor(),400);
}

// ============ BOSS DROP (6 pick 2) ============
function showBossPick(){
  if(window._bossPicks>=2){renderAll();setTimeout(()=>nextFloor(),400);return}
  let pool=window._bossPickPool;
  let html=PAUSE_BTN+`<h2>🏆 Boss掉落饰品</h2><p>从${pool.length}件饰品中选择第${window._bossPicks+1}件(共2件)</p>`;
  pool.forEach((a,i)=>{
    html+=`<div class="shop-item" onclick="pickBossAcc(${i})"><div class="si-name"><span class="${QCOLORS[a.quality]}">[${QNAMES[a.quality]}]</span> <strong>${a.name}</strong><br><span style="color:#8892b0;font-size:0.78rem">${a.desc}</span></div></div>`;
  });
  $('accDropMdl').innerHTML=html;$('accDropOv').classList.add('show');
}
function pickBossAcc(idx){
  let pool=window._bossPickPool;let acc=pool[idx];
  P.accessories.push(acc);stats.accessories.push(acc.name);
  addLog('art',`获得饰品:${acc.name}`);
  if(P.cls==='noble')showNobleAccTip(acc);
  pool.splice(idx,1);window._bossPicks++;
  $('accDropOv').classList.remove('show');
  if(window._bossPicks<2&&pool.length>0)setTimeout(()=>showBossPick(),200);
  else{renderAll();setTimeout(()=>nextFloor(),400)}
}

// ============ MIMIC DROP (3 pick 1) ============
function giveMimicAcc(){
  let pool=[];for(let i=0;i<3;i++)pool.push(genAccessory(P.floor,1));
  let html=PAUSE_BTN+`<h2>🎁 宝箱怪掉落</h2><p>从3件饰品中选择1件</p>`;
  pool.forEach((a,i)=>{
    html+=`<div class="shop-item" onclick="pickMimicAcc(${i})"><div class="si-name"><span class="${QCOLORS[a.quality]}">[${QNAMES[a.quality]}]</span> <strong>${a.name}</strong><br><span style="color:#8892b0;font-size:0.78rem">${a.desc}</span></div></div>`;
  });
  window._mimicPool=pool;
  $('accDropMdl').innerHTML=html;$('accDropOv').classList.add('show');
}
function pickMimicAcc(idx){
  let acc=window._mimicPool[idx];
  P.accessories.push(acc);stats.accessories.push(acc.name);
  addLog('art',`🎁 宝箱怪掉落饰品:${acc.name} (${QNAMES[acc.quality]})`);
  if(P.cls==='noble')showNobleAccTip(acc);
  $('accDropOv').classList.remove('show');
  renderAll();setTimeout(()=>nextFloor(),400);
}

// ============ NOBLE ACC TIP ============
function showNobleAccTip(acc){
  // Show after other overlays close
  setTimeout(()=>{
    let html=`<h2>💎 获得饰品</h2><p>没落贵族的收藏</p>`;
    html+=`<div class="drop-acc"><span class="${QCOLORS[acc.quality]}">[${QNAMES[acc.quality]}]</span> <strong>${acc.name}</strong><br><span style="color:#8892b0">${acc.desc}</span></div>`;
    html+=`<div class="cbtn-row"><button class="cbtn yes" onclick="$('accDropOv').classList.remove('show')">确认</button></div>`;
    $('accDropMdl').innerHTML=html;$('accDropOv').classList.add('show');
  },500);
}

// ============ HEAL ============
function doHeal(){
  P.mp=P.maxMp;let pct=hasCal('hunger')?rng(15,25):rng(30,50);
  let heal=Math.floor(P.maxHp*pct/100);P.hp=Math.min(P.maxHp,P.hp+heal);
  addLog('sys',`❤️ 找到医疗点,恢复${heal}HP (${pct}%)`);renderAll();setTimeout(()=>nextFloor(),400);
}

// ============ TREASURE (3 pick 1) ============
function doTreasure(){
  P.mp=P.maxMp;
  if(Math.random()<getMimicChance()){
    let mimic=genEnemy(P.floor,'normal');mimic.name='宝箱怪';mimic.icon='📦';
    if(hasCal('chest_trap')){mimic.hp=Math.floor(mimic.hp*1.5);mimic.maxHp=mimic.hp;mimic.atk=Math.floor(mimic.atk*1.5)}
    enemies=[mimic];tgtIdx=0;addLog('sys','🎁 宝藏!...等等,这是宝箱怪!');renderAll();
    window._mimicFight=true;return;
  }
  // Accessory 3-pick-1
  let pool=[];for(let i=0;i<3;i++)pool.push(genAccessory(P.floor));
  let html=PAUSE_BTN+`<h2>🎁 宝藏饰品</h2><p>从3件饰品中选择1件</p>`;
  pool.forEach((a,i)=>{
    html+=`<div class="shop-item" onclick="pickTreasureAcc(${i})"><div class="si-name"><span class="${QCOLORS[a.quality]}">[${QNAMES[a.quality]}]</span> <strong>${a.name}</strong><br><span style="color:#8892b0;font-size:0.78rem">${a.desc}</span></div></div>`;
  });
  window._treasPool=pool;
  $('eventMdl').innerHTML=html;$('eventOv').classList.add('show');
}
function pickTreasureAcc(idx){
  let acc=window._treasPool[idx];
  P.accessories.push(acc);stats.accessories.push(acc.name);
  addLog('art',`🎁 获得饰品:${acc.name} (${QNAMES[acc.quality]})`);
  if(P.cls==='noble')showNobleAccTip(acc);
  $('eventOv').classList.remove('show');
  renderAll();setTimeout(()=>nextFloor(),300);
}

// ============ WEAPON COMPARE ============
function showWeaponCompare(w){
  let cur=P.weapon;
  let html=PAUSE_BTN+`<h2>⚔️ 发现武器</h2><p>比较后决定是否替换</p><div class="compare">`;
  html+=`<div class="cmp-box"><div class="cb-n ${QCOLORS[cur.quality]}">${cur.name}</div><div class="cb-s">ATK+${cur.atkBonus} 耗蓝${cur.mpCost} ${WPN_EFF_NAME[cur.effect]}</div></div>`;
  html+=`<div class="cmp-box"><div class="cb-n ${QCOLORS[w.quality]}">${w.name}</div><div class="cb-s">ATK+${w.atkBonus} 耗蓝${w.mpCost} ${WPN_EFF_NAME[w.effect]}</div></div></div>`;
  html+=`<div class="cbtn-row"><button class="cbtn yes" onclick="equipWeapon()">装备新武器</button><button class="cbtn no" onclick="discardWeapon()">保留现有</button></div>`;
  window._pendingWpn=w;$('wpnMdl').innerHTML=html;$('wpnOv').classList.add('show');
}
function equipWeapon(){let w=window._pendingWpn;stats.weapons.push(`${w.name}(${QNAMES[w.quality]})`);P.weapon=w;window._pendingWpn=null;$('wpnOv').classList.remove('show');renderAll();setTimeout(()=>nextFloor(),300)}
function discardWeapon(){window._pendingWpn=null;$('wpnOv').classList.remove('show');renderAll();setTimeout(()=>nextFloor(),300)}

// ============ SHOP ============
function shopItemHtml(it,idx){
  let d='';
  if(it.type==='weapon'){let w=it.item;d=`<span class="${QCOLORS[w.quality]}">[${QNAMES[w.quality]}]</span> ATK+${w.atkBonus} 耗蓝${w.mpCost} 倍率${w.mult}x ${WPN_EFF_NAME[w.effect]}`}
  else if(it.type==='acc'){let a=it.item;d=`<span class="${QCOLORS[a.quality]}">[${QNAMES[a.quality]}]</span> ${a.desc}`}
  return`<div class="shop-item${it.price>P.gold?' off':''}" onclick="buyItem(${idx})"><div class="si-name">${it.name}${d?' <span style="font-weight:400;font-size:0.78rem;color:#8892b0;display:block;margin-top:2px">'+d+'</span>':''}</div><span class="si-price">${it.price}💰</span></div>`;
}
function refreshShop(){$('eventMdl').innerHTML=PAUSE_BTN+shopItems.map((it,i)=>it.sold?'':shopItemHtml(it,i)).join('')+`<div class="cbtn-row"><button class="cbtn no" onclick="$('eventOv').classList.remove('show');renderAll();setTimeout(()=>nextFloor(),300)">离开商店</button></div>`}
function doShop(){
  P.mp=P.maxMp;shopItems=[];
  let noble=P.cls==='noble';
  let disc=getShopDiscount(),calMult=hasCal('price_up')?1.5:1;
  // Fixed layout: 1(2) weapons + 2(3) accessories + 1 heal
  let wCnt=noble?2:1,aCnt=noble?3:2;
  for(let i=0;i<wCnt;i++){let w=genWeapon(P.floor);let price=Math.floor((80+P.floor*5.6)*disc*calMult);shopItems.push({type:'weapon',name:w.name,item:w,price,sold:false})}
  for(let i=0;i<aCnt;i++){let a=genAccessory(P.floor);let price=Math.floor((70+P.floor*4.3)*disc*calMult);shopItems.push({type:'acc',name:a.name,item:a,price,sold:false})}
  let hPrice=Math.floor(80*disc*calMult);shopItems.push({type:'heal',name:'恢复生命(50%HP)',item:null,price:hPrice,sold:false});
  refreshShop();$('eventOv').classList.add('show');
}
function buyItem(idx){
  let it=shopItems[idx];if(!it||it.sold||P.gold<it.price)return;
  P.gold-=it.price;it.sold=true;
  if(it.type==='weapon'){showShopWeaponCompare(it.item,idx)}
  else if(it.type==='acc'){P.accessories.push(it.item);stats.accessories.push(it.item.name);addLog('sys',`购买了饰品:${it.item.name}`);if(P.cls==='noble')showNobleAccTip(it.item);refreshShop();renderAll()}
  else if(it.type==='heal'){let h=Math.floor(P.maxHp*0.5);P.hp=Math.min(P.maxHp,P.hp+h);addLog('sys',`使用恢复,+${h}HP`);refreshShop();renderAll()}
}
function showShopWeaponCompare(w,idx){
  let cur=P.weapon;
  let html=`<h2>⚔️ 购买武器</h2><div class="compare">`;
  html+=`<div class="cmp-box"><div class="cb-n ${QCOLORS[cur.quality]}">${cur.name}</div><div class="cb-s">ATK+${cur.atkBonus}</div></div>`;
  html+=`<div class="cmp-box"><div class="cb-n ${QCOLORS[w.quality]}">${w.name}</div><div class="cb-s">ATK+${w.atkBonus}</div></div></div>`;
  html+=`<div class="cbtn-row"><button class="cbtn yes" onclick="buyWeapon(${idx})">装备</button><button class="cbtn no" onclick="skipShopWpn()">丢弃</button></div>`;
  $('wpnMdl').innerHTML=html;$('wpnOv').classList.add('show');window._shopWpnIdx=idx;
}
function buyWeapon(idx){
  let it=shopItems[idx];let w=it.item;stats.weapons.push(`${w.name}(${QNAMES[w.quality]})`);P.weapon=w;
  addLog('sys',`购买并装备武器:${w.name}`);$('wpnOv').classList.remove('show');refreshShop();renderAll();
}
function skipShopWpn(){let idx=window._shopWpnIdx;addLog('sys','丢弃了购买的武器');$('wpnOv').classList.remove('show');refreshShop();renderAll()}

// ============ MYSTERY ============
function doMystery(){
  P.mp=P.maxMp;let type=MYSTERY_POOL[rng(0,MYSTERY_POOL.length-1)];
  let html=PAUSE_BTN;
  if(type==='cursed_acc'){let a=genCursedAccessory(P.floor);html+=`<h2>🔮 诅咒饰品</h2><p>一个强大的诅咒饰品出现了</p><div class="drop-acc"><span class="${QCOLORS[a.quality]}">[${QNAMES[a.quality]}]</span> <strong>${a.name}</strong><br>${a.desc}</div><div class="cbtn-row"><button class="cbtn yes" onclick="takeCurseAcc()">接受诅咒</button><button class="cbtn no" onclick="closeMystery()">拒绝</button></div>`;window._curseAcc=a}
  else if(type==='blessing'){let boost=rng(1,3);P.atk+=boost;P.maxHp+=boost*5;P.hp=Math.min(P.maxHp,P.hp+boost*5);html+=`<h2>✨ 祝福</h2><p>神秘力量祝福了你!ATK+${boost},HP+${boost*5}</p><button class="rbtn" onclick="closeMystery()">继续</button>`}
  else if(type==='trap'){let d=rng(5,15);P.hp=Math.max(1,P.hp-d);html+=`<h2>💥 陷阱</h2><p>你触发了陷阱!受到${d}点伤害</p><button class="rbtn" onclick="closeMystery()">继续</button>`}
  else if(type==='gambling'){html+=`<h2>🎰 赌博</h2><p>赌一把?50%概率获得50金,50%概率失去30金</p><div class="cbtn-row"><button class="cbtn yes" onclick="doGamble(true)">下注</button><button class="cbtn no" onclick="closeMystery()">离开</button></div>`}
  $('eventMdl').innerHTML=html;$('eventOv').classList.add('show');
}
function takeCurseAcc(){let a=window._curseAcc;P.accessories.push(a);stats.accessories.push(a.name);P.cursedTurns=2;P.cursedAcc=a;addLog('sys',`接受了诅咒饰品:${a.name}`);closeMystery()}
function closeMystery(){$('eventOv').classList.remove('show');renderAll();setTimeout(()=>nextFloor(),300)}
function doGamble(go){if(go){if(Math.random()<0.5){P.gold+=50;stats.goldTotal+=50;addLog('art','🎰 赢了!+50💰')}else{P.gold=Math.max(0,P.gold-30);addLog('sys','🎰 输了...-30💰')}closeMystery()}else closeMystery()}

// ============ FLOOR PROGRESSION ============
function nextFloor(){
  P.floor++;stats.maxFloor=Math.max(stats.maxFloor,P.floor);
  P.mp=P.maxMp;
  if(hasAcc('life_fountain')){let h=Math.max(1,Math.floor(P.maxHp*accVal('life_fountain')/100));P.hp=Math.min(P.maxHp,P.hp+h);addLog('art',`🌊 生命之泉恢复${h}HP`)}
  // Calamity at multiples of 10
  if(P.floor>1&&P.floor%10===0){addCalamity()}
  // Check mimic fight result
  if(window._mimicFight){window._mimicFight=false;giveMimicAcc()}
  renderAll();showBranch();
}

// ============ ENDLESS MODE ============
function showEndlessChoice(){
  let html=`<h2>🏆 通关成功!</h2><p>你征服了全部30层深渊!</p>`;
  html+=`<div class="cbtn-row"><button class="cbtn yes" onclick="startEndless()" style="background:linear-gradient(135deg,#e94560,#c0392b)">⚔️ 无尽挑战</button><button class="cbtn no" onclick="showEnd(true)">查看总结</button></div>`;
  $('eventMdl').innerHTML=html;$('eventOv').classList.add('show');
}
function startEndless(){
  endlessMode=true;$('eventOv').classList.remove('show');
  addLog('sys','🔥 无尽模式开启!敌人将变得极其强大!');
  renderAll();showBranch();
}

// ============ END GAME ============
function showEnd(win){
  gameOver=true;renderActs();
  let mf=stats.maxFloor||P.floor;
  $('endTitle').textContent=endlessMode?'⚔️ 无尽挑战结束':win?'🏆 通关成功!':'💀 冒险失败';
  $('endTitle').style.color=endlessMode?'#e94560':win?'#ffd700':'#e74c3c';
  $('endMsg').textContent=endlessMode?`你在无尽模式到达了第${mf}层!`:win?'你征服了全部30层深渊!':`你倒在了第${P.floor}层...`;
  let s=`<strong>📊 通关总结</strong><br>${endlessMode?'最高层数':'到达层数'}: ${mf}${!endlessMode?'/'+TOTAL:''}<br>最终等级: Lv.${P.level}<br>`;
  s+=`击败敌人: ${stats.kills+stats.elites+stats.bosses} (普通${stats.kills}/精英${stats.elites}/Boss${stats.bosses})<br>`;
  s+=`总金币: ${stats.goldTotal} (剩余: ${P.gold})<br>武器: ${stats.weapons[stats.weapons.length-1]}<br>`;
  s+=`饰品(${P.accessories.length}件): ${stats.accessories.length?stats.accessories.join('、'):'无'}<br>`;
  s+=`灾厄(${calamities.length}个): ${calamities.map(c=>c.name).join('、')}`;
  $('endSummary').innerHTML=s;$('endOv').classList.add('show');
}
function restart(){$('endOv').classList.remove('show');$('logBox').innerHTML='';logs=[];newGame()}
newGame();
</script>
</body>
</html>

Game Source: Rogue 冒险 · 层级远征

Creator: QuantumPenguin78

Libraries: none

Complexity: complex (739 lines, 57.2 KB)

The full source code is displayed above on this page.

Remix Instructions

To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: rogue-quantumpenguin78" to link back to the original. Then publish at arcadelab.ai/publish.