Add gape seeds and god modal

This commit is contained in:
Ryan Lanny Jenkins 2025-06-01 20:22:58 -05:00
parent 9edb030079
commit 5eda5c158b
6 changed files with 143 additions and 49 deletions

View file

@ -1,4 +1,4 @@
import React from 'react'
import React, { useState, useEffect } from 'react'
import {
Tabs,
TabsContent,
@ -13,6 +13,7 @@ import Temple from './components/Temple'
import { ActionCooldown } from './components/ActionCooldown'
import { useSaveSystem } from './store/useSaveSystem'
import { Console } from './components/Console'
import { GodModal } from './components/GodModal'
const appContainerStyle: React.CSSProperties = {
maxWidth: '1200px',
@ -29,6 +30,18 @@ const tabsListStyles: React.CSSProperties = {
function App() {
useGameTick()
useSaveSystem()
const [isGodModalOpen, setIsGodModalOpen] = useState(false)
useEffect(() => {
const handleKeyPress = (event: KeyboardEvent) => {
if (event.key.toLowerCase() === 'g') {
setIsGodModalOpen(prev => !prev)
}
}
window.addEventListener('keydown', handleKeyPress)
return () => window.removeEventListener('keydown', handleKeyPress)
}, [])
return (
<div>
@ -56,6 +69,7 @@ function App() {
</Tabs>
<Console />
</div>
{isGodModalOpen && <GodModal onClose={() => setIsGodModalOpen(false)} />}
</div>
)
}

View file

@ -59,35 +59,6 @@ const getCropButtonStyle = (active: boolean): React.CSSProperties => ({
cursor: 'pointer',
})
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 getFieldGridStyle = (size: number): React.CSSProperties => ({
display: 'grid',
gridTemplateColumns: `repeat(${size}, 1fr)`,
@ -220,8 +191,6 @@ const FieldComponent: React.FC = () => {
harvest,
remove,
assignCrop,
gameSpeed,
setGameSpeed,
actionCooldown,
} = useGameStore()
@ -276,8 +245,6 @@ const FieldComponent: React.FC = () => {
return `rgb(${r}, ${g}, ${b})`
}
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: '🌱' },
@ -291,21 +258,6 @@ const FieldComponent: React.FC = () => {
<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={toolbarStyle}>
{tools.map((tool) => (
<button

View file

@ -0,0 +1,96 @@
import React from 'react'
import { styled } from '@linaria/react'
import { useGameStore } from '../store/useGameStore'
import Modal from './Modal'
const Section = styled.div`
margin-bottom: 1.5rem;
`
const SectionTitle = styled.h3`
font-size: 1.1rem;
font-weight: 500;
margin-bottom: 0.5rem;
`
const SpeedButtonsContainer = styled.div`
display: flex;
gap: 0.5rem;
flex-wrap: wrap;
`
const SpeedButton = styled.button<{ active?: boolean }>`
padding: 0.5rem 1rem;
border-radius: 0.25rem;
border: 1px solid #ccc;
background-color: ${props => props.active ? '#4ade80' : '#f3f4f6'};
cursor: pointer;
transition: all 0.2s;
&:hover {
background-color: ${props => props.active ? '#4ade80' : '#e5e7eb'};
}
`
const AddCashButton = styled.button`
padding: 0.75rem 1.5rem;
border-radius: 0.375rem;
background-color: #4f46e5;
color: white;
font-weight: 500;
border: none;
cursor: pointer;
transition: all 0.2s;
width: 100%;
margin-bottom: 0.5rem;
&:hover {
background-color: #4338ca;
}
`
interface GodModalProps {
onClose: () => void
}
export const GodModal: React.FC<GodModalProps> = ({ onClose }) => {
const { gameSpeed, setGameSpeed, addCash } = useGameStore()
const availableSpeeds = [1, 2, 4, 8, 16, 32, 64]
const presetAmounts = [5, 10, 100]
const handleAddCashClick = (amount: number) => {
addCash(amount)
}
return (
<Modal onClose={onClose} title="God Controls">
<Section>
<SectionTitle>Game Speed</SectionTitle>
<SpeedButtonsContainer>
{availableSpeeds.map((speed) => (
<SpeedButton
key={speed}
active={gameSpeed === speed}
onClick={() => setGameSpeed(speed)}
>
{speed}x
</SpeedButton>
))}
</SpeedButtonsContainer>
</Section>
<Section>
<SectionTitle>Add Cash</SectionTitle>
{presetAmounts.map((amount) => (
<AddCashButton
key={amount}
onClick={() => handleAddCashClick(amount)}
>
${amount}
</AddCashButton>
))}
</Section>
</Modal>
)
}

View file

@ -46,6 +46,18 @@ export const CROPS: CropDefinitions = {
isPerennial: true,
regrowthProgress: 150, // Resets to 150 ticks after harvest
},
grape_vine: {
id: 'grape_vine',
name: 'Grape Vine',
growthTicks: 250,
waterPerTick: 1 / 45,
yield: 2,
yieldType: 'grape',
fertilityDepletion: 0.15,
seedType: 'grape_seed',
isPerennial: true,
regrowthProgress: 200, // Resets to 200 ticks after harvest
},
}
export const INITIAL_INVENTORY = {
@ -172,6 +184,13 @@ export const ITEMS: Record<string, Item> = {
buyPrice: 5,
sellPrice: null,
},
grape_seed: {
id: 'grape_seed',
name: 'Grape Seeds',
emoji: '🌰',
buyPrice: 25,
sellPrice: null,
},
celery: {
id: 'celery',
name: 'Celery',

View file

@ -116,6 +116,7 @@ export const useGameStore = create<
buyEquipment: (equipmentId: string) => void
configureEquipment: (equipmentId: string, recipeId: string) => void
useEquipment: (equipmentId: string) => void
addCash: (amount: number) => void
}
>((set, get) => ({
cash: INITIAL_CASH,
@ -132,6 +133,7 @@ export const useGameStore = create<
piety: 50,
landPurchases: 0,
equipment: {},
milestones: {},
assignCrop: (row, col, cropId) => {
set(
@ -266,6 +268,10 @@ export const useGameStore = create<
state.plots[row][col].fertility - crop.fertilityDepletion,
)
state.actionCooldown = COOLDOWN_DURATION
// Track milestone for crop harvest
const milestoneKey = `crops_harvested_${crop.id}`
state.milestones[milestoneKey] = (state.milestones[milestoneKey] || 0) + yieldAmount
}),
)
}
@ -662,4 +668,10 @@ export const useGameStore = create<
}),
)
},
addCash: (amount) => {
set(
produce((state) => { state.cash += amount }),
)
},
}))

View file

@ -92,6 +92,7 @@ export interface GameState {
piety: number
landPurchases: number
equipment: EquipmentState
milestones: Partial<Record<string, number>>
}
export interface MarketTransaction {