Add the basis of the fertility system.

This commit is contained in:
Ryan Lanny Jenkins 2025-05-18 12:53:49 -05:00
parent 9a9ec999ff
commit b38cc9762e
7 changed files with 55 additions and 17 deletions

View file

@ -164,10 +164,20 @@ const FieldComponent: React.FC = () => {
} }
} }
const getBgColor = (hasCrop: boolean, isMature: boolean) => { const getBgColor = (
hasCrop: boolean,
isMature: boolean,
fertility: number,
) => {
if (isMature) return '#22c55e' if (isMature) return '#22c55e'
if (hasCrop) return '#86efac' if (hasCrop) return '#86efac'
return '#92400e'
// For empty plots, show fertility level through color
// Convert fertility (0-1) to a color between light grey-brown and dark brown
const r = Math.floor(146 + (73 - 146) * fertility) // 146 to 73
const g = Math.floor(131 + (47 - 131) * fertility) // 131 to 47
const b = Math.floor(120 + (23 - 120) * fertility) // 120 to 23
return `rgb(${r}, ${g}, ${b})`
} }
const availableSpeeds = [1, 2, 4, 8, 16, 32, 64] const availableSpeeds = [1, 2, 4, 8, 16, 32, 64]
@ -233,7 +243,7 @@ const FieldComponent: React.FC = () => {
row.slice(0, fieldSize).map((plot, colIndex) => { row.slice(0, fieldSize).map((plot, colIndex) => {
const hasCrop = !!plot.current const hasCrop = !!plot.current
const isMature = !!plot.current?.mature const isMature = !!plot.current?.mature
const bgColor = getBgColor(hasCrop, isMature) const bgColor = getBgColor(hasCrop, isMature, plot.fertility)
return ( return (
<div <div

View file

@ -1,4 +1,9 @@
import React, { useEffect, useState, useImperativeHandle, forwardRef } from 'react' import React, {
useEffect,
useState,
useImperativeHandle,
forwardRef,
} from 'react'
interface Message { interface Message {
id: string id: string
@ -20,12 +25,12 @@ const FloatingMessages = forwardRef<FloatingMessagesHandle>((_, ref) => {
text, text,
position, position,
} }
setMessages(prev => [...prev, newMessage]) setMessages((prev) => [...prev, newMessage])
} },
})) }))
const handleAnimationEnd = (messageId: string) => { const handleAnimationEnd = (messageId: string) => {
setMessages(prev => prev.filter(msg => msg.id !== messageId)) setMessages((prev) => prev.filter((msg) => msg.id !== messageId))
} }
return ( return (
@ -52,7 +57,7 @@ const FloatingMessages = forwardRef<FloatingMessagesHandle>((_, ref) => {
} }
`} `}
</style> </style>
{messages.map(message => ( {messages.map((message) => (
<div <div
key={message.id} key={message.id}
className="floating-message" className="floating-message"

View file

@ -86,7 +86,7 @@ const MarketComponent: React.FC = () => {
floatingMessagesRef.current?.addMessage( floatingMessagesRef.current?.addMessage(
`+1 ${item.emoji} ${item.name}`, `+1 ${item.emoji} ${item.name}`,
position position,
) )
} }
@ -107,7 +107,7 @@ const MarketComponent: React.FC = () => {
floatingMessagesRef.current?.addMessage( floatingMessagesRef.current?.addMessage(
`-1 ${item.emoji} ${item.name}`, `-1 ${item.emoji} ${item.name}`,
position position,
) )
} }

View file

@ -21,6 +21,7 @@ export const CROPS: CropDefinitions = {
waterPerTick: 1 / 20, waterPerTick: 1 / 20,
yield: 1, yield: 1,
yieldType: 'celery', yieldType: 'celery',
fertilityDepletion: 0.1,
}, },
corn: { corn: {
id: 'corn', id: 'corn',
@ -29,6 +30,7 @@ export const CROPS: CropDefinitions = {
waterPerTick: 1 / 40, waterPerTick: 1 / 40,
yield: 5, yield: 5,
yieldType: 'corn', yieldType: 'corn',
fertilityDepletion: 0.3,
}, },
} }

View file

@ -20,6 +20,7 @@ const initializeField = (size: number): PlotState[][] => {
.fill(0) .fill(0)
.map(() => ({ .map(() => ({
moisture: 0, moisture: 0,
fertility: 1,
})), })),
) )
} }
@ -175,6 +176,10 @@ export const useGameStore = create<
state.plots[row][col].current = undefined state.plots[row][col].current = undefined
state.inventory[yieldItem] = state.inventory[yieldItem] =
(state.inventory[yieldItem] || 0) + yieldAmount (state.inventory[yieldItem] || 0) + yieldAmount
state.plots[row][col].fertility = Math.max(
0,
state.plots[row][col].fertility - crop.fertilityDepletion,
)
state.actionCooldown = COOLDOWN_DURATION state.actionCooldown = COOLDOWN_DURATION
}), }),
) )
@ -190,6 +195,14 @@ export const useGameStore = create<
// Update plots // Update plots
state.plots.forEach((row: PlotState[], rowIndex: number) => { state.plots.forEach((row: PlotState[], rowIndex: number) => {
row.forEach((plot: PlotState, colIndex: number) => { row.forEach((plot: PlotState, colIndex: number) => {
// Regenerate fertility every 100 ticks
if (state.tickCount % 100 === 0 && plot.fertility < 1) {
state.plots[rowIndex][colIndex].fertility = Math.min(
1,
plot.fertility + 0.1,
)
}
if (!plot.current || plot.current.mature) { if (!plot.current || plot.current.mature) {
return return
} }
@ -197,8 +210,15 @@ export const useGameStore = create<
const crop = CROPS[plot.current.cropId] const crop = CROPS[plot.current.cropId]
const waterNeeded = crop.waterPerTick const waterNeeded = crop.waterPerTick
if (plot.moisture >= waterNeeded) { // Only grow if fertility is above 0.2
const newProgress = plot.current.progress + 1 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
}
const newProgress = plot.current.progress + growthRate
const mature = newProgress >= crop.growthTicks const mature = newProgress >= crop.growthTicks
state.plots[rowIndex][colIndex].moisture = state.plots[rowIndex][colIndex].moisture =
@ -257,7 +277,7 @@ export const useGameStore = create<
const { cash } = get() const { cash } = get()
const item = MARKET_ITEMS[itemId] const item = MARKET_ITEMS[itemId]
if (!item || item.buyPrice === null) { if (!item || item.buyPrice === null || item.buyPrice === undefined) {
return return
} }
@ -267,7 +287,7 @@ export const useGameStore = create<
set( set(
produce((state) => { produce((state) => {
state.cash -= item.buyPrice state.cash -= item.buyPrice!
state.inventory[itemId] = (state.inventory[itemId] || 0) + 1 state.inventory[itemId] = (state.inventory[itemId] || 0) + 1
}), }),
) )

View file

@ -1,5 +1,5 @@
import { useEffect } from "react" import { useEffect } from 'react'
import { hasSaveInSlot, useGameStore } from "./useGameStore" import { hasSaveInSlot, useGameStore } from './useGameStore'
export const useSaveSystem = () => { export const useSaveSystem = () => {
const { saveToSlot, loadFromSlot } = useGameStore() const { saveToSlot, loadFromSlot } = useGameStore()
@ -31,7 +31,6 @@ export const useSaveSystem = () => {
} }
} }
window.addEventListener('keydown', handleKeyDown) window.addEventListener('keydown', handleKeyDown)
const interval = setInterval(() => { const interval = setInterval(() => {
saveToSlot(0) saveToSlot(0)

View file

@ -5,6 +5,7 @@ export interface Crop {
waterPerTick: number waterPerTick: number
yield: number yield: number
yieldType: string yieldType: string
fertilityDepletion: number
} }
export interface CropDefinitions { export interface CropDefinitions {
@ -19,6 +20,7 @@ export interface PlotState {
mature: boolean mature: boolean
} }
moisture: number moisture: number
fertility: number
} }
export interface InventoryItem { export interface InventoryItem {