* #刻晴 角色卡片功能升级

* `#老婆设置刻晴,心海`不再检查是否具有角色或展示在米游社展柜
    * `#刻晴` 角色卡片优先使用面板数据进行展示,无面板数据时使用米游社数据
    * 在未能获取到角色数据时也会展示角色卡片
    * 角色卡片后续会进行样式升级,按需展示更多内容
* `#深渊出场率`增加楼层排序,以防止服务侧数据乱序导致顺序错乱
* B服面板的天赋与皇冠信息现在可以正确的展示了
This commit is contained in:
yoimiya-kokomi 2022-06-30 04:40:06 +08:00
parent b8019ff73e
commit b9eb8695ed
12 changed files with 238 additions and 147 deletions

View File

@ -1,17 +1,22 @@
# 1.8.2
# 1.8.3
* `#刻晴` 角色卡片功能升级
* `#老婆设置刻晴,心海`不再检查是否具有角色或展示在米游社展柜
* `#刻晴` 角色卡片优先使用面板数据进行展示,无面板数据时使用米游社数据
* 在未能获取到角色数据时也会展示角色卡片
* 角色卡片后续会进行样式升级,按需展示更多内容
* `#深渊出场率`增加楼层排序,以防止服务侧数据乱序导致顺序错乱
* B服面板的天赋与皇冠信息现在可以正确的展示了
# 1.8.1~1.8.2
* `#喵喵帮助`增加对自定义配置文件的支持
* 已有配置文件可更名为help-cfg.js防止后续更新冲突后续会支持更多配置项
* 在默认配置中增加部分新帮助命令
* `#喵喵日历`现在可通过`#日历 #日历列表`触发
* 修正胡桃E伤害计算错误
# 1.8.1
* 增加`#添加刻晴图像`命令,感谢 **@叶**
* 可通过命令上传添加指定角色图片,上传至 **resources/character-img/刻晴/upload**
* 请将图像与命令一同发送后续会支持at图像及命令后发送图像
* 部分功能的页面文案及功能优化
* `#喵喵日历`现在可通过`#日历 #日历列表`触发
# 1.8.0

View File

