feat: 角色命座

This commit is contained in:
bietiaop 2025-03-01 20:25:03 +08:00
parent 5830178362
commit 73715f015f
12 changed files with 324 additions and 14 deletions

View file

@ -208,6 +208,13 @@ const helpData = [
needSK: false,
commands: ['角色名+天赋[+等级]'],
},
{
title: '角色意象影画图鉴',
desc: '查看角色命座图鉴',
needCK: false,
needSK: false,
commands: ['角色名+命座', '角色名+意象', '角色名+影画'],
},
],
},
{

View file

@ -43,10 +43,15 @@ export class Wiki extends ZZZPlugin {
reg: `${rulePrefix}(.*)天赋(.*)$`,
fnc: 'skills',
},
{
reg: `${rulePrefix}(.*)(意象影画|意象|影画|命座)$`,
fnc: 'cinema',
},
],
});
}
async skills() {
logger.debug('skills');
const reg = new RegExp(`${rulePrefix}(.*)天赋(.*)$`);
const charname = this.e.msg.match(reg)[4];
if (!charname) return false;
@ -94,4 +99,20 @@ export class Wiki extends ZZZPlugin {
};
await this.render('skills/index.html', finalData);
}
async cinema() {
const reg = new RegExp(`${rulePrefix}(.*)(意象影画|意象|影画|命座)$`);
const charname = this.e.msg.match(reg)[4];
if (!charname) return false;
const charData = await getHakushCharacterData(charname);
const cinemaData = charData?.Talent;
if (!cinemaData) {
await this.reply(`未找到${charname}的数据`);
return false;
}
await charData.get_assets();
const finalData = {
charData,
};
await this.render('cinema/index.html', finalData);
}
}

View file

@ -7,3 +7,5 @@ export const ZZZ_CHARACTER = HAKUSH_API + '/character',
export const ZZZ_NEW = `${HAKUSH_BASE}/new.json`,
ZZZ_ALL_CHAR = `${HAKUSH_BASE}/data/character.json`,
ZZZ_ALL_WEAPON = `${HAKUSH_BASE}/data/weapon.json`;
export const ZZZ_UI = `${HAKUSH_BASE}/UI`;

View file

@ -165,3 +165,13 @@ export const getHakushWeapon = async weaponId => {
);
return result;
};
/**
* 获取Hakush UI
* @param {string} filename
* @returns {Promise<string>}
*/
export const getHakushUI = async filename => {
const result = await downloadHakushFile('ZZZ_UI', 'HAKUSH_UI_PATH', filename);
return result;
};

View file

@ -21,4 +21,5 @@ export const HAKUSH_CHARACTER_DATA_PATH = path.join(
dataResourcesPath,
'hakush/data/character'
),
HAKUSH_WEAPON_DATA_PATH = path.join(dataResourcesPath, 'hakush/data/weapon');
HAKUSH_WEAPON_DATA_PATH = path.join(dataResourcesPath, 'hakush/data/weapon'),
HAKUSH_UI_PATH = path.join(imageResourcesPath, 'hakush/ui');

View file

@ -74,7 +74,12 @@ export const downloadHakushFile = async (base, localBase, filename = '') => {
// 关闭文件
fs.closeSync(file);
// 返回文件内容
return JSON.parse(content.toString());
// 如果是JSON文件返回JSON对象
if (filename.endsWith('.json')) {
return JSON.parse(content.toString());
} else {
return filepath;
}
} else {
return null;
}

View file

