Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import React, { useState, useEffect } from 'react';
- import { Music, Star, Users, Trophy, Timer, DollarSign, Globe } from 'lucide-react';
- const EurovisionSimulator = () => {
- const [gameState, setGameState] = useState({
- phase: 'selection', // selection, preparation, competition
- budget: 1000000, // 1 million SEK
- publicSupport: 50,
- jurySupport: 50,
- internationalAppeal: 30,
- mediaAttention: 20,
- week: 1,
- maxWeeks: 12,
- finalScore: 0,
- placement: 0
- });
- const [selections, setSelections] = useState({
- artist: null,
- song: null,
- staging: null,
- choreographer: null,
- stylist: null
- });
- const [events, setEvents] = useState([]);
- const artists = [
- {
- id: 'pop_star',
- name: 'Astrid Lindberg',
- type: 'Pop Star',
- cost: 300000,
- publicAppeal: 80,
- juryAppeal: 60,
- international: 70,
- description: 'Famous Swedish pop sensation with chart-topping hits'
- },
- {
- id: 'indie_artist',
- name: 'Erik Svensson',
- type: 'Indie Artist',
- cost: 150000,
- publicAppeal: 60,
- juryAppeal: 85,
- international: 50,
- description: 'Critically acclaimed indie musician with unique sound'
- },
- {
- id: 'newcomer',
- name: 'Saga Nilsson',
- type: 'Newcomer',
- cost: 50000,
- publicAppeal: 40,
- juryAppeal: 70,
- international: 60,
- description: 'Fresh talent with viral social media presence'
- }
- ];
- const songs = [
- {
- id: 'upbeat_anthem',
- name: 'Northern Lights',
- type: 'Upbeat Anthem',
- cost: 200000,
- publicAppeal: 75,
- juryAppeal: 65,
- international: 80,
- description: 'High-energy anthem about Swedish nature and freedom'
- },
- {
- id: 'ballad',
- name: 'Silent Snow',
- type: 'Emotional Ballad',
- cost: 150000,
- publicAppeal: 60,
- juryAppeal: 90,
- international: 70,
- description: 'Haunting ballad showcasing vocal prowess'
- },
- {
- id: 'experimental',
- name: 'Digital Dreams',
- type: 'Electronic Fusion',
- cost: 250000,
- publicAppeal: 50,
- juryAppeal: 75,
- international: 85,
- description: 'Genre-bending electronic track with Swedish folk elements'
- }
- ];
- const stagingOptions = [
- {
- id: 'minimalist',
- name: 'Minimalist Nordic',
- cost: 200000,
- effect: { publicSupport: 5, jurySupport: 15, international: 10 }
- },
- {
- id: 'spectacular',
- name: 'Spectacular Show',
- cost: 400000,
- effect: { publicSupport: 20, jurySupport: 5, international: 15 }
- },
- {
- id: 'cultural',
- name: 'Swedish Heritage',
- cost: 300000,
- effect: { publicSupport: 15, jurySupport: 10, international: 5 }
- }
- ];
- const makeSelection = (category, option) => {
- if (gameState.budget >= option.cost) {
- setGameState(prev => ({
- ...prev,
- budget: prev.budget - option.cost,
- publicSupport: Math.min(100, prev.publicSupport + (option.publicAppeal || 0) / 5),
- jurySupport: Math.min(100, prev.jurySupport + (option.juryAppeal || 0) / 5),
- internationalAppeal: Math.min(100, prev.internationalAppeal + (option.international || 0) / 5)
- }));
- setSelections(prev => ({
- ...prev,
- [category]: option
- }));
- addEvent(`Selected ${option.name} for ${option.cost.toLocaleString()} SEK`);
- }
- };
- const addEvent = (message) => {
- setEvents(prev => [...prev, { week: gameState.week, message }]);
- };
- const advanceWeek = () => {
- if (gameState.week >= gameState.maxWeeks) {
- calculateFinalResult();
- return;
- }
- const randomEvents = [
- {
- message: "Positive media coverage boosts international attention",
- effect: { internationalAppeal: 5, mediaAttention: 10 }
- },
- {
- message: "Social media buzz increases public support",
- effect: { publicSupport: 8, mediaAttention: 5 }
- },
- {
- message: "Music critics praise the artistic vision",
- effect: { jurySupport: 10 }
- },
- {
- message: "Technical rehearsal goes smoothly",
- effect: { jurySupport: 5, publicSupport: 3 }
- }
- ];
- if (Math.random() < 0.3) {
- const event = randomEvents[Math.floor(Math.random() * randomEvents.length)];
- addEvent(event.message);
- setGameState(prev => ({
- ...prev,
- week: prev.week + 1,
- publicSupport: Math.min(100, prev.publicSupport + (event.effect.publicSupport || 0)),
- jurySupport: Math.min(100, prev.jurySupport + (event.effect.jurySupport || 0)),
- internationalAppeal: Math.min(100, prev.internationalAppeal + (event.effect.internationalAppeal || 0)),
- mediaAttention: Math.min(100, prev.mediaAttention + (event.effect.mediaAttention || 0))
- }));
- } else {
- setGameState(prev => ({ ...prev, week: prev.week + 1 }));
- }
- };
- const calculateFinalResult = () => {
- const { publicSupport, jurySupport, internationalAppeal, mediaAttention } = gameState;
- const score = (publicSupport * 0.3 + jurySupport * 0.4 + internationalAppeal * 0.2 + mediaAttention * 0.1);
- let placement;
- if (score >= 85) placement = Math.floor(Math.random() * 3) + 1; // Top 3
- else if (score >= 70) placement = Math.floor(Math.random() * 7) + 4; // 4-10
- else if (score >= 50) placement = Math.floor(Math.random() * 10) + 11; // 11-20
- else placement = Math.floor(Math.random() * 6) + 21; // 21-26
- setGameState(prev => ({
- ...prev,
- phase: 'results',
- finalScore: Math.round(score),
- placement
- }));
- };
- const startPreparation = () => {
- if (selections.artist && selections.song) {
- setGameState(prev => ({ ...prev, phase: 'preparation' }));
- addEvent("Preparation phase begins! Time to fine-tune your Eurovision entry.");
- }
- };
- const resetGame = () => {
- setGameState({
- phase: 'selection',
- budget: 1000000,
- publicSupport: 50,
- jurySupport: 50,
- internationalAppeal: 30,
- mediaAttention: 20,
- week: 1,
- maxWeeks: 12,
- finalScore: 0,
- placement: 0
- });
- setSelections({
- artist: null,
- song: null,
- staging: null,
- choreographer: null,
- stylist: null
- });
- setEvents([]);
- };
- if (gameState.phase === 'results') {
- return (
- <div className="max-w-4xl mx-auto p-6 bg-gradient-to-br from-blue-50 to-yellow-50 min-h-screen">
- <div className="bg-white rounded-lg shadow-xl p-8 text-center">
- <Trophy className="w-16 h-16 mx-auto mb-4 text-yellow-500" />
- <h1 className="text-3xl font-bold mb-4">Eurovision 2025 Results</h1>
- <div className="bg-gradient-to-r from-blue-500 to-yellow-500 text-white p-6 rounded-lg mb-6">
- <h2 className="text-2xl font-bold mb-2">Sweden finished #{gameState.placement}</h2>
- <p className="text-lg">Final Score: {gameState.finalScore}/100</p>
- </div>
- <div className="grid grid-cols-2 md:grid-cols-4 gap-4 mb-6">
- <div className="bg-blue-100 p-4 rounded">
- <Users className="w-8 h-8 mx-auto mb-2 text-blue-600" />
- <p className="font-semibold">Public</p>
- <p>{Math.round(gameState.publicSupport)}%</p>
- </div>
- <div className="bg-green-100 p-4 rounded">
- <Star className="w-8 h-8 mx-auto mb-2 text-green-600" />
- <p className="font-semibold">Jury</p>
- <p>{Math.round(gameState.jurySupport)}%</p>
- </div>
- <div className="bg-purple-100 p-4 rounded">
- <Globe className="w-8 h-8 mx-auto mb-2 text-purple-600" />
- <p className="font-semibold">International</p>
- <p>{Math.round(gameState.internationalAppeal)}%</p>
- </div>
- <div className="bg-orange-100 p-4 rounded">
- <div className="w-8 h-8 mx-auto mb-2 bg-orange-600 rounded"></div>
- <p className="font-semibold">Media</p>
- <p>{Math.round(gameState.mediaAttention)}%</p>
- </div>
- </div>
- <div className="mb-6">
- <h3 className="text-xl font-bold mb-2">Your Entry</h3>
- <p><strong>Artist:</strong> {selections.artist?.name}</p>
- <p><strong>Song:</strong> {selections.song?.name}</p>
- <p><strong>Staging:</strong> {selections.staging?.name || 'Basic staging'}</p>
- </div>
- <button
- onClick={resetGame}
- className="bg-blue-600 text-white px-6 py-3 rounded-lg hover:bg-blue-700 transition-colors"
- >
- Manage Sweden Again
- </button>
- </div>
- </div>
- );
- }
- return (
- <div className="max-w-6xl mx-auto p-6 bg-gradient-to-br from-blue-50 to-yellow-50 min-h-screen">
- <div className="bg-white rounded-lg shadow-xl p-6">
- <div className="flex items-center justify-between mb-6">
- <div className="flex items-center gap-3">
- <Music className="w-8 h-8 text-blue-600" />
- <h1 className="text-3xl font-bold text-gray-800">Eurovision Sweden Manager</h1>
- </div>
- <div className="text-right">
- <p className="text-lg font-semibold text-blue-600">🇸🇪 Sweden 2025</p>
- </div>
- </div>
- {/* Status Bar */}
- <div className="grid grid-cols-2 md:grid-cols-5 gap-4 mb-6 bg-gray-50 p-4 rounded-lg">
- <div className="flex items-center gap-2">
- <DollarSign className="w-5 h-5 text-green-600" />
- <div>
- <p className="text-sm text-gray-600">Budget</p>
- <p className="font-semibold">{gameState.budget.toLocaleString()} SEK</p>
- </div>
- </div>
- <div className="flex items-center gap-2">
- <Users className="w-5 h-5 text-blue-600" />
- <div>
- <p className="text-sm text-gray-600">Public Support</p>
- <p className="font-semibold">{Math.round(gameState.publicSupport)}%</p>
- </div>
- </div>
- <div className="flex items-center gap-2">
- <Star className="w-5 h-5 text-yellow-600" />
- <div>
- <p className="text-sm text-gray-600">Jury Appeal</p>
- <p className="font-semibold">{Math.round(gameState.jurySupport)}%</p>
- </div>
- </div>
- <div className="flex items-center gap-2">
- <Globe className="w-5 h-5 text-purple-600" />
- <div>
- <p className="text-sm text-gray-600">International</p>
- <p className="font-semibold">{Math.round(gameState.internationalAppeal)}%</p>
- </div>
- </div>
- <div className="flex items-center gap-2">
- <Timer className="w-5 h-5 text-red-600" />
- <div>
- <p className="text-sm text-gray-600">Week</p>
- <p className="font-semibold">{gameState.week}/{gameState.maxWeeks}</p>
- </div>
- </div>
- </div>
- {gameState.phase === 'selection' && (
- <div className="space-y-8">
- <div>
- <h2 className="text-2xl font-bold mb-4">Choose Your Artist</h2>
- <div className="grid grid-cols-1 md:grid-cols-3 gap-4">
- {artists.map(artist => (
- <div
- key={artist.id}
- className={`border-2 rounded-lg p-4 cursor-pointer transition-all ${
- selections.artist?.id === artist.id
- ? 'border-blue-500 bg-blue-50'
- : 'border-gray-200 hover:border-gray-300'
- }`}
- onClick={() => makeSelection('artist', artist)}
- >
- <h3 className="font-bold text-lg">{artist.name}</h3>
- <p className="text-gray-600 mb-2">{artist.type}</p>
- <p className="text-sm mb-3">{artist.description}</p>
- <div className="space-y-1 text-sm">
- <p>Cost: {artist.cost.toLocaleString()} SEK</p>
- <p>Public Appeal: {artist.publicAppeal}%</p>
- <p>Jury Appeal: {artist.juryAppeal}%</p>
- <p>International: {artist.international}%</p>
- </div>
- </div>
- ))}
- </div>
- </div>
- <div>
- <h2 className="text-2xl font-bold mb-4">Choose Your Song</h2>
- <div className="grid grid-cols-1 md:grid-cols-3 gap-4">
- {songs.map(song => (
- <div
- key={song.id}
- className={`border-2 rounded-lg p-4 cursor-pointer transition-all ${
- selections.song?.id === song.id
- ? 'border-blue-500 bg-blue-50'
- : 'border-gray-200 hover:border-gray-300'
- }`}
- onClick={() => makeSelection('song', song)}
- >
- <h3 className="font-bold text-lg">{song.name}</h3>
- <p className="text-gray-600 mb-2">{song.type}</p>
- <p className="text-sm mb-3">{song.description}</p>
- <div className="space-y-1 text-sm">
- <p>Cost: {song.cost.toLocaleString()} SEK</p>
- <p>Public Appeal: {song.publicAppeal}%</p>
- <p>Jury Appeal: {song.juryAppeal}%</p>
- <p>International: {song.international}%</p>
- </div>
- </div>
- ))}
- </div>
- </div>
- {selections.artist && selections.song && (
- <div className="text-center">
- <button
- onClick={startPreparation}
- className="bg-blue-600 text-white px-8 py-3 rounded-lg text-lg font-semibold hover:bg-blue-700 transition-colors"
- >
- Start Preparation Phase
- </button>
- </div>
- )}
- </div>
- )}
- {gameState.phase === 'preparation' && (
- <div className="space-y-6">
- <div className="bg-blue-50 p-4 rounded-lg">
- <h2 className="text-2xl font-bold mb-2">Preparation Phase</h2>
- <p className="text-gray-700">Fine-tune your entry with staging, choreography, and styling choices.</p>
- </div>
- <div>
- <h3 className="text-xl font-bold mb-4">Staging Options</h3>
- <div className="grid grid-cols-1 md:grid-cols-3 gap-4">
- {stagingOptions.map(staging => (
- <div
- key={staging.id}
- className={`border-2 rounded-lg p-4 cursor-pointer transition-all ${
- selections.staging?.id === staging.id
- ? 'border-blue-500 bg-blue-50'
- : 'border-gray-200 hover:border-gray-300'
- }`}
- onClick={() => {
- if (gameState.budget >= staging.cost) {
- setGameState(prev => ({
- ...prev,
- budget: prev.budget - staging.cost,
- ...Object.keys(staging.effect).reduce((acc, key) => {
- acc[key] = Math.min(100, prev[key] + staging.effect[key]);
- return acc;
- }, {})
- }));
- setSelections(prev => ({ ...prev, staging }));
- }
- }}
- >
- <h4 className="font-bold">{staging.name}</h4>
- <p className="text-sm text-gray-600 mb-2">Cost: {staging.cost.toLocaleString()} SEK</p>
- <div className="text-sm">
- {Object.entries(staging.effect).map(([key, value]) => (
- <p key={key}>+{value} {key.replace(/([A-Z])/g, ' $1').toLowerCase()}</p>
- ))}
- </div>
- </div>
- ))}
- </div>
- </div>
- <div className="flex justify-between items-center">
- <button
- onClick={advanceWeek}
- className="bg-green-600 text-white px-6 py-2 rounded-lg hover:bg-green-700 transition-colors"
- >
- Advance Week
- </button>
- {gameState.week >= gameState.maxWeeks && (
- <button
- onClick={calculateFinalResult}
- className="bg-yellow-600 text-white px-6 py-2 rounded-lg hover:bg-yellow-700 transition-colors"
- >
- Go to Eurovision!
- </button>
- )}
- </div>
- {events.length > 0 && (
- <div className="bg-gray-50 p-4 rounded-lg">
- <h3 className="font-bold mb-2">Recent Events</h3>
- <div className="space-y-1 text-sm max-h-32 overflow-y-auto">
- {events.slice(-5).map((event, idx) => (
- <p key={idx}><strong>Week {event.week}:</strong> {event.message}</p>
- ))}
- </div>
- </div>
- )}
- </div>
- )}
- </div>
- </div>
- );
- };
- export default EurovisionSimulator;
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement