@@ -424,6 +424,28 @@ window.CD_AUTO_PLANNER = (function() {
424424 return ( window . classColors && window . classColors [ ( cls || '' ) . toUpperCase ( ) ] ) || '#FFFFFF' ;
425425 }
426426
427+ // Normalisiert einen Spec-Namen für toleranten Vergleich:
428+ // - Kleinschreibung, Whitespace weg
429+ // - "1"-Suffix entfernen (Protection1 → protection, Holy1 → holy, ...)
430+ // - deutsche/alternative Schreibweisen auf den englischen Kern mappen
431+ // So matchen Roster-Spec und required-Spec auch bei Format-Unterschieden.
432+ var _SPEC_ALIASES = {
433+ 'schutz' : 'protection' , 'vergeltung' : 'retribution' , 'heilig' : 'holy' ,
434+ 'wiederherstellung' : 'restoration' , 'verstaerkung' : 'enhancement' ,
435+ 'verstärkung' : 'enhancement' , 'elementar' : 'elemental' ,
436+ 'disziplin' : 'discipline' , 'schatten' : 'shadow' , 'waechter' : 'guardian' ,
437+ 'wächter' : 'guardian' , 'wildheit' : 'feral' , 'gleichgewicht' : 'balance' ,
438+ 'blut' : 'blood' , 'frost' : 'frost' , 'unheilig' : 'unholy' ,
439+ 'braumeister' : 'brewmaster' , 'nebelwirker' : 'mistweaver' , 'windläufer' : 'windwalker' ,
440+ 'windlaeufer' : 'windwalker'
441+ } ;
442+ function normalizeSpec ( spec ) {
443+ var s = String ( spec || '' ) . toLowerCase ( ) . trim ( ) . replace ( / \s + / g, '' ) ;
444+ s = s . replace ( / [ 0 - 9 ] + $ / , '' ) ; // "protection1" → "protection"
445+ if ( _SPEC_ALIASES [ s ] ) s = _SPEC_ALIASES [ s ] ;
446+ return s ;
447+ }
448+
427449 function getPlayersOfClass ( cls , requiredRole , requiredSpec ) {
428450 // Immer dynamisch das aktuellste Roster laden
429451 var currentRoster = window . effectiveRoster || window . rosterData || [ ] ;
@@ -433,11 +455,11 @@ window.CD_AUTO_PLANNER = (function() {
433455
434456 // 2. Spec-Filter hat Vorrang vor Role-Filter
435457 if ( requiredSpec && requiredSpec . length > 0 ) {
436- var playerSpec = p . spec || p . specName || '' ;
437- if ( ! playerSpec ) return true ;
458+ var playerSpec = p . spec || p . specName || p . specialization || '' ;
459+ if ( ! playerSpec ) return true ; // Spieler ohne Spec-Angabe nicht ausschließen
438460 var specList = Array . isArray ( requiredSpec ) ? requiredSpec : [ requiredSpec ] ;
439- var pSpecLower = playerSpec . toLowerCase ( ) ;
440- return specList . some ( function ( s ) { return String ( s ) . toLowerCase ( ) === pSpecLower ; } ) ;
461+ var pSpecNorm = normalizeSpec ( playerSpec ) ;
462+ return specList . some ( function ( s ) { return normalizeSpec ( s ) === pSpecNorm ; } ) ;
441463 }
442464
443465 // 3. Role-Filter (wenn kein Spec-Filter gesetzt)
@@ -987,10 +1009,12 @@ window.CD_AUTO_PLANNER = (function() {
9871009 } ) ;
9881010 }
9891011
990- var recIds = { } ;
991- recommended . forEach ( function ( s ) { recIds [ String ( s . spellId ) ] = true ; } ) ;
1012+ // "Alle CDs" zeigt ALLE wählbaren Cooldowns — auch die oben empfohlenen.
1013+ // (Früher wurden empfohlene spellIds hier ausgeschlossen, wodurch z.B. die
1014+ // Aura der Hingabe komplett fehlte, sobald sie für irgendeine Spec empfohlen
1015+ // war. Das hat den Prot-Pala-Eintrag unauffindbar gemacht.)
9921016 var allCDs = cooldownsDB . filter ( function ( cd ) {
993- return cd . name && cd . spellId && cd . name . indexOf ( '---' ) !== 0 && cd . name . indexOf ( '-- ' ) !== 0 && cd . type !== 'Personal' && ! recIds [ String ( cd . spellId ) ] ;
1017+ return cd . name && cd . spellId && cd . name . indexOf ( '---' ) !== 0 && cd . name . indexOf ( '-- ' ) !== 0 && cd . type !== 'Personal' ;
9941018 } ) ;
9951019 var byClassA = { } ;
9961020 allCDs . forEach ( function ( cd ) {
@@ -1017,14 +1041,39 @@ window.CD_AUTO_PLANNER = (function() {
10171041 return html ;
10181042 }
10191043
1044+ // Markiert die Auto-Assign-Vorschau als veraltet (nach Event-Änderungen),
1045+ // ohne sie neu zu berechnen. Wird durch "Auto-Assign" wieder aufgehoben.
1046+ function markPreviewStale ( ) {
1047+ var btn = document . getElementById ( 'btn-auto-assign' ) ;
1048+ if ( btn && ! btn . dataset . _origText ) {
1049+ btn . dataset . _origText = btn . innerHTML ;
1050+ }
1051+ if ( btn ) {
1052+ btn . classList . add ( 'animate-pulse' ) ;
1053+ btn . style . boxShadow = '0 0 0 2px #f59e0b' ;
1054+ btn . title = 'Events wurden geändert — klicke Auto-Assign, um die Vorschau zu aktualisieren.' ;
1055+ }
1056+ updateStatus ( 'Events geändert — "Auto-Assign" klicken, um die Vorschau neu zu berechnen.' ) ;
1057+ }
1058+
1059+ function clearPreviewStale ( ) {
1060+ var btn = document . getElementById ( 'btn-auto-assign' ) ;
1061+ if ( btn ) {
1062+ btn . classList . remove ( 'animate-pulse' ) ;
1063+ btn . style . boxShadow = '' ;
1064+ btn . title = '' ;
1065+ }
1066+ }
1067+
10201068 function runAutoAssign ( ) {
10211069
10221070 rosterRef = window . effectiveRoster || window . rosterData || [ ] ;
1023-
1071+
10241072 var timeline = generateTimeline ( ) ;
10251073 assignments = autoAssign ( timeline ) ;
10261074 renderTimeline ( assignments ) ;
10271075 renderEventManager ( ) ;
1076+ clearPreviewStale ( ) ;
10281077 // Manager-Schutz erneut anwenden (neu erzeugte Felder)
10291078 if ( typeof window . _autoPlannerApplyProtection === 'function' ) {
10301079 window . _autoPlannerApplyProtection ( ) ;
@@ -1140,7 +1189,8 @@ window.CD_AUTO_PLANNER = (function() {
11401189 var key = e . target . dataset . key ;
11411190 if ( ! eventOverrides [ key ] ) eventOverrides [ key ] = { } ;
11421191 eventOverrides [ key ] . disabled = ! e . target . checked ;
1143- runAutoAssign ( ) ;
1192+ renderEventManager ( ) ;
1193+ markPreviewStale ( ) ;
11441194 } ) ;
11451195 } ) ;
11461196
@@ -1207,7 +1257,8 @@ window.CD_AUTO_PLANNER = (function() {
12071257 if ( ! confirm ( 'Event wirklich löschen?' ) ) return ;
12081258 customEvents = customEvents . filter ( function ( evt ) { return evt . _key !== key ; } ) ;
12091259 delete eventOverrides [ key ] ;
1210- runAutoAssign ( ) ;
1260+ renderEventManager ( ) ;
1261+ markPreviewStale ( ) ;
12111262 } ) ;
12121263 } ) ;
12131264
@@ -1227,7 +1278,8 @@ window.CD_AUTO_PLANNER = (function() {
12271278 eventDuration : 0 ,
12281279 requiredCDs : [ ]
12291280 } ) ;
1230- runAutoAssign ( ) ;
1281+ renderEventManager ( ) ;
1282+ markPreviewStale ( ) ;
12311283 } ) ;
12321284 }
12331285 }
@@ -1241,7 +1293,10 @@ window.CD_AUTO_PLANNER = (function() {
12411293 if ( ! eventOverrides [ key ] ) eventOverrides [ key ] = { } ;
12421294 eventOverrides [ key ] [ field ] = value ;
12431295 }
1244- runAutoAssign ( ) ;
1296+ // Event-Änderungen NICHT mehr sofort in die Vorschau verteilen — erst auf
1297+ // "Auto-Assign". Nur den Event-Manager neu zeichnen und Vorschau als veraltet markieren.
1298+ renderEventManager ( ) ;
1299+ markPreviewStale ( ) ;
12451300 }
12461301
12471302 // ── Trigger-Picker pro Event ──
@@ -1389,6 +1444,7 @@ window.CD_AUTO_PLANNER = (function() {
13891444 }
13901445 document . body . removeChild ( overlay ) ;
13911446 renderEventManager ( ) ;
1447+ markPreviewStale ( ) ;
13921448 } ) ;
13931449 modal . querySelector ( '#trg-pick-cancel' ) . addEventListener ( 'click' , function ( ) {
13941450 document . body . removeChild ( overlay ) ;
@@ -2676,6 +2732,52 @@ window.CD_AUTO_PLANNER = (function() {
26762732 }
26772733 } , 500 ) ;
26782734 setTimeout ( function ( ) { clearInterval ( w ) ; } , 15000 ) ;
2735+ } ,
2736+
2737+ // Diagnose-Helper: in der Browser-Konsole `CD_AUTO_PLANNER.debugRoster()` aufrufen,
2738+ // um zu sehen, welche class/spec/roles-Werte die Spieler tatsächlich haben.
2739+ // Zeigt auch, wie normalizeSpec sie interpretiert.
2740+ debugRoster : function ( filterClass ) {
2741+ var roster = window . effectiveRoster || window . rosterData || [ ] ;
2742+ var rows = roster
2743+ . filter ( function ( p ) { return ! filterClass || ( p . class || '' ) . toUpperCase ( ) === filterClass . toUpperCase ( ) ; } )
2744+ . map ( function ( p ) {
2745+ return {
2746+ name : p . name ,
2747+ class : p . class ,
2748+ spec : p . spec || p . specName || p . specialization || '(keine)' ,
2749+ specNormalisiert : normalizeSpec ( p . spec || p . specName || p . specialization || '' ) ,
2750+ roles : ( p . roles || [ ] ) . join ( ',' )
2751+ } ;
2752+ } ) ;
2753+ console . table ( rows ) ;
2754+ return rows ;
2755+ } ,
2756+
2757+ // Testet, welche Spieler für eine Klasse+Spec gefunden werden.
2758+ // z.B. CD_AUTO_PLANNER.debugMatch('PALADIN', 'Protection1')
2759+ debugMatch : function ( cls , spec ) {
2760+ var players = getPlayersOfClass ( cls , null , spec ? [ spec ] : null ) ;
2761+ console . log ( 'Treffer für' , cls , spec || '(alle)' , '→' , players ) ;
2762+
2763+ // Detail-Analyse: warum matcht/matcht nicht jeder Spieler der Klasse?
2764+ var roster = window . effectiveRoster || window . rosterData || [ ] ;
2765+ var detail = roster
2766+ . filter ( function ( p ) { return ( p . class || '' ) . toUpperCase ( ) === ( cls || '' ) . toUpperCase ( ) ; } )
2767+ . map ( function ( p ) {
2768+ var pSpec = p . spec || p . specName || p . specialization || '' ;
2769+ var pNorm = normalizeSpec ( pSpec ) ;
2770+ var reqNorm = spec ? normalizeSpec ( spec ) : '(kein Spec-Filter)' ;
2771+ return {
2772+ name : p . name ,
2773+ spec_roh : pSpec || '(keine)' ,
2774+ spec_normalisiert : pNorm ,
2775+ gesucht_normalisiert : reqNorm ,
2776+ match : spec ? ( pNorm === normalizeSpec ( spec ) ) : true
2777+ } ;
2778+ } ) ;
2779+ console . table ( detail ) ;
2780+ return players ;
26792781 }
26802782 } ;
26812783} ) ( ) ;
0 commit comments