363 lines
8.9 KiB
TypeScript
363 lines
8.9 KiB
TypeScript
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<T = any>(...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<T, K>(array: T[], callback: (t: T) => K) {
|
|
const map = new Map<K, T[]>()
|
|
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<T extends keyof any, K>(
|
|
status: Record<T, K>,
|
|
callback: (value: K, key: T) => K,
|
|
) {
|
|
const new_status = {} as Record<T, K>
|
|
for (const key in status) {
|
|
const value = callback(status[key], key)
|
|
if (value) {
|
|
new_status[key] = value
|
|
}
|
|
}
|
|
return new_status
|
|
}
|
|
|
|
Array.prototype.max = function <T>(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 <T>(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<string, Map<any, any>>) {
|
|
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,
|
|
}
|
|
}
|
|
}
|