// Production - The Idle Game: State Container & Persistence import { PRODUCTS } from '../data/products.js'; import { BOARDROOM_PERKS, calculateSharesEarned } from '../data/boardroom.js'; import { CONTRACT_TEMPLATES } from '../data/contracts.js'; import { ACHIEVEMENTS } from '../data/achievements.js'; const STORAGE_KEY = 'production_the_idle_game_save_v1'; export function getInitialState() { const state = { version: 1, companyName: 'Apex Production Industries', cash: 30.0, lifetimeRevenue: 0.0, valuation: 30.0, ventureShares: 0, totalVentureShares: 0, ipoCount: 0, reputation: 0, buyMode: '1', activeTab: 'production', selectedSector: 'all', selectedProductId: 'wheat', stats: { totalProduced: 0, contractsCompleted: 0, managersHired: 0 }, products: {}, boardroomPerks: {}, achievements: {}, activeContracts: [], lastTick: Date.now() }; // Initialize all products PRODUCTS.forEach(p => { state.products[p.id] = { unlocked: p.unlockedDefault, stock: 0.0, lines: p.unlockedDefault ? 1 : 0, manager: false, logistics: p.unlockedDefault ? 1 : 0, cycleProgress: 0.0, isBottlenecked: false, totalProduced: 0.0 }; }); // Initialize boardroom perks BOARDROOM_PERKS.forEach(perk => { state.boardroomPerks[perk.id] = 0; }); // Initialize achievements ACHIEVEMENTS.forEach(ach => { state.achievements[ach.id] = 0; }); // Generate initial starting contracts state.activeContracts = [ { uid: 'c_start_1', templateId: CONTRACT_TEMPLATES[0].id, productId: CONTRACT_TEMPLATES[0].productId, title: CONTRACT_TEMPLATES[0].title, clientId: CONTRACT_TEMPLATES[0].clientId, targetAmount: CONTRACT_TEMPLATES[0].requiredAmount, remainingSeconds: CONTRACT_TEMPLATES[0].timeLimit, rewardCash: CONTRACT_TEMPLATES[0].rewardCash, rewardRep: CONTRACT_TEMPLATES[0].rewardRep } ]; return state; } export class StateManager { constructor() { this.state = this.load(); this.listeners = new Set(); } subscribe(fn) { this.listeners.add(fn); return () => this.listeners.delete(fn); } notify(changedKeys = []) { for (const fn of this.listeners) { fn(this.state, changedKeys); } } load() { try { const raw = localStorage.getItem(STORAGE_KEY); if (!raw) return getInitialState(); const parsed = JSON.parse(raw); const fresh = getInitialState(); // Merge safely to preserve schema additions const merged = { ...fresh, ...parsed }; merged.products = { ...fresh.products, ...(parsed.products || {}) }; merged.boardroomPerks = { ...fresh.boardroomPerks, ...(parsed.boardroomPerks || {}) }; merged.achievements = { ...fresh.achievements, ...(parsed.achievements || {}) }; merged.stats = { ...fresh.stats, ...(parsed.stats || {}) }; if (!Array.isArray(merged.activeContracts) || merged.activeContracts.length === 0) { merged.activeContracts = fresh.activeContracts; } return merged; } catch (err) { console.error('Failed to load local state, fallback to fresh:', err); return getInitialState(); } } save() { try { localStorage.setItem(STORAGE_KEY, JSON.stringify(this.state)); } catch (err) { console.error('Failed to save state to localStorage:', err); } } reset() { this.state = getInitialState(); this.save(); this.notify(); } performIPO() { const earned = calculateSharesEarned(this.state.lifetimeRevenue); if (earned <= 0) return false; const startingBonusPerk = this.state.boardroomPerks['angel_syndicate'] || 0; const startingCashBonuses = [30.0, 500.0, 5000.0, 50000.0, 500000.0, 5000000.0]; const newStartingCash = startingCashBonuses[Math.min(startingBonusPerk, startingCashBonuses.length - 1)]; const preservedShares = this.state.ventureShares + earned; const preservedTotalShares = this.state.totalVentureShares + earned; const preservedPerks = { ...this.state.boardroomPerks }; const preservedIpoCount = this.state.ipoCount + 1; const preservedAchievements = { ...this.state.achievements }; // Fresh state this.state = getInitialState(); this.state.cash = newStartingCash; this.state.ventureShares = preservedShares; this.state.totalVentureShares = preservedTotalShares; this.state.ipoCount = preservedIpoCount; this.state.boardroomPerks = preservedPerks; this.state.achievements = preservedAchievements; this.save(); this.notify(); return true; } } export const GState = new StateManager();