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>
This commit is contained in:
@@ -7,6 +7,7 @@ import type {
|
||||
CompetitorState, TalentState, DataState,
|
||||
ReputationState, EventState, AchievementState,
|
||||
DataCenter, GpuType, GpuInventory, TrainingJob,
|
||||
ActiveResearch, EventConsequence, OwnedDataset,
|
||||
} from '@ai-tycoon/shared';
|
||||
import {
|
||||
INITIAL_SETTINGS, SAVE_VERSION,
|
||||
@@ -16,6 +17,7 @@ import {
|
||||
INITIAL_REPUTATION, INITIAL_EVENTS, INITIAL_ACHIEVEMENTS,
|
||||
GPU_CONFIGS,
|
||||
} from '@ai-tycoon/shared';
|
||||
import { INITIAL_RIVALS } from '@ai-tycoon/game-engine';
|
||||
|
||||
export type ActivePage = 'dashboard' | 'infrastructure' | 'research' | 'models'
|
||||
| 'market' | 'talent' | 'data' | 'competitors' | 'finance' | 'settings';
|
||||
@@ -48,6 +50,10 @@ interface Actions {
|
||||
deployModel: (modelId: string) => void;
|
||||
setProductPricing: (productLineId: string, field: string, value: number) => void;
|
||||
toggleProductLine: (productLineId: string) => void;
|
||||
startResearch: (research: ActiveResearch) => void;
|
||||
resolveEvent: (instanceId: string, choiceIndex: number) => void;
|
||||
hireDepartment: (departmentId: string, count: number) => void;
|
||||
purchaseDataset: (dataset: OwnedDataset, cost: number) => void;
|
||||
updateState: (partial: Partial<GameState>) => void;
|
||||
}
|
||||
|
||||
@@ -111,6 +117,10 @@ export const useGameStore = create<Store>()(
|
||||
createdAt: Date.now(),
|
||||
lastTickTimestamp: Date.now(),
|
||||
},
|
||||
competitors: {
|
||||
rivals: INITIAL_RIVALS,
|
||||
industryBenchmark: 0,
|
||||
},
|
||||
activePage: 'dashboard',
|
||||
notifications: [],
|
||||
}),
|
||||
@@ -218,6 +228,84 @@ export const useGameStore = create<Store>()(
|
||||
},
|
||||
})),
|
||||
|
||||
startResearch: (research) => set((s) => {
|
||||
if (s.research.activeResearch) return s;
|
||||
return {
|
||||
research: { ...s.research, activeResearch: research },
|
||||
};
|
||||
}),
|
||||
|
||||
resolveEvent: (instanceId, choiceIndex) => set((s) => {
|
||||
const event = s.events.activeEvents.find(e => e.instanceId === instanceId);
|
||||
if (!event) return s;
|
||||
|
||||
const choice = event.choices[choiceIndex];
|
||||
if (!choice) return s;
|
||||
|
||||
let money = s.economy.money;
|
||||
let reputation = { ...s.reputation };
|
||||
const consequences = choice.consequences;
|
||||
|
||||
for (const c of consequences) {
|
||||
switch (c.type) {
|
||||
case 'money': money += c.value; break;
|
||||
case 'reputation': reputation = { ...reputation, score: Math.min(100, Math.max(0, reputation.score + c.value)), publicPerception: Math.min(100, Math.max(0, reputation.publicPerception + c.value)) }; break;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
economy: { ...s.economy, money: Math.max(0, money) },
|
||||
reputation,
|
||||
events: {
|
||||
...s.events,
|
||||
activeEvents: s.events.activeEvents.filter(e => e.instanceId !== instanceId),
|
||||
eventHistory: [
|
||||
...s.events.eventHistory,
|
||||
{
|
||||
eventId: event.eventId,
|
||||
instanceId,
|
||||
title: event.title,
|
||||
category: event.category,
|
||||
tick: s.meta.tickCount,
|
||||
chosenOptionIndex: choiceIndex,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
}),
|
||||
|
||||
hireDepartment: (departmentId, count) => set((s) => {
|
||||
const costPerHire = 2000;
|
||||
const totalCost = costPerHire * count;
|
||||
if (s.economy.money < totalCost) return s;
|
||||
|
||||
return {
|
||||
economy: { ...s.economy, money: s.economy.money - totalCost },
|
||||
talent: {
|
||||
...s.talent,
|
||||
departments: {
|
||||
...s.talent.departments,
|
||||
[departmentId]: {
|
||||
...s.talent.departments[departmentId as keyof typeof s.talent.departments],
|
||||
headcount: s.talent.departments[departmentId as keyof typeof s.talent.departments].headcount + count,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}),
|
||||
|
||||
purchaseDataset: (dataset, cost) => set((s) => {
|
||||
if (s.economy.money < cost) return s;
|
||||
return {
|
||||
economy: { ...s.economy, money: s.economy.money - cost },
|
||||
data: {
|
||||
...s.data,
|
||||
ownedDatasets: [...s.data.ownedDatasets, dataset],
|
||||
totalTrainingTokens: s.data.totalTrainingTokens + dataset.sizeTokens,
|
||||
},
|
||||
};
|
||||
}),
|
||||
|
||||
updateState: (partial) => set((s) => {
|
||||
const newState: Partial<Store> = {};
|
||||
for (const key of Object.keys(partial) as (keyof GameState)[]) {
|
||||
|
||||
Reference in New Issue
Block a user