@ -1,8 +1,8 @@
import { Character } from "../../components/models.js";
import { Artifact, Character } from "../../components/models.js";
import { Cfg } from "../../components/index.js";
import lodash from "lodash";
import { segment } from "oicq";
import Common from "../../components/Common.js";
import { Common, Profile } from "../../components/index.js";
//角色昵称
let nameID = "";
@ -26,42 +26,45 @@ export async function renderAvatar(e, avatar, render, renderType = "card") {
return false;
}
let MysApi = await e.getMysApi({
auth: "all",
targetType: Cfg.get("char.queryOther", true) ? "all" : "self",
cookieType: "all",
actionName: "查询信息"
});
if (!MysApi) return true;
let charData = await MysApi.getCharacter();
if (!charData) return true;
let uid = e.targetUser.uid;
let avatars = charData.avatars;
let length = avatars.length;
char.checkAvatars(avatars);
avatars = lodash.keyBy(avatars, "id");
if (!avatars[char.id]) {
let name = lodash.truncate(e.sender.card, { length: 8 });
if (length > 8) {
e.reply([segment.at(e.user_id, name), `\n没有${e.msg}`]);
} else {
e.reply([segment.at(e.user_id, name), "\n请先在米游社展示该角色"]);
}
return true;
let profile = await Profile.get(uid, char.id, true);
if (profile) {
// 优先使用Profile数据
avatar = profile;
} else {
// 使用Mys数据兜底
let charData = await MysApi.getCharacter();
if (!charData) return true;
let avatars = charData.avatars;
let length = avatars.length;
char.checkAvatars(avatars);
avatars = lodash.keyBy(avatars, "id");
avatar = avatars[char.id] || { id: char.id, name: char.name, detail: false };
}
avatar = avatars[char.id];
}
return await renderCard(e, avatar, render, renderType);
}
// 渲染角色卡片
async function renderCard(e, avatar, render, renderType = "card") {
let talent = await getTalent(e, avatar);
// 计算皇冠个数
let crownNum = lodash.filter(lodash.map(talent, (d) => d.level_original), (d) => d >= 10).length;
let crownNum = lodash.filter(lodash.map(talent, (d) => d.original), (d) => d >= 10).length;
let uid = e.uid || (e.targetUser && e.targetUser.uid);
@ -97,54 +100,40 @@ async function renderCard(e, avatar, render, renderType = "card") {
//获取角色技能数据
async function getTalent(e, avatars) {
let MysApi = await e.getMysApi({
auth: "all",
targetType: Cfg.get("char.queryOther", true) ? "all" : "self",
cookieType: "all",
actionName: "查询信息"
});
if (!MysApi && !MysApi.isSelfCookie) return {};
let skill = {};
let skillRes = await MysApi.getAvatar(avatars.id);
if (skillRes && skillRes.skill_list) {
skill.id = avatars.id;
let skill_list = lodash.orderBy(skillRes.skill_list, ["id"], ["asc"]);
for (let val of skill_list) {
val.level_original = val.level_current;
if (val.name.includes("普通攻击")) {
skill.a = val;
continue;
}
if (val.max_level >= 10 && !skill.e) {
skill.e = val;
continue;
}
if (val.max_level >= 10 && !skill.q) {
skill.q = val;
}
}
if (avatars.actived_constellation_num >= 3) {
if (avatars.constellations[2].effect.includes(skill.e.name)) {
skill.e.level_current += 3;
} else if (avatars.constellations[2].effect.includes(skill.q.name)) {
skill.q.level_current += 3;
}
}
if (avatars.actived_constellation_num >= 5) {
if (avatars.constellations[4].effect.includes(skill.e.name)) {
skill.e.level_current += 3;
} else if (avatars.constellations[4].effect.includes(skill.q.name)) {
skill.q.level_current += 3;
let talent = {}, cons = 0, char = Character.get(avatars.id), mode = "level";
if (avatars.dataSource && avatars.talent) {
// profile模式
talent = avatars.talent || {};
cons = avatars.cons || 0;
} else {
let MysApi = await e.getMysApi({
auth: "all",
targetType: Cfg.get("char.queryOther", true) ? "all" : "self",
cookieType: "all",
actionName: "查询信息"
});
if (!MysApi && !MysApi.isSelfCookie) return {};
let skillRes = await MysApi.getAvatar(avatars.id);
cons = avatars.actived_constellation_num
mode = "original";
if (skillRes && skillRes.skill_list) {
let skill_list = lodash.orderBy(skillRes.skill_list, ["id"], ["asc"]);
for (let val of skill_list) {
if (val.name.includes("普通攻击")) {
talent.a = val;
continue;
}
if (val.max_level >= 10 && !talent.e) {
talent.e = val;
continue;
}
if (val.max_level >= 10 && !talent.q) {
talent.q = val;
}
}
}
}
return skill;
return char.getAvatarTalent(talent, cons, mode);
}
@ -181,54 +170,41 @@ async function pokeCharacter(e, { render }) {
function getCharacterData(avatars) {
let list = [];
let set = {};
let setArr = [];
let text1 = "";
let text2 = "";
let artiEffect = [];
let w = avatars.weapon || {};
let weapon = {
type: "weapon",
name: avatars.weapon.name,
showName: genshin.abbr[avatars.weapon.name] ? genshin.abbr[avatars.weapon.name] : avatars.weapon.name,
level: avatars.weapon.level,
affix_level: avatars.weapon.affix_level,
};
name: w.name || "",
showName: genshin.abbr[w.name] || w.name || "",
level: w.level || 1,
affix: w.affix || w.affix_level || 0
}
for (let val of avatars.reliquaries) {
if (set[val.set.name]) {
set[val.set.name]++;
let artis = avatars.artis || avatars.reliquaries;
if (set[val.set.name] == 2) {
if (text1) {
text2 = "2件套" + val.set.affixes[0].effect;
} else {
text1 = "2件套" + val.set.affixes[0].effect;
if (artis) {
lodash.forEach(artis, (val) => {
let setCfg = Artifact.getSetByArti(val.name);
if (!setCfg) {
return;
}
let setName = setCfg.name;
if (set[setName]) {
set[setName]++;
if (set[setName] === 2) {
artiEffect.push("2件套" + setCfg.effect['2'])
}
if (set[setName] === 4) {
artiEffect.push("4件套" + setCfg.name);
}
} else {
set[setName] = 1;
}
})
if (set[val.set.name] == 4) {
text2 = "4件套" + val.set.name;
}
} else {
set[val.set.name] = 1;
if (artiEffect.length === 0) {
artiEffect = ["无套装效果"];
}
list.push({
type: "reliquaries",
name: val.name,
level: val.level,
});
}
for (let val of Object.keys(set)) {
setArr.push({
name: val,
num: set[val],
showName: genshin.abbr[val] ? genshin.abbr[val] : val,
});
}
if (avatars.reliquaries.length >= 2 && !text1) {
text1 = "无套装效果";
}
if (avatars.id == "10000005") {
@ -241,14 +217,12 @@ function getCharacterData(avatars) {
return {
name: avatars.name,
showName: genshin.abbr[avatars.name] ? genshin.abbr[avatars.name] : avatars.name,
level: avatars.level,
level: avatars.lv || avatars.level,
fetter: avatars.fetter,
actived_constellation_num: avatars.actived_constellation_num,
cons: avatars.cons || avatars.actived_constellation_num,
weapon,
text1,
text2,
reliquaries,
set: setArr,
artiEffect,
reliquaries
};
}
@ -280,4 +254,8 @@ export async function getAvatarList(e, type, MysApi) {
let sortKey = "level,fetter,weapon_level,rarity,weapon_rarity,cons,weapon_affix_level";
list = lodash.orderBy(list, sortKey, lodash.repeat("desc,", sortKey.length).split(","));
return list;
}
export function checkWifeType(id, type) {
return genshin.wifeData[type].includes(Number(id));
}

View File

@ -1,8 +1,8 @@
//#老婆
import lodash from "lodash";
import { Cfg } from "../../components/index.js";
import {Character} from "../../components/models.js";
import { getAvatarList, renderAvatar } from "./avatar-card.js";
import { Character } from "../../components/models.js";
import { checkWifeType, getAvatarList, renderAvatar } from "./avatar-card.js";
const relationMap = {
wife: {
@ -68,12 +68,10 @@ export async function wife(e, { render, User }) {
cookieType: "all",
actionName: "查询信息"
});
if (!MysApi || !MysApi.selfUser) {
return true;
}
let selfUser = MysApi.selfUser;
let selfMysUser = await selfUser.getMysUser();
let isSelf = true;
if (!selfMysUser || selfMysUser.uid !== MysApi.targetUser.uid) {
@ -131,20 +129,20 @@ export async function wife(e, { render, User }) {
} else {
wifeList = lodash.map(wifeList, (name) => {
let char = Character.get(name);
if (char) {
if (char && checkWifeType(char.id, targetCfg.type)) {
return char.name;
}
});
wifeList = lodash.filter(lodash.uniq(wifeList), (d) => !!d);
/*
avatarList = await getAvatarList(e, targetCfg.type, MysApi);
avatarList = lodash.map(avatarList, (avatar) => avatar.name);
avatarList = lodash.filter(avatarList, (d) => !!d);
addRet = lodash.intersection(avatarList, wifeList);
*/
addRet = wifeList;
if (addRet.length === 0) {
e.reply(`在可选的${targetCfg.keyword[0]}列表中未能找到 ${actionParam} ~`);
if (!MysApi.isSelfCookie) {
e.reply("请确认已在米游社展示对应角色也可以绑定Cookie以查询所有角色..");
}
return true;
}
}
@ -156,12 +154,10 @@ export async function wife(e, { render, User }) {
case "是":
case "是谁":
// 查看当前选择老婆
if (!isSelf) {
e.reply("只能查看自己的哦~");
return true;
}
wifeList = await selfUser.getCfg(`wife.${targetCfg.key}`, []);
if (wifeList && wifeList.length > 0) {
e.reply(`你的${targetCfg.keyword[0]}是:${wifeList.join("")}`);

View File

@ -59,7 +59,7 @@ export async function renderProfile(e, char, render, mode = "profile", params =
};
let { artis,totalMark, totalMarkClass,usefulMark } = getArtis(char.name, profile.artis);
let { artis, totalMark, totalMarkClass, usefulMark } = getArtis(char.name, profile.artis);
let enemyLv = await selfUser.getCfg(`char.enemyLv`, 91);
@ -105,6 +105,7 @@ export async function renderProfile(e, char, render, mode = "profile", params =
cons: char.cons,
name: char.name,
elem: char.elem,
talent: char.getAvatarTalent(profile.talent, profile.cons),
dmgData,
dmgMsg,
dmgRet: dmgCalc.dmgRet,

View File

@ -120,10 +120,12 @@ export async function abyssPct(e, { render }) {
}
});
lodash.forEach(abyssData.data, (floorData) => {
let data = abyssData.data;
data = lodash.sortBy(data, "floor");
lodash.forEach(data, (floorData) => {
let floor = {
floor: floorData.floor,
};
let avatars = [];
lodash.forEach(floorData.avatarUsage, (ds) => {

View File

@ -116,7 +116,15 @@ let Profile = {
return false;
},
async get(uid, charId) {
async get(uid, charId, onlyAvailable = false) {
if (onlyAvailable) {
let data = await Profile.get(uid, charId);
if (data && data.dataSource && data.dataSource !== "input") {
return data;
}
return false;
}
let data = Profile._get(uid, charId);
let Serv = getServ(uid);
if (Serv.getCharData && data && data.id) {

View File

@ -3,6 +3,17 @@ import { attrValue, attrNameMap, attrMap, mainAttr, subAttr, usefulAttr }
import { Character } from "../models.js";
import lodash from "lodash";
import Format from "../Format.js";
import _Data from "../Data.js";
let _path = process.cwd();
let artis = _Data.readJSON(`${_path}/plugins/miao-plugin/resources/meta/reliquaries/`, "data.json") || {};
let artisMap = {}
lodash.forEach(artis, (ds) => {
artisMap[ds.name] = ds;
})
let charCfg = {};
let Artifact = {
@ -53,6 +64,7 @@ let Artifact = {
};
return charCfg[name];
},
getMaxAttr(charAttr = {}, list2 = [], maxLen = 1, banAttr = "") {
let tmp = [];
lodash.forEach(list2, (attr) => {
@ -66,6 +78,7 @@ let Artifact = {
lodash.forEach(tmp, (ds) => ret.push(ds.attr));
return ret.slice(0, maxLen);
},
getMaxMark(attrWeight) {
let ret = {};
for (let idx = 1; idx <= 5; idx++) {
@ -90,6 +103,7 @@ let Artifact = {
}
return ret;
},
getAttr(ds) {
let title = ds[0]
let attr = attrNameMap[title];
@ -100,6 +114,7 @@ let Artifact = {
}
return attr;
},
getAttrMark(attrMark, ds) {
if (!ds || !ds[1]) {
return 0;
@ -108,6 +123,7 @@ let Artifact = {
let val = ds[1];
return (attrMark[attr] || 0) * val;
},
getMark(charCfg, posIdx, mainAttr, subAttr) {
let ret = 0;
let { mark, maxMark, weight } = charCfg;
@ -136,6 +152,7 @@ let Artifact = {
});
return ret;
},
getMarkClass(mark) {
let pct = mark;
let scoreMap = [["D", 10], ["C", 16.5], ["B", 23.1], ["A", 29.7], ["S", 36.3], ["SS", 42.9], ["SSS", 49.5], ["ACE", 56.1], ["ACE²", 66]];
@ -145,20 +162,24 @@ let Artifact = {
}
}
},
getSet(name) {
for (let idx in meta) {
if (meta[idx].name === name) {
return meta[idx];
getSetByArti(name) {
for (let idx in artisMap) {
for (let idx2 in artisMap[idx].sets) {
if (artisMap[idx].sets[idx2].name === name) {
return artisMap[idx];
}
}
}
return false;
},
getMeta() {
return {
attrMap
}
},
formatArti(ds, markCfg = false, isMain = false) {
if (lodash.isArray(ds[0])) {
let ret = [];

View File

@ -99,6 +99,50 @@ class Character extends Base {
}
}
getAvatarTalent(talent = {}, cons = 0, mode = "level") {
let ret = {};
let consTalent = this.getConsTalent();
lodash.forEach(['a', 'e', 'q'], (key) => {
let ds = talent[key];
if (ds) {
let level;
if (lodash.isNumber(ds)) {
level = ds;
} else {
level = mode === "level" ? ds.level || ds.level_current || ds.original || ds.level_original :
ds.original || ds.level_original || ds.level || ds.level_current;
}
if (mode === "level") {
// 基于level计算original
ret[key] = {
level,
original: (key !== "a" && cons >= consTalent[key]) ? (level - 3) : level
}
} else {
// 基于original计算level
ret[key] = {
original: level,
level: (key !== "a" && cons >= consTalent[key]) ? (level + 3) : level
}
}
}
})
return ret;
}
getConsTalent() {
let e = this.talent.e.name,
q = this.talent.q.name;
let c3 = this.cons['3'].desc,
c5 = this.cons['5'].desc;
return {
e: c3.includes(e) ? 3 : 5,
q: c5.includes(q) ? 5 : 3,
}
}
get weaponType() {
const map = {
sword: "单手剑",

View File

@ -419,4 +419,17 @@ body.bottom_mode {
line-height: 20px;
font-size: 10px;
}
.no-info {
position: absolute;
padding: 5px 10px;
bottom: 30px;
right: 10px;
font-size: 16px;
background: rgba(0, 0, 0, 0.5);
text-shadow: 1px 1px 1px #000;
border-radius: 5px;
}
.bottom_mode .no-info {
bottom: 25px;
}
/*# sourceMappingURL=card.css.map */

View File

@ -7,12 +7,14 @@
<link rel="stylesheet" type="text/css" href="{{_res_path}}/character/card.css?v=1.0"/>
</head>
<body class="{{bg.mode}}_mode" {{sys.scale}}>
<div class="container" id="container">
{{if level}}
<div class="char_name">
<div class="uid">ID:{{uid}}</div>
<span>{{ds.name}}</span>
<span
class="cons cons-{{actived_constellation_num}}">{{actived_constellation_num}}命</span>
class="cons cons-{{cons}}">{{cons}}命</span>
</div>
<div class="info">
<div class="detail"> Lv.{{level}} ❤{{fetter}}
@ -24,11 +26,11 @@
<img title="{{weapon.name}}" src="{{_sys_res_path}}/genshin/logo/weapon/{{weapon.name}}.png"/>
<p class="weapon_lv">Lv.{{weapon.level}}</p>
<p class="weapon_affix">{{weapon.affix_level}}</p>
<p class="weapon_affix">{{weapon.affix}}</p>
</div>
<p class="weapon_name">
<span class="for_left">Lv.{{weapon.level}} 精{{weapon.affix_level}}</span>
<span class="for_bottom">精{{weapon.affix_level}} </span>
<span class="for_left">Lv.{{weapon.level}} 精{{weapon.affix}}</span>
<span class="for_bottom">精{{weapon.affix}} </span>
{{weapon.name}}
</p>
</div>
@ -36,7 +38,7 @@
{{ if talent.a }}
<div class="talent">
{{each talentMap name key}}
<div class="talent_{{key}}">{{name}}: <span>{{ talent[key].level_current}}</span></div>
<div class="talent_{{key}}">{{name}}: <span>{{ talent[key].level}}</span></div>
{{/each}}
</div>
{{else}}
@ -45,10 +47,17 @@
</div>
{{/if}}
<div class="equiv_info">
<div class="text">{{text1|| "-"}}</div>
<div class="text">{{text2 || "-"}}</div>
<div class="text">{{artiEffect[0]|| "-"}}</div>
<div class="text">{{artiEffect[1] || "-"}}</div>
</div>
</div>
{{else}}
<div class="char_name">
<div class="uid">ID:{{uid}}</div>
<span>{{ds.name}}</span>
</div>
<div class="no-info">未能获取到角色信息,请将角色放置在米游社角色展柜中</div>
{{/if}}
<div class="copyright">{{@sys.copyright}}</div>
<div>
<img src="{{_res_path}}{{bg.img}}" title="{{name}}" class="bg"></div>

View File

@ -506,4 +506,19 @@ body.bottom_mode {
text-shadow: 1px 1px 1px #000;
line-height: 20px;
font-size: 10px;
}
.no-info {
position: absolute;
padding: 5px 10px;
bottom: 30px;
right: 10px;
font-size: 16px;
background: rgba(0, 0, 0, 0.5);
text-shadow: 1px 1px 1px #000;
border-radius: 5px;
}
.bottom_mode .no-info {
bottom: 25px;
}

View File

@ -5,7 +5,6 @@
{{/block}}
{{set weapon = data.weapon}}
{{set talent = data.talent}}
{{set dataSource = data.dataSource}}
@ -21,11 +20,11 @@
{{each talentMap tName key}}
<div class="talent-item">
<div class="talent-icon
{{talent[key].level_current > talent[key].level_original ? `talent-plus`:``}}
{{talent[key].level_original == 10 ? `talent-crown`:``}}">
{{talent[key].level > talent[key].original ? `talent-plus`:``}}
{{talent[key].original == 10 ? `talent-crown`:``}}">
<div class="talent-icon-img"
style="background-image:url({{_res_path}}/meta/character/{{name}}/talent_{{key}}.png)"></div>
<span>{{talent[key].level_current}}</span>
<span>{{talent[key].level}}</span>
</div>
</div>
{{/each}}