Compare commits
3 Commits
9919b97931
...
ce29ab72ae
| Author | SHA1 | Date | |
|---|---|---|---|
| ce29ab72ae | |||
| d203fe4141 | |||
| 67fdf66cd4 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -2,6 +2,8 @@
|
|||||||
.vinxi
|
.vinxi
|
||||||
*.db
|
*.db
|
||||||
|
|
||||||
|
.DS_STORE
|
||||||
|
|
||||||
# ---> Node
|
# ---> Node
|
||||||
# Logs
|
# Logs
|
||||||
logs
|
logs
|
||||||
|
|||||||
7
deploy
Executable file
7
deploy
Executable file
@@ -0,0 +1,7 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
branch=$(git branch --show-current)
|
||||||
|
|
||||||
|
git switch prod
|
||||||
|
git merge $branch
|
||||||
|
git push
|
||||||
|
git switch $brnach
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "games",
|
"name": "games",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "0.0.8",
|
"version": "0.0.9",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "pnpm --parallel dev",
|
"dev": "pnpm --parallel dev",
|
||||||
"build": "pnpm run -F client build",
|
"build": "pnpm run -F client build",
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
"@solid-primitives/scheduled": "^1.5.2",
|
"@solid-primitives/scheduled": "^1.5.2",
|
||||||
"@solid-primitives/storage": "^4.3.3",
|
"@solid-primitives/storage": "^4.3.3",
|
||||||
"@solidjs/router": "^0.15.3",
|
"@solidjs/router": "^0.15.3",
|
||||||
|
"color2k": "^2.0.3",
|
||||||
"js-cookie": "^3.0.5",
|
"js-cookie": "^3.0.5",
|
||||||
"kefir": "^3.8.8",
|
"kefir": "^3.8.8",
|
||||||
"kefir-bus": "^2.3.1",
|
"kefir-bus": "^2.3.1",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Component, Suspense } from "solid-js";
|
import { Component, Suspense } from "solid-js";
|
||||||
|
|
||||||
import type { Card } from "@games/shared/cards";
|
import type { Card } from "@games/shared/cards";
|
||||||
import { Clickable, Sizable, Stylable } from "./toolbox";
|
import { Clickable, Scalable, Stylable } from "./toolbox";
|
||||||
|
|
||||||
const cardToSvgFilename = (card: Card) => {
|
const cardToSvgFilename = (card: Card) => {
|
||||||
if (card.kind == "joker") {
|
if (card.kind == "joker") {
|
||||||
@@ -17,7 +17,12 @@ const cardToSvgFilename = (card: Card) => {
|
|||||||
}`;
|
}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const CARD_RATIO = 1.456730769;
|
||||||
|
export const BASE_CARD_WIDTH = 100;
|
||||||
|
|
||||||
export default ((props) => {
|
export default ((props) => {
|
||||||
|
const width = () => BASE_CARD_WIDTH * (props.scale ?? 1);
|
||||||
|
const height = () => width() * CARD_RATIO;
|
||||||
return (
|
return (
|
||||||
<Suspense>
|
<Suspense>
|
||||||
<img
|
<img
|
||||||
@@ -25,8 +30,8 @@ export default ((props) => {
|
|||||||
draggable={false}
|
draggable={false}
|
||||||
class={props.class}
|
class={props.class}
|
||||||
style={props.style}
|
style={props.style}
|
||||||
width={props.width ?? "100px"}
|
width={`${width()}px`}
|
||||||
height={props.height}
|
height={`${height()}px`}
|
||||||
src={
|
src={
|
||||||
props.face == "down"
|
props.face == "down"
|
||||||
? "/views/back.svg"
|
? "/views/back.svg"
|
||||||
@@ -45,5 +50,5 @@ export default ((props) => {
|
|||||||
) &
|
) &
|
||||||
Stylable &
|
Stylable &
|
||||||
Clickable &
|
Clickable &
|
||||||
Sizable
|
Scalable
|
||||||
>;
|
>;
|
||||||
|
|||||||
@@ -10,16 +10,16 @@ export default (props: { handCount: number }) => {
|
|||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
face="down"
|
face="down"
|
||||||
width="40px"
|
scale={0.4}
|
||||||
style={{
|
style={{
|
||||||
"margin-left": "-10px",
|
"margin-left": "-12px",
|
||||||
"margin-right": "-10px",
|
"margin-right": "-12px",
|
||||||
transform: `rotate(${
|
transform: `translate(0px, ${Math.pow(
|
||||||
midOffset * 0.2
|
Math.abs(midOffset),
|
||||||
}rad) translate(0px, ${
|
2
|
||||||
2 ** Math.abs(midOffset) * 2
|
)}px) rotate(${midOffset * 0.12}rad)`,
|
||||||
}px)`,
|
"min-width": "40px",
|
||||||
"box-shadow": "-4px 4px 4px rgba(0, 0, 0, 0.7)",
|
"box-shadow": "-4px 4px 6px rgba(0, 0, 0, 0.6)",
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ export default () => {
|
|||||||
<GameContext.Provider value={{ view, submitAction }}>
|
<GameContext.Provider value={{ view, submitAction }}>
|
||||||
<Pile
|
<Pile
|
||||||
count={view().deckCount}
|
count={view().deckCount}
|
||||||
|
scale={0.8}
|
||||||
class="cursor-pointer fixed center"
|
class="cursor-pointer fixed center"
|
||||||
onClick={() => submitAction({ type: "draw" })}
|
onClick={() => submitAction({ type: "draw" })}
|
||||||
/>
|
/>
|
||||||
@@ -34,7 +35,14 @@ export default () => {
|
|||||||
hand={view().myHand}
|
hand={view().myHand}
|
||||||
onClickCard={(card) => submitAction({ type: "discard", card })}
|
onClickCard={(card) => submitAction({ type: "discard", card })}
|
||||||
/>
|
/>
|
||||||
<div class="absolute tc text-align-center">
|
<div
|
||||||
|
class="absolute tc text-align-center"
|
||||||
|
style={{
|
||||||
|
"background-color":
|
||||||
|
view().playerTurn == me() ? "var(--yellow)" : "transparent",
|
||||||
|
color: view().playerTurn == me() ? "var(--dark)" : "var(--light)",
|
||||||
|
}}
|
||||||
|
>
|
||||||
It's{" "}
|
It's{" "}
|
||||||
<span class="font-bold">
|
<span class="font-bold">
|
||||||
{view().playerTurn == me()
|
{view().playerTurn == me()
|
||||||
@@ -51,17 +59,20 @@ export default () => {
|
|||||||
>
|
>
|
||||||
Quit
|
Quit
|
||||||
</button>
|
</button>
|
||||||
<For each={Object.entries(view().playerHandCounts)}>
|
<For
|
||||||
|
each={Object.entries(view().playerHandCounts).filter(([key, _]) =>
|
||||||
|
table.players().includes(key)
|
||||||
|
)}
|
||||||
|
>
|
||||||
{([playerKey, handCount], i) => (
|
{([playerKey, handCount], i) => (
|
||||||
<Portal
|
<Portal
|
||||||
mount={document.getElementById(`player-${playerKey}`)!}
|
mount={document.getElementById(`player-${playerKey}`)!}
|
||||||
ref={(ref) => {
|
ref={(ref) => {
|
||||||
|
console.log("Setting hand ref");
|
||||||
const midOffset =
|
const midOffset =
|
||||||
i() + 0.5 - Object.values(view().playerHandCounts).length / 2;
|
i() + 0.5 - Object.values(view().playerHandCounts).length / 2;
|
||||||
|
|
||||||
ref.style = `position: absolute; display: flex; justify-content: center; top: 65%; transform: translate(${Math.abs(
|
ref.style = `position: absolute; display: flex; justify-content: center; top: 65%;`;
|
||||||
midOffset * 0
|
|
||||||
)}px, 0px) rotate(${midOffset * 1}rad)`;
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<FannedHand handCount={handCount} />
|
<FannedHand handCount={handCount} />
|
||||||
|
|||||||
@@ -1,22 +1,63 @@
|
|||||||
import { Component, For, JSX, Show } from "solid-js";
|
import { Component, createMemo, For, JSX, Show } from "solid-js";
|
||||||
import Card from "./Card";
|
import Card, { BASE_CARD_WIDTH, CARD_RATIO } from "./Card";
|
||||||
|
import { desaturate } from "color2k";
|
||||||
|
|
||||||
import { Clickable, Stylable } from "./toolbox";
|
import { Clickable, hashColor, Scalable, Stylable } from "./toolbox";
|
||||||
|
|
||||||
|
const cardOffset = 0.35; // Small offset for the stack effect
|
||||||
|
|
||||||
export default ((props) => {
|
export default ((props) => {
|
||||||
|
const cards = createMemo(() => {
|
||||||
|
const numCards = Math.max(0, props.count - 1); // Subtract 1 for the top card
|
||||||
|
return Array.from({ length: numCards }, (_, i) => i).toReversed();
|
||||||
|
});
|
||||||
|
const width = () => BASE_CARD_WIDTH * (props.scale ?? 1);
|
||||||
|
const height = () => width() * CARD_RATIO;
|
||||||
|
const offset = () => cardOffset * (props.scale ?? 1);
|
||||||
return (
|
return (
|
||||||
<Show when={props.count > 0}>
|
<Show when={props.count > 0}>
|
||||||
<Card
|
<div
|
||||||
onClick={props.onClick}
|
style={{
|
||||||
style={props.style}
|
...props.style,
|
||||||
class={props.class + " shadow-lg shadow-black"}
|
}}
|
||||||
face="down"
|
class={props.class}
|
||||||
/>
|
>
|
||||||
|
<svg
|
||||||
|
class="absolute z-[-1]"
|
||||||
|
width={width() + cards().length * offset()}
|
||||||
|
height={height() + cards().length * offset()}
|
||||||
|
viewBox={`0 0 ${width() + cards().length * offset()} ${
|
||||||
|
height() + cards().length * offset()
|
||||||
|
}`}
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<For each={cards()}>
|
||||||
|
{(i) => {
|
||||||
|
const xOffset = (i * offset()) / 2;
|
||||||
|
const yOffset = i * offset();
|
||||||
|
const color = desaturate(hashColor(i), 0.9);
|
||||||
|
return (
|
||||||
|
<rect
|
||||||
|
x={xOffset}
|
||||||
|
y={yOffset}
|
||||||
|
width={width()}
|
||||||
|
height={height()}
|
||||||
|
rx="5" // Rounded corners
|
||||||
|
ry="5"
|
||||||
|
fill={color}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</For>
|
||||||
|
</svg>
|
||||||
|
<Card onClick={props.onClick} face="down" scale={props.scale} />
|
||||||
|
</div>
|
||||||
</Show>
|
</Show>
|
||||||
);
|
);
|
||||||
}) satisfies Component<
|
}) satisfies Component<
|
||||||
{
|
{
|
||||||
count: number;
|
count: number;
|
||||||
} & Stylable &
|
} & Stylable &
|
||||||
Clickable
|
Clickable &
|
||||||
|
Scalable
|
||||||
>;
|
>;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { createSignal, useContext } from "solid-js";
|
import { createSignal, onMount, useContext } from "solid-js";
|
||||||
import { playerColor } from "~/profile";
|
import { playerColor } from "~/profile";
|
||||||
import { TableContext } from "./Table";
|
import { TableContext } from "./Table";
|
||||||
import { Stylable } from "./toolbox";
|
import { Stylable } from "./toolbox";
|
||||||
@@ -16,6 +16,8 @@ export default (props: { playerKey: string } & Stylable) => {
|
|||||||
|
|
||||||
const game = useContext(GameContext);
|
const game = useContext(GameContext);
|
||||||
|
|
||||||
|
onMount(() => console.log("Player mounted"));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
id={`player-${props.playerKey}`}
|
id={`player-${props.playerKey}`}
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ export const TableContext = createContext<{
|
|||||||
sendWs: (msg: TWsIn) => void;
|
sendWs: (msg: TWsIn) => void;
|
||||||
wsEvents: Stream<TWsOut, any>;
|
wsEvents: Stream<TWsOut, any>;
|
||||||
playerNames: Store<{ [key: string]: string }>;
|
playerNames: Store<{ [key: string]: string }>;
|
||||||
|
players: Accessor<string[]>;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
export default (props: { tableKey: string }) => {
|
export default (props: { tableKey: string }) => {
|
||||||
@@ -75,7 +76,9 @@ export default (props: { tableKey: string }) => {
|
|||||||
|
|
||||||
createEffect(() => sendWs({ ready: ready() }));
|
createEffect(() => sendWs({ ready: ready() }));
|
||||||
createEffect(() => sendWs({ name: name() }));
|
createEffect(() => sendWs({ name: name() }));
|
||||||
const view = createObservable(gameEvents.map((evt) => evt.view));
|
const viewProp = gameEvents.map((evt) => evt.view).toProperty();
|
||||||
|
viewProp.log();
|
||||||
|
const view = createObservable(viewProp);
|
||||||
const results = createObservable<string>(
|
const results = createObservable<string>(
|
||||||
merge([
|
merge([
|
||||||
gameEvents
|
gameEvents
|
||||||
@@ -92,6 +95,7 @@ export default (props: { tableKey: string }) => {
|
|||||||
wsEvents,
|
wsEvents,
|
||||||
view,
|
view,
|
||||||
playerNames,
|
playerNames,
|
||||||
|
players,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div class="flex justify-around p-t-14">
|
<div class="flex justify-around p-t-14">
|
||||||
@@ -129,8 +133,8 @@ export default (props: { tableKey: string }) => {
|
|||||||
|
|
||||||
"top-40",
|
"top-40",
|
||||||
"bottom-20",
|
"bottom-20",
|
||||||
"left-10",
|
"left-[2%]",
|
||||||
"right-10"
|
"right-[2%]"
|
||||||
)}
|
)}
|
||||||
style={{
|
style={{
|
||||||
"border-radius": "50%",
|
"border-radius": "50%",
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import hash, { NotUndefined } from "object-hash";
|
||||||
import { JSX } from "solid-js";
|
import { JSX } from "solid-js";
|
||||||
|
|
||||||
export type Stylable = {
|
export type Stylable = {
|
||||||
@@ -15,7 +16,8 @@ export type Clickable = {
|
|||||||
| undefined;
|
| undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Sizable = {
|
export type Scalable = {
|
||||||
width?: string;
|
scale?: number;
|
||||||
height?: string;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const hashColor = (obj: NotUndefined) => `#${hash(obj).substring(0, 6)}`;
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ export const WS = Bus<
|
|||||||
|
|
||||||
const api = new Elysia({ prefix: "/api" })
|
const api = new Elysia({ prefix: "/api" })
|
||||||
.post("/whoami", async ({ cookie: { token } }) => {
|
.post("/whoami", async ({ cookie: { token } }) => {
|
||||||
console.log("WHOAMI");
|
|
||||||
let key: string | undefined;
|
let key: string | undefined;
|
||||||
if (token.value == null || (key = resolveToken(token.value)) == null) {
|
if (token.value == null || (key = resolveToken(token.value)) == null) {
|
||||||
const [newToken, newKey] = generateTokenAndKey();
|
const [newToken, newKey] = generateTokenAndKey();
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ export default (config: SimpleConfiguration) =>
|
|||||||
resolveQuit: () => null,
|
resolveQuit: () => null,
|
||||||
getResult: (state) =>
|
getResult: (state) =>
|
||||||
Object.entries(state.playerHands).find(
|
Object.entries(state.playerHands).find(
|
||||||
([_, hand]) => hand.length === 2
|
([_, hand]) => hand.length === 52
|
||||||
)?.[0],
|
)?.[0],
|
||||||
} satisfies Game<
|
} satisfies Game<
|
||||||
SimpleGameState,
|
SimpleGameState,
|
||||||
|
|||||||
8
pnpm-lock.yaml
generated
8
pnpm-lock.yaml
generated
@@ -29,6 +29,9 @@ importers:
|
|||||||
'@solidjs/router':
|
'@solidjs/router':
|
||||||
specifier: ^0.15.3
|
specifier: ^0.15.3
|
||||||
version: 0.15.3(solid-js@1.9.9)
|
version: 0.15.3(solid-js@1.9.9)
|
||||||
|
color2k:
|
||||||
|
specifier: ^2.0.3
|
||||||
|
version: 2.0.3
|
||||||
js-cookie:
|
js-cookie:
|
||||||
specifier: ^3.0.5
|
specifier: ^3.0.5
|
||||||
version: 3.0.5
|
version: 3.0.5
|
||||||
@@ -927,6 +930,9 @@ packages:
|
|||||||
color-name@1.1.4:
|
color-name@1.1.4:
|
||||||
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
|
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
|
||||||
|
|
||||||
|
color2k@2.0.3:
|
||||||
|
resolution: {integrity: sha512-zW190nQTIoXcGCaU08DvVNFTmQhUpnJfVuAKfWqUQkflXKpaDdpaYoM0iluLS9lgJNHyBF58KKA2FBEwkD7wog==}
|
||||||
|
|
||||||
colorette@2.0.20:
|
colorette@2.0.20:
|
||||||
resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==}
|
resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==}
|
||||||
|
|
||||||
@@ -3238,6 +3244,8 @@ snapshots:
|
|||||||
|
|
||||||
color-name@1.1.4: {}
|
color-name@1.1.4: {}
|
||||||
|
|
||||||
|
color2k@2.0.3: {}
|
||||||
|
|
||||||
colorette@2.0.20: {}
|
colorette@2.0.20: {}
|
||||||
|
|
||||||
compare-func@2.0.0:
|
compare-func@2.0.0:
|
||||||
|
|||||||
Reference in New Issue
Block a user