284 lines
7.8 KiB
TypeScript
284 lines
7.8 KiB
TypeScript
import React, { useState } from 'react'
|
|
import { useGameStore } from '../store/useGameStore'
|
|
import { CROPS } from '../constants'
|
|
|
|
const fieldContainerStyle: React.CSSProperties = {
|
|
display: 'flex',
|
|
flexDirection: 'column',
|
|
gap: '1rem',
|
|
padding: '1rem',
|
|
}
|
|
|
|
const titleStyle: React.CSSProperties = {
|
|
fontSize: '1.5rem',
|
|
fontWeight: 'bold',
|
|
marginBottom: '1rem',
|
|
textAlign: 'center',
|
|
}
|
|
|
|
const cropSelectionContainerStyle: React.CSSProperties = {
|
|
display: 'flex',
|
|
flexWrap: 'wrap',
|
|
gap: '0.5rem',
|
|
marginBottom: '1rem',
|
|
justifyContent: 'center',
|
|
}
|
|
|
|
const cropSelectionLabelStyle: React.CSSProperties = {
|
|
width: '100%',
|
|
textAlign: 'center',
|
|
fontWeight: 500,
|
|
marginBottom: '0.5rem',
|
|
}
|
|
|
|
const getCropButtonStyle = (active: boolean): React.CSSProperties => ({
|
|
padding: '0.5rem 1rem',
|
|
borderRadius: '0.25rem',
|
|
border: '1px solid #ccc',
|
|
backgroundColor: active ? '#4ade80' : '#f3f4f6',
|
|
cursor: 'pointer',
|
|
})
|
|
|
|
const actionsContainerStyle: React.CSSProperties = {
|
|
display: 'flex',
|
|
gap: '0.5rem',
|
|
marginBottom: '1rem',
|
|
justifyContent: 'center',
|
|
}
|
|
|
|
const speedSelectorContainerStyle: React.CSSProperties = {
|
|
display: 'flex',
|
|
flexDirection: 'column',
|
|
alignItems: 'center',
|
|
marginBottom: '1rem',
|
|
padding: '0.5rem',
|
|
backgroundColor: '#f9fafb',
|
|
borderRadius: '0.5rem',
|
|
border: '1px solid #e5e7eb',
|
|
}
|
|
|
|
const speedSelectorLabelStyle: React.CSSProperties = {
|
|
fontWeight: 500,
|
|
marginBottom: '0.5rem',
|
|
}
|
|
|
|
const speedButtonsContainerStyle: React.CSSProperties = {
|
|
display: 'flex',
|
|
gap: '0.5rem',
|
|
}
|
|
|
|
const getSpeedButtonStyle = (active: boolean): React.CSSProperties => ({
|
|
padding: '0.5rem 1rem',
|
|
borderRadius: '0.25rem',
|
|
border: '1px solid #ccc',
|
|
backgroundColor: active ? '#4ade80' : '#f3f4f6',
|
|
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 => ({
|
|
display: 'grid',
|
|
gridTemplateColumns: `repeat(${size}, 1fr)`,
|
|
gridTemplateRows: `repeat(${size}, 1fr)`,
|
|
gap: '0.5rem',
|
|
width: '100%',
|
|
maxWidth: '600px',
|
|
margin: '0 auto',
|
|
})
|
|
|
|
const getPlotStyle = (bgColor: string): React.CSSProperties => ({
|
|
border: '1px solid #78350f',
|
|
aspectRatio: '1',
|
|
display: 'flex',
|
|
flexDirection: 'column',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
position: 'relative',
|
|
cursor: 'pointer',
|
|
backgroundColor: bgColor,
|
|
})
|
|
|
|
const getMoistureIndicatorStyle = (level: number): React.CSSProperties => ({
|
|
position: 'absolute',
|
|
bottom: 0,
|
|
left: 0,
|
|
width: '100%',
|
|
height: `${Math.min(level * 100, 100)}%`,
|
|
backgroundColor: 'rgba(147, 197, 253, 0.3)',
|
|
transition: 'height 0.3s',
|
|
zIndex: 1,
|
|
})
|
|
|
|
const getMaturityProgressContainerStyle = (): React.CSSProperties => ({
|
|
position: 'absolute',
|
|
bottom: '10px', // Moved up from the bottom to separate from moisture indicator
|
|
left: 0,
|
|
width: '100%',
|
|
height: '10px', // Increased height for better visibility
|
|
backgroundColor: 'rgba(0, 0, 0, 0.3)', // Darker background for better contrast
|
|
zIndex: 5, // Increased z-index to ensure it's above other elements
|
|
border: '1px solid #000', // Added border for better visibility
|
|
})
|
|
|
|
const getMaturityProgressBarStyle = (
|
|
progress: number,
|
|
total: number,
|
|
): React.CSSProperties => ({
|
|
position: 'absolute',
|
|
bottom: 0,
|
|
left: 0,
|
|
width: `${Math.min((progress / total) * 100, 100)}%`,
|
|
height: '10px', // Increased height to match container
|
|
backgroundColor: '#ff5722', // Brighter orange color for better visibility
|
|
transition: 'width 0.3s',
|
|
zIndex: 6, // Increased z-index to ensure it's above the container
|
|
boxShadow: '0 0 3px rgba(0, 0, 0, 0.5)', // Added shadow for better visibility
|
|
})
|
|
|
|
const FieldComponent: React.FC = () => {
|
|
const {
|
|
plots,
|
|
fieldSize,
|
|
plant,
|
|
water,
|
|
harvest,
|
|
assignCrop,
|
|
gameSpeed,
|
|
setGameSpeed,
|
|
actionCooldown,
|
|
} = useGameStore()
|
|
|
|
const [selectedCrop, setSelectedCrop] = useState<string | null>(null)
|
|
|
|
const handlePlotClick = (row: number, col: number) => {
|
|
if (selectedCrop) {
|
|
assignCrop(row, col, selectedCrop)
|
|
}
|
|
}
|
|
|
|
const getBgColor = (
|
|
hasCrop: boolean,
|
|
isMature: boolean,
|
|
fertility: number,
|
|
) => {
|
|
if (isMature) return '#22c55e'
|
|
if (hasCrop) return '#86efac'
|
|
|
|
// 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]
|
|
|
|
return (
|
|
<div style={fieldContainerStyle}>
|
|
<h2 style={titleStyle}>Fields</h2>
|
|
|
|
<div style={speedSelectorContainerStyle}>
|
|
<p style={speedSelectorLabelStyle}>Game Speed:</p>
|
|
<div style={speedButtonsContainerStyle}>
|
|
{availableSpeeds.map((speed) => (
|
|
<button
|
|
style={getSpeedButtonStyle(gameSpeed === speed)}
|
|
onClick={() => setGameSpeed(speed)}
|
|
key={speed}
|
|
>
|
|
{speed}x
|
|
</button>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
<div style={cropSelectionContainerStyle}>
|
|
<p style={cropSelectionLabelStyle}>Select a crop to plant:</p>
|
|
{Object.values(CROPS).map((crop) => (
|
|
<button
|
|
key={crop.id}
|
|
style={getCropButtonStyle(selectedCrop === crop.id)}
|
|
onClick={() => setSelectedCrop(crop.id)}
|
|
>
|
|
{crop.name}
|
|
</button>
|
|
))}
|
|
</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)}>
|
|
{plots.slice(0, fieldSize).map((row, rowIndex) =>
|
|
row.slice(0, fieldSize).map((plot, colIndex) => {
|
|
const hasCrop = !!plot.current
|
|
const isMature = !!plot.current?.mature
|
|
const bgColor = getBgColor(hasCrop, isMature, plot.fertility)
|
|
|
|
return (
|
|
<div
|
|
key={`${rowIndex}-${colIndex}`}
|
|
style={getPlotStyle(bgColor)}
|
|
onClick={() => handlePlotClick(rowIndex, colIndex)}
|
|
>
|
|
{plot.intended && !plot.current && (
|
|
<div>🌱 {CROPS[plot.intended]?.name}</div>
|
|
)}
|
|
{plot.current && (
|
|
<div>
|
|
{plot.current.mature ? '🌿' : '🌱'}{' '}
|
|
{CROPS[plot.current.cropId]?.name}
|
|
</div>
|
|
)}
|
|
<div style={getMoistureIndicatorStyle(plot.moisture)} />
|
|
{plot.current && !plot.current.mature && (
|
|
<div style={getMaturityProgressContainerStyle()}>
|
|
<div
|
|
style={getMaturityProgressBarStyle(
|
|
plot.current.progress,
|
|
CROPS[plot.current.cropId].growthTicks,
|
|
)}
|
|
/>
|
|
</div>
|
|
)}
|
|
</div>
|
|
)
|
|
}),
|
|
)}
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default FieldComponent
|