8c9555bc08
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>
85 lines
3.2 KiB
TypeScript
85 lines
3.2 KiB
TypeScript
import { AlertTriangle, Newspaper, Building2, Users, TrendingUp, X } from 'lucide-react';
|
|
import { useGameStore } from '@/store';
|
|
import type { ActiveEvent, EventCategory } from '@ai-tycoon/shared';
|
|
|
|
const CATEGORY_ICONS: Record<EventCategory, typeof AlertTriangle> = {
|
|
industry: Newspaper,
|
|
regulatory: Building2,
|
|
pr: Users,
|
|
internal: AlertTriangle,
|
|
market: TrendingUp,
|
|
};
|
|
|
|
const CATEGORY_COLORS: Record<EventCategory, string> = {
|
|
industry: 'border-blue-500/50 bg-blue-500/5',
|
|
regulatory: 'border-yellow-500/50 bg-yellow-500/5',
|
|
pr: 'border-purple-500/50 bg-purple-500/5',
|
|
internal: 'border-orange-500/50 bg-orange-500/5',
|
|
market: 'border-green-500/50 bg-green-500/5',
|
|
};
|
|
|
|
export function EventModal() {
|
|
const activeEvents = useGameStore((s) => s.events.activeEvents);
|
|
const resolveEvent = useGameStore((s) => s.resolveEvent);
|
|
|
|
if (activeEvents.length === 0) return null;
|
|
|
|
const event = activeEvents[0];
|
|
const Icon = CATEGORY_ICONS[event.category];
|
|
|
|
return (
|
|
<div className="fixed inset-0 bg-black/60 flex items-center justify-center z-50 p-4">
|
|
<div className={`bg-surface-900 border-2 rounded-xl max-w-lg w-full shadow-2xl ${CATEGORY_COLORS[event.category]}`}>
|
|
<div className="p-5">
|
|
<div className="flex items-start justify-between mb-3">
|
|
<div className="flex items-center gap-2">
|
|
<Icon size={20} className="text-accent-light" />
|
|
<h3 className="text-lg font-bold">{event.title}</h3>
|
|
</div>
|
|
<span className="text-xs text-surface-400 uppercase">{event.category}</span>
|
|
</div>
|
|
|
|
<p className="text-sm text-surface-300 mb-5 leading-relaxed">{event.description}</p>
|
|
|
|
<div className="space-y-2">
|
|
{event.choices.map((choice, idx) => (
|
|
<button
|
|
key={idx}
|
|
onClick={() => resolveEvent(event.instanceId, idx)}
|
|
className="w-full text-left bg-surface-800 hover:bg-surface-700 border border-surface-600 hover:border-surface-500 rounded-lg p-3 transition-all group"
|
|
>
|
|
<div className="text-sm font-medium group-hover:text-accent-light transition-colors">
|
|
{choice.label}
|
|
</div>
|
|
<div className="text-xs text-surface-400 mt-1">{choice.description}</div>
|
|
<div className="flex gap-2 mt-2">
|
|
{choice.consequences.map((c, i) => (
|
|
<ConsequenceTag key={i} type={c.type} value={c.value} />
|
|
))}
|
|
</div>
|
|
</button>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function ConsequenceTag({ type, value }: { type: string; value: number }) {
|
|
const isPositive = value > 0;
|
|
const label = type === 'money' ? `$${Math.abs(value).toLocaleString()}`
|
|
: type === 'reputation' ? `${Math.abs(value)} rep`
|
|
: type === 'talent' ? `${Math.abs(value)} talent`
|
|
: type === 'research_speed' ? `${Math.round(Math.abs(value) * 100)}% R&D`
|
|
: `${type}: ${value}`;
|
|
|
|
return (
|
|
<span className={`text-[10px] px-1.5 py-0.5 rounded ${
|
|
isPositive ? 'bg-success/20 text-success' : 'bg-danger/20 text-danger'
|
|
}`}>
|
|
{isPositive ? '+' : '-'}{label}
|
|
</span>
|
|
);
|
|
}
|