From 65ca8692cbb31a65fff0903f8e1426ba9ca07011 Mon Sep 17 00:00:00 2001 From: Thomas Date: Mon, 20 Jul 2026 14:24:55 +0200 Subject: [PATCH] Abzeichen: gemeinsame Bibliothek + Sammlung im Erfolge-Tab MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Neue badges.js: 5 einheitliche Abzeichen pro Sim (Einstieg/Fortgeschritten/ Könner/Meister/Ausdauer), Bedingungen aus allgemeinen Leistungsdaten (plays/ best_stars/level) → gelten sofort für ALLE Sims. Gesperrte Abzeichen sichtbar als Ziele. ggsErfolgToStars() für Sterne aus normiertem Erfolg. Erfolge-Tab in schueler.html zeigt die Sammlung pro Simulation (auch ungespielte = Ziele). Co-Authored-By: Claude Opus 4.8 --- App/assets/js/badges.js | 76 +++++++++++++++++++++++++++++++++++++++++ App/schueler.html | 35 +++++++++++++++++++ 2 files changed, 111 insertions(+) create mode 100644 App/assets/js/badges.js diff --git a/App/assets/js/badges.js b/App/assets/js/badges.js new file mode 100644 index 0000000..411f57e --- /dev/null +++ b/App/assets/js/badges.js @@ -0,0 +1,76 @@ +/* ============================================================================ + * GeoGraSim — gemeinsame Abzeichen-Bibliothek + * ---------------------------------------------------------------------------- + * Ein einheitliches Set von 5 Abzeichen pro Simulation, dessen Freischalt- + * Bedingungen sich aus den ALLGEMEINEN Leistungsdaten (plays, best_stars, + * level) berechnen. Dadurch gelten die Abzeichen SOFORT für JEDE Sim — auch + * für die ohne eigenen Achievement-Code — und sind über alle Sims vergleichbar. + * + * Abzeichen sind bewusst auch im GESPERRTEN Zustand sichtbar (Zielvorstellung), + * die Beschreibung ist zugleich das Ziel. Sterne laufen parallel (Qualität pro + * Durchlauf); ggsErfolgToStars() bildet den normierten Erfolg (0–100) auf + * 0–5 Sterne ab. + * + * Verwendung (Client): + * GGS_BADGES.forEach(b => { var earned = b.cond(progressObj); ... }); + * progressObj = { plays, best_stars, level } (pro Sim) + * ==========================================================================*/ +(function (root) { + 'use strict'; + + // Fünf Abzeichen-Typen, für alle Sims identisch. cond(p) wird pro Sim geprüft. + var GGS_BADGES = [ + { id: 'einstieg', icon: '🌱', name: 'Einstieg', + desc: 'Ersten Durchlauf geschafft', + cond: function (p) { return (p && p.plays || 0) >= 1; } }, + { id: 'fortgeschritten', icon: '📈', name: 'Fortgeschritten', + desc: 'Level 2 erreicht', + cond: function (p) { return (p && p.level || 1) >= 2; } }, + { id: 'koenner', icon: '⭐', name: 'Könner', + desc: 'Vier Sterne geschafft', + cond: function (p) { return (p && p.best_stars || 0) >= 4; } }, + { id: 'meister', icon: '🏆', name: 'Meister', + desc: 'Fünf Sterne — Bestleistung', + cond: function (p) { return (p && p.best_stars || 0) >= 5; } }, + { id: 'ausdauer', icon: '🔥', name: 'Ausdauer', + desc: 'Acht Durchläufe', + cond: function (p) { return (p && p.plays || 0) >= 8; } } + ]; + + // Spielbare Simulationen (aktiv/beta) — Reihenfolge wie im Schulbuch. + // Auch ungespielte erscheinen in der Sammlung: alle 5 Abzeichen gesperrt = Ziel. + var GGS_SIMS = [ + { id: 'tourismustal', title: 'Tourismustal', icon: '🏔️' }, + { id: 'tourismusregion', title: 'Tourismusregion', icon: '🗺️' }, + { id: 'kofferdetektiv', title: 'Kofferdetektiv', icon: '🧳' }, + { id: 'busfahrt', title: 'Busfahrt', icon: '🚌' }, + { id: 'logistik', title: 'Logistik Europa', icon: '🚚' }, + { id: 'weltkueche', title: 'Weltküche', icon: '🍽️' }, + { id: 'farmer', title: 'Landwirtschaft', icon: '🌾' }, + { id: 'klima', title: 'Klimawächter', icon: '🌍' }, + { id: 'klima-3d', title: 'Klimawächter 3D', icon: '🌊' }, + { id: 'fluss', title: 'Flussmanagement', icon: '🏞️' }, + { id: 'energiemanager', title: 'Energiemanager', icon: '⚡' }, + { id: 'heli', title: 'Heli-Navigation', icon: '🚁' }, + { id: 'sonnensystem', title: 'Sonnensystem', icon: '🪐' }, + { id: 'eu-werkstatt', title: 'EU-Werkstatt', icon: '🏛️' } + ]; + + // Normierter Erfolg (0–100) → 0–5 Sterne. Ein Rechenweg für alle Sims. + function ggsErfolgToStars(pct) { + if (pct == null || isNaN(pct)) return 0; + return Math.max(0, Math.min(5, Math.round(pct / 20))); + } + + // Wie viele der 5 Abzeichen sind für ein Progress-Objekt freigeschaltet? + function ggsBadgeCount(p) { + var n = 0; + for (var i = 0; i < GGS_BADGES.length; i++) if (GGS_BADGES[i].cond(p)) n++; + return n; + } + + root.GGS_BADGES = GGS_BADGES; + root.GGS_SIMS = GGS_SIMS; + root.ggsErfolgToStars = ggsErfolgToStars; + root.ggsBadgeCount = ggsBadgeCount; +})(typeof window !== 'undefined' ? window : this); diff --git a/App/schueler.html b/App/schueler.html index 3bb86c3..ca5d7da 100644 --- a/App/schueler.html +++ b/App/schueler.html @@ -57,6 +57,13 @@ .badge .desc{font-size:.6rem;color:#8a8a8a} .badge.earned{border-color:#4a7c8a;background:linear-gradient(135deg,#f0f8fa,#fff)} .badge.locked-b{opacity:.4} + .badge .desc{min-height:1.6em} + .sim-coll{margin-bottom:1.1rem} + .sim-coll-head{display:flex;align-items:center;gap:.5rem;margin:.5rem 0 .45rem;font-weight:700;font-size:.82rem} + .sim-coll-head .sc-ic{font-size:1.3rem} + .sim-coll-head .sc-t{flex:1} + .sim-coll-head .sc-stars{color:#e0a838;letter-spacing:1px;font-size:.82rem} + .sim-coll-head .sc-count{font-size:.68rem;color:#8a8a8a;background:#f0eeea;border-radius:99px;padding:.12rem .55rem;font-weight:700} /* Erfolgs-Liste pro Modul */ .ach-summary{display:grid;grid-template-columns:repeat(3,1fr);gap:.6rem;margin-bottom:1.2rem} .ach-summary .card{background:#fff;border-radius:10px;padding:.9rem .8rem;text-align:center;border:1.5px solid rgba(0,0,0,.05)} @@ -87,6 +94,7 @@ .hidden{display:none} @media(max-width:550px){.mod-grid{grid-template-columns:1fr}.badge-grid{grid-template-columns:repeat(2,1fr)}} +
@@ -363,6 +371,33 @@ async function renderAchievements() { html += '
'; }); html += ''; + + // === Abzeichen-Sammlung pro Simulation (gesperrte = sichtbare Ziele) === + if (typeof GGS_SIMS !== 'undefined') { + var progBySim = {}; + allProgress.forEach(function(p){ progBySim[p.sim_id] = p; }); + html += '

Abzeichen-Sammlung

'; + html += '

Jede Simulation hat fünf Abzeichen. Blasse sind noch nicht geschafft — das sind deine Ziele.

'; + GGS_SIMS.forEach(function(sim){ + var p = progBySim[sim.id] || {plays:0, best_stars:0, level:1}; + var got = ggsBadgeCount(p); + var st = (p.best_stars||0); + var stars = '★'.repeat(st) + '☆'.repeat(Math.max(0, 5-st)); + html += '
' + + ''+sim.icon+''+sim.title+'' + + ''+stars+'' + + ''+got+' / '+GGS_BADGES.length+'
'; + html += '
'; + GGS_BADGES.forEach(function(b){ + var e = b.cond(p); + html += '
' + + '
'+b.icon+'
'+b.name+'
' + + '
'+b.desc+'
'; + }); + html += '
'; + }); + } + document.getElementById('tab-achievements').innerHTML = html; }