1018 lines
37 KiB
PHP
1018 lines
37 KiB
PHP
<?php
|
|
if(!defined('DATE_ISO8601_EXPANDED')) // added in PHP 8.2...
|
|
define('DATE_ISO8601_EXPANDED', 'Y-m-d\\TH:i:sP');
|
|
|
|
define('SP_TYPE_SCHED', 'schedules');
|
|
define('SP_TYPE_FESTS', 'fests');
|
|
|
|
define('SP3_DEFAULT_LOCALE', 'en-GB');
|
|
define('SP3_LOCALES', ['de-DE', 'en-US', 'en-GB', 'es-ES', 'es-MX', 'fr-FR', 'fr-CA', 'it-IT', 'ja-JP', 'ko-KR', 'nl-NL', 'ru-RU', 'zh-CN', 'zh-TW']);
|
|
define('SP3_FEST_REGIONS', ['EU', 'JP', 'AP', 'US']);
|
|
define('SP3_DOMAIN', 'https://splatoon3.ink');
|
|
define('SP3_LOCALE', SP3_DOMAIN . '/data/locale/%s.json');
|
|
define('SP3_SCHED', SP3_DOMAIN . '/data/schedules.json');
|
|
define('SP3_FESTS', SP3_DOMAIN . '/data/festivals.json');
|
|
define('SP3_TYPES', [SP_TYPE_SCHED, SP_TYPE_FESTS]);
|
|
define('SP3_SCHED_FILTER', [
|
|
'regular', 'bankara', 'series', 'open', 'x',
|
|
'league', 'coop', 'bigrun', 'fest', 'eggstra',
|
|
]);
|
|
|
|
define('SP2_DEFAULT_LOCALE', 'en');
|
|
define('SP2_LOCALES', ['en', 'es', 'es-MX', 'fr', 'fr-CA', 'de', 'nl', 'it', 'ru', 'ja']);
|
|
define('SP2_FEST_REGIONS', ['eu', 'jp', 'na']);
|
|
define('SP2_DOMAIN', 'https://splatoon2.ink');
|
|
define('SP2_LOCALE', SP2_DOMAIN . '/data/locale/%s.json');
|
|
define('SP2_SCHED', SP2_DOMAIN . '/data/schedules.json');
|
|
define('SP2_COOP_SCHED', SP2_DOMAIN . '/data/coop-schedules.json');
|
|
define('SP2_FESTS', SP2_DOMAIN . '/data/festivals.json');
|
|
define('SP2_IMAGE_FORMAT', SP2_DOMAIN . '/assets/splatnet%s');
|
|
define('SP2_TYPES', [SP_TYPE_SCHED, SP_TYPE_FESTS]);
|
|
define('SP2_SCHED_FILTER', [
|
|
'regular', 'gachi', 'league', 'coop',
|
|
]);
|
|
define('SP2_FEST_REGIONS_ALIAS', [
|
|
'EU' => '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 === 'TEAM_CONTEST')
|
|
return 'EW';
|
|
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);
|
|
$includeEggstra = $filterAll || in_array('eggstra', $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->periods = [[
|
|
'start' => $node->startTime,
|
|
'end' => $node->endTime,
|
|
]];
|
|
}
|
|
|
|
function splatoon3_schedule_vs($target, $settings): void {
|
|
global $localeInfo;
|
|
|
|
$target->variant = 'vs';
|
|
$target->flags = [];
|
|
|
|
$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,
|
|
];
|
|
}
|
|
|
|
function splatoon3_schedule_periods($target, $periods): void {
|
|
$target->periods = [];
|
|
foreach($periods as $period)
|
|
$target->periods[] = [
|
|
'start' => $period->startTime,
|
|
'end' => $period->endTime,
|
|
];
|
|
}
|
|
|
|
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->eventSchedules->nodes)) {
|
|
$schedule = [];
|
|
|
|
foreach($data->eventSchedules->nodes as $node) {
|
|
if(empty($node->leagueMatchSetting))
|
|
continue;
|
|
|
|
$schedule[] = $item = new stdClass;
|
|
splatoon3_schedule_periods($item, $node->timePeriods);
|
|
splatoon3_schedule_vs($item, $node->leagueMatchSetting);
|
|
$item->flags[] = 'league';
|
|
|
|
if(!empty($node->leagueMatchSetting->leagueMatchEvent)) {
|
|
$event = $node->leagueMatchSetting->leagueMatchEvent;
|
|
$eventLocale = $localeInfo->events->{$event->id};
|
|
|
|
$item->league = [
|
|
'id' => $event->leagueMatchEventId,
|
|
'name' => $eventLocale->name ?? $event->name,
|
|
'desc' => $eventLocale->desc ?? $event->desc,
|
|
'rules' => [
|
|
'lines' => explode('<br />', $eventLocale->regulation ?? $event->regulation),
|
|
'url' => $event->regulationUrl,
|
|
],
|
|
];
|
|
}
|
|
}
|
|
|
|
if(!empty($schedule)) {
|
|
$gameModes[] = [
|
|
'id' => 'league',
|
|
'name' => 'Challenge 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($includeEggstra && !empty($data->coopGroupingSchedule->teamContestSchedules->nodes)) {
|
|
$schedule = [];
|
|
|
|
$nodes = $data->coopGroupingSchedule->teamContestSchedules->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' => 'eggstra',
|
|
'name' => 'Eggstra Work',
|
|
'colour' => 0xFFBE8800,
|
|
];
|
|
|
|
$schedules[] = [
|
|
'mode' => 'eggstra',
|
|
'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' => 0xFFB322FF,
|
|
];
|
|
|
|
$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->periods = [[
|
|
'start' => splatoon2_format_date($node->start_time),
|
|
'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;
|
|
}
|