Commit 5226ac18 authored by timel's avatar timel

feat: auto mode

parent d43799ce
export * from "./useApp";
export * from "./useAutoMode";
import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux"; import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux";
import type { RootState, AppDispatch } from "./store"; import type { RootState, AppDispatch } from "../store";
// Use throughout your app instead of plain `useDispatch` and `useSelector` // Use throughout your app instead of plain `useDispatch` and `useSelector`
export const useAppDispatch: () => AppDispatch = useDispatch; export const useAppDispatch: () => AppDispatch = useDispatch;
......
/*
* 旨在跳过登录界面,直接进入游戏(和AI对战、后续可能增加自动的PVP)。
* 仅在开发环境下生效,便于开发者快速调节决斗界面。
*/
interface AutoModeConfig {
defaultPlayer?: string;
defaultDeck?: string;
defaultPassword?: string;
defaultMora?: string;
isAiMode?: boolean;
isAiFirst?: boolean;
}
const autoModeConfig: AutoModeConfig = {
defaultPlayer: "",
defaultDeck: "",
defaultPassword: "",
};
const aiModeConfig: AutoModeConfig = {
defaultPlayer: "AiKiller",
defaultDeck: import.meta.env.VITE_AI_MODE_DEFAULT_DECK ?? "hero",
defaultPassword: "AI",
defaultMora: "scissors",
isAiMode: true,
isAiFirst: import.meta.env.VITE_IS_AI_FIRST === "true",
};
export function useAutoMode(): AutoModeConfig {
if (!import.meta.env.DEV) return {};
if (import.meta.env.VITE_IS_AI_MODE === "true") {
return aiModeConfig;
}
return autoModeConfig;
}
...@@ -7,16 +7,18 @@ ...@@ -7,16 +7,18 @@
* *
* */ * */
import { Input } from "antd"; import { Input } from "antd";
import React, { useState, ChangeEvent } from "react"; import React, { useState, ChangeEvent, useEffect } from "react";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import "../styles/core.scss"; import "../styles/core.scss";
import NeosConfig from "../../neos.config.json"; import NeosConfig from "../../neos.config.json";
import { useAutoMode } from "../hook";
const serverConfig = NeosConfig.servers; const serverConfig = NeosConfig.servers;
const { isAiMode, defaultPlayer, defaultPassword } = useAutoMode();
export default function Login() { export default function Login() {
const [player, setPlayer] = useState(""); const [player, setPlayer] = useState(isAiMode ? defaultPlayer : "");
const [passWd, setPasswd] = useState(""); const [passWd, setPasswd] = useState(isAiMode ? defaultPassword : "");
const [ip, setIp] = useState(`${serverConfig[0].ip}:${serverConfig[0].port}`); const [ip, setIp] = useState(`${serverConfig[0].ip}:${serverConfig[0].port}`);
const navigate = useNavigate(); const navigate = useNavigate();
...@@ -30,13 +32,19 @@ export default function Login() { ...@@ -30,13 +32,19 @@ export default function Login() {
setIp(event.target.value); setIp(event.target.value);
}; };
const handleSubmit = () => navigate(`/room/${player}/${passWd}/${ip}`);
useEffect(() => {
// 如果开启了AI模式,直接进入房间
if (isAiMode) {
handleSubmit();
}
}, []);
return ( return (
<div className="container"> <div className="container">
<div id="login"> <div id="login">
<form <form className="login-form" onSubmit={handleSubmit}>
className="login-form"
onSubmit={() => navigate(`/room/${player}/${passWd}/${ip}`)}
>
<span className="fa fa-user"></span> <span className="fa fa-user"></span>
<Input <Input
autoFocus autoFocus
......
import React from "react"; import React from "react";
import { sendHandResult, sendTpResult } from "../api/ocgcore/ocgHelper"; import { sendHandResult, sendTpResult } from "../api/ocgcore/ocgHelper";
import { useAppSelector } from "../hook"; import { useAppSelector, useAutoMode } from "../hook";
import { import {
selectHandSelectAble, selectHandSelectAble,
unSelectHandAble, unSelectHandAble,
...@@ -18,6 +18,8 @@ import { ...@@ -18,6 +18,8 @@ import {
TableOutlined, TableOutlined,
} from "@ant-design/icons"; } from "@ant-design/icons";
const { isAiMode, isAiFirst, defaultMora } = useAutoMode();
const Mora = () => { const Mora = () => {
const dispatch = store.dispatch; const dispatch = store.dispatch;
const selectHandAble = useAppSelector(selectHandSelectAble); const selectHandAble = useAppSelector(selectHandSelectAble);
...@@ -37,6 +39,18 @@ const Mora = () => { ...@@ -37,6 +39,18 @@ const Mora = () => {
} }
}, [duelHsStart]); }, [duelHsStart]);
useEffect(() => {
if (isAiMode) {
handleSelectMora(defaultMora!);
}
}, [selectHandAble]);
useEffect(() => {
if (isAiMode && !selectHandAble && selectTpAble) {
handleSelectTp(!isAiFirst);
}
}, [selectHandAble, selectTpAble]);
const handleSelectMora = (selected: string) => { const handleSelectMora = (selected: string) => {
sendHandResult(selected); sendHandResult(selected);
dispatch(unSelectHandAble()); dispatch(unSelectHandAble());
......
...@@ -19,7 +19,7 @@ import { ...@@ -19,7 +19,7 @@ import {
selectPlayer0, selectPlayer0,
selectPlayer1, selectPlayer1,
} from "../reducers/playerSlice"; } from "../reducers/playerSlice";
import { useAppSelector } from "../hook"; import { useAppSelector, useAutoMode } from "../hook";
import { selectJoined } from "../reducers/joinSlice"; import { selectJoined } from "../reducers/joinSlice";
import { selectChat } from "../reducers/chatSlice"; import { selectChat } from "../reducers/chatSlice";
import { fetchDeck, IDeck } from "../api/deck"; import { fetchDeck, IDeck } from "../api/deck";
...@@ -50,6 +50,8 @@ import { initStrings } from "../api/strings"; ...@@ -50,6 +50,8 @@ import { initStrings } from "../api/strings";
const READY_STATE = "ready"; const READY_STATE = "ready";
const { isAiMode, defaultDeck } = useAutoMode();
const WaitRoom = () => { const WaitRoom = () => {
const params = useParams<{ const params = useParams<{
player?: string; player?: string;
...@@ -90,7 +92,20 @@ const WaitRoom = () => { ...@@ -90,7 +92,20 @@ const WaitRoom = () => {
await rustInit(url); await rustInit(url);
}; };
init(); /** 如果是开发者模式下的人机对战,应该自动选择卡组,并自动准备和开始 */
const runAiMode = async () => {
await new Promise((resolve) => setTimeout(resolve, 500));
await handleChoseDeck(defaultDeck!);
handleChoseReady();
handleChoseStart();
};
(async () => {
await init();
if (isAiMode) {
await runAiMode();
}
})();
} }
}, []); }, []);
...@@ -103,12 +118,14 @@ const WaitRoom = () => { ...@@ -103,12 +118,14 @@ const WaitRoom = () => {
const duelStart = useAppSelector(selectDuelStart); const duelStart = useAppSelector(selectDuelStart);
const [api, contextHolder] = notification.useNotification(); const [api, contextHolder] = notification.useNotification();
const [deckTitle, setDeckTitle] = useState("请选择卡组"); const [deckTitle, setDeckTitle] = useState("请选择卡组");
// FIXME: 这些数据应该从`store`中获取 // FIXME: 这些数据应该从`store`中获取
// TODO: 云卡组 // TODO: 云卡组
const DEFAULT_DECK = "hero";
const decks: MenuProps["items"] = [ const decks: MenuProps["items"] = [
{ {
label: "hero", label: DEFAULT_DECK,
key: "hero", key: DEFAULT_DECK,
}, },
]; ];
const [uploadState, setUploadState] = useState(""); const [uploadState, setUploadState] = useState("");
...@@ -145,9 +162,9 @@ const WaitRoom = () => { ...@@ -145,9 +162,9 @@ const WaitRoom = () => {
}, },
}; };
const onDeckReady = (deck: IDeck) => { const onDeckReady = async (deck: IDeck) => {
sendUpdateDeck(deck); sendUpdateDeck(deck);
dispatch( await dispatch(
initMeExtraDeckMeta({ controler: 0, codes: deck.extra?.reverse() || [] }) initMeExtraDeckMeta({ controler: 0, codes: deck.extra?.reverse() || [] })
); );
setChoseDeck(true); setChoseDeck(true);
...@@ -155,8 +172,8 @@ const WaitRoom = () => { ...@@ -155,8 +172,8 @@ const WaitRoom = () => {
const handleChoseDeck = async (deckName: string) => { const handleChoseDeck = async (deckName: string) => {
const deck = await fetchDeck(deckName); const deck = await fetchDeck(deckName);
await onDeckReady(deck);
onDeckReady(deck); setDeckTitle(deckName);
}; };
const handleChoseReady = () => { const handleChoseReady = () => {
...@@ -263,10 +280,7 @@ const WaitRoom = () => { ...@@ -263,10 +280,7 @@ const WaitRoom = () => {
<Dropdown <Dropdown
menu={{ menu={{
items: decks, items: decks,
onClick: async ({ key }) => { onClick: ({ key }) => handleChoseDeck(key),
await handleChoseDeck(key);
setDeckTitle(key);
},
}} }}
> >
<a onClick={(e) => e.preventDefault()}> <a onClick={(e) => e.preventDefault()}>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment