支持定义新角色及别名

* 支持自定义角色,自定义角色可使用`#派蒙` `#派蒙图片`触发图片查看,后续会支持更多场景
* 新增角色 派蒙、瑶瑶、白术、伐难、散兵 的角色配置及图片
* 如需扩展可在喵喵config/character.js中定义
This commit is contained in:
yoimiya-kokomi 2022-06-30 07:05:31 +08:00
parent b9eb8695ed
commit e5ca1bd5d8
35 changed files with 174 additions and 48 deletions

View File

@ -5,6 +5,10 @@
* `#刻晴` 角色卡片优先使用面板数据进行展示,无面板数据时使用米游社数据 * `#刻晴` 角色卡片优先使用面板数据进行展示,无面板数据时使用米游社数据
* 在未能获取到角色数据时也会展示角色卡片 * 在未能获取到角色数据时也会展示角色卡片
* 角色卡片后续会进行样式升级,按需展示更多内容 * 角色卡片后续会进行样式升级,按需展示更多内容
* 支持定义新角色及别名
* 支持自定义角色,自定义角色可使用`#派蒙` `#派蒙图片`触发图片查看,后续会支持更多场景
* 新增角色 派蒙、瑶瑶、白术、伐难、散兵 的角色配置及图片
* 如需扩展可在喵喵config/character.js中定义
* `#深渊出场率`增加楼层排序,以防止服务侧数据乱序导致顺序错乱 * `#深渊出场率`增加楼层排序,以防止服务侧数据乱序导致顺序错乱
* B服面板的天赋与皇冠信息现在可以正确的展示了 * B服面板的天赋与皇冠信息现在可以正确的展示了

View File

