Idle Infinity - Equipment

Idle Infinity

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         Idle Infinity - Equipment
// @namespace    http://dazzyd.org/
// @version      0.5.2
// @description  Idle Infinity
// @author       Dazzy Ding
// @license      MIT
// @grant        GM_addStyle
// @match        https://www.idleinfinity.cn/Equipment/Query*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=idleinfinity.cn
// ==/UserScript==


function getStore(key, defualt) {
  return Object.assign({}, defualt, {
    load() {
      const saved = JSON.parse(localStorage.getItem(key) || "{}")
      for (const [key, val] of Object.entries(saved)) {
        this[key] = val
      }
    },
    save() {
      localStorage.setItem(key, JSON.stringify(this))
    },
  })
}
const equipStore = getStore(`dd_ui_equip`, { ancient: {} })
const configStore = getStore(`dd_ui_config`, { tips: {} })

const allSkills = ["牺牲", "白热", "复仇", "裁决", "圣光闪现", "圣光道标", "奉献", "忏悔", "祝福之锤", "力量光环", "专注光环", "狂热光环", "反抗光环", "荆棘光环", "审判光环", "祈祷光环", "拯救光环", "冥想光环", "火球", "烈焰风暴", "陨石", "静态力场", "连锁闪电", "雷云风暴", "冰弹", "霜之新星", "冰封球", "暖气", "强化", "支配火焰", "激活", "法力护盾", "支配闪电", "冰封装甲", "时间延缓", "支配冰冷", "顺劈斩", "狂乱", "旋风斩", "猛击", "蓄力攻击", "处决", "战斗怒吼", "浴血", "武器大师", "撕裂", "眩晕攻击", "雷霆一击", "嘲讽", "钢铁之躯", "命令怒吼", "自然抵抗", "重整旗鼓", "生存本能", "震荡射击", "穿刺箭", "箭雨", "毒蛇钉刺", "爆裂箭", "急冻箭", "多重箭", "闪电攻击", "闪电之怒", "内视", "强击光环", "致命攻击", "草本治疗", "诱捕", "刺穿", "专心", "回避", "女武神", "惩击", "神圣之火", "苦修", "恢复", "治疗之环", "救赎光环", "神圣护盾", "驱散", "痛苦压制", "暗影箭", "暗影之触", "暗影尖刺", "暗影魔", "灵魂吸取", "精神灼烧", "虚弱诅咒", "腐蚀光环", "暗影行者", "背刺", "刀扇", "魔影斗篷", "影舞", "出血", "绞杀", "切割", "伺机待发", "武学艺术", "淬毒匕首", "毒牙", "毒雾", "毒伤", "能量消解", "毒素蒸馏", "麻痹网", "闪光粉", "法术反噬", "风歌", "风切", "风怒", "狂野扑击", "风暴打击", "先祖之力", "幽魂步", "噬血", "复生", "灼热图腾", "地震术", "地心守卫", "闪电箭", "怒雷", "元素之怒", "闪电护盾", "治疗链", "大地之力", "牙", "骨矛", "白骨之魂", "生命分流", "衰老", "白骨装甲", "骨牢", "骨刺收割", "死亡主宰", "骷髅复生", "骷髅法师", "粘土石魔", "重生", "致伤诅咒", "支配骷髅", "献祭", "坟墓呼唤", "生生不息", "狼人变化", "狂犬病", "狂怒", "饥饿", "焰爪", "野性狂暴", "熊人变化", "撞槌", "大地震击", "洞察光环", "火山爆发", "自然之力", "小旋风", "狂风鞭笞", "龙卷风", "飓风装甲", "橡木智者", "狼獾之心", "猛虎击", "灵蛇击", "凤凰击", "烈焰拳", "雷电爪", "寒冰刃", "龙爪", "神龙摆尾", "飞龙在天", "惩戒真言", "治愈真言", "定罪真言", "暗言术•痛", "暗言术•破", "暗言术•灭", "真言术•慰", "真言术•障", "真言术•耀", "刃之守卫", "刀刃护盾", "爆炸陷阱", "火焰爆震", "狱火守卫", "稳固陷阱", "电能守卫", "雷电守卫", "亡者守卫", "猎人标记", "瞄准射击", "炮轰", "支配野兽", "灵魂链接", "召唤乌鸦", "狩猎呼唤", "森林狼", "召唤灰熊", "暗影爆发", "剥皮者", "伤害加深", "攻击反噬", "致死", "暴风雪", "降低抵抗", "圣光弹", "死亡射线", "微暗灵视", "法力燃烧", "闪电新星", "剧毒新星", "棱光射线", "残废", "升腾", "空间压缩", "分裂之眼", "灼热射线", "冻结射线", "剧毒射线", "激光射线", "平静", "定罪", "圣光普照", "虚化领域", "黑洞", "剧毒之种", "燃烧之种", "法力真空", "黑暗之种", "禁魔领域", "灵魂印记", "灵魂之刺", "灵魂剥离", "火焰之种", "律令.死亡", "虚化", "天罚", "冰风暴", "火风暴", "晶化之壳", "熔岩爆裂", "瘟疫诅咒", "地狱之牛", "火焰新星", "尸体爆炸", "骷髅射手", "啃咬", "感染", "毒刺", "烈焰长矛", "裂地斩", "致命绞杀", "冰霜之刺", "魔法箭", "多重施法", "狂战士", "庇护光环", "冰冻光环", "圣火光环", "冲击光环", "生命光环", "野性光环", "传送", "神恩", "愤怒光环", "唤醒光环", "恐惧光环", "狂暴之心", "灼烧光环", "束缚光环", "辉煌光环", "消散", "净化光环", "冰冷转换", "火焰转换", "闪电转换", "毒素转换", "魔法转换", "物理转换", "抗火光环"]
const tipRules = [
  { name: "几率施放", regexp: /^(.).+时有(\d+)%几率施放Lv(\d+)(.+)/, format: (a, b, c, d) => `${a}${b}%${d}${c}`, style: "skill", default: false },
  { name: "职业技能", regexp: /^\+(\d+) .{0,2}(.{2})技能/, format: (a, b) => `${a}${b}`, style: "skill", default: true },
  { name: "获得技能", regexp: new RegExp(`^\\+(\\d+) (${allSkills.join('|')})$`), format: (a, b) => `${a}${b}`, style: "skill", default: true },
  { name: "赋予技能", regexp: /^赋予Lv(\d+)(.+)/, format: (a, b) => `${a}${b}`, style: "magic", default: true },
  { name: "召唤数量", regexp: /^\+(\d+) (.{1,6})最大召唤数量/, format: (a, b) => `${a}${b}`, style: "magic", default: true },
  { name: "增强伤害", regexp: /^\+(\d+)\% 增强伤害$/, format: a => `${a}ed`, style: "physical", default: true },
  { name: "攻击速度", regexp: /^攻击速度提升 (\d+)\%/, format: a => `${a}ias`, style: "physical", default: true },
  { name: "施法速度", regexp: /^施法速度提升 (\d+)\%/, format: a => `${a}fcr`, style: "magic", default: true },
  { name: "魔法装备", regexp: /^\+(\d+)\% 更佳的机会取得魔法装备/, format: a => `${a}mf`, style: "state", default: true },
  { name: "额外金币", regexp: /^\+(\d+)\% 额外金币从怪物身上取得/, format: a => `${a}gf`, style: "lightning", default: true },
  { name: "元素抗性", regexp: /^元素抗性 \+(\d+)\%/, format: a => `${a}res`, style: "skill", default: true },
  { name: "抗火", regexp: /^抗火 \+(\d+)/, format: a => `${a}f`, style: "fire", default: true },
  { name: "抗寒", regexp: /^抗寒 \+(\d+)/, format: a => `${a}c`, style: "cold", default: true },
  { name: "抗闪电", regexp: /^抗闪电 \+(\d+)/, format: a => `${a}l`, style: "lightning", default: true },
  { name: "抗毒", regexp: /^抗毒 \+(\d+)/, format: a => `${a}p`, style: "poison", default: true },
  { name: "凹槽", regexp: /^凹槽(\(0\/\d+\))/, format: a => `${a}`, style: "", default: true },
  { name: "无法装备", regexp: /(无法装备)$/, format: () => `❌`, style: "", default: false },
  { name: "双手武器", regexp: /^双手伤害:/, format: () => `2H`, style: "", default: false },
  { name: "掉落等级", regexp: /^掉落等级:(\d+)/, format: a => `dlv${a}`, style: "", default: false },
  { name: "需要等级", regexp: /^需要等级:(\d+)/, format: a => `rlv${a}`, style: "", default: false },
  { name: "已有太古", regexp: /^(无形)?(.+?)\(\d+\)$/, format: ancient, style: "", default: false, first: true },
]

function ancient(_, name) {
  if (equipStore.ancient[name]) {
    return `⭕️`
  }
  if (name.endsWith("★")) {
    name = name.substring(0, name.length - 1)
    if (equipStore.ancient[name] !== 1) {
      equipStore.ancient[name] = 1
      equipStore.save()
    }
  }
}


function createElementByHTML(html) {
  const template = document.createElement('template')
  template.innerHTML = html.trim()
  return template.content.firstChild
}

function parseContent(id, node) {
  if (node == null) {
    return
  }

  // 判断content是否有内容
  const lines = node.querySelectorAll('p')
  if (lines.length === 0) {
    return
  }
  // 提取content内容,拼接成一个字符串
  const content_lines = []
  for (const line of lines) {
    if (line.classList.contains('divider')) {
      break
    }
    content_lines.push(line.innerText.replace(/\s{2,}/g, ''))
  }
  const content = content_lines.join('|')

  equipStore[id] = content
  equipStore.save()
}

function addTips(id, equipNode) {
  if (equipStore[id] == null) {
    return
  }
  if (equipNode == null) {
    equipNode = document.querySelector(`.equip-name[data-id="${id}"]`)
  }
  if (equipNode.parentNode.querySelector(".tips") != null) {
    return
  }

  const container = document.createElement('span')
  container.classList.add("tips")
  function addChild(text, className) {
    const span = document.createElement('span')
    span.innerText = text
    if (className != null && className != "") {
      span.classList.add(className)
    }
    container.append(span)
  }

  // 通过regexp提取信息
  const contents = equipStore[id].split('|')
    .map(content => content.replace(/(\d+-\d+)/g, ''))
  for (const rule of tipRules) {
    const enabled = configStore.tips[rule.name] != null ? configStore.tips[rule.name] : rule.default
    if (!enabled) continue

    for (const content of contents) {
      const match = content.match(rule.regexp)
      if (match == null) continue
      const tip = rule.format(...match.slice(1))
      if (tip != null) {
        addChild(' ' + tip, rule.style)
      }
      if (rule.first) {
        break
      }
    }
  }
  if (container.childElementCount === 0) {
    addChild(`-`)
  }

  equipNode.after(container)
}


