[add] 進機台

This commit is contained in:
建喵 2023-12-05 17:27:18 +08:00
parent 987aa1cb5e
commit d80118bb68
40 changed files with 1538 additions and 1109 deletions

View File

@ -17,7 +17,7 @@
<body oncontextmenu="return false"> <body oncontextmenu="return false">
<noscript>You need to enable JavaScript to run this app.</noscript> <noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div> <div id="root" style="height: 100vh;"></div>
<script type="module" src="/src/index.tsx"></script> <script type="module" src="/src/index.tsx"></script>
</body> </body>

View File

@ -1,23 +1,9 @@
import { CoroutineV2 } from "@/Engine/CatanEngine/CoroutineV2/CoroutineV2";
import { INetResponse } from "@/Engine/CatanEngine/NetManagerV2/Core/INetResponse"; import { INetResponse } from "@/Engine/CatanEngine/NetManagerV2/Core/INetResponse";
import CSSettingsV3 from "@/FormTable/CSSettingsV3"; import CSSettingsV3 from "@/FormTable/CSSettingsV3";
import { confirmModalObj } from "@/UI/MessageBox/ConfirmModalContext"; import { gameSync } from "@/utils/setRPCData";
import { modalObj } from "@/UIControl/ModalContext";
import { Cocos } from "@/assets/VueScript/Cocos";
import { CommonEventCallBack, JackpotPoolCallBack } from "@/assets/VueScript/CocosVueScript";
import GameData_Cocos from "@/assets/VueScript/share/GameData_Cocos";
import { chatLog, chatMessage, setChatBanTime } from "@/components/ModalContent/ChatRoomModal/chatUtils";
import Config from "@/define/Config";
import Player from "@/modules/player";
import UserData from "@/modules/player/UserData";
import { State } from "@/modules/player/define/data";
import { ILineShareData, ModalContentType } from "@/types";
import { CommonEventType } from "@/utils";
import { NumberEx } from "@/utils/Number/NumberEx";
import { activityComSync, backpackInfo, friendAllowList, friendDenyList, profileInfo, txnCenter, txnNew, txnTrade, txnUserAdd, vipInfo } from "@/utils/setRPCData";
import MainControl from "../MainControl/MainControl"; import MainControl from "../MainControl/MainControl";
import CSMessage from "../Message/CSMessage"; import CSMessage from "../Message/CSMessage";
import CSResource from "../ResourceItem/CSResource";
import IResourceItem from "../ResourceItem/IResourceItem";
export default class MainControlData { export default class MainControlData {
@ -46,7 +32,7 @@ export default class MainControlData {
private _disconnectErrorType: number = null; private _disconnectErrorType: number = null;
constructor() { constructor() {
Player.DataReceivedEvent.AddCallback(this._dataReceivedEvent, this); MainControl.DataReceivedEvent.AddCallback(this._dataReceivedEvent, this);
} }
private _dataReceivedEvent(param: any[] = null): void { private _dataReceivedEvent(param: any[] = null): void {
let type: MainControl.DataType = param[0]; let type: MainControl.DataType = param[0];
@ -67,139 +53,8 @@ export default class MainControlData {
private _serverData(resp: INetResponse<any>): void { private _serverData(resp: INetResponse<any>): void {
if (resp.IsValid) { if (resp.IsValid) {
switch (resp.Method) { switch (resp.Method) {
case "activity_com.sync": {
activityComSync(resp.Data);
break;
}
case "backpack.info": {
backpackInfo(resp.Data);
break;
}
case "chat.ban": {
setChatBanTime(resp.Data);
break;
}
case "chat.message": {
chatMessage(resp.Data);
break;
}
case "chat.log": {
chatLog(resp.Data);
break;
}
case "friend.allow_list": {
friendAllowList(resp.Data);
break;
}
case "friend.deny_list": {
friendDenyList(resp.Data);
break;
}
case "game.share": {
const { openLineShareGame } = confirmModalObj;
const data: ILineShareData = {
slotID: resp.Data[0],
winMilt: resp.Data[1]
};
openLineShareGame(data);
break;
}
case "game.sync": { case "game.sync": {
Cocos.VicKing_Bridge.InGameGetUUID = true; CoroutineV2.Single(gameSync()).Start();
break;
}
case "jackpot.get": {
const { handleOpen, setModalType } = modalObj;
const playerData: State = Player.data.getState();
playerData.account.jackpotGet.push(...resp.Data);
Player.data.setState(playerData);
setModalType(ModalContentType.jackpot);
handleOpen();
break;
}
case "jackpot.pool": {
const playerData: State = Player.data.getState();
if (!Config.IsVite || MainControl.Instance.IsInGame) {
for (let i = 0; i < playerData.game.jackpotPool.length; i++) {
const oldJpData: [gameId: number, maxJPId: number, jp: number] = playerData.game.jackpotPool[i];
let newJpData: [gameId: number, maxJPId: number, jp: number] = null;
for (let j = 0; j < resp.Data.length; j++) {
const [gameId, maxJPId, jp] = resp.Data[j];
if (gameId === oldJpData[0]) {
newJpData = resp.Data[j];
break;
}
}
if (!newJpData) {
JackpotPoolCallBack.DispatchCallback(oldJpData[0], 0);
}
}
}
playerData.game.jackpotPool = resp.Data;
Player.data.setState(playerData);
if (!Config.IsVite || MainControl.Instance.IsInGame) {
for (let i = 0; i < playerData.game.jackpotPool.length; i++) {
const jpData: [gameId: number, maxJPId: number, jp: number] = playerData.game.jackpotPool[i];
JackpotPoolCallBack.DispatchCallback(jpData[0], jpData[2]);
}
} else {
Cocos.CocosEventListener.DispatchCallback(GameData_Cocos.CELT.UpdateJPPool, null);
}
break;
}
case "maintain.info": {
const playerData: State = Player.data.getState();
if (resp.Data) {
resp.Data[3] = NumberEx.plus(MainControl.Instance.NowTime, resp.Data[3]);
playerData.maintain = resp.Data;
CommonEventCallBack.DispatchCallback(CommonEventType.Maintenance, null);
} else {
playerData.maintain = undefined;
}
Player.data.setState(playerData);
break;
}
case "profile.info": {
profileInfo(resp.Data);
break;
}
case "resource.update": {
const resourceItems: IResourceItem[] = CSResource.GetResourceItemsFromServer(resp.Data);
UserData.DoResUpdate(resourceItems);
break;
}
case "resource.bankruptcy": {
Cocos.CocosEventListener.DispatchCallback(GameData_Cocos.CELT.Bankruptcy, null);
break;
}
case "system.disconnect": {
this._disconnectErrorType = +resp.Data["c"];
break;
}
case "txn.new": {
txnNew(resp.Data);
break;
}
case "txn.center": {
txnCenter(resp.Data);
break;
}
case "txn.trade": {
txnTrade(resp.Data);
break;
}
case "txn.user_add": {
txnUserAdd(resp.Data);
break;
}
case "vip.info": {
vipInfo(resp.Data);
break;
}
case "vip.level": {
const playerData: State = Player.data.getState();
playerData.vip.level = resp.Data;
Player.data.setState(playerData);
break; break;
} }
default: default:
@ -207,10 +62,9 @@ export default class MainControlData {
} }
} else { } else {
switch (resp.Method) { switch (resp.Method) {
case "chat.log": { // case "": {
chatLog([null, null]); // break;
break; // }
}
default: default:
break; break;
} }

View File

@ -1,8 +1,4 @@
import CSSettingsV3 from "@/FormTable/CSSettingsV3"; import { IConfirmMessageData, modalObj } from "@/UIControl/ModalContext";
import { IConfirmMessageData } from "@/UI/MessageBox/ConfirmMessage";
import { confirmModalObj } from "@/UI/MessageBox/ConfirmModalContext";
import { StringFormKey } from "@/define/formkey";
/** 訊息框相關 */ /** 訊息框相關 */
export default class CSMessage { export default class CSMessage {
@ -10,7 +6,7 @@ export default class CSMessage {
/** 一個按鈕的訊息框 */ /** 一個按鈕的訊息框 */
public static CreateYesMsg(content: string, yesCallback: () => void = null, enterStr: string = null, title: string = null, textAlign: "center" | "left" | "right" = null) { public static CreateYesMsg(content: string, yesCallback: () => void = null, enterStr: string = null, title: string = null, textAlign: "center" | "left" | "right" = null) {
enterStr = enterStr ? enterStr : CSSettingsV3.prototype.CommonString(StringFormKey.String.Confirm); enterStr = enterStr ? enterStr : "確認";
let data: IConfirmMessageData = { let data: IConfirmMessageData = {
title: title, title: title,
content: content, content: content,
@ -19,14 +15,14 @@ export default class CSMessage {
enterStr: enterStr, enterStr: enterStr,
textAlign: textAlign textAlign: textAlign
}; };
const { openOtherConfirm } = confirmModalObj; const { handleOpen } = modalObj;
openOtherConfirm(data); handleOpen(data);
} }
/** 兩個按鈕的訊息框 */ /** 兩個按鈕的訊息框 */
public static CreateYesNoMsg(content: string, yesCallback: () => void = null, noCallback: () => void = null, enterStr: string = null, title: string = null, cancelStr: string = null, textAlign: "center" | "left" | "right" = null) { public static CreateYesNoMsg(content: string, yesCallback: () => void = null, noCallback: () => void = null, enterStr: string = null, title: string = null, cancelStr: string = null, textAlign: "center" | "left" | "right" = null) {
enterStr = enterStr ? enterStr : CSSettingsV3.prototype.CommonString(StringFormKey.String.Confirm); enterStr = enterStr ? enterStr : "確認";
cancelStr = cancelStr ? cancelStr : CSSettingsV3.prototype.CommonString(StringFormKey.String.Cancel); cancelStr = cancelStr ? cancelStr : "取消";
let data: IConfirmMessageData = { let data: IConfirmMessageData = {
title: title, title: title,
content: content, content: content,
@ -37,8 +33,8 @@ export default class CSMessage {
cancelStr: cancelStr, cancelStr: cancelStr,
textAlign: textAlign textAlign: textAlign
}; };
const { openOtherConfirm } = confirmModalObj; const { handleOpen } = modalObj;
openOtherConfirm(data); handleOpen(data);
} }
/** 網路錯誤訊息 */ /** 網路錯誤訊息 */

View File

@ -82,9 +82,9 @@ export class NetConnector {
// if (CC_DEBUG && NetConfig.ShowServerLog) { // if (CC_DEBUG && NetConfig.ShowServerLog) {
if (NetConfig.ShowServerLog) { if (NetConfig.ShowServerLog) {
if (req.Data != null && req.Data != undefined && !Number.isNaN(req.Data)) { if (req.Data != null && req.Data != undefined && !Number.isNaN(req.Data)) {
console.debug(`[RPC] 傳送server資料: ${req.Method}(${JSON.stringify(req.Data)})`); console.log(`[RPC] 傳送server資料: ${req.Method}(${JSON.stringify(req.Data)})`);
} else { } else {
console.debug(`[RPC] 傳送server資料: ${req.Method}()`); console.log(`[RPC] 傳送server資料: ${req.Method}()`);
} }
} }
@ -185,9 +185,9 @@ export class NetConnector {
// if (CC_DEBUG && NetConfig.ShowServerLog) { // if (CC_DEBUG && NetConfig.ShowServerLog) {
if (NetConfig.ShowServerLog) { if (NetConfig.ShowServerLog) {
if (data) { if (data) {
console.debug(`[RPC] 收到server呼叫:(${resp.Status}): ${resp.Method}(${JSON.stringify(resp.Data)})`); console.log(`[RPC] 收到server呼叫:(${resp.Status}): ${resp.Method}(${JSON.stringify(resp.Data)})`);
} else { } else {
console.debug(`[RPC] 收到server呼叫:(${resp.Status}): ${resp.Method}()`); console.log(`[RPC] 收到server呼叫:(${resp.Status}): ${resp.Method}()`);
} }
} }

View File

@ -0,0 +1,54 @@
import { INetRequest } from "./Core/INetRequest";
import { NetConnector } from "./NetConnector";
export class NetManagerSD {
static get IsConnected() {
return this._connector && this._connector.IsConnected;
}
static get HasInit() {
return this._connector != null;
}
private static _connector: NetConnector;
static Initialize(connector: NetConnector) {
this._connector = connector;
}
static ConnectAsync() {
this.CheckConnector();
return this._connector.ConnectAsync();
}
/**
*
*/
static Disconnect() {
this.CheckConnector();
this._connector.Disconnect();
}
/**
* Server,
* @param req
*/
static Send(req: INetRequest<any, any>) {
this.CheckConnector();
this._connector.Send(req);
}
/**
* Server,
* @param req
*/
static SendAsync(req: INetRequest<any, any>, mask: boolean) {
this.CheckConnector();
return this._connector.SendAsync(req, mask);
}
private static CheckConnector() {
if (!this._connector) throw new Error("請先呼叫CasinoNetManager.Initialize()初始化connector");
}
}

View File

@ -1,5 +1,5 @@
import { VueNetConnector } from "@/assets/VueScript/Net/VueNetConnector";
import { INetRequest } from "./Core/INetRequest"; import { INetRequest } from "./Core/INetRequest";
import { NetManager } from "./NetManager";
export abstract class NetRequest<TResquest, TResponse> implements INetRequest<TResquest, TResponse> { export abstract class NetRequest<TResquest, TResponse> implements INetRequest<TResquest, TResponse> {
abstract get Method(): string; abstract get Method(): string;
@ -11,18 +11,11 @@ export abstract class NetRequest<TResquest, TResponse> implements INetRequest<TR
Data: TResquest; Data: TResquest;
Result: import("./Core/INetResponse").INetResponse<TResponse>; Result: import("./Core/INetResponse").INetResponse<TResponse>;
/**
* Cocos會收到SERVER主動通知
* Cocos會收到SERVER主動通知
* Cocos會收到SERVER主動通知
*/
SendAsync(mask: boolean = false): Iterator<any> { SendAsync(mask: boolean = false): Iterator<any> {
// return NetManager.SendAsync(this, mask); return NetManager.SendAsync(this, mask);
return VueNetConnector.SendAsync(this, mask);
} }
Send() { Send() {
// NetManager.Send(this); NetManager.Send(this);
VueNetConnector.Send(this);
} }
} }

View File

@ -0,0 +1,21 @@
import { INetRequest } from "./Core/INetRequest";
import { NetManagerSD } from "./NetManagerSD";
export abstract class NetRequestSD<TResquest, TResponse> implements INetRequest<TResquest, TResponse> {
abstract get Method(): string;
get MethodBack(): string {
return this.Method;
}
Data: TResquest;
Result: import("./Core/INetResponse").INetResponse<TResponse>;
SendAsync(mask: boolean = false): Iterator<any> {
return NetManagerSD.SendAsync(this, mask);
}
Send() {
NetManagerSD.Send(this);
}
}

View File

@ -1,11 +1,9 @@
import MainControl from "@/Common/MainControl/MainControl";
import CSMessage from "@/Common/Message/CSMessage";
import { CoroutineV2 } from "@/Engine/CatanEngine/CoroutineV2/CoroutineV2";
import { INetResponse } from "@/Engine/CatanEngine/NetManagerV2/Core/INetResponse";
import { useGameItems } from "@/context/GameItemsContext"; import { useGameItems } from "@/context/GameItemsContext";
import { CommonAccountResponse, LineLoginRequest } from "@/define/Request/RegisterRequest"; import { Layout } from "antd";
import { useEffect } from "react"; import { useEffect } from "react";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import Player from "./Lobby/Player";
import SlotList from "./Lobby/SlotList";
const Lobby = () => { const Lobby = () => {
const { player, setPlayer } = useGameItems(); const { player, setPlayer } = useGameItems();
@ -17,30 +15,6 @@ const Lobby = () => {
navigate(`/`); navigate(`/`);
return; return;
} }
CoroutineV2.Single(onStart()).Start();
}
function* onStart() {
yield* MainControl.Instance.ConnectAsync();
}
function* registerLineLogin() {
let req: LineLoginRequest = new LineLoginRequest(token);
yield req.SendAsync(true);
let resp: INetResponse<CommonAccountResponse> = req.Result;
if (!resp.IsValid) {
//取得帳號失敗直接斷開SOCKET
if (resp.Status != 12) {
const msg: string = "Line Info Error. Error Code:" + req.Result.Status;
CSMessage.CreateYesMsg(msg);
return;
}
console.warn("LINE帳號無綁定");
return;
}
// yield* this.ServerAccountLogin(resp.Data.id, resp.Data.pw);
return;
} }
useEffect(() => { useEffect(() => {
@ -48,7 +22,10 @@ const Lobby = () => {
}, []); }, []);
return ( return (
<>Lobby</> <Layout hasSider style={{ height: "100%", position: "relative" }}>
<Player />
{<SlotList />}
</Layout>
); );
}; };

35
src/UI/Lobby/Player.tsx Normal file
View File

@ -0,0 +1,35 @@
import { CurrencyManager } from "@/FormTableExt/Manage/Currency/CurrencyManager";
import { useGameItems } from "@/context/GameItemsContext";
import { useEffect } from "react";
const Player = () => {
const { player } = useGameItems();
const { aId, name, m } = player;
function onLoad() {
}
useEffect(() => {
onLoad();
}, []);
return (
<div style={siderStyle}>
<p>aId: {aId}</p>
<p>: {name}</p>
<p>: {CurrencyManager.GetNumberWithComma(m)}</p>
</div>
);
};
export default Player;
const siderStyle: React.CSSProperties = {
fontSize: "1rem",
color: "#000000",
display: "flex",
textAlign: "left",
flexDirection: "column",
justifyContent: "center",
width: "20%"
};

145
src/UI/Lobby/SDGame.tsx Normal file
View File

@ -0,0 +1,145 @@
import CSMessage from "@/Common/Message/CSMessage";
import { CoroutineV2 } from "@/Engine/CatanEngine/CoroutineV2/CoroutineV2";
import { INetResponse } from "@/Engine/CatanEngine/NetManagerV2/Core/INetResponse";
import { NetConnector } from "@/Engine/CatanEngine/NetManagerV2/NetConnector";
import { NetManagerSD } from "@/Engine/CatanEngine/NetManagerV2/NetManagerSD";
import { useGameItems } from "@/context/GameItemsContext";
import SlotBase from "@/define/Game/Base/SlotBase";
import { SDAccountLoginRequest } from "@/define/Request/AccountRequest";
import { SlotInRequest } from "@/define/Request/SlotRequest";
import { Button, Flex } from "antd";
import { useEffect, useState } from "react";
const SDGame = (props: ISDGame) => {
const { gameUrl, onClickSlotOut } = props;
const { player, setPlayer } = useGameItems();
const { gameData } = useGameItems();
const { nowSlotId } = gameData;
const [slotId, setSlotId] = useState<number>(undefined);
const [slotData, setSlotData] = useState<Object>(undefined);
let conn: NetConnector = undefined;
function* onLoad(): IterableIterator<any> {
const url: URL = new URL(gameUrl);
const queryParameters: URLSearchParams = new URLSearchParams(url.search);
const token: string = queryParameters.get("token");
const slotid: number = +queryParameters.get("slotid");
const host: string = queryParameters.get("host");
const port: number = 9005;
setSlotId(slotid);
conn = new NetConnector("https://" + host, port);
conn.OnDataReceived.AddCallback(onNetDataReceived);
conn.OnDisconnected.AddCallback(onNetDisconnected);
NetManagerSD.Initialize(conn);
console.log("[SDsocket] connecting...");
yield NetManagerSD.ConnectAsync();
yield* login(token);
yield* slotIn(slotid);
}
function* login(token: string): IterableIterator<any> {
const req = new SDAccountLoginRequest(token);
yield req.SendAsync(true);
const resp = req.Result;
if (!resp.IsValid) {
CSMessage.NetError(resp.Method, resp.Status, "SD Account Login Fail");
return;
}
}
function* slotIn(slotid: number): IterableIterator<any> {
let req: SlotInRequest = new SlotInRequest(slotid);
yield req.SendAsync(true);
let resp: INetResponse<JSON> = req.Result;
if (resp.IsValid) {
setSlotData(resp.Data);
}
}
async function OnclickSpin() {
let slot: any;
const slotGroup: typeof import("../../define/Game/Base/Slot") = await import(/* @vite-ignore */`../../define/Game/Base/Slot`);
try {
slot = slotGroup[`Slot${slotId}`];
} catch (error) {
//
}
if (!slot) {
slot = slotGroup.SlotBase;
}
const slotClass: SlotBase = new slot(slotId);
// this.IsSpin.value = true;
CoroutineV2.Single(spin(slotClass)).Start();
}
function* spin(slotClass: SlotBase): IterableIterator<any> {
yield* slotClass.Spin(20);
// await Tools.Sleep(this.SpinDelay.value * 1000);
// if (this.IsSpin.value && this._bj_Casino_Bot.LobbyScript.IsSlotIn.value) {
// this.Spin();
// } else {
// this.IsRun = false;
// }
}
/**RPC回傳.若協定錯誤斷線.原因也會在這裡收到 */
function onNetDataReceived(resp: INetResponse<any>) {
// MainControl.DataReceivedEvent.DispatchCallback([MainControl.DataType.ServerData, resp]);
}
/**只要連線中斷不管主被動都會走到這裡 */
function onNetDisconnected() {
console.warn("[socket] Disconnected");
conn.OnDataReceived.RemoveAllCallbacks();
// MainControl.DataReceivedEvent.DispatchCallback([MainControl.DataType.NetDisconnected]);
}
useEffect(() => {
CoroutineV2.Single(onLoad()).Start();
}, []);
return (<>
<div style={siderStyle}>
<Flex gap="small" wrap="wrap">
<div style={controlStyle}>
Bet: 20
<p>
<Button type="primary" onClick={OnclickSpin} style={{ width: "20%" }}>Spin</Button>
<Button type="primary" danger onClick={onClickSlotOut} style={{ width: "20%" }}></Button>
</p>
</div>
<div>
Log
</div>
</Flex>
</div>
</>);
};
export default SDGame;
interface ISDGame {
gameUrl: string;
onClickSlotOut: () => void;
}
const siderStyle: React.CSSProperties = {
fontSize: "1rem",
color: "#000000",
display: "flex",
textAlign: "left",
flexDirection: "column",
justifyContent: "center",
width: "100%"
};
const controlStyle: React.CSSProperties = {
fontSize: "1rem",
display: "flex",
textAlign: "left",
flexDirection: "column",
justifyContent: "center",
lineHeight: "30px",
width: "30%"
};

93
src/UI/Lobby/SlotList.tsx Normal file
View File

@ -0,0 +1,93 @@
import CSMessage from "@/Common/Message/CSMessage";
import { CoroutineV2 } from "@/Engine/CatanEngine/CoroutineV2/CoroutineV2";
import { INetResponse } from "@/Engine/CatanEngine/NetManagerV2/Core/INetResponse";
import CSSettingsV3 from "@/FormTable/CSSettingsV3";
import BusinessTypeSetting from "@/_BusinessTypeSetting/BusinessTypeSetting";
import Image from "@/components/Image/Image";
import { useGameItems } from "@/context/GameItemsContext";
import { GameLaunchRequest, GameLeaveRequest, RpcGameLaunchResponse } from "@/define/Request/GameRequest";
import { SlotData } from "@/define/gameData";
import { Button, Flex } from "antd";
import { useEffect, useState } from "react";
import SDGame from "./SDGame";
const SlotList = () => {
const { gameData, setGameData } = useGameItems();
const { slotData, slotList, nowSlotId } = gameData;
const [isGameIn, setIsGameIn] = useState<boolean>(false);
const [gameUrl, setGameUrl] = useState<string>("");
function onLoad() {
}
function* onClickSlotIn(slotId: number): IterableIterator<any> {
const data: SlotData = slotData[slotId];
const [componyID] = data;
const req: GameLaunchRequest = new GameLaunchRequest(componyID, slotId);
yield req.SendAsync();
const resp: INetResponse<RpcGameLaunchResponse> = req.Result;
if (!resp.IsValid) {
if (resp.Status === 18) {
CSMessage.CreateYesMsg(CSSettingsV3.prototype.CommonString(16));
}
return;
}
setIsGameIn(true);
const url: string = resp.Data;
setGameData({
...gameData,
nowSlotId: slotId
});
if (componyID === 2) {
setGameUrl(url);
}
else {
window.open(url, "_blank");
}
}
function onClickSlotOut() {
const gameLeaveReq: GameLeaveRequest = new GameLeaveRequest(nowSlotId);
gameLeaveReq.Send();
setGameUrl("");
setIsGameIn(false);
}
useEffect(() => {
onLoad();
}, []);
return (<>
{isGameIn
? <>{gameUrl
? <SDGame gameUrl={gameUrl} onClickSlotOut={onClickSlotOut} />
: <Flex gap="small" wrap="wrap">
<Button type="primary" onClick={onClickSlotOut}></Button>
</Flex>}</>
: <div style={contentStyle}>
<Flex gap="small" wrap="wrap">
{slotList.map((slotId: number, index: number) =>
<Image key={index} width={80} height={80} src={`${BusinessTypeSetting.UseDownloadUrl}game/${slotId}/s`}
onClick={() => { CoroutineV2.Single(onClickSlotIn(slotId)).Start(); }} style={{ cursor: "pointer" }} />
)}
</Flex>
</div>
}
</>);
};
export default SlotList;
const contentStyle: React.CSSProperties = {
fontSize: "1rem",
minHeight: 120,
lineHeight: "120px",
color: "#000000",
display: "flex",
textAlign: "center",
flexDirection: "column",
justifyContent: "center",
width: "100%"
};

View File

@ -1,5 +1,11 @@
import MainControl from "@/Common/MainControl/MainControl";
import CSMessage from "@/Common/Message/CSMessage";
import { CoroutineV2 } from "@/Engine/CatanEngine/CoroutineV2/CoroutineV2";
import { INetResponse } from "@/Engine/CatanEngine/NetManagerV2/Core/INetResponse";
import { BusinessEnum } from "@/_BusinessTypeSetting/BusinessTypeSetting"; import { BusinessEnum } from "@/_BusinessTypeSetting/BusinessTypeSetting";
import { useGameItems } from "@/context/GameItemsContext"; import { useGameItems } from "@/context/GameItemsContext";
import { AccountLoginRequest } from "@/define/Request/AccountRequest";
import { CommonAccountResponse, LineLoginRequest } from "@/define/Request/RegisterRequest";
import { Button, Cascader } from "antd"; import { Button, Cascader } from "antd";
import TextArea from "antd/es/input/TextArea"; import TextArea from "antd/es/input/TextArea";
import React, { useState } from "react"; import React, { useState } from "react";
@ -17,7 +23,7 @@ const Login = () => {
const serverType: typeof BusinessEnum.ServerType = BusinessEnum.ServerType; const serverType: typeof BusinessEnum.ServerType = BusinessEnum.ServerType;
const [type, setType] = useState<number>(BusinessEnum.ServerType.Internal_Dev); const [type, setType] = useState<number>(BusinessEnum.ServerType.Internal_Dev);
const [isLogin, setIsLogin] = useState<boolean>(false); const [isLogin, setIsLogin] = useState<boolean>(false);
const [token, SetToken] = useState(""); const [token, SetToken] = useState("eyJhbGciOiJIUzI1NiJ9.Am-dhpCRUo9iBHYJ0kro12-zUlOyNAVOw9poXEkUV14hvkL2RPxVqqtrsYbS9_aoKep4EOFYROFTbv6MfVai7gomKdr07XkmTtADtkbchkfm-yuGXVzW1mYabf646_U66MnvXX2PHS-ATXDYYx5He9PJ-5lF9g5BmhtxUYPW98w.MGUUrFQbBeUBPDJeoKMilbqbg6IkwEqbu2oyJVSmw6M");
const options: Option[] = []; const options: Option[] = [];
for (let i = 0, names: string[] = Object.keys(serverType); i < names.length; i++) { for (let i = 0, names: string[] = Object.keys(serverType); i < names.length; i++) {
const key: string = names[i]; const key: string = names[i];
@ -29,11 +35,59 @@ const Login = () => {
} }
} }
async function login() { async function onClickLogin() {
if (!token) {
CSMessage.CreateYesMsg("請輸入token");
return;
}
setIsLogin(true); setIsLogin(true);
await onLoad(type); await onLoad(type);
CoroutineV2.Single(login()).Start();
}
function* login() {
yield* MainControl.Instance.ConnectAsync();
yield* registerLineLogin();
}
function* registerLineLogin() {
let req: LineLoginRequest = new LineLoginRequest(token);
yield req.SendAsync(true);
let resp: INetResponse<CommonAccountResponse> = req.Result;
if (!resp.IsValid) {
//取得帳號失敗直接斷開SOCKET
if (resp.Status != 12) {
const msg: string = "Line Info Error. Error Code:" + req.Result.Status;
CSMessage.CreateYesMsg(msg);
setIsLogin(false);
return;
}
console.warn("LINE帳號無綁定");
setIsLogin(false);
return;
}
yield* serverAccountLogin(resp.Data.id, resp.Data.pw);
}
/** 遊戲帳號登入取得玩家資料.統一登入時在刪除需要刪除的資料 */
function* serverAccountLogin(a: string, pw: string, partner: string = null): IterableIterator<any> {
let hasAP: boolean = (a && a != "null" && a != "undefined" && pw && pw != "null" && pw != "undefined");
if (!hasAP) {
CSMessage.CreateYesMsg("沒有帳號或密碼.請確認回傳接值.");
setIsLogin(false);
return;
}
let req: AccountLoginRequest = new AccountLoginRequest(a, pw, partner);
yield req.SendAsync(true);
let resp: INetResponse<any> = req.Result;
if (!resp.IsValid) {
CSMessage.CreateYesMsg("Login Account Error! Error Code : " + resp.Status);
setIsLogin(false);
return;
}
setPlayer({ setPlayer({
...player, ...player,
...resp.Data,
token: token token: token
}); });
navigate(`/lobby/`); navigate(`/lobby/`);
@ -45,9 +99,9 @@ const Login = () => {
<div style={boxStyle2}> <div style={boxStyle2}>
<Cascader defaultValue={[BusinessEnum.ServerType[BusinessEnum.ServerType.Internal_Dev]]} options={options} onChange={(v: string[]) => setType(+v[0])} /> <Cascader defaultValue={[BusinessEnum.ServerType[BusinessEnum.ServerType.Internal_Dev]]} options={options} onChange={(v: string[]) => setType(+v[0])} />
<br /> <br />
<TextArea rows={4} value={token} onChange={e => SetToken(e.target.value)} /> Token <TextArea rows={4} value={token} onChange={e => SetToken(e.target.value)} />
<br /> <br />
<Button type="primary" onClick={login}></Button> <Button type="primary" onClick={onClickLogin}></Button>
</div> </div>
</div> </div>
}</> }</>

View File

@ -0,0 +1,75 @@
import { Modal } from "antd";
import { ReactNode, createContext, useContext, useState } from "react";
type ModalProviderProps = {
children: ReactNode;
};
const ModalContext = createContext<IModal>(undefined);
export function useModal() {
return useContext(ModalContext);
}
export let modalObj: IModal = null;
export function ModalProvider({ children }: ModalProviderProps) {
const [isOpen, setIsOpen] = useState(false);
const [confirmData, setConfirmData] = useState<IConfirmMessageData>(undefined);
function handleOpen(data: IConfirmMessageData): void {
setConfirmData(data);
setIsOpen(true);
}
const handleClose = () => setIsOpen(false);
function handleConfirm(): void {
confirmData?.handleConfirm && confirmData?.handleConfirm();
handleClose();
}
function handleCancel(): void {
confirmData?.handleCancel && confirmData?.handleCancel();
handleClose();
}
const modal: IModal = modalObj = {
isOpen,
handleOpen,
handleClose,
confirmData,
};
return (
<ModalContext.Provider value={modal}>
{children}
<Modal title={confirmData?.title} open={isOpen} onOk={handleConfirm} onCancel={handleCancel} cancelText={confirmData?.cancelStr} style={{ top: "40%" }}>
<p>{confirmData?.content}</p>
</Modal>
</ModalContext.Provider>
);
}
export interface IModal {
isOpen: boolean;
handleOpen: (data: IConfirmMessageData) => void;
handleClose: () => void;
confirmData: IConfirmMessageData;
}
export interface IConfirmMessageData {
title?: string;
subTitle?: string;
content?: string;
enterStr?: string;
cancelStr?: string;
isShowCancel?: boolean;
isOrangeButton?: boolean;
isNeedClickConfirmClosePanel?: boolean;
handleCancel?: () => unknown;
handleConfirm?: (...args: any) => unknown;
render?: (data?: any) => ReactNode;
newRender?: (data?: any) => JSX.Element;
textAlign?: "center" | "left" | "right";
}

View File

@ -0,0 +1,15 @@
import { fallImg } from "@/utils";
interface AvatarProps extends React.ImgHTMLAttributes<HTMLImageElement> { }
/** 防止掉圖時 上個預設圖 */
const Image = (props: AvatarProps) => {
function onError(event: any) {
event.target.src = fallImg;
}
return (
<img {...props} onError={onError} />
);
};
export default Image;

View File

@ -1,5 +1,7 @@
import MainControlData from "@/Common/DataReceived/MainControlData";
import MainControl from "@/Common/MainControl/MainControl"; import MainControl from "@/Common/MainControl/MainControl";
import BusinessTypeSetting, { BusinessEnum } from "@/_BusinessTypeSetting/BusinessTypeSetting"; import BusinessTypeSetting, { BusinessEnum } from "@/_BusinessTypeSetting/BusinessTypeSetting";
import { GameData } from "@/define/gameData";
import { PlayerData } from "@/define/playerData"; import { PlayerData } from "@/define/playerData";
import { IGameItems } from "@/types"; import { IGameItems } from "@/types";
import { ReactNode, createContext, useContext, useState } from "react"; import { ReactNode, createContext, useContext, useState } from "react";
@ -15,21 +17,20 @@ export function useGameItems() {
export let gameObj: IGameItems = null; export let gameObj: IGameItems = null;
export function GameItemsProvider({ children }: GameItemsProviderProps) { export function GameItemsProvider({ children }: GameItemsProviderProps) {
const [gameId, setGameId] = useState<number>(null); const [player, setPlayer] = useState<PlayerData>(initPlayerData());
const [player, setPlayer] = useState<PlayerData>({ const [gameData, setGameData] = useState<GameData>(initGameData());
token: "",
});
const game: IGameItems = gameObj = { const game: IGameItems = gameObj = {
onLoad, onLoad,
gameId,
setGameId,
player, player,
setPlayer setPlayer,
gameData,
setGameData
}; };
async function onLoad(serverType: BusinessEnum.ServerType) { async function onLoad(serverType: BusinessEnum.ServerType) {
new MainControl(); new MainControl();
new MainControlData();
await Promise.all([ await Promise.all([
// 設定執行環境 // 設定執行環境
setBusinessType(serverType), setBusinessType(serverType),
@ -88,3 +89,27 @@ export function GameItemsProvider({ children }: GameItemsProviderProps) {
</GameItemsContext.Provider> </GameItemsContext.Provider>
); );
} }
function initPlayerData(): PlayerData {
return {
token: undefined,
aId: undefined,
f: undefined,
r: undefined,
rf: undefined,
name: undefined,
a: undefined,
m: 0,
lp: undefined,
tr: undefined,
lct: undefined
};
}
function initGameData(): GameData {
return {
slotData: [],
slotList: [],
nowSlotId: undefined,
};
}

View File

@ -0,0 +1,12 @@
export * from "../Slot1";
export * from "../Slot1201";
export * from "../Slot1305";
export * from "../Slot32";
export * from "../Slot34";
export * from "../Slot48";
export * from "../Slot50";
export * from "../Slot62";
export * from "../Slot64";
export * from "../Slot65";
export * from "../Slot66";
export * from "./SlotBase";

View File

@ -0,0 +1,192 @@
import CSMessage from "@/Common/Message/CSMessage";
import { INetResponse } from "@/Engine/CatanEngine/NetManagerV2/Core/INetResponse";
import { CommonSlotSpinRequest } from "../Request/CommonSlotRequest";
export class SlotBase {
//#region public
// public get ID(): number { return this._bj_Casino_Bot.LobbyScript.Slot; }
public get FreeID(): number { return 1; }
public get FreeCount(): number { return +this.GameRunData["free"][1]; }
public get HasChoiceFreeGame(): boolean { return false; }
public get SlotReqRespIsCount(): boolean { return false; }
public get HasRetriggerFreeSpin(): boolean { return false; }
public GameRunData: JSON = null;
//#endregion
//#region protected
protected id: number;
// protected _bj_Slot: BJ_Casino_Bot_Slot;
//#endregion
//#region Lifecycle
/**
*
*/
constructor(id: number) {
this.id = id;
this.onLoad();
}
public async onLoad(): Promise<void> {
//
}
//#endregion
//#region Custom
public *Spin(bet: number): IterableIterator<any> {
let gameRunData: JSON = null;
let req: CommonSlotSpinRequest = new CommonSlotSpinRequest(this.id, bet);
yield req.SendAsync();
let resp: INetResponse<JSON> = req.Result;
if (resp.IsValid) {
gameRunData = this.GameRunData = resp.Data;
} else {
CSMessage.NetError(resp.Method, resp.Status);
}
}
// public async Spin(): Promise<void> {
// let gameRunData: JSON = null;
// let req: CommonSlotSpinRequest = new CommonSlotSpinRequest(this.ID, this._bj_Slot.NowBet);
// await req.SendAsync();
// let resp: INetResponse<JSON> = req.Result;
// if (resp.IsValid) {
// gameRunData = this.GameRunData = resp.Data;
// } else {
// CSMessage.NetError(resp.Method, resp.Status);
// }
// let money: number = gameRunData["money"] ? +gameRunData["money"] : 0;
// let winMoney: number = 0;
// let winMoneyLog: string = "";
// let freeLog: string = "";
// let resources: any[] = gameRunData["get"];
// let free: any = gameRunData["free"];
// let choiceFreeGame: boolean = gameRunData["rs"] === 0;
// if (resources) {
// winMoney = this._getWinMoney(resources);
// }
// if (choiceFreeGame && this.HasChoiceFreeGame) {
// free = true;
// }
// if (free) {
// let freeCount: number = await this._getFreeCount();
// let fswinMoney: number = 0;
// let fsmoney: number = 0;
// [freeCount, fswinMoney, fsmoney] = await this.FreeSpin(freeCount);
// if (fsmoney > 0) {
// money = fsmoney;
// }
// if (fswinMoney > 0) {
// winMoney = fswinMoney;
// }
// freeLog = `, hasFree: ${freeCount}`;
// }
// if (winMoney > 0) {
// winMoneyLog = `, winMoney: ${winMoney}`;
// }
// this._bj_Casino_Bot.UserData.Money = money;
// this._bj_Casino_Bot.SetUI();
// let ratio: number = winMoney > 0 ? NumberEx.divide(winMoney, this._bj_Slot.NowBet) : 0;
// if (this._bj_Slot.IsRatioStop.value && ratio >= this._bj_Slot.RatioStop.value) {
// this._bj_Slot.OnclickStop();
// }
// if (this._bj_Slot.IsCountStop.value && this._bj_Slot.CountStop.value === 0) {
// this._bj_Slot.OnclickStop();
// }
// if (ratio > 100) {
// this._bj_Casino_Bot.AddLog(`Slot${this.ID} Spin Bet: ${this._bj_Slot.NowBet}, Ratio: ${ratio}, Money: ${money}${winMoneyLog}${freeLog}`);
// }
// // this._bj_Casino_Bot.AddLog(`Slot${this.ID} Spin Bet: ${this._bj_Slot.NowBet}, Money: ${money}${winMoneyLog}${freeLog}`);
// }
// public async FreeSpin(freeCount: number): Promise<number[]> {
// let fswinMoney: number = 0;
// let fsmoney: number = 0;
// for (let i: number = 0; i < freeCount; i++) {
// let gameRunData: JSON = null;
// let req: CommonSlotFgSpinRequest = new CommonSlotFgSpinRequest(this.ID);
// await req.SendAsync();
// let resp: INetResponse<JSON> = req.Result;
// if (resp.IsValid) {
// gameRunData = resp.Data;
// if (this.HasRetriggerFreeSpin) {
// let retriggercount: number = this._getRetriggerFreeSpinCount(gameRunData);
// freeCount += retriggercount;
// }
// if (i === freeCount - 1) {
// let resources: any[] = gameRunData["get"];
// if (resources) {
// fswinMoney = this._getWinMoney(resources);
// }
// fsmoney = gameRunData["money"] ? +gameRunData["money"] : 0;
// }
// } else {
// CSMessage.NetError(resp.Method, resp.Status);
// }
// // this._bj_Casino_Bot.AddLog(`Slot${this.ID} FreeSpin MaxCount: ${freeCount}, Count: ${i + 1}`);
// }
// return [freeCount, fswinMoney, fsmoney];
// }
// protected _getWinMoney(resources: any[]): number {
// for (let i: number = 0; i < resources.length; i++) {
// const resource: any[] = resources[i];
// if (resource[0] === 1) {
// return resource[1];
// }
// }
// return 0;
// }
// protected _getRetriggerFreeSpinCount(gameRunData: JSON): number {
// if (gameRunData["free"]) {
// return gameRunData["free"][1];
// }
// return 0;
// }
// protected async _getFreeCount(): Promise<number> {
// if (this.HasChoiceFreeGame) {
// return await this._getChoiceFreeCount();
// } else {
// return this.FreeCount;
// }
// }
// protected async _getChoiceFreeCount(): Promise<number> {
// let id: number = this._getFreeID();
// let request: Slot_ChoiceRequest = new Slot_ChoiceRequest(id);
// await request.SendAsync(true);
// var result: INetResponse<number> = request.Result;
// if (result.IsValid) {
// if (this.SlotReqRespIsCount) {
// return result.Data;
// } else {
// let slotNameSetting: string = CSSettingsV3.Slotset[this.ID].NameSetting;
// let free_info_id: number = result.Data;
// let slotSetting: any = CSSettingsV3[slotNameSetting];
// let freeInfo: any = slotSetting.FreeInfo;
// let count: number = freeInfo[free_info_id].Spins;
// return count;
// }
// }
// return 0;
// }
// protected _getFreeID(): number {
// return this.FreeID;
// }
//#endregion
}
export default SlotBase;

View File

@ -0,0 +1,78 @@
//=======================================================================================
import { NetRequestSD } from "../../../Engine/CatanEngine/NetManagerV2/NetRequestSD";
/**共用MAIN SPIN協定 */
export interface SpinRequest {
pay: number;
}
export class CommonSlotSpinRequest extends NetRequestSD<SpinRequest, JSON> {
private _id: number = 0;
get Method(): string {
return "slot" + this._id + ".spin";
}
constructor(slotId: number, totalBet: number) {
super();
this._id = slotId;
this.Data = {
pay: totalBet,
};
}
}
//=======================================================================================
export class CommonSlotFgSpinRequest extends NetRequestSD<any, JSON> {
private _id: number = 0;
get Method(): string {
return "slot" + this._id + ".fgspin";
}
constructor(slotId: number) {
super();
this._id = slotId;
}
}
//=======================================================================================
export class CommonSlotCollectUnlockRequest extends NetRequestSD<any, JSON> {
private _id: number = 0;
get Method(): string {
return "collect" + this._id + ".unlock";
}
constructor(slotId: number, grid: number) {
super();
this._id = slotId;
this.Data = {
Grid: grid,
};
}
}
//=======================================================================================
export class CommonSlotCollectSpinRequest extends NetRequestSD<any, JSON> {
private _id: number = 0;
get Method(): string {
return "collect" + this._id + ".spin";
}
constructor(slotId: number) {
super();
this._id = slotId;
}
}
//=======================================================================================
/**賓果 ChangeSet協定 */
export class CommonBingoChangeSet extends NetRequestSD<any, JSON> {
private _id: number = 0;
get Method(): string {
return "slot" + this._id + ".chset";
}
constructor(slotId: number) {
super();
this._id = slotId;
}
}
//=======================================================================================

View File

@ -0,0 +1,78 @@
import { NetRequestSD } from "@/Engine/CatanEngine/NetManagerV2/NetRequestSD";
//=======================================================================================
interface ChoiceRequest {
}
/**五龍選擇協定 */
export class Slot_ChoiceRequest extends NetRequestSD<ChoiceRequest, number> {
get Method(): string {
return "slot.req";
}
constructor(id: number) {
super();
this.Data = id;
}
}
//=======================================================================================
interface GmRequest {
pay: number;
slot: string;
type: number;
}
export class Slot_GMRequest extends NetRequestSD<GmRequest, JSON> {
get Method(): string {
return "slot.gm";
}
get MethodBack(): string {
return "slot" + this._id + ".spin";
}
private _id: number = 1;
constructor(id: number, totalBet: number, symbols: string, type: number) {
super();
this._id = id;
this.Data = {
pay: totalBet,
slot: symbols,
type: type,
};
}
}
//=======================================================================================
export class Bingo_GMRequest extends NetRequestSD<GmRequest, JSON> {
get Method(): string {
return "bingo.gm";
}
get MethodBack(): string {
return "slot" + this._id + ".spin";
}
private _id: number = 0;
constructor(id: number, totalBet: number, bingos: string, type: number) {
super();
this._id = id;
this.Data = {
pay: totalBet,
slot: bingos,
type: type,
};
}
}
//=======================================================================================
/**前後端金額對不上協定 */
export class Slot_ErrorRequest extends NetRequestSD<any, JSON> {
get Method(): string {
return "slot.error";
}
constructor() {
super();
}
}
//=======================================================================================

13
src/define/Game/Slot1.ts Normal file
View File

@ -0,0 +1,13 @@
import SlotBase from "./Base/SlotBase";
export class Slot1 extends SlotBase {
//#region public
public get ID(): number { return 1; }
public get HasRetriggerFreeSpin(): boolean { return true; }
//#endregion
}
export default Slot1;

View File

@ -0,0 +1,13 @@
import SlotBase from "./Base/SlotBase";
export class Slot1201 extends SlotBase {
//#region public
public get FreeCount(): number { return +this.GameRunData["free"]; }
public get HasRetriggerFreeSpin(): boolean { return true; }
//#endregion
}
export default Slot1201;

View File

@ -0,0 +1,13 @@
import SlotBase from "./Base/SlotBase";
export class Slot1305 extends SlotBase {
//#region public
public get ID(): number { return 1305; }
public get HasRetriggerFreeSpin(): boolean { return true; }
//#endregion
}
export default Slot1305;

13
src/define/Game/Slot32.ts Normal file
View File

@ -0,0 +1,13 @@
import SlotBase from "./Base/SlotBase";
export class Slot32 extends SlotBase {
//#region public
public get ID(): number { return 32; }
public get HasRetriggerFreeSpin(): boolean { return true; }
//#endregion
}
export default Slot32;

13
src/define/Game/Slot34.ts Normal file
View File

@ -0,0 +1,13 @@
import SlotBase from "./Base/SlotBase";
export class Slot34 extends SlotBase {
//#region public
public get ID(): number { return 34; }
public get HasRetriggerFreeSpin(): boolean { return true; }
//#endregion
}
export default Slot34;

16
src/define/Game/Slot48.ts Normal file
View File

@ -0,0 +1,16 @@
import SlotBase from "./Base/SlotBase";
export class Slot48 extends SlotBase {
//#region public
public get ID(): number { return 48; }
public get FreeID(): number { return 3; }
public get HasChoiceFreeGame(): boolean { return true; }
public get SlotReqRespIsCount(): boolean { return true; }
public get HasRetriggerFreeSpin(): boolean { return true; }
//#endregion
}
export default Slot48;

15
src/define/Game/Slot50.ts Normal file
View File

@ -0,0 +1,15 @@
import SlotBase from "./Base/SlotBase";
export class Slot50 extends SlotBase {
//#region public
public get ID(): number { return 50; }
public get FreeID(): number { return 1; }
public get HasChoiceFreeGame(): boolean { return true; }
public get HasRetriggerFreeSpin(): boolean { return true; }
//#endregion
}
export default Slot50;

31
src/define/Game/Slot62.ts Normal file
View File

@ -0,0 +1,31 @@
import SlotBase from "./Base/SlotBase";
export class Slot62 extends SlotBase {
//#region public
public get ID(): number { return 62; }
public get FreeID(): number { return 3; }
public get HasChoiceFreeGame(): boolean { return true; }
public get HasRetriggerFreeSpin(): boolean { return true; }
//#endregion
//#region Custom
protected _getFreeID(): number {
let gameRunData: JSON = this.GameRunData;
let scatterAmount: number = gameRunData["scatter"][0][0].length;
if (scatterAmount > 4) {
return 5;
} else if (scatterAmount > 3) {
return 4;
} else {
return this.FreeID;
}
}
//#endregion
}
export default Slot62;

13
src/define/Game/Slot64.ts Normal file
View File

@ -0,0 +1,13 @@
import SlotBase from "./Base/SlotBase";
export class Slot64 extends SlotBase {
//#region public
public get ID(): number { return 64; }
public get HasRetriggerFreeSpin(): boolean { return true; }
//#endregion
}
export default Slot64;

13
src/define/Game/Slot65.ts Normal file
View File

@ -0,0 +1,13 @@
import SlotBase from "./Base/SlotBase";
export class Slot65 extends SlotBase {
//#region public
public get ID(): number { return 65; }
public get HasRetriggerFreeSpin(): boolean { return true; }
//#endregion
}
export default Slot65;

13
src/define/Game/Slot66.ts Normal file
View File

@ -0,0 +1,13 @@
import SlotBase from "./Base/SlotBase";
export class Slot66 extends SlotBase {
//#region public
public get ID(): number { return 66; }
public get HasRetriggerFreeSpin(): boolean { return true; }
//#endregion
}
export default Slot66;

9
src/define/GameData.ts Normal file
View File

@ -0,0 +1,9 @@
interface GameData {
slotData: SlotData[];
slotList: number[];
nowSlotId: number;
}
export type SlotData = [componyID: number, slotId: number, vip: number, status: number, tag: number]
export { type GameData };

View File

@ -1,5 +1,15 @@
interface PlayerData { interface PlayerData {
token: string; token: string;
aId: number;
f: number;
r: number;
rf: number;
name: string;
a: number;
m: number;
lp: number;
tr: number;
lct: number;
} }
export { type PlayerData }; export { type PlayerData };

View File

@ -0,0 +1,311 @@
import { NetRequestSD } from "@/Engine/CatanEngine/NetManagerV2/NetRequestSD";
import { NetRequest } from "../../Engine/CatanEngine/NetManagerV2/NetRequest";
// =======================================================================================
/** 通用回傳SERVER創的遊戲帳號 */
export interface CommonAccountResponse {
id: string;
pw: string;
}
// =======================================================================================
interface CreateResquest {
p: number;
d: string;
}
/** 直接玩(訪客給SERVER創帳號) */
export class AccountCreateRequest extends NetRequest<CreateResquest, CommonAccountResponse> {
get Method(): string {
return "account.create";
}
/*constructor() {
super();
this.Data = {
p: Config.GetRunDevice(),
d: LocalStorageData.Instance.ComboDeviceID,
};
}*/
}
// =======================================================================================
interface LoginRequest {
// p: number;
// d: string;
// fcm_token: string;
id: number;
pw: string;
pl: number;
// ver: string;
}
interface LoginResponse {
pr: string;
cu: string;
}
/** 通用登入 */
export class AccountLoginRequest extends NetRequest<LoginRequest, LoginResponse> {
get Method(): string {
return "account.login";
}
constructor(account: string, password: string, partner: string) {
super();
this.Data = {
id: +account,
pw: password,
pl: 3
};
if (partner) {
this.Data["pn"] = partner;
}
}
}
interface SDLoginRequest {
token: string;
}
export class SDAccountLoginRequest extends NetRequestSD<SDLoginRequest, LoginResponse> {
get Method(): string {
return "account.login";
}
constructor(token: string) {
super();
this.Data = {
token: token,
};
}
}
// =======================================================================================
interface CustomResquest {
a: string;
pw: string;
}
/** 自定帳號榜定 */
export class CustomBindRequest extends NetRequest<CustomResquest, null> {
get Method(): string {
return "register.account_bind";
}
constructor(account: string, password: string) {
super();
this.Data = {
a: account,
pw: password,
};
}
}
/** 自定帳號登入(回傳SERVER帳號) */
export class CustomLoginRequest extends NetRequest<string, CommonAccountResponse> {
get Method(): string {
return "register.account_login";
}
constructor(account: string) {
super();
this.Data = account;
}
}
// =======================================================================================
interface FBResquest {
t: string;
}
/** FB綁定 */
export class FBBindRequest extends NetRequest<FBResquest, null> {
get Method(): string {
return "register.fb_bind";
}
constructor(token: string) {
super();
this.Data = {
t: token,
};
}
}
/** FB登入(回傳SERVER帳號) */
export class FBLoginRequest extends NetRequest<FBResquest, CommonAccountResponse> {
get Method(): string {
return "register.fb_login";
}
constructor(token: string) {
super();
this.Data = {
t: token,
};
}
}
// =======================================================================================
interface GoogleResquest {
c: string;
}
/** GOOGLE綁定 */
export class GoogleBindRequest extends NetRequest<GoogleResquest, null> {
get Method(): string {
return "register.google_bind";
}
constructor(token: string) {
super();
this.Data = {
c: token,
};
}
}
/** GOOGLE登入(回傳SERVER帳號) */
export class GoogleLoginRequest extends NetRequest<GoogleResquest, CommonAccountResponse> {
get Method(): string {
return "register.google_login";
}
constructor(token: string) {
super();
this.Data = {
c: token,
};
}
}
// =======================================================================================
interface AppleResquest {
c: string;
}
/** APPEL綁定 */
export class AppleBindRequest extends NetRequest<AppleResquest, null> {
get Method(): string {
return "register.apple_bind";
}
constructor(token: string) {
super();
this.Data = {
c: token,
};
}
}
/** APPLE登入(回傳SERVER帳號) */
export class AppleLoginRequest extends NetRequest<AppleResquest, CommonAccountResponse> {
get Method(): string {
return "register.apple_login";
}
constructor(token: string) {
super();
this.Data = {
c: token,
};
}
}
// =======================================================================================
/** 電話確認 */
export class PhoneCheck extends NetRequest<PhoneCodeRequest, string> {
get Method(): string {
return "register.phone_check";
}
constructor(p: string) {
super();
this.Data = {
p: p
};
}
}
/** 電話驗證 */
export interface PhoneCodeRequest {
p: string;
}
export class PhoneGet extends NetRequest<PhoneCodeRequest, string> {
get Method(): string {
return "register.phone_code";
}
constructor(p: string) {
super();
this.Data = {
p: p
};
}
}
export interface PhoneBindRequest {
c: string;
}
export class PhoneBind extends NetRequest<PhoneBindRequest, string> {
get Method(): string {
return "register.phone_bind";
}
constructor(c: string) {
super();
this.Data = {
c: c
};
}
}
// =======================================================================================
/** 旗標更新 */
export class FlagOpenAdd extends NetRequest<number, string> {
get Method(): string {
return "flag.open_add";
}
constructor(type: number) {
super();
this.Data = type;
}
}
// ========================================================================================
export interface ForgotInfo {
a: string;
p: string;
}
/** 忘記密碼 */
export class ForgotPassword extends NetRequest<ForgotInfo, null> {
get Method(): string {
return "register.account_forget";
}
constructor(account: string, phone: string) {
super();
this.Data = {
a: account,
p: phone,
};
}
}
export interface ChangePasswordInfo {
a: string;
opw: string;
pw: string;
}
/** 修改密碼 */
export class ChangePassword extends NetRequest<ChangePasswordInfo, null> {
get Method(): string {
return "register.account_change";
}
constructor(account: string, prePassword: string, password: string) {
super();
this.Data = {
a: account,
opw: prePassword,
pw: password
};
}
}
// =======================================================================================
export interface LineBindResponse {
n: string;
}
export class LineBind extends NetRequest<string, LineBindResponse> {
get Method(): string {
return "register.line_bind";
}
constructor(token: string) {
super();
this.Data = token;
}
}
/** LINE登入(回傳SERVER帳號) */
export class LineLoginRequest extends NetRequest<string, CommonAccountResponse> {
get Method(): string {
return "register.line_login";
}
constructor(token: string) {
super();
this.Data = token;
}
}
// =======================================================================================

View File

@ -3,6 +3,15 @@ import { NetRequest } from "@/Engine/CatanEngine/NetManagerV2/NetRequest";
// #region Request // #region Request
export type RpcGamInfoResponse = JSON
export class GameInfoRequest extends NetRequest<null, RpcGamInfoResponse> {
get Method(): string {
return "game.info";
}
constructor() {
super();
}
}
export type RpcGameLaunchRequest = number[] export type RpcGameLaunchRequest = number[]
export type RpcGameLaunchResponse = string export type RpcGameLaunchResponse = string

View File

@ -0,0 +1,83 @@
import { NetRequestSD } from "@/Engine/CatanEngine/NetManagerV2/NetRequestSD";
import { LanguageManager } from "@/FormTableExt/Manage/Language/LanguageManager";
import { NetRequest } from "../../Engine/CatanEngine/NetManagerV2/NetRequest";
//=======================================================================================
/**取得歷史紀錄網頁網址協定 */
export class HistoryRequest extends NetRequest<any, JSON> {
get Method(): string {
return "history.url";
}
constructor(slotId: number) {
super();
this.Data = {
slot: slotId,
lang: LanguageManager.UseLanguageUrlStr,
};
}
}
//=======================================================================================
export class SlotInRequest extends NetRequestSD<any, JSON> {
get Method(): string {
return "slot.in";
}
constructor(slotid: number, hall: number = 0, uid: number = 0) {
super();
this.Data = {
id: slotid
};
}
}
//=======================================================================================
export class SlotOutRequest extends NetRequest<null, null> {
get Method(): string {
return "slot.out";
}
}
//=======================================================================================
export class FishOutRequest extends NetRequest<null, null> {
get Method(): string {
return "fish.out";
}
}
//=======================================================================================
export class TableOutRequest extends NetRequest<null, null> {
get Method(): string {
return "table.out";
}
}
//=======================================================================================
export class Table3002OutRequest extends NetRequest<any, JSON> {
get Method(): string {
return "two_sicbo.leave";
}
constructor() {
super();
}
}
//=======================================================================================
export class Table3003OutRequest extends NetRequest<any, JSON> {
get Method(): string {
return "nine_sicbo.leave";
}
constructor() {
super();
}
}
//=======================================================================================
export class Table3012OutRequest extends NetRequest<any, JSON> {
get Method(): string {
return "triple_baccarat.leave";
}
constructor() {
super();
}
}
//=======================================================================================
export class PinBallOutRequest extends NetRequest<null, null> {
get Method(): string {
return "pinball.out";
}
}
//=======================================================================================

View File

@ -12,6 +12,7 @@ import { BaseEnumerator } from "./Engine/CatanEngine/CoroutineV2/Core/BaseEnumer
import Game from "./UI/Game"; import Game from "./UI/Game";
import Lobby from "./UI/Lobby"; import Lobby from "./UI/Lobby";
import Login from "./UI/Login"; import Login from "./UI/Login";
import { ModalProvider } from "./UIControl/ModalContext";
import "./index.css"; import "./index.css";
import "./utils/catan"; import "./utils/catan";
@ -35,7 +36,9 @@ const browserRouter: Router = createBrowserRouter(router);
const hashRouter: Router = createHashRouter(router); const hashRouter: Router = createHashRouter(router);
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
<GameItemsProvider> <ModalProvider>
<RouterProvider router={browserRouter} /> <GameItemsProvider>
</GameItemsProvider> <RouterProvider router={browserRouter} />
</GameItemsProvider>
</ModalProvider>
); );

View File

@ -1,10 +1,11 @@
import { BusinessEnum } from "@/_BusinessTypeSetting/BusinessTypeSetting"; import { BusinessEnum } from "@/_BusinessTypeSetting/BusinessTypeSetting";
import { GameData } from "@/define/gameData";
import { PlayerData } from "@/define/playerData"; import { PlayerData } from "@/define/playerData";
export interface IGameItems { export interface IGameItems {
onLoad: (serverType: BusinessEnum.ServerType) => Promise<void>; onLoad: (serverType: BusinessEnum.ServerType) => Promise<void>;
gameId: number;
setGameId: (v: number) => void;
player: PlayerData; player: PlayerData;
setPlayer: (v: PlayerData) => void; setPlayer: (v: PlayerData) => void;
gameData: GameData;
setGameData: (v: GameData) => void;
} }

View File

@ -1,652 +1 @@
import { ResourceItemType } from "@/Common/ResourceItem/ResourceItemType"; export const fallImg: string = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMIAAADDCAYAAADQvc6UAAABRWlDQ1BJQ0MgUHJvZmlsZQAAKJFjYGASSSwoyGFhYGDIzSspCnJ3UoiIjFJgf8LAwSDCIMogwMCcmFxc4BgQ4ANUwgCjUcG3awyMIPqyLsis7PPOq3QdDFcvjV3jOD1boQVTPQrgSkktTgbSf4A4LbmgqISBgTEFyFYuLykAsTuAbJEioKOA7DkgdjqEvQHEToKwj4DVhAQ5A9k3gGyB5IxEoBmML4BsnSQk8XQkNtReEOBxcfXxUQg1Mjc0dyHgXNJBSWpFCYh2zi+oLMpMzyhRcASGUqqCZ16yno6CkYGRAQMDKMwhqj/fAIcloxgHQqxAjIHBEugw5sUIsSQpBobtQPdLciLEVJYzMPBHMDBsayhILEqEO4DxG0txmrERhM29nYGBddr//5/DGRjYNRkY/l7////39v///y4Dmn+LgeHANwDrkl1AuO+pmgAAADhlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAAqACAAQAAAABAAAAwqADAAQAAAABAAAAwwAAAAD9b/HnAAAHlklEQVR4Ae3dP3PTWBSGcbGzM6GCKqlIBRV0dHRJFarQ0eUT8LH4BnRU0NHR0UEFVdIlFRV7TzRksomPY8uykTk/zewQfKw/9znv4yvJynLv4uLiV2dBoDiBf4qP3/ARuCRABEFAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghgg0Aj8i0JO4OzsrPv69Wv+hi2qPHr0qNvf39+iI97soRIh4f3z58/u7du3SXX7Xt7Z2enevHmzfQe+oSN2apSAPj09TSrb+XKI/f379+08+A0cNRE2ANkupk+ACNPvkSPcAAEibACyXUyfABGm3yNHuAECRNgAZLuYPgEirKlHu7u7XdyytGwHAd8jjNyng4OD7vnz51dbPT8/7z58+NB9+/bt6jU/TI+AGWHEnrx48eJ/EsSmHzx40L18+fLyzxF3ZVMjEyDCiEDjMYZZS5wiPXnyZFbJaxMhQIQRGzHvWR7XCyOCXsOmiDAi1HmPMMQjDpbpEiDCiL358eNHurW/5SnWdIBbXiDCiA38/Pnzrce2YyZ4//59F3ePLNMl4PbpiL2J0L979+7yDtHDhw8vtzzvdGnEXdvUigSIsCLAWavHp/+qM0BcXMd/q25n1vF57TYBp0a3mUzilePj4+7k5KSLb6gt6ydAhPUzXnoPR0dHl79WGTNCfBnn1uvSCJdegQhLI1vvCk+fPu2ePXt2tZOYEV6/fn31dz+shwAR1sP1cqvLntbEN9MxA9xcYjsxS1jWR4AIa2Ibzx0tc44fYX/16lV6NDFLXH+YL32jwiACRBiEbf5KcXoTIsQSpzXx4N28Ja4BQoK7rgXiydbHjx/P25TaQAJEGAguWy0+2Q8PD6/Ki4R8EVl+bzBOnZY95fq9rj9zAkTI2SxdidBHqG9+skdw43borCXO/ZcJdraPWdv22uIEiLA4q7nvvCug8WTqzQveOH26fodo7g6uFe/a17W3+nFBAkRYENRdb1vkkz1CH9cPsVy/jrhr27PqMYvENYNlHAIesRiBYwRy0V+8iXP8+/fvX11Mr7L7ECueb/r48eMqm7FuI2BGWDEG8cm+7G3NEOfmdcTQw4h9/55lhm7DekRYKQPZF2ArbXTAyu4kDYB2YxUzwg0gi/41ztHnfQG26HbGel/crVrm7tNY+/1btkOEAZ2M05r4FB7r9GbAIdxaZYrHdOsgJ/wCEQY0J74TmOKnbxxT9n3FgGGWWsVdowHtjt9Nnvf7yQM2aZU/TIAIAxrw6dOnAWtZZcoEnBpNuTuObWMEiLAx1HY0ZQJEmHJ3HNvGCBBhY6jtaMoEiJB0Z29vL6ls58vxPcO8/zfrdo5qvKO+d3Fx8Wu8zf1dW4p/cPzLly/dtv9Ts/EbcvGAHhHyfBIhZ6NSiIBTo0LNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiEC/wGgKKC4YMA4TAAAAABJRU5ErkJggg==";
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 = <T>(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<number, ItemCodeList>,
): PriceList[] {
if (itemCodeList) {
arr = arr.filter((item) => itemCodeList.has(item[0]));
}
const MyCardPriceRefMap = new Map<number, number>(
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<string, number[]>,
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<string, number[]> {
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<string, number[]>) {
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<any> {
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<any, any> = null;
axios.get(fileUrl).then((res: AxiosResponse<any, any>) => {
loadJsonProcess(null, res, formname);
resp = res;
});
while (!resp) {
await sleep(0.1);
}
}
function loadJsonProcess(
err: any,
res: AxiosResponse<any, any>,
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, "<br>");
}
/**
*
* @param str
* @param showBytes (12BYTES)
* @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);
}
}

View File

@ -1,235 +1,26 @@
import MainControl from "@/Common/MainControl/MainControl"; import CSMessage from "@/Common/Message/CSMessage";
import { CoroutineV2 } from "@/Engine/CatanEngine/CoroutineV2/CoroutineV2"; import { INetResponse } from "@/Engine/CatanEngine/NetManagerV2/Core/INetResponse";
import { TTeamBattleData } from "@/UI/RouterPage/GameContent/GameContentUtils";
import { Cocos } from "@/assets/VueScript/Cocos";
import { CommonEventCallBack } from "@/assets/VueScript/CocosVueScript";
import GameData_Cocos from "@/assets/VueScript/share/GameData_Cocos";
import { GiftCallBack, GiftEventEnum, PanelType, UpdateOneListInfo } from "@/components/ModalContent/TxnModal/txnUtils";
import { gameObj } from "@/context/GameItemsContext"; import { gameObj } from "@/context/GameItemsContext";
import { LocalStorage } from "@/define"; import { GameInfoRequest } from "@/define/Request/GameRequest";
import { EActivitySyncType, RpcActivityComSyncResponse, TActivityComSyncData, TActivitySyncData } from "@/define/Request/ActivityRequest";
import { ResponseBackpackInfo } from "@/define/Request/BackpackRequest";
import { FriendRequest } from "@/define/Request/FriendRequest";
import { ProfileRequest } from "@/define/Request/ProfileRequest";
import { TxnRequest } from "@/define/Request/TxnRequest";
import { VipRequest } from "@/define/Request/VIPRequest";
import Player from "@/modules/player";
import { State } from "@/modules/player/define/data";
import { BackpackItemData } from "@/modules/player/define/data/backpack";
import { CommonEventType } from ".";
let SN: number = 0; export function* gameSync(): IterableIterator<any> {
const { gameData, setGameData } = gameObj;
export function profileInfo(data: ProfileRequest.InfoResponse): void { let req: GameInfoRequest = new GameInfoRequest();
const playerData: State = Player.data.getState(); yield req.SendAsync();
if (data.aId !== playerData.account.aId) { let resp: INetResponse<JSON> = req.Result;
/** 0是UUID 1[0]是機台相關資訊 1[1]排序 */
if (resp.IsValid) {
const respD: any = resp.Data;
} else {
CSMessage.NetError(resp.Method, resp.Status, "Get GameInfoRequest Error");
return; return;
} }
playerData.account.name = data.name; const data: any = JSON.parse(resp.Data[1]);
playerData.account.message = data.msg; const slotData = data[0];
playerData.account.phone = data.phone; const slotList: number[] = data[1][0];
playerData.account.money = data.money; setGameData({
playerData.vip.level = data.vip; ...gameData,
Player.data.setState(playerData); slotData,
} slotList
});
export function vipInfo(data: VipRequest.InfoResponse): void {
const playerData: State = Player.data.getState();
playerData.vip.level = data.level;
playerData.vip.totalBet = data.bet;
playerData.vip.totalCharge = data.sv;
playerData.vip.rich = data.rich;
playerData.vip.et = data.et;
Player.data.setState(playerData);
}
export function friendAllowList(data: FriendRequest.ListFriendData): void {
const playerData: State = Player.data.getState();
playerData.account.allowList = data;
Player.data.setState(playerData);
}
export function friendDenyList(data: FriendRequest.ListFriendData): void {
const playerData: State = Player.data.getState();
playerData.account.denyList = data;
Player.data.setState(playerData);
}
export function activityComSync(data: RpcActivityComSyncResponse): void {
const { teamBattleData, setTeamBattleData } = gameObj;
for (let i = 0; i < data.length; i++) {
const activityComSyncData: TActivityComSyncData = data[i];
const [id, activitySyncDatas] = activityComSyncData;
// for (let j = 0; j < teamBattleData.length; j++) {
// const teamBattle: TTeamBattleData = teamBattleData[j];
// const [teamBattleId, , ,] = teamBattle;
// if (id === teamBattleId) {
// for (let k = 0; k < activitySyncDatas.length; k++) {
// const activitySyncData: TActivitySyncData = activitySyncDatas[k];
// const [type, value] = activitySyncData;
// switch (type) {
// case EActivitySyncType.IsOpen: {
// teamBattleData[j][1] = -1;
// setTeamBattleData(teamBattleData);
// break;
// }
// case EActivitySyncType.Sync: {
// teamBattleData[j][1] = value;
// setTeamBattleData(teamBattleData);
// break;
// }
// case EActivitySyncType.Task: {
// break;
// }
// default:
// break;
// }
// }
// break;
// }
// }
for (let j = 0; j < activitySyncDatas.length; j++) {
const activitySyncData: TActivitySyncData = activitySyncDatas[j];
const [type, value] = activitySyncData;
switch (type) {
case EActivitySyncType.IsOpen: {
// for (let k = 0; k < teamBattleData.length; k++) {
// const teamBattle: TTeamBattleData = teamBattleData[k];
// const [teamBattleId, , ,] = teamBattle;
// if (id === teamBattleId) {
// teamBattleData[j][1] = value;
// setTeamBattleData(teamBattleData);
// break;
// }
// }
break;
}
case EActivitySyncType.Sync: {
for (let k = 0; k < teamBattleData.length; k++) {
const teamBattle: TTeamBattleData = teamBattleData[k];
const [teamBattleId, , ,] = teamBattle;
if (id === teamBattleId) {
teamBattleData[j][1] = value;
setTeamBattleData(teamBattleData);
break;
}
}
break;
}
case EActivitySyncType.Task: {
break;
}
default:
break;
}
}
}
CommonEventCallBack.DispatchCallback(CommonEventType.ActivityReRender, null);
}
export function backpackInfo(data: ResponseBackpackInfo[], isAdd: boolean = false): void {
const playerData: State = Player.data.getState();
const backpackList: BackpackItemData[] = isAdd ? playerData.backpack.Copy() : [];
for (let i = 0; i < data.length; i++) {
const backpackServerData = data[i];
const id: number = backpackServerData[1][0];
const count: number = backpackServerData[1][1];
for (let j = 0; j < count; j++) {
const backpackClientData: BackpackItemData = {
SN: SN,
ResourceType: backpackServerData[0],
ID: id,
Viewed: false,
};
backpackList.push(backpackClientData);
SN++;
}
}
if (!isAdd) {
const oldBackpackListStr: string = localStorage.getItem(LocalStorage.Key.Backpack);
if (oldBackpackListStr) {
const oldBackpackList: BackpackItemData[] = JSON.parse(oldBackpackListStr);
for (let i = 0; i < backpackList.length; i++) {
const backpack: BackpackItemData = backpackList[i];
for (let j = 0; j < oldBackpackList.length; j++) {
const oldBackpack: BackpackItemData = oldBackpackList[j];
if (backpack.ID === oldBackpack.ID && backpack.ResourceType === oldBackpack.ResourceType) {
backpackList[i].Viewed = oldBackpack.Viewed;
oldBackpackList.splice(j, 1);
break;
}
}
}
}
}
playerData.backpack = backpackList;
localStorage.setItem(LocalStorage.Key.Backpack, JSON.stringify(playerData.backpack));
Player.data.setState(playerData);
const totalUnreadCount: number = playerData.backpack.filter((item) => !item.Viewed).length;
if (!MainControl.Instance.IsInGame) {
Cocos.CocosEventListener.DispatchCallback(GameData_Cocos.CELT.SetBackpackUnreadCount, totalUnreadCount);
}
}
export function txnNew(data: TxnRequest.TxnInfo): void {
const playerData = Player.data.getState();
const centerList = playerData.txn.centerList.slice();
centerList.push(data);
playerData.txn.centerList = centerList;
GiftCallBack.DispatchCallback(GiftEventEnum.ReFlash, null);
Cocos.CocosEventListener.DispatchCallback(GameData_Cocos.CELT.SetTxnUnreadCount, playerData.txn.centerList.length);
Player.data.setState(playerData);
}
export function txnCenter(data: TxnRequest.TxnInfo[]): void {
const playerData = Player.data.getState();
playerData.txn.centerList = data;
GiftCallBack.DispatchCallback(GiftEventEnum.ReFlash, null);
Cocos.CocosEventListener.DispatchCallback(GameData_Cocos.CELT.SetTxnUnreadCount, playerData.txn.centerList.length);
Player.data.setState(playerData);
}
export function txnTrade(data: TxnRequest.TradeResponse): void {
const playerData: State = Player.data.getState();
const centerList: TxnRequest.TxnInfo[] = playerData.txn.centerList;
let type: number = PanelType.RecordPanel;
let status: number = +data.s;
if (status < 20) {
type = PanelType.CenterPanel;
switch (status) {
case 11:
case 12:
case 13: {
for (let i: number = 0; i < centerList.length; i++) {
const centerData: TxnRequest.TxnInfo = centerList[i];
if (centerData[0] == +data.id && centerData[3][0] == playerData.account.aId) {
type = PanelType.RecordPanel;
break;
}
}
break;
}
default:
break;
}
}
CoroutineV2.Single(UpdateOneListInfo(data.id, type)).Start();
}
export function txnUserAdd(data: TxnRequest.UserAddResponse): void {
const playerData: State = Player.data.getState();
playerData.txn.receiverList.push(data.u);
Player.data.setState(playerData);
} }

View File

@ -13,7 +13,7 @@
"allowSyntheticDefaultImports": true, "allowSyntheticDefaultImports": true,
"strict": false, "strict": false,
"strictNullChecks": false, "strictNullChecks": false,
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": false,
"noFallthroughCasesInSwitch": true, "noFallthroughCasesInSwitch": true,
"module": "esnext", "module": "esnext",
"moduleResolution": "node", "moduleResolution": "node",