diff --git a/src/components/Console.tsx b/src/components/Console.tsx index 0795146..7f301b6 100644 --- a/src/components/Console.tsx +++ b/src/components/Console.tsx @@ -40,4 +40,4 @@ export const Console: React.FC = () => { )} ) -} \ No newline at end of file +} diff --git a/src/components/Market.tsx b/src/components/Market.tsx index bc72fb5..9bb2a92 100644 --- a/src/components/Market.tsx +++ b/src/components/Market.tsx @@ -1,6 +1,6 @@ import React, { useRef } from 'react' import { useGameStore } from '../store/useGameStore' -import { MARKET_ITEMS } from '../constants' +import { MARKET_ITEMS, MarketItem } from '../constants' import FloatingMessage, { FloatingMessagesHandle } from './FloatingMessages' import { styled } from '@linaria/react' @@ -72,12 +72,20 @@ const ActionButton = styled.button<{ disabled?: boolean }>` ` const MarketComponent: React.FC = () => { - const { inventory, cash, buyItem, sellItem } = useGameStore() + const { inventory, cash, buyItem, sellItem, landPurchases } = useGameStore() const floatingMessagesRef = useRef(null) + const getItemPrice = (item: MarketItem) => { + if (item.id === 'additional_land') { + return Math.floor(100 * Math.pow(1.5, landPurchases)) + } + return item.buyPrice + } + const handleBuy = (itemId: string, e: React.MouseEvent) => { const item = MARKET_ITEMS[itemId] - if (!item || item.buyPrice === null || cash < item.buyPrice) return + const price = getItemPrice(item) + if (!item || price === null || cash < price) return buyItem(itemId) @@ -182,11 +190,13 @@ const MarketComponent: React.FC = () => { {item.emoji} {item.name} - ${item.buyPrice} + ${getItemPrice(item)} handleBuy(item.id, e)} - disabled={item.buyPrice ? cash < item.buyPrice : true} + disabled={ + getItemPrice(item) ? cash < getItemPrice(item)! : true + } > Buy diff --git a/src/components/Temple.tsx b/src/components/Temple.tsx index e7e4263..5a748c6 100644 --- a/src/components/Temple.tsx +++ b/src/components/Temple.tsx @@ -34,10 +34,10 @@ const PrayerButton = styled.button<{ disabled?: boolean }>` padding: 1rem 2rem; font-size: 1.25rem; border-radius: 0.5rem; - background-color: ${props => props.disabled ? '#9ca3af' : '#8b5cf6'}; + background-color: ${(props) => (props.disabled ? '#9ca3af' : '#8b5cf6')}; color: white; font-weight: bold; - cursor: ${props => props.disabled ? 'not-allowed' : 'pointer'}; + cursor: ${(props) => (props.disabled ? 'not-allowed' : 'pointer')}; border: none; transition: all 0.2s; display: flex; @@ -63,7 +63,7 @@ const UpgradesGrid = styled.div` ` const UpgradeCard = styled.div<{ purchased?: boolean }>` - background-color: ${props => props.purchased ? '#e5e7eb' : '#f3f4f6'}; + background-color: ${(props) => (props.purchased ? '#e5e7eb' : '#f3f4f6')}; border: 1px solid #d1d5db; border-radius: 0.5rem; padding: 1rem; @@ -99,25 +99,33 @@ const CostItem = styled.div` const PurchaseButton = styled.button<{ disabled?: boolean }>` padding: 0.5rem 1rem; border-radius: 0.25rem; - background-color: ${props => props.disabled ? '#9ca3af' : '#3b82f6'}; + background-color: ${(props) => (props.disabled ? '#9ca3af' : '#3b82f6')}; color: white; font-weight: bold; - cursor: ${props => props.disabled ? 'not-allowed' : 'pointer'}; + cursor: ${(props) => (props.disabled ? 'not-allowed' : 'pointer')}; border: none; margin-top: auto; ` const TempleComponent: React.FC = () => { - const { inventory, purchasedUpgrades, purchaseUpgrade, pray, piety, actionCooldown } = useGameStore() + const { + inventory, + purchasedUpgrades, + purchaseUpgrade, + pray, + piety, + actionCooldown, + } = useGameStore() const handlePurchase = (upgradeId: string) => { purchaseUpgrade(upgradeId) } const canAffordUpgrade = (costs: UpgradeCost[]) => { - return costs.every(cost => - inventory[cost.itemId] !== undefined && - inventory[cost.itemId] >= cost.amount + return costs.every( + (cost) => + inventory[cost.itemId] !== undefined && + inventory[cost.itemId] >= cost.amount, ) } @@ -133,7 +141,10 @@ const TempleComponent: React.FC = () => { } // Check requirements - if (upgrade.id === 'aqueous_vigor_2' && !purchasedUpgrades.includes('aqueous_vigor_1')) { + if ( + upgrade.id === 'aqueous_vigor_2' && + !purchasedUpgrades.includes('aqueous_vigor_1') + ) { return false } @@ -144,7 +155,10 @@ const TempleComponent: React.FC = () => { if (purchasedUpgrades.includes(upgrade.id)) { return 'Purchased' } - if (upgrade.id === 'aqueous_vigor_2' && !purchasedUpgrades.includes('aqueous_vigor_1')) { + if ( + upgrade.id === 'aqueous_vigor_2' && + !purchasedUpgrades.includes('aqueous_vigor_1') + ) { return 'Requires Aqueous Vigor I' } if (!canAffordUpgrade(upgrade.cost)) { @@ -156,13 +170,10 @@ const TempleComponent: React.FC = () => { return ( Temple - + Piety Level: {piety} - pray()} - disabled={actionCooldown > 0} - > + pray()} disabled={actionCooldown > 0}> 🙏 Pray to the Gods {actionCooldown > 0 && ` (${(actionCooldown / 1000).toFixed(1)}s)`} @@ -182,10 +193,10 @@ const TempleComponent: React.FC = () => { Cost: {upgrade.cost.map((cost, index) => ( - {cost.amount} {cost.itemId} - (You have: {inventory[cost.itemId] || 0}) + {cost.amount} {cost.itemId} + (You have: {inventory[cost.itemId] || 0}) ))} @@ -203,4 +214,4 @@ const TempleComponent: React.FC = () => { ) } -export default TempleComponent \ No newline at end of file +export default TempleComponent diff --git a/src/constants/index.ts b/src/constants/index.ts index 0446bec..b7b02d3 100644 --- a/src/constants/index.ts +++ b/src/constants/index.ts @@ -43,20 +43,25 @@ export const UPGRADES: Record = { id: 'aqueous_vigor_1', name: 'Aqueous Vigor I', description: 'Reduces watering cooldown by 25%', - cost: [{ - itemId: 'celery', - amount: 10 - }] + cost: [ + { + itemId: 'celery', + amount: 10, + }, + ], }, aqueous_vigor_2: { id: 'aqueous_vigor_2', name: 'Aqueous Vigor II', - description: 'Reduces watering cooldown by an additional 25% (requires Aqueous Vigor I)', - cost: [{ - itemId: 'celery', - amount: 25 - }] - } + description: + 'Reduces watering cooldown by an additional 25% (requires Aqueous Vigor I)', + cost: [ + { + itemId: 'celery', + amount: 25, + }, + ], + }, } export interface MarketItem { @@ -68,6 +73,13 @@ export interface MarketItem { } export const MARKET_ITEMS: Record = { + additional_land: { + id: 'additional_land', + name: 'Additional Land', + emoji: '🌱', + buyPrice: 100, + sellPrice: null, + }, celery_seed: { id: 'celery_seed', name: 'Celery Seeds', diff --git a/src/store/useGameStore.ts b/src/store/useGameStore.ts index 1453503..3ff46fe 100644 --- a/src/store/useGameStore.ts +++ b/src/store/useGameStore.ts @@ -54,18 +54,20 @@ export const hasSaveInSlot = (slot: number): boolean => { return !!localStorage.getItem(`${SAVE_SLOT_PREFIX}${slot}`) } -const addConsoleMessage = (state: GameState, text: string) => ( - state.consoleMessages = [ +const addConsoleMessage = (state: GameState, text: string) => + (state.consoleMessages = [ { id: Math.random().toString(36).substring(7), text, timestamp: Date.now(), }, ...state.consoleMessages, - ].slice(0, 50) -) + ].slice(0, 50)) -const progressRandomImmaturePlot = (state: GameState, growthAmount: number): boolean => { +const progressRandomImmaturePlot = ( + state: GameState, + growthAmount: number, +): boolean => { // Find all immature plants const immaturePlots: { row: number; col: number }[] = [] state.plots.forEach((row, rowIndex) => { @@ -78,14 +80,15 @@ const progressRandomImmaturePlot = (state: GameState, growthAmount: number): boo if (immaturePlots.length > 0) { // Select a random immature plot and progress it - const randomPlot = immaturePlots[Math.floor(Math.random() * immaturePlots.length)] + const randomPlot = + immaturePlots[Math.floor(Math.random() * immaturePlots.length)] const plot = state.plots[randomPlot.row][randomPlot.col] - + const crop = CROPS[plot.current!.cropId] const actualGrowthAmount = crop.growthTicks * growthAmount plot.current!.progress = Math.min( crop.growthTicks, - plot.current!.progress + actualGrowthAmount + plot.current!.progress + actualGrowthAmount, ) return true } @@ -122,6 +125,7 @@ export const useGameStore = create< consoleMessages: [], purchasedUpgrades: [], piety: 50, + landPurchases: 0, assignCrop: (row, col, cropId) => { set( @@ -177,10 +181,7 @@ export const useGameStore = create< cooldownMultiplier *= 0.75 // Additional 25% reduction } - const finalCooldown = Math.max( - 0, - COOLDOWN_DURATION * cooldownMultiplier - ) + const finalCooldown = Math.max(0, COOLDOWN_DURATION * cooldownMultiplier) set( produce((state) => { @@ -262,7 +263,7 @@ export const useGameStore = create< state.plots[rowIndex][colIndex].moisture = plot.moisture - waterNeeded state.plots[rowIndex][colIndex].current.progress = newProgress - + // If the plot just became mature, add a message if (mature && !plot.current.mature) { addConsoleMessage( @@ -270,7 +271,7 @@ export const useGameStore = create< `Plot (${rowIndex + 1},${colIndex + 1}) ${crop.name} is ready to harvest!`, ) } - + state.plots[rowIndex][colIndex].current.mature = mature } }) @@ -321,21 +322,57 @@ export const useGameStore = create< }, buyItem: (itemId) => { - const { cash } = get() + const { cash, landPurchases, plots, fieldSize } = get() const item = MARKET_ITEMS[itemId] if (!item || item.buyPrice === null || item.buyPrice === undefined) { return } - if (cash < item.buyPrice) { + // Calculate price for Additional Land based on number of purchases + let price = item.buyPrice + if (itemId === 'additional_land') { + price = Math.floor(100 * Math.pow(1.5, landPurchases)) + } + + if (cash < price) { return } set( produce((state) => { - state.cash -= item.buyPrice! - state.inventory[itemId] = (state.inventory[itemId] || 0) + 1 + state.cash -= price + + // Special handling for Additional Land + if (itemId === 'additional_land') { + state.landPurchases += 1 + if (state.fieldSize < state.maxFieldSize) { + const newSize = state.fieldSize + 1 + + // Add a new row + state.plots.push( + Array(newSize) + .fill(0) + .map(() => ({ + moisture: 0, + fertility: 1, + })), + ) + + // Add a new column to each existing row + state.plots.forEach((row: PlotState[]) => { + row.push({ + moisture: 0, + fertility: 1, + }) + }) + + state.fieldSize = newSize + addConsoleMessage(state, 'Field size increased!') + } + } else { + state.inventory[itemId] = (state.inventory[itemId] || 0) + 1 + } }), ) }, @@ -400,7 +437,9 @@ export const useGameStore = create< // Check if player can afford all costs const canAfford = upgrade.cost.every( - cost => inventory[cost.itemId] !== undefined && inventory[cost.itemId] >= cost.amount + (cost) => + inventory[cost.itemId] !== undefined && + inventory[cost.itemId] >= cost.amount, ) if (!canAfford) { @@ -410,20 +449,17 @@ export const useGameStore = create< set( produce((state) => { // Deduct all costs - upgrade.cost.forEach(cost => { + upgrade.cost.forEach((cost) => { state.inventory[cost.itemId] -= cost.amount }) state.purchasedUpgrades.push(upgradeId) - addConsoleMessage( - state, - `Purchased upgrade: ${upgrade.name}` - ) + addConsoleMessage(state, `Purchased upgrade: ${upgrade.name}`) }), ) }, pray: () => { - const { actionCooldown, piety, plots } = get() + const { actionCooldown, piety } = get() if (actionCooldown > 0) { return @@ -438,26 +474,26 @@ export const useGameStore = create< state.piety += 1 addConsoleMessage( state, - "Nothing happens, but you feel the approval of the gods" + 'Nothing happens, but you feel the approval of the gods', ) } else if (result > 100) { if (progressRandomImmaturePlot(state, 0.3)) { addConsoleMessage( state, - "The gods have bestowed a significant blessing on one of your crops" + 'The gods have bestowed a significant blessing on one of your crops', ) } } else if (result > 50) { if (progressRandomImmaturePlot(state, 0.1)) { addConsoleMessage( state, - "The gods have bestowed a minor blessing on one of your crops" + 'The gods have bestowed a minor blessing on one of your crops', ) } } state.actionCooldown = 3000 - }) + }), ) }, })) diff --git a/src/types/index.ts b/src/types/index.ts index e278bd6..f8ce71a 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -60,6 +60,7 @@ export interface GameState { consoleMessages: ConsoleMessage[] purchasedUpgrades: string[] piety: number + landPurchases: number } export interface MarketTransaction {