Add complete game loop: training, revenue, market, offline catch-up

- Model training system: training jobs produce TrainedModels with
  calculated capabilities based on compute, data, and research
- Market system: organic API demand and consumer subscriptions now
  generate real revenue from deployed models
- Talent system: salary costs calculated from department headcount
- Toast notification system for game events (training complete, etc.)
- Offline catch-up: progress bar + summary screen when returning
- Market page: pricing controls for API and subscription products
- Finance page: income statement, cash charts, funding history
- Tick processor now runs all 7 systems in correct dependency order

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-24 17:02:58 -04:00
parent fdc8e544ae
commit 9a48c188ad
12 changed files with 757 additions and 21 deletions
+28 -1
View File
@@ -1,15 +1,42 @@
import { useState, useEffect } from 'react';
import { useGameStore } from '@/store';
import { MainLayout } from '@/components/layout/MainLayout';
import { NewGameScreen } from '@/components/game/NewGameScreen';
import { OfflineCatchUp } from '@/components/game/OfflineCatchUp';
import { useGameLoop } from '@/hooks/useGameLoop';
import { TICK_INTERVAL_MS } from '@ai-tycoon/shared';
export function App() {
const companyName = useGameStore((s) => s.meta.companyName);
useGameLoop();
const lastTickTimestamp = useGameStore((s) => s.meta.lastTickTimestamp);
const [catchUpTicks, setCatchUpTicks] = useState<number | null>(null);
const [catchUpDone, setCatchUpDone] = useState(false);
useEffect(() => {
if (!companyName || catchUpDone) return;
const elapsed = Date.now() - lastTickTimestamp;
const missed = Math.floor(elapsed / TICK_INTERVAL_MS);
if (missed > 10) {
setCatchUpTicks(missed);
} else {
setCatchUpDone(true);
}
}, [companyName, lastTickTimestamp, catchUpDone]);
useGameLoop(!catchUpDone);
if (!companyName) {
return <NewGameScreen />;
}
if (catchUpTicks !== null && !catchUpDone) {
return (
<OfflineCatchUp
missedTicks={catchUpTicks}
onComplete={() => setCatchUpDone(true)}
/>
);
}
return <MainLayout />;
}