From 15b14eece49a9f84acea9715f4f40a065b1c7aa3 Mon Sep 17 00:00:00 2001 From: bietiaop <1527109126@qq.com> Date: Mon, 2 Sep 2024 16:22:23 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E8=A7=92=E8=89=B2=E5=A4=A9=E8=B5=8B?= =?UTF-8?q?=E5=9B=BE=E9=89=B4=EF=BC=88=E6=94=AF=E6=8C=81=E8=87=AA=E5=AE=9A?= =?UTF-8?q?=E4=B9=89=E7=AD=89=E7=BA=A7=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 +- CHANGELOG.md | 3 + apps/help.js | 7 + apps/manage/assets.js | 124 +++++--- apps/wiki.js | 94 ++++++ lib/convert/char.js | 18 +- lib/download.js | 2 +- lib/download/const.js | 6 +- lib/download/download.js | 18 +- lib/hakush.js | 19 ++ lib/path.js | 2 + model/hakush/character.js | 299 +++++++++++++++----- resources/common/images/skills/assist.webp | Bin 0 -> 1300 bytes resources/common/images/skills/basic.webp | Bin 0 -> 1318 bytes resources/common/images/skills/chain.webp | Bin 0 -> 1442 bytes resources/common/images/skills/core.webp | Bin 0 -> 1474 bytes resources/common/images/skills/dodge.webp | Bin 0 -> 1462 bytes resources/common/images/skills/special.webp | Bin 0 -> 1082 bytes resources/common/style/index.css | 25 ++ resources/common/style/index.scss | 40 +++ resources/skills/index.css | 140 +++++++++ resources/skills/index.html | 112 ++++++++ resources/skills/index.scss | 150 ++++++++++ 23 files changed, 937 insertions(+), 124 deletions(-) create mode 100644 apps/wiki.js create mode 100644 lib/hakush.js create mode 100644 resources/common/images/skills/assist.webp create mode 100644 resources/common/images/skills/basic.webp create mode 100644 resources/common/images/skills/chain.webp create mode 100644 resources/common/images/skills/core.webp create mode 100644 resources/common/images/skills/dodge.webp create mode 100644 resources/common/images/skills/special.webp create mode 100644 resources/skills/index.css create mode 100644 resources/skills/index.html create mode 100644 resources/skills/index.scss diff --git a/.gitignore b/.gitignore index 98bf00a..5b4f4af 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,4 @@ data/**/*.* !data/.gitkeep resources/images/**/*.* -!resources/images/.gitkeep +resources/data/**/*.* diff --git a/CHANGELOG.md b/CHANGELOG.md index b2f2ded..80369c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +# 1.5 +* 新增角色天赋,发送 `%帮助` 查看如何使用 + # 1.4.2 * 版本机制 diff --git a/apps/help.js b/apps/help.js index cb53702..ccad74b 100644 --- a/apps/help.js +++ b/apps/help.js @@ -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: ['角色名+天赋[+等级]'], + }, ], }, { diff --git a/apps/manage/assets.js b/apps/manage/assets.js index 7d31364..98b3fb1 100644 --- a/apps/manage/assets.js +++ b/apps/manage/assets.js @@ -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')); } diff --git a/apps/wiki.js b/apps/wiki.js new file mode 100644 index 0000000..675d0d6 --- /dev/null +++ b/apps/wiki.js @@ -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); + } +} diff --git a/lib/convert/char.js b/lib/convert/char.js index acde264..a7cdf9f 100644 --- a/lib/convert/char.js +++ b/lib/convert/char.js @@ -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); diff --git a/lib/download.js b/lib/download.js index 4558ae3..673b281 100644 --- a/lib/download.js +++ b/lib/download.js @@ -135,7 +135,7 @@ export const getSuit3DImage = async suitId => { /** * 获取Hakush角色数据 * @param {string} charId - * @returns {Promise} + * @returns {Promise} 文件内容(JSON) */ export const getHakushCharacter = async charId => { const filename = `${charId}.json`; diff --git a/lib/download/const.js b/lib/download/const.js index e674675..84b667a 100644 --- a/lib/download/const.js +++ b/lib/download/const.js @@ -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'); diff --git a/lib/download/download.js b/lib/download/download.js index 613943b..4a6921f 100644 --- a/lib/download/download.js +++ b/lib/download/download.js @@ -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} 保存路径 */ 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} 保存路径 */ 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} 文件内容(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; + } }; diff --git a/lib/hakush.js b/lib/hakush.js new file mode 100644 index 0000000..38e68f5 --- /dev/null +++ b/lib/hakush.js @@ -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; +}; diff --git a/lib/path.js b/lib/path.js index faf3b24..81dd058 100644 --- a/lib/path.js +++ b/lib/path.js @@ -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 路径 diff --git a/model/hakush/character.js b/model/hakush/character.js index 9a9673d..d9f7ae3 100644 --- a/model/hakush/character.js +++ b/model/hakush/character.js @@ -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.>} Extra + * @property {Record>} 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.} Param + * @property {Record} 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 = + '
' + + this.Desc.replace( + //g, + '' + ) + .replace( + /(.+?)<\/color>/g, + '$2' + ) + .split('\n') + .join('
') + + '
'; } } /** * @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.>} Material + * @property {Recordstring, Record>} Material */ class SkillDetail { @@ -241,15 +318,71 @@ class SkillDetail { ); this.Material = data.Material; } + + /** + * 获取技能详情数据 + * @param {number} level + * @returns {Record} + */ + 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.} Basic - * @property {Object.} Dodge - * @property {Object.} Special - * @property {Object.} Chain - * @property {Object.} 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} + */ + getSkillData(skill, level = 12) { + return this[skill].getDetailData(level); + } + + /** + * 获取所有技能数据 + * @param {Record} levels + * @returns {Record>} + */ + 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 => + '
' + + item + .replace( + //g, + '' + ) + .replace( + /(.+?)<\/color>/g, + '$2' + ) + .split('\n') + .join('
') + + '
' + ); } } /** * @typedef {Object} PassiveData - * @property {Object.} Level - * @property {Object.>} Materials + * @property {Record} Level + * @property {Record>} 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.} WeaponType - * @property {Object.} ElementType - * @property {Object.} HitType - * @property {Object.} Camp + * @property {Record} WeaponType + * @property {Record} ElementType + * @property {Record} HitType + * @property {Record} 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; } } diff --git a/resources/common/images/skills/assist.webp b/resources/common/images/skills/assist.webp new file mode 100644 index 0000000000000000000000000000000000000000..29c670c203e2d5b6973a64a5fc0da47bb399ce0b GIT binary patch literal 1300 zcmV+v1?&1!Nk&Et1pok7MM6+kP&il$0000G0000l001ul06|PpNPGbR00C#9Mv|m7 zdWu^_cDGkRwTwBS*DN@8c?`5>8zS(s43?3<(DDsEQxJY#wk-;%r`k zN)cpsGk?|H)p$Zg|0lqz5B5GD4hIj|X!OEXKOw|PIY)In#+#l*e| zZPb0U6IYo5`D?qnVkL~P(+-jlAtZ^54=s?GUHKj$RV*xzm{Ecxu!83&wK6NO#~3A{ z(62%eYhTw95~>Z1NFGo_UcxFsguVtK#>SeUP=8X8pla#$QAJ|x`FE5c36P|W%MY0( zs$RI#m7tcz)fKZ=e4W~fM^NEwvv2S~@V=!EJOeH!mPT*$eqv_d(Z;2j*(TY9c{Vu~ z8N*J>Qb&(D7FPZNkC0EToHj;xF@xOu*`#EXtu?&l94;jb?yluU4Yw`6{aeNTTh@AQ z@8jYBgNNs>3RX}!AZh~u0I(4NodGH^05AYPZ8DQbq#~iABaQfg4T)s{#w3c#hS&~& z_VGX5`Vu`I?EX>z$#8?TXZaoI4mU(dhyKfL?{e=h#B|1bMb zYL7!N)+X2%BdcC6d;&l6l*!Es?t?uTl?r9rbv^|n8d%L==$+*(8zONa9d9)JI&)%m z^_%%9M-Wb-drklV{{NkE5oOb28U&gi=HPGk$F}9MvL^cW_nxBUvnZAT7d{#I{w;n` z8=KqkYNj#Fbzn70lDGB3$-F6V|3k0TP~KpV@8yQTYsQ^*3;RN z04|e=gWBJqX)b~cMnAA<;-e;*VmQZMaq6;lIb@ znTpaW6upE{UP*if;r}#J2K<(oX(%~Zv`CcM?V|FE`io!6*%6e3oTV9dsRPFU`q@WP zJgNjmO;lFjQ+U~Xi+!ZeFsOAk8};_$MHj+6Z`pN159GK^=?ZBVC!yFI<*CVMH~+f~Wzx<7t?D52!8=Y)V63atS+m*~+t z+&%#E(L6Q89DR?)r3p(n+Z%Q@E`im|VBDvG%L{vOvy*H-;Ex}Bm?TY1crqtu-QQB+ K{+DnxC;$Lvcyf3E literal 0 HcmV?d00001 diff --git a/resources/common/images/skills/basic.webp b/resources/common/images/skills/basic.webp new file mode 100644 index 0000000000000000000000000000000000000000..ba1f45833f5acb8a685190dbf5ebed54f6c96471 GIT binary patch literal 1318 zcmV+>1=;#iNk&E<1pok7MM6+kP&il$0000G0000l001ul06|PpNP7VQ00Cd1CXysI zdmP?9N2uZJBO-up{xRRJj({Qnj3Q?i&hwoudM0o<>o`Rzf?=~8z6FVEy|+eK=)o_S#6^Bt!Ub!P|^aslntc{1-YkR5}fwAibm4zZUTl9~|8 zhdmuDM>D=4n~->8FnE|M6@%BA#2e(KgK{3C1q7kMtU!W+d}=1dd7V&!71=`UJs}9{ zWa9nJ;dmI2swWd&q)d`+UUR9Mn7%F6Byc|aaX=x4cu>aLnT zOZ_KaMxpu+v_Y22kX*R16@b7j_C>i`2t}bSRdpq>Ts4w8qm7_$#_3ZQ>ua$p$U14(v&~1 zz|AS_4S11YUnnkn0092~R>7w`ZZL_b8*O6$+QVQWqw?jJCBXjC2{J^vVHkQ**RG?H z)*4fLjr^#B!JRvH9`rprwKxu%Dwtr;^B0(6RiTZDM_({;|Hd!#pNXB!5=k+RO}H~k zVEEU9qNLG9*}7Zjsi8ye^MN;;kBtcLfS&-)Ji93sABo<-AMHea#2s5YKk0M0A=D>g zYQNJiKKK8TbC^H;XB?w$6eDd<_|!twVJzNB4cPKM6xxiR2;f;Q^e(x4NUYwnJ}Tvj z=BwyCE1!wPWqBk;o*M}jd#Z|mzAKx_INfB(u8y(1EE=cltG#Q#as5LhUG?}jxYKC5;jClgb3 zNVfsto&0#p?4g6|`NBO>WycYM0a`5aRu-zko&PxGKj-%jU5h3Bp{xGlD`PMG)gL*m z(1vBKNq`N@!MIQ#PY^$H9Ghl1XT?;Unk(JKgS0ZBNOH~ES7 z$8GPb618FLZ}kt|kmoTF2$jQNG%=xh{7ZsOeyFxY2l{1s{av#R-faQ z&Sj%-v||53I2j=v^qsp*L=;7wqKXiN z;iuBOo22rgLbpmIJ1`SehLouB{qHKSu6fJ@J|=l}4z zOiqGAo#Q7WLclQp$)y5twCznJX}I;=U>Q1QJOK90%-e29)l^gNlN)R^dc5z8A9NI4#>nVfNwv3BqBT?Rx5dPkU0-ky|9x4gL2|eNYgaP1K!A^!meDhqJB=>6NH&gg=750aj2rAQ%Jy0Pqw5odGH^05AYPVJeVC zq#~i3uB}u+1_ZVMPMJbL_CS>n?i#{TpY{(i-c|ow_Ziwd?VH>K^?#&~m=D^o)(?9B zM_zy*(Eq0TFnb4j2KjD#A%4<+(fe6k(B-m_K_k|4EbSmuMDfM-cd$2emgw-yr~rnYXI72@?Uo&6_} zQcL|CL)7?~k&}N=$|mjO5ewK4QazADH}YCICXWlt2!Al0+&1Q1S8e%}LdB0)_RT2k zvw55yc&$4^qYxwo!*<;;eP)rrP(MA|>J%qFrX)yNt@CszP`|Nq?0S>hUj4{=6Gxwp zEDA??b&M7ZQGfnxNcUss=?*v7w&f@yo%F&yzAlf~r|iiqQHT3UO1tv(;G}1sF==SM zXKML*r8%!i`xc?;UY$2hsyv?})v*oSSSL~H z+>b*=20|d?39DRNgjD`ST7ooQ{H3iuZcL~9B^N+x!0q_`TlT;Iviy}ONP{gwS8}!k zFA_%Dzx_ToZz{7!8a_?F>5-rTYnpW$d^_F2uV>oiAhL?ZSONMkv@V(?|2_S~u+qaz zW@mG;&BFW(smV%e4sx6oI#S?+7p2bku&Mg9?kdm5;c+dbM6XtCvd$C237UU)AIzO6 z%l=*=<8Hy3;SI=98$7B|OAd+T%-=0L=GR5ZlM7Acl3Aidak<`-(d(oTF-p#uEVh#r zsWx0wF%`_RF^X?c1GfJ&b>iiJ-)Z@5_@&UO(7PX70@i#~*QNa$P}JV#$=7Mv+uS0+ zXRLv)Ulz=8e}@Z@NmU8huW8NEma5jKTplJ_bpzeU1M;ucQm}6xDvnfX9iygS!$wyu0v0JnvgxXYx?ej5tbD)p+-J zMT!D2J)fe{1z0O(h;)H%1KWYi3qN1paD_UE*w(mdh%97MWN(xnd|W=K&S>kYhoB(K`l{w<3sR;U^6I4DQs$xnzr&NiKg;$0b0wOt01^&24ZY&T w(+!ii{7AuUp-rUU3O9s&^}3uB@&%kXXMP0nRp&NRHQ=@z5{O9|4qSD!;%q_op7)K3(x%uk}hW~D~5L^0XlIUcT z@0v3GZm>Y)1s{pd5-~NO*_%UV)VU~PgE$RGEb9Yo|T9MM9c6eF$1a zNjKitf!FZ80)yu=V4C4)ZQNYWfEPoV=iLHWr$31nepb}*-qw5l8-N$}`df^@45>j- z7_47&=;7KRv2+3qK`b`Tz@J{ninQN$(fxWkq-=0BaQhZ zCFGCFH&!o@Ms}ThV9?5Hp@P;ZpT$fPBcFBtbHp!R<$oOOMGH2(2K8l@qaagC)nlko zRXvt13Q4Gbjd~}ONY}e7>oWQSV}B^QpZvf7I9E!)visYY)!!UeP&gp^0{{R}6abw8 zDlh;r06uLplSd>YA)zX@TBv{xiDdx9fZB+D*baaHKl|qj%-#_AI`coX{B&MV>^I{$ zLE2~hU*$jKcNkA4{(t_)*w#J$RsWIy)BYFESJ1!ozqfn@e=Po+{`32XYF|TN*k$-G zR%l5mv1`BkV4ihhm*z~WYt zG55Xse-}iJ`~U#{|DAEvr6O!%EKSL1{h|M%wB%|1$QiblcUAZ*Vt48Bg91bQjFE zFYcnmgUNF+E@yloGB4;z#R^$yLbKLsx$0Rie3(N|!~^I={|I2w7^sp^gx2$C9cEmd z?|9tQe0eHSW2&+gt_;&%icAyVc$m7#Oj^XUc)$5-l9mN~`1;Skn9j;%n@j4~|AbF` zb(SE8>Y!v97q>O(Xg1?cL;SO<_9l6OXrIb)M6`l=`Xb3CyXB89v zz$s8UGlGp05r;LWGV0`PMpEha**>995J~}xa?Fx)t17pCyV^Lb&xQPc9QIUq>+Mi% zdVq-%QHbNRz}(UbcxHM6^dvu`|8hm1psRx)r{-@NGW>y#Gk7Kyx={Ku0`d?kCldusow^nZHxX2!mQit3l3TuEikG{$GrpwnU0=oYD4 zmQSQOFP_n((mK4;Y&gN97jFC$i0BG$aQd(i{{kR=ktqd^v{1cnKUxzKmn5~p5N$Y{ z9t}qe&|HXVrAQpMmMosMHNykWm@Aop0Wwnb++It$0urPvQ$Cs}f*2up){aVtDM0bh zPCZOC3)5Pnl#9fY^WmzWGxzj{muokh-={x| zH`fF%u7(!PD2e69x5A>Z$Qp zHVJo}T{`#-O7Z(Gy1H_9Rmdny-&n)vF}vQwrASBeZ+PrO&I5r;4-_c1CXl%gh2l?4UgYb2Irj=i=xP-e{yngKt_VUuc=<80(m$rBn8m^!pcw=t z64^&{{JuVK@7z+J(Bw|^*sR(wMt}#A;17gweK*u`svtGVytx(tQybIdT&1+lGK9Fn c<_}@@A(QjPnBE40L+4_REt$uTvEKjy0AmW(L;wH) literal 0 HcmV?d00001 diff --git a/resources/common/images/skills/dodge.webp b/resources/common/images/skills/dodge.webp new file mode 100644 index 0000000000000000000000000000000000000000..0ec45aa9840054bc0715cd234a6d6c53dca6a668 GIT binary patch literal 1462 zcmV;n1xfl+Nk&Gl1pok7MM6+kP&il$0000G0000l001ul06|PpNTvY*00Cd1CXysI zc$iWM{{ko?0<`k?KGTMfYTLH5Hz(vdv1M?KDyj^BW8*U7qTU2D5hD6O0nvMT`}MzW@9ts@pO1~NlrPQ>i>)zP(w!SJ^?a-}fYRN{e9!!)qf83O zd^Iyw#V0Y?52BnO*~3zEQ_a**@F1}{->j!8tpaJb`6wpAwneu>sMWHlS{LB!-L9bGwxRnSQT*Y~-evo7?+t z03;ir(WAr0jgcBPJ4a0`gG{Yg$9)z!``(WY24sLjpw6aN(=YW1HItruFUJ#*L}ftc{x10HXHXiLnb+F4RrO zVHQcLB6lJ_C;_?04Hsj6Bs00^B2FCVWchwNB0g#|--(D={~xo>)B*Mcn^BIO!;-lw zK1oCD2T{*oI?lw6$b3IDRmIfSSXrgJbMrB9YfP4OH)8n4U&?o9W75mpU#%B+m!cO| zP&gps0{{RJ5&)e6Dlh;r06uLll13yVp`j|Q0DujNWdOyjtH^%X4uAhV%jD|F9tHSv z_201^fZ4V8ju3XW{@wWj!HJbPfYLQEZ~8yuJmdWR|9AX9z$fc3{BKvET_3WZ#ec3W z(#YQoxBjqH|M>B{91JNzy5y))xZClLv&P0RI2jy6Yckh1e3yTLNY1^ZuClnAAxB*Zm9f2c#Qi4d*`x0#7SidH{GH zrHbV$52c5%i`rmx&1hm=86IL1ZR;T2`RQ45)lgx4^gbT{^ID(jJ|^Zu7j;wl%lips zKph|?r~gxPNVP;hav;xwm=n01J6^@Fj{3{qgUSgDuPOok)5FS6?zbar@3=FNn3c|r zFTa0|I{ZQmXh#In#&q!C9*4P{xS>4GI4k`~40CHAat>`d_JI33$XDr|yTz5R8?LMD9{6FNdShwW!`{x&g1PweCtRB`Bl%?8|Gm3}A? zl1VHz+T1+iutOvQqgtMxUHKJJCBNgWg~4=tbl3jgCbuf*YbL3YzQzzGZ+MnjIn@SOOo z*>VKyZ_gNAm`@D*p%35*78jApavz>6jY(KMyCWZ24IfludH`B5|7-nI_wKhnYKb(0MlAP_~}mTrp+_bQ4gK>g8I}_nH;c!w=|NP*{Flucao3xPDRZ zTDLQwLk!X)vaN>G$Z+8+Jz(;7sH^j|;F#q(=9?>`Qp)`a3I82`{Rix;hrF*7BWJ#y z2lapT9F9SJ-I}XBG)hP-e|sX>+L*y_V%Ud?Rm7@%q7O9l88b;RnaQkc$1121tzJ(s zAWMrFh^VK>=nI`|6^*}WAQDJAOFi#1=vZ3+zuz5j4*DyrxmR-3L3ya+7(u!6F}UN$Nu-;_WTFu(#7wPEruODC0U zW?u2itUy*(_F@R_L`j2I3rjZfk*vY?50hG@+gna5~xsY6Lu>&$6E99MTAQ2_3 Qm-Q6W?x1fUn&p5108R15+5i9m literal 0 HcmV?d00001 diff --git a/resources/common/images/skills/special.webp b/resources/common/images/skills/special.webp new file mode 100644 index 0000000000000000000000000000000000000000..4007f5c98e681a2b08c5321e517117d912790053 GIT binary patch literal 1082 zcmV-A1jYMONk&F81ONb6MM6+kP&il$0000G0000l001ul06|PpNIC%k00DQvHUPk~ z!Pqv+JrNNAv#t&;*O7nF7;tObR;`@-vD7UiBPDaqe*r{{UjR<-kXlX$R)D)p1-z4e z|K}8O<=y={i0HopH&T=V&Ruu}1u}PV_5XJ^-%aV#P8*-<$>|80RNZT6;z;ywlu|Hj z4YuZ~{0()4WwW(JG!RZMn@teEv8IYFw)^p_NIJ}IUS!IuG2Qf{f@>6uT3!6Y8WqXg zXs{-6_%cWBtLd=>BefZNR!AnvR0M$!+lT8|aM!fT7}9nUA`u2&6qI~PFnGCwa#*v( z@5P;TT|v2Pp!|uV0>gMQP7OV`OZ+^G5}A_&7V!JR?-zd$`1>(n$HR8)eTq)H_l(sT z`2Cv<|Ah(?kGM7bj~e}t8UC*qy~h80R{ua9>kXDPf6|WnpRE2W-M{^&`8NVqP&gpc z0ssJT4gj42Dlh;r06uLrl}Mx_q9G%S0DujNYyh1(K~ny9&;FsJxd^SXUSKysI!S#1 zI#IvA|Ix7E{DpPz;7WoS=a8#ASXw$uIE_?>_n8qtxI(f_c4t`aQ|CmP(@qu3?zt%vk+t4EBZW7Z+5a`1PK85V88lgDCuTcg^$0hbl zH>N@p)!*~r3G8=F~ z;}cE{3of1j8DO_8I@v6M?hv(VsN{BmWDU#vWAE|zRK7P$qFegerAs7o8vEXp4+Mxr zbvS$LTe4bjNyGzhuOf4tzLbl*Pg7`6br6HM3+-var@Kj}ZxMzCl^&rWzAdyHMHB+& zj#YX8`6K+4AMySmEd)1VuU9{h1I->n@&k!oym2)+lDb$#&xBAT0>`|*Fl~& zv;V`#9{(;dr-A;uuz?+V-c6+Bw;H+B9-rLv6PM0YUR)w6SCiK)NVGzF`SnJxkaohldbxs{K8XRqnxY;P-J{;s=^DcgJE1WA)%ny-a_z8OKeeVl1# z3oe=a5t8pJEvV-1Amg;v>iC>vhDVU?a2=<7KJPwmk^ps@rj2O*HF2Gi6HW!WOpPuu z=Rj8);k*;gZ84DDqu|>)0~Dl}e^IZe-LqW-4H!uFVFO7lDT=Pe#I9*cs515IyMx3a z1D6c^Skn`hc+g8mc|Yn9g*3O32oEmb37pC^osl$Cr|}SCv)-?_9jDJTC!p;z*VQ7b zWOeIn=abzrGl1x7OcKeW09;CLaFVbmb3I5oBSZ-G1&}CO(K|0)F;2v}e*0Vi07qE) A+yDRo literal 0 HcmV?d00001 diff --git a/resources/common/style/index.css b/resources/common/style/index.css index 7b7ef8c..91289e3 100644 --- a/resources/common/style/index.css +++ b/resources/common/style/index.css @@ -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; diff --git a/resources/common/style/index.scss b/resources/common/style/index.scss index 72cd3e1..0c23fe1 100644 --- a/resources/common/style/index.scss +++ b/resources/common/style/index.scss @@ -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; diff --git a/resources/skills/index.css b/resources/skills/index.css new file mode 100644 index 0000000..c0d611f --- /dev/null +++ b/resources/skills/index.css @@ -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 */ diff --git a/resources/skills/index.html b/resources/skills/index.html new file mode 100644 index 0000000..21257f2 --- /dev/null +++ b/resources/skills/index.html @@ -0,0 +1,112 @@ +{{extend defaultLayout}} + +{{block 'css'}} + +{{/block}} + +{{block 'main'}} +
+
+ Avatar +
+
+
+
{{charData.PartnerInfo.Name}}
+
{{charData.PartnerInfo.FullName}}
+
+
+
{{@charData.PartnerInfo.ImpressionF}}
+
{{@charData.PartnerInfo.ImpressionM}}
+
+
+
+
+ {{each displays display}} +
+
+
+
+
+
+
{{display.name}}
+ {{each charData.Skill[display.key].Description skill}} + {{if !!skill.description}} +
+
{{skill.Name}}
+
{{@skill.description}}
+
+ {{/if}} + {{/each}} +
+
+
+
详细属性Lv.{{charData.Skill[display.key].level}}
+ {{if !!charData.Skill[display.key].rate}} + {{each charData.Skill[display.key].rate rate}} +
+
{{rate.name}}
+
+ {{each rate.data.rate rt}} +
+
{{rt.label}}
+
{{rt.value}}
+
+ {{/each}} +
+ +
+
+
#
+
A
+
B
+
C
+
D
+
E
+
F
+
G
+
+ {{each rate.data.details detail i}} +
+
{{i}}
+
{{detail.A}}
+
{{detail.B}}
+
{{detail.C}}
+
{{detail.D}}
+
{{detail.E}}
+
{{detail.F}}
+
+
+ {{/each}} +
+
+ {{/each}} + {{/if}} +
+
+ {{/each}} + {{if !!charData.Passive.currentLevel}} +
+
+
+
+
+
+
核心技
+
+
详细属性Lv.{{charData.Passive._level}}
+
+ {{if !!charData.Passive.currentLevel.description}} + {{each charData.Passive.currentLevel.description skill i}} +
+
{{charData.Passive.currentLevel.Name[i]}}
+
{{@skill}}
+
+ {{/each}} + {{/if}} +
+
+
+ {{/if}} +
+
数据来源于Hakush
+{{/block}} \ No newline at end of file diff --git a/resources/skills/index.scss b/resources/skills/index.scss new file mode 100644 index 0000000..523dc6c --- /dev/null +++ b/resources/skills/index.scss @@ -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; + } + } + } + } + } + } +}