Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
f3c9529
pvp now happens outeside the safe zone
MrDeadCe11 Aug 8, 2024
7e54bd4
fixed some bugs
MrDeadCe11 Aug 8, 2024
7678344
pvp is working with no rewards
MrDeadCe11 Aug 8, 2024
e01b856
added resupply loot manager
MrDeadCe11 Aug 9, 2024
41c2c9b
resupply tests
MrDeadCe11 Aug 9, 2024
8cd658c
created encounter system to handle the creation of new encounters ins…
MrDeadCe11 Aug 9, 2024
58a018c
changed from match to encounter in all cases
MrDeadCe11 Aug 9, 2024
1610c23
added magic action, untested
MrDeadCe11 Aug 10, 2024
84add98
New contracts out files
ECWireless Aug 12, 2024
0fc3c41
Merge branch 'main' into feat/10-add-pvp-battling-to-frontend
ECWireless Aug 12, 2024
4f90eef
Fix match-related frontend issues
ECWireless Aug 12, 2024
b938ed6
group with the highest total agi goes first
MrDeadCe11 Aug 13, 2024
2cac69a
timer tested
MrDeadCe11 Aug 13, 2024
a39454b
removed extra table
MrDeadCe11 Aug 13, 2024
468409b
Render "opponent" for battles, rather than monster
ECWireless Aug 13, 2024
a4b6fe8
Prevent battling in Safety Zone
ECWireless Aug 13, 2024
efc1e7c
Merge branch 'pvp' into feat/10-add-pvp-battling-to-frontend
ECWireless Aug 13, 2024
e34021b
Minor changes to allow faster entity to go first
ECWireless Aug 13, 2024
ba9970e
Add EncounterSystem ABI to git
ECWireless Aug 13, 2024
f6ebff4
fixed pve drops with monster with higher agi
MrDeadCe11 Aug 13, 2024
4cf39df
magic attacks tested
MrDeadCe11 Aug 13, 2024
93821ca
modified test
MrDeadCe11 Aug 13, 2024
3dce131
added ability for players to remove themselves from the board with re…
MrDeadCe11 Aug 13, 2024
cde83bb
added missing files
MrDeadCe11 Aug 13, 2024
94e27e9
map position tweaks totally done with pvp branch now
MrDeadCe11 Aug 13, 2024
5b19423
Merge branch 'main' into pvp
MrDeadCe11 Aug 13, 2024
9758bda
Merge branch 'pvp' into feat/10-add-pvp-battling-to-frontend
ECWireless Aug 14, 2024
6499ec9
New EncounterSystem ABI
ECWireless Aug 14, 2024
285e028
fixed mob attack bug
MrDeadCe11 Aug 14, 2024
cb25a53
Add monstersOnTile to MapContext
ECWireless Aug 14, 2024
e306203
Merge branch 'pvp' into feat/10-add-pvp-battling-to-frontend
ECWireless Aug 14, 2024
79096c7
real mob attack fix
MrDeadCe11 Aug 14, 2024
12582c2
Merge branch 'pvp' into feat/10-add-pvp-battling-to-frontend
ECWireless Aug 14, 2024
df90048
Show PvP turns
ECWireless Aug 14, 2024
d1b6cae
Fix PvP character rendering issues
ECWireless Aug 14, 2024
a64b3a1
Add progress bar as turn timer
ECWireless Aug 14, 2024
4693dd0
Don't show turn timer unless PvP
ECWireless Aug 14, 2024
9ad0c64
Merge pull request #107 from raid-guild/feat/10-add-pvp-battling-to-f…
ECWireless Aug 14, 2024
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
2 changes: 1 addition & 1 deletion packages/client/src/Routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ const AppRoutes: React.FC = () => {
<Route path={HOME_PATH} element={<Welcome />} />
<Route path={CHARACTER_CREATION_PATH} element={<CharacterCreation />} />
<Route path={GAME_BOARD_PATH} element={<GameBoard />} />
<Route path="/characters/:characterId" element={<CharacterPage />} />
<Route path="/characters/:id" element={<CharacterPage />} />
<Route path={LEADERBOARD_PATH} element={<Leaderboard />} />
</Routes>
);
Expand Down
189 changes: 155 additions & 34 deletions packages/client/src/components/ActionsPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ import {
Button,
Divider,
HStack,
Progress,
Stack,
Text,
VStack,
} from '@chakra-ui/react';
import { useEffect, useMemo, useRef } from 'react';
import { useEffect, useMemo, useRef, useState } from 'react';
import { Link } from 'react-router-dom';
// eslint-disable-next-line import/no-named-as-default
import Typist from 'react-typist';
Expand All @@ -16,25 +17,28 @@ import { useBattle } from '../contexts/BattleContext';
import { useCharacter } from '../contexts/CharacterContext';
import { useMap } from '../contexts/MapContext';
import { useMovement } from '../contexts/MovementContext';
import { EncounterType } from '../utils/types';

