Live/Aktuell: allgemeine Erfolgs-Spalten Fortschritt + Lage

- _liveProgressPct/_liveMood: aus dem Live-State jeder Sim ableitbarer
  Fortschritt (%) und Lage-Ampel (health/heuristik) -> modueluebergreifend
  vergleichbar
- weltkueche: redundante "Gerichte" ersetzt durch Fortschritt-Balken + Lage-Ampel

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-22 01:05:19 +02:00
parent ebdd1ce438
commit 5f6abeeb42
+55 -6
View File
@@ -729,16 +729,12 @@ var LIVE_PRIMARY_FIELDS = {
var done = v||0; var pct = done/t; var c = pct>=1 ? '#5a8a5e' : pct>=.5 ? '#c9913a' : '#8a7a4a';
return '<span style="color:'+c+';font-weight:700">'+done+' <span style="color:#8a8a8a;font-weight:400">/ '+t+'</span></span>';
}},
{k:'completed', label:'Gerichte', fmt:function(v, row){
var s = row.state||{}; var t = s.completedTotal||0;
if (!t) return '—';
var done = v||0; var pct = done/t; var c = pct>=1 ? '#5a8a5e' : pct>=.5 ? '#c9913a' : '#8a8a8a';
return '<span style="color:'+c+';font-weight:700">'+done+' <span style="color:#8a8a8a;font-weight:400">/ '+t+'</span></span>';
}},
{k:'mistakesInDish', label:'Fehlklicks', align:'center', fmt:function(v){
var n = v||0; var c = n>=12 ? '#c07a6b' : n>=5 ? '#c9913a' : '#5a8a5e';
return '<span style="color:'+c+';font-weight:700">'+n+'</span>';
}},
{k:'__prog', label:'Fortschritt', align:'left', fmt:function(v, row){ return _liveProgressBar(row.module_id, row.state); }},
{k:'__lage', label:'Lage', align:'center', fmt:function(v, row){ return _liveMoodBadge(row.module_id, row.state); }},
],
'busfahrt': [
{k:'levelId', label:'Stufe', align:'center'},
@@ -1159,6 +1155,59 @@ function _liveNeedsHelp(simId, s) {
}
return false;
}
// Allgemeiner „aktueller Erfolg": Fortschritt (% durch die Sim) + Lage (Ampel).
// Aus dem Live-State jeder Sim ableitbar → über alle Module vergleichbar.
function _liveProgressPct(simId, s) {
if (!s) return null;
var f = function(n, d) { return d > 0 ? Math.max(0, Math.min(100, Math.round((n||0)/d*100))) : null; };
switch (simId) {
case 'weltkueche': return f(s.completed, s.completedTotal);
case 'sonnensystem': return s.taskTotal ? f(s.completed, s.taskTotal) : (s.progressPct!=null?s.progressPct:null);
case 'kofferdetektiv': return s.cases_total ? f(s.cases_solved, s.cases_total) : (s.progressPct!=null?s.progressPct:null);
case 'eu-werkstatt': return f(s.step, s.stepsTotal);
case 'energiemanager': return f(s.day, s.dayTotal);
case 'busfahrt': case 'fluggesellschaft': return f(s.orderIndex, s.totalOrders);
case 'heli': return f(s.planIndex, s.planTotal);
case 'tourismusregion': return f(s.wave, s.waveTotal);
case 'tourismustal': return f(s.goalsDone, s.goalsTotal);
case 'fluss': return s.progressPct!=null ? s.progressPct : null;
}
return s.progressPct!=null ? s.progressPct : null;
}
function _liveMood(simId, s) {
if (!s) return null;
if (s.health) return ({good:'good',ok:'good',warning:'mid',bad:'bad',critical:'bad'})[s.health] || 'mid';
if (_liveNeedsHelp(simId, s)) return 'bad';
switch (simId) {
case 'weltkueche': return (s.mistakesInDish||0) <= 2 ? 'good' : (s.mistakesInDish >= 8 ? 'bad' : 'mid');
case 'klima': case 'klima-3d': return ((s.floodedPct==null?100:s.floodedPct) < 20 && (s.budget||0) >= 0) ? 'good' : 'mid';
case 'busfahrt': case 'fluggesellschaft': return (s.budget==null?0:s.budget) >= 5000 ? 'good' : (s.budget < 0 ? 'bad' : 'mid');
case 'tourismustal': return (s.money==null?0:s.money) >= 4000 ? 'good' : (s.money < 0 ? 'bad' : 'mid');
case 'heli': return (s.totalStars||0) >= 4 ? 'good' : 'mid';
case 'fluss': case 'kofferdetektiv': case 'sonnensystem':
return s.score!=null ? (s.score>=70?'good':s.score>=40?'mid':'bad') : 'mid';
}
return 'mid';
}
function _liveProgressBar(simId, s) {
var p = _liveProgressPct(simId, s);
if (p == null) return '<span style="color:#8a8a8a"></span>';
var c = p >= 75 ? '#5a8a5e' : p >= 40 ? '#c9913a' : '#c07a6b';
return '<span style="display:inline-flex;align-items:center;gap:.3rem">'
+ '<span style="display:inline-block;width:40px;height:6px;border-radius:3px;background:#ece8dc;overflow:hidden;vertical-align:middle">'
+ '<span style="display:block;height:100%;width:'+p+'%;background:'+c+'"></span></span>'
+ '<span style="font-size:.66rem;color:#6a6a6a">'+p+'%</span></span>';
}
function _liveMoodBadge(simId, s) {
var m = _liveMood(simId, s);
if (!m) return '<span style="color:#8a8a8a"></span>';
var map = { good:['#5a8a5e','Gut'], mid:['#c9913a','OK'], bad:['#c07a6b','Kritisch'] };
var x = map[m] || map.mid;
return '<span style="display:inline-flex;align-items:center;gap:.3rem">'
+ '<span style="width:8px;height:8px;border-radius:50%;background:'+x[0]+';display:inline-block"></span>'
+ '<span style="font-size:.66rem;color:'+x[0]+';font-weight:700">'+x[1]+'</span></span>';
}
var _liveReflCache = '', _liveReflAt = 0;
// Aktiv = Lebenszeichen in den letzten 90 s. Ältere Sessions bleiben sichtbar,
// aber als „pausiert" (Lernende:r macht Pause, kann später weitermachen).