Add "additional land" item to the store.
This commit is contained in:
parent
ba9b26ff64
commit
23c312299c
|
|
@ -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<FloatingMessagesHandle>(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 = () => {
|
|||
<ItemIcon>{item.emoji}</ItemIcon>
|
||||
{item.name}
|
||||
</MarketItemCell>
|
||||
<MarketItemCell>${item.buyPrice}</MarketItemCell>
|
||||
<MarketItemCell>${getItemPrice(item)}</MarketItemCell>
|
||||
<MarketItemCell>
|
||||
<ActionButton
|
||||
onClick={(e) => handleBuy(item.id, e)}
|
||||
disabled={item.buyPrice ? cash < item.buyPrice : true}
|
||||
disabled={
|
||||
getItemPrice(item) ? cash < getItemPrice(item)! : true
|
||||
}
|
||||
>
|
||||
Buy
|
||||
</ActionButton>
|
||||
|
|
|
|||
|
|
@ -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)) {
|
||||
|
|
@ -159,10 +173,7 @@ const TempleComponent: React.FC = () => {
|
|||
|
||||
<PrayerSection>
|
||||
<PietyDisplay>Piety Level: {piety}</PietyDisplay>
|
||||
<PrayerButton
|
||||
onClick={() => pray()}
|
||||
disabled={actionCooldown > 0}
|
||||
>
|
||||
<PrayerButton onClick={() => pray()} disabled={actionCooldown > 0}>
|
||||
🙏 Pray to the Gods
|
||||
{actionCooldown > 0 && ` (${(actionCooldown / 1000).toFixed(1)}s)`}
|
||||
</PrayerButton>
|
||||
|
|
@ -182,10 +193,10 @@ const TempleComponent: React.FC = () => {
|
|||
<span>Cost:</span>
|
||||
{upgrade.cost.map((cost, index) => (
|
||||
<CostItem key={index}>
|
||||
<span>{cost.amount} {cost.itemId}</span>
|
||||
<span>
|
||||
(You have: {inventory[cost.itemId] || 0})
|
||||
{cost.amount} {cost.itemId}
|
||||
</span>
|
||||
<span>(You have: {inventory[cost.itemId] || 0})</span>
|
||||
</CostItem>
|
||||
))}
|
||||
</UpgradeCost>
|
||||
|
|
|
|||
|
|
@ -43,20 +43,25 @@ export const UPGRADES: Record<string, Upgrade> = {
|
|||
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<string, MarketItem> = {
|
||||
additional_land: {
|
||||
id: 'additional_land',
|
||||
name: 'Additional Land',
|
||||
emoji: '🌱',
|
||||
buyPrice: 100,
|
||||
sellPrice: null,
|
||||
},
|
||||
celery_seed: {
|
||||
id: 'celery_seed',
|
||||
name: 'Celery Seeds',
|
||||
|
|
|
|||
|
|
@ -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) => {
|
||||
|
|
@ -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
|
||||
})
|
||||
}),
|
||||
)
|
||||
},
|
||||
}))
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ export interface GameState {
|
|||
consoleMessages: ConsoleMessage[]
|
||||
purchasedUpgrades: string[]
|
||||
piety: number
|
||||
landPurchases: number
|
||||
}
|
||||
|
||||
export interface MarketTransaction {
|
||||
|
|
|
|||
Loading…
Reference in a new issue