GameCreationForm.jsx 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. import ms from "pretty-ms";
  2. import { useCallback, useState } from "react";
  3. import { createGame } from "../../../domain/apiMethods";
  4. import {
  5. COUNTRY_RACE,
  6. DISTANCE,
  7. HARD,
  8. FROZEN,
  9. NORMAL,
  10. RACE,
  11. RANDOM_STREET_VIEW,
  12. TIME_BANK,
  13. URBAN,
  14. NIGHTMARE,
  15. RAMP,
  16. RAMP_HARD,
  17. } from "../../../domain/constants";
  18. import useCountryLookup from "../../../hooks/useCountryLookup";
  19. import Loading from "../Loading";
  20. import { CountryDropdown, Dropdown, DropdownGroup, Item } from "./Dropdown";
  21. import ErrorModal from "./ErrorModal";
  22. import styles from "./GameCreationForm.module.css";
  23. const DEFAULTS = {
  24. timer: 300,
  25. rounds: 5,
  26. countryLock: null,
  27. generationMethod: RANDOM_STREET_VIEW,
  28. gameMode: NORMAL,
  29. clockMode: NORMAL,
  30. scoreMethod: DISTANCE,
  31. roundPointCap: null,
  32. };
  33. const PRESETS = {
  34. URBAN_AMERICA: {
  35. ...DEFAULTS,
  36. generationMethod: URBAN,
  37. countryLock: "us",
  38. },
  39. URBAN_GLOBAL: {
  40. ...DEFAULTS,
  41. generationMethod: URBAN,
  42. },
  43. FAST_FROZEN: {
  44. ...DEFAULTS,
  45. timer: 30,
  46. rounds: 3,
  47. generationMethod: RANDOM_STREET_VIEW,
  48. gameMode: FROZEN,
  49. },
  50. COUNTRY_RACE: {
  51. ...DEFAULTS,
  52. scoreMethod: COUNTRY_RACE,
  53. },
  54. FROZEN_COUNTRY_RACE: {
  55. ...DEFAULTS,
  56. timer: 30,
  57. gameMode: FROZEN,
  58. scoreMethod: COUNTRY_RACE,
  59. },
  60. BOOTLEG_GG_DUEL: {
  61. ...DEFAULTS,
  62. clockMode: RACE,
  63. scoreMethod: RAMP,
  64. },
  65. };
  66. export const LastSettingsButton = ({ onClick }) => (
  67. <div
  68. className={styles.reusebutton}
  69. role="button"
  70. tabIndex="0"
  71. onClick={onClick}
  72. onKeyDown={({ key }) => {
  73. if (key === "Enter") {
  74. onClick();
  75. }
  76. }}
  77. title="Reuse Previous Game Settings"
  78. >
  79. <span aria-label="recycle" role="img">
  80. ♻️
  81. </span>
  82. </div>
  83. );
  84. const GameCreationForm = ({ afterCreate, lastSettings = null }) => {
  85. const [loading, setLoading] = useState(false);
  86. const [creationError, setCreationError] = useState(false);
  87. const [timer, setTimer] = useState(DEFAULTS.timer);
  88. const [rounds, setRounds] = useState(DEFAULTS.rounds);
  89. const [countryLock, setCountryLock] = useState(DEFAULTS.countryLock);
  90. const [generationMethod, setGenMethod] = useState(DEFAULTS.generationMethod);
  91. const [gameMode, setGameMode] = useState(DEFAULTS.gameMode);
  92. const [clockMode, setClockMode] = useState(DEFAULTS.clockMode);
  93. const [scoreMethod, setScoreMethod] = useState(DEFAULTS.scoreMethod);
  94. const [roundPointCap, setRoundPointCap] = useState(DEFAULTS.roundPointCap);
  95. const countryLookup = useCountryLookup(generationMethod);
  96. const [presetOpen, setPresetOpen] = useState(false);
  97. const setPreset = useCallback(
  98. ({
  99. timer: newTimer,
  100. rounds: newRounds,
  101. countryLock: newCountryLock,
  102. generationMethod: newGenMethod,
  103. gameMode: newGameMode,
  104. clockMode: newClockMode,
  105. scoreMethod: newScoreMethod,
  106. roundPointCap: newRoundPointCap,
  107. }) => {
  108. setTimer(newTimer);
  109. setRounds(newRounds);
  110. setCountryLock(newCountryLock);
  111. setGenMethod(newGenMethod);
  112. setGameMode(newGameMode);
  113. setClockMode(newClockMode);
  114. setScoreMethod(newScoreMethod);
  115. setRoundPointCap(newRoundPointCap);
  116. },
  117. []
  118. );
  119. if (loading || countryLookup === null) {
  120. return <Loading />;
  121. }
  122. const onCreateGame = async () => {
  123. setLoading(true);
  124. let gameId;
  125. try {
  126. gameId = await createGame(
  127. timer,
  128. rounds,
  129. countryLock,
  130. generationMethod,
  131. gameMode,
  132. clockMode,
  133. scoreMethod,
  134. roundPointCap
  135. );
  136. } catch (e) {
  137. setCreationError(true);
  138. setLoading(false);
  139. return;
  140. }
  141. if (afterCreate) {
  142. afterCreate(gameId);
  143. }
  144. };
  145. return (
  146. <div className={styles.form}>
  147. <ErrorModal
  148. open={creationError}
  149. onClose={() => setCreationError(false)}
  150. />
  151. <div className={styles.buttoncontainer}>
  152. <Dropdown
  153. buttonClass={styles.favbutton}
  154. selected={DEFAULTS}
  155. onClick={() => setPresetOpen(o => !o)}
  156. onSelect={v => {
  157. setPresetOpen(false);
  158. setPreset(v);
  159. }}
  160. open={presetOpen}
  161. >
  162. <Item value={DEFAULTS} display="⭐">
  163. Default
  164. </Item>
  165. <Item value={PRESETS.URBAN_AMERICA} display="⭐">
  166. Urban America
  167. </Item>
  168. <Item value={PRESETS.URBAN_GLOBAL} display="⭐">
  169. Urban Global
  170. </Item>
  171. <Item value={PRESETS.FAST_FROZEN} display="⭐">
  172. Fast Frozen
  173. </Item>
  174. <Item value={PRESETS.COUNTRY_RACE} display="⭐">
  175. Country Race
  176. </Item>
  177. <Item value={PRESETS.FROZEN_COUNTRY_RACE} display="⭐">
  178. Frozen Country Race
  179. </Item>
  180. <Item value={PRESETS.BOOTLEG_GG_DUEL} display="⭐">
  181. Legally Distinct from Geoguessr Duels
  182. </Item>
  183. </Dropdown>
  184. {lastSettings && (
  185. <LastSettingsButton onClick={() => setPreset(lastSettings)} />
  186. )}
  187. <button className={styles.start} onClick={onCreateGame} type="button">
  188. New Game
  189. </button>
  190. </div>
  191. <div className={styles.dropdowns}>
  192. <DropdownGroup>
  193. <Dropdown selected={timer} onSelect={setTimer} open="timer">
  194. <Item value={30} display={ms(30 * 1000)}>
  195. 30 Seconds
  196. </Item>
  197. <Item value={120} display={ms(2 * 60 * 1000)}>
  198. 2 Minutes
  199. </Item>
  200. <Item value={300} display={ms(5 * 60 * 1000)}>
  201. 5 Minutes
  202. </Item>
  203. <Item value={3600} display={ms(60 * 60 * 1000)}>
  204. 1 Hour
  205. </Item>
  206. </Dropdown>
  207. <Dropdown selected={rounds} onSelect={setRounds} open="rounds">
  208. <Item value={1}>1 Round</Item>
  209. <Item value={3}>3 Rounds</Item>
  210. <Item value={5}>5 Rounds</Item>
  211. <Item value={10}>10 Rounds</Item>
  212. </Dropdown>
  213. <Dropdown
  214. selected={generationMethod}
  215. onSelect={setGenMethod}
  216. open="gen"
  217. >
  218. <Item value={RANDOM_STREET_VIEW} display="🎲">
  219. Random Street View
  220. </Item>
  221. <Item value={URBAN} display="🏙️">
  222. Urban Centers
  223. </Item>
  224. </Dropdown>
  225. <CountryDropdown
  226. countryLookup={countryLookup}
  227. selected={countryLock}
  228. onSelect={setCountryLock}
  229. open="country"
  230. />
  231. <Dropdown selected={gameMode} onSelect={setGameMode} open="gameMode">
  232. <Item value={NORMAL} display="📍">
  233. Normal
  234. </Item>
  235. <Item value={FROZEN} display="❄️">
  236. Frozen
  237. </Item>
  238. </Dropdown>
  239. <Dropdown
  240. selected={clockMode}
  241. onSelect={setClockMode}
  242. open="clockMode"
  243. >
  244. <Item value={NORMAL} display="⏰">
  245. Standard
  246. </Item>
  247. <Item value={TIME_BANK} display="🏦">
  248. Time Bank
  249. </Item>
  250. <Item value={RACE} display="⚔️">
  251. Duel
  252. </Item>
  253. </Dropdown>
  254. <Dropdown
  255. selected={scoreMethod}
  256. onSelect={setScoreMethod}
  257. open="scoreMethod"
  258. >
  259. <Item value={DISTANCE} display="📏">
  260. Distance
  261. </Item>
  262. <Item value={RAMP} display="📈">
  263. Ramping
  264. </Item>
  265. <Item value={COUNTRY_RACE} display="🗾">
  266. Country Race
  267. </Item>
  268. <Item value={HARD} display="😬">
  269. Hard Mode
  270. </Item>
  271. <Item value={RAMP_HARD} display="🧀">
  272. Ramping Hard Mode
  273. </Item>
  274. <Item value={NIGHTMARE} display="💀">
  275. Nightmare Mode
  276. </Item>
  277. </Dropdown>
  278. <Dropdown
  279. selected={roundPointCap}
  280. onSelect={setRoundPointCap}
  281. open="roundPointCap"
  282. >
  283. <Item value={null} display="♾️">
  284. No Limit
  285. </Item>
  286. <Item value={10000} display="10k">
  287. 10k Total Points per Round
  288. </Item>
  289. <Item value={20000} display="20k">
  290. 20k Total Points per Round
  291. </Item>
  292. </Dropdown>
  293. </DropdownGroup>
  294. </div>
  295. </div>
  296. );
  297. };
  298. export default GameCreationForm;