Skip to content

Commit 7bd8207

Browse files
committed
Improve battles display
1 parent 1f5b4f5 commit 7bd8207

File tree

6 files changed

+66
-21
lines changed

6 files changed

+66
-21
lines changed

src/UI/components/ErrorHandler.tsx

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export function ErrorHandler({error}: {error: Error}) {
2+
return <div className='w-full h-full flex flex-col justify-center items-center'>
3+
<h1 className='text-2xl font-bold'>Something went wrong</h1>
4+
<p className='text-lg'>{error.message}</p>
5+
</div>;
6+
}
7+

src/UI/components/rooms/BattleRoom.tsx

+9-2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import { cn } from '@/lib/utils';
1010
import BattleWindow from './battle/Battle';
1111
import BattleControls from './battle/BattleControls';
1212
import Calcs from './battle/Calcs';
13+
import { ErrorBoundary } from 'react-error-boundary';
14+
import { ErrorHandler } from '../ErrorHandler';
1315

1416
export default function BattleRoom(props: Readonly<HTMLAttributes<HTMLDivElement>>) {
1517
const [userListOpen, setUserListOpen] = useState(false);
@@ -23,9 +25,14 @@ export default function BattleRoom(props: Readonly<HTMLAttributes<HTMLDivElement
2325
)}
2426
>
2527
<div className="flex flex-col w-3/4 justify-center items-center gap-8 p-8">
26-
<BattleWindow/>
28+
<ErrorBoundary FallbackComponent={ErrorHandler}>
29+
<BattleWindow/>
30+
</ErrorBoundary>
2731
<div className="flex flex-col w-full h-full justify-center items-center bg-gray-125">
28-
<BattleControls/>
32+
33+
<ErrorBoundary FallbackComponent={ErrorHandler}>
34+
<BattleControls/>
35+
</ErrorBoundary>
2936
</div>
3037
<Calcs/>
3138
</div>

src/UI/components/rooms/battle/Battle.tsx

+41-14
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,32 @@
11
import { assert, cn } from '@/lib/utils';
2-
import { HTMLAttributes } from 'react';
2+
import { HTMLAttributes, useEffect, useMemo, useState } from 'react';
33
import { useClientContext } from '../../single/ClientContext';
44
import { BattleRoom } from '@/client/room/battleRoom';
55
import { Icons, Sprites } from '@pkmn/img';
6-
import type { Pokemon as PokemonType } from '@pkmn/client';
6+
import type { Pokemon as PokemonType, Side } from '@pkmn/client';
77
import { Username } from '../../Username';
8-
import { useClientStore } from '@/client/client';
8+
import { client, useClientStore } from '@/client/client';
99

1010

