wsmud_api

使用于 Tampermonkey 的武神传说脚本的前置 API 库

Install this script?
Author's suggested script

You may also like wsmud_login.

Install this script

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

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

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

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

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

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         wsmud_api
// @namespace    com.wsmud
// @version      0.0.9
// @description  使用于 Tampermonkey 的武神传说脚本的前置 API 库
// @author       sq
// @date         2020/08/24
// @modified     2020/08/30
// @license      MIT
// @match        http://*.wsmud.com/*
// @exclude      http://*.wsmud.com/news/*
// @exclude      http://*.wsmud.com/pay.html
// @homepage     https://greasyfork.org/zh-CN/scripts/409901
// @require      https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.min.js
// @run-at       document-start
// @grant        unsafeWindow
// @grant        GM_addStyle
// ==/UserScript==

(function() {

'use strict'

if (!unsafeWindow.WebSocket) return

// GM_info.script.version

unsafeWindow.console.green = function(log) {
  console.log(`%c${log}`, 'color:green')
}
unsafeWindow.console.orange = function(log) {
  console.log(`%c${log}`, 'color:orange')
}
unsafeWindow.console.red = function(log) {
  console.log(`%c${log}`, 'color:red')
}
console.green = unsafeWindow.console.green
console.orange = unsafeWindow.console.orange
console.red = unsafeWindow.console.red

const RegExpListForColorValue = [
  /\s/,
  /^<wht>\S+<\/wht>$/i,
  /^<hig>\S+<\/hig>$/i,
  /^<hic>\S+<\/hic>$/i,
  /^<hiy>\S+<\/hiy>$/i,
  /^<hiz>\S+<\/hiz>$/i,
  /^<hio>\S+<\/hio>$/i,
  /^<(hir|ord)>\S+<\/(hir|ord)>$/i,
  /^\S+$/,
]
const RegExpListForBaseSkillSort = [
  /\s/,
  /^force$/,
  /^unarmed$/,
  /^dodge$/,
  /^parry$/,
  /^blade$/,
  /^sword$/,
  /^club$/,
  /^staff$/,
  /^whip$/,
  /^throwing$/,
  /^literate$/,
  /^\S+$/,
]
const RegExpListForPackSort = [
  /\s/,
  /师门补给包/, /背包扩充石/, /小箱子/, /随从礼包/,
  /^<hig>玄晶<\/hig>$/i,
  /^<hio>元晶<\/hio>$/i, /^<hio>帝魄碎片<\/hio>$/i,
  /^<(hir|ord)>神魂碎片<\/(hir|ord)>$/i,
  /^<hio>武道<\/hio>$/i, /^<hio>武道残页<\/hio>$/i,
  /^<hic>扫荡符<\/hic>$/i, /^<hiy>天师符<\/hiy>$/i,
  /洗髓丹/, /叛师符/,
  /^<hio>养精丹<\/hio>$/i, /^<hiz>养精丹<\/hiz>$/i,
  /^<hiy>养精丹<\/hiy>$/i, /^<hic>养精丹<\/hic>$/i, /^<hig>养精丹<\/hig>$/i,
  /^<hio>培元丹<\/hio>$/i, /^<hiz>培元丹<\/hiz>$/i,
  /^<hiy>培元丹<\/hiy>$/i, /^<hic>培元丹<\/hic>$/i, /^<hig>培元丹<\/hig>$/i,
  /^<hio>喜宴\S+<\/hio>$/i, /^<hiz>喜宴\S+<\/hiz>$/i,
  /^<hiy>喜宴\S+<\/hiy>$/i, /^<hic>喜宴\S+<\/hic>$/i, /^<hig>喜宴\S+<\/hig>$/i,
  /^<hio>师门令牌<\/hio>$/i, /^<hiz>师门令牌<\/hiz>$/i,
  /^<hiy>师门令牌<\/hiy>$/i, /^<hic>师门令牌<\/hic>$/i, /^<hig>师门令牌<\/hig>$/i,
  /^<ord>\S+秘籍<\/ord>$/i, /^<ord>\S+残页<\/ord>$/i,
  /^<hio>攻击之石<\/hio>$/i, /^<hio>命中之石<\/hio>$/i,
  /^<hio>躲闪之石<\/hio>$/i, /^<hio>气血之石<\/hio>$/i,
  /^<hio>\S+秘籍<\/hio>$/i, /^<hiz>\S+秘籍<\/hiz>$/i,
  /^<hiy>\S+秘籍<\/hiy>$/i, /^<hic>\S+秘籍<\/hic>$/i, /^<hig>\S+秘籍<\/hig>$/i,
  /^<hio>\S+残页<\/hio>$/i, /^<hiz>\S+残页<\/hiz>$/i,
  /^<hiy>\S+残页<\/hiy>$/i, /^<hic>\S+残页<\/hic>$/i, /^<hig>\S+残页<\/hig>$/i,
  /^<hiz>\S*红宝石<\/hiz>$/i, /^<hiy>\S*红宝石<\/hiy>$/i, /^<hic>\S*红宝石<\/hic>$/i, /^<hig>\S*红宝石<\/hig>$/i,
  /^<hiz>\S*绿宝石<\/hiz>$/i, /^<hiy>\S*绿宝石<\/hiy>$/i, /^<hic>\S*绿宝石<\/hic>$/i, /^<hig>\S*绿宝石<\/hig>$/i,
  /^<hiz>\S*蓝宝石<\/hiz>$/i, /^<hiy>\S*蓝宝石<\/hiy>$/i, /^<hic>\S*蓝宝石<\/hic>$/i, /^<hig>\S*蓝宝石<\/hig>$/i,
  /^<hiz>\S*黄宝石<\/hiz>$/i, /^<hiy>\S*黄宝石<\/hiy>$/i, /^<hic>\S*黄宝石<\/hic>$/i, /^<hig>\S*黄宝石<\/hig>$/i,
  /^<hio>聚气丹<\/hio>$/i, /^<hiz>聚气丹<\/hiz>$/i,
  /^<hiy>聚气丹<\/hiy>$/i, /^<hic>聚气丹<\/hic>$/i, /^<hig>聚气丹<\/hig>$/i,
  /^<hio>突破丹<\/hio>$/i, /^<hiz>突破丹<\/hiz>$/i,
  /^<hiy>突破丹<\/hiy>$/i, /^<hic>突破丹<\/hic>$/i, /^<hig>突破丹<\/hig>$/i,
  /鲤鱼/, /草鱼/, /鲢鱼/,
  /鲮鱼/, /鳊鱼/, /鲂鱼/,
  /黄金鳉/, /黄颡鱼/, /太湖银鱼/,
  /虹鳟/, /孔雀鱼/, /反天刀/,
  /银龙鱼/, /黑龙鱼/, /罗汉鱼/,
  /巨骨舌鱼/, /七星刀鱼/, /帝王老虎魟/,
  /当归/, /芦荟/, /山楂叶/,
  /柴胡/, /金银花/, /石楠叶/,
  /茯苓/, /沉香/, /熟地黄/,
  /九香虫/, /络石藤/, /冬虫夏草/,
  /人参/, /何首乌/, /凌霄花/,
  /灵芝/, /天仙藤/, /盘龙参/,
  /^<wht>\S+秘籍<\/wht>$/i,
  /四十二章经一/, /四十二章经二/, /四十二章经三/, /四十二章经四/,
  /四十二章经五/, /四十二章经六/, /四十二章经七/, /四十二章经八/,
  /^<wht>欠条<\/wht>$/i,
  /^<wht>高级皮毛<\/wht>$/i,
  /^<wht>阎基的头颅<\/wht>$/i,
  /^<hig>熊胆<\/hig>$/i,
  /^<hig>朱果<\/hig>$/i,
  /^<hig>潜灵果<\/hig>$/i,
  /^<hiy>补签卡<\/hiy>$/i,
  /^<hiy>玫瑰花<\/hiy>$/i,
  /^<hiy>蒙古密函<\/hiy>$/i,
  /^<hiz>技能重置卡<\/hiz>$/i,
  /^<hio>粽子<\/hio>$/i,
  /^<hio>顿悟丹<\/hio>$/i,
  /铁镐/, /钓鱼竿/, /药王神篇/,
  /^\S+$/,
]

class Api {
  constructor() {
    this.roles = this.getValue('roles')
    this.id = String()
    this.name = String()
    this.state = String()
    this.skills = Array()
    this.skilllimit = Number()
    this.packs = Array()
    this.packlimit = Number()
    this.money = Number()
    this.eqs = Array(11)
    this.performs = Array()
    this.prop = Object()
    this.room = Object()
    this.damage = Object()

    this.monitors = Object()
    this.commandQueue = Array()
    this.commandState = false
  }
  set roles(value) {
    if (value instanceof Array) {
      value.sort((a, b) => a.sort - b.sort)
      value.forEach((item, index) => {
        if (item.server) item.sort = index + 1
        else item.sort = 9999
      })
      this._roles = value
    } else {
      this._roles = []
    }
    this.setValue('roles', this._roles)
  }
  get roles() { return this._roles }

  set hp(value) { this.prop.hp = value }
  get hp() { return this.prop.hp }
  set hpmax(value) { this.prop.max_hp = value }
  get hpmax() { return this.prop.max_hp }
  set mp(value) { this.prop.mp = value }
  get mp() { return this.prop.mp }
  set mpmax(value) { this.prop.max_mp = value }
  get mpmax() { return this.prop.max_mp }
  set mplimit(value) { this.prop.limit_mp = value }
  get mplimit() { return this.prop.limit_mp }
  set jy(value) { this.prop.exp = value }
  get jy() { return this.prop.exp }
  set qn(value) { this.prop.pot = value }
  get qn() { return this.prop.pot }
  get wx1() { return this.prop.int }
  get wx2() { return this.prop.int_add }
  get xxxl() { return parseInt(this.prop.study_per) }
  get lxxl() { return parseInt(this.prop.lianxi_per) }

  ondata(data) {
    const type = data.type === 'dialog' ? data.dialog : data.type
    if (this.monitors[type]) {
      Object.keys(this.monitors[type]).forEach(name => {
        const callback = this.monitors[type][name]
        callback(data)
      })
    }
    const data2event = function(data) {
      if (!data.type) return
      if (data.type === 'text') return { data: data.text }
      else return { data: JSON.stringify(data) }
    }
    const event = data2event(data)
    if (event) this._onmessage(event)
  }
  ontext(text) {
    this.ondata({ type: 'text', text})
  }

  send(...args) {
    // 分割含有英文逗号的字符串
    args.forEach((item, index) => {
      if (typeof item === 'string' && /,/.test(item) && /^(?!setting)/.test(item)) args[index] = item.split(',')
    })
    // 加入指令队列
    this.commandQueue.push(...args.flat(Infinity))
    // 开启消息队列
    if (!this.commandState) {
      this.commandState = true
      this.sendloop(0)
    }
  }
  sendloop(delay = 256) {
    if (!this._websocket || !this.commandState || this.commandQueue.length === 0) {
      this.commandState = false
      return
    }
    // 取出首位元素
    const cmd = this.commandQueue.splice(0, 1)[0]
    // 判断是否数字
    const number = Number(cmd)
    if (!isNaN(number)) {
      console.orange(`CommandQueue: Wait for ${number}ms.`)
      this.sendloop(number)
      return
    }
    // 判断是否字符串
    if (typeof cmd === 'string') {
      setTimeout(() => {
        console.orange(cmd)
        this._websocket.send(cmd)
        this.sendloop()
      }, delay)
      return
    }
    // 判断是否函数
    if (typeof cmd === 'function') {
      setTimeout(() => {
        cmd()
        this.sendloop()
      }, delay)
      return
    }
  }
  clearCommandQueue() {
    this.commandQueue.splice(0)
  }

  addMonitor(type, name, callback) {
    if (!type || !name || typeof callback !== 'function') return
    if (!this.monitors[type]) this.monitors[type] = {}
    this.monitors[type][name] = callback.bind(this)
    console.green(`AddMonitor: [${type}]${name}`)
  }
  removeMonitor(type, name) {
    if (!type || !name) return
    delete this.monitors[type][name]
    console.red(`RemoveMonitor: type = ${type}; name = ${name};`)
    api.roles = new Object(api.roles)
  }
  addStyle(css) {
    GM_addStyle(css)
  }
  cookie() {
    const cookies = document.cookie.split(';').reduce((accumulator, currentValue) => {
      const i = currentValue.indexOf('=')
      const name = currentValue.substr(0, i).trim()
      const value = currentValue.substr(i + 1)
      accumulator[name] = value
      return accumulator
    }, {})
    const setCookie = (name, value) => document.cookie = name + '=' + value
    const deleteCookie = name => document.cookie = name + '=; expires=Thu, 01 Jan 1970 00:00:01 GMT;'
    return new Proxy(cookies, {
      set: (target, name, value) => {
        setCookie(name, value)
        return Reflect.set(target, name, value)
      },
      deleteProperty: (target, name) => {
        deleteCookie(name)
        return Reflect.deleteProperty(target, name)
      },
    })
  }
  setValue(key, value) {
    localStorage.setItem(key, JSON.stringify(value))
  }
  getValue(key) {
    return JSON.parse(localStorage.getItem(key))
  }
  deleteValue(key) {
    localStorage.removeItem(key)
  }
}

unsafeWindow.Vue = Vue
unsafeWindow.api = Vue.observable(new Api())

unsafeWindow.WebSocket = function (uri) {
  api._websocket = new WebSocket(uri)
}
unsafeWindow.WebSocket.prototype = {
  set onopen(fn) {
    api._websocket.onopen = fn
  },
  set onclose(fn) {
    api._websocket.onclose = fn
  },
  set onerror(fn) {
    api._websocket.onerror = fn
  },
  set onmessage(fn) {
    const event2data = function(event) {
      const data = event.data
      if (typeof data === 'string' && data[0] === '{') {
        try {
          return new Function('return ' + data)()
        } catch (error) {
          console.red(error)
          console.red(data)
        }
      }
      return { 'type': 'text', 'text': data }
    }
    api._onmessage = fn
    api._websocket.onmessage = function(event) {
      const data = event2data(event)
      api.ondata(data)
    }
  },
  get readyState() {
    return api._websocket.readyState
  },
  send: function(command) {
    api.send(command)
  },
}

api.addMonitor('roles', 'RoleList', function(data) {
  if (!(data.roles instanceof Array)) return
  data.roles.forEach(item => {
    const { id, name, title } = item
    const index = this.roles.findIndex(role => role.id === id)
    if (index === -1) {
      this.roles.push({ id, name, title, sort: 9999 })
    } else {
      this.roles[index].name = name
      this.roles[index].title = title
    }
  })
  this.roles = this.roles.slice(0)
})
api.addMonitor('login', 'Login', function(data) {
  const id = data.id
  if (this.id || !id) return
  this.id = id
  const index = this.roles.findIndex(role => role.id === id)
  this.name = this.roles[index].name
  const cookie = this.cookie()
  this.roles[index].u = cookie.u
  this.roles[index].p = cookie.p
  this.roles[index].s = cookie.s
  this.roles[index].server = ['一区', '二区', '三区', '四区', '测试'][cookie.s]
  this.roles = this.roles.slice(0)

  setTimeout(() => {
    this.send('pack,score2,score')
    setTimeout(() => $('[command=skills]').click(), 1000)
    setTimeout(() => $('[command=tasks]').click(), 2000)
    setTimeout(() => $('.dialog-close').click(), 3000)
    if (!$('.right-bar').width()) {
      setTimeout(() => $('[command=showtool]').click(), 500)
    }
    if (!$('.content-bottom').height()) {
      setTimeout(() => $('[command=showcombat]').click(), 1000)
    }
  }, 3000)
  // greet master 请安师傅
})
api.addMonitor('state', 'State', function(data) {
  if (typeof data.state !== 'string') {
    this.state = ''
    return
  }
  const list = [
    { regexp: /^你正在疗伤$/, value: '疗伤' },
    { regexp: /^你正在修炼$/, value: '修炼' },
    { regexp: /^你正在闭关$/, value: '闭关' },
    { regexp: /^你正在炼药$/, value: '炼药' },
    { regexp: /^你正在读书$/, value: '读书' },
    { regexp: /^你正在挖矿中$/, value: '挖矿' },
    { regexp: /^你正在打坐运功$/, value: '打坐' },
    { regexp: /^你正在练习技能$/, value: '练习' },
    { regexp: /^你正在学习(\S+)$/, value: '学习' },
    { regexp: /^你正在领悟技能$/, value: '武道领悟' },

  ]
  const result = list.find(item => item.regexp.test(data.state))
  if (result) data.state = result.value + (RegExp.$1 || '')
  this.state = data.state
  delete data.desc
})
api.addMonitor('combat', 'FightState', function(data) {
  if (data.start === 1) {
    this.state = '战斗'
    // if (this.fightTimer) clearInterval(this.fightTimer)
    // this.fightTimer = setInterval(() => {
    //   const now = Date.now()
    //   this.performs.find(pfm => {
    //     if (pfm.auto && (pfm.time < now)) {
    //       this.send(`perform ${pfm.id}`)
    //       return true
    //     }
    //   })
    // }, 256)
  } else if (data.end === 1) {
    this.state = ''
    // clearInterval(this.fightTimer)
  }
})
api.addMonitor('die', 'DieState', function(data) {
  if (data.relive) {
    this.state = ''
  } else {
    this.state = '死亡'
    // // 自动武庙复活
    // if (this.CanReliveWuMiao && !this.roomName.includes('副本')) {
    // this.send('relive')
  }
})
class Perform {
  constructor(item) {
    this.id = item.id
    this.name = item.name
    this.distime = item.distime
    this.timestamp = 0
    this.auto = api.getValue(this.key('auto')) || false
    this.sort = api.getValue(this.key('sort')) || 999
  }
  key(value) {
    return `${api.id}-${this.id}-${value}`
  }
  set auto(value) {
    this._auto = Boolean(value)
    api.setValue(this.key('auto'), this._auto)
  }
  get auto() {
    return this._auto
  }
  set sort(value) {
    this._sort = Number(value)
    api.setValue(this.key('sort'), this._auto)
  }
  get sort() {
    return this._sort
  }
}
api.addMonitor('perform', 'PerformList', function(data) {
  if (!(data.skills instanceof Array)) return
  data.skills.forEach((item, index) => data.skills[index] = new Perform(item))
  data.skills.forEach((a, b) => a.sort - b.sort)
  data.skills.forEach((item, index) => item.sort = index + 1)
  api.performs = data.skills
})
api.addMonitor('dispfm', 'PerformDistime', function(data) {
  if (data.hasOwnProperty('id') && data.hasOwnProperty('distime')) {
    api.performs.forEach(pfm => {
      if (pfm.id === data.id) {
        pfm.distime = data.distime
        pfm.timestamp = Date.now() + data.distime
      }
      if (data.hasOwnProperty('rtime')) {
        pfm.time = Math.max(Date.now() + data.rtime, pfm.timestamp)
      }
    })
  }
})
class Room {
  constructor(item) {
    this.name = item.name
    this.path = item.path
    this.commands = item.commands || Array()
    this.desc = item.desc
    this.exits = Object()
    this.items = Array()
  }
  get x() {
    if (/^(\S+)-(\S+)$/.test(this.name)) {
      return RegExp.$1
    }
  }
  get y() {
    if (/^(\S+)-(\S+)\(副本区域\)$/.test(this.name)) {
      return RegExp.$2
    } else if (/^(\S+)-(\S+)$/.test(this.name)) {
      return RegExp.$2
    }
  }
  set desc(value) {
    if (/cmd/.test(value)) {
      // 统一使用双引号
      value = value.replace(/'/g, '"')
      // 去除英文括号和里面的英文单词
      value = value.replace(/\([A-Za-z]+?\)/g, '')
      // 新手教程中的椅子
      value = value.replace(/<hig>椅子<\/hig>/, '椅子')
      // 兵营副本中的门
      value = value.replace(/<CMD cmd="look men">门<CMD>/, '<cmd cmd="look men">门</cmd>');
      // 古墓副本中的画和古琴
      value = value.replace(/span/ig, 'cmd')

      const htmls = value.match(/<cmd cmd="[^"]+?">[^<]+?<\/cmd>/g)
      htmls.forEach(html => {
        if (/<cmd cmd="([^"]+?)">([^<]+?)<\/cmd>/.test(html)) {
          this.commands.unshift({ cmd: RegExp.$1, name: `<hic>${RegExp.$2}</hic>` })
        }
      })
    }
    this._desc = value
  }
  get desc() {
    return this._desc
  }
  get npcs() {
    return this.items.filter(item => item.isNpc)
  }
}
api.addMonitor('room', 'RoomInfo', function(data) {
  api.room = new Room(data)
  data.desc = api.room.desc
  data.commands = api.room.commands
})
api.addMonitor('exits', 'RoomExitInfo', function(data) {
  if (typeof data.items !== 'object') return
  api.room.exits = {}
  Object.keys(data.items).forEach(key => api.room.exits[`go ${key}`] = data.items[key])
})
class Role {
  constructor(data) {
    this.id = data.id
    this.name = data.name
    if (data.hasOwnProperty('hp')) this.hp = data.hp
    if (data.hasOwnProperty('mp')) this.mp = data.mp
    if (data.hasOwnProperty('max_hp')) this.max_hp = data.max_hp
    if (data.hasOwnProperty('max_mp')) this.max_mp = data.max_mp
    if (data.hasOwnProperty('status')) this.status = data.status
    if (data.hasOwnProperty('p')) this.p = data.p // 玩家肯定有 p === 1
  }
  get isNpc() {
    return !this.hasOwnProperty('p') && this.hasOwnProperty('hp')
  }
  get sort() {
    // 自身
    if (this.id === api.id) return 1
    // NPC
    if (this.isNpc) return 10
    // 玩家
    if (this.hasOwnProperty('hp')) {
      const list = ['武神 ', '武帝 ', '武圣 ', '宗师 ', '武师 ', '武士 ', '普通百姓 ', '']
      const index = list.findIndex(item => this.name.includes(item))
      return 100 * index * (this.name.includes('<red>&lt;断线中&gt;</red>') ? 100 : 1)
    }
    // 其他物品或尸体
    return 1000
  }
}
api.addMonitor('items', 'RoomItemList', function(data) {
  if (!data.items instanceof Array) return
  data.items.splice(-1, 1) // 删除最后一个 0
  data.items.forEach((item, index) => data.items[index] = new Role(item))
  data.items.sort((a, b) => a.sort - b.sort)
  api.room.items = data.items
})
api.addMonitor('itemadd', 'RoomAddItem', function(data) {
  const item = new Role(data)
  const index = api.room.items.findIndex(item => item.id === data.id)
  if (index === -1) api.room.items.push(item)
  else api.room.items.splice(index, 1, item)
})
api.addMonitor('itemremove', 'RoomRemoveItem', function(data) {
  const index = api.room.items.findIndex(item => item.id === data.id)
  if (index !== -1) api.room.items.splice(index, 1)
})
api.addMonitor('score', 'PropData', function(data) {
  if (api.id !== data.id) return
  Object.keys(data).forEach(key => api.prop[key] = data[key])
  api.prop = Object.assign({}, api.prop)
})
class Skill {
  constructor(item) {
    this.id = item.id
    this.name = item.name
    this.level = item.level
    this.exp = item.exp
    if (item.hasOwnProperty('enable_skill')) this.enable_skill = item.enable_skill
    if (item.hasOwnProperty('can_enables')) this.can_enables = item.can_enables
    this.target = api.getValue(this.key('target'))
  }
  key(value) {
    return `${api.id}-${this.id}-${value}`
  }
  set target(value) {
    const number = Number(value)
    this._target = isNaN(number) ? 0 : number
    api.setValue(this.key('target'), this._target)
  }
  get target() {
    return this._target
  }
  get color() {
    return RegExpListForColorValue.findIndex(regexp => regexp.test(this.name))
  }
  get sort() {
    if (this.color === 1) {
      return RegExpListForBaseSkillSort.findIndex(regexp => regexp.test(this.id))
    }
    return 99999 - (this.level + this.color)
  }
}
api.addMonitor('skills', 'SkillData', function(data) {
  // 潜能
  if (data.hasOwnProperty('pot')) this.qn = data.pot
  // 等级上限
  if (data.hasOwnProperty('limit')) this.skilllimit = data.limit
  // 技能列表
  if (data.items instanceof Array) {
    data.items.forEach((item, index) => data.items[index] = new Skill(item))
    data.items.sort((a, b) => a.sort - b.sort)
    api.skills = data.items
  }
  // 学会新技能
  if (data.hasOwnProperty('item')) api.skills.push(new Skill(data.item))
  // 装备技能
  if (data.hasOwnProperty('enable')) {
    api.skills.find(skill => {
      if (skill.id === data.id) {
        skill.enable_skill = data.enable
        return true
      }
    })
  }
  // 技能经验提升
  if (data.hasOwnProperty('exp')) {
    const skill = this.skills.find(skill => skill.id === data.id)
    if (!skill) return
    skill.exp = data.exp
    // 技能等级提升
    if (data.hasOwnProperty('level')) skill.level = parseInt(data.level)
    // 技能消耗系数
    const k = skill.color * 2.5
    // 需要消耗潜能
    const target = skill.target > skill.level ? skill.target : this.skilllimit
    const qn = (Math.pow(target, 2) - Math.pow(skill.level, 2)) * k
    // 练习状态
    if (this.state.includes('练习')) {
      // 练习每跳消耗=(先天悟性+后天悟性)*(100%+练习效率%-先天悟性%)
      const cost = Math.ceil((this.wx1 + this.wx2) * (1 + this.lxxl / 100 - this.wx1 / 100))
      this.ontext(`你练习${ skill.name }消耗了${ cost }点潜能。<wht>${ skill.level }级 / ${ skill.exp }%</wht>`)
      // 需要分钟时间
      const minutes = Math.ceil(qn / cost / (60 / 5))
      const text = minutes < 60 ? minutes + '分钟' : Math.floor(minutes / 60) + '小时' + (minutes % 60) + '分钟'
      this.ondata({ type: 'state', state: `练习${ skill.name.replace(/<\S+?>/g, '') }至${ target }级余${ text }` })
    }
    // 学习状态
    if (this.state.includes('学习')) {
      // 学习每跳消耗=(先天悟性+后天悟性)*(100%+学习效率%-先天悟性%)*3
      const cost = Math.ceil((this.wx1 + this.wx2) * (1 + this.xxxl / 100 - this.wx1 / 100)) * 3
      this.ontext(`你学习${ skill.name }消耗了${ cost }点潜能。<wht>${ skill.level }级 / ${ skill.exp }%</wht>`)
    }
    // 炼药状态
    if (this.state.includes('炼药')) {
      this.ontext(`你修习${skill.name}消耗了药材换取经验。<wht>${ skill.level }级 / ${ skill.exp }%</wht>`)
    }
  }
})
api.addMonitor('master', 'MasterSkillData', function(data) {
  // 师傅技能或随从技能
  if (data.items instanceof Array) {
    data.items.forEach((item, index) => data.items[index] = new Skill(item))
    data.items.sort((a, b) => a.sort - b.sort)
  }
})
class Pack {
  constructor(item) {
    this.id = item.id
    this.name = item.name
    this.count = item.count
    this.unit = item.unit
    this.value = item.value
    if (item.hasOwnProperty('can_combine')) this.can_combine = item.can_combine
    if (item.hasOwnProperty('can_study')) this.can_study = item.can_study
    if (item.hasOwnProperty('can_use')) this.can_use = item.can_use
    if (item.hasOwnProperty('can_eq')) this.can_eq = item.can_eq
  }
  get color() {
    return RegExpListForColorValue.findIndex(regexp => regexp.test(this.name))
  }
  get sort() {
    return RegExpListForPackSort.findIndex(regexp => regexp.test(this.name))
  }
}
api.addMonitor('pack', 'PackData', function(data) {
  console.log(data)
  if (data.hasOwnProperty('money')) this.money = data.money
  if (data.hasOwnProperty('max_item_count')) this.packlimit = data.max_item_count
  // 物品列表
  if (data.items instanceof Array) {
    data.items.forEach((item, index) => data.items[index] = new Pack(item))
    data.items.sort((a, b) => a.sort - b.sort)
    this.packs = data.items
  }
  // 装备列表
  if (data.eqs instanceof Array) {
    data.eqs.forEach((eq, index) => this.eqs.splice(index, 1, eq ? new Pack(eq) : null))
  }
  // 穿装备
  if (typeof data.eq === 'number' && data.id) {
    // { id: "c7mj3f552d3", eq: 3 }
  }
  // 脱装备
  if (typeof data.uneq === 'number' && data.id) {
    // { id: "wpur2aee755", uneq: 0 }
  }
  // 物品描述
  if (data.from === 'eq' || data.from === 'item') {
    // { id: "pd232aa6f4c", desc: "<hic>★★★★神龙袍</hic>↵高级↵神龙教管理层的标准制服↵<hic>防御:+195↵气血:+975↵根骨:+39</hic>↵◇" }
    // { id: "sbgw4bfb80a", desc: "<hio>高级叛师符</hio>↵使用后可以使脱离当前门派,门派学到的所有武功会遗忘,会返还你学习和练习技能消耗的潜能" }
  }
})
api.addMonitor('pack2', 'SuiCongPackData', function(data) {
  if (data.items instanceof Array) {
    data.items.forEach((item, index) => data.items[index] = new Pack(item))
    data.items.sort((a, b) => a.sort - b.sort) // 随从背包物品排序
  }
})
api.addMonitor('list', 'ItemListData', function(data) {
  // 仓库数据
  if (data.stores instanceof Array) {
    data.stores.forEach((item, index) => data.stores[index] = new Pack(item))
    data.stores.sort((a, b) => a.sort - b.sort)
  }
})
api.addMonitor('sc', 'ScoreData', function(data) {
  // 自身气血内力数据
  if (this.id === data.id) {
    if (data.hasOwnProperty('hp')) this.hp = data.hp
    if (data.hasOwnProperty('mp')) this.mp = data.mp
    if (data.hasOwnProperty('max_hp')) this.hpmax = data.max_hp
    if (data.hasOwnProperty('max_mp')) this.mpmax = data.max_mp
  }
  // BOSS
  if (data.hasOwnProperty('damage')) {
    const id = data.id
    const npc = this.room.npcs.find(npc => npc.id === id)
    if (npc) {
      const name = npc.name
      const max = npc.max_hp
      // 当 damage === -1 时直接计算,否则根据具体的 damage 值计算伤害占比。
      const value = data.damage === -1 ? ((max-data.hp)/max*100).toFixed(2) : (data.damage/max*100).toFixed(2)
      if (this.damage.id !== id) this.damage.value = 0

      if (Math.floor((this.damage.value+5)/5)*5 < value) {
        // this.ontext(`<hir>Damage: [${value}]${name.replace(/<\S+?>|\s/g, '')}`)
      }
      this.damage = { id, name, value, max }
    } else if (this.damage.id === id) {
      const name = this.damage.name
      const max = this.damage.max
      const value = data.damage === -1 ? ((max-data.hp)/max*100).toFixed(2) : (data.damage/max*100).toFixed(2)
      if (Math.floor((this.damage.value+5)/5)*5 < value) {
        // this.ontext(`<hir>Damage: [${value}]${name.replace(/<\S+?>|\s/g, '')}`)
      }
      this.damage = { id, name, value, max }
    }
  }
})


api.addMonitor('', '', function(data) {})


new Vue({
  computed: {
    title() {
      return `${api.name} ${api.state}`
    },
  },
  watch: {
    title(value) {
      document.title = value
    },
  },
})

document.addEventListener('DOMContentLoaded', () => api.addStyle(`
`), false)

})()