Skip to content

Commit

Permalink
Added player slots system
Browse files Browse the repository at this point in the history
  • Loading branch information
Zequez committed Nov 19, 2024
1 parent 6d2669b commit 2060cd4
Show file tree
Hide file tree
Showing 38 changed files with 741 additions and 238 deletions.
408 changes: 368 additions & 40 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"build": "vite build",
"build-check": "npm run check && vite build",
"check": "svelte-check --tsconfig ./tsconfig.json --compiler-warnings a11y-click-events-have-key-events:ignore,a11y-mouse-events-have-key-events:ignore,a11y-no-static-element-interactions:ignore",
"check:watch": "svelte-check --tsconfig ./tsconfig.json --compiler-warnings a11y-click-events-have-key-events:ignore,a11y-mouse-events-have-key-events:ignore,a11y-no-static-element-interactions:ignore --watch",
"package": "rm -f dist.zip && npm run build && cd dist && bestzip ../dist.zip *"
},
"dependencies": {
Expand All @@ -30,7 +31,8 @@
"svelte-moveable": "0.45.0",
"svelte-portal": "^2.2.1",
"tslib": "^2.8.0",
"uuid": "^10.0.0"
"uuid": "^10.0.0",
"vite-plugin-checker": "^0.8.0"
},
"devDependencies": {
"@sveltejs/vite-plugin-svelte": "^3.1.1",
Expand Down
2 changes: 1 addition & 1 deletion ui/src/GameSpace/GameSpace.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@
<PeopleBar
canJoinGame={$canJoinGame}
canLeaveGame={$canLeaveGame}
players={$state.players}
playersSlots={$state.playersSlots}
participants={$participants}
onJoin={() => gameSpace.joinGame()}
onLeave={() => gameSpace.leaveGame()}
Expand Down
28 changes: 19 additions & 9 deletions ui/src/GameSpace/elements/Dice/Element.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,17 @@
export let isLocked: boolean;
$: state = gameSpace.state;
$: canPlay = $state.players.includes(gameSpace.pubKey) && !isLocked;
$: slotIndex = $state.playersSlots.findIndex((s) => s.pubKey === gameSpace.pubKey);
$: playerSlot = slotIndex !== -1 ? $state.playersSlots[slotIndex] : null;
$: canPlay = playerSlot && !isLocked;
function handleContainerClick() {
if (!canPlay) return;
const rolledDice = el.dice.map((d) => ({
faces: d.faces,
result: Math.floor(Math.random() * d.faces + 1),
}));
const roll = { dice: rolledDice, player: gameSpace.pubKey };
const roll = { dice: rolledDice, playerSlot: slotIndex };
const rolls = el.rolls.concat([roll]);
// Optimization for snappier interface
lastRoll = roll;
Expand All @@ -28,7 +30,7 @@
}, 50);
}
let lastRoll: { dice: { faces: number; result: number }[]; player: string };
let lastRoll: { dice: { faces: number; result: number }[]; playerSlot: number };
$: lastRoll = el.rolls[el.rolls.length - 1];
function diceChanged(rolls: Roll[], dice: DieType[]) {
const last = rolls[rolls.length - 1];
Expand All @@ -54,9 +56,12 @@
>
<div class="absolute z-10 inset-0 rounded-md bg-[url('/noise20.png')] opacity-25"></div>
{#if showLastRoll}
<div class="z-20 absolute -top-2 -left-2"
><AgentAvatar pubKey={lastRoll.player} size={28} /></div
>
{@const playerSlot = $state.playersSlots[lastRoll.playerSlot]}
{#if playerSlot && playerSlot.pubKey}
<div class="z-20 absolute -top-2 -left-2"
><AgentAvatar pubKey={playerSlot.pubKey} size={28} /></div
>
{/if}
{/if}
<div class="relative z-20 flexcc content-center h-full flex-wrap">
{#if showLastRoll}
Expand All @@ -82,10 +87,15 @@
style={`max-height: ${el.height}px;`}
>
{#each el.rolls.toReversed() as roll}
{@const playerSlot = $state.playersSlots[roll.playerSlot]}

<div>
<PlayerName class="font-bold" agentPubKey={roll.player} />: {roll.dice
.map((r) => `${r.result}/${r.faces}`)
.join(', ')}
{#if playerSlot && playerSlot.pubKey}
<PlayerName class="font-bold" agentPubKey={playerSlot.pubKey} />:
{:else}
<span>Player {roll.playerSlot + 1}:</span>
{/if}
{roll.dice.map((r) => `${r.result}/${r.faces}`).join(', ')}
</div>
{/each}
</div>
Expand Down
4 changes: 2 additions & 2 deletions ui/src/GameSpace/elements/Dice/config.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { type DiceElement } from './type';
import { type DiceElement, VERSION } from './type';

const config = {
type: 'Dice',
version: 1,
version: VERSION,
label: 'Dice',
icon: '🎲',
build: (): Partial<DiceElement> => ({
Expand Down
6 changes: 4 additions & 2 deletions ui/src/GameSpace/elements/Dice/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ import { type GElementBase } from '~/store';

export type Die = { faces: number };
export type RolledDie = { faces: number; result: number };
export type Roll = { dice: RolledDie[]; player: string };
export type Roll = { dice: RolledDie[]; playerSlot: number };

export const VERSION = 2;

export type DiceElement = GElementBase & {
type: 'Dice';
version: number;
version: 2;
dice: Die[];
rolls: Roll[];
};
34 changes: 23 additions & 11 deletions ui/src/GameSpace/elements/PlayerPiece/Element.svelte
Original file line number Diff line number Diff line change
@@ -1,23 +1,35 @@
<script lang="ts">
import cx from 'classnames';
import type { PlayerPieceElement } from './type';
import { type GameSpaceSyn } from '~/store';
import AgentAvatar from '~/shared/AgentAvatar.svelte';
export let el: Pick<PlayerPieceElement, 'width' | 'height' | 'agent' | 'colorRing'>;
// export let gameSpace: any = null;
export let el: Pick<PlayerPieceElement, 'width' | 'height' | 'playerSlot'>;
export let gameSpace: GameSpaceSyn;
let klass: string = '';
export { klass as class };
export let style = '';
$$restProps; // This prevents Svelte warnings from unused props
$: size = Math.min(el.width, el.height) - (el.colorRing ? 6 : 0);
$: state = gameSpace.state;
$: playerInfo = $state ? $state.playersSlots[el.playerSlot] : null;
$: color = playerInfo?.color || '#000';
$: contrastColor = '#fff';
$: size = Math.min(el.width, el.height) - (playerInfo.color ? 6 : 0);
$: {
console.log(el);
}
</script>

<AgentAvatar
pubKey={el.agent}
{size}
class={cx(klass, 'w-full h-full', {
'inline-block outline-solid outline-red outline-3 m[3px]': !!el.colorRing,
})}
style={`${style} ${el.colorRing ? `outline-color: ${el.colorRing}` : ''}`}
/>
<div
class={`relative flexcc rounded-full text-white b-2 b-black/10 shadow-md ${klass}`}
style={`width: ${size}px; height: ${size}px; background-color: ${color}; color: ${contrastColor}; ${style}`}
>
{#if playerInfo?.pubKey}
<AgentAvatar pubKey={playerInfo.pubKey} size={size - 4} class="relative z-20" />
{/if}
<div class="absolute z-10 inset-0 flexcc mix-blend-difference opacity-50">{el.playerSlot + 1}</div
>
</div>
18 changes: 0 additions & 18 deletions ui/src/GameSpace/elements/PlayerPiece/config.ts

This file was deleted.

1 change: 0 additions & 1 deletion ui/src/GameSpace/elements/PlayerPiece/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
export { default as ConfigMenu } from './ConfigMenu.svelte';
export { default as Element } from './Element.svelte';
export { default as config } from './config';

export type { PlayerPieceElement as ElType } from './type.ts';
7 changes: 4 additions & 3 deletions ui/src/GameSpace/elements/PlayerPiece/type.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { type GElementBase } from '~/store';

export const VERSION = 3;

export type PlayerPieceElement = GElementBase & {
type: 'PlayerPiece';
version: number;
agent: string;
colorRing: string;
version: 3;
playerSlot: number;
};
6 changes: 0 additions & 6 deletions ui/src/GameSpace/elements/PlayerPieceSource/ConfigMenu.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,6 @@
onInput={(v) => onUpdate({ canOnlyPickOwnPiece: v })}
disabled={el.lock.config}
/>
<Checkbox
value={el.colorCoded}
label="Color coded"
onInput={(v) => onUpdate({ colorCoded: v })}
disabled={el.lock.config}
/>
<IntegerInput
value={el.limit}
label="Limit"
Expand Down
62 changes: 30 additions & 32 deletions ui/src/GameSpace/elements/PlayerPieceSource/Element.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import { Element as PlayerPieceEl, type ElType as PlayerPieceType } from '../PlayerPiece';
import type { PlayerPieceSourceElement } from './type';
import { playerColor as playerColorUtil } from './utils';
export let gameSpace: GameSpaceSyn;
export let el: PlayerPieceSourceElement;
Expand All @@ -26,14 +25,14 @@
let prevCreatedPieces: string[] = [];
$: {
if (el.createdPieces !== prevCreatedPieces) {
const result: { [key: string]: number } = {};
const result: { [key: number]: number } = {};
el.createdPieces.forEach((pieceUuid) => {
const piece = elements.find((e) => e.uuid === pieceUuid) as PlayerPieceType;
if (piece) {
if (!result[piece.agent]) {
result[piece.agent] = 0;
if (!result[piece.playerSlot]) {
result[piece.playerSlot] = 0;
}
result[piece.agent]++;
result[piece.playerSlot]++;
}
playedPiecesCountByAgent = result;
});
Expand Down Expand Up @@ -77,15 +76,13 @@
}, 50);
}
$: playerColor = (i: number) => (el.colorCoded ? playerColorUtil(i, $state.players.length) : '');
type DragState = { agent: string; x: number; y: number } | null;
type DragState = { playerSlotIndex: number; x: number; y: number } | null;
let dragState: DragState = null;
function handleDragStart(ev: MouseEvent, agent: string) {
function handleDragStart(ev: MouseEvent, playerSlotIndex: number) {
if (isLocked) return;
ev.stopPropagation();
ev.preventDefault();
dragState = { agent, x: ev.clientX, y: ev.clientY };
dragState = { playerSlotIndex, x: ev.clientX, y: ev.clientY };
function handleMouseMoving(e: MouseEvent) {
const x = e.clientX;
Expand All @@ -96,8 +93,7 @@
function handleMouseUp() {
const surfacePos = gameSpace.getSurfaceCoordinates(dragState.x, dragState.y);
if (surfacePos) {
const colorRing = playerColor($state.players.indexOf(dragState.agent));
handleAddPiece(surfacePos, dragState.agent, colorRing);
handleAddPiece(surfacePos, dragState.playerSlotIndex);
}
dragState = null;
document.body.classList.remove('cursor-grabbing');
Expand All @@ -110,18 +106,17 @@
document.body.classList.add('cursor-grabbing');
}
async function handleAddPiece(pos: { x: number; y: number }, agent: string, colorRing: string) {
async function handleAddPiece(pos: { x: number; y: number }, playerSlotIndex: number) {
const newEl: PlayerPieceType = {
type: 'PlayerPiece' as 'PlayerPiece',
version: 2 as 2,
version: 3 as 3,
width: el.size,
height: el.size,
x: pos.x,
y: pos.y,
z: gameSpace.topZ(),
rotation: 0,
agent: agent,
colorRing: colorRing,
playerSlot: playerSlotIndex,
wals: [],
lock: {
position: false,
Expand All @@ -147,43 +142,46 @@
</script>

<div class={cx(klass, 'h-full w-full bg-main-900 rounded-md p2')}>
{#if $state.players.length === 0}
{#if $state.playersSlots.filter((s) => s.pubKey).length === 0}
<div class="opacity-70 flexcc h-full w-full">No players</div>
{/if}
<div
class={cx(`w-full grid gap-2`, {
class={cx(`w-full grid gap-x-2`, {
'grid-cols-[auto_1fr]': el.showNames,
'grid-cols-1': !el.showNames,
})}
>
{#each $state.players as player, i}
{@const hasPiecesLeft = el.limit
? el.limit - (playedPiecesCountByAgent[player] || 0) > 0
: true}
{@const ownPieces = player === gameSpace.pubKey}
{#each $state.playersSlots as playerSlot, i}
{@const playerPubKey = playerSlot.pubKey}
{@const hasPiecesLeft = el.limit ? el.limit - (playedPiecesCountByAgent[i] || 0) > 0 : true}
{@const ownPieces = playerPubKey === gameSpace.pubKey}
{@const isAllowedToGrab =
hasPiecesLeft &&
((ownPieces && el.canOnlyPickOwnPiece) || !el.canOnlyPickOwnPiece) &&
!isLocked}
{#if el.showNames}
<div class="flexce text-xs">
<PlayerName agentPubKey={player} />
{#if playerPubKey}
<PlayerName agentPubKey={playerPubKey} />
{:else}
Player {i + 1}
{/if}
</div>
{/if}
<div
class={cx('flexcs min-w-0 overflow-hidden', {
class={cx('flexcs min-w-0 overflow-hidden py1.5', {
'cursor-not-allowed': !isAllowedToGrab,
'cursor-grab': isAllowedToGrab,
})}
draggable={true}
on:dragstart={(ev) => (isAllowedToGrab ? handleDragStart(ev, player) : null)}
on:dragstart={(ev) => (isAllowedToGrab ? handleDragStart(ev, i) : null)}
bind:this={piecesContainer[i]}
>
<div class="flex">
{#each { length: el.limit || 1 } as _, j}
{@const isPlayed = el.limit
? (playedPiecesCountByAgent[player] || 0) +
(dragState && dragState.agent === player ? 1 : 0) >=
? (playedPiecesCountByAgent[i] || 0) +
(dragState && dragState.playerSlotIndex === i ? 1 : 0) >=
el.limit - j
: false}
<div
Expand All @@ -193,11 +191,11 @@
style={j > 0 ? `margin-left: var(--overlap); z-index: ${10 + j}` : ''}
>
<PlayerPieceEl
{gameSpace}
el={{
width: el.size,
height: el.size,
agent: player,
colorRing: el.colorCoded ? playerColor(i) : '',
playerSlot: i,
}}
/>
</div>
Expand All @@ -209,13 +207,13 @@
</div>

{#if dragState}
{@const colorRing = playerColor($state.players.indexOf(dragState.agent))}
<Portal target="body">
<div class="fixed z-100 top-0 left-0">
<PlayerPieceEl
class="inline-block fixed z-100 cursor-grabbing"
style={`transform: translate(${dragState.x - el.size / 2}px, ${dragState.y - el.size / 2}px) scale(${zoomLevel});`}
el={{ width: el.size, height: el.size, agent: dragState.agent, colorRing }}
{gameSpace}
el={{ width: el.size, height: el.size, playerSlot: dragState.playerSlotIndex }}
/>
</div>
</Portal>
Expand Down
1 change: 0 additions & 1 deletion ui/src/GameSpace/elements/PlayerPieceSource/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ const config = {
width: 100,
showNames: false,
canOnlyPickOwnPiece: true,
colorCoded: true,
size: 30,
limit: 5,
createdPieces: [],
Expand Down
Loading

0 comments on commit 2060cd4

Please sign in to comment.