Tourismustal: Naturwert aktiv zurueckgewinnen (Oeko-Umbau + Aufforsten)
Antwort auf die Design-Frage 'Naturwert zurueck ausser weniger bauen': - Oeko-Umbau pro Gebaeude (Button im Gebaeude-Dialog): einmalig, 40 % des Bauwerts, dafuer -20 % Betriebskosten, +2 Naturwert, Gebaeude belastet die taegliche Natur-Regeneration nicht mehr. Sichtbar: Solardach-Overlay (auch auf Megabauwerken, 1,6x). Didaktik: Investition vs. laufende Kosten, nachhaltiges Wirtschaften (GW 4.10). - Aufforsten als wiederholbare Aktion im Baumenue (1.500 Euro): pflanzt Wald auf freie Wiese/Alm/Feld, +3 Naturwert, Werkzeug bleibt aktiv fuer Serien-Pflanzung. Wissen: Renaturierung + Schutzwald (1/3 des oesterreichischen Walds). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -127,6 +127,7 @@
|
||||
<div id="biKnowledge" class="bi-knowledge hidden"></div>
|
||||
<div class="card-buttons">
|
||||
<button id="biUpgrade" class="primary"></button>
|
||||
<button id="biEco" class="secondary"></button>
|
||||
<button id="biClose" class="secondary">Schließen</button>
|
||||
</div>
|
||||
<button id="biDemolish" class="bi-demolish">🧨 Abreißen (30 % zurück)</button>
|
||||
|
||||
@@ -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.' },
|
||||
],
|
||||
};
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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×)
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user