调整圣遗物评分计算逻辑

This commit is contained in:
yoimiya-kokomi 2022-09-11 03:59:45 +08:00
parent d3d23da1e5
commit c9fe04952e
25 changed files with 185 additions and 150 deletions

View File

@ -1,17 +1,10 @@
# 1.10.5 # 1.10.6
* 面板圣遗物评分初步增加流派判定能力 * 面板圣遗物评分初步增加流派判定能力
* 实验性,尚未完全稳定,可能会导致一些角色圣遗物评分变化 * 实验性,尚未完全稳定,可能会导致一些角色圣遗物评分变化,如遇问题请反馈
* 目前实验暴力芭芭拉、血牛钟离的判定 * 目前实验暴力芭芭拉、血牛钟离的判定
* 其他一些已知Bug修复
# 1.10.4 # 1.10.1~1.10.5
* 增加`#心海图鉴`功能,可查看突破材料及常用武器
* 功能尚未完全稳定,信息还在继续补全中
* 如无需使用master可通过`#喵喵设置图鉴关闭`关闭,防止覆盖图鉴插件等图鉴功能
# 1.10.1~1.10.3
* `#刻晴面板`、`#芭芭拉圣遗物`支持展示角色时装 * `#刻晴面板`、`#芭芭拉圣遗物`支持展示角色时装
* 如果角色装备了时装,面板的角色图会展示时装立绘 * 如果角色装备了时装,面板的角色图会展示时装立绘
@ -22,6 +15,9 @@
* `#雷主天赋`、`#草主命座`功能升级 * `#雷主天赋`、`#草主命座`功能升级
* 页面样式微调,内部处理逻辑升级 * 页面样式微调,内部处理逻辑升级
* 支持旅行者天赋及命座信息查看 * 支持旅行者天赋及命座信息查看
* 增加`#心海图鉴`功能,可查看突破材料及常用武器
* 功能尚未完全稳定,信息还在继续补全中
* 如无需使用master可通过`#喵喵设置图鉴关闭`关闭,防止覆盖图鉴插件等图鉴功能
* 框架底层角色相关逻辑重构角色图像资源迁移为webp格式 * 框架底层角色相关逻辑重构角色图像资源迁移为webp格式
* 若遇到图像资源无法正常展示,可联系喵喵反馈 * 若遇到图像资源无法正常展示,可联系喵喵反馈

View File

@ -142,7 +142,7 @@ export async function autoGetProfile (e, uid, avatar, callback) {
return { err: true } return { err: true }
} else if (!['enka', 'input2', 'miao'].includes(profile.dataSource)) { } else if (!['enka', 'input2', 'miao'].includes(profile.dataSource)) {
if (!await refresh()) { if (!await refresh()) {
e.reply('由于数据格式升级,请重新获取面板信息后查看') e.reply('缓存数据错误,请重新获取面板信息后查看')
} }
return { err: true } return { err: true }
} }

View File

@ -23,18 +23,13 @@ export async function renderProfile (e, char, mode = 'profile', params = {}) {
} }
let profile = Profile.get(uid, char.id) let profile = Profile.get(uid, char.id)
if (!profile) { if (!profile || !profile.hasData) {
if (await refresh()) { if (await refresh()) {
return true return true
} else { } else {
e.reply(`请确认${char.name}已展示在【游戏内】的角色展柜中,并打开了“显示角色详情”。然后请使用 #更新面板\n命令来获取${char.name}的面板详情`) e.reply(`请确认${char.name}已展示在【游戏内】的角色展柜中,并打开了“显示角色详情”。然后请使用 #更新面板\n命令来获取${char.name}的面板详情`)
} }
return true return true
} else if (!profile.hasData) {
if (!await refresh()) {
e.reply('由于数据Api变更请重新获取面板信息后查看')
}
return true
} }
char = profile.char || char char = profile.char || char
let a = profile.attr let a = profile.attr
@ -88,13 +83,11 @@ export async function renderProfile (e, char, mode = 'profile', params = {}) {
basic.dmg = Format.comma(basic.dmg) basic.dmg = Format.comma(basic.dmg)
basic.avg = Format.comma(basic.avg) basic.avg = Format.comma(basic.avg)
} }
const costume = profile.costume ? '2' : ''
// 渲染图像 // 渲染图像
return await Common.render('character/profile-detail', { return await Common.render('character/profile-detail', {
save_id: uid, save_id: uid,
uid, uid,
data: profile.getData('cons,level,weapon,dataSource,updateTime'), data: profile.getData('cons,level,weapon,dataSource,updateTime'),
costume,
attr, attr,
name: char.name, name: char.name,
elem: char.elem, elem: char.elem,
@ -112,7 +105,7 @@ export async function renderProfile (e, char, mode = 'profile', params = {}) {
classTitle, classTitle,
usefulMark, usefulMark,
talentMap: { a: '普攻', e: '战技', q: '爆发' }, talentMap: { a: '普攻', e: '战技', q: '爆发' },
bodyClass: `char-${char.name}${costume}`, bodyClass: `char-${char.name}`,
mode mode
}, { e, scale: 1.6 }) }, { e, scale: 1.6 })
} }

