diff --git a/public/vite.svg b/public/vite.svg deleted file mode 100644 index e7b8dfb..0000000 --- a/public/vite.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/App.tsx b/src/App.tsx index 2e15be0..5f9d14b 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -4,6 +4,7 @@ import TierCard from './components/TierCard'; import HistoryList from './components/HistoryList'; import type { Upgrades } from './components/GubbleStore'; import GubbleStore from './components/GubbleStore'; +import GameHeader from './components/GameHeader'; // --- Scratch Card Game Types and State --- export type Tier = { @@ -29,6 +30,9 @@ const TIERS: Tier[] = [ { name: 'Emperor', unlockPrice: 10000000000, buyPrice: 8000000000, gubblePointChance: 0.7 }, { name: 'God', unlockPrice: 100000000000, buyPrice: 80000000000, gubblePointChance: 0.8 }, { name: 'Goddess', unlockPrice: 1000000000000, buyPrice: 800000000000, gubblePointChance: 0.9 }, + { name: 'UltraGod', unlockPrice: 100000000000000, buyPrice: 80000000000000, gubblePointChance: 1.0 }, + { name: 'UltraGoddess', unlockPrice: 1000000000000000, buyPrice: 800000000000000, gubblePointChance: 1.0 }, + { name: 'Finale', unlockPrice: 10000000000000000, buyPrice: 8000000000000000, gubblePointChance: 1.0 }, ]; const START_MONEY = 6; @@ -52,7 +56,7 @@ function App() { }[]>([]); // --- Gubble Store State --- const [isStoreOpen, setIsStoreOpen] = useState(false); - const [upgrades, setUpgrades] = useState({ evenDouble: false, oddDouble: false, allTripple: false, gubbleDouble: false, addFourthNumber: false }); + const [upgrades, setUpgrades] = useState({ evenDouble: false, oddDouble: false, allTripple: false, gubbleDouble: false, addFourthNumber: false, autoscratcher: false }); const [upgradeBought, setUpgradeBought] = useState(false); // Ref for scroll container @@ -150,12 +154,7 @@ function App() { // --- UI --- return (
-
-

Scratch Card Game