export const ActionsPanel = (): JSX.Element => {
const {
isRefreshing: isRefreshingCharacter,
character,
equippedWeapons,
} = useCharacter();
const { aliveMonsters, isSpawned, position } = useMap();
const { isSpawned, monstersOnTile, position } = useMap();
const {
actionOutcomes,
attackingItemId,
currentBattle,
lastestBattleOutcome,
monsterOponent,
onAttack,
onContinueToBattleOutcome,
opponent,
} = useBattle();
const { isRefreshing: isRefreshingMap } = useMovement();

const [turnTimeLeft, setTurnTimeLeft] = useState<number>(32);

const parentDivRef = useRef<HTMLDivElement>(null);

useEffect(() => {
Expand Down Expand Up @@ -90,7 +94,7 @@ export const ActionsPanel = (): JSX.Element => {
);
}

if ((position.x !== 0 || position.y !== 0) && aliveMonsters.length === 0) {
if ((position.x !== 0 || position.y !== 0) && monstersOnTile.length === 0) {
return (
<Typist
avgTypingDelay={10}
Expand All @@ -109,7 +113,7 @@ export const ActionsPanel = (): JSX.Element => {
);
}

if ((position.x !== 0 || position.y !== 0) && aliveMonsters.length > 0) {
if ((position.x !== 0 || position.y !== 0) && monstersOnTile.length > 0) {
return (
<Typist
avgTypingDelay={10}
Expand All @@ -129,36 +133,143 @@ export const ActionsPanel = (): JSX.Element => {

return '';
}, [
aliveMonsters,
isRefreshingCharacter,
isRefreshingMap,
isSpawned,
monstersOnTile,
position,
]);

const userTurn = useMemo(() => {
if (!(character && currentBattle)) return false;

if (currentBattle.encounterType === EncounterType.PvE) {
return true;
}

const attackersTurn = Number(currentBattle.currentTurn) % 2 === 1;

if (attackersTurn && currentBattle.attackers.includes(character?.id)) {
return true;
}

if (!attackersTurn && currentBattle.defenders.includes(character?.id)) {
return true;
}

return false;
}, [character, currentBattle]);

const turnEndTime = useMemo(() => {
if (!currentBattle) return 0;

const _turnEndTime =
(BigInt(currentBattle.currentTurnTimer) + BigInt(32)) * BigInt(1000);

return Number(_turnEndTime);
}, [currentBattle]);

useEffect(() => {
if (turnEndTime - Date.now() < 0) {
setTurnTimeLeft(0);
} else {
setTurnTimeLeft(Math.floor((turnEndTime - Date.now()) / 1000));
}

const interval = setInterval(() => {
if (turnEndTime - Date.now() < 0) {
setTurnTimeLeft(0);
return;
}
setTurnTimeLeft(prev => prev - 1);
}, 1000);

return () => clearInterval(interval);
}, [turnEndTime, turnTimeLeft]);

const canAttack = useMemo(() => {
if (!currentBattle) return false;

if (currentBattle.encounterType === EncounterType.PvE) {
return true;
}

if (userTurn) {
return true;
}

if (turnTimeLeft === 0) {
return true;
}

return false;
}, [currentBattle, userTurn, turnTimeLeft]);

return (
<Box maxH="100%" overflowY="auto" pb={4} ref={parentDivRef}>
{!battleOver && currentBattle && equippedWeapons && monsterOponent && (
{!battleOver && currentBattle && equippedWeapons && opponent && (
<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>
{equippedWeapons.length === 0 && (
<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="blue"
to={`/characters/${character?.characterId}`}
_hover={{ textDecoration: 'underline' }}
>
character page
</Text>{' '}
and equip at least 1 item.
{currentBattle.encounterType === EncounterType.PvE && (
<Text p={{ base: 2, lg: 4 }} size="xs" textAlign="center">
<Text as="span" fontWeight="bold">
Choose your move!
</Text>
</Text>
)}
<HStack spacing={0} w="100%">

{currentBattle.encounterType === EncounterType.PvP && (
<>
{userTurn && (
<Text p={{ base: 2, lg: 4 }} size="xs" textAlign="center">
<Text as="span" fontWeight="bold">
Choose your move!
</Text>{' '}
You have {turnTimeLeft} seconds before your opponent can
attack.
</Text>
)}
{!userTurn && !canAttack && (
<Text p={{ base: 2, lg: 4 }} size="xs" textAlign="center">
It is your opponent&apos;s turn. But you can attack in{' '}
{turnTimeLeft} seconds.
</Text>
)}
{!userTurn && canAttack && (
<Text p={{ base: 2, lg: 4 }} size="xs" textAlign="center">
Your opponent took too long to make a move.{' '}
<Text as="span" fontWeight={700}>
You can now attack!
</Text>
</Text>
)}
{equippedWeapons.length === 0 && (
<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="blue"
to={`/characters/${character?.id}`}
_hover={{ textDecoration: 'underline' }}
>
character page
</Text>{' '}
and equip at least 1 item.
</Text>
)}
</>
)}
<HStack position="relative" spacing={0} w="100%">
{currentBattle.encounterType === EncounterType.PvP && (
<Progress
position="absolute"
size="xs"
top={-1}
value={(turnTimeLeft / 32) * 100}
variant="timer"
w="100%"
/>
)}
{equippedWeapons.map((item, index) => (
<Button
borderLeft={index === 0 ? 'none' : '2px'}
Expand All @@ -168,11 +279,21 @@ export const ActionsPanel = (): JSX.Element => {
? 'none'
: '2px'
}
isDisabled={attackingItemId !== null}
isDisabled={attackingItemId !== null || !canAttack}
isLoading={attackingItemId === item.tokenId}
key={`equipped-item-${index}`}
loadingText="Attacking..."
onClick={() => onAttack(item.tokenId)}
onClick={() =>
onAttack(
item.tokenId,
userTurn ||
currentBattle.encounterType === EncounterType.PvE
? currentBattle.currentTurn
: (
BigInt(currentBattle.currentTurn) + BigInt(1)
).toString(),
)
}
variant="outline"
w="100%"
>
Expand All @@ -185,7 +306,7 @@ export const ActionsPanel = (): JSX.Element => {
<Stack p={{ base: 2, lg: 4 }}>
{!currentBattle && actionText}

{monsterOponent &&
{opponent &&
actionOutcomes.map((action, i) => {
if (action.miss) {
return (
Expand All @@ -195,14 +316,14 @@ export const ActionsPanel = (): JSX.Element => {
key={`battle-action-${i}`}
stdTypingDelay={10}
>
{action.attackerId === character?.characterId ? (
{action.attackerId === character?.id ? (
<Text
key={`battle-action-${i}`}
size={{ base: 'xs', sm: 'sm', lg: 'md' }}
>
You missed{' '}
<Text as="span" color="green">
{monsterOponent.name}
{opponent.name}
</Text>
.
</Text>
Expand All @@ -212,7 +333,7 @@ export const ActionsPanel = (): JSX.Element => {
size={{ base: 'xs', sm: 'sm', lg: 'md' }}
>
<Text as="span" color="green">
{monsterOponent.name}
{opponent.name}
</Text>{' '}
missed you.
</Text>
Expand All @@ -230,11 +351,11 @@ export const ActionsPanel = (): JSX.Element => {
key={`battle-action-${i}`}
stdTypingDelay={10}
>
{action.attackerId === character?.characterId ? (
{action.attackerId === character?.id ? (
<Text size={{ base: 'xs', sm: 'sm', lg: 'md' }}>
{critText}You attacked{' '}
<Text as="span" color="green">
{monsterOponent?.name}
{opponent?.name}
</Text>{' '}
for{' '}
<Text as="span" color="red">
Expand All @@ -246,7 +367,7 @@ export const ActionsPanel = (): JSX.Element => {
<Text size={{ base: 'xs', sm: 'sm', lg: 'md' }}>
{critText}
<Text as="span" color="green">
{monsterOponent?.name}
{opponent?.name}
</Text>{' '}
attacked you for{' '}
<Text as="span" color="red">
Expand All @@ -272,7 +393,7 @@ export const ActionsPanel = (): JSX.Element => {
size={{ base: 'xs', sm: 'sm', lg: 'md' }}
textAlign="center"
>
{lastestBattleOutcome?.winner === character?.characterId
{lastestBattleOutcome?.winner === character?.id
? 'You won!'
: 'You lost...'}
</Text>
Expand Down
Loading