11+
function PokeballIcon() {
12+
const item = Icons.getPokeball('pokeball')!;
13+
return (
14+
<span
15+
style={{
16+
background:
17+
`transparent url("${item.url}") no-repeat scroll ${item.left}px ${item.top}px`,
18+
width: '40px',
19+
height: '30px',
20+
border: 0,
21+
display: 'inline-block',
22+
imageRendering: 'pixelated',
23+
verticalAlign: '-7px',
24+
}}
25+
>
26+
</span>
27+
);
28+
}
29+
1130
function PokemonIcon({ pokemon }: Readonly<{
1231
pokemon: PokemonType | null,
1332
}>) {
@@ -40,31 +59,39 @@ function PokemonSprite({ pokemon, side }: Readonly<{
4059
return <img src={data.url} width={data.w} height={data.h} data-name={pokemon.name} alt={`${pokemon.name} sprite`} title={pokemon.name} />;
4160
}
4261

62+
function RenderTeam({ player }: Readonly<{ player: Side }>) {
63+
console.log(player);
64+
return <div className='w-[120px] grid grid-cols-2 md:grid-cols-3 p-2'>
65+
{player.team.map((pokemon, idx) => pokemon && <PokemonIcon key={idx} pokemon={pokemon} />)}
66+
{player.team.length < player.totalPokemon && [...Array(player.totalPokemon - player.team.length)].map((_, idx) => <PokeballIcon key={idx} />)}
67+
{/* {player.active.map((pokemon, idx) => pokemon && <PokemonSprite key={idx} pokemon={pokemon} />)} */}
68+
</div>;
69+
}
70+
4371
export default function BattleWindow(props: Readonly<HTMLAttributes<HTMLDivElement>>) {
4472
const battle = useClientStore(state => state.currentRoom) as BattleRoom;
4573
assert(battle?.type === 'battle', 'Trying to render BattleWindow in a room that is not a BattleRoom');
46-
return <div className={cn(props.className, 'h-full w-full bg-gray-125 grid grid-cols-12')}>
47-
<div className='col-span-2 flex flex-col items-center' id="side-1">
74+
return <div className={cn(props.className, 'h-full w-full bg-gray-125 flex flex-row')}>
75+
<div className=' flex flex-col items-center' id="side-1">
4876
<div className='text-center w-full'>
4977
<Username bold user={' ' + battle.battle.p1.name} />
5078
</div>
5179
<img src={Sprites.getAvatar(battle.battle.p1.avatar)} alt={`${battle.battle.p1.name}'s avatar`}/>
52-
<div className='w-[120px] grid grid-cols-3'>
53-
{battle.battle.p1.team.map((pokemon, idx) => pokemon && <PokemonIcon key={idx} pokemon={pokemon} />)}
80+
<div className='w-full'>
81+
<RenderTeam player={battle.battle.p1} />
5482
</div>
5583
</div>
56-
<div className='col-span-8 h-full bg-gray-100 flex justify-around items-center' id="battle">
57-
{battle.battle.p1.active.map((pokemon, idx) => pokemon && <PokemonSprite key={idx} pokemon={pokemon} side='p1' />)}
58-
{battle.battle.p2.active.map((pokemon, idx) => pokemon && <PokemonSprite key={idx} pokemon={pokemon} side='p2' />)}
59-
84+
<div className='h-full w-full bg-gray-100 flex justify-around items-center' id="battle">
85+
{battle.battle.p1.active.map((pokemon, idx) => pokemon && <PokemonSprite key={idx} pokemon={pokemon} side='p1'/>)}
86+
{battle.battle.p2.active.map((pokemon, idx) => pokemon && <PokemonSprite key={idx} pokemon={pokemon} side='p2'/>)}
6087
</div>
61-
<div className='col-span-2 flex flex-col items-center' id="side-2">
88+
<div className='flex flex-col items-center' id="side-2">
6289
<div className='text-center w-full'>
6390
<Username bold user={' ' + battle.battle.p2.name} />
6491
</div>
6592
<img src={Sprites.getAvatar(battle.battle.p2.avatar)} />
66-
<div className='w-[120px] grid grid-cols-3'>
67-
{battle.battle.p2.team.map((pokemon, idx) => pokemon && <PokemonIcon key={idx} pokemon={pokemon} />)}
93+
<div className='w-full'>
94+
<RenderTeam player={battle.battle.p2} />
6895
</div>
6996
</div>
7097
</div>;

src/UI/components/rooms/battle/BattleControls.tsx

+4-2
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@ export default function BattleControls(props: Readonly<HTMLAttributes<HTMLDivEle
2929
return <div>Loading...</div>;
3030
}
3131

32-
switch (req.requestType) {
32+
const requestType = req.requestType;
33+
if (!requestType) { return <WaitRequest req={req} battle={battle} />; }
34+
switch (requestType) {
3335
case 'move':
3436
return <MoveRequest req={req} battle={battle} />;
3537
case 'switch':
@@ -39,7 +41,7 @@ export default function BattleControls(props: Readonly<HTMLAttributes<HTMLDivEle
3941
case 'wait':
4042
return <WaitRequest req={req} battle={battle} />;
4143
default:
42-
assertNever(req);
44+
assertNever(requestType, 'Unexpected request type: ' + requestType);
4345
return null;
4446
}
4547
}

src/client/room/battleRoom.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@ export class BattleRoom extends Room {
2828
}
2929

3030
setFormatter(perspective: SideID) {
31-
assert(!this.formatter, 'Trying to create formatter twice');
31+
if (this.formatter) {
32+
return;
33+
}
3234
this.perspective = perspective;
3335
this.formatter = new LogFormatter(perspective, this.battle);
3436
}

src/lib/utils.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ export function cn(...inputs: ClassValue[]) {
2121
return twMerge(clsx(inputs))
2222
}
2323

24-
export function assertNever(x: never): asserts x is never {
25-
throw new AssertionError({message: 'Unexpected object: ' + x, expected: 'never'});
24+
export function assertNever(x: never, message?: string): asserts x is never {
25+
throw new AssertionError({message: message ?? 'Unexpected object: ' + x, expected: 'never'});
2626
}
2727

2828
export function assert(condition: any, message?: string): asserts condition {

0 commit comments

Comments
 (0)