diff --git a/package-lock.json b/package-lock.json
index 4fdb1ee..67f8639 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,11 +1,11 @@
{
- "name": "idle-farm-game",
+ "name": "dionysian-idle",
"version": "0.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
- "name": "idle-farm-game",
+ "name": "dionysian-idle",
"version": "0.0.0",
"dependencies": {
"@hookform/resolvers": "^5.0.1",
@@ -70,6 +70,7 @@
"@types/react": "^18.3.12",
"@types/react-dom": "^18.3.1",
"@vitejs/plugin-react": "^4.3.4",
+ "@wyw-in-js/vite": "^0.6.0",
"autoprefixer": "^10.4.20",
"eslint": "^9.15.0",
"eslint-plugin-react-hooks": "^5.0.0",
@@ -4863,6 +4864,72 @@
"url": "https://github.com/sponsors/isaacs"
}
},
+ "node_modules/@wyw-in-js/transform": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/@wyw-in-js/transform/-/transform-0.6.0.tgz",
+ "integrity": "sha512-/Rxw8e3KcmSjnqWs/6flmpcNTa4jLw8NViIyNwqem498AfSWXrPnYf3RyjWed9zDVL72q7Xjei6TcRqMupl3Eg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/core": "^7.23.5",
+ "@babel/generator": "^7.23.5",
+ "@babel/helper-module-imports": "^7.22.15",
+ "@babel/plugin-transform-modules-commonjs": "^7.23.3",
+ "@babel/template": "^7.22.15",
+ "@babel/traverse": "^7.23.5",
+ "@babel/types": "^7.23.5",
+ "@wyw-in-js/processor-utils": "0.6.0",
+ "@wyw-in-js/shared": "0.6.0",
+ "babel-merge": "^3.0.0",
+ "cosmiconfig": "^8.0.0",
+ "happy-dom": "^15.11.0",
+ "source-map": "^0.7.4",
+ "stylis": "^4.3.0",
+ "ts-invariant": "^0.10.3"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/@wyw-in-js/transform/node_modules/happy-dom": {
+ "version": "15.11.7",
+ "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-15.11.7.tgz",
+ "integrity": "sha512-KyrFvnl+J9US63TEzwoiJOQzZBJY7KgBushJA8X61DMbNsH+2ONkDuLDnCnwUiPTF42tLoEmrPyoqbenVA5zrg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "entities": "^4.5.0",
+ "webidl-conversions": "^7.0.0",
+ "whatwg-mimetype": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@wyw-in-js/transform/node_modules/stylis": {
+ "version": "4.3.6",
+ "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.6.tgz",
+ "integrity": "sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@wyw-in-js/vite": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/@wyw-in-js/vite/-/vite-0.6.0.tgz",
+ "integrity": "sha512-nGeH/dFrjecHRJfxd5/71cJu4V2YWakUmnEoAUpS0iTnVVC4GWjKqt1dLtsJvtKZIm2OeNoTzzdFrP4bxyXYxQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@wyw-in-js/shared": "0.6.0",
+ "@wyw-in-js/transform": "0.6.0"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ },
+ "peerDependencies": {
+ "vite": ">=3.2.7"
+ }
+ },
"node_modules/acorn": {
"version": "8.14.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz",
diff --git a/package.json b/package.json
index 5b47a32..5ea63b7 100644
--- a/package.json
+++ b/package.json
@@ -72,6 +72,7 @@
"@types/react": "^18.3.12",
"@types/react-dom": "^18.3.1",
"@vitejs/plugin-react": "^4.3.4",
+ "@wyw-in-js/vite": "^0.6.0",
"autoprefixer": "^10.4.20",
"eslint": "^9.15.0",
"eslint-plugin-react-hooks": "^5.0.0",
diff --git a/src/App.tsx b/src/App.tsx
index 1995d8c..068a1ac 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -3,6 +3,7 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from './components/CustomTab
import { useGameTick } from './hooks/useGameTick';
import Field from './components/Field';
import Warehouse from './components/Warehouse';
+import { ActionCooldown } from './components/ActionCooldown';
const appContainerStyle: React.CSSProperties = {
maxWidth: '1200px',
@@ -10,21 +11,6 @@ const appContainerStyle: React.CSSProperties = {
padding: '2rem'
};
-const headerStyle: React.CSSProperties = {
- textAlign: 'center',
- marginBottom: '2rem'
-};
-
-const titleStyle: React.CSSProperties = {
- fontSize: '2.25rem',
- fontWeight: 'bold',
- marginBottom: '0.5rem'
-};
-
-const subtitleStyle: React.CSSProperties = {
- color: '#6b7280'
-};
-
const tabsListStyles: React.CSSProperties = {
display: 'grid',
width: '100%',
@@ -35,26 +21,24 @@ function App() {
useGameTick();
return (
-
-
-
-
-
- Fields
- Warehouse
- Market
- Temple
-
-
-
-
-
-
-
-
+
+
+
+
+
+ Fields
+ Warehouse
+ Market
+ Temple
+
+
+
+
+
+
+
+
+
);
}
diff --git a/src/components/ActionCooldown.tsx b/src/components/ActionCooldown.tsx
new file mode 100644
index 0000000..4dbd629
--- /dev/null
+++ b/src/components/ActionCooldown.tsx
@@ -0,0 +1,59 @@
+import { useEffect, useState } from 'react';
+import { styled } from '@linaria/react';
+
+import { useGameStore } from '../store/useGameStore';
+
+const CooldownContainer = styled.div`
+ position: relative;
+ height: 1rem;
+ width: 100%;
+ background-color: #e5e7eb;
+ overflow: hidden;
+`;
+
+const ProgressBar = styled.div<{ progress: string }>`
+ height: 100%;
+ background-color: #3b82f6;
+ width: ${props => props.progress};
+`;
+
+export const ActionCooldown = () => {
+ const { actionCooldown, setActionCooldown } = useGameStore();
+ const [progress, setProgress] = useState(0);
+
+ useEffect(() => {
+ if (actionCooldown <= 0) {
+ setProgress(0);
+ return;
+ }
+
+ const startTime = Date.now();
+ const duration = actionCooldown;
+
+ const updateProgress = () => {
+ const elapsed = Date.now() - startTime;
+ const newProgress = Math.min(100, (elapsed / duration) * 100);
+
+ if (newProgress >= 100) {
+ setProgress(0);
+ setActionCooldown(0);
+ return;
+ }
+
+ setProgress(newProgress);
+ requestAnimationFrame(updateProgress);
+ };
+
+ const animationFrame = requestAnimationFrame(updateProgress);
+
+ return () => {
+ cancelAnimationFrame(animationFrame);
+ };
+ }, [actionCooldown, setActionCooldown]);
+
+ return (
+
+
+
+ );
+};
diff --git a/src/components/Field.tsx b/src/components/Field.tsx
index ddf8955..171b925 100644
--- a/src/components/Field.tsx
+++ b/src/components/Field.tsx
@@ -148,32 +148,12 @@ const FieldComponent: React.FC = () => {
water,
harvest,
assignCrop,
- lastPlantTime,
- lastWaterTime,
- lastHarvestTime,
gameSpeed,
- setGameSpeed
+ setGameSpeed,
+ actionCooldown
} = useGameStore();
const [selectedCrop, setSelectedCrop] = useState
(null);
- const [cooldowns, setCooldowns] = useState({
- plant: false,
- water: false,
- harvest: false
- });
-
- useEffect(() => {
- const interval = setInterval(() => {
- const now = Date.now();
- setCooldowns({
- plant: now - lastPlantTime < 2000,
- water: now - lastWaterTime < 2000,
- harvest: now - lastHarvestTime < 2000
- });
- }, 100);
-
- return () => clearInterval(interval);
- }, [lastPlantTime, lastWaterTime, lastHarvestTime]);
const handlePlotClick = (row: number, col: number) => {
if (selectedCrop) {
@@ -187,6 +167,8 @@ const FieldComponent: React.FC = () => {
return "#92400e";
};
+ const availableSpeeds = [1, 2, 4, 8, 16, 32, 64]
+
return (
Fields
@@ -194,24 +176,15 @@ const FieldComponent: React.FC = () => {
Game Speed:
-
-
-
+ {availableSpeeds.map(speed => (
+
+ ))}
@@ -230,23 +203,23 @@ const FieldComponent: React.FC = () => {
diff --git a/src/hooks/useGameTick.ts b/src/hooks/useGameTick.ts
index 2d3dfd6..5b7f385 100644
--- a/src/hooks/useGameTick.ts
+++ b/src/hooks/useGameTick.ts
@@ -1,26 +1,26 @@
-import { useEffect, useRef } from 'react';
-import { useGameStore } from '../store/useGameStore';
-import { TICK_INTERVAL } from '../constants';
+import { useEffect, useRef } from 'react'
+import { useGameStore } from '../store/useGameStore'
+import { TICK_INTERVAL } from '../constants'
export const useGameTick = () => {
- const tick = useGameStore(state => state.tick);
- const gameSpeed = useGameStore(state => state.gameSpeed);
- const intervalRef = useRef(null);
+ const tick = useGameStore(state => state.tick)
+ const gameSpeed = useGameStore(state => state.gameSpeed)
+ const intervalRef = useRef(null)
useEffect(() => {
if (intervalRef.current !== null) {
- clearInterval(intervalRef.current);
+ clearInterval(intervalRef.current)
}
const adjustedInterval = TICK_INTERVAL / gameSpeed;
intervalRef.current = window.setInterval(() => {
- tick();
- }, adjustedInterval);
+ tick()
+ }, adjustedInterval)
return () => {
if (intervalRef.current !== null) {
- clearInterval(intervalRef.current);
+ clearInterval(intervalRef.current)
}
- };
- }, [tick, gameSpeed]); // Add gameSpeed as dependency
+ }
+ }, [tick, gameSpeed])
};
diff --git a/src/store/useGameStore.ts b/src/store/useGameStore.ts
index 587951f..1144e19 100644
--- a/src/store/useGameStore.ts
+++ b/src/store/useGameStore.ts
@@ -6,7 +6,8 @@ import {
FIELD_UPGRADE_COSTS,
INITIAL_INVENTORY,
CROPS,
- INITIAL_GAME_SPEED
+ INITIAL_GAME_SPEED,
+ COOLDOWN_DURATION
} from '../constants';
import { GameState, PlotState } from '../types';
@@ -26,6 +27,7 @@ export const useGameStore = create void;
upgradeField: () => void;
setGameSpeed: (speed: number) => void;
+ setActionCooldown: (cooldown: number) => void;
}>((set, get) => ({
cash: INITIAL_CASH,
inventory: INITIAL_INVENTORY,
@@ -33,10 +35,8 @@ export const useGameStore = create {
set(produce(state => {
@@ -45,10 +45,9 @@ export const useGameStore = create {
- const now = Date.now();
- const { lastPlantTime, plots, inventory } = get();
+ const { plots, inventory, actionCooldown } = get();
- if (now - lastPlantTime < 2000) {
+ if (actionCooldown > 0) {
return;
}
@@ -68,7 +67,7 @@ export const useGameStore = create {
- const now = Date.now();
- const { lastWaterTime, plots } = get();
+ const { plots, actionCooldown } = get();
- if (now - lastWaterTime < 2000) {
+ if (actionCooldown > 0) {
return;
}
@@ -102,16 +100,15 @@ export const useGameStore = create= 0 && driestCol >= 0) {
set(produce(state => {
state.plots[driestRow][driestCol].moisture = 1;
- state.lastWaterTime = now;
+ state.actionCooldown = COOLDOWN_DURATION;
}));
}
},
harvest: () => {
- const now = Date.now();
- const { lastHarvestTime, plots } = get();
+ const { plots, actionCooldown } = get();
- if (now - lastHarvestTime < 2000) {
+ if (actionCooldown > 0) {
return;
}
@@ -127,7 +124,7 @@ export const useGameStore = create {
state.plots[row][col].current = undefined;
state.inventory[yieldItem] = (state.inventory[yieldItem] || 0) + yieldAmount;
- state.lastHarvestTime = now;
+ state.actionCooldown = COOLDOWN_DURATION;
}));
return;
}
@@ -136,7 +133,15 @@ export const useGameStore = create {
+ const { actionCooldown } = get();
+
set(produce(state => {
+ // Update cooldown
+ if (state.actionCooldown > 0) {
+ state.actionCooldown = Math.max(0, state.actionCooldown - (COOLDOWN_DURATION / 20));
+ }
+
+ // Update plots
state.plots.forEach((row: PlotState[], rowIndex: number) => {
row.forEach((plot: PlotState, colIndex: number) => {
if (!plot.current || plot.current.mature) {
@@ -184,5 +189,11 @@ export const useGameStore = create {
state.gameSpeed = speed;
}));
+ },
+
+ setActionCooldown: (cooldown) => {
+ set(produce(state => {
+ state.actionCooldown = cooldown;
+ }));
}
}));
diff --git a/src/types/index.ts b/src/types/index.ts
index 15424de..3e2efe5 100644
--- a/src/types/index.ts
+++ b/src/types/index.ts
@@ -33,8 +33,6 @@ export interface GameState {
maxFieldSize: number;
fieldUpgradeCosts: number[];
plots: PlotState[][];
- lastPlantTime: number;
- lastWaterTime: number;
- lastHarvestTime: number;
gameSpeed: number;
+ actionCooldown: number;
}
diff --git a/vite.config.ts b/vite.config.ts
index 0f2a00a..55ea0a2 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -1,12 +1,12 @@
import path from "path"
-import react from "@vitejs/plugin-react"
import { defineConfig } from "vite"
-import linaria from "@linaria/vite"
+import react from "@vitejs/plugin-react"
+import wyw from '@wyw-in-js/vite'
export default defineConfig({
plugins: [
react(),
- linaria({
+ wyw({
include: ['**/*.{ts,tsx}'],
babelOptions: {
presets: ['@babel/preset-typescript', '@babel/preset-react'],