diff --git a/src/App.tsx b/src/App.tsx index 6e797cf..0c8e7e8 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -11,20 +11,16 @@ import { InfoPanel } from './components/InfoPanel' import { Joker } from './components/Joker' import { Round } from './components/Round' import { Shop } from './components/Shop' -import { Blinds, Consumables, DeckType } from './Constants' +import { Blinds } from './Constants' import { gameReducer, GameStateContext, initialGameState } from './GameState' import { cardSnap } from './Utilities' +import { MainMenu } from './components/MainMenu' export default function App() { const [ game, dispatch ] = useReducer(gameReducer, initialGameState) const gameRef = useRef(game) gameRef.current = game - useEffect(() => { - dispatch({type: 'init', payload: {deck: DeckType.Red }}) - dispatch({type: 'addCard', payload: {cardLocation: 'consumables', card: Consumables[41]}}) - }, []) - useEffect(() => { document.addEventListener('keydown', handleKeys) @@ -53,91 +49,93 @@ export default function App() { return ( <GameStateContext.Provider value={{ state: game, dispatch }}> - <div className='container'> - <div id='sidebar'> - <div id='top-sidebar'> - {game.state === 'blind-select' && <div>Choose your<br />next Blind</div>} - {game.state === 'scoring' && <Blind type='sidebar' blind={currBlindType} />} - {game.state === 'shop' && <img id='shop-logo' src={shopIcon} />} - </div> - <Round /> - <Calculator /> - <InfoPanel /> - </div> - <div id='main'> - <div id='top'> - <div id='jokers' className='card-container'> - <div id='joker-area' className='card-area'> - <label id='joker-bkg'>JOKERS</label> - {game.jokers.map(j => - <Joker key={j.id} {...j}/> - )} - </div> - <div id='joker-label' className='counter'>{`${game.jokers.length}/${game.stats.jokerSize}`}</div> - </div> - <div id='consumables' className='card-container'> - <div id='consumables-area' className='card-area'> - <label id='consumable-bkg'>CONSUMABLES</label> - {game.cards.consumables.map(c => ( - <Consumable key={c.id} {...c} /> - ))} - </div> - <div id='consumables-label' className='counter'>{`${game.cards.consumables.length}/${game.stats.consumableSize}`}</div> + {game.state === 'main-menu' ? <MainMenu /> : + <div className='container'> + <div id='sidebar'> + <div id='top-sidebar'> + {game.state === 'blind-select' && <div>Choose your<br />next Blind</div>} + {game.state === 'scoring' && <Blind type='sidebar' blind={currBlindType} />} + {game.state === 'shop' && <img id='shop-logo' src={shopIcon} />} </div> + <Round /> + <Calculator /> + <InfoPanel /> </div> - <div id='lower'> - <div id='content'> - {game.state === 'blind-select' && <> - <div id='blinds-container'> - <Blind type='select' blind={Blinds[0]} /> - <Blind type='select' blind={Blinds[1]} /> - <Blind type='select' blind={game.blind.boss} /> - </div> - </>} - {game.state === 'scoring' && <> - <div id='mid'> - {game.cards.submitted.map(c => <Card key={c.id} {...c} />)} + <div id='main'> + <div id='top'> + <div id='jokers' className='card-container'> + <div id='joker-area' className='card-area'> + <label id='joker-bkg'>JOKERS</label> + {game.jokers.map(j => + <Joker key={j.id} {...j}/> + )} </div> - <div id='bot'> - <Hand /> + <div id='joker-label' className='counter'>{`${game.jokers.length}/${game.stats.jokerSize}`}</div> + </div> + <div id='consumables' className='card-container'> + <div id='consumables-area' className='card-area'> + <label id='consumable-bkg'>CONSUMABLES</label> + {game.cards.consumables.map(c => ( + <Consumable key={c.id} {...c} /> + ))} </div> - </>} - {game.state === 'post-scoring' && <> - <div id='post-outer'> - <div id='post-container'> - <div id='post-inner'> - <div id='cash-out' onClick={() => { - dispatch({type: 'state', payload: { - state: 'shop', - amount: reward, - }}) - }}>{`Cash Out: $${reward}`}</div> - <Blind type='post' blind={currBlindType} /> - <div id='post-dots'>{'. '.repeat(49)}</div> - {game.stats.hands > 0 && - <div id='remaining-hands' className='extra-reward'> - <div className='num-extra'>{game.stats.hands}</div> - <div className='extra-reward-text'>{'Remaining Hands \[$1 each\]'}</div> - <div className='reward'>{'$'.repeat(game.stats.hands)}</div> - </div> - } - {game.stats.money > 4 && - <div id='interest' className='extra-reward'> - <div className='num-extra'>{Math.min(Math.floor(game.stats.money / 5), 5)}</div> - <div className='extra-reward-text'>{'1 interest per $5 \[5 max\]'}</div> - <div className='reward'>{'$'.repeat(Math.min(Math.floor(game.stats.money / 5), 5))}</div> - </div> - } + <div id='consumables-label' className='counter'>{`${game.cards.consumables.length}/${game.stats.consumableSize}`}</div> + </div> + </div> + <div id='lower'> + <div id='content'> + {game.state === 'blind-select' && <> + <div id='blinds-container'> + <Blind type='select' blind={Blinds[0]} /> + <Blind type='select' blind={Blinds[1]} /> + <Blind type='select' blind={game.blind.boss} /> + </div> + </>} + {game.state === 'scoring' && <> + <div id='mid'> + {game.cards.submitted.map(c => <Card key={c.id} {...c} />)} + </div> + <div id='bot'> + <Hand /> + </div> + </>} + {game.state === 'post-scoring' && <> + <div id='post-outer'> + <div id='post-container'> + <div id='post-inner'> + <div id='cash-out' onClick={() => { + dispatch({type: 'state', payload: { + state: 'shop', + amount: reward, + }}) + }}>{`Cash Out: $${reward}`}</div> + <Blind type='post' blind={currBlindType} /> + <div id='post-dots'>{'. '.repeat(49)}</div> + {game.stats.hands > 0 && + <div id='remaining-hands' className='extra-reward'> + <div className='num-extra'>{game.stats.hands}</div> + <div className='extra-reward-text'>{'Remaining Hands \[$1 each\]'}</div> + <div className='reward'>{'$'.repeat(game.stats.hands)}</div> + </div> + } + {game.stats.money > 4 && + <div id='interest' className='extra-reward'> + <div className='num-extra'>{Math.min(Math.floor(game.stats.money / 5), 5)}</div> + <div className='extra-reward-text'>{'1 interest per $5 \[5 max\]'}</div> + <div className='reward'>{'$'.repeat(Math.min(Math.floor(game.stats.money / 5), 5))}</div> + </div> + } + </div> </div> </div> - </div> - </>} - {game.state === 'shop' && <Shop />} + </>} + {game.state === 'shop' && <Shop />} + </div> + <Deck /> </div> - <Deck /> </div> </div> - </div> + } </GameStateContext.Provider> ) } diff --git a/src/Constants.ts b/src/Constants.ts index a77abc1..993b068 100644 --- a/src/Constants.ts +++ b/src/Constants.ts @@ -1,5 +1,24 @@ export enum DeckType { Abandoned, Anaglyph, Black, Blue, Challenge, Checkered, Erratic, Ghost, Green, Magic, Nebula, Painted, Plasma, Red, Yellow, Zodiac } +export const deckInfo: {[D in keyof typeof DeckType]: string} = { + Abandoned: 'Start run with\n no/ {orange}Face Cards\n in your deck', + Anaglyph: '', + Black: '{orange}+1/ Joker slot\n {blue}-1/ hand\n every round', + Blue: '{blue}+1/ hand\n every round', + Challenge: '', + Checkered: 'Start run with\n {orange}26/ {dark-purple}Spades/ and\n {orange}26/ {red}Hearts/ in deck', + Erratic: 'All Ranks and\n Suits in deck\n are randomized', + Ghost: '{indigo}Spectral/ cards may\n appear in the shop,\n start with a/ {indigo}Hex/ card', + Green: 'At end of each Round:\n {yellow}$2/ {small black}per remaining/ {blue}Hand\n {yellow}$1/ {small black}per remaining/ {red}Discard\n Earn no/ {orange}Interest', + Magic: 'Start run with the\n {purple}Crystal Ball/ voucher\n and/ {orange}2/ copies\n of/ {purple}The Fool', + Nebula: 'Start run with the\n {aqua}Telescope/ voucher\n {red}-1/ consumable slot', + Painted: '{orange}+2/ Hand Size,\n {orange}-1/ Joker Slot', + Plasma: '', + Red: '{red}+1/ discard\n every round', + Yellow: 'Start with\n extra/ {yellow}$10', + Zodiac: 'Start run with\n {purple}Tarot Merchant/,\n {aqua}Planet Merchant/,\n and {orange}Overstock' +} + export enum Suit { Spades, Hearts, Clubs, Diamonds } export enum Rank { Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten, Jack, Queen, King, Ace } export enum Edition { Foil, Holographic, Negative, Polychrome } @@ -15,22 +34,22 @@ export const editionInfo: {[E in keyof typeof Edition]: string} = { } export const enhancementInfo: {[E in keyof typeof Enhancement]: string} = { - Base: "", - Bonus: "{blue}+30/ extra chips", - Glass: "{red-invert}X2/ Mult\n{green}1 in 4/ chance\nto destroy card", - Gold: "{yellow}+$3/ if this\ncard is held in hand\nat end of round", - Lucky: "{green}1 in 5/ chance\nfor /{red}+20/ Mult\n{green}1 in 15/ chance\nto win /{yellow}$20", - Mult: "{red}+4/ Mult", - Steel: "{red-invert}X1.5/ Mult\nwhile this card\nstays in hand", - Stone: "{blue}+50/ Chips\nno rank or suit", - Wild: "Can be used\nas any suit" + Base: '', + Bonus: '{blue}+30/ extra chips', + Glass: '{red-invert}X2/ Mult\n{green}1 in 4/ chance\nto destroy card', + Gold: '{yellow}+$3/ if this\ncard is held in hand\nat end of round', + Lucky: '{green}1 in 5/ chance\nfor /{red}+20/ Mult\n{green}1 in 15/ chance\nto win /{yellow}$20', + Mult: '{red}+4/ Mult', + Steel: '{red-invert}X1.5/ Mult\nwhile this card\nstays in hand', + Stone: '{blue}+50/ Chips\nno rank or suit', + Wild: 'Can be used\nas any suit' } export const sealInfo: {[S in keyof typeof Seal]: string} = { - Blue: "Creates the/{aqua}Planet/card\nfor final played/{orange}poker hand\nof round if/{orange}held/in hand\n{grey}(Must have room)", - Gold: "Earn/{yellow}$3/when this\ncard is played\nand scores", - Purple: "Creates a/{purple}Tarot/card\nwhen/{orange}discarded\n{grey}(Must have room)", - Red: "Retrigger this\ncard/{orange}1/time" + Blue: 'Creates the/{aqua}Planet/card\nfor final played/{orange}poker hand\nof round if/{orange}held/in hand\n{grey}(Must have room)', + Gold: 'Earn/{yellow}$3/when this\ncard is played\nand scores', + Purple: 'Creates a/{purple}Tarot/card\nwhen/{orange}discarded\n{grey}(Must have room)', + Red: 'Retrigger this\ncard/{orange}1/time' } export const rankChips: {[R in keyof typeof Rank]: number} = { diff --git a/src/GameState.ts b/src/GameState.ts index 09ddc49..6f38a9d 100644 --- a/src/GameState.ts +++ b/src/GameState.ts @@ -5,8 +5,7 @@ import { CardInfo } from "./components/CardInfo" import { Activation, JokerInstance, JokerType } from "./components/JokerInfo" import Rand from "rand-seed" -export const seed = (Math.random() + 1).toString(36).toUpperCase().slice(2) -export const Random = new Rand(seed) +export let Random: Rand = new Rand() export const levelHand = ({ hand, n = 1 }: {hand: keyof typeof handLevels, n?: number}) => { handLevels[hand].level += n @@ -14,10 +13,12 @@ export const levelHand = ({ hand, n = 1 }: {hand: keyof typeof handLevels, n?: n handLevels[hand].mult += handUpgrade[hand].mult * n } -type GameStates = 'blind-select' | 'scoring' | 'post-scoring' | 'shop' +type GameStates = 'main-menu' | 'blind-select' | 'scoring' | 'post-scoring' | 'shop' export type GameState = { state: GameStates + seed: string + seeded: boolean stats: { handSize: number @@ -96,7 +97,8 @@ export type GameAction = { 'setLastUsedConsumable' | 'updateJokers' | 'addJoker' | 'removeJoker' | 'shop-select' | 'shop-remove' | 'reroll' payload?: { - deck?: DeckType, + deck?: DeckType + seed?: string state?: GameStates @@ -113,7 +115,9 @@ export type GameAction = { } export const initialGameState: GameState = { - state: 'shop' as GameStates, + state: 'main-menu' as GameStates, + seed: '', + seeded: false, stats: { handSize: 8, @@ -134,8 +138,8 @@ export const initialGameState: GameState = { offers: [], weights: { Joker: 20, - Tarot: 0, - Planet: 1, + Tarot: 4, + Planet: 4, Card: 0, Spectral: 0 } @@ -192,16 +196,13 @@ export const gameReducer = (state: GameState, action: GameAction): GameState => case DeckType.Erratic: next.stats.deck = DeckType.Erratic for(let i = 1; i <= 52; i++) { - let rank = Rank[ranks[Math.floor(Random.next()*ranks.length)*0+4]] + let rank = Rank[ranks[Math.floor(Random.next()*ranks.length)]] arr.push( { id: i, suit: Suit[suits[Math.floor(Random.next()*suits.length)]], rank: rank, chips: rankChips[rank], - edition: Edition.Polychrome, - enhancement: (Random.next() < .9 ? Enhancement.Steel : Enhancement.Glass), - seal: Seal.Red, deck: DeckType.Erratic } ) @@ -220,7 +221,10 @@ export const gameReducer = (state: GameState, action: GameAction): GameState => ) })}) } + next = initialGameState next = {...next, + seed: action.payload?.seed ?? (Math.random() + 1).toString(36).toUpperCase().slice(2), + seeded: action.payload?.seed !== undefined, blind: {...state.blind, boss: boss_roll(state.stats.ante), base: ante_base(state.stats.ante) @@ -230,6 +234,7 @@ export const gameReducer = (state: GameState, action: GameAction): GameState => deck: arr } } + Random = new Rand(next.seed) break case 'state': next = {...next, diff --git a/src/assets/decks/Abandoned.png b/src/assets/decks/Abandoned.png new file mode 100644 index 0000000..8753d83 Binary files /dev/null and b/src/assets/decks/Abandoned.png differ diff --git a/src/assets/decks/Anaglyph.png b/src/assets/decks/Anaglyph.png new file mode 100644 index 0000000..bf9ec53 Binary files /dev/null and b/src/assets/decks/Anaglyph.png differ diff --git a/src/assets/decks/Black.png b/src/assets/decks/Black.png new file mode 100644 index 0000000..a2806ff Binary files /dev/null and b/src/assets/decks/Black.png differ diff --git a/src/assets/decks/Blue.png b/src/assets/decks/Blue.png new file mode 100644 index 0000000..0bd3880 Binary files /dev/null and b/src/assets/decks/Blue.png differ diff --git a/src/assets/decks/Checkered.png b/src/assets/decks/Checkered.png new file mode 100644 index 0000000..6143195 Binary files /dev/null and b/src/assets/decks/Checkered.png differ diff --git a/src/assets/decks/Erratic.png b/src/assets/decks/Erratic.png index e1bb80f..043e74f 100644 Binary files a/src/assets/decks/Erratic.png and b/src/assets/decks/Erratic.png differ diff --git a/src/assets/decks/Ghost.png b/src/assets/decks/Ghost.png new file mode 100644 index 0000000..c2cca84 Binary files /dev/null and b/src/assets/decks/Ghost.png differ diff --git a/src/assets/decks/Green.png b/src/assets/decks/Green.png new file mode 100644 index 0000000..42303ad Binary files /dev/null and b/src/assets/decks/Green.png differ diff --git a/src/assets/decks/Magic.png b/src/assets/decks/Magic.png new file mode 100644 index 0000000..cfc8a68 Binary files /dev/null and b/src/assets/decks/Magic.png differ diff --git a/src/assets/decks/Nebula.png b/src/assets/decks/Nebula.png new file mode 100644 index 0000000..3997a7b Binary files /dev/null and b/src/assets/decks/Nebula.png differ diff --git a/src/assets/decks/Painted.png b/src/assets/decks/Painted.png new file mode 100644 index 0000000..6ecdf4e Binary files /dev/null and b/src/assets/decks/Painted.png differ diff --git a/src/assets/decks/Plasma.png b/src/assets/decks/Plasma.png new file mode 100644 index 0000000..f3235da Binary files /dev/null and b/src/assets/decks/Plasma.png differ diff --git a/src/assets/decks/Red.png b/src/assets/decks/Red.png index 46b3952..5db9a10 100644 Binary files a/src/assets/decks/Red.png and b/src/assets/decks/Red.png differ diff --git a/src/assets/decks/Yellow.png b/src/assets/decks/Yellow.png new file mode 100644 index 0000000..4f3f6ca Binary files /dev/null and b/src/assets/decks/Yellow.png differ diff --git a/src/assets/decks/Zodiac.png b/src/assets/decks/Zodiac.png new file mode 100644 index 0000000..9fb9cca Binary files /dev/null and b/src/assets/decks/Zodiac.png differ diff --git a/src/assets/logo.png b/src/assets/logo.png new file mode 100644 index 0000000..a3ccb5e Binary files /dev/null and b/src/assets/logo.png differ diff --git a/src/components/Blind.css b/src/components/Blind.css index 50f87e4..6fc228f 100644 --- a/src/components/Blind.css +++ b/src/components/Blind.css @@ -243,6 +243,7 @@ #blind-select.true:hover { cursor: pointer; + filter: brightness(.75); } #blind-select.false { diff --git a/src/components/Card.css b/src/components/Card.css index 2e1c4b9..b08e692 100644 --- a/src/components/Card.css +++ b/src/components/Card.css @@ -68,7 +68,10 @@ } .card:hover { scale: 1.1; } -.card.standard:hover { cursor: pointer; } +.card.standard:hover { + cursor: pointer; + filter: brightness(.75); +} .card:active { z-index: 1; } diff --git a/src/components/Consumable.css b/src/components/Consumable.css index 4255e3a..e6d8194 100644 --- a/src/components/Consumable.css +++ b/src/components/Consumable.css @@ -17,6 +17,7 @@ div[id^='consumable_'].shopping { div[id^='consumable_']:hover { cursor: pointer; + filter: brightness(.75); scale: 1.1; } diff --git a/src/components/Deck.css b/src/components/Deck.css index dd0566d..73fd282 100644 --- a/src/components/Deck.css +++ b/src/components/Deck.css @@ -25,6 +25,7 @@ #face-down:hover { cursor: pointer; + filter: brightness(.75); } #deck-label { diff --git a/src/components/DeckMenu.css b/src/components/DeckMenu.css index a54bc30..53bc60e 100644 --- a/src/components/DeckMenu.css +++ b/src/components/DeckMenu.css @@ -41,6 +41,7 @@ .view-button:hover { cursor: pointer; + filter: brightness(.75); } .arrow { @@ -53,12 +54,12 @@ border-top: 20px solid var(--red); } -#deck-view { +#deck-menu #deck-view { display: flex; flex-direction: row; } -#deck-info { +#deck-menu #deck-info { display: flex; flex-direction: row; margin: 0 8px 0 36px; @@ -67,13 +68,13 @@ width: fit-content; } -#deck-info-left { +#deck-menu #deck-info-left { display: flex; flex-direction: column; justify-content: space-between; } -#deck-name { +#deck-menu #deck-name { background-color: var(--grey); text-align: center; border-radius: 8px; @@ -82,7 +83,7 @@ line-height: 32px; } -#deck-bio { +#deck-menu #deck-bio { display: flex; align-items: center; justify-content: center; @@ -201,4 +202,5 @@ #back:hover { cursor: pointer; + filter: brightness(.75); } \ No newline at end of file diff --git a/src/components/Hand.css b/src/components/Hand.css index ec1dd83..8231292 100644 --- a/src/components/Hand.css +++ b/src/components/Hand.css @@ -28,6 +28,7 @@ .button.true:hover { cursor: pointer; + filter: brightness(.75); } #ship.true { @@ -64,4 +65,5 @@ .sort-button:hover { cursor: pointer; + filter: brightness(.75); } \ No newline at end of file diff --git a/src/components/InfoPanel.css b/src/components/InfoPanel.css index fd62341..a02e61e 100644 --- a/src/components/InfoPanel.css +++ b/src/components/InfoPanel.css @@ -24,6 +24,7 @@ .info-button:hover { cursor: pointer; + filter: brightness(.75); } #run-info-button { diff --git a/src/components/Joker.css b/src/components/Joker.css index 1257d0b..a6a773e 100644 --- a/src/components/Joker.css +++ b/src/components/Joker.css @@ -27,6 +27,7 @@ div[id^='joker_'].shopping { div[id^='joker_']:hover { cursor: pointer; + filter: brightness(.75); scale: 1.1; } diff --git a/src/components/MainMenu.css b/src/components/MainMenu.css new file mode 100644 index 0000000..c261e3c --- /dev/null +++ b/src/components/MainMenu.css @@ -0,0 +1,25 @@ +#main-menu { + display: flex; + flex-direction: column; + align-items: center; +} + +#logo { + width: 666px; +} + +#main-menu-buttons { + display: flex; + width: fit-content; + justify-content: center; + background-color: var(--grey); + border-radius: 8px; +} + +#play { + font-size: 64px; + background-color: var(--blue); + border-radius: 8px; + margin: 8px 32px; + padding: 16px 64px; +} \ No newline at end of file diff --git a/src/components/MainMenu.tsx b/src/components/MainMenu.tsx new file mode 100644 index 0000000..fd38450 --- /dev/null +++ b/src/components/MainMenu.tsx @@ -0,0 +1,18 @@ +import { useState } from 'react' +import logo from '../assets/logo.png' +import './MainMenu.css' +import { PlayMenu } from './PlayMenu' + +export const MainMenu = () => { + const [ playMenu, setPlayMenu ] = useState(false) + + return ( + <div id='main-menu'> + <img id='logo' src={logo} /> + <div id='main-menu-buttons'> + <div id='play' onClick={() => setPlayMenu(true)}>PLAY</div> + </div> + {playMenu && <PlayMenu setMenu={setPlayMenu} />} + </div> + ) +} \ No newline at end of file diff --git a/src/components/Options.tsx b/src/components/Options.tsx index 2995ce3..73b62f4 100644 --- a/src/components/Options.tsx +++ b/src/components/Options.tsx @@ -1,24 +1,32 @@ +import { useContext } from 'react' import './Options.css' -import { seed } from '../GameState' +import { GameStateContext } from '../GameState' type OptionsProps = { setMenu: React.Dispatch<React.SetStateAction<boolean>> } export const Options = (props: OptionsProps) => { + const { state: game, dispatch } = useContext(GameStateContext) + return ( <div id='options-outline'> <div id='options-menu'> <div id='seed-menu'> <div>Seed:</div> - <div id='seed'>{seed}</div> + <div id='seed'>{game.seed}</div> <div id='copy-seed' onClick={() => { - navigator.clipboard.writeText(seed) + navigator.clipboard.writeText(game.seed) }}>Copy</div> </div> - <div id='new-run' className='options-button' onClick={() => { - window.location.reload() - }}>New Run</div> + <div id='main-menu' className='options-button' onClick={() => { + dispatch({type: 'init', payload: {deck: game.stats.deck}}) + dispatch({type: 'state', payload: {state: 'blind-select'}}) + props.setMenu(false) + }}>Main Menu</div> + <div id='main-menu' className='options-button' onClick={() => { + dispatch({type: 'state', payload: {state: 'main-menu'}}) + }}>Main Menu</div> <div id='options-back' onClick={() => props.setMenu(false)}>Back</div> </div> </div> diff --git a/src/components/PlayMenu.css b/src/components/PlayMenu.css new file mode 100644 index 0000000..ab082c9 --- /dev/null +++ b/src/components/PlayMenu.css @@ -0,0 +1,170 @@ +#play-menu { + position: absolute; + background-color: var(--grey); + border: 4px solid var(--light-grey); + border-radius: 8px; +} + +#play-menu > #views { + display: flex; + justify-content: center; + position: relative; + margin-top: 24px; +} + +#play-menu .view { + position: relative; + margin: 8px; + background-color: var(--red); + font-size: 32px; + line-height: 32px; + padding: 8px 24px; + border-radius: 8px; + text-align: center; + width: 140px; + padding: 8px; +} + +#play-menu .view:hover { + cursor: pointer; + filter: brightness(.75); +} + +#play-menu-arrow { transform: .1s; } + +#deck-selection { + display: flex; + margin: 16px 64px; +} + +.cycle-button { + display: flex; + align-items: center; + justify-content: center; + background-color: var(--red); + border-radius: 8px; + padding: 12px; + margin: 4px; +} + +.cycle-button:hover { + cursor: pointer; + filter: brightness(.75); +} + +#deck-display { + display: flex; + background-color: var(--dark-grey); + border-radius: 8px; + margin: 16px 0; +} + +#deck-display img { + flex: 1; + width: 104px; + height: 140px; + margin: 8px; +} + +#deck-info { + display: flex; + flex-direction: column; + align-items: center; + background-color: var(--grey); + border-radius: 8px; + margin: 8px; + width: 250px; +} + +#deck-name { + font-size: 32px; + margin-top: 8px; +} + +#description-bkg { + display: flex; + justify-content: center; + align-items: center; + text-align: center; + background-color: aliceblue; + border-radius: 8px; + margin: 8px 4px; + width: 242px; + height: 92px; + font-size: 20px; +} + +#seeded-info { + display: flex; + justify-content: center; + align-items: center; + min-height: 32px; +} + +#seed-disclaimer { + display: block; + width: 120px; + text-align: center; +} + +#enter-seed { + background-color: var(--blue); + border-radius: 8px; + border: none; + text-align: center; + font-size: 20px; + width: 130px; + font-family: m6x11; + font-size: 24px; +} +::placeholder { + color: deepskyblue; + font-family: m6x11; +} + +#paste-seed { + background-color: var(--blue); + border-radius: 8px; + margin: 0 8px; + width: 50px; + text-align: center; +} + +#bottom { + display: flex; + justify-content: center; + align-items: center; +} + +#seeded { + display: flex; +} + +#isSeeded { + border: 2px solid aliceblue; + background-color: var(--red); + border-radius: 16px; +} + +#play-button { + font-size: 48px; + background-color: var(--blue); + border-radius: 8px; + margin: 8px 32px; + padding: 8px 128px; +} + +#play-menu-back { + margin: 8px auto; + font-size: 32px; + line-height: 32px; + width: 98%; + text-align: center; + background-color: var(--orange); + border-radius: 8px; +} + +#play-menu-back:hover { + cursor: pointer; + filter: brightness(.75); +} \ No newline at end of file diff --git a/src/components/PlayMenu.tsx b/src/components/PlayMenu.tsx new file mode 100644 index 0000000..208bb81 --- /dev/null +++ b/src/components/PlayMenu.tsx @@ -0,0 +1,88 @@ +import { useContext, useEffect, useState } from 'react' +import './PlayMenu.css' +import { deckInfo, DeckType } from '../Constants' +import { getImage } from '../Utilities' +import { GameStateContext } from '../GameState' +const images: Record<string, { default: string }> = import.meta.glob('../assets/decks/*.png', { eager: true }) + +export const PlayMenu = ({ setMenu }: {setMenu: React.Dispatch<React.SetStateAction<boolean>>}) => { + const { dispatch } = useContext(GameStateContext) + const [ view, setView ] = useState<'new-run'>('new-run') + const decks = [DeckType.Red, DeckType.Blue, DeckType.Yellow, DeckType.Green, DeckType.Black, DeckType.Magic, DeckType.Nebula, DeckType.Ghost, DeckType.Abandoned, DeckType.Checkered, DeckType.Zodiac, DeckType.Painted, DeckType.Anaglyph, DeckType.Plasma, DeckType.Erratic] + const [ deckIndex, setDeckIndex ] = useState(0) + const [ seeded, setSeeded ] = useState(false) + const [ seed, setSeed ] = useState<string>() + + useEffect(() => { + const arrow = document.getElementById('play-menu-arrow')! + const button = document.getElementById(view)!, rect = button.getBoundingClientRect() + const left = button.offsetLeft + (rect.width - arrow.getBoundingClientRect().width) / 2 + arrow.style.left = `${left}px` + }, [view]) + + const deckImage = getImage(`../assets/decks/${DeckType[decks[deckIndex]]}.png`, images) + const deckDescription = deckInfo[DeckType[decks[deckIndex]] as keyof typeof deckInfo].split('\n').map((line, i) => + <div key={i}> + {line.split('/').map((str, i) => + <div key={i} className={str.match(/{.+}/)?.[0].slice(1, -1) ?? 'black'} style={{display: 'inline'}}> + {str.replace(/{.+}/g, '')} + </div> + )} + </div> + ) + + return ( + <div id='play-menu'> + <div id='views'> + <div id='play-menu-arrow' className='arrow' /> + <div id='new-run' className='view-container'> + <div className='view' onClick={() => setView('new-run')}>New Run</div> + </div> + </div> + <div id='new-run-area'> + {view === 'new-run' && <> + <div id='deck-selection'> + <div className='cycle-button' onClick={() => setDeckIndex(Math.max(0, deckIndex - 1))}>{'<'}</div> + <div id='deck-display'> + <img src={deckImage}/> + <div id='deck-info'> + <div id='deck-name'>{`${DeckType[decks[deckIndex]]} Deck`}</div> + <div id='description-bkg'> + <div id='description'> + {deckDescription} + </div> + </div> + </div> + </div> + <div className='cycle-button' onClick={() => setDeckIndex(Math.min(decks.length-1, deckIndex + 1))}>{'>'}</div> + </div> + <div id='stake-selection'></div> + <div id='seeded-info'> + {seeded && <> + <div id='seed-disclaimer'>All Unlocks and Discoveries disabled</div> + <input type='text' id='enter-seed' placeholder='Enter Seed' value={seed} onChange={e => setSeed(e.target.value)}/> + <div id='paste-seed' onClick={() => { + navigator.clipboard.readText().then(text => { + setSeed(text) + }).catch(_ => { + alert("Failed to read clipboard contents! (Make sure to allow us to read your clipboard contents)") + }) + }}>Paste Seed</div> + </>} + </div> + <div id='bottom'> + <div id='seeded'> + <div>Seeded Run</div> + <input id='isSeeded' type='checkbox' checked={seeded} onChange={() => setSeeded(!seeded)} /> + </div> + <div id='play-button' onClick={() => { + dispatch({type: 'init', payload: {deck: decks[deckIndex], seed: seed}}) + dispatch({type: 'state', payload: {state: 'blind-select'}}) + }}>PLAY</div> + </div> + </>} + </div> + <div id='play-menu-back' onClick={() => setMenu(false)}>Back</div> + </div> + ) +} \ No newline at end of file diff --git a/src/components/Round.css b/src/components/Round.css index dc6c210..3b1a9fc 100644 --- a/src/components/Round.css +++ b/src/components/Round.css @@ -37,6 +37,7 @@ #score-display:hover { cursor: pointer; + filter: brightness(.75); } #stake-icon { diff --git a/src/components/RunInfo.css b/src/components/RunInfo.css index e7a7548..dcf8fe6 100644 --- a/src/components/RunInfo.css +++ b/src/components/RunInfo.css @@ -1,6 +1,5 @@ #run-info { position: absolute; - z-index: 1; left: 350px; right: 370px; top: 100px; @@ -20,6 +19,7 @@ #run-info-views:hover { cursor: pointer; + filter: brightness(.75); } .run-info-view { @@ -152,4 +152,5 @@ #run-info-back:hover { cursor: pointer; + filter: brightness(.75); } \ No newline at end of file diff --git a/src/components/Shop.css b/src/components/Shop.css index 2a79cb8..822f313 100644 --- a/src/components/Shop.css +++ b/src/components/Shop.css @@ -47,6 +47,7 @@ .shop-button:hover { cursor: pointer; + filter: brightness(.75); } #next-round { background-color: var(--red); } diff --git a/src/index.css b/src/index.css index 9c5cb3c..2a4da90 100644 --- a/src/index.css +++ b/src/index.css @@ -46,4 +46,5 @@ .aqua { color: var(--aqua) } .purple { color: var(--purple) } .dark-purple { color: var(--dark-purple) } -.grey { color: var(--light-grey) } \ No newline at end of file +.grey { color: var(--light-grey) } +.black { color: black } \ No newline at end of file