Compare commits
7 Commits
c6c1c63dcd
...
trunk
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
269abd237e | ||
|
|
52636017a5 | ||
|
|
9ca34540e8 | ||
|
|
6e312d5d2e | ||
|
|
11245bf4d8 | ||
|
|
62c3b7b557 | ||
|
|
01a05d2aa1 |
@@ -63,14 +63,15 @@ export function AppProvider({ children }: { children: ReactNode }) {
|
|||||||
|
|
||||||
const { dmID } = gameData;
|
const { dmID } = gameData;
|
||||||
const isDM = !!dmID;
|
const isDM = !!dmID;
|
||||||
|
const settings = { ...gameData.settings, ...localSettings };
|
||||||
|
|
||||||
const appInterface = {
|
const appInterface = {
|
||||||
gameData,
|
gameData,
|
||||||
isDM,
|
isDM,
|
||||||
noGame,
|
noGame,
|
||||||
selectCardIndex,
|
selectCardIndex,
|
||||||
settings: { ...gameData.settings, ...localSettings },
|
settings,
|
||||||
tilts: reduceTilts(gameData, localTilt),
|
tilts: reduceTilts(gameData, localTilt, settings),
|
||||||
emitFlip,
|
emitFlip,
|
||||||
emitSettings,
|
emitSettings,
|
||||||
emitRedraw,
|
emitRedraw,
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ export default function GamePage() {
|
|||||||
return noGame ? (
|
return noGame ? (
|
||||||
<NotFound />
|
<NotFound />
|
||||||
) : (
|
) : (
|
||||||
<main className="min-h-screen flex flex-col items-center justify-center gap-4 bg-[url('/img/table3.png')] bg-cover bg-center">
|
<main className="h-dvh flex flex-col items-center justify-center gap-4 bg-[url('/img/table3.png')] bg-cover bg-center">
|
||||||
<SpectatorLink />
|
<SpectatorLink />
|
||||||
<Settings />
|
<Settings />
|
||||||
<TarokkaGrid />
|
<TarokkaGrid />
|
||||||
|
|||||||
@@ -1,26 +1,14 @@
|
|||||||
import type { Metadata } from 'next';
|
import type { Metadata } from 'next';
|
||||||
import { Pirata_One, Eagle_Lake, Cinzel_Decorative } from 'next/font/google';
|
import { Eagle_Lake } from 'next/font/google';
|
||||||
import { AppProvider } from '@/app/AppContext';
|
import { AppProvider } from '@/app/AppContext';
|
||||||
import './globals.css';
|
import './globals.css';
|
||||||
|
|
||||||
const pirataOne = Pirata_One({
|
|
||||||
variable: '--font-pirata',
|
|
||||||
subsets: ['latin'],
|
|
||||||
weight: '400',
|
|
||||||
});
|
|
||||||
|
|
||||||
const eagleLake = Eagle_Lake({
|
const eagleLake = Eagle_Lake({
|
||||||
variable: '--font-eagle-lake',
|
variable: '--font-eagle-lake',
|
||||||
subsets: ['latin'],
|
subsets: ['latin'],
|
||||||
weight: '400',
|
weight: '400',
|
||||||
});
|
});
|
||||||
|
|
||||||
const cinzel = Cinzel_Decorative({
|
|
||||||
variable: '--font-cinzel',
|
|
||||||
subsets: ['latin'],
|
|
||||||
weight: '400',
|
|
||||||
});
|
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: 'Tarokka',
|
title: 'Tarokka',
|
||||||
description: 'Fortune telling for D&D’s Curse of Strahd',
|
description: 'Fortune telling for D&D’s Curse of Strahd',
|
||||||
@@ -37,11 +25,8 @@ export default function RootLayout({
|
|||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}>) {
|
}>) {
|
||||||
return (
|
return (
|
||||||
<html
|
<html lang="en" className={`${eagleLake.variable} antialiased overscroll-none`}>
|
||||||
lang="en"
|
<body className={`${eagleLake.className} antialiased h-dvh`}>
|
||||||
className={`${pirataOne.variable} ${eagleLake.variable} ${cinzel.variable} antialiased`}
|
|
||||||
>
|
|
||||||
<body className={`${eagleLake.className} antialiased`}>
|
|
||||||
<AppProvider>{children}</AppProvider>
|
<AppProvider>{children}</AppProvider>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ export default function Home() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main className="min-h-screen flex justify-center items-center text-yellow-400 bg-[url('/img/table3.png')] bg-cover bg-center">
|
<main className="flex justify-center items-center h-dvh text-yellow-400 bg-[url('/img/table3.png')] bg-cover bg-center">
|
||||||
<div className="flex flex-col items-center gap-8 text-center">
|
<div className="flex flex-col items-center gap-8 text-center">
|
||||||
<h1 className="text-5xl font-bold text-center text-primary">Tarokka</h1>
|
<h1 className="text-5xl font-bold text-center text-primary">Tarokka</h1>
|
||||||
<p className="text-l text-center w-[350px] m-auto">
|
<p className="text-l text-center w-[350px] m-auto">
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ export default function Card({ card, cardIndex }: CardProps) {
|
|||||||
return (
|
return (
|
||||||
<ToolTip content={tooltip || getTooltip()}>
|
<ToolTip content={tooltip || getTooltip()}>
|
||||||
<TiltCard
|
<TiltCard
|
||||||
className={`h-[21vh] w-[15vh] relative perspective transition-transform duration-200 z-0 hover:z-10 hover:scale-150 ${isDM ? 'cursor-pointer' : ''} `}
|
className={`h-[21vh] w-[15vh] max-w-[30vw] relative perspective transition-transform duration-200 z-0 hover:z-10 hover:scale-150 ${isDM ? 'cursor-pointer' : ''} `}
|
||||||
cardIndex={cardIndex}
|
cardIndex={cardIndex}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ export default function CardStyle({ className }: { className?: string }) {
|
|||||||
flex justify-center
|
flex justify-center
|
||||||
cursor-pointer
|
cursor-pointer
|
||||||
w-full px-3 py-2
|
w-full px-3 py-2
|
||||||
text-xs font-medium
|
text-xs font-medium capitalize
|
||||||
border border-yellow-500
|
border border-yellow-500
|
||||||
transition hover:text-yellow-300 hover:drop-shadow-[0_0_3px_#ffd700]
|
transition hover:text-yellow-300 hover:drop-shadow-[0_0_3px_#ffd700]
|
||||||
${settings.cardStyle === option ? 'bg-slate-700 text-yellow-300 font-extrabold' : 'bg-slate-800 hover:bg-slate-700'}
|
${settings.cardStyle === option ? 'bg-slate-700 text-yellow-300 font-extrabold' : 'bg-slate-800 hover:bg-slate-700'}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { CircleX, Settings as Gear } from 'lucide-react';
|
import { CircleX, Settings as Gear } from 'lucide-react';
|
||||||
import { Cinzel_Decorative } from 'next/font/google';
|
|
||||||
|
|
||||||
import { useAppContext } from '@/app/AppContext';
|
import { useAppContext } from '@/app/AppContext';
|
||||||
import Scrim from '@/components/Scrim';
|
import Scrim from '@/components/Scrim';
|
||||||
@@ -12,18 +11,12 @@ import ExternalLinks from './ExternalLinks';
|
|||||||
import GameLinks from './GameLinks';
|
import GameLinks from './GameLinks';
|
||||||
import Permissions from './Permissions';
|
import Permissions from './Permissions';
|
||||||
|
|
||||||
const cinzel = Cinzel_Decorative({
|
|
||||||
variable: '--font-cinzel',
|
|
||||||
subsets: ['latin'],
|
|
||||||
weight: '400',
|
|
||||||
});
|
|
||||||
|
|
||||||
export default function Settings() {
|
export default function Settings() {
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const { isDM } = useAppContext();
|
const { isDM } = useAppContext();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`fixed top-4 right-4 z-25 ${cinzel.className}`}>
|
<div className={`fixed top-4 right-4 z-25`}>
|
||||||
<Scrim
|
<Scrim
|
||||||
clickAction={() => setOpen((prev) => !prev)}
|
clickAction={() => setOpen((prev) => !prev)}
|
||||||
className={`transition-all duration-250 ${open ? 'pointer-events-auto opacity-100' : 'pointer-events-none opacity-0'}`}
|
className={`transition-all duration-250 ${open ? 'pointer-events-auto opacity-100' : 'pointer-events-none opacity-0'}`}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export default function TarokkaGrid() {
|
|||||||
const arrangeCards = (_cell: unknown, index: number) => cards[cardMap[index]];
|
const arrangeCards = (_cell: unknown, index: number) => cards[cardMap[index]];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="grid grid-cols-3 grid-rows-3 gap-8 w-fit mx-auto">
|
<div className="grid grid-cols-3 grid-rows-3 gap-2 sm:gap-4 md:gap-8 w-fit mx-auto">
|
||||||
{Array.from({ length: 9 })
|
{Array.from({ length: 9 })
|
||||||
.map(arrangeCards)
|
.map(arrangeCards)
|
||||||
.map((card, index) => (
|
.map((card, index) => (
|
||||||
|
|||||||
@@ -41,13 +41,14 @@ export default function TiltCard({
|
|||||||
card.style.transform = ZERO_ROTATION;
|
card.style.transform = ZERO_ROTATION;
|
||||||
}, [untilt]);
|
}, [untilt]);
|
||||||
|
|
||||||
const handleMouseMove = throttle((e: React.MouseEvent) => {
|
const handleTilt = (x: number, y: number) => {
|
||||||
const card = cardRef.current;
|
const card = cardRef.current;
|
||||||
if (!card) return;
|
if (!card) return;
|
||||||
|
|
||||||
const rect = card.getBoundingClientRect();
|
const rect = card.getBoundingClientRect();
|
||||||
const x = e.clientX - rect.left;
|
x -= rect.left;
|
||||||
const y = e.clientY - rect.top;
|
y -= rect.top;
|
||||||
|
|
||||||
const centerX = rect.width / 2;
|
const centerX = rect.width / 2;
|
||||||
const centerY = rect.height / 2;
|
const centerY = rect.height / 2;
|
||||||
|
|
||||||
@@ -65,6 +66,27 @@ export default function TiltCard({
|
|||||||
};
|
};
|
||||||
|
|
||||||
setLocalTilt(newTilt);
|
setLocalTilt(newTilt);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleMouseMove = throttle((e: React.MouseEvent) => {
|
||||||
|
handleTilt(e.clientX, e.clientY);
|
||||||
|
}, thirtyFPS);
|
||||||
|
|
||||||
|
const handleTouchMove = throttle((e: React.TouchEvent) => {
|
||||||
|
const card = cardRef.current;
|
||||||
|
const touch = e.touches[0];
|
||||||
|
|
||||||
|
if (card && touch) {
|
||||||
|
const rect = card.getBoundingClientRect();
|
||||||
|
const x = touch.clientX;
|
||||||
|
const y = touch.clientY;
|
||||||
|
|
||||||
|
if (x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom) {
|
||||||
|
handleTilt(x, y);
|
||||||
|
} else {
|
||||||
|
setLocalTilt([]);
|
||||||
|
}
|
||||||
|
}
|
||||||
}, thirtyFPS);
|
}, thirtyFPS);
|
||||||
|
|
||||||
const handleMouseLeave = () => {
|
const handleMouseLeave = () => {
|
||||||
@@ -75,6 +97,8 @@ export default function TiltCard({
|
|||||||
<div
|
<div
|
||||||
className={`group ${className}`}
|
className={`group ${className}`}
|
||||||
onMouseMove={settings.tilt ? handleMouseMove : undefined}
|
onMouseMove={settings.tilt ? handleMouseMove : undefined}
|
||||||
|
onTouchMove={settings.tilt ? handleTouchMove : undefined}
|
||||||
|
onTouchEnd={handleMouseLeave}
|
||||||
onMouseLeave={handleMouseLeave}
|
onMouseLeave={handleMouseLeave}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
|||||||
2048
package-lock.json
generated
2048
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
|||||||
import { NextResponse } from 'next/server';
|
import { NextResponse } from 'next/server';
|
||||||
import type { NextRequest } from 'next/server';
|
import type { NextRequest } from 'next/server';
|
||||||
|
|
||||||
export function middleware(request: NextRequest) {
|
export function proxy(request: NextRequest) {
|
||||||
const url = request.nextUrl;
|
const url = request.nextUrl;
|
||||||
const slug = url.pathname.slice(1);
|
const slug = url.pathname.slice(1);
|
||||||
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 2.5 MiB After Width: | Height: | Size: 5.9 MiB |
@@ -1,5 +1,5 @@
|
|||||||
import { log, validTilt } from '@/tools';
|
import { validTilt } from '@/tools';
|
||||||
import { GameUpdate, Tilt } from '@/types';
|
import { GameUpdate, Settings, Tilt } from '@/types';
|
||||||
|
|
||||||
const combineTilts = (tilts: Tilt[]) =>
|
const combineTilts = (tilts: Tilt[]) =>
|
||||||
tilts.reduce(
|
tilts.reduce(
|
||||||
@@ -13,13 +13,15 @@ const combineTilts = (tilts: Tilt[]) =>
|
|||||||
{ pX: 0, pY: 0, rX: 0, rY: 0, count: 0 },
|
{ pX: 0, pY: 0, rX: 0, rY: 0, count: 0 },
|
||||||
);
|
);
|
||||||
|
|
||||||
export function reduceTilts(gameData: GameUpdate, localTilt: Tilt[]): Tilt[] {
|
export function reduceTilts(
|
||||||
|
gameData: GameUpdate,
|
||||||
|
localTilt: Tilt[],
|
||||||
|
{ tilt, remoteTilt }: Settings,
|
||||||
|
): Tilt[] {
|
||||||
const remoteTilts = gameData.tilts;
|
const remoteTilts = gameData.tilts;
|
||||||
const tiltEnabled = gameData.settings.tilt;
|
|
||||||
const remoteTiltEnabled = gameData.settings.remoteTilt;
|
|
||||||
|
|
||||||
if (!tiltEnabled) return [];
|
if (!tilt) return [];
|
||||||
if (!remoteTiltEnabled) return Array.from({ length: 5 }, (_, i) => localTilt[i]);
|
if (!remoteTilt) return localTilt;
|
||||||
|
|
||||||
return Array.from({ length: 5 }, (_, i) => (localTilt[i] ? [localTilt[i]] : []))
|
return Array.from({ length: 5 }, (_, i) => (localTilt[i] ? [localTilt[i]] : []))
|
||||||
.map((cardTilts, cardIndex) => [...remoteTilts[cardIndex], ...cardTilts])
|
.map((cardTilts, cardIndex) => [...remoteTilts[cardIndex], ...cardTilts])
|
||||||
|
|||||||
@@ -1,4 +1,9 @@
|
|||||||
import { Tilt } from '@/types';
|
import { Tilt } from '@/types';
|
||||||
|
|
||||||
export const validTilt = ({ percentX, percentY, rotateX, rotateY }: Tilt) =>
|
export const validTilt = (tilt: Tilt) => {
|
||||||
percentX >= 0 && percentY >= 0 && !!rotateX && !!rotateY;
|
if (!tilt) return false;
|
||||||
|
|
||||||
|
const { percentX, percentY, rotateX, rotateY } = tilt;
|
||||||
|
|
||||||
|
return percentX >= 0 && percentY >= 0 && !!rotateX && !!rotateY;
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "ES2020",
|
"target": "ES2020",
|
||||||
"lib": ["dom", "dom.iterable", "esnext"],
|
"lib": [
|
||||||
|
"dom",
|
||||||
|
"dom.iterable",
|
||||||
|
"esnext"
|
||||||
|
],
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"strict": false,
|
"strict": false,
|
||||||
@@ -12,7 +16,7 @@
|
|||||||
"moduleResolution": "node",
|
"moduleResolution": "node",
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
"jsx": "preserve",
|
"jsx": "react-jsx",
|
||||||
"incremental": true,
|
"incremental": true,
|
||||||
"plugins": [
|
"plugins": [
|
||||||
{
|
{
|
||||||
@@ -20,10 +24,20 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"paths": {
|
"paths": {
|
||||||
"@/*": ["./*"]
|
"@/*": [
|
||||||
|
"./*"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"strictNullChecks": true
|
"strictNullChecks": true
|
||||||
},
|
},
|
||||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
"include": [
|
||||||
"exclude": ["node_modules"]
|
"next-env.d.ts",
|
||||||
|
"**/*.ts",
|
||||||
|
"**/*.tsx",
|
||||||
|
".next/types/**/*.ts",
|
||||||
|
".next/dev/types/**/*.ts"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"node_modules"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user