diff --git a/App/sims/tourismustal/game.html b/App/sims/tourismustal/game.html index ea9e2ab..68cb4de 100644 --- a/App/sims/tourismustal/game.html +++ b/App/sims/tourismustal/game.html @@ -127,6 +127,7 @@
+
diff --git a/App/sims/tourismustal/js/data.js b/App/sims/tourismustal/js/data.js index 4fb2d81..de17b5a 100644 --- a/App/sims/tourismustal/js/data.js +++ b/App/sims/tourismustal/js/data.js @@ -83,6 +83,11 @@ export const BUILDINGS = { zone: 'valley', staff: 2, summerCap: 80, satBonus: 4, desc: 'Wasserspiele & Kletterburg für Familien – 80 Sommergäste und viel Zufriedenheit.', }, + forest: { + name: 'Aufforsten', emoji: '🌲', cost: 1500, upkeep: 0, + zone: 'any', staff: 0, action: 'plant', + desc: 'Pflanzt Wald auf eine freie Wiese: +3 Naturwert. Beliebig oft wiederholbar – so gestaltest du Natur aktiv.', + }, }; // Start im Sommer: ruhige Aufbauphase, der erste Winter wird zur Bewährungsprobe @@ -133,7 +138,7 @@ export const SCENARIOS = { deadlineDay: 600, goalsTitle: 'Ziele bis Ende 1979', prebuilt: [], - startUnlocked: ['restaurant'], // kleine Schritte! + startUnlocked: ['restaurant', 'forest'], // kleine Schritte! stageRow: 8, // Hochlagen anfangs wolkenverhangen quests: QUESTS_PIONIER, goals: [ @@ -748,4 +753,8 @@ export const KNOWLEDGE = { playground: [ { term: 'Zielgruppe Familien', text: 'Familien sind der Wachstumsmotor des Sommertourismus: Serfaus-Fiss-Ladis hat mit Familienangeboten seine Sommernächtigungen in 10 Jahren fast verdoppelt (+95 %).' }, ], + forest: [ + { term: 'Renaturierung', text: 'Aufforstung und Almpflege geben der Natur Fläche zurück. Viele Tourismusregionen investieren gezielt – die Landschaft IST ihr Kapital.' }, + { term: 'Schutzwald', text: 'Bergwald schützt Täler vor Lawinen und Muren. In Österreich ist rund ein Drittel des Waldes Schutzwald.' }, + ], }; diff --git a/App/sims/tourismustal/js/iso.js b/App/sims/tourismustal/js/iso.js index 1e70244..9874895 100644 --- a/App/sims/tourismustal/js/iso.js +++ b/App/sims/tourismustal/js/iso.js @@ -1757,6 +1757,7 @@ export class IsoRenderer { ctx.fill(); } } + if (tile.eco) this._drawSolar(x, y, !!tile.mega || (BUILDINGS[type]?.size || 1) > 1); return; } switch (type) { @@ -1960,6 +1961,30 @@ export class IsoRenderer { break; } } + if (tile.eco) this._drawSolar(x, y, !!tile.mega); + } + + // Solardach: Zeichen des Öko-Umbaus – dunkles Panel mit weißen Stegen + // und kleinem Glanzpunkt, stilisiert aufs Dach gesetzt. + _drawSolar(x, y, big = false) { + const ctx = this.ctx; + const s = big ? 1.6 : 1; + ctx.save(); + ctx.translate(x - 3 * s, y - (big ? 36 : 27)); + ctx.scale(s, s); + ctx.fillStyle = '#2d4a66'; + ctx.beginPath(); + ctx.moveTo(-9, 3); ctx.lineTo(1, -2); ctx.lineTo(10, 2.5); ctx.lineTo(0, 7.5); + ctx.closePath(); ctx.fill(); + ctx.strokeStyle = 'rgba(255,255,255,0.55)'; + ctx.lineWidth = 0.7; + ctx.beginPath(); + ctx.moveTo(-5.5, 4.6); ctx.lineTo(4.5, -0.4); + ctx.moveTo(-2, 6); ctx.lineTo(8, 1); + ctx.stroke(); + ctx.fillStyle = 'rgba(255,255,255,0.85)'; + ctx.beginPath(); ctx.arc(6.5, 0.6, 1, 0, 7); ctx.fill(); + ctx.restore(); } // Seil-Endpunkte eines Lifts: a = Talstation, b = Bergstation (Screen-Koordinaten) diff --git a/App/sims/tourismustal/js/main.js b/App/sims/tourismustal/js/main.js index e80da5a..7473a7d 100644 --- a/App/sims/tourismustal/js/main.js +++ b/App/sims/tourismustal/js/main.js @@ -1,7 +1,7 @@ // Einstiegspunkt: Szenenwahl → Spielstart. Verdrahtet Modell, Renderer, // Audio und UI und treibt die laufende Zeit an (einstellbare Zeitskala). -import { createState, simulateDay, canPlace, place, demolish, upgrade, buildingReport } from './model.js'; +import { createState, simulateDay, canPlace, place, demolish, upgrade, ecoUpgrade, buildingReport } from './model.js'; import { BUILDINGS, EVENTS, MILESTONES, DECISIONS, HINTS, ADVISOR, BUILD_INFO } from './data.js'; import { IsoRenderer } from './iso.js'; import { UI } from './ui.js'; @@ -102,6 +102,16 @@ function inspectBuilding(c, r) { telemetry.log('upgrade', { c, r, cost: res.cost, level: res.level }); ui._closeBuilding(); // Karte schließen → man sieht die Baustelle }, + onEco: () => { + const res = ecoUpgrade(state, c, r); + if (!res.ok) { ui.toast(`❌ ${res.reason}`); audio.error(); return; } + audio.build(); + renderer.addFloat('🌱 Öko-Umbau!', '#3f8f5e', c + 0.5, r - 0.5, true); + ui.updateKPIs(); + ui.toast(`🌱 ${res.name} läuft jetzt als Öko-Betrieb (−${res.cost.toLocaleString('de-AT')} €): −20 % Betriebskosten, +2 Naturwert.`, 6000); + telemetry.log('eco_upgrade', { c, r, cost: res.cost }); + inspectBuilding(c, r); // Karte mit neuem Status + }, onDemolish: () => { const res = demolish(state, c, r); if (!res.ok) { ui.toast(`❌ ${res.reason}`); audio.error(); return; } @@ -130,6 +140,13 @@ function onTileTap(c, r) { return; } const res = place(state, type, c, r); + if (res.planted) { + ui.toast('🌲 Wald gepflanzt: +3 Naturwert. Weiter tippen zum Aufforsten!', 4000); + audio.build(); + ui.updateKPIs(); + telemetry.log('forest_planted', { c, r, moneyLeft: state.money }); + return; // Werkzeug bleibt aktiv – Aufforsten in Serie + } ui.toast(`✅ ${BUILDINGS[type].name} gebaut.`); ui.clearBuildSelection(); // Baumodus verlassen → sicher navigieren audio.build(); diff --git a/App/sims/tourismustal/js/model.js b/App/sims/tourismustal/js/model.js index a261aeb..ea3d839 100644 --- a/App/sims/tourismustal/js/model.js +++ b/App/sims/tourismustal/js/model.js @@ -305,6 +305,9 @@ export function canPlace(state, type, c, r) { if (type === 'platform' && tile.elev < 5) { return { ok: false, reason: 'Die Aussichtsplattform braucht Aussicht – hoch am Berg bauen (weiter oben)!' }; } + if (type === 'forest' && !['meadow', 'alm', 'field'].includes(tile.terrain)) { + return { ok: false, reason: 'Aufforsten geht nur auf freien Wiesen, Almen oder Feldern.' }; + } if (type === 'access') { let nearRoad = false; for (let rr = r - 1; rr <= r + 1 && !nearRoad; rr++) { @@ -320,6 +323,14 @@ export function canPlace(state, type, c, r) { export function place(state, type, c, r) { const def = BUILDINGS[type]; const tile = state.map[r][c]; + // Aktion statt Gebäude: Aufforsten pflanzt Wald auf die Kachel + if (def.action === 'plant') { + state.money -= def.cost; + tile.terrain = 'forest'; + tile.deco = null; + state.nature = Math.min(100, state.nature + 3); + return { planted: true }; + } const wasForest = tile.terrain === 'forest'; const size = def.size || 1; state.buildings.push({ type, c, r, alt: tile.elev, size }); @@ -476,7 +487,7 @@ export function buildingReport(state, c, r) { const def = BUILDINGS[b.type]; const level = b.level || 1; const sf = seasonFactor(b.type, state.season); - const dailyUpkeep = Math.round(effVal(b, 'upkeep') * sf); + const dailyUpkeep = Math.round(effVal(b, 'upkeep') * sf * (b.eco ? 0.8 : 1)); const dailyWage = Math.round(effVal(b, 'staff') * sf) * state.scenario.wage; const lines = []; if (def.cap) lines.push(`Förderleistung: ${effVal(b, 'cap')} Gäste/Tag`); @@ -489,6 +500,7 @@ export function buildingReport(state, c, r) { if (def.boost) lines.push(`Verstärker: Bikepark & Panoramaweg im Umkreis +${Math.round(def.boost * 100)} % Gäste`); if (def.trailLen) lines.push(`Flowtrails: ${def.trailLen} Abschnitte talwärts`); if (b.mega) lines.push('🏛️ Skaleneffekt: 4-fache Leistung, aber nur 3-fache Betriebskosten'); + if (b.eco) lines.push('🌱 Öko-Betrieb: −20 % Betriebskosten, schont die Natur (Solardach)'); // Aktuelle Probleme dieses Gebäudes (erscheinen im Klick-Dialog) const problems = []; @@ -513,6 +525,8 @@ export function buildingReport(state, c, r) { knowledge: KNOWLEDGE[b.type] || [], canUpgrade: level < MAX_LEVEL && !b.underConstruction, nextCost: level < MAX_LEVEL ? upgradeCost(b.type, level) : null, + eco: !!b.eco, + ecoCost: b.eco ? null : Math.round(def.cost * 0.4 * (b.mega ? 4 : 1)), }; } @@ -521,6 +535,27 @@ export function upgradeCost(type, level) { return Math.round(BUILDINGS[type].cost * (0.25 + 0.25 * level)); } +// Öko-Umbau: einmalig pro Gebäude. Teuer (40 % des Bauwerts), dafür −20 % +// Betriebskosten (Solar/Wärmepumpe), +2 Naturwert sofort, und das Gebäude +// belastet die tägliche Natur-Regeneration nicht mehr. Sichtbar: Solardach. +export function ecoUpgrade(state, c, r) { + const ref = state.map[r]?.[c]?.buildingRef; + if (ref) ({ c, r } = ref); + const tile = state.map[r]?.[c]; + const b = state.buildings.find(x => x.c === c && x.r === r); + if (!tile?.building || !b) return { ok: false, reason: 'Hier steht kein Gebäude.' }; + if (b.eco) return { ok: false, reason: 'Läuft schon als Öko-Betrieb.' }; + if (b.underConstruction) return { ok: false, reason: 'Wird gerade umgebaut – bitte warten.' }; + const def = BUILDINGS[b.type]; + const cost = Math.round(def.cost * 0.4 * (b.mega ? 4 : 1)); + if (state.money < cost) return { ok: false, reason: `Öko-Umbau kostet ${cost.toLocaleString('de-AT')} € – nicht genug Geld.` }; + state.money -= cost; + b.eco = true; + tile.eco = true; + state.nature = Math.min(100, state.nature + 2); + return { ok: true, cost, name: def.name }; +} + export function upgrade(state, c, r) { const ref = state.map[r]?.[c]?.buildingRef; if (ref) ({ c, r } = ref); @@ -767,7 +802,7 @@ export function simulateDay(state) { // Betriebskosten je Gast (Liftstrom, Reinigung, Wäsche …): 55 % vom Umsatz const opsCost = Math.round(income * 0.58); const upkeep = Math.round(state.buildings.reduce((a, b) => - a + effVal(b, 'upkeep') * seasonFactor(b.type, state.season), 0)); + a + effVal(b, 'upkeep') * seasonFactor(b.type, state.season) * (b.eco ? 0.8 : 1), 0)); const wages = staffWorking * S.wage; state.money = Math.round(state.money + income - opsCost - upkeep - wages - cannonCost); state.lastNet = Math.round(income - opsCost - upkeep - wages - cannonCost); @@ -791,7 +826,7 @@ export function simulateDay(state) { // --- Natur erholt sich langsam --- state.nature = Math.min(100, - state.nature + 0.04 - state.buildings.length * 0.004); + state.nature + 0.04 - state.buildings.filter(b => !b.eco).length * 0.004); // --- Umbau-Fertigstellung (Fallback) --- // Primär animiert der Renderer den Umbau (10 s Spielzeit = 5 Tage bei 1×) diff --git a/App/sims/tourismustal/js/ui.js b/App/sims/tourismustal/js/ui.js index 859dc33..5a90239 100644 --- a/App/sims/tourismustal/js/ui.js +++ b/App/sims/tourismustal/js/ui.js @@ -660,6 +660,15 @@ export class UI { up.disabled = true; up.classList.remove('hidden'); } + // Öko-Umbau (Solar & Co.): einmalig, −20 % Betriebskosten, +Natur + const eco = $('biEco'); + if (report.eco) { + eco.textContent = '🌱 Öko-Betrieb ✓'; + eco.disabled = true; + } else { + eco.textContent = `🌱 Öko-Umbau (${report.ecoCost.toLocaleString('de-AT')} €)`; + eco.disabled = handlers.money < report.ecoCost; + } this.api.setSpeed(0); this.markSpeed(0); $('buildingOverlay').classList.remove('hidden'); @@ -677,6 +686,10 @@ export class UI { audio.click(); this._biHandlers?.onUpgrade?.(this._biReport); }); + $('biEco').addEventListener('click', () => { + audio.click(); + this._biHandlers?.onEco?.(this._biReport); + }); $('biDemolish').addEventListener('click', () => { audio.click(); this._biHandlers?.onDemolish?.(this._biReport);