diff --git a/Deck.ts b/Deck.ts new file mode 100644 index 0000000..8ca772a --- /dev/null +++ b/Deck.ts @@ -0,0 +1,48 @@ +export type Card = { + id: string; +} & T + +export class Deck { + private cards: Card[]; + private discard: Card[]; + + public hands: Card[][] = [] + + constructor(cards?: Card[]) { + this.cards = cards || []; + this.discard = []; + } + + public shuffle() { + this.cards.sort(Math.random); + } + public reshuffle() { + this.cards = this.cards.concat(this.discard).sort(Math.random); + this.discard = []; + } + public createHand() { + return this.hands.push([]) - 1; + } + + public drawToHand(handIndex: number, count = 1, reshuffle = true) { + const hand = this.hands.at(handIndex); + if (!hand) throw 'No hand at index ' + handIndex; + for (let i = 0; i < count; i++) { + if (!this.cards.length && reshuffle) this.reshuffle(); + const card = this.cards.pop(); + if (!card) break; + hand.push(card); + } + } + + public addCard(...cards: Card[]) { + this.cards.push(...cards); + } + + public getDeck() { + return [...this.cards]; + } + public getDiscard() { + return [...this.discard]; + } +} \ No newline at end of file diff --git a/app/page.tsx b/app/page.tsx index b5840ee..5f3f171 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,113 +1,66 @@ +'use client' + +import { Card } from '@/Deck'; +import { useDeck } from '@/hooks/useDeck' +import { camelToSpace } from '@/utils/camelToSpace'; import Image from 'next/image' +import { useCallback, useEffect, useState } from 'react'; + +type planeCard = { + maneuvers: [ + { + name: string, + complexity: 'simple' | 'complex', + length: number + } + ], + attacks: ('airToGround' | 'groundToAir' | 'airToAir')[], + penalties: ('Stall' | 'Damage' | 'Ground Battle')[], + type: 'action' | 'win condition' | 'wild card' | 'sudden death' +}; export default function Home() { + const { addCard, createHand, deck, discard, drawToHand, hands, reshuffle, shuffle, createDeck } = useDeck(); + const series = 1; + + const fetchDeckJson = useCallback(async () => { + const res = await fetch('./json/deck.json'); + const json: Card[] = await res.json(); + + json.forEach(c => c.id = crypto.randomUUID()); + + createDeck(json); + }, []) + + useEffect(() => { + fetchDeckJson().then(() => { + shuffle() + // setRender(!render) + }) + }, []) + return ( -
-
-

- Get started by editing  - app/page.tsx -

-
- - By{' '} - Vercel Logo - +
+ {deck.map((c,i) => (
+
+

{c.type}

+ + {c.attacks.length || c.maneuvers.length ? +

Select either one maneuver or one attack to make. If you can't complete one action from this card, you must take one penalty listed. If there are no penalties listed, discard this card.

: +

Bad luck! You must take one of the following penalties

+ } + + {c.maneuvers.length > 0 ?

Maneuvers: {c.maneuvers.map(a => camelToSpace(a.name)).join(', ')}

: ''} + {c.attacks.length > 0 ?

Attacks: {c.attacks.map(a => camelToSpace(a)).join(', ')}

: ''} + {c.penalties.length > 0 ? <>

Penalties: {c.penalties.join(', ')}

: ''}
-
-
- Next.js Logo -
- - -
+
+

Series {series}

+

{i + 1}/{deck.length}

+

© {new Date().getFullYear()} Cyborg Grizzly Games

+
+ ))} + ) } diff --git a/hooks/useDeck.ts b/hooks/useDeck.ts new file mode 100644 index 0000000..8186dee --- /dev/null +++ b/hooks/useDeck.ts @@ -0,0 +1,44 @@ +import { Card, Deck } from "@/Deck"; +import { useCallback, useRef, useState } from "react" + +export const useDeck = () => { + const deck = useRef(new Deck()); + + const shuffle = useCallback(() => { + deck.current.shuffle(); + }, []) + + const reshuffle = useCallback(() => { + deck.current.reshuffle(); + }, []) + + const createHand = useCallback(() => deck.current.createHand(), []) + + const drawToHand = useCallback((index: number, count?: number, reshuffle?: boolean) => { + deck.current.drawToHand(index, count, reshuffle); + }, []) + + const addCard = useCallback((...cards: Card[]) => { + deck.current.addCard(...cards) + }, []) + + const getDeck = useCallback(() => deck.current.getDeck(), []); + const getDiscard = useCallback(() => deck.current.getDiscard(), []); + const getHands = useCallback(() => deck.current.hands, []); + + const createDeck = useCallback((cards: Card[]) => { + deck.current = new Deck(cards); + }, []) + + return { + deck: getDeck(), + discard: getDiscard(), + hands: getHands(), + shuffle, + reshuffle, + createHand, + drawToHand, + addCard, + createDeck + } +} \ No newline at end of file diff --git a/public/json/deck.json b/public/json/deck.json new file mode 100644 index 0000000..24f7695 --- /dev/null +++ b/public/json/deck.json @@ -0,0 +1,1007 @@ +[ + { + "maneuvers": [], + "attacks": [ + "airToGround" + ], + "penalties": [ + "Damage", + "Ground Battle", + "Stall" + ], + "type": "action" + }, + { + "maneuvers": [ + { + "name": "sBend", + "complexity": "simple", + "length": 4 + } + ], + "attacks": [ + "airToAir", + "airToGround", + "groundToAir" + ], + "penalties": [ + "Land Battle" + ], + "type": "action" + }, + { + "maneuvers": [ + { + "name": "sBend", + "complexity": "simple", + "length": 4 + }, + { + "name": "immelmann", + "complexity": "complex", + "length": 10 + } + ], + "attacks": [ + "airToAir", + "groundToAir" + ], + "penalties": [ + "Ground Battle", + "Stall" + ], + "type": "action" + }, + { + "maneuvers": [ + { + "name": "tightBank", + "complexity": "complex", + "length": 4 + }, + { + "name": "sBend", + "complexity": "simple", + "length": 4 + } + ], + "attacks": [], + "penalties": [ + "Damage", + "Ground Battle", + "Stall" + ], + "type": "action" + }, + { + "maneuvers": [ + { + "name": "tightBank", + "complexity": "complex", + "length": 4 + }, + { + "name": "straight", + "complexity": "simple", + "length": 4 + } + ], + "attacks": [ + "airToGround" + ], + "penalties": [ + "Damage", + "Stall" + ], + "type": "action" + }, + { + "maneuvers": [ + { + "name": "immelmann", + "complexity": "complex", + "length": 10 + }, + { + "name": "sBend", + "complexity": "simple", + "length": 4 + } + ], + "attacks": [], + "penalties": [ + "Damage", + "Ground Battle" + ], + "type": "action" + }, + { + "maneuvers": [], + "attacks": [], + "penalties": [ + "Ground Battle" + ], + "type": "action" + }, + { + "maneuvers": [], + "attacks": [], + "penalties": [ + "Stall" + ], + "type": "action" + }, + { + "maneuvers": [ + { + "name": "straight", + "complexity": "simple", + "length": 4 + }, + { + "name": "turn", + "complexity": "simple", + "length": 4 + } + ], + "attacks": [], + "penalties": [], + "type": "action" + }, + { + "maneuvers": [ + { + "name": "straight", + "complexity": "simple", + "length": 4 + } + ], + "attacks": [ + "groundToAir" + ], + "penalties": [ + "Damage" + ], + "type": "action" + }, + { + "maneuvers": [ + { + "name": "bankedTurn", + "complexity": "simple", + "length": 6 + }, + { + "name": "straight", + "complexity": "simple", + "length": 4 + } + ], + "attacks": [], + "penalties": [ + "Damage", + "Ground Battle", + "Stall" + ], + "type": "action" + }, + { + "maneuvers": [ + { + "name": "immelmann", + "complexity": "complex", + "length": 10 + }, + { + "name": "turn", + "complexity": "simple", + "length": 4 + } + ], + "attacks": [], + "penalties": [ + "Damage", + "Ground Battle" + ], + "type": "action" + }, + { + "maneuvers": [], + "attacks": [ + "airToGround" + ], + "penalties": [ + "Damage", + "Ground Battle", + "Stall" + ], + "type": "action", + "needsEffect": true + }, + { + "maneuvers": [ + { + "name": "straight", + "complexity": "simple", + "length": 4 + } + ], + "attacks": [ + "groundToAir" + ], + "penalties": [ + "Ground Battle", + "Stall" + ], + "type": "action" + }, + { + "maneuvers": [ + { + "name": "immelmann", + "complexity": "complex", + "length": 10 + }, + { + "name": "barrelRoll", + "complexity": "complex", + "length": 8 + } + ], + "attacks": [ + "airToGround" + ], + "penalties": [], + "type": "action" + }, + { + "maneuvers": [], + "attacks": [ + "airToGround" + ], + "penalties": [ + "Damage", + "Ground Battle", + "Stall" + ], + "type": "action", + "needsEffect": true + }, + { + "maneuvers": [ + { + "name": "straight", + "complexity": "simple", + "length": 4 + }, + { + "name": "sBend", + "complexity": "simple", + "length": 4 + } + ], + "attacks": [ + "airToAir", + "airToGround" + ], + "penalties": [ + "Damage", + "Ground Battle" + ], + "type": "action" + }, + { + "maneuvers": [ + { + "name": "immelmann", + "complexity": "complex", + "length": 10 + } + ], + "attacks": [], + "penalties": [ + "Damage", + "Ground Battle", + "Stall" + ], + "type": "action" + }, + { + "maneuvers": [ + { + "name": "barrelRoll", + "complexity": "complex", + "length": 8 + } + ], + "attacks": [], + "penalties": [], + "type": "action" + }, + { + "maneuvers": [ + { + "name": "barrelRoll", + "complexity": "complex", + "length": 8 + }, + { + "name": "turn", + "complexity": "simple", + "length": 4 + } + ], + "attacks": [ + "airToAir", + "groundToAir" + ], + "penalties": [ + "Damage", + "Ground Battle", + "Stall" + ], + "type": "action" + }, + { + "maneuvers": [ + { + "name": "tightBank", + "complexity": "complex", + "length": 4 + }, + { + "name": "immelmann", + "complexity": "complex", + "length": 10 + } + ], + "attacks": [ + "airToGround" + ], + "penalties": [], + "type": "action" + }, + { + "maneuvers": [ + { + "name": "barrelRoll", + "complexity": "complex", + "length": 8 + }, + { + "name": "tightBank", + "complexity": "complex", + "length": 4 + } + ], + "attacks": [ + "airToAir", + "airToGround" + ], + "penalties": [ + "Ground Battle", + "Stall" + ], + "type": "action" + }, + { + "maneuvers": [ + { + "name": "turn", + "complexity": "simple", + "length": 4 + } + ], + "attacks": [], + "penalties": [ + "Damage", + "Ground Battle", + "Stall" + ], + "type": "action" + }, + { + "maneuvers": [ + { + "name": "turn", + "complexity": "simple", + "length": 4 + } + ], + "attacks": [ + "airToGround" + ], + "penalties": [ + "Damage", + "Ground Battle", + "Stall" + ], + "type": "action" + }, + { + "maneuvers": [], + "attacks": [ + "airToGround" + ], + "penalties": [ + "Damage", + "Ground Battle", + "Stall" + ], + "type": "action", + "needsEffect": true + }, + { + "maneuvers": [ + { + "name": "immelmann", + "complexity": "complex", + "length": 10 + } + ], + "attacks": [ + "airToAir", + "groundToAir" + ], + "penalties": [ + "Ground Battle" + ], + "type": "action" + }, + { + "maneuvers": [ + { + "name": "sBend", + "complexity": "simple", + "length": 4 + } + ], + "attacks": [ + "airToGround" + ], + "penalties": [ + "Damage", + "Ground Battle", + "Stall" + ], + "type": "action" + }, + { + "maneuvers": [], + "attacks": [ + "groundToAir" + ], + "penalties": [ + "Damage", + "Ground Battle", + "Stall" + ], + "type": "action" + }, + { + "maneuvers": [ + { + "name": "immelmann", + "complexity": "complex", + "length": 10 + }, + { + "name": "turn", + "complexity": "simple", + "length": 4 + } + ], + "attacks": [ + "airToAir" + ], + "penalties": [ + "Damage" + ], + "type": "action" + }, + { + "maneuvers": [ + { + "name": "sBend", + "complexity": "simple", + "length": 4 + } + ], + "attacks": [ + "airToGround" + ], + "penalties": [ + "Ground Battle" + ], + "type": "action" + }, + { + "maneuvers": [ + { + "name": "immelmann", + "complexity": "complex", + "length": 10 + } + ], + "attacks": [], + "penalties": [], + "type": "action" + }, + { + "maneuvers": [ + { + "name": "sBend", + "complexity": "simple", + "length": 4 + } + ], + "attacks": [], + "penalties": [ + "Stall" + ], + "type": "action" + }, + { + "maneuvers": [ + { + "name": "sBend", + "complexity": "simple", + "length": 4 + }, + { + "name": "barrelRoll", + "complexity": "complex", + "length": 8 + } + ], + "attacks": [], + "penalties": [], + "type": "action" + }, + { + "maneuvers": [ + { + "name": "tightBank", + "complexity": "complex", + "length": 4 + } + ], + "attacks": [], + "penalties": [], + "type": "action" + }, + { + "maneuvers": [ + { + "name": "tightBank", + "complexity": "complex", + "length": 4 + } + ], + "attacks": [ + "airToGround" + ], + "penalties": [], + "type": "action" + }, + { + "maneuvers": [ + { + "name": "tightBank", + "complexity": "complex", + "length": 4 + }, + { + "name": "barrelRoll", + "complexity": "complex", + "length": 8 + } + ], + "attacks": [ + "airToAir" + ], + "penalties": [ + "Stall" + ], + "type": "action" + }, + { + "maneuvers": [ + { + "name": "bankedTurn", + "complexity": "simple", + "length": 6 + } + ], + "attacks": [ + "airToAir", + "groundToAir" + ], + "penalties": [ + "Ground Battle" + ], + "type": "action" + }, + { + "maneuvers": [], + "attacks": [ + "airToGround" + ], + "penalties": [ + "Damage", + "Ground Battle", + "Stall" + ], + "type": "action", + "needsEffect": true + }, + { + "maneuvers": [ + { + "name": "sBend", + "complexity": "simple", + "length": 4 + } + ], + "attacks": [ + "airToGround" + ], + "penalties": [], + "type": "action" + }, + { + "maneuvers": [], + "attacks": [ + "airToAir", + "groundToAir" + ], + "penalties": [ + "Land Battle" + ], + "type": "action" + }, + { + "maneuvers": [ + { + "name": "sBend", + "complexity": "simple", + "length": 4 + }, + { + "name": "straight", + "complexity": "simple", + "length": 4 + } + ], + "attacks": [], + "penalties": [ + "Damage", + "Ground Battle" + ], + "type": "action" + }, + { + "maneuvers": [ + { + "name": "immelmann", + "complexity": "complex", + "length": 10 + }, + { + "name": "tightBank", + "complexity": "complex", + "length": 4 + } + ], + "attacks": [ + "airToAir", + "airToGround" + ], + "penalties": [ + "Damage", + "Stall" + ], + "type": "action" + }, + { + "maneuvers": [ + { + "name": "sBend", + "complexity": "simple", + "length": 4 + }, + { + "name": "immelmann", + "complexity": "complex", + "length": 10 + } + ], + "attacks": [], + "penalties": [ + "Damage", + "Ground Battle", + "Stall" + ], + "type": "action" + }, + { + "maneuvers": [ + { + "name": "turn", + "complexity": "simple", + "length": 4 + }, + { + "name": "barrelRoll", + "complexity": "complex", + "length": 8 + } + ], + "attacks": [], + "penalties": [ + "Damage", + "Ground Battle", + "Stall" + ], + "type": "action" + }, + { + "maneuvers": [], + "attacks": [ + "groundToAir" + ], + "penalties": [ + "Damage", + "Ground Battle", + "Stall" + ], + "type": "action", + "needsEffect": true + }, + { + "maneuvers": [ + { + "name": "tightBank", + "complexity": "complex", + "length": 4 + }, + { + "name": "straight", + "complexity": "simple", + "length": 4 + } + ], + "attacks": [], + "penalties": [ + "Stall" + ], + "type": "action" + }, + { + "maneuvers": [ + { + "name": "barrelRoll", + "complexity": "complex", + "length": 8 + } + ], + "attacks": [], + "penalties": [ + "Damage", + "Ground Battle", + "Stall" + ], + "type": "action" + }, + { + "maneuvers": [ + { + "name": "tightBank", + "complexity": "complex", + "length": 4 + } + ], + "attacks": [ + "groundToAir" + ], + "penalties": [ + "Land Battle" + ], + "type": "action" + }, + { + "maneuvers": [ + { + "name": "barrelRoll", + "complexity": "complex", + "length": 8 + }, + { + "name": "turn", + "complexity": "simple", + "length": 4 + } + ], + "attacks": [], + "penalties": [ + "Stall" + ], + "type": "action" + }, + { + "maneuvers": [], + "attacks": [], + "penalties": [ + "Ground Battle" + ], + "type": "action", + "needsEffect": true + }, + { + "maneuvers": [], + "attacks": [], + "penalties": [ + "Damage", + "Stall" + ], + "type": "action" + }, + { + "maneuvers": [ + { + "name": "tightBank", + "complexity": "complex", + "length": 4 + }, + { + "name": "straight", + "complexity": "simple", + "length": 4 + } + ], + "attacks": [ + "groundToAir" + ], + "penalties": [ + "Damage", + "Stall" + ], + "type": "action" + }, + { + "maneuvers": [ + { + "name": "tightBank", + "complexity": "complex", + "length": 4 + }, + { + "name": "straight", + "complexity": "simple", + "length": 4 + } + ], + "attacks": [ + "groundToAir" + ], + "penalties": [ + "Land Battle" + ], + "type": "action" + }, + { + "maneuvers": [ + { + "name": "barrelRoll", + "complexity": "complex", + "length": 8 + }, + { + "name": "turn", + "complexity": "simple", + "length": 4 + } + ], + "attacks": [], + "penalties": [ + "Damage", + "Ground Battle", + "Stall" + ], + "type": "action", + "needsEffect": true + }, + { + "maneuvers": [ + { + "name": "bankedTurn", + "complexity": "simple", + "length": 6 + }, + { + "name": "immelmann", + "complexity": "complex", + "length": 10 + } + ], + "attacks": [ + "airToGround" + ], + "penalties": [], + "type": "action" + }, + { + "maneuvers": [ + { + "name": "sBend", + "complexity": "simple", + "length": 4 + } + ], + "attacks": [ + "airToGround" + ], + "penalties": [ + "Stall" + ], + "type": "action" + }, + { + "maneuvers": [ + { + "name": "tightBank", + "complexity": "complex", + "length": 4 + } + ], + "attacks": [], + "penalties": [ + "Ground Battle" + ], + "type": "action" + }, + { + "maneuvers": [ + { + "name": "immelmann", + "complexity": "complex", + "length": 10 + }, + { + "name": "straight", + "complexity": "simple", + "length": 4 + } + ], + "attacks": [ + "groundToAir" + ], + "penalties": [ + "Damage", + "Ground Battle", + "Stall" + ], + "type": "action" + }, + { + "maneuvers": [], + "attacks": [ + "groundToAir" + ], + "penalties": [ + "Damage" + ], + "type": "action" + }, + { + "maneuvers": [], + "attacks": [ + "airToGround" + ], + "penalties": [ + "Damage", + "Stall" + ], + "type": "action" + } +] \ No newline at end of file diff --git a/tailwind.config.js b/tailwind.config.js index 8c4d1b2..42ece3e 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -7,12 +7,13 @@ module.exports = { ], theme: { extend: { - backgroundImage: { - 'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))', - 'gradient-conic': - 'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))', + container: { + center: true }, - }, + aspectRatio: { + card: '2.5 / 3.5' + }, + } }, plugins: [], } diff --git a/utils/camelToSpace.ts b/utils/camelToSpace.ts new file mode 100644 index 0000000..7dd709b --- /dev/null +++ b/utils/camelToSpace.ts @@ -0,0 +1 @@ +export const camelToSpace = (value: string) => value.replace(/[A-Z0-9]/g, e => ` ${e.toLowerCase()}`); \ No newline at end of file