Add Week 3 polish and late-game features
VC funding system (seed through IPO with requirements gating), 15 achievements with engine checker, model tuning presets and unlockable sliders, overload policy controls, open-source mechanic with reputation boost, enhanced Recharts analytics (subscriber/reputation/revenue vs expenses charts), M&A acquisition system, sidebar NEW badges on era transitions, tutorial hints, and wired-up settings toggles. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -9,6 +9,7 @@ import type {
|
||||
DataCenter, GpuType, GpuInventory, TrainingJob,
|
||||
ActiveResearch, EventConsequence, OwnedDataset,
|
||||
} from '@ai-tycoon/shared';
|
||||
import type { FundingRoundType, OverloadPolicy, TuningPreset, ModelTuning } from '@ai-tycoon/shared';
|
||||
import {
|
||||
INITIAL_SETTINGS, SAVE_VERSION,
|
||||
INITIAL_ECONOMY, INITIAL_INFRASTRUCTURE, INITIAL_COMPUTE,
|
||||
@@ -16,11 +17,13 @@ import {
|
||||
INITIAL_COMPETITORS, INITIAL_TALENT, INITIAL_DATA,
|
||||
INITIAL_REPUTATION, INITIAL_EVENTS, INITIAL_ACHIEVEMENTS,
|
||||
GPU_CONFIGS,
|
||||
FUNDING_ROUNDS,
|
||||
OPEN_SOURCE_REPUTATION_BOOST,
|
||||
} 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';
|
||||
| 'market' | 'talent' | 'data' | 'competitors' | 'finance' | 'achievements' | 'settings';
|
||||
|
||||
interface UIState {
|
||||
activePage: ActivePage;
|
||||
@@ -54,6 +57,11 @@ interface Actions {
|
||||
resolveEvent: (instanceId: string, choiceIndex: number) => void;
|
||||
hireDepartment: (departmentId: string, count: number) => void;
|
||||
purchaseDataset: (dataset: OwnedDataset, cost: number) => void;
|
||||
raiseFunding: (roundType: FundingRoundType) => void;
|
||||
openSourceModel: (modelId: string) => void;
|
||||
setOverloadPolicy: (policy: Partial<OverloadPolicy>) => void;
|
||||
setModelTuning: (modelId: string, tuning: Partial<ModelTuning>) => void;
|
||||
acquireCompetitor: (competitorId: string) => void;
|
||||
updateState: (partial: Partial<GameState>) => void;
|
||||
}
|
||||
|
||||
@@ -306,6 +314,84 @@ export const useGameStore = create<Store>()(
|
||||
};
|
||||
}),
|
||||
|
||||
raiseFunding: (roundType) => set((s) => {
|
||||
const config = FUNDING_ROUNDS[roundType];
|
||||
if (!config) return s;
|
||||
const amount = config.amount;
|
||||
const dilution = config.dilution;
|
||||
return {
|
||||
economy: {
|
||||
...s.economy,
|
||||
money: s.economy.money + amount,
|
||||
funding: {
|
||||
...s.economy.funding,
|
||||
totalRaised: s.economy.funding.totalRaised + amount,
|
||||
founderEquity: s.economy.funding.founderEquity * (1 - dilution),
|
||||
completedRounds: [
|
||||
...s.economy.funding.completedRounds,
|
||||
{ type: roundType, amount, dilution, completedAtTick: s.meta.tickCount },
|
||||
],
|
||||
isPublic: roundType === 'ipo',
|
||||
},
|
||||
},
|
||||
};
|
||||
}),
|
||||
|
||||
openSourceModel: (modelId) => set((s) => {
|
||||
if (s.market.openSourcedModels.includes(modelId)) return s;
|
||||
return {
|
||||
market: {
|
||||
...s.market,
|
||||
openSourcedModels: [...s.market.openSourcedModels, modelId],
|
||||
},
|
||||
reputation: {
|
||||
...s.reputation,
|
||||
score: Math.min(100, s.reputation.score + OPEN_SOURCE_REPUTATION_BOOST),
|
||||
publicPerception: Math.min(100, s.reputation.publicPerception + OPEN_SOURCE_REPUTATION_BOOST),
|
||||
},
|
||||
};
|
||||
}),
|
||||
|
||||
setOverloadPolicy: (policy) => set((s) => ({
|
||||
market: {
|
||||
...s.market,
|
||||
overloadPolicy: { ...s.market.overloadPolicy, ...policy },
|
||||
},
|
||||
})),
|
||||
|
||||
setModelTuning: (modelId, tuning) => set((s) => ({
|
||||
models: {
|
||||
...s.models,
|
||||
trainedModels: s.models.trainedModels.map(m =>
|
||||
m.id === modelId ? { ...m, tuning: { ...m.tuning, ...tuning } } : m,
|
||||
),
|
||||
},
|
||||
})),
|
||||
|
||||
acquireCompetitor: (competitorId) => set((s) => {
|
||||
const rival = s.competitors.rivals.find(r => r.id === competitorId);
|
||||
if (!rival || rival.status === 'acquired') return s;
|
||||
const cost = rival.estimatedRevenue * 500 + rival.estimatedCapability * 100_000;
|
||||
if (s.economy.money < cost) return s;
|
||||
return {
|
||||
economy: { ...s.economy, money: s.economy.money - cost },
|
||||
competitors: {
|
||||
...s.competitors,
|
||||
rivals: s.competitors.rivals.map(r =>
|
||||
r.id === competitorId ? { ...r, status: 'acquired' as const } : r,
|
||||
),
|
||||
},
|
||||
talent: {
|
||||
...s.talent,
|
||||
departments: {
|
||||
...s.talent.departments,
|
||||
research: { ...s.talent.departments.research, headcount: s.talent.departments.research.headcount + 5 },
|
||||
engineering: { ...s.talent.departments.engineering, headcount: s.talent.departments.engineering.headcount + 3 },
|
||||
},
|
||||
},
|
||||
};
|
||||
}),
|
||||
|
||||
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