import { ResourceItemType } from "@/Common/ResourceItem/ResourceItemType"; import { BaseEnumerator } from "@/Engine/CatanEngine/CoroutineV2/Core/BaseEnumerator"; import { TableManager } from "@/Engine/CatanEngine/TableV3/TableManager"; import CSSettingsV3 from "@/FormTable/CSSettingsV3"; import { ShopMycardTableRow, ShopShow2TableRow } from "@/FormTable/Tables/ShopTable"; import { Cocos } from "@/assets/VueScript/Cocos"; import { CocosVueScript } from "@/assets/VueScript/CocosVueScript"; import GameData_Cocos from "@/assets/VueScript/share/GameData_Cocos"; import { FriendRequest } from "@/define/Request/FriendRequest"; import { TxnRequest } from "@/define/Request/TxnRequest"; import { VIPLevelMapForChat } from "@/map"; import Player from "@/modules/player"; import { UserBindFlag } from "@/modules/player/define/userbind_flag"; import { ChatRoomRole, Games, ItemCodeList, ItemPropsType, ItemSize, PriceList, SelectorItemProps, StringContentType, TagTypes, TxnCenterData } from "@/types"; import liff from "@line/liff"; import axios, { AxiosResponse } from "axios"; import { isMobile } from "react-device-detect"; import * as Scroll from "react-scroll"; import stringWidth from "string-width"; import BusinessTypeSetting, { FolderName } from "../_BusinessTypeSetting/BusinessTypeSetting"; export const transArray = (array: T[], split: number): T[][] => { const newArr = []; const copiedArr = array ? array?.slice() : []; const length = copiedArr?.length; for (let i = 0, j = 0; i < length; i += split, j++) { newArr[j] = copiedArr.splice(0, split); } return newArr; }; /** 去掉英文+百分比% */ export function onlyNumber(stringText: string): number { const str = +stringText.replace(/[A-Za-z%]/g, ""); return str; } export function wordsLimit(limit: number, string: string) { const length = string?.length; const isOverLimit = length > limit; const result = isOverLimit ? "..." : ""; return string?.substring(0, limit) + result; } export const capitalize = (str: string) => `${str.charAt(0).toUpperCase()}${str.slice(1)}`; export function checkForUnique(str: string): boolean { const chineseCharacterMatch = /[\p{Unified_Ideograph}\u3006\u3007][\ufe00-\ufe0f\u{e0100}-\u{e01ef}]?/gmu; const arr = str.match(chineseCharacterMatch); if (arr === null) return true; return !hasDuplicates(arr); } export function onlyChinese(str: string) { const chineseCharacterMatch = /[\p{Unified_Ideograph}\u3006\u3007][\ufe00-\ufe0f\u{e0100}-\u{e01ef}]?/gmu; const arr = str.match(chineseCharacterMatch); return arr.join(""); } export function hasDuplicates(array: string[]): boolean { return new Set(array).size !== array.length; } export function generatePriceList( arr: [ID: number, ProductId: number, ShowMoney: number][], items: { [id: string]: [ID: number, price: number] }, priceRef: ShopMycardTableRow[], itemCodeList?: Map, ): PriceList[] { if (itemCodeList) { arr = arr.filter((item) => itemCodeList.has(item[0])); } const MyCardPriceRefMap = new Map( priceRef.map((item) => [item.Id, item.Price]), ); const newArr: [ID: number, ProductId: number, ShowMoney: number][] = []; for (const item of Object.values(items)) { arr.forEach((v: [ID: number, ProductId: number, ShowMoney: number]) => { if (v[1] === item[0]) { newArr.push(v); } }); } return newArr .map((item) => ({ ID: item[0], points: item[2], price: MyCardPriceRefMap.get(item[1]), })) .sort((a, b) => b.price - a.price); } /** 支付列表 */ export function generatePaymentList( arr: string[], priceRef: ShopShow2TableRow[], ): ShopShow2TableRow[] { const newArr: ShopShow2TableRow[] = []; for (const item of Object.values(priceRef)) { arr.forEach((x) => { if (item.Show && x === item.Key) newArr.push(item); }); } // priceRef.forEach((value: ShopShow2TableRow) => { // arr.includes(value.Key); // }) return newArr; } /** getQueryParameters */ export function getQueryParameters(v: string): string { const queryParameters = new URLSearchParams(location.search); return queryParameters.get(v); } export function createMap(obj: unknown) { return new Map(Object.entries(obj)); } export function isArray(itemCode: string | string[]): boolean { return Array.isArray(itemCode); } export function formatTime(date: Date): string { const hours = date.getHours(); const minutes = date.getMinutes(); const amPm = hours >= 12 ? "pm" : "am"; const formattedHours = hours % 12 === 0 ? 12 : hours % 12; const formattedMinutes = minutes < 10 ? "0" + minutes : minutes; return `${formattedHours}:${formattedMinutes} ${amPm}`; } export function getCurrentLocalTime() { const date = new Date(); const utcTime = date.getTime(); return new Date(utcTime).toLocaleTimeString(); } export function transferColorText(str: string): string { const regex = /color=/g; const replacement = "span style=color:"; const result = str.replace(regex, replacement); const regex2 = /color>/g; const replacement2 = "span>"; return result.replace(regex2, replacement2); } export function generateItemsData( games: Map, favoriteGames: number[], ): ItemPropsType[] { let itemsData: boolean | ItemPropsType[] = []; const s = new Set(favoriteGames); if (games.size) { // @ts-ignore for (const [gameID, [vendorID, id, VIPLimit, status, tag]] of games) { const dataObj: ItemPropsType = { id: gameID, vendorID: vendorID.toString(), img: { url: `${BusinessTypeSetting.UseDownloadUrl}${FolderName.Game}${id}/b`, }, size: ItemSize.small, tag: tag as unknown as TagTypes, lockBtn: VIPLimit, like: s.has(id), status, }; itemsData.push(dataObj); } } else { itemsData = []; } return itemsData; } export function generateItemsDataMap(games: Games): Map { const map = new Map(); for (const [gameID, value] of Object.entries(games)) { map.set(gameID, value); } return map; } export function sortedGames(sorts: number[], map: Map) { const obj = new Map(); sorts?.forEach((gameID) => { obj.set("" + gameID, map?.get("" + gameID)); }); return obj; } export function addPropertiesToDefaultItem( games: any[], selectedID: number, ): SelectorItemProps[] { const index = games.findIndex((item) => parseInt(item.id) === selectedID); const newGames = games.slice(); return newGames.map((g, i) => i === index ? { ...g, selected: true, defaultItem: true } : { ...g, selected: false, defaultItem: false, }, ); } export function switchObjToMap(games: Games) { // for (const [gameID, value] of Object.entries(games)) { // map.set(gameID, value) // } return new Map(Object.entries(games)); } export function hexToRgb( hex: string, // eslint-disable-next-line @typescript-eslint/typedef result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex), ) { const x = result ? result.map((i) => parseInt(i, 16)).slice(1) : null; return { r: x[0], g: x[1], b: x[2], }; } export function replaceBorderColor(string: string, color: string) { const hasComma = string.includes(","); if (hasComma) { const result = string.split(",").map((s, i) => { const index = s.indexOf("#"); const replaceStr = s.substring(index); return s.replace(replaceStr, i === 0 ? "black" : color); }); return result.join(","); } else { const index = string.indexOf("#"); const replaceStr = string.substring(index); return string.replace(replaceStr, color); } } export function generateMessage(message: string) { return { AID: "10000000063", nickName: "masterkai", profileIMG: "./img/png/avatar.png", role: ChatRoomRole.player, message: message, created: getCurrentLocalTime(), vip: VIPLevelMapForChat.get(4), }; } export function getVIPLevelFromStr(vipStr: string) { const arr = vipStr.split("."); const str = arr[0]; return str.at(-1); } interface Accumulator { [AID: number]: { AID: number; nickName: string; avatar: number; role: number; vip: number; messages: { message: string; created: number; }[]; }; } export function sleep(ms: any): Promise { return new Promise((resolve) => setTimeout(resolve, ms)); } export async function downloadJSON(formname: string) { const patchUrl: string = BusinessTypeSetting.UsePatch + BusinessTypeSetting.FolderUrlJson; let fileUrl: string = `${patchUrl}${formname}.json`; fileUrl = fileUrl + "?v=" + Date.now(); let resp: AxiosResponse = null; axios.get(fileUrl).then((res: AxiosResponse) => { loadJsonProcess(null, res, formname); resp = res; }); while (!resp) { await sleep(0.1); } } function loadJsonProcess( err: any, res: AxiosResponse, formname: string, ) { res["name"] = formname; TableManager.AddJsonAsset(res.data); } export function getProfileImgUrl(avatar: number, aId: number) { return avatar === 1 ? `${BusinessTypeSetting.UseDownloadUrl}avatar/${aId}` : "./img/common/DefaultAvatar.png"; } export const CssStringContent = ( StringKey: number, StringID: number | string, ): string => { switch (StringKey) { case StringContentType.String: return StringID?.toString(); case StringContentType.CSSString: return CSSettingsV3.prototype.CommonString(+StringID); case StringContentType.CSSMailString: return CSSettingsV3.prototype.CSSMailString(+StringID); case StringContentType.CSSNetworkString: return ""; // CSSettingsV3.Network.Priority[StringID][LanguageManager.GetMsgId()]; case StringContentType.HallString: /* 廳管1~4 但表格是從2號位開始 */ return CSSettingsV3.prototype.LobbyString(1 + +StringID); default: break; } return; }; export function txnDataTransformer( arr: TxnRequest.TxnInfo[], playerAid: number, ): TxnCenterData[] { return arr.map( ([ sn, time, giver, receiver, status, fee, [[category, categoryId, quantity]], ]) => ({ serialNum: sn, createdAt: time, giverAid: giver[0], giverName: giver[1], isGiver: giver[0] === playerAid, receiverAid: receiver[0], receiverName: receiver[1], status: +status, quantity, fee, }), ); } export function calculatedReward(rewards: [number, number][]) { const maximum = 6; const result = []; const couponType = ResourceItemType.Card_Coupon; rewards.forEach(([type, quantity]) => { if (type === couponType && quantity > maximum) { const packNum = Math.floor(quantity / maximum); const returnPack = Array.from({ length: packNum }, () => [ couponType, maximum, ]); const remainder = quantity % maximum; const returnRemainder = remainder ? [couponType, remainder] : null; if (returnRemainder) { returnPack.push(returnRemainder); } returnPack.forEach((item) => result.push(item)); } else { result.push([type, quantity]); } }); return result; } export const random = (min: number, max: number) => Math.floor(Math.random() * (max - min)) + min; // Default color is a bright yellow const DEFAULT_COLOR = "hsl(50deg, 100%, 50%)"; export const generateSparkle = (color = DEFAULT_COLOR) => { return { id: "" + random(10000, 99999), createdAt: Date.now(), // Bright yellow color: color, size: random(20, 60), style: { // Pick a random spot in the available space top: random(0, 100) + "%", left: random(0, 100) + "%", // Float sparkles above sibling content zIndex: 2, }, }; }; export const range = (start, end, step = 1) => { const output = []; if (typeof end === "undefined") { end = start; start = 0; } for (let i = start; i < end; i += step) { output.push(i); } return output; }; export async function waitSetBusinessType() { while (!BusinessTypeSetting.UseHost) { await sleep(100); } } /** * 是否為好友 * @param {number} aId AID */ export function isMyFriend(aId: number): boolean { let isTrue: boolean = false; const playerData = Player.data.getState(); const lists: FriendRequest.ListFriendData = playerData.account.allowList; for (let i = 0; i < lists.length; i++) { const list: FriendRequest.SingleFriendData = lists[i]; if (list[0] === aId) { isTrue = true; break; } } return isTrue; } /** * 是否為黑單 * @param {number} aId AID */ export function isMyDeny(aId: number): boolean { let isTrue: boolean = false; const playerData = Player.data.getState(); const lists: FriendRequest.ListFriendData = playerData.account.denyList; for (let i = 0; i < lists.length; i++) { const list: FriendRequest.SingleFriendData = lists[i]; if (list[0] === aId) { isTrue = true; break; } } return isTrue; } export const sheetNameResourceTypeSwitcher = ( resourceType: ResourceItemType, ) => { switch (resourceType) { case ResourceItemType.Card_Coupon: return "CouponSetting"; case ResourceItemType.Card: return "Card1Setting"; default: return "Card1Setting"; } }; export function escapeCodesNToBr(v: string): string { return v.replace(/\n/g, "
"); } /** * 切割顯示字串長度 * @param str * @param showBytes 字元數(1中文2BYTES) * @returns */ export function trimString( str: string, showBytes: number = 12, ellipses: boolean = true, ): string { if (!str) { return str; } let bytes: number = stringWidth(str); if (bytes <= showBytes) { return str; } let byteAmount: number = 0; let strLength: number = str.length; for (let i: number = 0; i < strLength; i++) { let word: string = str[i]; bytes = stringWidth(word); byteAmount += bytes; if (byteAmount > showBytes) { let checkStr: string = str.substring(0, i + 1); let checkByte: number = stringWidth(checkStr); if (checkByte < showBytes) { byteAmount = checkByte; continue; } let result: string = str.substring(0, i); if (ellipses) { return result + "..."; } else { return result; } } } console.error("Trim Nickname Error."); return str; } /** CommonEventType */ export enum CommonEventType { /** Maintenance */ Maintenance, /**ActivityReRender */ ActivityReRender, } export function responsiveText(characters: number): number { if (characters <= 10) return 1.125; if (characters > 10 && characters <= 20) return 0.9; if (characters > 20) return 0.8; } export function discount(numberOff: number): number { return (100 - numberOff) / 100; } /** 預載字體 */ export function PreloadFont(fonts: string[]): void { // Check if API exists if (document && document.fonts) { // Do not block page loading setTimeout(function (): void { let successCount: number = 0; for (let i: number = 0; i < fonts.length; i++) { const font: string = fonts[i]; // eslint-disable-next-line no-loop-func document.fonts.load(`16px ${font}`).then(() => { // Make font using elements visible successCount++; if (successCount === fonts.length) { document.documentElement.classList.add("font-loaded"); } }); } }, 0); } else { // Fallback if API does not exist document.documentElement.classList.add("font-loaded"); } } // PWA /** BeforeInstallPromptEvent */ export let deferredPrompt: any; window.addEventListener("beforeinstallprompt", (e) => { // Prevent Chrome 67 and earlier from automatically showing the prompt e.preventDefault(); // Stash the event so it can be triggered later. deferredPrompt = e; // Update UI to notify the user they can add to home screen Cocos.CocosEventListener.DispatchCallback(GameData_Cocos.CELT.PWAInitOK, e); }); export function addToHomeScreen(): void { if (isMobile) { let url: string = BusinessTypeSetting.UsePatch + "addtohomescreen/index.html" + "?v=" + Date.now(); liff.openWindow({ url: url, external: true, }); } else { if (deferredPrompt) { // Show the prompt deferredPrompt.prompt(); // Wait for the user to respond to the prompt deferredPrompt.userChoice.then((choiceResult) => { if (choiceResult.outcome === "accepted") { // console.log("User accepted the A2HS prompt"); Cocos.CocosEventListener.DispatchCallback(GameData_Cocos.CELT.PWAInitOK, false); } else { // console.log("User dismissed the A2HS prompt"); } deferredPrompt = null; }); } } } export function getArray(count: number): number[] { const array: number[] = []; for (let i: number = 0; i < count; i++) { array.push(i); } return array; } /** 判斷登入並且Line綁定完 */ export function checkWait(): boolean { const playerData = Player.data.getState(); const isLineBind: boolean = Player.hasUserBindFlag(UserBindFlag.LineBind); if (!BaseEnumerator.isInit) { return true; } else if (!CocosVueScript.Instance || !CocosVueScript.Instance?.GetLoginData()) { return true; } else if (!playerData.account.role && !isLineBind) { return true; } return false; } export function scrollToBottom(dynamicListHeight: number, duration: number = 200) { Scroll.animateScroll.scrollTo(dynamicListHeight, { duration: duration, smooth: "easeInQuad", containerId: "scrollableDiv", offset: 50 }); } export function Copy(serialNum: string) { try { navigator.clipboard.writeText(serialNum); } catch (error) { console.error(error); } }