'eu', 'JP' => 'jp', 'US' => 'na', ]); define('SP2_FEST_REGIONS_ALIAS_REVERSE', [ 'eu' => 'EU', 'jp' => 'JP', 'na' => 'US', ]); define('SP2_LOCALES_ALIAS', [ 'en-US' => 'en', 'en-GB' => 'en', 'es-ES' => 'es', 'es-MX' => 'es', 'fr-FR' => 'fr', 'fr-CA' => 'fr', 'de-DE' => 'de', 'nl-NL' => 'nl', 'it-IT' => 'it', 'ru-RU' => 'ru', 'ja-JP' => 'ja', ]); define('SP_GAME_SP3', 's3'); define('SP_GAME_SP2', 's2'); define('SP_GAMES', [SP_GAME_SP3, SP_GAME_SP2]); define('SP_GAME_INFOS', [ SP_GAME_SP3 => [ 'name' => 'Splatoon 3', 'colour' => 0xFFEFFE65, 'variant' => SP_GAME_SP3, 'types' => SP3_TYPES, 'locales' => SP3_LOCALES, 'locale' => SP3_DEFAULT_LOCALE, 'localeUrlFormat' => SP3_LOCALE, 'schedUrls' => ['all' => SP3_SCHED], 'schedFilters' => SP3_SCHED_FILTER, 'festUrl' => SP3_FESTS, 'festRegions' => SP3_FEST_REGIONS, ], SP_GAME_SP2 => [ 'name' => 'Splatoon 2', 'colour' => 0xFFFF4506, 'variant' => SP_GAME_SP2, 'types' => SP2_TYPES, 'locales' => SP2_LOCALES, 'localeAliases' => SP2_LOCALES_ALIAS, 'locale' => SP2_DEFAULT_LOCALE, 'localeUrlFormat' => SP2_LOCALE, 'schedUrls' => ['vs' => SP2_SCHED, 'co' => SP2_COOP_SCHED], 'schedFilters' => SP2_SCHED_FILTER, 'festUrl' => SP2_FESTS, 'festRegions' => SP2_FEST_REGIONS, 'festRegionAliases' => SP2_FEST_REGIONS_ALIAS, 'festRegionAliasesReverse' => SP2_FEST_REGIONS_ALIAS_REVERSE, ], ]); function httpRequest(string $url): mixed { $curl = curl_init($url); curl_setopt_array($curl, [ CURLOPT_AUTOREFERER => true, CURLOPT_CERTINFO => false, CURLOPT_FAILONERROR => false, CURLOPT_FOLLOWLOCATION => true, CURLOPT_MAXREDIRS => 5, CURLOPT_PATH_AS_IS => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_TCP_FASTOPEN => true, CURLOPT_PROTOCOLS => CURLPROTO_HTTPS, CURLOPT_REDIR_PROTOCOLS => CURLPROTO_HTTPS, CURLOPT_CONNECTTIMEOUT => 2, CURLOPT_TIMEOUT => 5, CURLOPT_USERAGENT => 'Satori/20230203 (+https://fii.moe/satori)', CURLOPT_HTTPHEADER => ['Accept: application/json'], ]); $response = json_decode(curl_exec($curl)); curl_close($curl); return $response; } function cached(string $name, callable $match, callable $create): mixed { $fileName = sys_get_temp_dir() . '/satori-splatoon-' . $name . '.tmp'; if(is_file($fileName) && $match(filemtime($fileName), $fileName)) return unserialize(file_get_contents($fileName)); $output = $create(); file_put_contents($fileName, serialize($output)); return $output; } function cache_lifetime_15min($mtime): bool { return is_int($mtime) && floor($mtime / 900) >= floor(time() / 900); } function splatoon2_format_date(int $time): string { return str_replace('+00:00', 'Z', date(DATE_ISO8601_EXPANDED, $time)); } function splatoon2_translate_ruleset(string $id): string { if($id === 'tower_control') return 'LOFT'; if($id === 'splat_zones') return 'AREA'; if($id === 'clam_blitz') return 'CLAM'; if($id === 'rainmaker') return 'GOAL'; if($id === 'turf_war') return 'TURF_WAR'; return $id; } function splatoon2_get_mode_colour(string $id): int { if($id === 'regular') return 0xFF19D719; if($id === 'gachi') return 0xFFF54910; if($id === 'league') return 0xFFF02D7D; if($id === 'coop') return 0xFFFF5600; return 0xFFFFFFFF; } function splatoon2_ruleset_short(string $id): string { if($id === 'tower_control') return 'TC'; if($id === 'splat_zones') return 'SZ'; if($id === 'clam_blitz') return 'CB'; if($id === 'rainmaker') return 'RM'; if($id === 'turf_war') return 'TW'; if($id === 'coop') return 'SR'; return $id; } function splatoon3_ruleset_short(string $id): string { if($id === 'LOFT') return 'TC'; if($id === 'AREA') return 'SZ'; if($id === 'CLAM') return 'CB'; if($id === 'GOAL') return 'RM'; if($id === 'TURF_WAR') return 'TW'; if($id === 'coop') return 'SR'; if($id === 'bigrun') return 'BR'; return $id; } date_default_timezone_set('utc'); header('Content-Type: application/json; charset=utf-8'); $gameId = (string)filter_input(INPUT_GET, 'g'); if(!in_array($gameId, SP_GAMES)) die('{"error":"game:id"}'); $gameInfo = SP_GAME_INFOS[$gameId] ?? null; if(!isset($gameInfo)) die('{"error":"game:info"}'); $typeId = (string)filter_input(INPUT_GET, 't'); if(empty($typeId) || !in_array($typeId, $gameInfo['types'])) die('{"error":"type:id"}'); $localeId = (string)filter_input(INPUT_GET, 'l'); if(empty($localeId)) $localeId = $gameInfo['locale']; elseif(!in_array($localeId, $gameInfo['locales'])) { if(isset($gameInfo['localeAliases']) && array_key_exists($localeId, $gameInfo['localeAliases'])) $localeId = $gameInfo['localeAliases'][$localeId]; else die('{"error":"locale:id"}'); } $localeInfo = cached(sprintf('%s-locale-%s', $gameId, $localeId), 'cache_lifetime_15min', function() use($gameInfo, $localeId) { if(empty($gameInfo['localeUrlFormat'])) die('{"error":"locale:url"}'); $localeInfo = httpRequest(sprintf($gameInfo['localeUrlFormat'], $localeId)); if(!is_object($localeInfo)) die('{"error":"locale:info"}'); return $localeInfo; }); if($typeId === SP_TYPE_SCHED) { // SCHEDULE SHIT $filters = array_filter(explode(',', (string)filter_input(INPUT_GET, 'sf'))); $filterAll = empty($filters); foreach($filters as $filter) if(!in_array($filter, $gameInfo['schedFilters'])) die('{"error":"schedules:filter"}'); $schedRaw = (object)cached(sprintf('%s-sched', $gameId), 'cache_lifetime_15min', function() use($gameInfo) { if(empty($gameInfo['schedUrls'])) die('{"error":"schedules:url"}'); $output = []; foreach($gameInfo['schedUrls'] as $name => $url) $output[$name] = httpRequest($url); return $output; }); $gameModes = []; $schedules = []; $currentFest = null; if($gameInfo['variant'] === SP_GAME_SP3) { $data = $schedRaw->all->data; $includeRegular = $filterAll || in_array('regular', $filters); $includeSeries = $filterAll || in_array('series', $filters); $includeOpen = $filterAll || in_array('open', $filters); $includeRanked = $filterAll || in_array('bankara', $filters); $includeX = $filterAll || in_array('x', $filters); $includeLeague = $filterAll || in_array('league', $filters); $includeSalmon = $filterAll || in_array('coop', $filters); $includeBigRun = $filterAll || in_array('bigrun', $filters); $includeFest = $filterAll || in_array('fest', $filters); $includeSeries = $includeSeries || $includeRanked; $includeOpen = $includeOpen || $includeRanked; $includeRanked = $includeSeries || $includeOpen; function splatoon3_schedule_gen($target, $node): void { $target->start = $node->startTime; $target->end = $node->endTime; } function splatoon3_schedule_vs($target, $settings): void { global $localeInfo; $target->variant = 'vs'; $target->ruleset = [ 'id' => $settings->vsRule->rule, 'name' => $localeInfo->rules->{$settings->vsRule->id}?->name ?? $settings->vsRule->name, 'short' => splatoon3_ruleset_short($settings->vsRule->rule), ]; $target->stages = []; foreach($settings->vsStages as $stage) $target->stages[] = [ 'id' => (string)$stage->vsStageId, 'name' => $localeInfo->stages->{$stage->id}?->name ?? $stage->name, 'image' => $stage->image->url, ]; } if($includeRegular && !empty($data->regularSchedules->nodes)) { $schedule = []; foreach($data->regularSchedules->nodes as $node) { if(empty($node->regularMatchSetting)) continue; $schedule[] = $item = new stdClass; splatoon3_schedule_gen($item, $node); splatoon3_schedule_vs($item, $node->regularMatchSetting); } if(!empty($schedule)) { $gameModes[] = [ 'id' => 'regular', 'name' => 'Regular Battle', 'colour' => 0xFF19D719, ]; $schedules[] = [ 'mode' => 'regular', 'schedule' => $schedule, ]; } } if($includeRanked && !empty($data->bankaraSchedules->nodes)) { $series = []; $open = []; foreach($data->bankaraSchedules->nodes as $node) { if(empty($node->bankaraMatchSettings)) continue; foreach($node->bankaraMatchSettings as $settings) { if($settings->mode === 'CHALLENGE') { $series[] = $item = new stdClass; } elseif($settings->mode === 'OPEN') { $open[] = $item = new stdClass; } else continue; splatoon3_schedule_gen($item, $node); splatoon3_schedule_vs($item, $settings); } } if($includeSeries && !empty($series)) { if(empty($gameModes['bankara'])) $gameModes['bankara'] = [ 'id' => 'bankara', 'name' => 'Anarchy Battle', 'colour' => 0xFFF54910, 'variants' => [], ]; $gameModes['bankara']['variants'][] = [ 'id' => 'CHALLENGE', 'name' => 'Series', 'colour' => 0xFF603BFF, ]; $schedules[] = [ 'mode' => 'bankara', 'variant' => 'CHALLENGE', 'schedule' => $series, ]; } if($includeOpen && !empty($open)) { if(empty($gameModes['bankara'])) $gameModes['bankara'] = [ 'id' => 'bankara', 'name' => 'Anarchy Battle', 'colour' => 0xFFF54910, 'variants' => [], ]; $gameModes['bankara']['variants'][] = [ 'id' => 'OPEN', 'name' => 'Open', 'colour' => 0xFF603BFF, ]; $schedules[] = [ 'mode' => 'bankara', 'variant' => 'OPEN', 'schedule' => $open, ]; } } if($includeX && !empty($data->xSchedules->nodes)) { $schedule = []; foreach($data->xSchedules->nodes as $node) { if(empty($node->xMatchSetting)) continue; $schedule[] = $item = new stdClass; splatoon3_schedule_gen($item, $node); splatoon3_schedule_vs($item, $node->xMatchSetting); } if(!empty($schedule)) { $gameModes[] = [ 'id' => 'x', 'name' => 'X Battle', 'colour' => 0xFF0FDB9B, ]; $schedules[] = [ 'mode' => 'x', 'schedule' => $schedule, ]; } } if($includeLeague && !empty($data->leagueSchedules->nodes)) { $schedule = []; foreach($data->leagueSchedules->nodes as $node) { if(empty($node->leagueMatchSetting)) continue; $schedule[] = $item = new stdClass; splatoon3_schedule_gen($item, $node); splatoon3_schedule_vs($item, $node->leagueMatchSetting); } if(!empty($schedule)) { $gameModes[] = [ 'id' => 'league', 'name' => 'League Battle', 'colour' => 0xFFF02D7D, ]; $schedules[] = [ 'mode' => 'league', 'schedule' => $schedule, ]; } } if($includeFest && !empty($data->festSchedules->nodes)) { $schedule = []; foreach($data->festSchedules->nodes as $node) { if(empty($node->festMatchSetting)) continue; $schedule[] = $item = new stdClass; splatoon3_schedule_gen($item, $node); splatoon3_schedule_vs($item, $node->festMatchSetting); } if(!empty($schedule)) { $gameModes[] = [ 'id' => 'fest', 'name' => 'Splatfest Battle', 'colour' => 0xFF71717A, ]; $schedules[] = [ 'mode' => 'fest', 'schedule' => $schedule, ]; } } if($includeSalmon && !empty($data->coopGroupingSchedule->regularSchedules->nodes)) { $schedule = []; $nodes = $data->coopGroupingSchedule->regularSchedules->nodes; foreach($nodes as $node) { if(empty($node->setting)) continue; $schedule[] = $item = new stdClass; splatoon3_schedule_gen($item, $node); $item->variant = 'coop'; $item->stages = [ [ 'name' => $node->setting->coopStage->name, 'image' => $node->setting->coopStage->image->url, ], ]; $item->weapons = []; foreach($node->setting->weapons as $weapon) $item->weapons[] = [ 'name' => $localeInfo->weapons->{$weapon->__splatoon3ink_id}?->name ?? $weapon->name, 'image' => $weapon->image->url, ]; } if(!empty($schedule)) { $gameModes[] = [ 'id' => 'coop', 'name' => 'Salmon Run', 'colour' => 0xFFFF5600, ]; $schedules[] = [ 'mode' => 'coop', 'schedule' => $schedule, ]; } } if($includeBigRun && !empty($data->coopGroupingSchedule->bigRunSchedules->nodes)) { $schedule = []; $nodes = $data->coopGroupingSchedule->bigRunSchedules->nodes; foreach($nodes as $node) { if(empty($node->setting)) continue; $schedule[] = $item = new stdClass; splatoon3_schedule_gen($item, $node); $item->variant = 'coop'; $item->stages = [ [ 'name' => $node->setting->coopStage->name, 'image' => $node->setting->coopStage->image->url, ], ]; $item->weapons = []; foreach($node->setting->weapons as $weapon) $item->weapons[] = [ 'name' => $localeInfo->weapons->{$weapon->__splatoon3ink_id}?->name ?? $weapon->name, 'image' => $weapon->image->url, ]; } if(!empty($schedule)) { $gameModes[] = [ 'id' => 'bigrun', 'name' => 'Big Run', 'colour' => 0xFFFF5600, // reinvestigate this ]; $schedules[] = [ 'mode' => 'bigrun', 'schedule' => $schedule, ]; } } if(!empty($data->currentFest->id)) { $currentFest = new stdClass; $currentFest->id = $data->currentFest->id; $currentFest->title = $data->currentFest->title; $currentFest->start = $data->currentFest->startTime; $currentFest->end = $data->currentFest->endTime; $currentFest->midterm = $data->currentFest->midtermTime; $currentFest->state = $data->currentFest->state; $currentFest->teams = []; if($includeFest && !empty($data->currentFest->tricolorStage)) { $gameModes[] = [ 'id' => 'tricolor', 'name' => 'Tricolor Battle', 'colour' => 0xFF71717A, ]; $schedules[] = [ 'mode' => 'tricolor', 'schedule' => [ [ 'start' => splatoon2_format_date(strtotime('today')), 'end' => splatoon2_format_date(strtotime('tomorrow')), 'variant' => 'vs', 'ruleset' => [ 'id' => 'TRICOLOR_TURF_WAR', 'name' => 'Tricolor Turf War', 'short' => 'TTW', ], 'stages' => [ [ 'id' => $data->currentFest->tricolorStage->id, 'name' => $data->currentFest->tricolorStage->name, 'image' => $data->currentFest->tricolorStage->image->url, ], ], ], ], ]; } foreach($data->currentFest->teams as $rawTeam) { $currentFest->teams[] = $teamInfo = new stdClass; $teamInfo->id = $rawTeam->id; $teamInfo->colour = (round($rawTeam->color->a * 0xFF) << 24) | (round($rawTeam->color->r * 0xFF) << 16) | (round($rawTeam->color->g * 0xFF) << 8) | round($rawTeam->color->b * 0xFF); } } } elseif($gameInfo['variant'] === SP_GAME_SP2) { $vs = $schedRaw->vs; $coop = $schedRaw->co; $includeRegular = $filterAll || in_array('regular', $filters); $includeRanked = $filterAll || in_array('gachi', $filters); $includeLeague = $filterAll || in_array('league', $filters); $includeSalmon = $filterAll || in_array('coop', $filters); function splatoon2_schedule_gen($target, $node): void { $target->start = splatoon2_format_date($node->start_time); $target->end = splatoon2_format_date($node->end_time); } function splatoon2_schedule_vs($target, $node): void { global $localeInfo, $gameModes; if(!array_key_exists($node->game_mode->key, $gameModes)) $gameModes[$node->game_mode->key] = [ 'id' => $node->game_mode->key, 'name' => $localeInfo->game_modes->{$node->game_mode->key}?->name ?? $node->game_mode->name, 'colour' => splatoon2_get_mode_colour($node->game_mode->key), ]; $target->variant = 'vs'; $target->ruleset = [ 'id' => splatoon2_translate_ruleset($node->rule->key), 'name' => $localeInfo->rules->{$node->rule->key}?->name ?? $node->rule->name, 'short' => splatoon2_ruleset_short($node->rule->key), ]; $target->stages = []; foreach(['stage_a', 'stage_b', 'stage_c'] as $stage) { $stage = $node->{$stage} ?? null; if(empty($stage)) continue; $target->stages[] = [ 'id' => $stage->id, 'name' => $localeInfo->stages->{$stage->id}?->name ?? $stage->name, 'image' => sprintf(SP2_IMAGE_FORMAT, $stage->image), ]; } } if($includeRegular && !empty($vs->regular)) { $schedule = []; foreach($vs->regular as $node) { if(empty($node->rule)) continue; $schedule[] = $item = new stdClass; splatoon2_schedule_gen($item, $node); splatoon2_schedule_vs($item, $node); } if(!empty($schedule)) $schedules[] = [ 'mode' => 'regular', 'schedule' => $schedule, ]; } if($includeRanked && !empty($vs->gachi)) { $schedule = []; foreach($vs->gachi as $node) { if(empty($node->rule)) continue; $schedule[] = $item = new stdClass; splatoon2_schedule_gen($item, $node); splatoon2_schedule_vs($item, $node); } if(!empty($schedule)) $schedules[] = [ 'mode' => 'gachi', 'schedule' => $schedule, ]; } if($includeLeague && !empty($vs->league)) { $schedule = []; foreach($vs->league as $node) { if(empty($node->rule)) continue; $schedule[] = $item = new stdClass; splatoon2_schedule_gen($item, $node); splatoon2_schedule_vs($item, $node); } if(!empty($schedule)) $schedules[] = [ 'mode' => 'league', 'schedule' => $schedule, ]; } if($includeSalmon && !empty($coop->schedules)) { $schedule = []; foreach($coop->schedules as $key => $node) { $schedule[] = $item = new stdClass; splatoon2_schedule_gen($item, $node); $item->variant = 'coop'; $details = $coop->details[$key] ?? null; if(!empty($details)) { $item->stages = [ [ 'name' => $details->stage->name, 'image' => sprintf(SP2_IMAGE_FORMAT, $details->stage->image), ], ]; $item->weapons = []; foreach($details->weapons as $weapon) { if($weapon->id === '-1') { $weaponImage = $weapon->coop_special_weapon->image; $weaponName = $localeInfo->coop_special_weapons->{$weaponImage}?->name ?? $weapon->coop_special_weapon->name; } else { $weaponName = $localeInfo->weapons->{$weapon->weapon->id}?->name ?? $weapon->weapon->name; $weaponImage = $weapon->weapon->image; } $item->weapons[] = [ 'id' => $weapon->id, 'name' => $weaponName, 'image' => sprintf(SP2_IMAGE_FORMAT, $weaponImage), ]; } } } if(!empty($schedule)) { $gameModes[] = [ 'id' => 'coop', 'name' => 'Salmon Run', 'colour' => splatoon2_get_mode_colour('coop'), ]; $schedules[] = [ 'mode' => 'coop', 'schedule' => $schedule, ]; } } } else die('{"error":"schedules:variant"}'); echo json_encode([ 'game' => [ 'id' => $gameId, 'name' => $gameInfo['name'], 'colour' => $gameInfo['colour'], 'variant' => $gameInfo['variant'], ], 'fest' => $currentFest, 'modes' => array_values($gameModes), 'schedules' => $schedules, ]); exit; } if($typeId === SP_TYPE_FESTS) { // SPLATFEST SHIT $regions = array_filter(explode(',', (string)filter_input(INPUT_GET, 'fr'))); $regionsAll = empty($regions); foreach($regions as $key => $region) { if(!empty($gameInfo['festRegionAliases']) && array_key_exists($region, $gameInfo['festRegionAliases'])) $region = $gameInfo['festRegionAliases'][$region]; if(!in_array($region, $gameInfo['festRegions'])) die('{"error":"fests:region"}'); $regions[$key] = $region; } if($regionsAll) $regions = $gameInfo['festRegions']; $festsRaw = (object)cached(sprintf('%s-fests', $gameId), 'cache_lifetime_15min', function() use($gameInfo) { if(empty($gameInfo['festUrl'])) die('{"error":"fests:url"}'); return httpRequest($gameInfo['festUrl']); }); $fests = []; if($gameInfo['variant'] === SP_GAME_SP3) { foreach($regions as $region) { if(empty($festsRaw->{$region}->data->festRecords->nodes)) continue; $nodes = $festsRaw->{$region}->data->festRecords->nodes; if(!empty($gameInfo['festRegionAliasesReverse']) && array_key_exists($region, $gameInfo['festRegionAliasesReverse'])) $region = $gameInfo['festRegionAliasesReverse'][$region]; foreach($nodes as $node) { $festId = $node->image->url; if(empty($fests[$festId])) $fests[$festId] = [ 'regions' => [], ]; $fests[$festId]['regions'][] = $festInfo = new stdClass; $festInfo->id = $node->id; $festInfo->region = $region; $festInfo->state = $node->state; $festInfo->start = $node->startTime; $festInfo->end = $node->endTime; $festInfo->title = $node->title; $festInfo->image = $node->image->url; $festInfo->can_vote = $node->isVotable; $festInfo->teams = []; foreach($node->teams as $rawTeam) { $festInfo->teams[] = $team = new stdClass; $team->id = $rawTeam->id; $team->name = $rawTeam->teamName; $team->image = $rawTeam->image->url; $team->colour = (round($rawTeam->color->a * 0xFF) << 24) | (round($rawTeam->color->r * 0xFF) << 16) | (round($rawTeam->color->g * 0xFF) << 8) | round($rawTeam->color->b * 0xFF); $team->has_results = !empty($rawTeam->result); if(!$team->has_results) continue; $team->is_winner = $rawTeam->result->isWinner; $team->results = []; foreach(['horagai', 'vote', 'regularContribution', 'challengeContribution', 'tricolorContribution'] as $rateType) { if(!isset($rawTeam->result->{$rateType . 'Ratio'})) continue; $rateCat = $rateType; if(str_ends_with($rateCat, 'Contribution')) $rateCat = substr($rateCat, 0, -12); $team->results[] = [ 'category' => $rateCat, 'ratio' => $rawTeam->result->{$rateType . 'Ratio'}, 'is_top' => $rawTeam->result->{'is' . ucfirst($rateType) . 'RatioTop'}, ]; } } } } } elseif($gameInfo['variant'] === SP_GAME_SP2) { foreach($regions as $region) { if(empty($festsRaw->{$region}->festivals)) continue; $festivals = $festsRaw->{$region}->festivals; $results = $festsRaw->{$region}->results; if(!empty($gameInfo['festRegionAliasesReverse']) && array_key_exists($region, $gameInfo['festRegionAliasesReverse'])) $region = $gameInfo['festRegionAliasesReverse'][$region]; foreach($festivals as $festId => $festRaw) { if(empty($fests[$festId])) $fests[$festId] = [ 'regions' => [], ]; $festNames = $localeInfo->festivals->{$festRaw->festival_id}->names ?? null; if(empty($festNames)) continue; foreach($results as $findResultRaw) if($findResultRaw->festival_id === $festRaw->festival_id) { $resultsRaw = $findResultRaw; break; } $time = time(); $state = ''; if($time > $festRaw->times->end) $state = 'CLOSED'; elseif($time > $festRaw->times->start) $state = 'OPEN'; // this is an assumption else $state = 'SCHEDULED'; $fests[$festId]['regions'][] = $festInfo = new stdClass; $festInfo->id = base64_encode(sprintf('FestTeam-%s:JEA-%05d', $region, $festRaw->festival_id)); $festInfo->region = $region; $festInfo->state = $state; $festInfo->start = splatoon2_format_date($festRaw->times->start); $festInfo->end = splatoon2_format_date($festRaw->times->end); $festInfo->title = sprintf('%s vs %s', $festNames->alpha_short, $festNames->bravo_short); $festInfo->image = sprintf(SP2_IMAGE_FORMAT, $festRaw->images->panel); $festInfo->can_vote = $state !== 'CLOSED'; $festInfo->teams = []; $festInfo->special_stages = [ [ 'id' => $festRaw->special_stage->id, 'name' => $localeInfo->stages->{$festRaw->special_stage->id}?->name ?? $festRaw->special_stage->name, 'image' => sprintf(SP2_IMAGE_FORMAT, $festRaw->special_stage->image), ], ]; $tmpResults = []; foreach(['alpha', 'bravo'] as $teamId => $teamName) { $festInfo->teams[] = $team = new stdClass; $team->id = base64_encode(sprintf('FestTeam-%s:JEA-%05d:%s', $region, $festInfo->id, ucfirst($teamName))); $team->name = $festNames->{$teamName . '_long'}; $team->image = sprintf(SP2_IMAGE_FORMAT, $festRaw->images->{$teamName}); $rawColour = $festRaw->colors->{$teamName}; $team->colour = (round($rawColour->a * 0xFF) << 24) | (round($rawColour->r * 0xFF) << 16) | (round($rawColour->g * 0xFF) << 8) | round($rawColour->b * 0xFF); $team->has_results = !empty($resultsRaw); if(!$team->has_results) continue; $team->is_winner = $resultsRaw->summary->total === $teamId; $team->results = []; foreach(['challenge', 'vote', 'regular', 'solo', 'team'] as $rateType) { $rawRate = $resultsRaw->rates->{$rateType}->{$teamName} ?? null; if(empty($rawRate)) continue; if(!isset($tmpResults[$rateType])) $tmpResults[$rateType] = $rawRate > $resultsRaw->rates->{$rateType}->{'bravo'} ?? 0; $team->results[] = [ 'category' => $rateType, 'ratio' => $rawRate / 10000, 'is_top' => $tmpResults[$rateType], ]; $tmpResults[$rateType] = !$tmpResults[$rateType]; } } unset($tmpResults); } } } else die('{"error":"fests:variant"}'); echo json_encode([ 'game' => [ 'id' => $gameId, 'name' => $gameInfo['name'], 'colour' => $gameInfo['colour'], 'variant' => $gameInfo['variant'], ], 'fests' => array_values($fests), ]); exit; }