Files
AIHostingTycoon/apps/web/src/hooks/useGameLoop.ts
T
josh 8c9555bc08 Add Week 2 depth systems: research, events, competitors, talent, data
Tech tree with 21 research nodes across 5 categories (infrastructure,
efficiency, generation, specialization, safety). Research page with
category-grouped cards, progress tracking, prerequisite gating.

Event engine with 34 events across industry/regulatory/PR/internal/market
categories, weighted random firing, cooldowns, expiry, and choice modal
with consequence preview. Events auto-expire with default choice.

Competitor system with 3 rival AI labs (Prometheus AI, Nexus Labs, Titan
Computing), personality-driven milestone progression, and comparison UI.

Talent page with department hiring, headcount management, and key hire
recruitment from a pool of 10 named characters with special abilities.

Data marketplace with 8 purchasable datasets, user data flywheel from
subscribers, and data system processing in tick loop.

Era transition system checks revenue/capability/reputation thresholds.
All new systems integrated into tick processor with notifications.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-24 17:30:24 -04:00

70 lines
2.0 KiB
TypeScript

import { useEffect, useRef } from 'react';
import { GameEngine, setEventDefinitions, EVENT_DEFINITIONS } from '@ai-tycoon/game-engine';
import type { TickNotification } from '@ai-tycoon/game-engine';
import { useGameStore } from '@/store';
export function useGameLoop(skip = false) {
const engineRef = useRef<GameEngine | null>(null);
const companyName = useGameStore((s) => s.meta.companyName);
const gameSpeed = useGameStore((s) => s.meta.gameSpeed);
useEffect(() => {
if (!companyName || skip) return;
setEventDefinitions(EVENT_DEFINITIONS);
const engine = new GameEngine({
getState: () => {
const state = useGameStore.getState();
return {
meta: state.meta,
economy: state.economy,
infrastructure: state.infrastructure,
compute: state.compute,
research: state.research,
models: state.models,
market: state.market,
competitors: state.competitors,
talent: state.talent,
data: state.data,
reputation: state.reputation,
events: state.events,
achievements: state.achievements,
};
},
setState: (partial) => {
const notifications = (partial as Record<string, unknown>)['_notifications'] as TickNotification[] | undefined;
delete (partial as Record<string, unknown>)['_notifications'];
useGameStore.getState().updateState(partial);
if (notifications?.length) {
const store = useGameStore.getState();
for (const n of notifications) {
store.addNotification({
title: n.title,
message: n.message,
type: n.type,
tick: store.meta.tickCount,
});
}
}
},
});
engineRef.current = engine;
engine.start();
return () => {
engine.stop();
engineRef.current = null;
};
}, [companyName, skip]);
useEffect(() => {
if (engineRef.current) {
engineRef.current.setSpeed(gameSpeed);
}
}, [gameSpeed]);
}