mirror of
https://github.com/ZZZure/ZZZ-Plugin.git
synced 2025-12-15 12:47:48 +00:00
feat: 角色天赋图鉴(支持自定义等级)
This commit is contained in:
parent
4f02e6b2ce
commit
15b14eece4
23 changed files with 937 additions and 124 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -8,4 +8,4 @@ data/**/*.*
|
|||
!data/.gitkeep
|
||||
|
||||
resources/images/**/*.*
|
||||
!resources/images/.gitkeep
|
||||
resources/data/**/*.*
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
# 1.5
|
||||
* 新增角色天赋,发送 `%帮助` 查看如何使用
|
||||
|
||||
# 1.4.2
|
||||
* 版本机制
|
||||
|
||||
|
|
|
|||
|
|
@ -161,6 +161,13 @@ const helpData = [
|
|||
needSK: false,
|
||||
commands: ['更新+角色名+攻略[+0~7]'],
|
||||
},
|
||||
{
|
||||
title: '角色天赋图鉴',
|
||||
desc: '查看角色天赋,默认等级为12级,核心技等级为F,你可以在指令后面加上自定义等级,以英文句号点分隔,顺序依次为:普通攻击、闪避、支援技、特殊技、连携技、核心技,其中除核心技等级为0和A~F表示外,其他等级为1~16的数字。例如:%猫又天赋6.12.11.10.9.F',
|
||||
needCK: false,
|
||||
needSK: false,
|
||||
commands: ['角色名+天赋[+等级]'],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,10 +1,13 @@
|
|||
import fs from 'fs';
|
||||
import {
|
||||
getRoleImage,
|
||||
getRoleCircleImage,
|
||||
getSmallSquareAvatar,
|
||||
getSquareAvatar,
|
||||
getSuitImage,
|
||||
getWeaponImage,
|
||||
getHakushCharacter,
|
||||
getHakushWeapon,
|
||||
} from '../../lib/download.js';
|
||||
import { char } from '../../lib/convert.js';
|
||||
import { getAllEquipID } from '../../lib/convert/equip.js';
|
||||
|
|
@ -17,30 +20,49 @@ export async function downloadAll() {
|
|||
const equipSprites = getAllEquipID();
|
||||
const weaponSprites = getAllWeaponID();
|
||||
const result = {
|
||||
char: {
|
||||
success: 0,
|
||||
failed: 0,
|
||||
total: charIDs.length,
|
||||
images: {
|
||||
char: {
|
||||
success: 0,
|
||||
failed: 0,
|
||||
total: charIDs.length,
|
||||
},
|
||||
charSmallSquare: {
|
||||
success: 0,
|
||||
failed: 0,
|
||||
total: charIDs.length,
|
||||
},
|
||||
charCircle: {
|
||||
success: 0,
|
||||
failed: 0,
|
||||
total: charIDs.length,
|
||||
},
|
||||
charSquare: {
|
||||
success: 0,
|
||||
failed: 0,
|
||||
total: charIDs.length,
|
||||
},
|
||||
equip: {
|
||||
success: 0,
|
||||
failed: 0,
|
||||
total: equipSprites.length,
|
||||
},
|
||||
weapon: {
|
||||
success: 0,
|
||||
failed: 0,
|
||||
total: weaponSprites.length,
|
||||
},
|
||||
},
|
||||
charSmallSquare: {
|
||||
success: 0,
|
||||
failed: 0,
|
||||
total: charIDs.length,
|
||||
},
|
||||
charSquare: {
|
||||
success: 0,
|
||||
failed: 0,
|
||||
total: charIDs.length,
|
||||
},
|
||||
equip: {
|
||||
success: 0,
|
||||
failed: 0,
|
||||
total: equipSprites.length,
|
||||
},
|
||||
weapon: {
|
||||
success: 0,
|
||||
failed: 0,
|
||||
total: weaponSprites.length,
|
||||
hakush: {
|
||||
char: {
|
||||
success: 0,
|
||||
failed: 0,
|
||||
total: charIDs.length,
|
||||
},
|
||||
equip: {
|
||||
success: 0,
|
||||
failed: 0,
|
||||
total: equipSprites.length,
|
||||
},
|
||||
},
|
||||
};
|
||||
await this.reply(
|
||||
|
|
@ -51,51 +73,75 @@ export async function downloadAll() {
|
|||
for (const id of charIDs) {
|
||||
try {
|
||||
await getSquareAvatar(id);
|
||||
result.charSquare.success++;
|
||||
result.images.charSquare.success++;
|
||||
} catch (error) {
|
||||
logger.error('getSquareAvatar', id, error);
|
||||
result.charSquare.failed++;
|
||||
result.images.charSquare.failed++;
|
||||
}
|
||||
try {
|
||||
await getSmallSquareAvatar(id);
|
||||
result.charSmallSquare.success++;
|
||||
result.images.charSmallSquare.success++;
|
||||
} catch (error) {
|
||||
logger.error('getSmallSquareAvatar', id, error);
|
||||
result.charSmallSquare.failed++;
|
||||
result.images.charSmallSquare.failed++;
|
||||
}
|
||||
try {
|
||||
await getRoleImage(id);
|
||||
result.char.success++;
|
||||
result.images.char.success++;
|
||||
} catch (error) {
|
||||
logger.error('getRoleImage', id, error);
|
||||
result.char.failed++;
|
||||
result.images.char.failed++;
|
||||
}
|
||||
try {
|
||||
await getRoleCircleImage(id);
|
||||
result.images.charCircle.success++;
|
||||
} catch (error) {
|
||||
logger.error('getRoleCircleImage', id, error);
|
||||
result.images.charCircle.failed++;
|
||||
}
|
||||
try {
|
||||
await getHakushCharacter(id);
|
||||
result.hakush.char.success++;
|
||||
} catch (error) {
|
||||
logger.error('getHakushCharacter', id, error);
|
||||
result.hakush.char.failed++;
|
||||
}
|
||||
}
|
||||
for (const sprite of equipSprites) {
|
||||
try {
|
||||
await getSuitImage(sprite);
|
||||
result.equip.success++;
|
||||
result.images.equip.success++;
|
||||
} catch (error) {
|
||||
logger.error('getSuitImage', sprite, error);
|
||||
result.equip.failed++;
|
||||
result.images.equip.failed++;
|
||||
}
|
||||
}
|
||||
for (const sprite of weaponSprites) {
|
||||
try {
|
||||
await getWeaponImage(sprite);
|
||||
result.weapon.success++;
|
||||
result.images.weapon.success++;
|
||||
} catch (error) {
|
||||
logger.error('getWeaponImage', sprite, error);
|
||||
result.weapon.failed++;
|
||||
result.images.weapon.failed++;
|
||||
}
|
||||
try {
|
||||
await getHakushWeapon(sprite);
|
||||
result.hakush.equip.success++;
|
||||
} catch (error) {
|
||||
logger.error('getHakushWeapon', sprite, error);
|
||||
result.hakush.equip.failed++;
|
||||
}
|
||||
}
|
||||
const messages = [
|
||||
'资源下载完成(成功的包含先前下载的图片)',
|
||||
`角色图需下载${charIDs.length}张,成功${result.char.success}张,失败${result.char.failed}张`,
|
||||
`角色头像图需下载${charIDs.length}张,成功${result.charSquare.success}张,失败${result.charSquare.failed}张`,
|
||||
`角色头像图(练度统计)需下载${charIDs.length}张,成功${result.charSmallSquare.success}张,失败${result.charSmallSquare.failed}张`,
|
||||
`驱动盘套装图需下载${equipSprites.length}张,成功${result.equip.success}张,失败${result.equip.failed}张`,
|
||||
`武器图需下载${weaponSprites.length}张,成功${result.weapon.success}张,失败${result.weapon.failed}张`,
|
||||
'资源下载完成(成功的包含先前下载的资源)',
|
||||
`角色图需下载${charIDs.length}张,成功${result.images.char.success}张,失败${result.images.char.failed}张`,
|
||||
`角色头像图需下载${charIDs.length}张,成功${result.images.charSquare.success}张,失败${result.images.charSquare.failed}张`,
|
||||
`角色圆形图需下载${charIDs.length}张,成功${result.images.charCircle.success}张,失败${result.images.charCircle.failed}张`,
|
||||
`角色头像图(练度统计)需下载${charIDs.length}张,成功${result.images.charSmallSquare.success}张,失败${result.images.charSmallSquare.failed}张`,
|
||||
`驱动盘套装图需下载${equipSprites.length}张,成功${result.images.equip.success}张,失败${result.images.equip.failed}张`,
|
||||
`武器图需下载${weaponSprites.length}张,成功${result.images.weapon.success}张,失败${result.images.weapon.failed}张`,
|
||||
`Hakush角色数据需下载${charIDs.length}个,成功${result.hakush.char.success}张,失败${result.hakush.char.failed}个`,
|
||||
`Hakush驱动盘数据需下载${equipSprites.length}个,成功${result.hakush.equip.success}张,失败${result.hakush.equip.failed}个`,
|
||||
];
|
||||
await this.reply(messages.join('\n'));
|
||||
}
|
||||
|
|
|
|||
94
apps/wiki.js
Normal file
94
apps/wiki.js
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
import { ZZZPlugin } from '../lib/plugin.js';
|
||||
import settings from '../lib/settings.js';
|
||||
import _ from 'lodash';
|
||||
import { rulePrefix } from '../lib/common.js';
|
||||
import { getHakushCharacterData, isSkillLevelLegal } from '../lib/hakush.js';
|
||||
const displays = [
|
||||
{
|
||||
key: 'Basic',
|
||||
name: '普通攻击',
|
||||
icon: 'basic',
|
||||
},
|
||||
{
|
||||
key: 'Dodge',
|
||||
name: '闪避',
|
||||
icon: 'dodge',
|
||||
},
|
||||
{
|
||||
key: 'Assist',
|
||||
name: '支援技',
|
||||
icon: 'assist',
|
||||
},
|
||||
{
|
||||
key: 'Special',
|
||||
name: '特殊技',
|
||||
icon: 'special',
|
||||
},
|
||||
{
|
||||
key: 'Chain',
|
||||
name: '连携技',
|
||||
icon: 'chain',
|
||||
},
|
||||
];
|
||||
|
||||
export class Abyss extends ZZZPlugin {
|
||||
constructor() {
|
||||
super({
|
||||
name: '[ZZZ-Plugin]wiki',
|
||||
dsc: 'zzzWiki',
|
||||
event: 'message',
|
||||
priority: _.get(settings.getConfig('priority'), 'wiki', 70),
|
||||
rule: [
|
||||
{
|
||||
reg: `${rulePrefix}(.*)天赋(.*)$`,
|
||||
fnc: 'skills',
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
async skills() {
|
||||
const reg = new RegExp(`${rulePrefix}(.*)天赋(.*)$`);
|
||||
const charname = this.e.msg.match(reg)[4];
|
||||
if (!charname) return false;
|
||||
const levelsChar = this.e.msg.match(reg)[5];
|
||||
const [
|
||||
BasicLevel = 12,
|
||||
DodgeLevel = 12,
|
||||
AssistLevel = 12,
|
||||
SpecialLevel = 12,
|
||||
ChainLevel = 12,
|
||||
CoreLevel = 6,
|
||||
] = levelsChar.split('.').map(x => {
|
||||
const _x = Number(x.trim());
|
||||
if (!_.isNaN(_x)) return _x;
|
||||
if (_.isString(x)) return x.charCodeAt(0) - 64;
|
||||
return null;
|
||||
});
|
||||
if (
|
||||
!isSkillLevelLegal('BasicLevel', BasicLevel) ||
|
||||
!isSkillLevelLegal('DodgeLevel', DodgeLevel) ||
|
||||
!isSkillLevelLegal('AssistLevel', AssistLevel) ||
|
||||
!isSkillLevelLegal('SpecialLevel', SpecialLevel) ||
|
||||
!isSkillLevelLegal('ChainLevel', ChainLevel) ||
|
||||
!isSkillLevelLegal('CoreLevel', CoreLevel)
|
||||
) {
|
||||
await this.reply(`${charname}天赋等级参数不合法`);
|
||||
return false;
|
||||
}
|
||||
const charData = await getHakushCharacterData(charname);
|
||||
charData.Skill.getAllSkillData({
|
||||
BasicLevel,
|
||||
DodgeLevel,
|
||||
AssistLevel,
|
||||
SpecialLevel,
|
||||
ChainLevel,
|
||||
});
|
||||
charData.Passive.getPassiveData(CoreLevel);
|
||||
await charData.get_assets();
|
||||
const finalData = {
|
||||
charData,
|
||||
displays,
|
||||
};
|
||||
await this.render('skills/index.html', finalData);
|
||||
}
|
||||
}
|
||||
|
|
@ -9,7 +9,7 @@ const PartnerId2SpriteId = getMapData('PartnerId2Data');
|
|||
* @param {string | number} id
|
||||
* @param {boolean} full 显示全称
|
||||
* @param {boolean} en 是否为英文
|
||||
* @returns string | null
|
||||
* @returns {string | null}
|
||||
*/
|
||||
export const IDToCharName = (id, full = true, en = false) => {
|
||||
const data = PartnerId2SpriteId?.[id];
|
||||
|
|
@ -22,7 +22,7 @@ export const IDToCharName = (id, full = true, en = false) => {
|
|||
/**
|
||||
*
|
||||
* @param {string | number} id
|
||||
* @returns string | null
|
||||
* @returns {string | null}
|
||||
*/
|
||||
export const IDToCharSprite = id => {
|
||||
const data = PartnerId2SpriteId?.[id];
|
||||
|
|
@ -32,7 +32,7 @@ export const IDToCharSprite = id => {
|
|||
|
||||
/**
|
||||
* @param {string} name
|
||||
* @returns number | null
|
||||
* @returns {number | null}
|
||||
*/
|
||||
export const charNameToID = name => {
|
||||
for (const [id, data] of Object.entries(PartnerId2SpriteId)) {
|
||||
|
|
@ -43,7 +43,7 @@ export const charNameToID = name => {
|
|||
|
||||
/**
|
||||
* @param {string} name
|
||||
* @returns string | null
|
||||
* @returns {string | null}
|
||||
*/
|
||||
export const charNameToSprite = name => {
|
||||
for (const [_id, data] of Object.entries(PartnerId2SpriteId)) {
|
||||
|
|
@ -53,8 +53,8 @@ export const charNameToSprite = name => {
|
|||
};
|
||||
|
||||
/**
|
||||
* @param {string} alias
|
||||
* @returns string | null
|
||||
* @param {string} _alias
|
||||
* @returns {string | null}
|
||||
*/
|
||||
export const aliasToName = _alias => {
|
||||
const alias = settings.getConfig('alias');
|
||||
|
|
@ -67,7 +67,7 @@ export const aliasToName = _alias => {
|
|||
|
||||
/**
|
||||
* @param {string} _alias
|
||||
* @returns string | null
|
||||
* @returns {string | null}
|
||||
*/
|
||||
export const aliasToSprite = _alias => {
|
||||
const name = aliasToName(_alias);
|
||||
|
|
@ -76,7 +76,7 @@ export const aliasToSprite = _alias => {
|
|||
|
||||
/**
|
||||
* @param {string} name
|
||||
* @returns number | null
|
||||
* @returns {number | null}
|
||||
*/
|
||||
export const aliasToID = name => {
|
||||
const _name = aliasToName(name);
|
||||
|
|
@ -86,7 +86,7 @@ export const aliasToID = name => {
|
|||
|
||||
/**
|
||||
* 获取所有角色ID
|
||||
* @returns string[]
|
||||
* @returns {string[]}
|
||||
*/
|
||||
export const getAllCharactersID = () => {
|
||||
return Object.keys(PartnerId2SpriteId);
|
||||
|
|
|
|||
|
|
@ -135,7 +135,7 @@ export const getSuit3DImage = async suitId => {
|
|||
/**
|
||||
* 获取Hakush角色数据
|
||||
* @param {string} charId
|
||||
* @returns {Promise<string>}
|
||||
* @returns {Promise<object | null>} 文件内容(JSON)
|
||||
*/
|
||||
export const getHakushCharacter = async charId => {
|
||||
const filename = `${charId}.json`;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import path from 'path';
|
||||
import { imageResourcesPath } from '../path.js';
|
||||
import { imageResourcesPath, dataResourcesPath } from '../path.js';
|
||||
|
||||
export const ZZZ_SQUARE_AVATAR_PATH = path.join(
|
||||
imageResourcesPath,
|
||||
|
|
@ -18,7 +18,7 @@ export const ZZZ_SQUARE_AVATAR_PATH = path.join(
|
|||
// const ZZZ_GUIDES_PATH = path.join(imageResourcesPath, 'guides');
|
||||
|
||||
export const HAKUSH_CHARACTER_DATA_PATH = path.join(
|
||||
imageResourcesPath,
|
||||
dataResourcesPath,
|
||||
'hakush/data/character'
|
||||
),
|
||||
HAKUSH_WEAPON_DATA_PATH = path.join(imageResourcesPath, 'hakush/data/weapon');
|
||||
HAKUSH_WEAPON_DATA_PATH = path.join(dataResourcesPath, 'hakush/data/weapon');
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
import { checkFile } from './core.js';
|
||||
import { getResourceRemotePath } from '../assets.js';
|
||||
import * as MysURL from '../assets/mysurl.js';
|
||||
|
|
@ -10,6 +11,7 @@ import * as LocalURI from './const.js';
|
|||
* @param {keyof LocalURI} localBase 本地地址
|
||||
* @param {string} filename 文件名
|
||||
* @param {keyof MysURL} newBase 新远程地址
|
||||
* @returns {Promise<string | null>} 保存路径
|
||||
*/
|
||||
export const downloadMysImage = async (
|
||||
base,
|
||||
|
|
@ -38,6 +40,7 @@ export const downloadMysImage = async (
|
|||
* @param {keyof LocalURI} localBase 本地地址
|
||||
* @param {string} filename 文件名
|
||||
* @param {string} replaceFilename 替换文件名(如果资源不存在)
|
||||
* @returns {Promise<string | null>} 保存路径
|
||||
*/
|
||||
export const downloadResourceImage = async (
|
||||
remoteLabel,
|
||||
|
|
@ -62,6 +65,7 @@ export const downloadResourceImage = async (
|
|||
* @param {keyof HakushURL} base 远程地址
|
||||
* @param {keyof LocalURI} localBase 本地地址
|
||||
* @param {string} filename 文件名
|
||||
* @returns {Promise<object | null>} 文件内容(JSON)
|
||||
*/
|
||||
export const downloadHakushFile = async (base, localBase, filename = '') => {
|
||||
base = HakushURL[base];
|
||||
|
|
@ -71,5 +75,17 @@ export const downloadHakushFile = async (base, localBase, filename = '') => {
|
|||
if (filename) {
|
||||
url += `/${filename}`;
|
||||
}
|
||||
return checkFile(url, finalPath);
|
||||
const filepath = await checkFile(url, finalPath);
|
||||
if (filepath) {
|
||||
// 打开文件
|
||||
const file = fs.openSync(filepath, 'r');
|
||||
// 读取文件内容
|
||||
const content = fs.readFileSync(file);
|
||||
// 关闭文件
|
||||
fs.closeSync(file);
|
||||
// 返回文件内容
|
||||
return JSON.parse(content.toString());
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
19
lib/hakush.js
Normal file
19
lib/hakush.js
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
import { Character } from '../model/hakush/character.js';
|
||||
import * as convert from './convert.js';
|
||||
import { getHakushCharacter } from './download.js';
|
||||
export const getHakushCharacterData = async alias => {
|
||||
const name = convert.char.aliasToName(alias);
|
||||
const id = convert.char.charNameToID(name);
|
||||
if (!id) return null;
|
||||
const data = await getHakushCharacter(id);
|
||||
if (!data) return null;
|
||||
const result = new Character(data);
|
||||
return result;
|
||||
};
|
||||
|
||||
export const isSkillLevelLegal = (key, level) => {
|
||||
if (key === 'CoreLevel') {
|
||||
return !!level && level >= 0 && level <= 6;
|
||||
}
|
||||
return !!level && level >= 1 && level <= 12;
|
||||
};
|
||||
|
|
@ -21,6 +21,8 @@ export const resourcesPath = path.join(pluginPath, 'resources');
|
|||
|
||||
export const imageResourcesPath = path.join(resourcesPath, 'images');
|
||||
|
||||
export const dataResourcesPath = path.join(resourcesPath, 'data');
|
||||
|
||||
export const mapResourcesPath = path.join(resourcesPath, 'map');
|
||||
|
||||
// config 路径
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { getSquareAvatar } from '../../lib/download.js';
|
||||
/**
|
||||
* @typedef {Object} StatsData
|
||||
* @property {number} Armor
|
||||
|
|
@ -141,7 +142,7 @@ class Level {
|
|||
/**
|
||||
* @typedef {Object} ExtraLevelData
|
||||
* @property {number} MaxLevel
|
||||
* @property {Object.<string, Object.<string, string|number|float>>} Extra
|
||||
* @property {Record<string, Record<string, string|number|float>>} Extra
|
||||
*/
|
||||
|
||||
class ExtraLevel {
|
||||
|
|
@ -154,11 +155,60 @@ class ExtraLevel {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} PartnerInfoData
|
||||
* @property {string} Birthday
|
||||
* @property {string} FullName
|
||||
* @property {string} Gender
|
||||
* @property {string} IconPath
|
||||
* @property {string} ImpressionF
|
||||
* @property {string} ImpressionM
|
||||
* @property {string} Name
|
||||
* @property {string} OutlookDesc
|
||||
* @property {string} ProfileDesc
|
||||
* @property {string} Race
|
||||
* @property {string} RoleIcon
|
||||
* @property {string} Stature
|
||||
* @property {string[]} UnlockCondition
|
||||
*/
|
||||
|
||||
class PartnerInfo {
|
||||
/**
|
||||
* @param {PartnerInfoData} data
|
||||
*/
|
||||
constructor(data) {
|
||||
this.Birthday = data.Birthday;
|
||||
this.FullName = data.FullName;
|
||||
this.Gender = data.Gender;
|
||||
this.IconPath = data.IconPath;
|
||||
this.ImpressionF = data.ImpressionF;
|
||||
this.ImpressionM = data.ImpressionM;
|
||||
this.Name = data.Name;
|
||||
this.OutlookDesc = data.OutlookDesc;
|
||||
this.ProfileDesc = data.ProfileDesc;
|
||||
this.Race = data.Race;
|
||||
this.RoleIcon = data.RoleIcon;
|
||||
this.Stature = data.Stature;
|
||||
this.UnlockCondition = data.UnlockCondition;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} SkillValueData
|
||||
* @property {number} Main
|
||||
* @property {number} Growth
|
||||
* @property {string} Format
|
||||
* @property {number[]} AttackData
|
||||
* @property {number} AttributeInfliction
|
||||
* @property {number} FeverRecovery
|
||||
* @property {number} FeverRecoveryGrowth
|
||||
* @property {number} SpConsume
|
||||
* @property {number} SpRecovery
|
||||
* @property {number} SpRecoveryGrowth
|
||||
* @property {number} StunRatio
|
||||
* @property {number} StunRatioGrowth
|
||||
* @property {number} DamagePercentage
|
||||
* @property {number} DamagePercentageGrowth
|
||||
*/
|
||||
|
||||
class SkillValue {
|
||||
|
|
@ -169,6 +219,17 @@ class SkillValue {
|
|||
this.Main = data.Main;
|
||||
this.Growth = data.Growth;
|
||||
this.Format = data.Format;
|
||||
this.AttackData = data.AttackData;
|
||||
this.AttributeInfliction = data.AttributeInfliction;
|
||||
this.DamagePercentage = data.DamagePercentage;
|
||||
this.DamagePercentageGrowth = data.DamagePercentageGrowth;
|
||||
this.FeverRecovery = data.FeverRecovery;
|
||||
this.FeverRecoveryGrowth = data.FeverRecoveryGrowth;
|
||||
this.SpConsume = data.SpConsume;
|
||||
this.SpRecovery = data.SpRecovery;
|
||||
this.SpRecoveryGrowth = data.SpRecoveryGrowth;
|
||||
this.StunRatio = data.StunRatio;
|
||||
this.StunRatioGrowth = data.StunRatioGrowth;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -176,7 +237,7 @@ class SkillValue {
|
|||
* @typedef {Object} SkillParamData
|
||||
* @property {string} Name
|
||||
* @property {string} Desc
|
||||
* @property {Object.<string, SkillValueData>} Param
|
||||
* @property {Record<string, SkillValueData>} Param
|
||||
*/
|
||||
|
||||
class SkillParam {
|
||||
|
|
@ -186,9 +247,11 @@ class SkillParam {
|
|||
constructor(data) {
|
||||
this.Name = data.Name;
|
||||
this.Desc = data.Desc;
|
||||
this.Param = {};
|
||||
for (const [key, value] of Object.entries(data.Param)) {
|
||||
this.Param[key] = new SkillValue(value);
|
||||
if (data.Param) {
|
||||
this.Param = {};
|
||||
for (const [key, value] of Object.entries(data.Param)) {
|
||||
this.Param[key] = new SkillValue(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -206,13 +269,27 @@ class SkillDescription {
|
|||
constructor(data) {
|
||||
this.Name = data.Name;
|
||||
this.Desc = data.Desc;
|
||||
/** @type {string} */
|
||||
this.description =
|
||||
'<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>';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} SkillDescription2Data
|
||||
* @property {string} Name
|
||||
* @property {SkillParamData} Param
|
||||
* @property {SkillParamData[]} Param
|
||||
*/
|
||||
|
||||
class SkillDescription2 {
|
||||
|
|
@ -221,14 +298,14 @@ class SkillDescription2 {
|
|||
*/
|
||||
constructor(data) {
|
||||
this.Name = data.Name;
|
||||
this.Param = new SkillParam(data.Param);
|
||||
this.Param = data.Param.map(param => new SkillParam(param));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} SkillDetailData
|
||||
* @property {(SkillDescriptionData|SkillDescription2Data)[]} Description
|
||||
* @property {Object.<string, Object.<string, number>>} Material
|
||||
* @property {Recordstring, Record<string, number>>} Material
|
||||
*/
|
||||
|
||||
class SkillDetail {
|
||||
|
|
@ -241,15 +318,71 @@ class SkillDetail {
|
|||
);
|
||||
this.Material = data.Material;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取技能详情数据
|
||||
* @param {number} level
|
||||
* @returns {Record<string, string|number>}
|
||||
*/
|
||||
getDetailData(level = 12) {
|
||||
this.level = level;
|
||||
const rate = [];
|
||||
for (const desc of this.Description) {
|
||||
if (desc.Param) {
|
||||
const itemData = {
|
||||
rate: [],
|
||||
details: [],
|
||||
};
|
||||
for (const param of desc.Param) {
|
||||
if (!!param.Param) {
|
||||
const value = Object.values(param.Param)[0];
|
||||
let final = value.Main + value.Growth * (level - 1);
|
||||
if (value.Format === '%') {
|
||||
final = `${final / 100}%`;
|
||||
}
|
||||
itemData['rate'].push({
|
||||
label: param.Name,
|
||||
value: final,
|
||||
});
|
||||
itemData['details'].push({
|
||||
A: (value.Main + value.Growth * (level - 1)) / 100,
|
||||
B: (value.StunRatio + value.StunRatioGrowth * (level - 1)) / 100,
|
||||
C:
|
||||
(value.SpRecovery + value.SpRecoveryGrowth * (level - 1)) /
|
||||
10000,
|
||||
D:
|
||||
(value.FeverRecovery +
|
||||
value.FeverRecoveryGrowth * (level - 1)) /
|
||||
10000,
|
||||
E: value.AttributeInfliction / 100,
|
||||
F: 0,
|
||||
G: 0,
|
||||
});
|
||||
} else {
|
||||
itemData['rate'].push({
|
||||
label: param.Name,
|
||||
value: param.Desc,
|
||||
});
|
||||
}
|
||||
}
|
||||
rate.push({
|
||||
name: desc.Name,
|
||||
data: itemData,
|
||||
});
|
||||
}
|
||||
}
|
||||
this.rate = rate;
|
||||
return rate;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} SkillData
|
||||
* @property {Object.<string, SkillDetailData>} Basic
|
||||
* @property {Object.<string, SkillDetailData>} Dodge
|
||||
* @property {Object.<string, SkillDetailData>} Special
|
||||
* @property {Object.<string, SkillDetailData>} Chain
|
||||
* @property {Object.<string, SkillDetailData>} Assist
|
||||
* @property {SkillDetailData} Basic
|
||||
* @property {SkillDetailData} Dodge
|
||||
* @property {SkillDetailData} Special
|
||||
* @property {SkillDetailData>} Chain
|
||||
* @property {SkillDetailData>} Assist
|
||||
*/
|
||||
|
||||
class Skill {
|
||||
|
|
@ -257,27 +390,43 @@ class Skill {
|
|||
* @param {SkillData} data
|
||||
*/
|
||||
constructor(data) {
|
||||
this.Basic = {};
|
||||
this.Dodge = {};
|
||||
this.Special = {};
|
||||
this.Chain = {};
|
||||
this.Assist = {};
|
||||
this.Basic = new SkillDetail(data.Basic);
|
||||
this.Dodge = new SkillDetail(data.Dodge);
|
||||
this.Special = new SkillDetail(data.Special);
|
||||
this.Chain = new SkillDetail(data.Chain);
|
||||
this.Assist = new SkillDetail(data.Assist);
|
||||
}
|
||||
|
||||
for (const [key, value] of Object.entries(data.Basic)) {
|
||||
this.Basic[key] = new SkillDetail(value);
|
||||
}
|
||||
for (const [key, value] of Object.entries(data.Dodge)) {
|
||||
this.Dodge[key] = new SkillDetail(value);
|
||||
}
|
||||
for (const [key, value] of Object.entries(data.Special)) {
|
||||
this.Special[key] = new SkillDetail(value);
|
||||
}
|
||||
for (const [key, value] of Object.entries(data.Chain)) {
|
||||
this.Chain[key] = new SkillDetail(value);
|
||||
}
|
||||
for (const [key, value] of Object.entries(data.Assist)) {
|
||||
this.Assist[key] = new SkillDetail(value);
|
||||
}
|
||||
/**
|
||||
* 获取技能数据
|
||||
* @param {string} skill
|
||||
* @param {number} level
|
||||
* @returns {Record<string, string|number>}
|
||||
*/
|
||||
getSkillData(skill, level = 12) {
|
||||
return this[skill].getDetailData(level);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有技能数据
|
||||
* @param {Record<string, number>} levels
|
||||
* @returns {Record<string, Record<'BasicLevel'|'DodgeLevel'|'AssistLevel'|'SpecialLevel'|'ChainLevel', number>>}
|
||||
*/
|
||||
getAllSkillData(levels) {
|
||||
const {
|
||||
BasicLevel = 12,
|
||||
DodgeLevel = 12,
|
||||
AssistLevel = 12,
|
||||
SpecialLevel = 12,
|
||||
ChainLevel = 12,
|
||||
} = levels;
|
||||
return {
|
||||
Basic: this.getSkillData('Basic', BasicLevel),
|
||||
Dodge: this.getSkillData('Dodge', DodgeLevel),
|
||||
Assist: this.getSkillData('Assist', AssistLevel),
|
||||
Special: this.getSkillData('Special', SpecialLevel),
|
||||
Chain: this.getSkillData('Chain', ChainLevel),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -298,13 +447,31 @@ class PassiveLevel {
|
|||
this.Id = data.Id;
|
||||
this.Name = data.Name;
|
||||
this.Desc = data.Desc;
|
||||
|
||||
/** @type {string[]} */
|
||||
this.description = data.Desc.map(
|
||||
item =>
|
||||
'<div class="line">' +
|
||||
item
|
||||
.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>'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} PassiveData
|
||||
* @property {Object.<number, PassiveLevelData>} Level
|
||||
* @property {Object.<string, Object.<string, number>>} Materials
|
||||
* @property {Record<number, PassiveLevelData>} Level
|
||||
* @property {Record<string, Record<string, number>>} Materials
|
||||
*/
|
||||
|
||||
class Passive {
|
||||
|
|
@ -319,6 +486,13 @@ class Passive {
|
|||
this.Level[key] = new PassiveLevel(value);
|
||||
}
|
||||
}
|
||||
|
||||
/** @type {PassiveLevel} */
|
||||
getPassiveData(level = 1) {
|
||||
this._level = level;
|
||||
this.currentLevel = this.Level[level];
|
||||
return this.currentLevel;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -341,30 +515,6 @@ class TalentLevel {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} TalentData
|
||||
* @property {TalentLevelData} Heroism
|
||||
* @property {TalentLevelData} YouthfulArrogance
|
||||
* @property {TalentLevelData} Insensitive
|
||||
* @property {TalentLevelData} OriginalAspiration
|
||||
* @property {TalentLevelData} LongingDistance
|
||||
* @property {TalentLevelData} Idealism
|
||||
*/
|
||||
|
||||
class Talent {
|
||||
/**
|
||||
* @param {TalentData} data
|
||||
*/
|
||||
constructor(data) {
|
||||
this.Heroism = new TalentLevel(data.Heroism);
|
||||
this.YouthfulArrogance = new TalentLevel(data.YouthfulArrogance);
|
||||
this.Insensitive = new TalentLevel(data.Insensitive);
|
||||
this.OriginalAspiration = new TalentLevel(data.OriginalAspiration);
|
||||
this.LongingDistance = new TalentLevel(data.LongingDistance);
|
||||
this.Idealism = new TalentLevel(data.Idealism);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} CharacterData
|
||||
* @property {number} Id
|
||||
|
|
@ -372,21 +522,21 @@ class Talent {
|
|||
* @property {string} Name
|
||||
* @property {string} CodeName
|
||||
* @property {number} Rarity
|
||||
* @property {Object.<string, string>} WeaponType
|
||||
* @property {Object.<string, string>} ElementType
|
||||
* @property {Object.<string, string>} HitType
|
||||
* @property {Object.<string, string>} Camp
|
||||
* @property {Record<string, string>} WeaponType
|
||||
* @property {Record<string, string>} ElementType
|
||||
* @property {Record<string, string>} HitType
|
||||
* @property {Record<string, string>} Camp
|
||||
* @property {number} Gender
|
||||
* @property {Object} PartnerInfo
|
||||
* @property {PartnerInfoData} PartnerInfo
|
||||
* @property {StatsData} Stats
|
||||
* @property {Object.<('1'|'2'|'3'|'4'|'5'|'6'), LevelData>} Level
|
||||
* @property {Object.<('1'|'2'|'3'|'4'|'5'|'6'), ExtraLevelData>} ExtraLevel
|
||||
* @property {Record<('1'|'2'|'3'|'4'|'5'|'6'), LevelData>} Level
|
||||
* @property {Record<('1'|'2'|'3'|'4'|'5'|'6'), ExtraLevelData>} ExtraLevel
|
||||
* @property {SkillData} Skill
|
||||
* @property {PassiveData} Passive
|
||||
* @property {TalentData} Talent
|
||||
* @property {Record<('1'|'2'|'3'|'4'|'5'|'6'),TalentLevel>} Talent
|
||||
*/
|
||||
|
||||
class Character {
|
||||
export class Character {
|
||||
/**
|
||||
* @param {CharacterData} data
|
||||
*/
|
||||
|
|
@ -401,10 +551,11 @@ class Character {
|
|||
this.HitType = data.HitType;
|
||||
this.Camp = data.Camp;
|
||||
this.Gender = data.Gender;
|
||||
this.PartnerInfo = data.PartnerInfo;
|
||||
this.PartnerInfo = new PartnerInfo(data.PartnerInfo);
|
||||
this.Stats = new Stats(data.Stats);
|
||||
this.Level = {};
|
||||
this.ExtraLevel = {};
|
||||
this.Talent = {};
|
||||
|
||||
for (const [key, value] of Object.entries(data.Level)) {
|
||||
this.Level[key] = new Level(value);
|
||||
|
|
@ -414,6 +565,14 @@ class Character {
|
|||
}
|
||||
this.Skill = new Skill(data.Skill);
|
||||
this.Passive = new Passive(data.Passive);
|
||||
this.Talent = new Talent(data.Talent);
|
||||
|
||||
for (const [key, value] of Object.entries(data.Talent)) {
|
||||
this.Talent[key] = new TalentLevel(value);
|
||||
}
|
||||
}
|
||||
|
||||
async get_assets() {
|
||||
const result = await getSquareAvatar(this.Id);
|
||||
this.square_icon = result;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
BIN
resources/common/images/skills/assist.webp
Normal file
BIN
resources/common/images/skills/assist.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.3 KiB |
BIN
resources/common/images/skills/basic.webp
Normal file
BIN
resources/common/images/skills/basic.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.3 KiB |
BIN
resources/common/images/skills/chain.webp
Normal file
BIN
resources/common/images/skills/chain.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
BIN
resources/common/images/skills/core.webp
Normal file
BIN
resources/common/images/skills/core.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
BIN
resources/common/images/skills/dodge.webp
Normal file
BIN
resources/common/images/skills/dodge.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
BIN
resources/common/images/skills/special.webp
Normal file
BIN
resources/common/images/skills/special.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
|
|
@ -165,6 +165,31 @@
|
|||
background-image: url("../images/prop/IconSupport.png");
|
||||
}
|
||||
|
||||
.skill-icon {
|
||||
aspect-ratio: 1;
|
||||
background-size: contain;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
.skill-icon.assist, .skill-icon.Switch, .skill-icon.switch {
|
||||
background-image: url("../images/skills/assist.webp");
|
||||
}
|
||||
.skill-icon.basic, .skill-icon.Normal, .skill-icon.normal {
|
||||
background-image: url("../images/skills/basic.webp");
|
||||
}
|
||||
.skill-icon.chain, .skill-icon.Ultimate, .skill-icon.UltimateReady, .skill-icon.ultimateready {
|
||||
background-image: url("../images/skills/chain.webp");
|
||||
}
|
||||
.skill-icon.core, .skill-icon.Core, .skill-icon.CoreSkill, .skill-icon.coreskill {
|
||||
background-image: url("../images/skills/core.webp");
|
||||
}
|
||||
.skill-icon.dodge, .skill-icon.Evade, .skill-icon.evade {
|
||||
background-image: url("../images/skills/dodge.webp");
|
||||
}
|
||||
.skill-icon.special, .skill-icon.Special, .skill-icon.SpecialReady, .skill-icon.specialready {
|
||||
background-image: url("../images/skills/special.webp");
|
||||
}
|
||||
|
||||
.special-title {
|
||||
width: 100%;
|
||||
background-size: contain;
|
||||
|
|
|
|||
|
|
@ -182,6 +182,46 @@
|
|||
}
|
||||
}
|
||||
|
||||
.skill-icon {
|
||||
aspect-ratio: 1;
|
||||
background-size: contain;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
&.assist,
|
||||
&.Switch,
|
||||
&.switch {
|
||||
background-image: url('../images/skills/assist.webp');
|
||||
}
|
||||
&.basic,
|
||||
&.Normal,
|
||||
&.normal {
|
||||
background-image: url('../images/skills/basic.webp');
|
||||
}
|
||||
&.chain,
|
||||
&.Ultimate,
|
||||
&.UltimateReady,
|
||||
&.ultimateready {
|
||||
background-image: url('../images/skills/chain.webp');
|
||||
}
|
||||
&.core,
|
||||
&.Core,
|
||||
&.CoreSkill,
|
||||
&.coreskill {
|
||||
background-image: url('../images/skills/core.webp');
|
||||
}
|
||||
&.dodge,
|
||||
&.Evade,
|
||||
&.evade {
|
||||
background-image: url('../images/skills/dodge.webp');
|
||||
}
|
||||
&.special,
|
||||
&.Special,
|
||||
&.SpecialReady,
|
||||
&.specialready {
|
||||
background-image: url('../images/skills/special.webp');
|
||||
}
|
||||
}
|
||||
|
||||
.special-title {
|
||||
width: 100%;
|
||||
background-size: contain;
|
||||
|
|
|
|||
140
resources/skills/index.css
Normal file
140
resources/skills/index.css
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
.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;
|
||||
}
|
||||
|
||||
.skills {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5em;
|
||||
margin: 0.5em;
|
||||
}
|
||||
.skills .skill {
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
padding: 0.5em;
|
||||
border-radius: 0.5em;
|
||||
backdrop-filter: blur(5px);
|
||||
}
|
||||
.skills .skill .description {
|
||||
display: flex;
|
||||
gap: 0.5em;
|
||||
}
|
||||
.skills .skill .description .icon {
|
||||
width: 4em;
|
||||
aspect-ratio: 1;
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.skills .skill .description .icon .skill-icon {
|
||||
width: 100%;
|
||||
}
|
||||
.skills .skill .description .info .name {
|
||||
font-size: 1.4em;
|
||||
color: rgb(246, 202, 69);
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.skills .skill .description .info .item {
|
||||
margin-bottom: 0.3em;
|
||||
}
|
||||
.skills .skill .description .info .item .title {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
.skills .skill .description .info .item .content .line .skill-icon {
|
||||
width: 1.2em;
|
||||
margin-bottom: -0.2em;
|
||||
display: inline-block;
|
||||
}
|
||||
.skills .skill .detail .title {
|
||||
font-size: 1.1em;
|
||||
color: rgb(246, 202, 69);
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.skills .skill .detail .title .level {
|
||||
font-size: 0.8em;
|
||||
color: white;
|
||||
background-color: rgb(246, 202, 69);
|
||||
padding: 0.1em 0.3em;
|
||||
border-radius: 0.3em;
|
||||
margin-left: 0.5em;
|
||||
text-shadow: 0 0 0.1em rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
.skills .skill .detail .item .rate {
|
||||
display: grid;
|
||||
text-align: center;
|
||||
}
|
||||
.skills .skill .detail .item .rate .tb-tr {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
border-bottom: 0.1em solid rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
.skills .skill .detail .item .rate .tb-tr:first-child {
|
||||
border-top: 0.1em solid rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
.skills .skill .detail .item .rate .tb-tr:nth-child(odd) {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
.skills .skill .detail .item .rate .tb-td {
|
||||
padding: 0.2em 0.5em;
|
||||
font-size: 0.9em;
|
||||
border-right: 0.1em solid rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
.skills .skill .detail .item .rate .tb-td:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
.skills .skill .detail .item .detail {
|
||||
display: grid;
|
||||
text-align: center;
|
||||
}
|
||||
.skills .skill .detail .item .detail .tb-tr {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(8, 1fr);
|
||||
border-bottom: 0.1em solid rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
.skills .skill .detail .item .detail .tb-tr:first-child {
|
||||
border-top: 0.1em solid rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
.skills .skill .detail .item .detail .tb-tr:nth-child(odd) {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
.skills .skill .detail .item .detail .tb-td {
|
||||
padding: 0.2em 0.5em;
|
||||
font-size: 0.9em;
|
||||
border-right: 0.1em solid rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
.skills .skill .detail .item .detail .tb-td:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
/*# sourceMappingURL=index.css.map */
|
||||
112
resources/skills/index.html
Normal file
112
resources/skills/index.html
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
{{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="skills">
|
||||
{{each displays display}}
|
||||
<div class="skill">
|
||||
<div class="description">
|
||||
<div class="icon">
|
||||
<div class="skill-icon {{display.icon}}"></div>
|
||||
</div>
|
||||
<div class="info">
|
||||
<div class="name">{{display.name}}</div>
|
||||
{{each charData.Skill[display.key].Description skill}}
|
||||
{{if !!skill.description}}
|
||||
<div class="item">
|
||||
<div class="title">{{skill.Name}}</div>
|
||||
<div class="content no-zzz-font">{{@skill.description}}</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="detail">
|
||||
<div class="title">详细属性<span class="level">Lv.{{charData.Skill[display.key].level}}</span></div>
|
||||
{{if !!charData.Skill[display.key].rate}}
|
||||
{{each charData.Skill[display.key].rate rate}}
|
||||
<div class="item">
|
||||
<div class="name">{{rate.name}}</div>
|
||||
<div class="rate no-zzz-font">
|
||||
{{each rate.data.rate rt}}
|
||||
<div class="tb-tr">
|
||||
<div class="tb-td">{{rt.label}}</div>
|
||||
<div class="tb-td">{{rt.value}}</div>
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
|
||||
<div class="detail no-zzz-font">
|
||||
<div class="tb-tr">
|
||||
<div class="tb-td">#</div>
|
||||
<div class="tb-td">A</div>
|
||||
<div class="tb-td">B</div>
|
||||
<div class="tb-td">C</div>
|
||||
<div class="tb-td">D</div>
|
||||
<div class="tb-td">E</div>
|
||||
<div class="tb-td">F</div>
|
||||
<div class="tb-td">G</div>
|
||||
</div>
|
||||
{{each rate.data.details detail i}}
|
||||
<div class="tb-tr">
|
||||
<div class="tb-td">{{i}}</div>
|
||||
<div class="tb-td">{{detail.A}}</div>
|
||||
<div class="tb-td">{{detail.B}}</div>
|
||||
<div class="tb-td">{{detail.C}}</div>
|
||||
<div class="tb-td">{{detail.D}}</div>
|
||||
<div class="tb-td">{{detail.E}}</div>
|
||||
<div class="tb-td">{{detail.F}}</div>
|
||||
<div class="tb-td"></div>
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
</div>
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
{{/each}}
|
||||
{{if !!charData.Passive.currentLevel}}
|
||||
<div class="skill">
|
||||
<div class="description">
|
||||
<div class="icon">
|
||||
<div class="skill-icon core"></div>
|
||||
</div>
|
||||
<div class="info">
|
||||
<div class="name">核心技</div>
|
||||
<div class="detail">
|
||||
<div class="title">详细属性<span class="level">Lv.{{charData.Passive._level}}</span></div>
|
||||
</div>
|
||||
{{if !!charData.Passive.currentLevel.description}}
|
||||
{{each charData.Passive.currentLevel.description skill i}}
|
||||
<div class="item">
|
||||
<div class="title">{{charData.Passive.currentLevel.Name[i]}}</div>
|
||||
<div class="content no-zzz-font">{{@skill}}</div>
|
||||
</div>
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div style="text-align: center; font-size: 1em; color: #666; margin: 2em 0;">数据来源于Hakush</div>
|
||||
{{/block}}
|
||||
150
resources/skills/index.scss
Normal file
150
resources/skills/index.scss
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
.skills {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5em;
|
||||
margin: 0.5em;
|
||||
.skill {
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
padding: 0.5em;
|
||||
border-radius: 0.5em;
|
||||
backdrop-filter: blur(5px);
|
||||
.description {
|
||||
display: flex;
|
||||
gap: 0.5em;
|
||||
.icon {
|
||||
width: 4em;
|
||||
aspect-ratio: 1;
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
.skill-icon {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
.info {
|
||||
.name {
|
||||
font-size: 1.4em;
|
||||
color: rgb(246, 202, 69);
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.item {
|
||||
margin-bottom: 0.3em;
|
||||
.title {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
.content {
|
||||
.line {
|
||||
.skill-icon {
|
||||
width: 1.2em;
|
||||
margin-bottom: -0.2em;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.detail {
|
||||
.title {
|
||||
font-size: 1.1em;
|
||||
color: rgb(246, 202, 69);
|
||||
margin: 0.5em 0;
|
||||
.level {
|
||||
font-size: 0.8em;
|
||||
color: white;
|
||||
background-color: rgb(246, 202, 69);
|
||||
padding: 0.1em 0.3em;
|
||||
border-radius: 0.3em;
|
||||
margin-left: 0.5em;
|
||||
text-shadow: 0 0 0.1em rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
}
|
||||
.item {
|
||||
.rate {
|
||||
display: grid;
|
||||
text-align: center;
|
||||
.tb-tr {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
border-bottom: 0.1em solid rgba(255, 255, 255, 0.2);
|
||||
&:first-child {
|
||||
border-top: 0.1em solid rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
&:nth-child(odd) {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
.tb-td {
|
||||
padding: 0.2em 0.5em;
|
||||
font-size: 0.9em;
|
||||
border-right: 0.1em solid rgba(255, 255, 255, 0.2);
|
||||
&:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.detail {
|
||||
display: grid;
|
||||
text-align: center;
|
||||
.tb-tr {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(8, 1fr);
|
||||
border-bottom: 0.1em solid rgba(255, 255, 255, 0.2);
|
||||
&:first-child {
|
||||
border-top: 0.1em solid rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
&:nth-child(odd) {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
.tb-td {
|
||||
padding: 0.2em 0.5em;
|
||||
font-size: 0.9em;
|
||||
border-right: 0.1em solid rgba(255, 255, 255, 0.2);
|
||||
&:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue