Add prayer action to the temple.

This commit is contained in:
Ryan Lanny Jenkins 2025-05-24 15:50:55 -05:00
parent 6d72c8c0f9
commit ba9b26ff64
4 changed files with 175 additions and 9 deletions

View file

@ -2,7 +2,7 @@ import React from 'react'
import { useGameStore } from '../store/useGameStore' import { useGameStore } from '../store/useGameStore'
import { UPGRADES } from '../constants' import { UPGRADES } from '../constants'
import { styled } from '@linaria/react' import { styled } from '@linaria/react'
import type { UpgradeCost } from '../types' import type { UpgradeCost, Upgrade } from '../types'
const TempleContainer = styled.div` const TempleContainer = styled.div`
padding: 1rem; padding: 1rem;
@ -15,6 +15,46 @@ const Title = styled.h2`
text-align: center; 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` const UpgradesGrid = styled.div`
display: grid; display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
@ -68,7 +108,7 @@ const PurchaseButton = styled.button<{ disabled?: boolean }>`
` `
const TempleComponent: React.FC = () => { const TempleComponent: React.FC = () => {
const { inventory, purchasedUpgrades, purchaseUpgrade } = useGameStore() const { inventory, purchasedUpgrades, purchaseUpgrade, pray, piety, actionCooldown } = useGameStore()
const handlePurchase = (upgradeId: string) => { const handlePurchase = (upgradeId: string) => {
purchaseUpgrade(upgradeId) 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 ( return (
<TempleContainer> <TempleContainer>
<Title>Temple</Title> <Title>Temple</Title>
<PrayerSection>
<PietyDisplay>Piety Level: {piety}</PietyDisplay>
<PrayerButton
onClick={() => pray()}
disabled={actionCooldown > 0}
>
🙏 Pray to the Gods
{actionCooldown > 0 && ` (${(actionCooldown / 1000).toFixed(1)}s)`}
</PrayerButton>
</PrayerSection>
<UpgradesGrid> <UpgradesGrid>
{Object.values(UPGRADES).map((upgrade) => { {Object.values(UPGRADES).map((upgrade) => {
const isPurchased = purchasedUpgrades.includes(upgrade.id) const isPurchased = purchasedUpgrades.includes(upgrade.id)
const canAfford = canAffordUpgrade(upgrade.cost) const canPurchase = canPurchaseUpgrade(upgrade)
const status = getUpgradeStatus(upgrade)
return ( return (
<UpgradeCard key={upgrade.id} purchased={isPurchased}> <UpgradeCard key={upgrade.id} purchased={isPurchased}>
@ -106,9 +191,9 @@ const TempleComponent: React.FC = () => {
</UpgradeCost> </UpgradeCost>
<PurchaseButton <PurchaseButton
onClick={() => handlePurchase(upgrade.id)} onClick={() => handlePurchase(upgrade.id)}
disabled={isPurchased || !canAfford} disabled={!canPurchase}
> >
{isPurchased ? 'Purchased' : 'Purchase'} {status}
</PurchaseButton> </PurchaseButton>
</UpgradeCard> </UpgradeCard>
) )

View file

@ -47,6 +47,15 @@ export const UPGRADES: Record<string, Upgrade> = {
itemId: 'celery', itemId: 'celery',
amount: 10 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
}]
} }
} }

View file

@ -65,6 +65,32 @@ const addConsoleMessage = (state: GameState, text: string) => (
].slice(0, 50) ].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< export const useGameStore = create<
GameState & { GameState & {
@ -81,6 +107,7 @@ export const useGameStore = create<
saveToSlot: (slot: number) => void saveToSlot: (slot: number) => void
loadFromSlot: (slot: number) => void loadFromSlot: (slot: number) => void
purchaseUpgrade: (upgradeId: string) => void purchaseUpgrade: (upgradeId: string) => void
pray: () => void
} }
>((set, get) => ({ >((set, get) => ({
cash: INITIAL_CASH, cash: INITIAL_CASH,
@ -94,6 +121,7 @@ export const useGameStore = create<
tickCount: 0, tickCount: 0,
consoleMessages: [], consoleMessages: [],
purchasedUpgrades: [], purchasedUpgrades: [],
piety: 50,
assignCrop: (row, col, cropId) => { assignCrop: (row, col, cropId) => {
set( set(
@ -140,15 +168,18 @@ export const useGameStore = create<
const plot = plots[row][col] const plot = plots[row][col]
if (plot.current && plot.moisture < 1) { if (plot.current && plot.moisture < 1) {
// Calculate cooldown reduction from upgrades // Calculate cooldown reduction from upgrades (multiplicative)
let cooldownReduction = 0 let cooldownMultiplier = 1
if (purchasedUpgrades.includes('aqueous_vigor_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( const finalCooldown = Math.max(
0, 0,
COOLDOWN_DURATION * (1 - cooldownReduction) COOLDOWN_DURATION * cooldownMultiplier
) )
set( set(
@ -346,6 +377,7 @@ export const useGameStore = create<
saveToSlot, saveToSlot,
loadFromSlot, loadFromSlot,
purchaseUpgrade, purchaseUpgrade,
pray,
...gameState ...gameState
} = state } = state
saveGame(slot, gameState) 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
})
)
},
})) }))

View file

@ -59,6 +59,7 @@ export interface GameState {
actionCooldown: number actionCooldown: number
consoleMessages: ConsoleMessage[] consoleMessages: ConsoleMessage[]
purchasedUpgrades: string[] purchasedUpgrades: string[]
piety: number
} }
export interface MarketTransaction { export interface MarketTransaction {