Compare commits
2 commits
9a9ec999ff
...
00db07e637
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
00db07e637 | ||
|
|
b38cc9762e |
|
|
@ -1,6 +1,7 @@
|
||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
import { useGameStore } from '../store/useGameStore'
|
import { useGameStore } from '../store/useGameStore'
|
||||||
import { CROPS } from '../constants'
|
import { CROPS } from '../constants'
|
||||||
|
import { FieldTool, PlotState } from '../types'
|
||||||
|
|
||||||
const fieldContainerStyle: React.CSSProperties = {
|
const fieldContainerStyle: React.CSSProperties = {
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
|
|
@ -16,6 +17,24 @@ const titleStyle: React.CSSProperties = {
|
||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const toolbarStyle: React.CSSProperties = {
|
||||||
|
display: 'flex',
|
||||||
|
gap: '0.5rem',
|
||||||
|
marginBottom: '1rem',
|
||||||
|
justifyContent: 'center',
|
||||||
|
}
|
||||||
|
|
||||||
|
const getToolButtonStyle = (active: boolean): React.CSSProperties => ({
|
||||||
|
padding: '0.5rem 1rem',
|
||||||
|
borderRadius: '0.25rem',
|
||||||
|
border: '1px solid #ccc',
|
||||||
|
backgroundColor: active ? '#4ade80' : '#f3f4f6',
|
||||||
|
cursor: 'pointer',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: '0.5rem',
|
||||||
|
})
|
||||||
|
|
||||||
const cropSelectionContainerStyle: React.CSSProperties = {
|
const cropSelectionContainerStyle: React.CSSProperties = {
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexWrap: 'wrap',
|
flexWrap: 'wrap',
|
||||||
|
|
@ -39,13 +58,6 @@ const getCropButtonStyle = (active: boolean): React.CSSProperties => ({
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
})
|
})
|
||||||
|
|
||||||
const actionsContainerStyle: React.CSSProperties = {
|
|
||||||
display: 'flex',
|
|
||||||
gap: '0.5rem',
|
|
||||||
marginBottom: '1rem',
|
|
||||||
justifyContent: 'center',
|
|
||||||
}
|
|
||||||
|
|
||||||
const speedSelectorContainerStyle: React.CSSProperties = {
|
const speedSelectorContainerStyle: React.CSSProperties = {
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
|
|
@ -75,15 +87,6 @@ const getSpeedButtonStyle = (active: boolean): React.CSSProperties => ({
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
})
|
})
|
||||||
|
|
||||||
const getActionButtonStyle = (disabled: boolean): React.CSSProperties => ({
|
|
||||||
padding: '0.5rem 1rem',
|
|
||||||
borderRadius: '0.25rem',
|
|
||||||
fontWeight: 'bold',
|
|
||||||
color: 'white',
|
|
||||||
backgroundColor: disabled ? '#9ca3af' : '#22c55e',
|
|
||||||
cursor: disabled ? 'not-allowed' : 'pointer',
|
|
||||||
})
|
|
||||||
|
|
||||||
const getFieldGridStyle = (size: number): React.CSSProperties => ({
|
const getFieldGridStyle = (size: number): React.CSSProperties => ({
|
||||||
display: 'grid',
|
display: 'grid',
|
||||||
gridTemplateColumns: `repeat(${size}, 1fr)`,
|
gridTemplateColumns: `repeat(${size}, 1fr)`,
|
||||||
|
|
@ -119,13 +122,13 @@ const getMoistureIndicatorStyle = (level: number): React.CSSProperties => ({
|
||||||
|
|
||||||
const getMaturityProgressContainerStyle = (): React.CSSProperties => ({
|
const getMaturityProgressContainerStyle = (): React.CSSProperties => ({
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
bottom: '10px', // Moved up from the bottom to separate from moisture indicator
|
bottom: '10px',
|
||||||
left: 0,
|
left: 0,
|
||||||
width: '100%',
|
width: '100%',
|
||||||
height: '10px', // Increased height for better visibility
|
height: '10px',
|
||||||
backgroundColor: 'rgba(0, 0, 0, 0.3)', // Darker background for better contrast
|
backgroundColor: 'rgba(0, 0, 0, 0.3)',
|
||||||
zIndex: 5, // Increased z-index to ensure it's above other elements
|
zIndex: 5,
|
||||||
border: '1px solid #000', // Added border for better visibility
|
border: '1px solid #000',
|
||||||
})
|
})
|
||||||
|
|
||||||
const getMaturityProgressBarStyle = (
|
const getMaturityProgressBarStyle = (
|
||||||
|
|
@ -136,13 +139,117 @@ const getMaturityProgressBarStyle = (
|
||||||
bottom: 0,
|
bottom: 0,
|
||||||
left: 0,
|
left: 0,
|
||||||
width: `${Math.min((progress / total) * 100, 100)}%`,
|
width: `${Math.min((progress / total) * 100, 100)}%`,
|
||||||
height: '10px', // Increased height to match container
|
height: '10px',
|
||||||
backgroundColor: '#ff5722', // Brighter orange color for better visibility
|
backgroundColor: '#ff5722',
|
||||||
transition: 'width 0.3s',
|
transition: 'width 0.3s',
|
||||||
zIndex: 6, // Increased z-index to ensure it's above the container
|
zIndex: 6,
|
||||||
boxShadow: '0 0 3px rgba(0, 0, 0, 0.5)', // Added shadow for better visibility
|
boxShadow: '0 0 3px rgba(0, 0, 0, 0.5)',
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const modalOverlayStyle: React.CSSProperties = {
|
||||||
|
position: 'fixed',
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
bottom: 0,
|
||||||
|
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
zIndex: 1000,
|
||||||
|
}
|
||||||
|
|
||||||
|
const modalContentStyle: React.CSSProperties = {
|
||||||
|
backgroundColor: 'white',
|
||||||
|
padding: '2rem',
|
||||||
|
borderRadius: '0.5rem',
|
||||||
|
maxWidth: '400px',
|
||||||
|
width: '90%',
|
||||||
|
position: 'relative',
|
||||||
|
}
|
||||||
|
|
||||||
|
const modalCloseButtonStyle: React.CSSProperties = {
|
||||||
|
position: 'absolute',
|
||||||
|
top: '0.5rem',
|
||||||
|
right: '0.5rem',
|
||||||
|
padding: '0.5rem',
|
||||||
|
border: 'none',
|
||||||
|
background: 'none',
|
||||||
|
cursor: 'pointer',
|
||||||
|
fontSize: '1.5rem',
|
||||||
|
}
|
||||||
|
|
||||||
|
const plotInfoStyle: React.CSSProperties = {
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
gap: '1rem',
|
||||||
|
}
|
||||||
|
|
||||||
|
const plotInfoItemStyle: React.CSSProperties = {
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
alignItems: 'center',
|
||||||
|
padding: '0.5rem',
|
||||||
|
backgroundColor: '#f3f4f6',
|
||||||
|
borderRadius: '0.25rem',
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PlotInfoModalProps {
|
||||||
|
plot: PlotState
|
||||||
|
onClose: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const PlotInfoModal: React.FC<PlotInfoModalProps> = ({ plot, onClose }) => {
|
||||||
|
const formatPercentage = (value: number) => `${Math.round(value * 100)}%`
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={modalOverlayStyle} onClick={onClose}>
|
||||||
|
<div style={modalContentStyle} onClick={(e) => e.stopPropagation()}>
|
||||||
|
<button style={modalCloseButtonStyle} onClick={onClose}>
|
||||||
|
×
|
||||||
|
</button>
|
||||||
|
<h3 style={{ marginTop: 0 }}>Plot Information</h3>
|
||||||
|
<div style={plotInfoStyle}>
|
||||||
|
<div style={plotInfoItemStyle}>
|
||||||
|
<span>Current Crop:</span>
|
||||||
|
<span>
|
||||||
|
{plot.current
|
||||||
|
? `${CROPS[plot.current.cropId].name}${
|
||||||
|
plot.current.mature ? ' (Mature)' : ''
|
||||||
|
}`
|
||||||
|
: 'None'}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div style={plotInfoItemStyle}>
|
||||||
|
<span>Intended Crop:</span>
|
||||||
|
<span>
|
||||||
|
{plot.intended ? CROPS[plot.intended].name : 'None'}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div style={plotInfoItemStyle}>
|
||||||
|
<span>Water Level:</span>
|
||||||
|
<span>{formatPercentage(plot.moisture)}</span>
|
||||||
|
</div>
|
||||||
|
<div style={plotInfoItemStyle}>
|
||||||
|
<span>Fertility Level:</span>
|
||||||
|
<span>{formatPercentage(plot.fertility)}</span>
|
||||||
|
</div>
|
||||||
|
{plot.current && !plot.current.mature && (
|
||||||
|
<div style={plotInfoItemStyle}>
|
||||||
|
<span>Growth Progress:</span>
|
||||||
|
<span>
|
||||||
|
{formatPercentage(
|
||||||
|
plot.current.progress / CROPS[plot.current.cropId].growthTicks,
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
const FieldComponent: React.FC = () => {
|
const FieldComponent: React.FC = () => {
|
||||||
const {
|
const {
|
||||||
plots,
|
plots,
|
||||||
|
|
@ -157,21 +264,63 @@ const FieldComponent: React.FC = () => {
|
||||||
} = useGameStore()
|
} = useGameStore()
|
||||||
|
|
||||||
const [selectedCrop, setSelectedCrop] = useState<string | null>(null)
|
const [selectedCrop, setSelectedCrop] = useState<string | null>(null)
|
||||||
|
const [selectedTool, setSelectedTool] = useState<FieldTool>('mark')
|
||||||
|
const [inspectedPlot, setInspectedPlot] = useState<{
|
||||||
|
plot: PlotState
|
||||||
|
row: number
|
||||||
|
col: number
|
||||||
|
} | null>(null)
|
||||||
|
|
||||||
const handlePlotClick = (row: number, col: number) => {
|
const handlePlotClick = (row: number, col: number) => {
|
||||||
|
if (actionCooldown > 0) return
|
||||||
|
|
||||||
|
switch (selectedTool) {
|
||||||
|
case 'mark':
|
||||||
if (selectedCrop) {
|
if (selectedCrop) {
|
||||||
assignCrop(row, col, selectedCrop)
|
assignCrop(row, col, selectedCrop)
|
||||||
}
|
}
|
||||||
|
break
|
||||||
|
case 'plant':
|
||||||
|
plant(row, col)
|
||||||
|
break
|
||||||
|
case 'water':
|
||||||
|
water(row, col)
|
||||||
|
break
|
||||||
|
case 'harvest':
|
||||||
|
harvest(row, col)
|
||||||
|
break
|
||||||
|
case 'inspect':
|
||||||
|
setInspectedPlot({ plot: plots[row][col], row, col })
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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]
|
||||||
|
|
||||||
|
const tools: { id: FieldTool; label: string; icon: string }[] = [
|
||||||
|
{ id: 'mark', label: 'Mark', icon: '🎯' },
|
||||||
|
{ id: 'plant', label: 'Plant', icon: '🌱' },
|
||||||
|
{ id: 'water', label: 'Water', icon: '💧' },
|
||||||
|
{ id: 'harvest', label: 'Harvest', icon: '✂️' },
|
||||||
|
{ id: 'inspect', label: 'Inspect', icon: '🔍' },
|
||||||
|
]
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={fieldContainerStyle}>
|
<div style={fieldContainerStyle}>
|
||||||
<h2 style={titleStyle}>Fields</h2>
|
<h2 style={titleStyle}>Fields</h2>
|
||||||
|
|
@ -191,8 +340,22 @@ const FieldComponent: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div style={toolbarStyle}>
|
||||||
|
{tools.map((tool) => (
|
||||||
|
<button
|
||||||
|
key={tool.id}
|
||||||
|
style={getToolButtonStyle(selectedTool === tool.id)}
|
||||||
|
onClick={() => setSelectedTool(tool.id)}
|
||||||
|
>
|
||||||
|
<span>{tool.icon}</span>
|
||||||
|
<span>{tool.label}</span>
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{selectedTool === 'mark' && (
|
||||||
<div style={cropSelectionContainerStyle}>
|
<div style={cropSelectionContainerStyle}>
|
||||||
<p style={cropSelectionLabelStyle}>Select a crop to plant:</p>
|
<p style={cropSelectionLabelStyle}>Select a crop to mark:</p>
|
||||||
{Object.values(CROPS).map((crop) => (
|
{Object.values(CROPS).map((crop) => (
|
||||||
<button
|
<button
|
||||||
key={crop.id}
|
key={crop.id}
|
||||||
|
|
@ -203,37 +366,14 @@ const FieldComponent: React.FC = () => {
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
<div style={actionsContainerStyle}>
|
|
||||||
<button
|
|
||||||
style={getActionButtonStyle(actionCooldown > 0)}
|
|
||||||
onClick={plant}
|
|
||||||
disabled={actionCooldown > 0}
|
|
||||||
>
|
|
||||||
Plant Crop
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
style={getActionButtonStyle(actionCooldown > 0)}
|
|
||||||
onClick={water}
|
|
||||||
disabled={actionCooldown > 0}
|
|
||||||
>
|
|
||||||
Water Crop
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
style={getActionButtonStyle(actionCooldown > 0)}
|
|
||||||
onClick={harvest}
|
|
||||||
disabled={actionCooldown > 0}
|
|
||||||
>
|
|
||||||
Harvest Crop
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style={getFieldGridStyle(fieldSize)}>
|
<div style={getFieldGridStyle(fieldSize)}>
|
||||||
{plots.slice(0, fieldSize).map((row, rowIndex) =>
|
{plots.slice(0, fieldSize).map((row, rowIndex) =>
|
||||||
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
|
||||||
|
|
@ -266,6 +406,13 @@ const FieldComponent: React.FC = () => {
|
||||||
}),
|
}),
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{inspectedPlot && (
|
||||||
|
<PlotInfoModal
|
||||||
|
plot={inspectedPlot.plot}
|
||||||
|
onClose={() => setInspectedPlot(null)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import {
|
||||||
COOLDOWN_DURATION,
|
COOLDOWN_DURATION,
|
||||||
MARKET_ITEMS,
|
MARKET_ITEMS,
|
||||||
} from '../constants'
|
} from '../constants'
|
||||||
import { GameState, PlotState } from '../types'
|
import { GameState, PlotState, FieldTool } from '../types'
|
||||||
|
|
||||||
const initializeField = (size: number): PlotState[][] => {
|
const initializeField = (size: number): PlotState[][] => {
|
||||||
return Array(size)
|
return Array(size)
|
||||||
|
|
@ -20,6 +20,7 @@ const initializeField = (size: number): PlotState[][] => {
|
||||||
.fill(0)
|
.fill(0)
|
||||||
.map(() => ({
|
.map(() => ({
|
||||||
moisture: 0,
|
moisture: 0,
|
||||||
|
fertility: 1,
|
||||||
})),
|
})),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -54,9 +55,9 @@ export const hasSaveInSlot = (slot: number): boolean => {
|
||||||
|
|
||||||
export const useGameStore = create<
|
export const useGameStore = create<
|
||||||
GameState & {
|
GameState & {
|
||||||
plant: () => void
|
plant: (row: number, col: number) => void
|
||||||
water: () => void
|
water: (row: number, col: number) => void
|
||||||
harvest: () => void
|
harvest: (row: number, col: number) => void
|
||||||
tick: () => void
|
tick: () => void
|
||||||
assignCrop: (row: number, col: number, cropId: string) => void
|
assignCrop: (row: number, col: number, cropId: string) => void
|
||||||
upgradeField: () => void
|
upgradeField: () => void
|
||||||
|
|
@ -86,17 +87,14 @@ export const useGameStore = create<
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
plant: () => {
|
plant: (row, col) => {
|
||||||
const { plots, inventory, actionCooldown } = get()
|
const { plots, inventory, actionCooldown } = get()
|
||||||
|
|
||||||
if (actionCooldown > 0) {
|
if (actionCooldown > 0) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let row = 0; row < plots.length; row++) {
|
|
||||||
for (let col = 0; col < plots[row].length; col++) {
|
|
||||||
const plot = plots[row][col]
|
const plot = plots[row][col]
|
||||||
|
|
||||||
if (plot.intended && !plot.current) {
|
if (plot.intended && !plot.current) {
|
||||||
const seedId = `${plot.intended}_seed`
|
const seedId = `${plot.intended}_seed`
|
||||||
|
|
||||||
|
|
@ -113,58 +111,36 @@ export const useGameStore = create<
|
||||||
state.actionCooldown = COOLDOWN_DURATION
|
state.actionCooldown = COOLDOWN_DURATION
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
water: () => {
|
water: (row, col) => {
|
||||||
const { plots, actionCooldown } = get()
|
const { plots, actionCooldown } = get()
|
||||||
|
|
||||||
if (actionCooldown > 0) {
|
if (actionCooldown > 0) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let driestRow = -1
|
const plot = plots[row][col]
|
||||||
let driestCol = -1
|
if (plot.current && plot.moisture < 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(
|
set(
|
||||||
produce((state) => {
|
produce((state) => {
|
||||||
state.plots[driestRow][driestCol].moisture = 1
|
state.plots[row][col].moisture = 1
|
||||||
state.actionCooldown = COOLDOWN_DURATION
|
state.actionCooldown = COOLDOWN_DURATION
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
harvest: () => {
|
harvest: (row, col) => {
|
||||||
const { plots, actionCooldown } = get()
|
const { plots, actionCooldown } = get()
|
||||||
|
|
||||||
if (actionCooldown > 0) {
|
if (actionCooldown > 0) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let row = 0; row < plots.length; row++) {
|
|
||||||
for (let col = 0; col < plots[row].length; col++) {
|
|
||||||
const plot = plots[row][col]
|
const plot = plots[row][col]
|
||||||
|
|
||||||
if (plot.current && plot.current.mature) {
|
if (plot.current && plot.current.mature) {
|
||||||
const crop = CROPS[plot.current.cropId]
|
const crop = CROPS[plot.current.cropId]
|
||||||
const yieldItem = crop.yieldType
|
const yieldItem = crop.yieldType
|
||||||
|
|
@ -175,12 +151,13 @@ 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
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -190,6 +167,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 +182,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 +249,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 +259,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
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
@ -26,6 +28,8 @@ export interface InventoryItem {
|
||||||
count: number
|
count: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type FieldTool = 'mark' | 'plant' | 'water' | 'harvest' | 'inspect'
|
||||||
|
|
||||||
export interface GameState {
|
export interface GameState {
|
||||||
cash: number
|
cash: number
|
||||||
inventory: Record<string, number>
|
inventory: Record<string, number>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue