Add our first upgrade to the temple.
This commit is contained in:
parent
9644cc4b4a
commit
6d72c8c0f9
|
|
@ -9,6 +9,7 @@ import { useGameTick } from './hooks/useGameTick'
|
||||||
import Field from './components/Field'
|
import Field from './components/Field'
|
||||||
import Warehouse from './components/Warehouse'
|
import Warehouse from './components/Warehouse'
|
||||||
import Market from './components/Market'
|
import Market from './components/Market'
|
||||||
|
import Temple from './components/Temple'
|
||||||
import { ActionCooldown } from './components/ActionCooldown'
|
import { ActionCooldown } from './components/ActionCooldown'
|
||||||
import { useSaveSystem } from './store/useSaveSystem'
|
import { useSaveSystem } from './store/useSaveSystem'
|
||||||
import { Console } from './components/Console'
|
import { Console } from './components/Console'
|
||||||
|
|
@ -38,9 +39,7 @@ function App() {
|
||||||
<TabsTrigger value="fields">Fields</TabsTrigger>
|
<TabsTrigger value="fields">Fields</TabsTrigger>
|
||||||
<TabsTrigger value="warehouse">Warehouse</TabsTrigger>
|
<TabsTrigger value="warehouse">Warehouse</TabsTrigger>
|
||||||
<TabsTrigger value="market">Market</TabsTrigger>
|
<TabsTrigger value="market">Market</TabsTrigger>
|
||||||
<TabsTrigger value="temple" disabled>
|
<TabsTrigger value="temple">Temple</TabsTrigger>
|
||||||
Temple
|
|
||||||
</TabsTrigger>
|
|
||||||
</TabsList>
|
</TabsList>
|
||||||
<TabsContent value="fields">
|
<TabsContent value="fields">
|
||||||
<Field />
|
<Field />
|
||||||
|
|
@ -51,6 +50,9 @@ function App() {
|
||||||
<TabsContent value="market">
|
<TabsContent value="market">
|
||||||
<Market />
|
<Market />
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
<TabsContent value="temple">
|
||||||
|
<Temple />
|
||||||
|
</TabsContent>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
<Console />
|
<Console />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
121
src/components/Temple.tsx
Normal file
121
src/components/Temple.tsx
Normal file
|
|
@ -0,0 +1,121 @@
|
||||||
|
import React from 'react'
|
||||||
|
import { useGameStore } from '../store/useGameStore'
|
||||||
|
import { UPGRADES } from '../constants'
|
||||||
|
import { styled } from '@linaria/react'
|
||||||
|
import type { UpgradeCost } from '../types'
|
||||||
|
|
||||||
|
const TempleContainer = styled.div`
|
||||||
|
padding: 1rem;
|
||||||
|
`
|
||||||
|
|
||||||
|
const Title = styled.h2`
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
text-align: center;
|
||||||
|
`
|
||||||
|
|
||||||
|
const UpgradesGrid = styled.div`
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
|
||||||
|
gap: 1rem;
|
||||||
|
margin-top: 1rem;
|
||||||
|
`
|
||||||
|
|
||||||
|
const UpgradeCard = styled.div<{ purchased?: boolean }>`
|
||||||
|
background-color: ${props => props.purchased ? '#e5e7eb' : '#f3f4f6'};
|
||||||
|
border: 1px solid #d1d5db;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
padding: 1rem;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5rem;
|
||||||
|
`
|
||||||
|
|
||||||
|
const UpgradeName = styled.h3`
|
||||||
|
font-size: 1.25rem;
|
||||||
|
font-weight: 500;
|
||||||
|
margin: 0;
|
||||||
|
`
|
||||||
|
|
||||||
|
const UpgradeDescription = styled.p`
|
||||||
|
color: #4b5563;
|
||||||
|
margin: 0;
|
||||||
|
`
|
||||||
|
|
||||||
|
const UpgradeCost = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.25rem;
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
`
|
||||||
|
|
||||||
|
const CostItem = styled.div`
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
`
|
||||||
|
|
||||||
|
const PurchaseButton = styled.button<{ disabled?: boolean }>`
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
background-color: ${props => props.disabled ? '#9ca3af' : '#3b82f6'};
|
||||||
|
color: white;
|
||||||
|
font-weight: bold;
|
||||||
|
cursor: ${props => props.disabled ? 'not-allowed' : 'pointer'};
|
||||||
|
border: none;
|
||||||
|
margin-top: auto;
|
||||||
|
`
|
||||||
|
|
||||||
|
const TempleComponent: React.FC = () => {
|
||||||
|
const { inventory, purchasedUpgrades, purchaseUpgrade } = 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 (
|
||||||
|
<TempleContainer>
|
||||||
|
<Title>Temple</Title>
|
||||||
|
<UpgradesGrid>
|
||||||
|
{Object.values(UPGRADES).map((upgrade) => {
|
||||||
|
const isPurchased = purchasedUpgrades.includes(upgrade.id)
|
||||||
|
const canAfford = canAffordUpgrade(upgrade.cost)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<UpgradeCard key={upgrade.id} purchased={isPurchased}>
|
||||||
|
<UpgradeName>{upgrade.name}</UpgradeName>
|
||||||
|
<UpgradeDescription>{upgrade.description}</UpgradeDescription>
|
||||||
|
<UpgradeCost>
|
||||||
|
<span>Cost:</span>
|
||||||
|
{upgrade.cost.map((cost, index) => (
|
||||||
|
<CostItem key={index}>
|
||||||
|
<span>{cost.amount} {cost.itemId}</span>
|
||||||
|
<span>
|
||||||
|
(You have: {inventory[cost.itemId] || 0})
|
||||||
|
</span>
|
||||||
|
</CostItem>
|
||||||
|
))}
|
||||||
|
</UpgradeCost>
|
||||||
|
<PurchaseButton
|
||||||
|
onClick={() => handlePurchase(upgrade.id)}
|
||||||
|
disabled={isPurchased || !canAfford}
|
||||||
|
>
|
||||||
|
{isPurchased ? 'Purchased' : 'Purchase'}
|
||||||
|
</PurchaseButton>
|
||||||
|
</UpgradeCard>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</UpgradesGrid>
|
||||||
|
</TempleContainer>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TempleComponent
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { CropDefinitions } from '../types'
|
import { CropDefinitions, Upgrade } from '../types'
|
||||||
|
|
||||||
export const INITIAL_CASH = 50
|
export const INITIAL_CASH = 50
|
||||||
export const INITIAL_FIELD_SIZE = 3
|
export const INITIAL_FIELD_SIZE = 3
|
||||||
|
|
@ -38,6 +38,18 @@ export const INITIAL_INVENTORY = {
|
||||||
celery_seed: 8,
|
celery_seed: 8,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const UPGRADES: Record<string, Upgrade> = {
|
||||||
|
aqueous_vigor_1: {
|
||||||
|
id: 'aqueous_vigor_1',
|
||||||
|
name: 'Aqueous Vigor I',
|
||||||
|
description: 'Reduces watering cooldown by 25%',
|
||||||
|
cost: [{
|
||||||
|
itemId: 'celery',
|
||||||
|
amount: 10
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export interface MarketItem {
|
export interface MarketItem {
|
||||||
id: string
|
id: string
|
||||||
name: string
|
name: string
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import {
|
||||||
INITIAL_GAME_SPEED,
|
INITIAL_GAME_SPEED,
|
||||||
COOLDOWN_DURATION,
|
COOLDOWN_DURATION,
|
||||||
MARKET_ITEMS,
|
MARKET_ITEMS,
|
||||||
|
UPGRADES,
|
||||||
} from '../constants'
|
} from '../constants'
|
||||||
import { GameState, PlotState } from '../types'
|
import { GameState, PlotState } from '../types'
|
||||||
|
|
||||||
|
|
@ -79,6 +80,7 @@ export const useGameStore = create<
|
||||||
sellItem: (itemId: string) => void
|
sellItem: (itemId: string) => void
|
||||||
saveToSlot: (slot: number) => void
|
saveToSlot: (slot: number) => void
|
||||||
loadFromSlot: (slot: number) => void
|
loadFromSlot: (slot: number) => void
|
||||||
|
purchaseUpgrade: (upgradeId: string) => void
|
||||||
}
|
}
|
||||||
>((set, get) => ({
|
>((set, get) => ({
|
||||||
cash: INITIAL_CASH,
|
cash: INITIAL_CASH,
|
||||||
|
|
@ -91,6 +93,7 @@ export const useGameStore = create<
|
||||||
actionCooldown: 0,
|
actionCooldown: 0,
|
||||||
tickCount: 0,
|
tickCount: 0,
|
||||||
consoleMessages: [],
|
consoleMessages: [],
|
||||||
|
purchasedUpgrades: [],
|
||||||
|
|
||||||
assignCrop: (row, col, cropId) => {
|
assignCrop: (row, col, cropId) => {
|
||||||
set(
|
set(
|
||||||
|
|
@ -129,7 +132,7 @@ export const useGameStore = create<
|
||||||
},
|
},
|
||||||
|
|
||||||
water: (row, col) => {
|
water: (row, col) => {
|
||||||
const { plots, actionCooldown } = get()
|
const { plots, actionCooldown, purchasedUpgrades } = get()
|
||||||
|
|
||||||
if (actionCooldown > 0) {
|
if (actionCooldown > 0) {
|
||||||
return
|
return
|
||||||
|
|
@ -137,10 +140,21 @@ 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
|
||||||
|
let cooldownReduction = 0
|
||||||
|
if (purchasedUpgrades.includes('aqueous_vigor_1')) {
|
||||||
|
cooldownReduction += 0.25 // 25% reduction
|
||||||
|
}
|
||||||
|
|
||||||
|
const finalCooldown = Math.max(
|
||||||
|
0,
|
||||||
|
COOLDOWN_DURATION * (1 - cooldownReduction)
|
||||||
|
)
|
||||||
|
|
||||||
set(
|
set(
|
||||||
produce((state) => {
|
produce((state) => {
|
||||||
state.plots[row][col].moisture = 1
|
state.plots[row][col].moisture = 1
|
||||||
state.actionCooldown = COOLDOWN_DURATION
|
state.actionCooldown = finalCooldown
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -331,6 +345,7 @@ export const useGameStore = create<
|
||||||
sellItem,
|
sellItem,
|
||||||
saveToSlot,
|
saveToSlot,
|
||||||
loadFromSlot,
|
loadFromSlot,
|
||||||
|
purchaseUpgrade,
|
||||||
...gameState
|
...gameState
|
||||||
} = state
|
} = state
|
||||||
saveGame(slot, gameState)
|
saveGame(slot, gameState)
|
||||||
|
|
@ -342,4 +357,36 @@ export const useGameStore = create<
|
||||||
set(savedState)
|
set(savedState)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
purchaseUpgrade: (upgradeId) => {
|
||||||
|
const { inventory, purchasedUpgrades } = get()
|
||||||
|
const upgrade = UPGRADES[upgradeId]
|
||||||
|
|
||||||
|
if (!upgrade || purchasedUpgrades.includes(upgradeId)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if player can afford all costs
|
||||||
|
const canAfford = upgrade.cost.every(
|
||||||
|
cost => inventory[cost.itemId] !== undefined && inventory[cost.itemId] >= cost.amount
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!canAfford) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
set(
|
||||||
|
produce((state) => {
|
||||||
|
// Deduct all costs
|
||||||
|
upgrade.cost.forEach(cost => {
|
||||||
|
state.inventory[cost.itemId] -= cost.amount
|
||||||
|
})
|
||||||
|
state.purchasedUpgrades.push(upgradeId)
|
||||||
|
addConsoleMessage(
|
||||||
|
state,
|
||||||
|
`Purchased upgrade: ${upgrade.name}`
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
},
|
||||||
}))
|
}))
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,18 @@ export interface ConsoleMessage {
|
||||||
timestamp: number
|
timestamp: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface UpgradeCost {
|
||||||
|
itemId: string
|
||||||
|
amount: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Upgrade {
|
||||||
|
id: string
|
||||||
|
name: string
|
||||||
|
description: string
|
||||||
|
cost: UpgradeCost[]
|
||||||
|
}
|
||||||
|
|
||||||
export interface GameState {
|
export interface GameState {
|
||||||
cash: number
|
cash: number
|
||||||
inventory: Record<string, number>
|
inventory: Record<string, number>
|
||||||
|
|
@ -46,6 +58,7 @@ export interface GameState {
|
||||||
gameSpeed: number
|
gameSpeed: number
|
||||||
actionCooldown: number
|
actionCooldown: number
|
||||||
consoleMessages: ConsoleMessage[]
|
consoleMessages: ConsoleMessage[]
|
||||||
|
purchasedUpgrades: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MarketTransaction {
|
export interface MarketTransaction {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue