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 { NetConnector } from "@/Engine/CatanEngine/NetManagerV2/NetConnector"; import { NetManagerSD } from "@/Engine/CatanEngine/NetManagerV2/NetManagerSD"; import CSSettingsSDV3 from "@/FormTableSD/CSSettingsSDV3"; import { SlotsetTableRow } from "@/FormTableSD/Tables/SlotsetTable"; import A from "@/components/CustomA"; import { useGameItems } from "@/context/GameItemsContext"; import { SDAccountLoginRequest } from "@/define/Request/AccountRequest"; import { SlotInRequest } from "@/define/Request/SlotRequest"; import GameManager from "@/modules/GameManager"; import { Button, Checkbox, Dropdown, Flex, Input, MenuProps } from "antd"; import { CheckboxChangeEvent } from "antd/es/checkbox"; import dayjs from "dayjs"; import { ChangeEvent, useEffect, useState } from "react"; const SDGame = (props: ISDGame) => { const { gameUrl, onClickSlotOut } = props; const { player, setPlayer } = useGameItems(); const { gameData } = useGameItems(); const { m: money } = player; const { nowSlotId } = gameData; const [isOK, setIsOK] = useState<boolean>(false); const [isSpin, setIsSpin] = useState<boolean>(false); const [items, setItems] = useState<MenuProps["items"]>([]); const [bet, setBet] = useState<number>(0); const [delay, setDelay] = useState<number>(0.1); const [isRatioStop, setIsRatioStop] = useState<boolean>(false); const [ratioStop, setRatioStop] = useState<number>(200); const [isCountStop, setIsCountStop] = useState<boolean>(false); const [countStop, setCountStop] = useState<number>(200); const [log, setLog] = useState<string[]>([]); 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; GameManager.SlotData.SlotId = slotid; GameManager.SlotData.IsRatioStop = isRatioStop; GameManager.SlotData.RatioStop = ratioStop; GameManager.SlotData.IsCountStop = isCountStop; GameManager.SlotData.CountStop = countStop; conn = new NetConnector("https://" + host, port); conn.OnDataReceived.AddCallback(onNetDataReceived); conn.OnDisconnected.AddCallback(onNetDisconnected); loadJSON(slotid); setSlotClass(); NetManagerSD.Initialize(conn); console.log("[SDsocket] connecting..."); yield NetManagerSD.ConnectAsync(); yield* login(token); yield* slotIn(); } 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(): IterableIterator<any> { let req: SlotInRequest = new SlotInRequest(GameManager.SlotData.SlotId); yield req.SendAsync(true); let resp: INetResponse<JSON> = req.Result; if (resp.IsValid) { const br: number[] = resp.Data["br"]; const db: number = resp.Data["db"]; const betGroup: MenuProps["items"] = []; for (let i = 0; i < br.length; i++) { betGroup.push({ key: i, label: ( <A href="#" onClick={() => { setBet(br[i]); GameManager.SlotData.NowBet = br[i]; }}> {br[i]} </A> ), }); } setItems(betGroup); setBet(br[db]); GameManager.SlotData.NowBet = br[db]; setIsOK(true); } } async function loadJSON(slotid: number) { let slotset: SlotsetTableRow = CSSettingsSDV3.Slotset[slotid]; let formName: string[] = slotset.FormName; let parallel: Promise<void>[] = []; for (let i: number = 0; i < formName.length; i++) { await MainControl.DownloadFormSetting(formName[i]); } // set Form await Promise.all(parallel); } async function setSlotClass() { let slot: any; const slotGroup: typeof import("../../define/Game/Base/Slot") = await import(/* @vite-ignore */`../../define/Game/Base/Slot`); try { slot = slotGroup[`Slot${GameManager.SlotData.SlotId}`]; } catch (error) { // } if (!slot) { slot = slotGroup.SlotBase; } GameManager.SlotData.SlotClass = new slot(GameManager.SlotData.SlotId, AddLog); } async function OnClickSpin() { GameManager.SlotData.IsSpin = true; setIsSpin(true); CoroutineV2.Single(spin()).Start(); } function OnClickStop() { GameManager.SlotData.IsSpin = false; setIsSpin(false); } function* spin(): IterableIterator<any> { if (money < GameManager.SlotData.NowBet) { noMoney(); return; } if (isCountStop) { if (GameManager.SlotData.CountStop <= 0) { OnClickStop(); return; } GameManager.SlotData.CountStop--; setCountStop((v) => v - 1); } yield* GameManager.SlotData.SlotClass.Spin(bet, OnClickStop); yield CoroutineV2.WaitTime(delay); if (GameManager.SlotData.IsSpin) { yield* spin(); } else { GameManager.SlotData.IsSpin = false; setIsSpin(false); } } function noMoney(): void { GameManager.SlotData.IsSpin = false; AddLog(`金額不足: ${money}`); } function AddLog(s: string) { const todayTimeStr: string = dayjs().format("YYYY/MM/DD 00:00:00"); setLog((v) => { if (v.length > 100) { v.pop(); } v.unshift(`${todayTimeStr} ${s}`); return v; }); } /**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}> {isOK && <Flex gap="small" wrap="wrap"> <div style={controlStyle}> <p> 押注 <Dropdown menu={{ items }} placement="bottom"> <Button>{bet}</Button> </Dropdown> </p> <p> 延遲 <Input onInput={(e: ChangeEvent<HTMLInputElement>) => setDelay(+e.target.value)} type="text" value={delay} placeholder={delay.toString()} style={{ width: "10%" }} /> 秒 </p> <p> 倍率 <Checkbox onChange={(e: CheckboxChangeEvent) => { setIsRatioStop(e.target.checked); GameManager.SlotData.IsRatioStop = e.target.checked; }} /> {isRatioStop && <><Input onInput={(e: ChangeEvent<HTMLInputElement>) => { setRatioStop(+e.target.value); GameManager.SlotData.RatioStop = +e.target.value; }} type="text" value={ratioStop} placeholder={ratioStop.toString()} style={{ width: "10%" }} />倍</>} </p> <p> 轉數 <Checkbox onChange={(e: CheckboxChangeEvent) => { setIsCountStop(e.target.checked); GameManager.SlotData.IsCountStop = e.target.checked; }} /> {isCountStop && <><Input onInput={(e: ChangeEvent<HTMLInputElement>) => { setCountStop(+e.target.value); GameManager.SlotData.CountStop = +e.target.value; }} type="text" value={countStop} placeholder={countStop.toString()} style={{ width: "10%" }} />轉</>} </p> <p> {isSpin ? <Button type="primary" onClick={OnClickStop} style={{ width: "20%" }}>Stop</Button> : <Button type="primary" onClick={OnClickSpin} style={{ width: "20%" }}>Spin</Button> } </p> </div> <div style={{ height: "90vh", position: "relative", top: "0%", overflowY: "scroll" }}> {log.map((log: string, index: number) => <p key={index}>{log}</p>)} </div> </Flex>} <Button type="primary" danger onClick={onClickSlotOut} style={{ width: "20%", position: "fixed", bottom: "1%" }}>離開機台</Button> </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%" };