Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 17 additions & 83 deletions packages/client/src/components/ActionsPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,54 +1,12 @@
import { Button, HStack, Stack, Text, VStack } from '@chakra-ui/react';
import { useMemo } from 'react';
import { Box, Button, HStack, Stack, Text, VStack } from '@chakra-ui/react';
import { useEffect, useMemo, useRef } from 'react';
import { Link } from 'react-router-dom';
// eslint-disable-next-line import/no-named-as-default
import Typist from 'react-typist';

import { useCharacter } from '../contexts/CharacterContext';
import { useMapNavigation } from '../contexts/MapNavigationContext';

// enum ActionEvents {
// Attack = 'attack',
// Defend = 'defend against',
// GainGold = 'gold',
// GainExperience = 'experience',
// }

// type BattleEvent = {
// type: ActionEvents;
// monster: string;
// amount: number;
// };

// type ResolutionEvent = {
// type: ActionEvents;
// amount: number;
// };

// const BATTLE_EVENTS: BattleEvent[] = [
// {
// type: ActionEvents.Defend,
// amount: 1,
// monster: 'Green Slime',
// },
// {
// type: ActionEvents.Attack,
// amount: 2,
// monster: 'Green Slime',
// },
// ];

// const RESOLUTION_EVENTS: ResolutionEvent[] = [
// {
// type: ActionEvents.GainGold,
// amount: 2,
// },
// {
// type: ActionEvents.GainExperience,
// amount: 3,
// },
// ];

