您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
摸鱼放置助手 - 全新UI
当前为
// ==UserScript== // @name MoYuIdleHelper // @namespace https://tampermonkey.net/ // @version 1.0 // @description 摸鱼放置助手 - 全新UI // @author Mid // @license MIT // @match https://www.moyu-idle.com/* // @match https://moyu-idle.com/* // @grant none // @require https://cdn.jsdelivr.net/npm/[email protected]/dist/pako.min.js // ==/UserScript== (function () { 'use strict'; let combatLogEnabled = true; const damageAccum = new Map(); // uuid → 累计伤害 const actionCount = new Map(); // uuid → 动作次数 const healAccum = new Map(); // uuid → 累计治疗 // 仓库物品变动跟踪 let inventory = {}; // 当前仓库状态 let inventoryChanges = new Map(); // 物品变动记录 // 技能名称映射 const skillNames = { baseAttack: "普通攻击", boneShield: "骨盾", corrosiveBreath: "腐蚀吐息", summonBerryBird: "召唤浆果鸟", baseHeal: "基础治疗", poison: "中毒", selfHeal: "自我疗愈", sweep: "横扫", baseGroupHeal: "基础群体治疗", powerStrike: "重击", guardianLaser: "守护者激光", lavaBreath: "熔岩吐息", dragonRoar: "龙之咆哮", doubleStrike: "双重打击", lowestHpStrike: "弱点打击", explosiveShot: "爆炸射击", freeze: "冻结", iceBomb: "冰弹", lifeDrain: "吸血", roar: "咆哮", blizzard: "暴风雪", ironWall: "铁壁", curse: "诅咒", shadowBurst: "暗影爆发", groupCurse: "群体诅咒", holyLight: "神圣之光", bless: "祝福", revive: "复活", groupRegen: "群体再生", astralBarrier: "星辉结界", astralBlast: "星辉冲击", groupSilence: "群体沉默", selfRepair: "自我修复", cleanse: "驱散", cometStrike: "彗星打击", armorBreak: "破甲", starTrap: "星辰陷阱", emperorCatFinale_forAstralEmpressBoss: "星辉终极裁决", astralStorm: "星辉风暴", groupShield: "群体护盾", sneak: "潜行", ambush: "偷袭", poisonClaw: "毒爪", shadowStep: "暗影步", silenceStrike: "沉默打击", slientSmokeScreen: "静默烟雾弹", mirrorImage: "镜像影分身", shadowAssassinUlt: "绝影连杀", stardustMouseSwap: "偷天换日", dizzySpin: "眩晕旋转", carouselOverdrive: "失控加速", candyBomb: "糖果爆裂", prankSmoke: "恶作剧烟雾", plushTaunt: "毛绒嘲讽", starlightSanctuary: "星光治愈", ghostlyStrike: "鬼影冲锋", paradeHorn: "狂欢号角", clownSummon: "小丑召集令", kingAegis: "猫王庇护" }; // 物品ID到名称映射 const itemIdNameMap = { "__satiety": "饱食度", "__cat": "小猫咪", "gold": "金币", "catPawCoin": "猫爪古钱币", "wood": "木材", "stone": "矿石", "coal": "煤炭", "iron": "铁", "steel": "钢", "silverOre": "银矿", "silverIngot": "银锭", "mithrilOre": "秘银矿", "mithrilIngot": "秘银锭", "bamboo": "竹子", "fish": "鱼", "mushroom": "蘑菇", "berry": "浆果", "chickenEgg": "鸡蛋", "milk": "牛奶", "salmon": "鲑鱼", "tuna": "金枪鱼", "honey": "蜂蜜", "herb": "草药", "wool": "羊毛", "silk": "蚕丝", "cashmere": "羊绒布料", "silkFabric": "丝绸布料", "axe": "斧头", "pickaxe": "铁镐", "baseHealSkillBook": "基础治疗技能书", "sweepSkillBook": "横扫", "collectRing": "采集戒指", "collectRing2": "附魔采集戒指", "catTailorClothes": "毛毛裁缝服", "catTailorGloves": "毛毛裁缝手套", "woolTailorClothes": "羊毛裁缝服", "woolTailorGloves": "羊毛裁缝手套", "goblinDaggerPlus": "哥布林匕首·改", "wolfPeltArmor": "狼皮甲", "skeletonShieldPlus": "骷髅盾·强化", "trollClubPlus": "巨魔木棒·重型", "scorpionStingerSpear": "巨蝎毒矛", "guardianCoreAmulet": "守护者核心护符", "moonlightGuardianCoreAmulet": "月光守护者", "dragonScaleArmor": "龙鳞甲", "woolCoat": "羊毛衣", "woolHat": "羊毛帽", "woolGloves": "羊毛手套", "woolPants": "羊毛裤", "ironCoat": "铁甲衣", "ironHat": "铁头盔", "ironGloves": "铁护手", "ironPants": "铁护腿", "steelCoat": "钢甲衣", "steelHat": "钢头盔", "steelGloves": "钢护手", "steelPants": "钢护腿", "silverSword": "银质剑", "silverDagger": "银质匕首", "silverCoat": "银护甲", "silverHat": "银头盔", "silverGloves": "银护手", "silverPants": "银护腿", "simpleSalad": "野草沙拉", "wildFruitMix": "野果拼盘", "fishSoup": "鱼汤", "berryPie": "浆果派", "mushroomStew": "蘑菇炖汤", "catMint": "猫薄荷饼干", "catSnack": "猫咪零食", "luxuryCatFood": "豪华猫粮", "sashimiPlatter": "鲜鱼刺身拼盘", "catGiftBag": "猫猫礼袋", "luckyCatBox": "幸运猫盒", "mysteryCan": "神秘罐头", "catnipSurprise": "猫薄荷惊喜包", "meowEnergyBall": "喵能量球", "dreamFeatherBag": "梦羽袋", "woodSword": "木剑", "ironSword": "铁剑", "steelSword": "钢剑", "catFurCoat": "毛毛衣", "catFurHat": "毛毛帽", "catFurGloves": "毛毛手套", "catFurPants": "毛毛裤", "collectingBracelet": "采集手环", "fishingHat": "钓鱼帽", "miningBelt": "采矿工作服", "farmingGloves": "园艺手套", "heavyMinerGloves": "重型矿工手套", "agileGatherBoots": "灵巧采集靴", "focusedFishingCap": "钓鱼专注帽", "woodFishingRod": "木钓竿", "chefHat": "厨师帽", "ancientFishboneNecklace": "远古鱼骨项链", "moonlightPendant": "月光吊坠", "testResource": "测试资源", "forestDagger": "冰霜匕首", "snowWolfCloak": "雪狼皮披风", "iceFeatherBoots": "冰羽靴", "icePickaxe": "冰稿", "woolBurqa": "羊毛罩袍", "woolMageHat": "羊毛法师帽", "woolMageLongGloves": "羊毛法师手套", "woolMagePants": "羊毛法师裤", "silkMageBurqa": "丝质罩袍", "silkMageHat": "丝质法师帽", "silkMageLongGloves": "丝质法师手套", "silkMagePants": "丝质法师裤", "woolTightsCloth": "羊毛紧身衣", "woolDexHeadScarf": "羊毛裹头巾", "woolDexGloves": "羊毛绑带手套", "woolTightsPants": "羊毛紧身裤", "silkTightsCloth": "丝质夜行衣", "silkDexHeadScarf": "丝质裹头巾", "silkDexGloves": "丝质绑带手套", "silkTightsPants": "丝质宽松裤", "woodStaff": "木法杖", "ironDagger": "铁匕首", "moonlightStaff": "月光法杖", "mewShadowStaff": "喵影法杖", "groupShieldSkillBook": "群体护盾技能书", "silverNecklace": "银项链", "silverBracelet": "银手链", "catPotionSilverBracelet": "猫薄荷手链", "catFurCuteHat": "毛毛可爱帽", "woolCuteHat": "羊毛可爱帽", "catPawStamp": "猫爪印章", "rareCatfish": "稀有猫鱼", "mysticalKoi": "神秘锦鲤", "treasureMap": "藏宝图", "catPawFossil": "猫爪化石", "catStatue": "猫雕像", "mysteriousBell": "神秘铃铛", "ancientCatBowl": "古代猫碗", "catScroll": "猫之卷轴", "catAntiqueShard": "猫咪文物碎片", "catHairball": "猫毛球", "luckyCatCharm": "招财猫护符", "catnipGem": "猫薄荷宝石", "ancientFishBone": "远古鱼骨", "whiskerFeather": "胡须羽毛", "moonlightBell": "月光铃铛", "shell": "贝壳", "mysticalEssence": "神秘精华", "catPotion": "猫薄荷药剂", "magicScroll": "魔法卷轴", "catRelic": "猫咪圣物", "blessedBell": "祝福铃铛", "slimeGel": "史莱姆凝胶", "slimeCore": "史莱姆核心", "goblinEar": "哥布林耳朵", "goblinDagger": "哥布林匕首", "batWing": "蝙蝠翅膀", "batTooth": "蝙蝠牙", "wolfFang": "狼牙", "wolfPelt": "狼皮", "skeletonBone": "骷髅骨", "skeletonShield": "骷髅残盾", "toxicSpore": "毒孢子", "mushroomCap": "蘑菇怪帽", "lizardScale": "蜥蜴鳞片", "lizardTail": "蜥蜴尾巴", "spiritEssence": "幽灵精华", "ectoplasm": "灵质", "trollHide": "巨魔兽皮", "trollClub": "巨魔木棒", "scorpionStinger": "巨蝎毒针", "scorpionCarapace": "巨蝎甲壳", "guardianCore": "守护者核心", "ancientGear": "古代齿轮", "lavaHeart": "熔岩之心", "dragonScale": "龙鳞", "venomDagger": "剧毒匕首", "emberAegis": "余烬庇护", "iceGel": "冰霜凝胶", "frostCrystal": "霜之结晶", "snowWolfFur": "雪狼皮", "frostDagger": "冰霜匕首", "iceBomb": "冰弹", "iceBatWing": "冰蝙蝠翅膀", "snowRabbitFur": "雪兔皮", "frostEssence": "霜之精华", "snowBeastFang": "巨兽獠牙", "snowBeastHide": "巨兽皮", "frostCrown": "霜之王冠", "shadowFur": "暗影猫皮", "catShadowGem": "猫影宝石", "dungeonKey": "地牢钥匙", "ironPawArmor": "铁爪护甲", "phantomWhisker": "幻影胡须", "curseWing": "诅咒之翼", "golemCore": "猫偶核心", "witchHat": "巫术猫帽", "shadowOrb": "暗影法球", "abyssalCloak": "深渊披风", "ancestorCrown": "猫祖王冠", "starEssence": "星辉精华", "starShard": "星辰碎片", "trapParts": "陷阱零件", "starDust": "星尘", "starCrown": "星辉王冠", "starRelic": "星辉遗物", "nightEyeGem": "夜瞳宝石", "toxicFur": "剧毒皮毛", "whiskerCharm": "胡须护符", "shadowCape": "暗影披风", "rareClaw": "稀有利爪", "smokeBall": "烟雾弹", "candyBomb": "糖果炸弹", "plushFur": "毛绒绒", "ghostEssence": "幽灵精华", "loadOfamusementPark": "游乐园之王", "paradeCape": "游行披风", "empressCloak": "女皇披风" // ...如有遗漏可继续补充 }; // 运行时间 const startTime = Date.now(); // 创建主面板 const panel = document.createElement('div'); panel.id = 'moyu-helper-panel'; panel.style.cssText = ` position: fixed; top: 40px; left: 40px; min-width: 320px; max-width: 90vw; min-height: 60px; background: rgba(30, 32, 40, 0.96); color: #fff; border-radius: 12px; box-shadow: 0 4px 24px 0 rgba(0,0,0,0.25); z-index: 99999; font-size: 14px; user-select: none; transition: box-shadow 0.2s; `; // 标题栏 const titleBar = document.createElement('div'); titleBar.style.cssText = ` display: flex; align-items: center; justify-content: space-between; padding: 10px 16px 8px 16px; border-bottom: 1px solid #444; cursor: move; font-weight: bold; background: transparent; `; titleBar.innerHTML = ` <span>摸鱼放置助手 v1.0</span> <div style="display:flex;align-items:center;gap:12px;"> <span id="moyu-runtime" style="font-size:12px;color:#ffd700;"></span> <button id="moyu-collapse" style="background:none;border:none;color:#fff;font-size:18px;cursor:pointer;">−</button> </div> `; // 标签栏 const tabBar = document.createElement('div'); tabBar.style.cssText = ` display: flex; gap: 2px; padding: 0 10px; border-bottom: 1px solid #333; background: transparent; `; const tabs = [ { key: 'combat', label: '战斗统计' }, { key: 'inventory', label: '物品统计' } ]; let activeTab = 'combat'; const tabBtns = {}; tabs.forEach(tab => { const btn = document.createElement('button'); btn.textContent = tab.label; btn.style.cssText = ` flex: 1; padding: 6px 0; background: none; border: none; border-bottom: 2px solid transparent; color: #fff; font-size: 15px; cursor: pointer; transition: border-color 0.2s, color 0.2s; `; btn.addEventListener('click', () => setActiveTab(tab.key)); tabBar.appendChild(btn); tabBtns[tab.key] = btn; }); // 内容区 const content = document.createElement('div'); content.style.cssText = ` padding: 12px 16px 16px 16px; min-height: 120px; max-height: 60vh; overflow-y: auto; font-size: 14px; `; // 折叠逻辑 let isCollapsed = false; const collapseBtn = titleBar.querySelector('#moyu-collapse'); collapseBtn.addEventListener('click', toggleCollapse); function toggleCollapse() { isCollapsed = !isCollapsed; content.style.display = isCollapsed ? 'none' : ''; tabBar.style.display = isCollapsed ? 'none' : 'flex'; collapseBtn.textContent = isCollapsed ? '+' : '−'; } // 运行时间刷新 function updateRuntime() { const now = Date.now(); let diff = Math.floor((now - startTime) / 1000); const h = Math.floor(diff / 3600); diff %= 3600; const m = Math.floor(diff / 60); const s = diff % 60; document.getElementById('moyu-runtime').textContent = `已运行: ${h > 0 ? h + '小时' : ''}${m > 0 ? m + '分' : ''}${s}秒`; } setInterval(updateRuntime, 1000); setTimeout(updateRuntime, 100); // 拖动逻辑(仅标题栏可拖动) (function makeDraggable(handle, el) { let isDown = false, offsetX = 0, offsetY = 0; handle.addEventListener('mousedown', e => { isDown = true; el.style.right = 'auto'; offsetX = e.clientX - el.offsetLeft; offsetY = e.clientY - el.offsetTop; e.preventDefault(); }); document.addEventListener('mousemove', e => { if (!isDown) return; el.style.left = (e.clientX - offsetX) + 'px'; el.style.top = (e.clientY - offsetY) + 'px'; }); document.addEventListener('mouseup', () => { isDown = false; }); })(titleBar, panel); // 标签切换逻辑 function setActiveTab(key) { activeTab = key; for (const k in tabBtns) { tabBtns[k].style.borderBottomColor = (k === key) ? '#ffd700' : 'transparent'; tabBtns[k].style.color = (k === key) ? '#ffd700' : '#fff'; } renderContent(); // 切换后刷新对应数据 if (activeTab === 'combat') { setTimeout(() => { // 需要有 playerUuids 和 memberMap 数据 if (typeof updateDpsPanel === 'function' && window.playerUuids && window.memberMap) { updateDpsPanel(window.playerUuids, window.memberMap); } }, 0); } else if (activeTab === 'inventory') { setTimeout(() => { if (typeof updateInventoryPanel === 'function') { updateInventoryPanel(); } }, 0); } } // 内容渲染 function renderContent() { if (activeTab === 'combat') { content.innerHTML = renderCombatPanel(); } else if (activeTab === 'inventory') { content.innerHTML = renderInventoryPanel(); } } // 战斗统计内容(可根据实际数据结构填充) function renderCombatPanel() { return ` <table style="width:100%;border-collapse:collapse;font-size:13px;"> <thead> <tr style="color:#ffd700;"> <th style="text-align:left;">玩家</th> <th>输出效率</th> <th>治疗效率</th> <th>总伤害</th> <th>总治疗</th> </tr> </thead> <tbody id="dpsTableBody"> <!-- 动态填充 --> </tbody> </table> <div style="margin-top:12px;"> <div style="font-weight:bold;margin-bottom:4px;">战斗日志</div> <ul id="battleLog" style="max-height:120px;overflow-y:auto;padding-left:18px;font-size:13px;"></ul> </div> `; } // 物品统计内容(可根据实际数据结构填充) function renderInventoryPanel() { return ` <table style="width:100%;border-collapse:collapse;font-size:13px;"> <thead> <tr style="color:#ffd700;"> <th style="text-align:left;">物品</th> <th>数量</th> </tr> </thead> <tbody id="inventoryTableBody"> <!-- 动态填充 --> </tbody> </table> `; } // 组装面板 panel.appendChild(titleBar); panel.appendChild(tabBar); panel.appendChild(content); document.body.appendChild(panel); // 初始化 setActiveTab('combat'); // 你可以在此处挂载实际的统计数据渲染逻辑 // 例如:动态填充dpsTableBody、battleLog、inventoryTableBody等 // 判断压缩格式 function detectCompression(buf) { const b = new Uint8Array(buf); if (b.length >= 2) { if (b[0] === 0x1f && b[1] === 0x8b) return 'gzip'; if (b[0] === 0x78 && (((b[0] << 8) | b[1]) % 31) === 0) return 'zlib'; } return 'deflate'; } // WebSocket 拦截与解压 const NativeWS = window.WebSocket; window.WebSocket = function (url, protocols) { const ws = protocols ? new NativeWS(url, protocols) : new NativeWS(url); const originalSend = ws.send; ws.send = function (data) { console.log('[WS 发送]', data); return originalSend.call(this, data); }; let messageID = 0; let lastCmd = null; ws.addEventListener('message', ev => { messageID++; let obj = null; let text = null; if (ev.data instanceof ArrayBuffer) { const format = detectCompression(ev.data); switch (format) { case 'gzip': text = pako.ungzip(new Uint8Array(ev.data), { to: 'string' }); break; case 'zlib': text = pako.inflate(new Uint8Array(ev.data), { to: 'string' }); break; default: text = pako.inflateRaw(new Uint8Array(ev.data), { to: 'string' }); } obj = JSON.parse(text); } else { text = ev.data if (typeof text === 'string' && /^\d+-/.test(text)) { const idx = text.indexOf('-'); if (idx !== -1) { try { const arr = JSON.parse(text.slice(idx + 1)); if (Array.isArray(arr) && typeof arr[0] === 'string') { lastCmd = arr[0]; } } catch (e) { lastCmd = null; console.log(e) } } return; } console.log(`[WS 未处理数据]`, text); } // 根据 lastCmd 分发处理 if (obj && lastCmd) { if (lastCmd.startsWith('inventory:increase:success')) { updateInventory(obj.data); } else if (lastCmd.startsWith('battle:fullInfo:success')) { // battle:fullInfo:success、battle:round:success等 obj = obj.data if (obj && obj.battleInfo && obj.thisRoundAction) { const battle = obj.battleInfo; const action = obj.thisRoundAction; const members = battle.members || []; const playerUuids = (battle.groups?.player) || []; const memberMap = Object.fromEntries( members.map(m => [m.uuid, m]) ); const srcUuid = action.sourceUnitUuid; const dmgObj = action.damage || {}; // { uuid: amount, … } const healObj = action.heal || {}; // { uuid: amount, … } const skill = action.castSkillId || '未知技能'; const targets = action.targetUnitUuidList || []; // 只统计玩家组 if (playerUuids.includes(srcUuid)) { let hasAction = false; for (const [tUuid, amt] of Object.entries(dmgObj)) { damageAccum.set(srcUuid, (damageAccum.get(srcUuid) || 0) + amt); hasAction = true; } for (const [tUuid, amt] of Object.entries(healObj)) { healAccum.set(srcUuid, (healAccum.get(srcUuid) || 0) + amt); hasAction = true; } if (hasAction) { actionCount.set(srcUuid, (actionCount.get(srcUuid) || 0) + 1); } window.playerUuids = playerUuids; window.memberMap = memberMap; updateDpsPanel(playerUuids, memberMap); } // 战斗日志输出 if (combatLogEnabled) { let logLines = []; if (playerUuids.includes(srcUuid) || targets.some(t => playerUuids.includes(t))) { const srcName = memberMap[srcUuid]?.name || srcUuid; for (const tUuid of targets) { const tName = memberMap[tUuid]?.name || tUuid; const dmg = dmgObj[tUuid] || 0; const heal = healObj[tUuid] || 0; const skillName = skillNames[skill] || skill; if (dmg > 0) logLines.push(`🗡️ <b>${srcName}</b> 用 <b>${skillName}</b> 对 <b>${tName}</b> 造成 <span style='color:#ff7675;'>${dmg}</span> 伤害`); if (heal > 0) logLines.push(`💚 <b>${srcName}</b> 用 <b>${skillName}</b> 治疗 <b>${tName}</b> <span style='color:#00e676;'>${heal}</span> 生命`); } } if (logLines.length) addBattleLog(logLines); } } } else { console.log(`[WS 未处理指令]`, lastCmd); console.log(`[WS 未处理对象]`, obj); } // 其他指令可在此扩展 lastCmd = null; } else { // 没有指令时,打印日志 } }); return ws; }; window.WebSocket.prototype = NativeWS.prototype; Object.getOwnPropertyNames(NativeWS).forEach(prop => { if (!(prop in window.WebSocket)) { window.WebSocket[prop] = NativeWS[prop]; } }); console.log('✅ MoYuIdleHelper 已启动'); // 新增:DPS面板渲染函数 function updateDpsPanel(playerUuids, memberMap) { const tbody = document.querySelector('#dpsTableBody'); if (!tbody) return; if (!playerUuids || !memberMap || playerUuids.length === 0) { tbody.innerHTML = `<tr><td colspan="5" style="text-align:center;color:#888;">暂无数据</td></tr>`; return; } // 收集玩家数据 const rows = playerUuids.map(uuid => { const total = damageAccum.get(uuid) || 0; const heal = healAccum.get(uuid) || 0; const cnt = actionCount.get(uuid) || 1; const name = memberMap[uuid]?.name || uuid; const dps = Math.round(total / cnt); // 输出效率 const hps = Math.round(heal / cnt); // 治疗效率 return { name, dps, hps, total, heal }; }); // 按输出效率降序 rows.sort((a, b) => b.dps - a.dps); tbody.innerHTML = rows.map(r => `<tr><td>${r.name}</td><td style='text-align:right;'>${r.dps}</td><td style='text-align:right;'>${r.hps}</td><td style='text-align:right;'>${r.total}</td><td style='text-align:right;'>${r.heal}</td></tr>`).join(''); } // 新增:战斗日志管理 const logList = []; // 日志自动滚动控制 let logAutoScroll = true; // 日志面板增加滚动条美化和事件监听 const logPanelCss = document.createElement('style'); logPanelCss.innerHTML = ` #logPanel::-webkit-scrollbar { width: 8px; background: transparent; } #logPanel::-webkit-scrollbar-thumb { background: linear-gradient(120deg, #444 30%, #888 100%); border-radius: 6px; } #logPanel:hover::-webkit-scrollbar-thumb { background: linear-gradient(120deg, #666 30%, #aaa 100%); } #logPanel { scrollbar-width: thin; scrollbar-color: #888 #222; } `; document.head.appendChild(logPanelCss); setTimeout(() => { const logPanel = document.getElementById('battleLog'); if (logPanel) { logPanel.addEventListener('scroll', function () { // 判断是否在底部 const atBottom = logPanel.scrollTop + logPanel.clientHeight >= logPanel.scrollHeight - 2; logAutoScroll = atBottom; }); logPanel.addEventListener('mouseenter', () => { logAutoScroll = false; }); logPanel.addEventListener('mouseleave', () => { // 如果离开时已在底部,则恢复自动滚动 const atBottom = logPanel.scrollTop + logPanel.clientHeight >= logPanel.scrollHeight - 2; logAutoScroll = atBottom; }); } }, 500); function addBattleLog(lines) { const ul = document.getElementById('battleLog'); if (!ul) return; for (const line of lines) { logList.push(line); } // 限制日志条数 while (logList.length > 200) logList.shift(); ul.innerHTML = logList.map(l => `<li style='margin-bottom:2px;'>${l}</li>`).join(''); // 自动滚动到底部(仅在自动滚动开启时) const logPanel = document.getElementById('battleLog'); if (logPanel && logAutoScroll) { logPanel.scrollTop = logPanel.scrollHeight; } } // 更新物品变动统计 function updateInventory(newInventory) { for (const [itemId, itemData] of Object.entries(newInventory)) { const oldCount = inventory[itemId] || 0; const newCount = itemData.count; inventory[itemId] = oldCount + newCount; } updateInventoryPanel(); } // 更新物品变动面板 function updateInventoryPanel() { const tbody = document.querySelector('#inventoryTableBody'); if (!tbody) return; tbody.innerHTML = ''; const sortedItems = Object.entries(inventory) .filter(([itemId, total]) => total > 0) .sort((a, b) => b[1] - a[1]); for (const [itemId, total] of sortedItems) { const row = document.createElement('tr'); row.innerHTML = ` <td>${itemIdNameMap[itemId] || itemId}</td> <td style="text-align:right;">${total}</td> `; tbody.appendChild(row); } } })();