import { create } from 'zustand' import { persist } from 'zustand/middleware' import { playPurchaseSound, playLevelUpSound, playUpgradeSound } from '../utils/soundUtils' // Building information interface export interface BuildingInfo { id?: string cost: number levelRequirement: number title: string description: string production: { points?: number techParts?: number } } interface GameState { // Resources points: number // Buildings mouseFarms: number keyboardFactories: number monitorDisplays: number officeSpace: number serverRooms: number dataCenters: number dataCities: number dataCountries: number dataContinents: number dataWorlds: number dataMoons: number dataSolarSystems: number dataGalaxies: number dataUniverses: number dataGods: number // Building Levels mouseFarmsLevel: number keyboardFactoriesLevel: number monitorDisplaysLevel: number officeSpaceLevel: number serverRoomsLevel: number dataCentersLevel: number dataCitiesLevel: number dataCountriesLevel: number dataContinentsLevel: number dataWorldsLevel: number dataMoonsLevel: number dataSolarSystemsLevel: number dataGalaxiesLevel: number dataUniversesLevel: number dataGodsLevel: number // Upgrades autoClickers: number clickPower: number clickPowerUpgrades: number // Production rates pointsPerSecond: number // Player level playerLevel: number // Actions click: () => void buyBuilding: (buildingType: BuildingType) => void buyUpgrade: (upgradeType: UpgradeType) => void resetGame: () => void tick: () => void upgradeBuilding: (buildingType: BuildingType) => void getClickPowerUpgradeCost: () => number canUpgradeBuilding: (buildingType: BuildingType) => boolean getBuildingUpgradeCost: (buildingType: BuildingType) => number // New getter for available buildings getAvailableBuildings: () => BuildingInfo[] } // Define tiers based on level requirements for buildings type BuildingTier = 'early' | 'mid' | 'late'; // Building information with costs and requirements export const BUILDING_INFO: Record> = { mouseFarms: { title: 'Mouse Farm', description: 'A basic facility that produces computer mice. Each mouse farm generates points automatically.', production: { points: 0.1 }, cost: 10, levelRequirement: 1 }, keyboardFactories: { title: 'Keyboard Factory', description: 'Manufactures mechanical keyboards and generates tech parts. Essential for advanced upgrades.', production: { points: 0.2 }, cost: 50, levelRequirement: 1 }, monitorDisplays: { title: 'Monitor Display', description: 'High-resolution displays that generate both points and tech parts. A balanced production facility.', production: { points: 0.2 }, cost: 100, levelRequirement: 1 }, serverRooms: { title: 'Server Room', description: 'A powerful facility that generates significant amounts of both resources. Requires proper cooling.', production: { points: 5 }, cost: 1000, levelRequirement: 1 }, dataCenters: { title: 'Data Center', description: 'The ultimate production facility. Generates massive amounts of resources but requires significant investment.', production: { points: 25 }, cost: 5000, levelRequirement: 5 }, dataCities: { title: 'Data City', description: 'A massive network of data centers spanning an entire city. Requires level 10 to unlock.', production: { points: 100 }, cost: 25000, levelRequirement: 10 }, dataCountries: { title: 'Data Country', description: 'A country-wide network of data cities. Requires level 20 to unlock.', production: { points: 500 }, cost: 100000, levelRequirement: 20 }, dataContinents: { title: 'Data Continent', description: 'A continent-spanning network of data countries. Requires level 30 to unlock.', production: { points: 2500 }, cost: 500000, levelRequirement: 30 }, dataWorlds: { title: 'Data World', description: 'A world-wide network of data continents. Requires level 40 to unlock.', production: { points: 10000 }, cost: 2000000, levelRequirement: 40 }, dataMoons: { title: 'Data Moon', description: 'A moon-sized data processing facility. Requires level 50 to unlock.', production: { points: 50000 }, cost: 10000000, levelRequirement: 50 }, dataSolarSystems: { title: 'Data Solar System', description: 'A solar system-wide network of data moons. Requires level 60 to unlock.', production: { points: 250000 }, cost: 50000000, levelRequirement: 60 }, dataGalaxies: { title: 'Data Galaxy', description: 'A galaxy-spanning network of data solar systems. Requires level 70 to unlock.', production: { points: 1000000 }, cost: 200000000, levelRequirement: 70 }, dataUniverses: { title: 'Data Universe', description: 'A universe-wide network of data galaxies. Requires level 80 to unlock.', production: { points: 5000000 }, cost: 1000000000, levelRequirement: 80 }, dataGods: { title: 'Data God', description: 'The ultimate data processing entity. Requires level 90 to unlock.', production: { points: 25000000 }, cost: 5000000000, levelRequirement: 90 } } const initialState = { points: 0, mouseFarms: 0, keyboardFactories: 0, monitorDisplays: 0, officeSpace: 0, serverRooms: 0, dataCenters: 0, dataCities: 0, dataCountries: 0, dataContinents: 0, dataWorlds: 0, dataMoons: 0, dataSolarSystems: 0, dataGalaxies: 0, dataUniverses: 0, dataGods: 0, mouseFarmsLevel: 1, keyboardFactoriesLevel: 1, monitorDisplaysLevel: 1, officeSpaceLevel: 1, serverRoomsLevel: 1, dataCentersLevel: 1, dataCitiesLevel: 1, dataCountriesLevel: 1, dataContinentsLevel: 1, dataWorldsLevel: 1, dataMoonsLevel: 1, dataSolarSystemsLevel: 1, dataGalaxiesLevel: 1, dataUniversesLevel: 1, dataGodsLevel: 1, autoClickers: 0, clickPower: 1, clickPowerUpgrades: 0, pointsPerSecond: 0, playerLevel: 1, } // Production rates per building const PRODUCTION_RATES = { mouseFarms: { points: 0.1 }, keyboardFactories: { points: 0.2 }, monitorDisplays: { points: 0.2 }, officeSpace: { points: 1 }, serverRooms: { points: 5 }, dataCenters: { points: 25 }, dataCities: { points: 100 }, dataCountries: { points: 500 }, dataContinents: { points: 2500 }, dataWorlds: { points: 10000 }, dataMoons: { points: 50000 }, dataSolarSystems: { points: 250000 }, dataGalaxies: { points: 1000000 }, dataUniverses: { points: 5000000 }, dataGods: { points: 25000000 }, } // Building costs const BUILDING_COSTS = { mouseFarms: 10, keyboardFactories: 50, monitorDisplays: 100, officeSpace: 500, serverRooms: 1000, dataCenters: 5000, dataCities: 25000, dataCountries: 100000, dataContinents: 500000, dataWorlds: 2000000, dataMoons: 10000000, dataSolarSystems: 50000000, dataGalaxies: 200000000, dataUniverses: 1000000000, dataGods: 5000000000, } // Building level requirements const BUILDING_LEVEL_REQUIREMENTS = { mouseFarms: 1, keyboardFactories: 1, monitorDisplays: 1, officeSpace: 1, serverRooms: 1, dataCenters: 5, dataCities: 10, dataCountries: 20, dataContinents: 30, dataWorlds: 40, dataMoons: 50, dataSolarSystems: 60, dataGalaxies: 70, dataUniverses: 80, dataGods: 90, } // Helper function to determine building tier based on level requirement const getBuildingTier = (buildingType: BuildingType): BuildingTier => { const levelRequirement = BUILDING_LEVEL_REQUIREMENTS[buildingType]; if (levelRequirement <= 5) { return 'early'; } else if (levelRequirement <= 50) { return 'mid'; } else { return 'late'; } } // Get tier-based upgrade cost multiplier const getTierMultiplier = (tier: BuildingTier): number => { switch (tier) { case 'early': return 1.75; // Steeper curve for early buildings case 'mid': return 1.6; // Medium curve for mid-game case 'late': return 1.5; // Unchanged for late game } } // Get tier-based production scaling power const getTierScalingPower = (tier: BuildingTier): number => { switch (tier) { case 'early': return 0.9; // Sub-linear scaling for early buildings case 'mid': return 0.95; // Slightly sub-linear for mid-game case 'late': return 1; // Linear scaling for late game } } // Calculate upgrade cost based on current level and building tier const calculateUpgradeCost = (buildingType: BuildingType, currentLevel: number): number => { const baseCost = BUILDING_COSTS[buildingType] const tier = getBuildingTier(buildingType) const multiplier = getTierMultiplier(tier) // Formula: baseCost * multiplier^(currentLevel - 1) // This ensures costs scale exponentially with level const cost = Math.floor(baseCost * Math.pow(multiplier, currentLevel - 1)) // For debugging console.log(`Upgrade cost for ${buildingType} at level ${currentLevel}: ${cost} points, tier: ${tier}, multiplier: ${multiplier}`) return cost } // Calculate building production based on level, count, and tier const calculateProduction = (buildingType: BuildingType, level: number, count: number): number => { const baseRate = PRODUCTION_RATES[buildingType].points || 0 const tier = getBuildingTier(buildingType) const scalingPower = getTierScalingPower(tier) // Apply tier-based scaling to level const effectiveLevel = Math.pow(level, scalingPower) return baseRate * effectiveLevel * count } // Helper function to calculate click power upgrade cost const calculateClickPowerUpgradeCost = (upgrades: number): number => { return Math.floor(20 * Math.pow(1.5, upgrades)); } type BuildingType = keyof typeof BUILDING_COSTS type UpgradeType = 'autoClickers' | 'clickPower' // Helper function to get available buildings const getAvailableBuildings = (playerLevel: number): BuildingInfo[] => { return Object.entries(BUILDING_INFO) .filter(([_, building]) => building.levelRequirement <= playerLevel) .map(([id, building]) => ({ ...building, id })) .sort((a, b) => a.cost - b.cost) } export const useGameStore = create()( persist( (set, get) => ({ ...initialState, click: () => { const { clickPower, autoClickers } = get() const pointsPerClick = clickPower * (1 + autoClickers * 0.1) set((state) => ({ points: state.points + pointsPerClick, })) }, buyBuilding: (buildingType: BuildingType) => { const state = get() const cost = BUILDING_COSTS[buildingType] // Get building info for level requirement check const info = BUILDING_INFO[buildingType]; // Check if player meets level requirement if (state.playerLevel < info.levelRequirement) { return; } if (state.points >= cost) { // Play purchase sound playPurchaseSound() set((state) => { const newCount = state[buildingType] + 1 const level = state[`${buildingType}Level` as keyof GameState] as number // Use the new calculateProduction function const newProduction = calculateProduction(buildingType, level, newCount) const oldProduction = calculateProduction(buildingType, level, newCount - 1) const productionDifference = newProduction - oldProduction return { points: state.points - cost, [buildingType]: newCount, pointsPerSecond: state.pointsPerSecond + productionDifference, } }) } }, buyUpgrade: (type: UpgradeType) => { const state = get(); if (type === 'clickPower') { const cost = get().getClickPowerUpgradeCost(); // Check if we have enough points if (state.points < cost) return; // Apply the upgrade set({ points: state.points - cost, clickPower: state.clickPower + 1, clickPowerUpgrades: state.clickPowerUpgrades + 1 }); // Play upgrade sound instead of purchase sound playUpgradeSound(); } }, getClickPowerUpgradeCost: () => { const { clickPowerUpgrades } = get() return calculateClickPowerUpgradeCost(clickPowerUpgrades) }, getBuildingUpgradeCost: (buildingType: BuildingType) => { const state = get() const currentLevel = state[`${buildingType}Level` as keyof GameState] as number return calculateUpgradeCost(buildingType, currentLevel) }, canUpgradeBuilding: (buildingType: BuildingType) => { const state = get() // Check if player owns at least one building if (state[buildingType] <= 0) { return false } // Get current level and calculate upgrade cost const currentLevel = state[`${buildingType}Level` as keyof GameState] as number const cost = calculateUpgradeCost(buildingType, currentLevel) // Check if player has enough points return state.points >= cost }, upgradeBuilding: (buildingType: BuildingType) => { const state = get() const currentLevel = state[`${buildingType}Level` as keyof GameState] as number const cost = calculateUpgradeCost(buildingType, currentLevel) const ownedCount = state[buildingType] console.log(`Attempting to upgrade ${buildingType}: - Current level: ${currentLevel} - Upgrade cost: ${cost} - Player points: ${state.points} - Buildings owned: ${ownedCount} - Can afford: ${state.points >= cost} - Owns building: ${ownedCount > 0} `) if (state.points >= cost && state[buildingType] > 0) { // Play upgrade sound instead of purchase sound playUpgradeSound() set((state) => { const newLevel = (state[`${buildingType}Level` as keyof GameState] as number) + 1 const count = state[buildingType] // Use the new calculateProduction function for both old and new production const oldProduction = calculateProduction(buildingType, currentLevel, count) const newProduction = calculateProduction(buildingType, newLevel, count) const productionDifference = newProduction - oldProduction console.log(`Upgrade successful! New level: ${newLevel}, Production increase: +${productionDifference}/s`) return { points: state.points - cost, [`${buildingType}Level`]: newLevel, pointsPerSecond: state.pointsPerSecond + productionDifference, } }) } else { // Log why upgrade failed console.warn(`Upgrade failed! ${state.points < cost ? 'Not enough points' : 'No buildings owned'}`) } }, resetGame: () => { set(initialState) }, tick: () => { const state = get() // Add production from buildings if (state.pointsPerSecond > 0) { set((state) => ({ points: state.points + state.pointsPerSecond / 10, // 10 ticks per second })) } // Update player level based on points per second const currentLevel = state.playerLevel const newLevel = Math.max(1, Math.floor(Math.log10(state.pointsPerSecond) + 1)) if (newLevel !== currentLevel) { // If level has increased, play the level up sound if (newLevel > currentLevel) { playLevelUpSound() } set({ playerLevel: newLevel }) } }, getAvailableBuildings: () => { const { playerLevel } = get() return getAvailableBuildings(playerLevel) }, }), { name: 'clicker-game', } ) )