export const ActionsPanel = (): JSX.Element => {
const {
isRefreshing: isRefreshingCharacter,
Expand All @@ -67,6 +25,17 @@ export const ActionsPanel = (): JSX.Element => {
position,
} = useMapNavigation();

const parentDivRef = useRef<HTMLDivElement>(null);

useEffect(() => {
if (parentDivRef.current) {
parentDivRef.current.scrollTo({
behavior: 'smooth',
top: parentDivRef.current.scrollHeight,
});
}
}, [actionOutcomes]);

const actionText = useMemo(() => {
if (isRefreshingCharacter || isRefreshingMap) return '';

Expand Down Expand Up @@ -152,19 +121,19 @@ export const ActionsPanel = (): JSX.Element => {
]);

return (
<>
<Box maxH="100%" overflowY="auto" pb={4} ref={parentDivRef}>
{currentBattle && equippedItems && monsterOponent && (
<VStack bgColor="white" position="sticky" spacing={0} top={0} w="100%">
<Text p={{ base: 2, lg: 4 }} size="xs" textAlign="center">
Choose your move:
</Text>
{equippedItems.length === 0 && (
<Text color="red" fontWeight={700}>
<Text color="red" fontWeight={700} p={{ base: 2, lg: 4 }}>
You have no equipped items. In order to attack, you must go to
your{' '}
<Text
as={Link}
color="green"
color="blue"
to={`/characters/${character?.characterId}`}
_hover={{ textDecoration: 'underline' }}
>
Expand Down Expand Up @@ -244,41 +213,6 @@ export const ActionsPanel = (): JSX.Element => {
);
})}
</Stack>
{/* <Stack>
{BATTLE_EVENTS.map((event, i) => (
<Text
key={`battle-event-${i}`}
size={{ base: 'xs', sm: 'sm', lg: 'md' }}
>
You {event.type}{' '}
<Text as="span" color="green">
{event.monster}
</Text>{' '}
{event.type === ActionEvents.Attack ? 'for' : 'taking'}{' '}
<Text as="span" color="red">
{event.amount} damage
</Text>
.
</Text>
))}
</Stack>
<Stack>
{RESOLUTION_EVENTS.map((event, i) => (
<Text
key={`resolution-event-${i}`}
size={{ base: 'xs', sm: 'sm', lg: 'md' }}
>
You gain {event.amount}{' '}
<Text
as="span"
color={event.type === ActionEvents.GainGold ? 'yellow' : 'green'}
>
{event.type === ActionEvents.GainGold ? '$GOLD' : 'experience'}
</Text>
!
</Text>
))}
</Stack> */}
</>
</Box>
);
};
66 changes: 59 additions & 7 deletions packages/client/src/components/BattleOutcomeModal.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
Box,
Button,
Divider,
Modal,
ModalBody,
ModalCloseButton,
Expand All @@ -11,10 +12,14 @@ import {
Text,
VStack,
} from '@chakra-ui/react';
import { useComponentValue } from '@latticexyz/react';
import { encodeEntity } from '@latticexyz/store-sync/recs';
import { useCallback, useMemo } from 'react';
import { Link } from 'react-router-dom';

import { useCharacter } from '../contexts/CharacterContext';
import { useMapNavigation } from '../contexts/MapNavigationContext';
import { useMUD } from '../contexts/MUDContext';
import { BATTLE_OUTCOME_SEEN_KEY } from '../utils/constants';
import { type CombatOutcomeType } from '../utils/types';

Expand All @@ -29,8 +34,11 @@ export const BattleOutcomeModal: React.FC<BattleOutcomeModalProps> = ({
onClose,
battleOutcome,
}): JSX.Element => {
const {
components: { Levels },
} = useMUD();
const { character } = useCharacter();
const { allMonsters, otherPlayers } = useMapNavigation();
const { allMonsters, otherCharactersOnTile } = useMapNavigation();

const opponent = useMemo(() => {
if (!character) return null;
Expand All @@ -46,21 +54,35 @@ export const BattleOutcomeModal: React.FC<BattleOutcomeModalProps> = ({
return monsterOpponent;
}

const characterOpponent = otherPlayers.find(
player => player.characterId === opponent,
const characterOpponent = otherCharactersOnTile.find(
c => c.characterId === opponent,
);
if (characterOpponent) {
return characterOpponent;
}

return null;
}, [allMonsters, battleOutcome, character, otherPlayers]);
}, [allMonsters, battleOutcome, character, otherCharactersOnTile]);

const onAcknowledge = useCallback(() => {
localStorage.setItem(BATTLE_OUTCOME_SEEN_KEY, battleOutcome.encounterId);
onClose();
}, [battleOutcome.encounterId, onClose]);

const nextLevelXpRequirement =
useComponentValue(
Levels,
character
? encodeEntity({ level: 'uint256' }, { level: BigInt(character.level) })
: undefined,
)?.experience ?? BigInt(0);

const canLevel = useMemo(() => {
if (!character) return false;
if (nextLevelXpRequirement === BigInt(0)) return false;
return BigInt(character.experience) >= nextLevelXpRequirement;
}, [character, nextLevelXpRequirement]);

if (!character) {
return <Box />;
}
Expand All @@ -75,15 +97,15 @@ export const BattleOutcomeModal: React.FC<BattleOutcomeModalProps> = ({
{winner === character.characterId ? 'Victory!' : 'Defeat...'}
</ModalHeader>
<ModalCloseButton />
<ModalBody p={4}>
<VStack alignItems="center" pb={8} spacing={4}>
<ModalBody p={4} textAlign="center">
<VStack alignItems="center" pb={canLevel ? 4 : 8} spacing={4}>
<Text>
{winner === character.characterId
? `You defeated ${opponent?.name}!`
: `You lost to ${opponent?.name}.`}
</Text>
{winner === character.characterId && (
<Text textAlign="center">
<Text>
You earned{' '}
<Text as="span" color="green" fontWeight="bold">
{expDropped}
Expand All @@ -106,6 +128,36 @@ export const BattleOutcomeModal: React.FC<BattleOutcomeModalProps> = ({
</Text>
)}
</VStack>
{canLevel && (
<VStack alignItems="center" pb={8} spacing={4}>
<Divider />
<Text fontWeight="bold">
You have enough experience to level up!
</Text>
<Text>
Leveling involves spending{' '}
<Text as="span" fontWeight="bold">
2 ability points
</Text>{' '}
on your character&apos;s stats.
</Text>
<Text>
To level up, visit your{' '}
<Text
as={Link}
color="blue"
to={`/characters/${character?.characterId}`}
onClick={onAcknowledge}
_hover={{
textDecoration: 'underline',
}}
>
character page
</Text>
.
</Text>
</VStack>
)}
</ModalBody>
<ModalFooter>
<Button onClick={onAcknowledge}>Continue</Button>
Expand Down
10 changes: 8 additions & 2 deletions packages/client/src/components/ConnectWalletModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
Text,
VStack,
} from '@chakra-ui/react';
import { useMemo } from 'react';
import { useEffect, useMemo } from 'react';
import { useAccount, useWalletClient } from 'wagmi';

import { useMUD } from '../contexts/MUDContext';
Expand All @@ -26,7 +26,13 @@ export const ConnectWalletModal = ({
}): JSX.Element => {
const { data: externalWalletClient } = useWalletClient();
const { isConnected, address } = useAccount();
const { burnerAddress } = useMUD();
const { burnerAddress, delegatorAddress } = useMUD();

useEffect(() => {
if (delegatorAddress && isConnected) {
onClose();
}
}, [delegatorAddress, isConnected, onClose]);

const bodyContent = useMemo(() => {
if (address && externalWalletClient && isConnected) {
Expand Down
6 changes: 4 additions & 2 deletions packages/client/src/components/EditCharacterModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export const EditCharacterModal: React.FC<EditCharacterModalProps> = ({
onClose,
tokenId,
}): JSX.Element => {
const { renderSuccess, renderError } = useToast();
const { renderError, renderSuccess, renderWarning } = useToast();

const {
delegatorAddress,
Expand Down Expand Up @@ -106,7 +106,8 @@ export const EditCharacterModal: React.FC<EditCharacterModalProps> = ({

if (!((avatar || image) && newDescription && newName)) {
setShowError(true);
throw new Error('Missing required fields.');
renderWarning('Missing required fields.');
return;
}

const avatarCid = await onUpload();
Expand Down Expand Up @@ -180,6 +181,7 @@ export const EditCharacterModal: React.FC<EditCharacterModalProps> = ({
refreshCharacter,
renderError,
renderSuccess,
renderWarning,
tokenId,
updateTokenUri,
],
Expand Down
2 changes: 1 addition & 1 deletion packages/client/src/components/LevelingPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ export const LevelingPanel = ({
</Text>
<VStack w="100%">
<HStack justify="space-between" w="100%">
<Text>HP - Hit</Text>
<Text>HP - Hit Points</Text>
<Text>
{character.currentHp}/{character.baseHp}
</Text>
Expand Down
6 changes: 3 additions & 3 deletions packages/client/src/components/MapPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ const SAFE_ZONE_AREA = {

export const MapPanel = (): JSX.Element => {
const {
allSpawnedCharacters,
currentBattle,
isRefreshing,
isSpawned,
isSpawning,
onMove,
onSpawn,
otherPlayers,
position,
} = useMapNavigation();

Expand Down Expand Up @@ -86,8 +86,8 @@ export const MapPanel = (): JSX.Element => {
</HStack>
)}
<Text size="xs">
Dark Cave - {otherPlayers.length + 1} Player
{otherPlayers.length + 1 > 1 ? 's' : ''}
Dark Cave - {allSpawnedCharacters.length} Player
{allSpawnedCharacters.length === 1 ? '' : 's'}
</Text>
</Stack>
</Box>
Expand Down
10 changes: 5 additions & 5 deletions packages/client/src/components/StatsPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,13 @@ export const StatsPanel = (): JSX.Element => {
const levelPercent = useMemo(() => {
if (!character) return 0;

const xpSinceLastLevel =
const xpEarnedSinceLastLevel =
BigInt(character.experience) - currentLevelXpRequirement;
const xpNeededSinceLastLevel =
nextLevelXpRequirement - currentLevelXpRequirement;

const percent =
(100 * Number(xpSinceLastLevel)) / Number(nextLevelXpRequirement);
(100 * Number(xpEarnedSinceLastLevel)) / Number(xpNeededSinceLastLevel);
return percent > 100 ? 100 : percent;
}, [character, currentLevelXpRequirement, nextLevelXpRequirement]);

Expand Down Expand Up @@ -154,9 +156,7 @@ export const StatsPanel = (): JSX.Element => {
BigInt(experience) >= nextLevelXpRequirement ? 'bold' : 'normal'
}
>
{BigInt(experience) >= nextLevelXpRequirement
? nextLevelXpRequirement.toString()
: experience}
{experience}
</Text>
/{nextLevelXpRequirement.toString()}
</Text>
Expand Down
Loading