feat: panel

This commit is contained in:
bietiaop 2024-07-13 17:52:31 +08:00
parent 35c8c95b80
commit 27171c5727
77 changed files with 2126 additions and 312 deletions

View file

@ -36,7 +36,6 @@ export class Card extends ZZZPlugin {
}); });
if (!zzzBuddyList) return false; if (!zzzBuddyList) return false;
indexData.buddy_list = zzzBuddyList.list; indexData.buddy_list = zzzBuddyList.list;
const finalIndexData = new ZZZIndexResp(indexData); const finalIndexData = new ZZZIndexResp(indexData);
this.e.playerCard.player.region_name = this.e.playerCard.player.region_name =
finalIndexData.stats.world_level_name; finalIndexData.stats.world_level_name;

View file

@ -103,10 +103,12 @@ export class GachaLog extends ZZZPlugin {
} }
await redis.set(`ZZZ:GACHA:${uid}:LASTTIME`, Date.now()); await redis.set(`ZZZ:GACHA:${uid}:LASTTIME`, Date.now());
this.reply('正在更新抽卡记录,可能需要一段时间,请耐心等待'); this.reply('正在更新抽卡记录,可能需要一段时间,请耐心等待');
const data = await updateGachaLog(key, uid); const { data, count } = await updateGachaLog(key, uid);
let msg = `抽卡记录更新成功,共${Object.keys(data).length}个卡池`; let msg = `抽卡记录更新成功,共${Object.keys(data).length}个卡池`;
for (const name in data) { for (const name in data) {
msg += `\n${name}一共${data[name].length}条记录`; msg += `\n${name}一共${data[name].length}条记录,新增${
count[name] || 0
}条记录`;
} }
await this.reply(msg); await this.reply(msg);
return false; return false;

View file

@ -17,11 +17,11 @@ export class Panel extends ZZZPlugin {
}, },
{ {
reg: `${rulePrefix}面板列表$`, reg: `${rulePrefix}面板列表$`,
fnc: 'getPanelList', fnc: 'getCharPanelList',
}, },
{ {
reg: `${rulePrefix}(.+)面板$`, reg: `${rulePrefix}(.+)面板$`,
fnc: 'getPanel', fnc: 'getCharPanel',
}, },
], ],
}); });
@ -32,10 +32,11 @@ export class Panel extends ZZZPlugin {
await this.getPlayerInfo(); await this.getPlayerInfo();
const result = await refreshPanel(this.e, api, uid, deviceFp); const result = await refreshPanel(this.e, api, uid, deviceFp);
const newChar = result.filter(item => item.isNew); const newChar = result.filter(item => item.isNew);
let str = '面板列表获取成功,本次共刷新了' + newChar.length + '个角色:'; let str = '面板列表获取成功,本次共刷新了' + newChar.length + '个角色:\n';
for (const item of result) { for (const item of result) {
str += '\n' + item.name_mi18n; str += item.name_mi18n + (item.isNew ? '(新)' : '') + '、';
} }
str = str.slice(0, -1);
str += '\n总计' + result.length + '个角色'; str += '\n总计' + result.length + '个角色';
await this.reply(str); await this.reply(str);
// const finalData = { // const finalData = {
@ -43,7 +44,7 @@ export class Panel extends ZZZPlugin {
// }; // };
// await render(this.e, 'panel/refresh.html', finalData); // await render(this.e, 'panel/refresh.html', finalData);
} }
async getPanelList() { async getCharPanelList() {
const uid = await this.getUID(); const uid = await this.getUID();
if (!uid) return false; if (!uid) return false;
const noteData = getPanelList(uid); const noteData = getPanelList(uid);
@ -59,16 +60,20 @@ export class Panel extends ZZZPlugin {
// }; // };
// await render(this.e, 'panel/list.html', finalData); // await render(this.e, 'panel/list.html', finalData);
} }
async getPanel() { async getCharPanel() {
const uid = await this.getUID(); const uid = await this.getUID();
if (!uid) return false; if (!uid) return false;
const reg = new RegExp(`${rulePrefix}(.+)面板$`); const reg = new RegExp(`${rulePrefix}(.+)面板$`);
const name = this.e.msg.match(reg)[4]; const name = this.e.msg.match(reg)[4];
const data = getPanel(uid, name); const data = getPanel(uid, name);
await this.reply(JSON.stringify(data, null, 2)); if (!data) {
// const finalData = { await this.reply(`未找到角色${name}的面板信息`);
// list: noteData, return false;
// }; }
// await render(this.e, 'panel/list.html', finalData); await data.get_detail_assets();
const finalData = {
charData: data,
};
await render(this.e, 'panel/card.html', finalData);
} }
} }

View file