setTimeout(() => {
  equipStore.load()
  configStore.load()

  // 先从缓存中渲染
  for (const node of document.querySelectorAll(".equip-name[data-id]")) {
    const id = node.attributes['data-id'].value
    addTips(id, node)
  }
  // 解析已装备
  for (const contentNode of document.querySelectorAll(".panel .equip-content")) {
    if (contentNode.childElementCount === 0) {
      continue
    }
    const equipNode = contentNode.previousElementSibling.getElementsByClassName('equip-name')[0]
    const id = equipNode.attributes['data-id'].value
    parseContent(id, contentNode)
    addTips(id, equipNode)
  }
  // 解析背包中
  const equipObserver = new MutationObserver((mutationList, _) => {
    for (const mutation of mutationList) {
      if (mutation.type === 'childList') {
        const node = mutation.target
        const id = node.attributes['data-id'].value
        parseContent(id, node)
        addTips(id)
        return
      }
    }
  })
  for (const node of document.querySelectorAll(".equip-content-container .equip-content")) {
    equipObserver.observe(node, { childList: true })
  }

  // 增加CSS,鼠标移上去隐藏标记,避免内容过长换行
  GM_addStyle(`.equip-container > p:hover > .tips { display: none; }`)

  // 网页上增加配置标记的功能
  const configNode = createElementByHTML(`
    <div class="btn-group">
      <button type="button" class="btn btn-default btn-xs dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
        小尾巴
        <span class="caret"></span>
      </button>
      <ul class="dropdown-menu">
        ${tipRules.map(rule => `<li><a class="base">
          <input type="checkbox" name="${rule.name}" ${(configStore.tips[rule.name] != null ? configStore.tips[rule.name] : rule.default) ? "checked" : ""}>
          ${rule.name}
        </a></li>`).join('')}
      </ul>
    </div>
  `)
  document.querySelector(".panel-heading > .pull-right").prepend(configNode)
  for (const node of configNode.querySelectorAll("input")) {
    node.addEventListener('change', (event) => {
      const node = event.target
      console.log(`on change ${node.name}`)
      configStore.tips[node.name] = node.checked
      configStore.save()
    })
  }
}, 0)