@ -1,4 +1,4 @@
import { getSquareAvatar } from '../../lib/download.js';
import { getHakushUI, getSquareAvatar } from '../../lib/download.js';
/**
* @typedef {Object} StatsData
* @property {number} Armor
@ -512,6 +512,38 @@ class TalentLevel {
this.Name = data.Name;
this.Desc = data.Desc;
this.Desc2 = data.Desc2;
/** @type {string} */
this.description = this.Desc
? '<div class="line">' +
this.Desc.replace(
/<IconMap:Icon_(\w+)>/g,
'<span class="skill-icon $1"></span>'
)
.replace(
/<color=#(\w+?)>(.+?)<\/color>/g,
'<span style="color:#$1"><strong>$2</strong></span>'
)
.split('\n')
.join('</div><div class="line">') +
'</div>'
: '';
/** @type {string} */
this.description2 = this.Desc2
? '<div class="line">' +
this.Desc2.replace(
/<IconMap:Icon_(\w+)>/g,
'<span class="skill-icon $1"></span>'
)
.replace(
/<color=#(\w+?)>(.+?)<\/color>/g,
'<span style="color:#$1"><strong>$2</strong></span>'
)
.split('\n')
.join('</div><div class="line">') +
'</div>'
: '';
}
}
@ -555,7 +587,7 @@ export class Character {
this.Stats = new Stats(data.Stats);
this.Level = {};
this.ExtraLevel = {};
this.Talent = {};
this.Talent = [];
for (const [key, value] of Object.entries(data.Level)) {
this.Level[key] = new Level(value);
@ -566,13 +598,19 @@ export class Character {
this.Skill = new Skill(data.Skill);
this.Passive = new Passive(data.Passive);
for (const [key, value] of Object.entries(data.Talent)) {
this.Talent[key] = new TalentLevel(value);
for (const [_, value] of Object.entries(data.Talent)) {
this.Talent.push(new TalentLevel(value));
}
}
async get_assets() {
const result = await getSquareAvatar(this.Id);
this.square_icon = result;
await this.get_cinema_assets();
}
async get_cinema_assets() {
const result = await getHakushUI(`Mindscape_${this.Id}_3.webp`);
this.cinema_image = result;
}
}

View file

@ -0,0 +1,86 @@
.char-info {
display: flex;
align-items: flex-start;
padding: 1em;
gap: 1em;
}
.char-info .avatar {
width: 5em;
aspect-ratio: 1;
flex-grow: 0;
flex-shrink: 0;
background-color: white;
border-radius: 50%;
overflow: hidden;
}
.char-info .avatar img {
width: 100%;
height: 100%;
object-fit: cover;
}
.char-info .info {
display: flex;
flex-direction: column;
gap: 0.5em;
}
.char-info .info .name {
display: flex;
align-items: flex-end;
gap: 0.5em;
}
.char-info .info .name .simple {
font-size: 1.5em;
}
.char-info .info .description {
font-size: 0.8em;
}
.cinema {
padding: 1em;
}
.cinema .header {
margin-bottom: 1rem;
}
.cinema .title {
color: white;
font-size: 2rem;
margin: 0;
}
.cinema .subtitle {
color: #6666ff;
font-size: 1.5rem;
margin: 0;
}
.cinema .content {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1rem;
}
.cinema .section {
margin-bottom: 1rem;
}
.cinema .section-title {
font-size: 1.5rem;
margin-bottom: 0.5rem;
}
.cinema .section-content {
font-size: 1.3rem;
line-height: 1.5;
}
.cinema .highlight {
color: #ffd700;
}
.cinema .skill-tag {
display: inline-block;
margin-right: 0.5rem;
}
.cinema .image-container {
text-align: center;
margin-top: 1rem;
}
.cinema .character-image {
max-width: 100%;
border-radius: 0.5rem;
}
/*# sourceMappingURL=index.css.map */

View file

@ -0,0 +1,43 @@
{{extend defaultLayout}}
{{block 'css'}}
<link rel="stylesheet" href="{{@sys.currentPath}}/index.css">
{{/block}}
{{block 'main'}}
<div class="char-info">
<div class="avatar">
<img src="{{charData.square_icon}}" alt="Avatar">
</div>
<div class="info">
<div class="name">
<div class="simple">{{charData.PartnerInfo.Name}}</div>
<div class="full">{{charData.PartnerInfo.FullName}}</div>
</div>
<div class="description no-zzz-font">
<div class="f">{{@charData.PartnerInfo.ImpressionF}}</div>
<div class="m">{{@charData.PartnerInfo.ImpressionM}}</div>
</div>
</div>
</div>
<div class="cinema">
<div class="header">
<h1 class="title">意象影画</h1>
<h2 class="subtitle">CINEMA</h2>
</div>
<div class="content">
{{each charData.Talent talent}}
<div class="section">
<h3 class="section-title">{{talent.Level}}. {{talent.Name}}</h3>
<div class="section-content no-zzz-font">
{{@talent.description}}
</div>
</div>
{{/each}}
</div>
<div class="image-container">
<img src="{{charData.cinema_image}}" alt="角色插图" class="character-image">
</div>
<div style="text-align: center; font-size: 1em; color: #666; margin: 2em 0;">数据来源于Hakush</div>
{{/block}}

View file

@ -0,0 +1,95 @@
.char-info {
display: flex;
align-items: flex-start;
padding: 1em;
gap: 1em;
.avatar {
width: 5em;
aspect-ratio: 1;
flex-grow: 0;
flex-shrink: 0;
background-color: white;
border-radius: 50%;
overflow: hidden;
img {
width: 100%;
height: 100%;
object-fit: cover;
}
}
.info {
display: flex;
flex-direction: column;
gap: 0.5em;
.name {
display: flex;
align-items: flex-end;
gap: 0.5em;
.simple {
font-size: 1.5em;
}
}
.description {
font-size: 0.8em;
}
}
}
.cinema {
padding: 1em;
.header {
margin-bottom: 1rem;
}
.title {
color: white;
font-size: 2rem;
margin: 0;
}
.subtitle {
color: #6666ff;
font-size: 1.5rem;
margin: 0;
}
.content {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1rem;
}
.section {
margin-bottom: 1rem;
}
.section-title {
font-size: 1.5rem;
margin-bottom: 0.5rem;
}
.section-content {
font-size: 1.3rem;
line-height: 1.5;
}
.highlight {
color: #ffd700;
}
.skill-tag {
display: inline-block;
margin-right: 0.5rem;
}
.image-container {
text-align: center;
margin-top: 1rem;
}
.character-image {
max-width: 100%;
border-radius: 0.5rem;
}
}

View file

@ -5,14 +5,10 @@
@font-face {
font-family: "tttgbnumber";
src: url("../../../../../genshin/resources/font/tttgbnumber.ttf");
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: "HYWenHei-55W";
src: url("../../../../../genshin/resources/font/HYWenHei-55W.ttf");
font-weight: normal;
font-style: normal;
}
.zzz-font {
font-family: "zzz", "tttgbnumber", "HYWenHei-55W", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
@ -21,6 +17,10 @@
.no-zzz-font {
font-family: "tttgbnumber", "HYWenHei-55W", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
}
.no-zzz-font strong,
.no-zzz-font b {
font-family: "zzz", "tttgbnumber", "HYWenHei-55W", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
}
.rank-icon {
aspect-ratio: 1;

View file

@ -8,15 +8,11 @@
@font-face {
font-family: 'tttgbnumber';
src: url('../../../../../genshin/resources/font/tttgbnumber.ttf');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'HYWenHei-55W';
src: url('../../../../../genshin/resources/font/HYWenHei-55W.ttf');
font-weight: normal;
font-style: normal;
}
.zzz-font {
@ -29,6 +25,12 @@
font-family: 'tttgbnumber', 'HYWenHei-55W', system-ui, -apple-system,
BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell,
'Open Sans', 'Helvetica Neue', sans-serif;
strong,
b {
font-family: 'zzz', 'tttgbnumber', 'HYWenHei-55W', system-ui, -apple-system,
BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell,
'Open Sans', 'Helvetica Neue', sans-serif;
}
}
.rank-icon {