From ba9b26ff6411b1bb69654ab5b25ed4d73599ba6f Mon Sep 17 00:00:00 2001 From: Ryan Lanny Jenkins Date: Sat, 24 May 2025 15:50:55 -0500 Subject: [PATCH] Add prayer action to the temple. --- src/components/Temple.tsx | 95 ++++++++++++++++++++++++++++++++++++--- src/constants/index.ts | 9 ++++ src/store/useGameStore.ts | 79 ++++++++++++++++++++++++++++++-- src/types/index.ts | 1 + 4 files changed, 175 insertions(+), 9 deletions(-) diff --git a/src/components/Temple.tsx b/src/components/Temple.tsx index b666165..e7e4263 100644 --- a/src/components/Temple.tsx +++ b/src/components/Temple.tsx @@ -2,7 +2,7 @@ import React from 'react' import { useGameStore } from '../store/useGameStore' import { UPGRADES } from '../constants' import { styled } from '@linaria/react' -import type { UpgradeCost } from '../types' +import type { UpgradeCost, Upgrade } from '../types' const TempleContainer = styled.div` padding: 1rem; @@ -15,6 +15,46 @@ const Title = styled.h2` text-align: center; ` +const PrayerSection = styled.div` + background-color: #f8fafc; + border: 2px solid #e2e8f0; + border-radius: 0.75rem; + padding: 1.5rem; + margin-bottom: 2rem; + text-align: center; +` + +const PietyDisplay = styled.div` + font-size: 1.25rem; + color: #4b5563; + margin-bottom: 1rem; +` + +const PrayerButton = styled.button<{ disabled?: boolean }>` + padding: 1rem 2rem; + font-size: 1.25rem; + border-radius: 0.5rem; + background-color: ${props => props.disabled ? '#9ca3af' : '#8b5cf6'}; + color: white; + font-weight: bold; + cursor: ${props => props.disabled ? 'not-allowed' : 'pointer'}; + border: none; + transition: all 0.2s; + display: flex; + align-items: center; + gap: 0.5rem; + margin: 0 auto; + + &:hover:not(:disabled) { + background-color: #7c3aed; + transform: translateY(-1px); + } + + &:active:not(:disabled) { + transform: translateY(0); + } +` + const UpgradesGrid = styled.div` display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); @@ -68,7 +108,7 @@ const PurchaseButton = styled.button<{ disabled?: boolean }>` ` const TempleComponent: React.FC = () => { - const { inventory, purchasedUpgrades, purchaseUpgrade } = useGameStore() + const { inventory, purchasedUpgrades, purchaseUpgrade, pray, piety, actionCooldown } = useGameStore() const handlePurchase = (upgradeId: string) => { purchaseUpgrade(upgradeId) @@ -81,13 +121,58 @@ const TempleComponent: React.FC = () => { ) } + const canPurchaseUpgrade = (upgrade: Upgrade) => { + // Check if already purchased + if (purchasedUpgrades.includes(upgrade.id)) { + return false + } + + // Check if can afford + if (!canAffordUpgrade(upgrade.cost)) { + return false + } + + // Check requirements + if (upgrade.id === 'aqueous_vigor_2' && !purchasedUpgrades.includes('aqueous_vigor_1')) { + return false + } + + return true + } + + const getUpgradeStatus = (upgrade: Upgrade) => { + if (purchasedUpgrades.includes(upgrade.id)) { + return 'Purchased' + } + if (upgrade.id === 'aqueous_vigor_2' && !purchasedUpgrades.includes('aqueous_vigor_1')) { + return 'Requires Aqueous Vigor I' + } + if (!canAffordUpgrade(upgrade.cost)) { + return 'Cannot Afford' + } + return 'Purchase' + } + return ( Temple + + + Piety Level: {piety} + pray()} + disabled={actionCooldown > 0} + > + 🙏 Pray to the Gods + {actionCooldown > 0 && ` (${(actionCooldown / 1000).toFixed(1)}s)`} + + + {Object.values(UPGRADES).map((upgrade) => { const isPurchased = purchasedUpgrades.includes(upgrade.id) - const canAfford = canAffordUpgrade(upgrade.cost) + const canPurchase = canPurchaseUpgrade(upgrade) + const status = getUpgradeStatus(upgrade) return ( @@ -106,9 +191,9 @@ const TempleComponent: React.FC = () => { handlePurchase(upgrade.id)} - disabled={isPurchased || !canAfford} + disabled={!canPurchase} > - {isPurchased ? 'Purchased' : 'Purchase'} + {status} ) diff --git a/src/constants/index.ts b/src/constants/index.ts index c5345ed..0446bec 100644 --- a/src/constants/index.ts +++ b/src/constants/index.ts @@ -47,6 +47,15 @@ export const UPGRADES: Record = { 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 + }] } } diff --git a/src/store/useGameStore.ts b/src/store/useGameStore.ts index 264335c..1453503 100644 --- a/src/store/useGameStore.ts +++ b/src/store/useGameStore.ts @@ -65,6 +65,32 @@ const addConsoleMessage = (state: GameState, text: string) => ( ].slice(0, 50) ) +const progressRandomImmaturePlot = (state: GameState, growthAmount: number): boolean => { + // Find all immature plants + const immaturePlots: { row: number; col: number }[] = [] + state.plots.forEach((row, rowIndex) => { + row.forEach((plot, colIndex) => { + if (plot.current && !plot.current.mature) { + immaturePlots.push({ row: rowIndex, col: colIndex }) + } + }) + }) + + if (immaturePlots.length > 0) { + // Select a random immature plot and progress it + 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 + ) + return true + } + return false +} export const useGameStore = create< GameState & { @@ -81,6 +107,7 @@ export const useGameStore = create< saveToSlot: (slot: number) => void loadFromSlot: (slot: number) => void purchaseUpgrade: (upgradeId: string) => void + pray: () => void } >((set, get) => ({ cash: INITIAL_CASH, @@ -94,6 +121,7 @@ export const useGameStore = create< tickCount: 0, consoleMessages: [], purchasedUpgrades: [], + piety: 50, assignCrop: (row, col, cropId) => { set( @@ -140,15 +168,18 @@ export const useGameStore = create< const plot = plots[row][col] if (plot.current && plot.moisture < 1) { - // Calculate cooldown reduction from upgrades - let cooldownReduction = 0 + // Calculate cooldown reduction from upgrades (multiplicative) + let cooldownMultiplier = 1 if (purchasedUpgrades.includes('aqueous_vigor_1')) { - cooldownReduction += 0.25 // 25% reduction + cooldownMultiplier *= 0.75 // 25% reduction + } + if (purchasedUpgrades.includes('aqueous_vigor_2')) { + cooldownMultiplier *= 0.75 // Additional 25% reduction } const finalCooldown = Math.max( 0, - COOLDOWN_DURATION * (1 - cooldownReduction) + COOLDOWN_DURATION * cooldownMultiplier ) set( @@ -346,6 +377,7 @@ export const useGameStore = create< saveToSlot, loadFromSlot, purchaseUpgrade, + pray, ...gameState } = state saveGame(slot, gameState) @@ -389,4 +421,43 @@ export const useGameStore = create< }), ) }, + + pray: () => { + const { actionCooldown, piety, plots } = get() + + if (actionCooldown > 0) { + return + } + + set( + produce((state) => { + const roll = Math.random() + const result = roll * piety + + if (result < 50 || roll < 0.3) { + state.piety += 1 + addConsoleMessage( + state, + "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" + ) + } + } else if (result > 50) { + if (progressRandomImmaturePlot(state, 0.1)) { + addConsoleMessage( + state, + "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 bfb1a05..e278bd6 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -59,6 +59,7 @@ export interface GameState { actionCooldown: number consoleMessages: ConsoleMessage[] purchasedUpgrades: string[] + piety: number } export interface MarketTransaction {