all repos — clicker-ts @ main

Unnamed repository; edit this file 'description' to name the repository.

src/App.tsx (view raw)

  1import { useEffect, useState } from 'react';
  2import Header from './components/Header';
  3import ClickArea from './components/ClickArea';
  4import UpgradeShop from './components/UpgradeShop';
  5import PrestigePanel from './components/PrestigePanel';
  6import { GameState } from './types';
  7import { initialUpgrades, initialPrestigeUpgrades } from './data/gameData';
  8import { formatNumber } from './utils/formatters';
  9import { saveGame, loadGame } from './utils/storage';
 10import './App.css';
 11
 12function App() {
 13  const [gameState, setGameState] = useState<GameState>({
 14    currency: 0,
 15    totalCurrency: 0,
 16    clickValue: 1,
 17    passiveIncome: 0,
 18    autoClickRate: 0,
 19    prestigePoints: 0,
 20    prestigeMultiplier: 1,
 21    lastSaveTime: Date.now(),
 22    upgrades: initialUpgrades,
 23    prestigeUpgrades: initialPrestigeUpgrades
 24  });
 25
 26  // Load saved game on startup
 27  useEffect(() => {
 28    const savedGame = loadGame();
 29    if (savedGame) {
 30      // Calculate offline progress
 31      const currentTime = Date.now();
 32      const timeDiff = (currentTime - savedGame.lastSaveTime) / 1000; // in seconds
 33      const offlineEarnings = Math.floor(savedGame.passiveIncome * timeDiff);
 34      
 35      if (offlineEarnings > 0) {
 36        savedGame.currency += offlineEarnings;
 37        savedGame.totalCurrency += offlineEarnings;
 38        alert(`Welcome back! You earned ${formatNumber(offlineEarnings)} while away.`);
 39      }
 40      
 41      savedGame.lastSaveTime = currentTime;
 42      setGameState(savedGame);
 43    }
 44  }, []);
 45
 46  // Save game periodically
 47  useEffect(() => {
 48    const saveInterval = setInterval(() => {
 49      saveGame({...gameState, lastSaveTime: Date.now()});
 50    }, 10000); // Save every 10 seconds
 51    
 52    return () => clearInterval(saveInterval);
 53  }, [gameState]);
 54
 55  // Passive income logic
 56  useEffect(() => {
 57    const incomeInterval = setInterval(() => {
 58      if (gameState.passiveIncome > 0) {
 59        setGameState(prev => ({
 60          ...prev,
 61          currency: prev.currency + prev.passiveIncome,
 62          totalCurrency: prev.totalCurrency + prev.passiveIncome
 63        }));
 64      }
 65    }, 1000); // Every second
 66    
 67    return () => clearInterval(incomeInterval);
 68  }, [gameState.passiveIncome]);
 69
 70  // Auto click logic
 71  useEffect(() => {
 72    if (gameState.autoClickRate <= 0) return;
 73    
 74    const clickInterval = setInterval(() => {
 75      handleCurrencyClick();
 76    }, 1000 / gameState.autoClickRate);
 77    
 78    return () => clearInterval(clickInterval);
 79  }, [gameState.autoClickRate, gameState.clickValue]);
 80
 81  const handleCurrencyClick = () => {
 82    const value = gameState.clickValue * gameState.prestigeMultiplier;
 83    setGameState(prev => ({
 84      ...prev,
 85      currency: prev.currency + value,
 86      totalCurrency: prev.totalCurrency + value
 87    }));
 88  };
 89
 90  const purchaseUpgrade = (upgradeId: string) => {
 91    const upgrade = gameState.upgrades.find(u => u.id === upgradeId);
 92    if (!upgrade || gameState.currency < upgrade.cost) return;
 93
 94    const updatedUpgrades = gameState.upgrades.map(u => {
 95      if (u.id === upgradeId) {
 96        const newLevel = u.level + 1;
 97        const newCost = Math.floor(u.baseCost * Math.pow(u.costMultiplier, newLevel));
 98        
 99        return { ...u, level: newLevel, cost: newCost };
100      }
101      return u;
102    });
103
104    let newClickValue = gameState.clickValue;
105    let newPassiveIncome = gameState.passiveIncome;
106    let newAutoClickRate = gameState.autoClickRate;
107
108    switch (upgrade.type) {
109      case 'clickValue':
110        newClickValue += upgrade.value;
111        break;
112      case 'passive':
113        newPassiveIncome += upgrade.value;
114        break;
115      case 'autoClick':
116        newAutoClickRate += upgrade.value;
117        break;
118    }
119
120    setGameState(prev => ({
121      ...prev,
122      currency: prev.currency - upgrade.cost,
123      clickValue: newClickValue,
124      passiveIncome: newPassiveIncome,
125      autoClickRate: newAutoClickRate,
126      upgrades: updatedUpgrades
127    }));
128  };
129
130  const purchasePrestigeUpgrade = (upgradeId: string) => {
131    const upgrade = gameState.prestigeUpgrades.find(u => u.id === upgradeId);
132    if (!upgrade || gameState.prestigePoints < upgrade.cost || upgrade.purchased) return;
133
134    const updatedUpgrades = gameState.prestigeUpgrades.map(u => {
135      if (u.id === upgradeId) {
136        return { ...u, purchased: true };
137      }
138      return u;
139    });
140
141    let newPrestigeMultiplier = gameState.prestigeMultiplier;
142    newPrestigeMultiplier += upgrade.multiplierBonus;
143
144    setGameState(prev => ({
145      ...prev,
146      prestigePoints: prev.prestigePoints - upgrade.cost,
147      prestigeMultiplier: newPrestigeMultiplier,
148      prestigeUpgrades: updatedUpgrades
149    }));
150  };
151
152  const performPrestige = () => {
153    // Calculate prestige points based on total currency earned
154    const newPrestigePoints = Math.floor(Math.sqrt(gameState.totalCurrency / 1000000));
155    
156    if (newPrestigePoints <= 0) {
157      alert("You need to earn more currency before prestiging!");
158      return;
159    }
160
161    if (!window.confirm(`Are you sure you want to prestige? You'll earn ${newPrestigePoints} prestige points but will lose all progress except prestige upgrades.`)) {
162      return;
163    }
164
165    // Reset game but keep prestige upgrades and add prestige points
166    setGameState(prev => ({
167      ...prev,
168      currency: 0,
169      totalCurrency: 0,
170      clickValue: 1,
171      passiveIncome: 0,
172      autoClickRate: 0,
173      prestigePoints: prev.prestigePoints + newPrestigePoints,
174      lastSaveTime: Date.now(),
175      upgrades: initialUpgrades
176      // Note: prestigeUpgrades and prestigeMultiplier are kept
177    }));
178  };
179
180  return (
181    <div className="app-container">
182      <Header 
183        currency={gameState.currency} 
184        clickValue={gameState.clickValue * gameState.prestigeMultiplier}
185        passiveIncome={gameState.passiveIncome}
186        autoClickRate={gameState.autoClickRate}
187        prestigePoints={gameState.prestigePoints}
188        prestigeMultiplier={gameState.prestigeMultiplier}
189      />
190      
191      <main>
192        <ClickArea onCurrencyClick={handleCurrencyClick} />
193        
194        <div className="game-sections">
195          <UpgradeShop 
196            upgrades={gameState.upgrades} 
197            currency={gameState.currency}
198            onPurchase={purchaseUpgrade}
199            prestigeMultiplier={gameState.prestigeMultiplier}
200          />
201          
202          <PrestigePanel
203            totalCurrency={gameState.totalCurrency}
204            prestigePoints={gameState.prestigePoints}
205            prestigeUpgrades={gameState.prestigeUpgrades}
206            onPrestige={performPrestige}
207            onPurchaseUpgrade={purchasePrestigeUpgrade}
208          />
209        </div>
210      </main>
211    </div>
212  );
213}
214
215export default App;