- HP bar per player in AoE timeline (3-segment: remaining/damage/missing) - Death highlight: red border + background when hp === 0 - Mitigation tracking refactored to read buffs field directly from damage events instead of separate applybuff/removebuff window tracking (simpler, more accurate) - Mitigations are now per-target instead of per-event - Add Magick Barrier to tracked mitigation abilities - Player filter text input above AoE timeline - Player cards as toggle buttons; tanks hidden by default; sorted Healer→DPS→Tank - Fix Temperance double-display (dedup by name instead of abilityId) - Event Explorer in Report tab: DataType, raw event type, ability, player dropdowns, limit, time range; abilities and players loaded on fight select - Fight percentage display fix (was divided by 100 twice) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
128 lines
4.3 KiB
PHP
128 lines
4.3 KiB
PHP
<?php
|
|
ini_set('display_errors', '0');
|
|
require_once __DIR__ . '/../config.php';
|
|
session_start_safe();
|
|
|
|
header('Content-Type: application/json');
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { http_response_code(405); echo json_encode(['error' => 'Method not allowed']); exit; }
|
|
if (empty($_SESSION['access_token'])) { echo json_encode(['reauth' => true]); exit; }
|
|
if (($_SESSION['token_expires'] ?? 0) <= time()) { echo json_encode(['reauth' => true]); exit; }
|
|
|
|
$reportCode = preg_replace('/[^a-zA-Z0-9]/', '', $_POST['report_code'] ?? '');
|
|
$fightId = (int)($_POST['fight_id'] ?? 0);
|
|
$startTime = (float)($_POST['start_time'] ?? 0);
|
|
$endTime = (float)($_POST['end_time'] ?? 0);
|
|
$playerName = trim($_POST['player_name'] ?? '');
|
|
$eventType = trim($_POST['event_type'] ?? '');
|
|
$abilityId = (int)($_POST['ability_id'] ?? 0);
|
|
$limit = max(1, min(500, (int)($_POST['limit'] ?? 20)));
|
|
$startOffset = (float)($_POST['start_offset'] ?? 0) * 1000; // s → ms
|
|
$endOffset = isset($_POST['end_offset']) && $_POST['end_offset'] !== ''
|
|
? (float)$_POST['end_offset'] * 1000
|
|
: null;
|
|
|
|
$allowedTypes = ['DamageTaken', 'DamageDone', 'Healing', 'Casts', 'Buffs', 'Deaths'];
|
|
$dataType = in_array($_POST['data_type'] ?? '', $allowedTypes) ? $_POST['data_type'] : 'DamageTaken';
|
|
|
|
if (!$reportCode || !$fightId) { http_response_code(400); echo json_encode(['error' => 'Missing params']); exit; }
|
|
|
|
$queryStart = $startTime + $startOffset;
|
|
$queryEnd = $endOffset !== null ? $startTime + $endOffset : $endTime;
|
|
$queryEnd = min($queryEnd, $endTime);
|
|
|
|
$token = $_SESSION['access_token'];
|
|
|
|
function dbg_gql(string $query): array {
|
|
global $token;
|
|
$ch = curl_init(GRAPHQL_URI);
|
|
curl_setopt_array($ch, [
|
|
CURLOPT_POST => true,
|
|
CURLOPT_POSTFIELDS => json_encode(['query' => $query]),
|
|
CURLOPT_RETURNTRANSFER => true,
|
|
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Authorization: Bearer ' . $token],
|
|
CURLOPT_SSL_VERIFYPEER => !DEV_MODE,
|
|
]);
|
|
$body = curl_exec($ch);
|
|
curl_close($ch);
|
|
return json_decode($body, true) ?? [];
|
|
}
|
|
|
|
// Resolve player name → actor IDs (source + target)
|
|
$playerIds = [];
|
|
if ($playerName !== '') {
|
|
$pd = dbg_gql(<<<GQL
|
|
{
|
|
reportData {
|
|
report(code: "$reportCode") {
|
|
playerDetails(fightIDs: [$fightId])
|
|
}
|
|
}
|
|
}
|
|
GQL);
|
|
|
|
$pdRaw = $pd['data']['reportData']['report']['playerDetails'] ?? null;
|
|
$pdParsed = is_string($pdRaw) ? json_decode($pdRaw, true) : $pdRaw;
|
|
$pdGroups = $pdParsed['data']['playerDetails'] ?? [];
|
|
|
|
foreach (['tanks', 'healers', 'dps'] as $group) {
|
|
foreach ($pdGroups[$group] ?? [] as $p) {
|
|
if (stripos($p['name'], $playerName) !== false) {
|
|
$playerIds[] = (int)$p['id'];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Fetch events
|
|
$includeResources = in_array($dataType, ['DamageTaken', 'DamageDone']) ? 'includeResources: true,' : '';
|
|
|
|
$result = dbg_gql(<<<GQL
|
|
{
|
|
reportData {
|
|
report(code: "$reportCode") {
|
|
events(
|
|
fightIDs: [$fightId],
|
|
dataType: $dataType,
|
|
$includeResources
|
|
startTime: $queryStart,
|
|
endTime: $queryEnd
|
|
) {
|
|
data
|
|
}
|
|
}
|
|
}
|
|
}
|
|
GQL);
|
|
|
|
$events = $result['data']['reportData']['report']['events']['data'] ?? [];
|
|
|
|
// Filter by raw event type, player (source OR target), then apply limit
|
|
$filtered = [];
|
|
foreach ($events as $ev) {
|
|
if ($eventType !== '' && ($ev['type'] ?? '') !== $eventType) continue;
|
|
if ($abilityId > 0 && (int)($ev['abilityGameID'] ?? 0) !== $abilityId) continue;
|
|
if (!empty($playerIds)) {
|
|
$srcId = (int)($ev['sourceID'] ?? -1);
|
|
$tgtId = (int)($ev['targetID'] ?? -1);
|
|
if (!in_array($srcId, $playerIds) && !in_array($tgtId, $playerIds)) continue;
|
|
}
|
|
$filtered[] = $ev;
|
|
if (count($filtered) >= $limit) break;
|
|
}
|
|
|
|
echo json_encode([
|
|
'data_type' => $dataType,
|
|
'event_type' => $eventType ?: null,
|
|
'ability_id' => $abilityId ?: null,
|
|
'player_name' => $playerName ?: null,
|
|
'player_ids' => $playerIds ?: null,
|
|
'time_range' => [
|
|
'from_ms' => (int)$queryStart,
|
|
'to_ms' => (int)$queryEnd,
|
|
],
|
|
'total_before_limit' => count($events),
|
|
'count' => count($filtered),
|
|
'events' => $filtered,
|
|
], JSON_PRETTY_PRINT);
|