import { create } from 'zustand' import { produce } from 'immer' import { INITIAL_CASH, INITIAL_FIELD_SIZE, FIELD_UPGRADE_COSTS, INITIAL_INVENTORY, CROPS, INITIAL_GAME_SPEED, COOLDOWN_DURATION, MARKET_ITEMS, } from '../constants' import { GameState, PlotState } from '../types' import { saveGame, loadGame } from '../utils/saveSystem' const initializeField = (size: number): PlotState[][] => { return Array(size) .fill(0) .map(() => Array(size) .fill(0) .map(() => ({ moisture: 0, })), ) } export const useGameStore = create< GameState & { plant: () => void water: () => void harvest: () => void tick: () => void assignCrop: (row: number, col: number, cropId: string) => void upgradeField: () => void setGameSpeed: (speed: number) => void setActionCooldown: (cooldown: number) => void buyItem: (itemId: string) => void sellItem: (itemId: string) => void saveToSlot: (slot: number) => void loadFromSlot: (slot: number) => void } >((set, get) => ({ cash: INITIAL_CASH, inventory: INITIAL_INVENTORY, fieldSize: INITIAL_FIELD_SIZE, maxFieldSize: INITIAL_FIELD_SIZE + FIELD_UPGRADE_COSTS.length, fieldUpgradeCosts: FIELD_UPGRADE_COSTS, plots: initializeField(INITIAL_FIELD_SIZE), gameSpeed: INITIAL_GAME_SPEED, actionCooldown: 0, tickCount: 0, assignCrop: (row, col, cropId) => { set( produce((state) => { state.plots[row][col].intended = cropId }), ) }, plant: () => { const { plots, inventory, actionCooldown } = get() if (actionCooldown > 0) { return } for (let row = 0; row < plots.length; row++) { for (let col = 0; col < plots[row].length; col++) { const plot = plots[row][col] if (plot.intended && !plot.current) { const seedId = `${plot.intended}_seed` if (inventory[seedId] && inventory[seedId] > 0) { set( produce((state) => { state.plots[row][col].current = { cropId: plot.intended!, progress: 0, mature: false, } state.inventory[seedId] = state.inventory[seedId] - 1 state.actionCooldown = COOLDOWN_DURATION }), ) return } } } } }, water: () => { const { plots, actionCooldown } = get() if (actionCooldown > 0) { return } let driestRow = -1 let driestCol = -1 let lowestMoisture = 1 for (let row = 0; row < plots.length; row++) { for (let col = 0; col < plots[row].length; col++) { if ( plots[row][col].current && plots[row][col].moisture < lowestMoisture ) { lowestMoisture = plots[row][col].moisture driestRow = row driestCol = col } } } if (driestRow >= 0 && driestCol >= 0) { set( produce((state) => { state.plots[driestRow][driestCol].moisture = 1 state.actionCooldown = COOLDOWN_DURATION }), ) } }, harvest: () => { const { plots, actionCooldown } = get() if (actionCooldown > 0) { return } for (let row = 0; row < plots.length; row++) { for (let col = 0; col < plots[row].length; col++) { const plot = plots[row][col] if (plot.current && plot.current.mature) { const crop = CROPS[plot.current.cropId] const yieldItem = crop.yieldType const yieldAmount = crop.yield set( produce((state) => { state.plots[row][col].current = undefined state.inventory[yieldItem] = (state.inventory[yieldItem] || 0) + yieldAmount state.actionCooldown = COOLDOWN_DURATION }), ) return } } } }, tick: () => { set( produce((state) => { // Update plots state.plots.forEach((row: PlotState[], rowIndex: number) => { row.forEach((plot: PlotState, colIndex: number) => { if (!plot.current || plot.current.mature) { return } const crop = CROPS[plot.current.cropId] const waterNeeded = crop.waterPerTick if (plot.moisture >= waterNeeded) { const newProgress = plot.current.progress + 1 const mature = newProgress >= crop.growthTicks state.plots[rowIndex][colIndex].moisture = plot.moisture - waterNeeded state.plots[rowIndex][colIndex].current.progress = newProgress state.plots[rowIndex][colIndex].current.mature = mature } }) }) state.tickCount = state.tickCount + 1 }), ) }, upgradeField: () => { set( produce((state) => { if (state.fieldSize >= state.maxFieldSize) { return } const upgradeIndex = state.fieldSize - INITIAL_FIELD_SIZE const cost = state.fieldUpgradeCosts[upgradeIndex] if (state.cash < cost) { return } const newSize = state.fieldSize + 1 state.cash = state.cash - cost state.fieldSize = newSize state.plots = initializeField(newSize) }), ) }, setGameSpeed: (speed) => { set( produce((state) => { state.gameSpeed = speed }), ) }, setActionCooldown: (cooldown) => { set( produce((state) => { state.actionCooldown = cooldown }), ) }, buyItem: (itemId) => { const { cash } = get() const item = MARKET_ITEMS[itemId] if (!item || item.buyPrice === null) { return } if (cash < item.buyPrice) { return } set( produce((state) => { state.cash -= item.buyPrice state.inventory[itemId] = (state.inventory[itemId] || 0) + 1 }), ) }, sellItem: (itemId) => { const { inventory } = get() const item = MARKET_ITEMS[itemId] if ( !item || item.sellPrice === null || !inventory[itemId] || inventory[itemId] <= 0 ) { return } set( produce((state) => { state.cash += item.sellPrice state.inventory[itemId] -= 1 }), ) }, saveToSlot: (slot: number) => { const state = get() const { plant, water, harvest, tick, assignCrop, upgradeField, setGameSpeed, setActionCooldown, buyItem, sellItem, saveToSlot, loadFromSlot, ...gameState } = state saveGame(slot, gameState) }, loadFromSlot: (slot: number) => { const savedState = loadGame(slot) if (savedState) { set(savedState) } }, }))