export function in_range(num: number, start: number, end: number) { return start <= num && num <= end } export function to_percent(num?: number, defaultValue: number = 0, suffix = '', withPlus = false) { num = num ?? defaultValue return (num > 0 && withPlus ? '+' : '') + num?.multiply(100).round(2).toString().concat(suffix) } export function get_num(num?: number, defaultValue: number = 0) { return num ?? defaultValue } export function format_string(str: string, params: any = {}, ...args: any[]) { let num = 0 str = str.replace(/{}/g, (match) => { return args[num++] ?? match }) str = str.replace(/{\d+}/g, (match) => { const i = Number.parseInt(match.slice(1, -1)) return args[i] ?? match }) str = str.replace(/{\w+}/g, (match) => { const name = match.slice(1, -1) return params[name] ?? match }) return str } export function format_bytes(bytes: number, decimals = 2) { if (bytes === 0) { return '0 Bytes' } const k = 1024 const dm = decimals < 0 ? 0 : decimals const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'] const i = Math.floor(Math.log(bytes) / Math.log(k)) return `${Number.parseFloat((bytes / k ** i).toFixed(dm))} ${sizes[i]}` } export function getFloat(value: any, n: any) { value = Number(value) n = n ? Number.parseInt(n) : 0 if (n <= 0) { return Math.round(value) } value = Math.round(value * 10 ** n) / 10 ** n value = Number(value).toFixed(n) return value } export function cartesian(...arr: T[][]): T[][] { if (arr.length < 2) { return arr } return arr.reduce( (a, b) => { const ret: T[][] = [] a.forEach((a) => { b.forEach((b) => { ret.push(a.concat([b])) }) }) return ret }, [[]] as T[][], ) } export function groupBy(array: T[], callback: (t: T) => K) { const map = new Map() for (let i = 0; i < array.length; i++) { const t = array[i] const key = callback(t) const temp: T[] = map.get(key) || [] if (!map.has(key)) { map.set(key, temp) } temp.push(t) } return Array.from(map.values()) } export function debounce_last(func: Function, wait: number) { let timer = Number.NaN return function () { if (timer) { clearTimeout(timer) } timer = setTimeout(func, wait) } } export function padding(num: number | string, length: number): string { if (`${num}`.length >= length) { return `${num}` } return padding(`0${num}`, length) } export function format_float(f: number, digit: number = 2) { const m = 10 ** digit return (f * m).round(10) / m } export function mapRecord( status: Record, callback: (value: K, key: T) => K, ) { const new_status = {} as Record for (const key in status) { const value = callback(status[key], key) if (value) { new_status[key] = value } } return new_status } Array.prototype.max = function (callback: (t: T) => number): [number, T | undefined] { let rs: [number, T | undefined] = [-1, undefined] let max = -1 for (let i = 0; i < this.length; i++) { const item = this.at(i) as T const count = callback(item) if (count > max) { max = count rs = [i, item] } } return rs } Array.prototype.min = function (callback: (t: T) => number): [number, T | undefined] { let rs: [number, T | undefined] = [-1, undefined] let min = -1 for (let i = 0; i < this.length; i++) { const item = this.at(i) as T const count = callback(item) if (count < min) { min = count rs = [i, item] } } return rs } Number.prototype.multiply = function (value: number) { return Number(this) * value } Number.prototype.round = function (digit?: number) { const m = 10 ** (digit ?? 0) return Math.round(Number(this) * m) / m } export function formatStr(template: string, ...params: any) { let str = template if (params.length == 1 && params[0].length > 0) { params = params[0] } for (let i = 0; i < params.length; i++) { str = str.replaceAll(`{${i}}`, params[i]) } return str } export function toObj(data: any): any { const temp = {} for (const key in data) { try { if (typeof data[key] === 'number' || typeof data[key] === 'string') { Object.defineProperty(temp, key, { value: data[key], writable: true, enumerable: true, configurable: true, }) } else { Object.defineProperty(temp, key, { value: RecordToObj(data[key]), writable: true, enumerable: true, configurable: true, }) } } catch { Object.defineProperty(temp, key, { value: data[key], writable: true, enumerable: true, configurable: true, }) } } return temp } export function toMap(data: any, except: string[] = []) { const temp = {} for (const key in data) { if (typeof data[key] === 'object' && data[key].constructor != Array && !except.includes(key)) { // 需要转换部分,不能是Array,也会被转换 const sub = {} for (const subkey in data[key]) { Object.defineProperty(sub, subkey, { value: new Map(Object.entries(data[key][subkey])), writable: true, enumerable: true, configurable: true, }) } Object.defineProperty(temp, key, { value: sub, writable: true, enumerable: true, configurable: true, }) } else { Object.defineProperty(temp, key, { value: data[key], writable: true, enumerable: true, configurable: true, }) } } return temp } export function RecordToObj(todo: Record>) { const temp = {} for (const key in todo) { Object.defineProperty(temp, key, { value: Object.fromEntries(todo[key]), writable: true, enumerable: true, configurable: true, }) } return temp } export function getUuid() { if (typeof crypto === 'object') { if (typeof crypto.randomUUID === 'function') { return crypto.randomUUID() } if (typeof crypto.getRandomValues === 'function' && typeof Uint8Array === 'function') { const callback = (c: any) => { const num = Number(c) return (num ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (num / 4)))).toString( 16, ) } return '10000000-1000-4000-8000-100000000000'.replace(/[018]/g, callback) } } let timestamp = new Date().getTime() let perforNow = (typeof performance !== 'undefined' && performance.now && performance.now() * 1000) || 0 return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => { let random = Math.random() * 16 if (timestamp > 0) { random = Math.trunc((timestamp + random) % 16) timestamp = Math.floor(timestamp / 16) } else { random = Math.trunc((perforNow + random) % 16) perforNow = Math.floor(perforNow / 16) } return (c === 'x' ? random : (random & 0x3) | 0x8).toString(16) }) } export function rarityClass(type: string) { switch (type) { case '太初': return 'primeval' case '史诗': return 'epic' case '神话': return 'myth' case '智慧产物': return 'epic' case '神器': return 'artifact' case '稀有': return 'rare' case '传说': return 'legend' case '勇者': return 'chronicle' case '高级': return 'advanced' default: return 'epic' } } export function formatAttr(type: string, value: number) { switch (type) { case 'STR': return `力量 ${value}` case 'INT': return `智力 ${value}` case 'Vitality': return `体力 ${value}` case 'Spirit': return `精神 ${value}` case 'AtkP': return `物理攻击力 ${value}` case 'AtkM': return `魔法攻击力 ${value}` case 'AtkI': return `独立攻击力 ${value}` case 'SkillAttack': return `技能伤害 增加 ${(value * 100).toFixed(2)}%` case 'Attack': return `攻击强化 +${value.toFixed(1)}%` case 'Buffer': return `增益量 ${value}` default: return `${type} ${value}` } } export const decrypt = (encryptedData: string) => { try { encryptedData = decodeURIComponent(encryptedData) .split('') .map((a) => { if (a == '=') { return a } else { return String.fromCharCode(a.charCodeAt(0) - 1) } }) .join('') const decryptedData = atob(encryptedData) const len = decryptedData.length const decryptedBinaryOffset = decryptedData.slice(0, len - 13) const decryptedTimestamp = decryptedData.slice(len - 13, len) return { uid: parseInt(decryptedBinaryOffset, 2), time: parseInt(decryptedTimestamp), } } catch (e) { // console.log(e) return { uid: -1, time: 0, } } }