View File

@ -30,7 +30,6 @@ export async function profileList (e) {
tmp.source = profile.dataSource tmp.source = profile.dataSource
tmp.level = profile.level || 1 tmp.level = profile.level || 1
tmp.isNew = 0 tmp.isNew = 0
tmp.costume = profile.costume ? '2' : ''
if (newChar[char.name]) { if (newChar[char.name]) {
tmp.isNew = 1 tmp.isNew = 1
newCount++ newCount++

View File

@ -33,7 +33,7 @@ const attrMap = {
} }
let EnkaData = { let EnkaData = {
getProfile(data) { getProfile (data) {
let char = Character.get(data.avatarId) let char = Character.get(data.avatarId)
let profile = new ProfileData({ id: char.id }) let profile = new ProfileData({ id: char.id })
profile.setBasic({ profile.setBasic({
@ -54,7 +54,7 @@ let EnkaData = {
} }
return EnkaData.dataFix(profile) return EnkaData.dataFix(profile)
}, },
getAttr(data) { getAttr (data) {
let ret = {} let ret = {}
let attrKey = { let attrKey = {
// atk: 2001, // atk: 2001,
@ -105,7 +105,7 @@ let EnkaData = {
return ret return ret
}, },
getArtifact(data) { getArtifact (data) {
let ret = {} let ret = {}
let get = function (d) { let get = function (d) {
@ -144,7 +144,7 @@ let EnkaData = {
}) })
return ret return ret
}, },
getWeapon(data) { getWeapon (data) {
let ds = {} let ds = {}
lodash.forEach(data, (temp) => { lodash.forEach(data, (temp) => {
if (temp.flat && temp.flat.itemType === 'ITEM_WEAPON') { if (temp.flat && temp.flat.itemType === 'ITEM_WEAPON') {
@ -161,7 +161,7 @@ let EnkaData = {
affix: (lodash.values(weapon.affixMap)[0] || 0) + 1 affix: (lodash.values(weapon.affixMap)[0] || 0) + 1
} }
}, },
getTalent(charid, ds = {}) { getTalent (charid, ds = {}) {
let char = Character.get(charid) let char = Character.get(charid)
let { talentId = {}, talentElem = {}, talentKey = {} } = char.meta let { talentId = {}, talentElem = {}, talentKey = {} } = char.meta
let elem = '' let elem = ''
@ -185,7 +185,7 @@ let EnkaData = {
talent: ret talent: ret
} }
}, },
dataFix(ret) { dataFix (ret) {
if (ret._fix) { if (ret._fix) {
return ret return ret
} }

View File

@ -4,6 +4,20 @@ let cacheMap = {}
let reFn = {} let reFn = {}
export default class Base { export default class Base {
constructor () {
return new Proxy(this, {
get (self, key) {
if (key in self) {
return self[key]
}
if (self._get) {
return self._get(key)
}
return (self._meta || self._data || self.meta || {})[key]
}
})
}
getData (arrList = '', cfg = {}) { getData (arrList = '', cfg = {}) {
arrList = arrList || this._dataKey || '' arrList = arrList || this._dataKey || ''
return Data.getData(this, arrList, cfg) return Data.getData(this, arrList, cfg)
@ -25,9 +39,12 @@ export default class Base {
// 设置缓存 // 设置缓存
_cache (time = 10 * 60) { _cache (time = 10 * 60) {
let id = this._uuid let id = this._uuid
this._expire(time) if (id) {
cacheMap[id] = this._proxy() this._expire(time)
return cacheMap[id] cacheMap[id] = this
return cacheMap[id]
}
return this
} }
// 设置超时时间 // 设置超时时间
@ -58,21 +75,4 @@ export default class Base {
} }
return ret return ret
} }
// 返回代理对象
_proxy () {
return new Proxy(this, {
get (self, key) {
if (key in self) {
return self[key]
}
if (self._get) {
if (key in self) {
return self._get(key)
}
}
return (self._meta || self._data || self.meta || {})[key]
}
})
}
} }

View File

@ -5,7 +5,6 @@ import CharImg from './character-lib/CharImg.js'
import CharTalent from './character-lib/CharTalent.js' import CharTalent from './character-lib/CharTalent.js'
import CharId from './character-lib/CharId.js' import CharId from './character-lib/CharId.js'
import CharMeta from './character-lib/CharMeta.js' import CharMeta from './character-lib/CharMeta.js'
import CharArtis from './character-lib/CharArtis.js'
let { abbrMap, wifeMap, idSort } = CharId let { abbrMap, wifeMap, idSort } = CharId
@ -23,9 +22,6 @@ class Character extends Base {
if (!this.isCustom) { if (!this.isCustom) {
let meta = getMeta(name) let meta = getMeta(name)
this.meta = meta this.meta = meta
for (let key of 'abbr,title,star,allegiance,weapon,astro,cncv,jpcv,ver,talentCons'.split(',')) {
this[key] = meta[key]
}
this.elem = CharId.getElem(elem || meta.elem) || 'anemo' this.elem = CharId.getElem(elem || meta.elem) || 'anemo'
} else { } else {
this.meta = {} this.meta = {}
@ -139,6 +135,9 @@ class Character extends Base {
// 获取角色插画 // 获取角色插画
getImgs (costume = '') { getImgs (costume = '') {
let costumeId = this.checkCostume(costume) ? '2' : '' let costumeId = this.checkCostume(costume) ? '2' : ''
if (costume === 'super') {
costumeId = '0'
}
let cacheId = `costume${costumeId}` let cacheId = `costume${costumeId}`
if (!this._imgs) { if (!this._imgs) {
this._imgs = {} this._imgs = {}
@ -211,10 +210,6 @@ class Character extends Base {
} }
return await this.getTraveler(uid) return await this.getTraveler(uid)
} }
getArtisMarkCfg (profile, artis) {
return CharArtis.getCharArtisCfg(this, profile, artis)
}
} }
let getMeta = function (name) { let getMeta = function (name) {

View File

@ -4,9 +4,10 @@
import lodash from 'lodash' import lodash from 'lodash'
import Base from './Base.js' import Base from './Base.js'
import { Artifact, Character } from './index.js' import { Artifact, Character } from './index.js'
import { Format } from '../components/index.js' import { Format, Data } from '../components/index.js'
import ArtisMark from './profile-lib/ArtisMark.js' import ArtisMark from './profile-lib/ArtisMark.js'
import { attrMap, attrNameMap, attrValue } from '../resources/meta/reliquaries/artis-mark.js' import { attrMap, attrNameMap, attrValue } from '../resources/meta/reliquaries/artis-mark.js'
import CharArtis from './profile-lib/CharArtis.js'
export default class ProfileArtis extends Base { export default class ProfileArtis extends Base {
constructor (charid = 0, ds = false) { constructor (charid = 0, ds = false) {
@ -16,7 +17,6 @@ export default class ProfileArtis extends Base {
if (ds) { if (ds) {
this.setArtisSet(ds) this.setArtisSet(ds)
} }
return this._proxy()
} }
setProfile (profile, artis) { setProfile (profile, artis) {
@ -54,30 +54,6 @@ export default class ProfileArtis extends Base {
}) })
} }
get 1 () {
return this.artis[1]
}
get 2 () {
return this.artis[2]
}
get 3 () {
return this.artis[3]
}
get 4 () {
return this.artis[4]
}
get 5 () {
return this.artis[5]
}
get length () {
return lodash.keys(this.artis).length
}
_get (key) { _get (key) {
let artis = this.artis let artis = this.artis
switch (key) { switch (key) {
@ -101,7 +77,14 @@ export default class ProfileArtis extends Base {
return this.getSetData().names return this.getSetData().names
} }
mainAttr (idx) { mainAttr (idx = '') {
if (!idx) {
let ret = {}
for (let i = 1; i <= 5; i++) {
ret[i] = this.mainAttr(i)
}
return ret
}
let main = this.artis[idx]?.main let main = this.artis[idx]?.main
if (!main) { if (!main) {
return '' return ''
@ -118,25 +101,63 @@ export default class ProfileArtis extends Base {
return '' return ''
} }
is (check, pos = '') {
if (pos) {
return this.isAttr(check, pos)
}
let sets = this.getSetData()?.abbrs || []
let ret = false
Data.eachStr(check, (s) => {
if (sets.includes(s)) {
ret = true
return false
}
})
return ret
}
isAttr (attr, pos = '3,4,5') {
let mainAttr = this.mainAttr()
let check = true
Data.eachStr(pos, (p) => {
if (!attr.split(',').includes(mainAttr[p])) {
check = false
return false
}
})
console.log(attr, pos, mainAttr, check)
return check
}
getSetData () { getSetData () {
if (this._setData) {
return this._setData
}
let setCount = {} let setCount = {}
this.forEach((arti, idx) => { this.forEach((arti, idx) => {
setCount[arti.set] = (setCount[arti.set] || 0) + 1 setCount[arti.set] = (setCount[arti.set] || 0) + 1
}) })
let sets = {} let sets = {}
let names = [] let names = []
let abbrs = []
for (let set in setCount) { for (let set in setCount) {
if (setCount[set] >= 2) { if (setCount[set] >= 2) {
sets[set] = setCount[set] >= 4 ? 4 : 2 sets[set] = setCount[set] >= 4 ? 4 : 2
names.push(Artifact.getArtiBySet(set)) names.push(Artifact.getArtiBySet(set))
} }
} }
return { sets, names } lodash.forEach(sets, (v, k) => {
abbrs.push(k + v)
abbrs.push(Artifact.getAbbrBySet(k) + v)
})
this._setData = { sets, names, abbrs }
return this._setData
} }
getCharCfg () { getCharCfg () {
let char = Character.get(this.charid) let char = Character.get(this.charid)
let { attrWeight, title } = char.getArtisMarkCfg(this.profile, this) // let { attrWeight, title } = char.getArtisMarkCfg(this.profile, this)
let { attrWeight, title } = CharArtis.getCharArtisCfg(char, this.profile, this)
let attrMark = {} let attrMark = {}
let baseAttr = char.baseAttr || { hp: 14000, atk: 230, def: 700 } let baseAttr = char.baseAttr || { hp: 14000, atk: 230, def: 700 }

View File

@ -92,15 +92,15 @@ export default class ProfileData extends Base {
} }
get costume () { get costume () {
let cMap = { let cMap = [
// 10000033: 900001, // 公子 10000022, // 温迪
10000052: 900002, // 雷神 10000030, // 钟离
10000030: 900003 // 钟离 10000052 // 雷神
} ]
let talent = this.talent ? lodash.map(this.talent, (ds) => ds.original).join('') : '' let talent = this.talent ? lodash.map(this.talent, (ds) => ds.original).join('') : ''
if (cMap[this.id]) { if (cMap.includes(this.id)) {
if (this.cons === 6 || ['ACE', 'ACE²'].includes(this.artis?.markClass) || talent === '101010') { if (this.cons === 6 || ['ACE', 'ACE²'].includes(this.artis?.markClass) || talent === '101010') {
return cMap[this.id] return 'super'
} }
} }
return this._costume return this._costume
@ -108,7 +108,7 @@ export default class ProfileData extends Base {
// toJSON 供保存使用 // toJSON 供保存使用
toJSON () { toJSON () {
return this._proxy().getData('id,name,level,cons,fetter,attr,weapon,talent,artis,dataSource,costume,elem,_time') return this.getData('id,name,level,cons,fetter,attr,weapon,talent,artis,dataSource,costume,elem,_time')
} }
get updateTime () { get updateTime () {

View File

@ -1,50 +0,0 @@
import { usefulAttr } from '../../resources/meta/reliquaries/artis-mark.js'
const CharArtis = {
getCharArtisCfg (char, profile, artis) {
let { attr, weapon } = profile
let cn = '通用'
let check = true
// 实验性实现,后期逐步迁移至配置文件
switch (char.name) {
case '芭芭拉':
if (attr.cpct * 2 + attr.cdmg >= 180 && artis.mainAttr(4) === 'dmg') {
cn = '暴力'
}
break
case '钟离':
for (let idx = 3; idx <= 5; idx++) {
check = check && (artis.mainAttr(idx) === '大生命')
}
if (check) {
cn = '血牛'
}
break
case '雷电将军':
if (weapon.name === '薙草之稻光' && weapon.affix >= 3) {
cn = '高精'
}
break
}
if (cn !== '通用' && usefulAttr[`${char.name}-${cn}`]) {
return {
title: `${char.abbr}-${cn}`,
attrWeight: usefulAttr[`${char.name}-${cn}`]
}
} else {
let artisSet = artis.getSetData()?.sets || {}
let weight = usefulAttr[char.name] || { atk: 75, cp: 100, cd: 100 }
if (artisSet['绝缘之旗印'] === 4 && weight.recharge < 75) {
weight.recharge = 75
cn = '绝缘4'
}
return {
title: `${char.abbr}-${cn}`,
attrWeight: weight
}
}
}
}
export default CharArtis

View File

@ -56,18 +56,23 @@ const CharImg = {
if (!['空', '荧', '旅行者'].includes(name)) { if (!['空', '荧', '旅行者'].includes(name)) {
travelerElem = '' travelerElem = ''
} }
const rPath = `${process.cwd()}/plugins/miao-plugin/resources`
const nPath = `/meta/character/${name}/` const nPath = `/meta/character/${name}/`
const tPath = `/meta/character/旅行者/${travelerElem}/` const tPath = `/meta/character/旅行者/${travelerElem}/`
let add = (key, path, traveler = false) => { let add = (key, path, path2) => {
imgs[key] = `${traveler ? tPath : nPath}${path}.webp` if (path2 && fs.existsSync(`${rPath}/${nPath}/${path2}.webp`)) {
imgs[key] = `${nPath}${path2}.webp`
} else {
imgs[key] = `${nPath}${path}.webp`
}
} }
let tAdd = (key, path) => { let tAdd = (key, path) => {
add(key, path, !!travelerElem) imgs[key] = `${travelerElem ? tPath : nPath}${path}.webp`
} }
add('face', `imgs/face${costumeId}`) add('face', 'imgs/face', `imgs/face${costumeId}`)
add('side', `imgs/side${costumeId}`) add('side', 'imgs/side', `imgs/side${costumeId}`)
add('gacha', 'imgs/gacha') add('gacha', 'imgs/gacha')
add('splash', `imgs/splash${costumeId}`) add('splash', 'imgs/splash', `imgs/splash${costumeId}`)
tAdd('card', 'imgs/card') tAdd('card', 'imgs/card')
tAdd('banner', 'imgs/banner') tAdd('banner', 'imgs/banner')
for (let i = 1; i <= 6; i++) { for (let i = 1; i <= 6; i++) {

View File

@ -40,7 +40,7 @@ export const growAttrName = {
const mKeys = [{ const mKeys = [{
key: 'gem', key: 'gem',
num: '1/9/6/6' num: '1/9/9/6'
}, { }, {
key: 'boss', key: 'boss',
num: '46', num: '46',

View File

@ -0,0 +1,51 @@
import { usefulAttr } from '../../resources/meta/reliquaries/artis-mark.js'
import { Data } from '../../components/index.js'
import fs from 'fs'
let charCfg = {}
async function init () {
let charPath = process.cwd() + '/plugins/miao-plugin/resources/meta/character'
let chars = fs.readdirSync(charPath)
for (let char of chars) {
if (fs.existsSync(`${charPath}/${char}/artis.js`)) {
charCfg[char] = await Data.importModule(`resources/meta/character/${char}/artis.js`)
}
}
}
await init()
const CharArtis = {
getCharArtisCfg (char, profile, artis) {
let { attr } = profile
let rule = function (title, attrWeight) {
return {
title,
attrWeight
}
}
let def = function (attrWeight) {
let title = '通用'
let weight = attrWeight || usefulAttr[char.name] || { atk: 75, cp: 100, cd: 100 }
if (artis.is('绝缘4') && weight.recharge < 75) {
weight.recharge = 75
title = '绝缘4'
}
return {
title: `${char.abbr}-${title}`,
attrWeight: weight
}
}
let charRule = charCfg[char.name]?.default || function ({ def }) {
return def(usefulAttr[char.name] || { atk: 75, cp: 100, cd: 100 })
}
if (charRule) {
return charRule({ attr, artis, rule, def, weapon: profile.weapon })
}
}
}
export default CharArtis

Binary file not shown.

After

Width:  |  Height:  |  Size: 346 KiB

View File

@ -0,0 +1,6 @@
export default function ({ attr, artis, rule, def }) {
if (attr.cpct * 2 + attr.cdmg >= 180 && artis.mainAttr(4) === 'dmg') {
return rule('暴力芭芭拉', { hp: 50, atk: 75, cp: 100, cd: 100, mastery: 75, dmg: 100, recharge: 30, heal: 50 })
}
return def({ hp: 100, atk: 50, cp: 50, cd: 50, dmg: 80, recharge: 55, heal: 100 })
}

View File

@ -0,0 +1,10 @@
export default function ({ attr, artis, rule, def }) {
if (artis.is('hp', '4,5')) {
if (artis.is('绝缘4') && artis.is('hp,recharge', 3)) {
return rule('绝缘血牛钟离', { hp: 100, atk: 50, cp: 50, cd: 50, recharge: 100 })
} else if (artis.is('hp', 3)) {
return rule('血牛钟离', { hp: 100, atk: 50, cp: 50, cd: 50, recharge: 50 })
}
}
return def({ hp: 80, atk: 75, cp: 100, cd: 100, dmg: 100, phy: 50, recharge: 55 })
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 541 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 506 KiB

View File

@ -0,0 +1,10 @@
export default function ({ attr, weapon, rule, def }) {
// 辅助精通雷神,具体数值待定
if (attr.mastery > 500) {
return rule('雷神-精通', { atk: 75, cp: 90, cd: 90, mastery: 100, dmg: 75, recharge: 90 })
}
if (weapon.name === '薙草之稻光' && weapon.affix >= 3) {
return rule('雷神-高精', { atk: 90, cp: 100, cd: 100, dmg: 90, recharge: 90 })
}
return def({ atk: 75, cp: 100, cd: 100, mastery: 0, dmg: 75, recharge: 90 })
}

View File

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

View File

Before

Width:  |  Height:  |  Size: 624 KiB

After

Width:  |  Height:  |  Size: 624 KiB

View File

@ -54,7 +54,6 @@ export const usefulAttr = {
'雷电将军-高精': { hp: 0, atk: 90, def: 0, cp: 100, cd: 100, mastery: 0, dmg: 90, phy: 0, recharge: 90, heal: 0 }, '雷电将军-高精': { hp: 0, atk: 90, def: 0, cp: 100, cd: 100, mastery: 0, dmg: 90, phy: 0, recharge: 90, heal: 0 },
行秋: { hp: 0, atk: 75, def: 0, cp: 100, cd: 100, mastery: 0, dmg: 100, phy: 0, recharge: 75, heal: 0 }, 行秋: { hp: 0, atk: 75, def: 0, cp: 100, cd: 100, mastery: 0, dmg: 100, phy: 0, recharge: 75, heal: 0 },
钟离: { hp: 80, atk: 75, def: 0, cp: 100, cd: 100, mastery: 0, dmg: 100, phy: 50, recharge: 55, heal: 0 }, 钟离: { hp: 80, atk: 75, def: 0, cp: 100, cd: 100, mastery: 0, dmg: 100, phy: 50, recharge: 55, heal: 0 },
'钟离-血牛': { hp: 100, atk: 50, def: 0, cp: 50, cd: 50, mastery: 0, dmg: 0, phy: 0, recharge: 50, heal: 0 },
神里绫华: { hp: 0, atk: 75, def: 0, cp: 100, cd: 100, mastery: 0, dmg: 100, phy: 0, recharge: 30, heal: 0 }, 神里绫华: { hp: 0, atk: 75, def: 0, cp: 100, cd: 100, mastery: 0, dmg: 100, phy: 0, recharge: 30, heal: 0 },
香菱: { hp: 0, atk: 75, def: 0, cp: 100, cd: 100, mastery: 75, dmg: 100, phy: 0, recharge: 55, heal: 0 }, 香菱: { hp: 0, atk: 75, def: 0, cp: 100, cd: 100, mastery: 75, dmg: 100, phy: 0, recharge: 55, heal: 0 },
胡桃: { hp: 80, atk: 50, def: 0, cp: 100, cd: 100, mastery: 75, dmg: 100, phy: 0, recharge: 0, heal: 0 }, 胡桃: { hp: 80, atk: 50, def: 0, cp: 100, cd: 100, mastery: 75, dmg: 100, phy: 0, recharge: 0, heal: 0 },