From b1bc592a28472ef35d313e9be6f1f70ad2faf2d8 Mon Sep 17 00:00:00 2001 From: Ryan Lanny Jenkins Date: Sat, 7 Jun 2025 20:12:06 -0500 Subject: [PATCH] Make celery much quicker to grow, change the way crop growth rates are expressed. --- src/App.tsx | 2 +- src/components/Field.tsx | 24 ++++---- src/components/FloatingMessages.tsx | 2 +- src/constants/index.ts | 27 +++++---- src/store/useGameStore.ts | 93 +++++++++++++---------------- src/types/index.ts | 6 +- 6 files changed, 73 insertions(+), 81 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index d38abaf..030da33 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from 'react' +import { useState, useEffect } from 'react' import { styled } from '@linaria/react' import { useGameTick } from './hooks/useGameTick' import Field from './components/Field' diff --git a/src/components/Field.tsx b/src/components/Field.tsx index c96f4e2..ff20ad9 100644 --- a/src/components/Field.tsx +++ b/src/components/Field.tsx @@ -1,6 +1,6 @@ import React, { useState } from 'react' import { useGameStore } from '../store/useGameStore' -import { CROPS, ITEMS } from '../constants' +import { CROPS, ITEMS, secondsToTicks } from '../constants' import { FieldTool, PlotState } from '../types' import Modal from './Modal' @@ -105,17 +105,15 @@ const getMaturityProgressContainerStyle = (): React.CSSProperties => ({ const getMaturityProgressBarStyle = ( progress: number, - total: number, + cropId: string, ): React.CSSProperties => ({ - position: 'absolute', - bottom: 0, - left: 0, - width: `${Math.min((progress / total) * 100, 100)}%`, - height: '10px', - backgroundColor: '#ff5722', - transition: 'width 0.3s', - zIndex: 6, - boxShadow: '0 0 3px rgba(0, 0, 0, 0.5)', + width: `${Math.min( + 100, + (progress / secondsToTicks(CROPS[cropId].growthTimeSeconds)) * 100, + )}%`, + height: '100%', + backgroundColor: '#22c55e', + transition: 'width 0.5s ease-in-out', }) const plotInfoStyle: React.CSSProperties = { @@ -168,7 +166,7 @@ const PlotInfoModal: React.FC = ({ plot, onClose }) => { {formatPercentage( plot.current.progress / - CROPS[plot.current.cropId].growthTicks, + secondsToTicks(CROPS[plot.current.cropId].growthTimeSeconds), )} @@ -324,7 +322,7 @@ const FieldComponent: React.FC = () => {
diff --git a/src/components/FloatingMessages.tsx b/src/components/FloatingMessages.tsx index 0c54bcc..d25cef6 100644 --- a/src/components/FloatingMessages.tsx +++ b/src/components/FloatingMessages.tsx @@ -1,4 +1,4 @@ -import React, { +import { useState, useImperativeHandle, forwardRef, diff --git a/src/constants/index.ts b/src/constants/index.ts index fa226d0..2aab49c 100644 --- a/src/constants/index.ts +++ b/src/constants/index.ts @@ -3,7 +3,7 @@ import { CropDefinitions, Upgrade, Equipment } from '../types' export const INITIAL_CASH = 50 export const INITIAL_FIELD_SIZE = 3 export const COOLDOWN_DURATION = 2000 // 2 seconds in milliseconds -export const TICK_INTERVAL = 5000 +export const TICK_INTERVAL = 1000 // 1 second in milliseconds export const GAME_SPEEDS = { NORMAL: 1, FAST: 5, @@ -13,12 +13,17 @@ export const INITIAL_GAME_SPEED = GAME_SPEEDS.NORMAL export const FIELD_UPGRADE_COSTS = [100, 1000, 10000, 100000] +// Helper function to convert seconds to ticks based on current tick interval +export const secondsToTicks = (seconds: number): number => { + return Math.ceil((seconds * 1000) / TICK_INTERVAL) +} + export const CROPS: CropDefinitions = { celery: { id: 'celery', name: 'Celery', - growthTicks: 10, - waterPerTick: 1 / 5, + growthTimeSeconds: 10, + waterPerSecond: 0.20, // 1/5 water per tick = 0.04 water per second yield: 1, yieldType: 'celery', fertilityDepletion: 0.1, @@ -27,8 +32,8 @@ export const CROPS: CropDefinitions = { corn: { id: 'corn', name: 'Corn', - growthTicks: 120, - waterPerTick: 1 / 40, + growthTimeSeconds: 600, + waterPerSecond: 0.005, // 1/40 water per tick = 0.005 water per second yield: 5, yieldType: 'corn', fertilityDepletion: 0.3, @@ -37,26 +42,26 @@ export const CROPS: CropDefinitions = { olive: { id: 'olive', name: 'Olive Tree', - growthTicks: 200, - waterPerTick: 1 / 50, + growthTimeSeconds: 1000, + waterPerSecond: 0.004, // 1/50 water per tick = 0.004 water per second yield: 5, yieldType: 'olive', fertilityDepletion: 0.1, seedType: 'olive', // Uses olives as seeds isPerennial: true, - regrowthProgress: 150, // Resets to 150 ticks after harvest + regrowthTimeSeconds: 750, // 150 ticks = 750 seconds }, grape_vine: { id: 'grape_vine', name: 'Grape Vine', - growthTicks: 250, - waterPerTick: 1 / 45, + growthTimeSeconds: 1250, + waterPerSecond: 0.00444, // 1/45 water per tick = 0.00444 water per second yield: 2, yieldType: 'grape', fertilityDepletion: 0.15, seedType: 'grape_seed', isPerennial: true, - regrowthProgress: 200, // Resets to 200 ticks after harvest + regrowthTimeSeconds: 1000, // 200 ticks = 1000 seconds }, } diff --git a/src/store/useGameStore.ts b/src/store/useGameStore.ts index 5c8002b..f8468b9 100644 --- a/src/store/useGameStore.ts +++ b/src/store/useGameStore.ts @@ -10,6 +10,8 @@ import { ITEMS, UPGRADES, EQUIPMENT, + TICK_INTERVAL, + secondsToTicks, } from '../constants' import { GameState, PlotState, Equipment } from '../types' @@ -64,9 +66,10 @@ const progressRandomImmaturePlot = ( const plot = state.plots[randomPlot.row][randomPlot.col] const crop = CROPS[plot.current!.cropId] - const actualGrowthAmount = crop.growthTicks * growthAmount + const growthTicks = secondsToTicks(crop.growthTimeSeconds) + const actualGrowthAmount = growthTicks * growthAmount plot.current!.progress = Math.min( - crop.growthTicks, + growthTicks, plot.current!.progress + actualGrowthAmount, ) return true @@ -222,9 +225,10 @@ export const useGameStore = create< produce((state) => { if (crop.isPerennial) { // For perennial crops, reset progress instead of clearing + const regrowthTicks = secondsToTicks(crop.regrowthTimeSeconds!) state.plots[row][col].current = { cropId: plot.current!.cropId, - progress: crop.regrowthProgress!, + progress: regrowthTicks, mature: false, } } else { @@ -270,67 +274,52 @@ export const useGameStore = create< set( produce((state) => { // Regenerate stamina - if (state.stamina < state.maxStamina) { - const oldStamina = state.stamina - state.stamina = Math.min(state.maxStamina, state.stamina + 1) - - // Increment milestone counter for stamina gained - if (state.stamina > oldStamina) { - const staminaGained = state.stamina - oldStamina - state.milestones.stamina_gained = (state.milestones.stamina_gained || 0) + staminaGained - } - } - - // Update plots + state.stamina = Math.min( + state.maxStamina, + state.stamina + 0.5, + ) + + // Process each plot state.plots.forEach((row: PlotState[], rowIndex: number) => { row.forEach((plot: PlotState, colIndex: number) => { // Regenerate fertility every 100 ticks - if (state.tickCount % 100 === 0 && plot.fertility < 1) { + if (state.tickCount % 100 === 0) { state.plots[rowIndex][colIndex].fertility = Math.min( 1, - plot.fertility + 0.1, + plot.fertility + 0.01, ) } - if (!plot.current || plot.current.mature) { - return - } + if (plot.current && !plot.current.mature) { + const crop = CROPS[plot.current.cropId] + const growthTicks = secondsToTicks(crop.growthTimeSeconds) + const waterNeeded = crop.waterPerSecond * (TICK_INTERVAL / 1000) - const crop = CROPS[plot.current.cropId] - const waterNeeded = crop.waterPerTick + // Only grow if fertility is above 0.2 + if (plot.moisture >= waterNeeded && plot.fertility >= 0.2) { + let growthRate = 1 + // Half growth rate if fertility is between 0.2 and 0.5 + if (plot.fertility < 0.5) { + growthRate = 0.5 + } - // Check if water is running low (less than 25% of what's needed) - if (plot.moisture < waterNeeded * 0.25 && plot.moisture > 0) { - addConsoleMessage( - state, - `Plot (${rowIndex + 1},${colIndex + 1}) ${crop.name} is running low on water!`, - ) - } + const newProgress = plot.current.progress + growthRate + const mature = newProgress >= growthTicks - // Only grow if fertility is above 0.2 - if (plot.moisture >= waterNeeded && plot.fertility >= 0.2) { - let growthRate = 1 - // Half growth rate if fertility is between 0.2 and 0.5 - if (plot.fertility < 0.5) { - growthRate = 0.5 + state.plots[rowIndex][colIndex].moisture = + plot.moisture - waterNeeded + state.plots[rowIndex][colIndex].current.progress = newProgress + + // If the plot just became mature, add a message + if (mature && !plot.current.mature) { + addConsoleMessage( + state, + `Plot (${rowIndex + 1},${colIndex + 1}) ${crop.name} is ready to harvest!`, + ) + } + + state.plots[rowIndex][colIndex].current.mature = mature } - - const newProgress = plot.current.progress + growthRate - const mature = newProgress >= crop.growthTicks - - state.plots[rowIndex][colIndex].moisture = - plot.moisture - waterNeeded - state.plots[rowIndex][colIndex].current.progress = newProgress - - // If the plot just became mature, add a message - if (mature && !plot.current.mature) { - addConsoleMessage( - state, - `Plot (${rowIndex + 1},${colIndex + 1}) ${crop.name} is ready to harvest!`, - ) - } - - state.plots[rowIndex][colIndex].current.mature = mature } }) }) diff --git a/src/types/index.ts b/src/types/index.ts index 25c01b0..a082c33 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,14 +1,14 @@ export interface Crop { id: string name: string - growthTicks: number - waterPerTick: number + growthTimeSeconds: number + waterPerSecond: number yield: number yieldType: string fertilityDepletion: number seedType: string isPerennial?: boolean - regrowthProgress?: number + regrowthTimeSeconds?: number } export interface CropDefinitions {