diff --git a/README.md b/README.md
index a75ac52..b421d33 100644
--- a/README.md
+++ b/README.md
@@ -1,40 +1,9 @@
-This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
-
-## Getting Started
-
-First, run the development server:
-
-```bash
-npm run dev
-# or
-yarn dev
-# or
-pnpm dev
-# or
-bun dev
-```
-
-Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
-
-You can start editing the page by modifying `pages/index.tsx`. The page auto-updates as you edit the file.
-
-[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.ts`.
-
-The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.
-
-This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
-
-## Learn More
-
-To learn more about Next.js, take a look at the following resources:
-
-- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
-- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
-
-You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
-
-## Deploy on Vercel
-
-The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
-
-Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
+Playtest Sorcery TCG in the browser
+
+## Todo
+- [ ] add health counter
+- [ ] landing page
+- [ ] add realms app deck loader
+- [ ] add solo turn rotator (play against yourself)
+ - use top header for info
+- [ ] add websocket
diff --git a/daily.js b/daily.js
new file mode 100644
index 0000000..dae4608
--- /dev/null
+++ b/daily.js
@@ -0,0 +1,52 @@
+const fs = require("fs");
+const path = require("path");
+const { execSync } = require("child_process");
+
+// Get today's date in the required format
+const today = new Date();
+const formattedDate = today.toISOString().split("T")[0]; // YYYY-MM-DD format
+const formattedTime = today.toTimeString().split(" ")[0].slice(0, 5); // HH:MM format
+
+// Path to the daily note in the relative 'notes' folder
+const notesDir = path.join(__dirname, "notes");
+const notePath = path.join(notesDir, `${formattedDate}.md`);
+
+// Check if the file exists, if not create it
+if (!fs.existsSync(notePath)) {
+ fs.writeFileSync(notePath, "");
+}
+
+// Function to add an entry to the daily note
+const addEntry = (entryText, isTodo) => {
+ // Read the file contents
+ const fileContent = fs.readFileSync(notePath, "utf-8");
+ const lastTimeStamp = (fileContent.match(/#### (\d{2}:\d{2})/g) || []).pop();
+
+ let newContent = "";
+
+ // If the last timestamp is not equal to the current time, add a new timestamp
+ if (!lastTimeStamp || lastTimeStamp !== `#### ${formattedTime}`) {
+ newContent += `\n#### ${formattedTime}\n`;
+ }
+
+ if (isTodo) {
+ newContent += `- [ ] ${entryText}\n`;
+ } else {
+ newContent += `- ${entryText}\n`;
+ }
+
+ fs.appendFileSync(notePath, newContent);
+};
+
+// Main script
+// const command = process.argv[2];
+const command = "add";
+const entryText = process.argv.slice(3).join(" ");
+
+if (command === "add" || command === "todo") {
+ const isTodo = command === "todo";
+ addEntry(entryText, isTodo);
+} else {
+ // Open the note in the default editor
+ execSync(`vi "${notePath}"`, { stdio: "inherit" });
+}
diff --git a/notes/2024-09-11.md b/notes/2024-09-11.md
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/notes/2024-09-11.md
@@ -0,0 +1 @@
+
diff --git a/notes/2024-09-12.md b/notes/2024-09-12.md
new file mode 100644
index 0000000..7c4ba74
--- /dev/null
+++ b/notes/2024-09-12.md
@@ -0,0 +1,12 @@
+
+#### 02:37
+- im making progress in setting up the multiplayer but now im running into an issue of every player is updating the same thing
+
+#### 02:38
+- actually i might be getting somewhere
+
+#### 02:49
+- okay i got it working but now i have to solve auras
+
+#### 03:18
+- i got it working. the tricky thing with auras was i had to adjust the top left values because they would always look the same no matter the order
diff --git a/package.json b/package.json
index 7e9ea52..90b35bb 100644
--- a/package.json
+++ b/package.json
@@ -7,7 +7,8 @@
"dev": "next dev",
"build": "next build",
"start": "next start",
- "lint": "next lint"
+ "lint": "next lint",
+ "note": "node daily.js"
},
"dependencies": {
"@dnd-kit/core": "^6.1.0",
diff --git a/src/components/organisms/GameBoard/Auras/index.tsx b/src/components/organisms/GameBoard/Auras/index.tsx
index bcb71c4..c8df08d 100644
--- a/src/components/organisms/GameBoard/Auras/index.tsx
+++ b/src/components/organisms/GameBoard/Auras/index.tsx
@@ -8,31 +8,39 @@ import { Modal } from "@/components/atoms/Modal";
import { FullCardAtlas } from "@/components/atoms/card-view/atlas";
import { CardImage as FullCard } from "@/components/atoms/card-view/card";
import { CARD_CDN, GRIDS } from "../constants";
+import { useRouter } from "next/router";
export const Auras = (props: GameStateActions) => {
return (
- {Array.from({ length: 12 }).map((_, index) => (
-
- ))}
+ {Array.from({ length: 12 })
+ .map((_, index) => index)
+ .map((index) => (
+
+ ))}
);
};
const Aura = (props: { card: GameCard; gridIndex: number; index: number }) => {
- const auraCard = props.card;
+ const { query } = useRouter();
+ const name = query?.name ?? "p1";
+ const isReversed = name === "p2";
+ const auraCard = props.card;
const auraIndex = props.gridIndex - GRIDS.AURA_1; // normalize to zero
const topIndex = Math.floor(auraIndex / 4) + 1; // increments every 4
- const top = `${topIndex * 25}%`; // in percent
+ // const top = `${topIndex * 25}%`; // in percent
const leftIndex = 1 + (auraIndex % 4); // every 4, resets
- const left = `${leftIndex * 20}%`; // in percent
+ // const left = `${leftIndex * 20}%`; // in percent
+ const top = isReversed ? `${(4 - topIndex) * 25}%` : `${topIndex * 25}%`; // Reverse top
+ const left = isReversed ? `${(5 - leftIndex) * 20}%` : `${leftIndex * 20}%`; // Reverse left
return (
Playtest cards on a grid. Click to tap. Right click to view big.
+
+
p1
+
+
+
+
p2
+
diff --git a/src/components/organisms/GameBoard/constants.ts b/src/components/organisms/GameBoard/constants.ts
index 0da08ba..9dbaaed 100644
--- a/src/components/organisms/GameBoard/constants.ts
+++ b/src/components/organisms/GameBoard/constants.ts
@@ -17,8 +17,8 @@ export const LAYOUT_HEIGHTS = { nav, footer, body };
* GRID ORDERING
*
* Contains the grid, auras, hand, deck, atlas deck, grave
+ * grave is last element in enum
* */
-
export enum GRIDS {
GRID_1,
GRID_2,
@@ -57,3 +57,9 @@ export enum GRIDS {
ATLAS_DECK,
GRAVE,
}
+
+/**
+ * Length of hand, deck, deck-atlas, and grave (4)
+ * used to splice playerstate from global state
+ * */
+export const GRIDS_PERSONAL_LENGTH = GRIDS.GRAVE - GRIDS.AURA_12;
diff --git a/src/components/organisms/GameBoard/index.tsx b/src/components/organisms/GameBoard/index.tsx
index ef29c7d..a60edc1 100644
--- a/src/components/organisms/GameBoard/index.tsx
+++ b/src/components/organisms/GameBoard/index.tsx
@@ -2,13 +2,7 @@ import { CardAtlas } from "@/components/atoms/mock-cards/atlas";
import { SortableContext, rectSortingStrategy } from "@dnd-kit/sortable";
import { GameLayout } from "./Layout";
import { DroppableGridItem } from "@/components/molecules/DropGridItem";
-import {
- closestCenter,
- closestCorners,
- CollisionDetection,
- DndContext,
- DragOverlay,
-} from "@dnd-kit/core";
+import { DndContext, DragOverlay } from "@dnd-kit/core";
import { useHandleDrag } from "./useHandleDrag";
import { CardImage } from "@/components/atoms/mock-cards/card";
import { CardImage as FullCard } from "@/components/atoms/card-view/card";
@@ -16,71 +10,63 @@ import { SortableItem } from "@/components/molecules/SortItem";
import { Box } from "styled-system/jsx";
import { Modal } from "@/components/atoms/Modal";
-import { Dispatch, SetStateAction, useState } from "react";
+import { useState } from "react";
import { FullCardAtlas } from "@/components/atoms/card-view/atlas";
import { GameCard, GameState } from "@/types/card";
+import { GRIDS } from "./constants";
+import { useRouter } from "next/router";
export type GameStateActions = {
gridItems: GameState;
- setGridItems: Dispatch>;
+ setGridItems: (state: GameState) => void;
};
export const GameBoard = ({ gridItems, setGridItems }: GameStateActions) => {
- const { sensors, handleDragEnd, handleDragStart, activeId, activeCard } =
- useHandleDrag({
- gridItems,
- setGridItems,
- });
+ const { query } = useRouter();
- const collision: CollisionDetection = (props) => {
- // Access the current translated Y position of the dragged item
- const currentY = props?.active?.rect?.current?.translated?.top;
+ const { activeCard, activeId, ...dragProps } = useHandleDrag({
+ gridItems,
+ setGridItems,
+ });
- // Get the height of the viewport
- const viewportHeight = window.innerHeight;
-
- // Check if the current Y position is within the bottom 170px of the page
- const isInFooter = currentY && currentY > viewportHeight - 170;
-
- // Check if the current Y position is within the bottom 170px of the page
- // If the item is in the bottom 170px, use closestCenter for the footer
- if (isInFooter) {
- return closestCenter(props);
- }
-
- return closestCorners(props);
- };
+ const name = query?.name ?? "p1";
+ const isReversed = name === "p2";
return (
-
+
- {gridItems?.slice(0, 20)?.map((cards, gridIndex) => (
-
- card.id)}
- strategy={rectSortingStrategy}
+ {(isReversed
+ ? gridItems.slice(0, 20).reverse()
+ : gridItems.slice(0, 20)
+ )?.map((cards, _gridIndex) => {
+ // Adjust the gridIndex if array was reversed
+ const gridIndex = isReversed
+ ? GRIDS.GRID_20 - _gridIndex
+ : _gridIndex;
+
+ return (
+
- {cards.map((card, cardIndex) => (
-
- ))}
-
-
- ))}
+ card.id)}
+ strategy={rectSortingStrategy}
+ >
+ {cards.map((card, cardIndex) => (
+
+ ))}
+
+
+ );
+ })}
{activeId ? (
@@ -119,16 +105,13 @@ const SortItemWrapper = ({
const isTapped = card.isTapped;
function toggleTap() {
- props.setGridItems(() => {
- const newGrid = [...props.gridItems];
- const card = newGrid[gridIndex][cardIndex];
- newGrid[gridIndex][cardIndex] = {
- ...card,
- isTapped: !card.isTapped,
- } as GameCard;
-
- return newGrid;
- });
+ const newGrid = [...props.gridItems];
+ const card = newGrid[gridIndex][cardIndex];
+ newGrid[gridIndex][cardIndex] = {
+ ...card,
+ isTapped: !card.isTapped,
+ } as GameCard;
+ props.setGridItems(newGrid);
}
return (
diff --git a/src/components/organisms/GameBoard/useHandleDrag.ts b/src/components/organisms/GameBoard/useHandleDrag.ts
index edb1717..965f527 100644
--- a/src/components/organisms/GameBoard/useHandleDrag.ts
+++ b/src/components/organisms/GameBoard/useHandleDrag.ts
@@ -1,6 +1,9 @@
import { SorceryCard } from "@/types/card";
import { actMoveCard } from "@/utils/actions/grid";
import {
+ closestCenter,
+ closestCorners,
+ CollisionDetection,
DragEndEvent,
KeyboardSensor,
MouseSensor,
@@ -14,6 +17,25 @@ import { useState } from "react";
type GameCard = SorceryCard & { id: string }; // for game position
type Cards = GameCard[][];
+const collision: CollisionDetection = (props) => {
+ // Access the current translated Y position of the dragged item
+ const currentY = props?.active?.rect?.current?.translated?.top;
+
+ // Get the height of the viewport
+ const viewportHeight = window.innerHeight;
+
+ // Check if the current Y position is within the bottom 170px of the page
+ const isInFooter = currentY && currentY > viewportHeight - 170;
+
+ // Check if the current Y position is within the bottom 170px of the page
+ // If the item is in the bottom 170px, use closestCenter for the footer
+ if (isInFooter) {
+ return closestCenter(props);
+ }
+
+ return closestCorners(props);
+};
+
export const useHandleDrag = ({
gridItems,
setGridItems,
@@ -49,9 +71,10 @@ export const useHandleDrag = ({
}
return {
+ collision,
sensors,
- handleDragEnd,
- handleDragStart,
+ onDragEnd: handleDragEnd,
+ onDragStart: handleDragStart,
activeId: active?.id,
activeCard:
gridItems?.[active?.data?.current?.gridIndex]?.[
diff --git a/src/pages/game.tsx b/src/pages/game.tsx
index ed25720..293f126 100644
--- a/src/pages/game.tsx
+++ b/src/pages/game.tsx
@@ -1,24 +1,70 @@
import { LoadDeck } from "@/components/molecules/LoadDeck";
import { GameBoard } from "@/components/organisms/GameBoard";
import { GRIDS } from "@/components/organisms/GameBoard/constants";
-import { GameCard, GameState } from "@/types/card";
-import { useState } from "react";
+import { GameCard, GameState, PlayersState } from "@/types/card";
+import { useRouter } from "next/router";
+import { useMemo, useState } from "react";
-const initCards: GameCard[][] = Array.from({ length: 36 }, () => []);
+const initGameState: GameCard[][] = Array.from({ length: 36 }, () => []);
export default function GamePage() {
- const [gridItems, setGridItems] = useState(initCards);
+ const { query } = useRouter();
+ const name = (query?.name as string | undefined) ?? "p1";
- if (
- gridItems[GRIDS.DECK].length === 0 &&
- gridItems[GRIDS.HAND].length === 0 &&
- gridItems[GRIDS.GRAVE].length === 0
- )
+ // const [gridItems, setGridItems] = useState(initGameState);
+ const [players, setPlayers] = useState({
+ p1: initGameState,
+ p2: initGameState,
+ GLOBAL: initGameState,
+ });
+
+ function setPlayer(playerName: keyof typeof players) {
+ return (state: GameState) => {
+ const newState = [...state]; // make a copy of state
+ const GLOBAL = newState.splice(0, GRIDS.AURA_12 + 1); // GLOBAL takes game grid
+ const GLOBAL_EMPTY = Array.from({ length: 4 }, () => []); // empties player data
+ setPlayers((prev) => ({
+ ...prev,
+ GLOBAL: [...GLOBAL, ...GLOBAL_EMPTY],
+ [playerName]: state,
+ }));
+ };
+ }
+
+ const state = useMemo(
+ () => [
+ ...players.GLOBAL.slice(0, GRIDS.AURA_12),
+ ...players[name as keyof typeof players].slice(
+ GRIDS.AURA_12,
+ GRIDS.AURA_12 + 5,
+ ),
+ ],
+ [players, name],
+ );
+
+ if (needsDeck(players["p1"]))
+ return (
+
+
Load deck for player 1
+
+
+ );
+
+ if (needsDeck(players["p2"]))
return (
-
+
Load deck for player 2
+
);
- return ;
+ return ;
+}
+
+function needsDeck(state: GameState) {
+ return (
+ state[GRIDS.DECK].length === 0 &&
+ state[GRIDS.HAND].length === 0 &&
+ state[GRIDS.GRAVE].length === 0
+ );
}
diff --git a/src/pages/index.tsx b/src/pages/index.tsx
index c6bbad9..ee73882 100644
--- a/src/pages/index.tsx
+++ b/src/pages/index.tsx
@@ -4,7 +4,7 @@ import { useRouter } from "next/router";
export default function Home() {
const router = useRouter();
useEffect(() => {
- router.push("/game");
+ router.push("/game?name=p1");
}, []);
return null;
}
diff --git a/src/types/card.ts b/src/types/card.ts
index 935a00c..e10ffcb 100644
--- a/src/types/card.ts
+++ b/src/types/card.ts
@@ -6,6 +6,11 @@ type SorceryCardType =
| "minion"
| "magic";
+/**
+ * Static props needed to render a card
+ * img for display
+ * type only matters to rotate sites (sites are horizontal)
+ * */
export type SorceryCard = {
img: string; // used with CDN
type: SorceryCardType; // site card is rotated sideways
@@ -13,16 +18,35 @@ export type SorceryCard = {
/**
* Card props used for playtesting
+ * id is used for drag-n-drop
+ * isTapped ...well yeah for tapping
* */
type GameProps = {
id: string;
isTapped?: boolean;
};
+
/**
* Card type used for game playtesting
* */
export type GameCard = SorceryCard & GameProps;
+/**
+ * Represents an array of cards.
+ * Every container of cards is a grid item.
+ * For example, hand, deck, discard, individual cell on the game grid.
+ * */
export type GridItem = GameCard[];
+/**
+ * The entire state of the board is an array of 36 arrays.
+ * 1-20 Game Grid (5x4 grid)
+ * 21-32 Aura intersections
+ * 33 HAND
+ * 34 DECK
+ * 35 ATLAS_DECK
+ * 36 GRAVE
+ * */
export type GameState = GridItem[];
+
+export type PlayersState = Record & { GLOBAL: GameState };