@ -96,10 +96,11 @@ export function getPanelList(uid) {
* @returns {ZZZAvatarInfo | null} * @returns {ZZZAvatarInfo | null}
*/ */
export function getPanel(uid, name) { export function getPanel(uid, name) {
logger.debug('获取面板数据', uid, name); const _data = getPanelData(uid);
const data = getPanelData(uid).map(item => new ZZZAvatarInfo(item)); const data = _data.map(item => new ZZZAvatarInfo(item));
const id = char.atlasToID(name); const id = char.atlasToID(name);
logger.debug('获取角色ID', id);
if (!id) return null; if (!id) return null;
return data.find(item => item.id === id) || null; const result = data.find(item => item.id === id);
if (!result) return null;
return result;
} }

View file

@ -3,3 +3,9 @@ export * as element from './convert/element.js';
export * as char from './convert/char.js'; export * as char from './convert/char.js';
export * as weapon from './convert/weapon.js'; export * as weapon from './convert/weapon.js';
export * as equip from './convert/equip.js';
export * as rank from './convert/rank.js';
export * as property from './convert/property.js';

View file

@ -27,7 +27,7 @@ export const IDToCharName = (id, full = true, en = false) => {
export const IDToCharSprite = id => { export const IDToCharSprite = id => {
const data = PartnerId2SpriteId?.[id]; const data = PartnerId2SpriteId?.[id];
if (!data) return null; if (!data) return null;
return data?.['sprite']; return data?.['sprite_id'];
}; };
/** /**

19
lib/convert/equip.js Normal file
View file

@ -0,0 +1,19 @@
import { getMapData } from '../../utils/file.js';
const equipData = getMapData('EquipId2Data');
/**
* 获取驱动盘装备的图片
* @param {string | number} equipId
* @returns {string | null}
*/
export function equipIdToSprite(equipId) {
equipId = equipId.toString();
if (equipId.length === 5) {
const suitId = equipId.slice(0, 3) + '00';
if (equipData.hasOwnProperty(suitId)) {
return equipData[suitId]['sprite_file'].replace('3D', '');
}
}
return null;
}

41
lib/convert/property.js Normal file
View file

@ -0,0 +1,41 @@
const prop_id = {
111: 'hpmax',
121: 'attack',
131: 'def',
122: 'breakstun',
201: 'crit',
211: 'critdam',
314: 'elementabnormalpower',
312: 'elementmystery',
231: 'penratio',
232: 'penvalue',
305: 'sprecover',
310: 'spgetratio',
115: 'spmax',
315: 'physdmg',
316: 'fire',
317: 'ice',
318: 'thunder',
319: 'dungeonbuffether',
};
const pro_id = {
1: 'attack',
2: 'stun',
3: 'anomaly',
4: 'support',
5: 'defense',
};
/**
* 获取属性css类名
* @param {string} _id 属性id
* @returns {string | null}
*/
export function idToClassName(_id) {
let propId = _id.toString();
propId = propId.slice(0, 3);
const propIcon = prop_id[propId];
if (!propIcon) return null;
return propIcon;
}

14
lib/convert/rank.js Normal file
View file

@ -0,0 +1,14 @@
const RANK_MAP = {
4: 'S',
3: 'A',
2: 'B',
};
/**
* 获取星级对应的字母
* @param {string | number} id
* @returns {string}
*/
export function getRankChar(id) {
return RANK_MAP[id] || '';
}

View file

@ -2,7 +2,7 @@ import path from 'path';
import fs from 'fs'; import fs from 'fs';
import { ZZZ_SQUARE_AVATAR, ZZZ_SQUARE_BANGBOO } from './mysapi/api.js'; import { ZZZ_SQUARE_AVATAR, ZZZ_SQUARE_BANGBOO } from './mysapi/api.js';
import { imageResourcesPath } from './path.js'; import { imageResourcesPath } from './path.js';
import { weapon } from './convert.js'; import { char, equip, weapon } from './convert.js';
import { getResourceRemotePath } from './assets.js'; import { getResourceRemotePath } from './assets.js';
const ZZZ_SQUARE_AVATAR_PATH = path.join(imageResourcesPath, 'square_avatar'); const ZZZ_SQUARE_AVATAR_PATH = path.join(imageResourcesPath, 'square_avatar');
@ -11,9 +11,18 @@ const ZZZ_SQUARE_BANGBOO_PATH = path.join(
'bangboo_square_avatar' 'bangboo_square_avatar'
); );
const ZZZ_WEAPON_PATH = path.join(imageResourcesPath, 'weapon'); const ZZZ_WEAPON_PATH = path.join(imageResourcesPath, 'weapon');
const ZZZ_GUIDES_PATH = path.join(imageResourcesPath, 'guides'); const ZZZ_ROLE_PATH = path.join(imageResourcesPath, 'role');
const ZZZ_ROLE_CIRCLE_PATH = path.join(imageResourcesPath, 'role_circle');
const ZZZ_SUIT_3D_PATH = path.join(imageResourcesPath, 'suit_3d');
const ZZZ_SUIT_PATH = path.join(imageResourcesPath, 'suit');
// const ZZZ_GUIDES_PATH = path.join(imageResourcesPath, 'guides');
// 将下面的下载封装起来支持错误重试5次 /**
* 下载文件
* @param {string} url 下载地址
* @param {string} savePath 保存路径
* @returns
*/
const downloadFile = async (url, savePath) => { const downloadFile = async (url, savePath) => {
const _download = async (url, savePath, retry = 0) => { const _download = async (url, savePath, retry = 0) => {
if (retry > 5) { if (retry > 5) {
@ -36,7 +45,7 @@ const downloadFile = async (url, savePath) => {
}; };
/** /**
* * 获取角色头像方形
* @param {string | number} charID * @param {string | number} charID
* @returns Promise<string> * @returns Promise<string>
*/ */
@ -51,7 +60,7 @@ export const getSquareAvatar = async charID => {
}; };
/** /**
* * 获取邦布头像方形
* @param {string | number} bangbooId * @param {string | number} bangbooId
* @returns Promise<string> * @returns Promise<string>
*/ */
@ -66,7 +75,7 @@ export const getSquareBangboo = async bangbooId => {
}; };
/** /**
* Get weapon image path * 获取武器图片
* @param {string} id * @param {string} id
* @returns Promise<string> * @returns Promise<string>
*/ */
@ -80,6 +89,72 @@ export const getWeaponImage = async id => {
const url = await getResourceRemotePath('weapon', filename); const url = await getResourceRemotePath('weapon', filename);
const savePath = weaponPath; const savePath = weaponPath;
const download = await downloadFile(url, savePath); const download = await downloadFile(url, savePath);
logger.mark('getWeaponImage', download); logger.debug('getWeaponImage', download);
return download;
};
/**
* 获取角色图片
* @param {string | number} id
* @returns Promise<string>
*/
export const getRoleImage = async id => {
const sprite = char.IDToCharSprite(id);
if (sprite === null) return null;
const filename = `IconRole${sprite}.png`;
const rolePath = path.join(ZZZ_ROLE_PATH, filename);
if (fs.existsSync(rolePath)) return rolePath;
const url = await getResourceRemotePath('role', filename);
const savePath = rolePath;
const download = await downloadFile(url, savePath);
return download;
};
/**
* 获取角色圆形图片
* @param {string | number} id
* @returns Promise<string>
*/
export const getRoleCircleImage = async id => {
const sprite = char.IDToCharSprite(id);
if (sprite === null) return null;
const filename = `IconRoleCircle${sprite}.png`;
const roleCirclePath = path.join(ZZZ_ROLE_CIRCLE_PATH, filename);
if (fs.existsSync(roleCirclePath)) return roleCirclePath;
const url = await getResourceRemotePath('role_circle', filename);
const savePath = roleCirclePath;
const download = await downloadFile(url, savePath);
return download;
};
/**
* 获取套装图片
* @param {string | number} suitId
* @returns Promise<string>
*/
export const getSuitImage = async suitId => {
const suitName = equip.equipIdToSprite(suitId);
const filename = `${suitName}.png`;
const suitPath = path.join(ZZZ_SUIT_PATH, filename);
if (fs.existsSync(suitPath)) return suitPath;
const url = await getResourceRemotePath('suit', filename);
const savePath = suitPath;
const download = await downloadFile(url, savePath);
return download;
};
/**
* 获取3D套装图片
* @param {string | number} suitId
* @returns Promise<string>
*/
export const getSuit3DImage = async suitId => {
const suitName = equip.equipIdToSprite(suitId);
const filename = `${suitName}_3d.png`;
const suitPath = path.join(ZZZ_SUIT_3D_PATH, filename);
if (fs.existsSync(suitPath)) return suitPath;
const url = await getResourceRemotePath('suit_3d', filename);
const savePath = suitPath;
const download = await downloadFile(url, savePath);
return download; return download;
}; };

View file

@ -1,5 +1,6 @@
import { SingleGachaLog, ZZZGachaLogResp } from '../model/gacha.js'; import { SingleGachaLog, ZZZGachaLogResp } from '../model/gacha.js';
import { sleep } from '../utils/time.js'; import { sleep } from '../utils/time.js';
import { rank } from './convert.js';
import { getGachaLog, saveGachaLog } from './db.js'; import { getGachaLog, saveGachaLog } from './db.js';
import { ZZZ_GET_GACHA_LOG_API } from './mysapi/api.js'; import { ZZZ_GET_GACHA_LOG_API } from './mysapi/api.js';
@ -96,7 +97,12 @@ export async function getZZZGachaLogByAuthkey(
* @param {string} authKey * @param {string} authKey
* @param {string} uid * @param {string} uid
* @returns {Promise<{ * @returns {Promise<{
* [x: string]: SingleGachaLog[]; * data: {
* [x: string]: SingleGachaLog[];
* },
* count: {
* [x: string]: number;
* }
* }>} * }>}
*/ */
export async function updateGachaLog(authKey, uid) { export async function updateGachaLog(authKey, uid) {
@ -104,26 +110,13 @@ export async function updateGachaLog(authKey, uid) {
if (!previousLog) { if (!previousLog) {
previousLog = {}; previousLog = {};
} }
let newCount = {};
for (const name in gacha_type_meta_data) { for (const name in gacha_type_meta_data) {
if (!previousLog[name]) { if (!previousLog[name]) {
previousLog[name] = []; previousLog[name] = [];
} }
previousLog[name] = previousLog[name].map( newCount[name] = 0;
i => previousLog[name] = previousLog[name].map(i => new SingleGachaLog(i));
new SingleGachaLog(
i.uid,
i.gacha_id,
i.gacha_type,
i.item_id,
i.count,
i.time,
i.name,
i.lang,
i.item_type,
i.rank_type,
i.id
)
);
const lastSaved = previousLog[name]?.[0]; const lastSaved = previousLog[name]?.[0];
let page = 1; let page = 1;
let endId = '0'; let endId = '0';
@ -145,6 +138,7 @@ export async function updateGachaLog(authKey, uid) {
break queryLabel; break queryLabel;
} }
newData.push(item); newData.push(item);
newCount[name]++;
} }
endId = log.list[log.list.length - 1]?.id || endId; endId = log.list[log.list.length - 1]?.id || endId;
page++; page++;
@ -154,14 +148,12 @@ export async function updateGachaLog(authKey, uid) {
previousLog[name] = [...newData, ...previousLog[name]]; previousLog[name] = [...newData, ...previousLog[name]];
} }
saveGachaLog(uid, previousLog); saveGachaLog(uid, previousLog);
return previousLog; return {
data: previousLog,
count: newCount,
};
} }
const RANK_MAP = {
4: 'S',
3: 'A',
2: 'B',
};
const HOMO_TAG = ['非到极致', '运气不好', '平稳保底', '小欧一把', '欧狗在此']; const HOMO_TAG = ['非到极致', '运气不好', '平稳保底', '小欧一把', '欧狗在此'];
const EMOJI = [ const EMOJI = [
[4, 8, 13], [4, 8, 13],
@ -186,10 +178,10 @@ const NORMAL_LIST = [
]; ];
const FLOORS_MAP = { const FLOORS_MAP = {
'邦布频段': [50, 70], 邦布频段: [50, 70],
'音擎频段': [50, 70], 音擎频段: [50, 70],
'独家频段': [60, 80], 独家频段: [60, 80],
'常驻频段': [60, 80], 常驻频段: [60, 80],
}; };
function getLevelFromList(ast, lst) { function getLevelFromList(ast, lst) {
@ -214,22 +206,7 @@ export async function anaylizeGachaLog(uid) {
} }
const result = []; const result = [];
for (const name in savedData) { for (const name in savedData) {
const data = savedData[name].map( const data = savedData[name].map(item => new SingleGachaLog(item));
item =>
new SingleGachaLog(
item.uid,
item.gacha_id,
item.gacha_type,
item.item_id,
item.count,
item.time,
item.name,
item.lang,
item.item_type,
item.rank_type,
item.id
)
);
const earliest = data[data.length - 1]; const earliest = data[data.length - 1];
const latest = data[0]; const latest = data[0];
const list = []; const list = [];
@ -256,7 +233,7 @@ export async function anaylizeGachaLog(uid) {
} }
list.push({ list.push({
...item, ...item,
rank_type_label: RANK_MAP[item.rank_type], rank_type_label: rank.getRankChar(item.rank_type),
isUp: isUp, isUp: isUp,
totalCount: '-', totalCount: '-',
color: 'white', color: 'white',

View file

@ -47,6 +47,7 @@ function render(e, renderPath, renderData = {}, cfg = {}) {
resourcesPath: resPath, resourcesPath: resPath,
currentPath: renderPathFull, currentPath: renderPathFull,
playerInfo: path.join(layoutPathFull, 'playerinfo.html'), playerInfo: path.join(layoutPathFull, 'playerinfo.html'),
specialTitle: path.join(layoutPathFull, 'specialtitle.html'),
copyright: `Created By ${version.name}<span class="version">${version.yunzai}</span> & ${pluginName}<span class="version">${version.version}</span>`, copyright: `Created By ${version.name}<span class="version">${version.yunzai}</span> & ${pluginName}<span class="version">${version.version}</span>`,
createdby: `Created By <span class="highlight">${pluginName}</span> & Powered By <span class="highlight">ZZZure</span>`, createdby: `Created By <span class="highlight">${pluginName}</span> & Powered By <span class="highlight">ZZZure</span>`,
}, },

View file

@ -1,5 +1,5 @@
import { element } from '../lib/convert.js'; import { element } from '../lib/convert.js';
import { getSquareAvatar } from '../lib/download.js'; import { getRoleImage, getSquareAvatar } from '../lib/download.js';
import { Equip, Weapon } from './equip.js'; import { Equip, Weapon } from './equip.js';
import { Property } from './property.js'; import { Property } from './property.js';
import { Skill } from './skill.js'; import { Skill } from './skill.js';
@ -114,20 +114,40 @@ export class ZZZAvatarBasic {
this.element_str = element.IDToElement(element_type); this.element_str = element.IDToElement(element_type);
} }
async get_assets() {
const result = await getSquareAvatar(this.id);
this.square_icon = result;
}
} }
/** /**
* @class * @class
*/ */
export class Rank { export class Rank {
// 类型标注
/** @type {number} */
id;
/** @type {string} */
name;
/** @type {string} */
desc;
/** @type {number} */
pos;
/** @type {boolean} */
is_unlocked;
/** /**
* @param {number} id * @param {{
* @param {string} name * id: number;
* @param {string} desc * name: string;
* @param {number} pos * desc: string;
* @param {boolean} is_unlocked * pos: number;
* is_unlocked: boolean;
* }} data
*/ */
constructor(id, name, desc, pos, is_unlocked) { constructor(data) {
const { id, name, desc, pos, is_unlocked } = data;
this.id = id; this.id = id;
this.name = name; this.name = name;
this.desc = desc; this.desc = desc;
@ -158,11 +178,46 @@ export class ZZZAvatarInfo {
* skills: Skill[]; * skills: Skill[];
* rank: number; * rank: number;
* ranks: Rank[]; * ranks: Rank[];
*
* isNew?: boolean; * isNew?: boolean;
* }} data * }} data
*/ */
constructor(data) { constructor(data) {
// 类型标注
/** @type {number} */
this.id;
/** @type {number} */
this.level;
/** @type {string} */
this.name_mi18n;
/** @type {string} */
this.full_name_mi18n;
/** @type {number} */
this.element_type;
/** @type {string} */
this.camp_name_mi18n;
/** @type {number} */
this.avatar_profession;
/** @type {string} */
this.rarity;
/** @type {string} */
this.group_icon_path;
/** @type {string} */
this.hollow_icon_path;
/** @type {Equip[]} */
this.equip;
/** @type {Weapon} */
this.weapon;
/** @type {Property[]} */
this.properties;
/** @type {Skill[]} */
this.skills;
/** @type {number} */
this.rank;
/** @type {Rank[]} */
this.ranks;
/** @type {boolean} */
this.isNew;
const { const {
id, id,
level, level,
@ -192,22 +247,59 @@ export class ZZZAvatarInfo {
this.rarity = rarity; this.rarity = rarity;
this.group_icon_path = group_icon_path; this.group_icon_path = group_icon_path;
this.hollow_icon_path = hollow_icon_path; this.hollow_icon_path = hollow_icon_path;
this.equip = equip; this.equip =
this.weapon = weapon; equip &&
this.properties = properties; (Array.isArray(equip)
this.skills = skills; ? equip.map(equip => new Equip(equip))
: new Equip(equip));
this.weapon = weapon ? new Weapon(weapon) : null;
this.properties =
properties && properties.map(property => new Property(property));
this.skills = skills && skills.map(skill => new Skill(skill));
this.rank = rank; this.rank = rank;
this.ranks = ranks; this.ranks = ranks && ranks.map(rank => new Rank(rank));
this.ranks_num = this.ranks.filter(rank => rank.is_unlocked).length;
this.element_str = element.IDToElement(element_type); this.element_str = element.IDToElement(element_type);
this.isNew = isNew; this.isNew = isNew;
} }
getProperty(name) {
return this.properties.find(property => property.property_name === name);
}
get basic_properties() {
const data = {
hpmax: this.getProperty('生命值'),
attack: this.getProperty('攻击力'),
def: this.getProperty('防御力'),
breakstun: this.getProperty('冲击力'),
crit: this.getProperty('暴击率'),
critdam: this.getProperty('暴击伤害'),
elementabnormalpower: this.getProperty('异常掌控'),
elementmystery: this.getProperty('异常精通'),
penratio: this.getProperty('穿透率'),
sprecover: this.getProperty('能量自动回复'),
};
logger.debug('basic_properties', data);
return data;
}
async get_basic_assets() { async get_basic_assets() {
const result = await getSquareAvatar(this.id); const result = await getSquareAvatar(this.id);
this.square_icon = result; this.square_icon = result;
} }
async get_detail_assets() {
const role_icon = await getRoleImage(this.id);
this.role_icon = role_icon;
await this.weapon.get_assets();
for (const equip of this.equip) {
await equip.get_assets();
}
}
async get_assets() { async get_assets() {
await this.get_basic_assets(); await this.get_basic_assets();
} }

View file

@ -1,16 +1,34 @@
import { property } from '../lib/convert.js';
import { getSuitImage, getWeaponImage } from '../lib/download.js';
/** /**
* @class * @class
*/ */
export class EquipProperty { export class EquipProperty {
// 类型标注
/** @type {string} */
property_name;
/** @type {number} */
property_id;
/** @type {string} */
base;
/** @type {string} */
classname;
/** /**
* @param {string} property_name * @param {{
* @param {number} property_id * property_name: string;
* @param {string} base * property_id: number;
* base: string
* }} data
*/ */
constructor(property_name, property_id, base) { constructor(data) {
const { property_name, property_id, base } = data;
this.property_name = property_name; this.property_name = property_name;
this.property_id = property_id; this.property_id = property_id;
this.base = base; this.base = base;
this.classname = property.idToClassName(property_id);
} }
} }
@ -18,15 +36,30 @@ export class EquipProperty {
* @class * @class
*/ */
export class EquipMainProperty { export class EquipMainProperty {
// 类型标注
/** @type {string} */
property_name;
/** @type {number} */
property_id;
/** @type {string} */
base;
/** @type {string} */
classname;
/** /**
* @param {string} property_name * @param {{
* @param {number} property_id * property_name: string;
* @param {string} base * property_id: number;
* base: string;
* }} data
*/ */
constructor(property_name, property_id, base) { constructor(data) {
const { property_name, property_id, base } = data;
this.property_name = property_name; this.property_name = property_name;
this.property_id = property_id; this.property_id = property_id;
this.base = base; this.base = base;
this.classname = property.idToClassName(property_id);
} }
} }
@ -55,79 +88,143 @@ export class EquipSuit {
*/ */
export class Equip { export class Equip {
/** /**
* @param {number} id * @param {{
* @param {number} level * id: number;
* @param {string} name * level: number;
* @param {string} icon * name: string;
* @param {string} rarity * icon: string;
* @param {EquipProperty[]} properties * rarity: string;
* @param {EquipMainProperty[]} main_properties * properties: EquipProperty[];
* @param {EquipSuit} equip_suit * main_properties: EquipMainProperty[];
* @param {number} equipment_type * equip_suit: EquipSuit;
* equipment_type: number;
* }} data
*/ */
constructor( constructor(data) {
id, // 类型标注
level, /** @type {number} */
name, this.id;
icon, /** @type {number} */
rarity, this.level;
properties, /** @type {string} */
main_properties, this.name;
equip_suit, /** @type {string} */
equipment_type this.icon;
) { /** @type {string} */
this.rarity;
/** @type {EquipProperty[]} */
this.properties;
/** @type {EquipMainProperty[]} */
this.main_properties;
/** @type {EquipSuit} */
this.equip_suit;
/** @type {number} */
this.equipment_type;
const {
id,
level,
name,
icon,
rarity,
properties,
main_properties,
equip_suit,
equipment_type,
} = data;
this.id = id; this.id = id;
this.level = level; this.level = level;
this.name = name; this.name = name;
this.icon = icon; this.icon = icon;
this.rarity = rarity; this.rarity = rarity;
this.properties = properties; this.properties = properties.map(item => new EquipProperty(item));
this.main_properties = main_properties; this.main_properties = main_properties.map(
item => new EquipMainProperty(item)
);
this.equip_suit = equip_suit; this.equip_suit = equip_suit;
this.equipment_type = equipment_type; this.equipment_type = equipment_type;
} }
async get_assets() {
const result = await getSuitImage(this.id);
this.suit_icon = result;
}
} }
/** /**
* @class * @class
*/ */
export class Weapon { export class Weapon {
// 类型标注
/** @type {number} */
id;
/** @type {number} */
level;
/** @type {string} */
name;
/** @type {number} */
star;
/** @type {string} */
icon;
/** @type {string} */
rarity;
/** @type {EquipProperty[]} */
properties;
/** @type {EquipMainProperty[]} */
main_properties;
/** @type {string} */
talent_title;
/** @type {string} */
talent_content;
/** @type {number} */
profession;
/** /**
* @param {number} id * @param {{
* @param {number} level * id: number;
* @param {string} name * level: number;
* @param {number} star * name: string;
* @param {string} icon * star: number;
* @param {string} rarity * icon: string;
* @param {EquipProperty[]} properties * rarity: string;
* @param {EquipMainProperty[]} main_properties * properties: EquipProperty[];
* @param {string} talent_title * main_properties: EquipMainProperty[];
* @param {string} talent_content * talent_title: string;
* @param {number} profession * talent_content: string;
* profession: number;
* }} data
*/ */
constructor( constructor(data) {
id, const {
level, id,
name, level,
star, name,
icon, star,
rarity, icon,
properties, rarity,
main_properties, properties,
talent_title, main_properties,
talent_content, talent_title,
profession talent_content,
) { profession,
} = data;
this.id = id; this.id = id;
this.level = level; this.level = level;
this.name = name; this.name = name;
this.star = star; this.star = star;
this.icon = icon; this.icon = icon;
this.rarity = rarity; this.rarity = rarity;
this.properties = properties; this.properties = properties.map(item => new EquipProperty(item));
this.main_properties = main_properties; this.main_properties = main_properties.map(
item => new EquipMainProperty(item)
);
this.talent_title = talent_title; this.talent_title = talent_title;
this.talent_content = talent_content; this.talent_content = talent_content;
this.profession = profession; this.profession = profession;
} }
async get_assets() {
const result = await getWeaponImage(this.id);
this.square_icon = result;
}
} }

View file

@ -8,32 +8,58 @@ import {
* @class * @class
*/ */
export class SingleGachaLog { export class SingleGachaLog {
// 类型标注
/** @type {string} */
uid;
/** @type {string} */
gacha_id;
/** @type {string} */
gacha_type;
/** @type {string} */
item_id;
/** @type {string} */
count;
/** @type {string} */
time;
/** @type {string} */
name;
/** @type {string} */
lang;
/** @type {string} */
item_type;
/** @type {string} */
rank_type;
/** @type {string} */
id;
/** /**
* @param {string} uid * @param {{
* @param {string} gacha_id * uid: string;
* @param {string} gacha_type * gacha_id: string;
* @param {string} item_id * gacha_type: string;
* @param {string} count * item_id: string;
* @param {string} time * count: string;
* @param {string} name * time: string;
* @param {string} lang * name: string;
* @param {string} item_type * lang: string;
* @param {string} rank_type * item_type: string;
* @param {string} id * rank_type: string;
* id: string;
* }} data
*/ */
constructor( constructor(data) {
uid, const {
gacha_id, uid,
gacha_type, gacha_id,
item_id, gacha_type,
count, item_id,
time, count,
name, time,
lang, name,
item_type, lang,
rank_type, item_type,
id rank_type,
) { id,
} = data;
this.uid = uid; this.uid = uid;
this.gacha_id = gacha_id; this.gacha_id = gacha_id;
this.gacha_type = gacha_type; this.gacha_type = gacha_type;
@ -93,22 +119,7 @@ export class ZZZGachaLogResp {
const { page, size, list, region, region_time_zone } = data; const { page, size, list, region, region_time_zone } = data;
this.page = page; this.page = page;
this.size = size; this.size = size;
this.list = list.map( this.list = list.map(item => new SingleGachaLog(item));
item =>
new SingleGachaLog(
item.uid,
item.gacha_id,
item.gacha_type,
item.item_id,
item.count,
item.time,
item.name,
item.lang,
item.item_type,
item.rank_type,
item.id
)
);
this.region = region; this.region = region;
this.region_time_zone = region_time_zone; this.region_time_zone = region_time_zone;
} }

View file

@ -1,4 +1,4 @@
import { ZZZAvatarInfo } from './avatar.js'; import { ZZZAvatarBasic } from './avatar.js';
import { Buddy } from './bangboo.js'; import { Buddy } from './bangboo.js';
/** /**
@ -34,7 +34,7 @@ export class ZZZIndexResp {
/** /**
* @param {{ * @param {{
* stats: Stats; * stats: Stats;
* avatar_list: ZZZAvatarInfo[]; * avatar_list: ZZZAvatarBasic[];
* cur_head_icon_url: string; * cur_head_icon_url: string;
* buddy_list: Buddy[]; * buddy_list: Buddy[];
* }} data * }} data
@ -42,7 +42,7 @@ export class ZZZIndexResp {
constructor(data) { constructor(data) {
const { stats, avatar_list, cur_head_icon_url, buddy_list } = data; const { stats, avatar_list, cur_head_icon_url, buddy_list } = data;
this.stats = stats; this.stats = stats;
this.avatar_list = avatar_list.map(item => new ZZZAvatarInfo(item)); this.avatar_list = avatar_list.map(item => new ZZZAvatarBasic(item));
this.cur_head_icon_url = cur_head_icon_url; this.cur_head_icon_url = cur_head_icon_url;
this.buddy_list = buddy_list.map(item => new Buddy(item)); this.buddy_list = buddy_list.map(item => new Buddy(item));
} }

View file

@ -3,13 +3,21 @@
*/ */
export class Property { export class Property {
/** /**
* @param {{
* property_name: string,
* property_id: number,
* base: string,
* add: string,
* final: string
* }} data
* @param {string} property_name * @param {string} property_name
* @param {number} property_id * @param {number} property_id
* @param {string} base * @param {string} base
* @param {string} add * @param {string} add
* @param {string} final * @param {string} final
*/ */
constructor(property_name, property_id, base, add, final) { constructor(data) {
const { property_name, property_id, base, add, final } = data;
this.property_name = property_name; this.property_name = property_name;
this.property_id = property_id; this.property_id = property_id;
this.base = base; this.base = base;

View file

@ -17,11 +17,14 @@ export class SkillItem {
*/ */
export class Skill { export class Skill {
/** /**
* @param {number} level * @param {{
* @param {number} skill_type * level: number,
* @param {SkillItem[]} items * skill_type: number,
* items: SkillItem[]
* }} data
*/ */
constructor(level, skill_type, items) { constructor(data) {
const { level, skill_type, items } = data;
this.level = level; this.level = level;
this.skill_type = skill_type; this.skill_type = skill_type;
this.items = items; this.items = items;

View file

@ -42,7 +42,7 @@
width: 100%; width: 100%;
position: relative; position: relative;
} }
.card .list .item * { .card .list .item > * {
position: inherit; position: inherit;
z-index: 1; z-index: 1;
} }
@ -82,13 +82,44 @@
object-fit: contain; object-fit: contain;
display: block; display: block;
} }
.card .list .item .level { .card .list .item .c-info {
position: absolute; position: absolute;
bottom: 0; bottom: 0;
left: 0; left: 0;
width: 100%; width: 100%;
background: rgb(0, 0, 0); background: rgb(0, 0, 0);
color: white; color: white;
display: flex;
text-align: center;
}
.card .list .item .c-info .ranks {
background: rgb(117, 117, 117);
padding: 0 0.4em;
}
.card .list .item .c-info .ranks.r1 {
background: rgb(58, 116, 43);
}
.card .list .item .c-info .ranks.r2 {
background: rgb(61, 132, 214);
}
.card .list .item .c-info .ranks.r3 {
background: rgb(223, 118, 118);
}
.card .list .item .c-info .ranks.r4 {
background: rgb(51, 54, 161);
}
.card .list .item .c-info .ranks.r5 {
background: rgb(102, 127, 19);
}
.card .list .item .c-info .ranks.r6 {
background: rgb(218, 48, 32);
}
.card .list .item .c-info .level {
flex-grow: 1;
flex-shrink: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
text-align: center; text-align: center;
} }
.card .list .item .property { .card .list .item .property {

View file

@ -25,62 +25,37 @@
<div class="label">式舆防卫战</div> <div class="label">式舆防卫战</div>
</div> </div>
</div> </div>
<div class="special-title"> <% include(sys.specialTitle, {en: 'AGENT' , cn: '代理人信息' }) %>
<div class="bg-content"> <div class="list">
AGENT {{each card.avatar_list char i}}
</div> <div class="item rank{{char.rarity}}">
<div class="parallelograms"> <div class="rank rank-icon {{char.rarity}}"></div>
<span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span> <div class="property {{char.element_str}}"></div>
</div> <div class="image">
<div class="content"> <img src="{{char.square_icon}}" alt="">
代理人信息 </div>
</div> <div class="c-info">
<div class="parallelograms"> <div class="ranks r{{char.rank}}">{{char.rank}}命</div>
<span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span> <div class="level">等级{{char.level}}</div>
</div> </div>
</div>
<div class="list">
{{each card.avatar_list char i}}
<div class="item rank{{char.rarity}}">
<div class="rank rank-icon {{char.rarity}}"></div>
<div class="property {{char.element_str}}"></div>
<div class="image">
<img src="{{char.square_icon}}" alt="">
</div> </div>
<div class="level">等级{{char.level}}</div> {{/each}}
</div> </div>
{{/each}}
</div>
<% include(sys.specialTitle, {en: 'BANGBOO' , cn: '邦布信息' }) %>
<div class="special-title"> <div class="list">
<div class="bg-content"> {{each card.buddy_list bangboo i}}
BANGBOO <div class="item rank{{bangboo.rarity}}">
</div> <div class="rank rank-icon {{bangboo.rarity}}"></div>
<div class="parallelograms"> <div class="image">
<span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span> <img src="{{bangboo.square_icon}}" alt="">
</div> </div>
<div class="content"> <div class="c-info">
邦布信息 <div class="ranks r{{bangboo.star}}">{{bangboo.star}}命</div>
</div> <div class="level">等级{{bangboo.level}}</div>
<div class="parallelograms"> </div>
<span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span> </div>
</div> {{/each}}
</div>
<div class="list">
{{each card.buddy_list bangboo i}}
<div class="item rank{{bangboo.rarity}}">
<div class="rank rank-icon {{bangboo.rarity}}"></div>
<div class="image">
<img src="{{bangboo.square_icon}}" alt="">
</div> </div>
<div class="level">等级{{bangboo.level}}</div>
</div>
{{/each}}
</div>
</div> </div>
{{/block}} {{/block}}

View file

@ -39,7 +39,7 @@
.item { .item {
width: 100%; width: 100%;
position: relative; position: relative;
* { > * {
position: inherit; position: inherit;
z-index: 1; z-index: 1;
} }
@ -83,14 +83,45 @@
display: block; display: block;
} }
} }
.level { .c-info {
position: absolute; position: absolute;
bottom: 0; bottom: 0;
left: 0; left: 0;
width: 100%; width: 100%;
background: rgb(0, 0, 0); background: rgb(0, 0, 0);
color: white; color: white;
display: flex;
text-align: center; text-align: center;
.ranks {
background: rgb(117, 117, 117);
padding: 0 0.4em;
&.r1 {
background: rgb(58, 116, 43);
}
&.r2 {
background: rgb(61, 132, 214);
}
&.r3 {
background: rgb(223, 118, 118);
}
&.r4 {
background: rgb(51, 54, 161);
}
&.r5 {
background: rgb(102, 127, 19);
}
&.r6 {
background: rgb(218, 48, 32);
}
}
.level {
flex-grow: 1;
flex-shrink: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
text-align: center;
}
} }
.property { .property {
position: absolute; position: absolute;

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 821 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View file

@ -0,0 +1,18 @@
<div class="special-title">
<div class="bg-content">
{{en}}
</div>
<div class="parallelograms">
<% for(let i=1 ; i < (count || 9) ; i++) { %>
<span></span>
<% } %>
</div>
<div class="content">
{{cn}}
</div>
<div class="parallelograms">
<% for(let i=1 ; i < (count || 9) ; i++) { %>
<span></span>
<% } %>
</div>
</div>

View file

@ -34,6 +34,28 @@
background-image: url("../images/RANK_B.png"); background-image: url("../images/RANK_B.png");
} }
.rarity-icon {
aspect-ratio: 1;
background-repeat: no-repeat;
background-position: center;
background-size: contain;
}
.rarity-icon.a, .rarity-icon.A {
background-image: url("../images/Rarity_A.png");
}
.rarity-icon.b, .rarity-icon.B {
background-image: url("../images/Rarity_B.png");
}
.rarity-icon.c, .rarity-icon.C {
background-image: url("../images/Rarity_C.png");
}
.rarity-icon.s, .rarity-icon.S {
background-image: url("../images/Rarity_S.png");
}
.rarity-icon.x, .rarity-icon.X {
background-image: url("../images/Rarity_X.png");
}
.property { .property {
aspect-ratio: 1; aspect-ratio: 1;
background-size: contain; background-size: contain;
@ -56,6 +78,89 @@
background-image: url("../images/IconDungeonBuffEther.png"); background-image: url("../images/IconDungeonBuffEther.png");
} }
.prop-icon {
aspect-ratio: 1;
background-size: contain;
background-position: center;
background-repeat: no-repeat;
}
.prop-icon.attack {
background-image: url("../images/property/IconAttack.png");
}
.prop-icon.breakstun {
background-image: url("../images/property/IconBreakStun.png");
}
.prop-icon.crit {
background-image: url("../images/property/IconCrit.png");
}
.prop-icon.critdam {
background-image: url("../images/property/IconCritDam.png");
}
.prop-icon.def {
background-image: url("../images/property/IconDef.png");
}
.prop-icon.dungeonbuffether {
background-image: url("../images/property/IconDungeonBuffEther.png");
}
.prop-icon.elementabnormalpower {
background-image: url("../images/property/IconElementAbnormalPower.png");
}
.prop-icon.elementmystery {
background-image: url("../images/property/IconElementMystery.png");
}
.prop-icon.fire {
background-image: url("../images/property/IconFire.png");
}
.prop-icon.hpmax {
background-image: url("../images/property/IconHpMax.png");
}
.prop-icon.ice {
background-image: url("../images/property/IconIce.png");
}
.prop-icon.penratio {
background-image: url("../images/property/IconPenRatio.png");
}
.prop-icon.penvalue {
background-image: url("../images/property/IconPenValue.png");
}
.prop-icon.physdmg {
background-image: url("../images/property/IconPhysDmg.png");
}
.prop-icon.spgetratio {
background-image: url("../images/property/IconSpGetRatio.png");
}
.prop-icon.spmax {
background-image: url("../images/property/IconSpMax.png");
}
.prop-icon.sprecover {
background-image: url("../images/property/IconSpRecover.png");
}
.prop-icon.thunder {
background-image: url("../images/property/IconThunder.png");
}
.pro-icon {
aspect-ratio: 1;
background-size: contain;
background-position: center;
background-repeat: no-repeat;
}
.pro-icon.anomaly {
background-image: url("../images/prop/IconAnomaly.png");
}
.pro-icon.attack {
background-image: url("../images/prop/IconAttack.png");
}
.pro-icon.defense {
background-image: url("../images/prop/IconDefence.png");
}
.pro-icon.stun {
background-image: url("../images/prop/IconStun.png");
}
.pro-icon.support {
background-image: url("../images/prop/IconSupport.png");
}
.special-title { .special-title {
width: 100%; width: 100%;
background-size: contain; background-size: contain;

View file

@ -59,6 +59,33 @@
} }
} }
.rarity-icon {
aspect-ratio: 1;
background-repeat: no-repeat;
background-position: center;
background-size: contain;
&.a,
&.A {
background-image: url('../images/Rarity_A.png');
}
&.b,
&.B {
background-image: url('../images/Rarity_B.png');
}
&.c,
&.C {
background-image: url('../images/Rarity_C.png');
}
&.s,
&.S {
background-image: url('../images/Rarity_S.png');
}
&.x,
&.X {
background-image: url('../images/Rarity_X.png');
}
}
.property { .property {
aspect-ratio: 1; aspect-ratio: 1;
background-size: contain; background-size: contain;
@ -81,6 +108,89 @@
} }
} }
.prop-icon {
aspect-ratio: 1;
background-size: contain;
background-position: center;
background-repeat: no-repeat;
&.attack {
background-image: url('../images/property/IconAttack.png');
}
&.breakstun {
background-image: url('../images/property/IconBreakStun.png');
}
&.crit {
background-image: url('../images/property/IconCrit.png');
}
&.critdam {
background-image: url('../images/property/IconCritDam.png');
}
&.def {
background-image: url('../images/property/IconDef.png');
}
&.dungeonbuffether {
background-image: url('../images/property/IconDungeonBuffEther.png');
}
&.elementabnormalpower {
background-image: url('../images/property/IconElementAbnormalPower.png');
}
&.elementmystery {
background-image: url('../images/property/IconElementMystery.png');
}
&.fire {
background-image: url('../images/property/IconFire.png');
}
&.hpmax {
background-image: url('../images/property/IconHpMax.png');
}
&.ice {
background-image: url('../images/property/IconIce.png');
}
&.penratio {
background-image: url('../images/property/IconPenRatio.png');
}
&.penvalue {
background-image: url('../images/property/IconPenValue.png');
}
&.physdmg {
background-image: url('../images/property/IconPhysDmg.png');
}
&.spgetratio {
background-image: url('../images/property/IconSpGetRatio.png');
}
&.spmax {
background-image: url('../images/property/IconSpMax.png');
}
&.sprecover {
background-image: url('../images/property/IconSpRecover.png');
}
&.thunder {
background-image: url('../images/property/IconThunder.png');
}
}
.pro-icon {
aspect-ratio: 1;
background-size: contain;
background-position: center;
background-repeat: no-repeat;
&.anomaly {
background-image: url('../images/prop/IconAnomaly.png');
}
&.attack {
background-image: url('../images/prop/IconAttack.png');
}
&.defense {
background-image: url('../images/prop/IconDefence.png');
}
&.stun {
background-image: url('../images/prop/IconStun.png');
}
&.support {
background-image: url('../images/prop/IconSupport.png');
}
}
.special-title { .special-title {
width: 100%; width: 100%;
background-size: contain; background-size: contain;

View file

@ -7,80 +7,54 @@
{{block 'main'}} {{block 'main'}}
<div class="card"> <div class="card">
{{include sys.playerInfo}} {{include sys.playerInfo}}
<div class="special-title"> <% include(sys.specialTitle, {en: 'STAMINA' , cn: '电池电量' }) %>
<div class="bg-content"> <div class="battery">
STAMINA <div class="icon">
</div> <img src="{{@sys.currentPath}}/images/IconStamina.png" alt="">
<div class="parallelograms">
<span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span>
</div>
<div class="content">
电池电量
</div>
<div class="parallelograms">
<span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span>
</div>
</div>
<div class="battery">
<div class="icon">
<img src="{{@sys.currentPath}}/images/IconStamina.png" alt="">
</div>
<div class="info">
<div class="bvalue">
<div class="title">电量</div>
<div class="value"><span class="big">{{note.energy.progress.current}}</span>/{{note.energy.progress.max}}</div>
</div> </div>
<div class="bleft"> <div class="info">
<div class="title">剩余</div> <div class="bvalue">
<div class="value">{{note.energy.progress.rest}}</div> <div class="title">电量</div>
</div> <div class="value"><span class="big">{{note.energy.progress.current}}</span>/{{note.energy.progress.max}}
<div class="texture"> </div>
<div class="bar"> </div>
<div class="progress" style="width: {{note.energy.progress.percent}}%;"></div> <div class="bleft">
<div class="title">剩余</div>
<div class="value">{{note.energy.progress.rest}}</div>
</div>
<div class="texture">
<div class="bar">
<div class="progress" style="width: {{note.energy.progress.percent}}%;"></div>
</div>
</div> </div>
</div> </div>
</div> </div>
</div>
<div class="special-title">
<div class="bg-content">
ACTIVE
</div>
<div class="parallelograms">
<span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span>
</div>
<div class="content">
每日情况
</div>
<div class="parallelograms">
<span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span>
</div>
</div>
<div class="active-list"> <% include(sys.specialTitle, {en: 'ACTIVE' , cn: '每日情况' }) %>
<div class="active"> <div class="active-list">
<div class="status {{note.vitality.finish && 'finish'}}"></div> <div class="active">
<div class="title">今日活跃度</div> <div class="status {{note.vitality.finish && 'finish'}}"></div>
<div class="value"> <div class="title">今日活跃度</div>
{{note.vitality.current}} <div class="value">
<span class="sub">/{{note.vitality.max}}</span> {{note.vitality.current}}
<span class="sub">/{{note.vitality.max}}</span>
</div>
</div>
<div class="active">
<div class="status {{note.vhs_sale.state && 'finish'}}"></div>
<div class="title">录像店经营</div>
<div class="value">
{{note.vhs_sale.state_label}}
</div>
</div>
<div class="active">
<div class="status {{note.sign && 'finish'}}"></div>
<div class="title">刮刮卡</div>
<div class="value">
{{note.sign_label}}
</div>
</div>
</div> </div>
</div>
<div class="active">
<div class="status {{note.vhs_sale.state && 'finish'}}"></div>
<div class="title">录像店经营</div>
<div class="value">
{{note.vhs_sale.state_label}}
</div>
</div>
<div class="active">
<div class="status {{note.sign && 'finish'}}"></div>
<div class="title">刮刮卡</div>
<div class="value">
{{note.sign_label}}
</div>
</div>
</div>
</div> </div>
{{/block}} {{/block}}

592
resources/panel/card.css Normal file
View file

@ -0,0 +1,592 @@
.card {
padding-top: 0.8em;
overflow: hidden;
}
.card .star {
width: 5.5em;
height: 1.5em;
}
.card .star.star0 {
background-size: cover;
background-repeat: no-repeat;
background-position: center;
background-image: url("./images/star/0.png");
}
.card .star.star1 {
background-size: cover;
background-repeat: no-repeat;
background-position: center;
background-image: url("./images/star/1.png");
}
.card .star.star2 {
background-size: cover;
background-repeat: no-repeat;
background-position: center;
background-image: url("./images/star/2.png");
}
.card .star.star3 {
background-size: cover;
background-repeat: no-repeat;
background-position: center;
background-image: url("./images/star/3.png");
}
.card .star.star4 {
background-size: cover;
background-repeat: no-repeat;
background-position: center;
background-image: url("./images/star/4.png");
}
.card .star.star5 {
background-size: cover;
background-repeat: no-repeat;
background-position: center;
background-image: url("./images/star/5.png");
}
.card .basic {
display: flex;
align-items: stretch;
overflow: hidden;
}
.card .basic .char {
width: 55%;
position: relative;
flex-grow: 1;
}
.card .basic .char .avatar {
height: 100%;
padding-top: 1em;
overflow: hidden;
}
.card .basic .char .avatar img {
width: 100%;
height: 100%;
object-fit: cover;
object-position: top center;
position: absolute;
}
.card .basic .char .skills {
position: absolute;
width: 120%;
height: 3.5em;
background: url("./images/skill_bg.png");
background-size: contain;
background-repeat: no-repeat;
background-position: center;
bottom: 3em;
right: -1.6em;
display: flex;
align-items: flex-end;
padding-left: 2.5em;
padding-bottom: 0.2em;
}
.card .basic .char .skills .skill {
width: 1.4em;
aspect-ratio: 1;
margin-right: 1.38em;
display: flex;
justify-content: center;
align-items: center;
}
.card .basic .info {
width: 45%;
flex-grow: 0;
flex-shrink: 0;
font-size: 1.2em;
position: relative;
z-index: 2;
}
.card .basic .info .char_info {
width: 140%;
position: relative;
right: 20%;
padding: 0.3em 0.2em;
padding-right: 20%;
padding-left: 8%;
border-image-slice: 0 30 0 40 fill;
border-image-width: 0em 1.5em 0em 2em;
border-image-outset: 0 0 0 0;
border-image-repeat: stretch stretch;
border-image-source: url("./images/CurseBG04.png");
filter: drop-shadow(0 0 0.1em rgb(0, 0, 0));
}
.card .basic .info .char_info .base {
display: flex;
align-items: center;
gap: 0.2em;
overflow: hidden;
}
.card .basic .info .char_info .base .rank {
width: 1.2em;
flex-grow: 0;
flex-shrink: 0;
}
.card .basic .info .char_info .base .property {
width: 1em;
flex-grow: 0;
flex-shrink: 0;
}
.card .basic .info .char_info .base .name {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.card .basic .info .char_info .addition {
display: flex;
align-items: center;
font-size: 0.7em;
gap: 0.2em;
padding-left: 1em;
}
.card .basic .info .char_info .addition .level {
background-color: #000;
padding: 0em 0.7em;
border-radius: 1em;
}
.card .basic .info .char_info .addition .role_ranks {
display: flex;
gap: 0.1em;
}
.card .basic .info .char_info .addition .role_ranks span {
width: 1.2em;
aspect-ratio: 1;
border-radius: 1em;
background-size: contain;
background-repeat: no-repeat;
background-position: center;
opacity: 0.4;
}
.card .basic .info .char_info .addition .role_ranks span:nth-child(1) {
background-image: url("./images/ranks/1.png");
}
.card .basic .info .char_info .addition .role_ranks.r1 span:nth-child(1) {
opacity: 1 !important;
}
.card .basic .info .char_info .addition .role_ranks span:nth-child(2) {
background-image: url("./images/ranks/2.png");
}
.card .basic .info .char_info .addition .role_ranks.r2 span:nth-child(1) {
opacity: 1 !important;
}
.card .basic .info .char_info .addition .role_ranks.r2 span:nth-child(2) {
opacity: 1 !important;
}
.card .basic .info .char_info .addition .role_ranks span:nth-child(3) {
background-image: url("./images/ranks/3.png");
}
.card .basic .info .char_info .addition .role_ranks.r3 span:nth-child(1) {
opacity: 1 !important;
}
.card .basic .info .char_info .addition .role_ranks.r3 span:nth-child(2) {
opacity: 1 !important;
}
.card .basic .info .char_info .addition .role_ranks.r3 span:nth-child(3) {
opacity: 1 !important;
}
.card .basic .info .char_info .addition .role_ranks span:nth-child(4) {
background-image: url("./images/ranks/4.png");
}
.card .basic .info .char_info .addition .role_ranks.r4 span:nth-child(1) {
opacity: 1 !important;
}
.card .basic .info .char_info .addition .role_ranks.r4 span:nth-child(2) {
opacity: 1 !important;
}
.card .basic .info .char_info .addition .role_ranks.r4 span:nth-child(3) {
opacity: 1 !important;
}
.card .basic .info .char_info .addition .role_ranks.r4 span:nth-child(4) {
opacity: 1 !important;
}
.card .basic .info .char_info .addition .role_ranks span:nth-child(5) {
background-image: url("./images/ranks/5.png");
}
.card .basic .info .char_info .addition .role_ranks.r5 span:nth-child(1) {
opacity: 1 !important;
}
.card .basic .info .char_info .addition .role_ranks.r5 span:nth-child(2) {
opacity: 1 !important;
}
.card .basic .info .char_info .addition .role_ranks.r5 span:nth-child(3) {
opacity: 1 !important;
}
.card .basic .info .char_info .addition .role_ranks.r5 span:nth-child(4) {
opacity: 1 !important;
}
.card .basic .info .char_info .addition .role_ranks.r5 span:nth-child(5) {
opacity: 1 !important;
}
.card .basic .info .char_info .addition .role_ranks span:nth-child(6) {
background-image: url("./images/ranks/6.png");
}
.card .basic .info .char_info .addition .role_ranks.r6 span:nth-child(1) {
opacity: 1 !important;
}
.card .basic .info .char_info .addition .role_ranks.r6 span:nth-child(2) {
opacity: 1 !important;
}
.card .basic .info .char_info .addition .role_ranks.r6 span:nth-child(3) {
opacity: 1 !important;
}
.card .basic .info .char_info .addition .role_ranks.r6 span:nth-child(4) {
opacity: 1 !important;
}
.card .basic .info .char_info .addition .role_ranks.r6 span:nth-child(5) {
opacity: 1 !important;
}
.card .basic .info .char_info .addition .role_ranks.r6 span:nth-child(6) {
opacity: 1 !important;
}
.card .basic .info .char_info .addition .role_ranks span:nth-child(7) {
background-image: url("./images/ranks/7.png");
}
.card .basic .info .char_info .addition .role_ranks.r7 span:nth-child(1) {
opacity: 1 !important;
}
.card .basic .info .char_info .addition .role_ranks.r7 span:nth-child(2) {
opacity: 1 !important;
}
.card .basic .info .char_info .addition .role_ranks.r7 span:nth-child(3) {
opacity: 1 !important;
}
.card .basic .info .char_info .addition .role_ranks.r7 span:nth-child(4) {
opacity: 1 !important;
}
.card .basic .info .char_info .addition .role_ranks.r7 span:nth-child(5) {
opacity: 1 !important;
}
.card .basic .info .char_info .addition .role_ranks.r7 span:nth-child(6) {
opacity: 1 !important;
}
.card .basic .info .char_info .addition .role_ranks.r7 span:nth-child(7) {
opacity: 1 !important;
}
.card .basic .info .property_info {
background-color: rgb(47, 47, 47);
border-left: 0.1em solid rgb(0, 0, 0);
padding-top: 0.2em;
background: url("./images/BgFrame01.png") center/150% no-repeat;
}
.card .basic .info .property_info .title {
font-size: 0.6em;
}
.card .basic .info .property_info .title .special-title {
margin-bottom: 0;
padding-bottom: 0;
}
.card .basic .info .property_info .list {
display: flex;
flex-direction: column;
gap: 0.2em;
padding-bottom: 0.2em;
}
.card .basic .info .property_info .list .properties {
display: flex;
align-items: center;
gap: 0.4em;
padding: 0.05em 1em 0em 0.05em;
}
.card .basic .info .property_info .list .properties:nth-child(odd) {
background-color: rgba(221, 224, 221, 0.25);
}
.card .basic .info .property_info .list .properties .prop-icon {
width: 1em;
flex-grow: 0;
flex-shrink: 0;
margin: 0 0.4em;
}
.card .basic .info .property_info .list .properties .label {
flex-grow: 1;
flex-shrink: 1;
font-size: 0.65em;
color: rgb(166, 166, 166);
}
.card .basic .info .property_info .list .properties .label.yellow {
color: rgb(247, 199, 54);
}
.card .basic .info .property_info .list .properties .label.blue {
color: rgb(65, 147, 237);
}
.card .basic .info .property_info .list .properties .value {
flex-grow: 0;
flex-shrink: 0;
font-size: 0.8em;
}
.card .basic .info .weapon_info {
border-image-slice: 118 0 68 43 fill;
border-image-width: 4.5em 0em 2.7em 1.7em;
border-image-outset: 0em 0em 0em 0em;
border-image-repeat: stretch stretch;
border-image-source: url("./images/weapon_bg.png");
margin-top: -0.8em;
width: 115%;
margin-left: -15%;
font-size: 0.8em;
padding: 2em 1.2em;
padding-bottom: 3em;
position: relative;
overflow: hidden;
}
.card .basic .info .weapon_info .info {
width: 100%;
position: relative;
z-index: 2;
}
.card .basic .info .weapon_info .info .base {
width: 100%;
display: flex;
align-items: center;
gap: 0.5em;
}
.card .basic .info .weapon_info .info .base .rarity-icon {
width: 2em;
}
.card .basic .info .weapon_info .info .base .name {
text-shadow: 0 0 0.2em rgb(0, 0, 0);
}
.card .basic .info .weapon_info .info .main {
display: flex;
flex-direction: column;
justify-content: stretch;
width: max-content;
}
.card .basic .info .weapon_info .info .main .addition {
font-size: 0.8em;
display: flex;
align-items: center;
gap: 0.4em;
}
.card .basic .info .weapon_info .info .main .addition .level {
-webkit-clip-path: polygon(0.2em 0%, calc(100% - 0.2em) 0%, 100% 0.2em, 100% calc(100% - 0.2em), calc(100% - 0.2em) 100%, 0.2em 100%, 0% calc(100% - 0.2em), 0% 0.2em);
clip-path: polygon(0.2em 0%, calc(100% - 0.2em) 0%, 100% 0.2em, 100% calc(100% - 0.2em), calc(100% - 0.2em) 100%, 0.2em 100%, 0% calc(100% - 0.2em), 0% 0.2em);
padding: 0 0.4em;
font-size: 0.9em;
display: flex;
justify-content: center;
align-items: center;
color: rgb(43, 38, 40);
margin: 0.1em 0;
background-color: rgb(243, 203, 69);
}
.card .basic .info .weapon_info .info .main .properties {
display: flex;
align-items: center;
background-color: rgb(65, 147, 237);
gap: 0.2em;
padding: 0 0.5em;
border-radius: 1em;
margin: 0.2em 0;
}
.card .basic .info .weapon_info .info .main .properties.sub {
background-color: rgb(0, 0, 0);
}
.card .basic .info .weapon_info .info .main .properties .prop-icon {
width: 1em;
flex-grow: 0;
flex-shrink: 0;
}
.card .basic .info .weapon_info .info .main .properties .label {
flex-grow: 1;
flex-shrink: 1;
font-size: 0.7em;
color: rgb(222, 222, 222);
}
.card .basic .info .weapon_info .icon {
position: absolute;
top: 1.2em;
right: 0;
z-index: 1;
height: 70%;
margin-right: -1em;
}
.card .basic .info .weapon_info .icon img {
height: 100%;
}
.card .other {
border-image-source: url("./images/BgFrame01.png");
border-image-slice: 200 100 70 280 fill;
border-image-width: 2em 1em 0.7em 2.8em;
border-image-outset: 2em 1em 0.7em 2.8em;
border-image-repeat: stretch stretch;
padding-bottom: 3.3em;
margin-top: -1.4em;
position: relative;
z-index: 5;
}
.card .equip-list {
display: grid;
gap: 1em;
grid-template-columns: repeat(3, 1fr);
padding: 0 1.8em;
align-items: stretch;
margin-top: 1em;
}
.card .equip-list .box {
border-image-source: url("./images/equip_bg.png");
border-image-slice: 190 90 110 170 fill;
border-image-width: 5.7em 2.7em 3.3em 5.1em;
border-image-outset: 1.8em 1.5em 1.8em 1.5em;
border-image-repeat: stretch stretch;
position: relative;
margin-top: 1.3em;
padding-bottom: 1em;
min-height: 10em;
}
.card .equip-list .box.empty::after {
content: "";
position: absolute;
width: 2.5em;
height: 2.5em;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-image: url("./images/empty_equip_07.png");
background-size: contain;
background-repeat: no-repeat;
background-position: center;
}
.card .equip-list .box:nth-child(1)::before {
content: "01";
position: absolute;
font-size: 2em;
left: 50%;
top: -0.85em;
transform: translate(-50%, 0);
z-index: -1;
}
.card .equip-list .box:nth-child(2)::before {
content: "02";
position: absolute;
font-size: 2em;
left: 50%;
top: -0.85em;
transform: translate(-50%, 0);
z-index: -1;
}
.card .equip-list .box:nth-child(3)::before {
content: "03";
position: absolute;
font-size: 2em;
left: 50%;
top: -0.85em;
transform: translate(-50%, 0);
z-index: -1;
}
.card .equip-list .box:nth-child(4)::before {
content: "04";
position: absolute;
font-size: 2em;
left: 50%;
top: -0.85em;
transform: translate(-50%, 0);
z-index: -1;
}
.card .equip-list .box:nth-child(5)::before {
content: "05";
position: absolute;
font-size: 2em;
left: 50%;
top: -0.85em;
transform: translate(-50%, 0);
z-index: -1;
}
.card .equip-list .box:nth-child(6)::before {
content: "06";
position: absolute;
font-size: 2em;
left: 50%;
top: -0.85em;
transform: translate(-50%, 0);
z-index: -1;
}
.card .equip-list .box .icon {
width: 2.7em;
aspect-ratio: 1;
position: relative;
left: -0.9em;
top: -1.2em;
background-color: rgb(0, 0, 0);
background-image: url("./images/empty_equip_03.png");
background-size: contain;
background-repeat: no-repeat;
background-position: center;
border-radius: 50%;
}
.card .equip-list .box .icon img {
width: 100%;
height: 100%;
object-fit: contain;
display: block;
}
.card .equip-list .box .info {
display: flex;
padding-left: 2.2em;
margin-top: -2.5em;
gap: 0.5em;
align-items: center;
}
.card .equip-list .box .info .level {
flex-grow: 1;
flex-shrink: 1;
text-align: center;
font-size: 0.76em;
background-color: #000;
padding: 0.1em 0.5em;
border-radius: 1em;
margin-bottom: 0.3em;
}
.card .equip-list .box .info .rarity-icon {
width: 2.2em;
}
.card .equip-list .box .name {
padding: 0 0.5em;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
text-align: center;
}
.card .equip-list .box .property-list {
padding: 0 0.5em;
}
.card .equip-list .box .property-list .properties {
display: flex;
align-items: center;
background-color: rgb(0, 0, 0);
gap: 0.2em;
padding: 0 0.5em;
border-radius: 1em;
margin: 0.2em 0;
}
.card .equip-list .box .property-list .properties .prop-icon {
width: 1em;
flex-grow: 0;
flex-shrink: 0;
}
.card .equip-list .box .property-list .properties .label {
flex-grow: 1;
flex-shrink: 1;
font-size: 0.7em;
color: rgb(222, 222, 222);
}
.card .equip-list .box .property-list .properties .value {
color: rgb(249, 189, 64);
font-size: 0.7em;
}
.card .equip-list .box .property-list .properties.main {
background-color: rgb(65, 147, 237);
}
.card .equip-list .box .property-list .properties.main .prop-icon {
width: 1.2em;
margin-bottom: 0.2em;
}
.card .equip-list .box .property-list .properties.main .value {
color: rgb(255, 255, 255);
font-size: 1em;
}
.copyright {
margin-top: -3.3em;
position: relative;
z-index: 5;
}
/*# sourceMappingURL=card.css.map */

178
resources/panel/card.html Normal file
View file

@ -0,0 +1,178 @@
{{extend defaultLayout}}
{{block 'css'}}
<link rel="stylesheet" href="{{@sys.currentPath}}/card.css">
{{/block}}
{{block 'main'}}
<div class="card">
<div class="basic">
<div class="char">
<div class="avatar">
<img src="{{charData.role_icon}}" alt="">
</div>
<div class="skills">
{{each charData.skills skill}}
<div class="skill">{{skill.level}}</div>
{{/each}}
</div>
</div>
<div class="info">
<div class="char_info">
<div class="base">
<div class="rank rank-icon {{charData.rarity}}"></div>
<div class="property {{charData.element_str}}"></div>
<div class="name">{{charData.full_name_mi18n}}</div>
</div>
<div class="addition">
<div class="level">Lv.{{charData.level}}</div>
<div class="role_ranks r{{charData.rank}}">
<span></span><span></span><span></span><span></span><span></span><span></span>
</div>
</div>
</div>
<div class="property_info">
<div class="title">
<% include(sys.specialTitle, {en: 'PROPERTY' , cn: '属性' , count: 6 }) %>
</div>
<div class="list">
<div class="properties">
<div class="prop-icon hpmax"></div>
<div class="label yellow">生命值</div>
<div class="value">{{charData.basic_properties.hpmax.final}}</div>
</div>
<div class="properties">
<div class="prop-icon attack"></div>
<div class="label yellow">攻击力</div>
<div class="value">{{charData.basic_properties.attack.final}}</div>
</div>
<div class="properties">
<div class="prop-icon def"></div>
<div class="label yellow">防御力</div>
<div class="value">{{charData.basic_properties.def.final}}</div>
</div>
<div class="properties">
<div class="prop-icon breakstun"></div>
<div class="label">冲击力</div>
<div class="value">{{charData.basic_properties.breakstun.final}}</div>
</div>
<div class="properties">
<div class="prop-icon crit"></div>
<div class="label blue">暴击率</div>
<div class="value">{{charData.basic_properties.crit.final}}</div>
</div>
<div class="properties">
<div class="prop-icon critdam"></div>
<div class="label blue">暴击伤害</div>
<div class="value">{{charData.basic_properties.critdam.final}}</div>
</div>
<div class="properties">
<div class="prop-icon elementabnormalpower"></div>
<div class="label">异常掌控</div>
<div class="value">{{charData.basic_properties.elementabnormalpower.final}}</div>
</div>
<div class="properties">
<div class="prop-icon elementmystery"></div>
<div class="label">异常精通</div>
<div class="value">{{charData.basic_properties.elementmystery.final}}</div>
</div>
<div class="properties">
<div class="prop-icon penratio"></div>
<div class="label">穿透率</div>
<div class="value">{{charData.basic_properties.penratio.final}}</div>
</div>
<div class="properties">
<div class="prop-icon sprecover"></div>
<div class="label">能量恢复</div>
<div class="value">{{charData.basic_properties.sprecover.final}}</div>
</div>
</div>
</div>
<div class="weapon_info">
{{if charData.weapon}}
<div class="info">
<div class="base">
<div class="rarity-icon {{charData.weapon.rarity}}"></div>
<div class="name">{{charData.weapon.name}}</div>
</div>
<div class="main">
<div class="addition">
<div class="star star{{charData.weapon.star}}"></div>
<div class="level">Lv.{{charData.weapon.level}}</div>
</div>
{{each charData.weapon.main_properties prop}}
<div class="properties">
<div class="prop-icon {{prop.classname}}"></div>
<div class="label">{{prop.property_name}}</div>
<div class="value">{{prop.base}}</div>
</div>
{{/each}}
{{each charData.weapon.properties prop}}
<div class="properties sub">
<div class="prop-icon {{prop.classname}}"></div>
<div class="label">{{prop.property_name}}</div>
<div class="value">{{prop.base}}</div>
</div>
{{/each}}
</div>
</div>
<div class="icon">
<img src="{{charData.weapon.square_icon}}" alt="">
</div>
{{else}}
<div class="info empty">
<div class="base">
<div class="rarity-icon"></div>
<div class="name"></div>
</div>
</div>
{{/if}}
</div>
</div>
</div>
<div class="other">
<div class="title">
<% include(sys.specialTitle, {en: 'METAL' , cn: '驱动盘信息' }) %>
</div>
<div class="equip-list">
{{each charData.equip equip}}
<div class="box">
<div class="icon">
<img src="{{equip.suit_icon}}" alt="">
</div>
<div class="info">
<div class="level">等级{{equip.level}}</div>
<div class="rarity-icon {{equip.rarity}}"></div>
</div>
<div class="name">{{equip.name}}</div>
<div class="property-list">
{{each equip.main_properties prop}}
<div class="properties main">
<div class="prop-icon {{prop.classname}}"></div>
<div class="label yellow">{{prop.property_name}}</div>
<div class="value">{{prop.base}}</div>
</div>
{{/each}}
{{each equip.properties prop}}
<div class="properties">
<div class="prop-icon {{prop.classname}}"></div>
<div class="label yellow">{{prop.property_name}}</div>
<div class="value">{{prop.base}}</div>
</div>
{{/each}}
</div>
</div>
{{/each}}
<% for(let i=charData.equip.length; i < 6 ; i++) { %>
<div class="box empty">
<div class="icon">
</div>
</div>
<% } %>
</div>
</div>
</div>
{{/block}}

449
resources/panel/card.scss Normal file
View file

@ -0,0 +1,449 @@
.card {
padding-top: 0.8em;
overflow: hidden;
.star {
width: 5.5em;
height: 1.5em;
@for $i from 0 through 5 {
&.star#{$i} {
background-size: cover;
background-repeat: no-repeat;
background-position: center;
background-image: url('./images/star/#{$i}.png');
}
}
}
.basic {
display: flex;
align-items: stretch;
overflow: hidden;
.char {
width: 55%;
position: relative;
flex-grow: 1;
.avatar {
height: 100%;
padding-top: 1em;
overflow: hidden;
img {
width: 100%;
height: 100%;
object-fit: cover;
object-position: top center;
position: absolute;
}
}
.skills {
position: absolute;
width: 120%;
height: 3.5em;
background: url('./images/skill_bg.png');
background-size: contain;
background-repeat: no-repeat;
background-position: center;
bottom: 3em;
right: -1.6em;
display: flex;
align-items: flex-end;
padding-left: 2.5em;
padding-bottom: 0.2em;
.skill {
width: 1.4em;
aspect-ratio: 1;
margin-right: 1.38em;
display: flex;
justify-content: center;
align-items: center;
}
}
}
.info {
width: 45%;
flex-grow: 0;
flex-shrink: 0;
font-size: 1.2em;
position: relative;
z-index: 2;
.char_info {
width: 140%;
position: relative;
right: 20%;
padding: 0.3em 0.2em;
padding-right: 20%;
padding-left: 8%;
border-image-slice: 0 30 0 40 fill;
border-image-width: 0em 1.5em 0em 2em;
border-image-outset: 0 0 0 0;
border-image-repeat: stretch stretch;
border-image-source: url('./images/CurseBG04.png');
filter: drop-shadow(0 0 0.1em rgb(0, 0, 0));
.base {
display: flex;
align-items: center;
gap: 0.2em;
overflow: hidden;
.rank {
width: 1.2em;
flex-grow: 0;
flex-shrink: 0;
}
.property {
width: 1em;
flex-grow: 0;
flex-shrink: 0;
}
.name {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
.addition {
display: flex;
align-items: center;
font-size: 0.7em;
gap: 0.2em;
padding-left: 1em;
.level {
background-color: #000;
padding: 0em 0.7em;
border-radius: 1em;
}
.role_ranks {
display: flex;
gap: 0.1em;
span {
width: 1.2em;
aspect-ratio: 1;
border-radius: 1em;
background-size: contain;
background-repeat: no-repeat;
background-position: center;
opacity: 0.4;
}
@for $i from 1 through 7 {
span:nth-child(#{$i}) {
background-image: url('./images/ranks/#{$i}.png');
}
&.r#{$i} {
@for $j from 1 through $i {
span:nth-child(#{$j}) {
opacity: 1 !important;
}
}
}
}
}
}
}
.property_info {
background-color: rgb(47, 47, 47);
border-left: 0.1em solid rgb(0, 0, 0);
padding-top: 0.2em;
background: url('./images/BgFrame01.png') center / 150% no-repeat;
.title {
font-size: 0.6em;
.special-title {
margin-bottom: 0;
padding-bottom: 0;
}
}
.list {
display: flex;
flex-direction: column;
gap: 0.2em;
padding-bottom: 0.2em;
.properties {
display: flex;
align-items: center;
gap: 0.4em;
padding: 0.05em 1em 0em 0.05em;
&:nth-child(odd) {
background-color: rgba(221, 224, 221, 0.25);
}
.prop-icon {
width: 1em;
flex-grow: 0;
flex-shrink: 0;
margin: 0 0.4em;
}
.label {
flex-grow: 1;
flex-shrink: 1;
font-size: 0.65em;
color: rgb(166, 166, 166);
&.yellow {
color: rgb(247, 199, 54);
}
&.blue {
color: rgb(65, 147, 237);
}
}
.value {
flex-grow: 0;
flex-shrink: 0;
font-size: 0.8em;
}
}
}
}
.weapon_info {
border-image-slice: 118 0 68 43 fill;
border-image-width: 4.5em 0em 2.7em 1.7em;
border-image-outset: 0em 0em 0em 0em;
border-image-repeat: stretch stretch;
border-image-source: url('./images/weapon_bg.png');
margin-top: -0.8em;
width: 115%;
margin-left: -15%;
font-size: 0.8em;
padding: 2em 1.2em;
padding-bottom: 3em;
position: relative;
overflow: hidden;
.info {
width: 100%;
position: relative;
z-index: 2;
.base {
width: 100%;
display: flex;
align-items: center;
gap: 0.5em;
.rarity-icon {
width: 2em;
}
.name {
text-shadow: 0 0 0.2em rgb(0, 0, 0);
}
}
.main {
display: flex;
flex-direction: column;
justify-content: stretch;
width: max-content;
.addition {
font-size: 0.8em;
display: flex;
align-items: center;
gap: 0.4em;
.level {
$label-width: 0.2em;
-webkit-clip-path: polygon(
$label-width 0%,
calc(100% - $label-width) 0%,
100% $label-width,
100% calc(100% - $label-width),
calc(100% - $label-width) 100%,
$label-width 100%,
0% calc(100% - $label-width),
0% $label-width
);
clip-path: polygon(
$label-width 0%,
calc(100% - $label-width) 0%,
100% $label-width,
100% calc(100% - $label-width),
calc(100% - $label-width) 100%,
$label-width 100%,
0% calc(100% - $label-width),
0% $label-width
);
padding: 0 0.4em;
font-size: 0.9em;
display: flex;
justify-content: center;
align-items: center;
color: rgb(43, 38, 40);
margin: 0.1em 0;
background-color: rgb(243, 203, 69);
}
}
.properties {
display: flex;
align-items: center;
background-color: rgb(65, 147, 237);
gap: 0.2em;
padding: 0 0.5em;
border-radius: 1em;
margin: 0.2em 0;
&.sub {
background-color: rgb(0, 0, 0);
}
.prop-icon {
width: 1em;
flex-grow: 0;
flex-shrink: 0;
}
.label {
flex-grow: 1;
flex-shrink: 1;
font-size: 0.7em;
color: rgb(222, 222, 222);
}
}
}
}
.icon {
position: absolute;
top: 1.2em;
right: 0;
z-index: 1;
height: 70%;
margin-right: -1em;
img {
height: 100%;
}
}
}
}
}
.other {
border-image-source: url('./images/BgFrame01.png');
border-image-slice: 200 100 70 280 fill;
border-image-width: 2em 1em 0.7em 2.8em;
border-image-outset: 2em 1em 0.7em 2.8em;
border-image-repeat: stretch stretch;
padding-bottom: 3.3em;
margin-top: -1.4em;
position: relative;
z-index: 5;
}
.equip-list {
display: grid;
gap: 1em;
grid-template-columns: repeat(3, 1fr);
padding: 0 1.8em;
align-items: stretch;
margin-top: 1em;
.box {
$size: 3em;
border-image-source: url('./images/equip_bg.png');
border-image-slice: 190 90 110 170 fill;
border-image-width: 1.9 * $size 0.9 * $size 1.1 * $size 1.7 * $size;
border-image-outset: 0.6 * $size 0.5 * $size 0.6 * $size 0.5 * $size;
border-image-repeat: stretch stretch;
position: relative;
margin-top: 1.3em;
padding-bottom: 1em;
min-height: 10em;
&.empty {
&::after {
content: '';
position: absolute;
width: 2.5em;
height: 2.5em;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-image: url('./images/empty_equip_07.png');
background-size: contain;
background-repeat: no-repeat;
background-position: center;
}
}
@for $i from 1 through 6 {
&:nth-child(#{$i}) {
&::before {
content: '0#{$i}';
position: absolute;
font-size: 2em;
left: 50%;
top: -0.85em;
transform: translate(-50%, 0);
z-index: -1;
}
}
}
.icon {
width: 0.9 * $size;
aspect-ratio: 1;
position: relative;
left: -0.3 * $size;
top: -0.4 * $size;
background-color: rgb(0, 0, 0);
background-image: url('./images/empty_equip_03.png');
background-size: contain;
background-repeat: no-repeat;
background-position: center;
border-radius: 50%;
img {
width: 100%;
height: 100%;
object-fit: contain;
display: block;
}
}
.info {
display: flex;
padding-left: 2.2em;
margin-top: -2.5em;
gap: 0.5em;
align-items: center;
.level {
flex-grow: 1;
flex-shrink: 1;
text-align: center;
font-size: 0.76em;
background-color: #000;
padding: 0.1em 0.5em;
border-radius: 1em;
margin-bottom: 0.3em;
}
.rarity-icon {
width: 2.2em;
}
}
.name {
padding: 0 0.5em;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
text-align: center;
}
.property-list {
padding: 0 0.5em;
.properties {
display: flex;
align-items: center;
background-color: rgb(0, 0, 0);
gap: 0.2em;
padding: 0 0.5em;
border-radius: 1em;
margin: 0.2em 0;
.prop-icon {
width: 1em;
flex-grow: 0;
flex-shrink: 0;
}
.label {
flex-grow: 1;
flex-shrink: 1;
font-size: 0.7em;
color: rgb(222, 222, 222);
}
.value {
color: rgb(249, 189, 64);
font-size: 0.7em;
}
&.main {
background-color: rgb(65, 147, 237);
.prop-icon {
width: 1.2em;
margin-bottom: 0.2em;
}
.value {
color: rgb(255, 255, 255);
font-size: 1em;
}
}
}
}
}
}
}
.copyright {
margin-top: -3.3em;
position: relative;
z-index: 5;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 996 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB