ff14-mitigator/CLAUDE.md
xziino d792d5b718 Initial commit: FFLogs mitigation analyzer
Two-tab app: report viewer + analysis tab with AoE timeline,
per-player mitigation icons (local XIVAPI PNGs), and fight-wide
buff/debuff window tracking.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 10:42:38 +02:00

185 lines
8.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# ff14-mitigator — FFLogs Mitigation Analyzer
## Projekt
PHP/HTML/JS-Tool zum Analysieren von FFXIV-Raidlogs via FFLogs OAuth2 PKCE + GraphQL API.
Kein Framework, kein Composer, kein npm — Plain PHP für Shared Hosting.
Zwei Tabs:
- **Report-Tab**: Report-Code eingeben, Fight auswählen, Raw-JSON-Ausgabe
- **Analyse-Tab**: Spielerübersicht + AoE-Timeline mit Mitigation-Tracking
## Architektur & Konventionen
### Trennung von PHP, HTML und JS
- **PHP-Logik** gehört ausschließlich in `index.php` (und API/Auth-Endpunkte). Keine Geschäftslogik in Templates.
- **HTML** gehört in `templates/`. Jede logisch in sich geschlossene Komponente ist eine eigene Datei.
- **CSS** gehört in `css/`. Jede CSS-Datei hat einen klar abgegrenzten Scope (base, layout, components, analysis).
- **JavaScript** gehört in `js/`. Keine Inline-Scripts in Templates außer dem `<script src="...">` Tag in `page.php`.
### Template-System
`index.php` setzt alle Variablen und ruft dann `require templates/page.php` auf. Templates sind reine Ausgabe — sie lesen Variablen aus dem Scope, setzen aber keine.
### Geteilter JS-State
`window.App` in `app.js` hält den gemeinsamen State für alle Tabs:
```js
window.App = { reportCode, fightId, fightStart, fightEnd }
```
`window.analysisTab` (definiert in `analysis.js`) stellt Hooks bereit:
- `onFightSelected()` — wird von `app.js` aufgerufen wenn ein Fight gewählt wird
- `onTabOpen()` — wird von `tabs.js` aufgerufen wenn der Analyse-Tab geöffnet wird
- `reset()` — wird von `app.js` aufgerufen wenn ein neuer Report geladen wird
## Dateistruktur
```
index.php — PHP-Logik: Auth-Check, Variablen, require page.php
config.php — Konstanten (CLIENT_ID, URIs) + session_start_safe()
templates/
page.php — HTML-Skeleton (head, body), routet zu login oder app
login.php — Login-Overlay (nicht authentifiziert / Token abgelaufen)
topbar.php — Topbar mit Logo + Tab-Navigation + Token-Ablaufzeit
tab-report.php — Report-Tab: includes report-form, fight-select, output-card
tab-analysis.php — Analyse-Tab: Spieler-Grid + AoE-Timeline HTML
report-form.php — Report-Code-Eingabe Card
fight-select.php — Fight-Auswahl Dropdown Card
output-card.php — Terminal-Ausgabe Card + Initial-Hint
css/
base.css — CSS-Variablen, Reset, Basis-Styles, Feedback-Klassen
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
js/
app.js — Formular, Fight-Dropdown, Fetch, window.App State
tabs.js — Tab-Switching, ruft window.analysisTab.onTabOpen() auf
analysis.js — Analyse-Tab: Daten laden, Spieler rendern, Timeline rendern
auth/
start.php — PKCE generieren, Session speichern, Redirect zu FFLogs
callback.php — Code gegen Token tauschen, Token in Session speichern
api/
fight.php — POST-Endpunkt: Fight-Liste via GraphQL → JSON
analysis.php — POST-Endpunkt: Spieler + AoE-Events + Mitigations → JSON
assets/
icons/mitigation/ — Lokal gespeicherte Ability-Icons (PNG, von XIVAPI)
debug/
schema.php — Einmaliges Schema-Explorer Tool (nicht produktiv deployen)
```
## Design-System
CSS-Variablen in `css/base.css`:
- Hintergründe: `--bg0` (#08090d) bis `--bg3` (#1c2130), `--bgcard` (#121620)
- Akzentfarbe: `--gold` (#c8a84b)
- Text: `--t1` (hell) / `--t2` (gedimmt) / `--t3` (sehr gedimmt)
- Farben: `--blue`, `--green`, `--red`, `--orange`
- Fonts: `--font-d` Cinzel (Titel/Logo), `--font-b` Inter (Body)
- Border-Radius: `--r` (klein), `--rl` (groß)
## Analyse-Tab — Konzepte & Entscheidungen
### AoE-Erkennung
- Nur `calculateddamage` Events (post-Mitigation Snapshot) — **nicht** `damage` (Application), da sonst doppelte Events
- Gruppierung: 300ms-Zeitfenster × `abilityGameID` → Bucket
- AoE = Bucket mit ≥ 3 unterschiedlichen `targetID`s
- Auto-Attacks und Fähigkeiten mit `abilityGameID ≤ 7` werden gefiltert
- Tick-Schaden (`ev['tick'] = true`) wird ignoriert
### Spielernamen statt IDs
- `masterData.abilities` (gameID → Name) wird zusammen mit `playerDetails` in einem einzigen Query abgerufen
- `$mitigIdMap` (gameID → Mitigation-Meta) wird nur für Abilities gebaut, die tatsächlich im Report vorkommen
### Mitigation-Tracking
Getrackte party-wide Buffs + Boss-Debuffs (definiert in `MITIGATION_ABILITIES` in `api/analysis.php`):
| Ability | DR | Typ |
|---|---|---|
| Passage of Arms | 15% | buff |
| Divine Veil | Barrier | buff |
| Shake It Off | Barrier | buff |
| Dark Missionary | 10% | buff |
| Heart of Light | 10% | buff |
| Temperance | 10% | buff |
| Sacred Soil | 10% | buff |
| Expedient | 10% | buff |
| Fey Illumination | 5% | buff |
| Collective Unconscious | 10% | buff |
| Holos | 10% | buff |
| Kerachole | 10% | buff |
| Panhaima | Barrier | buff |
| Troubadour | 15% | buff |
| Tactician | 15% | buff |
| Shield Samba | 15% | buff |
| Reprisal | 10% | debuff |
| Feint | 10% | debuff |
| Addle | 10% | debuff |
**Fenster-Tracking:** `applybuff`/`applydebuff` öffnet Fenster (nur erstes pro `abilityId_sourceId`-Key, da party-wide Buffs einmal pro Partymitglied feuern). `removebuff`/`removedebuff` schließt das Fenster. Noch offene Fenster am Fight-Ende werden mit `endTime` geschlossen.
### Mitigation-Icons
- Icons lokal gespeichert in `assets/icons/mitigation/` als PNG
- Quelle: XIVAPI v2 (`https://v2.xivapi.com/api/asset?path=...&format=png`)
- Icon-Pfade per Action-Row-ID abgerufen: `https://v2.xivapi.com/api/sheet/Action/{id}?fields=Name,Icon`
- Dateinamen: kebab-case des Ability-Namens (z.B. `passage-of-arms.png`)
- Mapping in `analysis.js`: `MITIG_ICONS` Objekt (Ability-Name → lokaler Pfad)
- Darstellung: 16×16px Icon unter jedem Spieler-Target, kein Text, `title`-Tooltip mit Name + DR% + Caster
## Geplante Features
### HP-Balken pro Spieler (nächstes Feature)
Für jeden Spieler in der AoE-Timeline einen 3-Segment-Balken anzeigen, der den HP-Stand im Kontext des Treffers zeigt:
```
[████████████▓▓▓░░░░░░░░]
^verbleibend ^Schaden ^vorher schon fehlend
```
- **Grün** (links): HP nach dem Treffer (als % von MaxHP)
- **Rot/Orange** (Mitte): erlittener Schaden (als % von MaxHP)
- **Dunkelgrau** (rechts): HP die bereits vor dem Treffer fehlten
- Grünton dynamisch: >50% grün, 2550% gelb/amber, <25% rot
- Balken sitzt direkt unter Name+Schaden-Zeile in der Spieler-Box
**Datenbedarf:** FFLogs `calculateddamage` Events enthalten `hitPoints` (HP vor dem Treffer) und `maxHitPoints`. Diese müssen in `api/analysis.php` pro Target mitgegeben werden.
**Backend-Änderung:** In `$buckets[$key]['targets'][$tgtId]` zusätzlich `hp` und `maxHp` aus dem letzten Event speichern und in der Response durchreichen.
## Konfiguration
- `config.php`: `CLIENT_ID`, `REDIRECT_URI`, `DEV_MODE` anpassen
- `DEV_MODE = true` deaktiviert SSL-Verifizierung (nur lokal, nie in Produktion)
- `session.cookie_secure` ist bei `DEV_MODE` automatisch `false`
## FFLogs API
- OAuth2 PKCE (kein Client Secret, öffentliche App)
- App registrieren: https://www.fflogs.com/api/clients/
- GraphQL Endpoint (user-scoped): `https://www.fflogs.com/api/v2/user`
- Token Endpoint: `https://www.fflogs.com/oauth/token`
- Kein Refresh Token für öffentliche Clients abgelaufene Sessions starten PKCE neu
- Event-Typen: `calculateddamage` (Snapshot nach Mitigation, den wir nutzen) vs. `damage` (Application, ignorieren)
## XIVAPI
- Basis-URL: `https://v2.xivapi.com`
- Action-Lookup per Row-ID: `/api/sheet/Action/{id}?fields=Name,Icon`
- Asset-Download: `/api/asset?path={tex_path}&format=png`
- Icons nicht hotlinken lokal speichern (Community-Service, kein SLA)
- XIVAPI-Suche (`/api/search`) gibt bei manchen Abilities ClassJob-Daten statt Action-Daten zurück direkt per Row-ID abrufen
## Repository
- Remote: `https://git.epow0.org/xziino/ff14-mitigator`
- Platform: Gitea (git.epow0.org)
- Branch: `main`
## Lokale Entwicklung
```
php -S localhost:8080
```
Dann `http://localhost:8080` im Browser öffnen.
Redirect URI in FFLogs App und config.php: `http://localhost:8080/auth/callback.php`
## Bekannte Schema-Infos (ReportFight)
Verfügbare aber noch nicht genutzte Felder: `friendlyPlayers`, `enemyNPCs`,
`lastPhase`, `standardComposition`, `hasEcho`, `combatTime`, `phaseTransitions`
Vollständiges Schema: siehe `debug/schema.php` oder `fflogs-schema.json`
## Deployment
- `DEV_MODE` auf `false` setzen
- `REDIRECT_URI` auf produktive HTTPS-URL anpassen
- `debug/` Ordner nicht deployen
- `assets/` Ordner deployen (enthält lokale Icons)