diff --git a/CLAUDE.md b/CLAUDE.md index f7893d1..1df4d29 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -31,6 +31,10 @@ window.App = { reportCode, fightId, fightStart, fightEnd, phases: [], fights: [] - `onFightsLoaded(fights)` — wird von `app.js` aufgerufen nach Report-Load (befüllt Vergleichs-Dropdown) - `reset()` — wird von `app.js` aufgerufen wenn ein neuer Report geladen wird +`window.plannerTab` (definiert in `planner.js`) stellt Hooks bereit: +- `onTabOpen()` — wird von `tabs.js` aufgerufen wenn der Planer-Tab geöffnet wird +- `importFromAnalysis(aoeEvents, refEvents, options)` — wird vom Analyse-Tab aufgerufen beim Export + ## Dateistruktur ``` index.php — PHP-Logik: Auth-Check, Variablen, require page.php @@ -41,6 +45,7 @@ templates/ topbar.php — Topbar mit Logo + Tab-Navigation + Token-Ablaufzeit tab-report.php — Report-Tab: includes report-form, fight-select, event-explorer, output-card tab-analysis.php — Analyse-Tab: Spieler-Grid + Pull-Vergleich + AoE-Timeline HTML + tab-planner.php — Planer-Tab: Plan-Liste, Mechanik-Timeline, Job-Aufstellung (in Entwicklung) report-form.php — Report-Code-Eingabe Card fight-select.php — Fight-Auswahl Dropdown Card event-explorer.php — Event Explorer Card (Ability/DataType/EventType/Spieler-Filter) @@ -50,10 +55,12 @@ css/ layout.css — App-Shell, Topbar, Tabs, Login-Overlay, Form-Helpers components.css — Cards, Inputs, Buttons, Badges, Terminal analysis.css — Spieler-Grid, AoE-Timeline, Mitigation-Icons, HP-Bar, Ref-Row + planner.css — Planer-Tab: Plan-Liste, Mechanik-Cards, Job-Slots, Ability-Modal (in Entwicklung) js/ app.js — Formular, Fight-Dropdown, Fetch, window.App State, Event Explorer tabs.js — Tab-Switching, ruft window.analysisTab.onTabOpen() auf analysis.js — Analyse-Tab: Daten laden, Spieler rendern, Timeline rendern, Pull-Vergleich + planner.js — Planer-Tab: localStorage CRUD, Plan-Rendering, Ability-Zuweisung (in Entwicklung) auth/ start.php — PKCE generieren, Session speichern, Redirect zu FFLogs callback.php — Code gegen Token tauschen, Token in Session speichern @@ -64,6 +71,9 @@ api/ debug-events.php — POST-Endpunkt: Raw Events für Event Explorer (mit Filterung) assets/ icons/mitigation/ — Lokal gespeicherte Ability-Icons (PNG, von XIVAPI) +data/ + recast-times.json — Recast-Zeiten pro Ability (in Entwicklung) + ability-equivalents.json — Funktionale Äquivalente über Jobs hinweg (in Entwicklung) debug/ schema.php — Einmaliges Schema-Explorer Tool (nicht produktiv deployen) ``` @@ -220,7 +230,7 @@ Vollständiges Schema: siehe `debug/schema.php` oder `fflogs-schema.json` ## Planer-Tab — Konzept & Roadmap ### Ziel -Raid-Cooldown-Planer: Welche Mitigation-Ability wird für welche Mechanik eingesetzt? Basierend auf Log-Daten oder manuell aufgebaut. Überlebt Browser-Neustarts via localStorage. +Raid-Cooldown-Planer: Welche Mitigation-Ability wird für welche Mechanik eingesetzt? Basierend auf Log-Daten oder manuell aufgebaut. Überlebt Browser-Neustarts via localStorage. Kein Server-State — alles im Browser. ### Datenmodell (Plan) ```json @@ -236,7 +246,9 @@ Raid-Cooldown-Planer: Welche Mitigation-Ability wird für welche Mechanik einges "id": "uuid", "name": "Fourth-Wall Fusion", "timestamp": 83000, + "phase": "Phase 1", "unmitigatedDamage": 280000, + "notes": "", "assignments": [ { "ability": "Reprisal", "job": "PLD" }, { "ability": "Shield Samba", "job": "BRD" } @@ -248,31 +260,42 @@ Raid-Cooldown-Planer: Welche Mitigation-Ability wird für welche Mechanik einges Mehrere Pläne gespeichert in `localStorage` unter `ff14-planner-plans` als Array. -### Import-Flow (erster Meilenstein) -**Ziel:** Einen bestehenden Log als saubere Mechanik-Vorlage laden — ohne vorhandene Mechaniken zu überschreiben. +### Primärer Import-Flow: Export aus dem Analyse-Tab +Der Haupteinstieg ist der Analyse-Tab — der Nutzer hat die Daten bereits geladen und sieht die Timeline. -1. Benutzer wählt Report-Code + Kampf (gleicher Flow wie im Report-Tab, eigenes Mini-Formular im Planer) -2. `api/analysis.php` wird aufgerufen → liefert `aoe_events` mit Name, Timestamp, `unmitigatedAmount` -3. Jedes AoE-Event wird als Mechanik-Kandidat angezeigt (Name + Timestamp + Rohschaden) -4. Benutzer kann einzelne Events auswählen oder alle übernehmen -5. **Merge-Logik:** Beim Import in einen bestehenden Plan werden nur Mechaniken hinzugefügt die noch nicht vorhanden sind — Matching per `abilityName` + Timestamp-Nähe (± 5s). Bestehende Assignments bleiben erhalten. -6. Neue Mechaniken werden an der richtigen Timestamp-Position eingefügt (Timeline bleibt sortiert) +1. Button **"In Planer exportieren"** erscheint im Analyse-Tab sobald Daten geladen sind (auch für den Ref-Log separat) +2. Dialog mit zwei Entscheidungen: + - **Was importieren?** "Nur Mechaniken" vs. "Mechaniken + erkannte Mitigations als Startpunkt" + - **Wohin?** Neuen Plan anlegen (Name eingeben) vs. bestehenden Plan überschreiben/mergen (Dropdown) +3. Bei Merge: explizite Bestätigung — niemals implizit überschreiben +4. AoE-Events werden zu Mechaniken; Phase-Information aus `phaseTransitions` wird mitübernommen +5. Weiterarbeiten im Planer-Tab -**Warum Merge statt Überschreiben:** Progress-Szenario — erster Import enthält Phase 1, späterer Import (weiter im Fight) fügt Phase 2 hinzu ohne Phase-1-Planung zu verlieren. +**Merge-Logik:** Mechaniken gelten als identisch wenn `abilityName` gleich und `|timestamp_a - timestamp_b| < 5000ms`. Nur neue Mechaniken werden hinzugefügt, bestehende Assignments bleiben erhalten. Neue Mechaniken werden timestamp-sortiert eingefügt. -### Geplante Features (Übersicht) +**Warum Merge statt Überschreiben:** Progress-Szenario — erster Import enthält Phase 1, späterer Import fügt Phase 2 hinzu ohne Phase-1-Planung zu verlieren. -| Prio | Feature | Beschreibung | +### Implementierungs-Reihenfolge + +| Schritt | Feature | Beschreibung | |---|---|---| -| 1 | **Import-Flow** | Log → Mechanik-Timeline, Merge bei Teilimporten | -| 2 | **Jobaufstellung** | 8 Slots (2 Tank, 2 Healer, 4 DPS), Auswahl bestimmt verfügbare Spells | -| 3 | **Cooldown-Zuweisung** | Pro Mechanik Abilities zuweisen/entfernen per Klick | -| 4 | **DR-Simulation** | `simuliert = unmitigated × ∏(1 − dr_i)` live berechnet beim Toggle | -| 5 | **Recast-Tracking** | Recast-Datenbank pro Ability; Konflikt-Warnung wenn CD noch läuft | -| 6 | **Coverage-Ansicht** | Gantt-Chart: Mechaniken auf X-Achse, Buff-Dauer als Balken | -| 7 | **Analyse-Overlay** | Planer-Tab: Vergleich geplanter vs. tatsächlich genutzter CDs (Job-basiertes Matching, nicht Spielername) | -| 8 | **Shield-Schätzung** | Empirisch aus Log-Durchschnitt (`absorbed`-Werte), nicht exakt | -| 9 | **JSON-Export/Import** | Plan als Datei teilen mit Raidkollegen | +| 1 | **Datenfundament** | Plan-Datenmodell + localStorage CRUD (create, read, update, delete, copy) | +| 2 | **Tab-Grundgerüst** | Leere Tab-Hülle wie Analyse-Tab; Plan-Liste; Mechanik-Timeline (read-only) | +| 3 | **Import aus Analyse-Tab** | Export-Button + Dialog (s. oben); `window.plannerTab.importFromAnalysis()` | +| 4 | **Jobaufstellung** | 8 Slots mit Job-Dropdown; bestimmt verfügbare Abilities in Schritt 5 | +| 5 | **Ability-Zuweisung** | Pro Mechanik Abilities per Modal-Picker hinzufügen/entfernen | +| 6 | **Recast-Konflikt** | `data/recast-times.json`; rote Warnung wenn CD zwischen zwei Mechaniken noch läuft | +| 7 | **DR-Simulation** | `simuliert = unmitigated × ∏(1 − dr_i)`; grün/rot ob Spieler laut Simulation überlebt | +| 8 | **Job-Äquivalente** | `data/ability-equivalents.json`; auto-substituieren beim Job-Wechsel | +| 9 | **Analyse-Overlay** | Vergleich geplanter vs. tatsächlich genutzter CDs im Analyse-Tab | + +Schritte 1–3 = nutzbarer MVP. Schritte 4–6 = praktisch einsetzbar. 7–9 = Power-Features. + +### UI-Paradigma +- Visuell dem Analyse-Tab ähneln (Cards, gleiche CSS-Variablen, einheitliches Look & Feel) +- Mechaniken als vertikale Timeline-Cards +- Ability-Picker als **Modal** (kein Inline-Dropdown) +- Nicht für mobile Geräte ausgelegt ### Spell-Verfügbarkeit nach Job Jobaufstellung → verfügbare Abilities (Subset von `MITIGATION_ABILITIES`): @@ -301,17 +324,31 @@ Jobaufstellung → verfügbare Abilities (Subset von `MITIGATION_ABILITIES`): | RDM | Addle, Magick Barrier | | PCT | Addle, Tempera Coat, Tempera Grassa | -### Recast-Zeiten (geplante Datenbank) -Wird benötigt für Konflikt-Erkennung. Beispiele: +### Job-Äquivalente (`data/ability-equivalents.json`) +Abilities die funktional gleich sind aber unterschiedliche Namen haben — relevant beim Job-Wechsel im Slot: + +| Gruppe | Abilities | +|---|---| +| 15% Party-Mitigation | Troubadour, Tactician, Shield Samba | +| 10% Ground-Barrier | Sacred Soil, Kerachole | + +Reprisal, Feint und Addle sind identische Ability-Namen über Jobs hinweg — kein Mapping nötig, die gleiche Ability bleibt einfach bestehen. + +**Verhalten beim Job-Wechsel:** Assignment wird auto-substituiert wenn Äquivalent für neuen Job existiert (mit Hinweis "automatisch gemappt"). Kein Äquivalent → Assignment ausgegraut (nicht gelöscht, Nutzer entscheidet). + +### Recast-Zeiten (`data/recast-times.json`) +Wird für Konflikt-Erkennung benötigt (Schritt 6). Vollständige Liste wird beim Implementieren vervollständigt, Beispiele: - Reprisal: 60s - Feint / Addle: 90s +- Dark Missionary / Heart of Light: 90s - Troubadour / Tactician / Shield Samba: 120s - Temperance: 120s -- Dark Missionary / Heart of Light: 90s ### Technische Entscheidungen -- **Persistenz:** `localStorage` — kein Backend nötig, mehrere Pläne möglich +- **Persistenz:** `localStorage` unter `ff14-planner-plans` — kein Backend nötig - **IDs:** `crypto.randomUUID()` für Plan- und Mechanik-IDs -- **Merge-Matching:** Mechaniken gelten als identisch wenn `abilityName` gleich und `|timestamp_a - timestamp_b| < 5000ms` -- **Keine Spielernamen im Planer:** Assignments sind Job-basiert (`{ ability, job }`), damit Pläne übertragbar sind -- **Analyse-Tab Overlay (Prio 7):** Job aus tatsächlichem Pull → lookup welche Ability dieser Job im Plan hatte → Soll/Ist-Vergleich +- **Keine Spielernamen:** Assignments sind Job-basiert (`{ ability, job }`), damit Pläne übertragbar sind +- **Kein Ability-Stacking:** FFXIV erlaubt keine doppelte Anwendung derselben Ability — jede Ability kommt pro Mechanik maximal einmal vor, doppelte Instanzen desselben Jobs im Static sind daher kein Sonderfall +- **Analyse-Overlay (Schritt 9):** Job aus tatsächlichem Pull → lookup welche Ability dieser Job im Plan hatte → Soll/Ist-Vergleich (kein Spielername-Matching nötig) +- **Shield-Attribution:** Aktuell nicht lösbar — `absorbed` im `damage`-Event ist ein Gesamtwert ohne Aufschlüsselung per Shield. Zu untersuchen: ob `calculatedheal`-Events die Shield-Kapazität beim Auftragen mitliefern. Vorerst zurückgestellt. +- **Plan kopieren:** Duplicate-Funktion für Plan-Varianten ("Week 3 v2") ohne Original zu verlieren