-
Money: ${formatter.format(money)}
-
Gubble Points: {formatter.format(gubblePoints)}
- -
+ )} - +
); } diff --git a/src/components/Card.tsx b/src/components/Card.tsx index f711b3a..6227b1b 100644 --- a/src/components/Card.tsx +++ b/src/components/Card.tsx @@ -1,6 +1,7 @@ import React from 'react'; import type { Tier } from '../App'; import type { Upgrades } from './GubbleStore'; +import ScratchFieldButton from './ScratchFieldButton'; type CardProps = { card: { @@ -35,50 +36,17 @@ const Card: React.FC = ({ card, setCard, setMoney, setGubblePoints, u
{card.fields.map((field, idx) => ( - + field={field} + idx={idx} + card={card} + setCard={setCard} + setMoney={setMoney} + setGubblePoints={setGubblePoints} + upgrades={upgrades} + formatter={formatter} + /> ))}
Scratch each field one at a time. Winnings: 1-2 = 50%, 3-4 = 80%, 5-6 = 120% of card price.
diff --git a/src/components/GameHeader.tsx b/src/components/GameHeader.tsx new file mode 100644 index 0000000..c555ce3 --- /dev/null +++ b/src/components/GameHeader.tsx @@ -0,0 +1,25 @@ +import React from 'react'; + +interface GameHeaderProps { + money: number; + gubblePoints: number; + formatter: Intl.NumberFormat; + setIsStoreOpen: React.Dispatch>; +} + +const GameHeader: React.FC = ({ money, gubblePoints, formatter, setIsStoreOpen }) => ( +
+

Rubellos Glücksspiel

+
Money: ${formatter.format(money)}
+
Gubble Points: {formatter.format(gubblePoints)}
+ +
+); + +export default GameHeader; \ No newline at end of file diff --git a/src/components/GubbleStore.tsx b/src/components/GubbleStore.tsx index 4c02bbc..d03dd65 100644 --- a/src/components/GubbleStore.tsx +++ b/src/components/GubbleStore.tsx @@ -6,6 +6,7 @@ type Upgrades = { allTripple: boolean; gubbleDouble: boolean; addFourthNumber: boolean; + autoscratcher: boolean; }; type GubbleStoreProps = { @@ -22,6 +23,7 @@ const UPGRADE_LIST: { key: keyof Upgrades; label: string; cost: number; descript { key: 'allTripple', label: 'Tripple All Winnings', cost: 150, description: 'Tripple all winnings.' }, { key: 'gubbleDouble', label: 'Double Gubble Point Gains', cost: 50, description: 'Double gubble point gains.' }, { key: 'addFourthNumber', label: 'Add Fourth Winning Number', cost: 200, description: 'Adds a fourth winning number to each card.' }, + { key: 'autoscratcher', label: 'Auto Scratcher', cost: 400, description: 'Automatically scratches all tiles when you buy a card.' }, ]; const GubbleStore: React.FC = ({ isOpen, onClose, gubblePoints, upgrades, onBuyUpgrade }) => { @@ -32,6 +34,7 @@ const GubbleStore: React.FC = ({ isOpen, onClose, gubblePoints

Gubble Point Store

Gubble Points: {gubblePoints}
+

When you buy an upgrade, you will lose all your money and unlocked tiers. And return to the start with the upgrades enabled.

    {UPGRADE_LIST.map(upg => (
  • diff --git a/src/components/HistoryList.tsx b/src/components/HistoryList.tsx index fce9093..e50f472 100644 --- a/src/components/HistoryList.tsx +++ b/src/components/HistoryList.tsx @@ -9,9 +9,10 @@ type HistoryItem = { type HistoryListProps = { history: HistoryItem[]; + formatter: Intl.NumberFormat; }; -const HistoryList: React.FC = React.memo(({ history }) => { +const HistoryList: React.FC = React.memo(({ history, formatter }) => { if (history.length === 0) return null; return (
    @@ -21,7 +22,7 @@ const HistoryList: React.FC = React.memo(({ history }) => {
  • {h.tier} - Win: ${h.winnings} + Win: ${formatter.format(h.winnings)}
    {h.winningNumbers.map((n, j) => ( diff --git a/src/components/ScratchFieldButton.tsx b/src/components/ScratchFieldButton.tsx new file mode 100644 index 0000000..7fe7acf --- /dev/null +++ b/src/components/ScratchFieldButton.tsx @@ -0,0 +1,66 @@ +import React from 'react'; +import type { Upgrades } from './GubbleStore'; +import type { CardProps } from './Card'; + +const ScratchFieldButton: React.FC<{ + field: { value: number; scratched: boolean; won: number | null }; + idx: number; + card: CardProps['card']; + setCard: CardProps['setCard']; + setMoney: CardProps['setMoney']; + setGubblePoints: CardProps['setGubblePoints']; + upgrades?: Upgrades; + formatter: Intl.NumberFormat; +}> = ({ field, idx, card, setCard, setMoney, setGubblePoints, upgrades, formatter }) => { + const handleScratch = () => { + if (field.scratched) return; + // Gubble point gain (with upgrade) + if (Math.random() < (card.tier?.gubblePointChance ?? 0)) { + setGubblePoints((g) => g + (upgrades?.gubbleDouble ? 2 : 1)); + } + // Determine win amount + let won = null; + if (card.winningNumbers.includes(field.value)) { + won = 0; + if (idx < 2) won = Math.ceil((card.tier?.buyPrice ?? 0) * 0.5); + else if (idx < 4) won = Math.ceil((card.tier?.buyPrice ?? 0) * 0.8); + else won = Math.ceil((card.tier?.buyPrice ?? 0) * 1.2); + // Apply upgrades + if (upgrades?.allTripple) { + won = won * 2; + } + if (upgrades?.evenDouble && field.value % 2 === 0) { + won = won * 2; + } + if (upgrades?.oddDouble && field.value % 2 === 1) { + won = won * 2; + } + } + setCard((prev) => { + if (!prev) return prev; + const newFields = prev.fields.map((f, i) => i === idx ? { ...f, scratched: true, won } : f); + return { ...prev, fields: newFields }; + }); + if (won) setMoney((m) => m + won); + }; + if (upgrades?.autoscratcher) { + handleScratch(); + } + + return ( + + ); +}; + +export default ScratchFieldButton; \ No newline at end of file diff --git a/vite.config.ts b/vite.config.ts index c4069b7..44780a5 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -4,5 +4,6 @@ import tailwindcss from '@tailwindcss/vite' // https://vite.dev/config/ export default defineConfig({ + base: './', plugins: [react(), tailwindcss()], })