Tourismusregion: Komplett-Integration (zweite neue Sim, beta)
Wellenbasierte Routen-Sim (DefensegeograSimMac) nach dem Tourismustal- Playbook integriert: Sim nach App/sims/tourismusregion (game.html, Sprites 47MB->9.5MB, ohne Eigen-Backend/Editor/Dashboard/Keys), Plattform-Hooks (GGS_LIVE_STATE mit Welle/Budget/Umwelt/Sterne/Lenkung, Submit bei Partie-Ende mit fertigem 0-100-Score, GGS_TUTORIAL, Home- Button, Musik leise). Wrapper + Modul-Detailseite. SQL: module_info (beta, Karte), Glossar-Kernbegriff Besucherlenkung (+Bild, Leichte Sprache) + 12 Zuordnungen, Lehrplan-Mapping auf tourismus- raumentwicklung (primaer) + verkehrserziehung/systemisches-denken. LEHRPLAN.md mit Schwerpunkt Besucherlenkung & Mobilitaet. Lehrer- Backend: Benchmark (Sim-Score direkt), Live-Cockpit-Spalten, needsHelp, Highlights. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,614 @@
|
||||
// ALLE Spielinhalte data-driven: Besuchergruppen, Infrastrukturkatalog,
|
||||
// Phasen-Wellen, Ereigniskarten, Entscheidungen, Feed-Texte, Regionsprofile.
|
||||
// Zahlen folgen dem Pflichtenheft (§7, §10, §11, §20) und wurden mit dem
|
||||
// Headless-Modelltest (tests/model-test.mjs) kalibriert.
|
||||
|
||||
export const GAME_LEN = 1200; // 20 Minuten Echtzeit (Sekunden)
|
||||
export const PHASE_LEN = 150; // 8 Phasen à 2:30
|
||||
export const SEASON_LEN = 300; // 4 Saisonen à 5:00
|
||||
export const SEASONS = ['Frühling', 'Sommer', 'Herbst', 'Winter'];
|
||||
export const SEASON_KEYS = ['spring', 'summer', 'autumn', 'winter'];
|
||||
export const CONTACT_NORM = 5; // Normkontaktzeit in Sekunden (§11.6)
|
||||
export const MAX_GAIN_FACTOR = 1.2; // Deckel je Attraktion (× Grundwirkung)
|
||||
export const MAX_BRAKE = 0.6; // Maximalbremse (§11.5)
|
||||
export const START_BUDGET = 500;
|
||||
export const START_ENV = 75;
|
||||
|
||||
// ---------- Besuchergruppen (§7.4) ----------
|
||||
// speed in Kacheln/s · budget = max. Ausgaben · satTarget = Zufriedenheitsziel
|
||||
// entry: W = Straße West, N = Radweg Nord, S = Fußweg Süd
|
||||
export const GROUPS = {
|
||||
spaziergaenger: {
|
||||
name: 'Spaziergänger', emoji: '🚶', color: '#3f8f6b', size: 2,
|
||||
speed: 0.55, budget: 30, satTarget: 60, maxTime: 160,
|
||||
envSens: 0.8, trafficSens: 0.6, transport: 'foot', entry: 'S',
|
||||
needsService: false, bergAffinity: 0.1, lakeAffinity: 0.7,
|
||||
likes: 'Natur · Café · Aussicht', wish: 'ein gemütliches Café',
|
||||
who: 'Einheimische und Gäste aus der Umgebung, oft Paare auf Tagesausflug. Kleines Budget (~30 €), aber wichtig für den Anfang: Sie erzählen weiter, wie schön es bei euch ist.',
|
||||
fromPhase: 1,
|
||||
},
|
||||
wanderer: {
|
||||
name: 'Wanderer', emoji: '🥾', color: '#2f6ea0', size: 2,
|
||||
speed: 0.62, budget: 45, satTarget: 75, maxTime: 200,
|
||||
envSens: 1.0, trafficSens: 0.5, transport: 'foot', entry: 'S',
|
||||
needsService: false, bergAffinity: 0.9, lakeAffinity: 0.4,
|
||||
likes: 'Natur · Aussicht · Gasthaus', wish: 'ein Weg auf den Berg',
|
||||
who: 'Naturfreunde mit Rucksack, kommen zu Fuß über den Südweg. Mittleres Budget (~45 €), sehr umweltbewusst – eine kaputte Landschaft vergrault sie sofort. Träumen von einem Weg auf den Berg.',
|
||||
fromPhase: 1,
|
||||
},
|
||||
familie: {
|
||||
name: 'Familie', emoji: '👨👩👧👦', color: '#e8892b', size: 4,
|
||||
speed: 0.5, budget: 90, satTarget: 100, maxTime: 180,
|
||||
envSens: 0.7, trafficSens: 0.8, transport: 'car', entry: 'W',
|
||||
needsService: true, bergAffinity: 0.25, lakeAffinity: 0.9,
|
||||
likes: 'Spielplatz · See · Eis · WC', wish: 'ein Spielplatz für die Kinder',
|
||||
who: 'Eltern mit zwei Kindern aus der Stadt, kommen mit dem Auto über die Weststraße. Solides Budget (~90 €), aber klare Ansprüche: Ohne Spielplatz, WC und Abkühlung am See wird der Ausflug zum Stresstest.',
|
||||
fromPhase: 2,
|
||||
},
|
||||
radfahrer: {
|
||||
name: 'Radfahrer', emoji: '🚴', color: '#c9531f', size: 2,
|
||||
speed: 1.35, budget: 40, satTarget: 60, maxTime: 90,
|
||||
envSens: 0.8, trafficSens: 0.9, transport: 'bike', entry: 'N',
|
||||
needsService: false, bergAffinity: 0.15, lakeAffinity: 0.4,
|
||||
likes: 'Radstation · Café · Aussicht', wish: 'eine Radreparaturstation',
|
||||
who: 'Sportliche Gäste auf dem Radweg von Norden – schnell unterwegs! Angebote wirken nur direkt an der Strecke. Kleines Budget (~40 €): Reparaturstation, Café, kurze Stopps.',
|
||||
fromPhase: 3,
|
||||
},
|
||||
senioren: {
|
||||
name: 'Seniorengruppe', emoji: '👵', color: '#a34a8e', size: 3,
|
||||
speed: 0.38, budget: 80, satTarget: 90, maxTime: 220,
|
||||
envSens: 0.6, trafficSens: 0.9, transport: 'car', entry: 'W',
|
||||
needsService: true, bergAffinity: 0.15, lakeAffinity: 0.5,
|
||||
likes: 'Café · Kultur · Aussicht · Sitzbänke', wish: 'ein Museum oder Café',
|
||||
who: 'Reiselustige Pensionistinnen und Pensionisten mit gutem Budget (~80 €) und viel Zeit. Gehen langsam, mögen keinen Verkehrslärm – dafür Café, Kultur und schöne Ausblicke.',
|
||||
fromPhase: 5,
|
||||
},
|
||||
schulklasse: {
|
||||
name: 'Schulklasse', emoji: '🎒', color: '#2e8b8b', size: 6,
|
||||
speed: 0.6, budget: 25, satTarget: 80, maxTime: 170,
|
||||
envSens: 0.5, trafficSens: 0.4, transport: 'bus', entry: 'S',
|
||||
needsService: true, bergAffinity: 0.4, lakeAffinity: 0.8,
|
||||
likes: 'Erlebnis · Bildung · günstige Jause', wish: 'etwas zum Entdecken (Museum, Spielplatz)',
|
||||
who: 'Eine Klasse mit Lehrpersonen auf Exkursion – knappes Budget (~25 €), aber die Gäste von morgen! Brauchen Erlebnis, etwas zum Lernen (Museum!) und eine günstige Jause.',
|
||||
fromPhase: 5,
|
||||
},
|
||||
busgruppe: {
|
||||
name: 'Busreisegruppe', emoji: '🚌', color: '#8c6239', size: 6,
|
||||
speed: 0.45, budget: 70, satTarget: 95, maxTime: 150,
|
||||
envSens: 0.4, trafficSens: 0.5, transport: 'bus', entry: 'W',
|
||||
needsService: true, needsParking: true, bergAffinity: 0.3, lakeAffinity: 0.4,
|
||||
likes: 'Sehenswürdigkeit · Souvenirs · Gasthaus · WC', wish: 'ein Busparkplatz und ein WC',
|
||||
who: 'Organisierte Tagesreisende mit Reiseleitung, mittleres Budget (~70 €). Achtung: Ohne Busparkplatz fährt der Bus einfach weiter! Programm: Sehenswürdigkeit, Souvenirs, Gasthaus, WC.',
|
||||
fromPhase: 5,
|
||||
},
|
||||
junge_gruppe: {
|
||||
name: 'Junge Reisegruppe', emoji: '🤸', color: '#5b6ee1', size: 4,
|
||||
speed: 0.8, budget: 60, satTarget: 105, maxTime: 170,
|
||||
envSens: 0.5, trafficSens: 0.3, transport: 'car', entry: 'W',
|
||||
needsService: false, bergAffinity: 0.95, lakeAffinity: 0.6,
|
||||
likes: 'Action · Berg · Aussicht · günstige Gastro', wish: 'Action am Berg (Skilift!)',
|
||||
who: 'Freundesgruppen auf der Suche nach Action am Berg. Mittleres Budget (~60 €), unempfindlich gegen Trubel – aber ohne Lift und Erlebnis ziehen sie weiter.',
|
||||
fromPhase: 6,
|
||||
},
|
||||
luxuspaar: {
|
||||
name: 'Wohlhabendes Paar', emoji: '💎', color: '#b0356b', size: 2,
|
||||
speed: 0.5, budget: 200, satTarget: 140, maxTime: 200,
|
||||
envSens: 0.9, trafficSens: 0.8, transport: 'car', entry: 'W',
|
||||
needsService: false, bergAffinity: 0.6, lakeAffinity: 0.5,
|
||||
likes: 'feines Restaurant · Wellness · Aussicht · Kultur',
|
||||
wish: 'ein wirklich gutes Restaurant (Wirtshaus Stufe 4+)',
|
||||
who: 'Wohlhabende Paare mit hohen Erwartungen und großem Budget (~200 €). Sie kommen nur, wenn die Qualität stimmt: feines Restaurant (Wirtshaus Stufe 4+), Wellness, Kultur – und eine intakte Landschaft.',
|
||||
fromPhase: 7,
|
||||
},
|
||||
};
|
||||
|
||||
// ---------- Infrastrukturkatalog (§9/§10) ----------
|
||||
// Arrays sind je Ausbaustufe (Index 0 = Stufe 1).
|
||||
// cost[0] = Baukosten, cost[i] = Kosten des Upgrades auf Stufe i+1.
|
||||
// sat = Grundwirkung Zufriedenheit je Normkontakt · slow = Bremse (Anteil)
|
||||
// opCost = Betriebskosten pro Minute · revenue = Basisumsatz je Normkontakt
|
||||
// env = Umweltwirkung beim Erreichen der Stufe (Delta) · range in Kacheln
|
||||
// target = Zielgruppenfaktor (fehlend ⇒ 0.4) · season = Saisonfaktor
|
||||
export const BUILDINGS = {
|
||||
wirtshaus: {
|
||||
name: 'Wirtshaus', emoji: '🍽️', cat: 'gastronomy', unlockPhase: 1, item: '🍲', itemsBy: { luxuspaar: '🍷', schulklasse: '🍟' }, sound: 'bite',
|
||||
desc: 'Stärkt fast alle Gäste. Solides Startgebäude.',
|
||||
levelNames: ['Wirtshaus', 'Gasthof', 'Landgasthof', 'Restaurant', 'Gourmetrestaurant'],
|
||||
cost: [100, 180, 320, 520, 900],
|
||||
sat: [8, 13, 20, 28, 40],
|
||||
slow: [0.02, 0.04, 0.06, 0.08, 0.1],
|
||||
range: [4, 4.5, 5, 5.5, 6],
|
||||
capacity: [5, 8, 12, 18, 25],
|
||||
opCost: [4, 7, 12, 20, 35],
|
||||
revenue: [7, 10, 15, 22, 32],
|
||||
env: [-1, -1, -1, -1, -2],
|
||||
target: {
|
||||
wanderer: 1.2, spaziergaenger: 1.1, familie: 0.8, senioren: 1.1,
|
||||
schulklasse: 0.6, busgruppe: 1.0, radfahrer: 0.9, junge_gruppe: 0.9, luxuspaar: 0.3,
|
||||
},
|
||||
targetByLevel: { luxuspaar: [0.3, 0.3, 0.3, 1.2, 1.3] },
|
||||
season: { spring: 1, summer: 1, autumn: 1.1, winter: 1.2 },
|
||||
place: { near: 'path' },
|
||||
},
|
||||
cafe: {
|
||||
name: 'Café', emoji: '☕', cat: 'gastronomy', unlockPhase: 1, item: '☕', itemsBy: { familie: '🧃', schulklasse: '🧃', senioren: '🍰' }, sound: 'sip',
|
||||
desc: 'Bremst und erfreut – besonders Senioren, Spaziergänger, Paare.',
|
||||
cost: [80, 140, 240, 400, 650],
|
||||
sat: [5, 8, 12, 17, 24],
|
||||
slow: [0.10, 0.15, 0.20, 0.25, 0.30],
|
||||
range: [3.5, 4, 4.5, 5, 5.5],
|
||||
capacity: [5, 7, 10, 14, 20],
|
||||
opCost: [3, 5, 8, 13, 20],
|
||||
revenue: [5, 7, 10, 14, 20],
|
||||
env: [-1, 0, -1, -1, -1],
|
||||
target: {
|
||||
senioren: 1.4, spaziergaenger: 1.3, luxuspaar: 1.0, familie: 0.9,
|
||||
radfahrer: 1.1, wanderer: 0.9, junge_gruppe: 0.8, busgruppe: 0.8, schulklasse: 0.5,
|
||||
},
|
||||
season: { spring: 1.1, summer: 1, autumn: 1.1, winter: 1 },
|
||||
place: { near: 'path' },
|
||||
},
|
||||
marktstand: {
|
||||
name: 'Marktstand', emoji: '🧺', cat: 'shopping', unlockPhase: 1, item: '🍎', itemsBy: { familie: '🧺', busgruppe: '🧺' }, sound: 'bite',
|
||||
desc: 'Reine Bremse: Gäste bleiben länger, kleiner Umsatz.',
|
||||
cost: [60, 100, 170, 280, 450],
|
||||
sat: [1, 2, 3, 4, 5],
|
||||
slow: [0.08, 0.12, 0.17, 0.22, 0.28],
|
||||
range: [3, 3.5, 4, 4.5, 5],
|
||||
capacity: [8, 12, 16, 22, 30],
|
||||
opCost: [2, 3, 5, 8, 12],
|
||||
revenue: [3, 5, 7, 10, 14],
|
||||
env: [0, 0, 0, -1, -1],
|
||||
target: {
|
||||
spaziergaenger: 1.1, senioren: 1.2, familie: 1.0, busgruppe: 1.2,
|
||||
wanderer: 0.8, luxuspaar: 0.8, schulklasse: 0.7, junge_gruppe: 0.8, radfahrer: 0.5,
|
||||
},
|
||||
season: { spring: 1, summer: 1.1, autumn: 1.2, winter: 0.9 },
|
||||
place: { near: 'path' },
|
||||
},
|
||||
aussicht: {
|
||||
name: 'Aussichtspunkt', emoji: '🔭', cat: 'nature', unlockPhase: 1, fx: '📷', sound: 'click',
|
||||
desc: 'Landschaftserlebnis. Wirkt stärker in erhöhter Lage.',
|
||||
cost: [90, 160, 280, 470, 780],
|
||||
sat: [7, 12, 18, 26, 36],
|
||||
slow: [0.04, 0.05, 0.06, 0.07, 0.08],
|
||||
range: [4, 4.5, 5, 5.5, 6],
|
||||
capacity: [8, 12, 16, 22, 30],
|
||||
opCost: [1, 2, 3, 5, 8],
|
||||
revenue: [0, 0, 1, 1, 2],
|
||||
env: [0, 0, 0, -1, -1],
|
||||
target: {
|
||||
wanderer: 1.4, senioren: 1.2, luxuspaar: 1.2, spaziergaenger: 1.2,
|
||||
busgruppe: 1.1, familie: 0.8, radfahrer: 0.9, junge_gruppe: 1.0, schulklasse: 0.8,
|
||||
},
|
||||
season: { spring: 1, summer: 1.1, autumn: 1.2, winter: 1.1 },
|
||||
place: { near: 'path' }, elevBonus: true, natureBoosted: true,
|
||||
},
|
||||
spielplatz: {
|
||||
name: 'Spielplatz', emoji: '🛝', cat: 'play', unlockPhase: 2, fx: '🎈', sound: 'pling',
|
||||
desc: 'Familien-Magnet, auch Schulklassen. Kaum Umsatz, viel Freude.',
|
||||
cost: [120, 200, 340, 560, 900],
|
||||
sat: [12, 18, 26, 35, 45],
|
||||
slow: [0.08, 0.10, 0.12, 0.14, 0.16],
|
||||
range: [4, 4.5, 5, 5.5, 6],
|
||||
capacity: [6, 9, 13, 18, 25],
|
||||
opCost: [2, 3, 5, 8, 12],
|
||||
revenue: [0, 0, 0, 1, 1],
|
||||
env: [0, 0, 0, -1, -1],
|
||||
target: { familie: 1.0, schulklasse: 0.7, spaziergaenger: 0.3, junge_gruppe: 0.4 },
|
||||
season: { spring: 1.1, summer: 1.2, autumn: 1, winter: 0.7 },
|
||||
place: { near: 'path' },
|
||||
},
|
||||
wc: {
|
||||
name: 'WC / Servicepunkt', emoji: '🚻', cat: 'service', unlockPhase: 2, maxLevel: 3,
|
||||
desc: 'Unauffällig, aber wichtig: verhindert Frust bei langen Aufenthalten.',
|
||||
cost: [60, 110, 190],
|
||||
sat: [2, 3, 4],
|
||||
slow: [0, 0, 0],
|
||||
range: [5, 6, 7],
|
||||
capacity: [12, 20, 30],
|
||||
opCost: [2, 3, 5],
|
||||
revenue: [0, 0, 0],
|
||||
env: [0, 0, 0],
|
||||
target: { familie: 1.2, senioren: 1.2, busgruppe: 1.2, schulklasse: 1.0 },
|
||||
season: { spring: 1, summer: 1, autumn: 1, winter: 1 },
|
||||
place: { near: 'path' }, service: true,
|
||||
},
|
||||
badesteg: {
|
||||
name: 'Badesteg', emoji: '🏖️', cat: 'nature', unlockPhase: 2, fx: '🏊', sound: 'splash',
|
||||
desc: 'Badespaß am See – im Sommer der Hit, im Winter tot.',
|
||||
cost: [110, 190, 320, 530, 860],
|
||||
sat: [8, 13, 19, 27, 37],
|
||||
slow: [0.10, 0.12, 0.15, 0.18, 0.22],
|
||||
range: [4, 4.5, 5, 5.5, 6],
|
||||
capacity: [6, 9, 13, 18, 25],
|
||||
opCost: [2, 3, 5, 8, 12],
|
||||
revenue: [2, 3, 4, 6, 8],
|
||||
env: [-1, -1, -1, -2, -2],
|
||||
target: {
|
||||
familie: 1.3, junge_gruppe: 1.0, schulklasse: 1.0, spaziergaenger: 0.8,
|
||||
wanderer: 0.5, luxuspaar: 0.6,
|
||||
},
|
||||
season: { spring: 0.4, summer: 1.6, autumn: 0.3, winter: 0.1 },
|
||||
place: { near: 'lake' }, natureBoosted: true,
|
||||
},
|
||||
eisdiele: {
|
||||
name: 'Eisdiele', emoji: '🍦', cat: 'gastronomy', unlockPhase: 3, item: '🍦', sound: 'bite',
|
||||
desc: 'Süße Bremse für Familien und Kinder, stark im Sommer.',
|
||||
cost: [90, 150, 260, 430, 700],
|
||||
sat: [6, 9, 13, 18, 25],
|
||||
slow: [0.08, 0.11, 0.14, 0.17, 0.20],
|
||||
range: [3.5, 4, 4.5, 5, 5.5],
|
||||
capacity: [6, 9, 13, 18, 25],
|
||||
opCost: [2, 4, 6, 10, 15],
|
||||
revenue: [4, 6, 9, 13, 18],
|
||||
env: [0, 0, -1, -1, -1],
|
||||
target: {
|
||||
familie: 1.4, schulklasse: 1.2, junge_gruppe: 1.0, spaziergaenger: 1.0,
|
||||
radfahrer: 1.0, senioren: 0.9,
|
||||
},
|
||||
season: { spring: 0.9, summer: 1.5, autumn: 0.7, winter: 0.3 },
|
||||
place: { near: 'path' },
|
||||
},
|
||||
radstation: {
|
||||
name: 'Radreparaturstation', emoji: '🚲', cat: 'service', unlockPhase: 3, item: '🔧', sound: 'tool',
|
||||
desc: 'Wirkt fast nur auf Radfahrer – dafür richtig stark.',
|
||||
cost: [70, 120, 210, 350, 570],
|
||||
sat: [15, 23, 32, 43, 56],
|
||||
slow: [0.10, 0.12, 0.14, 0.16, 0.18],
|
||||
range: [3.5, 4, 4.5, 5, 5.5],
|
||||
capacity: [4, 6, 9, 13, 18],
|
||||
opCost: [1, 2, 3, 5, 8],
|
||||
revenue: [2, 3, 5, 7, 10],
|
||||
env: [0, 0, 0, 0, 0],
|
||||
target: { radfahrer: 1.0, junge_gruppe: 0.3 },
|
||||
season: { spring: 1.1, summer: 1.2, autumn: 1, winter: 0.4 },
|
||||
place: { near: 'path' },
|
||||
},
|
||||
tourismusinfo: {
|
||||
name: 'Tourismusinfo', emoji: 'ℹ️', cat: 'service', unlockPhase: 3, maxLevel: 5,
|
||||
desc: 'Multiplikator: verstärkt alle Attraktionen in ihrer Nähe.',
|
||||
cost: [130, 220, 370, 600, 950],
|
||||
sat: [2, 3, 4, 5, 6],
|
||||
slow: [0.03, 0.04, 0.05, 0.06, 0.07],
|
||||
range: [5, 5.5, 6, 6.5, 7],
|
||||
capacity: [10, 14, 20, 28, 40],
|
||||
opCost: [3, 5, 8, 12, 18],
|
||||
revenue: [0, 0, 0, 0, 0],
|
||||
env: [0, 0, 0, 0, 0],
|
||||
boost: [0.05, 0.10, 0.16, 0.23, 0.32], // +% Wirkung benachbarter Gebäude
|
||||
target: { busgruppe: 1.0, senioren: 1.0, familie: 0.8 },
|
||||
season: { spring: 1, summer: 1, autumn: 1, winter: 1 },
|
||||
place: { near: 'path' },
|
||||
},
|
||||
naturschutz: {
|
||||
name: 'Naturschutzgebiet', emoji: '🌿', cat: 'nature', unlockPhase: 4, maxLevel: 1, fx: '🦋', sound: 'pling',
|
||||
desc: 'Umwelt +20, Naturerlebnis +15 % – sperrt aber Bauplätze im Umkreis.',
|
||||
cost: [300],
|
||||
sat: [6],
|
||||
slow: [0.05],
|
||||
range: [5],
|
||||
capacity: [999],
|
||||
opCost: [2],
|
||||
revenue: [0],
|
||||
env: [20],
|
||||
target: { wanderer: 1.3, spaziergaenger: 1.1, luxuspaar: 0.9, familie: 0.7, schulklasse: 0.9 },
|
||||
season: { spring: 1.2, summer: 1.1, autumn: 1.1, winter: 0.8 },
|
||||
place: { near: 'any', notVillage: true }, blocksRadius: 2.5,
|
||||
},
|
||||
museum: {
|
||||
name: 'Heimatmuseum', emoji: '🏛️', cat: 'culture', unlockPhase: 5, item: '🎟️', itemsBy: { schulklasse: '📚' }, sound: 'cash',
|
||||
desc: 'Kultur für Senioren, Schulklassen und Busgruppen.',
|
||||
cost: [200, 340, 560, 900, 1400],
|
||||
sat: [10, 15, 22, 30, 40],
|
||||
slow: [0.08, 0.10, 0.12, 0.14, 0.16],
|
||||
range: [4, 4.5, 5, 5.5, 6],
|
||||
capacity: [6, 9, 13, 18, 25],
|
||||
opCost: [4, 6, 10, 15, 22],
|
||||
revenue: [3, 5, 7, 10, 14],
|
||||
env: [0, 0, 0, 0, 0],
|
||||
target: {
|
||||
senioren: 1.3, schulklasse: 1.2, busgruppe: 1.2, luxuspaar: 1.0,
|
||||
wanderer: 0.5, familie: 0.6,
|
||||
},
|
||||
season: { spring: 1, summer: 0.9, autumn: 1.2, winter: 1.2 },
|
||||
place: { near: 'path' },
|
||||
},
|
||||
parkplatz: {
|
||||
name: 'Parkplatz', emoji: '🅿️', cat: 'service', unlockPhase: 5,
|
||||
desc: 'Bringt mehr Auto- und Busgäste – erzeugt aber Verkehr und kostet Umwelt.',
|
||||
cost: [150, 260, 430, 700, 1100],
|
||||
sat: [1, 1, 2, 2, 3],
|
||||
slow: [0, 0, 0, 0, 0],
|
||||
range: [5, 5.5, 6, 6.5, 7],
|
||||
capacity: [999, 999, 999, 999, 999],
|
||||
opCost: [2, 3, 5, 8, 12],
|
||||
revenue: [2, 3, 5, 7, 10],
|
||||
env: [-8, -3, -3, -4, -5],
|
||||
visitorPlus: [0.05, 0.10, 0.17, 0.25, 0.35], // + Anteil Auto-/Busgäste
|
||||
traffic: [5, 10, 17, 28, 42],
|
||||
target: { busgruppe: 1.2, familie: 0.8, senioren: 0.8, luxuspaar: 0.6 },
|
||||
season: { spring: 1, summer: 1, autumn: 1, winter: 1 },
|
||||
place: { near: 'road' },
|
||||
},
|
||||
bushaltestelle: {
|
||||
name: 'Bushaltestelle', emoji: '🚏', cat: 'service', unlockPhase: 5, maxLevel: 5,
|
||||
desc: 'Nachhaltige Anreise: mehr Gruppen, kaum Umweltbelastung.',
|
||||
cost: [120, 210, 350, 570, 900],
|
||||
sat: [1, 1, 2, 2, 3],
|
||||
slow: [0, 0, 0, 0, 0],
|
||||
range: [5, 5.5, 6, 6.5, 7],
|
||||
capacity: [999, 999, 999, 999, 999],
|
||||
opCost: [3, 5, 8, 12, 18],
|
||||
revenue: [1, 2, 3, 4, 6],
|
||||
env: [1, 1, 1, 1, 1],
|
||||
busPlus: [0.04, 0.08, 0.14, 0.20, 0.28], // + Anteil Bus-Gäste
|
||||
target: { busgruppe: 1.0, senioren: 1.0, schulklasse: 1.0 },
|
||||
season: { spring: 1, summer: 1, autumn: 1, winter: 1 },
|
||||
place: { near: 'road' },
|
||||
},
|
||||
souvenir: {
|
||||
name: 'Souvenirshop', emoji: '🎁', cat: 'shopping', unlockPhase: 5, item: '🎁', itemsBy: { junge_gruppe: '🧢' }, sound: 'cash',
|
||||
desc: 'Bremse + Umsatz, geliebt von Busgruppen.',
|
||||
cost: [100, 170, 290, 480, 780],
|
||||
sat: [4, 6, 9, 13, 18],
|
||||
slow: [0.10, 0.13, 0.16, 0.20, 0.24],
|
||||
range: [3.5, 4, 4.5, 5, 5.5],
|
||||
capacity: [6, 9, 13, 18, 25],
|
||||
opCost: [2, 4, 6, 10, 15],
|
||||
revenue: [5, 7, 10, 14, 20],
|
||||
env: [0, 0, -1, -1, -1],
|
||||
target: {
|
||||
busgruppe: 1.4, senioren: 1.0, familie: 0.8, luxuspaar: 0.7,
|
||||
schulklasse: 0.8, junge_gruppe: 0.6,
|
||||
},
|
||||
season: { spring: 1, summer: 1.1, autumn: 1.1, winter: 1.1 },
|
||||
place: { near: 'path' },
|
||||
},
|
||||
skilift: {
|
||||
name: 'Skilift', emoji: '🚡', cat: 'sport', unlockPhase: 6, fx: '🎿', sound: 'pling',
|
||||
desc: 'Erschließt den Berg: neuer Rundweg + Bauplätze oben. Teuer, stark im Winter, Umwelteingriff.',
|
||||
levelNames: ['Schlepplift', 'Sessellift', '6er-Sessellift', 'Gondelbahn', 'Erlebnisbahn'],
|
||||
cost: [800, 1300, 2100, 3500, 5500],
|
||||
sat: [18, 26, 36, 48, 62],
|
||||
slow: [0, 0, 0, 0, 0],
|
||||
range: [4, 4.5, 5, 5.5, 6],
|
||||
capacity: [8, 14, 22, 32, 45],
|
||||
opCost: [15, 22, 32, 45, 60],
|
||||
revenue: [10, 14, 20, 28, 40],
|
||||
env: [-10, -2, -2, -3, -3],
|
||||
target: {
|
||||
junge_gruppe: 1.3, wanderer: 1.0, familie: 0.7, schulklasse: 0.9,
|
||||
luxuspaar: 0.8, senioren: 0.3,
|
||||
},
|
||||
targetByLevel: { senioren: [0.3, 0.3, 0.5, 1.0, 1.0] }, // Gondel hilft Senioren
|
||||
season: { spring: 0.5, summer: 0.7, autumn: 0.6, winter: 1.5 },
|
||||
place: { zone: 'hang', unique: true }, opensBerg: true,
|
||||
},
|
||||
almhuette: {
|
||||
name: 'Almhütte', emoji: '🏔️', cat: 'gastronomy', unlockPhase: 6, item: '🧀', itemsBy: { wanderer: '🥨', junge_gruppe: '🥤' }, sound: 'bite',
|
||||
desc: 'Einkehr am Berg – nur sinnvoll, wenn der Lift Gäste bringt.',
|
||||
cost: [180, 300, 500, 820, 1300],
|
||||
sat: [10, 15, 22, 30, 40],
|
||||
slow: [0.10, 0.13, 0.16, 0.20, 0.24],
|
||||
range: [4, 4.5, 5, 5.5, 6],
|
||||
capacity: [5, 8, 12, 18, 25],
|
||||
opCost: [4, 6, 10, 15, 22],
|
||||
revenue: [6, 9, 13, 19, 27],
|
||||
env: [-1, -1, -1, -1, -2],
|
||||
target: { wanderer: 1.3, junge_gruppe: 1.2, luxuspaar: 0.9, schulklasse: 0.8, familie: 0.8, senioren: 0.9 },
|
||||
season: { spring: 0.8, summer: 1.1, autumn: 1.1, winter: 1.4 },
|
||||
place: { zone: 'berg', needsLift: true },
|
||||
},
|
||||
wellness: {
|
||||
name: 'Wellnesshaus', emoji: '💆', cat: 'culture', unlockPhase: 7, item: '🌺', itemsBy: { luxuspaar: '💆' }, sound: 'sip',
|
||||
desc: 'Ruhe und Luxus für anspruchsvolle Gäste.',
|
||||
cost: [600, 1000, 1600, 2600, 4200],
|
||||
sat: [14, 20, 28, 38, 50],
|
||||
slow: [0.12, 0.14, 0.17, 0.20, 0.24],
|
||||
range: [4, 4.5, 5, 5.5, 6],
|
||||
capacity: [4, 6, 9, 13, 18],
|
||||
opCost: [8, 12, 18, 26, 38],
|
||||
revenue: [12, 17, 24, 34, 48],
|
||||
env: [-3, -1, -2, -2, -2],
|
||||
target: { luxuspaar: 1.4, senioren: 0.9, familie: 0.3, junge_gruppe: 0.4 },
|
||||
season: { spring: 1, summer: 0.9, autumn: 1.1, winter: 1.3 },
|
||||
place: { near: 'path' },
|
||||
},
|
||||
};
|
||||
|
||||
for (const def of Object.values(BUILDINGS)) {
|
||||
def.maxLevel = def.maxLevel ?? def.cost.length;
|
||||
}
|
||||
|
||||
// ---------- Wellen je Phase (§20) ----------
|
||||
// Basisanzahl; skaliert mit Besucherfaktor (0.7 + Ø-Sterne/5) und
|
||||
// Parkplatz-/Bushaltestellen-Bonus für Auto-/Busgruppen.
|
||||
export const WAVES = {
|
||||
1: { spaziergaenger: 5, wanderer: 3 },
|
||||
2: { spaziergaenger: 5, wanderer: 4, familie: 2 },
|
||||
3: { spaziergaenger: 3, wanderer: 3, familie: 5, radfahrer: 4 },
|
||||
4: { spaziergaenger: 2, wanderer: 3, familie: 4, radfahrer: 8 },
|
||||
5: { spaziergaenger: 2, wanderer: 2, familie: 3, radfahrer: 3, senioren: 4, busgruppe: 3, schulklasse: 3 },
|
||||
6: { spaziergaenger: 2, wanderer: 3, familie: 4, radfahrer: 3, senioren: 3, busgruppe: 3, schulklasse: 2, junge_gruppe: 3 },
|
||||
7: { wanderer: 2, familie: 3, senioren: 2, busgruppe: 2, junge_gruppe: 5, luxuspaar: 3 },
|
||||
8: { spaziergaenger: 2, wanderer: 3, familie: 4, radfahrer: 2, senioren: 3, busgruppe: 3, schulklasse: 2, junge_gruppe: 4, luxuspaar: 3 },
|
||||
};
|
||||
|
||||
// ---------- Ereignisse & Ankündigungen (§13) ----------
|
||||
// t in Sekunden. type: 'card' (Info/Ankündigung), 'decision' (A/B-Wahl).
|
||||
export const EVENTS = [
|
||||
{
|
||||
id: 'hint_familien', wave: 2, type: 'card', icon: '📣',
|
||||
title: 'Tourismusverband meldet',
|
||||
text: 'Immer mehr Familien entdecken die Region als Ausflugsziel. '
|
||||
+ 'In etwa 5 Minuten werden deutlich mehr Familien erwartet.\n\n'
|
||||
+ 'Fachlicher Hinweis: Familien achten auf kindgerechte Angebote, '
|
||||
+ 'kurze Wege und Sanitäranlagen.',
|
||||
},
|
||||
{
|
||||
id: 'hint_radweg', wave: 3, type: 'card', icon: '🚴',
|
||||
title: 'Ankündigung: Radfernweg',
|
||||
text: 'Ein Radfernweg wird eröffnet – in 2 Phasen kommen deutlich mehr '
|
||||
+ 'Radfahrer durch den Ort.\n\nFachlicher Hinweis: Radfahrer sind schnell '
|
||||
+ 'unterwegs. Angebote wirken nur, wenn sie nah am Radweg liegen.',
|
||||
},
|
||||
{
|
||||
id: 'decision_investor', wave: 4, type: 'decision', icon: '🏗️',
|
||||
title: 'Ein Investor will einsteigen',
|
||||
text: 'Ein Großinvestor bietet der Gemeinde 800 € Beteiligung – dafür will '
|
||||
+ 'er großflächig am Seeufer bauen. Der Eingriff würde die Natur belasten '
|
||||
+ '(Umwelt −10).\n\nRaumplanung heißt abwägen: kurzfristiges Geld gegen '
|
||||
+ 'langfristige Qualität.',
|
||||
yes: { label: 'Angebot annehmen (+800 €, Umwelt −10)', apply: s => { s.budget += 800; s.env = Math.max(5, s.env - 10); } },
|
||||
no: { label: 'Ablehnen (Umwelt +3)', apply: s => { s.env = Math.min(100, s.env + 3); } },
|
||||
},
|
||||
{
|
||||
id: 'hint_bus', wave: 5, type: 'card', icon: '🚌',
|
||||
title: 'Reiseveranstalter plant Tagesfahrten',
|
||||
text: 'Ein Reiseveranstalter nimmt die Region ins Programm: Ab jetzt '
|
||||
+ 'kommen Busreisegruppen und Schulklassen.\n\nFachlicher Hinweis: '
|
||||
+ 'Busgruppen brauchen eine Parkmöglichkeit – sonst reisen sie '
|
||||
+ 'verärgert wieder ab. WC und Gasthaus nicht vergessen.',
|
||||
},
|
||||
{
|
||||
id: 'hint_winter', wave: 6, type: 'card', icon: '❄️',
|
||||
title: 'Der Winter naht',
|
||||
text: 'Schneesicherheit ist im Tal unsicher, aber höher gelegene Bereiche '
|
||||
+ 'bleiben attraktiv. Ein Lift würde den Hang erschließen und oben neue '
|
||||
+ 'Bauplätze schaffen.\n\nFachlicher Hinweis: Ein Lift ist teuer und '
|
||||
+ 'belastet die Umwelt – ganzjährige Nutzung (Wandern, Aussicht) '
|
||||
+ 'verbessert die Rechnung.',
|
||||
},
|
||||
{
|
||||
id: 'decision_dorffest', wave: 6, type: 'decision', icon: '🎪',
|
||||
title: 'Dorffest am Wochenende?',
|
||||
text: 'Der Verein möchte ein Dorffest veranstalten. Kosten: 150 €. '
|
||||
+ 'Dafür steigt die Stimmung: Alle Gäste der nächsten Zeit sind '
|
||||
+ 'spürbar zufriedener.',
|
||||
yes: {
|
||||
label: 'Fest finanzieren (−150 €)', can: s => s.budget >= 150,
|
||||
apply: s => { s.budget -= 150; s.festUntil = s.time + 120; },
|
||||
},
|
||||
no: { label: 'Diesmal nicht', apply: () => {} },
|
||||
},
|
||||
{
|
||||
id: 'hint_luxus', wave: 7, type: 'card', icon: '💎',
|
||||
title: 'Anspruchsvolle Gäste im Anmarsch',
|
||||
text: 'Die Region wird bekannter: Junge Reisegruppen suchen Action am '
|
||||
+ 'Berg, wohlhabende Paare erwarten Qualität – gutes Restaurant '
|
||||
+ '(Wirtshaus Stufe 4+), Wellness, Kultur und eine intakte Landschaft.',
|
||||
},
|
||||
{
|
||||
id: 'hint_endphase', wave: 8, type: 'card', icon: '🏁',
|
||||
title: 'Endphase',
|
||||
text: 'Die letzten 2:30 Minuten! Starke Besucherströme kommen. Jetzt '
|
||||
+ 'zählt: Läuft deine Region rund – für alle Gruppen, wirtschaftlich '
|
||||
+ 'und ökologisch?',
|
||||
},
|
||||
];
|
||||
|
||||
// ---------- Social-Media-Feed (§8.3) ----------
|
||||
export const FEED_TEXTS = {
|
||||
5: [
|
||||
'{best} war perfekt für unseren Ausflug – gerne wieder!',
|
||||
'Traumhafter Tag! Besonders {best} hat uns begeistert.',
|
||||
'Fünf Sterne! {best} und die Landschaft – ein Traum.',
|
||||
],
|
||||
4: [
|
||||
'Sehr schöner Ausflug, {best} hat uns gut gefallen.',
|
||||
'Fast perfekt – {best} war toll, ein bisschen mehr Auswahl wäre schön.',
|
||||
],
|
||||
3: [
|
||||
'Schöne Landschaft, aber {missing} haben wir vermisst.',
|
||||
'Ganz nett. {best} war okay, aber es fehlt noch etwas – z. B. {missing}.',
|
||||
],
|
||||
2: [
|
||||
'Für uns gab es leider zu wenig zu tun – {missing} würde helfen.',
|
||||
'Enttäuschend wenig Angebot. {missing} fehlt.',
|
||||
],
|
||||
1: [
|
||||
'Nie wieder. Es gab praktisch nichts für uns – {missing} fehlt völlig.',
|
||||
'Schade um die schöne Gegend – ohne Angebote fahren wir woanders hin.',
|
||||
],
|
||||
traffic: 'Schöne Gegend, aber der Verkehr im Ort war wirklich mühsam.',
|
||||
env: 'Die vielen Eingriffe in die Natur haben uns den Ausflug verleidet.',
|
||||
noParking: 'Unser Bus fand keinen Parkplatz – wir mussten weiterfahren!',
|
||||
};
|
||||
|
||||
// ---------- Regionsprofile (§12.3) ----------
|
||||
export const PROFILES = [
|
||||
{
|
||||
id: 'krisenregion', name: 'Krisenregion', icon: '🌧️',
|
||||
check: s => s.result.avgStars < 2.5 || s.budget < -1000 || s.env < 35,
|
||||
text: 'Schlechte Bewertungen, Geldsorgen oder Umweltprobleme: Die Region hat ihre Richtung noch nicht gefunden.',
|
||||
},
|
||||
{
|
||||
id: 'nachhaltig', name: 'Nachhaltige Modellregion', icon: '🌍',
|
||||
check: s => s.result.avgStars >= 3.5 && s.env >= 65,
|
||||
text: 'Hohe Zufriedenheit UND intakte Umwelt – so sieht zukunftsfähiger Tourismus aus.',
|
||||
},
|
||||
{
|
||||
id: 'massentourismus', name: 'Massentourismusregion', icon: '🏗️',
|
||||
check: s => s.revenueTotal >= 3000 && s.env < 50,
|
||||
text: 'Wirtschaftlich stark, aber auf Kosten der Natur – wie lange geht das gut?',
|
||||
},
|
||||
{
|
||||
id: 'familienregion', name: 'Familienregion', icon: '👨👩👧👦',
|
||||
check: s => (s.stats.groups.familie?.count ?? 0) >= 8 && (s.stats.groups.familie?.avg ?? 0) >= 3.8,
|
||||
text: 'Familien fühlen sich hier besonders wohl – kindgerechte Angebote zahlen sich aus.',
|
||||
},
|
||||
{
|
||||
id: 'aktivregion', name: 'Aktivregion', icon: '🚵',
|
||||
check: s => avgOf(s, ['radfahrer', 'wanderer', 'junge_gruppe']) >= 3.8,
|
||||
text: 'Sport, Berg und Bewegung: Aktive Gäste lieben deine Region.',
|
||||
},
|
||||
{
|
||||
id: 'kulturregion', name: 'Kulturregion', icon: '🏛️',
|
||||
check: s => s.buildings.some(b => b.type === 'museum') && avgOf(s, ['senioren', 'schulklasse', 'busgruppe']) >= 3.8,
|
||||
text: 'Kultur und Dorfleben ziehen Senioren, Schulklassen und Gruppen an.',
|
||||
},
|
||||
{
|
||||
id: 'entwicklung', name: 'Ausflugsregion in Entwicklung', icon: '🌱',
|
||||
check: () => true,
|
||||
text: 'Eine solide Basis ist gelegt – die klare Spezialisierung fehlt noch.',
|
||||
},
|
||||
];
|
||||
|
||||
function avgOf(s, ids) {
|
||||
let sum = 0, n = 0;
|
||||
for (const id of ids) {
|
||||
const g = s.stats.groups[id];
|
||||
if (g?.count) { sum += g.stars; n += g.count; }
|
||||
}
|
||||
return n ? sum / n : 0;
|
||||
}
|
||||
|
||||
// ---------- Synergien (§9.2 synergyRules) ----------
|
||||
// Stehen zwei passende Gebäude nah beieinander (Pixel-Radius), verstärken sie
|
||||
// sich gegenseitig. bonus = zusätzlicher Wirkungsfaktor (+X %). Beidseitig.
|
||||
export const SYNERGIES = [
|
||||
{ a: 'skilift', b: 'almhuette', radius: 260, bonus: 0.25, why: 'Almhütte + Lift: Einkehr direkt an der Bergstation' },
|
||||
{ a: 'aussicht', b: 'naturschutz', radius: 200, bonus: 0.20, why: 'Aussicht + Naturschutz: unverbaute Landschaft' },
|
||||
{ a: 'cafe', b: 'marktstand', radius: 160, bonus: 0.18, why: 'Café + Marktstand: gemütlicher Bummel' },
|
||||
{ a: 'wirtshaus', b: 'souvenir', radius: 170, bonus: 0.15, why: 'Wirtshaus + Souvenirshop: Einkehr & Andenken' },
|
||||
{ a: 'spielplatz', b: 'eisdiele', radius: 160, bonus: 0.22, why: 'Spielplatz + Eisdiele: Familienglück' },
|
||||
{ a: 'badesteg', b: 'eisdiele', radius: 190, bonus: 0.18, why: 'Badesteg + Eisdiele: Sommer am See' },
|
||||
{ a: 'museum', b: 'cafe', radius: 170, bonus: 0.15, why: 'Museum + Café: Kultur mit Pause' },
|
||||
{ a: 'radstation', b: 'cafe', radius: 170, bonus: 0.15, why: 'Radstation + Café: Boxenstopp für Radler' },
|
||||
{ a: 'skilift', b: 'aussicht', radius: 240, bonus: 0.18, why: 'Lift + Aussicht: Panorama vom Gipfel' },
|
||||
{ a: 'wellness', b: 'aussicht', radius: 200, bonus: 0.15, why: 'Wellness + Aussicht: Erholung mit Weitblick' },
|
||||
];
|
||||
|
||||
// Ziele fürs Ziele-Panel (§12.1)
|
||||
export const GOALS = [
|
||||
{ id: 'rating', label: 'Ø Bewertung ≥ 3,5 Sterne', check: s => currentAvg(s) >= 3.5 },
|
||||
{ id: 'revenue', label: 'Wertschöpfung ≥ 2500 €', check: s => s.revenueTotal >= 2500 },
|
||||
{ id: 'env', label: 'Umweltwert ≥ 45', check: s => s.env >= 45 },
|
||||
{ id: 'debt', label: 'Nicht unter −1000 € rutschen', check: s => s.budget > -1000 },
|
||||
];
|
||||
|
||||
export function currentAvg(s) {
|
||||
const g = s.stats.all;
|
||||
return g.count ? g.stars / g.count : 0;
|
||||
}
|
||||
Reference in New Issue
Block a user