Add gape seeds and god modal
This commit is contained in:
parent
9edb030079
commit
5eda5c158b
16
src/App.tsx
16
src/App.tsx
|
|
@ -1,4 +1,4 @@
|
||||||
import React from 'react'
|
import React, { useState, useEffect } from 'react'
|
||||||
import {
|
import {
|
||||||
Tabs,
|
Tabs,
|
||||||
TabsContent,
|
TabsContent,
|
||||||
|
|
@ -13,6 +13,7 @@ import Temple from './components/Temple'
|
||||||
import { ActionCooldown } from './components/ActionCooldown'
|
import { ActionCooldown } from './components/ActionCooldown'
|
||||||
import { useSaveSystem } from './store/useSaveSystem'
|
import { useSaveSystem } from './store/useSaveSystem'
|
||||||
import { Console } from './components/Console'
|
import { Console } from './components/Console'
|
||||||
|
import { GodModal } from './components/GodModal'
|
||||||
|
|
||||||
const appContainerStyle: React.CSSProperties = {
|
const appContainerStyle: React.CSSProperties = {
|
||||||
maxWidth: '1200px',
|
maxWidth: '1200px',
|
||||||
|
|
@ -29,6 +30,18 @@ const tabsListStyles: React.CSSProperties = {
|
||||||
function App() {
|
function App() {
|
||||||
useGameTick()
|
useGameTick()
|
||||||
useSaveSystem()
|
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 (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -56,6 +69,7 @@ function App() {
|
||||||
</Tabs>
|
</Tabs>
|
||||||
<Console />
|
<Console />
|
||||||
</div>
|
</div>
|
||||||
|
{isGodModalOpen && <GodModal onClose={() => setIsGodModalOpen(false)} />}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -59,35 +59,6 @@ const getCropButtonStyle = (active: boolean): React.CSSProperties => ({
|
||||||
cursor: 'pointer',
|
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 => ({
|
const getFieldGridStyle = (size: number): React.CSSProperties => ({
|
||||||
display: 'grid',
|
display: 'grid',
|
||||||
gridTemplateColumns: `repeat(${size}, 1fr)`,
|
gridTemplateColumns: `repeat(${size}, 1fr)`,
|
||||||
|
|
@ -220,8 +191,6 @@ const FieldComponent: React.FC = () => {
|
||||||
harvest,
|
harvest,
|
||||||
remove,
|
remove,
|
||||||
assignCrop,
|
assignCrop,
|
||||||
gameSpeed,
|
|
||||||
setGameSpeed,
|
|
||||||
actionCooldown,
|
actionCooldown,
|
||||||
} = useGameStore()
|
} = useGameStore()
|
||||||
|
|
||||||
|
|
@ -276,8 +245,6 @@ const FieldComponent: React.FC = () => {
|
||||||
return `rgb(${r}, ${g}, ${b})`
|
return `rgb(${r}, ${g}, ${b})`
|
||||||
}
|
}
|
||||||
|
|
||||||
const availableSpeeds = [1, 2, 4, 8, 16, 32, 64]
|
|
||||||
|
|
||||||
const tools: { id: FieldTool; label: string; icon: string }[] = [
|
const tools: { id: FieldTool; label: string; icon: string }[] = [
|
||||||
{ id: 'mark', label: 'Mark', icon: '🎯' },
|
{ id: 'mark', label: 'Mark', icon: '🎯' },
|
||||||
{ id: 'plant', label: 'Plant', icon: '🌱' },
|
{ id: 'plant', label: 'Plant', icon: '🌱' },
|
||||||
|
|
@ -291,21 +258,6 @@ const FieldComponent: React.FC = () => {
|
||||||
<div style={fieldContainerStyle}>
|
<div style={fieldContainerStyle}>
|
||||||
<h2 style={titleStyle}>Fields</h2>
|
<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}>
|
<div style={toolbarStyle}>
|
||||||
{tools.map((tool) => (
|
{tools.map((tool) => (
|
||||||
<button
|
<button
|
||||||
|
|
|
||||||
96
src/components/GodModal.tsx
Normal file
96
src/components/GodModal.tsx
Normal 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>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -46,6 +46,18 @@ export const CROPS: CropDefinitions = {
|
||||||
isPerennial: true,
|
isPerennial: true,
|
||||||
regrowthProgress: 150, // Resets to 150 ticks after harvest
|
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 = {
|
export const INITIAL_INVENTORY = {
|
||||||
|
|
@ -172,6 +184,13 @@ export const ITEMS: Record<string, Item> = {
|
||||||
buyPrice: 5,
|
buyPrice: 5,
|
||||||
sellPrice: null,
|
sellPrice: null,
|
||||||
},
|
},
|
||||||
|
grape_seed: {
|
||||||
|
id: 'grape_seed',
|
||||||
|
name: 'Grape Seeds',
|
||||||
|
emoji: '🌰',
|
||||||
|
buyPrice: 25,
|
||||||
|
sellPrice: null,
|
||||||
|
},
|
||||||
celery: {
|
celery: {
|
||||||
id: 'celery',
|
id: 'celery',
|
||||||
name: 'Celery',
|
name: 'Celery',
|
||||||
|
|
|
||||||
|
|
@ -116,6 +116,7 @@ export const useGameStore = create<
|
||||||
buyEquipment: (equipmentId: string) => void
|
buyEquipment: (equipmentId: string) => void
|
||||||
configureEquipment: (equipmentId: string, recipeId: string) => void
|
configureEquipment: (equipmentId: string, recipeId: string) => void
|
||||||
useEquipment: (equipmentId: string) => void
|
useEquipment: (equipmentId: string) => void
|
||||||
|
addCash: (amount: number) => void
|
||||||
}
|
}
|
||||||
>((set, get) => ({
|
>((set, get) => ({
|
||||||
cash: INITIAL_CASH,
|
cash: INITIAL_CASH,
|
||||||
|
|
@ -132,6 +133,7 @@ export const useGameStore = create<
|
||||||
piety: 50,
|
piety: 50,
|
||||||
landPurchases: 0,
|
landPurchases: 0,
|
||||||
equipment: {},
|
equipment: {},
|
||||||
|
milestones: {},
|
||||||
|
|
||||||
assignCrop: (row, col, cropId) => {
|
assignCrop: (row, col, cropId) => {
|
||||||
set(
|
set(
|
||||||
|
|
@ -266,6 +268,10 @@ export const useGameStore = create<
|
||||||
state.plots[row][col].fertility - crop.fertilityDepletion,
|
state.plots[row][col].fertility - crop.fertilityDepletion,
|
||||||
)
|
)
|
||||||
state.actionCooldown = COOLDOWN_DURATION
|
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 }),
|
||||||
|
)
|
||||||
|
},
|
||||||
}))
|
}))
|
||||||
|
|
|
||||||
|
|
@ -92,6 +92,7 @@ export interface GameState {
|
||||||
piety: number
|
piety: number
|
||||||
landPurchases: number
|
landPurchases: number
|
||||||
equipment: EquipmentState
|
equipment: EquipmentState
|
||||||
|
milestones: Partial<Record<string, number>>
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MarketTransaction {
|
export interface MarketTransaction {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue