From 4ee6bee4ecbd605658fc7b608688c728bb4779e6 Mon Sep 17 00:00:00 2001 From: ECWireless Date: Fri, 19 Jul 2024 08:59:46 -0600 Subject: [PATCH 1/6] Render insufficient funds error --- packages/client/src/hooks/useToast.ts | 18 +++++++- .../client/src/lib/mud/createSystemCalls.ts | 42 +++++++++++-------- packages/client/src/utils/errors.ts | 1 + 3 files changed, 42 insertions(+), 19 deletions(-) diff --git a/packages/client/src/hooks/useToast.ts b/packages/client/src/hooks/useToast.ts index 170733d3b..92af25ceb 100644 --- a/packages/client/src/hooks/useToast.ts +++ b/packages/client/src/hooks/useToast.ts @@ -1,7 +1,11 @@ import { useToast as useChakraToast } from '@chakra-ui/react'; import { useCallback } from 'react'; -import { getError, USER_ERRORS } from '../utils/errors'; +import { + getError, + INSUFFICIENT_FUNDS_MESSAGE, + USER_ERRORS, +} from '../utils/errors'; export const useToast = (): { renderError: (errorMsg: string, errorLog?: unknown) => void; @@ -21,6 +25,18 @@ export const useToast = (): { // eslint-disable-next-line no-console console.error(errorLog); + if ((errorLog as Error).message === INSUFFICIENT_FUNDS_MESSAGE) { + toast({ + description: (errorLog as Error).message, + position: 'top', + status: 'error', + containerStyle: { + bg: 'red', + }, + }); + return; + } + toast({ description: errorMsg, position: 'top', diff --git a/packages/client/src/lib/mud/createSystemCalls.ts b/packages/client/src/lib/mud/createSystemCalls.ts index 6559a493f..f77b14d7d 100644 --- a/packages/client/src/lib/mud/createSystemCalls.ts +++ b/packages/client/src/lib/mud/createSystemCalls.ts @@ -21,12 +21,14 @@ import { BaseError, ContractFunctionRevertedError, getContract, + InsufficientFundsError, keccak256, parseAbiItem, stringToHex, toBytes, } from 'viem'; +import { INSUFFICIENT_FUNDS_MESSAGE } from '../../utils/errors'; import { EncounterType, StatsClasses } from '../../utils/types'; import { ClientComponents } from './createClientComponents'; import { SetupNetworkResult } from './setupNetwork'; @@ -38,15 +40,19 @@ type SystemCallReturn = Promise<{ error?: string; }>; -const getContractError = (error: unknown): string => { - if (error instanceof BaseError) { - const revertError = error.walk( - e => e instanceof ContractFunctionRevertedError, - ); - if (revertError instanceof ContractFunctionRevertedError) { - const args = revertError.data?.args ?? []; - return args[0] as string; - } +const getContractError = (error: BaseError): string => { + const revertError = error.walk( + e => e instanceof ContractFunctionRevertedError, + ); + if (revertError instanceof ContractFunctionRevertedError) { + const args = revertError.data?.args ?? []; + return args[0] as string; + } + const insufficientFundsError = error.walk( + e => e instanceof InsufficientFundsError, + ); + if (insufficientFundsError instanceof InsufficientFundsError) { + return INSUFFICIENT_FUNDS_MESSAGE; } return 'An error occurred calling the contract.'; }; @@ -131,7 +137,7 @@ export function createSystemCalls( }; } catch (e) { return { - error: getContractError(e), + error: getContractError(e as BaseError), success: false, }; } @@ -195,7 +201,7 @@ export function createSystemCalls( }; } catch (e) { return { - error: getContractError(e), + error: getContractError(e as BaseError), success: false, }; } @@ -223,7 +229,7 @@ export function createSystemCalls( }; } catch (e) { return { - error: getContractError(e), + error: getContractError(e as BaseError), success: false, }; } @@ -268,7 +274,7 @@ export function createSystemCalls( }; } catch (e) { return { - error: getContractError(e), + error: getContractError(e as BaseError), success: false, }; } @@ -318,7 +324,7 @@ export function createSystemCalls( }; } catch (e) { return { - error: getContractError(e), + error: getContractError(e as BaseError), success: false, }; } @@ -363,7 +369,7 @@ export function createSystemCalls( }; } catch (e) { return { - error: getContractError(e), + error: getContractError(e as BaseError), success: false, }; } finally { @@ -413,7 +419,7 @@ export function createSystemCalls( }; } catch (e) { return { - error: getContractError(e), + error: getContractError(e as BaseError), success: false, }; } @@ -442,7 +448,7 @@ export function createSystemCalls( }; } catch (e) { return { - error: getContractError(e), + error: getContractError(e as BaseError), success: false, }; } @@ -485,7 +491,7 @@ export function createSystemCalls( }; } catch (e) { return { - error: getContractError(e), + error: getContractError(e as BaseError), success: false, }; } diff --git a/packages/client/src/utils/errors.ts b/packages/client/src/utils/errors.ts index f1ba2eb40..d5ff835bf 100644 --- a/packages/client/src/utils/errors.ts +++ b/packages/client/src/utils/errors.ts @@ -1,4 +1,5 @@ export const USER_ERRORS = ['User denied signature']; +export const INSUFFICIENT_FUNDS_MESSAGE = 'Insufficient funds.'; export const getError = ( error: unknown, From efd790093c5a5cd73c38fbc3e05e2db0ced1a4d9 Mon Sep 17 00:00:00 2001 From: ECWireless Date: Fri, 19 Jul 2024 09:23:16 -0600 Subject: [PATCH 2/6] Remove auction house link in character creation --- packages/client/src/pages/CharacterCreation.tsx | 9 --------- 1 file changed, 9 deletions(-) diff --git a/packages/client/src/pages/CharacterCreation.tsx b/packages/client/src/pages/CharacterCreation.tsx index 116f2cad2..f6a68f4a2 100644 --- a/packages/client/src/pages/CharacterCreation.tsx +++ b/packages/client/src/pages/CharacterCreation.tsx @@ -9,7 +9,6 @@ import { Heading, HStack, Input, - Link, SimpleGrid, Stack, Text, @@ -626,14 +625,6 @@ export const CharacterCreation = (): JSX.Element => { )} - - Auction House ▶ - {!isSmallScreen && ( From 9b73d9f49d2b08ca5e049ccb51734554bdc20451 Mon Sep 17 00:00:00 2001 From: ECWireless Date: Fri, 19 Jul 2024 11:09:48 -0600 Subject: [PATCH 3/6] Remove InitiateCombatModal --- .../client/src/components/ActionsPanel.tsx | 8 -- .../src/components/InitiateCombatModal.tsx | 109 ------------------ .../client/src/components/ItemEquipModal.tsx | 15 --- .../src/components/TileDetailsPanel.tsx | 81 ++++++++++--- .../src/contexts/MapNavigationContext.tsx | 16 +-- .../client/src/pages/CharacterCreation.tsx | 15 --- packages/client/src/utils/errors.ts | 3 +- 7 files changed, 68 insertions(+), 179 deletions(-) delete mode 100644 packages/client/src/components/InitiateCombatModal.tsx diff --git a/packages/client/src/components/ActionsPanel.tsx b/packages/client/src/components/ActionsPanel.tsx index 9de776e45..805fe1555 100644 --- a/packages/client/src/components/ActionsPanel.tsx +++ b/packages/client/src/components/ActionsPanel.tsx @@ -56,7 +56,6 @@ import { HealthBar } from './HealthBar'; export const ActionsPanel = (): JSX.Element => { const { renderError } = useToast(); const { - burnerBalance, components: { Actions }, delegatorAddress, systemCalls: { endTurn }, @@ -84,12 +83,6 @@ export const ActionsPanel = (): JSX.Element => { try { setIsAttacking(true); - if (burnerBalance === '0') { - throw new Error( - 'Insufficient funds. Please top off your session account.', - ); - } - if (!delegatorAddress) { throw new Error('Missing delegation.'); } @@ -139,7 +132,6 @@ export const ActionsPanel = (): JSX.Element => { }, [ Actions, - burnerBalance, character, currentBattle, delegatorAddress, diff --git a/packages/client/src/components/InitiateCombatModal.tsx b/packages/client/src/components/InitiateCombatModal.tsx deleted file mode 100644 index 43cc3c8be..000000000 --- a/packages/client/src/components/InitiateCombatModal.tsx +++ /dev/null @@ -1,109 +0,0 @@ -import { - Button, - Modal, - ModalBody, - ModalCloseButton, - ModalContent, - ModalFooter, - ModalHeader, - ModalOverlay, -} from '@chakra-ui/react'; -import { useCallback, useState } from 'react'; - -import { useCharacter } from '../contexts/CharacterContext'; -import { useMUD } from '../contexts/MUDContext'; -import { useToast } from '../hooks/useToast'; -import { EncounterType, type Monster } from '../utils/types'; - -type InitiateCombatModalProps = Monster & { - isOpen: boolean; - onClose: () => void; -}; - -export const InitiateCombatModal: React.FC = ({ - isOpen, - onClose, - ...monster -}): JSX.Element => { - const { renderError, renderSuccess } = useToast(); - const { - burnerBalance, - delegatorAddress, - systemCalls: { createMatch }, - } = useMUD(); - const { character } = useCharacter(); - - const [isInitiating, setIsInitiating] = useState(false); - - const onInitiateCombat = useCallback(async () => { - try { - setIsInitiating(true); - - if (!character) { - throw new Error('Character not found.'); - } - - if (burnerBalance === '0') { - throw new Error( - 'Insufficient funds. Please top off your session account.', - ); - } - - if (!delegatorAddress) { - throw new Error('Missing delegation.'); - } - - const { error, success } = await createMatch( - EncounterType.PvE, - [character.characterId], - [monster.monsterId], - ); - - if (error && !success) { - throw new Error(error); - } - - renderSuccess(`Battle has begun!`); - onClose(); - } catch (e) { - renderError('Failed to initiate battle.', e); - } finally { - setIsInitiating(false); - } - }, [ - burnerBalance, - character, - createMatch, - delegatorAddress, - monster, - onClose, - renderError, - renderSuccess, - ]); - - return ( - - - - Initiate Battle - - - Are you sure you want to initiate a battle with {monster.name}? - - - - - - - - ); -}; diff --git a/packages/client/src/components/ItemEquipModal.tsx b/packages/client/src/components/ItemEquipModal.tsx index e6d7b6ed7..0a3afb528 100644 --- a/packages/client/src/components/ItemEquipModal.tsx +++ b/packages/client/src/components/ItemEquipModal.tsx @@ -31,7 +31,6 @@ export const ItemEquipModal: React.FC = ({ }): JSX.Element => { const { renderError, renderSuccess } = useToast(); const { - burnerBalance, delegatorAddress, systemCalls: { equipItems, unequipItem }, } = useMUD(); @@ -52,12 +51,6 @@ export const ItemEquipModal: React.FC = ({ throw new Error('Character not found.'); } - if (burnerBalance === '0') { - throw new Error( - 'Insufficient funds. Please top off your session account.', - ); - } - if (!delegatorAddress) { throw new Error('Missing delegation.'); } @@ -79,7 +72,6 @@ export const ItemEquipModal: React.FC = ({ setIsEquipping(false); } }, [ - burnerBalance, character, delegatorAddress, equipItems, @@ -98,12 +90,6 @@ export const ItemEquipModal: React.FC = ({ throw new Error('Character not found.'); } - if (burnerBalance === '0') { - throw new Error( - 'Insufficient funds. Please top off your session account.', - ); - } - if (!delegatorAddress) { throw new Error('Missing delegation.'); } @@ -126,7 +112,6 @@ export const ItemEquipModal: React.FC = ({ setIsEquipping(false); } }, [ - burnerBalance, character, delegatorAddress, onClose, diff --git a/packages/client/src/components/TileDetailsPanel.tsx b/packages/client/src/components/TileDetailsPanel.tsx index 978d0e8ff..d6bd521aa 100644 --- a/packages/client/src/components/TileDetailsPanel.tsx +++ b/packages/client/src/components/TileDetailsPanel.tsx @@ -8,27 +8,67 @@ import { Spinner, Text, useBreakpointValue, - useDisclosure, VStack, } from '@chakra-ui/react'; -import { useState } from 'react'; +import { useCallback, useState } from 'react'; import { GiCrossedSwords } from 'react-icons/gi'; import { IoIosArrowForward } from 'react-icons/io'; import { useNavigate } from 'react-router-dom'; +import { useCharacter } from '../contexts/CharacterContext'; import { useCombat } from '../contexts/CombatContext'; import { useMapNavigation } from '../contexts/MapNavigationContext'; -import { type Character, type Monster } from '../utils/types'; -import { InitiateCombatModal } from './InitiateCombatModal'; +import { useMUD } from '../contexts/MUDContext'; +import { useToast } from '../hooks/useToast'; +import { type Character, EncounterType, type Monster } from '../utils/types'; const ROW_HEIGHT = { base: 5, md: 8, lg: 10 }; export const TileDetailsPanel = (): JSX.Element => { + const { renderError, renderSuccess } = useToast(); + + const { + delegatorAddress, + systemCalls: { createMatch }, + } = useMUD(); + const { character } = useCharacter(); const { isRefreshing, monsters, otherPlayers } = useMapNavigation(); - const { isOpen, onOpen, onClose } = useDisclosure(); const { currentBattle } = useCombat(); - const [selectedMonster, setSelectedMonster] = useState(null); + const [isInitiating, setIsInitiating] = useState(false); + + const onInitiateCombat = useCallback( + async (monster: Monster) => { + try { + setIsInitiating(true); + + if (!character) { + throw new Error('Character not found.'); + } + + if (!delegatorAddress) { + throw new Error('Missing delegation.'); + } + + const { error, success } = await createMatch( + EncounterType.PvE, + [character.characterId], + [monster.monsterId], + ); + + if (error && !success) { + throw new Error(error); + } + + renderSuccess('Battle has begun!'); + } catch (e) { + renderError('Failed to initiate battle.', e); + } finally { + setIsInitiating(false); + } + }, + [character, createMatch, delegatorAddress, renderError, renderSuccess], + ); if (isRefreshing) { return ( @@ -67,8 +107,7 @@ export const TileDetailsPanel = (): JSX.Element => { key={`tile-monster-${i}-${monster.name}`} monster={monster} onClick={() => { - setSelectedMonster(monster); - onOpen(); + onInitiateCombat(monster); }} /> ))} @@ -108,12 +147,22 @@ export const TileDetailsPanel = (): JSX.Element => { )} - {selectedMonster && ( - + {isInitiating && ( + + + + Initiating battle! + + + + )} {currentBattle && ( { > - Combat in progress! + Battle in progress! @@ -175,7 +224,7 @@ const MonsterRow = ({ }} > {name} diff --git a/packages/client/src/contexts/MapNavigationContext.tsx b/packages/client/src/contexts/MapNavigationContext.tsx index a5adb43d1..0ce50b33e 100644 --- a/packages/client/src/contexts/MapNavigationContext.tsx +++ b/packages/client/src/contexts/MapNavigationContext.tsx @@ -57,7 +57,6 @@ export const MapNavigationProvider = ({ const { pathname } = useLocation(); const { renderError, renderSuccess } = useToast(); const { - burnerBalance, components: { Characters, CharactersTokenURI, @@ -258,12 +257,6 @@ export const MapNavigationProvider = ({ try { setIsSpawning(true); - if (burnerBalance === '0') { - throw new Error( - 'Insufficient funds. Please top off your session account.', - ); - } - if (!delegatorAddress) { throw new Error('Missing delegation.'); } @@ -284,14 +277,7 @@ export const MapNavigationProvider = ({ } finally { setIsSpawning(false); } - }, [ - burnerBalance, - character, - delegatorAddress, - renderError, - renderSuccess, - spawn, - ]); + }, [character, delegatorAddress, renderError, renderSuccess, spawn]); const onMove = useCallback( async (direction: 'up' | 'down' | 'left' | 'right') => { diff --git a/packages/client/src/pages/CharacterCreation.tsx b/packages/client/src/pages/CharacterCreation.tsx index f6a68f4a2..b60c6e7e2 100644 --- a/packages/client/src/pages/CharacterCreation.tsx +++ b/packages/client/src/pages/CharacterCreation.tsx @@ -45,7 +45,6 @@ export const CharacterCreation = (): JSX.Element => { const isSmallScreen = useBreakpointValue({ base: true, lg: false }); const { data: externalWalletClient } = useWalletClient(); const { - burnerBalance, components: { ItemsBaseURI, ItemsTokenURI, UltimateDominionConfig }, delegatorAddress, isSynced, @@ -149,12 +148,6 @@ export const CharacterCreation = (): JSX.Element => { try { setIsCreating(true); - if (burnerBalance === '0') { - throw new Error( - 'Insufficient funds. Please top off your session account.', - ); - } - if (!delegatorAddress) { throw new Error('Missing delegation.'); } @@ -219,7 +212,6 @@ export const CharacterCreation = (): JSX.Element => { }, [ avatar, - burnerBalance, delegatorAddress, description, mintCharacter, @@ -235,12 +227,6 @@ export const CharacterCreation = (): JSX.Element => { try { setIsRollingStats(true); - if (burnerBalance === '0') { - throw new Error( - 'Insufficient funds. Please top off your session account.', - ); - } - if (!delegatorAddress) { throw new Error('Missing delegation.'); } @@ -266,7 +252,6 @@ export const CharacterCreation = (): JSX.Element => { setIsRollingStats(false); } }, [ - burnerBalance, character, characterClass, delegatorAddress, diff --git a/packages/client/src/utils/errors.ts b/packages/client/src/utils/errors.ts index d5ff835bf..604205238 100644 --- a/packages/client/src/utils/errors.ts +++ b/packages/client/src/utils/errors.ts @@ -1,5 +1,6 @@ export const USER_ERRORS = ['User denied signature']; -export const INSUFFICIENT_FUNDS_MESSAGE = 'Insufficient funds.'; +export const INSUFFICIENT_FUNDS_MESSAGE = + 'Insufficient funds. Please top off your session account.'; export const getError = ( error: unknown, From 21a7fc4729b72dadc7fa085d48b5e337dcf72cb0 Mon Sep 17 00:00:00 2001 From: ECWireless Date: Fri, 19 Jul 2024 13:56:06 -0600 Subject: [PATCH 4/6] Add InfoModal component --- packages/client/src/components/Header.tsx | 8 +++- packages/client/src/components/InfoModal.tsx | 41 ++++++++++++++++++++ packages/client/src/pages/GameBoard.tsx | 36 ++++++++++++++++- 3 files changed, 81 insertions(+), 4 deletions(-) create mode 100644 packages/client/src/components/InfoModal.tsx diff --git a/packages/client/src/components/Header.tsx b/packages/client/src/components/Header.tsx index 1fac2878d..19e14f521 100644 --- a/packages/client/src/components/Header.tsx +++ b/packages/client/src/components/Header.tsx @@ -1,7 +1,7 @@ import { Box, Button, Heading, Stack } from '@chakra-ui/react'; -import { useLocation } from 'react-router-dom'; +import { useLocation, useNavigate } from 'react-router-dom'; -import { HOME_PATH } from '../Routes'; +import { GAME_BOARD_PATH, HOME_PATH } from '../Routes'; const PAGES_WITHOUT_HEADER = [HOME_PATH]; @@ -11,6 +11,8 @@ export const Header = ({ onOpenWalletDetailsModal: () => void; }): JSX.Element => { const { pathname } = useLocation(); + const navigate = useNavigate(); + if (PAGES_WITHOUT_HEADER.includes(pathname)) { return ; } @@ -34,6 +36,8 @@ export const Header = ({ Wallet Details navigate(GAME_BOARD_PATH)} size={{ base: 'sm', sm: 'md' }} textAlign={{ base: 'left', lg: 'right' }} > diff --git a/packages/client/src/components/InfoModal.tsx b/packages/client/src/components/InfoModal.tsx new file mode 100644 index 000000000..0eca30bab --- /dev/null +++ b/packages/client/src/components/InfoModal.tsx @@ -0,0 +1,41 @@ +import { + Button, + Modal, + ModalBody, + ModalCloseButton, + ModalContent, + ModalFooter, + ModalHeader, + ModalOverlay, +} from '@chakra-ui/react'; +import { ReactNode } from 'react'; + +type InfoModalProps = { + children: ReactNode; + heading: string; + isOpen: boolean; + onClose: () => void; +}; + +export const InfoModal: React.FC = ({ + children, + heading, + isOpen, + onClose, +}): JSX.Element => { + return ( + + + + {heading} + + {children} + + + + + + ); +}; diff --git a/packages/client/src/pages/GameBoard.tsx b/packages/client/src/pages/GameBoard.tsx index fa3a0106c..60726ee96 100644 --- a/packages/client/src/pages/GameBoard.tsx +++ b/packages/client/src/pages/GameBoard.tsx @@ -6,13 +6,16 @@ import { Popover, PopoverContent, PopoverTrigger, + Text, + useDisclosure, VStack, } from '@chakra-ui/react'; import { useEffect } from 'react'; -import { useNavigate } from 'react-router-dom'; +import { Link, useNavigate } from 'react-router-dom'; import { useWalletClient } from 'wagmi'; import { ActionsPanel } from '../components/ActionsPanel'; +import { InfoModal } from '../components/InfoModal'; import { MapPanel } from '../components/MapPanel'; import { StatsPanel } from '../components/StatsPanel'; import { TileDetailsPanel } from '../components/TileDetailsPanel'; @@ -23,10 +26,12 @@ import { useMUD } from '../contexts/MUDContext'; import { GAME_BOARD_PATH, HOME_PATH } from '../Routes'; export const GameBoard = (): JSX.Element => { + const { isOpen, onOpen, onClose } = useDisclosure(); + const { data: externalWalletClient } = useWalletClient(); const navigate = useNavigate(); const { delegatorAddress, isSynced } = useMUD(); - const { character } = useCharacter(); + const { character, equippedItems } = useCharacter(); useEffect(() => { if (!isSynced) return; @@ -44,6 +49,12 @@ export const GameBoard = (): JSX.Element => { } }, [character, delegatorAddress, externalWalletClient, isSynced, navigate]); + useEffect(() => { + if (character?.experience === '0' && equippedItems?.length === 0) { + onOpen(); + } + }, [character, equippedItems, onOpen]); + return ( @@ -118,6 +129,27 @@ export const GameBoard = (): JSX.Element => { + + + In order to start battling, you must have at least 1 weapon + equipped. Go to your{' '} + + character page + {' '} + to equip a weapon. + + ); From 3a34bb9e5cdb42f03e4ce340c482675593f9e9aa Mon Sep 17 00:00:00 2001 From: ECWireless Date: Fri, 19 Jul 2024 14:41:25 -0600 Subject: [PATCH 5/6] Add Outer Realms warning --- packages/client/src/App.tsx | 8 +- packages/client/src/components/InfoModal.tsx | 5 +- packages/client/src/pages/GameBoard.tsx | 245 +++++++++++-------- 3 files changed, 158 insertions(+), 100 deletions(-) diff --git a/packages/client/src/App.tsx b/packages/client/src/App.tsx index 5eccde0bf..35dbfd7f0 100644 --- a/packages/client/src/App.tsx +++ b/packages/client/src/App.tsx @@ -5,13 +5,19 @@ import { BrowserRouter as Router, useLocation } from 'react-router-dom'; import { Footer } from './components/Footer'; import { Header } from './components/Header'; import { WalletDetailsModal } from './components/WalletDetailsModal'; +import { CombatProvider } from './contexts/CombatContext'; +import { MapNavigationProvider } from './contexts/MapNavigationContext'; import { useMUD } from './contexts/MUDContext'; import AppRoutes, { HOME_PATH } from './Routes'; export const App = (): JSX.Element => { return ( - + + + + + ); }; diff --git a/packages/client/src/components/InfoModal.tsx b/packages/client/src/components/InfoModal.tsx index 0eca30bab..893e951fc 100644 --- a/packages/client/src/components/InfoModal.tsx +++ b/packages/client/src/components/InfoModal.tsx @@ -7,6 +7,7 @@ import { ModalFooter, ModalHeader, ModalOverlay, + Text, } from '@chakra-ui/react'; import { ReactNode } from 'react'; @@ -27,7 +28,9 @@ export const InfoModal: React.FC = ({ - {heading} + + {heading} + {children} diff --git a/packages/client/src/pages/GameBoard.tsx b/packages/client/src/pages/GameBoard.tsx index 60726ee96..54989ea7e 100644 --- a/packages/client/src/pages/GameBoard.tsx +++ b/packages/client/src/pages/GameBoard.tsx @@ -10,7 +10,8 @@ import { useDisclosure, VStack, } from '@chakra-ui/react'; -import { useEffect } from 'react'; +import { useCallback, useEffect } from 'react'; +import { IoIosWarning } from 'react-icons/io'; import { Link, useNavigate } from 'react-router-dom'; import { useWalletClient } from 'wagmi'; @@ -20,18 +21,27 @@ import { MapPanel } from '../components/MapPanel'; import { StatsPanel } from '../components/StatsPanel'; import { TileDetailsPanel } from '../components/TileDetailsPanel'; import { useCharacter } from '../contexts/CharacterContext'; -import { CombatProvider } from '../contexts/CombatContext'; -import { MapNavigationProvider } from '../contexts/MapNavigationContext'; +import { useMapNavigation } from '../contexts/MapNavigationContext'; import { useMUD } from '../contexts/MUDContext'; import { GAME_BOARD_PATH, HOME_PATH } from '../Routes'; export const GameBoard = (): JSX.Element => { - const { isOpen, onOpen, onClose } = useDisclosure(); + const { + isOpen: isEquipInfoModalOpen, + onOpen: onOpenEquipInfoModal, + onClose: onCloseEquipInfoModal, + } = useDisclosure(); + const { + isOpen: isOuterRealmsInfoModalOpen, + onOpen: onOpenOuterRealmsInfoModal, + onClose: onCloseOuterRealmsInfoModal, + } = useDisclosure(); const { data: externalWalletClient } = useWalletClient(); const navigate = useNavigate(); const { delegatorAddress, isSynced } = useMUD(); const { character, equippedItems } = useCharacter(); + const { position } = useMapNavigation(); useEffect(() => { if (!isSynced) return; @@ -51,106 +61,145 @@ export const GameBoard = (): JSX.Element => { useEffect(() => { if (character?.experience === '0' && equippedItems?.length === 0) { - onOpen(); + onOpenEquipInfoModal(); } - }, [character, equippedItems, onOpen]); + }, [character, equippedItems, onOpenEquipInfoModal]); + + useEffect(() => { + if (!(character && position)) return; + const outerRealms = position.x === 5 || position.y === 5; + + const outerRealmsSeenKey = `outer-realms-warning-seen-${character.characterId}`; + + const hasSeenWarning = localStorage.getItem(outerRealmsSeenKey); + if (hasSeenWarning) return; + + if (character.level === '1' && outerRealms) { + onOpenOuterRealmsInfoModal(); + } + }, [character, onOpenOuterRealmsInfoModal, position]); + + const onAcknowledgeOuterRealmsWarning = useCallback(() => { + if (!character) return; + + const outerRealmsSeenKey = `outer-realms-warning-seen-${character.characterId}`; + localStorage.setItem(outerRealmsSeenKey, 'true'); + onCloseOuterRealmsInfoModal(); + }, [character, onCloseOuterRealmsInfoModal]); return ( - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - + + + + + In order to start battling, you must have at least 1 weapon equipped. + Go to your{' '} + - - - - - - - - - - - - - - - In order to start battling, you must have at least 1 weapon - equipped. Go to your{' '} - - character page + character page + {' '} + to equip a weapon. + + + + + + + The{' '} + + Outer Realms {' '} - to equip a weapon. + is a dangerous place for a level 1 character. Any other player could + attack you at any time. + + + It is recommended that you level up your character more before + entering. - - - + + + ); }; From 8a466664c8e5c546b7b36011e527e289940cc080 Mon Sep 17 00:00:00 2001 From: ECWireless Date: Fri, 19 Jul 2024 17:57:29 -0600 Subject: [PATCH 6/6] Fix rollStats issue --- packages/client/src/lib/mud/createSystemCalls.ts | 13 +++++++++++-- packages/client/src/pages/Character.tsx | 1 - packages/client/src/pages/CharacterCreation.tsx | 2 +- packages/client/src/pages/GameBoard.tsx | 4 ++-- 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/packages/client/src/lib/mud/createSystemCalls.ts b/packages/client/src/lib/mud/createSystemCalls.ts index f77b14d7d..a5732f298 100644 --- a/packages/client/src/lib/mud/createSystemCalls.ts +++ b/packages/client/src/lib/mud/createSystemCalls.ts @@ -90,6 +90,7 @@ export function createSystemCalls( CombatEncounter, Position, Spawned, + Stats, }: ClientComponents, ) { const createMatch = async ( @@ -411,9 +412,17 @@ export function createSystemCalls( }, ); - await waitForTransaction(tx); + const { blockNumber } = await waitForTransaction(tx); + + const blockToWaitFor = blockNumber + BigInt(2); + + let currentBlockNumber = await publicClient.getBlockNumber(); + while (currentBlockNumber < blockToWaitFor) { + await new Promise(resolve => setTimeout(resolve, 2000)); + currentBlockNumber = await publicClient.getBlockNumber(); + } - const success = !!getComponentValue(Characters, characterEntity); + const success = !!getComponentValue(Stats, characterEntity); return { success, }; diff --git a/packages/client/src/pages/Character.tsx b/packages/client/src/pages/Character.tsx index ee2ce4354..ad85fd5b3 100644 --- a/packages/client/src/pages/Character.tsx +++ b/packages/client/src/pages/Character.tsx @@ -286,7 +286,6 @@ export const CharacterPage = (): JSX.Element => { {character ? ( { throw new Error(error); } - refreshCharacter(); + await refreshCharacter(); renderSuccess('Stats rolled!'); } catch (e) { renderError('Failed to roll stats.', e); diff --git a/packages/client/src/pages/GameBoard.tsx b/packages/client/src/pages/GameBoard.tsx index 54989ea7e..4e106202a 100644 --- a/packages/client/src/pages/GameBoard.tsx +++ b/packages/client/src/pages/GameBoard.tsx @@ -90,10 +90,10 @@ export const GameBoard = (): JSX.Element => { return (