微调面板相关逻辑

This commit is contained in:
Kokomi 2023-02-22 03:49:49 +08:00
parent d0b9d4c175
commit f0348841a3
13 changed files with 512 additions and 414 deletions

View File

@ -1,7 +1,7 @@
import { App, Cfg } from '../components/index.js'
import { profileHelp } from './profile/ProfileCommon.js'
import { profileArtisList } from './profile/ProfileArtis.js'
import { profileDetail } from './profile/ProfileDetail.js'
import ProfileDetail from './profile/ProfileDetail.js'
import ProfileStat from './profile/ProfileStat.js'
import ProfileList from './profile/ProfileList.js'
import { uploadCharacterImg, delProfileImg, profileImgList } from './character/ImgUpload.js'
@ -16,13 +16,13 @@ let app = App.init({
app.reg({
profileDetail: {
name: '角色面板',
fn: profileDetail,
rule: /^#*([^#]+)\s*(详细|详情|面板|面版|圣遗物|伤害[1-7]?)\s*(\d{9})*(.*[换变改].*)?$/
fn: ProfileDetail.detail,
rule: /^#*([^#]+)\s*(详细|详情|面板|面版|圣遗物|武器[1-7]?|伤害[1-7]?)\s*(\d{9})*(.*[换变改].*)?$/
},
profileChange: {
name: '角色面板计算',
fn: profileDetail,
fn: ProfileDetail.detail,
rule: /^#.+换.+$/
},

View File

@ -2,17 +2,18 @@ import lodash from 'lodash'
import { getTargetUid, getProfileRefresh } from './ProfileCommon.js'
import ProfileList from './ProfileList.js'
import { Cfg, Common, Format } from '../../components/index.js'
import { MysApi, ProfileRank, ProfileArtis, Player, Character } from '../../models/index.js'
import { MysApi, ProfileRank, ProfileArtis, Character, Weapon } from '../../models/index.js'
import ProfileChange from './ProfileChange.js'
import { profileArtis } from './ProfileArtis.js'
// 查看当前角色
export async function profileDetail (e) {
let ProfileDetail = {
async detail (e) {
let msg = e.original_msg || e.msg
if (!msg) {
return false
}
if (!/详细|详情|面板|面版|圣遗物|伤害|换/.test(msg)) {
if (!/详细|详情|面板|面版|圣遗物|伤害|武器|换/.test(msg)) {
return false
}
let mode = 'profile'
@ -40,7 +41,7 @@ export async function profileDetail (e) {
let name = msg.replace(/#|老婆|老公/g, '').trim()
msg = msg.replace('面版', '面板')
let dmgRet = /伤害(\d?)$/.exec(name)
let dmgRet = /(?:伤害|武器)(\d?)$/.exec(name)
let dmgIdx = 0
if (/(最强|最高|最高分|最牛|第一)/.test(msg)) {
mode = /(分|圣遗物|评分|ACE)/.test(msg) ? 'rank-mark' : 'rank-dmg'
@ -50,8 +51,9 @@ export async function profileDetail (e) {
mode = 'profile'
name = name.replace(/(详情|详细|面板)/, '').trim()
} else if (dmgRet) {
// mode = /武器/.test(msg) ? 'weapon' : 'dmg'
mode = 'dmg'
name = name.replace(/伤害[0-5]?/, '').trim()
name = name.replace(/(伤害|武器)+[0-7]?/, '').trim()
if (dmgRet[1]) {
dmgIdx = dmgRet[1] * 1
}
@ -91,8 +93,8 @@ export async function profileDetail (e) {
}
}
if (mode === 'profile' || mode === 'dmg') {
return renderProfile(e, char, mode, { dmgIdx })
if (mode === 'profile' || mode === 'dmg' || mode === 'weapon') {
return ProfileDetail.render(e, char, mode, { dmgIdx })
} else if (mode === 'refresh') {
await ProfileList.refresh(e)
return true
@ -100,9 +102,9 @@ export async function profileDetail (e) {
return profileArtis(e)
}
return true
}
},
export async function renderProfile (e, char, mode = 'profile', params = {}) {
async render (e, char, mode = 'profile', params = {}) {
let selfUser = await MysApi.initUser(e)
if (!selfUser) {
@ -139,7 +141,52 @@ export async function renderProfile (e, char, mode = 'profile', params = {}) {
dmg: p(Math.max(a.dmg * 1 || 0, a.phy * 1 || 0))
}
let weapon = Weapon.get(profile.weapon.name)
let w = profile.weapon
let wCfg = {}
if (mode === 'weapon') {
wCfg = weapon.calcAttr(w.level, w.promote)
wCfg.info = weapon.getAffixInfo(weapon.affix)
}
let enemyLv = await selfUser.getCfg('char.enemyLv', 91)
let dmgCalc = await ProfileDetail.getProfileDmgCalc({ profile, enemyLv, mode, params })
let rank = false
if (e.group_id && !e._profile) {
rank = await ProfileRank.create({ group: e.group_id, uid, qq: e.user_id })
await rank.getRank(profile, true)
}
let artisDetail = profile.getArtisMark()
let artisKeyTitle = ProfileArtis.getArtisKeyTitle()
let costumeSplash = profile.costumeSplash
// 渲染图像
let msgRes = await Common.render('character/profile-detail', {
save_id: uid,
uid,
data: profile.getData('name,abbr,cons,level,weapon,talent,dataSource,updateTime,imgs,costumeSplash'),
attr,
elem: char.elem,
dmgCalc,
artisDetail,
artisKeyTitle,
bodyClass: `char-${char.name}`,
mode,
wCfg,
changeProfile: e._profileMsg
}, { e, scale: 1.6, retMsgId: true })
if (msgRes && msgRes.message_id) {
// 如果消息发送成功就将message_id和图片路径存起来3小时过期
await redis.set(`miao:original-picture:${msgRes.message_id}`, JSON.stringify({
type: 'profile',
img: costumeSplash
}), { EX: 3600 * 3 })
}
return true
},
async getProfileDmgCalc ({ profile, enemyLv, mode, params }) {
let dmgMsg = []
let dmgData = []
let dmgCalc = await profile.calcDmg({
@ -157,6 +204,9 @@ export async function renderProfile (e, char, mode = 'profile', params = {}) {
msg.replace(':', '')
dmgMsg.push(msg.split(''))
})
dmgCalc.dmgMsg = dmgMsg
dmgCalc.dmgData = dmgData
}
if (mode === 'dmg' && dmgCalc.dmgRet) {
@ -171,41 +221,9 @@ export async function renderProfile (e, char, mode = 'profile', params = {}) {
basic.dmg = Format.comma(basic.dmg)
basic.avg = Format.comma(basic.avg)
}
let rank = false
if (e.group_id && !e._profile) {
rank = await ProfileRank.create({ group: e.group_id, uid, qq: e.user_id })
await rank.getRank(profile, true)
}
let artisDetail = profile.getArtisMark()
let artisKeyTitle = ProfileArtis.getArtisKeyTitle()
let imgs = char.getImgs(profile.costume)
let costumeSplash = profile.costumeSplash
// 渲染图像
let msgRes = await Common.render('character/profile-detail', {
save_id: uid,
uid,
data: profile.getData('name,abbr,cons,level,weapon,talent,dataSource,updateTime'),
attr,
elem: char.elem,
dmgData,
dmgMsg,
dmgRet: dmgCalc.dmgRet || false,
dmgCfg: dmgCalc.dmgCfg || false,
artisDetail,
artisKeyTitle,
enemyLv,
imgs,
costumeSplash,
enemyName: dmgCalc.enemyName || '小宝',
talentMap: { a: '普攻', e: '战技', q: '爆发' },
bodyClass: `char-${char.name}`,
mode,
changeProfile: e._profileMsg
}, { e, scale: 1.6, retMsgId: true })
if (msgRes && msgRes.message_id) {
// 如果消息发送成功就将message_id和图片路径存起来3小时过期
await redis.set(`miao:original-picture:${msgRes.message_id}`, JSON.stringify({ type: 'profile', img: costumeSplash }), { EX: 3600 * 3 })
return dmgCalc
}
return true
}
export default ProfileDetail

View File

@ -1,5 +1,5 @@
import { Character, ProfileRank, ProfileDmg, Player } from '../../models/index.js'
import { renderProfile } from './ProfileDetail.js'
import ProfileDetail from './ProfileDetail.js'
import { Data, Common, Format } from '../../components/index.js'
import lodash from 'lodash'
@ -32,7 +32,7 @@ export async function groupRank (e) {
let player = Player.create(100000000)
if (player.getProfile(char.id)) {
e.uid = 100000000
return await renderProfile(e, char)
return await ProfileDetail.render(e, char)
} else {
return true
}
@ -51,7 +51,7 @@ export async function groupRank (e) {
let uid = await ProfileRank.getGroupMaxUid(groupId, char.id, mode)
if (uid) {
e.uid = uid
return await renderProfile(e, char)
return await ProfileDetail.render(e, char)
} else {
if (mode === 'dmg' && !ProfileDmg.dmgRulePath(char.name)) {
e.reply(`暂无排名:${char.name}暂不支持伤害计算,无法进行排名..`)

View File

@ -75,7 +75,6 @@ class Artifact extends Base {
static getAttrsByIds (ids, star = 5) {
let ret = []
let tmp = {}
let starEff = { 1: 0.21, 2: 0.36, 3: 0.6, 4: 0.8, 5: 1 }
lodash.forEach(ids, (id) => {
let cfg = attrIdMap[id]
if (!cfg) {
@ -85,14 +84,12 @@ class Artifact extends Base {
if (!tmp[key]) {
tmp[key] = {
key,
eff: 0,
upNum: 0,
value: 0
}
ret.push(tmp[key])
}
tmp[key].eff += value / attrMap[key].value * starEff[star]
tmp[key].value += value * (attrMap[key].format === 'pct' ? 100 : 1) * starEff[star]
tmp[key].value += value * (attrMap[key].format === 'pct' ? 100 : 1)
tmp[key].upNum++
})
return ret

View File

@ -18,6 +18,10 @@ export default class ProfileData extends AvatarData {
return this.isProfile
}
get imgs(){
return this.char.getImgs(this.costume)
}
get costumeSplash () {
let costume = this._costume
costume = this.char.checkCostume(costume) ? '2' : ''

View File

@ -21,7 +21,7 @@ export default class ProfileDmg extends Base {
static dmgRulePath (name) {
const _path = process.cwd()
let path = `${_path}/plugins/miao-plugin/resources/meta/character/${name}/calc_auto.js`
if (fs.existsSync(path)&&Common.cfg('teamCalc')) {
if (fs.existsSync(path) && Common.cfg('teamCalc')) {
return path
}
path = `${_path}/plugins/miao-plugin/resources/meta/character/${name}/calc.js`
@ -243,7 +243,8 @@ export default class ProfileDmg extends Base {
msg,
dmgRet,
enemyName,
dmgCfg: dmgDetail
dmgCfg: dmgDetail,
enemyLv
}
}
}

View File

@ -56,7 +56,12 @@ export default class ProfileReq extends Base {
}
}
log (msg) {
logger.mark(`${logger.cyan(`【面板】${this.uid}`)} ${msg}`)
}
async requestProfile (player, serv) {
let self = this
this.serv = serv
let uid = this.uid
let reqParam = await serv.getReqParam(uid)
@ -65,7 +70,6 @@ export default class ProfileReq extends Base {
return this.err(`请求过快,请${cdTime}秒后重试..`)
}
await this.setCd(20)
let self = this
// 若3秒后还未响应则返回提示
setTimeout(() => {
if (self._isReq) {
@ -73,7 +77,8 @@ export default class ProfileReq extends Base {
}
}, 3000)
// 发起请求
logger.mark(`面板请求UID:${uid},面板服务:${serv.name}...`)
this.log(`${logger.yellow('开始请求数据')},面板服务:${serv.name}...`)
const startTime = new Date() * 1
let data = {}
try {
let params = reqParam.params || {}
@ -82,6 +87,8 @@ export default class ProfileReq extends Base {
let req = await fetch(reqParam.url, params)
data = await req.text()
self._isReq = false
const reqTime = new Date() * 1 - startTime
this.log(`${logger.green(`请求结束,请求用时${reqTime}ms`)},面板服务:${serv.name}...`)
if (data[0] === '<') {
let titleRet = /<title>(.+)<\/title>/.exec(data)
if (titleRet && titleRet[1]) {

View File

@ -33,6 +33,14 @@ class Weapon extends Base {
return `meta/weapon/${this.type}/${this.name}/icon.webp`
}
get imgs () {
return {
icon: `meta/weapon/${this.type}/${this.name}/icon.webp`,
icon2: `meta/weapon/${this.type}/${this.name}/awaken.webp`,
gacha: `meta/weapon/${this.type}/${this.name}/gacha.webp`
}
}
get icon () {
return this.img
}
@ -50,19 +58,6 @@ class Weapon extends Base {
return (datas['0'] && datas['0'][4]) ? 5 : 1
}
getDetail () {
if (this._detail) {
return this._detail
}
const path = 'resources/meta/weapon'
try {
this._detail = Data.readJSON(`${path}/${this.type}/${this.name}/data.json`)
} catch (e) {
console.log(e)
}
return this._detail
}
static isWeaponSet (name) {
return weaponSet.includes(name)
}
@ -80,6 +75,72 @@ class Weapon extends Base {
}
return false
}
getDetail () {
if (this._detail) {
return this._detail
}
const path = 'resources/meta/weapon'
try {
this._detail = Data.readJSON(`${path}/${this.type}/${this.name}/data.json`)
} catch (e) {
console.log(e)
}
return this._detail
}
calcAttr (level, promote = -1) {
let lvLeft = 1
let lvRight = 20
let lvStep = [1, 20, 40, 50, 60, 70, 80, 90]
let currPromote = 0
for (let idx = 0; idx < lvStep.length - 1; idx++) {
if (promote === -1 || (currPromote === promote)) {
if (level >= lvStep[idx] && level <= lvStep[idx + 1]) {
lvLeft = lvStep[idx]
lvRight = lvStep[idx + 1]
break
}
}
currPromote++
}
let wAttr = this?.detail?.attr || {}
let wAtk = wAttr.atk || {}
let valueLeft = wAtk[lvLeft + '+'] || wAtk[lvLeft] || {}
let valueRight = wAtk[lvRight] || {}
let atkBase = valueLeft * 1 + ((valueRight - valueLeft) * (level - lvLeft) / (lvRight - lvLeft))
let wBonus = wAttr.bonusData || {}
valueLeft = wBonus[lvLeft + '+'] || wBonus[lvLeft]
valueRight = wBonus[lvRight]
let stepCount = Math.ceil((lvRight - lvLeft) / 5)
let valueStep = (valueRight - valueLeft) / stepCount
let value = valueLeft + (stepCount - Math.ceil((lvRight - level) / 5)) * valueStep
return {
atkBase,
attr: {
key: wAttr.bonusKey,
value
}
}
}
getAffixInfo (affix) {
let d = this.getDetail()
let ad = this.detail.affixData
let txt = ad.text
lodash.forEach(ad.datas, (ds, idx) => {
txt = txt.replace(`$[${idx}]`, ds[affix - 1])
})
return {
name: d.name,
star: d.star,
desc: d.desc,
imgs: this.imgs,
affix,
affixTitle: d.affixTitle,
affixDetail: txt
}
}
}
export default Weapon

View File

@ -70,7 +70,7 @@ let ArtisMark = {
let isIdAttr = false
lodash.forEach(ds, (d) => {
isIdAttr = !!d.eff
isIdAttr = !d.isCalcNum
let arti = ArtisMark.formatArti(d, charAttrCfg)
ret.push(arti)
if (isIdAttr) {
@ -115,13 +115,15 @@ let ArtisMark = {
val = Format[arrCfg.format](val, 1)
let ret = {
key,
value: val
value: val,
upNum: ds.upNum || 0
}
if (!isMain && !ret.eff) {
if (!isMain && !ret.upNum) {
let incRet = ArtisMark.getIncNum(key, value)
ret.upNum = incRet.num
ret.hasGt = incRet.hasGt
ret.hasLt = incRet.hasLt
ret.isCalcNum = true
}
if (charAttrCfg) {

View File

@ -120,34 +120,12 @@ class AttrCalc {
setWeaponAttr () {
let wData = this.profile?.weapon || {}
let weapon = Weapon.get(wData?.name)
let level = wData.level
let promote = lodash.isUndefined(wData.promote) ? -1 : wData.promote
let lvLeft = 1
let lvRight = 20
let lvStep = [1, 20, 40, 50, 60, 70, 80, 90]
let currPromote = 0
for (let idx = 0; idx < lvStep.length - 1; idx++) {
if (promote === -1 || (currPromote === promote)) {
if (level >= lvStep[idx] && level <= lvStep[idx + 1]) {
lvLeft = lvStep[idx]
lvRight = lvStep[idx + 1]
break
let wCalcRet = weapon.calcAttr(wData.level, wData.promote)
if (wCalcRet) {
this.addAttr('atkBase', wCalcRet.atkBase)
this.addAttr(wCalcRet.key, wCalcRet.value)
}
}
currPromote++
}
let wAttr = weapon?.detail?.attr || {}
let wAtk = wAttr.atk || {}
let valueLeft = wAtk[lvLeft + '+'] || wAtk[lvLeft] || {}
let valueRight = wAtk[lvRight] || {}
this.addAttr('atkBase', valueLeft * 1 + ((valueRight - valueLeft) * (level - lvLeft) / (lvRight - lvLeft)))
let wBonus = wAttr.bonusData || {}
valueLeft = wBonus[lvLeft + '+'] || wBonus[lvLeft]
valueRight = wBonus[lvRight]
let stepCount = Math.ceil((lvRight - lvLeft) / 5)
let valueStep = (valueRight - valueLeft) / stepCount
let add = valueLeft + (stepCount - Math.ceil((lvRight - level) / 5)) * valueStep
this.addAttr(wAttr.bonusKey, add)
let wBuffs = weaponBuffs[weapon.name] || []
if (lodash.isPlainObject(wBuffs)) {

View File

@ -359,14 +359,16 @@ body {
.profile-mode .dmg-title {
width: 33.3333%;
}
.dmg-mode .dmg-idx {
.dmg-mode .dmg-idx,
.weapon-mode .dmg-idx {
display: table-cell;
width: 5%;
min-width: initial;
padding-right: 0;
text-align: center;
}
.dmg-mode .dmg-title {
.dmg-mode .dmg-title,
.weapon-mode .dmg-title {
width: 31%;
min-width: initial;
text-align: left;

View File

@ -5,17 +5,21 @@
{{/block}}
{{set weapon = data.weapon}}
{{set dataSource = data.dataSource}}
{{set dataSource = data.dataSource}}
{{set talentMap = {a: '普攻', e: '战技', q: '爆发'} }}
{{set {imgs,costumeSplash} = data }}
{{block 'main'}}
<div class="basic">
<div class="main-pic"
style="background-image:url({{_res_path}}{{costumeSplash || imgs?.splash}})"></div>
style="background-image:url({{_res_path}}{{costumeSplash||imgs?.splash}})"></div>
<div class="detail">
<div class="char-name">{{data.name}}</div>
<div class="char-lv">UID {{uid}} - Lv.{{data.level}}
<span class="cons cons-{{data.cons}}">{{data.cons}}命</span></div>
<div>
{{if mode !== 'weapon'}}
<div class="char-talents">
{{each talentMap tName key}}
{{set talent = data.talent[key] || {} }}
@ -40,8 +44,19 @@
<li><i class="i-re"></i>元素充能<strong>{{attr.recharge}}</strong></li>
<li><i class="i-dmg"></i>伤害加成<strong>{{attr.dmg}}</strong></li>
</ul>
{{else}}
<div class="char-weapon">
<div class="img" style="background-image:url({{_res_path}}{{weapon.img}})"></div>
<div class="head">
<strong>{{weapon.name}}</strong>
<div class="star star-{{weapon.star}}"></div>
<span>Lv.{{weapon.leve || weapon.level}} <span
class="affix affix-{{weapon.affix}}">精{{weapon.affix}}</span></span>
</div>
</div>
{{/if}}
</div>
</div>
<div class="char-cons">
{{set cons = [1,2,3,4,5,6]}}
{{each cons idx}}
@ -72,9 +87,11 @@
</div>
{{/if}}
{{if mode === "profile"}}
{{set ad = artisDetail}}
<div class="artis">
<!-- 【 武器+圣遗物列表】 -->
<div>
{{if mode === "profile"}}
{{set ad = artisDetail}}
<div class="artis">
<div>
<div class="item weapon">
<div class="img" style="background-image:url({{_res_path}}{{weapon.img}})"></div>
@ -119,21 +136,26 @@
{{/if}}
</div>
{{/each}}
</div>
{{/if}}
</div>
{{/if}}
{{if mode === "profile"}}
<div class="cont">
<!-- 【 伤害表格 】 -->
{{ set {dmgRet, dmgCfg, enemyName, dmgMsg, dmgData} = dmgCalc }}
<div>
{{if mode === "profile"}}
<div class="cont">
<div class="cont-footer dmg-desc">
当前评分为<strong>喵喵版评分规则</strong>,分值仅供参考与娱乐。可使用<strong>#{{data.abbr}}圣遗物</strong>来查看评分详情
</div>
</div>
{{/if}}
</div>
{{/if}}
{{if dmgData.length > 0}}
<div class="dmg-cont dmg-list cont">
{{if dmgData.length > 0}}
<div class="dmg-cont dmg-list cont">
<div class="cont-title">
伤害计算<span>目标为{{enemyLv}}级{{enemyName}},如需调整等级可使用 #敌人等级{{enemyLv}} 来进行设置</span>
伤害计算<span>目标为{{enemyLv}}级{{enemyName||'小宝'}},如需调整等级可使用 #敌人等级{{enemyLv}} 来进行设置</span>
</div>
<div class="cont-table">
<div class="tr thead">
@ -159,14 +181,15 @@
<div class="cont-footer dmg-desc">
使用命令<strong>#{{data.abbr}}伤害</strong>可以查看伤害详情,使用命令<strong>#角色面板帮助</strong>可查看帮助说明
</div>
</div>
{{/if}}
</div>
{{/if}}
{{if mode === "dmg"}}
{{if dmgCfg && dmgCfg.attr && dmgCfg.attr.length>0 && dmgRet}}
<div class="dmg-calc dmg-cont cont">
<!-- 【 伤害变化详情 】 -->
<div>
{{if mode === "dmg"}}
{{if dmgCfg && dmgCfg.attr && dmgCfg.attr.length>0 && dmgRet && dmgRet.length >3}}
<div class="dmg-calc dmg-cont cont">
<div class="cont-title">
词条伤害计算<span>#{{data.abbr}}伤害{{dmgCfg.userIdx+1}}: 当前计算为[{{dmgCfg.title}}]</span>
</div>
@ -211,10 +234,10 @@
<li>如需更换计算的伤害类型,可使用命令 <strong>#{{data.abbr}}伤害+序号</strong>来切换,序号参见伤害计算板块</li>
</ul>
</div>
</div>
{{/if}}
</div>
{{/if}}
<div class="dmg-cont dmg-msg cont">
<div class="dmg-cont dmg-msg cont">
<div class="cont-title">Buff列表<span>部分Buff的触发条件以及层数可能影响实际伤害结果</span></div>
<div class="cont-table">
{{each dmgMsg msg}}
@ -224,6 +247,8 @@
</div>
{{/each}}
</div>
</div>
{{/if}}
</div>
{{/if}}
{{/block}}

View File

@ -433,20 +433,23 @@ body {
width: 33.3333%;
}
.dmg-mode .dmg-idx {
.dmg-mode,
.weapon-mode {
.dmg-idx {
display: table-cell;
width: 5%;
min-width: initial;
padding-right: 0;
text-align: center;
}
}
.dmg-mode .dmg-title {
.dmg-title {
width: 31%;
min-width: initial;
text-align: left;
padding-left: 10px;
padding-right: 0;
}
}