ProductionGame/frontend/js/core/state.js

165 lines
5.2 KiB
JavaScript

// 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,
logisticsEnabled: true,
supplyDownstreamEnabled: true,
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();