diff --git a/api/analysis.php b/api/analysis.php index 282d02f..3fcefbc 100644 --- a/api/analysis.php +++ b/api/analysis.php @@ -333,6 +333,7 @@ if (!empty($shieldStatusIds)) { // Group events by abilityId, then cluster by time proximity (≤ 1000ms from // the first event in the cluster) to avoid fixed-window boundary splits. const CLUSTER_WINDOW_MS = 1000; +const HEAVY_TANKBUSTER_MIN_HP_RATIO = 0.33; $byAbility = []; // abilityId → [{ts, tgtId, amount, hp, maxHp, buffs, name}] foreach ($allEvents as $ev) { @@ -404,7 +405,26 @@ foreach ($byAbility as $abId => $events) { $aoeEvents = []; foreach ($clusters as $group) { - if (count($group['targets']) < 3) continue; + $targetCount = count($group['targets']); + $isHeavyTankbuster = false; + if ($targetCount < 3) { + foreach ($group['targets'] as $tgtId => $tgt) { + $p = $players[$tgtId] ?? null; + if (($p['role'] ?? null) !== 'tank') continue; + + $tankMaxHp = (int)($tgt['maxHp'] ?? 0); + $rawDamage = max( + (int)($tgt['unmitigatedAmount'] ?? 0), + (int)($tgt['amount'] ?? 0) + (int)($tgt['absorbed'] ?? 0) + (int)($tgt['mitigated'] ?? 0) + ); + if ($tankMaxHp > 0 && $rawDamage >= $tankMaxHp * HEAVY_TANKBUSTER_MIN_HP_RATIO) { + $isHeavyTankbuster = true; + break; + } + } + } + + if ($targetCount < 3 && !$isHeavyTankbuster) continue; $targets = []; foreach ($group['targets'] as $tgtId => $tgt) { @@ -443,11 +463,12 @@ foreach ($clusters as $group) { }); $aoeEvents[] = [ - 'timestamp' => $group['timestamp'], - 'abilityId' => $group['abilityId'], - 'abilityName' => $group['abilityName'], - 'targets' => $targets, - 'totalDamage' => array_sum(array_column($targets, 'amount')), + 'timestamp' => $group['timestamp'], + 'abilityId' => $group['abilityId'], + 'abilityName' => $group['abilityName'], + 'targets' => $targets, + 'totalDamage' => array_sum(array_column($targets, 'amount')), + 'isHeavyTankbuster' => $isHeavyTankbuster, ]; } usort($aoeEvents, fn($a, $b) => $a['timestamp'] <=> $b['timestamp']); diff --git a/auth/callback.php b/auth/callback.php index 795a115..b96897d 100644 --- a/auth/callback.php +++ b/auth/callback.php @@ -2,10 +2,17 @@ require_once __DIR__ . '/../config.php'; session_start_safe(); +function redirect_with_error(string $returnPath, string $error): void { + $separator = str_contains($returnPath, '?') ? '&' : '?'; + header('Location: ' . $returnPath . $separator . 'error=' . rawurlencode($error)); + exit; +} + +$returnPath = safe_return_path($_SESSION['oauth_return'] ?? null); + // user denied access if (isset($_GET['error'])) { - header('Location: ../index.php?error=' . urlencode($_GET['error'])); - exit; + redirect_with_error($returnPath, $_GET['error']); } // CSRF check @@ -21,12 +28,11 @@ if ( } if (empty($_GET['code'])) { - header('Location: ../index.php?error=missing_code'); - exit; + redirect_with_error($returnPath, 'missing_code'); } $verifier = $_SESSION['pkce_verifier']; -unset($_SESSION['pkce_verifier'], $_SESSION['oauth_state']); +unset($_SESSION['pkce_verifier'], $_SESSION['oauth_state'], $_SESSION['oauth_return']); $post = http_build_query([ 'grant_type' => 'authorization_code', @@ -56,12 +62,11 @@ if ($curlError || $status !== 200 || empty($data['access_token'])) { 'http_status' => $status, 'response_body' => $body, ]; - header('Location: ../index.php?error=token_failed'); - exit; + redirect_with_error($returnPath, 'token_failed'); } $_SESSION['access_token'] = $data['access_token']; $_SESSION['token_expires'] = time() + ($data['expires_in'] ?? 3600); -header('Location: ../index.php'); +header('Location: ' . $returnPath); exit; diff --git a/auth/start.php b/auth/start.php index fa39fda..a3f96da 100644 --- a/auth/start.php +++ b/auth/start.php @@ -8,6 +8,7 @@ $state = bin2hex(random_bytes(16)); $_SESSION['pkce_verifier'] = $verifier; $_SESSION['oauth_state'] = $state; +$_SESSION['oauth_return'] = safe_return_path($_GET['return'] ?? ($_SERVER['HTTP_REFERER'] ?? null)); $params = http_build_query([ 'response_type' => 'code', diff --git a/js/analysis.js b/js/analysis.js index fdef6f6..258cda5 100644 --- a/js/analysis.js +++ b/js/analysis.js @@ -341,7 +341,7 @@ body: new URLSearchParams({ report_code: code, language: window.App.language }), }); const json = await res.json(); - if (json.reauth) { window.location.href = 'auth/start.php'; return; } + if (json.reauth) { window.location.href = window.App?.authStartUrl?.() ?? 'auth/start.php'; return; } const fights = json?.data?.reportData?.report?.fights ?? []; extFights = fights; @@ -459,6 +459,7 @@ !hiddenPlayers.has(t.id) && (!playerFilter || t.name.toLowerCase().includes(playerFilter)) ); + if (ev.isHeavyTankbuster && !visibleTargets.some(t => t.role === 'tank')) return ''; if (!visibleTargets.length) return ''; // Collect boss debuffs (Reprisal/Feint/Addle) once at event level @@ -675,7 +676,7 @@ return; } - if (json.reauth) { window.location.href = 'auth/start.php'; return; } + if (json.reauth) { window.location.href = window.App?.authStartUrl?.() ?? 'auth/start.php'; return; } if (json.error) { setEmpty('Fehler: ' + json.error); return; } lastFightId = fightId; diff --git a/js/app.js b/js/app.js index 701f0c5..0c7b0f7 100644 --- a/js/app.js +++ b/js/app.js @@ -60,6 +60,11 @@ document.addEventListener('DOMContentLoaded', () => { } window.App.setUrlState = setUrlState; + function authStartUrl() { + return 'auth/start.php?return=' + encodeURIComponent(window.location.pathname + window.location.search); + } + window.App.authStartUrl = authStartUrl; + function fflogsReportUrl(reportCode, fightId = 0) { const code = String(reportCode || '').trim(); if (!code) return '#'; @@ -237,7 +242,7 @@ document.addEventListener('DOMContentLoaded', () => { body: new URLSearchParams(params), }); const json = await res.json(); - if (json.reauth) { window.location.href = 'auth/start.php'; return; } + if (json.reauth) { window.location.href = authStartUrl(); return; } output.textContent = JSON.stringify(json, null, 2); } catch (err) { output.textContent = '// Fehler: ' + err.message; @@ -287,7 +292,7 @@ document.addEventListener('DOMContentLoaded', () => { if (json.reauth) { output.textContent = '// session expired — redirecting...'; - setTimeout(() => { window.location.href = 'auth/start.php'; }, 1500); + setTimeout(() => { window.location.href = authStartUrl(); }, 1500); return; } diff --git a/templates/login.php b/templates/login.php index ef804a3..336f18c 100644 --- a/templates/login.php +++ b/templates/login.php @@ -21,7 +21,7 @@
- + = $tokenExpired ? 'Reconnect to FFLogs' : 'Connect to FFLogs' ?> diff --git a/templates/report-form.php b/templates/report-form.php index 09da893..9f0c275 100644 --- a/templates/report-form.php +++ b/templates/report-form.php @@ -23,7 +23,7 @@ - Reconnect + Reconnect