@ -99,6 +99,11 @@ export async function character(e, { render, User }) {
e.uid = await getTargetUid(e); e.uid = await getTargetUid(e);
e.avatar = char.id; e.avatar = char.id;
if (char.isCustom) {
e.reply("自定义角色暂不支持此功能");
return true;
}
if (mode === "profile" || mode === "dmg") { if (mode === "profile" || mode === "dmg") {
return renderProfile(e, char, render, mode, { dmgIdx }); return renderProfile(e, char, render, mode, { dmgIdx });
} else if (mode === "refresh" || mode === "input") { } else if (mode === "refresh" || mode === "input") {

View File

@ -26,7 +26,6 @@ export async function renderAvatar(e, avatar, render, renderType = "card") {
return false; return false;
} }
let MysApi = await e.getMysApi({ let MysApi = await e.getMysApi({
auth: "all", auth: "all",
targetType: Cfg.get("char.queryOther", true) ? "all" : "self", targetType: Cfg.get("char.queryOther", true) ? "all" : "self",
@ -38,21 +37,24 @@ export async function renderAvatar(e, avatar, render, renderType = "card") {
let uid = e.targetUser.uid; let uid = e.targetUser.uid;
if (char.isCustom) {
let profile = await Profile.get(uid, char.id, true); avatar = { id: char.id, name: char.name, detail: false }
if (profile) {
// 优先使用Profile数据
avatar = profile;
} else { } else {
// 使用Mys数据兜底 let profile = await Profile.get(uid, char.id, true);
let charData = await MysApi.getCharacter(); if (profile) {
if (!charData) return true; // 优先使用Profile数据
avatar = profile;
} else {
// 使用Mys数据兜底
let charData = await MysApi.getCharacter();
if (!charData) return true;
let avatars = charData.avatars; let avatars = charData.avatars;
let length = avatars.length; let length = avatars.length;
char.checkAvatars(avatars); char.checkAvatars(avatars);
avatars = lodash.keyBy(avatars, "id"); avatars = lodash.keyBy(avatars, "id");
avatar = avatars[char.id] || { id: char.id, name: char.name, detail: false }; avatar = avatars[char.id] || { id: char.id, name: char.name, detail: false };
}
} }
} }
return await renderCard(e, avatar, render, renderType); return await renderCard(e, avatar, render, renderType);
@ -61,20 +63,20 @@ export async function renderAvatar(e, avatar, render, renderType = "card") {
// 渲染角色卡片 // 渲染角色卡片
async function renderCard(e, avatar, render, renderType = "card") { async function renderCard(e, avatar, render, renderType = "card") {
let talent = await getTalent(e, avatar);
// 计算皇冠个数
let crownNum = lodash.filter(lodash.map(talent, (d) => d.original), (d) => d >= 10).length;
let uid = e.uid || (e.targetUser && e.targetUser.uid);
let char = Character.get(avatar); let char = Character.get(avatar);
if (!char) { if (!char) {
return false; return false;
} }
let bg = char.getCardImg(Cfg.get("char.se", false)); let uid = e.uid || (e.targetUser && e.targetUser.uid);
let crownNum = 0, talent = {};
if (!char.isCustom) {
talent = await getTalent(e, avatar);
// 计算皇冠个数
crownNum = lodash.filter(lodash.map(talent, (d) => d.original), (d) => d >= 10).length;
}
let bg = char.getCardImg(Cfg.get("char.se", false));
if (renderType === "photo") { if (renderType === "photo") {
e.reply(segment.image(process.cwd() + "/plugins/miao-plugin/resources/" + bg.img)); e.reply(segment.image(process.cwd() + "/plugins/miao-plugin/resources/" + bg.img));
} else { } else {
@ -86,6 +88,7 @@ async function renderCard(e, avatar, render, renderType = "card") {
crownNum, crownNum,
talentMap: { a: "普攻", e: "战技", q: "爆发" }, talentMap: { a: "普攻", e: "战技", q: "爆发" },
bg, bg,
custom: char.isCustom,
...getCharacterData(avatar), ...getCharacterData(avatar),
ds: char.getData("name,id,title,desc"), ds: char.getData("name,id,title,desc"),
}, { e, render, scale: 1.6 }); }, { e, render, scale: 1.6 });
@ -101,6 +104,10 @@ async function renderCard(e, avatar, render, renderType = "card") {
//获取角色技能数据 //获取角色技能数据
async function getTalent(e, avatars) { async function getTalent(e, avatars) {
let talent = {}, cons = 0, char = Character.get(avatars.id), mode = "level"; let talent = {}, cons = 0, char = Character.get(avatars.id), mode = "level";
console.log('isCustom', char.isCustom, char.id);
if (char.isCustom) {
return {}
}
if (avatars.dataSource && avatars.talent) { if (avatars.dataSource && avatars.talent) {
// profile模式 // profile模式
talent = avatars.talent || {}; talent = avatars.talent || {};
@ -133,6 +140,7 @@ async function getTalent(e, avatars) {
} }
} }
} }
return char.getAvatarTalent(talent, cons, mode); return char.getAvatarTalent(talent, cons, mode);
} }
@ -257,5 +265,5 @@ export async function getAvatarList(e, type, MysApi) {
} }
export function checkWifeType(id, type) { export function checkWifeType(id, type) {
return genshin.wifeData[type].includes(Number(id)); return Chargenshin.wifeData[type].includes(Number(id));
} }

View File

@ -129,7 +129,7 @@ export async function wife(e, { render, User }) {
} else { } else {
wifeList = lodash.map(wifeList, (name) => { wifeList = lodash.map(wifeList, (name) => {
let char = Character.get(name); let char = Character.get(name);
if (char && checkWifeType(char.id, targetCfg.type)) { if (char && char.checkWifeType(targetCfg.type)) {
return char.name; return char.name;
} }
}); });

View File

@ -17,6 +17,10 @@ export async function renderProfile(e, char, render, mode = "profile", params =
e.reply("暂不支持主角的面板信息查看"); e.reply("暂不支持主角的面板信息查看");
return true; return true;
} }
if (char.isCustom) {
e.reply(`暂不支持自定义角色${char.name}的面板信息查看`);
return true;
}
let refresh = async () => { let refresh = async () => {
let refreshRet = await autoRefresh(e); let refreshRet = await autoRefresh(e);

View File

@ -44,7 +44,7 @@ export async function wiki(e, { render }) {
} }
if (mode === "pic") { if (mode === "pic") {
let img = char.getCardImg(Cfg.get("char.se", false),false); let img = char.getCardImg(Cfg.get("char.se", false), false);
if (img && img.img) { if (img && img.img) {
e.reply(segment.image(process.cwd() + "/plugins/miao-plugin/resources/" + img.img)); e.reply(segment.image(process.cwd() + "/plugins/miao-plugin/resources/" + img.img));
} else { } else {
@ -53,6 +53,11 @@ export async function wiki(e, { render }) {
return true; return true;
} }
if (char.isCustom) {
e.reply("暂不支持自定义角色");
return true;
}
return await Common.render("wiki/character", { return await Common.render("wiki/character", {
save_id: "天赋" + char.name, save_id: "天赋" + char.name,
...char, ...char,

View File

@ -2,6 +2,8 @@ import lodash from "lodash";
import fs from "fs"; import fs from "fs";
import request from "request"; import request from "request";
const _path = process.cwd();
let Data = { let Data = {
/* /*
@ -52,6 +54,19 @@ let Data = {
return fs.writeFileSync(`${path}/${file}`, JSON.stringify(data, null, space)); return fs.writeFileSync(`${path}/${file}`, JSON.stringify(data, null, space));
}, },
async importModule(path, file, rootPath = _path) {
if (!/\.js$/.test(file)) {
file = file + ".js";
}
// 检查并创建目录
Data.createDir(_path, path, true);
if (fs.existsSync(`${_path}/${path}/${file}`)) {
let data = await import (`file://${_path}/${path}/${file}`);
return data || {};
}
return {}
},
/* /*
* 返回一个从 target 中选中的属性的对象 * 返回一个从 target 中选中的属性的对象
* *
@ -173,7 +188,6 @@ let Data = {
return new Promise((resolve) => setTimeout(resolve, ms)); return new Promise((resolve) => setTimeout(resolve, ms));
} }
} }
export default Data; export default Data;

View File

@ -2,16 +2,55 @@ import Base from "./Base.js";
import lodash from "lodash"; import lodash from "lodash";
import fs from "fs"; import fs from "fs";
import Data from "../Data.js"; import Data from "../Data.js";
import request from "request";
import path from "path";
import sizeOf from "image-size"; import sizeOf from "image-size";
import { customCharacters } from "../../config/character_default.js";
let characterMap = {};
let aliasMap = {}, idMap = {}, abbrMap = {}, wifeMap = {};
const _path = process.cwd(); const _path = process.cwd();
let genshin = await import(`file://${_path}/config/genshin/roleId.js`);
const metaPath = `${_path}/plugins/miao-plugin/resources/meta/character/` const metaPath = `${_path}/plugins/miao-plugin/resources/meta/character/`
async function init() {
let sysCfg = await Data.importModule(`config/genshin`, 'roleId.js'),
charCfg = await Data.importModule(`plugins/miao-plugin/config`, 'character_default.js'),
custom = await Data.importModule(`plugins/miao-plugin/config`, 'character.js');
lodash.forEach([charCfg.customCharacters, custom.customCharacters, sysCfg.roleId], (roleIds) => {
lodash.forEach(roleIds || {}, (aliases, id) => {
aliases = aliases || [];
if (aliases.length === 0) {
return;
}
// 建立别名映射
lodash.forEach(aliases || [], (alias) => {
aliasMap[alias] = id;
})
aliasMap[id] = id;
idMap[id] = aliases[0];
})
})
lodash.forEach([sysCfg.wifeData, charCfg.wifeData, custom.wifeData], (wifeData) => {
lodash.forEach(wifeData || {}, (ids, type) => {
type = { girlfriend: 0, boyfriend: 1, daughter: 2, son: 3 }[type] || type;
if (!wifeMap[type]) {
wifeMap[type] = {};
}
lodash.forEach(ids, (id) => {
id = idMap[id];
if (id) {
wifeMap[type][id] = true;
}
})
})
})
abbrMap = sysCfg.abbr;
}
await init();
class Character extends Base { class Character extends Base {
constructor(name, id) { constructor(name, id) {
super(); super();
@ -26,17 +65,14 @@ class Character extends Base {
if (name === "主角" || name === "旅行者" || /.主/.test(name)) { if (name === "主角" || name === "旅行者" || /.主/.test(name)) {
this.id = 20000000; this.id = 20000000;
} }
this.id = id;
} }
getCardImg(se = false, def = true) { getCardImg(se = false, def = true) {
let name = this.name; let name = this.name;
const charImgPath = `./plugins/miao-plugin/resources/character-img/${name}/`; const charImgPath = `./plugins/miao-plugin/resources/character-img/${name}/`;
let list = []; let list = [];
let addImg = function (charImgPath, disable = false) { let addImg = function (charImgPath, disable = false) {
let dirPath = `./plugins/miao-plugin/resources/${charImgPath}`; let dirPath = `./plugins/miao-plugin/resources/${charImgPath}`;
@ -154,6 +190,14 @@ class Character extends Base {
let weaponType = this.weapon || ""; let weaponType = this.weapon || "";
return map[weaponType.toLowerCase()] || ""; return map[weaponType.toLowerCase()] || "";
} }
get isCustom() {
return !/10\d{6}/.test(this.id);
}
checkWifeType(type) {
return !!wifeMap[type][this.id];
}
} }
let getMeta = function (name) { let getMeta = function (name) {
@ -161,30 +205,30 @@ let getMeta = function (name) {
} }
Character.get = function (val) { Character.get = function (val) {
let roleid, name; let id, name;
if (!val) { if (!val) {
return false; return false;
} }
if (typeof (val) === "number" || /^\d*$/.test(val)) { if (typeof (val) === "number" || /^\d*$/.test(val)) {
roleid = val; id = val;
} else if (val.id) { } else if (val.id) {
roleid = val.id; id = val.id;
name = val.name || YunzaiApps.mysInfo['roleIdToName'](roleid, true); name = val.name || idMap[id];
} else { } else {
roleid = YunzaiApps.mysInfo['roleIdToName'](val); id = aliasMap[val];
} }
if (!name) { if (!name) {
name = YunzaiApps.mysInfo['roleIdToName'](roleid, true); name = idMap[id];
} }
if (!name) { if (!name) {
return false; return false;
} }
return new Character(name, roleid); return new Character(name, id);
}; };
Character.getAbbr = function () { Character.getAbbr = function () {
return genshin.abbr; return abbrMap;
} }
Character.getRandomImg = function (type) { Character.getRandomImg = function (type) {
@ -211,11 +255,9 @@ let idSort = {};
lodash.forEach(charPosIdx, (chars, pos) => { lodash.forEach(charPosIdx, (chars, pos) => {
chars = chars.split(","); chars = chars.split(",");
lodash.forEach(chars, (name, idx) => { lodash.forEach(chars, (name, idx) => {
if (global.YunzaiApps) { let id = aliasMap[name];
let id = YunzaiApps.mysInfo['roleIdToName'](name); if (id) {
if (id) { idSort[id] = pos * 100 + idx;
idSort[id] = pos * 100 + idx;
}
} }
}) })
}) })

View File

@ -0,0 +1,40 @@
/*
* 请不要直接修改此文件防止后续冲突
* 如需编辑可复制此文件改名为character.js自行编辑
* character.js character_default.js两份配置会叠加生效
*
* 暂未做热更新修改完毕请重启yunzai
* */
// 角色列表,首位需要是标准名字
// 实装的角色需要以roleid为key非实装请以英文为key
export const customCharacters = {
paimon: ["派蒙", "应急食物", "应急食品", "吉祥物", "宠物", "外置器官", "会说话的动物", "矮堇瓜", "飞行矮堇瓜", "最好的伙伴"],
// 啊, 原谅英语白痴。。
sb: ["散兵", "国崩", "雷电国崩", "大炮", "雷电大炮", "逆子"],
baizhu: ["白术", "长生"],
yaoyao: ["瑶瑶", "遥遥无期"],
fanan: ["伐难", "水夜叉"]
}
/*
* 追加设置每个关系的可选角色会与yunzai的设置同时起作用
* 一个角色可以在多个关系中
* */
export const wifeData = {
// 老婆&女朋友:成女、少女
girlfriend: ['伐难'],
// 老公&男朋友:成男、少男
boyfriend: ['散兵', '白术'],
// 女儿:萝莉
daughter: ['派蒙', '瑶瑶'],
// 儿子:正太
son: []
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 331 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 575 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 435 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 271 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 276 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 454 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 387 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 293 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 314 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 360 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 385 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 351 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 646 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 640 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 402 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 438 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 324 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 305 KiB

View File

@ -56,8 +56,12 @@
<div class="uid">ID:{{uid}}</div> <div class="uid">ID:{{uid}}</div>
<span>{{ds.name}}</span> <span>{{ds.name}}</span>
</div> </div>
{{if custom}}
<div class="no-info">自定义角色暂无角色信息</div>
{{else}}
<div class="no-info">未能获取到角色信息,请将角色放置在米游社角色展柜中</div> <div class="no-info">未能获取到角色信息,请将角色放置在米游社角色展柜中</div>
{{/if}} {{/if}}
{{/if}}
<div class="copyright">{{@sys.copyright}}</div> <div class="copyright">{{@sys.copyright}}</div>
<div> <div>
<img src="{{_res_path}}{{bg.img}}" title="{{name}}" class="bg"></div> <img src="{{_res_path}}{{bg.img}}" title="{{name}}" class="bg"></div>