mirror of
https://github.com/Pax1601/DCSOlympus.git
synced 2025-10-29 16:56:34 +00:00
feat: Added wiki modes to audio, drawing and gamemaster menus.
Feat: improved looks of gamemaster menu
This commit is contained in:
parent
791b1fc4ab
commit
0765459cfd
@ -754,4 +754,12 @@ export async function getWikipediaSummary(unitName: string): Promise<string | nu
|
||||
console.error('Error fetching data from Wikipedia:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export function secondsToTimeString(seconds: number) {
|
||||
const hours = Math.floor(seconds / 3600);
|
||||
const minutes = Math.floor((seconds % 3600) / 60);
|
||||
const secs = Math.floor(seconds % 60);
|
||||
|
||||
return `${zeroPad(hours, 2)}:${zeroPad(minutes, 2)}:${zeroPad(secs, 2)}`;
|
||||
}
|
||||
@ -116,7 +116,82 @@ export function AudioMenu(props: { open: boolean; onClose: () => void; children?
|
||||
const lineDistance = (paddingRight - 40) / lineCounters[lineCounters.length - 1];
|
||||
|
||||
return (
|
||||
<Menu title="Audio menu" open={props.open} showBackButton={false} onClose={props.onClose}>
|
||||
<Menu
|
||||
title="Audio menu"
|
||||
open={props.open}
|
||||
showBackButton={false}
|
||||
onClose={props.onClose}
|
||||
wiki={() => {
|
||||
return (
|
||||
<div
|
||||
className={`
|
||||
h-full flex-col overflow-auto p-4 text-gray-400 no-scrollbar flex
|
||||
gap-2
|
||||
`}
|
||||
>
|
||||
<h2 className="mb-4 font-bold">Audio menu</h2>
|
||||
<div>
|
||||
The audio menu allows you to add and manage audio sources, connect them to unit loudspeakers and radios, and to tune radio frequencies using the SRS integration. In other words, it allows you to communicate over SRS without the need of using the SRS client.
|
||||
</div>
|
||||
<div>
|
||||
Because of the limitations of the browser, you need to enable the audio backend by clicking on the volume icon in the navigation header. Moreover, you need to allow the browser to access your microphone and speakers. It may take a couple of seconds for the audio backend to start.
|
||||
</div>
|
||||
<div className="text-red-500">
|
||||
For security reasons, the audio backend will only work if the page is served over HTTPS.
|
||||
</div>
|
||||
<h2 className="my-4 font-bold">Managing the audio backend</h2>
|
||||
<div>
|
||||
You can select the input and output devices for the audio backend. The input device is the microphone that will be used to transmit your voice. The output device is the speaker that will be used to play the audio from the other players.
|
||||
</div>
|
||||
<div>
|
||||
You can also select the radio coalition. This will determine the default coalition for the radios you create. If you are in command mode, you can change the radio
|
||||
coalition by clicking on the coalition toggle button. This will have no effect if radio coalition enforcing is not enabled in the SRS server.
|
||||
</div>
|
||||
<h2 className="my-4 font-bold">Creating audio sources</h2>
|
||||
<div>
|
||||
You can add audio sources by clicking on the "Add audio source" button. By default, a microphone and a text to speech source are created, but you can add file sources as well, which allow to play audio files such as music, prerecorded messages, or background noise, such as gunfire or engine sounds.
|
||||
</div>
|
||||
<div>
|
||||
The text to speech generation works using the Google Cloud speech API and by default it works in English. For it to work, a valid Google Cloud API key must be installed on the Olympus backend server machine. See the backend documentation for more information. {/* TODO: put link here */}
|
||||
</div>
|
||||
<div>
|
||||
Text to speech and file sources can be set to operate in loop mode, which will make them repeat the audio indefinitely. This is useful for background noise or music. Moreover, you can set the volume of the audio sources.
|
||||
</div>
|
||||
<h2 className="my-4 font-bold">Creating radios and loudspeakers</h2>
|
||||
<div>
|
||||
By default, two radios are created, but you can add more by clicking on the "Add radio" button. Radios can be tuned to different frequencies, and they can be set to operate in AM or FM mode. You can also set the volume of the radios, and change the balance between the left and right channels.
|
||||
</div>
|
||||
<div>
|
||||
When a new radio is created, it will NOT be in "listen" mode, so you will need to click on the "Tune to radio" button to start listening.
|
||||
</div>
|
||||
<div>
|
||||
You have three options to transmit on the radio:
|
||||
<div>
|
||||
<li>By clicking on the "Talk on frequency" button on the radio panel. This will enable continuous transmission and will remain "on" until clicked again.</li>
|
||||
<li>By clicking on the "Push to talk" button located over the mouse coordinates panel, on the bottom right corner of the map.</li>
|
||||
<li>By using the "Push to talk" keyboard shortcuts, which can be edited in the options menu.</li>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
Loudspeakers can be used to simulate environmental sounds, like 5MC calls on the carrier, or sirens. To create a loudspeaker, click on the unit that should broadcast the sound, and then click on the "Loudspeakers" button. PTT buttons for loudspeakers operate in the same way as radios.
|
||||
</div>
|
||||
<div className="text-red-500">
|
||||
The loudspeakers system uses the SRS integration, so it will only work if other players' SRS clients are running and connected to the same server as Olympus. Moreover, the loudspeaker system operates using the INTERCOM radio in SRS, and for the time being it will only work for those radios that have the INTERCOM radio enabled (i.e. usually multicrew aircraft).
|
||||
</div>
|
||||
<h2 className="my-4 font-bold">Connecting sources and radios/loudspeakers</h2>
|
||||
<div>
|
||||
Each source can be connected to one or more radios or loudspeakers. To connect a source to a radio or loudspeaker, click on the "+" button on the right of the source panel, then click on the equivalent button on the desired radio/loudspeaker. To disconnect a source from a radio or loudspeaker, click on the "-" button next to the radio/loudspeaker.
|
||||
</div>
|
||||
<div>
|
||||
The connection lines will show the connections between the sources and the radios/loudspeakers. The color of the line is randomly generated and will be different for each source.
|
||||
</div>
|
||||
<div>
|
||||
By connecting multiple sources to the same radio/loudspeaker, you can create complex audio setups, like playing background music while transmitting on the radio.
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
>
|
||||
<div className="flex content-center gap-4 p-4">
|
||||
<div className="my-auto text-gray-400">
|
||||
<FaQuestionCircle />
|
||||
|
||||
@ -12,6 +12,7 @@ export function Menu(props: {
|
||||
children?: JSX.Element | JSX.Element[];
|
||||
autohide?: boolean;
|
||||
wiki?: () => JSX.Element | JSX.Element[];
|
||||
wikiDisabled?: boolean;
|
||||
}) {
|
||||
const [hide, setHide] = useState(true);
|
||||
const [wiki, setWiki] = useState(false);
|
||||
@ -56,9 +57,8 @@ export function Menu(props: {
|
||||
<div
|
||||
data-hide={hide}
|
||||
className={`
|
||||
pointer-events-auto h-[calc(100vh-58px)] w-full overflow-y-auto
|
||||
overflow-x-hidden backdrop-blur-lg backdrop-grayscale
|
||||
transition-transform no-scrollbar
|
||||
pointer-events-auto h-[calc(100vh-58px)] w-full backdrop-blur-lg
|
||||
backdrop-grayscale transition-transform
|
||||
dark:bg-olympus-800/90
|
||||
data-[hide='true']:-translate-x-full
|
||||
`}
|
||||
@ -81,16 +81,18 @@ export function Menu(props: {
|
||||
/>
|
||||
)}
|
||||
{props.title}
|
||||
<FontAwesomeIcon
|
||||
onClick={() => setWiki(!wiki)}
|
||||
icon={faCircleQuestion}
|
||||
className={`
|
||||
ml-auto flex cursor-pointer items-center justify-center rounded-md
|
||||
p-2 text-lg
|
||||
dark:text-gray-500 dark:hover:bg-gray-700 dark:hover:text-white
|
||||
hover:bg-gray-200
|
||||
`}
|
||||
/>
|
||||
{!(props.wikiDisabled === true) && (
|
||||
<FontAwesomeIcon
|
||||
onClick={() => setWiki(!wiki)}
|
||||
icon={faCircleQuestion}
|
||||
className={`
|
||||
ml-auto flex cursor-pointer items-center justify-center
|
||||
rounded-md p-2 text-lg
|
||||
dark:text-gray-500 dark:hover:bg-gray-700 dark:hover:text-white
|
||||
hover:bg-gray-200
|
||||
`}
|
||||
/>
|
||||
)}
|
||||
<FontAwesomeIcon
|
||||
onClick={() => setHide(true)}
|
||||
icon={faEyeSlash}
|
||||
@ -112,16 +114,18 @@ export function Menu(props: {
|
||||
`}
|
||||
/>
|
||||
</h5>
|
||||
<div className="flex flex-col h-[calc(100%-3rem)] w-full sm:flex-row">
|
||||
<div
|
||||
className={`
|
||||
flex h-[calc(100%-3rem)] w-full flex-col
|
||||
sm:flex-row
|
||||
`}
|
||||
>
|
||||
<div
|
||||
data-wiki={wiki}
|
||||
className={`
|
||||
w-0 overflow-hidden transition-all
|
||||
h-0
|
||||
data-[wiki='true']:min-h-[50%]
|
||||
data-[wiki='true']:min-w-full
|
||||
sm:data-[wiki='true']:min-w-[50%]
|
||||
sm:data-[wiki='true']:min-h-full
|
||||
h-0 w-0 overflow-hidden transition-all
|
||||
data-[wiki='true']:min-h-[50%] data-[wiki='true']:min-w-full
|
||||
sm:data-[wiki='true']:min-h-full sm:data-[wiki='true']:min-w-[50%]
|
||||
`}
|
||||
>
|
||||
{props.wiki ? props.wiki() : <div className={`p-4 text-gray-200`}>Work in progress</div>}
|
||||
@ -129,6 +133,7 @@ export function Menu(props: {
|
||||
<div
|
||||
data-wiki={wiki}
|
||||
className={`
|
||||
relative overflow-y-auto overflow-x-hidden no-scrollbar
|
||||
sm:w-[400px]
|
||||
`}
|
||||
>
|
||||
|
||||
@ -92,7 +92,6 @@ export function DrawingMenu(props: { open: boolean; onClose: () => void }) {
|
||||
hover:scale-125 hover:text-gray-200
|
||||
`}
|
||||
onClick={() => {
|
||||
|
||||
if (container === mainDrawingsContainer.container) {
|
||||
getApp().getMap().setOption("showMissionDrawings", !getApp().getMap().getOptions().showMissionDrawings);
|
||||
} else {
|
||||
@ -157,6 +156,55 @@ export function DrawingMenu(props: { open: boolean; onClose: () => void }) {
|
||||
getApp().getCoalitionAreasManager().setSelectedArea(null);
|
||||
getApp().setState(OlympusState.DRAW, DrawSubState.NO_SUBSTATE);
|
||||
}}
|
||||
wiki={() => {
|
||||
return (
|
||||
<div
|
||||
className={`
|
||||
h-full flex-col overflow-auto p-4 text-gray-400 no-scrollbar flex
|
||||
gap-2
|
||||
`}
|
||||
>
|
||||
<h2 className="mb-4 font-bold">Drawing menu</h2>
|
||||
<div>
|
||||
The drawing menu allows you to create and manage custom drawings, such as polygons and circles, and to generate IADS (Integrated Air Defense
|
||||
System) areas. Moreover, you can manage the visibility and opacity of mission drawings, i.e. drawings from the Mission Editor.
|
||||
</div>
|
||||
<h2 className="my-4 font-bold">Custom drawings and IADS</h2>
|
||||
<div>
|
||||
To create a custom drawing, click on the 'Add polygon' or 'Add circle' buttons, then click on the map to add polygons or to move the drawing.
|
||||
Double-click on the map to finish your creation. You can then edit the drawing by clicking on it. You can also move it up or down in the list, or
|
||||
delete it.
|
||||
</div>
|
||||
<div>
|
||||
You can change the name and the coalition of the area. You can also generate an IADS area by selecting the types, eras, and ranges of units you
|
||||
want to include in the area. You can also set the density and distribution of the IADS. If you check the 'Force coalition appropriate units' box,
|
||||
the IADS will only include units that are appropriate for the coalition of the area (e.g. Hawk SAMs for {""}
|
||||
<span className="text-blue-500">blue</span> and SA-6 SAMs for{" "}
|
||||
<span
|
||||
className={`text-red-500`}
|
||||
>
|
||||
red
|
||||
</span>
|
||||
).
|
||||
</div>
|
||||
<div>
|
||||
The IADS generator will create a random distribution of units in the area, based on the density and distribution you set. Units will be
|
||||
concentrated around cities, and airbases that belong to the selected coalition.
|
||||
</div>
|
||||
<h2 className="my-4 font-bold">Mission drawings</h2>
|
||||
<div>
|
||||
You can manage the visibility and opacity of mission drawings by clicking on the eye icon. Moreover, you can change the opacity of the drawing by
|
||||
using the slider. You can also hide or show all the drawings in a container.
|
||||
</div>
|
||||
<div>
|
||||
You can search for a specific drawing by typing in the search bar. The search is case-insensitive and will match any part of the drawing name.
|
||||
</div>
|
||||
<div>
|
||||
Any change you make is persistent and will be saved for the next time you reload Olympus, as long as the DCS mission was not restarted.
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
>
|
||||
<>
|
||||
{appState === OlympusState.DRAW && appSubState === DrawSubState.NO_SUBSTATE && (
|
||||
@ -301,11 +349,9 @@ export function DrawingMenu(props: { open: boolean; onClose: () => void }) {
|
||||
bg-olympus-600 p-5
|
||||
`}
|
||||
>
|
||||
<div
|
||||
className={`border-b-2 border-b-olympus-100 pb-4 text-gray-300`}
|
||||
>
|
||||
Automatic IADS generation
|
||||
</div>
|
||||
<div className={`
|
||||
border-b-2 border-b-olympus-100 pb-4 text-gray-300
|
||||
`}>Automatic IADS generation</div>
|
||||
<OlDropdown className="" label="Units types" disableAutoClose={true}>
|
||||
{types.map((type, idx) => {
|
||||
if (!(type in typesSelection)) {
|
||||
|
||||
@ -6,6 +6,9 @@ import { getApp } from "../../olympusapp";
|
||||
import { ServerStatus } from "../../interfaces";
|
||||
import { CommandModeOptionsChangedEvent, ServerStatusUpdatedEvent } from "../../events";
|
||||
import { BLUE_COMMANDER, COMMAND_MODE_OPTIONS_DEFAULTS, ERAS_ORDER, GAME_MASTER, RED_COMMANDER } from "../../constants/constants";
|
||||
import { secondsToTimeString } from "../../other/utils";
|
||||
import { FaQuestionCircle } from "react-icons/fa";
|
||||
import { FaMinus, FaPlus } from "react-icons/fa6";
|
||||
|
||||
export function GameMasterMenu(props: { open: boolean; onClose: () => void; children?: JSX.Element | JSX.Element[] }) {
|
||||
const [commandModeOptions, setCommandModeOptions] = useState(COMMAND_MODE_OPTIONS_DEFAULTS);
|
||||
@ -21,53 +24,131 @@ export function GameMasterMenu(props: { open: boolean; onClose: () => void; chil
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Menu title="Game Master options" open={props.open} showBackButton={false} onClose={props.onClose}>
|
||||
<Menu title="Game Master options" open={props.open} showBackButton={false} onClose={props.onClose} wiki={() => {
|
||||
return (
|
||||
<div
|
||||
className={`
|
||||
h-full flex-col overflow-auto p-4 text-gray-400 no-scrollbar flex
|
||||
gap-2
|
||||
`}
|
||||
>
|
||||
<h2 className="mb-4 font-bold">Game Master menu</h2>
|
||||
<div>
|
||||
The Game Master menu allows the Game Master to set up the game session for the Real Time Strategy game mode of DCS Olympus.
|
||||
</div>
|
||||
<div>
|
||||
In this mode, commanders can play against eachother in a real-time strategy game, where they can spawn a limited amount of units. Each commander can only control units belonging to their coalition. Moreover, they can only see enemy units if detected, so proper placement of radars is crucial.
|
||||
</div>
|
||||
<div>
|
||||
The Game Master can set up the game session by restricting the unit spawns, setting the setup time, and restricting the eras of the units that can be spawned. Moreover, the Game Master can set the amount of spawn points available for each coalition.
|
||||
</div>
|
||||
<div>
|
||||
During the setup time, commanders can prepare the battlefield. As long as they have sufficient spawn points, they can place units anywhere on the map. After the setup time ends, the game starts and the restrictions are enforced.
|
||||
</div>
|
||||
<div>
|
||||
When restrictions are enforced, commanders will no longer be able to spawn ground units, and air units can only be spawned from airfields.
|
||||
</div>
|
||||
<div>
|
||||
There are multiple additional modes of play. You can disable the spawn restrictions to allow commanders to spawn units freely, but can only see detected units, or you can set the spawn points to 0 to disable unit spawns entirely and force commanders to only use the units they have at the start of the game or that you provide.
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}}>
|
||||
<div
|
||||
className={`
|
||||
flex flex-col gap-2 p-5 font-normal text-gray-800
|
||||
dark:text-white
|
||||
`}
|
||||
>
|
||||
You are operating as:
|
||||
{commandModeOptions.commandMode === GAME_MASTER && (
|
||||
<div
|
||||
className={`
|
||||
w-full rounded-md bg-olympus-400 p-2 text-center font-bold
|
||||
`}
|
||||
>
|
||||
GAME MASTER
|
||||
</div>
|
||||
)}
|
||||
{commandModeOptions.commandMode === BLUE_COMMANDER && <div className={`
|
||||
w-full rounded-md bg-blue-600 p-2 text-center font-bold
|
||||
`}>BLUE COMMANDER</div>}
|
||||
{commandModeOptions.commandMode === RED_COMMANDER && <div className={`
|
||||
w-full rounded-md bg-red-700 p-2 text-center font-bold
|
||||
`}>RED COMMANDER</div>}
|
||||
{serverStatus.elapsedTime > currentSetupTime && (
|
||||
<div
|
||||
className={`
|
||||
w-full rounded-md bg-orange-600 p-2 text-center font-bold
|
||||
`}
|
||||
>
|
||||
Setup time has ended
|
||||
</div>
|
||||
)}
|
||||
{serverStatus.elapsedTime <= currentSetupTime && (
|
||||
<div
|
||||
className={`
|
||||
w-full rounded-md bg-green-700 p-2 text-center font-bold
|
||||
`}
|
||||
>
|
||||
SETUP ends in {(currentSetupTime - serverStatus.elapsedTime)?.toFixed()} seconds
|
||||
{commandModeOptions.restrictSpawns ? (
|
||||
<>
|
||||
<div className="mb-4 flex content-center gap-4">
|
||||
<div className="my-auto text-gray-400">
|
||||
<FaQuestionCircle />
|
||||
</div>
|
||||
<div className="text-sm text-gray-400">
|
||||
Unit spawns are restricted. During the SETUP phase, commanders can spawn units according to the settings below. After the SETUP phase ends,
|
||||
ground/navy units and air spawns are disabled, and commanders can spawn aircraft/helicopters only from airfields.
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex">
|
||||
{commandModeOptions.commandMode === GAME_MASTER && (
|
||||
<button
|
||||
className={`
|
||||
h-10 rounded-s-lg bg-gray-100 p-3
|
||||
dark:bg-gray-700 dark:hover:bg-gray-600
|
||||
dark:focus:ring-blue-700
|
||||
focus:outline-none focus:ring-2 focus:ring-gray-100
|
||||
hover:bg-gray-200
|
||||
`}
|
||||
onClick={() => {
|
||||
const newCommandModeOptions = { ...commandModeOptions };
|
||||
newCommandModeOptions.setupTime = Math.max(serverStatus.elapsedTime, newCommandModeOptions.setupTime - 60);
|
||||
if (commandModeOptions.commandMode !== GAME_MASTER) return;
|
||||
setCommandModeOptions(newCommandModeOptions);
|
||||
setCurrentSetupTime(newCommandModeOptions.setupTime);
|
||||
getApp().getServerManager().setCommandModeOptions(newCommandModeOptions);
|
||||
}}
|
||||
>
|
||||
<FaMinus className="my-auto" />
|
||||
</button>
|
||||
)}
|
||||
<div className={`
|
||||
relative z-[-1] flex h-10 w-[360px] bg-olympus-600
|
||||
`}>
|
||||
<div
|
||||
className={`
|
||||
absolute my-auto w-full text-center before
|
||||
before:absolute before:left-0 before:z-[-1] before:h-10
|
||||
before:w-full before:bg-olympus-400 before:content-['']
|
||||
`}
|
||||
style={{ width: `${Math.min(100, 100 - ((currentSetupTime - serverStatus.elapsedTime) / currentSetupTime) * 100)}%` }}
|
||||
></div>
|
||||
{currentSetupTime - serverStatus.elapsedTime > 0 ? (
|
||||
<div className="mx-auto my-auto">SETUP ends in {secondsToTimeString(currentSetupTime - serverStatus.elapsedTime)}</div>
|
||||
) : (
|
||||
<div className="mx-auto my-auto animate-pulse">SETUP ended, restrictions active</div>
|
||||
)}
|
||||
</div>
|
||||
{commandModeOptions.commandMode === GAME_MASTER && (
|
||||
<button
|
||||
className={`
|
||||
h-10 rounded-e-lg bg-gray-100 p-3
|
||||
dark:bg-gray-700 dark:hover:bg-gray-600
|
||||
dark:focus:ring-blue-700
|
||||
focus:outline-none focus:ring-2 focus:ring-gray-100
|
||||
hover:bg-gray-200
|
||||
`}
|
||||
onClick={() => {
|
||||
const newCommandModeOptions = { ...commandModeOptions };
|
||||
newCommandModeOptions.setupTime = Math.max(serverStatus.elapsedTime + 60, newCommandModeOptions.setupTime + 60);
|
||||
if (commandModeOptions.commandMode !== GAME_MASTER) return;
|
||||
setCommandModeOptions(newCommandModeOptions);
|
||||
setCurrentSetupTime(newCommandModeOptions.setupTime);
|
||||
getApp().getServerManager().setCommandModeOptions(newCommandModeOptions);
|
||||
}}
|
||||
>
|
||||
<FaPlus className="my-auto" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<div className="flex content-center gap-4">
|
||||
<div className="my-auto text-gray-400">
|
||||
<FaQuestionCircle />
|
||||
</div>
|
||||
<div className="text-sm text-gray-400">
|
||||
Unit spawns are NOT restricted, therefore no setup time is enforced and commanders can spawn units as desired. Only unit detection is enforced.
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<span className="mt-5">Options: </span>
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="flex flex-col gap-3">
|
||||
<div
|
||||
className={`
|
||||
group flex flex-row rounded-md justify-content cursor-pointer
|
||||
gap-4 p-2
|
||||
gap-4 px-2
|
||||
dark:hover:bg-olympus-400
|
||||
`}
|
||||
onClick={() => {
|
||||
@ -82,13 +163,13 @@ export function GameMasterMenu(props: { open: boolean; onClose: () => void; chil
|
||||
data-disabled={!commandModeOptions.restrictSpawns || commandModeOptions.commandMode !== GAME_MASTER}
|
||||
className={`data-[disabled='true']:text-gray-400`}
|
||||
>
|
||||
Restrict unit spanws
|
||||
Restrict unit spawns
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
className={`
|
||||
group flex flex-row rounded-md justify-content cursor-pointer
|
||||
gap-4 p-2
|
||||
gap-4 px-2
|
||||
dark:hover:bg-olympus-400
|
||||
`}
|
||||
onClick={() => {
|
||||
@ -120,7 +201,7 @@ export function GameMasterMenu(props: { open: boolean; onClose: () => void; chil
|
||||
key={era}
|
||||
className={`
|
||||
group flex flex-row rounded-md justify-content
|
||||
cursor-pointer gap-4 p-2
|
||||
cursor-pointer gap-4 px-2
|
||||
dark:hover:bg-olympus-400
|
||||
`}
|
||||
onClick={() => {
|
||||
@ -224,56 +305,6 @@ export function GameMasterMenu(props: { open: boolean; onClose: () => void; chil
|
||||
}}
|
||||
></OlNumberInput>
|
||||
</div>
|
||||
<div
|
||||
className={`
|
||||
group flex flex-row rounded-md justify-content gap-4 px-4 py-2
|
||||
`}
|
||||
>
|
||||
<span
|
||||
data-disabled={!commandModeOptions.restrictSpawns || commandModeOptions.commandMode !== GAME_MASTER}
|
||||
className={`
|
||||
my-auto mr-auto
|
||||
data-[disabled='true']:text-gray-400
|
||||
`}
|
||||
>
|
||||
Setup time (seconds)
|
||||
</span>
|
||||
<OlNumberInput
|
||||
min={0}
|
||||
max={6000}
|
||||
value={commandModeOptions.setupTime}
|
||||
onChange={(e) => {
|
||||
if (!commandModeOptions.restrictSpawns || commandModeOptions.commandMode !== GAME_MASTER) return;
|
||||
const newCommandModeOptions = { ...commandModeOptions };
|
||||
newCommandModeOptions.setupTime = parseInt(e.target.value);
|
||||
setCommandModeOptions(newCommandModeOptions);
|
||||
}}
|
||||
onIncrease={() => {
|
||||
if (!commandModeOptions.restrictSpawns || commandModeOptions.commandMode !== GAME_MASTER) return;
|
||||
const newCommandModeOptions = { ...commandModeOptions };
|
||||
newCommandModeOptions.setupTime = Math.min(newCommandModeOptions.setupTime + 10, 6000);
|
||||
setCommandModeOptions(newCommandModeOptions);
|
||||
}}
|
||||
onDecrease={() => {
|
||||
if (!commandModeOptions.restrictSpawns || commandModeOptions.commandMode !== GAME_MASTER) return;
|
||||
const newCommandModeOptions = { ...commandModeOptions };
|
||||
newCommandModeOptions.setupTime = Math.max(newCommandModeOptions.setupTime - 10, 0);
|
||||
setCommandModeOptions(newCommandModeOptions);
|
||||
}}
|
||||
></OlNumberInput>
|
||||
</div>
|
||||
<div
|
||||
className={`
|
||||
group flex flex-row rounded-md justify-content gap-4 px-4 py-2
|
||||
`}
|
||||
>
|
||||
<span className="mr-auto">Elapsed time (seconds)</span>{" "}
|
||||
<span
|
||||
className={`w-32 text-center`}
|
||||
>
|
||||
{serverStatus.elapsedTime?.toFixed()}
|
||||
</span>
|
||||
</div>
|
||||
{commandModeOptions.commandMode === GAME_MASTER && (
|
||||
<button
|
||||
type="button"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user