diff --git a/README.md b/README.md index 917880b4..f3566da5 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# CS2 Simulator +# CS2 Simulator [![Release](https://img.shields.io/github/v/release/Rarmash/CS2-Simulator?display_name=tag)](https://github.com/Rarmash/CS2-Simulator/releases) [![Release Build](https://img.shields.io/github/actions/workflow/status/Rarmash/CS2-Simulator/release.yml?label=release%20build)](https://github.com/Rarmash/CS2-Simulator/actions/workflows/release.yml) @@ -23,6 +23,8 @@ Unless explicitly stated otherwise, the repository license applies to the source - Case opening with roulette animation - Optional X-Ray opening mechanic - Souvenir packages with tournament-based dates +- Major tournament section covering CS:GO and CS2 eras +- Major teams and players browsing - Operation and Armory reward collections - Legacy operation collections - Sticker capsules @@ -142,19 +144,13 @@ The source code in this repository is licensed under `AGPL-3.0`. The project is actively evolving, with current work focused on: -- expanding simulator coverage for CS2 collectible content +- expanding the simulation layer beyond basic opening flows +- improving long-term data quality for tournaments, teams, and players - continuing UI/codebase refactoring to reduce duplicated screen logic -- reducing release size through better asset compression -- preparing a more reusable foundation for future non-skin glossaries +- preparing larger simulation features such as pattern support and collection tracking ## Roadmap -### v0.11 - -- Major tournament section covering CS:GO and CS2 eras -- Tournament pages with dates, organizers, winners, and placements -- Better linking between Majors and their souvenir packages, sticker capsules, and autograph capsules - ### v0.12 - Skin pattern and finish seed support, including knife phases, gem variants, fade-style finishes, and other pattern-driven outcomes @@ -171,3 +167,4 @@ The project is actively evolving, with current work focused on: - Better automated test coverage beyond basic smoke checks - Music Kit preview playback if a reliable audio source is available - Optional China / Perfect World visual mode if a reliable alternate asset source is available + diff --git a/assets/data/tournament_metadata.json b/assets/data/tournament_metadata.json new file mode 100644 index 00000000..11cffc8d --- /dev/null +++ b/assets/data/tournament_metadata.json @@ -0,0 +1,10022 @@ +[ + { + "name": "BLAST.tv Austin 2025", + "winner": "Team Vitality", + "tournamentLogo": "assets/tournament_logos/blast_tv_austin_2025.png", + "startDate": "2025-06-03", + "endDate": "2025-06-22", + "placements": [ + { + "place": "1st", + "team": "Team Vitality", + "teamLogo": "assets/tournament_logos/team_vitality_41px_team_vitality_2023_lightmode_png.png" + }, + { + "place": "2nd", + "team": "The MongolZ", + "teamLogo": "assets/tournament_logos/the_mongolz_39px_the_mongolz_2024_03_allmode_png.png" + }, + { + "place": "3rd-4th", + "team": "MOUZ", + "teamLogo": "assets/tournament_logos/mouz_47px_mouz_2021_allmode_png.png" + }, + { + "place": "3rd-4th", + "team": "paiN Gaming", + "teamLogo": "assets/tournament_logos/pain_gaming_75px_pain_gaming_2023_lightmode_png.png" + }, + { + "place": "5th-8th", + "team": "Team Spirit", + "teamLogo": "assets/tournament_logos/team_spirit_43px_team_spirit_2022_lightmode_png.png" + }, + { + "place": "5th-8th", + "team": "Natus Vincere", + "teamLogo": "assets/tournament_logos/natus_vincere_57px_natus_vincere_2021_lightmode_png.png" + }, + { + "place": "5th-8th", + "team": "FaZe Clan", + "teamLogo": "assets/tournament_logos/faze_clan_76px_faze_clan_2025_allmode_png.png" + }, + { + "place": "5th-8th", + "team": "FURIA", + "teamLogo": "assets/tournament_logos/furia_51px_furia_esports_allmode_png.png" + }, + { + "place": "9th-11th", + "team": "Virtus.pro", + "teamLogo": "assets/tournament_logos/virtus_pro_51px_virtus_pro_2019_allmode_png.png" + }, + { + "place": "9th-11th", + "team": "Legacy", + "teamLogo": "assets/tournament_logos/legacy_49px_legacy_allmode_png.png" + }, + { + "place": "9th-11th", + "team": "G2 Esports", + "teamLogo": "assets/tournament_logos/g2_esports_43px_g2_esports_2020_lightmode_png.png" + }, + { + "place": "12th-14th", + "team": "Aurora Gaming", + "teamLogo": "assets/tournament_logos/aurora_gaming_50px_aurora_gaming_2025_allmode_png.png" + }, + { + "place": "12th-14th", + "team": "3DMAX", + "teamLogo": "assets/tournament_logos/3dmax_36px_3dmax_2024_allmode_png.png" + }, + { + "place": "12th-14th", + "team": "Lynn Vision Gaming", + "teamLogo": "assets/tournament_logos/lynn_vision_gaming_74px_lynn_vision_gaming_2024_allmode_png.png" + }, + { + "place": "15th-16th", + "team": "Nemiga Gaming", + "teamLogo": "assets/tournament_logos/nemiga_gaming_50px_nemiga_gaming_2020_temporary_png.png" + }, + { + "place": "15th-16th", + "team": "Team Liquid", + "teamLogo": "assets/tournament_logos/team_liquid_44px_team_liquid_2024_lightmode_png.png" + }, + { + "place": "17th-19th", + "team": "B8", + "teamLogo": "assets/tournament_logos/b8_41px_b8_lightmode_png.png" + }, + { + "place": "17th-19th", + "team": "MIBR", + "teamLogo": "assets/tournament_logos/mibr_100px_mibr_2018_lightmode_png.png" + }, + { + "place": "17th-19th", + "team": "HEROIC", + "teamLogo": "assets/tournament_logos/heroic_57px_heroic_2024_allmode_png.png" + }, + { + "place": "20th-22nd", + "team": "TYLOO", + "teamLogo": "assets/tournament_logos/tyloo_56px_tyloo_2016_allmode_png.png" + }, + { + "place": "20th-22nd", + "team": "M80", + "teamLogo": "assets/tournament_logos/m80_52px_m80_esports_2024_lightmode_png.png" + }, + { + "place": "20th-22nd", + "team": "Team Falcons", + "teamLogo": "assets/tournament_logos/team_falcons_41px_team_falcons_2022_allmode_png.png" + }, + { + "place": "23rd-24th", + "team": "BetBoom Team", + "teamLogo": "assets/tournament_logos/betboom_team_56px_betboom_team_2024_allmode_png.png" + }, + { + "place": "23rd-24th", + "team": "OG", + "teamLogo": "assets/tournament_logos/og_34px_og_rb_allmode_png.png" + }, + { + "place": "25th-27th", + "team": "FlyQuest", + "teamLogo": "assets/tournament_logos/flyquest_51px_flyquest_2021_allmode_png.png" + }, + { + "place": "25th-27th", + "team": "NRG", + "teamLogo": "assets/tournament_logos/nrg_100px_nrg_2024_lightmode_png.png" + }, + { + "place": "25th-27th", + "team": "Wildcard", + "teamLogo": "assets/tournament_logos/wildcard_42px_wildcard_2024_lightmode_png.png" + }, + { + "place": "28th-30th", + "team": "Imperial Esports", + "teamLogo": "assets/tournament_logos/imperial_esports_44px_imperial_esports_2022_allmode_png.png" + }, + { + "place": "28th-30th", + "team": "Chinggis Warriors", + "teamLogo": "assets/tournament_logos/chinggis_warriors_50px_cw_allmode_png.png" + }, + { + "place": "28th-30th", + "team": "Complexity", + "teamLogo": "assets/tournament_logos/complexity_100px_complexity_2025_allmode_png.png" + }, + { + "place": "31st-32nd", + "team": "Metizport", + "teamLogo": "assets/tournament_logos/metizport_50px_metizport_2023_allmode_png.png" + }, + { + "place": "31st-32nd", + "team": "Fluxo", + "teamLogo": "assets/tournament_logos/fluxo_50px_fluxo_lightmode_png.png" + } + ], + "teamRosters": [ + { + "team": "Team Vitality", + "teamLogo": "assets/tournament_logos/team_vitality_41px_team_vitality_2023_lightmode_png.png", + "players": [ + "apEX", + "ZywOo", + "flameZ", + "mezii", + "ropz" + ] + }, + { + "team": "MOUZ", + "teamLogo": "assets/tournament_logos/mouz_47px_mouz_2021_allmode_png.png", + "players": [ + "torzsi", + "xertioN", + "Jimpphat", + "Brollan", + "Spinx" + ] + }, + { + "team": "Team Spirit", + "teamLogo": "assets/tournament_logos/team_spirit_43px_team_spirit_2022_lightmode_png.png", + "players": [ + "chopper", + "magixx", + "zont1x", + "donk", + "sh1ro" + ] + }, + { + "team": "G2 Esports", + "teamLogo": "assets/tournament_logos/g2_esports_43px_g2_esports_2020_lightmode_png.png", + "players": [ + "huNter-", + "malbsMd", + "Snax", + "HeavyGod", + "hades" + ] + }, + { + "team": "The MongolZ", + "teamLogo": "assets/tournament_logos/the_mongolz_39px_the_mongolz_2024_03_allmode_png.png", + "players": [ + "bLitz", + "Techno4K", + "910", + "mzinho", + "Senzu" + ] + }, + { + "team": "Aurora Gaming", + "teamLogo": "assets/tournament_logos/aurora_gaming_50px_aurora_gaming_2025_allmode_png.png", + "players": [ + "XANTARES", + "MAJ3R", + "Wicadia", + "woxic", + "jottAAA" + ] + }, + { + "team": "Natus Vincere", + "teamLogo": "assets/tournament_logos/natus_vincere_57px_natus_vincere_2021_lightmode_png.png", + "players": [ + "b1t", + "Aleksib", + "jL", + "iM", + "w0nderful" + ] + }, + { + "team": "Team Liquid", + "teamLogo": "assets/tournament_logos/team_liquid_44px_team_liquid_2024_lightmode_png.png", + "players": [ + "NAF", + "Twistzz", + "ultimate", + "NertZ", + "siuhy" + ] + }, + { + "team": "Team Falcons", + "teamLogo": "assets/tournament_logos/team_falcons_41px_team_falcons_2022_allmode_png.png", + "players": [ + "Magisk", + "NiKo", + "TeSeS", + "kyxsan", + "m0NESY" + ] + }, + { + "team": "FaZe Clan", + "teamLogo": "assets/tournament_logos/faze_clan_76px_faze_clan_2025_allmode_png.png", + "players": [ + "rain", + "broky", + "karrigan", + "frozen", + "EliGE" + ] + }, + { + "team": "3DMAX", + "teamLogo": "assets/tournament_logos/3dmax_36px_3dmax_2024_allmode_png.png", + "players": [ + "Lucky", + "Ex3rcice", + "Maka", + "Graviti", + "bodyy" + ] + }, + { + "team": "Virtus.pro", + "teamLogo": "assets/tournament_logos/virtus_pro_51px_virtus_pro_2019_allmode_png.png", + "players": [ + "FL1T", + "fame", + "electroNic", + "FL4MUS", + "ICY" + ] + }, + { + "team": "paiN Gaming", + "teamLogo": "assets/tournament_logos/pain_gaming_75px_pain_gaming_2023_lightmode_png.png", + "players": [ + "biguzera", + "nqz", + "snow", + "dav1deuS", + "dgt" + ] + }, + { + "team": "FURIA", + "teamLogo": "assets/tournament_logos/furia_51px_furia_esports_allmode_png.png", + "players": [ + "yuurih", + "KSCERATO", + "FalleN", + "skullz", + "molodoy" + ] + }, + { + "team": "MIBR", + "teamLogo": "assets/tournament_logos/mibr_100px_mibr_2018_lightmode_png.png", + "players": [ + "exit", + "brnz4n", + "insani", + "saffee", + "Lucaozy" + ] + }, + { + "team": "M80", + "teamLogo": "assets/tournament_logos/m80_52px_m80_esports_2024_lightmode_png.png", + "players": [ + "Swisher", + "reck", + "slaxz-", + "s1n", + "Lake" + ] + }, + { + "team": "Complexity", + "teamLogo": "assets/tournament_logos/complexity_100px_complexity_2025_allmode_png.png", + "players": [ + "JT", + "Grim", + "hallzerk", + "Cxzi", + "nicx" + ] + }, + { + "team": "HEROIC", + "teamLogo": "assets/tournament_logos/heroic_57px_heroic_2024_allmode_png.png", + "players": [ + "SunPayus", + "LNZ", + "yxngstxr", + "xfl0ud", + "tN1R" + ] + }, + { + "team": "B8", + "teamLogo": "assets/tournament_logos/b8_41px_b8_lightmode_png.png", + "players": [ + "npl", + "esenthial", + "headtr1ck", + "alex666", + "kensizor" + ] + }, + { + "team": "BetBoom Team", + "teamLogo": "assets/tournament_logos/betboom_team_56px_betboom_team_2024_allmode_png.png", + "players": [ + "s1ren", + "zorte", + "Magnojez", + "Ax1Le", + "Boombl4" + ] + }, + { + "team": "TYLOO", + "teamLogo": "assets/tournament_logos/tyloo_56px_tyloo_2016_allmode_png.png", + "players": [ + "Attacker", + "JamYoung", + "Moseyuh", + "Mercury", + "Jee" + ] + }, + { + "team": "Lynn Vision Gaming", + "teamLogo": "assets/tournament_logos/lynn_vision_gaming_74px_lynn_vision_gaming_2024_allmode_png.png", + "players": [ + "westmelon", + "z4kr", + "EmiliaQAQ", + "Starry", + "C4LLM3SU3" + ] + }, + { + "team": "Wildcard", + "teamLogo": "assets/tournament_logos/wildcard_42px_wildcard_2024_lightmode_png.png", + "players": [ + "stanislaw", + "JBa", + "Sonic", + "susp", + "phzy" + ] + }, + { + "team": "FlyQuest", + "teamLogo": "assets/tournament_logos/flyquest_51px_flyquest_2021_allmode_png.png", + "players": [ + "INS", + "Liazz", + "Vexite", + "regali", + "nettik" + ] + }, + { + "team": "OG", + "teamLogo": "assets/tournament_logos/og_34px_og_rb_allmode_png.png", + "players": [ + "F1KU", + "Chr1zN", + "Buzz", + "spooke", + "nicoodoz" + ] + }, + { + "team": "Chinggis Warriors", + "teamLogo": "assets/tournament_logos/chinggis_warriors_50px_cw_allmode_png.png", + "players": [ + "controlez", + "ariucle", + "ROUX", + "efire", + "cool4st" + ] + }, + { + "team": "Imperial Esports", + "teamLogo": "assets/tournament_logos/imperial_esports_44px_imperial_esports_2022_allmode_png.png", + "players": [ + "VINI", + "noway", + "decenty", + "try", + "chayJESUS" + ] + }, + { + "team": "Nemiga Gaming", + "teamLogo": "assets/tournament_logos/nemiga_gaming_50px_nemiga_gaming_2020_temporary_png.png", + "players": [ + "1eeR", + "khaN", + "riskyb0b", + "Xant3r", + "zweih" + ] + }, + { + "team": "NRG", + "teamLogo": "assets/tournament_logos/nrg_100px_nrg_2024_lightmode_png.png", + "players": [ + "oSee", + "HexT", + "nitr0", + "Jeorge", + "br0" + ] + }, + { + "team": "Legacy", + "teamLogo": "assets/tournament_logos/legacy_49px_legacy_allmode_png.png", + "players": [ + "latto", + "dumau", + "saadzin", + "n1ssim", + "lux" + ] + }, + { + "team": "Metizport", + "teamLogo": "assets/tournament_logos/metizport_50px_metizport_2023_allmode_png.png", + "players": [ + "adamb", + "Plopski", + "L00m1", + "isak", + "hampus" + ] + }, + { + "team": "Fluxo", + "teamLogo": "assets/tournament_logos/fluxo_50px_fluxo_lightmode_png.png", + "players": [ + "zevy", + "arT", + "kye", + "piriajr", + "mlhzin" + ] + }, + { + "team": "BESTIA", + "players": [ + "Noktse", + "luchov", + "tomaszin", + "cass1n", + "timo" + ] + } + ], + "stageDates": [ + { + "phase": "Stage 1", + "startDate": "2025-06-03", + "endDate": "2025-06-06" + }, + { + "phase": "Stage 2", + "startDate": "2025-06-07", + "endDate": "2025-06-10" + }, + { + "phase": "Stage 3", + "startDate": "2025-06-12", + "endDate": "2025-06-15" + }, + { + "phase": "Playoffs", + "startDate": "2025-06-19", + "endDate": "2025-06-22" + } + ], + "playoffMatches": [ + { + "round": "Quarterfinals", + "team1": "Team Spirit", + "team2": "MOUZ", + "team1Logo": "assets/tournament_logos/team_spirit_43px_team_spirit_2022_lightmode_png.png", + "team2Logo": "assets/tournament_logos/mouz_47px_mouz_2021_allmode_png.png", + "score1": "1", + "score2": "2", + "date": "June 19, 2025 - 14:05 CDT" + }, + { + "round": "Quarterfinals", + "team1": "Team Vitality", + "team2": "Natus Vincere", + "team1Logo": "assets/tournament_logos/team_vitality_41px_team_vitality_2023_lightmode_png.png", + "team2Logo": "assets/tournament_logos/natus_vincere_57px_natus_vincere_2021_lightmode_png.png", + "score1": "2", + "score2": "0", + "date": "June 20, 2025 - 13:10 CDT" + }, + { + "round": "Semifinals", + "team1": "MOUZ", + "team2": "Team Vitality", + "team1Logo": "assets/tournament_logos/mouz_47px_mouz_2021_allmode_png.png", + "team2Logo": "assets/tournament_logos/team_vitality_41px_team_vitality_2023_lightmode_png.png", + "score1": "1", + "score2": "2", + "date": "June 21, 2025 - 13:00 CDT" + }, + { + "round": "Quarterfinals", + "team1": "FaZe Clan", + "team2": "The MongolZ", + "team1Logo": "assets/tournament_logos/faze_clan_76px_faze_clan_2025_allmode_png.png", + "team2Logo": "assets/tournament_logos/the_mongolz_39px_the_mongolz_2024_03_allmode_png.png", + "score1": "0", + "score2": "2", + "date": "June 20, 2025 - 16:15 CDT" + }, + { + "round": "Quarterfinals", + "team1": "PaiN Gaming", + "team2": "FURIA", + "team1Logo": "assets/tournament_logos/pain_gaming_75px_pain_gaming_2023_lightmode_png.png", + "team2Logo": "assets/tournament_logos/furia_51px_furia_esports_allmode_png.png", + "score1": "2", + "score2": "1", + "date": "June 19, 2025 - 19:20 CDT" + }, + { + "round": "Semifinals", + "team1": "The MongolZ", + "team2": "PaiN Gaming", + "team1Logo": "assets/tournament_logos/the_mongolz_39px_the_mongolz_2024_03_allmode_png.png", + "team2Logo": "assets/tournament_logos/pain_gaming_75px_pain_gaming_2023_lightmode_png.png", + "score1": "2", + "score2": "0", + "date": "June 21, 2025 - 16:50 CDT" + }, + { + "round": "Grand Final", + "team1": "Team Vitality", + "team2": "The MongolZ", + "team1Logo": "assets/tournament_logos/team_vitality_41px_team_vitality_2023_lightmode_png.png", + "team2Logo": "assets/tournament_logos/the_mongolz_39px_the_mongolz_2024_03_allmode_png.png", + "score1": "2", + "score2": "1", + "date": "June 22, 2025 - 14:30 CDT" + } + ] + }, + { + "name": "BLAST.tv Paris 2023", + "winner": "Team Vitality", + "tournamentLogo": "assets/tournament_logos/blast_tv_paris_2023.png", + "startDate": "2023-05-08", + "endDate": "2023-05-21", + "placements": [ + { + "place": "1st", + "team": "Team Vitality", + "teamLogo": "assets/tournament_logos/team_vitality_41px_team_vitality_2023_lightmode_png.png" + }, + { + "place": "2nd", + "team": "GamerLegion", + "teamLogo": "assets/tournament_logos/gamerlegion_91px_gamerlegion_cs_2023_allmode_png.png" + }, + { + "place": "3rd-4th", + "team": "Heroic", + "teamLogo": "assets/tournament_logos/heroic_57px_heroic_2023_allmode_png.png" + }, + { + "place": "3rd-4th", + "team": "Apeks", + "teamLogo": "assets/tournament_logos/apeks_100px_apeks_2021_darkmode_png.png" + }, + { + "place": "5th-8th", + "team": "FaZe Clan", + "teamLogo": "assets/tournament_logos/faze_clan_75px_faze_clan_november_2021_lightmode_png.png" + }, + { + "place": "5th-8th", + "team": "Monte", + "teamLogo": "assets/tournament_logos/monte_35px_monte_2022_allmode_png.png" + }, + { + "place": "5th-8th", + "team": "Team Liquid", + "teamLogo": "assets/tournament_logos/team_liquid_44px_team_liquid_2023_lightmode_png.png" + }, + { + "place": "5th-8th", + "team": "Into The Breach", + "teamLogo": "assets/tournament_logos/into_the_breach_47px_into_the_breach_pink_2022_allmode_png.png" + }, + { + "place": "9th-11th", + "team": "Natus Vincere", + "teamLogo": "assets/tournament_logos/natus_vincere_57px_natus_vincere_2021_lightmode_png.png" + }, + { + "place": "9th-11th", + "team": "Fnatic", + "teamLogo": "assets/tournament_logos/fnatic_77px_fnatic_2020_allmode_png.png" + }, + { + "place": "9th-11th", + "team": "Ninjas in Pyjamas", + "teamLogo": "assets/tournament_logos/ninjas_in_pyjamas_50px_ninjas_in_pyjamas_2021_lightmode_png.png" + }, + { + "place": "12th-14th", + "team": "ENCE", + "teamLogo": "assets/tournament_logos/ence_50px_ence_2020_allmode_png.png" + }, + { + "place": "12th-14th", + "team": "Bad News Eagles", + "teamLogo": "assets/tournament_logos/bad_news_eagles_34px_bad_news_eagles_allmode_png.png" + }, + { + "place": "12th-14th", + "team": "G2 Esports", + "teamLogo": "assets/tournament_logos/g2_esports_43px_g2_esports_2020_lightmode_png.png" + }, + { + "place": "15th-16th", + "team": "9INE", + "teamLogo": "assets/tournament_logos/9ine_50px_9ine_2022_lightmode_png.png" + }, + { + "place": "15th-16th", + "team": "FURIA Esports", + "teamLogo": "assets/tournament_logos/furia_esports_51px_furia_esports_allmode_png.png" + }, + { + "place": "17th-19th", + "team": "FORZE Esports", + "teamLogo": "assets/tournament_logos/forze_esports_46px_forze_esports_2023_february_lightmode_png.png" + }, + { + "place": "17th-19th", + "team": "Grayhound Gaming", + "teamLogo": "assets/tournament_logos/grayhound_gaming_43px_grayhound_gaming_allmode_png.png" + }, + { + "place": "17th-19th", + "team": "paiN Gaming", + "teamLogo": "assets/tournament_logos/pain_gaming_75px_pain_gaming_2017_lightmode_png.png" + }, + { + "place": "20th-22nd", + "team": "Complexity Gaming", + "teamLogo": "assets/tournament_logos/complexity_gaming_53px_complexity_gaming_2019_lightmode_png.png" + }, + { + "place": "20th-22nd", + "team": "The MongolZ", + "teamLogo": "assets/tournament_logos/the_mongolz_41px_the_mongolz_lightmode_png.png" + }, + { + "place": "20th-22nd", + "team": "OG", + "teamLogo": "assets/tournament_logos/og_34px_og_rb_allmode_png.png" + }, + { + "place": "23rd-24th", + "team": "MOUZ", + "teamLogo": "assets/tournament_logos/mouz_47px_mouz_2021_allmode_png.png" + }, + { + "place": "23rd-24th", + "team": "Fluxo", + "teamLogo": "assets/tournament_logos/fluxo_50px_fluxo_lightmode_png.png" + } + ], + "teamRosters": [ + { + "team": "Natus Vincere", + "teamLogo": "assets/tournament_logos/natus_vincere_57px_natus_vincere_2021_lightmode_png.png", + "players": [ + "s1mple", + "electronic", + "Perfecto", + "b1t", + "npl" + ] + }, + { + "team": "9INE", + "teamLogo": "assets/tournament_logos/9ine_50px_9ine_2022_lightmode_png.png", + "players": [ + "Goofy", + "KEi", + "Kylar", + "mynio", + "hades" + ] + }, + { + "team": "FURIA Esports", + "teamLogo": "assets/tournament_logos/furia_esports_51px_furia_esports_allmode_png.png", + "players": [ + "yuurih", + "arT", + "KSCERATO", + "drop", + "saffee" + ] + }, + { + "team": "Fnatic", + "teamLogo": "assets/tournament_logos/fnatic_77px_fnatic_2020_allmode_png.png", + "players": [ + "KRIMZ", + "mezii", + "nicoodoz", + "roeJ", + "FASHR" + ] + }, + { + "team": "Heroic", + "teamLogo": "assets/tournament_logos/heroic_57px_heroic_2023_allmode_png.png", + "players": [ + "stavn", + "cadiaN", + "TeSeS", + "sjuush", + "jabbi" + ] + }, + { + "team": "Into The Breach", + "teamLogo": "assets/tournament_logos/into_the_breach_47px_into_the_breach_pink_2022_allmode_png.png", + "players": [ + "Thomas", + "CYPHER", + "rallen", + "volt", + "CRUC1AL" + ] + }, + { + "team": "Team Vitality", + "teamLogo": "assets/tournament_logos/team_vitality_41px_team_vitality_2023_lightmode_png.png", + "players": [ + "apEX", + "ZywOo", + "dupreeh", + "Magisk", + "Spinx" + ] + }, + { + "team": "Bad News Eagles", + "teamLogo": "assets/tournament_logos/bad_news_eagles_34px_bad_news_eagles_allmode_png.png", + "players": [ + "SENER1", + "gxx-", + "juanflatroo", + "sinnopsyy", + "rigoN" + ] + }, + { + "team": "Monte", + "teamLogo": "assets/tournament_logos/monte_35px_monte_2022_allmode_png.png", + "players": [ + "Woro2k", + "BOROS", + "DemQQ", + "kRaSnaL", + "sdy" + ] + }, + { + "team": "paiN Gaming", + "teamLogo": "assets/tournament_logos/pain_gaming_75px_pain_gaming_2017_lightmode_png.png", + "players": [ + "biguzera", + "hardzao", + "NEKIZ", + "zevy", + "skullz" + ] + }, + { + "team": "G2 Esports", + "teamLogo": "assets/tournament_logos/g2_esports_43px_g2_esports_2020_lightmode_png.png", + "players": [ + "huNter-", + "NiKo", + "m0NESY", + "jks", + "HooXi" + ] + }, + { + "team": "GamerLegion", + "teamLogo": "assets/tournament_logos/gamerlegion_91px_gamerlegion_cs_2023_allmode_png.png", + "players": [ + "iM", + "isak", + "acoR", + "siuhy", + "Keoz" + ] + }, + { + "team": "FORZE Esports", + "teamLogo": "assets/tournament_logos/forze_esports_46px_forze_esports_2023_february_lightmode_png.png", + "players": [ + "Jerry", + "zorte", + "shalfey", + "Krad", + "r3salt" + ] + }, + { + "team": "Apeks", + "teamLogo": "assets/tournament_logos/apeks_100px_apeks_2021_darkmode_png.png", + "players": [ + "nawwk", + "jkaem", + "STYKO", + "jL", + "kyxsan" + ] + }, + { + "team": "Ninjas in Pyjamas", + "teamLogo": "assets/tournament_logos/ninjas_in_pyjamas_50px_ninjas_in_pyjamas_2021_lightmode_png.png", + "players": [ + "REZ", + "Brollan", + "Aleksib", + "headtr1ck", + "k0nfig" + ] + }, + { + "team": "OG", + "teamLogo": "assets/tournament_logos/og_34px_og_rb_allmode_png.png", + "players": [ + "flameZ", + "NEOFRAG", + "F1KU", + "degster", + "niko" + ] + }, + { + "team": "ENCE", + "teamLogo": "assets/tournament_logos/ence_50px_ence_2020_allmode_png.png", + "players": [ + "Snappi", + "dycha", + "maden", + "SunPayus", + "NertZ" + ] + }, + { + "team": "MOUZ", + "teamLogo": "assets/tournament_logos/mouz_47px_mouz_2021_allmode_png.png", + "players": [ + "frozen", + "dexter", + "torzsi", + "JDC", + "xertioN" + ] + }, + { + "team": "Team Liquid", + "teamLogo": "assets/tournament_logos/team_liquid_44px_team_liquid_2023_lightmode_png.png", + "players": [ + "EliGE", + "NAF", + "oSee", + "nitr0", + "YEKINDAR" + ] + }, + { + "team": "Grayhound Gaming", + "teamLogo": "assets/tournament_logos/grayhound_gaming_43px_grayhound_gaming_allmode_png.png", + "players": [ + "Sico", + "INS", + "aliStair", + "Liazz", + "Vexite" + ] + }, + { + "team": "Complexity Gaming", + "teamLogo": "assets/tournament_logos/complexity_gaming_53px_complexity_gaming_2019_lightmode_png.png", + "players": [ + "JT", + "FaNg", + "floppy", + "Grim", + "hallzerk" + ] + }, + { + "team": "The MongolZ", + "teamLogo": "assets/tournament_logos/the_mongolz_41px_the_mongolz_lightmode_png.png", + "players": [ + "bLitz", + "Techno4K", + "Bart4k", + "ANNIHILATION", + "sk0R" + ] + }, + { + "team": "Fluxo", + "teamLogo": "assets/tournament_logos/fluxo_50px_fluxo_lightmode_png.png", + "players": [ + "felps", + "Lucaozy", + "v$m", + "WOOD7", + "history" + ] + }, + { + "team": "FaZe Clan", + "teamLogo": "assets/tournament_logos/faze_clan_75px_faze_clan_november_2021_lightmode_png.png", + "players": [ + "rain", + "broky", + "Twistzz", + "karrigan", + "ropz" + ] + } + ], + "stageDates": [ + { + "phase": "Challengers Stage", + "startDate": "2023-05-08", + "endDate": "2023-05-11" + }, + { + "phase": "Legends Stage", + "startDate": "2023-05-13", + "endDate": "2023-05-16" + }, + { + "phase": "Champions Stage", + "startDate": "2023-05-18", + "endDate": "2023-05-21" + } + ], + "playoffMatches": [ + { + "round": "Quarterfinals", + "team1": "HEROIC", + "team2": "FaZe Clan", + "team1Logo": "assets/tournament_logos/heroic_57px_heroic_2023_allmode_png.png", + "team2Logo": "assets/tournament_logos/faze_clan_75px_faze_clan_november_2021_lightmode_png.png", + "score1": "2", + "score2": "1", + "date": "May 18, 2023 - 15:00 CEST" + }, + { + "round": "Quarterfinals", + "team1": "GamerLegion", + "team2": "Monte", + "team1Logo": "assets/tournament_logos/gamerlegion_91px_gamerlegion_cs_2023_allmode_png.png", + "team2Logo": "assets/tournament_logos/monte_35px_monte_2022_allmode_png.png", + "score1": "2", + "score2": "0", + "date": "May 19, 2023 - 15:00 CEST" + }, + { + "round": "Semifinals", + "team1": "HEROIC", + "team2": "GamerLegion", + "team1Logo": "assets/tournament_logos/heroic_57px_heroic_2023_allmode_png.png", + "team2Logo": "assets/tournament_logos/gamerlegion_91px_gamerlegion_cs_2023_allmode_png.png", + "score1": "1", + "score2": "2", + "date": "May 20, 2023 - 15:00 CEST" + }, + { + "round": "Quarterfinals", + "team1": "Team Liquid", + "team2": "Apeks", + "team1Logo": "assets/tournament_logos/team_liquid_44px_team_liquid_2023_lightmode_png.png", + "team2Logo": "assets/tournament_logos/apeks_100px_apeks_2021_darkmode_png.png", + "score1": "0", + "score2": "2", + "date": "May 19, 2023 - 17:55 CEST" + }, + { + "round": "Quarterfinals", + "team1": "Into The Breach", + "team2": "Team Vitality", + "team1Logo": "assets/tournament_logos/into_the_breach_47px_into_the_breach_pink_2022_allmode_png.png", + "team2Logo": "assets/tournament_logos/team_vitality_41px_team_vitality_2023_lightmode_png.png", + "score1": "0", + "score2": "2", + "date": "May 18, 2023 - 19:15 CEST" + }, + { + "round": "Semifinals", + "team1": "Apeks", + "team2": "Team Vitality", + "team1Logo": "assets/tournament_logos/apeks_100px_apeks_2021_darkmode_png.png", + "team2Logo": "assets/tournament_logos/team_vitality_41px_team_vitality_2023_lightmode_png.png", + "score1": "0", + "score2": "2", + "date": "May 20, 2023 - 19:35 CEST" + }, + { + "round": "Grand Final", + "team1": "GamerLegion", + "team2": "Team Vitality", + "team1Logo": "assets/tournament_logos/gamerlegion_91px_gamerlegion_cs_2023_allmode_png.png", + "team2Logo": "assets/tournament_logos/team_vitality_41px_team_vitality_2023_lightmode_png.png", + "score1": "0", + "score2": "2", + "date": "May 21, 2023 - 17:00 CEST" + } + ] + }, + { + "name": "DreamHack Cluj-Napoca 2015", + "winner": "Team EnVyUs", + "tournamentLogo": "assets/tournament_logos/dreamhack_cluj_napoca_2015.png", + "startDate": "2015-10-28", + "endDate": "2015-11-01", + "placements": [ + { + "place": "1st", + "team": "Team EnVyUs", + "teamLogo": "assets/tournament_logos/team_envyus_50px_team_envyus_lightmode_png.png" + }, + { + "place": "2nd", + "team": "Natus Vincere", + "teamLogo": "assets/tournament_logos/natus_vincere_56px_natus_vincere_allmode_png.png" + }, + { + "place": "3rd-4th", + "team": "G2 Esports", + "teamLogo": "assets/tournament_logos/g2_esports_43px_g2_esports_allmode_png.png" + }, + { + "place": "3rd-4th", + "team": "Ninjas in Pyjamas", + "teamLogo": "assets/tournament_logos/ninjas_in_pyjamas_50px_ninjas_in_pyjamas_2012_allmode_png.png" + }, + { + "place": "5th-8th", + "team": "Fnatic", + "teamLogo": "assets/tournament_logos/fnatic_61px_fnatic_allmode_png.png" + }, + { + "place": "5th-8th", + "team": "Virtus.pro", + "teamLogo": "assets/tournament_logos/virtus_pro_48px_virtus_pro_allmode_png.png" + }, + { + "place": "5th-8th", + "team": "Team SoloMid", + "teamLogo": "assets/tournament_logos/team_solomid_50px_team_solomid_lightmode_png.png" + }, + { + "place": "5th-8th", + "team": "Luminosity Gaming", + "teamLogo": "assets/tournament_logos/luminosity_gaming_51px_luminosity_gaming_allmode_png.png" + }, + { + "place": "9th-12th", + "team": "Cloud9", + "teamLogo": "assets/tournament_logos/cloud9_70px_cloud9_allmode_png.png" + }, + { + "place": "9th-12th", + "team": "mousesports", + "teamLogo": "assets/tournament_logos/mousesports_92px_mousesports_new_png.png" + }, + { + "place": "9th-12th", + "team": "Titan", + "teamLogo": "assets/tournament_logos/titan_46px_titan_lightmode_png.png" + }, + { + "place": "9th-12th", + "team": "Counter Logic Gaming", + "teamLogo": "assets/tournament_logos/counter_logic_gaming_70px_counter_logic_gaming_allmode_png.png" + }, + { + "place": "13th-16th", + "team": "Vexed Gaming", + "teamLogo": "assets/tournament_logos/vexed_gaming_41px_vexed_gaming_allmode_png.png" + }, + { + "place": "13th-16th", + "team": "FlipSid3 Tactics", + "teamLogo": "assets/tournament_logos/flipsid3_tactics_47px_flipsid3_tactics_lightmode_png.png" + }, + { + "place": "13th-16th", + "team": "Team Liquid", + "teamLogo": "assets/tournament_logos/team_liquid_50px_team_liquid_allmode_png.png" + }, + { + "place": "13th-16th", + "team": "Team Dignitas", + "teamLogo": "assets/tournament_logos/team_dignitas_44px_team_dignitas_allmode_png.png" + } + ], + "teamRosters": [ + { + "team": "Fnatic", + "teamLogo": "assets/tournament_logos/fnatic_61px_fnatic_allmode_png.png", + "players": [ + "JW", + "flusha", + "pronax", + "olofmeister", + "KRIMZ" + ] + }, + { + "team": "Team EnVyUs", + "teamLogo": "assets/tournament_logos/team_envyus_50px_team_envyus_lightmode_png.png", + "players": [ + "Happy", + "NBK-", + "kioShiMa", + "kennyS", + "apEX" + ] + }, + { + "team": "Virtus.pro", + "teamLogo": "assets/tournament_logos/virtus_pro_48px_virtus_pro_allmode_png.png", + "players": [ + "TaZ", + "NEO", + "pashaBiceps", + "Snax", + "byali" + ] + }, + { + "team": "Team SoloMid", + "teamLogo": "assets/tournament_logos/team_solomid_50px_team_solomid_lightmode_png.png", + "players": [ + "device", + "dupreeh", + "Xyp9x", + "cajunb", + "karrigan" + ] + }, + { + "team": "Natus Vincere", + "teamLogo": "assets/tournament_logos/natus_vincere_56px_natus_vincere_allmode_png.png", + "players": [ + "Zeus", + "seized", + "Edward", + "GuardiaN", + "flamie" + ] + }, + { + "team": "G2 Esports", + "teamLogo": "assets/tournament_logos/g2_esports_43px_g2_esports_allmode_png.png", + "players": [ + "Maikelele", + "fox", + "rain", + "dennis", + "jkaem" + ] + }, + { + "team": "Ninjas in Pyjamas", + "teamLogo": "assets/tournament_logos/ninjas_in_pyjamas_50px_ninjas_in_pyjamas_2012_allmode_png.png", + "players": [ + "f0rest", + "GeT_RiGhT", + "Xizt", + "friberg", + "allu" + ] + }, + { + "team": "Luminosity Gaming", + "teamLogo": "assets/tournament_logos/luminosity_gaming_51px_luminosity_gaming_allmode_png.png", + "players": [ + "FalleN", + "fer", + "boltz", + "steel", + "coldzera" + ] + }, + { + "team": "Cloud9", + "teamLogo": "assets/tournament_logos/cloud9_70px_cloud9_allmode_png.png", + "players": [ + "n0thing", + "sgares", + "shroud", + "fREAKAZOiD", + "Skadoodle" + ] + }, + { + "team": "Counter Logic Gaming", + "teamLogo": "assets/tournament_logos/counter_logic_gaming_70px_counter_logic_gaming_allmode_png.png", + "players": [ + "reltuC", + "FNS", + "hazed", + "tarik", + "jdm64" + ] + }, + { + "team": "mousesports", + "teamLogo": "assets/tournament_logos/mousesports_92px_mousesports_new_png.png", + "players": [ + "chrisJ", + "gob b", + "NiKo", + "nex", + "denis" + ] + }, + { + "team": "Titan", + "teamLogo": "assets/tournament_logos/titan_46px_titan_lightmode_png.png", + "players": [ + "Ex6TenZ", + "RpK", + "shox", + "SmithZz", + "ScreaM" + ] + }, + { + "team": "Team Dignitas", + "teamLogo": "assets/tournament_logos/team_dignitas_44px_team_dignitas_allmode_png.png", + "players": [ + "aizy", + "Kjaerbye", + "Pimp", + "MSL", + "tenzki" + ] + }, + { + "team": "FlipSid3 Tactics", + "teamLogo": "assets/tournament_logos/flipsid3_tactics_47px_flipsid3_tactics_lightmode_png.png", + "players": [ + "B1ad3", + "WorldEdit", + "bondik", + "markeloff", + "DavCost" + ] + }, + { + "team": "Team Liquid", + "teamLogo": "assets/tournament_logos/team_liquid_50px_team_liquid_allmode_png.png", + "players": [ + "adreN", + "nitr0", + "FugLy", + "EliGE", + "Hiko" + ] + }, + { + "team": "Vexed Gaming", + "teamLogo": "assets/tournament_logos/vexed_gaming_41px_vexed_gaming_allmode_png.png", + "players": [ + "Furlan", + "Hyper", + "GruBy", + "peet", + "rallen" + ] + } + ], + "stageDates": [], + "playoffMatches": [ + { + "round": "Quarterfinals", + "team1": "Team Envy", + "team2": "Fnatic", + "team1Logo": "assets/tournament_logos/team_envy_50px_team_envyus_lightmode_png.png", + "team2Logo": "assets/tournament_logos/fnatic_61px_fnatic_allmode_png.png", + "score1": "2", + "score2": "1", + "date": "October 31, 2015 - 11:15 EET" + }, + { + "round": "Quarterfinals", + "team1": "Virtus.pro", + "team2": "G2 Esports", + "team1Logo": "assets/tournament_logos/virtus_pro_48px_virtus_pro_allmode_png.png", + "team2Logo": "assets/tournament_logos/g2_esports_43px_g2_esports_allmode_png.png", + "score1": "0", + "score2": "2", + "date": "October 31, 2015 - 14:55 EET" + }, + { + "round": "Semifinals", + "team1": "Team Envy", + "team2": "G2 Esports", + "team1Logo": "assets/tournament_logos/team_envy_50px_team_envyus_lightmode_png.png", + "team2Logo": "assets/tournament_logos/g2_esports_43px_g2_esports_allmode_png.png", + "score1": "2", + "score2": "1", + "date": "November 1, 2015 - 12:05 EET" + }, + { + "round": "Quarterfinals", + "team1": "TSM", + "team2": "Ninjas in Pyjamas", + "team1Logo": "assets/tournament_logos/tsm_50px_team_solomid_lightmode_png.png", + "team2Logo": "assets/tournament_logos/ninjas_in_pyjamas_50px_ninjas_in_pyjamas_2012_allmode_png.png", + "score1": "0", + "score2": "2", + "date": "October 31, 2015 - 17:45 EET" + }, + { + "round": "Quarterfinals", + "team1": "Luminosity Gaming", + "team2": "Natus Vincere", + "team1Logo": "assets/tournament_logos/luminosity_gaming_51px_luminosity_gaming_allmode_png.png", + "team2Logo": "assets/tournament_logos/natus_vincere_56px_natus_vincere_allmode_png.png", + "score1": "0", + "score2": "2", + "date": "October 31, 2015 - 20:25 EET" + }, + { + "round": "Semifinals", + "team1": "Ninjas in Pyjamas", + "team2": "Natus Vincere", + "team1Logo": "assets/tournament_logos/ninjas_in_pyjamas_50px_ninjas_in_pyjamas_2012_allmode_png.png", + "team2Logo": "assets/tournament_logos/natus_vincere_56px_natus_vincere_allmode_png.png", + "score1": "0", + "score2": "2", + "date": "November 1, 2015 - 16:30 EET" + }, + { + "round": "Grand Final", + "team1": "Team Envy", + "team2": "Natus Vincere", + "team1Logo": "assets/tournament_logos/team_envy_50px_team_envyus_lightmode_png.png", + "team2Logo": "assets/tournament_logos/natus_vincere_56px_natus_vincere_allmode_png.png", + "score1": "2", + "score2": "0", + "date": "November 1, 2015 - 19:20 EET" + } + ] + }, + { + "name": "DreamHack Winter 2013", + "winner": "Fnatic", + "tournamentLogo": "assets/tournament_logos/dreamhack_winter_2013.png", + "startDate": "2013-11-28", + "endDate": "2013-11-30", + "placements": [ + { + "place": "1st", + "team": "Fnatic", + "teamLogo": "assets/tournament_logos/fnatic_61px_fnatic_allmode_png.png" + }, + { + "place": "2nd", + "team": "Ninjas in Pyjamas", + "teamLogo": "assets/tournament_logos/ninjas_in_pyjamas_50px_ninjas_in_pyjamas_2012_allmode_png.png" + }, + { + "place": "3rd-4th", + "team": "VeryGames", + "teamLogo": "assets/tournament_logos/verygames_88px_verygames_allmode_png.png" + }, + { + "place": "3rd-4th", + "team": "compLexity Gaming", + "teamLogo": "assets/tournament_logos/complexity_gaming_100px_complexity_logo_png.png" + }, + { + "place": "5th-8th", + "team": "LGB eSports", + "teamLogo": "assets/tournament_logos/lgb_esports_43px_lgb_esports_allmode_png.png" + }, + { + "place": "5th-8th", + "team": "Copenhagen Wolves", + "teamLogo": "assets/tournament_logos/copenhagen_wolves_50px_copenhagen_wolves_2012_allmode_png.png" + }, + { + "place": "5th-8th", + "team": "Recursive eSports", + "teamLogo": "assets/tournament_logos/recursive_esports_50px_recursive_esports_2014_allmode_png.png" + }, + { + "place": "5th-8th", + "team": "Astana Dragons", + "teamLogo": "assets/tournament_logos/astana_dragons_50px_astana_dragons_allmode_png.png" + }, + { + "place": "9th-12th", + "team": "Clan-Mystik", + "teamLogo": "assets/tournament_logos/clan_mystik_36px_clan_mystik_lightmode_png.png" + }, + { + "place": "9th-12th", + "team": "Universal Soldiers", + "teamLogo": "assets/tournament_logos/universal_soldiers_50px_universal_soldiers_allmode_png.png" + }, + { + "place": "9th-12th", + "team": "n!faculty", + "teamLogo": "assets/tournament_logos/n_faculty_55px_n_faculty_allmode_png.png" + }, + { + "place": "9th-12th", + "team": "Reason Gaming", + "teamLogo": "assets/tournament_logos/reason_gaming_49px_reason_gaming_allmode_png.png" + }, + { + "place": "13th-16th", + "team": "Natus Vincere", + "teamLogo": "assets/tournament_logos/natus_vincere_56px_natus_vincere_allmode_png.png" + }, + { + "place": "13th-16th", + "team": "Team iBUYPOWER", + "teamLogo": "assets/tournament_logos/team_ibuypower_86px_ibuypower_lightmode_png.png" + }, + { + "place": "13th-16th", + "team": "Xapso", + "teamLogo": "assets/tournament_logos/xapso_50px_xapso_allmode_png.png" + }, + { + "place": "13th-16th", + "team": "SK Gaming", + "teamLogo": "assets/tournament_logos/sk_gaming_50px_sk_gaming_2003_lightmode_png.png" + } + ], + "teamRosters": [ + { + "team": "Ninjas in Pyjamas", + "teamLogo": "assets/tournament_logos/ninjas_in_pyjamas_50px_ninjas_in_pyjamas_2012_allmode_png.png", + "players": [ + "f0rest", + "GeT_RiGhT", + "Xizt", + "Fifflaren", + "friberg" + ] + }, + { + "team": "n!faculty", + "teamLogo": "assets/tournament_logos/n_faculty_55px_n_faculty_allmode_png.png", + "players": [ + "gla1ve", + "karrigan", + "cajunb", + "Pimp", + "raalz" + ] + }, + { + "team": "SK Gaming", + "teamLogo": "assets/tournament_logos/sk_gaming_50px_sk_gaming_2003_lightmode_png.png", + "players": [ + "pita", + "twist", + "xelos", + "Delpan", + "MODDII" + ] + }, + { + "team": "Copenhagen Wolves", + "teamLogo": "assets/tournament_logos/copenhagen_wolves_50px_copenhagen_wolves_2012_allmode_png.png", + "players": [ + "FeTiSh", + "dupreeh", + "Xyp9x", + "device", + "Nico" + ] + }, + { + "team": "Universal Soldiers", + "teamLogo": "assets/tournament_logos/universal_soldiers_50px_universal_soldiers_allmode_png.png", + "players": [ + "TaZ", + "NEO", + "pashaBiceps", + "Snax", + "byali" + ] + }, + { + "team": "Astana Dragons", + "teamLogo": "assets/tournament_logos/astana_dragons_50px_astana_dragons_allmode_png.png", + "players": [ + "ANGE1", + "Dosia", + "AdreN", + "markeloff", + "kUcheR" + ] + }, + { + "team": "compLexity Gaming", + "teamLogo": "assets/tournament_logos/complexity_gaming_100px_complexity_logo_png.png", + "players": [ + "Hiko", + "seang@res", + "Semphis", + "swag", + "n0thing" + ] + }, + { + "team": "VeryGames", + "teamLogo": "assets/tournament_logos/verygames_88px_verygames_allmode_png.png", + "players": [ + "Ex6TenZ", + "NBK-", + "SmithZz", + "ScreaM", + "shox" + ] + }, + { + "team": "Clan-Mystik", + "teamLogo": "assets/tournament_logos/clan_mystik_36px_clan_mystik_lightmode_png.png", + "players": [ + "HaRts", + "ioRek", + "kioShiMa", + "KQLY", + "apEX" + ] + }, + { + "team": "Fnatic", + "teamLogo": "assets/tournament_logos/fnatic_61px_fnatic_allmode_png.png", + "players": [ + "JW", + "flusha", + "schneider", + "Devilwalk", + "pronax" + ] + }, + { + "team": "Team iBUYPOWER", + "teamLogo": "assets/tournament_logos/team_ibuypower_86px_ibuypower_lightmode_png.png", + "players": [ + "anger", + "Skadoodle", + "adreN", + "AZK", + "DaZeD" + ] + }, + { + "team": "Natus Vincere", + "teamLogo": "assets/tournament_logos/natus_vincere_56px_natus_vincere_allmode_png.png", + "players": [ + "Zeus", + "starix", + "ceh9", + "seized", + "kibaken" + ] + }, + { + "team": "LGB eSports", + "teamLogo": "assets/tournament_logos/lgb_esports_43px_lgb_esports_allmode_png.png", + "players": [ + "SKYTTEN", + "eksem", + "KRiMZ", + "dennis", + "olofm" + ] + }, + { + "team": "Recursive eSports", + "teamLogo": "assets/tournament_logos/recursive_esports_50px_recursive_esports_2014_allmode_png.png", + "players": [ + "Happy", + "GMX", + "kennyS", + "Uzzziii", + "Maniac" + ] + }, + { + "team": "Xapso", + "teamLogo": "assets/tournament_logos/xapso_50px_xapso_allmode_png.png", + "players": [ + "ultra", + "centeks", + "cadiaN", + "aizy", + "robiin" + ] + }, + { + "team": "Reason Gaming", + "teamLogo": "assets/tournament_logos/reason_gaming_49px_reason_gaming_allmode_png.png", + "players": [ + "MSL", + "coloN", + "smF", + "EXR", + "LOMME" + ] + } + ], + "stageDates": [], + "playoffMatches": [ + { + "round": "Quarterfinals", + "team1": "LGB eSports", + "team2": "Ninjas in Pyjamas", + "team1Logo": "assets/tournament_logos/lgb_esports_43px_lgb_esports_allmode_png.png", + "team2Logo": "assets/tournament_logos/ninjas_in_pyjamas_50px_ninjas_in_pyjamas_2012_allmode_png.png", + "score1": "1", + "score2": "2", + "date": "November 28, 2013 - 22:45 CET" + }, + { + "round": "Quarterfinals", + "team1": "Copenhagen Wolves", + "team2": "VeryGames", + "team1Logo": "assets/tournament_logos/copenhagen_wolves_50px_copenhagen_wolves_2012_allmode_png.png", + "team2Logo": "assets/tournament_logos/verygames_88px_verygames_allmode_png.png", + "score1": "1", + "score2": "2", + "date": "November 29, 2013 - 20:00 CET" + }, + { + "round": "Semifinals", + "team1": "Ninjas in Pyjamas", + "team2": "VeryGames", + "team1Logo": "assets/tournament_logos/ninjas_in_pyjamas_50px_ninjas_in_pyjamas_2012_allmode_png.png", + "team2Logo": "assets/tournament_logos/verygames_88px_verygames_allmode_png.png", + "score1": "2", + "score2": "1", + "date": "November 30, 2013 - 14:30 CET" + }, + { + "round": "Quarterfinals", + "team1": "Fnatic", + "team2": "Recursive eSports", + "team1Logo": "assets/tournament_logos/fnatic_61px_fnatic_allmode_png.png", + "team2Logo": "assets/tournament_logos/recursive_esports_50px_recursive_esports_2014_allmode_png.png", + "score1": "2", + "score2": "1", + "date": "November 28, 2013 - 22:30 CET" + }, + { + "round": "Quarterfinals", + "team1": "Complexity", + "team2": "Astana Dragons", + "team1Logo": "assets/tournament_logos/complexity_100px_complexity_logo_png.png", + "team2Logo": "assets/tournament_logos/astana_dragons_50px_astana_dragons_allmode_png.png", + "score1": "2", + "score2": "1", + "date": "November 29, 2013 - 20:00 CET" + }, + { + "round": "Semifinals", + "team1": "Fnatic", + "team2": "Complexity", + "team1Logo": "assets/tournament_logos/fnatic_61px_fnatic_allmode_png.png", + "team2Logo": "assets/tournament_logos/complexity_100px_complexity_logo_png.png", + "score1": "2", + "score2": "0", + "date": "November 30, 2013 - 18:00 CET" + }, + { + "round": "Grand Final", + "team1": "Ninjas in Pyjamas", + "team2": "Fnatic", + "team1Logo": "assets/tournament_logos/ninjas_in_pyjamas_50px_ninjas_in_pyjamas_2012_allmode_png.png", + "team2Logo": "assets/tournament_logos/fnatic_61px_fnatic_allmode_png.png", + "score1": "1", + "score2": "2", + "date": "November 30, 2013 - 22:30 CET" + } + ] + }, + { + "name": "DreamHack Winter 2014", + "winner": "Team LDLC.com", + "tournamentLogo": "assets/tournament_logos/dreamhack_winter_2014.png", + "startDate": "2014-11-27", + "endDate": "2014-11-29", + "placements": [ + { + "place": "1st", + "team": "Team LDLC.com", + "teamLogo": "assets/tournament_logos/team_ldlc_com_100px_team_ldlc_2012_lightmode_png.png" + }, + { + "place": "2nd", + "team": "Ninjas in Pyjamas", + "teamLogo": "assets/tournament_logos/ninjas_in_pyjamas_50px_ninjas_in_pyjamas_2012_allmode_png.png" + }, + { + "place": "3rd-4th", + "team": "Virtus.pro", + "teamLogo": "assets/tournament_logos/virtus_pro_48px_virtus_pro_allmode_png.png" + }, + { + "place": "3rd-4th", + "team": "Natus Vincere", + "teamLogo": "assets/tournament_logos/natus_vincere_56px_natus_vincere_allmode_png.png" + }, + { + "place": "5th-8th", + "team": "HellRaisers", + "teamLogo": "assets/tournament_logos/hellraisers_57px_hellraisers_2014_allmode_png.png" + }, + { + "place": "5th-8th", + "team": "PENTA Sports", + "teamLogo": "assets/tournament_logos/penta_sports_37px_penta_sports_lightmode_png.png" + }, + { + "place": "5th-8th", + "team": "Fnatic", + "teamLogo": "assets/tournament_logos/fnatic_61px_fnatic_allmode_png.png" + }, + { + "place": "5th-8th", + "team": "Team Dignitas", + "teamLogo": "assets/tournament_logos/team_dignitas_44px_team_dignitas_allmode_png.png" + }, + { + "place": "9th-12th", + "team": "Cloud9", + "teamLogo": "assets/tournament_logos/cloud9_70px_cloud9_allmode_png.png" + }, + { + "place": "9th-12th", + "team": "Team iBUYPOWER", + "teamLogo": "assets/tournament_logos/team_ibuypower_86px_ibuypower_lightmode_png.png" + }, + { + "place": "9th-12th", + "team": "ESC Gaming", + "teamLogo": "assets/tournament_logos/esc_gaming_39px_esc_gaming_allmode_png.png" + }, + { + "place": "9th-12th", + "team": "FlipSid3 Tactics", + "teamLogo": "assets/tournament_logos/flipsid3_tactics_47px_flipsid3_tactics_lightmode_png.png" + }, + { + "place": "13th-16th", + "team": "Bravado Gaming", + "teamLogo": "assets/tournament_logos/bravado_gaming_49px_bravado_gaming_allmode_png.png" + }, + { + "place": "13th-16th", + "team": "Copenhagen Wolves", + "teamLogo": "assets/tournament_logos/copenhagen_wolves_50px_copenhagen_wolves_2012_allmode_png.png" + }, + { + "place": "13th-16th", + "team": "Planetkey Dynamics", + "teamLogo": "assets/tournament_logos/planetkey_dynamics_68px_planetkey_dynamics_allmode_png.png" + }, + { + "place": "13th-16th", + "team": "myXMG", + "teamLogo": "assets/tournament_logos/myxmg_65px_myxmg_allmode_png.png" + } + ], + "teamRosters": [ + { + "team": "Ninjas in Pyjamas", + "teamLogo": "assets/tournament_logos/ninjas_in_pyjamas_50px_ninjas_in_pyjamas_2012_allmode_png.png", + "players": [ + "f0rest", + "GeT_RiGhT", + "Xizt", + "friberg", + "Maikelele" + ] + }, + { + "team": "Fnatic", + "teamLogo": "assets/tournament_logos/fnatic_61px_fnatic_allmode_png.png", + "players": [ + "JW", + "flusha", + "pronax", + "olofmeister", + "KRIMZ" + ] + }, + { + "team": "Team Dignitas", + "teamLogo": "assets/tournament_logos/team_dignitas_44px_team_dignitas_allmode_png.png", + "players": [ + "FeTiSh", + "dupreeh", + "Xyp9x", + "device", + "cajunb" + ] + }, + { + "team": "Natus Vincere", + "teamLogo": "assets/tournament_logos/natus_vincere_56px_natus_vincere_allmode_png.png", + "players": [ + "Zeus", + "starix", + "seized", + "Edward", + "GuardiaN" + ] + }, + { + "team": "Virtus.pro", + "teamLogo": "assets/tournament_logos/virtus_pro_48px_virtus_pro_allmode_png.png", + "players": [ + "TaZ", + "NEO", + "pashaBiceps", + "Snax", + "byali" + ] + }, + { + "team": "Cloud9", + "teamLogo": "assets/tournament_logos/cloud9_70px_cloud9_allmode_png.png", + "players": [ + "Hiko", + "seang@res", + "SEMPHIS", + "n0thing", + "shroud" + ] + }, + { + "team": "Team LDLC.com", + "teamLogo": "assets/tournament_logos/team_ldlc_com_100px_team_ldlc_2012_lightmode_png.png", + "players": [ + "NBK-", + "SmithZz", + "kioShiMa", + "shox", + "Happy" + ] + }, + { + "team": "Planetkey Dynamics", + "teamLogo": "assets/tournament_logos/planetkey_dynamics_68px_planetkey_dynamics_allmode_png.png", + "players": [ + "strux1", + "stavros", + "alexRr", + "Troubley", + "nex" + ] + }, + { + "team": "PENTA Sports", + "teamLogo": "assets/tournament_logos/penta_sports_37px_penta_sports_lightmode_png.png", + "players": [ + "fel1x", + "kRYSTAL", + "r0bs3n", + "denis", + "Spiidi" + ] + }, + { + "team": "ESC Gaming", + "teamLogo": "assets/tournament_logos/esc_gaming_39px_esc_gaming_allmode_png.png", + "players": [ + "innocent", + "SZPERO", + "mouz", + "MINISE", + "rallen" + ] + }, + { + "team": "HellRaisers", + "teamLogo": "assets/tournament_logos/hellraisers_57px_hellraisers_2014_allmode_png.png", + "players": [ + "ANGE1", + "Dosia", + "markeloff", + "kUcheR", + "s1mple" + ] + }, + { + "team": "myXMG", + "teamLogo": "assets/tournament_logos/myxmg_65px_myxmg_allmode_png.png", + "players": [ + "MSL", + "HUNDEN", + "AcilioN", + "Friis", + "smF" + ] + }, + { + "team": "Team iBUYPOWER", + "teamLogo": "assets/tournament_logos/team_ibuypower_86px_ibuypower_lightmode_png.png", + "players": [ + "AZK", + "Skadoodle", + "swag", + "nitr0", + "desi" + ] + }, + { + "team": "Copenhagen Wolves", + "teamLogo": "assets/tournament_logos/copenhagen_wolves_50px_copenhagen_wolves_2012_allmode_png.png", + "players": [ + "gla1ve", + "Pimp", + "tenzki", + "Kjaerbye", + "cadiaN" + ] + }, + { + "team": "FlipSid3 Tactics", + "teamLogo": "assets/tournament_logos/flipsid3_tactics_47px_flipsid3_tactics_lightmode_png.png", + "players": [ + "twist", + "berg", + "zende", + "BENDJI", + "Dumas" + ] + }, + { + "team": "Bravado Gaming", + "teamLogo": "assets/tournament_logos/bravado_gaming_49px_bravado_gaming_allmode_png.png", + "players": [ + "Cent", + "Detrony", + "Racno", + "bLacKpoisoN", + "deviaNt" + ] + } + ], + "stageDates": [], + "playoffMatches": [ + { + "round": "Quarterfinals", + "team1": "HellRaisers", + "team2": "Ninjas in Pyjamas", + "team1Logo": "assets/tournament_logos/hellraisers_57px_hellraisers_2014_allmode_png.png", + "team2Logo": "assets/tournament_logos/ninjas_in_pyjamas_50px_ninjas_in_pyjamas_2012_allmode_png.png", + "score1": "0", + "score2": "2", + "date": "November 28, 2014 - 11:05 CET" + }, + { + "round": "Quarterfinals", + "team1": "Virtus.pro", + "team2": "PENTA Sports", + "team1Logo": "assets/tournament_logos/virtus_pro_48px_virtus_pro_allmode_png.png", + "team2Logo": "assets/tournament_logos/penta_sports_37px_penta_sports_lightmode_png.png", + "score1": "2", + "score2": "0", + "date": "November 28, 2014 - 14:30 CET" + }, + { + "round": "Semifinals", + "team1": "Ninjas in Pyjamas", + "team2": "Virtus.pro", + "team1Logo": "assets/tournament_logos/ninjas_in_pyjamas_50px_ninjas_in_pyjamas_2012_allmode_png.png", + "team2Logo": "assets/tournament_logos/virtus_pro_48px_virtus_pro_allmode_png.png", + "score1": "2", + "score2": "1", + "date": "November 29, 2014 - 13:00 CET" + }, + { + "round": "Quarterfinals", + "team1": "Team LDLC", + "team2": "Fnatic", + "team1Logo": "assets/tournament_logos/team_ldlc_100px_team_ldlc_2012_lightmode_png.png", + "team2Logo": "assets/tournament_logos/fnatic_61px_fnatic_allmode_png.png", + "score1": "2", + "score2": "1", + "date": "November 28, 2014 - 17:30 CET" + }, + { + "round": "Quarterfinals", + "team1": "Dignitas", + "team2": "Natus Vincere", + "team1Logo": "assets/tournament_logos/dignitas_44px_team_dignitas_allmode_png.png", + "team2Logo": "assets/tournament_logos/natus_vincere_56px_natus_vincere_allmode_png.png", + "score1": "0", + "score2": "2", + "date": "November 28, 2014 - 21:30 CET" + }, + { + "round": "Semifinals", + "team1": "Team LDLC", + "team2": "Natus Vincere", + "team1Logo": "assets/tournament_logos/team_ldlc_100px_team_ldlc_2012_lightmode_png.png", + "team2Logo": "assets/tournament_logos/natus_vincere_56px_natus_vincere_allmode_png.png", + "score1": "2", + "score2": "0", + "date": "November 29, 2014 - 16:30 CET" + }, + { + "round": "Grand Final", + "team1": "Ninjas in Pyjamas", + "team2": "Team LDLC", + "team1Logo": "assets/tournament_logos/ninjas_in_pyjamas_50px_ninjas_in_pyjamas_2012_allmode_png.png", + "team2Logo": "assets/tournament_logos/team_ldlc_100px_team_ldlc_2012_lightmode_png.png", + "score1": "1", + "score2": "2", + "date": "November 29, 2014 - 21:00 CET" + } + ] + }, + { + "name": "ELEAGUE Atlanta 2017", + "winner": "Astralis", + "tournamentLogo": "assets/tournament_logos/eleague_atlanta_2017.png", + "startDate": "2017-01-22", + "endDate": "2017-01-29", + "placements": [ + { + "place": "1st", + "team": "Astralis", + "teamLogo": "assets/tournament_logos/astralis_41px_astralis_logo_png.png" + }, + { + "place": "2nd", + "team": "Virtus.pro", + "teamLogo": "assets/tournament_logos/virtus_pro_48px_virtus_pro_allmode_png.png" + }, + { + "place": "3rd-4th", + "team": "Fnatic", + "teamLogo": "assets/tournament_logos/fnatic_60px_fnatic_2015_allmode_png.png" + }, + { + "place": "3rd-4th", + "team": "SK Gaming", + "teamLogo": "assets/tournament_logos/sk_gaming_50px_sk_gaming_2003_lightmode_png.png" + }, + { + "place": "5th-8th", + "team": "Natus Vincere", + "teamLogo": "assets/tournament_logos/natus_vincere_56px_natus_vincere_allmode_png.png" + }, + { + "place": "5th-8th", + "team": "Gambit Esports", + "teamLogo": "assets/tournament_logos/gambit_esports_46px_gambit_esports_allmode_png.png" + }, + { + "place": "5th-8th", + "team": "North", + "teamLogo": "assets/tournament_logos/north_48px_north_lightmode_png.png" + }, + { + "place": "5th-8th", + "team": "FaZe Clan", + "teamLogo": "assets/tournament_logos/faze_clan_76px_faze_clan_2017_allmode_png.png" + }, + { + "place": "9th-11th", + "team": "Team EnVyUs", + "teamLogo": "assets/tournament_logos/team_envyus_50px_team_envyus_lightmode_png.png" + }, + { + "place": "9th-11th", + "team": "GODSENT", + "teamLogo": "assets/tournament_logos/godsent_58px_godsent_lightmode_png.png" + }, + { + "place": "9th-11th", + "team": "Team Liquid", + "teamLogo": "assets/tournament_logos/team_liquid_50px_team_liquid_2017_lightmode_png.png" + }, + { + "place": "12th-14th", + "team": "OpTic Gaming", + "teamLogo": "assets/tournament_logos/optic_gaming_77px_optic_gaming_lightmode_png.png" + }, + { + "place": "12th-14th", + "team": "G2 Esports", + "teamLogo": "assets/tournament_logos/g2_esports_43px_g2_esports_allmode_png.png" + }, + { + "place": "12th-14th", + "team": "mousesports", + "teamLogo": "assets/tournament_logos/mousesports_92px_mousesports_2016_allmode_png.png" + }, + { + "place": "15th-16th", + "team": "FlipSid3 Tactics", + "teamLogo": "assets/tournament_logos/flipsid3_tactics_47px_flipsid3_tactics_lightmode_png.png" + }, + { + "place": "15th-16th", + "team": "HellRaisers", + "teamLogo": "assets/tournament_logos/hellraisers_57px_hellraisers_2014_allmode_png.png" + } + ], + "teamRosters": [ + { + "team": "SK Gaming", + "teamLogo": "assets/tournament_logos/sk_gaming_50px_sk_gaming_2003_lightmode_png.png", + "players": [ + "FalleN", + "fer", + "coldzera", + "TACO", + "fox" + ] + }, + { + "team": "Team Liquid", + "teamLogo": "assets/tournament_logos/team_liquid_50px_team_liquid_2017_lightmode_png.png", + "players": [ + "nitr0", + "EliGE", + "Hiko", + "jdm64", + "Pimp" + ] + }, + { + "team": "Virtus.pro", + "teamLogo": "assets/tournament_logos/virtus_pro_48px_virtus_pro_allmode_png.png", + "players": [ + "TaZ", + "NEO", + "pashaBiceps", + "Snax", + "byali" + ] + }, + { + "team": "Fnatic", + "teamLogo": "assets/tournament_logos/fnatic_60px_fnatic_2015_allmode_png.png", + "players": [ + "olofmeister", + "dennis", + "twist", + "KRIMZ", + "disco doplan" + ] + }, + { + "team": "Astralis", + "teamLogo": "assets/tournament_logos/astralis_41px_astralis_logo_png.png", + "players": [ + "device", + "dupreeh", + "Xyp9x", + "Kjaerbye", + "gla1ve" + ] + }, + { + "team": "FlipSid3 Tactics", + "teamLogo": "assets/tournament_logos/flipsid3_tactics_47px_flipsid3_tactics_lightmode_png.png", + "players": [ + "B1ad3", + "markeloff", + "WorldEdit", + "wayLander", + "electronic" + ] + }, + { + "team": "Natus Vincere", + "teamLogo": "assets/tournament_logos/natus_vincere_56px_natus_vincere_allmode_png.png", + "players": [ + "seized", + "Edward", + "GuardiaN", + "flamie", + "s1mple" + ] + }, + { + "team": "Gambit Esports", + "teamLogo": "assets/tournament_logos/gambit_esports_46px_gambit_esports_allmode_png.png", + "players": [ + "Dosia", + "AdreN", + "mou", + "Zeus", + "HObbit" + ] + }, + { + "team": "GODSENT", + "teamLogo": "assets/tournament_logos/godsent_58px_godsent_lightmode_png.png", + "players": [ + "pronax", + "znajder", + "flusha", + "JW", + "Lekr0" + ] + }, + { + "team": "FaZe Clan", + "teamLogo": "assets/tournament_logos/faze_clan_76px_faze_clan_2017_allmode_png.png", + "players": [ + "rain", + "aizy", + "allu", + "karrigan", + "kioShiMa" + ] + }, + { + "team": "North", + "teamLogo": "assets/tournament_logos/north_48px_north_lightmode_png.png", + "players": [ + "MSL", + "k0nfig", + "RUBINO", + "cajunb", + "Magisk" + ] + }, + { + "team": "mousesports", + "teamLogo": "assets/tournament_logos/mousesports_92px_mousesports_2016_allmode_png.png", + "players": [ + "chrisJ", + "NiKo", + "denis", + "Spiidi", + "loWel" + ] + }, + { + "team": "OpTic Gaming", + "teamLogo": "assets/tournament_logos/optic_gaming_77px_optic_gaming_lightmode_png.png", + "players": [ + "NAF", + "RUSH", + "mixwell", + "tarik", + "stanislaw" + ] + }, + { + "team": "Team EnVyUs", + "teamLogo": "assets/tournament_logos/team_envyus_50px_team_envyus_lightmode_png.png", + "players": [ + "Happy", + "NBK-", + "kennyS", + "apEX", + "SIXER" + ] + }, + { + "team": "G2 Esports", + "teamLogo": "assets/tournament_logos/g2_esports_43px_g2_esports_allmode_png.png", + "players": [ + "shox", + "RpK", + "SmithZz", + "ScreaM", + "bodyy" + ] + }, + { + "team": "HellRaisers", + "teamLogo": "assets/tournament_logos/hellraisers_57px_hellraisers_2014_allmode_png.png", + "players": [ + "ANGE1", + "STYKO", + "Zero", + "bondik", + "DeadFox" + ] + } + ], + "stageDates": [], + "playoffMatches": [ + { + "round": "Quarterfinals", + "team1": "Natus Vincere", + "team2": "Astralis", + "team1Logo": "assets/tournament_logos/natus_vincere_56px_natus_vincere_allmode_png.png", + "team2Logo": "assets/tournament_logos/astralis_41px_astralis_logo_png.png", + "score1": "1", + "score2": "2", + "date": "January 27, 2017 - 10:00 EST" + }, + { + "round": "Quarterfinals", + "team1": "Fnatic", + "team2": "Gambit Esports", + "team1Logo": "assets/tournament_logos/fnatic_60px_fnatic_2015_allmode_png.png", + "team2Logo": "assets/tournament_logos/gambit_esports_46px_gambit_esports_allmode_png.png", + "score1": "2", + "score2": "1", + "date": "January 27, 2017 - 14:45 EST" + }, + { + "round": "Semifinals", + "team1": "Astralis", + "team2": "Fnatic", + "team1Logo": "assets/tournament_logos/astralis_41px_astralis_logo_png.png", + "team2Logo": "assets/tournament_logos/fnatic_60px_fnatic_2015_allmode_png.png", + "score1": "2", + "score2": "0", + "date": "January 28, 2017 - 13:40 EST" + }, + { + "round": "Quarterfinals", + "team1": "Virtus.pro", + "team2": "North", + "team1Logo": "assets/tournament_logos/virtus_pro_48px_virtus_pro_allmode_png.png", + "team2Logo": "assets/tournament_logos/north_48px_north_lightmode_png.png", + "score1": "2", + "score2": "1", + "date": "January 27, 2017 - 18:35 EST" + }, + { + "round": "Quarterfinals", + "team1": "FaZe Clan", + "team2": "SK Gaming", + "team1Logo": "assets/tournament_logos/faze_clan_76px_faze_clan_2017_allmode_png.png", + "team2Logo": "assets/tournament_logos/sk_gaming_50px_sk_gaming_2003_lightmode_png.png", + "score1": "1", + "score2": "2", + "date": "January 28, 2017 - 10:00 EST" + }, + { + "round": "Semifinals", + "team1": "Virtus.pro", + "team2": "SK Gaming", + "team1Logo": "assets/tournament_logos/virtus_pro_48px_virtus_pro_allmode_png.png", + "team2Logo": "assets/tournament_logos/sk_gaming_50px_sk_gaming_2003_lightmode_png.png", + "score1": "2", + "score2": "0", + "date": "January 28, 2017 - 17:00 EST" + }, + { + "round": "Grand Final", + "team1": "Astralis", + "team2": "Virtus.pro", + "team1Logo": "assets/tournament_logos/astralis_41px_astralis_logo_png.png", + "team2Logo": "assets/tournament_logos/virtus_pro_48px_virtus_pro_allmode_png.png", + "score1": "2", + "score2": "1", + "date": "January 29, 2017 - 10:00 EST" + } + ] + }, + { + "name": "ELEAGUE Boston 2018", + "winner": "Cloud9", + "tournamentLogo": "assets/tournament_logos/eleague_boston_2018.png", + "startDate": "2018-01-12", + "endDate": "2018-01-28", + "placements": [ + { + "place": "1st", + "team": "Cloud9", + "teamLogo": "assets/tournament_logos/cloud9_70px_cloud9_allmode_png.png" + }, + { + "place": "2nd", + "team": "FaZe Clan", + "teamLogo": "assets/tournament_logos/faze_clan_76px_faze_clan_2017_allmode_png.png" + }, + { + "place": "3rd-4th", + "team": "Natus Vincere", + "teamLogo": "assets/tournament_logos/natus_vincere_56px_natus_vincere_allmode_png.png" + }, + { + "place": "3rd-4th", + "team": "SK Gaming", + "teamLogo": "assets/tournament_logos/sk_gaming_50px_sk_gaming_2003_lightmode_png.png" + }, + { + "place": "5th-8th", + "team": "mousesports", + "teamLogo": "assets/tournament_logos/mousesports_92px_mousesports_2016_allmode_png.png" + }, + { + "place": "5th-8th", + "team": "Quantum Bellator Fire", + "teamLogo": "assets/tournament_logos/quantum_bellator_fire_38px_quantum_bellator_fire_allmode_png.png" + }, + { + "place": "5th-8th", + "team": "G2 Esports", + "teamLogo": "assets/tournament_logos/g2_esports_43px_g2_esports_2020_lightmode_png.png" + }, + { + "place": "5th-8th", + "team": "Fnatic", + "teamLogo": "assets/tournament_logos/fnatic_60px_fnatic_2015_allmode_png.png" + }, + { + "place": "9th-11th", + "team": "Gambit Esports", + "teamLogo": "assets/tournament_logos/gambit_esports_46px_gambit_esports_allmode_png.png" + }, + { + "place": "9th-11th", + "team": "Vega Squadron", + "teamLogo": "assets/tournament_logos/vega_squadron_47px_vega_squadron_2016_allmode_png.png" + }, + { + "place": "9th-11th", + "team": "Space Soldiers", + "teamLogo": "assets/tournament_logos/space_soldiers_57px_space_soldiers_lightmode_png.png" + }, + { + "place": "12th-14th", + "team": "BIG", + "teamLogo": "assets/tournament_logos/big_35px_big_lightmode_png.png" + }, + { + "place": "12th-14th", + "team": "Astralis", + "teamLogo": "assets/tournament_logos/astralis_41px_astralis_logo_png.png" + }, + { + "place": "12th-14th", + "team": "Team Liquid", + "teamLogo": "assets/tournament_logos/team_liquid_50px_team_liquid_2017_lightmode_png.png" + }, + { + "place": "15th-16th", + "team": "North", + "teamLogo": "assets/tournament_logos/north_48px_north_lightmode_png.png" + }, + { + "place": "15th-16th", + "team": "Virtus.pro", + "teamLogo": "assets/tournament_logos/virtus_pro_48px_virtus_pro_allmode_png.png" + }, + { + "place": "17th", + "team": "AVANGAR", + "teamLogo": "assets/tournament_logos/avangar_57px_avangar_allmode_png.png" + }, + { + "place": "18th", + "team": "Renegades", + "teamLogo": "assets/tournament_logos/renegades_47px_renegades_png.png" + }, + { + "place": "19th-21st", + "team": "Sprout", + "teamLogo": "assets/tournament_logos/sprout_29px_sprout_allmode_png.png" + }, + { + "place": "19th-21st", + "team": "Team Envy", + "teamLogo": "assets/tournament_logos/team_envy_50px_team_envy_2018_lightmode_png.png" + }, + { + "place": "19th-21st", + "team": "Misfits Gaming", + "teamLogo": "assets/tournament_logos/misfits_gaming_36px_misfits_gaming_2017_allmode_png.png" + }, + { + "place": "22nd-23rd", + "team": "Flash Gaming", + "teamLogo": "assets/tournament_logos/flash_gaming_56px_flash_gaming_allmode_png.png" + }, + { + "place": "22nd-23rd", + "team": "FlipSid3 Tactics", + "teamLogo": "assets/tournament_logos/flipsid3_tactics_47px_flipsid3_tactics_lightmode_png.png" + } + ], + "teamRosters": [ + { + "team": "Gambit Esports", + "teamLogo": "assets/tournament_logos/gambit_esports_46px_gambit_esports_allmode_png.png", + "players": [ + "Dosia", + "AdreN", + "mou", + "Hobbit", + "fitch" + ] + }, + { + "team": "Astralis", + "teamLogo": "assets/tournament_logos/astralis_41px_astralis_logo_png.png", + "players": [ + "device", + "dupreeh", + "Xyp9x", + "Kjaerbye", + "gla1ve" + ] + }, + { + "team": "Virtus.pro", + "teamLogo": "assets/tournament_logos/virtus_pro_48px_virtus_pro_allmode_png.png", + "players": [ + "TaZ", + "NEO", + "pashaBiceps", + "Snax", + "byali" + ] + }, + { + "team": "Fnatic", + "teamLogo": "assets/tournament_logos/fnatic_60px_fnatic_2015_allmode_png.png", + "players": [ + "KRIMZ", + "JW", + "flusha", + "Golden", + "Lekr0" + ] + }, + { + "team": "SK Gaming", + "teamLogo": "assets/tournament_logos/sk_gaming_50px_sk_gaming_2003_lightmode_png.png", + "players": [ + "FalleN", + "fer", + "coldzera", + "TACO", + "felps" + ] + }, + { + "team": "BIG", + "teamLogo": "assets/tournament_logos/big_35px_big_lightmode_png.png", + "players": [ + "gob b", + "LEGIJA", + "tabseN", + "nex", + "keev" + ] + }, + { + "team": "North", + "teamLogo": "assets/tournament_logos/north_48px_north_lightmode_png.png", + "players": [ + "MSL", + "k0nfig", + "cajunb", + "aizy", + "valde" + ] + }, + { + "team": "Forfeited Slot", + "players": [ + "HEN1", + "LUCAS1", + "boltz", + "steel", + "kNgV-" + ] + }, + { + "team": "Cloud9", + "teamLogo": "assets/tournament_logos/cloud9_70px_cloud9_allmode_png.png", + "players": [ + "Skadoodle", + "Stewie2K", + "autimatic", + "RUSH", + "tarik" + ] + }, + { + "team": "FlipSid3 Tactics", + "teamLogo": "assets/tournament_logos/flipsid3_tactics_47px_flipsid3_tactics_lightmode_png.png", + "players": [ + "B1ad3", + "WorldEdit", + "markeloff", + "wayLander", + "seized" + ] + }, + { + "team": "G2 Esports", + "teamLogo": "assets/tournament_logos/g2_esports_43px_g2_esports_2020_lightmode_png.png", + "players": [ + "shox", + "bodyy", + "NBK-", + "kennyS", + "apEX" + ] + }, + { + "team": "Natus Vincere", + "teamLogo": "assets/tournament_logos/natus_vincere_56px_natus_vincere_allmode_png.png", + "players": [ + "Edward", + "flamie", + "s1mple", + "Zeus", + "electronic" + ] + }, + { + "team": "mousesports", + "teamLogo": "assets/tournament_logos/mousesports_92px_mousesports_2016_allmode_png.png", + "players": [ + "chrisJ", + "oskar", + "ropz", + "suNny", + "STYKO" + ] + }, + { + "team": "Sprout", + "teamLogo": "assets/tournament_logos/sprout_29px_sprout_allmode_png.png", + "players": [ + "kRYSTAL", + "innocent", + "zehN", + "denis", + "Spiidi" + ] + }, + { + "team": "FaZe Clan", + "teamLogo": "assets/tournament_logos/faze_clan_76px_faze_clan_2017_allmode_png.png", + "players": [ + "rain", + "karrigan", + "NiKo", + "GuardiaN", + "olofmeister" + ] + }, + { + "team": "Vega Squadron", + "teamLogo": "assets/tournament_logos/vega_squadron_47px_vega_squadron_2016_allmode_png.png", + "players": [ + "mir", + "chopper", + "jR", + "keshandr", + "hutji" + ] + }, + { + "team": "Space Soldiers", + "teamLogo": "assets/tournament_logos/space_soldiers_57px_space_soldiers_lightmode_png.png", + "players": [ + "XANTARES", + "ngiN", + "paz", + "MAJ3R", + "Calyx" + ] + }, + { + "team": "Team Liquid", + "teamLogo": "assets/tournament_logos/team_liquid_50px_team_liquid_2017_lightmode_png.png", + "players": [ + "nitr0", + "EliGE", + "jdm64", + "Twistzz", + "zews" + ] + }, + { + "team": "AVANGAR", + "teamLogo": "assets/tournament_logos/avangar_57px_avangar_allmode_png.png", + "players": [ + "buster", + "KrizzeN", + "qikert", + "dimasick", + "Jame" + ] + }, + { + "team": "Renegades", + "teamLogo": "assets/tournament_logos/renegades_47px_renegades_png.png", + "players": [ + "AZR", + "jks", + "USTILO", + "Nifty", + "NAF" + ] + }, + { + "team": "Team Envy", + "teamLogo": "assets/tournament_logos/team_envy_50px_team_envy_2018_lightmode_png.png", + "players": [ + "Happy", + "SIXER", + "RpK", + "xms", + "ScreaM" + ] + }, + { + "team": "Misfits Gaming", + "teamLogo": "assets/tournament_logos/misfits_gaming_36px_misfits_gaming_2017_allmode_png.png", + "players": [ + "seang@res", + "SicK", + "ShahZaM", + "AmaNEk", + "devoduvek" + ] + }, + { + "team": "Quantum Bellator Fire", + "teamLogo": "assets/tournament_logos/quantum_bellator_fire_38px_quantum_bellator_fire_allmode_png.png", + "players": [ + "balblna", + "Kvik", + "jmqa", + "waterfaLLZ", + "Boombl4" + ] + }, + { + "team": "Flash Gaming", + "teamLogo": "assets/tournament_logos/flash_gaming_56px_flash_gaming_allmode_png.png", + "players": [ + "Summer", + "Karsa", + "LOVEYY", + "Attacker", + "kaze" + ] + }, + { + "team": "TYLOO", + "players": [ + "Mo", + "DD", + "somebody", + "BnTeT", + "bondik" + ] + } + ], + "stageDates": [ + { + "phase": "Challengers Stage", + "startDate": "2018-01-12", + "endDate": "2018-01-15" + } + ], + "playoffMatches": [ + { + "round": "Quarterfinals", + "team1": "FaZe Clan", + "team2": "MOUZ", + "team1Logo": "assets/tournament_logos/faze_clan_76px_faze_clan_2017_allmode_png.png", + "team2Logo": "assets/tournament_logos/mouz_92px_mousesports_2016_allmode_png.png", + "score1": "2", + "score2": "0", + "date": "January 26, 2018 - 10:00 EST" + }, + { + "round": "Quarterfinals", + "team1": "Natus Vincere", + "team2": "Quantum Bellator Fire", + "team1Logo": "assets/tournament_logos/natus_vincere_56px_natus_vincere_allmode_png.png", + "team2Logo": "assets/tournament_logos/quantum_bellator_fire_38px_quantum_bellator_fire_allmode_png.png", + "score1": "2", + "score2": "0", + "date": "January 26, 2018 - 14:35 EST" + }, + { + "round": "Semifinals", + "team1": "FaZe Clan", + "team2": "Natus Vincere", + "team1Logo": "assets/tournament_logos/faze_clan_76px_faze_clan_2017_allmode_png.png", + "team2Logo": "assets/tournament_logos/natus_vincere_56px_natus_vincere_allmode_png.png", + "score1": "2", + "score2": "0", + "date": "January 27, 2018 - 16:00 EST" + }, + { + "round": "Quarterfinals", + "team1": "G2 Esports", + "team2": "Cloud9", + "team1Logo": "assets/tournament_logos/g2_esports_43px_g2_esports_2020_lightmode_png.png", + "team2Logo": "assets/tournament_logos/cloud9_70px_cloud9_allmode_png.png", + "score1": "0", + "score2": "2", + "date": "January 26, 2018 - 16:30 EST" + }, + { + "round": "Quarterfinals", + "team1": "SK Gaming", + "team2": "Fnatic", + "team1Logo": "assets/tournament_logos/sk_gaming_50px_sk_gaming_2003_lightmode_png.png", + "team2Logo": "assets/tournament_logos/fnatic_60px_fnatic_2015_allmode_png.png", + "score1": "2", + "score2": "1", + "date": "January 27, 2018 - 10:00 EST" + }, + { + "round": "Semifinals", + "team1": "Cloud9", + "team2": "SK Gaming", + "team1Logo": "assets/tournament_logos/cloud9_70px_cloud9_allmode_png.png", + "team2Logo": "assets/tournament_logos/sk_gaming_50px_sk_gaming_2003_lightmode_png.png", + "score1": "2", + "score2": "1", + "date": "January 27, 2018 - 18:30 EST" + }, + { + "round": "Grand Final", + "team1": "FaZe Clan", + "team2": "Cloud9", + "team1Logo": "assets/tournament_logos/faze_clan_76px_faze_clan_2017_allmode_png.png", + "team2Logo": "assets/tournament_logos/cloud9_70px_cloud9_allmode_png.png", + "score1": "1", + "score2": "2", + "date": "January 28, 2018 - 14:00 EST" + } + ] + }, + { + "name": "EMS One Katowice 2014", + "winner": "Virtus.pro", + "tournamentLogo": "assets/tournament_logos/ems_one_katowice_2014.jpg", + "startDate": "2014-03-13", + "endDate": "2014-03-16", + "placements": [ + { + "place": "1st", + "team": "Virtus.pro", + "teamLogo": "assets/tournament_logos/virtus_pro_48px_virtus_pro_allmode_png.png" + }, + { + "place": "2nd", + "team": "Ninjas in Pyjamas", + "teamLogo": "assets/tournament_logos/ninjas_in_pyjamas_50px_ninjas_in_pyjamas_2012_allmode_png.png" + }, + { + "place": "3rd-4th", + "team": "Team Dignitas", + "teamLogo": "assets/tournament_logos/team_dignitas_44px_team_dignitas_allmode_png.png" + }, + { + "place": "3rd-4th", + "team": "LGB eSports", + "teamLogo": "assets/tournament_logos/lgb_esports_43px_lgb_esports_allmode_png.png" + }, + { + "place": "5th-8th", + "team": "compLexity Gaming", + "teamLogo": "assets/tournament_logos/complexity_gaming_100px_complexity_logo_png.png" + }, + { + "place": "5th-8th", + "team": "HellRaisers", + "teamLogo": "assets/tournament_logos/hellraisers_57px_hellraisers_2014_allmode_png.png" + }, + { + "place": "5th-8th", + "team": "Team LDLC.com", + "teamLogo": "assets/tournament_logos/team_ldlc_com_100px_team_ldlc_2012_lightmode_png.png" + }, + { + "place": "5th-8th", + "team": "Fnatic", + "teamLogo": "assets/tournament_logos/fnatic_61px_fnatic_allmode_png.png" + }, + { + "place": "9th-12th", + "team": "Titan", + "teamLogo": "assets/tournament_logos/titan_46px_titan_lightmode_png.png" + }, + { + "place": "9th-12th", + "team": "3DMAX", + "teamLogo": "assets/tournament_logos/3dmax_36px_3dmax_2011_allmode_png.png" + }, + { + "place": "9th-12th", + "team": "Reason Gaming", + "teamLogo": "assets/tournament_logos/reason_gaming_49px_reason_gaming_allmode_png.png" + }, + { + "place": "9th-12th", + "team": "Clan-Mystik", + "teamLogo": "assets/tournament_logos/clan_mystik_36px_clan_mystik_lightmode_png.png" + }, + { + "place": "13th-16th", + "team": "mousesports", + "teamLogo": "assets/tournament_logos/mousesports_92px_mousesports_new_png.png" + }, + { + "place": "13th-16th", + "team": "Vox Eminor", + "teamLogo": "assets/tournament_logos/vox_eminor_89px_vox_eminor_allmode_png.png" + }, + { + "place": "13th-16th", + "team": "Team iBUYPOWER", + "teamLogo": "assets/tournament_logos/team_ibuypower_86px_ibuypower_lightmode_png.png" + }, + { + "place": "13th-16th", + "team": "Natus Vincere", + "teamLogo": "assets/tournament_logos/natus_vincere_56px_natus_vincere_allmode_png.png" + } + ], + "teamRosters": [ + { + "team": "Fnatic", + "teamLogo": "assets/tournament_logos/fnatic_61px_fnatic_allmode_png.png", + "players": [ + "JW", + "flusha", + "schneider", + "Devilwalk", + "pronax" + ] + }, + { + "team": "Ninjas in Pyjamas", + "teamLogo": "assets/tournament_logos/ninjas_in_pyjamas_50px_ninjas_in_pyjamas_2012_allmode_png.png", + "players": [ + "f0rest", + "GeT_RiGhT", + "Xizt", + "Fifflaren", + "friberg" + ] + }, + { + "team": "compLexity Gaming", + "teamLogo": "assets/tournament_logos/complexity_gaming_100px_complexity_logo_png.png", + "players": [ + "Hiko", + "Semphis", + "seang@res", + "swag", + "n0thing" + ] + }, + { + "team": "Titan", + "teamLogo": "assets/tournament_logos/titan_46px_titan_lightmode_png.png", + "players": [ + "Ex6TenZ", + "NBK-", + "SmithZz", + "ScreaM", + "shox" + ] + }, + { + "team": "LGB eSports", + "teamLogo": "assets/tournament_logos/lgb_esports_43px_lgb_esports_allmode_png.png", + "players": [ + "KRiMZ", + "dennis", + "olofm", + "twist", + "cype" + ] + }, + { + "team": "Team Dignitas", + "teamLogo": "assets/tournament_logos/team_dignitas_44px_team_dignitas_allmode_png.png", + "players": [ + "FeTiSh", + "dupreeh", + "Xyp9x", + "device", + "cajunb" + ] + }, + { + "team": "Team LDLC.com", + "teamLogo": "assets/tournament_logos/team_ldlc_com_100px_team_ldlc_2012_lightmode_png.png", + "players": [ + "Happy", + "Uzzziii", + "Maniac", + "KQLY", + "apEX" + ] + }, + { + "team": "HellRaisers", + "teamLogo": "assets/tournament_logos/hellraisers_57px_hellraisers_2014_allmode_png.png", + "players": [ + "ANGE1", + "Dosia", + "AdreN", + "markeloff", + "kUcheR" + ] + }, + { + "team": "Natus Vincere", + "teamLogo": "assets/tournament_logos/natus_vincere_56px_natus_vincere_allmode_png.png", + "players": [ + "Zeus", + "starix", + "seized", + "Edward", + "GuardiaN" + ] + }, + { + "team": "Virtus.pro", + "teamLogo": "assets/tournament_logos/virtus_pro_48px_virtus_pro_allmode_png.png", + "players": [ + "TaZ", + "NEO", + "pashaBiceps", + "Snax", + "byali" + ] + }, + { + "team": "Reason Gaming", + "teamLogo": "assets/tournament_logos/reason_gaming_49px_reason_gaming_allmode_png.png", + "players": [ + "EXR", + "LOMME", + "Friis", + "karrigan", + "smF" + ] + }, + { + "team": "Clan-Mystik", + "teamLogo": "assets/tournament_logos/clan_mystik_36px_clan_mystik_lightmode_png.png", + "players": [ + "HaRts", + "kioShiMa", + "Sf", + "GMX", + "kennyS" + ] + }, + { + "team": "3DMAX", + "teamLogo": "assets/tournament_logos/3dmax_36px_3dmax_2011_allmode_png.png", + "players": [ + "gla1ve", + "Pimp", + "raalz", + "MSL", + "aizy" + ] + }, + { + "team": "mousesports", + "teamLogo": "assets/tournament_logos/mousesports_92px_mousesports_new_png.png", + "players": [ + "chrisJ", + "LEGIJA", + "tiziaN", + "cadiaN", + "tabseN" + ] + }, + { + "team": "Team iBUYPOWER", + "teamLogo": "assets/tournament_logos/team_ibuypower_86px_ibuypower_lightmode_png.png", + "players": [ + "anger", + "Skadoodle", + "adreN", + "AZK", + "DaZeD" + ] + }, + { + "team": "Vox Eminor", + "teamLogo": "assets/tournament_logos/vox_eminor_89px_vox_eminor_allmode_png.png", + "players": [ + "SPUNJ", + "AZR", + "Havoc", + "SnypeR", + "TopguN" + ] + } + ], + "stageDates": [], + "playoffMatches": [ + { + "round": "Quarterfinals", + "team1": "Ninjas in Pyjamas", + "team2": "Complexity", + "team1Logo": "assets/tournament_logos/ninjas_in_pyjamas_50px_ninjas_in_pyjamas_2012_allmode_png.png", + "team2Logo": "assets/tournament_logos/complexity_100px_complexity_logo_png.png", + "score1": "2", + "score2": "1", + "date": "March 14, 2014 - 18:55 CET" + }, + { + "round": "Quarterfinals", + "team1": "Dignitas", + "team2": "HellRaisers", + "team1Logo": "assets/tournament_logos/dignitas_44px_team_dignitas_allmode_png.png", + "team2Logo": "assets/tournament_logos/hellraisers_57px_hellraisers_2014_allmode_png.png", + "score1": "2", + "score2": "0", + "date": "March 14, 2014 - 13:00 CET" + }, + { + "round": "Semifinals", + "team1": "Ninjas in Pyjamas", + "team2": "Dignitas", + "team1Logo": "assets/tournament_logos/ninjas_in_pyjamas_50px_ninjas_in_pyjamas_2012_allmode_png.png", + "team2Logo": "assets/tournament_logos/dignitas_44px_team_dignitas_allmode_png.png", + "score1": "2", + "score2": "0", + "date": "March 15, 2014 - 17:05 CET" + }, + { + "round": "Quarterfinals", + "team1": "Virtus.pro", + "team2": "Team LDLC", + "team1Logo": "assets/tournament_logos/virtus_pro_48px_virtus_pro_allmode_png.png", + "team2Logo": "assets/tournament_logos/team_ldlc_100px_team_ldlc_2012_lightmode_png.png", + "score1": "2", + "score2": "0", + "date": "March 14, 2014 - 16:00 CET" + }, + { + "round": "Quarterfinals", + "team1": "Fnatic", + "team2": "LGB eSports", + "team1Logo": "assets/tournament_logos/fnatic_61px_fnatic_allmode_png.png", + "team2Logo": "assets/tournament_logos/lgb_esports_43px_lgb_esports_allmode_png.png", + "score1": "1", + "score2": "2", + "date": "March 15, 2014 - 13:00 CET" + }, + { + "round": "Semifinals", + "team1": "Virtus.pro", + "team2": "LGB eSports", + "team1Logo": "assets/tournament_logos/virtus_pro_48px_virtus_pro_allmode_png.png", + "team2Logo": "assets/tournament_logos/lgb_esports_43px_lgb_esports_allmode_png.png", + "score1": "2", + "score2": "1", + "date": "March 15, 2014 - 19:00 CET" + }, + { + "round": "Grand Final", + "team1": "Ninjas in Pyjamas", + "team2": "Virtus.pro", + "team1Logo": "assets/tournament_logos/ninjas_in_pyjamas_50px_ninjas_in_pyjamas_2012_allmode_png.png", + "team2Logo": "assets/tournament_logos/virtus_pro_48px_virtus_pro_allmode_png.png", + "score1": "0", + "score2": "2", + "date": "March 16, 2014 - 13:00 CET" + } + ] + }, + { + "name": "ESL One Cologne 2014", + "winner": "Ninjas in Pyjamas", + "tournamentLogo": "assets/tournament_logos/esl_one_cologne_2014.jpg", + "startDate": "2014-08-14", + "endDate": "2014-08-17", + "placements": [ + { + "place": "1st", + "team": "Ninjas in Pyjamas", + "teamLogo": "assets/tournament_logos/ninjas_in_pyjamas_50px_ninjas_in_pyjamas_2012_allmode_png.png" + }, + { + "place": "2nd", + "team": "Fnatic", + "teamLogo": "assets/tournament_logos/fnatic_61px_fnatic_allmode_png.png" + }, + { + "place": "3rd-4th", + "team": "Team Dignitas", + "teamLogo": "assets/tournament_logos/team_dignitas_44px_team_dignitas_allmode_png.png" + }, + { + "place": "3rd-4th", + "team": "Team LDLC.com", + "teamLogo": "assets/tournament_logos/team_ldlc_com_100px_team_ldlc_2012_lightmode_png.png" + }, + { + "place": "5th-8th", + "team": "Natus Vincere", + "teamLogo": "assets/tournament_logos/natus_vincere_56px_natus_vincere_allmode_png.png" + }, + { + "place": "5th-8th", + "team": "Epsilon eSports", + "teamLogo": "assets/tournament_logos/epsilon_esports_100px_epsilon_esports_lightmode_png.png" + }, + { + "place": "5th-8th", + "team": "Virtus.pro", + "teamLogo": "assets/tournament_logos/virtus_pro_48px_virtus_pro_allmode_png.png" + }, + { + "place": "5th-8th", + "team": "Cloud9", + "teamLogo": "assets/tournament_logos/cloud9_70px_cloud9_allmode_png.png" + }, + { + "place": "9th-12th", + "team": "HellRaisers", + "teamLogo": "assets/tournament_logos/hellraisers_57px_hellraisers_2014_allmode_png.png" + }, + { + "place": "9th-12th", + "team": "Copenhagen Wolves", + "teamLogo": "assets/tournament_logos/copenhagen_wolves_50px_copenhagen_wolves_2012_allmode_png.png" + }, + { + "place": "9th-12th", + "team": "Team iBUYPOWER", + "teamLogo": "assets/tournament_logos/team_ibuypower_86px_ibuypower_lightmode_png.png" + }, + { + "place": "9th-12th", + "team": "Titan", + "teamLogo": "assets/tournament_logos/titan_46px_titan_lightmode_png.png" + }, + { + "place": "13th-16th", + "team": "Team Wolf", + "teamLogo": "assets/tournament_logos/team_wolf_53px_team_wolf_allmode_png.png" + }, + { + "place": "13th-16th", + "team": "London Conspiracy", + "teamLogo": "assets/tournament_logos/london_conspiracy_50px_london_conspiracy_allmode_png.png" + }, + { + "place": "13th-16th", + "team": "dAT Team", + "teamLogo": "assets/tournament_logos/dat_team_47px_dat_team_allmode_png.png" + }, + { + "place": "13th-16th", + "team": "Vox Eminor", + "teamLogo": "assets/tournament_logos/vox_eminor_89px_vox_eminor_allmode_png.png" + } + ], + "teamRosters": [ + { + "team": "Virtus.pro", + "teamLogo": "assets/tournament_logos/virtus_pro_48px_virtus_pro_allmode_png.png", + "players": [ + "TaZ", + "NEO", + "pashaBiceps", + "Snax", + "byali" + ] + }, + { + "team": "Ninjas in Pyjamas", + "teamLogo": "assets/tournament_logos/ninjas_in_pyjamas_50px_ninjas_in_pyjamas_2012_allmode_png.png", + "players": [ + "f0rest", + "GeT_RiGhT", + "Xizt", + "Fifflaren", + "friberg" + ] + }, + { + "team": "Team Dignitas", + "teamLogo": "assets/tournament_logos/team_dignitas_44px_team_dignitas_allmode_png.png", + "players": [ + "FeTiSh", + "dupreeh", + "Xyp9x", + "device", + "aizy" + ] + }, + { + "team": "Cloud9", + "teamLogo": "assets/tournament_logos/cloud9_70px_cloud9_allmode_png.png", + "players": [ + "Hiko", + "SEMPHIS", + "seang@res", + "n0thing", + "shroud" + ] + }, + { + "team": "HellRaisers", + "teamLogo": "assets/tournament_logos/hellraisers_57px_hellraisers_2014_allmode_png.png", + "players": [ + "ANGE1", + "Dosia", + "AdreN", + "markeloff", + "kUcheR" + ] + }, + { + "team": "Team LDLC.com", + "teamLogo": "assets/tournament_logos/team_ldlc_com_100px_team_ldlc_2012_lightmode_png.png", + "players": [ + "Happy", + "Uzzziii", + "Maniac", + "KQLY", + "apEX" + ] + }, + { + "team": "Fnatic", + "teamLogo": "assets/tournament_logos/fnatic_61px_fnatic_allmode_png.png", + "players": [ + "JW", + "flusha", + "pronax", + "olofmeister", + "KRIMZ" + ] + }, + { + "team": "London Conspiracy", + "teamLogo": "assets/tournament_logos/london_conspiracy_50px_london_conspiracy_allmode_png.png", + "players": [ + "Polly", + "Skurk", + "RUBINO", + "rain", + "prb" + ] + }, + { + "team": "dAT Team", + "teamLogo": "assets/tournament_logos/dat_team_47px_dat_team_allmode_png.png", + "players": [ + "B1ad3", + "WorldEdit", + "bondik", + "ub1que", + "flamie" + ] + }, + { + "team": "Epsilon eSports", + "teamLogo": "assets/tournament_logos/epsilon_esports_100px_epsilon_esports_lightmode_png.png", + "players": [ + "kioShiMa", + "Sf", + "GMX", + "shox", + "fxy0" + ] + }, + { + "team": "Titan", + "teamLogo": "assets/tournament_logos/titan_46px_titan_lightmode_png.png", + "players": [ + "Ex6TenZ", + "NBK-", + "SmithZz", + "ScreaM", + "kennyS" + ] + }, + { + "team": "Copenhagen Wolves", + "teamLogo": "assets/tournament_logos/copenhagen_wolves_50px_copenhagen_wolves_2012_allmode_png.png", + "players": [ + "gla1ve", + "karrigan", + "cajunb", + "Pimp", + "Nico" + ] + }, + { + "team": "Natus Vincere", + "teamLogo": "assets/tournament_logos/natus_vincere_56px_natus_vincere_allmode_png.png", + "players": [ + "Zeus", + "starix", + "seized", + "Edward", + "GuardiaN" + ] + }, + { + "team": "Team iBUYPOWER", + "teamLogo": "assets/tournament_logos/team_ibuypower_86px_ibuypower_lightmode_png.png", + "players": [ + "Skadoodle", + "AZK", + "DaZeD", + "steel", + "swag" + ] + }, + { + "team": "Vox Eminor", + "teamLogo": "assets/tournament_logos/vox_eminor_89px_vox_eminor_allmode_png.png", + "players": [ + "SPUNJ", + "AZR", + "Havoc", + "topguN", + "jks" + ] + }, + { + "team": "Team Wolf", + "teamLogo": "assets/tournament_logos/team_wolf_53px_team_wolf_allmode_png.png", + "players": [ + "RiTz", + "RiX", + "astaRR", + "Ace", + "MithilF" + ] + } + ], + "stageDates": [], + "playoffMatches": [ + { + "round": "Quarterfinals", + "team1": "Fnatic", + "team2": "Natus Vincere", + "team1Logo": "assets/tournament_logos/fnatic_61px_fnatic_allmode_png.png", + "team2Logo": "assets/tournament_logos/natus_vincere_56px_natus_vincere_allmode_png.png", + "score1": "2", + "score2": "1", + "date": "August 16, 2014 - 09:25 CEST" + }, + { + "round": "Quarterfinals", + "team1": "Epsilon Esports", + "team2": "Dignitas", + "team1Logo": "assets/tournament_logos/epsilon_esports_100px_epsilon_esports_lightmode_png.png", + "team2Logo": "assets/tournament_logos/dignitas_44px_team_dignitas_allmode_png.png", + "score1": "0", + "score2": "2", + "date": "August 16, 2014 - 13:25 CEST" + }, + { + "round": "Semifinals", + "team1": "Fnatic", + "team2": "Dignitas", + "team1Logo": "assets/tournament_logos/fnatic_61px_fnatic_allmode_png.png", + "team2Logo": "assets/tournament_logos/dignitas_44px_team_dignitas_allmode_png.png", + "score1": "2", + "score2": "0", + "date": "August 17, 2014 - 09:00 CEST" + }, + { + "round": "Quarterfinals", + "team1": "Team LDLC", + "team2": "Virtus.pro", + "team1Logo": "assets/tournament_logos/team_ldlc_100px_team_ldlc_2012_lightmode_png.png", + "team2Logo": "assets/tournament_logos/virtus_pro_48px_virtus_pro_allmode_png.png", + "score1": "2", + "score2": "0", + "date": "August 16, 2014 - 14:40 CEST" + }, + { + "round": "Quarterfinals", + "team1": "Cloud9", + "team2": "Ninjas in Pyjamas", + "team1Logo": "assets/tournament_logos/cloud9_70px_cloud9_allmode_png.png", + "team2Logo": "assets/tournament_logos/ninjas_in_pyjamas_50px_ninjas_in_pyjamas_2012_allmode_png.png", + "score1": "1", + "score2": "2", + "date": "August 16, 2014 - 17:30 CEST" + }, + { + "round": "Semifinals", + "team1": "Team LDLC", + "team2": "Ninjas in Pyjamas", + "team1Logo": "assets/tournament_logos/team_ldlc_100px_team_ldlc_2012_lightmode_png.png", + "team2Logo": "assets/tournament_logos/ninjas_in_pyjamas_50px_ninjas_in_pyjamas_2012_allmode_png.png", + "score1": "1", + "score2": "2", + "date": "August 17, 2014 - 11:55 CEST" + }, + { + "round": "Grand Final", + "team1": "Fnatic", + "team2": "Ninjas in Pyjamas", + "team1Logo": "assets/tournament_logos/fnatic_61px_fnatic_allmode_png.png", + "team2Logo": "assets/tournament_logos/ninjas_in_pyjamas_50px_ninjas_in_pyjamas_2012_allmode_png.png", + "score1": "1", + "score2": "2", + "date": "August 17, 2014 - 16:00 CEST" + } + ] + }, + { + "name": "ESL One Cologne 2015", + "winner": "Fnatic", + "tournamentLogo": "assets/tournament_logos/esl_one_cologne_2015.png", + "startDate": "2015-08-20", + "endDate": "2015-08-23", + "placements": [ + { + "place": "1st", + "team": "Fnatic", + "teamLogo": "assets/tournament_logos/fnatic_61px_fnatic_allmode_png.png" + }, + { + "place": "2nd", + "team": "Team EnVyUs", + "teamLogo": "assets/tournament_logos/team_envyus_50px_team_envyus_lightmode_png.png" + }, + { + "place": "3rd-4th", + "team": "Team SoloMid", + "teamLogo": "assets/tournament_logos/team_solomid_50px_team_solomid_lightmode_png.png" + }, + { + "place": "3rd-4th", + "team": "Virtus.pro", + "teamLogo": "assets/tournament_logos/virtus_pro_48px_virtus_pro_allmode_png.png" + }, + { + "place": "5th-8th", + "team": "Natus Vincere", + "teamLogo": "assets/tournament_logos/natus_vincere_56px_natus_vincere_allmode_png.png" + }, + { + "place": "5th-8th", + "team": "Team Kinguin", + "teamLogo": "assets/tournament_logos/team_kinguin_33px_team_kinguin_allmode_png.png" + }, + { + "place": "5th-8th", + "team": "Ninjas in Pyjamas", + "teamLogo": "assets/tournament_logos/ninjas_in_pyjamas_50px_ninjas_in_pyjamas_2012_allmode_png.png" + }, + { + "place": "5th-8th", + "team": "Luminosity Gaming", + "teamLogo": "assets/tournament_logos/luminosity_gaming_51px_luminosity_gaming_allmode_png.png" + }, + { + "place": "9th-12th", + "team": "Counter Logic Gaming", + "teamLogo": "assets/tournament_logos/counter_logic_gaming_70px_counter_logic_gaming_allmode_png.png" + }, + { + "place": "9th-12th", + "team": "Cloud9", + "teamLogo": "assets/tournament_logos/cloud9_70px_cloud9_allmode_png.png" + }, + { + "place": "9th-12th", + "team": "Renegades", + "teamLogo": "assets/tournament_logos/renegades_47px_renegades_png.png" + }, + { + "place": "9th-12th", + "team": "FlipSid3 Tactics", + "teamLogo": "assets/tournament_logos/flipsid3_tactics_47px_flipsid3_tactics_lightmode_png.png" + }, + { + "place": "13th-16th", + "team": "Team eBettle", + "teamLogo": "assets/tournament_logos/team_ebettle_38px_team_ebettle_allmode_png.png" + }, + { + "place": "13th-16th", + "team": "Team Immunity", + "teamLogo": "assets/tournament_logos/team_immunity_61px_team_immunity_allmode_png.png" + }, + { + "place": "13th-16th", + "team": "Titan", + "teamLogo": "assets/tournament_logos/titan_46px_titan_lightmode_png.png" + }, + { + "place": "13th-16th", + "team": "mousesports", + "teamLogo": "assets/tournament_logos/mousesports_92px_mousesports_new_png.png" + } + ], + "teamRosters": [ + { + "team": "Fnatic", + "teamLogo": "assets/tournament_logos/fnatic_61px_fnatic_allmode_png.png", + "players": [ + "JW", + "flusha", + "pronax", + "olofmeister", + "KRIMZ" + ] + }, + { + "team": "Ninjas in Pyjamas", + "teamLogo": "assets/tournament_logos/ninjas_in_pyjamas_50px_ninjas_in_pyjamas_2012_allmode_png.png", + "players": [ + "f0rest", + "GeT_RiGhT", + "Xizt", + "friberg", + "allu" + ] + }, + { + "team": "Virtus.pro", + "teamLogo": "assets/tournament_logos/virtus_pro_48px_virtus_pro_allmode_png.png", + "players": [ + "TaZ", + "NEO", + "pashaBiceps", + "Snax", + "byali" + ] + }, + { + "team": "Team EnVyUs", + "teamLogo": "assets/tournament_logos/team_envyus_50px_team_envyus_lightmode_png.png", + "players": [ + "Happy", + "NBK-", + "kioShiMa", + "kennyS", + "apEX" + ] + }, + { + "team": "mousesports", + "teamLogo": "assets/tournament_logos/mousesports_92px_mousesports_new_png.png", + "players": [ + "chrisJ", + "gob b", + "denis", + "Spiidi", + "nex" + ] + }, + { + "team": "Luminosity Gaming", + "teamLogo": "assets/tournament_logos/luminosity_gaming_51px_luminosity_gaming_allmode_png.png", + "players": [ + "FalleN", + "fer", + "steel", + "boltz", + "coldzera" + ] + }, + { + "team": "Natus Vincere", + "teamLogo": "assets/tournament_logos/natus_vincere_56px_natus_vincere_allmode_png.png", + "players": [ + "Zeus", + "seized", + "Edward", + "GuardiaN", + "flamie" + ] + }, + { + "team": "Team SoloMid", + "teamLogo": "assets/tournament_logos/team_solomid_50px_team_solomid_lightmode_png.png", + "players": [ + "device", + "dupreeh", + "Xyp9x", + "cajunb", + "karrigan" + ] + }, + { + "team": "Team Kinguin", + "teamLogo": "assets/tournament_logos/team_kinguin_33px_team_kinguin_allmode_png.png", + "players": [ + "ScreaM", + "Maikelele", + "rain", + "fox", + "dennis" + ] + }, + { + "team": "Team eBettle", + "teamLogo": "assets/tournament_logos/team_ebettle_38px_team_ebettle_allmode_png.png", + "players": [ + "Furlan", + "Hyper", + "GruBy", + "peet", + "rallen" + ] + }, + { + "team": "FlipSid3 Tactics", + "teamLogo": "assets/tournament_logos/flipsid3_tactics_47px_flipsid3_tactics_lightmode_png.png", + "players": [ + "B1ad3", + "WorldEdit", + "bondik", + "markeloff", + "DavCost" + ] + }, + { + "team": "Titan", + "teamLogo": "assets/tournament_logos/titan_46px_titan_lightmode_png.png", + "players": [ + "Ex6TenZ", + "Maniac", + "RpK", + "SmithZz", + "shox" + ] + }, + { + "team": "Counter Logic Gaming", + "teamLogo": "assets/tournament_logos/counter_logic_gaming_70px_counter_logic_gaming_allmode_png.png", + "players": [ + "reltuC", + "FNS", + "hazed", + "tarik", + "jdm64" + ] + }, + { + "team": "Cloud9", + "teamLogo": "assets/tournament_logos/cloud9_70px_cloud9_allmode_png.png", + "players": [ + "sgares", + "n0thing", + "shroud", + "fREAKAZOiD", + "Skadoodle" + ] + }, + { + "team": "Team Immunity", + "teamLogo": "assets/tournament_logos/team_immunity_61px_team_immunity_allmode_png.png", + "players": [ + "SnypeR", + "Rickeh", + "James", + "emagine", + "USTILO" + ] + }, + { + "team": "Renegades", + "teamLogo": "assets/tournament_logos/renegades_47px_renegades_png.png", + "players": [ + "SPUNJ", + "AZR", + "Havoc", + "jks", + "yam" + ] + } + ], + "stageDates": [], + "playoffMatches": [ + { + "round": "Qualified", + "team1": "Ninjas in Pyjamas", + "team2": "Counter Logic Gaming", + "team1Logo": "assets/tournament_logos/ninjas_in_pyjamas_50px_ninjas_in_pyjamas_2012_allmode_png.png", + "team2Logo": "assets/tournament_logos/counter_logic_gaming_70px_counter_logic_gaming_allmode_png.png", + "score1": "16", + "score2": "13", + "date": "August 20, 2015 - 14:15 CEST" + }, + { + "round": "Qualified", + "team1": "Renegades", + "team2": "TSM", + "team1Logo": "assets/tournament_logos/renegades_47px_renegades_png.png", + "team2Logo": "assets/tournament_logos/tsm_50px_team_solomid_lightmode_png.png", + "score1": "11", + "score2": "16", + "date": "August 20, 2015 - 15:45 CEST" + }, + { + "round": "Qualified", + "team1": "Ninjas in Pyjamas", + "team2": "TSM", + "team1Logo": "assets/tournament_logos/ninjas_in_pyjamas_50px_ninjas_in_pyjamas_2012_allmode_png.png", + "team2Logo": "assets/tournament_logos/tsm_50px_team_solomid_lightmode_png.png", + "score1": "3", + "score2": "16", + "date": "August 20, 2015 - 17:10 CEST" + }, + { + "round": "Qualified", + "team1": "Team Envy", + "team2": "FlipSid3 Tactics", + "team1Logo": "assets/tournament_logos/team_envy_50px_team_envyus_lightmode_png.png", + "team2Logo": "assets/tournament_logos/flipsid3_tactics_47px_flipsid3_tactics_lightmode_png.png", + "score1": "16", + "score2": "3", + "date": "August 20, 2015 - 14:15 CEST" + }, + { + "round": "Qualified", + "team1": "Luminosity Gaming", + "team2": "Devils.one", + "team1Logo": "assets/tournament_logos/luminosity_gaming_51px_luminosity_gaming_allmode_png.png", + "team2Logo": "assets/tournament_logos/devils_one_33px_team_kinguin_allmode_png.png", + "score1": "16", + "score2": "6", + "date": "August 20, 2015 - 15:25 CEST" + }, + { + "round": "Qualified", + "team1": "Team Envy", + "team2": "Luminosity Gaming", + "team1Logo": "assets/tournament_logos/team_envy_50px_team_envyus_lightmode_png.png", + "team2Logo": "assets/tournament_logos/luminosity_gaming_51px_luminosity_gaming_allmode_png.png", + "score1": "19", + "score2": "16", + "date": "August 20, 2015 - 16:35 CEST" + }, + { + "round": "Qualified", + "team1": "Fnatic", + "team2": "Team eBettle", + "team1Logo": "assets/tournament_logos/fnatic_61px_fnatic_allmode_png.png", + "team2Logo": "assets/tournament_logos/team_ebettle_38px_team_ebettle_allmode_png.png", + "score1": "16", + "score2": "2", + "date": "August 20, 2015 - 18:35 CEST" + }, + { + "round": "Qualified", + "team1": "Titan", + "team2": "Natus Vincere", + "team1Logo": "assets/tournament_logos/titan_46px_titan_lightmode_png.png", + "team2Logo": "assets/tournament_logos/natus_vincere_56px_natus_vincere_allmode_png.png", + "score1": "17", + "score2": "19", + "date": "August 20, 2015 - 19:50 CEST" + }, + { + "round": "Qualified", + "team1": "Fnatic", + "team2": "Natus Vincere", + "team1Logo": "assets/tournament_logos/fnatic_61px_fnatic_allmode_png.png", + "team2Logo": "assets/tournament_logos/natus_vincere_56px_natus_vincere_allmode_png.png", + "score1": "16", + "score2": "2", + "date": "August 20, 2015 - 22:25 CEST" + }, + { + "round": "Qualified", + "team1": "Virtus.pro", + "team2": "Team Immunity", + "team1Logo": "assets/tournament_logos/virtus_pro_48px_virtus_pro_allmode_png.png", + "team2Logo": "assets/tournament_logos/team_immunity_61px_team_immunity_allmode_png.png", + "score1": "16", + "score2": "8", + "date": "August 20, 2015 - 18:20 CEST" + }, + { + "round": "Qualified", + "team1": "MOUZ", + "team2": "Cloud9", + "team1Logo": "assets/tournament_logos/mouz_92px_mousesports_new_png.png", + "team2Logo": "assets/tournament_logos/cloud9_70px_cloud9_allmode_png.png", + "score1": "10", + "score2": "16", + "date": "August 20, 2015 - 19:45 CEST" + }, + { + "round": "Qualified", + "team1": "Virtus.pro", + "team2": "Cloud9", + "team1Logo": "assets/tournament_logos/virtus_pro_48px_virtus_pro_allmode_png.png", + "team2Logo": "assets/tournament_logos/cloud9_70px_cloud9_allmode_png.png", + "score1": "16", + "score2": "8", + "date": "August 20, 2015 - 21:20 CEST" + }, + { + "round": "Qualified", + "team1": "Renegades", + "team2": "Titan", + "team1Logo": "assets/tournament_logos/renegades_47px_renegades_png.png", + "team2Logo": "assets/tournament_logos/titan_46px_titan_lightmode_png.png", + "score1": "16", + "score2": "9", + "date": "August 21, 2015 - 18:30 CEST" + }, + { + "round": "Qualified", + "team1": "Ninjas in Pyjamas", + "team2": "Renegades", + "team1Logo": "assets/tournament_logos/ninjas_in_pyjamas_50px_ninjas_in_pyjamas_2012_allmode_png.png", + "team2Logo": "assets/tournament_logos/renegades_47px_renegades_png.png", + "score1": "16", + "score2": "5", + "date": "August 21, 2015 - 19:35 CEST" + }, + { + "round": "Qualified", + "team1": "FlipSid3 Tactics", + "team2": "MOUZ", + "team1Logo": "assets/tournament_logos/flipsid3_tactics_47px_flipsid3_tactics_lightmode_png.png", + "team2Logo": "assets/tournament_logos/mouz_92px_mousesports_new_png.png", + "score1": "16", + "score2": "14", + "date": "August 21, 2015 - 20:40 CEST" + }, + { + "round": "Qualified", + "team1": "Luminosity Gaming", + "team2": "FlipSid3 Tactics", + "team1Logo": "assets/tournament_logos/luminosity_gaming_51px_luminosity_gaming_allmode_png.png", + "team2Logo": "assets/tournament_logos/flipsid3_tactics_47px_flipsid3_tactics_lightmode_png.png", + "score1": "22", + "score2": "18", + "date": "August 21, 2015 - 22:10 CEST" + }, + { + "round": "Qualified", + "team1": "Team eBettle", + "team2": "Counter Logic Gaming", + "team1Logo": "assets/tournament_logos/team_ebettle_38px_team_ebettle_allmode_png.png", + "team2Logo": "assets/tournament_logos/counter_logic_gaming_70px_counter_logic_gaming_allmode_png.png", + "score1": "14", + "score2": "16", + "date": "August 21, 2015 - 15:25 CEST" + }, + { + "round": "Qualified", + "team1": "Natus Vincere", + "team2": "Counter Logic Gaming", + "team1Logo": "assets/tournament_logos/natus_vincere_56px_natus_vincere_allmode_png.png", + "team2Logo": "assets/tournament_logos/counter_logic_gaming_70px_counter_logic_gaming_allmode_png.png", + "score1": "16", + "score2": "14", + "date": "August 21, 2015 - 17:00 CEST" + }, + { + "round": "Qualified", + "team1": "Team Immunity", + "team2": "Devils.one", + "team1Logo": "assets/tournament_logos/team_immunity_61px_team_immunity_allmode_png.png", + "team2Logo": "assets/tournament_logos/devils_one_33px_team_kinguin_allmode_png.png", + "score1": "13", + "score2": "16", + "date": "August 21, 2015 - 12:30 CEST" + }, + { + "round": "Qualified", + "team1": "Cloud9", + "team2": "Devils.one", + "team1Logo": "assets/tournament_logos/cloud9_70px_cloud9_allmode_png.png", + "team2Logo": "assets/tournament_logos/devils_one_33px_team_kinguin_allmode_png.png", + "score1": "13", + "score2": "16", + "date": "August 21, 2015 - 14:00 CEST" + }, + { + "round": "Qualified", + "team1": "Team Envy", + "team2": "Natus Vincere", + "team1Logo": "assets/tournament_logos/team_envy_50px_team_envyus_lightmode_png.png", + "team2Logo": "assets/tournament_logos/natus_vincere_56px_natus_vincere_allmode_png.png", + "score1": "2", + "score2": "0", + "date": "August 22, 2015 - 12:00 CEST" + }, + { + "round": "Qualified", + "team1": "TSM", + "team2": "Devils.one", + "team1Logo": "assets/tournament_logos/tsm_50px_team_solomid_lightmode_png.png", + "team2Logo": "assets/tournament_logos/devils_one_33px_team_kinguin_allmode_png.png", + "score1": "2", + "score2": "0", + "date": "August 22, 2015 - 15:55 CEST" + }, + { + "round": "Qualified", + "team1": "Team Envy", + "team2": "TSM", + "team1Logo": "assets/tournament_logos/team_envy_50px_team_envyus_lightmode_png.png", + "team2Logo": "assets/tournament_logos/tsm_50px_team_solomid_lightmode_png.png", + "score1": "2", + "score2": "1", + "date": "August 23, 2015 - 12:25 CEST" + }, + { + "round": "Qualified", + "team1": "Virtus.pro", + "team2": "Ninjas in Pyjamas", + "team1Logo": "assets/tournament_logos/virtus_pro_48px_virtus_pro_allmode_png.png", + "team2Logo": "assets/tournament_logos/ninjas_in_pyjamas_50px_ninjas_in_pyjamas_2012_allmode_png.png", + "score1": "2", + "score2": "0", + "date": "August 22, 2015 - 18:30 CEST" + }, + { + "round": "Qualified", + "team1": "Fnatic", + "team2": "Luminosity Gaming", + "team1Logo": "assets/tournament_logos/fnatic_61px_fnatic_allmode_png.png", + "team2Logo": "assets/tournament_logos/luminosity_gaming_51px_luminosity_gaming_allmode_png.png", + "score1": "2", + "score2": "0", + "date": "August 22, 2015 - 21:20 CEST" + }, + { + "round": "Qualified", + "team1": "Virtus.pro", + "team2": "Fnatic", + "team1Logo": "assets/tournament_logos/virtus_pro_48px_virtus_pro_allmode_png.png", + "team2Logo": "assets/tournament_logos/fnatic_61px_fnatic_allmode_png.png", + "score1": "1", + "score2": "2", + "date": "August 23, 2015 - 15:50 CEST" + }, + { + "round": "Qualified", + "team1": "Team Envy", + "team2": "Fnatic", + "team1Logo": "assets/tournament_logos/team_envy_50px_team_envyus_lightmode_png.png", + "team2Logo": "assets/tournament_logos/fnatic_61px_fnatic_allmode_png.png", + "score1": "0", + "score2": "2", + "date": "August 23, 2015 - 19:25 CEST" + } + ] + }, + { + "name": "ESL One Cologne 2016", + "winner": "SK Gaming", + "tournamentLogo": "assets/tournament_logos/esl_one_cologne_2016.png", + "startDate": "2016-07-05", + "endDate": "2016-07-10", + "placements": [ + { + "place": "1st", + "team": "SK Gaming", + "teamLogo": "assets/tournament_logos/sk_gaming_50px_sk_gaming_2003_lightmode_png.png" + }, + { + "place": "2nd", + "team": "Team Liquid", + "teamLogo": "assets/tournament_logos/team_liquid_50px_team_liquid_allmode_png.png" + }, + { + "place": "3rd-4th", + "team": "Virtus.pro", + "teamLogo": "assets/tournament_logos/virtus_pro_48px_virtus_pro_allmode_png.png" + }, + { + "place": "3rd-4th", + "team": "Fnatic", + "teamLogo": "assets/tournament_logos/fnatic_60px_fnatic_2015_allmode_png.png" + }, + { + "place": "5th-8th", + "team": "Astralis", + "teamLogo": "assets/tournament_logos/astralis_41px_astralis_logo_png.png" + }, + { + "place": "5th-8th", + "team": "FlipSid3 Tactics", + "teamLogo": "assets/tournament_logos/flipsid3_tactics_47px_flipsid3_tactics_lightmode_png.png" + }, + { + "place": "5th-8th", + "team": "Natus Vincere", + "teamLogo": "assets/tournament_logos/natus_vincere_56px_natus_vincere_allmode_png.png" + }, + { + "place": "5th-8th", + "team": "Gambit Gaming", + "teamLogo": "assets/tournament_logos/gambit_gaming_46px_gambit_esports_allmode_png.png" + }, + { + "place": "9th-12th", + "team": "Team Dignitas", + "teamLogo": "assets/tournament_logos/team_dignitas_44px_team_dignitas_allmode_png.png" + }, + { + "place": "9th-12th", + "team": "Ninjas in Pyjamas", + "teamLogo": "assets/tournament_logos/ninjas_in_pyjamas_50px_ninjas_in_pyjamas_2012_allmode_png.png" + }, + { + "place": "9th-12th", + "team": "mousesports", + "teamLogo": "assets/tournament_logos/mousesports_92px_mousesports_2016_allmode_png.png" + }, + { + "place": "9th-12th", + "team": "FaZe Clan", + "teamLogo": "assets/tournament_logos/faze_clan_76px_faze_clan_2017_allmode_png.png" + }, + { + "place": "13th-16th", + "team": "Counter Logic Gaming", + "teamLogo": "assets/tournament_logos/counter_logic_gaming_70px_counter_logic_gaming_allmode_png.png" + }, + { + "place": "13th-16th", + "team": "OpTic Gaming", + "teamLogo": "assets/tournament_logos/optic_gaming_77px_optic_gaming_lightmode_png.png" + }, + { + "place": "13th-16th", + "team": "Team EnVyUs", + "teamLogo": "assets/tournament_logos/team_envyus_50px_team_envyus_lightmode_png.png" + }, + { + "place": "13th-16th", + "team": "G2 Esports", + "teamLogo": "assets/tournament_logos/g2_esports_43px_g2_esports_allmode_png.png" + } + ], + "teamRosters": [ + { + "team": "SK Gaming", + "teamLogo": "assets/tournament_logos/sk_gaming_50px_sk_gaming_2003_lightmode_png.png", + "players": [ + "FalleN", + "fer", + "coldzera", + "fnx", + "TACO" + ] + }, + { + "team": "Natus Vincere", + "teamLogo": "assets/tournament_logos/natus_vincere_56px_natus_vincere_allmode_png.png", + "players": [ + "Zeus", + "seized", + "Edward", + "GuardiaN", + "flamie" + ] + }, + { + "team": "Astralis", + "teamLogo": "assets/tournament_logos/astralis_41px_astralis_logo_png.png", + "players": [ + "device", + "dupreeh", + "Xyp9x", + "karrigan", + "gla1ve" + ] + }, + { + "team": "Team Liquid", + "teamLogo": "assets/tournament_logos/team_liquid_50px_team_liquid_allmode_png.png", + "players": [ + "nitr0", + "EliGE", + "Hiko", + "s1mple", + "jdm64" + ] + }, + { + "team": "Ninjas in Pyjamas", + "teamLogo": "assets/tournament_logos/ninjas_in_pyjamas_50px_ninjas_in_pyjamas_2012_allmode_png.png", + "players": [ + "f0rest", + "GeT_RiGhT", + "Xizt", + "friberg", + "pyth" + ] + }, + { + "team": "Fnatic", + "teamLogo": "assets/tournament_logos/fnatic_60px_fnatic_2015_allmode_png.png", + "players": [ + "JW", + "flusha", + "olofmeister", + "KRIMZ", + "dennis" + ] + }, + { + "team": "CLG", + "players": [ + "reltuC", + "hazed", + "tarik", + "koosta", + "pita" + ] + }, + { + "team": "Virtus.pro", + "teamLogo": "assets/tournament_logos/virtus_pro_48px_virtus_pro_allmode_png.png", + "players": [ + "TaZ", + "NEO", + "pashaBiceps", + "Snax", + "byali" + ] + }, + { + "team": "mousesports", + "teamLogo": "assets/tournament_logos/mousesports_92px_mousesports_2016_allmode_png.png", + "players": [ + "chrisJ", + "NiKo", + "denis", + "nex", + "Spiidi" + ] + }, + { + "team": "FaZe Clan", + "teamLogo": "assets/tournament_logos/faze_clan_76px_faze_clan_2017_allmode_png.png", + "players": [ + "rain", + "fox", + "jkaem", + "aizy", + "kioShiMa" + ] + }, + { + "team": "OpTic Gaming", + "teamLogo": "assets/tournament_logos/optic_gaming_77px_optic_gaming_lightmode_png.png", + "players": [ + "daps", + "RUSH", + "NAF", + "stanislaw", + "mixwell" + ] + }, + { + "team": "Gambit Gaming", + "teamLogo": "assets/tournament_logos/gambit_gaming_46px_gambit_esports_allmode_png.png", + "players": [ + "hooch", + "Dosia", + "AdreN", + "mou", + "spaze" + ] + }, + { + "team": "Team EnVyUs", + "teamLogo": "assets/tournament_logos/team_envyus_50px_team_envyus_lightmode_png.png", + "players": [ + "Happy", + "NBK-", + "kennyS", + "apEX", + "DEVIL" + ] + }, + { + "team": "Team Dignitas", + "teamLogo": "assets/tournament_logos/team_dignitas_44px_team_dignitas_allmode_png.png", + "players": [ + "MSL", + "tenzki", + "k0nfig", + "RUBINO", + "cajunb" + ] + }, + { + "team": "FlipSid3 Tactics", + "teamLogo": "assets/tournament_logos/flipsid3_tactics_47px_flipsid3_tactics_lightmode_png.png", + "players": [ + "B1ad3", + "WorldEdit", + "markeloff", + "Shara", + "wayLander" + ] + }, + { + "team": "G2 Esports", + "teamLogo": "assets/tournament_logos/g2_esports_43px_g2_esports_allmode_png.png", + "players": [ + "RpK", + "SmithZz", + "shox", + "ScreaM", + "bodyy" + ] + } + ], + "stageDates": [], + "playoffMatches": [ + { + "round": "Quarterfinals", + "team1": "Virtus.pro", + "team2": "Astralis", + "team1Logo": "assets/tournament_logos/virtus_pro_48px_virtus_pro_allmode_png.png", + "team2Logo": "assets/tournament_logos/astralis_41px_astralis_logo_png.png", + "score1": "2", + "score2": "0", + "date": "July 8, 2016 - 10:00 CEST" + }, + { + "round": "Quarterfinals", + "team1": "SK Gaming", + "team2": "FlipSid3 Tactics", + "team1Logo": "assets/tournament_logos/sk_gaming_50px_sk_gaming_2003_lightmode_png.png", + "team2Logo": "assets/tournament_logos/flipsid3_tactics_47px_flipsid3_tactics_lightmode_png.png", + "score1": "2", + "score2": "0", + "date": "July 8, 2016 - 14:00 CEST" + }, + { + "round": "Semifinals", + "team1": "Virtus.pro", + "team2": "SK Gaming", + "team1Logo": "assets/tournament_logos/virtus_pro_48px_virtus_pro_allmode_png.png", + "team2Logo": "assets/tournament_logos/sk_gaming_50px_sk_gaming_2003_lightmode_png.png", + "score1": "1", + "score2": "2", + "date": "July 9, 2016 - 15:00 CEST" + }, + { + "round": "Quarterfinals", + "team1": "Natus Vincere", + "team2": "Team Liquid", + "team1Logo": "assets/tournament_logos/natus_vincere_56px_natus_vincere_allmode_png.png", + "team2Logo": "assets/tournament_logos/team_liquid_50px_team_liquid_allmode_png.png", + "score1": "1", + "score2": "2", + "date": "July 8, 2016 - 17:15 CEST" + }, + { + "round": "Quarterfinals", + "team1": "Gambit Esports", + "team2": "Fnatic", + "team1Logo": "assets/tournament_logos/gambit_esports_46px_gambit_esports_allmode_png.png", + "team2Logo": "assets/tournament_logos/fnatic_60px_fnatic_2015_allmode_png.png", + "score1": "0", + "score2": "2", + "date": "July 8, 2016 - 21:15 CEST" + }, + { + "round": "Semifinals", + "team1": "Team Liquid", + "team2": "Fnatic", + "team1Logo": "assets/tournament_logos/team_liquid_50px_team_liquid_allmode_png.png", + "team2Logo": "assets/tournament_logos/fnatic_60px_fnatic_2015_allmode_png.png", + "score1": "2", + "score2": "0", + "date": "July 9, 2016 - 19:30 CEST" + }, + { + "round": "Grand Final", + "team1": "SK Gaming", + "team2": "Team Liquid", + "team1Logo": "assets/tournament_logos/sk_gaming_50px_sk_gaming_2003_lightmode_png.png", + "team2Logo": "assets/tournament_logos/team_liquid_50px_team_liquid_allmode_png.png", + "score1": "2", + "score2": "0", + "date": "July 10, 2016 - 17:00 CEST" + } + ] + }, + { + "name": "ESL One Katowice 2015", + "winner": "Fnatic", + "tournamentLogo": "assets/tournament_logos/esl_one_katowice_2015.png", + "startDate": "2015-03-12", + "endDate": "2015-03-15", + "placements": [ + { + "place": "1st", + "team": "Fnatic", + "teamLogo": "assets/tournament_logos/fnatic_61px_fnatic_allmode_png.png" + }, + { + "place": "2nd", + "team": "Ninjas in Pyjamas", + "teamLogo": "assets/tournament_logos/ninjas_in_pyjamas_50px_ninjas_in_pyjamas_2012_allmode_png.png" + }, + { + "place": "3rd-4th", + "team": "Virtus.pro", + "teamLogo": "assets/tournament_logos/virtus_pro_48px_virtus_pro_allmode_png.png" + }, + { + "place": "3rd-4th", + "team": "Team EnVyUs", + "teamLogo": "assets/tournament_logos/team_envyus_50px_team_envyus_lightmode_png.png" + }, + { + "place": "5th-8th", + "team": "PENTA Sports", + "teamLogo": "assets/tournament_logos/penta_sports_37px_penta_sports_lightmode_png.png" + }, + { + "place": "5th-8th", + "team": "Keyd Stars", + "teamLogo": "assets/tournament_logos/keyd_stars_38px_keyd_stars_2015_allmode_png.png" + }, + { + "place": "5th-8th", + "team": "Natus Vincere", + "teamLogo": "assets/tournament_logos/natus_vincere_56px_natus_vincere_allmode_png.png" + }, + { + "place": "5th-8th", + "team": "Team SoloMid", + "teamLogo": "assets/tournament_logos/team_solomid_50px_team_solomid_lightmode_png.png" + }, + { + "place": "9th-12th", + "team": "Vox Eminor", + "teamLogo": "assets/tournament_logos/vox_eminor_89px_vox_eminor_allmode_png.png" + }, + { + "place": "9th-12th", + "team": "LGB eSports", + "teamLogo": "assets/tournament_logos/lgb_esports_43px_lgb_esports_allmode_png.png" + }, + { + "place": "9th-12th", + "team": "Counter Logic Gaming", + "teamLogo": "assets/tournament_logos/counter_logic_gaming_70px_counter_logic_gaming_allmode_png.png" + }, + { + "place": "9th-12th", + "team": "Cloud9", + "teamLogo": "assets/tournament_logos/cloud9_70px_cloud9_allmode_png.png" + }, + { + "place": "13th-16th", + "team": "FlipSid3 Tactics", + "teamLogo": "assets/tournament_logos/flipsid3_tactics_47px_flipsid3_tactics_lightmode_png.png" + }, + { + "place": "13th-16th", + "team": "Titan", + "teamLogo": "assets/tournament_logos/titan_46px_titan_lightmode_png.png" + }, + { + "place": "13th-16th", + "team": "HellRaisers", + "teamLogo": "assets/tournament_logos/hellraisers_57px_hellraisers_2014_allmode_png.png" + }, + { + "place": "13th-16th", + "team": "3DMAX", + "teamLogo": "assets/tournament_logos/3dmax_36px_3dmax_2011_allmode_png.png" + } + ], + "teamRosters": [ + { + "team": "Team EnVyUs", + "teamLogo": "assets/tournament_logos/team_envyus_50px_team_envyus_lightmode_png.png", + "players": [ + "Happy", + "NBK-", + "kioShiMa", + "shox", + "SmithZz" + ] + }, + { + "team": "Ninjas in Pyjamas", + "teamLogo": "assets/tournament_logos/ninjas_in_pyjamas_50px_ninjas_in_pyjamas_2012_allmode_png.png", + "players": [ + "f0rest", + "GeT_RiGhT", + "Xizt", + "friberg", + "allu" + ] + }, + { + "team": "Virtus.pro", + "teamLogo": "assets/tournament_logos/virtus_pro_48px_virtus_pro_allmode_png.png", + "players": [ + "TaZ", + "NEO", + "pashaBiceps", + "Snax", + "byali" + ] + }, + { + "team": "Natus Vincere", + "teamLogo": "assets/tournament_logos/natus_vincere_56px_natus_vincere_allmode_png.png", + "players": [ + "Zeus", + "starix", + "seized", + "Edward", + "GuardiaN" + ] + }, + { + "team": "HellRaisers", + "teamLogo": "assets/tournament_logos/hellraisers_57px_hellraisers_2014_allmode_png.png", + "players": [ + "ANGE1", + "kUcheR", + "Dosia", + "AdreN", + "flamie" + ] + }, + { + "team": "PENTA Sports", + "teamLogo": "assets/tournament_logos/penta_sports_37px_penta_sports_lightmode_png.png", + "players": [ + "Troubley", + "kRYSTAL", + "nex", + "denis", + "Spiidi" + ] + }, + { + "team": "Team SoloMid", + "teamLogo": "assets/tournament_logos/team_solomid_50px_team_solomid_lightmode_png.png", + "players": [ + "device", + "dupreeh", + "Xyp9x", + "cajunb", + "karrigan" + ] + }, + { + "team": "Fnatic", + "teamLogo": "assets/tournament_logos/fnatic_61px_fnatic_allmode_png.png", + "players": [ + "flusha", + "JW", + "pronax", + "olofmeister", + "KRIMZ" + ] + }, + { + "team": "Counter Logic Gaming", + "teamLogo": "assets/tournament_logos/counter_logic_gaming_70px_counter_logic_gaming_allmode_png.png", + "players": [ + "reltuC", + "hazed", + "tarik", + "ptr", + "FNS" + ] + }, + { + "team": "LGB eSports", + "teamLogo": "assets/tournament_logos/lgb_esports_43px_lgb_esports_allmode_png.png", + "players": [ + "RUBINO", + "rain", + "jkaem", + "zEVES", + "Polly" + ] + }, + { + "team": "Cloud9", + "teamLogo": "assets/tournament_logos/cloud9_70px_cloud9_allmode_png.png", + "players": [ + "n0thing", + "sgares", + "SEMPHIS", + "shroud", + "ShahZaM" + ] + }, + { + "team": "Vox Eminor", + "teamLogo": "assets/tournament_logos/vox_eminor_89px_vox_eminor_allmode_png.png", + "players": [ + "AZR", + "Havoc", + "jks", + "SPUNJ", + "topguN" + ] + }, + { + "team": "FlipSid3 Tactics", + "teamLogo": "assets/tournament_logos/flipsid3_tactics_47px_flipsid3_tactics_lightmode_png.png", + "players": [ + "B1ad3", + "bondik", + "WorldEdit", + "markeloff", + "DavCost" + ] + }, + { + "team": "Titan", + "teamLogo": "assets/tournament_logos/titan_46px_titan_lightmode_png.png", + "players": [ + "kennyS", + "Ex6TenZ", + "apEX", + "Maniac", + "RpK" + ] + }, + { + "team": "Keyd Stars", + "teamLogo": "assets/tournament_logos/keyd_stars_38px_keyd_stars_2015_allmode_png.png", + "players": [ + "FalleN", + "fer", + "steel", + "zqk", + "boltz" + ] + }, + { + "team": "3DMAX", + "teamLogo": "assets/tournament_logos/3dmax_36px_3dmax_2011_allmode_png.png", + "players": [ + "diSTURBED", + "natu", + "KHRN", + "stonde", + "xartE" + ] + } + ], + "stageDates": [], + "playoffMatches": [ + { + "round": "Quarterfinals", + "team1": "Fnatic", + "team2": "PENTA Sports", + "team1Logo": "assets/tournament_logos/fnatic_61px_fnatic_allmode_png.png", + "team2Logo": "assets/tournament_logos/penta_sports_37px_penta_sports_lightmode_png.png", + "score1": "2", + "score2": "0", + "date": "March 13, 2015 - 17:00 CET" + }, + { + "round": "Quarterfinals", + "team1": "Virtus.pro", + "team2": "Keyd Stars", + "team1Logo": "assets/tournament_logos/virtus_pro_48px_virtus_pro_allmode_png.png", + "team2Logo": "assets/tournament_logos/keyd_stars_38px_keyd_stars_2015_allmode_png.png", + "score1": "2", + "score2": "1", + "date": "March 13, 2015 - 19:45 CET" + }, + { + "round": "Semifinals", + "team1": "Fnatic", + "team2": "Virtus.pro", + "team1Logo": "assets/tournament_logos/fnatic_61px_fnatic_allmode_png.png", + "team2Logo": "assets/tournament_logos/virtus_pro_48px_virtus_pro_allmode_png.png", + "score1": "2", + "score2": "0", + "date": "March 14, 2015 - 17:00 CET" + }, + { + "round": "Quarterfinals", + "team1": "Team Envy", + "team2": "Natus Vincere", + "team1Logo": "assets/tournament_logos/team_envy_50px_team_envyus_lightmode_png.png", + "team2Logo": "assets/tournament_logos/natus_vincere_56px_natus_vincere_allmode_png.png", + "score1": "2", + "score2": "1", + "date": "March 13, 2015 - 13:00 CET" + }, + { + "round": "Quarterfinals", + "team1": "Ninjas in Pyjamas", + "team2": "TSM", + "team1Logo": "assets/tournament_logos/ninjas_in_pyjamas_50px_ninjas_in_pyjamas_2012_allmode_png.png", + "team2Logo": "assets/tournament_logos/tsm_50px_team_solomid_lightmode_png.png", + "score1": "2", + "score2": "1", + "date": "March 14, 2015 - 13:00 CET" + }, + { + "round": "Semifinals", + "team1": "Team Envy", + "team2": "Ninjas in Pyjamas", + "team1Logo": "assets/tournament_logos/team_envy_50px_team_envyus_lightmode_png.png", + "team2Logo": "assets/tournament_logos/ninjas_in_pyjamas_50px_ninjas_in_pyjamas_2012_allmode_png.png", + "score1": "0", + "score2": "2", + "date": "March 14, 2015 - 20:00 CET" + }, + { + "round": "Grand Final", + "team1": "Fnatic", + "team2": "Ninjas in Pyjamas", + "team1Logo": "assets/tournament_logos/fnatic_61px_fnatic_allmode_png.png", + "team2Logo": "assets/tournament_logos/ninjas_in_pyjamas_50px_ninjas_in_pyjamas_2012_allmode_png.png", + "score1": "2", + "score2": "1", + "date": "March 15, 2015 - 13:00 CET" + } + ] + }, + { + "name": "FACEIT London 2018", + "winner": "Astralis", + "tournamentLogo": "assets/tournament_logos/faceit_london_2018.png", + "startDate": "2018-09-05", + "endDate": "2018-09-23", + "placements": [ + { + "place": "1st", + "team": "Astralis", + "teamLogo": "assets/tournament_logos/astralis_41px_astralis_logo_png.png" + }, + { + "place": "2nd", + "team": "Natus Vincere", + "teamLogo": "assets/tournament_logos/natus_vincere_56px_natus_vincere_allmode_png.png" + }, + { + "place": "3rd-4th", + "team": "MIBR", + "teamLogo": "assets/tournament_logos/mibr_100px_mibr_2018_lightmode_png.png" + }, + { + "place": "3rd-4th", + "team": "Team Liquid", + "teamLogo": "assets/tournament_logos/team_liquid_50px_team_liquid_2017_lightmode_png.png" + }, + { + "place": "5th-8th", + "team": "compLexity Gaming", + "teamLogo": "assets/tournament_logos/complexity_gaming_85px_complexity_gaming_2016_allmode_png.png" + }, + { + "place": "5th-8th", + "team": "BIG", + "teamLogo": "assets/tournament_logos/big_35px_big_lightmode_png.png" + }, + { + "place": "5th-8th", + "team": "HellRaisers", + "teamLogo": "assets/tournament_logos/hellraisers_57px_hellraisers_2018_allmode_png.png" + }, + { + "place": "5th-8th", + "team": "FaZe Clan", + "teamLogo": "assets/tournament_logos/faze_clan_76px_faze_clan_2018_lightmode_png.png" + }, + { + "place": "9th-11th", + "team": "Ninjas in Pyjamas", + "teamLogo": "assets/tournament_logos/ninjas_in_pyjamas_50px_ninjas_in_pyjamas_2017_lightmode_png.png" + }, + { + "place": "9th-11th", + "team": "Fnatic", + "teamLogo": "assets/tournament_logos/fnatic_60px_fnatic_2015_allmode_png.png" + }, + { + "place": "9th-11th", + "team": "G2 Esports", + "teamLogo": "assets/tournament_logos/g2_esports_43px_g2_esports_2020_lightmode_png.png" + }, + { + "place": "12th-14th", + "team": "Vega Squadron", + "teamLogo": "assets/tournament_logos/vega_squadron_47px_vega_squadron_2016_allmode_png.png" + }, + { + "place": "12th-14th", + "team": "Cloud9", + "teamLogo": "assets/tournament_logos/cloud9_70px_cloud9_allmode_png.png" + }, + { + "place": "12th-14th", + "team": "TYLOO", + "teamLogo": "assets/tournament_logos/tyloo_56px_tyloo_2016_allmode_png.png" + }, + { + "place": "15th-16th", + "team": "Winstrike Team", + "teamLogo": "assets/tournament_logos/winstrike_team_75px_winstrike_team_2018_allmode_png.png" + }, + { + "place": "15th-16th", + "team": "mousesports", + "teamLogo": "assets/tournament_logos/mousesports_92px_mousesports_2016_allmode_png.png" + }, + { + "place": "17th-19th", + "team": "Team Spirit", + "teamLogo": "assets/tournament_logos/team_spirit_38px_team_spirit_2016_alt_allmode_png.png" + }, + { + "place": "17th-19th", + "team": "OpTic Gaming", + "teamLogo": "assets/tournament_logos/optic_gaming_77px_optic_gaming_lightmode_png.png" + }, + { + "place": "17th-19th", + "team": "North", + "teamLogo": "assets/tournament_logos/north_48px_north_lightmode_png.png" + }, + { + "place": "20th-22nd", + "team": "Renegades", + "teamLogo": "assets/tournament_logos/renegades_47px_renegades_png.png" + }, + { + "place": "20th-22nd", + "team": "Gambit Esports", + "teamLogo": "assets/tournament_logos/gambit_esports_46px_gambit_esports_allmode_png.png" + }, + { + "place": "20th-22nd", + "team": "Rogue", + "teamLogo": "assets/tournament_logos/rogue_44px_rogue_allmode_png.png" + }, + { + "place": "23rd-24th", + "team": "Space Soldiers", + "teamLogo": "assets/tournament_logos/space_soldiers_57px_space_soldiers_lightmode_png.png" + }, + { + "place": "23rd-24th", + "team": "Virtus.pro", + "teamLogo": "assets/tournament_logos/virtus_pro_48px_vp_megafon_style_png.png" + } + ], + "teamRosters": [ + { + "team": "Cloud9", + "teamLogo": "assets/tournament_logos/cloud9_70px_cloud9_allmode_png.png", + "players": [ + "Skadoodle", + "autimatic", + "RUSH", + "Golden", + "STYKO" + ] + }, + { + "team": "FaZe Clan", + "teamLogo": "assets/tournament_logos/faze_clan_76px_faze_clan_2018_lightmode_png.png", + "players": [ + "rain", + "karrigan", + "NiKo", + "GuardiaN", + "olofmeister" + ] + }, + { + "team": "Natus Vincere", + "teamLogo": "assets/tournament_logos/natus_vincere_56px_natus_vincere_allmode_png.png", + "players": [ + "Edward", + "flamie", + "s1mple", + "Zeus", + "electronic" + ] + }, + { + "team": "MIBR", + "teamLogo": "assets/tournament_logos/mibr_100px_mibr_2018_lightmode_png.png", + "players": [ + "FalleN", + "fer", + "coldzera", + "Stewie2K", + "tarik" + ] + }, + { + "team": "mousesports", + "teamLogo": "assets/tournament_logos/mousesports_92px_mousesports_2016_allmode_png.png", + "players": [ + "chrisJ", + "oskar", + "ropz", + "suNny", + "Snax" + ] + }, + { + "team": "Winstrike Team", + "teamLogo": "assets/tournament_logos/winstrike_team_75px_winstrike_team_2018_allmode_png.png", + "players": [ + "balblna", + "Kvik", + "jmqa", + "waterfaLLZ", + "Boombl4" + ] + }, + { + "team": "G2 Esports", + "teamLogo": "assets/tournament_logos/g2_esports_43px_g2_esports_2020_lightmode_png.png", + "players": [ + "shox", + "bodyy", + "kennyS", + "SmithZz", + "Ex6TenZ" + ] + }, + { + "team": "Fnatic", + "teamLogo": "assets/tournament_logos/fnatic_60px_fnatic_2015_allmode_png.png", + "players": [ + "KRIMZ", + "JW", + "flusha", + "Xizt", + "draken" + ] + }, + { + "team": "Gambit Esports", + "teamLogo": "assets/tournament_logos/gambit_esports_46px_gambit_esports_allmode_png.png", + "players": [ + "Dosia", + "AdreN", + "mou", + "Hobbit", + "mir" + ] + }, + { + "team": "Vega Squadron", + "teamLogo": "assets/tournament_logos/vega_squadron_47px_vega_squadron_2016_allmode_png.png", + "players": [ + "chopper", + "jR", + "hutji", + "crush", + "tonyblack" + ] + }, + { + "team": "Space Soldiers", + "teamLogo": "assets/tournament_logos/space_soldiers_57px_space_soldiers_lightmode_png.png", + "players": [ + "XANTARES", + "paz", + "MAJ3R", + "Calyx", + "hardstyle" + ] + }, + { + "team": "Team Liquid", + "teamLogo": "assets/tournament_logos/team_liquid_50px_team_liquid_2017_lightmode_png.png", + "players": [ + "nitr0", + "EliGE", + "Twistzz", + "NAF", + "TACO" + ] + }, + { + "team": "BIG", + "teamLogo": "assets/tournament_logos/big_35px_big_lightmode_png.png", + "players": [ + "gob b", + "tabseN", + "nex", + "tiziaN", + "smooya" + ] + }, + { + "team": "Astralis", + "teamLogo": "assets/tournament_logos/astralis_41px_astralis_logo_png.png", + "players": [ + "device", + "dupreeh", + "Xyp9x", + "gla1ve", + "Magisk" + ] + }, + { + "team": "North", + "teamLogo": "assets/tournament_logos/north_48px_north_lightmode_png.png", + "players": [ + "MSL", + "aizy", + "valde", + "Kjaerbye", + "niko" + ] + }, + { + "team": "Virtus.pro", + "teamLogo": "assets/tournament_logos/virtus_pro_48px_vp_megafon_style_png.png", + "players": [ + "NEO", + "pashaBiceps", + "byali", + "MICHU", + "snatchie" + ] + }, + { + "team": "Ninjas in Pyjamas", + "teamLogo": "assets/tournament_logos/ninjas_in_pyjamas_50px_ninjas_in_pyjamas_2017_lightmode_png.png", + "players": [ + "f0rest", + "GeT_RiGhT", + "REZ", + "dennis", + "Lekr0" + ] + }, + { + "team": "compLexity Gaming", + "teamLogo": "assets/tournament_logos/complexity_gaming_85px_complexity_gaming_2016_allmode_png.png", + "players": [ + "dephh", + "ANDROID", + "yay", + "stanislaw", + "ShahZaM" + ] + }, + { + "team": "HellRaisers", + "teamLogo": "assets/tournament_logos/hellraisers_57px_hellraisers_2018_allmode_png.png", + "players": [ + "ANGE1", + "bondik", + "DeadFox", + "woxic", + "ISSAA" + ] + }, + { + "team": "Renegades", + "teamLogo": "assets/tournament_logos/renegades_47px_renegades_png.png", + "players": [ + "AZR", + "jks", + "USTILO", + "Nifty", + "jkaem" + ] + }, + { + "team": "OpTic Gaming", + "teamLogo": "assets/tournament_logos/optic_gaming_77px_optic_gaming_lightmode_png.png", + "players": [ + "k0nfig", + "cajunb", + "gade", + "Snappi", + "JUGi" + ] + }, + { + "team": "Rogue", + "teamLogo": "assets/tournament_logos/rogue_44px_rogue_allmode_png.png", + "players": [ + "vice", + "Hiko", + "cadiaN", + "SicK", + "Rickeh" + ] + }, + { + "team": "Team Spirit", + "teamLogo": "assets/tournament_logos/team_spirit_38px_team_spirit_2016_alt_allmode_png.png", + "players": [ + "COLDY", + "S0tF1k", + "DavCost", + "Dima", + "somedieyoung" + ] + }, + { + "team": "TYLOO", + "teamLogo": "assets/tournament_logos/tyloo_56px_tyloo_2016_allmode_png.png", + "players": [ + "captainMo", + "DD", + "somebody", + "BnTeT", + "xccurate" + ] + } + ], + "stageDates": [], + "playoffMatches": [ + { + "round": "Quarterfinals", + "team1": "Complexity", + "team2": "MIBR", + "team1Logo": "assets/tournament_logos/complexity_85px_complexity_gaming_2016_allmode_png.png", + "team2Logo": "assets/tournament_logos/mibr_100px_mibr_2018_lightmode_png.png", + "score1": "0", + "score2": "2", + "date": "September 20, 2018 - 18:05 BST" + }, + { + "round": "Quarterfinals", + "team1": "BIG", + "team2": "Natus Vincere", + "team1Logo": "assets/tournament_logos/big_35px_big_lightmode_png.png", + "team2Logo": "assets/tournament_logos/natus_vincere_56px_natus_vincere_allmode_png.png", + "score1": "0", + "score2": "2", + "date": "September 20, 2018 - 15:15 BST" + }, + { + "round": "Semifinals", + "team1": "MIBR", + "team2": "Natus Vincere", + "team1Logo": "assets/tournament_logos/mibr_100px_mibr_2018_lightmode_png.png", + "team2Logo": "assets/tournament_logos/natus_vincere_56px_natus_vincere_allmode_png.png", + "score1": "0", + "score2": "2", + "date": "September 22, 2018 - 16:15 BST" + }, + { + "round": "Quarterfinals", + "team1": "Team Liquid", + "team2": "HellRaisers", + "team1Logo": "assets/tournament_logos/team_liquid_50px_team_liquid_2017_lightmode_png.png", + "team2Logo": "assets/tournament_logos/hellraisers_57px_hellraisers_2018_allmode_png.png", + "score1": "2", + "score2": "1", + "date": "September 21, 2018 - 16:15 BST" + }, + { + "round": "Quarterfinals", + "team1": "Astralis", + "team2": "FaZe Clan", + "team1Logo": "assets/tournament_logos/astralis_41px_astralis_logo_png.png", + "team2Logo": "assets/tournament_logos/faze_clan_76px_faze_clan_2018_lightmode_png.png", + "score1": "2", + "score2": "0", + "date": "September 21, 2018 - 20:05 BST" + }, + { + "round": "Semifinals", + "team1": "Team Liquid", + "team2": "Astralis", + "team1Logo": "assets/tournament_logos/team_liquid_50px_team_liquid_2017_lightmode_png.png", + "team2Logo": "assets/tournament_logos/astralis_41px_astralis_logo_png.png", + "score1": "0", + "score2": "2", + "date": "September 22, 2018 - 20:05 BST" + }, + { + "round": "Grand Final", + "team1": "Natus Vincere", + "team2": "Astralis", + "team1Logo": "assets/tournament_logos/natus_vincere_56px_natus_vincere_allmode_png.png", + "team2Logo": "assets/tournament_logos/astralis_41px_astralis_logo_png.png", + "score1": "0", + "score2": "2", + "date": "September 23, 2018 - 18:10 BST" + } + ] + }, + { + "name": "IEM Katowice 2019", + "winner": "Astralis", + "tournamentLogo": "assets/tournament_logos/iem_katowice_2019.png", + "startDate": "2019-02-13", + "endDate": "2019-03-03", + "placements": [ + { + "place": "1st", + "team": "Astralis", + "teamLogo": "assets/tournament_logos/astralis_41px_astralis_logo_png.png" + }, + { + "place": "2nd", + "team": "ENCE", + "teamLogo": "assets/tournament_logos/ence_50px_ence_esports_2016_allmode_png.png" + }, + { + "place": "3rd-4th", + "team": "Natus Vincere", + "teamLogo": "assets/tournament_logos/natus_vincere_56px_natus_vincere_allmode_png.png" + }, + { + "place": "3rd-4th", + "team": "MIBR", + "teamLogo": "assets/tournament_logos/mibr_100px_mibr_2018_lightmode_png.png" + }, + { + "place": "5th-8th", + "team": "FaZe Clan", + "teamLogo": "assets/tournament_logos/faze_clan_76px_faze_clan_2018_lightmode_png.png" + }, + { + "place": "5th-8th", + "team": "Team Liquid", + "teamLogo": "assets/tournament_logos/team_liquid_50px_team_liquid_2017_lightmode_png.png" + }, + { + "place": "5th-8th", + "team": "Renegades", + "teamLogo": "assets/tournament_logos/renegades_47px_renegades_2019_allmode_png.png" + }, + { + "place": "5th-8th", + "team": "Ninjas in Pyjamas", + "teamLogo": "assets/tournament_logos/ninjas_in_pyjamas_50px_ninjas_in_pyjamas_2017_lightmode_png.png" + }, + { + "place": "9th-11th", + "team": "Team Vitality", + "teamLogo": "assets/tournament_logos/team_vitality_39px_team_vitality_2018_allmode_png.png" + }, + { + "place": "9th-11th", + "team": "Cloud9", + "teamLogo": "assets/tournament_logos/cloud9_70px_cloud9_allmode_png.png" + }, + { + "place": "9th-11th", + "team": "AVANGAR", + "teamLogo": "assets/tournament_logos/avangar_53px_avangar_2018_allmode_png.png" + }, + { + "place": "12th-14th", + "team": "HellRaisers", + "teamLogo": "assets/tournament_logos/hellraisers_57px_hellraisers_2018_allmode_png.png" + }, + { + "place": "12th-14th", + "team": "G2 Esports", + "teamLogo": "assets/tournament_logos/g2_esports_43px_g2_esports_2020_lightmode_png.png" + }, + { + "place": "12th-14th", + "team": "compLexity Gaming", + "teamLogo": "assets/tournament_logos/complexity_gaming_85px_complexity_gaming_2016_allmode_png.png" + }, + { + "place": "15th-16th", + "team": "BIG", + "teamLogo": "assets/tournament_logos/big_35px_big_lightmode_png.png" + }, + { + "place": "15th-16th", + "team": "NRG Esports", + "teamLogo": "assets/tournament_logos/nrg_esports_76px_nrg_esports_2017_lightmode_png.png" + }, + { + "place": "17th-19th", + "team": "TYLOO", + "teamLogo": "assets/tournament_logos/tyloo_56px_tyloo_2016_allmode_png.png" + }, + { + "place": "17th-19th", + "team": "ViCi Gaming", + "teamLogo": "assets/tournament_logos/vici_gaming_49px_vici_gaming_allmode_png.png" + }, + { + "place": "17th-19th", + "team": "Winstrike Team", + "teamLogo": "assets/tournament_logos/winstrike_team_75px_winstrike_team_2018_allmode_png.png" + }, + { + "place": "20th-22nd", + "team": "Fnatic", + "teamLogo": "assets/tournament_logos/fnatic_60px_fnatic_2015_allmode_png.png" + }, + { + "place": "20th-22nd", + "team": "Vega Squadron", + "teamLogo": "assets/tournament_logos/vega_squadron_47px_vega_squadron_2016_allmode_png.png" + }, + { + "place": "20th-22nd", + "team": "FURIA Esports", + "teamLogo": "assets/tournament_logos/furia_esports_51px_furia_esports_allmode_png.png" + }, + { + "place": "23rd-24th", + "team": "Team Spirit", + "teamLogo": "assets/tournament_logos/team_spirit_38px_team_spirit_2016_alt_allmode_png.png" + }, + { + "place": "23rd-24th", + "team": "Grayhound Gaming", + "teamLogo": "assets/tournament_logos/grayhound_gaming_43px_grayhound_gaming_allmode_png.png" + } + ], + "teamRosters": [ + { + "team": "Astralis", + "teamLogo": "assets/tournament_logos/astralis_41px_astralis_logo_png.png", + "players": [ + "device", + "dupreeh", + "Xyp9x", + "gla1ve", + "Magisk" + ] + }, + { + "team": "Natus Vincere", + "teamLogo": "assets/tournament_logos/natus_vincere_56px_natus_vincere_allmode_png.png", + "players": [ + "Edward", + "flamie", + "s1mple", + "Zeus", + "electronic" + ] + }, + { + "team": "MIBR", + "teamLogo": "assets/tournament_logos/mibr_100px_mibr_2018_lightmode_png.png", + "players": [ + "FalleN", + "fer", + "coldzera", + "TACO", + "felps" + ] + }, + { + "team": "Team Liquid", + "teamLogo": "assets/tournament_logos/team_liquid_50px_team_liquid_2017_lightmode_png.png", + "players": [ + "nitr0", + "EliGE", + "Twistzz", + "NAF", + "Stewie2K" + ] + }, + { + "team": "compLexity", + "players": [ + "dephh", + "stanislaw", + "ShahZaM", + "Rickeh", + "n0thing" + ] + }, + { + "team": "BIG", + "teamLogo": "assets/tournament_logos/big_35px_big_lightmode_png.png", + "players": [ + "gob b", + "tabseN", + "nex", + "tiziaN", + "XANTARES" + ] + }, + { + "team": "HellRaisers", + "teamLogo": "assets/tournament_logos/hellraisers_57px_hellraisers_2018_allmode_png.png", + "players": [ + "ANGE1", + "DeadFox", + "woxic", + "ISSAA", + "Hobbit" + ] + }, + { + "team": "FaZe Clan", + "teamLogo": "assets/tournament_logos/faze_clan_76px_faze_clan_2018_lightmode_png.png", + "players": [ + "rain", + "NiKo", + "GuardiaN", + "olofmeister", + "AdreN" + ] + }, + { + "team": "Ninjas in Pyjamas", + "teamLogo": "assets/tournament_logos/ninjas_in_pyjamas_50px_ninjas_in_pyjamas_2017_lightmode_png.png", + "players": [ + "f0rest", + "GeT_RiGhT", + "REZ", + "dennis", + "Lekr0" + ] + }, + { + "team": "Fnatic", + "teamLogo": "assets/tournament_logos/fnatic_60px_fnatic_2015_allmode_png.png", + "players": [ + "KRIMZ", + "JW", + "Xizt", + "twist", + "Brollan" + ] + }, + { + "team": "G2 Esports", + "teamLogo": "assets/tournament_logos/g2_esports_43px_g2_esports_2020_lightmode_png.png", + "players": [ + "shox", + "bodyy", + "kennyS", + "Lucky", + "JACKZ" + ] + }, + { + "team": "Vega Squadron", + "teamLogo": "assets/tournament_logos/vega_squadron_47px_vega_squadron_2016_allmode_png.png", + "players": [ + "chopper", + "jR", + "hutji", + "crush", + "tonyblack" + ] + }, + { + "team": "Cloud9", + "teamLogo": "assets/tournament_logos/cloud9_70px_cloud9_allmode_png.png", + "players": [ + "autimatic", + "RUSH", + "flusha", + "kioShiMa", + "Zellsis" + ] + }, + { + "team": "TYLOO", + "teamLogo": "assets/tournament_logos/tyloo_56px_tyloo_2016_allmode_png.png", + "players": [ + "somebody", + "BnTeT", + "xccurate", + "Summer", + "Attacker" + ] + }, + { + "team": "ENCE", + "teamLogo": "assets/tournament_logos/ence_50px_ence_esports_2016_allmode_png.png", + "players": [ + "allu", + "Aleksib", + "sergej", + "Aerial", + "xseveN" + ] + }, + { + "team": "NRG Esports", + "teamLogo": "assets/tournament_logos/nrg_esports_76px_nrg_esports_2017_lightmode_png.png", + "players": [ + "FugLy", + "daps", + "Brehze", + "CeRq", + "Ethan" + ] + }, + { + "team": "AVANGAR", + "teamLogo": "assets/tournament_logos/avangar_53px_avangar_2018_allmode_png.png", + "players": [ + "buster", + "KrizzeN", + "qikert", + "Jame", + "fitch" + ] + }, + { + "team": "Renegades", + "teamLogo": "assets/tournament_logos/renegades_47px_renegades_2019_allmode_png.png", + "players": [ + "AZR", + "jks", + "jkaem", + "Liazz", + "Gratisfaction" + ] + }, + { + "team": "Team Vitality", + "teamLogo": "assets/tournament_logos/team_vitality_39px_team_vitality_2018_allmode_png.png", + "players": [ + "NBK-", + "apEX", + "RpK", + "ZywOo", + "ALEX" + ] + }, + { + "team": "FURIA Esports", + "teamLogo": "assets/tournament_logos/furia_esports_51px_furia_esports_allmode_png.png", + "players": [ + "yuurih", + "arT", + "VINI", + "KSCERATO", + "ableJ" + ] + }, + { + "team": "Team Spirit", + "teamLogo": "assets/tournament_logos/team_spirit_38px_team_spirit_2016_alt_allmode_png.png", + "players": [ + "COLDYY1", + "S0tF1k", + "DavCost", + "Dima", + "somedieyoung" + ] + }, + { + "team": "Grayhound Gaming", + "teamLogo": "assets/tournament_logos/grayhound_gaming_43px_grayhound_gaming_allmode_png.png", + "players": [ + "erkaSt", + "dexter", + "DickStacy", + "malta", + "sterling" + ] + }, + { + "team": "Winstrike Team", + "teamLogo": "assets/tournament_logos/winstrike_team_75px_winstrike_team_2018_allmode_png.png", + "players": [ + "Boombl4", + "Kvik", + "n0rb3r7", + "WorldEdit", + "wayLander" + ] + }, + { + "team": "ViCi Gaming", + "teamLogo": "assets/tournament_logos/vici_gaming_49px_vici_gaming_allmode_png.png", + "players": [ + "zhokiNg", + "Freeman", + "aumaN", + "advent", + "kaze" + ] + } + ], + "stageDates": [ + { + "phase": "Challengers Stage", + "startDate": "2019-02-13", + "endDate": "2019-02-17" + }, + { + "phase": "Legends Stage", + "startDate": "2019-02-20", + "endDate": "2019-02-24" + }, + { + "phase": "Champions Stage", + "startDate": "2019-02-28", + "endDate": "2019-03-03" + } + ], + "playoffMatches": [ + { + "round": "Quarterfinals", + "team1": "Natus Vincere", + "team2": "FaZe Clan", + "team1Logo": "assets/tournament_logos/natus_vincere_56px_natus_vincere_allmode_png.png", + "team2Logo": "assets/tournament_logos/faze_clan_76px_faze_clan_2018_lightmode_png.png", + "score1": "2", + "score2": "0", + "date": "February 28, 2019 - 16:20 CET" + }, + { + "round": "Quarterfinals", + "team1": "Team Liquid", + "team2": "ENCE", + "team1Logo": "assets/tournament_logos/team_liquid_50px_team_liquid_2017_lightmode_png.png", + "team2Logo": "assets/tournament_logos/ence_50px_ence_esports_2016_allmode_png.png", + "score1": "0", + "score2": "2", + "date": "February 28, 2019 - 19:40 CET" + }, + { + "round": "Semifinals", + "team1": "Natus Vincere", + "team2": "ENCE", + "team1Logo": "assets/tournament_logos/natus_vincere_56px_natus_vincere_allmode_png.png", + "team2Logo": "assets/tournament_logos/ence_50px_ence_esports_2016_allmode_png.png", + "score1": "1", + "score2": "2", + "date": "March 2, 2019 - 16:10 CET" + }, + { + "round": "Quarterfinals", + "team1": "MIBR", + "team2": "Renegades", + "team1Logo": "assets/tournament_logos/mibr_100px_mibr_2018_lightmode_png.png", + "team2Logo": "assets/tournament_logos/renegades_47px_renegades_2019_allmode_png.png", + "score1": "2", + "score2": "0", + "date": "March 1, 2019 - 16:10 CET" + }, + { + "round": "Quarterfinals", + "team1": "Astralis", + "team2": "Ninjas in Pyjamas", + "team1Logo": "assets/tournament_logos/astralis_41px_astralis_logo_png.png", + "team2Logo": "assets/tournament_logos/ninjas_in_pyjamas_50px_ninjas_in_pyjamas_2017_lightmode_png.png", + "score1": "2", + "score2": "0", + "date": "March 1, 2019 - 19:35 CET" + }, + { + "round": "Semifinals", + "team1": "MIBR", + "team2": "Astralis", + "team1Logo": "assets/tournament_logos/mibr_100px_mibr_2018_lightmode_png.png", + "team2Logo": "assets/tournament_logos/astralis_41px_astralis_logo_png.png", + "score1": "0", + "score2": "2", + "date": "March 2, 2019 - 20:20 CET" + }, + { + "round": "Grand Final", + "team1": "ENCE", + "team2": "Astralis", + "team1Logo": "assets/tournament_logos/ence_50px_ence_esports_2016_allmode_png.png", + "team2Logo": "assets/tournament_logos/astralis_41px_astralis_logo_png.png", + "score1": "0", + "score2": "2", + "date": "March 3, 2019 - 19:00 CET" + } + ] + }, + { + "name": "IEM Rio 2022", + "winner": "Outsiders", + "tournamentLogo": "assets/tournament_logos/iem_rio_2022.png", + "startDate": "2022-10-31", + "endDate": "2022-11-13", + "placements": [ + { + "place": "1st", + "team": "Outsiders", + "teamLogo": "assets/tournament_logos/outsiders_49px_outsiders_csgo_allmode_png.png" + }, + { + "place": "2nd", + "team": "Heroic", + "teamLogo": "assets/tournament_logos/heroic_100px_heroic_2019_lightmode_png.png" + }, + { + "place": "3rd-4th", + "team": "MOUZ", + "teamLogo": "assets/tournament_logos/mouz_47px_mouz_2021_allmode_png.png" + }, + { + "place": "3rd-4th", + "team": "FURIA Esports", + "teamLogo": "assets/tournament_logos/furia_esports_51px_furia_esports_allmode_png.png" + }, + { + "place": "5th-8th", + "team": "Cloud9", + "teamLogo": "assets/tournament_logos/cloud9_70px_cloud9_allmode_png.png" + }, + { + "place": "5th-8th", + "team": "Fnatic", + "teamLogo": "assets/tournament_logos/fnatic_77px_fnatic_2020_allmode_png.png" + }, + { + "place": "5th-8th", + "team": "Team Spirit", + "teamLogo": "assets/tournament_logos/team_spirit_43px_team_spirit_2022_lightmode_png.png" + }, + { + "place": "5th-8th", + "team": "Natus Vincere", + "teamLogo": "assets/tournament_logos/natus_vincere_57px_natus_vincere_2021_lightmode_png.png" + }, + { + "place": "9th-11th", + "team": "BIG", + "teamLogo": "assets/tournament_logos/big_35px_big_2020_lightmode_png.png" + }, + { + "place": "9th-11th", + "team": "Team Liquid", + "teamLogo": "assets/tournament_logos/team_liquid_44px_team_liquid_2020_lightmode_png.png" + }, + { + "place": "9th-11th", + "team": "ENCE", + "teamLogo": "assets/tournament_logos/ence_50px_ence_2020_allmode_png.png" + }, + { + "place": "12th-14th", + "team": "Bad News Eagles", + "teamLogo": "assets/tournament_logos/bad_news_eagles_34px_bad_news_eagles_allmode_png.png" + }, + { + "place": "12th-14th", + "team": "Team Vitality", + "teamLogo": "assets/tournament_logos/team_vitality_40px_team_vitality_2021_allmode_png.png" + }, + { + "place": "12th-14th", + "team": "Sprout", + "teamLogo": "assets/tournament_logos/sprout_29px_sprout_allmode_png.png" + }, + { + "place": "15th-16th", + "team": "Ninjas in Pyjamas", + "teamLogo": "assets/tournament_logos/ninjas_in_pyjamas_50px_ninjas_in_pyjamas_2021_lightmode_png.png" + }, + { + "place": "15th-16th", + "team": "FaZe Clan", + "teamLogo": "assets/tournament_logos/faze_clan_75px_faze_clan_november_2021_lightmode_png.png" + }, + { + "place": "17th-19th", + "team": "Team GamerLegion", + "teamLogo": "assets/tournament_logos/team_gamerlegion_50px_team_gamerlegion_allmode_png.png" + }, + { + "place": "17th-19th", + "team": "OG", + "teamLogo": "assets/tournament_logos/og_34px_og_rb_allmode_png.png" + }, + { + "place": "17th-19th", + "team": "9z Team", + "teamLogo": "assets/tournament_logos/9z_team_54px_9z_team_allmode_png.png" + }, + { + "place": "20th-22nd", + "team": "Grayhound Gaming", + "teamLogo": "assets/tournament_logos/grayhound_gaming_43px_grayhound_gaming_allmode_png.png" + }, + { + "place": "20th-22nd", + "team": "Evil Geniuses", + "teamLogo": "assets/tournament_logos/evil_geniuses_37px_evil_geniuses_2020_lightmode_png.png" + }, + { + "place": "20th-22nd", + "team": "IHC Esports", + "teamLogo": "assets/tournament_logos/ihc_esports_42px_ihc_esports_allmode_png.png" + }, + { + "place": "23rd-24th", + "team": "00 Nation", + "teamLogo": "assets/tournament_logos/00_nation_50px_00_nation_2022_lightmode_2_png.png" + }, + { + "place": "23rd-24th", + "team": "Imperial Esports", + "teamLogo": "assets/tournament_logos/imperial_esports_44px_imperial_esports_2022_allmode_png.png" + } + ], + "teamRosters": [ + { + "team": "FaZe Clan", + "teamLogo": "assets/tournament_logos/faze_clan_75px_faze_clan_november_2021_lightmode_png.png", + "players": [ + "rain", + "broky", + "Twistzz", + "karrigan", + "ropz" + ] + }, + { + "team": "Natus Vincere", + "teamLogo": "assets/tournament_logos/natus_vincere_57px_natus_vincere_2021_lightmode_png.png", + "players": [ + "s1mple", + "electronic", + "Perfecto", + "b1t", + "sdy" + ] + }, + { + "team": "Ninjas in Pyjamas", + "teamLogo": "assets/tournament_logos/ninjas_in_pyjamas_50px_ninjas_in_pyjamas_2021_lightmode_png.png", + "players": [ + "REZ", + "hampus", + "es3tag", + "Brollan", + "Aleksib" + ] + }, + { + "team": "ENCE", + "teamLogo": "assets/tournament_logos/ence_50px_ence_2020_allmode_png.png", + "players": [ + "Snappi", + "Dycha", + "maden", + "SunPayus", + "valde" + ] + }, + { + "team": "Sprout", + "teamLogo": "assets/tournament_logos/sprout_29px_sprout_allmode_png.png", + "players": [ + "slaxz-", + "Staehr", + "lauNX", + "Zyphon", + "refrezh" + ] + }, + { + "team": "Heroic", + "teamLogo": "assets/tournament_logos/heroic_100px_heroic_2019_lightmode_png.png", + "players": [ + "stavn", + "cadiaN", + "TeSeS", + "sjuush", + "jabbi" + ] + }, + { + "team": "Team Spirit", + "teamLogo": "assets/tournament_logos/team_spirit_43px_team_spirit_2022_lightmode_png.png", + "players": [ + "chopper", + "magixx", + "Patsi", + "S1ren", + "w0nderful" + ] + }, + { + "team": "Team Liquid", + "teamLogo": "assets/tournament_logos/team_liquid_44px_team_liquid_2020_lightmode_png.png", + "players": [ + "EliGE", + "NAF", + "oSee", + "nitr0", + "YEKINDAR" + ] + }, + { + "team": "OG", + "teamLogo": "assets/tournament_logos/og_34px_og_rb_allmode_png.png", + "players": [ + "FlameZ", + "nexa", + "NEOFRAG", + "F1KU", + "degster" + ] + }, + { + "team": "Team Vitality", + "teamLogo": "assets/tournament_logos/team_vitality_40px_team_vitality_2021_allmode_png.png", + "players": [ + "apEX", + "ZywOo", + "dupreeh", + "Magisk", + "Spinx" + ] + }, + { + "team": "Evil Geniuses", + "teamLogo": "assets/tournament_logos/evil_geniuses_37px_evil_geniuses_2020_lightmode_png.png", + "players": [ + "Brehze", + "CeRq", + "autimatic", + "HexT", + "neaLaN" + ] + }, + { + "team": "Cloud9", + "teamLogo": "assets/tournament_logos/cloud9_70px_cloud9_allmode_png.png", + "players": [ + "nafany", + "sh1ro", + "interz", + "Ax1Le", + "Hobbit" + ] + }, + { + "team": "BIG", + "teamLogo": "assets/tournament_logos/big_35px_big_2020_lightmode_png.png", + "players": [ + "tabseN", + "syrsoN", + "faveN", + "Krimbo", + "k1to" + ] + }, + { + "team": "Bad News Eagles", + "teamLogo": "assets/tournament_logos/bad_news_eagles_34px_bad_news_eagles_allmode_png.png", + "players": [ + "SENER1", + "gxx-", + "juanflatroo", + "sinnopsyy", + "rigoN" + ] + }, + { + "team": "MOUZ", + "teamLogo": "assets/tournament_logos/mouz_47px_mouz_2021_allmode_png.png", + "players": [ + "frozen", + "dexter", + "torzsi", + "JDC", + "xertioN" + ] + }, + { + "team": "9z Team", + "teamLogo": "assets/tournament_logos/9z_team_54px_9z_team_allmode_png.png", + "players": [ + "dgt", + "max", + "dav1deuS", + "nqz", + "buda" + ] + }, + { + "team": "Team GamerLegion", + "teamLogo": "assets/tournament_logos/team_gamerlegion_50px_team_gamerlegion_allmode_png.png", + "players": [ + "iM", + "isak", + "acoR", + "siuhy", + "Keoz" + ] + }, + { + "team": "Outsiders", + "teamLogo": "assets/tournament_logos/outsiders_49px_outsiders_csgo_allmode_png.png", + "players": [ + "qikert", + "Jame", + "FL1T", + "n0rb3r7", + "fame" + ] + }, + { + "team": "00 Nation", + "teamLogo": "assets/tournament_logos/00_nation_50px_00_nation_2022_lightmode_2_png.png", + "players": [ + "coldzera", + "TRY", + "TACO", + "latto", + "dumau" + ] + }, + { + "team": "FURIA Esports", + "teamLogo": "assets/tournament_logos/furia_esports_51px_furia_esports_allmode_png.png", + "players": [ + "yuurih", + "arT", + "KSCERATO", + "drop", + "saffee" + ] + }, + { + "team": "Fnatic", + "teamLogo": "assets/tournament_logos/fnatic_77px_fnatic_2020_allmode_png.png", + "players": [ + "KRIMZ", + "mezii", + "nicoodoz", + "roeJ", + "FASHR" + ] + }, + { + "team": "IHC Esports", + "teamLogo": "assets/tournament_logos/ihc_esports_42px_ihc_esports_allmode_png.png", + "players": [ + "bLitz", + "Techno4K", + "kabal", + "sk0R", + "ANNIHILATION" + ] + }, + { + "team": "Imperial Esports", + "teamLogo": "assets/tournament_logos/imperial_esports_44px_imperial_esports_2022_allmode_png.png", + "players": [ + "FalleN", + "fer", + "boltz", + "VINI", + "chelo" + ] + }, + { + "team": "Grayhound Gaming", + "teamLogo": "assets/tournament_logos/grayhound_gaming_43px_grayhound_gaming_allmode_png.png", + "players": [ + "Sico", + "INS", + "aliStair", + "Liazz", + "vexite" + ] + } + ], + "stageDates": [ + { + "phase": "Challengers Stage", + "startDate": "2022-10-31", + "endDate": "2022-11-03" + }, + { + "phase": "Legends Stage", + "startDate": "2022-11-05", + "endDate": "2022-11-08" + }, + { + "phase": "Champions Stage", + "startDate": "2022-11-10", + "endDate": "2022-11-13" + } + ], + "playoffMatches": [ + { + "round": "Quarterfinals", + "team1": "Cloud9", + "team2": "MOUZ", + "team1Logo": "assets/tournament_logos/cloud9_70px_cloud9_allmode_png.png", + "team2Logo": "assets/tournament_logos/mouz_47px_mouz_2021_allmode_png.png", + "score1": "1", + "score2": "2", + "date": "November 10, 2022 - 17:30 BRT" + }, + { + "round": "Quarterfinals", + "team1": "Fnatic", + "team2": "Outsiders", + "team1Logo": "assets/tournament_logos/fnatic_77px_fnatic_2020_allmode_png.png", + "team2Logo": "assets/tournament_logos/outsiders_49px_outsiders_csgo_allmode_png.png", + "score1": "0", + "score2": "2", + "date": "November 10, 2022 - 14:00 BRT" + }, + { + "round": "Semifinals", + "team1": "MOUZ", + "team2": "Outsiders", + "team1Logo": "assets/tournament_logos/mouz_47px_mouz_2021_allmode_png.png", + "team2Logo": "assets/tournament_logos/outsiders_49px_outsiders_csgo_allmode_png.png", + "score1": "1", + "score2": "2", + "date": "November 12, 2022 - 14:00 BRT" + }, + { + "round": "Quarterfinals", + "team1": "HEROIC", + "team2": "Team Spirit", + "team1Logo": "assets/tournament_logos/heroic_100px_heroic_2019_lightmode_png.png", + "team2Logo": "assets/tournament_logos/team_spirit_43px_team_spirit_2022_lightmode_png.png", + "score1": "2", + "score2": "0", + "date": "November 11, 2022 - 14:15 BRT" + }, + { + "round": "Quarterfinals", + "team1": "Natus Vincere", + "team2": "FURIA", + "team1Logo": "assets/tournament_logos/natus_vincere_57px_natus_vincere_2021_lightmode_png.png", + "team2Logo": "assets/tournament_logos/furia_51px_furia_esports_allmode_png.png", + "score1": "1", + "score2": "2", + "date": "November 11, 2022 - 17:45 BRT" + }, + { + "round": "Semifinals", + "team1": "HEROIC", + "team2": "FURIA", + "team1Logo": "assets/tournament_logos/heroic_100px_heroic_2019_lightmode_png.png", + "team2Logo": "assets/tournament_logos/furia_51px_furia_esports_allmode_png.png", + "score1": "2", + "score2": "1", + "date": "November 12, 2022 - 19:00 BRT" + }, + { + "round": "Grand Final", + "team1": "Outsiders", + "team2": "HEROIC", + "team1Logo": "assets/tournament_logos/outsiders_49px_outsiders_csgo_allmode_png.png", + "team2Logo": "assets/tournament_logos/heroic_100px_heroic_2019_lightmode_png.png", + "score1": "2", + "score2": "0", + "date": "November 13, 2022 - 15:00 BRT" + } + ] + }, + { + "name": "MLG Columbus 2016", + "winner": "Luminosity Gaming", + "tournamentLogo": "assets/tournament_logos/mlg_columbus_2016.png", + "startDate": "2016-03-29", + "endDate": "2016-04-03", + "placements": [ + { + "place": "1st", + "team": "Luminosity Gaming", + "teamLogo": "assets/tournament_logos/luminosity_gaming_51px_luminosity_gaming_allmode_png.png" + }, + { + "place": "2nd", + "team": "Natus Vincere", + "teamLogo": "assets/tournament_logos/natus_vincere_56px_natus_vincere_allmode_png.png" + }, + { + "place": "3rd-4th", + "team": "Astralis", + "teamLogo": "assets/tournament_logos/astralis_41px_astralis_logo_png.png" + }, + { + "place": "3rd-4th", + "team": "Team Liquid", + "teamLogo": "assets/tournament_logos/team_liquid_50px_team_liquid_allmode_png.png" + }, + { + "place": "5th-8th", + "team": "Ninjas in Pyjamas", + "teamLogo": "assets/tournament_logos/ninjas_in_pyjamas_50px_ninjas_in_pyjamas_2012_allmode_png.png" + }, + { + "place": "5th-8th", + "team": "Fnatic", + "teamLogo": "assets/tournament_logos/fnatic_60px_fnatic_2015_allmode_png.png" + }, + { + "place": "5th-8th", + "team": "Counter Logic Gaming", + "teamLogo": "assets/tournament_logos/counter_logic_gaming_70px_counter_logic_gaming_allmode_png.png" + }, + { + "place": "5th-8th", + "team": "Virtus.pro", + "teamLogo": "assets/tournament_logos/virtus_pro_48px_virtus_pro_allmode_png.png" + }, + { + "place": "9th-12th", + "team": "mousesports", + "teamLogo": "assets/tournament_logos/mousesports_92px_mousesports_new_png.png" + }, + { + "place": "9th-12th", + "team": "FaZe Clan", + "teamLogo": "assets/tournament_logos/faze_clan_76px_faze_clan_2017_allmode_png.png" + }, + { + "place": "9th-12th", + "team": "Gambit Gaming", + "teamLogo": "assets/tournament_logos/gambit_gaming_41px_gambit_gaming_allmode_png.png" + }, + { + "place": "9th-12th", + "team": "G2 Esports", + "teamLogo": "assets/tournament_logos/g2_esports_43px_g2_esports_allmode_png.png" + }, + { + "place": "13th-16th", + "team": "FlipSid3 Tactics", + "teamLogo": "assets/tournament_logos/flipsid3_tactics_47px_flipsid3_tactics_lightmode_png.png" + }, + { + "place": "13th-16th", + "team": "Splyce", + "teamLogo": "assets/tournament_logos/splyce_43px_splyce_2015_allmode_png.png" + }, + { + "place": "13th-16th", + "team": "Team EnVyUs", + "teamLogo": "assets/tournament_logos/team_envyus_50px_team_envyus_lightmode_png.png" + }, + { + "place": "13th-16th", + "team": "Cloud9", + "teamLogo": "assets/tournament_logos/cloud9_70px_cloud9_allmode_png.png" + } + ], + "teamRosters": [ + { + "team": "Team EnVyUs", + "teamLogo": "assets/tournament_logos/team_envyus_50px_team_envyus_lightmode_png.png", + "players": [ + "Happy", + "NBK-", + "kennyS", + "apEX", + "DEVIL" + ] + }, + { + "team": "Natus Vincere", + "teamLogo": "assets/tournament_logos/natus_vincere_56px_natus_vincere_allmode_png.png", + "players": [ + "Zeus", + "Edward", + "seized", + "flamie", + "GuardiaN" + ] + }, + { + "team": "FaZe Clan", + "teamLogo": "assets/tournament_logos/faze_clan_76px_faze_clan_2017_allmode_png.png", + "players": [ + "Maikelele", + "rain", + "fox", + "jkaem", + "aizy" + ] + }, + { + "team": "Ninjas in Pyjamas", + "teamLogo": "assets/tournament_logos/ninjas_in_pyjamas_50px_ninjas_in_pyjamas_2012_allmode_png.png", + "players": [ + "f0rest", + "GeT_RiGhT", + "Xizt", + "friberg", + "THREAT" + ] + }, + { + "team": "Fnatic", + "teamLogo": "assets/tournament_logos/fnatic_60px_fnatic_2015_allmode_png.png", + "players": [ + "flusha", + "JW", + "olofmeister", + "KRIMZ", + "dennis" + ] + }, + { + "team": "Virtus.pro", + "teamLogo": "assets/tournament_logos/virtus_pro_48px_virtus_pro_allmode_png.png", + "players": [ + "TaZ", + "NEO", + "pashaBiceps", + "Snax", + "byali" + ] + }, + { + "team": "Astralis", + "teamLogo": "assets/tournament_logos/astralis_41px_astralis_logo_png.png", + "players": [ + "device", + "dupreeh", + "Xyp9x", + "cajunb", + "karrigan" + ] + }, + { + "team": "Luminosity Gaming", + "teamLogo": "assets/tournament_logos/luminosity_gaming_51px_luminosity_gaming_allmode_png.png", + "players": [ + "FalleN", + "fer", + "coldzera", + "fnx", + "TACO" + ] + }, + { + "team": "G2 Esports", + "teamLogo": "assets/tournament_logos/g2_esports_43px_g2_esports_allmode_png.png", + "players": [ + "Ex6TenZ", + "RpK", + "shox", + "SmithZz", + "ScreaM" + ] + }, + { + "team": "mousesports", + "teamLogo": "assets/tournament_logos/mousesports_92px_mousesports_new_png.png", + "players": [ + "NiKo", + "chrisJ", + "nex", + "denis", + "Spiidi" + ] + }, + { + "team": "Splyce", + "teamLogo": "assets/tournament_logos/splyce_43px_splyce_2015_allmode_png.png", + "players": [ + "arya", + "abE", + "jasonR", + "Professor_Chaos", + "DAVEY" + ] + }, + { + "team": "Gambit Gaming", + "teamLogo": "assets/tournament_logos/gambit_gaming_41px_gambit_gaming_allmode_png.png", + "players": [ + "hooch", + "Dosia", + "AdreN", + "mou", + "wayLander" + ] + }, + { + "team": "FlipSid3 Tactics", + "teamLogo": "assets/tournament_logos/flipsid3_tactics_47px_flipsid3_tactics_lightmode_png.png", + "players": [ + "B1ad3", + "bondik", + "markeloff", + "Shara", + "WorldEdit" + ] + }, + { + "team": "Team Liquid", + "teamLogo": "assets/tournament_logos/team_liquid_50px_team_liquid_allmode_png.png", + "players": [ + "nitr0", + "EliGE", + "Hiko", + "s1mple", + "adreN" + ] + }, + { + "team": "Counter Logic Gaming", + "teamLogo": "assets/tournament_logos/counter_logic_gaming_70px_counter_logic_gaming_allmode_png.png", + "players": [ + "hazed", + "jdm64", + "FugLy", + "tarik", + "reltuC" + ] + }, + { + "team": "Cloud9", + "teamLogo": "assets/tournament_logos/cloud9_70px_cloud9_allmode_png.png", + "players": [ + "n0thing", + "shroud", + "Skadoodle", + "fREAKAZOiD", + "Stewie2K" + ] + } + ], + "stageDates": [], + "playoffMatches": [ + { + "round": "Quarterfinals", + "team1": "Natus Vincere", + "team2": "Ninjas in Pyjamas", + "team1Logo": "assets/tournament_logos/natus_vincere_56px_natus_vincere_allmode_png.png", + "team2Logo": "assets/tournament_logos/ninjas_in_pyjamas_50px_ninjas_in_pyjamas_2012_allmode_png.png", + "score1": "2", + "score2": "0", + "date": "April 1, 2016 - 10:00 EDT" + }, + { + "round": "Quarterfinals", + "team1": "Astralis", + "team2": "Fnatic", + "team1Logo": "assets/tournament_logos/astralis_41px_astralis_logo_png.png", + "team2Logo": "assets/tournament_logos/fnatic_60px_fnatic_2015_allmode_png.png", + "score1": "2", + "score2": "0", + "date": "April 1, 2016 - 13:30 EDT" + }, + { + "round": "Semifinals", + "team1": "Natus Vincere", + "team2": "Astralis", + "team1Logo": "assets/tournament_logos/natus_vincere_56px_natus_vincere_allmode_png.png", + "team2Logo": "assets/tournament_logos/astralis_41px_astralis_logo_png.png", + "score1": "2", + "score2": "0", + "date": "April 2, 2016 - 15:15 EDT" + }, + { + "round": "Quarterfinals", + "team1": "Team Liquid", + "team2": "Counter Logic Gaming", + "team1Logo": "assets/tournament_logos/team_liquid_50px_team_liquid_allmode_png.png", + "team2Logo": "assets/tournament_logos/counter_logic_gaming_70px_counter_logic_gaming_allmode_png.png", + "score1": "2", + "score2": "0", + "date": "April 1, 2016 - 16:20 EDT" + }, + { + "round": "Quarterfinals", + "team1": "Luminosity Gaming", + "team2": "Virtus.pro", + "team1Logo": "assets/tournament_logos/luminosity_gaming_51px_luminosity_gaming_allmode_png.png", + "team2Logo": "assets/tournament_logos/virtus_pro_48px_virtus_pro_allmode_png.png", + "score1": "2", + "score2": "1", + "date": "April 2, 2016 - 10:30 EDT" + }, + { + "round": "Semifinals", + "team1": "Team Liquid", + "team2": "Luminosity Gaming", + "team1Logo": "assets/tournament_logos/team_liquid_50px_team_liquid_allmode_png.png", + "team2Logo": "assets/tournament_logos/luminosity_gaming_51px_luminosity_gaming_allmode_png.png", + "score1": "0", + "score2": "2", + "date": "April 2, 2016 - 18:15 EDT" + }, + { + "round": "Grand Final", + "team1": "Natus Vincere", + "team2": "Luminosity Gaming", + "team1Logo": "assets/tournament_logos/natus_vincere_56px_natus_vincere_allmode_png.png", + "team2Logo": "assets/tournament_logos/luminosity_gaming_51px_luminosity_gaming_allmode_png.png", + "score1": "0", + "score2": "2", + "date": "April 3, 2016 - 14:15 EDT" + } + ] + }, + { + "name": "PGL Antwerp 2022", + "winner": "FaZe Clan", + "tournamentLogo": "assets/tournament_logos/pgl_antwerp_2022.png", + "startDate": "2022-05-09", + "endDate": "2022-05-22", + "placements": [ + { + "place": "1st", + "team": "FaZe Clan", + "teamLogo": "assets/tournament_logos/faze_clan_75px_faze_clan_november_2021_lightmode_png.png" + }, + { + "place": "2nd", + "team": "Natus Vincere", + "teamLogo": "assets/tournament_logos/natus_vincere_57px_natus_vincere_2021_lightmode_png.png" + }, + { + "place": "3rd-4th", + "team": "Team Spirit", + "teamLogo": "assets/tournament_logos/team_spirit_43px_team_spirit_2022_lightmode_png.png" + }, + { + "place": "3rd-4th", + "team": "ENCE", + "teamLogo": "assets/tournament_logos/ence_50px_ence_2020_allmode_png.png" + }, + { + "place": "5th-8th", + "team": "FURIA Esports", + "teamLogo": "assets/tournament_logos/furia_esports_51px_furia_esports_allmode_png.png" + }, + { + "place": "5th-8th", + "team": "Ninjas in Pyjamas", + "teamLogo": "assets/tournament_logos/ninjas_in_pyjamas_50px_ninjas_in_pyjamas_2021_lightmode_png.png" + }, + { + "place": "5th-8th", + "team": "Copenhagen Flames", + "teamLogo": "assets/tournament_logos/copenhagen_flames_30px_copenhagen_flames_2018_lightmode_png.png" + }, + { + "place": "5th-8th", + "team": "Heroic", + "teamLogo": "assets/tournament_logos/heroic_100px_heroic_2019_lightmode_png.png" + }, + { + "place": "9th-11th", + "team": "G2 Esports", + "teamLogo": "assets/tournament_logos/g2_esports_43px_g2_esports_2020_lightmode_png.png" + }, + { + "place": "9th-11th", + "team": "Team Vitality", + "teamLogo": "assets/tournament_logos/team_vitality_40px_team_vitality_2021_allmode_png.png" + }, + { + "place": "9th-11th", + "team": "Imperial Esports", + "teamLogo": "assets/tournament_logos/imperial_esports_44px_imperial_esports_2022_allmode_png.png" + }, + { + "place": "12th-14th", + "team": "BIG", + "teamLogo": "assets/tournament_logos/big_35px_big_2020_lightmode_png.png" + }, + { + "place": "12th-14th", + "team": "Cloud9", + "teamLogo": "assets/tournament_logos/cloud9_70px_cloud9_allmode_png.png" + }, + { + "place": "12th-14th", + "team": "Outsiders", + "teamLogo": "assets/tournament_logos/outsiders_49px_outsiders_csgo_allmode_png.png" + }, + { + "place": "15th-16th", + "team": "Bad News Eagles", + "teamLogo": "assets/tournament_logos/bad_news_eagles_34px_bad_news_eagles_allmode_png.png" + }, + { + "place": "15th-16th", + "team": "Team Liquid", + "teamLogo": "assets/tournament_logos/team_liquid_44px_team_liquid_2020_lightmode_png.png" + }, + { + "place": "17th-19th", + "team": "forZe", + "teamLogo": "assets/tournament_logos/forze_42px_forze_2019_allmode_png.png" + }, + { + "place": "17th-19th", + "team": "Astralis", + "teamLogo": "assets/tournament_logos/astralis_41px_astralis_2020_allmode_png.png" + }, + { + "place": "17th-19th", + "team": "MIBR", + "teamLogo": "assets/tournament_logos/mibr_100px_mibr_2018_lightmode_png.png" + }, + { + "place": "20th-22nd", + "team": "Complexity Gaming", + "teamLogo": "assets/tournament_logos/complexity_gaming_53px_complexity_gaming_2019_lightmode_png.png" + }, + { + "place": "20th-22nd", + "team": "Eternal Fire", + "teamLogo": "assets/tournament_logos/eternal_fire_95px_eternal_fire_allmode_png.png" + }, + { + "place": "20th-22nd", + "team": "IHC Esports", + "teamLogo": "assets/tournament_logos/ihc_esports_42px_ihc_esports_allmode_png.png" + }, + { + "place": "23rd-24th", + "team": "9z Team", + "teamLogo": "assets/tournament_logos/9z_team_54px_9z_team_allmode_png.png" + }, + { + "place": "23rd-24th", + "team": "Renegades", + "teamLogo": "assets/tournament_logos/renegades_47px_renegades_2019_allmode_png.png" + } + ], + "teamRosters": [ + { + "team": "Heroic", + "teamLogo": "assets/tournament_logos/heroic_100px_heroic_2019_lightmode_png.png", + "players": [ + "stavn", + "cadiaN", + "TeSeS", + "refrezh", + "sjuush" + ] + }, + { + "team": "Copenhagen Flames", + "teamLogo": "assets/tournament_logos/copenhagen_flames_30px_copenhagen_flames_2018_lightmode_png.png", + "players": [ + "jabbi", + "nicoodoz", + "roeJ", + "HooXi", + "Zyphon" + ] + }, + { + "team": "BIG", + "teamLogo": "assets/tournament_logos/big_35px_big_2020_lightmode_png.png", + "players": [ + "tabseN", + "tiziaN", + "syrsoN", + "faveN", + "Krimbo" + ] + }, + { + "team": "Cloud9", + "teamLogo": "assets/tournament_logos/cloud9_70px_cloud9_allmode_png.png", + "players": [ + "nafany", + "sh1ro", + "interz", + "Ax1Le", + "Hobbit" + ] + }, + { + "team": "FURIA Esports", + "teamLogo": "assets/tournament_logos/furia_esports_51px_furia_esports_allmode_png.png", + "players": [ + "yuurih", + "arT", + "KSCERATO", + "drop", + "saffee" + ] + }, + { + "team": "FaZe Clan", + "teamLogo": "assets/tournament_logos/faze_clan_75px_faze_clan_november_2021_lightmode_png.png", + "players": [ + "rain", + "broky", + "Twistzz", + "karrigan", + "ropz" + ] + }, + { + "team": "Ninjas in Pyjamas", + "teamLogo": "assets/tournament_logos/ninjas_in_pyjamas_50px_ninjas_in_pyjamas_2021_lightmode_png.png", + "players": [ + "REZ", + "Plopski", + "hampus", + "es3tag", + "Brollan" + ] + }, + { + "team": "Natus Vincere", + "teamLogo": "assets/tournament_logos/natus_vincere_57px_natus_vincere_2021_lightmode_png.png", + "players": [ + "s1mple", + "electronic", + "Boombl4", + "Perfecto", + "b1t" + ] + }, + { + "team": "ENCE", + "teamLogo": "assets/tournament_logos/ence_50px_ence_2020_allmode_png.png", + "players": [ + "Snappi", + "Spinx", + "dycha", + "hades", + "maden" + ] + }, + { + "team": "G2 Esports", + "teamLogo": "assets/tournament_logos/g2_esports_43px_g2_esports_2020_lightmode_png.png", + "players": [ + "JaCkz", + "huNter", + "NiKo", + "m0NESY", + "Aleksib" + ] + }, + { + "team": "forZe", + "teamLogo": "assets/tournament_logos/forze_42px_forze_2019_allmode_png.png", + "players": [ + "Jerry", + "KENSi", + "zorte", + "Norwi", + "shalfey" + ] + }, + { + "team": "Astralis", + "teamLogo": "assets/tournament_logos/astralis_41px_astralis_2020_allmode_png.png", + "players": [ + "Xyp9x", + "gla1ve", + "k0nfig", + "blameF", + "Farlig" + ] + }, + { + "team": "Team Vitality", + "teamLogo": "assets/tournament_logos/team_vitality_40px_team_vitality_2021_allmode_png.png", + "players": [ + "apEX", + "ZywOo", + "misutaaa", + "dupreeh", + "Magisk" + ] + }, + { + "team": "MIBR", + "teamLogo": "assets/tournament_logos/mibr_100px_mibr_2018_lightmode_png.png", + "players": [ + "chelo", + "exit", + "WOOD7", + "Tuurtle", + "JOTA" + ] + }, + { + "team": "Imperial Esports", + "teamLogo": "assets/tournament_logos/imperial_esports_44px_imperial_esports_2022_allmode_png.png", + "players": [ + "FalleN", + "fer", + "fnx", + "boltz", + "VINI" + ] + }, + { + "team": "Bad News Eagles", + "teamLogo": "assets/tournament_logos/bad_news_eagles_34px_bad_news_eagles_allmode_png.png", + "players": [ + "SENER1", + "gxx-", + "juanflatroo", + "sinnopsyy", + "rigoN" + ] + }, + { + "team": "Eternal Fire", + "teamLogo": "assets/tournament_logos/eternal_fire_95px_eternal_fire_allmode_png.png", + "players": [ + "XANTARES", + "woxic", + "Calyx", + "imoRR", + "xfl0ud" + ] + }, + { + "team": "Team Spirit", + "teamLogo": "assets/tournament_logos/team_spirit_43px_team_spirit_2022_lightmode_png.png", + "players": [ + "chopper", + "magixx", + "degster", + "Patsi", + "S1ren" + ] + }, + { + "team": "Outsiders", + "teamLogo": "assets/tournament_logos/outsiders_49px_outsiders_csgo_allmode_png.png", + "players": [ + "buster", + "qikert", + "Jame", + "YEKINDAR", + "FL1T" + ] + }, + { + "team": "Complexity Gaming", + "teamLogo": "assets/tournament_logos/complexity_gaming_53px_complexity_gaming_2019_lightmode_png.png", + "players": [ + "JT", + "FaNg", + "floppy", + "Grim", + "junior" + ] + }, + { + "team": "IHC Esports", + "teamLogo": "assets/tournament_logos/ihc_esports_42px_ihc_esports_allmode_png.png", + "players": [ + "bLitz", + "Techno4K", + "kabal", + "nin9", + "sk0R" + ] + }, + { + "team": "Renegades", + "teamLogo": "assets/tournament_logos/renegades_47px_renegades_2019_allmode_png.png", + "players": [ + "sico", + "ins", + "hatz", + "aliStair", + "Liazz" + ] + }, + { + "team": "Team Liquid", + "teamLogo": "assets/tournament_logos/team_liquid_44px_team_liquid_2020_lightmode_png.png", + "players": [ + "EliGE", + "NAF", + "oSee", + "shox", + "nitr0" + ] + }, + { + "team": "9z Team", + "teamLogo": "assets/tournament_logos/9z_team_54px_9z_team_allmode_png.png", + "players": [ + "dgt", + "max", + "rox", + "luken", + "dav1d" + ] + } + ], + "stageDates": [ + { + "phase": "Challengers Stage", + "startDate": "2022-05-09", + "endDate": "2022-05-12" + }, + { + "phase": "Legends Stage", + "startDate": "2022-05-14", + "endDate": "2022-05-17" + }, + { + "phase": "Champions Stage", + "startDate": "2022-05-19", + "endDate": "2022-05-22" + } + ], + "playoffMatches": [ + { + "round": "Quarterfinals", + "team1": "Team Spirit", + "team2": "FURIA", + "team1Logo": "assets/tournament_logos/team_spirit_43px_team_spirit_2022_lightmode_png.png", + "team2Logo": "assets/tournament_logos/furia_51px_furia_esports_allmode_png.png", + "score1": "2", + "score2": "0", + "date": "May 19, 2022 - 21:05 CEST" + }, + { + "round": "Quarterfinals", + "team1": "FaZe Clan", + "team2": "Ninjas in Pyjamas", + "team1Logo": "assets/tournament_logos/faze_clan_75px_faze_clan_november_2021_lightmode_png.png", + "team2Logo": "assets/tournament_logos/ninjas_in_pyjamas_50px_ninjas_in_pyjamas_2021_lightmode_png.png", + "score1": "2", + "score2": "1", + "date": "May 19, 2022 - 16:30 CEST" + }, + { + "round": "Semifinals", + "team1": "Team Spirit", + "team2": "FaZe Clan", + "team1Logo": "assets/tournament_logos/team_spirit_43px_team_spirit_2022_lightmode_png.png", + "team2Logo": "assets/tournament_logos/faze_clan_75px_faze_clan_november_2021_lightmode_png.png", + "score1": "0", + "score2": "2", + "date": "May 21, 2022 - 16:30 CEST" + }, + { + "round": "Quarterfinals", + "team1": "ENCE", + "team2": "Copenhagen Flames", + "team1Logo": "assets/tournament_logos/ence_50px_ence_2020_allmode_png.png", + "team2Logo": "assets/tournament_logos/copenhagen_flames_30px_copenhagen_flames_2018_lightmode_png.png", + "score1": "2", + "score2": "0", + "date": "May 20, 2022 - 16:30 CEST" + }, + { + "round": "Quarterfinals", + "team1": "HEROIC", + "team2": "Natus Vincere", + "team1Logo": "assets/tournament_logos/heroic_100px_heroic_2019_lightmode_png.png", + "team2Logo": "assets/tournament_logos/natus_vincere_57px_natus_vincere_2021_lightmode_png.png", + "score1": "1", + "score2": "2", + "date": "May 20, 2022 - 19:30 CEST" + }, + { + "round": "Semifinals", + "team1": "ENCE", + "team2": "Natus Vincere", + "team1Logo": "assets/tournament_logos/ence_50px_ence_2020_allmode_png.png", + "team2Logo": "assets/tournament_logos/natus_vincere_57px_natus_vincere_2021_lightmode_png.png", + "score1": "0", + "score2": "2", + "date": "May 21, 2022 - 20:25 CEST" + }, + { + "round": "Grand Final", + "team1": "FaZe Clan", + "team2": "Natus Vincere", + "team1Logo": "assets/tournament_logos/faze_clan_75px_faze_clan_november_2021_lightmode_png.png", + "team2Logo": "assets/tournament_logos/natus_vincere_57px_natus_vincere_2021_lightmode_png.png", + "score1": "2", + "score2": "0", + "date": "May 22, 2022 - 20:00 CEST" + } + ] + }, + { + "name": "PGL Copenhagen 2024", + "winner": "Natus Vincere", + "tournamentLogo": "assets/tournament_logos/pgl_copenhagen_2024.png", + "startDate": "2024-03-17", + "endDate": "2024-03-31", + "placements": [ + { + "place": "1st", + "team": "Natus Vincere", + "teamLogo": "assets/tournament_logos/natus_vincere_57px_natus_vincere_2021_lightmode_png.png" + }, + { + "place": "2nd", + "team": "FaZe Clan", + "teamLogo": "assets/tournament_logos/faze_clan_75px_faze_clan_november_2021_lightmode_png.png" + }, + { + "place": "3rd-4th", + "team": "Team Vitality", + "teamLogo": "assets/tournament_logos/team_vitality_41px_team_vitality_2023_lightmode_png.png" + }, + { + "place": "3rd-4th", + "team": "G2 Esports", + "teamLogo": "assets/tournament_logos/g2_esports_43px_g2_esports_2020_lightmode_png.png" + }, + { + "place": "5th-8th", + "team": "Team Spirit", + "teamLogo": "assets/tournament_logos/team_spirit_43px_team_spirit_2022_lightmode_png.png" + }, + { + "place": "5th-8th", + "team": "Cloud9", + "teamLogo": "assets/tournament_logos/cloud9_73px_cloud9_2023_allmode_png.png" + }, + { + "place": "5th-8th", + "team": "Eternal Fire", + "teamLogo": "assets/tournament_logos/eternal_fire_95px_eternal_fire_2023_allmode_png.png" + }, + { + "place": "5th-8th", + "team": "MOUZ", + "teamLogo": "assets/tournament_logos/mouz_47px_mouz_2021_allmode_png.png" + }, + { + "place": "9th-11th", + "team": "Complexity Gaming", + "teamLogo": "assets/tournament_logos/complexity_gaming_53px_complexity_gaming_2019_lightmode_png.png" + }, + { + "place": "9th-11th", + "team": "Virtus.pro", + "teamLogo": "assets/tournament_logos/virtus_pro_51px_virtus_pro_2019_allmode_png.png" + }, + { + "place": "9th-11th", + "team": "paiN Gaming", + "teamLogo": "assets/tournament_logos/pain_gaming_75px_pain_gaming_2023_lightmode_png.png" + }, + { + "place": "12th-14th", + "team": "Imperial Esports", + "teamLogo": "assets/tournament_logos/imperial_esports_44px_imperial_esports_2022_allmode_png.png" + }, + { + "place": "12th-14th", + "team": "ECSTATIC", + "teamLogo": "assets/tournament_logos/ecstatic_50px_ecstatic_2023_allmode_png.png" + }, + { + "place": "12th-14th", + "team": "HEROIC", + "teamLogo": "assets/tournament_logos/heroic_57px_heroic_2023_allmode_png.png" + }, + { + "place": "15th-16th", + "team": "The MongolZ", + "teamLogo": "assets/tournament_logos/the_mongolz_39px_the_mongolz_2024_03_allmode_png.png" + }, + { + "place": "15th-16th", + "team": "FURIA Esports", + "teamLogo": "assets/tournament_logos/furia_esports_51px_furia_esports_allmode_png.png" + }, + { + "place": "17th-19th", + "team": "SAW", + "teamLogo": "assets/tournament_logos/saw_60px_saw_2021_allmode_png.png" + }, + { + "place": "17th-19th", + "team": "Legacy", + "teamLogo": "assets/tournament_logos/legacy_49px_legacy_allmode_png.png" + }, + { + "place": "17th-19th", + "team": "GamerLegion", + "teamLogo": "assets/tournament_logos/gamerlegion_91px_gamerlegion_cs_2023_allmode_png.png" + }, + { + "place": "20th-22nd", + "team": "Lynn Vision Gaming", + "teamLogo": "assets/tournament_logos/lynn_vision_gaming_56px_lynn_vision_gaming_2020_allmode_png.png" + }, + { + "place": "20th-22nd", + "team": "ENCE", + "teamLogo": "assets/tournament_logos/ence_50px_ence_2020_allmode_png.png" + }, + { + "place": "20th-22nd", + "team": "Apeks", + "teamLogo": "assets/tournament_logos/apeks_100px_apeks_2021_darkmode_png.png" + }, + { + "place": "23rd-24th", + "team": "AMKAL ESPORTS", + "teamLogo": "assets/tournament_logos/amkal_esports_49px_amkal_esports_lightmode_png.png" + }, + { + "place": "23rd-24th", + "team": "KOI", + "teamLogo": "assets/tournament_logos/koi_31px_koi_2024_blue_allmode_png.png" + } + ], + "teamRosters": [ + { + "team": "FaZe Clan", + "teamLogo": "assets/tournament_logos/faze_clan_75px_faze_clan_november_2021_lightmode_png.png", + "players": [ + "rain", + "broky", + "karrigan", + "ropz", + "frozen" + ] + }, + { + "team": "Team Spirit", + "teamLogo": "assets/tournament_logos/team_spirit_43px_team_spirit_2022_lightmode_png.png", + "players": [ + "chopper", + "magixx", + "zont1x", + "donk", + "sh1ro" + ] + }, + { + "team": "Team Vitality", + "teamLogo": "assets/tournament_logos/team_vitality_41px_team_vitality_2023_lightmode_png.png", + "players": [ + "apEX", + "ZywOo", + "Spinx", + "flameZ", + "mezii" + ] + }, + { + "team": "MOUZ", + "teamLogo": "assets/tournament_logos/mouz_47px_mouz_2021_allmode_png.png", + "players": [ + "torzsi", + "xertioN", + "siuhy", + "Jimpphat", + "Brollan" + ] + }, + { + "team": "Complexity Gaming", + "teamLogo": "assets/tournament_logos/complexity_gaming_53px_complexity_gaming_2019_lightmode_png.png", + "players": [ + "JT", + "floppy", + "Grim", + "hallzerk", + "EliGE" + ] + }, + { + "team": "Virtus.pro", + "teamLogo": "assets/tournament_logos/virtus_pro_51px_virtus_pro_2019_allmode_png.png", + "players": [ + "Jame", + "FL1T", + "fame", + "n0rb3r7", + "mir" + ] + }, + { + "team": "Natus Vincere", + "teamLogo": "assets/tournament_logos/natus_vincere_57px_natus_vincere_2021_lightmode_png.png", + "players": [ + "b1t", + "Aleksib", + "jL", + "iM", + "w0nderful" + ] + }, + { + "team": "G2 Esports", + "teamLogo": "assets/tournament_logos/g2_esports_43px_g2_esports_2020_lightmode_png.png", + "players": [ + "huNter-", + "NiKo", + "m0NESY", + "HooXi", + "nexa" + ] + }, + { + "team": "Cloud9", + "teamLogo": "assets/tournament_logos/cloud9_73px_cloud9_2023_allmode_png.png", + "players": [ + "Ax1Le", + "Hobbit", + "electronic", + "Perfecto", + "Boombl4" + ] + }, + { + "team": "Eternal Fire", + "teamLogo": "assets/tournament_logos/eternal_fire_95px_eternal_fire_2023_allmode_png.png", + "players": [ + "XANTARES", + "Calyx", + "MAJ3R", + "Wicadia", + "woxic" + ] + }, + { + "team": "ENCE", + "teamLogo": "assets/tournament_logos/ence_50px_ence_2020_allmode_png.png", + "players": [ + "dycha", + "gla1ve", + "Goofy", + "Kylar", + "hades" + ] + }, + { + "team": "Apeks", + "teamLogo": "assets/tournament_logos/apeks_100px_apeks_2021_darkmode_png.png", + "players": [ + "nawwk", + "jkaem", + "STYKO", + "CacaNito", + "sense" + ] + }, + { + "team": "HEROIC", + "teamLogo": "assets/tournament_logos/heroic_57px_heroic_2023_allmode_png.png", + "players": [ + "TeSeS", + "sjuush", + "NertZ", + "nicoodoz", + "kyxsan" + ] + }, + { + "team": "GamerLegion", + "teamLogo": "assets/tournament_logos/gamerlegion_91px_gamerlegion_cs_2023_allmode_png.png", + "players": [ + "isak", + "acoR", + "Keoz", + "volt", + "Snax" + ] + }, + { + "team": "SAW", + "teamLogo": "assets/tournament_logos/saw_60px_saw_2021_allmode_png.png", + "players": [ + "MUTiRiS", + "roman", + "ewjerkz", + "story", + "arrozdoce" + ] + }, + { + "team": "FURIA Esports", + "teamLogo": "assets/tournament_logos/furia_esports_51px_furia_esports_allmode_png.png", + "players": [ + "yuurih", + "arT", + "KSCERATO", + "FalleN", + "chelo" + ] + }, + { + "team": "ECSTATIC", + "teamLogo": "assets/tournament_logos/ecstatic_50px_ecstatic_2023_allmode_png.png", + "players": [ + "kraghen", + "Queenix", + "salazar", + "Nodios", + "Patti" + ] + }, + { + "team": "The MongolZ", + "teamLogo": "assets/tournament_logos/the_mongolz_39px_the_mongolz_2024_03_allmode_png.png", + "players": [ + "bLitz", + "Techno4K", + "910", + "mzinho", + "Senzu" + ] + }, + { + "team": "Imperial Esports", + "teamLogo": "assets/tournament_logos/imperial_esports_44px_imperial_esports_2022_allmode_png.png", + "players": [ + "VINI", + "HEN1", + "felps", + "noway", + "decenty" + ] + }, + { + "team": "paiN Gaming", + "teamLogo": "assets/tournament_logos/pain_gaming_75px_pain_gaming_2023_lightmode_png.png", + "players": [ + "biguzera", + "lux", + "kauez", + "nqz", + "n1ssim" + ] + }, + { + "team": "Lynn Vision Gaming", + "teamLogo": "assets/tournament_logos/lynn_vision_gaming_56px_lynn_vision_gaming_2020_allmode_png.png", + "players": [ + "westmelon", + "z4KR", + "Starry", + "EmiliaQAQ", + "Jee" + ] + }, + { + "team": "AMKAL ESPORTS", + "teamLogo": "assets/tournament_logos/amkal_esports_49px_amkal_esports_lightmode_png.png", + "players": [ + "TRAVIS", + "Forester", + "NickelBack", + "Krad", + "ICY" + ] + }, + { + "team": "KOI", + "teamLogo": "assets/tournament_logos/koi_31px_koi_2024_blue_allmode_png.png", + "players": [ + "mopoz", + "dav1g", + "JUST", + "adamS", + "stadodo" + ] + }, + { + "team": "Legacy", + "teamLogo": "assets/tournament_logos/legacy_49px_legacy_allmode_png.png", + "players": [ + "coldzera", + "latto", + "dumau", + "NEKIZ", + "b4rtiN" + ] + }, + { + "team": "9Pandas", + "players": [ + "seized", + "d1Ledez", + "clax", + "iDISBALANCE", + "glowiing" + ] + } + ], + "stageDates": [ + { + "phase": "Opening Stage", + "startDate": "2024-03-17", + "endDate": "2024-03-20" + }, + { + "phase": "Elimination Stage", + "startDate": "2024-03-21", + "endDate": "2024-03-24" + }, + { + "phase": "Playoff Stage", + "startDate": "2024-03-28", + "endDate": "2024-03-31" + } + ], + "playoffMatches": [ + { + "round": "Quarterfinals", + "team1": "Team Spirit", + "team2": "FaZe Clan", + "team1Logo": "assets/tournament_logos/team_spirit_43px_team_spirit_2022_lightmode_png.png", + "team2Logo": "assets/tournament_logos/faze_clan_75px_faze_clan_november_2021_lightmode_png.png", + "score1": "1", + "score2": "2", + "date": "March 28, 2024 - 19:55 CET" + }, + { + "round": "Quarterfinals", + "team1": "Team Vitality", + "team2": "Cloud9", + "team1Logo": "assets/tournament_logos/team_vitality_41px_team_vitality_2023_lightmode_png.png", + "team2Logo": "assets/tournament_logos/cloud9_73px_cloud9_2023_allmode_png.png", + "score1": "2", + "score2": "0", + "date": "March 28, 2024 - 17:00 CET" + }, + { + "round": "Semifinals", + "team1": "FaZe Clan", + "team2": "Team Vitality", + "team1Logo": "assets/tournament_logos/faze_clan_75px_faze_clan_november_2021_lightmode_png.png", + "team2Logo": "assets/tournament_logos/team_vitality_41px_team_vitality_2023_lightmode_png.png", + "score1": "2", + "score2": "1", + "date": "March 30, 2024 - 17:00 CET" + }, + { + "round": "Quarterfinals", + "team1": "Eternal Fire", + "team2": "Natus Vincere", + "team1Logo": "assets/tournament_logos/eternal_fire_95px_eternal_fire_2023_allmode_png.png", + "team2Logo": "assets/tournament_logos/natus_vincere_57px_natus_vincere_2021_lightmode_png.png", + "score1": "0", + "score2": "2", + "date": "March 29, 2024 - 17:00 CET" + }, + { + "round": "Quarterfinals", + "team1": "G2 Esports", + "team2": "MOUZ", + "team1Logo": "assets/tournament_logos/g2_esports_43px_g2_esports_2020_lightmode_png.png", + "team2Logo": "assets/tournament_logos/mouz_47px_mouz_2021_allmode_png.png", + "score1": "2", + "score2": "0", + "date": "March 29, 2024 - 20:20 CET" + }, + { + "round": "Semifinals", + "team1": "Natus Vincere", + "team2": "G2 Esports", + "team1Logo": "assets/tournament_logos/natus_vincere_57px_natus_vincere_2021_lightmode_png.png", + "team2Logo": "assets/tournament_logos/g2_esports_43px_g2_esports_2020_lightmode_png.png", + "score1": "2", + "score2": "1", + "date": "March 30, 2024 - 20:50 CET" + }, + { + "round": "Grand Final", + "team1": "FaZe Clan", + "team2": "Natus Vincere", + "team1Logo": "assets/tournament_logos/faze_clan_75px_faze_clan_november_2021_lightmode_png.png", + "team2Logo": "assets/tournament_logos/natus_vincere_57px_natus_vincere_2021_lightmode_png.png", + "score1": "1", + "score2": "2", + "date": "March 31, 2024 - 20:00 CEST" + } + ] + }, + { + "name": "PGL Kraków 2017", + "winner": "Gambit Esports", + "tournamentLogo": "assets/tournament_logos/pgl_krak_w_2017.png", + "startDate": "2017-07-16", + "endDate": "2017-07-23", + "placements": [ + { + "place": "1st", + "team": "Gambit Esports", + "teamLogo": "assets/tournament_logos/gambit_esports_46px_gambit_esports_allmode_png.png" + }, + { + "place": "2nd", + "team": "Immortals", + "teamLogo": "assets/tournament_logos/immortals_50px_immortals_allmode_png.png" + }, + { + "place": "3rd-4th", + "team": "Astralis", + "teamLogo": "assets/tournament_logos/astralis_41px_astralis_logo_png.png" + }, + { + "place": "3rd-4th", + "team": "Virtus.pro", + "teamLogo": "assets/tournament_logos/virtus_pro_48px_virtus_pro_allmode_png.png" + }, + { + "place": "5th-8th", + "team": "Fnatic", + "teamLogo": "assets/tournament_logos/fnatic_60px_fnatic_2015_allmode_png.png" + }, + { + "place": "5th-8th", + "team": "SK Gaming", + "teamLogo": "assets/tournament_logos/sk_gaming_50px_sk_gaming_2003_lightmode_png.png" + }, + { + "place": "5th-8th", + "team": "BIG", + "teamLogo": "assets/tournament_logos/big_35px_big_lightmode_png.png" + }, + { + "place": "5th-8th", + "team": "North", + "teamLogo": "assets/tournament_logos/north_48px_north_lightmode_png.png" + }, + { + "place": "9th-11th", + "team": "Cloud9", + "teamLogo": "assets/tournament_logos/cloud9_70px_cloud9_allmode_png.png" + }, + { + "place": "9th-11th", + "team": "FlipSid3 Tactics", + "teamLogo": "assets/tournament_logos/flipsid3_tactics_47px_flipsid3_tactics_lightmode_png.png" + }, + { + "place": "9th-11th", + "team": "G2 Esports", + "teamLogo": "assets/tournament_logos/g2_esports_43px_g2_esports_allmode_png.png" + }, + { + "place": "12th-14th", + "team": "Natus Vincere", + "teamLogo": "assets/tournament_logos/natus_vincere_56px_natus_vincere_allmode_png.png" + }, + { + "place": "12th-14th", + "team": "mousesports", + "teamLogo": "assets/tournament_logos/mousesports_92px_mousesports_2016_allmode_png.png" + }, + { + "place": "12th-14th", + "team": "PENTA Sports", + "teamLogo": "assets/tournament_logos/penta_sports_37px_penta_sports_lightmode_png.png" + }, + { + "place": "15th-16th", + "team": "FaZe Clan", + "teamLogo": "assets/tournament_logos/faze_clan_76px_faze_clan_2017_allmode_png.png" + }, + { + "place": "15th-16th", + "team": "Vega Squadron", + "teamLogo": "assets/tournament_logos/vega_squadron_47px_vega_squadron_2016_allmode_png.png" + } + ], + "teamRosters": [ + { + "team": "Astralis", + "teamLogo": "assets/tournament_logos/astralis_41px_astralis_logo_png.png", + "players": [ + "device", + "dupreeh", + "Xyp9x", + "Kjaerbye", + "gla1ve" + ] + }, + { + "team": "Virtus.pro", + "teamLogo": "assets/tournament_logos/virtus_pro_48px_virtus_pro_allmode_png.png", + "players": [ + "TaZ", + "NEO", + "pashaBiceps", + "Snax", + "byali" + ] + }, + { + "team": "Fnatic", + "teamLogo": "assets/tournament_logos/fnatic_60px_fnatic_2015_allmode_png.png", + "players": [ + "olofmeister", + "dennis", + "KRIMZ", + "JW", + "flusha" + ] + }, + { + "team": "SK Gaming", + "teamLogo": "assets/tournament_logos/sk_gaming_50px_sk_gaming_2003_lightmode_png.png", + "players": [ + "FalleN", + "fer", + "coldzera", + "TACO", + "felps" + ] + }, + { + "team": "Natus Vincere", + "teamLogo": "assets/tournament_logos/natus_vincere_56px_natus_vincere_allmode_png.png", + "players": [ + "seized", + "GuardiaN", + "Edward", + "flamie", + "s1mple" + ] + }, + { + "team": "Gambit Esports", + "teamLogo": "assets/tournament_logos/gambit_esports_46px_gambit_esports_allmode_png.png", + "players": [ + "Dosia", + "AdreN", + "mou", + "Zeus", + "HObbit" + ] + }, + { + "team": "North", + "teamLogo": "assets/tournament_logos/north_48px_north_lightmode_png.png", + "players": [ + "MSL", + "k0nfig", + "cajunb", + "Magisk", + "aizy" + ] + }, + { + "team": "FaZe Clan", + "teamLogo": "assets/tournament_logos/faze_clan_76px_faze_clan_2017_allmode_png.png", + "players": [ + "rain", + "allu", + "karrigan", + "kioShiMa", + "NiKo" + ] + }, + { + "team": "mousesports", + "teamLogo": "assets/tournament_logos/mousesports_92px_mousesports_2016_allmode_png.png", + "players": [ + "chrisJ", + "denis", + "oskar", + "loWel", + "ropz" + ] + }, + { + "team": "G2 Esports", + "teamLogo": "assets/tournament_logos/g2_esports_43px_g2_esports_allmode_png.png", + "players": [ + "shox", + "bodyy", + "NBK-", + "kennyS", + "apEX" + ] + }, + { + "team": "BIG", + "teamLogo": "assets/tournament_logos/big_35px_big_lightmode_png.png", + "players": [ + "gob b", + "LEGIJA", + "tabseN", + "nex", + "keev" + ] + }, + { + "team": "Cloud9", + "teamLogo": "assets/tournament_logos/cloud9_70px_cloud9_allmode_png.png", + "players": [ + "n0thing", + "shroud", + "Skadoodle", + "Stewie2K", + "autimatic" + ] + }, + { + "team": "PENTA Sports", + "teamLogo": "assets/tournament_logos/penta_sports_37px_penta_sports_lightmode_png.png", + "players": [ + "kRYSTAL", + "suNny", + "HS", + "innocent", + "zehN" + ] + }, + { + "team": "Immortals", + "teamLogo": "assets/tournament_logos/immortals_50px_immortals_allmode_png.png", + "players": [ + "HEN1", + "LUCAS1", + "boltz", + "steel", + "kNgV-" + ] + }, + { + "team": "Vega Squadron", + "teamLogo": "assets/tournament_logos/vega_squadron_47px_vega_squadron_2016_allmode_png.png", + "players": [ + "mir", + "chopper", + "jR", + "keshandr", + "hutji" + ] + }, + { + "team": "FlipSid3 Tactics", + "teamLogo": "assets/tournament_logos/flipsid3_tactics_47px_flipsid3_tactics_lightmode_png.png", + "players": [ + "B1ad3", + "WorldEdit", + "markeloff", + "wayLander", + "electronic" + ] + } + ], + "stageDates": [], + "playoffMatches": [ + { + "round": "Quarterfinals", + "team1": "Gambit Esports", + "team2": "Fnatic", + "team1Logo": "assets/tournament_logos/gambit_esports_46px_gambit_esports_allmode_png.png", + "team2Logo": "assets/tournament_logos/fnatic_60px_fnatic_2015_allmode_png.png", + "score1": "2", + "score2": "0", + "date": "July 21, 2017 - 13:00 CEST" + }, + { + "round": "Quarterfinals", + "team1": "SK Gaming", + "team2": "Astralis", + "team1Logo": "assets/tournament_logos/sk_gaming_50px_sk_gaming_2003_lightmode_png.png", + "team2Logo": "assets/tournament_logos/astralis_41px_astralis_logo_png.png", + "score1": "0", + "score2": "2", + "date": "July 21, 2017 - 17:15 CEST" + }, + { + "round": "Semifinals", + "team1": "Gambit Esports", + "team2": "Astralis", + "team1Logo": "assets/tournament_logos/gambit_esports_46px_gambit_esports_allmode_png.png", + "team2Logo": "assets/tournament_logos/astralis_41px_astralis_logo_png.png", + "score1": "2", + "score2": "1", + "date": "July 22, 2017 - 15:30 CEST" + }, + { + "round": "Quarterfinals", + "team1": "BIG", + "team2": "Immortals", + "team1Logo": "assets/tournament_logos/big_35px_big_lightmode_png.png", + "team2Logo": "assets/tournament_logos/immortals_50px_immortals_allmode_png.png", + "score1": "1", + "score2": "2", + "date": "July 21, 2017 - 20:25 CEST" + }, + { + "round": "Quarterfinals", + "team1": "North", + "team2": "Virtus.pro", + "team1Logo": "assets/tournament_logos/north_48px_north_lightmode_png.png", + "team2Logo": "assets/tournament_logos/virtus_pro_48px_virtus_pro_allmode_png.png", + "score1": "0", + "score2": "2", + "date": "July 22, 2017 - 13:00 CEST" + }, + { + "round": "Semifinals", + "team1": "Immortals", + "team2": "Virtus.pro", + "team1Logo": "assets/tournament_logos/immortals_50px_immortals_allmode_png.png", + "team2Logo": "assets/tournament_logos/virtus_pro_48px_virtus_pro_allmode_png.png", + "score1": "2", + "score2": "0", + "date": "July 22, 2017 - 19:45 CEST" + }, + { + "round": "Grand Final", + "team1": "Gambit Esports", + "team2": "Immortals", + "team1Logo": "assets/tournament_logos/gambit_esports_46px_gambit_esports_allmode_png.png", + "team2Logo": "assets/tournament_logos/immortals_50px_immortals_allmode_png.png", + "score1": "2", + "score2": "1", + "date": "July 23, 2017 - 17:00 CEST" + } + ] + }, + { + "name": "PGL Stockholm 2021", + "winner": "Natus Vincere", + "tournamentLogo": "assets/tournament_logos/pgl_stockholm_2021.png", + "startDate": "2021-10-26", + "endDate": "2021-11-07", + "placements": [ + { + "place": "1st", + "team": "Natus Vincere", + "teamLogo": "assets/tournament_logos/natus_vincere_57px_natus_vincere_2021_lightmode_png.png" + }, + { + "place": "2nd", + "team": "G2 Esports", + "teamLogo": "assets/tournament_logos/g2_esports_43px_g2_esports_2020_lightmode_png.png" + }, + { + "place": "3rd-4th", + "team": "Gambit Esports", + "teamLogo": "assets/tournament_logos/gambit_esports_44px_gambit_esports_sep_2020_allmode_png.png" + }, + { + "place": "3rd-4th", + "team": "Heroic", + "teamLogo": "assets/tournament_logos/heroic_100px_heroic_2019_lightmode_png.png" + }, + { + "place": "5th-8th", + "team": "Team Vitality", + "teamLogo": "assets/tournament_logos/team_vitality_40px_team_vitality_2021_allmode_png.png" + }, + { + "place": "5th-8th", + "team": "FURIA Esports", + "teamLogo": "assets/tournament_logos/furia_esports_51px_furia_esports_allmode_png.png" + }, + { + "place": "5th-8th", + "team": "Virtus.pro", + "teamLogo": "assets/tournament_logos/virtus_pro_51px_virtus_pro_2019_allmode_png.png" + }, + { + "place": "5th-8th", + "team": "Ninjas in Pyjamas", + "teamLogo": "assets/tournament_logos/ninjas_in_pyjamas_50px_ninjas_in_pyjamas_2021_lightmode_png.png" + }, + { + "place": "9th-11th", + "team": "Entropiq", + "teamLogo": "assets/tournament_logos/entropiq_38px_entropiq_allmode_png.png" + }, + { + "place": "9th-11th", + "team": "Copenhagen Flames", + "teamLogo": "assets/tournament_logos/copenhagen_flames_30px_copenhagen_flames_2018_lightmode_png.png" + }, + { + "place": "9th-11th", + "team": "FaZe Clan", + "teamLogo": "assets/tournament_logos/faze_clan_76px_faze_clan_2021_lightmode_png.png" + }, + { + "place": "12th-14th", + "team": "Astralis", + "teamLogo": "assets/tournament_logos/astralis_41px_astralis_2020_allmode_png.png" + }, + { + "place": "12th-14th", + "team": "MOUZ", + "teamLogo": "assets/tournament_logos/mouz_47px_mouz_2021_allmode_png.png" + }, + { + "place": "12th-14th", + "team": "Team Liquid", + "teamLogo": "assets/tournament_logos/team_liquid_44px_team_liquid_2020_lightmode_png.png" + }, + { + "place": "15th-16th", + "team": "Evil Geniuses", + "teamLogo": "assets/tournament_logos/evil_geniuses_37px_evil_geniuses_2020_lightmode_png.png" + }, + { + "place": "15th-16th", + "team": "ENCE", + "teamLogo": "assets/tournament_logos/ence_50px_ence_2020_allmode_png.png" + }, + { + "place": "17th-19th", + "team": "BIG", + "teamLogo": "assets/tournament_logos/big_35px_big_2020_lightmode_png.png" + }, + { + "place": "17th-19th", + "team": "Team Spirit", + "teamLogo": "assets/tournament_logos/team_spirit_43px_team_spirit_2021_lightmode_png.png" + }, + { + "place": "17th-19th", + "team": "Movistar Riders", + "teamLogo": "assets/tournament_logos/movistar_riders_69px_movistar_riders_allmode_png.png" + }, + { + "place": "20th-22nd", + "team": "paiN Gaming", + "teamLogo": "assets/tournament_logos/pain_gaming_75px_pain_gaming_2017_lightmode_png.png" + }, + { + "place": "20th-22nd", + "team": "Renegades", + "teamLogo": "assets/tournament_logos/renegades_47px_renegades_2019_allmode_png.png" + }, + { + "place": "20th-22nd", + "team": "TYLOO", + "teamLogo": "assets/tournament_logos/tyloo_56px_tyloo_2016_allmode_png.png" + }, + { + "place": "23rd-24th", + "team": "GODSENT", + "teamLogo": "assets/tournament_logos/godsent_58px_godsent_lightmode_png.png" + }, + { + "place": "23rd-24th", + "team": "Sharks Esports", + "teamLogo": "assets/tournament_logos/sharks_esports_54px_sharks_esports_png.png" + } + ], + "teamRosters": [ + { + "team": "Ninjas in Pyjamas", + "teamLogo": "assets/tournament_logos/ninjas_in_pyjamas_50px_ninjas_in_pyjamas_2021_lightmode_png.png", + "players": [ + "REZ", + "Plopski", + "hampus", + "device", + "LNZ" + ] + }, + { + "team": "Team Vitality", + "teamLogo": "assets/tournament_logos/team_vitality_40px_team_vitality_2021_allmode_png.png", + "players": [ + "apEX", + "ZywOo", + "shox", + "misutaaa", + "Kyojin" + ] + }, + { + "team": "G2 Esports", + "teamLogo": "assets/tournament_logos/g2_esports_43px_g2_esports_2020_lightmode_png.png", + "players": [ + "JACKZ", + "AMANEK", + "nexa", + "huNter-", + "NiKo" + ] + }, + { + "team": "FURIA Esports", + "teamLogo": "assets/tournament_logos/furia_esports_51px_furia_esports_allmode_png.png", + "players": [ + "yuurih", + "arT", + "VINI", + "KSCERATO", + "drop" + ] + }, + { + "team": "Team Liquid", + "teamLogo": "assets/tournament_logos/team_liquid_44px_team_liquid_2020_lightmode_png.png", + "players": [ + "EliGE", + "NAF", + "Stewie2K", + "Grim", + "FalleN" + ] + }, + { + "team": "Evil Geniuses", + "teamLogo": "assets/tournament_logos/evil_geniuses_37px_evil_geniuses_2020_lightmode_png.png", + "players": [ + "Brehze", + "CeRq", + "stanislaw", + "oBo", + "MICHU" + ] + }, + { + "team": "Natus Vincere", + "teamLogo": "assets/tournament_logos/natus_vincere_57px_natus_vincere_2021_lightmode_png.png", + "players": [ + "s1mple", + "electroNic", + "Boombl4", + "Perfecto", + "b1t" + ] + }, + { + "team": "Gambit Esports", + "teamLogo": "assets/tournament_logos/gambit_esports_44px_gambit_esports_sep_2020_allmode_png.png", + "players": [ + "nafany", + "sh1ro", + "interz", + "Ax1Le", + "HObbit" + ] + }, + { + "team": "Astralis", + "teamLogo": "assets/tournament_logos/astralis_41px_astralis_2020_allmode_png.png", + "players": [ + "dupreeh", + "Xyp9x", + "gla1ve", + "Magisk", + "Lucky" + ] + }, + { + "team": "ENCE", + "teamLogo": "assets/tournament_logos/ence_50px_ence_2020_allmode_png.png", + "players": [ + "doto", + "Snappi", + "Spinx", + "dycha", + "hades" + ] + }, + { + "team": "BIG", + "teamLogo": "assets/tournament_logos/big_35px_big_2020_lightmode_png.png", + "players": [ + "tabseN", + "tiziaN", + "syrsoN", + "k1to", + "gade" + ] + }, + { + "team": "Movistar Riders", + "teamLogo": "assets/tournament_logos/movistar_riders_69px_movistar_riders_allmode_png.png", + "players": [ + "mopoz", + "ALEX", + "DeathZz", + "SunPayus", + "dav1g" + ] + }, + { + "team": "Heroic", + "teamLogo": "assets/tournament_logos/heroic_100px_heroic_2019_lightmode_png.png", + "players": [ + "stavn", + "cadiaN", + "TeSeS", + "refrezh", + "sjuush" + ] + }, + { + "team": "MOUZ", + "teamLogo": "assets/tournament_logos/mouz_47px_mouz_2021_allmode_png.png", + "players": [ + "ropz", + "frozen", + "Bymas", + "acoR", + "dexter" + ] + }, + { + "team": "paiN Gaming", + "teamLogo": "assets/tournament_logos/pain_gaming_75px_pain_gaming_2017_lightmode_png.png", + "players": [ + "PKL", + "biguzera", + "hardzao", + "NEKIZ", + "saffee" + ] + }, + { + "team": "Team Spirit", + "teamLogo": "assets/tournament_logos/team_spirit_43px_team_spirit_2021_lightmode_png.png", + "players": [ + "sdy", + "chopper", + "mir", + "magixx", + "degster" + ] + }, + { + "team": "Copenhagen Flames", + "teamLogo": "assets/tournament_logos/copenhagen_flames_30px_copenhagen_flames_2018_lightmode_png.png", + "players": [ + "Jabbi", + "nicoodoz", + "roeJ", + "HooXi", + "Zyphon" + ] + }, + { + "team": "FaZe Clan", + "teamLogo": "assets/tournament_logos/faze_clan_76px_faze_clan_2021_lightmode_png.png", + "players": [ + "rain", + "olofmeister", + "broky", + "Twistzz", + "karrigan" + ] + }, + { + "team": "GODSENT", + "teamLogo": "assets/tournament_logos/godsent_58px_godsent_lightmode_png.png", + "players": [ + "TACO", + "felps", + "latto", + "b4rtiN", + "dumau" + ] + }, + { + "team": "Entropiq", + "teamLogo": "assets/tournament_logos/entropiq_38px_entropiq_allmode_png.png", + "players": [ + "El1an", + "Lack1", + "NickelBack", + "Krad", + "Forester" + ] + }, + { + "team": "Virtus.pro", + "teamLogo": "assets/tournament_logos/virtus_pro_51px_virtus_pro_2019_allmode_png.png", + "players": [ + "buster", + "Qikert", + "Jame", + "YEKINDAR", + "FL1T" + ] + }, + { + "team": "Sharks Esports", + "teamLogo": "assets/tournament_logos/sharks_esports_54px_sharks_esports_png.png", + "players": [ + "jnt", + "pancc", + "Lucaozy", + "realziN", + "zevy" + ] + }, + { + "team": "TYLOO", + "teamLogo": "assets/tournament_logos/tyloo_56px_tyloo_2016_allmode_png.png", + "players": [ + "somebody", + "Summer", + "Attacker", + "SLOWLY", + "DANK1NG" + ] + }, + { + "team": "Renegades", + "teamLogo": "assets/tournament_logos/renegades_47px_renegades_2019_allmode_png.png", + "players": [ + "malta", + "Sico", + "INS", + "Hatz", + "aliStair" + ] + } + ], + "stageDates": [ + { + "phase": "Challengers Stage", + "startDate": "2021-10-26", + "endDate": "2021-10-29" + }, + { + "phase": "Legends Stage", + "startDate": "2021-10-30", + "endDate": "2021-11-02" + }, + { + "phase": "Champions Stage", + "startDate": "2021-11-04", + "endDate": "2021-11-07" + } + ], + "playoffMatches": [ + { + "round": "Quarterfinals", + "team1": "Natus Vincere", + "team2": "Team Vitality", + "team1Logo": "assets/tournament_logos/natus_vincere_57px_natus_vincere_2021_lightmode_png.png", + "team2Logo": "assets/tournament_logos/team_vitality_40px_team_vitality_2021_allmode_png.png", + "score1": "2", + "score2": "0", + "date": "November 5, 2021 - 20:05 CET" + }, + { + "round": "Quarterfinals", + "team1": "FURIA", + "team2": "Gambit Esports", + "team1Logo": "assets/tournament_logos/furia_51px_furia_esports_allmode_png.png", + "team2Logo": "assets/tournament_logos/gambit_esports_44px_gambit_esports_sep_2020_allmode_png.png", + "score1": "0", + "score2": "2", + "date": "November 5, 2021 - 16:30 CET" + }, + { + "round": "Semifinals", + "team1": "Natus Vincere", + "team2": "Gambit Esports", + "team1Logo": "assets/tournament_logos/natus_vincere_57px_natus_vincere_2021_lightmode_png.png", + "team2Logo": "assets/tournament_logos/gambit_esports_44px_gambit_esports_sep_2020_allmode_png.png", + "score1": "2", + "score2": "0", + "date": "November 6, 2021 - 21:10 CET" + }, + { + "round": "Quarterfinals", + "team1": "HEROIC", + "team2": "Virtus.pro", + "team1Logo": "assets/tournament_logos/heroic_100px_heroic_2019_lightmode_png.png", + "team2Logo": "assets/tournament_logos/virtus_pro_51px_virtus_pro_2019_allmode_png.png", + "score1": "2", + "score2": "1", + "date": "November 4, 2021 - 16:30 CET" + }, + { + "round": "Quarterfinals", + "team1": "Ninjas in Pyjamas", + "team2": "G2 Esports", + "team1Logo": "assets/tournament_logos/ninjas_in_pyjamas_50px_ninjas_in_pyjamas_2021_lightmode_png.png", + "team2Logo": "assets/tournament_logos/g2_esports_43px_g2_esports_2020_lightmode_png.png", + "score1": "0", + "score2": "2", + "date": "November 4, 2021 - 21:00 CET" + }, + { + "round": "Semifinals", + "team1": "HEROIC", + "team2": "G2 Esports", + "team1Logo": "assets/tournament_logos/heroic_100px_heroic_2019_lightmode_png.png", + "team2Logo": "assets/tournament_logos/g2_esports_43px_g2_esports_2020_lightmode_png.png", + "score1": "1", + "score2": "2", + "date": "November 6, 2021 - 16:30 CET" + }, + { + "round": "Grand Final", + "team1": "Natus Vincere", + "team2": "G2 Esports", + "team1Logo": "assets/tournament_logos/natus_vincere_57px_natus_vincere_2021_lightmode_png.png", + "team2Logo": "assets/tournament_logos/g2_esports_43px_g2_esports_2020_lightmode_png.png", + "score1": "2", + "score2": "0", + "date": "November 7, 2021 - 20:00 CET" + } + ] + }, + { + "name": "Perfect World Shanghai 2024", + "winner": "Team Spirit", + "tournamentLogo": "assets/tournament_logos/perfect_world_shanghai_2024.png", + "startDate": "2024-11-30", + "endDate": "2024-12-15", + "placements": [ + { + "place": "1st", + "team": "Team Spirit", + "teamLogo": "assets/tournament_logos/team_spirit_43px_team_spirit_2022_lightmode_png.png" + }, + { + "place": "2nd", + "team": "FaZe Clan", + "teamLogo": "assets/tournament_logos/faze_clan_75px_faze_clan_2024_allmode_png.png" + }, + { + "place": "3rd-4th", + "team": "MOUZ", + "teamLogo": "assets/tournament_logos/mouz_47px_mouz_2021_allmode_png.png" + }, + { + "place": "3rd-4th", + "team": "G2 Esports", + "teamLogo": "assets/tournament_logos/g2_esports_43px_g2_esports_2020_lightmode_png.png" + }, + { + "place": "5th-8th", + "team": "The MongolZ", + "teamLogo": "assets/tournament_logos/the_mongolz_39px_the_mongolz_2024_03_allmode_png.png" + }, + { + "place": "5th-8th", + "team": "Team Liquid", + "teamLogo": "assets/tournament_logos/team_liquid_44px_team_liquid_2024_lightmode_png.png" + }, + { + "place": "5th-8th", + "team": "HEROIC", + "teamLogo": "assets/tournament_logos/heroic_57px_heroic_2024_allmode_png.png" + }, + { + "place": "5th-8th", + "team": "Team Vitality", + "teamLogo": "assets/tournament_logos/team_vitality_41px_team_vitality_2023_lightmode_png.png" + }, + { + "place": "9th-11th", + "team": "FURIA Esports", + "teamLogo": "assets/tournament_logos/furia_esports_51px_furia_esports_allmode_png.png" + }, + { + "place": "9th-11th", + "team": "MIBR", + "teamLogo": "assets/tournament_logos/mibr_100px_mibr_2018_lightmode_png.png" + }, + { + "place": "9th-11th", + "team": "Natus Vincere", + "teamLogo": "assets/tournament_logos/natus_vincere_57px_natus_vincere_2021_lightmode_png.png" + }, + { + "place": "12th-14th", + "team": "GamerLegion", + "teamLogo": "assets/tournament_logos/gamerlegion_91px_gamerlegion_cs_2023_allmode_png.png" + }, + { + "place": "12th-14th", + "team": "3DMAX", + "teamLogo": "assets/tournament_logos/3dmax_36px_3dmax_2024_allmode_png.png" + }, + { + "place": "12th-14th", + "team": "paiN Gaming", + "teamLogo": "assets/tournament_logos/pain_gaming_75px_pain_gaming_2023_lightmode_png.png" + }, + { + "place": "15th-16th", + "team": "Wildcard", + "teamLogo": "assets/tournament_logos/wildcard_42px_wildcard_2024_lightmode_png.png" + }, + { + "place": "15th-16th", + "team": "BIG", + "teamLogo": "assets/tournament_logos/big_35px_big_2020_lightmode_png.png" + }, + { + "place": "17th-19th", + "team": "FlyQuest", + "teamLogo": "assets/tournament_logos/flyquest_51px_flyquest_2021_allmode_png.png" + }, + { + "place": "17th-19th", + "team": "Passion UA", + "teamLogo": "assets/tournament_logos/passion_ua_27px_passion_ua_2024_allmode_png.png" + }, + { + "place": "17th-19th", + "team": "Complexity Gaming", + "teamLogo": "assets/tournament_logos/complexity_gaming_53px_complexity_gaming_2019_lightmode_png.png" + }, + { + "place": "20th-22nd", + "team": "Virtus.pro", + "teamLogo": "assets/tournament_logos/virtus_pro_51px_virtus_pro_2019_allmode_png.png" + }, + { + "place": "20th-22nd", + "team": "Cloud9", + "teamLogo": "assets/tournament_logos/cloud9_73px_cloud9_2023_allmode_png.png" + }, + { + "place": "20th-22nd", + "team": "Rare Atom", + "teamLogo": "assets/tournament_logos/rare_atom_55px_rare_atom_2024_allmode_png.png" + }, + { + "place": "23rd-24th", + "team": "Imperial Esports", + "teamLogo": "assets/tournament_logos/imperial_esports_44px_imperial_esports_2022_allmode_png.png" + }, + { + "place": "23rd-24th", + "team": "Fnatic", + "teamLogo": "assets/tournament_logos/fnatic_77px_fnatic_2020_allmode_png.png" + } + ], + "teamRosters": [ + { + "team": "G2 Esports", + "teamLogo": "assets/tournament_logos/g2_esports_43px_g2_esports_2020_lightmode_png.png", + "players": [ + "huNter-", + "NiKo", + "m0NESY", + "malbsMd", + "Snax" + ] + }, + { + "team": "Natus Vincere", + "teamLogo": "assets/tournament_logos/natus_vincere_57px_natus_vincere_2021_lightmode_png.png", + "players": [ + "b1t", + "Aleksib", + "jL", + "iM", + "w0nderful" + ] + }, + { + "team": "Team Vitality", + "teamLogo": "assets/tournament_logos/team_vitality_41px_team_vitality_2023_lightmode_png.png", + "players": [ + "apEX", + "ZywOo", + "Spinx", + "flameZ", + "mezii" + ] + }, + { + "team": "Team Spirit", + "teamLogo": "assets/tournament_logos/team_spirit_43px_team_spirit_2022_lightmode_png.png", + "players": [ + "chopper", + "magixx", + "zont1x", + "donk", + "sh1ro" + ] + }, + { + "team": "MOUZ", + "teamLogo": "assets/tournament_logos/mouz_47px_mouz_2021_allmode_png.png", + "players": [ + "torzsi", + "xertioN", + "siuhy", + "Jimpphat", + "Brollan" + ] + }, + { + "team": "FaZe Clan", + "teamLogo": "assets/tournament_logos/faze_clan_75px_faze_clan_2024_allmode_png.png", + "players": [ + "rain", + "broky", + "karrigan", + "ropz", + "frozen" + ] + }, + { + "team": "HEROIC", + "teamLogo": "assets/tournament_logos/heroic_57px_heroic_2024_allmode_png.png", + "players": [ + "TeSeS", + "sjuush", + "NertZ", + "kyxsan", + "degster" + ] + }, + { + "team": "3DMAX", + "teamLogo": "assets/tournament_logos/3dmax_36px_3dmax_2024_allmode_png.png", + "players": [ + "Lucky", + "Djoko", + "Ex3rcice", + "Maka", + "Graviti" + ] + }, + { + "team": "FURIA Esports", + "teamLogo": "assets/tournament_logos/furia_esports_51px_furia_esports_allmode_png.png", + "players": [ + "yuurih", + "KSCERATO", + "FalleN", + "chelo", + "skullz" + ] + }, + { + "team": "Virtus.pro", + "teamLogo": "assets/tournament_logos/virtus_pro_51px_virtus_pro_2019_allmode_png.png", + "players": [ + "Jame", + "FL1T", + "fame", + "n0rb3r7", + "electroNic" + ] + }, + { + "team": "Team Liquid", + "teamLogo": "assets/tournament_logos/team_liquid_44px_team_liquid_2024_lightmode_png.png", + "players": [ + "NAF", + "YEKINDAR", + "Twistzz", + "ultimate", + "jks" + ] + }, + { + "team": "Complexity Gaming", + "teamLogo": "assets/tournament_logos/complexity_gaming_53px_complexity_gaming_2019_lightmode_png.png", + "players": [ + "JT", + "floppy", + "Grim", + "hallzerk", + "EliGE" + ] + }, + { + "team": "BIG", + "teamLogo": "assets/tournament_logos/big_35px_big_2020_lightmode_png.png", + "players": [ + "tabseN", + "Krimbo", + "syrsoN", + "JDC", + "rigoN" + ] + }, + { + "team": "Fnatic", + "teamLogo": "assets/tournament_logos/fnatic_77px_fnatic_2020_allmode_png.png", + "players": [ + "KRIMZ", + "afro", + "bodyy", + "matys", + "blameF" + ] + }, + { + "team": "The MongolZ", + "teamLogo": "assets/tournament_logos/the_mongolz_39px_the_mongolz_2024_03_allmode_png.png", + "players": [ + "bLitz", + "Techno4K", + "910", + "mzinho", + "Senzu" + ] + }, + { + "team": "paiN Gaming", + "teamLogo": "assets/tournament_logos/pain_gaming_75px_pain_gaming_2023_lightmode_png.png", + "players": [ + "biguzera", + "lux", + "kauez", + "nqz", + "snow" + ] + }, + { + "team": "GamerLegion", + "teamLogo": "assets/tournament_logos/gamerlegion_91px_gamerlegion_cs_2023_allmode_png.png", + "players": [ + "volt", + "sl3nd", + "aNdu", + "FL4MUS", + "ztr" + ] + }, + { + "team": "MIBR", + "teamLogo": "assets/tournament_logos/mibr_100px_mibr_2018_lightmode_png.png", + "players": [ + "exit", + "brnz4n", + "insani", + "drop", + "saffee" + ] + }, + { + "team": "Cloud9", + "teamLogo": "assets/tournament_logos/cloud9_73px_cloud9_2023_allmode_png.png", + "players": [ + "Ax1Le", + "Boombl4", + "Perfecto", + "HeavyGod", + "ICY" + ] + }, + { + "team": "FlyQuest", + "teamLogo": "assets/tournament_logos/flyquest_51px_flyquest_2021_allmode_png.png", + "players": [ + "INS", + "aliStair", + "Liazz", + "Vexite", + "dexter" + ] + }, + { + "team": "Passion UA", + "teamLogo": "assets/tournament_logos/passion_ua_27px_passion_ua_2024_allmode_png.png", + "players": [ + "jambo", + "jackasmo", + "zeRRoFIX", + "s-chilla", + "fear" + ] + }, + { + "team": "Wildcard", + "teamLogo": "assets/tournament_logos/wildcard_42px_wildcard_2024_lightmode_png.png", + "players": [ + "stanislaw", + "JBa", + "Sonic", + "susp", + "phzy" + ] + }, + { + "team": "Rare Atom", + "teamLogo": "assets/tournament_logos/rare_atom_55px_rare_atom_2024_allmode_png.png", + "players": [ + "somebody", + "Summer", + "L1haNg", + "ChildKing", + "kaze" + ] + }, + { + "team": "Imperial Esports", + "teamLogo": "assets/tournament_logos/imperial_esports_44px_imperial_esports_2022_allmode_png.png", + "players": [ + "VINI", + "felps", + "noway", + "decenty", + "try" + ] + } + ], + "stageDates": [ + { + "phase": "Opening Stage", + "startDate": "2024-11-30", + "endDate": "2024-12-03" + }, + { + "phase": "Elimination Stage", + "startDate": "2024-12-05", + "endDate": "2024-12-08" + }, + { + "phase": "Playoff Stage", + "startDate": "2024-12-12", + "endDate": "2024-12-15" + } + ], + "playoffMatches": [ + { + "round": "Quarterfinals", + "team1": "The MongolZ", + "team2": "MOUZ", + "team1Logo": "assets/tournament_logos/the_mongolz_39px_the_mongolz_2024_03_allmode_png.png", + "team2Logo": "assets/tournament_logos/mouz_47px_mouz_2021_allmode_png.png", + "score1": "0", + "score2": "2", + "date": "December 12, 2024 - 14:00 CST" + }, + { + "round": "Quarterfinals", + "team1": "Team Liquid", + "team2": "Team Spirit", + "team1Logo": "assets/tournament_logos/team_liquid_44px_team_liquid_2024_lightmode_png.png", + "team2Logo": "assets/tournament_logos/team_spirit_43px_team_spirit_2022_lightmode_png.png", + "score1": "0", + "score2": "2", + "date": "December 12, 2024 - 17:30 CST" + }, + { + "round": "Semifinals", + "team1": "MOUZ", + "team2": "Team Spirit", + "team1Logo": "assets/tournament_logos/mouz_47px_mouz_2021_allmode_png.png", + "team2Logo": "assets/tournament_logos/team_spirit_43px_team_spirit_2022_lightmode_png.png", + "score1": "1", + "score2": "2", + "date": "December 14, 2024 - 14:00 CST" + }, + { + "round": "Quarterfinals", + "team1": "G2 Esports", + "team2": "HEROIC", + "team1Logo": "assets/tournament_logos/g2_esports_43px_g2_esports_2020_lightmode_png.png", + "team2Logo": "assets/tournament_logos/heroic_57px_heroic_2024_allmode_png.png", + "score1": "2", + "score2": "1", + "date": "December 13, 2024 - 14:00 CST" + }, + { + "round": "Quarterfinals", + "team1": "FaZe Clan", + "team2": "Team Vitality", + "team1Logo": "assets/tournament_logos/faze_clan_75px_faze_clan_2024_allmode_png.png", + "team2Logo": "assets/tournament_logos/team_vitality_41px_team_vitality_2023_lightmode_png.png", + "score1": "2", + "score2": "1", + "date": "December 13, 2024 - 18:25 CST" + }, + { + "round": "Semifinals", + "team1": "G2 Esports", + "team2": "FaZe Clan", + "team1Logo": "assets/tournament_logos/g2_esports_43px_g2_esports_2020_lightmode_png.png", + "team2Logo": "assets/tournament_logos/faze_clan_75px_faze_clan_2024_allmode_png.png", + "score1": "0", + "score2": "2", + "date": "December 14, 2024 - 18:35 CST" + }, + { + "round": "Grand Final", + "team1": "Team Spirit", + "team2": "FaZe Clan", + "team1Logo": "assets/tournament_logos/team_spirit_43px_team_spirit_2022_lightmode_png.png", + "team2Logo": "assets/tournament_logos/faze_clan_75px_faze_clan_2024_allmode_png.png", + "score1": "2", + "score2": "1", + "date": "December 15, 2024 - 17:25 CST" + } + ] + }, + { + "name": "StarLadder Berlin 2019", + "winner": "Astralis", + "tournamentLogo": "assets/tournament_logos/starladder_berlin_2019.png", + "startDate": "2019-08-23", + "endDate": "2019-09-08", + "placements": [ + { + "place": "1st", + "team": "Astralis", + "teamLogo": "assets/tournament_logos/astralis_41px_astralis_logo_png.png" + }, + { + "place": "2nd", + "team": "AVANGAR", + "teamLogo": "assets/tournament_logos/avangar_56px_avangar_2019_allmode_png.png" + }, + { + "place": "3rd-4th", + "team": "Renegades", + "teamLogo": "assets/tournament_logos/renegades_47px_renegades_2019_allmode_png.png" + }, + { + "place": "3rd-4th", + "team": "NRG Esports", + "teamLogo": "assets/tournament_logos/nrg_esports_89px_nrg_2019_lightmode_png.png" + }, + { + "place": "5th-8th", + "team": "ENCE", + "teamLogo": "assets/tournament_logos/ence_50px_ence_esports_2016_allmode_png.png" + }, + { + "place": "5th-8th", + "team": "Team Vitality", + "teamLogo": "assets/tournament_logos/team_vitality_39px_team_vitality_2018_allmode_png.png" + }, + { + "place": "5th-8th", + "team": "Natus Vincere", + "teamLogo": "assets/tournament_logos/natus_vincere_56px_natus_vincere_allmode_png.png" + }, + { + "place": "5th-8th", + "team": "Team Liquid", + "teamLogo": "assets/tournament_logos/team_liquid_50px_team_liquid_2017_lightmode_png.png" + }, + { + "place": "9th-11th", + "team": "mousesports", + "teamLogo": "assets/tournament_logos/mousesports_92px_mousesports_2016_allmode_png.png" + }, + { + "place": "9th-11th", + "team": "G2 Esports", + "teamLogo": "assets/tournament_logos/g2_esports_43px_g2_esports_2020_lightmode_png.png" + }, + { + "place": "9th-11th", + "team": "CR4ZY", + "teamLogo": "assets/tournament_logos/cr4zy_50px_cr4zy_allmode_png.png" + }, + { + "place": "12th-14th", + "team": "MIBR", + "teamLogo": "assets/tournament_logos/mibr_100px_mibr_2018_lightmode_png.png" + }, + { + "place": "12th-14th", + "team": "FaZe Clan", + "teamLogo": "assets/tournament_logos/faze_clan_76px_faze_clan_2018_lightmode_png.png" + }, + { + "place": "12th-14th", + "team": "North", + "teamLogo": "assets/tournament_logos/north_48px_north_lightmode_png.png" + }, + { + "place": "15th-16th", + "team": "Ninjas in Pyjamas", + "teamLogo": "assets/tournament_logos/ninjas_in_pyjamas_50px_ninjas_in_pyjamas_2017_lightmode_png.png" + }, + { + "place": "15th-16th", + "team": "DreamEaters", + "teamLogo": "assets/tournament_logos/dreameaters_49px_dreameaters_2018_allmode_png.png" + }, + { + "place": "17th-19th", + "team": "forZe", + "teamLogo": "assets/tournament_logos/forze_42px_forze_2019_allmode_png.png" + }, + { + "place": "17th-19th", + "team": "Grayhound Gaming", + "teamLogo": "assets/tournament_logos/grayhound_gaming_43px_grayhound_gaming_allmode_png.png" + }, + { + "place": "17th-19th", + "team": "Syman Gaming", + "teamLogo": "assets/tournament_logos/syman_gaming_50px_syman_2018_png.png" + }, + { + "place": "20th-22nd", + "team": "FURIA Esports", + "teamLogo": "assets/tournament_logos/furia_esports_51px_furia_esports_allmode_png.png" + }, + { + "place": "20th-22nd", + "team": "HellRaisers", + "teamLogo": "assets/tournament_logos/hellraisers_57px_hellraisers_2018_allmode_png.png" + }, + { + "place": "20th-22nd", + "team": "Complexity Gaming", + "teamLogo": "assets/tournament_logos/complexity_gaming_53px_complexity_gaming_2019_lightmode_png.png" + }, + { + "place": "23rd-24th", + "team": "TYLOO", + "teamLogo": "assets/tournament_logos/tyloo_56px_tyloo_2016_allmode_png.png" + }, + { + "place": "23rd-24th", + "team": "INTZ eSports", + "teamLogo": "assets/tournament_logos/intz_esports_79px_intz_esports_allmode_png.png" + } + ], + "teamRosters": [ + { + "team": "Astralis", + "teamLogo": "assets/tournament_logos/astralis_41px_astralis_logo_png.png", + "players": [ + "device", + "dupreeh", + "Xyp9x", + "gla1ve", + "Magisk" + ] + }, + { + "team": "ENCE", + "teamLogo": "assets/tournament_logos/ence_50px_ence_esports_2016_allmode_png.png", + "players": [ + "allu", + "Aleksib", + "sergej", + "Aerial", + "xseveN" + ] + }, + { + "team": "Natus Vincere", + "teamLogo": "assets/tournament_logos/natus_vincere_56px_natus_vincere_allmode_png.png", + "players": [ + "flamie", + "s1mple", + "Zeus", + "electronic", + "Boombl4" + ] + }, + { + "team": "MIBR", + "teamLogo": "assets/tournament_logos/mibr_100px_mibr_2018_lightmode_png.png", + "players": [ + "FalleN", + "fer", + "TACO", + "LUCAS1", + "zews" + ] + }, + { + "team": "FaZe Clan", + "teamLogo": "assets/tournament_logos/faze_clan_76px_faze_clan_2018_lightmode_png.png", + "players": [ + "rain", + "NiKo", + "GuardiaN", + "olofmeister", + "NEO" + ] + }, + { + "team": "Team Liquid", + "teamLogo": "assets/tournament_logos/team_liquid_50px_team_liquid_2017_lightmode_png.png", + "players": [ + "nitr0", + "EliGE", + "Twistzz", + "NAF", + "Stewie2K" + ] + }, + { + "team": "Renegades", + "teamLogo": "assets/tournament_logos/renegades_47px_renegades_2019_allmode_png.png", + "players": [ + "AZR", + "jks", + "jkaem", + "Liazz", + "Gratisfaction" + ] + }, + { + "team": "Ninjas in Pyjamas", + "teamLogo": "assets/tournament_logos/ninjas_in_pyjamas_50px_ninjas_in_pyjamas_2017_lightmode_png.png", + "players": [ + "f0rest", + "GeT_RiGhT", + "REZ", + "Lekr0", + "Golden" + ] + }, + { + "team": "Team Vitality", + "teamLogo": "assets/tournament_logos/team_vitality_39px_team_vitality_2018_allmode_png.png", + "players": [ + "NBK-", + "apEX", + "RpK", + "ZywOo", + "ALEX" + ] + }, + { + "team": "AVANGAR", + "teamLogo": "assets/tournament_logos/avangar_56px_avangar_2019_allmode_png.png", + "players": [ + "buster", + "qikert", + "Jame", + "SANJI", + "AdreN" + ] + }, + { + "team": "Forfeited Slot", + "players": [ + "autimatic", + "RUSH", + "flusha", + "kioShiMa", + "Zellsis" + ] + }, + { + "team": "HellRaisers", + "teamLogo": "assets/tournament_logos/hellraisers_57px_hellraisers_2018_allmode_png.png", + "players": [ + "ANGE1", + "ISSAA", + "oskar", + "loWel", + "nukkye" + ] + }, + { + "team": "G2 Esports", + "teamLogo": "assets/tournament_logos/g2_esports_43px_g2_esports_2020_lightmode_png.png", + "players": [ + "shox", + "kennyS", + "Lucky", + "JACKZ", + "AMANEK" + ] + }, + { + "team": "Complexity Gaming", + "teamLogo": "assets/tournament_logos/complexity_gaming_53px_complexity_gaming_2019_lightmode_png.png", + "players": [ + "dephh", + "ShahZaM", + "Rickeh", + "SicK", + "oBo" + ] + }, + { + "team": "mousesports", + "teamLogo": "assets/tournament_logos/mousesports_92px_mousesports_2016_allmode_png.png", + "players": [ + "chrisJ", + "ropz", + "karrigan", + "woxic", + "frozen" + ] + }, + { + "team": "NRG Esports", + "teamLogo": "assets/tournament_logos/nrg_esports_89px_nrg_2019_lightmode_png.png", + "players": [ + "Brehze", + "CeRq", + "Ethan", + "tarik", + "stanislaw" + ] + }, + { + "team": "forZe", + "teamLogo": "assets/tournament_logos/forze_42px_forze_2019_allmode_png.png", + "players": [ + "facecrack", + "Jerry", + "almazer", + "xsepower", + "FL1T" + ] + }, + { + "team": "Grayhound Gaming", + "teamLogo": "assets/tournament_logos/grayhound_gaming_43px_grayhound_gaming_allmode_png.png", + "players": [ + "erkaSt", + "dexter", + "DickStacy", + "malta", + "Sico" + ] + }, + { + "team": "CR4ZY", + "teamLogo": "assets/tournament_logos/cr4zy_50px_cr4zy_allmode_png.png", + "players": [ + "LETN1", + "huNter-", + "nexa", + "EspiranTo", + "ottoNd" + ] + }, + { + "team": "FURIA Esports", + "teamLogo": "assets/tournament_logos/furia_esports_51px_furia_esports_allmode_png.png", + "players": [ + "yuurih", + "arT", + "VINI", + "KSCERATO", + "ableJ" + ] + }, + { + "team": "Syman Gaming", + "teamLogo": "assets/tournament_logos/syman_gaming_50px_syman_2018_png.png", + "players": [ + "neaLaN", + "t0rick", + "Perfecto", + "Ramz1kBO$$", + "Keoz" + ] + }, + { + "team": "TYLOO", + "teamLogo": "assets/tournament_logos/tyloo_56px_tyloo_2016_allmode_png.png", + "players": [ + "somebody", + "BnTeT", + "Summer", + "Attacker", + "Freeman" + ] + }, + { + "team": "North", + "teamLogo": "assets/tournament_logos/north_48px_north_lightmode_png.png", + "players": [ + "aizy", + "valde", + "Kjaerbye", + "gade", + "JUGi" + ] + }, + { + "team": "DreamEaters", + "teamLogo": "assets/tournament_logos/dreameaters_49px_dreameaters_2018_allmode_png.png", + "players": [ + "speed4k", + "kinqie", + "Krad", + "Forester", + "svyat" + ] + }, + { + "team": "INTZ eSports", + "teamLogo": "assets/tournament_logos/intz_esports_79px_intz_esports_allmode_png.png", + "players": [ + "kNgV-", + "chelo", + "xand", + "DeStiNy", + "yel" + ] + } + ], + "stageDates": [ + { + "phase": "Challengers Stage", + "startDate": "2019-08-23", + "endDate": "2019-08-26" + }, + { + "phase": "Legends Stage", + "startDate": "2019-08-28", + "endDate": "2019-09-01" + }, + { + "phase": "Champions Stage", + "startDate": "2019-08-05", + "endDate": "2019-09-08" + } + ], + "playoffMatches": [ + { + "round": "Quarterfinals", + "team1": "ENCE", + "team2": "Renegades", + "team1Logo": "assets/tournament_logos/ence_50px_ence_esports_2016_allmode_png.png", + "team2Logo": "assets/tournament_logos/renegades_47px_renegades_2019_allmode_png.png", + "score1": "0", + "score2": "2", + "date": "September 5, 2019 - 15:30 CEST" + }, + { + "round": "Quarterfinals", + "team1": "Team Vitality", + "team2": "AVANGAR", + "team1Logo": "assets/tournament_logos/team_vitality_39px_team_vitality_2018_allmode_png.png", + "team2Logo": "assets/tournament_logos/avangar_56px_avangar_2019_allmode_png.png", + "score1": "1", + "score2": "2", + "date": "September 5, 2019 - 18:30 CEST" + }, + { + "round": "Semifinals", + "team1": "Renegades", + "team2": "AVANGAR", + "team1Logo": "assets/tournament_logos/renegades_47px_renegades_2019_allmode_png.png", + "team2Logo": "assets/tournament_logos/avangar_56px_avangar_2019_allmode_png.png", + "score1": "0", + "score2": "2", + "date": "September 7, 2019 - 15:00 CEST" + }, + { + "round": "Quarterfinals", + "team1": "NRG", + "team2": "Natus Vincere", + "team1Logo": "assets/tournament_logos/nrg_89px_nrg_2019_lightmode_png.png", + "team2Logo": "assets/tournament_logos/natus_vincere_56px_natus_vincere_allmode_png.png", + "score1": "2", + "score2": "0", + "date": "September 6, 2019 - 15:00 CEST" + }, + { + "round": "Quarterfinals", + "team1": "Astralis", + "team2": "Team Liquid", + "team1Logo": "assets/tournament_logos/astralis_41px_astralis_logo_png.png", + "team2Logo": "assets/tournament_logos/team_liquid_50px_team_liquid_2017_lightmode_png.png", + "score1": "2", + "score2": "0", + "date": "September 6, 2019 - 18:30 CEST" + }, + { + "round": "Semifinals", + "team1": "NRG", + "team2": "Astralis", + "team1Logo": "assets/tournament_logos/nrg_89px_nrg_2019_lightmode_png.png", + "team2Logo": "assets/tournament_logos/astralis_41px_astralis_logo_png.png", + "score1": "0", + "score2": "2", + "date": "September 7, 2019 - 18:30 CEST" + }, + { + "round": "Grand Final", + "team1": "AVANGAR", + "team2": "Astralis", + "team1Logo": "assets/tournament_logos/avangar_56px_avangar_2019_allmode_png.png", + "team2Logo": "assets/tournament_logos/astralis_41px_astralis_logo_png.png", + "score1": "0", + "score2": "2", + "date": "September 8, 2019 - 18:00 CEST" + } + ] + }, + { + "name": "StarLadder Budapest 2025", + "winner": "Team Vitality", + "tournamentLogo": "assets/tournament_logos/starladder_budapest_2025.png", + "startDate": "2025-11-24", + "endDate": "2025-12-14", + "placements": [ + { + "place": "1st", + "team": "Team Vitality", + "teamLogo": "assets/tournament_logos/team_vitality_41px_team_vitality_2023_lightmode_png.png" + }, + { + "place": "2nd", + "team": "FaZe Clan", + "teamLogo": "assets/tournament_logos/faze_clan_59px_faze_esports_october_2025_lightmode_png.png" + }, + { + "place": "3rd-4th", + "team": "Team Spirit", + "teamLogo": "assets/tournament_logos/team_spirit_43px_team_spirit_2022_lightmode_png.png" + }, + { + "place": "3rd-4th", + "team": "Natus Vincere", + "teamLogo": "assets/tournament_logos/natus_vincere_57px_natus_vincere_2021_lightmode_png.png" + }, + { + "place": "5th-8th", + "team": "Team Falcons", + "teamLogo": "assets/tournament_logos/team_falcons_41px_team_falcons_2022_allmode_png.png" + }, + { + "place": "5th-8th", + "team": "The MongolZ", + "teamLogo": "assets/tournament_logos/the_mongolz_39px_the_mongolz_2024_03_allmode_png.png" + }, + { + "place": "5th-8th", + "team": "MOUZ", + "teamLogo": "assets/tournament_logos/mouz_47px_mouz_2021_allmode_png.png" + }, + { + "place": "5th-8th", + "team": "FURIA", + "teamLogo": "assets/tournament_logos/furia_51px_furia_esports_allmode_png.png" + }, + { + "place": "9th-11th", + "team": "B8", + "teamLogo": "assets/tournament_logos/b8_41px_b8_lightmode_png.png" + }, + { + "place": "9th-11th", + "team": "G2 Esports", + "teamLogo": "assets/tournament_logos/g2_esports_43px_g2_esports_2020_lightmode_png.png" + }, + { + "place": "9th-11th", + "team": "Passion UA", + "teamLogo": "assets/tournament_logos/passion_ua_27px_passion_ua_2025_lightmode_png.png" + }, + { + "place": "12th-14th", + "team": "Imperial Esports", + "teamLogo": "assets/tournament_logos/imperial_esports_39px_imperial_esports_2025_allmode_png.png" + }, + { + "place": "12th-14th", + "team": "3DMAX", + "teamLogo": "assets/tournament_logos/3dmax_36px_3dmax_2024_allmode_png.png" + }, + { + "place": "12th-14th", + "team": "paiN Gaming", + "teamLogo": "assets/tournament_logos/pain_gaming_75px_pain_gaming_2023_lightmode_png.png" + }, + { + "place": "15th-16th", + "team": "Team Liquid", + "teamLogo": "assets/tournament_logos/team_liquid_44px_team_liquid_2024_lightmode_png.png" + }, + { + "place": "15th-16th", + "team": "PARIVISION", + "teamLogo": "assets/tournament_logos/parivision_56px_parivision_allmode_png.png" + }, + { + "place": "17th-19th", + "team": "Ninjas in Pyjamas", + "teamLogo": "assets/tournament_logos/ninjas_in_pyjamas_50px_ninjas_in_pyjamas_2021_lightmode_png.png" + }, + { + "place": "17th-19th", + "team": "M80", + "teamLogo": "assets/tournament_logos/m80_44px_m80_2023_allmode_png.png" + }, + { + "place": "17th-19th", + "team": "Astralis", + "teamLogo": "assets/tournament_logos/astralis_41px_astralis_2020_allmode_png.png" + }, + { + "place": "20th-22nd", + "team": "Fnatic", + "teamLogo": "assets/tournament_logos/fnatic_77px_fnatic_2020_allmode_png.png" + }, + { + "place": "20th-22nd", + "team": "Aurora Gaming", + "teamLogo": "assets/tournament_logos/aurora_gaming_50px_aurora_gaming_2025_allmode_png.png" + }, + { + "place": "20th-22nd", + "team": "TYLOO", + "teamLogo": "assets/tournament_logos/tyloo_56px_tyloo_2016_allmode_png.png" + }, + { + "place": "23rd-24th", + "team": "MIBR", + "teamLogo": "assets/tournament_logos/mibr_100px_mibr_2018_lightmode_png.png" + }, + { + "place": "23rd-24th", + "team": "FlyQuest", + "teamLogo": "assets/tournament_logos/flyquest_51px_flyquest_2021_allmode_png.png" + }, + { + "place": "25th-27th", + "team": "NRG", + "teamLogo": "assets/tournament_logos/nrg_100px_nrg_2024_lightmode_png.png" + }, + { + "place": "25th-27th", + "team": "Fluxo", + "teamLogo": "assets/tournament_logos/fluxo_50px_fluxo_lightmode_png.png" + }, + { + "place": "25th-27th", + "team": "Legacy", + "teamLogo": "assets/tournament_logos/legacy_49px_legacy_allmode_png.png" + }, + { + "place": "28th-30th", + "team": "The Huns Esports", + "teamLogo": "assets/tournament_logos/the_huns_esports_45px_the_huns_esports_2025_allmode_png.png" + }, + { + "place": "28th-30th", + "team": "RED Canids", + "teamLogo": "assets/tournament_logos/red_canids_57px_red_canids_allmode_png.png" + }, + { + "place": "28th-30th", + "team": "GamerLegion", + "teamLogo": "assets/tournament_logos/gamerlegion_91px_gamerlegion_cs_2023_allmode_png.png" + }, + { + "place": "31st-32nd", + "team": "Lynn Vision Gaming", + "teamLogo": "assets/tournament_logos/lynn_vision_gaming_74px_lynn_vision_gaming_2024_allmode_png.png" + }, + { + "place": "31st-32nd", + "team": "Rare Atom", + "teamLogo": "assets/tournament_logos/rare_atom_55px_rare_atom_2024_allmode_png.png" + } + ], + "teamRosters": [ + { + "team": "FURIA", + "teamLogo": "assets/tournament_logos/furia_51px_furia_esports_allmode_png.png", + "players": [ + "yuurih", + "KSCERATO", + "FalleN", + "molodoy", + "YEKINDAR" + ] + }, + { + "team": "Team Vitality", + "teamLogo": "assets/tournament_logos/team_vitality_41px_team_vitality_2023_lightmode_png.png", + "players": [ + "apEX", + "ZywOo", + "flameZ", + "mezii", + "ropz" + ] + }, + { + "team": "Team Falcons", + "teamLogo": "assets/tournament_logos/team_falcons_41px_team_falcons_2022_allmode_png.png", + "players": [ + "NiKo", + "TeSeS", + "kyxsan", + "m0NESY", + "kyousuke" + ] + }, + { + "team": "The MongolZ", + "teamLogo": "assets/tournament_logos/the_mongolz_39px_the_mongolz_2024_03_allmode_png.png", + "players": [ + "bLitz", + "Techno4K", + "910", + "mzinho", + "Senzu" + ] + }, + { + "team": "MOUZ", + "teamLogo": "assets/tournament_logos/mouz_47px_mouz_2021_allmode_png.png", + "players": [ + "torzsi", + "xertioN", + "Jimpphat", + "Brollan", + "Spinx" + ] + }, + { + "team": "Team Spirit", + "teamLogo": "assets/tournament_logos/team_spirit_43px_team_spirit_2022_lightmode_png.png", + "players": [ + "chopper", + "zont1x", + "donk", + "sh1ro", + "zweih" + ] + }, + { + "team": "G2 Esports", + "teamLogo": "assets/tournament_logos/g2_esports_43px_g2_esports_2020_lightmode_png.png", + "players": [ + "huNter-", + "malbsMd", + "HeavyGod", + "SunPayus", + "matys" + ] + }, + { + "team": "paiN Gaming", + "teamLogo": "assets/tournament_logos/pain_gaming_75px_pain_gaming_2023_lightmode_png.png", + "players": [ + "biguzera", + "nqz", + "snow", + "dav1deuS", + "dgt" + ] + }, + { + "team": "Aurora Gaming", + "teamLogo": "assets/tournament_logos/aurora_gaming_50px_aurora_gaming_2025_allmode_png.png", + "players": [ + "XANTARES", + "MAJ3R", + "Wicadia", + "woxic", + "jottAAA" + ] + }, + { + "team": "Natus Vincere", + "teamLogo": "assets/tournament_logos/natus_vincere_57px_natus_vincere_2021_lightmode_png.png", + "players": [ + "b1t", + "Aleksib", + "iM", + "w0nderful", + "makazze" + ] + }, + { + "team": "Team Liquid", + "teamLogo": "assets/tournament_logos/team_liquid_44px_team_liquid_2024_lightmode_png.png", + "players": [ + "NAF", + "ultimate", + "NertZ", + "siuhy", + "EliGE" + ] + }, + { + "team": "3DMAX", + "teamLogo": "assets/tournament_logos/3dmax_36px_3dmax_2024_allmode_png.png", + "players": [ + "Lucky", + "Ex3rcice", + "Maka", + "Graviti", + "bodyy" + ] + }, + { + "team": "Astralis", + "teamLogo": "assets/tournament_logos/astralis_41px_astralis_2020_allmode_png.png", + "players": [ + "dev1ce", + "Staehr", + "jabbi", + "HooXi", + "Magisk" + ] + }, + { + "team": "TYLOO", + "teamLogo": "assets/tournament_logos/tyloo_56px_tyloo_2016_allmode_png.png", + "players": [ + "Attacker", + "JamYoung", + "Moseyuh", + "Mercury", + "Jee" + ] + }, + { + "team": "MIBR", + "teamLogo": "assets/tournament_logos/mibr_100px_mibr_2018_lightmode_png.png", + "players": [ + "exit", + "brnz4n", + "insani", + "kl1m", + "Qikert" + ] + }, + { + "team": "Passion UA", + "teamLogo": "assets/tournament_logos/passion_ua_27px_passion_ua_2025_lightmode_png.png", + "players": [ + "Kvem", + "JT", + "Grim", + "hallzerk", + "nicx" + ] + }, + { + "team": "Legacy", + "teamLogo": "assets/tournament_logos/legacy_49px_legacy_allmode_png.png", + "players": [ + "latto", + "dumau", + "saadzin", + "n1ssim", + "lux" + ] + }, + { + "team": "FaZe Clan", + "teamLogo": "assets/tournament_logos/faze_clan_59px_faze_esports_october_2025_lightmode_png.png", + "players": [ + "rain", + "broky", + "karrigan", + "frozen", + "jcobbb" + ] + }, + { + "team": "B8", + "teamLogo": "assets/tournament_logos/b8_41px_b8_lightmode_png.png", + "players": [ + "npl", + "esenthial", + "headtr1ck", + "alex666", + "kensizor" + ] + }, + { + "team": "GamerLegion", + "teamLogo": "assets/tournament_logos/gamerlegion_91px_gamerlegion_cs_2023_allmode_png.png", + "players": [ + "ztr", + "Tauson", + "PR", + "REZ", + "Kursy" + ] + }, + { + "team": "Fnatic", + "teamLogo": "assets/tournament_logos/fnatic_77px_fnatic_2020_allmode_png.png", + "players": [ + "KRIMZ", + "blameF", + "fear", + "jambo", + "CYPHER" + ] + }, + { + "team": "PARIVISION", + "teamLogo": "assets/tournament_logos/parivision_56px_parivision_allmode_png.png", + "players": [ + "BELCHONOKK", + "Jame", + "nota", + "xiELO", + "AW" + ] + }, + { + "team": "Ninjas in Pyjamas", + "teamLogo": "assets/tournament_logos/ninjas_in_pyjamas_50px_ninjas_in_pyjamas_2021_lightmode_png.png", + "players": [ + "r1nkle", + "ewjerkz", + "sjuush", + "Snappi", + "xKacpersky" + ] + }, + { + "team": "Imperial Esports", + "teamLogo": "assets/tournament_logos/imperial_esports_39px_imperial_esports_2025_allmode_png.png", + "players": [ + "VINI", + "noway", + "try", + "chelo", + "skullz" + ] + }, + { + "team": "FlyQuest", + "teamLogo": "assets/tournament_logos/flyquest_51px_flyquest_2021_allmode_png.png", + "players": [ + "INS", + "Vexite", + "regali", + "nettik", + "jks" + ] + }, + { + "team": "Lynn Vision Gaming", + "teamLogo": "assets/tournament_logos/lynn_vision_gaming_74px_lynn_vision_gaming_2024_allmode_png.png", + "players": [ + "westmelon", + "z4kr", + "EmiliaQAQ", + "Starry", + "C4LLM3SU3" + ] + }, + { + "team": "M80", + "teamLogo": "assets/tournament_logos/m80_44px_m80_2023_allmode_png.png", + "players": [ + "Swisher", + "slaxz-", + "s1n", + "Lake", + "HexT" + ] + }, + { + "team": "Fluxo", + "teamLogo": "assets/tournament_logos/fluxo_50px_fluxo_lightmode_png.png", + "players": [ + "zevy", + "arT", + "kye", + "decenty", + "Lucaozy" + ] + }, + { + "team": "RED Canids", + "teamLogo": "assets/tournament_logos/red_canids_57px_red_canids_allmode_png.png", + "players": [ + "venomzera", + "drop", + "kauez", + "history", + "chayJESUS" + ] + }, + { + "team": "The Huns Esports", + "teamLogo": "assets/tournament_logos/the_huns_esports_45px_the_huns_esports_2025_allmode_png.png", + "players": [ + "nin9", + "Bart4k", + "cobra", + "xerolte", + "sk0R" + ] + }, + { + "team": "NRG", + "teamLogo": "assets/tournament_logos/nrg_100px_nrg_2024_lightmode_png.png", + "players": [ + "nitr0", + "Jeorge", + "br0", + "XotiC", + "Sonic" + ] + }, + { + "team": "Rare Atom", + "teamLogo": "assets/tournament_logos/rare_atom_55px_rare_atom_2024_allmode_png.png", + "players": [ + "Summer", + "L1haNg", + "ChildKing", + "TiGeR", + "Marek" + ] + } + ], + "stageDates": [ + { + "phase": "Stage 1", + "startDate": "2025-11-24", + "endDate": "2025-11-27" + }, + { + "phase": "Stage 2", + "startDate": "2025-11-29", + "endDate": "2025-12-02" + }, + { + "phase": "Stage 3", + "startDate": "2025-12-04", + "endDate": "2025-12-07" + }, + { + "phase": "Playoffs", + "startDate": "2025-12-11", + "endDate": "2025-12-14" + } + ], + "playoffMatches": [ + { + "round": "Quarterfinals", + "team1": "Team Spirit", + "team2": "Team Falcons", + "team1Logo": "assets/tournament_logos/team_spirit_43px_team_spirit_2022_lightmode_png.png", + "team2Logo": "assets/tournament_logos/team_falcons_41px_team_falcons_2022_allmode_png.png", + "score1": "2", + "score2": "0", + "date": "December 11, 2025 - 17:00 CET" + }, + { + "round": "Quarterfinals", + "team1": "The MongolZ", + "team2": "Team Vitality", + "team1Logo": "assets/tournament_logos/the_mongolz_39px_the_mongolz_2024_03_allmode_png.png", + "team2Logo": "assets/tournament_logos/team_vitality_41px_team_vitality_2023_lightmode_png.png", + "score1": "0", + "score2": "2", + "date": "December 11, 2025 - 20:10 CET" + }, + { + "round": "Semifinals", + "team1": "Team Spirit", + "team2": "Team Vitality", + "team1Logo": "assets/tournament_logos/team_spirit_43px_team_spirit_2022_lightmode_png.png", + "team2Logo": "assets/tournament_logos/team_vitality_41px_team_vitality_2023_lightmode_png.png", + "score1": "0", + "score2": "2", + "date": "December 13, 2025 - 17:00 CET" + }, + { + "round": "Quarterfinals", + "team1": "MOUZ", + "team2": "FaZe Clan", + "team1Logo": "assets/tournament_logos/mouz_47px_mouz_2021_allmode_png.png", + "team2Logo": "assets/tournament_logos/faze_clan_59px_faze_esports_october_2025_lightmode_png.png", + "score1": "0", + "score2": "2", + "date": "December 12, 2025 - 17:00 CET" + }, + { + "round": "Quarterfinals", + "team1": "Natus Vincere", + "team2": "FURIA", + "team1Logo": "assets/tournament_logos/natus_vincere_57px_natus_vincere_2021_lightmode_png.png", + "team2Logo": "assets/tournament_logos/furia_51px_furia_esports_allmode_png.png", + "score1": "2", + "score2": "1", + "date": "December 12, 2025 - 20:00 CET" + }, + { + "round": "Semifinals", + "team1": "FaZe Clan", + "team2": "Natus Vincere", + "team1Logo": "assets/tournament_logos/faze_clan_59px_faze_esports_october_2025_lightmode_png.png", + "team2Logo": "assets/tournament_logos/natus_vincere_57px_natus_vincere_2021_lightmode_png.png", + "score1": "2", + "score2": "1", + "date": "December 13, 2025 - 21:00 CET" + }, + { + "round": "Grand Final", + "team1": "Team Vitality", + "team2": "FaZe Clan", + "team1Logo": "assets/tournament_logos/team_vitality_41px_team_vitality_2023_lightmode_png.png", + "team2Logo": "assets/tournament_logos/faze_clan_59px_faze_esports_october_2025_lightmode_png.png", + "score1": "3", + "score2": "1", + "date": "December 14, 2025 - 18:00 CET" + } + ] + } +] diff --git a/assets/tournament_logos/00_nation_50px_00_nation_2022_lightmode_2_png.png b/assets/tournament_logos/00_nation_50px_00_nation_2022_lightmode_2_png.png new file mode 100644 index 00000000..1cd0ace2 Binary files /dev/null and b/assets/tournament_logos/00_nation_50px_00_nation_2022_lightmode_2_png.png differ diff --git a/assets/tournament_logos/3dmax_36px_3dmax_2011_allmode_png.png b/assets/tournament_logos/3dmax_36px_3dmax_2011_allmode_png.png new file mode 100644 index 00000000..35c62648 Binary files /dev/null and b/assets/tournament_logos/3dmax_36px_3dmax_2011_allmode_png.png differ diff --git a/assets/tournament_logos/3dmax_36px_3dmax_2024_allmode_png.png b/assets/tournament_logos/3dmax_36px_3dmax_2024_allmode_png.png new file mode 100644 index 00000000..405aae8f Binary files /dev/null and b/assets/tournament_logos/3dmax_36px_3dmax_2024_allmode_png.png differ diff --git a/assets/tournament_logos/9ine_50px_9ine_2022_lightmode_png.png b/assets/tournament_logos/9ine_50px_9ine_2022_lightmode_png.png new file mode 100644 index 00000000..733edc6e Binary files /dev/null and b/assets/tournament_logos/9ine_50px_9ine_2022_lightmode_png.png differ diff --git a/assets/tournament_logos/9z_team_54px_9z_team_allmode_png.png b/assets/tournament_logos/9z_team_54px_9z_team_allmode_png.png new file mode 100644 index 00000000..bee1b5cb Binary files /dev/null and b/assets/tournament_logos/9z_team_54px_9z_team_allmode_png.png differ diff --git a/assets/tournament_logos/amkal_esports_49px_amkal_esports_lightmode_png.png b/assets/tournament_logos/amkal_esports_49px_amkal_esports_lightmode_png.png new file mode 100644 index 00000000..9ba01db1 Binary files /dev/null and b/assets/tournament_logos/amkal_esports_49px_amkal_esports_lightmode_png.png differ diff --git a/assets/tournament_logos/apeks_100px_apeks_2021_darkmode_png.png b/assets/tournament_logos/apeks_100px_apeks_2021_darkmode_png.png new file mode 100644 index 00000000..efa27d75 Binary files /dev/null and b/assets/tournament_logos/apeks_100px_apeks_2021_darkmode_png.png differ diff --git a/assets/tournament_logos/astana_dragons_50px_astana_dragons_allmode_png.png b/assets/tournament_logos/astana_dragons_50px_astana_dragons_allmode_png.png new file mode 100644 index 00000000..793a61a6 Binary files /dev/null and b/assets/tournament_logos/astana_dragons_50px_astana_dragons_allmode_png.png differ diff --git a/assets/tournament_logos/astralis_41px_astralis_2020_allmode_png.png b/assets/tournament_logos/astralis_41px_astralis_2020_allmode_png.png new file mode 100644 index 00000000..c614f984 Binary files /dev/null and b/assets/tournament_logos/astralis_41px_astralis_2020_allmode_png.png differ diff --git a/assets/tournament_logos/astralis_41px_astralis_logo_png.png b/assets/tournament_logos/astralis_41px_astralis_logo_png.png new file mode 100644 index 00000000..0ff8db45 Binary files /dev/null and b/assets/tournament_logos/astralis_41px_astralis_logo_png.png differ diff --git a/assets/tournament_logos/aurora_gaming_50px_aurora_gaming_2025_allmode_png.png b/assets/tournament_logos/aurora_gaming_50px_aurora_gaming_2025_allmode_png.png new file mode 100644 index 00000000..c7346748 Binary files /dev/null and b/assets/tournament_logos/aurora_gaming_50px_aurora_gaming_2025_allmode_png.png differ diff --git a/assets/tournament_logos/avangar_53px_avangar_2018_allmode_png.png b/assets/tournament_logos/avangar_53px_avangar_2018_allmode_png.png new file mode 100644 index 00000000..171d7f24 Binary files /dev/null and b/assets/tournament_logos/avangar_53px_avangar_2018_allmode_png.png differ diff --git a/assets/tournament_logos/avangar_56px_avangar_2019_allmode_png.png b/assets/tournament_logos/avangar_56px_avangar_2019_allmode_png.png new file mode 100644 index 00000000..2a77c64a Binary files /dev/null and b/assets/tournament_logos/avangar_56px_avangar_2019_allmode_png.png differ diff --git a/assets/tournament_logos/avangar_57px_avangar_allmode_png.png b/assets/tournament_logos/avangar_57px_avangar_allmode_png.png new file mode 100644 index 00000000..dd1d8a13 Binary files /dev/null and b/assets/tournament_logos/avangar_57px_avangar_allmode_png.png differ diff --git a/assets/tournament_logos/b8_41px_b8_lightmode_png.png b/assets/tournament_logos/b8_41px_b8_lightmode_png.png new file mode 100644 index 00000000..36fb0331 Binary files /dev/null and b/assets/tournament_logos/b8_41px_b8_lightmode_png.png differ diff --git a/assets/tournament_logos/bad_news_eagles_34px_bad_news_eagles_allmode_png.png b/assets/tournament_logos/bad_news_eagles_34px_bad_news_eagles_allmode_png.png new file mode 100644 index 00000000..0fbdbfff Binary files /dev/null and b/assets/tournament_logos/bad_news_eagles_34px_bad_news_eagles_allmode_png.png differ diff --git a/assets/tournament_logos/betboom_team_56px_betboom_team_2024_allmode_png.png b/assets/tournament_logos/betboom_team_56px_betboom_team_2024_allmode_png.png new file mode 100644 index 00000000..f821ed43 Binary files /dev/null and b/assets/tournament_logos/betboom_team_56px_betboom_team_2024_allmode_png.png differ diff --git a/assets/tournament_logos/big_35px_big_2020_lightmode_png.png b/assets/tournament_logos/big_35px_big_2020_lightmode_png.png new file mode 100644 index 00000000..7bb8469a Binary files /dev/null and b/assets/tournament_logos/big_35px_big_2020_lightmode_png.png differ diff --git a/assets/tournament_logos/big_35px_big_lightmode_png.png b/assets/tournament_logos/big_35px_big_lightmode_png.png new file mode 100644 index 00000000..627cfd29 Binary files /dev/null and b/assets/tournament_logos/big_35px_big_lightmode_png.png differ diff --git a/assets/tournament_logos/blast_tv_austin_2025.png b/assets/tournament_logos/blast_tv_austin_2025.png index 36aec9b1..73912ddf 100644 Binary files a/assets/tournament_logos/blast_tv_austin_2025.png and b/assets/tournament_logos/blast_tv_austin_2025.png differ diff --git a/assets/tournament_logos/blast_tv_paris_2023.png b/assets/tournament_logos/blast_tv_paris_2023.png index f8743287..73912ddf 100644 Binary files a/assets/tournament_logos/blast_tv_paris_2023.png and b/assets/tournament_logos/blast_tv_paris_2023.png differ diff --git a/assets/tournament_logos/bravado_gaming_49px_bravado_gaming_allmode_png.png b/assets/tournament_logos/bravado_gaming_49px_bravado_gaming_allmode_png.png new file mode 100644 index 00000000..9aacc74a Binary files /dev/null and b/assets/tournament_logos/bravado_gaming_49px_bravado_gaming_allmode_png.png differ diff --git a/assets/tournament_logos/chinggis_warriors_50px_cw_allmode_png.png b/assets/tournament_logos/chinggis_warriors_50px_cw_allmode_png.png new file mode 100644 index 00000000..5a704573 Binary files /dev/null and b/assets/tournament_logos/chinggis_warriors_50px_cw_allmode_png.png differ diff --git a/assets/tournament_logos/clan_mystik_36px_clan_mystik_lightmode_png.png b/assets/tournament_logos/clan_mystik_36px_clan_mystik_lightmode_png.png new file mode 100644 index 00000000..eb2c9bf5 Binary files /dev/null and b/assets/tournament_logos/clan_mystik_36px_clan_mystik_lightmode_png.png differ diff --git a/assets/tournament_logos/cloud9_70px_cloud9_allmode_png.png b/assets/tournament_logos/cloud9_70px_cloud9_allmode_png.png new file mode 100644 index 00000000..cce3d8f0 Binary files /dev/null and b/assets/tournament_logos/cloud9_70px_cloud9_allmode_png.png differ diff --git a/assets/tournament_logos/cloud9_73px_cloud9_2023_allmode_png.png b/assets/tournament_logos/cloud9_73px_cloud9_2023_allmode_png.png new file mode 100644 index 00000000..9cca128e Binary files /dev/null and b/assets/tournament_logos/cloud9_73px_cloud9_2023_allmode_png.png differ diff --git a/assets/tournament_logos/complexity_100px_complexity_2025_allmode_png.png b/assets/tournament_logos/complexity_100px_complexity_2025_allmode_png.png new file mode 100644 index 00000000..0e73c983 Binary files /dev/null and b/assets/tournament_logos/complexity_100px_complexity_2025_allmode_png.png differ diff --git a/assets/tournament_logos/complexity_100px_complexity_logo_png.png b/assets/tournament_logos/complexity_100px_complexity_logo_png.png new file mode 100644 index 00000000..dbbd6707 Binary files /dev/null and b/assets/tournament_logos/complexity_100px_complexity_logo_png.png differ diff --git a/assets/tournament_logos/complexity_85px_complexity_gaming_2016_allmode_png.png b/assets/tournament_logos/complexity_85px_complexity_gaming_2016_allmode_png.png new file mode 100644 index 00000000..a40a8b16 Binary files /dev/null and b/assets/tournament_logos/complexity_85px_complexity_gaming_2016_allmode_png.png differ diff --git a/assets/tournament_logos/complexity_gaming_100px_complexity_logo_png.png b/assets/tournament_logos/complexity_gaming_100px_complexity_logo_png.png new file mode 100644 index 00000000..dbbd6707 Binary files /dev/null and b/assets/tournament_logos/complexity_gaming_100px_complexity_logo_png.png differ diff --git a/assets/tournament_logos/complexity_gaming_53px_complexity_gaming_2019_lightmode_png.png b/assets/tournament_logos/complexity_gaming_53px_complexity_gaming_2019_lightmode_png.png new file mode 100644 index 00000000..a59df7da Binary files /dev/null and b/assets/tournament_logos/complexity_gaming_53px_complexity_gaming_2019_lightmode_png.png differ diff --git a/assets/tournament_logos/complexity_gaming_85px_complexity_gaming_2016_allmode_png.png b/assets/tournament_logos/complexity_gaming_85px_complexity_gaming_2016_allmode_png.png new file mode 100644 index 00000000..a40a8b16 Binary files /dev/null and b/assets/tournament_logos/complexity_gaming_85px_complexity_gaming_2016_allmode_png.png differ diff --git a/assets/tournament_logos/copenhagen_flames_30px_copenhagen_flames_2018_lightmode_png.png b/assets/tournament_logos/copenhagen_flames_30px_copenhagen_flames_2018_lightmode_png.png new file mode 100644 index 00000000..3e37bc18 Binary files /dev/null and b/assets/tournament_logos/copenhagen_flames_30px_copenhagen_flames_2018_lightmode_png.png differ diff --git a/assets/tournament_logos/copenhagen_wolves_50px_copenhagen_wolves_2012_allmode_png.png b/assets/tournament_logos/copenhagen_wolves_50px_copenhagen_wolves_2012_allmode_png.png new file mode 100644 index 00000000..e9bfc540 Binary files /dev/null and b/assets/tournament_logos/copenhagen_wolves_50px_copenhagen_wolves_2012_allmode_png.png differ diff --git a/assets/tournament_logos/counter_logic_gaming_70px_counter_logic_gaming_allmode_png.png b/assets/tournament_logos/counter_logic_gaming_70px_counter_logic_gaming_allmode_png.png new file mode 100644 index 00000000..b30714d2 Binary files /dev/null and b/assets/tournament_logos/counter_logic_gaming_70px_counter_logic_gaming_allmode_png.png differ diff --git a/assets/tournament_logos/cr4zy_50px_cr4zy_allmode_png.png b/assets/tournament_logos/cr4zy_50px_cr4zy_allmode_png.png new file mode 100644 index 00000000..2828eece Binary files /dev/null and b/assets/tournament_logos/cr4zy_50px_cr4zy_allmode_png.png differ diff --git a/assets/tournament_logos/dat_team_47px_dat_team_allmode_png.png b/assets/tournament_logos/dat_team_47px_dat_team_allmode_png.png new file mode 100644 index 00000000..5d7d9556 Binary files /dev/null and b/assets/tournament_logos/dat_team_47px_dat_team_allmode_png.png differ diff --git a/assets/tournament_logos/devils_one_33px_team_kinguin_allmode_png.png b/assets/tournament_logos/devils_one_33px_team_kinguin_allmode_png.png new file mode 100644 index 00000000..ceabe80f Binary files /dev/null and b/assets/tournament_logos/devils_one_33px_team_kinguin_allmode_png.png differ diff --git a/assets/tournament_logos/dignitas_44px_team_dignitas_allmode_png.png b/assets/tournament_logos/dignitas_44px_team_dignitas_allmode_png.png new file mode 100644 index 00000000..5fe0b274 Binary files /dev/null and b/assets/tournament_logos/dignitas_44px_team_dignitas_allmode_png.png differ diff --git a/assets/tournament_logos/dreameaters_49px_dreameaters_2018_allmode_png.png b/assets/tournament_logos/dreameaters_49px_dreameaters_2018_allmode_png.png new file mode 100644 index 00000000..2c098d02 Binary files /dev/null and b/assets/tournament_logos/dreameaters_49px_dreameaters_2018_allmode_png.png differ diff --git a/assets/tournament_logos/dreamhack_cluj_napoca_2015.png b/assets/tournament_logos/dreamhack_cluj_napoca_2015.png index 38bb01ab..560c6d02 100644 Binary files a/assets/tournament_logos/dreamhack_cluj_napoca_2015.png and b/assets/tournament_logos/dreamhack_cluj_napoca_2015.png differ diff --git a/assets/tournament_logos/dreamhack_winter_2013.png b/assets/tournament_logos/dreamhack_winter_2013.png index ba998899..c1d18ee2 100644 Binary files a/assets/tournament_logos/dreamhack_winter_2013.png and b/assets/tournament_logos/dreamhack_winter_2013.png differ diff --git a/assets/tournament_logos/dreamhack_winter_2014.png b/assets/tournament_logos/dreamhack_winter_2014.png index 70af7835..c1d18ee2 100644 Binary files a/assets/tournament_logos/dreamhack_winter_2014.png and b/assets/tournament_logos/dreamhack_winter_2014.png differ diff --git a/assets/tournament_logos/ecstatic_50px_ecstatic_2023_allmode_png.png b/assets/tournament_logos/ecstatic_50px_ecstatic_2023_allmode_png.png new file mode 100644 index 00000000..589fd49f Binary files /dev/null and b/assets/tournament_logos/ecstatic_50px_ecstatic_2023_allmode_png.png differ diff --git a/assets/tournament_logos/eleague_atlanta_2017.png b/assets/tournament_logos/eleague_atlanta_2017.png index ff0fea2b..06210954 100644 Binary files a/assets/tournament_logos/eleague_atlanta_2017.png and b/assets/tournament_logos/eleague_atlanta_2017.png differ diff --git a/assets/tournament_logos/eleague_boston_2018.png b/assets/tournament_logos/eleague_boston_2018.png index c12c9d23..06210954 100644 Binary files a/assets/tournament_logos/eleague_boston_2018.png and b/assets/tournament_logos/eleague_boston_2018.png differ diff --git a/assets/tournament_logos/ems_one_katowice_2014.jpg b/assets/tournament_logos/ems_one_katowice_2014.jpg new file mode 100644 index 00000000..373884f4 Binary files /dev/null and b/assets/tournament_logos/ems_one_katowice_2014.jpg differ diff --git a/assets/tournament_logos/ems_one_katowice_2014.png b/assets/tournament_logos/ems_one_katowice_2014.png deleted file mode 100644 index 1e9d9085..00000000 Binary files a/assets/tournament_logos/ems_one_katowice_2014.png and /dev/null differ diff --git a/assets/tournament_logos/ence_50px_ence_2020_allmode_png.png b/assets/tournament_logos/ence_50px_ence_2020_allmode_png.png new file mode 100644 index 00000000..4c3d8929 Binary files /dev/null and b/assets/tournament_logos/ence_50px_ence_2020_allmode_png.png differ diff --git a/assets/tournament_logos/ence_50px_ence_esports_2016_allmode_png.png b/assets/tournament_logos/ence_50px_ence_esports_2016_allmode_png.png new file mode 100644 index 00000000..13b46ad7 Binary files /dev/null and b/assets/tournament_logos/ence_50px_ence_esports_2016_allmode_png.png differ diff --git a/assets/tournament_logos/entropiq_38px_entropiq_allmode_png.png b/assets/tournament_logos/entropiq_38px_entropiq_allmode_png.png new file mode 100644 index 00000000..5f727561 Binary files /dev/null and b/assets/tournament_logos/entropiq_38px_entropiq_allmode_png.png differ diff --git a/assets/tournament_logos/epsilon_esports_100px_epsilon_esports_lightmode_png.png b/assets/tournament_logos/epsilon_esports_100px_epsilon_esports_lightmode_png.png new file mode 100644 index 00000000..f13ec67f Binary files /dev/null and b/assets/tournament_logos/epsilon_esports_100px_epsilon_esports_lightmode_png.png differ diff --git a/assets/tournament_logos/esc_gaming_39px_esc_gaming_allmode_png.png b/assets/tournament_logos/esc_gaming_39px_esc_gaming_allmode_png.png new file mode 100644 index 00000000..5e404ab4 Binary files /dev/null and b/assets/tournament_logos/esc_gaming_39px_esc_gaming_allmode_png.png differ diff --git a/assets/tournament_logos/esl_one_cologne_2014.jpg b/assets/tournament_logos/esl_one_cologne_2014.jpg new file mode 100644 index 00000000..44545c46 Binary files /dev/null and b/assets/tournament_logos/esl_one_cologne_2014.jpg differ diff --git a/assets/tournament_logos/esl_one_cologne_2014.png b/assets/tournament_logos/esl_one_cologne_2014.png deleted file mode 100644 index 6d89b7f3..00000000 Binary files a/assets/tournament_logos/esl_one_cologne_2014.png and /dev/null differ diff --git a/assets/tournament_logos/esl_one_cologne_2015.png b/assets/tournament_logos/esl_one_cologne_2015.png index be1d8b86..7896a848 100644 Binary files a/assets/tournament_logos/esl_one_cologne_2015.png and b/assets/tournament_logos/esl_one_cologne_2015.png differ diff --git a/assets/tournament_logos/esl_one_cologne_2016.png b/assets/tournament_logos/esl_one_cologne_2016.png index 93ff2860..8f662dc2 100644 Binary files a/assets/tournament_logos/esl_one_cologne_2016.png and b/assets/tournament_logos/esl_one_cologne_2016.png differ diff --git a/assets/tournament_logos/esl_one_katowice_2015.png b/assets/tournament_logos/esl_one_katowice_2015.png index 8f76bec1..7896a848 100644 Binary files a/assets/tournament_logos/esl_one_katowice_2015.png and b/assets/tournament_logos/esl_one_katowice_2015.png differ diff --git a/assets/tournament_logos/eternal_fire_95px_eternal_fire_2023_allmode_png.png b/assets/tournament_logos/eternal_fire_95px_eternal_fire_2023_allmode_png.png new file mode 100644 index 00000000..32a98359 Binary files /dev/null and b/assets/tournament_logos/eternal_fire_95px_eternal_fire_2023_allmode_png.png differ diff --git a/assets/tournament_logos/eternal_fire_95px_eternal_fire_allmode_png.png b/assets/tournament_logos/eternal_fire_95px_eternal_fire_allmode_png.png new file mode 100644 index 00000000..caa12f0a Binary files /dev/null and b/assets/tournament_logos/eternal_fire_95px_eternal_fire_allmode_png.png differ diff --git a/assets/tournament_logos/evil_geniuses_37px_evil_geniuses_2020_lightmode_png.png b/assets/tournament_logos/evil_geniuses_37px_evil_geniuses_2020_lightmode_png.png new file mode 100644 index 00000000..070423b1 Binary files /dev/null and b/assets/tournament_logos/evil_geniuses_37px_evil_geniuses_2020_lightmode_png.png differ diff --git a/assets/tournament_logos/faceit_london_2018.png b/assets/tournament_logos/faceit_london_2018.png index 8de6c7a0..97cd0368 100644 Binary files a/assets/tournament_logos/faceit_london_2018.png and b/assets/tournament_logos/faceit_london_2018.png differ diff --git a/assets/tournament_logos/faze_clan_59px_faze_esports_october_2025_lightmode_png.png b/assets/tournament_logos/faze_clan_59px_faze_esports_october_2025_lightmode_png.png new file mode 100644 index 00000000..6d929ad0 Binary files /dev/null and b/assets/tournament_logos/faze_clan_59px_faze_esports_october_2025_lightmode_png.png differ diff --git a/assets/tournament_logos/faze_clan_75px_faze_clan_2024_allmode_png.png b/assets/tournament_logos/faze_clan_75px_faze_clan_2024_allmode_png.png new file mode 100644 index 00000000..0b3fd561 Binary files /dev/null and b/assets/tournament_logos/faze_clan_75px_faze_clan_2024_allmode_png.png differ diff --git a/assets/tournament_logos/faze_clan_75px_faze_clan_november_2021_lightmode_png.png b/assets/tournament_logos/faze_clan_75px_faze_clan_november_2021_lightmode_png.png new file mode 100644 index 00000000..ed91697b Binary files /dev/null and b/assets/tournament_logos/faze_clan_75px_faze_clan_november_2021_lightmode_png.png differ diff --git a/assets/tournament_logos/faze_clan_76px_faze_clan_2017_allmode_png.png b/assets/tournament_logos/faze_clan_76px_faze_clan_2017_allmode_png.png new file mode 100644 index 00000000..a0a3419a Binary files /dev/null and b/assets/tournament_logos/faze_clan_76px_faze_clan_2017_allmode_png.png differ diff --git a/assets/tournament_logos/faze_clan_76px_faze_clan_2018_lightmode_png.png b/assets/tournament_logos/faze_clan_76px_faze_clan_2018_lightmode_png.png new file mode 100644 index 00000000..839d8b81 Binary files /dev/null and b/assets/tournament_logos/faze_clan_76px_faze_clan_2018_lightmode_png.png differ diff --git a/assets/tournament_logos/faze_clan_76px_faze_clan_2021_lightmode_png.png b/assets/tournament_logos/faze_clan_76px_faze_clan_2021_lightmode_png.png new file mode 100644 index 00000000..f7210cd0 Binary files /dev/null and b/assets/tournament_logos/faze_clan_76px_faze_clan_2021_lightmode_png.png differ diff --git a/assets/tournament_logos/faze_clan_76px_faze_clan_2025_allmode_png.png b/assets/tournament_logos/faze_clan_76px_faze_clan_2025_allmode_png.png new file mode 100644 index 00000000..a4e1b813 Binary files /dev/null and b/assets/tournament_logos/faze_clan_76px_faze_clan_2025_allmode_png.png differ diff --git a/assets/tournament_logos/flash_gaming_56px_flash_gaming_allmode_png.png b/assets/tournament_logos/flash_gaming_56px_flash_gaming_allmode_png.png new file mode 100644 index 00000000..3a256b67 Binary files /dev/null and b/assets/tournament_logos/flash_gaming_56px_flash_gaming_allmode_png.png differ diff --git a/assets/tournament_logos/flipsid3_tactics_47px_flipsid3_tactics_lightmode_png.png b/assets/tournament_logos/flipsid3_tactics_47px_flipsid3_tactics_lightmode_png.png new file mode 100644 index 00000000..aaaa653f Binary files /dev/null and b/assets/tournament_logos/flipsid3_tactics_47px_flipsid3_tactics_lightmode_png.png differ diff --git a/assets/tournament_logos/fluxo_50px_fluxo_lightmode_png.png b/assets/tournament_logos/fluxo_50px_fluxo_lightmode_png.png new file mode 100644 index 00000000..0bd4f7b0 Binary files /dev/null and b/assets/tournament_logos/fluxo_50px_fluxo_lightmode_png.png differ diff --git a/assets/tournament_logos/flyquest_51px_flyquest_2021_allmode_png.png b/assets/tournament_logos/flyquest_51px_flyquest_2021_allmode_png.png new file mode 100644 index 00000000..dff18508 Binary files /dev/null and b/assets/tournament_logos/flyquest_51px_flyquest_2021_allmode_png.png differ diff --git a/assets/tournament_logos/fnatic_60px_fnatic_2015_allmode_png.png b/assets/tournament_logos/fnatic_60px_fnatic_2015_allmode_png.png new file mode 100644 index 00000000..01980f53 Binary files /dev/null and b/assets/tournament_logos/fnatic_60px_fnatic_2015_allmode_png.png differ diff --git a/assets/tournament_logos/fnatic_61px_fnatic_allmode_png.png b/assets/tournament_logos/fnatic_61px_fnatic_allmode_png.png new file mode 100644 index 00000000..a584042d Binary files /dev/null and b/assets/tournament_logos/fnatic_61px_fnatic_allmode_png.png differ diff --git a/assets/tournament_logos/fnatic_77px_fnatic_2020_allmode_png.png b/assets/tournament_logos/fnatic_77px_fnatic_2020_allmode_png.png new file mode 100644 index 00000000..6c58b6ef Binary files /dev/null and b/assets/tournament_logos/fnatic_77px_fnatic_2020_allmode_png.png differ diff --git a/assets/tournament_logos/forze_42px_forze_2019_allmode_png.png b/assets/tournament_logos/forze_42px_forze_2019_allmode_png.png new file mode 100644 index 00000000..8241860a Binary files /dev/null and b/assets/tournament_logos/forze_42px_forze_2019_allmode_png.png differ diff --git a/assets/tournament_logos/forze_esports_46px_forze_esports_2023_february_lightmode_png.png b/assets/tournament_logos/forze_esports_46px_forze_esports_2023_february_lightmode_png.png new file mode 100644 index 00000000..48e0147d Binary files /dev/null and b/assets/tournament_logos/forze_esports_46px_forze_esports_2023_february_lightmode_png.png differ diff --git a/assets/tournament_logos/furia_51px_furia_esports_allmode_png.png b/assets/tournament_logos/furia_51px_furia_esports_allmode_png.png new file mode 100644 index 00000000..9b1d7671 Binary files /dev/null and b/assets/tournament_logos/furia_51px_furia_esports_allmode_png.png differ diff --git a/assets/tournament_logos/furia_esports_51px_furia_esports_allmode_png.png b/assets/tournament_logos/furia_esports_51px_furia_esports_allmode_png.png new file mode 100644 index 00000000..9b1d7671 Binary files /dev/null and b/assets/tournament_logos/furia_esports_51px_furia_esports_allmode_png.png differ diff --git a/assets/tournament_logos/g2_esports_43px_g2_esports_2020_lightmode_png.png b/assets/tournament_logos/g2_esports_43px_g2_esports_2020_lightmode_png.png new file mode 100644 index 00000000..d348fdad Binary files /dev/null and b/assets/tournament_logos/g2_esports_43px_g2_esports_2020_lightmode_png.png differ diff --git a/assets/tournament_logos/g2_esports_43px_g2_esports_allmode_png.png b/assets/tournament_logos/g2_esports_43px_g2_esports_allmode_png.png new file mode 100644 index 00000000..d416c5b3 Binary files /dev/null and b/assets/tournament_logos/g2_esports_43px_g2_esports_allmode_png.png differ diff --git a/assets/tournament_logos/gambit_esports_44px_gambit_esports_sep_2020_allmode_png.png b/assets/tournament_logos/gambit_esports_44px_gambit_esports_sep_2020_allmode_png.png new file mode 100644 index 00000000..f07b75a4 Binary files /dev/null and b/assets/tournament_logos/gambit_esports_44px_gambit_esports_sep_2020_allmode_png.png differ diff --git a/assets/tournament_logos/gambit_esports_46px_gambit_esports_allmode_png.png b/assets/tournament_logos/gambit_esports_46px_gambit_esports_allmode_png.png new file mode 100644 index 00000000..c1cf0dc6 Binary files /dev/null and b/assets/tournament_logos/gambit_esports_46px_gambit_esports_allmode_png.png differ diff --git a/assets/tournament_logos/gambit_gaming_41px_gambit_gaming_allmode_png.png b/assets/tournament_logos/gambit_gaming_41px_gambit_gaming_allmode_png.png new file mode 100644 index 00000000..5185e7f8 Binary files /dev/null and b/assets/tournament_logos/gambit_gaming_41px_gambit_gaming_allmode_png.png differ diff --git a/assets/tournament_logos/gambit_gaming_46px_gambit_esports_allmode_png.png b/assets/tournament_logos/gambit_gaming_46px_gambit_esports_allmode_png.png new file mode 100644 index 00000000..c1cf0dc6 Binary files /dev/null and b/assets/tournament_logos/gambit_gaming_46px_gambit_esports_allmode_png.png differ diff --git a/assets/tournament_logos/gamerlegion_91px_gamerlegion_cs_2023_allmode_png.png b/assets/tournament_logos/gamerlegion_91px_gamerlegion_cs_2023_allmode_png.png new file mode 100644 index 00000000..290b15ac Binary files /dev/null and b/assets/tournament_logos/gamerlegion_91px_gamerlegion_cs_2023_allmode_png.png differ diff --git a/assets/tournament_logos/godsent_58px_godsent_lightmode_png.png b/assets/tournament_logos/godsent_58px_godsent_lightmode_png.png new file mode 100644 index 00000000..a5b1deeb Binary files /dev/null and b/assets/tournament_logos/godsent_58px_godsent_lightmode_png.png differ diff --git a/assets/tournament_logos/grayhound_gaming_43px_grayhound_gaming_allmode_png.png b/assets/tournament_logos/grayhound_gaming_43px_grayhound_gaming_allmode_png.png new file mode 100644 index 00000000..249d1f5d Binary files /dev/null and b/assets/tournament_logos/grayhound_gaming_43px_grayhound_gaming_allmode_png.png differ diff --git a/assets/tournament_logos/hellraisers_57px_hellraisers_2014_allmode_png.png b/assets/tournament_logos/hellraisers_57px_hellraisers_2014_allmode_png.png new file mode 100644 index 00000000..f19ce460 Binary files /dev/null and b/assets/tournament_logos/hellraisers_57px_hellraisers_2014_allmode_png.png differ diff --git a/assets/tournament_logos/hellraisers_57px_hellraisers_2018_allmode_png.png b/assets/tournament_logos/hellraisers_57px_hellraisers_2018_allmode_png.png new file mode 100644 index 00000000..3ab6a881 Binary files /dev/null and b/assets/tournament_logos/hellraisers_57px_hellraisers_2018_allmode_png.png differ diff --git a/assets/tournament_logos/heroic_100px_heroic_2019_lightmode_png.png b/assets/tournament_logos/heroic_100px_heroic_2019_lightmode_png.png new file mode 100644 index 00000000..8969b48d Binary files /dev/null and b/assets/tournament_logos/heroic_100px_heroic_2019_lightmode_png.png differ diff --git a/assets/tournament_logos/heroic_57px_heroic_2023_allmode_png.png b/assets/tournament_logos/heroic_57px_heroic_2023_allmode_png.png new file mode 100644 index 00000000..e0e05b6b Binary files /dev/null and b/assets/tournament_logos/heroic_57px_heroic_2023_allmode_png.png differ diff --git a/assets/tournament_logos/heroic_57px_heroic_2024_allmode_png.png b/assets/tournament_logos/heroic_57px_heroic_2024_allmode_png.png new file mode 100644 index 00000000..e3a9eaeb Binary files /dev/null and b/assets/tournament_logos/heroic_57px_heroic_2024_allmode_png.png differ diff --git a/assets/tournament_logos/iem_katowice_2019.png b/assets/tournament_logos/iem_katowice_2019.png index 04615159..a5b863ec 100644 Binary files a/assets/tournament_logos/iem_katowice_2019.png and b/assets/tournament_logos/iem_katowice_2019.png differ diff --git a/assets/tournament_logos/iem_rio_2022.png b/assets/tournament_logos/iem_rio_2022.png index 89712193..87beaad9 100644 Binary files a/assets/tournament_logos/iem_rio_2022.png and b/assets/tournament_logos/iem_rio_2022.png differ diff --git a/assets/tournament_logos/ihc_esports_42px_ihc_esports_allmode_png.png b/assets/tournament_logos/ihc_esports_42px_ihc_esports_allmode_png.png new file mode 100644 index 00000000..967018d6 Binary files /dev/null and b/assets/tournament_logos/ihc_esports_42px_ihc_esports_allmode_png.png differ diff --git a/assets/tournament_logos/immortals_50px_immortals_allmode_png.png b/assets/tournament_logos/immortals_50px_immortals_allmode_png.png new file mode 100644 index 00000000..bd4ac0b4 Binary files /dev/null and b/assets/tournament_logos/immortals_50px_immortals_allmode_png.png differ diff --git a/assets/tournament_logos/imperial_esports_39px_imperial_esports_2025_allmode_png.png b/assets/tournament_logos/imperial_esports_39px_imperial_esports_2025_allmode_png.png new file mode 100644 index 00000000..08f42e4a Binary files /dev/null and b/assets/tournament_logos/imperial_esports_39px_imperial_esports_2025_allmode_png.png differ diff --git a/assets/tournament_logos/imperial_esports_44px_imperial_esports_2022_allmode_png.png b/assets/tournament_logos/imperial_esports_44px_imperial_esports_2022_allmode_png.png new file mode 100644 index 00000000..07dc1411 Binary files /dev/null and b/assets/tournament_logos/imperial_esports_44px_imperial_esports_2022_allmode_png.png differ diff --git a/assets/tournament_logos/into_the_breach_47px_into_the_breach_pink_2022_allmode_png.png b/assets/tournament_logos/into_the_breach_47px_into_the_breach_pink_2022_allmode_png.png new file mode 100644 index 00000000..46a1a4a8 Binary files /dev/null and b/assets/tournament_logos/into_the_breach_47px_into_the_breach_pink_2022_allmode_png.png differ diff --git a/assets/tournament_logos/intz_esports_79px_intz_esports_allmode_png.png b/assets/tournament_logos/intz_esports_79px_intz_esports_allmode_png.png new file mode 100644 index 00000000..3608b1aa Binary files /dev/null and b/assets/tournament_logos/intz_esports_79px_intz_esports_allmode_png.png differ diff --git a/assets/tournament_logos/keyd_stars_38px_keyd_stars_2015_allmode_png.png b/assets/tournament_logos/keyd_stars_38px_keyd_stars_2015_allmode_png.png new file mode 100644 index 00000000..fee19650 Binary files /dev/null and b/assets/tournament_logos/keyd_stars_38px_keyd_stars_2015_allmode_png.png differ diff --git a/assets/tournament_logos/koi_31px_koi_2024_blue_allmode_png.png b/assets/tournament_logos/koi_31px_koi_2024_blue_allmode_png.png new file mode 100644 index 00000000..1248af02 Binary files /dev/null and b/assets/tournament_logos/koi_31px_koi_2024_blue_allmode_png.png differ diff --git a/assets/tournament_logos/legacy_49px_legacy_allmode_png.png b/assets/tournament_logos/legacy_49px_legacy_allmode_png.png new file mode 100644 index 00000000..97ff1728 Binary files /dev/null and b/assets/tournament_logos/legacy_49px_legacy_allmode_png.png differ diff --git a/assets/tournament_logos/lgb_esports_43px_lgb_esports_allmode_png.png b/assets/tournament_logos/lgb_esports_43px_lgb_esports_allmode_png.png new file mode 100644 index 00000000..5c4ecf94 Binary files /dev/null and b/assets/tournament_logos/lgb_esports_43px_lgb_esports_allmode_png.png differ diff --git a/assets/tournament_logos/london_conspiracy_50px_london_conspiracy_allmode_png.png b/assets/tournament_logos/london_conspiracy_50px_london_conspiracy_allmode_png.png new file mode 100644 index 00000000..a141daff Binary files /dev/null and b/assets/tournament_logos/london_conspiracy_50px_london_conspiracy_allmode_png.png differ diff --git a/assets/tournament_logos/luminosity_gaming_51px_luminosity_gaming_allmode_png.png b/assets/tournament_logos/luminosity_gaming_51px_luminosity_gaming_allmode_png.png new file mode 100644 index 00000000..3e9ae77b Binary files /dev/null and b/assets/tournament_logos/luminosity_gaming_51px_luminosity_gaming_allmode_png.png differ diff --git a/assets/tournament_logos/lynn_vision_gaming_56px_lynn_vision_gaming_2020_allmode_png.png b/assets/tournament_logos/lynn_vision_gaming_56px_lynn_vision_gaming_2020_allmode_png.png new file mode 100644 index 00000000..2bc139a6 Binary files /dev/null and b/assets/tournament_logos/lynn_vision_gaming_56px_lynn_vision_gaming_2020_allmode_png.png differ diff --git a/assets/tournament_logos/lynn_vision_gaming_74px_lynn_vision_gaming_2024_allmode_png.png b/assets/tournament_logos/lynn_vision_gaming_74px_lynn_vision_gaming_2024_allmode_png.png new file mode 100644 index 00000000..02bd2ed5 Binary files /dev/null and b/assets/tournament_logos/lynn_vision_gaming_74px_lynn_vision_gaming_2024_allmode_png.png differ diff --git a/assets/tournament_logos/m80_44px_m80_2023_allmode_png.png b/assets/tournament_logos/m80_44px_m80_2023_allmode_png.png new file mode 100644 index 00000000..c0574f5c Binary files /dev/null and b/assets/tournament_logos/m80_44px_m80_2023_allmode_png.png differ diff --git a/assets/tournament_logos/m80_52px_m80_esports_2024_lightmode_png.png b/assets/tournament_logos/m80_52px_m80_esports_2024_lightmode_png.png new file mode 100644 index 00000000..75ddaa77 Binary files /dev/null and b/assets/tournament_logos/m80_52px_m80_esports_2024_lightmode_png.png differ diff --git a/assets/tournament_logos/metizport_50px_metizport_2023_allmode_png.png b/assets/tournament_logos/metizport_50px_metizport_2023_allmode_png.png new file mode 100644 index 00000000..7f0aa5b6 Binary files /dev/null and b/assets/tournament_logos/metizport_50px_metizport_2023_allmode_png.png differ diff --git a/assets/tournament_logos/mibr_100px_mibr_2018_lightmode_png.png b/assets/tournament_logos/mibr_100px_mibr_2018_lightmode_png.png new file mode 100644 index 00000000..00b5c5cc Binary files /dev/null and b/assets/tournament_logos/mibr_100px_mibr_2018_lightmode_png.png differ diff --git a/assets/tournament_logos/misfits_gaming_36px_misfits_gaming_2017_allmode_png.png b/assets/tournament_logos/misfits_gaming_36px_misfits_gaming_2017_allmode_png.png new file mode 100644 index 00000000..59c7e730 Binary files /dev/null and b/assets/tournament_logos/misfits_gaming_36px_misfits_gaming_2017_allmode_png.png differ diff --git a/assets/tournament_logos/mlg_columbus_2016.png b/assets/tournament_logos/mlg_columbus_2016.png index 8ae0019e..6278293d 100644 Binary files a/assets/tournament_logos/mlg_columbus_2016.png and b/assets/tournament_logos/mlg_columbus_2016.png differ diff --git a/assets/tournament_logos/monte_35px_monte_2022_allmode_png.png b/assets/tournament_logos/monte_35px_monte_2022_allmode_png.png new file mode 100644 index 00000000..29f94ac0 Binary files /dev/null and b/assets/tournament_logos/monte_35px_monte_2022_allmode_png.png differ diff --git a/assets/tournament_logos/mousesports_92px_mousesports_2016_allmode_png.png b/assets/tournament_logos/mousesports_92px_mousesports_2016_allmode_png.png new file mode 100644 index 00000000..c7be36cd Binary files /dev/null and b/assets/tournament_logos/mousesports_92px_mousesports_2016_allmode_png.png differ diff --git a/assets/tournament_logos/mousesports_92px_mousesports_new_png.png b/assets/tournament_logos/mousesports_92px_mousesports_new_png.png new file mode 100644 index 00000000..934ded0d Binary files /dev/null and b/assets/tournament_logos/mousesports_92px_mousesports_new_png.png differ diff --git a/assets/tournament_logos/mouz_47px_mouz_2021_allmode_png.png b/assets/tournament_logos/mouz_47px_mouz_2021_allmode_png.png new file mode 100644 index 00000000..aeb69a87 Binary files /dev/null and b/assets/tournament_logos/mouz_47px_mouz_2021_allmode_png.png differ diff --git a/assets/tournament_logos/mouz_92px_mousesports_2016_allmode_png.png b/assets/tournament_logos/mouz_92px_mousesports_2016_allmode_png.png new file mode 100644 index 00000000..c7be36cd Binary files /dev/null and b/assets/tournament_logos/mouz_92px_mousesports_2016_allmode_png.png differ diff --git a/assets/tournament_logos/mouz_92px_mousesports_new_png.png b/assets/tournament_logos/mouz_92px_mousesports_new_png.png new file mode 100644 index 00000000..934ded0d Binary files /dev/null and b/assets/tournament_logos/mouz_92px_mousesports_new_png.png differ diff --git a/assets/tournament_logos/movistar_riders_69px_movistar_riders_allmode_png.png b/assets/tournament_logos/movistar_riders_69px_movistar_riders_allmode_png.png new file mode 100644 index 00000000..8bce17b4 Binary files /dev/null and b/assets/tournament_logos/movistar_riders_69px_movistar_riders_allmode_png.png differ diff --git a/assets/tournament_logos/myxmg_65px_myxmg_allmode_png.png b/assets/tournament_logos/myxmg_65px_myxmg_allmode_png.png new file mode 100644 index 00000000..d16e6bdd Binary files /dev/null and b/assets/tournament_logos/myxmg_65px_myxmg_allmode_png.png differ diff --git a/assets/tournament_logos/n_faculty_55px_n_faculty_allmode_png.png b/assets/tournament_logos/n_faculty_55px_n_faculty_allmode_png.png new file mode 100644 index 00000000..db69b12a Binary files /dev/null and b/assets/tournament_logos/n_faculty_55px_n_faculty_allmode_png.png differ diff --git a/assets/tournament_logos/natus_vincere_56px_natus_vincere_allmode_png.png b/assets/tournament_logos/natus_vincere_56px_natus_vincere_allmode_png.png new file mode 100644 index 00000000..9e37f76f Binary files /dev/null and b/assets/tournament_logos/natus_vincere_56px_natus_vincere_allmode_png.png differ diff --git a/assets/tournament_logos/natus_vincere_57px_natus_vincere_2021_lightmode_png.png b/assets/tournament_logos/natus_vincere_57px_natus_vincere_2021_lightmode_png.png new file mode 100644 index 00000000..1738d2a3 Binary files /dev/null and b/assets/tournament_logos/natus_vincere_57px_natus_vincere_2021_lightmode_png.png differ diff --git a/assets/tournament_logos/nemiga_gaming_50px_nemiga_gaming_2020_temporary_png.png b/assets/tournament_logos/nemiga_gaming_50px_nemiga_gaming_2020_temporary_png.png new file mode 100644 index 00000000..4092769a Binary files /dev/null and b/assets/tournament_logos/nemiga_gaming_50px_nemiga_gaming_2020_temporary_png.png differ diff --git a/assets/tournament_logos/ninjas_in_pyjamas_50px_ninjas_in_pyjamas_2012_allmode_png.png b/assets/tournament_logos/ninjas_in_pyjamas_50px_ninjas_in_pyjamas_2012_allmode_png.png new file mode 100644 index 00000000..a7d841da Binary files /dev/null and b/assets/tournament_logos/ninjas_in_pyjamas_50px_ninjas_in_pyjamas_2012_allmode_png.png differ diff --git a/assets/tournament_logos/ninjas_in_pyjamas_50px_ninjas_in_pyjamas_2017_lightmode_png.png b/assets/tournament_logos/ninjas_in_pyjamas_50px_ninjas_in_pyjamas_2017_lightmode_png.png new file mode 100644 index 00000000..7a82ecb5 Binary files /dev/null and b/assets/tournament_logos/ninjas_in_pyjamas_50px_ninjas_in_pyjamas_2017_lightmode_png.png differ diff --git a/assets/tournament_logos/ninjas_in_pyjamas_50px_ninjas_in_pyjamas_2021_lightmode_png.png b/assets/tournament_logos/ninjas_in_pyjamas_50px_ninjas_in_pyjamas_2021_lightmode_png.png new file mode 100644 index 00000000..df0028bb Binary files /dev/null and b/assets/tournament_logos/ninjas_in_pyjamas_50px_ninjas_in_pyjamas_2021_lightmode_png.png differ diff --git a/assets/tournament_logos/north_48px_north_lightmode_png.png b/assets/tournament_logos/north_48px_north_lightmode_png.png new file mode 100644 index 00000000..be99cb1e Binary files /dev/null and b/assets/tournament_logos/north_48px_north_lightmode_png.png differ diff --git a/assets/tournament_logos/nrg_100px_nrg_2024_lightmode_png.png b/assets/tournament_logos/nrg_100px_nrg_2024_lightmode_png.png new file mode 100644 index 00000000..f5147803 Binary files /dev/null and b/assets/tournament_logos/nrg_100px_nrg_2024_lightmode_png.png differ diff --git a/assets/tournament_logos/nrg_89px_nrg_2019_lightmode_png.png b/assets/tournament_logos/nrg_89px_nrg_2019_lightmode_png.png new file mode 100644 index 00000000..4737310a Binary files /dev/null and b/assets/tournament_logos/nrg_89px_nrg_2019_lightmode_png.png differ diff --git a/assets/tournament_logos/nrg_esports_76px_nrg_esports_2017_lightmode_png.png b/assets/tournament_logos/nrg_esports_76px_nrg_esports_2017_lightmode_png.png new file mode 100644 index 00000000..5fd9000a Binary files /dev/null and b/assets/tournament_logos/nrg_esports_76px_nrg_esports_2017_lightmode_png.png differ diff --git a/assets/tournament_logos/nrg_esports_89px_nrg_2019_lightmode_png.png b/assets/tournament_logos/nrg_esports_89px_nrg_2019_lightmode_png.png new file mode 100644 index 00000000..4737310a Binary files /dev/null and b/assets/tournament_logos/nrg_esports_89px_nrg_2019_lightmode_png.png differ diff --git a/assets/tournament_logos/og_34px_og_rb_allmode_png.png b/assets/tournament_logos/og_34px_og_rb_allmode_png.png new file mode 100644 index 00000000..1a50627e Binary files /dev/null and b/assets/tournament_logos/og_34px_og_rb_allmode_png.png differ diff --git a/assets/tournament_logos/optic_gaming_77px_optic_gaming_lightmode_png.png b/assets/tournament_logos/optic_gaming_77px_optic_gaming_lightmode_png.png new file mode 100644 index 00000000..04f96fa0 Binary files /dev/null and b/assets/tournament_logos/optic_gaming_77px_optic_gaming_lightmode_png.png differ diff --git a/assets/tournament_logos/outsiders_49px_outsiders_csgo_allmode_png.png b/assets/tournament_logos/outsiders_49px_outsiders_csgo_allmode_png.png new file mode 100644 index 00000000..cfae73b7 Binary files /dev/null and b/assets/tournament_logos/outsiders_49px_outsiders_csgo_allmode_png.png differ diff --git a/assets/tournament_logos/pain_gaming_75px_pain_gaming_2017_lightmode_png.png b/assets/tournament_logos/pain_gaming_75px_pain_gaming_2017_lightmode_png.png new file mode 100644 index 00000000..cabc1445 Binary files /dev/null and b/assets/tournament_logos/pain_gaming_75px_pain_gaming_2017_lightmode_png.png differ diff --git a/assets/tournament_logos/pain_gaming_75px_pain_gaming_2023_lightmode_png.png b/assets/tournament_logos/pain_gaming_75px_pain_gaming_2023_lightmode_png.png new file mode 100644 index 00000000..b87c3c96 Binary files /dev/null and b/assets/tournament_logos/pain_gaming_75px_pain_gaming_2023_lightmode_png.png differ diff --git a/assets/tournament_logos/parivision_56px_parivision_allmode_png.png b/assets/tournament_logos/parivision_56px_parivision_allmode_png.png new file mode 100644 index 00000000..9c55f743 Binary files /dev/null and b/assets/tournament_logos/parivision_56px_parivision_allmode_png.png differ diff --git a/assets/tournament_logos/passion_ua_27px_passion_ua_2024_allmode_png.png b/assets/tournament_logos/passion_ua_27px_passion_ua_2024_allmode_png.png new file mode 100644 index 00000000..275affa9 Binary files /dev/null and b/assets/tournament_logos/passion_ua_27px_passion_ua_2024_allmode_png.png differ diff --git a/assets/tournament_logos/passion_ua_27px_passion_ua_2025_lightmode_png.png b/assets/tournament_logos/passion_ua_27px_passion_ua_2025_lightmode_png.png new file mode 100644 index 00000000..eecc2a47 Binary files /dev/null and b/assets/tournament_logos/passion_ua_27px_passion_ua_2025_lightmode_png.png differ diff --git a/assets/tournament_logos/penta_sports_37px_penta_sports_lightmode_png.png b/assets/tournament_logos/penta_sports_37px_penta_sports_lightmode_png.png new file mode 100644 index 00000000..51661867 Binary files /dev/null and b/assets/tournament_logos/penta_sports_37px_penta_sports_lightmode_png.png differ diff --git a/assets/tournament_logos/perfect_world_shanghai_2024.png b/assets/tournament_logos/perfect_world_shanghai_2024.png index 2f6e1625..c0e2f501 100644 Binary files a/assets/tournament_logos/perfect_world_shanghai_2024.png and b/assets/tournament_logos/perfect_world_shanghai_2024.png differ diff --git a/assets/tournament_logos/pgl_antwerp_2022.png b/assets/tournament_logos/pgl_antwerp_2022.png index 05a53948..6c8321c9 100644 Binary files a/assets/tournament_logos/pgl_antwerp_2022.png and b/assets/tournament_logos/pgl_antwerp_2022.png differ diff --git a/assets/tournament_logos/pgl_copenhagen_2024.png b/assets/tournament_logos/pgl_copenhagen_2024.png index bf81b7e2..daa4771d 100644 Binary files a/assets/tournament_logos/pgl_copenhagen_2024.png and b/assets/tournament_logos/pgl_copenhagen_2024.png differ diff --git a/assets/tournament_logos/pgl_krak_w_2017.png b/assets/tournament_logos/pgl_krak_w_2017.png new file mode 100644 index 00000000..ca976a3c Binary files /dev/null and b/assets/tournament_logos/pgl_krak_w_2017.png differ diff --git a/assets/tournament_logos/pgl_stockholm_2021.png b/assets/tournament_logos/pgl_stockholm_2021.png index fddc4dc2..e09436c3 100644 Binary files a/assets/tournament_logos/pgl_stockholm_2021.png and b/assets/tournament_logos/pgl_stockholm_2021.png differ diff --git a/assets/tournament_logos/planetkey_dynamics_68px_planetkey_dynamics_allmode_png.png b/assets/tournament_logos/planetkey_dynamics_68px_planetkey_dynamics_allmode_png.png new file mode 100644 index 00000000..a168222d Binary files /dev/null and b/assets/tournament_logos/planetkey_dynamics_68px_planetkey_dynamics_allmode_png.png differ diff --git a/assets/tournament_logos/quantum_bellator_fire_38px_quantum_bellator_fire_allmode_png.png b/assets/tournament_logos/quantum_bellator_fire_38px_quantum_bellator_fire_allmode_png.png new file mode 100644 index 00000000..74181197 Binary files /dev/null and b/assets/tournament_logos/quantum_bellator_fire_38px_quantum_bellator_fire_allmode_png.png differ diff --git a/assets/tournament_logos/rare_atom_55px_rare_atom_2024_allmode_png.png b/assets/tournament_logos/rare_atom_55px_rare_atom_2024_allmode_png.png new file mode 100644 index 00000000..143b8316 Binary files /dev/null and b/assets/tournament_logos/rare_atom_55px_rare_atom_2024_allmode_png.png differ diff --git a/assets/tournament_logos/reason_gaming_49px_reason_gaming_allmode_png.png b/assets/tournament_logos/reason_gaming_49px_reason_gaming_allmode_png.png new file mode 100644 index 00000000..57661811 Binary files /dev/null and b/assets/tournament_logos/reason_gaming_49px_reason_gaming_allmode_png.png differ diff --git a/assets/tournament_logos/recursive_esports_50px_recursive_esports_2014_allmode_png.png b/assets/tournament_logos/recursive_esports_50px_recursive_esports_2014_allmode_png.png new file mode 100644 index 00000000..42e734e4 Binary files /dev/null and b/assets/tournament_logos/recursive_esports_50px_recursive_esports_2014_allmode_png.png differ diff --git a/assets/tournament_logos/red_canids_57px_red_canids_allmode_png.png b/assets/tournament_logos/red_canids_57px_red_canids_allmode_png.png new file mode 100644 index 00000000..ac898003 Binary files /dev/null and b/assets/tournament_logos/red_canids_57px_red_canids_allmode_png.png differ diff --git a/assets/tournament_logos/renegades_47px_renegades_2019_allmode_png.png b/assets/tournament_logos/renegades_47px_renegades_2019_allmode_png.png new file mode 100644 index 00000000..63676ab4 Binary files /dev/null and b/assets/tournament_logos/renegades_47px_renegades_2019_allmode_png.png differ diff --git a/assets/tournament_logos/renegades_47px_renegades_png.png b/assets/tournament_logos/renegades_47px_renegades_png.png new file mode 100644 index 00000000..46cb29dd Binary files /dev/null and b/assets/tournament_logos/renegades_47px_renegades_png.png differ diff --git a/assets/tournament_logos/rogue_44px_rogue_allmode_png.png b/assets/tournament_logos/rogue_44px_rogue_allmode_png.png new file mode 100644 index 00000000..b89a4e9e Binary files /dev/null and b/assets/tournament_logos/rogue_44px_rogue_allmode_png.png differ diff --git a/assets/tournament_logos/saw_60px_saw_2021_allmode_png.png b/assets/tournament_logos/saw_60px_saw_2021_allmode_png.png new file mode 100644 index 00000000..e1ddfb77 Binary files /dev/null and b/assets/tournament_logos/saw_60px_saw_2021_allmode_png.png differ diff --git a/assets/tournament_logos/sharks_esports_54px_sharks_esports_png.png b/assets/tournament_logos/sharks_esports_54px_sharks_esports_png.png new file mode 100644 index 00000000..7f3f8d33 Binary files /dev/null and b/assets/tournament_logos/sharks_esports_54px_sharks_esports_png.png differ diff --git a/assets/tournament_logos/sk_gaming_50px_sk_gaming_2003_lightmode_png.png b/assets/tournament_logos/sk_gaming_50px_sk_gaming_2003_lightmode_png.png new file mode 100644 index 00000000..f52f7ae5 Binary files /dev/null and b/assets/tournament_logos/sk_gaming_50px_sk_gaming_2003_lightmode_png.png differ diff --git a/assets/tournament_logos/space_soldiers_57px_space_soldiers_lightmode_png.png b/assets/tournament_logos/space_soldiers_57px_space_soldiers_lightmode_png.png new file mode 100644 index 00000000..5402267d Binary files /dev/null and b/assets/tournament_logos/space_soldiers_57px_space_soldiers_lightmode_png.png differ diff --git a/assets/tournament_logos/splyce_43px_splyce_2015_allmode_png.png b/assets/tournament_logos/splyce_43px_splyce_2015_allmode_png.png new file mode 100644 index 00000000..accbff9c Binary files /dev/null and b/assets/tournament_logos/splyce_43px_splyce_2015_allmode_png.png differ diff --git a/assets/tournament_logos/sprout_29px_sprout_allmode_png.png b/assets/tournament_logos/sprout_29px_sprout_allmode_png.png new file mode 100644 index 00000000..ba264fa8 Binary files /dev/null and b/assets/tournament_logos/sprout_29px_sprout_allmode_png.png differ diff --git a/assets/tournament_logos/starladder_berlin_2019.png b/assets/tournament_logos/starladder_berlin_2019.png index c9bf106f..24783b37 100644 Binary files a/assets/tournament_logos/starladder_berlin_2019.png and b/assets/tournament_logos/starladder_berlin_2019.png differ diff --git a/assets/tournament_logos/starladder_budapest_2025.png b/assets/tournament_logos/starladder_budapest_2025.png index ca771f88..a871db57 100644 Binary files a/assets/tournament_logos/starladder_budapest_2025.png and b/assets/tournament_logos/starladder_budapest_2025.png differ diff --git a/assets/tournament_logos/syman_gaming_50px_syman_2018_png.png b/assets/tournament_logos/syman_gaming_50px_syman_2018_png.png new file mode 100644 index 00000000..bdc2a428 Binary files /dev/null and b/assets/tournament_logos/syman_gaming_50px_syman_2018_png.png differ diff --git a/assets/tournament_logos/team_dignitas_44px_team_dignitas_allmode_png.png b/assets/tournament_logos/team_dignitas_44px_team_dignitas_allmode_png.png new file mode 100644 index 00000000..5fe0b274 Binary files /dev/null and b/assets/tournament_logos/team_dignitas_44px_team_dignitas_allmode_png.png differ diff --git a/assets/tournament_logos/team_ebettle_38px_team_ebettle_allmode_png.png b/assets/tournament_logos/team_ebettle_38px_team_ebettle_allmode_png.png new file mode 100644 index 00000000..4d9ced66 Binary files /dev/null and b/assets/tournament_logos/team_ebettle_38px_team_ebettle_allmode_png.png differ diff --git a/assets/tournament_logos/team_envy_50px_team_envy_2018_lightmode_png.png b/assets/tournament_logos/team_envy_50px_team_envy_2018_lightmode_png.png new file mode 100644 index 00000000..07ccca60 Binary files /dev/null and b/assets/tournament_logos/team_envy_50px_team_envy_2018_lightmode_png.png differ diff --git a/assets/tournament_logos/team_envy_50px_team_envyus_lightmode_png.png b/assets/tournament_logos/team_envy_50px_team_envyus_lightmode_png.png new file mode 100644 index 00000000..d84c0bd8 Binary files /dev/null and b/assets/tournament_logos/team_envy_50px_team_envyus_lightmode_png.png differ diff --git a/assets/tournament_logos/team_envyus_50px_team_envyus_lightmode_png.png b/assets/tournament_logos/team_envyus_50px_team_envyus_lightmode_png.png new file mode 100644 index 00000000..d84c0bd8 Binary files /dev/null and b/assets/tournament_logos/team_envyus_50px_team_envyus_lightmode_png.png differ diff --git a/assets/tournament_logos/team_falcons_41px_team_falcons_2022_allmode_png.png b/assets/tournament_logos/team_falcons_41px_team_falcons_2022_allmode_png.png new file mode 100644 index 00000000..2c964230 Binary files /dev/null and b/assets/tournament_logos/team_falcons_41px_team_falcons_2022_allmode_png.png differ diff --git a/assets/tournament_logos/team_gamerlegion_50px_team_gamerlegion_allmode_png.png b/assets/tournament_logos/team_gamerlegion_50px_team_gamerlegion_allmode_png.png new file mode 100644 index 00000000..cf815276 Binary files /dev/null and b/assets/tournament_logos/team_gamerlegion_50px_team_gamerlegion_allmode_png.png differ diff --git a/assets/tournament_logos/team_ibuypower_86px_ibuypower_lightmode_png.png b/assets/tournament_logos/team_ibuypower_86px_ibuypower_lightmode_png.png new file mode 100644 index 00000000..e2a15e1c Binary files /dev/null and b/assets/tournament_logos/team_ibuypower_86px_ibuypower_lightmode_png.png differ diff --git a/assets/tournament_logos/team_immunity_61px_team_immunity_allmode_png.png b/assets/tournament_logos/team_immunity_61px_team_immunity_allmode_png.png new file mode 100644 index 00000000..9027178f Binary files /dev/null and b/assets/tournament_logos/team_immunity_61px_team_immunity_allmode_png.png differ diff --git a/assets/tournament_logos/team_kinguin_33px_team_kinguin_allmode_png.png b/assets/tournament_logos/team_kinguin_33px_team_kinguin_allmode_png.png new file mode 100644 index 00000000..ceabe80f Binary files /dev/null and b/assets/tournament_logos/team_kinguin_33px_team_kinguin_allmode_png.png differ diff --git a/assets/tournament_logos/team_ldlc_100px_team_ldlc_2012_lightmode_png.png b/assets/tournament_logos/team_ldlc_100px_team_ldlc_2012_lightmode_png.png new file mode 100644 index 00000000..6f30ba21 Binary files /dev/null and b/assets/tournament_logos/team_ldlc_100px_team_ldlc_2012_lightmode_png.png differ diff --git a/assets/tournament_logos/team_ldlc_com_100px_team_ldlc_2012_lightmode_png.png b/assets/tournament_logos/team_ldlc_com_100px_team_ldlc_2012_lightmode_png.png new file mode 100644 index 00000000..6f30ba21 Binary files /dev/null and b/assets/tournament_logos/team_ldlc_com_100px_team_ldlc_2012_lightmode_png.png differ diff --git a/assets/tournament_logos/team_liquid_44px_team_liquid_2020_lightmode_png.png b/assets/tournament_logos/team_liquid_44px_team_liquid_2020_lightmode_png.png new file mode 100644 index 00000000..e24898c3 Binary files /dev/null and b/assets/tournament_logos/team_liquid_44px_team_liquid_2020_lightmode_png.png differ diff --git a/assets/tournament_logos/team_liquid_44px_team_liquid_2023_lightmode_png.png b/assets/tournament_logos/team_liquid_44px_team_liquid_2023_lightmode_png.png new file mode 100644 index 00000000..f3fd9db2 Binary files /dev/null and b/assets/tournament_logos/team_liquid_44px_team_liquid_2023_lightmode_png.png differ diff --git a/assets/tournament_logos/team_liquid_44px_team_liquid_2024_lightmode_png.png b/assets/tournament_logos/team_liquid_44px_team_liquid_2024_lightmode_png.png new file mode 100644 index 00000000..3ae38185 Binary files /dev/null and b/assets/tournament_logos/team_liquid_44px_team_liquid_2024_lightmode_png.png differ diff --git a/assets/tournament_logos/team_liquid_50px_team_liquid_2017_lightmode_png.png b/assets/tournament_logos/team_liquid_50px_team_liquid_2017_lightmode_png.png new file mode 100644 index 00000000..cc2f5a1d Binary files /dev/null and b/assets/tournament_logos/team_liquid_50px_team_liquid_2017_lightmode_png.png differ diff --git a/assets/tournament_logos/team_liquid_50px_team_liquid_allmode_png.png b/assets/tournament_logos/team_liquid_50px_team_liquid_allmode_png.png new file mode 100644 index 00000000..873380c6 Binary files /dev/null and b/assets/tournament_logos/team_liquid_50px_team_liquid_allmode_png.png differ diff --git a/assets/tournament_logos/team_solomid_50px_team_solomid_lightmode_png.png b/assets/tournament_logos/team_solomid_50px_team_solomid_lightmode_png.png new file mode 100644 index 00000000..3a53eca0 Binary files /dev/null and b/assets/tournament_logos/team_solomid_50px_team_solomid_lightmode_png.png differ diff --git a/assets/tournament_logos/team_spirit_38px_team_spirit_2016_alt_allmode_png.png b/assets/tournament_logos/team_spirit_38px_team_spirit_2016_alt_allmode_png.png new file mode 100644 index 00000000..5794b4ba Binary files /dev/null and b/assets/tournament_logos/team_spirit_38px_team_spirit_2016_alt_allmode_png.png differ diff --git a/assets/tournament_logos/team_spirit_43px_team_spirit_2021_lightmode_png.png b/assets/tournament_logos/team_spirit_43px_team_spirit_2021_lightmode_png.png new file mode 100644 index 00000000..911b0eac Binary files /dev/null and b/assets/tournament_logos/team_spirit_43px_team_spirit_2021_lightmode_png.png differ diff --git a/assets/tournament_logos/team_spirit_43px_team_spirit_2022_lightmode_png.png b/assets/tournament_logos/team_spirit_43px_team_spirit_2022_lightmode_png.png new file mode 100644 index 00000000..a772d9af Binary files /dev/null and b/assets/tournament_logos/team_spirit_43px_team_spirit_2022_lightmode_png.png differ diff --git a/assets/tournament_logos/team_vitality_39px_team_vitality_2018_allmode_png.png b/assets/tournament_logos/team_vitality_39px_team_vitality_2018_allmode_png.png new file mode 100644 index 00000000..cdb04032 Binary files /dev/null and b/assets/tournament_logos/team_vitality_39px_team_vitality_2018_allmode_png.png differ diff --git a/assets/tournament_logos/team_vitality_40px_team_vitality_2021_allmode_png.png b/assets/tournament_logos/team_vitality_40px_team_vitality_2021_allmode_png.png new file mode 100644 index 00000000..1336759c Binary files /dev/null and b/assets/tournament_logos/team_vitality_40px_team_vitality_2021_allmode_png.png differ diff --git a/assets/tournament_logos/team_vitality_41px_team_vitality_2023_lightmode_png.png b/assets/tournament_logos/team_vitality_41px_team_vitality_2023_lightmode_png.png new file mode 100644 index 00000000..4b6c64dd Binary files /dev/null and b/assets/tournament_logos/team_vitality_41px_team_vitality_2023_lightmode_png.png differ diff --git a/assets/tournament_logos/team_wolf_53px_team_wolf_allmode_png.png b/assets/tournament_logos/team_wolf_53px_team_wolf_allmode_png.png new file mode 100644 index 00000000..26844866 Binary files /dev/null and b/assets/tournament_logos/team_wolf_53px_team_wolf_allmode_png.png differ diff --git a/assets/tournament_logos/the_huns_esports_45px_the_huns_esports_2025_allmode_png.png b/assets/tournament_logos/the_huns_esports_45px_the_huns_esports_2025_allmode_png.png new file mode 100644 index 00000000..3c006ece Binary files /dev/null and b/assets/tournament_logos/the_huns_esports_45px_the_huns_esports_2025_allmode_png.png differ diff --git a/assets/tournament_logos/the_mongolz_39px_the_mongolz_2024_03_allmode_png.png b/assets/tournament_logos/the_mongolz_39px_the_mongolz_2024_03_allmode_png.png new file mode 100644 index 00000000..5b7c394a Binary files /dev/null and b/assets/tournament_logos/the_mongolz_39px_the_mongolz_2024_03_allmode_png.png differ diff --git a/assets/tournament_logos/the_mongolz_41px_the_mongolz_lightmode_png.png b/assets/tournament_logos/the_mongolz_41px_the_mongolz_lightmode_png.png new file mode 100644 index 00000000..a773fb80 Binary files /dev/null and b/assets/tournament_logos/the_mongolz_41px_the_mongolz_lightmode_png.png differ diff --git a/assets/tournament_logos/titan_46px_titan_lightmode_png.png b/assets/tournament_logos/titan_46px_titan_lightmode_png.png new file mode 100644 index 00000000..a6a1d3f7 Binary files /dev/null and b/assets/tournament_logos/titan_46px_titan_lightmode_png.png differ diff --git a/assets/tournament_logos/tsm_50px_team_solomid_lightmode_png.png b/assets/tournament_logos/tsm_50px_team_solomid_lightmode_png.png new file mode 100644 index 00000000..3a53eca0 Binary files /dev/null and b/assets/tournament_logos/tsm_50px_team_solomid_lightmode_png.png differ diff --git a/assets/tournament_logos/tyloo_56px_tyloo_2016_allmode_png.png b/assets/tournament_logos/tyloo_56px_tyloo_2016_allmode_png.png new file mode 100644 index 00000000..98c2335b Binary files /dev/null and b/assets/tournament_logos/tyloo_56px_tyloo_2016_allmode_png.png differ diff --git a/assets/tournament_logos/universal_soldiers_50px_universal_soldiers_allmode_png.png b/assets/tournament_logos/universal_soldiers_50px_universal_soldiers_allmode_png.png new file mode 100644 index 00000000..6606a3ad Binary files /dev/null and b/assets/tournament_logos/universal_soldiers_50px_universal_soldiers_allmode_png.png differ diff --git a/assets/tournament_logos/vega_squadron_47px_vega_squadron_2016_allmode_png.png b/assets/tournament_logos/vega_squadron_47px_vega_squadron_2016_allmode_png.png new file mode 100644 index 00000000..6cbd1e2f Binary files /dev/null and b/assets/tournament_logos/vega_squadron_47px_vega_squadron_2016_allmode_png.png differ diff --git a/assets/tournament_logos/verygames_88px_verygames_allmode_png.png b/assets/tournament_logos/verygames_88px_verygames_allmode_png.png new file mode 100644 index 00000000..cb9a9802 Binary files /dev/null and b/assets/tournament_logos/verygames_88px_verygames_allmode_png.png differ diff --git a/assets/tournament_logos/vexed_gaming_41px_vexed_gaming_allmode_png.png b/assets/tournament_logos/vexed_gaming_41px_vexed_gaming_allmode_png.png new file mode 100644 index 00000000..f1a3321e Binary files /dev/null and b/assets/tournament_logos/vexed_gaming_41px_vexed_gaming_allmode_png.png differ diff --git a/assets/tournament_logos/vici_gaming_49px_vici_gaming_allmode_png.png b/assets/tournament_logos/vici_gaming_49px_vici_gaming_allmode_png.png new file mode 100644 index 00000000..394ac72f Binary files /dev/null and b/assets/tournament_logos/vici_gaming_49px_vici_gaming_allmode_png.png differ diff --git a/assets/tournament_logos/virtus_pro_48px_virtus_pro_allmode_png.png b/assets/tournament_logos/virtus_pro_48px_virtus_pro_allmode_png.png new file mode 100644 index 00000000..3f553fca Binary files /dev/null and b/assets/tournament_logos/virtus_pro_48px_virtus_pro_allmode_png.png differ diff --git a/assets/tournament_logos/virtus_pro_48px_vp_megafon_style_png.png b/assets/tournament_logos/virtus_pro_48px_vp_megafon_style_png.png new file mode 100644 index 00000000..215132bf Binary files /dev/null and b/assets/tournament_logos/virtus_pro_48px_vp_megafon_style_png.png differ diff --git a/assets/tournament_logos/virtus_pro_51px_virtus_pro_2019_allmode_png.png b/assets/tournament_logos/virtus_pro_51px_virtus_pro_2019_allmode_png.png new file mode 100644 index 00000000..9838cb38 Binary files /dev/null and b/assets/tournament_logos/virtus_pro_51px_virtus_pro_2019_allmode_png.png differ diff --git a/assets/tournament_logos/vox_eminor_89px_vox_eminor_allmode_png.png b/assets/tournament_logos/vox_eminor_89px_vox_eminor_allmode_png.png new file mode 100644 index 00000000..457ed5c7 Binary files /dev/null and b/assets/tournament_logos/vox_eminor_89px_vox_eminor_allmode_png.png differ diff --git a/assets/tournament_logos/wildcard_42px_wildcard_2024_lightmode_png.png b/assets/tournament_logos/wildcard_42px_wildcard_2024_lightmode_png.png new file mode 100644 index 00000000..e39289f3 Binary files /dev/null and b/assets/tournament_logos/wildcard_42px_wildcard_2024_lightmode_png.png differ diff --git a/assets/tournament_logos/winstrike_team_75px_winstrike_team_2018_allmode_png.png b/assets/tournament_logos/winstrike_team_75px_winstrike_team_2018_allmode_png.png new file mode 100644 index 00000000..7cde928d Binary files /dev/null and b/assets/tournament_logos/winstrike_team_75px_winstrike_team_2018_allmode_png.png differ diff --git a/assets/tournament_logos/xapso_50px_xapso_allmode_png.png b/assets/tournament_logos/xapso_50px_xapso_allmode_png.png new file mode 100644 index 00000000..55c24c0c Binary files /dev/null and b/assets/tournament_logos/xapso_50px_xapso_allmode_png.png differ diff --git a/lib/core/utils/date_format_helper.dart b/lib/core/utils/date_format_helper.dart index d0f6125f..a0fdefd4 100644 --- a/lib/core/utils/date_format_helper.dart +++ b/lib/core/utils/date_format_helper.dart @@ -26,4 +26,20 @@ class DateFormatHelper { return '$day $month $year'; } + + static String? formatDateRange(String? start, String? end) { + final formattedStart = formatReleaseDate(start); + final formattedEnd = formatReleaseDate(end); + + if (formattedStart == null || formattedStart.isEmpty) { + return formattedEnd; + } + if (formattedEnd == null || formattedEnd.isEmpty) { + return formattedStart; + } + if (start == end) { + return formattedStart; + } + return '$formattedStart - $formattedEnd'; + } } diff --git a/lib/core/utils/team_name_helper.dart b/lib/core/utils/team_name_helper.dart new file mode 100644 index 00000000..9fc9a09c --- /dev/null +++ b/lib/core/utils/team_name_helper.dart @@ -0,0 +1,27 @@ +class TeamNameHelper { + const TeamNameHelper._(); + + static const Map _aliases = { + 'complexity gaming': 'Complexity', + 'complexitygaming': 'Complexity', + 'gambit gaming': 'Gambit Esports', + 'gambit esports': 'Gambit Esports', + 'heroic': 'HEROIC', + 'mousesports': 'MOUZ', + 'team envy': 'Team EnVyUs', + }; + + static String canonicalize(String rawName) { + final normalized = rawName.trim().replaceAll(RegExp(r'\s+'), ' '); + if (normalized.isEmpty) { + return normalized; + } + + final lower = normalized.toLowerCase(); + if (_aliases.containsKey(lower)) { + return _aliases[lower]!; + } + + return normalized; + } +} diff --git a/lib/data/models/container_dto.dart b/lib/data/models/container_dto.dart index 637b9147..90750f34 100644 --- a/lib/data/models/container_dto.dart +++ b/lib/data/models/container_dto.dart @@ -7,6 +7,8 @@ class ContainerDto { final String? sourceType; final String? sourceId; final String? sourceName; + final String? tournamentName; + final String? tournamentLogo; final String? currency; final int? cost; @@ -19,6 +21,8 @@ class ContainerDto { required this.sourceType, required this.sourceId, required this.sourceName, + required this.tournamentName, + required this.tournamentLogo, required this.currency, required this.cost, }); @@ -33,6 +37,8 @@ class ContainerDto { sourceType: json['sourceType'] as String?, sourceId: json['sourceId'] as String?, sourceName: json['sourceName'] as String?, + tournamentName: json['tournamentName'] as String?, + tournamentLogo: json['tournamentLogo'] as String?, currency: json['currency'] as String?, cost: (json['cost'] as num?)?.toInt(), ); diff --git a/lib/data/models/tournament_dto.dart b/lib/data/models/tournament_dto.dart new file mode 100644 index 00000000..7422a0dd --- /dev/null +++ b/lib/data/models/tournament_dto.dart @@ -0,0 +1,75 @@ +class TournamentDto { + final String name; + final String imagePath; + final String? releaseDate; + final String? startDate; + final String? endDate; + final String organizer; + final int souvenirPackageCount; + final int stickerContainerCount; + + const TournamentDto({ + required this.name, + required this.imagePath, + required this.releaseDate, + required this.startDate, + required this.endDate, + required this.organizer, + required this.souvenirPackageCount, + required this.stickerContainerCount, + }); + + int? get year { + final match = RegExp(r'(20\d{2}|201\d)').allMatches(name).lastOrNull; + if (match == null) return null; + return int.tryParse(match.group(0)!); + } + + bool get isCs2Era => (year ?? 0) >= 2023; + + String get eraLabel => isCs2Era ? 'CS2 Era' : 'CS:GO Era'; +} + +class TournamentTeamResultDto { + final String teamName; + final String? teamLogo; + final String tournamentName; + final String tournamentImagePath; + final String organizer; + final String place; + final String? startDate; + final String? endDate; + + const TournamentTeamResultDto({ + required this.teamName, + required this.teamLogo, + required this.tournamentName, + required this.tournamentImagePath, + required this.organizer, + required this.place, + required this.startDate, + required this.endDate, + }); +} + +class TournamentTeamSummaryDto { + final String teamName; + final String? teamLogo; + final int tournamentCount; + final int titleCount; + final String? bestPlace; + final String? latestTournamentName; + final String? latestTournamentImagePath; + final String? latestStartDate; + + const TournamentTeamSummaryDto({ + required this.teamName, + required this.teamLogo, + required this.tournamentCount, + required this.titleCount, + required this.bestPlace, + required this.latestTournamentName, + required this.latestTournamentImagePath, + required this.latestStartDate, + }); +} diff --git a/lib/data/models/tournament_metadata_dto.dart b/lib/data/models/tournament_metadata_dto.dart new file mode 100644 index 00000000..13215c61 --- /dev/null +++ b/lib/data/models/tournament_metadata_dto.dart @@ -0,0 +1,156 @@ +class TournamentPlacementDto { + final String place; + final String team; + final String? teamLogo; + + const TournamentPlacementDto({ + required this.place, + required this.team, + required this.teamLogo, + }); + + factory TournamentPlacementDto.fromJson(Map json) { + return TournamentPlacementDto( + place: json['place'] as String, + team: json['team'] as String, + teamLogo: json['teamLogo'] as String?, + ); + } +} + +class TournamentTeamRosterDto { + final String team; + final String? teamLogo; + final List players; + + const TournamentTeamRosterDto({ + required this.team, + required this.teamLogo, + required this.players, + }); + + factory TournamentTeamRosterDto.fromJson(Map json) { + return TournamentTeamRosterDto( + team: json['team'] as String, + teamLogo: json['teamLogo'] as String?, + players: (json['players'] as List? ?? const []) + .map((entry) => entry.toString()) + .where((entry) => entry.trim().isNotEmpty) + .toList(), + ); + } +} + +class TournamentStageDateDto { + final String phase; + final String? startDate; + final String? endDate; + + const TournamentStageDateDto({ + required this.phase, + required this.startDate, + required this.endDate, + }); + + factory TournamentStageDateDto.fromJson(Map json) { + return TournamentStageDateDto( + phase: json['phase'] as String, + startDate: json['startDate'] as String?, + endDate: json['endDate'] as String?, + ); + } +} + +class TournamentPlayoffMatchDto { + final String round; + final String team1; + final String team2; + final String? team1Logo; + final String? team2Logo; + final String? score1; + final String? score2; + final String? date; + + const TournamentPlayoffMatchDto({ + required this.round, + required this.team1, + required this.team2, + required this.team1Logo, + required this.team2Logo, + required this.score1, + required this.score2, + required this.date, + }); + + factory TournamentPlayoffMatchDto.fromJson(Map json) { + return TournamentPlayoffMatchDto( + round: json['round'] as String, + team1: json['team1'] as String, + team2: json['team2'] as String, + team1Logo: json['team1Logo'] as String?, + team2Logo: json['team2Logo'] as String?, + score1: json['score1'] as String?, + score2: json['score2'] as String?, + date: json['date'] as String?, + ); + } +} + +class TournamentMetadataDto { + final String name; + final String winner; + final String? tournamentLogo; + final String? startDate; + final String? endDate; + final List placements; + final List teamRosters; + final List stageDates; + final List playoffMatches; + + const TournamentMetadataDto({ + required this.name, + required this.winner, + required this.tournamentLogo, + required this.startDate, + required this.endDate, + required this.placements, + required this.teamRosters, + required this.stageDates, + required this.playoffMatches, + }); + + factory TournamentMetadataDto.fromJson(Map json) { + return TournamentMetadataDto( + name: json['name'] as String, + winner: json['winner'] as String, + tournamentLogo: json['tournamentLogo'] as String?, + startDate: json['startDate'] as String?, + endDate: json['endDate'] as String?, + placements: (json['placements'] as List) + .map( + (entry) => + TournamentPlacementDto.fromJson(entry as Map), + ) + .toList(), + teamRosters: (json['teamRosters'] as List? ?? const []) + .map( + (entry) => + TournamentTeamRosterDto.fromJson(entry as Map), + ) + .toList(), + stageDates: (json['stageDates'] as List? ?? const []) + .map( + (entry) => + TournamentStageDateDto.fromJson(entry as Map), + ) + .toList(), + playoffMatches: (json['playoffMatches'] as List? ?? const []) + .map( + (entry) => TournamentPlayoffMatchDto.fromJson( + entry as Map, + ), + ) + .toList(), + ); + } +} diff --git a/lib/data/models/tournament_player_dto.dart b/lib/data/models/tournament_player_dto.dart new file mode 100644 index 00000000..9acf11c7 --- /dev/null +++ b/lib/data/models/tournament_player_dto.dart @@ -0,0 +1,55 @@ +class TournamentPlayerAppearanceDto { + final String playerName; + final String? teamName; + final String? teamLogo; + final String? place; + final String tournamentName; + final String tournamentImagePath; + final String? startDate; + final String? endDate; + final int autographCount; + final List effects; + final String? sampleStickerImage; + + const TournamentPlayerAppearanceDto({ + required this.playerName, + required this.teamName, + required this.teamLogo, + required this.place, + required this.tournamentName, + required this.tournamentImagePath, + required this.startDate, + required this.endDate, + required this.autographCount, + required this.effects, + required this.sampleStickerImage, + }); +} + +class TournamentPlayerSummaryDto { + final String playerName; + final int tournamentCount; + final int autographCount; + final int titleCount; + final String? bestPlace; + final String? latestTournamentName; + final String? latestTournamentImagePath; + final String? latestStartDate; + final String? latestTeamName; + final String? latestTeamLogo; + final String? sampleStickerImage; + + const TournamentPlayerSummaryDto({ + required this.playerName, + required this.tournamentCount, + required this.autographCount, + required this.titleCount, + required this.bestPlace, + required this.latestTournamentName, + required this.latestTournamentImagePath, + required this.latestStartDate, + required this.latestTeamName, + required this.latestTeamLogo, + required this.sampleStickerImage, + }); +} diff --git a/lib/data/repositories/local_data_repository.dart b/lib/data/repositories/local_data_repository.dart index 4e1c962c..3058e9a7 100644 --- a/lib/data/repositories/local_data_repository.dart +++ b/lib/data/repositories/local_data_repository.dart @@ -2,6 +2,7 @@ import 'dart:convert'; import 'package:flutter/services.dart'; +import '../../core/utils/team_name_helper.dart'; import '../models/agent_collection_content_dto.dart'; import '../models/agent_dto.dart'; import '../models/container_content_dto.dart'; @@ -22,6 +23,9 @@ import '../models/reward_collection_content_dto.dart'; import '../models/skin_dto.dart'; import '../models/sticker_content_dto.dart'; import '../models/sticker_dto.dart'; +import '../models/tournament_dto.dart'; +import '../models/tournament_metadata_dto.dart'; +import '../models/tournament_player_dto.dart'; part 'local_data_repository_loaders.dart'; part 'local_data_repository_queries.dart'; diff --git a/lib/data/repositories/local_data_repository_loaders.dart b/lib/data/repositories/local_data_repository_loaders.dart index 9f7667c1..bfc0dc77 100644 --- a/lib/data/repositories/local_data_repository_loaders.dart +++ b/lib/data/repositories/local_data_repository_loaders.dart @@ -42,6 +42,13 @@ mixin _LocalDataRepositoryLoaders { return _loadDtoList('assets/data/charms.json', CharmDto.fromJson); } + Future> loadTournamentMetadata() async { + return _loadDtoList( + 'assets/data/tournament_metadata.json', + TournamentMetadataDto.fromJson, + ); + } + Future> loadContainerContents() async { return _loadDtoList( 'assets/data/container_contents.json', diff --git a/lib/data/repositories/local_data_repository_queries.dart b/lib/data/repositories/local_data_repository_queries.dart index 07c79113..8108b235 100644 --- a/lib/data/repositories/local_data_repository_queries.dart +++ b/lib/data/repositories/local_data_repository_queries.dart @@ -1,6 +1,606 @@ part of 'local_data_repository.dart'; mixin _LocalDataRepositoryQueries on _LocalDataRepositoryLoaders { + Future> loadTournaments() async { + final containers = await loadContainers(); + final stickers = await loadStickers(); + final stickerContents = await loadStickerContents(); + final metadata = await loadTournamentMetadata(); + final metadataByName = { + for (final entry in metadata) _canonicalTournamentName(entry.name): entry, + }; + + final containerById = { + for (final container in containers) container.id: container, + }; + final stickerById = {for (final sticker in stickers) sticker.id: sticker}; + final tournamentBuilders = {}; + + _TournamentBuilder ensureBuilder(String tournamentName) { + final canonicalName = _canonicalTournamentName(tournamentName); + if (_isIgnoredTournamentName(canonicalName)) { + return _TournamentBuilder(name: ''); + } + return tournamentBuilders.putIfAbsent( + canonicalName, + () => _TournamentBuilder(name: canonicalName), + ); + } + + for (final container in containers) { + final tournamentName = (container.tournamentName ?? '').trim(); + if (tournamentName.isEmpty) continue; + + final builder = ensureBuilder(tournamentName); + if (builder.name.isEmpty) continue; + builder.imagePath ??= + metadataByName[builder.name]?.tournamentLogo ?? + _preferredTournamentImage(container); + builder.releaseDate = _earlierDate( + builder.releaseDate, + container.releaseDate, + ); + + if (container.isSouvenirPackage) { + builder.souvenirContainerIds.add(container.id); + } + if (container.isStickerCapsule || container.isStickerCollection) { + builder.stickerContainerIds.add(container.id); + } + } + + for (final content in stickerContents) { + final container = containerById[content.containerId]; + if (container == null) continue; + + final tournamentNames = {}; + for (final stickerId in content.stickerIds) { + final tournamentName = (stickerById[stickerId]?.tournament ?? '') + .trim(); + if (tournamentName.isNotEmpty) { + tournamentNames.add(tournamentName); + } + } + + for (final tournamentName in tournamentNames) { + final builder = ensureBuilder(tournamentName); + if (builder.name.isEmpty) continue; + builder.imagePath ??= + metadataByName[builder.name]?.tournamentLogo ?? + _preferredTournamentImage(container); + builder.releaseDate = _earlierDate( + builder.releaseDate, + container.releaseDate, + ); + + if (container.isStickerCapsule || container.isStickerCollection) { + builder.stickerContainerIds.add(container.id); + } + } + } + + final tournaments = tournamentBuilders.values + .where((builder) => builder.imagePath != null) + .map((builder) { + final metadata = metadataByName[builder.name]; + return TournamentDto( + name: builder.name, + imagePath: ((metadata?.tournamentLogo ?? '').trim().isNotEmpty + ? metadata!.tournamentLogo! + : builder.imagePath!), + releaseDate: builder.releaseDate, + startDate: metadata?.startDate, + endDate: metadata?.endDate, + organizer: _inferTournamentOrganizer(builder.name), + souvenirPackageCount: builder.souvenirContainerIds.length, + stickerContainerCount: builder.stickerContainerIds.length, + ); + }) + .toList(); + + tournaments.sort((a, b) { + final byDate = (b.startDate ?? b.releaseDate ?? '').compareTo( + a.startDate ?? a.releaseDate ?? '', + ); + if (byDate != 0) return byDate; + return a.name.compareTo(b.name); + }); + + return tournaments; + } + + Future> loadTeamTournamentResults( + String teamName, + ) async { + final canonicalTeamName = TeamNameHelper.canonicalize(teamName); + final tournaments = await loadTournaments(); + final metadata = await loadTournamentMetadata(); + final tournamentByName = { + for (final tournament in tournaments) + _canonicalTournamentName(tournament.name): tournament, + }; + + final results = []; + for (final entry in metadata) { + final tournament = tournamentByName[_canonicalTournamentName(entry.name)]; + if (tournament == null) { + continue; + } + + for (final placement in entry.placements) { + if (TeamNameHelper.canonicalize(placement.team) != canonicalTeamName) { + continue; + } + results.add( + TournamentTeamResultDto( + teamName: canonicalTeamName, + teamLogo: placement.teamLogo, + tournamentName: tournament.name, + tournamentImagePath: tournament.imagePath, + organizer: tournament.organizer, + place: placement.place, + startDate: entry.startDate ?? tournament.startDate, + endDate: entry.endDate ?? tournament.endDate, + ), + ); + } + } + + results.sort((a, b) { + final byDate = (b.startDate ?? '').compareTo(a.startDate ?? ''); + if (byDate != 0) return byDate; + return a.tournamentName.compareTo(b.tournamentName); + }); + return results; + } + + Future> loadTournamentTeams() async { + final results = >{}; + final allResults = await _loadAllTeamTournamentResults(); + + for (final result in allResults) { + results + .putIfAbsent(result.teamName, () => []) + .add(result); + } + + final summaries = results.entries.map((entry) { + final items = List.from(entry.value) + ..sort((a, b) { + final byDate = (b.startDate ?? '').compareTo(a.startDate ?? ''); + if (byDate != 0) return byDate; + return _comparePlace(a.place, b.place); + }); + + String? bestPlace; + for (final item in items) { + if (bestPlace == null || _comparePlace(item.place, bestPlace) < 0) { + bestPlace = item.place; + } + } + + final latest = items.isNotEmpty ? items.first : null; + final titleCount = items.where((item) => item.place == '1st').length; + + return TournamentTeamSummaryDto( + teamName: entry.key, + teamLogo: items + .map((item) => item.teamLogo) + .firstWhere((logo) => (logo ?? '').isNotEmpty, orElse: () => null), + tournamentCount: items.length, + titleCount: titleCount, + bestPlace: bestPlace, + latestTournamentName: latest?.tournamentName, + latestTournamentImagePath: latest?.tournamentImagePath, + latestStartDate: latest?.startDate, + ); + }).toList(); + + summaries.sort((a, b) { + final byTitles = b.titleCount.compareTo(a.titleCount); + if (byTitles != 0) return byTitles; + final byBestPlace = _comparePlace(a.bestPlace, b.bestPlace); + if (byBestPlace != 0) return byBestPlace; + final byAppearances = b.tournamentCount.compareTo(a.tournamentCount); + if (byAppearances != 0) return byAppearances; + return a.teamName.compareTo(b.teamName); + }); + + return summaries; + } + + Future> loadTournamentPlayers() async { + final appearances = await _loadAllPlayerAppearances(); + final grouped = >{}; + + for (final appearance in appearances) { + grouped + .putIfAbsent( + _playerLookupKey(appearance.playerName), + () => [], + ) + .add(appearance); + } + + final summaries = grouped.entries.map((entry) { + final items = List.from(entry.value) + ..sort((a, b) { + final byDate = (b.startDate ?? '').compareTo(a.startDate ?? ''); + if (byDate != 0) return byDate; + return a.playerName.compareTo(b.playerName); + }); + + final latest = items.isNotEmpty ? items.first : null; + final autographCount = items.fold( + 0, + (sum, item) => sum + item.autographCount, + ); + String? bestPlace; + for (final item in items) { + if ((item.place ?? '').isEmpty) { + continue; + } + if (bestPlace == null || _comparePlace(item.place, bestPlace) < 0) { + bestPlace = item.place; + } + } + final titleCount = items.where((item) => item.place == '1st').length; + + return TournamentPlayerSummaryDto( + playerName: latest?.playerName ?? entry.value.first.playerName, + tournamentCount: items.length, + autographCount: autographCount, + titleCount: titleCount, + bestPlace: bestPlace, + latestTournamentName: latest?.tournamentName, + latestTournamentImagePath: latest?.tournamentImagePath, + latestStartDate: latest?.startDate, + latestTeamName: latest?.teamName, + latestTeamLogo: latest?.teamLogo, + sampleStickerImage: latest?.sampleStickerImage, + ); + }).toList(); + + summaries.sort((a, b) { + final byTournaments = b.tournamentCount.compareTo(a.tournamentCount); + if (byTournaments != 0) return byTournaments; + final byAutographs = b.autographCount.compareTo(a.autographCount); + if (byAutographs != 0) return byAutographs; + return a.playerName.toLowerCase().compareTo(b.playerName.toLowerCase()); + }); + + return summaries; + } + + Future> loadPlayerTournamentAppearances( + String playerName, + ) async { + final canonical = _playerLookupKey(playerName); + final appearances = await _loadAllPlayerAppearances(); + final result = + appearances + .where( + (appearance) => + _playerLookupKey(appearance.playerName) == canonical, + ) + .toList() + ..sort((a, b) { + final byDate = (b.startDate ?? '').compareTo(a.startDate ?? ''); + if (byDate != 0) return byDate; + return a.tournamentName.compareTo(b.tournamentName); + }); + return result; + } + + Future> loadPlayersForTournament( + String tournamentName, + ) async { + final normalizedTournamentName = _canonicalTournamentName(tournamentName); + final appearances = await _loadAllPlayerAppearances(); + final grouped = >{}; + + for (final appearance in appearances) { + if (_canonicalTournamentName(appearance.tournamentName) != + normalizedTournamentName) { + continue; + } + grouped + .putIfAbsent( + _playerLookupKey(appearance.playerName), + () => [], + ) + .add(appearance); + } + + final result = + grouped.entries.map((entry) { + final items = entry.value; + final first = items.first; + return TournamentPlayerSummaryDto( + playerName: first.playerName, + tournamentCount: items.length, + autographCount: items.fold( + 0, + (sum, item) => sum + item.autographCount, + ), + titleCount: items.where((item) => item.place == '1st').length, + bestPlace: (() { + String? bestPlace; + for (final item in items) { + if ((item.place ?? '').isEmpty) { + continue; + } + if (bestPlace == null || + _comparePlace(item.place, bestPlace) < 0) { + bestPlace = item.place; + } + } + return bestPlace; + })(), + latestTournamentName: first.tournamentName, + latestTournamentImagePath: first.tournamentImagePath, + latestStartDate: first.startDate, + latestTeamName: first.teamName, + latestTeamLogo: first.teamLogo, + sampleStickerImage: first.sampleStickerImage, + ); + }).toList()..sort( + (a, b) => + a.playerName.toLowerCase().compareTo(b.playerName.toLowerCase()), + ); + + return result; + } + + Future loadTournamentMetadataByName( + String tournamentName, + ) async { + final metadata = await loadTournamentMetadata(); + final normalizedName = _canonicalTournamentName(tournamentName); + + for (final entry in metadata) { + if (_canonicalTournamentName(entry.name) == normalizedName) { + return entry; + } + } + + return null; + } + + Future> loadSouvenirPackagesForTournament( + String tournamentName, + ) async { + final containers = await loadContainers(); + final normalizedTournamentName = _canonicalTournamentName(tournamentName); + final result = containers + .where( + (container) => + container.isSouvenirPackage && + _canonicalTournamentName( + (container.tournamentName ?? '').trim(), + ) == + normalizedTournamentName, + ) + .toList(); + result.sort(_compareContainerByReleaseDateAsc); + return result; + } + + Future> loadStickerContainersForTournament( + String tournamentName, + ) async { + final containers = await loadContainers(); + final stickerContents = await loadStickerContents(); + final stickers = await loadStickers(); + final normalizedTournamentName = _canonicalTournamentName(tournamentName); + + final stickerById = {for (final sticker in stickers) sticker.id: sticker}; + final relevantContainerIds = {}; + + for (final content in stickerContents) { + final hasTournamentSticker = content.stickerIds.any( + (stickerId) => + _canonicalTournamentName( + (stickerById[stickerId]?.tournament ?? '').trim(), + ) == + normalizedTournamentName, + ); + if (hasTournamentSticker) { + relevantContainerIds.add(content.containerId); + } + } + + final result = containers + .where( + (container) => + relevantContainerIds.contains(container.id) && + (container.isStickerCapsule || container.isStickerCollection), + ) + .toList(); + result.sort(_compareContainerByReleaseDateAsc); + return result; + } + + Future> _loadAllTeamTournamentResults() async { + final tournaments = await loadTournaments(); + final metadata = await loadTournamentMetadata(); + final tournamentByName = { + for (final tournament in tournaments) + _canonicalTournamentName(tournament.name): tournament, + }; + + final results = []; + for (final entry in metadata) { + final tournament = tournamentByName[_canonicalTournamentName(entry.name)]; + if (tournament == null) { + continue; + } + + for (final placement in entry.placements) { + final canonicalTeamName = TeamNameHelper.canonicalize(placement.team); + results.add( + TournamentTeamResultDto( + teamName: canonicalTeamName, + teamLogo: placement.teamLogo, + tournamentName: tournament.name, + tournamentImagePath: tournament.imagePath, + organizer: tournament.organizer, + place: placement.place, + startDate: entry.startDate ?? tournament.startDate, + endDate: entry.endDate ?? tournament.endDate, + ), + ); + } + } + + return results; + } + + Future> + _loadAllPlayerAppearances() async { + final tournaments = await loadTournaments(); + final metadata = await loadTournamentMetadata(); + final tournamentByName = { + for (final tournament in tournaments) + _canonicalTournamentName(tournament.name): tournament, + }; + final metadataByName = { + for (final entry in metadata) _canonicalTournamentName(entry.name): entry, + }; + final teamInfoByPlayerTournament = + < + String, + ({ + String playerName, + String? teamName, + String? teamLogo, + String? place, + }) + >{}; + + for (final entry in metadata) { + final canonicalTournamentName = _canonicalTournamentName(entry.name); + final placeByTeam = { + for (final placement in entry.placements) + TeamNameHelper.canonicalize(placement.team): placement, + }; + + for (final roster in entry.teamRosters) { + final canonicalTeamName = TeamNameHelper.canonicalize(roster.team); + final placement = placeByTeam[canonicalTeamName]; + for (final player in roster.players) { + final playerKey = _playerLookupKey(player); + teamInfoByPlayerTournament['$playerKey|$canonicalTournamentName'] = ( + playerName: _preferredPlayerDisplayName(player), + teamName: roster.team, + teamLogo: roster.teamLogo ?? placement?.teamLogo, + place: placement?.place, + ); + } + } + } + + final grouped = {}; + + for (final entry in metadata) { + final canonicalTournamentName = _canonicalTournamentName(entry.name); + final tournament = tournamentByName[canonicalTournamentName]; + if (tournament == null) { + continue; + } + + for (final roster in entry.teamRosters) { + final canonicalTeamName = TeamNameHelper.canonicalize(roster.team); + final placement = entry.placements + .where( + (item) => + TeamNameHelper.canonicalize(item.team) == canonicalTeamName, + ) + .cast() + .firstOrNull; + + for (final rawPlayerName in roster.players) { + final playerName = _preferredPlayerDisplayName(rawPlayerName); + final key = + '${_playerLookupKey(playerName)}|$canonicalTournamentName'; + grouped.putIfAbsent( + key, + () => _PlayerAppearanceBuilder( + playerName: playerName, + teamName: roster.team, + teamLogo: roster.teamLogo ?? placement?.teamLogo, + place: placement?.place, + tournamentName: tournament.name, + tournamentImagePath: tournament.imagePath, + startDate: entry.startDate ?? tournament.startDate, + endDate: entry.endDate ?? tournament.endDate, + ), + ); + } + } + } + + final stickers = await loadStickers(); + for (final sticker in stickers) { + if (sticker.stickerType != 'AUTOGRAPH') { + continue; + } + + final tournamentName = (sticker.tournament ?? '').trim(); + if (tournamentName.isEmpty) { + continue; + } + + final canonicalTournamentName = _canonicalTournamentName(tournamentName); + final tournament = tournamentByName[canonicalTournamentName]; + if (tournament == null) { + continue; + } + + final playerName = _extractAutographPlayerName(sticker.name); + if (playerName.isEmpty) { + continue; + } + + final key = '${_playerLookupKey(playerName)}|$canonicalTournamentName'; + final builder = grouped.putIfAbsent(key, () { + final teamInfo = teamInfoByPlayerTournament[key]; + final tournamentMetadata = metadataByName[canonicalTournamentName]; + return _PlayerAppearanceBuilder( + playerName: teamInfo?.playerName ?? playerName, + teamName: teamInfo?.teamName, + teamLogo: teamInfo?.teamLogo, + place: teamInfo?.place, + tournamentName: tournament.name, + tournamentImagePath: tournament.imagePath, + startDate: tournamentMetadata?.startDate ?? tournament.startDate, + endDate: tournamentMetadata?.endDate ?? tournament.endDate, + ); + }); + builder.autographCount += 1; + builder.sampleStickerImage ??= sticker.stickerImage; + builder.effects.add(_autographEffectLabel(sticker.effect)); + } + + return grouped.values + .map( + (item) => TournamentPlayerAppearanceDto( + playerName: item.playerName, + teamName: item.teamName, + teamLogo: item.teamLogo, + place: item.place, + tournamentName: item.tournamentName, + tournamentImagePath: item.tournamentImagePath, + startDate: item.startDate, + endDate: item.endDate, + autographCount: item.autographCount, + effects: item.effects.toList()..sort(), + sampleStickerImage: item.sampleStickerImage, + ), + ) + .toList(); + } + Future> loadSkinsForContainer(String containerId) async { final skins = await loadSkins(); final contents = await loadContainerContents(); @@ -448,3 +1048,181 @@ mixin _LocalDataRepositoryQueries on _LocalDataRepositoryLoaders { return result; } } + +class _TournamentBuilder { + final String name; + String? imagePath; + String? releaseDate; + final Set souvenirContainerIds = {}; + final Set stickerContainerIds = {}; + + _TournamentBuilder({required this.name}); +} + +class _PlayerAppearanceBuilder { + final String playerName; + final String? teamName; + final String? teamLogo; + final String? place; + final String tournamentName; + final String tournamentImagePath; + final String? startDate; + final String? endDate; + int autographCount = 0; + String? sampleStickerImage; + final Set effects = {}; + + _PlayerAppearanceBuilder({ + required this.playerName, + required this.teamName, + required this.teamLogo, + required this.place, + required this.tournamentName, + required this.tournamentImagePath, + required this.startDate, + required this.endDate, + }); +} + +String? _earlierDate(String? a, String? b) { + if (a == null || a.isEmpty) return b; + if (b == null || b.isEmpty) return a; + return a.compareTo(b) <= 0 ? a : b; +} + +String _preferredTournamentImage(ContainerDto container) { + final tournamentLogo = (container.tournamentLogo ?? '').trim(); + if (tournamentLogo.isNotEmpty) { + return tournamentLogo; + } + return container.containerImage; +} + +String _extractAutographPlayerName(String stickerName) { + final left = stickerName.split(' | ').first.trim(); + return _preferredPlayerDisplayName( + _cleanPlayerName(left.replaceFirst(RegExp(r'\s+\([^)]*\)$'), '')), + ); +} + +String _cleanPlayerName(String playerName) { + return playerName + .trim() + .replaceAll(RegExp(r'\s*\|[A-Za-z0-9_]+\s*=.*$'), '') + .replaceFirst(RegExp(r'\s+\([^)]*\)$'), '') + .trim(); +} + +String _playerLookupKey(String playerName) { + return _cleanPlayerName(playerName).toLowerCase(); +} + +String _preferredPlayerDisplayName(String playerName) { + return _preferredPlayerNames[_playerLookupKey(playerName)] ?? + _cleanPlayerName(playerName); +} + +const _preferredPlayerNames = { + 'adren': 'AdreN', + 'amanek': 'AmaNEk', + 'dycha': 'dycha', + 'electronic': 'electroNic', + 'hobbit': 'Hobbit', + 'ins': 'INS', + 'jackz': 'JACKZ', + 'niko': 'NiKo', + 'qikert': 'Qikert', + 'sico': 'sico', + 'tenzki': 'TENZKI', +}; + +String _autographEffectLabel(String effect) { + switch (effect) { + case 'FOIL': + return 'Foil'; + case 'GOLD': + return 'Gold'; + case 'GLITTER': + return 'Glitter'; + case 'HOLO': + return 'Holo'; + default: + return 'Paper'; + } +} + +String _inferTournamentOrganizer(String tournamentName) { + const prefixes = { + 'DreamHack ': 'DreamHack', + 'EMS One ': 'EMS One', + 'ESL One ': 'ESL One', + 'MLG ': 'MLG', + 'ELEAGUE ': 'ELEAGUE', + 'PGL ': 'PGL', + 'FACEIT ': 'FACEIT', + 'StarLadder ': 'StarLadder', + 'IEM ': 'Intel Extreme Masters', + 'BLAST.tv ': 'BLAST.tv', + 'Perfect World ': 'Perfect World', + }; + + for (final entry in prefixes.entries) { + if (tournamentName.startsWith(entry.key)) { + return entry.value; + } + } + + return tournamentName.split(' ').first; +} + +String _canonicalTournamentName(String rawTournamentName) { + final trimmed = rawTournamentName + .trim() + .replaceAll('ELEAGUE Major Boston 2018', 'ELEAGUE Boston 2018') + .replaceAll('KrakГіw', 'Kraków') + .replaceAll('Krakow', 'Kraków') + .replaceAll(RegExp(r'\s+'), ' '); + if (trimmed.isEmpty) { + return trimmed; + } + + final yearPrefix = RegExp(r'^(20\d{2}) (.+)$').firstMatch(trimmed); + if (yearPrefix != null) { + final year = yearPrefix.group(1)!; + final rest = yearPrefix.group(2)!; + return '$rest $year'; + } + + return trimmed; +} + +bool _isIgnoredTournamentName(String tournamentName) { + return tournamentName == '2020 RMR' || + tournamentName.contains('2020 RMR') || + tournamentName.contains('RMR 2020'); +} + +int _comparePlace(String? a, String? b) { + final rankA = _placeRank(a); + final rankB = _placeRank(b); + if (rankA != rankB) { + return rankA.compareTo(rankB); + } + return (a ?? '').compareTo(b ?? ''); +} + +int _placeRank(String? place) { + if (place == null || place.isEmpty) { + return 1 << 20; + } + + final match = RegExp( + r'^(\d+)(?:st|nd|rd|th)?(?:-(\d+)(?:st|nd|rd|th)?)?$', + ).firstMatch(place); + if (match == null) { + return 1 << 20; + } + + final start = int.tryParse(match.group(1) ?? ''); + return start ?? (1 << 20); +} diff --git a/lib/presentation/helpers/app_navigation_helper.dart b/lib/presentation/helpers/app_navigation_helper.dart index 310f4d77..883fed21 100644 --- a/lib/presentation/helpers/app_navigation_helper.dart +++ b/lib/presentation/helpers/app_navigation_helper.dart @@ -99,7 +99,7 @@ class AppNavigationHelper { return ContainerOpenScreen( containerDto: containerDto, repository: repository, - settingsController: settingsController!, + settingsController: settingsController, ); } } diff --git a/lib/presentation/helpers/sticker_ui_helper.dart b/lib/presentation/helpers/sticker_ui_helper.dart index a5be7e36..5c60c61a 100644 --- a/lib/presentation/helpers/sticker_ui_helper.dart +++ b/lib/presentation/helpers/sticker_ui_helper.dart @@ -12,7 +12,7 @@ class StickerUiHelper { case 'EXOTIC': return Colors.pink; case 'EXTRAORDINARY': - return Colors.amber; + return const Color(0xFFEB4B4B); case 'CONTRABAND': return const Color(0xFFFF8A00); default: diff --git a/lib/presentation/screens/container_open_screen.dart b/lib/presentation/screens/container_open_screen.dart index 7ff37ab5..9aabd5e6 100644 --- a/lib/presentation/screens/container_open_screen.dart +++ b/lib/presentation/screens/container_open_screen.dart @@ -27,7 +27,7 @@ import '../widgets/xray_reveal_card.dart'; class ContainerOpenScreen extends StatefulWidget { final ContainerDto containerDto; final LocalDataRepository repository; - final SettingsController settingsController; + final SettingsController? settingsController; const ContainerOpenScreen({ super.key, @@ -57,7 +57,8 @@ class _ContainerOpenScreenState extends State { bool get _isSouvenirPackage => widget.containerDto.isSouvenirPackage; bool get _isCollectionPackage => widget.containerDto.isCollectionPackage; bool get _isXrayPackage => widget.containerDto.isXrayPackage; - bool get _xrayModeEnabled => widget.settingsController.xrayOpeningEnabled; + bool get _xrayModeEnabled => + widget.settingsController?.xrayOpeningEnabled ?? false; bool get _shouldUseSettingsXrayMode => _xrayModeEnabled && _isRegularCase && !_isXrayPackage; diff --git a/lib/presentation/screens/home_screen.dart b/lib/presentation/screens/home_screen.dart index 386c24b1..fb5a0ee3 100644 --- a/lib/presentation/screens/home_screen.dart +++ b/lib/presentation/screens/home_screen.dart @@ -10,9 +10,12 @@ import 'charm_collection_list_screen.dart'; import 'glossary_hub_screen.dart'; import 'operation_collection_list_screen.dart'; import 'patch_collection_list_screen.dart'; +import 'player_list_screen.dart'; import 'reward_collection_list_screen.dart'; import 'settings_screen.dart'; +import 'team_list_screen.dart'; import 'sticker_collection_list_screen.dart'; +import 'tournament_list_screen.dart'; import 'tradeup_screen.dart'; class HomeScreen extends StatelessWidget { @@ -75,6 +78,21 @@ class HomeScreen extends StatelessWidget { title: 'Charm Collections', buildScreen: () => CharmCollectionListScreen(repository: repository), ), + _HomeMenuItem( + icon: Icons.emoji_events, + title: 'Majors', + buildScreen: () => TournamentListScreen(repository: repository), + ), + _HomeMenuItem( + icon: Icons.groups_2, + title: 'Major Teams', + buildScreen: () => TeamListScreen(repository: repository), + ), + _HomeMenuItem( + icon: Icons.person_search, + title: 'Major Players', + buildScreen: () => PlayerListScreen(repository: repository), + ), _HomeMenuItem( icon: Icons.swap_horiz, title: 'Trade-Up', diff --git a/lib/presentation/screens/player_details_screen.dart b/lib/presentation/screens/player_details_screen.dart new file mode 100644 index 00000000..e0cd16c3 --- /dev/null +++ b/lib/presentation/screens/player_details_screen.dart @@ -0,0 +1,456 @@ +import 'package:flutter/material.dart'; + +import '../../core/utils/date_format_helper.dart'; +import '../../data/models/tournament_dto.dart'; +import '../../data/models/tournament_player_dto.dart'; +import '../../data/repositories/local_data_repository.dart'; +import '../helpers/app_navigation_helper.dart'; +import '../widgets/adaptive_logo_image.dart'; +import '../widgets/async_collection_loader.dart'; +import 'team_details_screen.dart'; +import 'tournament_details_screen.dart'; + +class PlayerDetailsScreen extends StatefulWidget { + final String playerName; + final LocalDataRepository repository; + + const PlayerDetailsScreen({ + super.key, + required this.playerName, + required this.repository, + }); + + @override + State createState() => _PlayerDetailsScreenState(); +} + +class _PlayerDetailsScreenState extends State { + late Future> _future; + + @override + void initState() { + super.initState(); + _future = widget.repository.loadPlayerTournamentAppearances( + widget.playerName, + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: Text(widget.playerName)), + body: AsyncCollectionLoader( + future: _future, + builder: (context, items) { + final latest = items.isNotEmpty ? items.first : null; + final autographCount = items.fold( + 0, + (sum, item) => sum + item.autographCount, + ); + final titleCount = items.where((item) => item.place == '1st').length; + final teams = items + .map((item) => item.teamName) + .where((entry) => (entry ?? '').isNotEmpty) + .cast() + .toSet(); + String? bestPlace; + for (final item in items) { + if ((item.place ?? '').isEmpty) { + continue; + } + if (bestPlace == null || + _comparePlaces(item.place!, bestPlace) < 0) { + bestPlace = item.place; + } + } + + return ListView( + padding: const EdgeInsets.all(12), + children: [ + Card( + child: Padding( + padding: const EdgeInsets.all(16), + child: Row( + children: [ + _PlayerStickerBadge( + imagePath: latest?.sampleStickerImage, + size: 72, + ), + const SizedBox(width: 16), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + widget.playerName, + style: const TextStyle( + fontSize: 24, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 8), + Wrap( + spacing: 8, + runSpacing: 8, + children: [ + _PlayerStatChip( + label: '${items.length} Major appearances', + color: Colors.blueAccent, + ), + _PlayerStatChip( + label: '$autographCount autographs', + color: Colors.amber, + ), + if (bestPlace != null) + _PlayerStatChip( + label: 'Best: $bestPlace', + color: Colors.greenAccent, + ), + if (titleCount > 0) + _PlayerStatChip( + label: '$titleCount titles', + color: Colors.pinkAccent, + ), + ], + ), + if (teams.isNotEmpty) ...[ + const SizedBox(height: 10), + Text( + 'Teams: ${teams.join(', ')}', + style: const TextStyle( + color: Colors.white70, + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ], + if ((latest?.latestDateText ?? '').isNotEmpty) ...[ + const SizedBox(height: 10), + Text( + 'Latest Major: ${latest!.tournamentName}', + style: const TextStyle( + color: Colors.white70, + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + const SizedBox(height: 4), + Text( + latest.latestDateText!, + style: const TextStyle( + color: Colors.white60, + fontSize: 12, + ), + ), + if ((latest.teamName ?? '').isNotEmpty) ...[ + const SizedBox(height: 6), + Row( + children: [ + _PlayerTeamLogo( + logoPath: latest.teamLogo, + size: 18, + ), + const SizedBox(width: 8), + Expanded( + child: Text( + '${latest.teamName}${(latest.place ?? '').isNotEmpty ? ' • ${latest.place}' : ''}', + style: const TextStyle( + color: Colors.white70, + fontSize: 13, + ), + ), + ), + ], + ), + ], + ], + ], + ), + ), + ], + ), + ), + ), + const SizedBox(height: 12), + ...items.map( + (item) => Padding( + padding: const EdgeInsets.only(bottom: 12), + child: _PlayerTournamentCard( + item: item, + onOpenTeam: (item.teamName ?? '').isEmpty + ? null + : () { + AppNavigationHelper.pushScreen( + context, + TeamDetailsScreen( + teamName: item.teamName!, + repository: widget.repository, + ), + ); + }, + onTap: () async { + final tournaments = await widget.repository + .loadTournaments(); + TournamentDto? tournament; + for (final entry in tournaments) { + if (entry.name == item.tournamentName) { + tournament = entry; + break; + } + } + if (tournament == null || !context.mounted) return; + AppNavigationHelper.pushScreen( + context, + TournamentDetailsScreen( + repository: widget.repository, + tournament: tournament, + ), + ); + }, + ), + ), + ), + ], + ); + }, + ), + ); + } +} + +class _PlayerTournamentCard extends StatelessWidget { + final TournamentPlayerAppearanceDto item; + final VoidCallback onTap; + final VoidCallback? onOpenTeam; + + const _PlayerTournamentCard({ + required this.item, + required this.onTap, + required this.onOpenTeam, + }); + + @override + Widget build(BuildContext context) { + return InkWell( + borderRadius: BorderRadius.circular(16), + onTap: onTap, + child: Card( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + side: const BorderSide(color: Colors.white10), + ), + child: Padding( + padding: const EdgeInsets.all(16), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ClipRRect( + borderRadius: BorderRadius.circular(10), + child: AdaptiveLogoImage( + logoPath: item.tournamentImagePath, + width: 92, + height: 56, + fit: BoxFit.contain, + fallback: const Icon(Icons.emoji_events_outlined, size: 40), + ), + ), + const SizedBox(width: 14), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + item.tournamentName, + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w700, + ), + ), + if ((item.latestDateText ?? '').isNotEmpty) ...[ + const SizedBox(height: 4), + Text( + item.latestDateText!, + style: const TextStyle( + color: Colors.white60, + fontSize: 12, + ), + ), + ], + const SizedBox(height: 8), + Wrap( + spacing: 8, + runSpacing: 8, + children: [ + if ((item.place ?? '').isNotEmpty) + _PlayerStatChip( + label: item.place!, + color: Colors.greenAccent, + ), + if ((item.teamName ?? '').isNotEmpty) + InkWell( + borderRadius: BorderRadius.circular(999), + onTap: onOpenTeam, + child: Container( + padding: const EdgeInsets.symmetric( + horizontal: 10, + vertical: 6, + ), + decoration: BoxDecoration( + color: Colors.white.withValues(alpha: 0.08), + borderRadius: BorderRadius.circular(999), + border: Border.all(color: Colors.white12), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + _PlayerTeamLogo( + logoPath: item.teamLogo, + size: 16, + ), + const SizedBox(width: 6), + Text( + item.teamName!, + style: const TextStyle( + color: Colors.white, + fontSize: 12, + fontWeight: FontWeight.w700, + ), + ), + ], + ), + ), + ), + _PlayerStatChip( + label: '${item.autographCount} variants', + color: Colors.orangeAccent, + ), + for (final effect in item.effects) + _PlayerStatChip(label: effect, color: Colors.white70), + ], + ), + ], + ), + ), + ], + ), + ), + ), + ); + } +} + +class _PlayerTeamLogo extends StatelessWidget { + final String? logoPath; + final double size; + + const _PlayerTeamLogo({required this.logoPath, required this.size}); + + @override + Widget build(BuildContext context) { + final value = logoPath ?? ''; + if (value.isEmpty) { + return Icon(Icons.groups_2_outlined, size: size, color: Colors.white60); + } + if (value.startsWith('assets/')) { + return AdaptiveLogoImage( + logoPath: value, + width: size, + height: size, + fallback: Icon( + Icons.groups_2_outlined, + size: size, + color: Colors.white60, + ), + ); + } + return Image.network( + value, + width: size, + height: size, + fit: BoxFit.contain, + errorBuilder: (_, _, _) => + Icon(Icons.groups_2_outlined, size: size, color: Colors.white60), + ); + } +} + +class _PlayerStatChip extends StatelessWidget { + final String label; + final Color color; + + const _PlayerStatChip({required this.label, required this.color}); + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6), + decoration: BoxDecoration( + color: color.withValues(alpha: 0.16), + borderRadius: BorderRadius.circular(999), + border: Border.all(color: color.withValues(alpha: 0.35)), + ), + child: Text( + label, + style: TextStyle( + color: color, + fontSize: 12, + fontWeight: FontWeight.w700, + ), + ), + ); + } +} + +class _PlayerStickerBadge extends StatelessWidget { + final String? imagePath; + final double size; + + const _PlayerStickerBadge({required this.imagePath, required this.size}); + + @override + Widget build(BuildContext context) { + return Container( + width: size, + height: size, + decoration: BoxDecoration( + color: Colors.white.withValues(alpha: 0.06), + shape: BoxShape.circle, + border: Border.all(color: Colors.white10), + ), + clipBehavior: Clip.antiAlias, + child: Padding( + padding: const EdgeInsets.all(10), + child: Image.asset( + imagePath ?? '', + fit: BoxFit.contain, + errorBuilder: (_, _, _) => const Icon(Icons.draw_outlined, size: 36), + ), + ), + ); + } +} + +extension on TournamentPlayerAppearanceDto { + String? get latestDateText => + DateFormatHelper.formatDateRange(startDate, endDate) ?? + DateFormatHelper.formatReleaseDate(startDate); +} + +int _comparePlaces(String a, String b) { + final rankA = _placeRank(a); + final rankB = _placeRank(b); + if (rankA != rankB) { + return rankA.compareTo(rankB); + } + return a.compareTo(b); +} + +int _placeRank(String place) { + final match = RegExp( + r'^(\d+)(?:st|nd|rd|th)?(?:-(\d+)(?:st|nd|rd|th)?)?$', + ).firstMatch(place); + if (match == null) { + return 1 << 20; + } + return int.tryParse(match.group(1) ?? '') ?? (1 << 20); +} diff --git a/lib/presentation/screens/player_list_screen.dart b/lib/presentation/screens/player_list_screen.dart new file mode 100644 index 00000000..7ff21fb0 --- /dev/null +++ b/lib/presentation/screens/player_list_screen.dart @@ -0,0 +1,238 @@ +import 'package:flutter/material.dart'; + +import '../../core/utils/date_format_helper.dart'; +import '../../data/models/tournament_player_dto.dart'; +import '../../data/repositories/local_data_repository.dart'; +import '../widgets/async_collection_loader.dart'; +import 'player_details_screen.dart'; + +class PlayerListScreen extends StatefulWidget { + final LocalDataRepository repository; + + const PlayerListScreen({super.key, required this.repository}); + + @override + State createState() => _PlayerListScreenState(); +} + +class _PlayerListScreenState extends State { + late Future> _future; + String _query = ''; + + @override + void initState() { + super.initState(); + _future = widget.repository.loadTournamentPlayers(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text('Major Players')), + body: AsyncCollectionLoader( + future: _future, + builder: (context, items) { + final filtered = items.where((item) { + final query = _query.trim().toLowerCase(); + if (query.isEmpty) return true; + return item.playerName.toLowerCase().contains(query); + }).toList(); + + return Column( + children: [ + Padding( + padding: const EdgeInsets.fromLTRB(12, 12, 12, 0), + child: TextField( + onChanged: (value) => setState(() => _query = value), + decoration: const InputDecoration( + prefixIcon: Icon(Icons.search), + hintText: 'Search players', + ), + ), + ), + Expanded( + child: filtered.isEmpty + ? const Center( + child: Text( + 'No players found.', + style: TextStyle(color: Colors.white70), + ), + ) + : ListView.separated( + padding: const EdgeInsets.all(12), + itemCount: filtered.length, + separatorBuilder: (_, _) => const SizedBox(height: 12), + itemBuilder: (context, index) { + final player = filtered[index]; + return _PlayerSummaryCard( + player: player, + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (_) => PlayerDetailsScreen( + playerName: player.playerName, + repository: widget.repository, + ), + ), + ); + }, + ); + }, + ), + ), + ], + ); + }, + ), + ); + } +} + +class _PlayerSummaryCard extends StatelessWidget { + final TournamentPlayerSummaryDto player; + final VoidCallback onTap; + + const _PlayerSummaryCard({required this.player, required this.onTap}); + + @override + Widget build(BuildContext context) { + final latestDate = DateFormatHelper.formatReleaseDate( + player.latestStartDate, + ); + + return InkWell( + borderRadius: BorderRadius.circular(16), + onTap: onTap, + child: Card( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + side: const BorderSide(color: Colors.white10), + ), + child: Padding( + padding: const EdgeInsets.all(16), + child: Row( + children: [ + _PlayerStickerBadge( + imagePath: player.sampleStickerImage, + size: 56, + ), + const SizedBox(width: 14), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + player.playerName, + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.w700, + ), + ), + const SizedBox(height: 8), + Wrap( + spacing: 8, + runSpacing: 8, + children: [ + _PlayerStatChip( + label: '${player.tournamentCount} Majors', + color: Colors.blueAccent, + ), + _PlayerStatChip( + label: '${player.autographCount} autographs', + color: Colors.amber, + ), + ], + ), + if ((player.latestTournamentName ?? '').isNotEmpty) ...[ + const SizedBox(height: 10), + Text( + 'Latest Major: ${player.latestTournamentName}', + style: const TextStyle( + color: Colors.white70, + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ], + if (latestDate != null) ...[ + const SizedBox(height: 4), + Text( + 'Latest appearance: $latestDate', + style: const TextStyle( + color: Colors.white60, + fontSize: 12, + ), + ), + ], + ], + ), + ), + ], + ), + ), + ), + ); + } +} + +class _PlayerStatChip extends StatelessWidget { + final String label; + final Color color; + + const _PlayerStatChip({required this.label, required this.color}); + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6), + decoration: BoxDecoration( + color: color.withValues(alpha: 0.16), + borderRadius: BorderRadius.circular(999), + border: Border.all(color: color.withValues(alpha: 0.35)), + ), + child: Text( + label, + style: TextStyle( + color: color, + fontSize: 12, + fontWeight: FontWeight.w700, + ), + ), + ); + } +} + +class _PlayerStickerBadge extends StatelessWidget { + final String? imagePath; + final double size; + + const _PlayerStickerBadge({required this.imagePath, required this.size}); + + @override + Widget build(BuildContext context) { + return Container( + width: size, + height: size, + decoration: BoxDecoration( + color: Colors.white.withValues(alpha: 0.06), + shape: BoxShape.circle, + border: Border.all(color: Colors.white10), + ), + clipBehavior: Clip.antiAlias, + child: Padding(padding: const EdgeInsets.all(8), child: _buildImage()), + ); + } + + Widget _buildImage() { + final value = imagePath ?? ''; + if (value.isEmpty) { + return const Icon(Icons.draw_outlined, size: 28); + } + return Image.asset( + value, + fit: BoxFit.contain, + errorBuilder: (_, _, _) => const Icon(Icons.draw_outlined, size: 28), + ); + } +} diff --git a/lib/presentation/screens/team_details_screen.dart b/lib/presentation/screens/team_details_screen.dart new file mode 100644 index 00000000..12285dbe --- /dev/null +++ b/lib/presentation/screens/team_details_screen.dart @@ -0,0 +1,523 @@ +import 'package:flutter/material.dart'; + +import '../../core/utils/date_format_helper.dart'; +import '../../core/utils/team_name_helper.dart'; +import '../../data/models/tournament_dto.dart'; +import '../../data/models/tournament_metadata_dto.dart'; +import '../../data/repositories/local_data_repository.dart'; +import '../helpers/app_navigation_helper.dart'; +import '../widgets/adaptive_logo_image.dart'; +import '../widgets/detail_info_row.dart'; +import '../widgets/detail_tag.dart'; +import 'player_details_screen.dart'; +import 'tournament_details_screen.dart'; + +class TeamDetailsScreen extends StatefulWidget { + final String teamName; + final LocalDataRepository repository; + + const TeamDetailsScreen({ + super.key, + required this.teamName, + required this.repository, + }); + + @override + State createState() => _TeamDetailsScreenState(); +} + +class _TeamDetailsScreenState extends State { + late Future<_TeamDetailsData> _future; + + @override + void initState() { + super.initState(); + _future = _loadData(); + } + + Future<_TeamDetailsData> _loadData() async { + final results = await widget.repository.loadTeamTournamentResults( + widget.teamName, + ); + final metadata = await widget.repository.loadTournamentMetadata(); + final metadataByName = {for (final entry in metadata) entry.name: entry}; + + return _TeamDetailsData(results: results, metadataByName: metadataByName); + } + + @override + Widget build(BuildContext context) { + final canonicalTeamName = TeamNameHelper.canonicalize(widget.teamName); + + return Scaffold( + appBar: AppBar(title: Text(canonicalTeamName)), + body: FutureBuilder<_TeamDetailsData>( + future: _future, + builder: (context, snapshot) { + if (snapshot.connectionState != ConnectionState.done) { + return const Center(child: CircularProgressIndicator()); + } + + if (snapshot.hasError) { + return Center( + child: Padding( + padding: const EdgeInsets.all(24), + child: Text( + 'Failed to load team details.\n${snapshot.error}', + textAlign: TextAlign.center, + ), + ), + ); + } + + final data = snapshot.data; + if (data == null || data.results.isEmpty) { + return const Center( + child: Text( + 'No Major results found for this team.', + style: TextStyle(color: Colors.white70), + ), + ); + } + + final sorted = List.from(data.results) + ..sort((a, b) { + final byDate = (b.startDate ?? '').compareTo(a.startDate ?? ''); + if (byDate != 0) return byDate; + return _comparePlaces(a.place, b.place); + }); + + final titles = sorted.where((item) => item.place == '1st').length; + final bestPlace = sorted.isEmpty + ? null + : (sorted.map((item) => item.place).toList() + ..sort(_comparePlaces)) + .first; + final latest = sorted.isNotEmpty ? sorted.first : null; + final recurringPlayers = _buildRecurringPlayers( + data, + canonicalTeamName, + ); + + return Column( + children: [ + Padding( + padding: const EdgeInsets.fromLTRB(12, 12, 12, 0), + child: Card( + child: Padding( + padding: const EdgeInsets.all(16), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _TeamLogoBadge(logoUrl: latest?.teamLogo, size: 72), + const SizedBox(width: 16), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + canonicalTeamName, + style: const TextStyle( + fontSize: 24, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 10), + Wrap( + spacing: 8, + runSpacing: 8, + children: [ + DetailTag( + text: '${sorted.length} Major appearances', + ), + if (bestPlace != null) + DetailTag( + text: 'Best: $bestPlace', + color: Colors.amber.shade400, + ), + if (titles > 0) + DetailTag( + text: '$titles Major titles', + color: Colors.greenAccent.shade400, + ), + ], + ), + const SizedBox(height: 14), + DetailInfoRow( + title: 'Latest Major', + value: latest?.tournamentName ?? '-', + ), + DetailInfoRow( + title: 'Latest Dates', + value: + DateFormatHelper.formatDateRange( + latest?.startDate, + latest?.endDate, + ) ?? + '-', + ), + ], + ), + ), + ], + ), + ), + ), + ), + if (recurringPlayers.isNotEmpty) + Padding( + padding: const EdgeInsets.fromLTRB(12, 12, 12, 0), + child: Card( + child: Padding( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + 'Recurring Players', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 10), + Wrap( + spacing: 8, + runSpacing: 8, + children: [ + for (final entry in recurringPlayers) + ActionChip( + label: Text( + '${entry.name} (${entry.appearances})', + ), + onPressed: () { + AppNavigationHelper.pushScreen( + context, + PlayerDetailsScreen( + playerName: entry.name, + repository: widget.repository, + ), + ); + }, + ), + ], + ), + ], + ), + ), + ), + ), + Expanded( + child: ListView.separated( + padding: const EdgeInsets.all(12), + itemCount: sorted.length, + separatorBuilder: (_, _) => const SizedBox(height: 12), + itemBuilder: (context, index) { + final result = sorted[index]; + final roster = _findRoster( + data.metadataByName[result.tournamentName], + canonicalTeamName, + ); + + return _TeamTournamentCard( + result: result, + roster: roster, + onOpenTournament: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (_) => TournamentDetailsScreen( + repository: widget.repository, + tournament: TournamentDto( + name: result.tournamentName, + imagePath: result.tournamentImagePath, + releaseDate: result.startDate, + startDate: result.startDate, + endDate: result.endDate, + organizer: result.organizer, + souvenirPackageCount: 0, + stickerContainerCount: 0, + ), + ), + ), + ); + }, + onOpenPlayer: (playerName) { + AppNavigationHelper.pushScreen( + context, + PlayerDetailsScreen( + playerName: playerName, + repository: widget.repository, + ), + ); + }, + ); + }, + ), + ), + ], + ); + }, + ), + ); + } + + TournamentTeamRosterDto? _findRoster( + TournamentMetadataDto? metadata, + String canonicalTeamName, + ) { + if (metadata == null) { + return null; + } + + for (final roster in metadata.teamRosters) { + if (TeamNameHelper.canonicalize(roster.team) == canonicalTeamName) { + return roster; + } + } + + return null; + } + + List<_RecurringPlayer> _buildRecurringPlayers( + _TeamDetailsData data, + String canonicalTeamName, + ) { + final appearances = {}; + final displayNames = {}; + + for (final metadata in data.metadataByName.values) { + final roster = _findRoster(metadata, canonicalTeamName); + if (roster == null) { + continue; + } + for (final player in roster.players) { + final key = player.toLowerCase(); + appearances.update(key, (value) => value + 1, ifAbsent: () => 1); + displayNames.putIfAbsent(key, () => player); + } + } + + final result = + appearances.entries + .map( + (entry) => _RecurringPlayer( + name: displayNames[entry.key] ?? entry.key, + appearances: entry.value, + ), + ) + .toList() + ..sort((a, b) { + final byAppearances = b.appearances.compareTo(a.appearances); + if (byAppearances != 0) return byAppearances; + return a.name.toLowerCase().compareTo(b.name.toLowerCase()); + }); + + return result.take(12).toList(); + } +} + +class _TeamTournamentCard extends StatelessWidget { + final TournamentTeamResultDto result; + final TournamentTeamRosterDto? roster; + final VoidCallback onOpenTournament; + final ValueChanged onOpenPlayer; + + const _TeamTournamentCard({ + required this.result, + required this.roster, + required this.onOpenTournament, + required this.onOpenPlayer, + }); + + @override + Widget build(BuildContext context) { + return InkWell( + borderRadius: BorderRadius.circular(16), + onTap: onOpenTournament, + child: Card( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + side: const BorderSide(color: Colors.white10), + ), + child: Padding( + padding: const EdgeInsets.all(16), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ClipRRect( + borderRadius: BorderRadius.circular(10), + child: AdaptiveLogoImage( + logoPath: result.tournamentImagePath, + width: 96, + height: 60, + fit: BoxFit.contain, + fallback: const Icon(Icons.emoji_events_outlined, size: 40), + ), + ), + const SizedBox(width: 14), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + result.tournamentName, + style: const TextStyle( + fontSize: 17, + fontWeight: FontWeight.w700, + ), + ), + const SizedBox(height: 4), + Text( + DateFormatHelper.formatDateRange( + result.startDate, + result.endDate, + ) ?? + '-', + style: const TextStyle( + color: Colors.white60, + fontSize: 12, + ), + ), + const SizedBox(height: 8), + Wrap( + spacing: 8, + runSpacing: 8, + children: [ + _TeamPlaceChip(place: result.place), + DetailTag(text: result.organizer), + ], + ), + if (roster != null && roster!.players.isNotEmpty) ...[ + const SizedBox(height: 12), + const Text( + 'Roster', + style: TextStyle( + color: Colors.white70, + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + const SizedBox(height: 8), + Wrap( + spacing: 8, + runSpacing: 8, + children: [ + for (final player in roster!.players) + ActionChip( + label: Text(player), + onPressed: () => onOpenPlayer(player), + ), + ], + ), + ], + ], + ), + ), + ], + ), + ), + ), + ); + } +} + +class _TeamPlaceChip extends StatelessWidget { + final String place; + + const _TeamPlaceChip({required this.place}); + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6), + decoration: BoxDecoration( + color: Colors.amber.withValues(alpha: 0.16), + borderRadius: BorderRadius.circular(999), + border: Border.all(color: Colors.amber.withValues(alpha: 0.35)), + ), + child: Text( + place, + style: const TextStyle( + color: Colors.amber, + fontSize: 12, + fontWeight: FontWeight.w700, + ), + ), + ); + } +} + +class _TeamLogoBadge extends StatelessWidget { + final String? logoUrl; + final double size; + + const _TeamLogoBadge({required this.logoUrl, required this.size}); + + @override + Widget build(BuildContext context) { + return Container( + width: size, + height: size, + decoration: BoxDecoration( + color: Colors.white.withValues(alpha: 0.06), + shape: BoxShape.circle, + border: Border.all(color: Colors.white10), + ), + clipBehavior: Clip.antiAlias, + child: Padding(padding: const EdgeInsets.all(10), child: _buildLogo()), + ); + } + + Widget _buildLogo() { + final value = logoUrl ?? ''; + if (value.isEmpty) { + return const Icon(Icons.groups_2, size: 34); + } + if (value.startsWith('assets/')) { + return AdaptiveLogoImage( + logoPath: value, + fit: BoxFit.contain, + fallback: const Icon(Icons.groups_2, size: 34), + ); + } + return Image.network( + value, + fit: BoxFit.contain, + errorBuilder: (_, _, _) => const Icon(Icons.groups_2, size: 34), + ); + } +} + +class _RecurringPlayer { + final String name; + final int appearances; + + const _RecurringPlayer({required this.name, required this.appearances}); +} + +class _TeamDetailsData { + final List results; + final Map metadataByName; + + const _TeamDetailsData({required this.results, required this.metadataByName}); +} + +int _comparePlaces(String a, String b) { + final rankA = _placeRank(a); + final rankB = _placeRank(b); + if (rankA != rankB) { + return rankA.compareTo(rankB); + } + return a.compareTo(b); +} + +int _placeRank(String place) { + final match = RegExp( + r'^(\d+)(?:st|nd|rd|th)?(?:-(\d+)(?:st|nd|rd|th)?)?$', + ).firstMatch(place); + if (match == null) { + return 1 << 20; + } + return int.tryParse(match.group(1) ?? '') ?? (1 << 20); +} diff --git a/lib/presentation/screens/team_list_screen.dart b/lib/presentation/screens/team_list_screen.dart new file mode 100644 index 00000000..2ad80ff5 --- /dev/null +++ b/lib/presentation/screens/team_list_screen.dart @@ -0,0 +1,248 @@ +import 'package:flutter/material.dart'; + +import '../../core/utils/date_format_helper.dart'; +import '../../data/models/tournament_dto.dart'; +import '../../data/repositories/local_data_repository.dart'; +import '../widgets/adaptive_logo_image.dart'; +import '../widgets/async_collection_loader.dart'; +import 'team_details_screen.dart'; + +class TeamListScreen extends StatefulWidget { + final LocalDataRepository repository; + + const TeamListScreen({super.key, required this.repository}); + + @override + State createState() => _TeamListScreenState(); +} + +class _TeamListScreenState extends State { + late Future> _future; + String _query = ''; + + @override + void initState() { + super.initState(); + _future = widget.repository.loadTournamentTeams(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text('Major Teams')), + body: AsyncCollectionLoader( + future: _future, + builder: (context, items) { + final filtered = items.where((item) { + final query = _query.trim().toLowerCase(); + if (query.isEmpty) return true; + return item.teamName.toLowerCase().contains(query); + }).toList(); + + return Column( + children: [ + Padding( + padding: const EdgeInsets.fromLTRB(12, 12, 12, 0), + child: TextField( + onChanged: (value) => setState(() => _query = value), + decoration: const InputDecoration( + prefixIcon: Icon(Icons.search), + hintText: 'Search teams', + ), + ), + ), + Expanded( + child: filtered.isEmpty + ? const Center( + child: Text( + 'No teams found.', + style: TextStyle(color: Colors.white70), + ), + ) + : ListView.separated( + padding: const EdgeInsets.all(12), + itemCount: filtered.length, + separatorBuilder: (_, _) => const SizedBox(height: 12), + itemBuilder: (context, index) { + final team = filtered[index]; + return _TeamSummaryCard( + team: team, + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (_) => TeamDetailsScreen( + teamName: team.teamName, + repository: widget.repository, + ), + ), + ); + }, + ); + }, + ), + ), + ], + ); + }, + ), + ); + } +} + +class _TeamSummaryCard extends StatelessWidget { + final TournamentTeamSummaryDto team; + final VoidCallback onTap; + + const _TeamSummaryCard({required this.team, required this.onTap}); + + @override + Widget build(BuildContext context) { + final latestDate = DateFormatHelper.formatReleaseDate(team.latestStartDate); + + return InkWell( + borderRadius: BorderRadius.circular(16), + onTap: onTap, + child: Card( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + side: const BorderSide(color: Colors.white10), + ), + child: Padding( + padding: const EdgeInsets.all(16), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _TeamLogoBadge(logoUrl: team.teamLogo, size: 56), + const SizedBox(width: 14), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + team.teamName, + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.w700, + ), + ), + const SizedBox(height: 8), + Wrap( + spacing: 8, + runSpacing: 8, + children: [ + _StatChip( + label: '${team.tournamentCount} Majors', + color: Colors.blueAccent, + ), + if ((team.bestPlace ?? '').isNotEmpty) + _StatChip( + label: 'Best: ${team.bestPlace}', + color: Colors.amber, + ), + if (team.titleCount > 0) + _StatChip( + label: '${team.titleCount} titles', + color: Colors.greenAccent, + ), + ], + ), + if ((team.latestTournamentName ?? '').isNotEmpty) ...[ + const SizedBox(height: 10), + Text( + 'Latest Major: ${team.latestTournamentName}', + style: const TextStyle( + color: Colors.white70, + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ], + if (latestDate != null) ...[ + const SizedBox(height: 4), + Text( + 'Latest appearance: $latestDate', + style: const TextStyle( + color: Colors.white60, + fontSize: 12, + ), + ), + ], + ], + ), + ), + ], + ), + ), + ), + ); + } +} + +class _StatChip extends StatelessWidget { + final String label; + final Color color; + + const _StatChip({required this.label, required this.color}); + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6), + decoration: BoxDecoration( + color: color.withValues(alpha: 0.16), + borderRadius: BorderRadius.circular(999), + border: Border.all(color: color.withValues(alpha: 0.35)), + ), + child: Text( + label, + style: TextStyle( + color: color, + fontSize: 12, + fontWeight: FontWeight.w700, + ), + ), + ); + } +} + +class _TeamLogoBadge extends StatelessWidget { + final String? logoUrl; + final double size; + + const _TeamLogoBadge({required this.logoUrl, required this.size}); + + @override + Widget build(BuildContext context) { + return Container( + width: size, + height: size, + decoration: BoxDecoration( + color: Colors.white.withValues(alpha: 0.06), + shape: BoxShape.circle, + border: Border.all(color: Colors.white10), + ), + clipBehavior: Clip.antiAlias, + child: Padding(padding: const EdgeInsets.all(8), child: _buildLogo()), + ); + } + + Widget _buildLogo() { + final value = logoUrl ?? ''; + if (value.isEmpty) { + return const Icon(Icons.groups_2_outlined, size: 28); + } + if (value.startsWith('assets/')) { + return AdaptiveLogoImage( + logoPath: value, + fit: BoxFit.contain, + fallback: const Icon(Icons.groups_2_outlined, size: 28), + ); + } + return Image.network( + value, + fit: BoxFit.contain, + errorBuilder: (_, _, _) => const Icon(Icons.groups_2_outlined, size: 28), + ); + } +} diff --git a/lib/presentation/screens/tournament_details_screen.dart b/lib/presentation/screens/tournament_details_screen.dart new file mode 100644 index 00000000..dab6eec1 --- /dev/null +++ b/lib/presentation/screens/tournament_details_screen.dart @@ -0,0 +1,882 @@ +import 'package:flutter/material.dart'; + +import '../../core/utils/team_name_helper.dart'; +import '../../core/utils/date_format_helper.dart'; +import '../../data/models/container_dto.dart'; +import '../../data/models/tournament_dto.dart'; +import '../../data/models/tournament_metadata_dto.dart'; +import '../../data/repositories/local_data_repository.dart'; +import '../helpers/app_navigation_helper.dart'; +import '../widgets/detail_info_row.dart'; +import '../widgets/detail_source_section.dart'; +import '../widgets/detail_source_tile.dart'; +import '../widgets/detail_tag.dart'; +import '../widgets/adaptive_logo_image.dart'; +import 'player_details_screen.dart'; +import 'team_details_screen.dart'; + +class TournamentDetailsScreen extends StatelessWidget { + final LocalDataRepository repository; + final TournamentDto tournament; + + const TournamentDetailsScreen({ + super.key, + required this.repository, + required this.tournament, + }); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: Text(tournament.name)), + body: FutureBuilder<_TournamentDetailsData>( + future: _loadData(), + builder: (context, snapshot) { + if (snapshot.connectionState != ConnectionState.done) { + return const Center(child: CircularProgressIndicator()); + } + + if (snapshot.hasError) { + return Center( + child: Padding( + padding: const EdgeInsets.all(24), + child: Text( + 'Failed to load tournament details.\n${snapshot.error}', + textAlign: TextAlign.center, + ), + ), + ); + } + + final data = snapshot.data ?? const _TournamentDetailsData(); + + return ListView( + padding: const EdgeInsets.all(12), + children: [ + Card( + child: Padding( + padding: const EdgeInsets.all(16), + child: LayoutBuilder( + builder: (context, constraints) { + final narrow = constraints.maxWidth < 700; + + final image = Container( + alignment: Alignment.center, + child: AdaptiveLogoImage( + logoPath: tournament.imagePath, + height: narrow ? 150 : 200, + fit: BoxFit.contain, + fallback: const Icon(Icons.emoji_events, size: 72), + ), + ); + + final info = Column( + crossAxisAlignment: narrow + ? CrossAxisAlignment.center + : CrossAxisAlignment.start, + children: [ + Text( + tournament.name, + textAlign: narrow + ? TextAlign.center + : TextAlign.left, + style: const TextStyle( + fontSize: 22, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 6), + Text( + tournament.organizer, + textAlign: narrow + ? TextAlign.center + : TextAlign.left, + style: const TextStyle( + color: Colors.white70, + fontSize: 16, + ), + ), + const SizedBox(height: 12), + Wrap( + spacing: 8, + runSpacing: 8, + children: [ + DetailTag( + text: 'Major', + color: Colors.amber.shade400, + ), + DetailTag(text: tournament.eraLabel), + ], + ), + const SizedBox(height: 14), + DetailInfoRow( + title: 'Organizer', + value: tournament.organizer, + ), + if (data.metadata != null) + DetailInfoRow( + title: 'Winner', + value: data.metadata!.winner, + ), + DetailInfoRow( + title: 'Era', + value: tournament.eraLabel, + ), + if (data.metadata?.startDate != null || + data.metadata?.endDate != null) + DetailInfoRow( + title: 'Tournament Dates', + value: + DateFormatHelper.formatDateRange( + data.metadata?.startDate, + data.metadata?.endDate, + ) ?? + '-', + ), + DetailInfoRow( + title: 'Souvenir Packages', + value: data.souvenirPackages.length.toString(), + ), + DetailInfoRow( + title: 'Sticker Sources', + value: data.stickerSources.length.toString(), + ), + ], + ); + + if (narrow) { + return Column( + children: [image, const SizedBox(height: 16), info], + ); + } + + return Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded(flex: 4, child: image), + const SizedBox(width: 16), + Expanded(flex: 5, child: info), + ], + ); + }, + ), + ), + ), + const SizedBox(height: 12), + if (_hasMeaningfulPlayoffBracket(data.metadata)) ...[ + DetailSourceSection( + title: 'Playoff Bracket', + items: data.metadata!.playoffMatches, + emptyText: 'No playoff bracket data available.', + itemBuilder: (_) => const SizedBox.shrink(), + contentBuilder: (items) => + _buildPlayoffBracket(context, items, data.metadata), + ), + const SizedBox(height: 12), + ], + DetailSourceSection( + title: 'Placements', + items: + data.metadata?.placements ?? + const [], + emptyText: 'No placement data added for this tournament yet.', + itemBuilder: (_) => const SizedBox.shrink(), + contentBuilder: (items) => + _buildPlacementsByPhase(context, items, data.metadata), + ), + const SizedBox(height: 12), + DetailSourceSection( + title: 'Souvenir Packages', + items: data.souvenirPackages, + emptyText: 'No souvenir packages found for this tournament.', + itemBuilder: (item) => _buildContainerTile(context, item), + ), + const SizedBox(height: 12), + DetailSourceSection( + title: 'Sticker and Autograph Capsules', + items: data.stickerSources, + emptyText: 'No sticker sources found for this tournament.', + itemBuilder: (item) => _buildContainerTile(context, item), + ), + ], + ); + }, + ), + ); + } + + Future<_TournamentDetailsData> _loadData() async { + final souvenirPackages = await repository.loadSouvenirPackagesForTournament( + tournament.name, + ); + final stickerSources = await repository.loadStickerContainersForTournament( + tournament.name, + ); + final metadata = await repository.loadTournamentMetadataByName( + tournament.name, + ); + return _TournamentDetailsData( + metadata: metadata, + souvenirPackages: souvenirPackages, + stickerSources: stickerSources, + ); + } + + Widget _buildPlacementsByPhase( + BuildContext context, + List items, + TournamentMetadataDto? metadata, + ) { + final scheme = _placementScheme(tournament); + if (scheme == _PlacementScheme.none) { + return _buildPlacementRows(context, items, metadata); + } + + final groupedByPhase = >{}; + for (final item in items) { + final phase = _placementPhaseLabel(tournament, item.place); + groupedByPhase + .putIfAbsent(phase, () => []) + .add(item); + } + + final orderedPhases = groupedByPhase.keys.toList() + ..sort( + (a, b) => _placementPhaseOrder( + tournament, + a, + ).compareTo(_placementPhaseOrder(tournament, b)), + ); + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + for (final phase in orderedPhases) ...[ + Padding( + padding: const EdgeInsets.only(bottom: 8), + child: Row( + children: [ + Icon( + _placementPhaseIcon(phase), + size: 18, + color: Colors.amber.shade300, + ), + const SizedBox(width: 8), + Text( + phase, + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w700, + ), + ), + if (_stageDateText(metadata, phase) case final dateText?) + Padding( + padding: const EdgeInsets.only(left: 8), + child: Text( + dateText, + style: const TextStyle( + color: Colors.white60, + fontSize: 13, + fontWeight: FontWeight.w500, + ), + ), + ), + ], + ), + ), + _buildPlacementRows(context, groupedByPhase[phase]!, metadata), + if (phase != orderedPhases.last) const SizedBox(height: 8), + ], + ], + ); + } + + Widget _buildPlacementRows( + BuildContext context, + List items, + TournamentMetadataDto? metadata, + ) { + final grouped = >{}; + for (final item in items) { + grouped.putIfAbsent(item.place, () => []).add(item.team); + } + + return Column( + children: [ + for (final entry in grouped.entries) + Container( + margin: const EdgeInsets.only(bottom: 10), + padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 12), + decoration: BoxDecoration( + color: Colors.white.withValues(alpha: 0.04), + borderRadius: BorderRadius.circular(12), + border: Border.all(color: Colors.white10), + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + width: 68, + child: Padding( + padding: const EdgeInsets.only(top: 2), + child: Text( + entry.key, + style: const TextStyle( + fontWeight: FontWeight.w700, + color: Colors.amber, + ), + ), + ), + ), + const SizedBox(width: 12), + Expanded( + child: Wrap( + spacing: 8, + runSpacing: 8, + children: [ + for (final team in entry.value) + InkWell( + borderRadius: BorderRadius.circular(10), + onTap: () { + _showTournamentTeamSheet( + context, + team: team, + logoPath: items + .firstWhere((item) => item.team == team) + .teamLogo, + metadata: metadata, + ); + }, + child: Container( + padding: const EdgeInsets.symmetric( + horizontal: 10, + vertical: 8, + ), + decoration: BoxDecoration( + color: Colors.white.withValues(alpha: 0.05), + borderRadius: BorderRadius.circular(10), + border: Border.all(color: Colors.white12), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + _TeamLogo( + logoPath: items + .firstWhere((item) => item.team == team) + .teamLogo, + size: 18, + ), + const SizedBox(width: 8), + Flexible( + child: Text( + team, + style: const TextStyle( + fontWeight: FontWeight.w600, + ), + ), + ), + ], + ), + ), + ), + ], + ), + ), + ], + ), + ), + ], + ); + } + + Widget _buildContainerTile(BuildContext context, ContainerDto item) { + final date = + DateFormatHelper.formatReleaseDate(item.releaseDate) ?? + item.releaseDate ?? + '-'; + + return DetailSourceTile( + imagePath: item.containerImage, + title: item.name, + subtitle: item.typeLabel, + trailing: date, + onTap: () { + AppNavigationHelper.pushScreen( + context, + AppNavigationHelper.buildContainerOpenScreen( + containerDto: item, + repository: repository, + ), + ); + }, + ); + } + + String? _stageDateText(TournamentMetadataDto? metadata, String phase) { + if (metadata == null) { + return null; + } + + for (final stage in metadata.stageDates) { + if (stage.phase == phase) { + return DateFormatHelper.formatDateRange(stage.startDate, stage.endDate); + } + } + + return null; + } + + Widget _buildPlayoffBracket( + BuildContext context, + List items, + TournamentMetadataDto? metadata, + ) { + final grouped = >{}; + for (final item in items) { + grouped + .putIfAbsent(item.round, () => []) + .add(item); + } + + final rounds = grouped.keys.toList(); + return SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + for (final round in rounds) + Container( + width: 260, + margin: EdgeInsets.only(right: round == rounds.last ? 0 : 12), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + round, + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w700, + ), + ), + const SizedBox(height: 8), + ...grouped[round]!.map( + (match) => _buildPlayoffMatchCard(context, match, metadata), + ), + ], + ), + ), + ], + ), + ); + } + + Widget _buildPlayoffMatchCard( + BuildContext context, + TournamentPlayoffMatchDto match, + TournamentMetadataDto? metadata, + ) { + return Container( + margin: const EdgeInsets.only(bottom: 10), + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: Colors.white.withValues(alpha: 0.04), + borderRadius: BorderRadius.circular(12), + border: Border.all(color: Colors.white10), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildPlayoffTeamRow( + context, + match.team1, + match.team1Logo, + match.score1, + metadata, + ), + const SizedBox(height: 6), + _buildPlayoffTeamRow( + context, + match.team2, + match.team2Logo, + match.score2, + metadata, + ), + if ((match.date ?? '').isNotEmpty) ...[ + const SizedBox(height: 10), + Text( + match.date!, + style: const TextStyle(color: Colors.white60, fontSize: 12), + ), + ], + ], + ), + ); + } + + Widget _buildPlayoffTeamRow( + BuildContext context, + String team, + String? logoPath, + String? score, + TournamentMetadataDto? metadata, + ) { + return Row( + children: [ + Expanded( + child: InkWell( + borderRadius: BorderRadius.circular(8), + onTap: () { + _showTournamentTeamSheet( + context, + team: team, + logoPath: logoPath, + metadata: metadata, + ); + }, + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 2), + child: Row( + children: [ + _TeamLogo(logoPath: logoPath, size: 18), + const SizedBox(width: 8), + Expanded( + child: Text( + team, + style: const TextStyle(fontWeight: FontWeight.w600), + ), + ), + ], + ), + ), + ), + ), + if ((score ?? '').isNotEmpty) + Text( + score!, + style: const TextStyle( + color: Colors.amber, + fontWeight: FontWeight.w700, + ), + ), + ], + ); + } + + void _showTournamentTeamSheet( + BuildContext context, { + required String team, + required String? logoPath, + required TournamentMetadataDto? metadata, + }) { + final canonical = TeamNameHelper.canonicalize(team); + TournamentTeamRosterDto? roster; + + if (metadata != null) { + for (final entry in metadata.teamRosters) { + if (TeamNameHelper.canonicalize(entry.team) == canonical) { + roster = entry; + break; + } + } + } + + showModalBottomSheet( + context: context, + isScrollControlled: true, + builder: (sheetContext) { + return SafeArea( + child: Padding( + padding: const EdgeInsets.fromLTRB(16, 16, 16, 24), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + _TeamLogo(logoPath: roster?.teamLogo ?? logoPath, size: 28), + const SizedBox(width: 10), + Expanded( + child: Text( + team, + style: const TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + ), + ], + ), + const SizedBox(height: 14), + if (roster != null && roster.players.isNotEmpty) ...[ + const Text( + 'Tournament roster', + style: TextStyle( + color: Colors.white70, + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + const SizedBox(height: 10), + Wrap( + spacing: 8, + runSpacing: 8, + children: [ + for (final player in roster.players) + ActionChip( + label: Text(player), + onPressed: () { + Navigator.pop(sheetContext); + AppNavigationHelper.pushScreen( + context, + PlayerDetailsScreen( + playerName: player, + repository: repository, + ), + ); + }, + ), + ], + ), + const SizedBox(height: 16), + ] else ...[ + const Text( + 'No roster data available for this tournament team.', + style: TextStyle(color: Colors.white70), + ), + const SizedBox(height: 16), + ], + SizedBox( + width: double.infinity, + child: FilledButton.tonal( + onPressed: () { + Navigator.pop(sheetContext); + AppNavigationHelper.pushScreen( + context, + TeamDetailsScreen( + teamName: team, + repository: repository, + ), + ); + }, + child: const Text('Open team page'), + ), + ), + ], + ), + ), + ); + }, + ); + } +} + +String _placementPhaseLabel(TournamentDto tournament, String place) { + final start = _placementStart(place); + if (start == null) { + return 'Placements'; + } + + switch (_placementScheme(tournament)) { + case _PlacementScheme.none: + return 'Placements'; + case _PlacementScheme.classic: + if (start <= 8) return 'Champions Stage'; + if (start <= 16) return 'Legends Stage'; + return 'Challengers Stage'; + case _PlacementScheme.cs2Transitional: + if (start <= 8) return 'Playoff Stage'; + if (start <= 16) return 'Elimination Stage'; + return 'Opening Stage'; + case _PlacementScheme.modern: + if (start <= 8) return 'Playoffs'; + if (start <= 16) return 'Stage 3'; + if (start <= 24) return 'Stage 2'; + return 'Stage 1'; + } +} + +int _placementPhaseOrder(TournamentDto tournament, String phase) { + switch (_placementScheme(tournament)) { + case _PlacementScheme.none: + return 0; + case _PlacementScheme.classic: + switch (phase) { + case 'Champions Stage': + return 0; + case 'Legends Stage': + return 1; + case 'Challengers Stage': + return 2; + default: + return 3; + } + case _PlacementScheme.cs2Transitional: + switch (phase) { + case 'Playoff Stage': + return 0; + case 'Elimination Stage': + return 1; + case 'Opening Stage': + return 2; + default: + return 3; + } + case _PlacementScheme.modern: + switch (phase) { + case 'Playoffs': + return 0; + case 'Stage 3': + return 1; + case 'Stage 2': + return 2; + case 'Stage 1': + return 3; + default: + return 4; + } + } +} + +IconData _placementPhaseIcon(String phase) { + switch (phase) { + case 'Champions Stage': + case 'Playoff Stage': + case 'Playoffs': + return Icons.emoji_events_outlined; + case 'Elimination Stage': + case 'Legends Stage': + case 'Stage 3': + return Icons.workspace_premium_outlined; + case 'Opening Stage': + case 'Challengers Stage': + case 'Stage 1': + case 'Stage 2': + return Icons.groups_2_outlined; + default: + return Icons.style_outlined; + } +} + +_PlacementScheme _placementScheme(TournamentDto tournament) { + final name = tournament.name; + + if (_preBostonMajors.contains(name)) { + return _PlacementScheme.none; + } + + if (_classicStageMajors.contains(name)) { + return _PlacementScheme.classic; + } + + if (name == 'PGL Copenhagen 2024' || name == 'Perfect World Shanghai 2024') { + return _PlacementScheme.cs2Transitional; + } + + return _PlacementScheme.modern; +} + +int? _placementStart(String place) { + final match = RegExp( + r'^(\d+)(?:st|nd|rd|th)?(?:-(\d+)(?:st|nd|rd|th)?)?$', + ).firstMatch(place); + if (match == null) { + return null; + } + return int.tryParse(match.group(1)!); +} + +const _preBostonMajors = { + 'DreamHack Winter 2013', + 'EMS One Katowice 2014', + 'ESL One Cologne 2014', + 'DreamHack Winter 2014', + 'ESL One Katowice 2015', + 'ESL One Cologne 2015', + 'DreamHack Cluj-Napoca 2015', + 'MLG Columbus 2016', + 'ESL One Cologne 2016', + 'ELEAGUE Atlanta 2017', + 'PGL Kraków 2017', +}; + +const _classicStageMajors = { + 'ELEAGUE Boston 2018', + 'ELEAGUE Major Boston 2018', + 'FACEIT London 2018', + 'IEM Katowice 2019', + 'StarLadder Berlin 2019', + 'PGL Stockholm 2021', + 'PGL Antwerp 2022', + 'IEM Rio 2022', + 'BLAST.tv Paris 2023', +}; + +enum _PlacementScheme { none, classic, cs2Transitional, modern } + +class _TeamLogo extends StatelessWidget { + final String? logoPath; + final double size; + + const _TeamLogo({required this.logoPath, required this.size}); + + @override + Widget build(BuildContext context) { + final value = logoPath ?? ''; + if (value.isEmpty) { + return Icon(Icons.shield_outlined, size: size, color: Colors.white60); + } + + if (value.startsWith('assets/')) { + return AdaptiveLogoImage( + logoPath: value, + width: size, + height: size, + fit: BoxFit.contain, + fallback: Icon( + Icons.shield_outlined, + size: size, + color: Colors.white60, + ), + ); + } + + return Image.network( + value, + width: size, + height: size, + fit: BoxFit.contain, + errorBuilder: (_, _, _) => + Icon(Icons.shield_outlined, size: size, color: Colors.white60), + ); + } +} + +class _TournamentDetailsData { + final TournamentMetadataDto? metadata; + final List souvenirPackages; + final List stickerSources; + + const _TournamentDetailsData({ + this.metadata, + this.souvenirPackages = const [], + this.stickerSources = const [], + }); +} + +bool _hasMeaningfulPlayoffBracket(TournamentMetadataDto? metadata) { + final matches = + metadata?.playoffMatches ?? const []; + if (matches.isEmpty) { + return false; + } + + final validRounds = { + 'Quarterfinals', + 'Semifinals', + 'Grand Final', + 'Playoff Stage', + 'Playoffs', + }; + + return matches.any((match) => validRounds.contains(match.round)); +} diff --git a/lib/presentation/screens/tournament_list_screen.dart b/lib/presentation/screens/tournament_list_screen.dart new file mode 100644 index 00000000..c1f19ae8 --- /dev/null +++ b/lib/presentation/screens/tournament_list_screen.dart @@ -0,0 +1,125 @@ +import 'package:flutter/material.dart'; + +import '../../core/utils/date_format_helper.dart'; +import '../../data/models/tournament_dto.dart'; +import '../../data/repositories/local_data_repository.dart'; +import '../helpers/source_color_helper.dart'; +import '../widgets/async_collection_loader.dart'; +import '../widgets/chip_badge.dart'; +import '../widgets/collection_list_card.dart'; +import '../widgets/responsive_collection_grid.dart'; +import 'player_list_screen.dart'; +import 'team_list_screen.dart'; +import 'tournament_details_screen.dart'; + +class TournamentListScreen extends StatefulWidget { + final LocalDataRepository repository; + + const TournamentListScreen({super.key, required this.repository}); + + @override + State createState() => _TournamentListScreenState(); +} + +class _TournamentListScreenState extends State { + late Future> _future; + + @override + void initState() { + super.initState(); + _future = widget.repository.loadTournaments(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Majors'), + actions: [ + IconButton( + tooltip: 'Players', + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (_) => + PlayerListScreen(repository: widget.repository), + ), + ); + }, + icon: const Icon(Icons.person_search_outlined), + ), + IconButton( + tooltip: 'Teams', + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (_) => TeamListScreen(repository: widget.repository), + ), + ); + }, + icon: const Icon(Icons.groups_2_outlined), + ), + ], + ), + body: AsyncCollectionLoader( + future: _future, + builder: (context, items) { + return ResponsiveCollectionGrid( + items: items, + emptyMessage: 'No tournaments found.', + itemBuilder: (context, tournament) { + final eraColor = tournament.isCs2Era + ? Colors.tealAccent.shade400 + : SourceColorHelper.containerTypeColor('SOUVENIR_PACKAGE'); + + return CollectionListCard( + imagePath: tournament.imagePath, + title: tournament.name, + dateLabel: 'Dates', + releaseDate: + DateFormatHelper.formatDateRange( + tournament.startDate, + tournament.endDate, + ) ?? + tournament.releaseDate, + chips: [ + ChipBadge(label: 'Major', color: Colors.amber.shade400), + ChipBadge(label: tournament.eraLabel, color: eraColor), + ], + metadata: [ + const SizedBox(height: 8), + Text( + tournament.organizer, + style: const TextStyle( + color: Colors.white70, + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + const SizedBox(height: 4), + Text( + '${tournament.souvenirPackageCount} souvenir packages • ${tournament.stickerContainerCount} sticker sources', + style: const TextStyle(color: Colors.white60, fontSize: 12), + ), + ], + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (_) => TournamentDetailsScreen( + repository: widget.repository, + tournament: tournament, + ), + ), + ); + }, + ); + }, + ); + }, + ), + ); + } +} diff --git a/lib/presentation/widgets/adaptive_logo_image.dart b/lib/presentation/widgets/adaptive_logo_image.dart new file mode 100644 index 00000000..83518193 --- /dev/null +++ b/lib/presentation/widgets/adaptive_logo_image.dart @@ -0,0 +1,76 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +class AdaptiveLogoImage extends StatelessWidget { + final String logoPath; + final double? width; + final double? height; + final BoxFit fit; + final Widget? fallback; + + const AdaptiveLogoImage({ + super.key, + required this.logoPath, + this.width, + this.height, + this.fit = BoxFit.contain, + this.fallback, + }); + + static bool shouldUseAdaptiveTint(String path) { + final normalized = path.toLowerCase(); + return normalized.startsWith('assets/tournament_logos/'); + } + + bool get _isSvg => logoPath.toLowerCase().endsWith('.svg'); + + bool _shouldLightenForTheme(BuildContext context) { + return Theme.of(context).brightness == Brightness.dark && + shouldUseAdaptiveTint(logoPath); + } + + Widget _buildImage(BuildContext context, Widget fallbackWidget) { + if (_isSvg) { + return SvgPicture.asset( + logoPath, + width: width, + height: height, + fit: fit, + placeholderBuilder: (_) => SizedBox( + width: width, + height: height, + child: Center(child: fallbackWidget), + ), + ); + } + + return Image.asset( + logoPath, + width: width, + height: height, + fit: fit, + errorBuilder: (_, _, _) => fallbackWidget, + ); + } + + @override + Widget build(BuildContext context) { + final fallbackWidget = + fallback ?? + Icon(Icons.image_not_supported_outlined, size: width ?? height ?? 24); + final image = _buildImage(context, fallbackWidget); + + if (!_shouldLightenForTheme(context)) { + return image; + } + + return Container( + padding: const EdgeInsets.all(8), + decoration: BoxDecoration( + color: Colors.white.withValues(alpha: 0.92), + borderRadius: BorderRadius.circular(14), + ), + child: image, + ); + } +} diff --git a/lib/presentation/widgets/asset_collection_image.dart b/lib/presentation/widgets/asset_collection_image.dart index 623d3c16..4eb6d48c 100644 --- a/lib/presentation/widgets/asset_collection_image.dart +++ b/lib/presentation/widgets/asset_collection_image.dart @@ -1,6 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'adaptive_logo_image.dart'; + class AssetCollectionImage extends StatelessWidget { final String assetPath; final double? height; @@ -17,6 +19,10 @@ class AssetCollectionImage extends StatelessWidget { @override Widget build(BuildContext context) { + if (AdaptiveLogoImage.shouldUseAdaptiveTint(assetPath)) { + return AdaptiveLogoImage(logoPath: assetPath, height: height, fit: fit); + } + if (_isSvg) { return SvgPicture.asset( assetPath, diff --git a/lib/presentation/widgets/collection_list_card.dart b/lib/presentation/widgets/collection_list_card.dart index a04a7f99..9b8da016 100644 --- a/lib/presentation/widgets/collection_list_card.dart +++ b/lib/presentation/widgets/collection_list_card.dart @@ -7,6 +7,7 @@ class CollectionListCard extends StatelessWidget { final String imagePath; final String title; final String? releaseDate; + final String dateLabel; final List chips; final List metadata; final VoidCallback onTap; @@ -16,6 +17,7 @@ class CollectionListCard extends StatelessWidget { required this.imagePath, required this.title, required this.releaseDate, + this.dateLabel = 'Released', required this.chips, required this.metadata, required this.onTap, @@ -66,7 +68,7 @@ class CollectionListCard extends StatelessWidget { if (formattedReleaseDate != null) ...[ const SizedBox(height: 6), Text( - 'Released: $formattedReleaseDate', + '$dateLabel: $formattedReleaseDate', style: const TextStyle( color: Colors.white70, fontSize: 13, diff --git a/lib/presentation/widgets/detail_source_section.dart b/lib/presentation/widgets/detail_source_section.dart index 1acf0ee8..f222fd0c 100644 --- a/lib/presentation/widgets/detail_source_section.dart +++ b/lib/presentation/widgets/detail_source_section.dart @@ -5,6 +5,7 @@ class DetailSourceSection extends StatelessWidget { final List items; final String emptyText; final Widget Function(T item) itemBuilder; + final Widget Function(List items)? contentBuilder; const DetailSourceSection({ super.key, @@ -12,6 +13,7 @@ class DetailSourceSection extends StatelessWidget { required this.items, required this.emptyText, required this.itemBuilder, + this.contentBuilder, }); @override @@ -29,6 +31,8 @@ class DetailSourceSection extends StatelessWidget { const SizedBox(height: 12), if (items.isEmpty) Text(emptyText, style: const TextStyle(color: Colors.white70)) + else if (contentBuilder != null) + contentBuilder!(items) else ...items.map(itemBuilder), ], diff --git a/pubspec.yaml b/pubspec.yaml index add6e9b9..256d806a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: cs2_simulator description: "Counter-Strike 2 case opening and Trade-Up contract creation simulator written in Flutter + Dart." publish_to: 'none' -version: 0.10.0 +version: 0.11.0 environment: sdk: ^3.11.3 diff --git a/tool/generate_app_icon.dart b/tool/generate_app_icon.dart index 85c2aebc..49d0f73e 100644 --- a/tool/generate_app_icon.dart +++ b/tool/generate_app_icon.dart @@ -7,6 +7,9 @@ void main() async { final root = Directory.current; final containersFile = File('${root.path}/assets/data/containers.json'); + final tournamentMetadataFile = File( + '${root.path}/assets/data/tournament_metadata.json', + ); if (!containersFile.existsSync()) { stderr.writeln('containers.json not found: ${containersFile.path}'); @@ -17,6 +20,19 @@ void main() async { await containersFile.readAsString(), 'containers.json', ); + final tournamentMetadata = tournamentMetadataFile.existsSync() + ? _readJsonList( + await tournamentMetadataFile.readAsString(), + 'tournament_metadata.json', + ) + : const >[]; + final tournamentLogoByName = { + for (final item in tournamentMetadata) + if (((item['name'] as String?) ?? '').trim().isNotEmpty && + ((item['tournamentLogo'] as String?) ?? '').trim().isNotEmpty) + _canonicalTournamentName((item['name'] as String).trim()): + (item['tournamentLogo'] as String).trim(), + }; final candidates = <_IconCandidate>[]; @@ -29,7 +45,12 @@ void main() async { String? imageRel; switch (type) { case 'SOUVENIR_PACKAGE': - final tournamentLogo = (item['tournamentLogo'] as String?)?.trim(); + final tournamentName = (item['tournamentName'] as String?)?.trim(); + final tournamentLogo = + ((tournamentName != null && tournamentName.isNotEmpty) + ? tournamentLogoByName[_canonicalTournamentName(tournamentName)] + : null) ?? + (item['tournamentLogo'] as String?)?.trim(); imageRel = (tournamentLogo != null && tournamentLogo.isNotEmpty) ? tournamentLogo : (item['containerImage'] as String?)?.trim(); @@ -235,6 +256,26 @@ void main() async { stdout.writeln('Generated: assets/app_icon/transparent_bg.png'); } +String _canonicalTournamentName(String rawTournamentName) { + final trimmed = rawTournamentName + .trim() + .replaceAll('ELEAGUE Major Boston 2018', 'ELEAGUE Boston 2018') + .replaceAll('Krakow', 'Kraków') + .replaceAll(RegExp(r'\s+'), ' '); + if (trimmed.isEmpty) { + return trimmed; + } + + final yearPrefix = RegExp(r'^(20\d{2}) (.+)$').firstMatch(trimmed); + if (yearPrefix != null) { + final year = yearPrefix.group(1)!; + final rest = yearPrefix.group(2)!; + return '$rest $year'; + } + + return trimmed; +} + class _IconCandidate { final String name; final String kind; diff --git a/tool/import_tournament_data.dart b/tool/import_tournament_data.dart new file mode 100644 index 00000000..41c55cd9 --- /dev/null +++ b/tool/import_tournament_data.dart @@ -0,0 +1,1029 @@ +import 'dart:convert'; +import 'dart:io'; + +const _userAgent = 'CS2-Simulator tournament importer/0.11'; +const _liquipediaApiBase = 'https://liquipedia.net/counterstrike/api.php'; +const _liquipediaRawBase = + 'https://liquipedia.net/counterstrike/index.php?action=raw&title='; +const _liquipediaRenderBase = + 'https://liquipedia.net/counterstrike/index.php?action=render&title='; + +const _pageTitleOverrides = { + 'BLAST.tv Austin 2025': 'BLAST/Major/2025/Austin', + 'BLAST.tv Paris 2023': 'BLAST/Major/2023/Paris', + 'DreamHack Cluj-Napoca 2015': 'DreamHack/2015/Cluj-Napoca', + 'DreamHack Winter 2013': 'DreamHack/2013/Winter', + 'DreamHack Winter 2014': 'DreamHack/2014/Winter', + 'ELEAGUE Atlanta 2017': 'ELEAGUE/2017/Major', + 'ELEAGUE Boston 2018': 'ELEAGUE/2018/Major', + 'EMS One Katowice 2014': 'ESL/Major_Series_One/2014/Katowice', + 'ESL One Cologne 2014': 'ESL/One/2014/Cologne', + 'ESL One Cologne 2015': 'ESL/One/2015/Cologne', + 'ESL One Cologne 2016': 'ESL/One/2016/Cologne', + 'ESL One Katowice 2015': 'ESL/One/2015/Katowice', + 'FACEIT London 2018': 'FACEIT/2018/Major', + 'IEM Katowice 2019': 'Intel_Extreme_Masters/Season_XIII/World_Championship', + 'IEM Rio 2022': 'Intel_Extreme_Masters/2022/Rio', + 'MLG Columbus 2016': 'MLG/2016/Columbus', + 'PGL Antwerp 2022': 'PGL/2022/Antwerp', + 'PGL Copenhagen 2024': 'PGL/2024/Copenhagen', + 'PGL Kraków 2017': 'PGL/2017/Krakow', + 'PGL Stockholm 2021': 'PGL/2021/Stockholm', + 'Perfect World Shanghai 2024': 'Perfect_World/Major/2024/Shanghai', + 'StarLadder Berlin 2019': 'StarLadder/2019/Major', + 'StarLadder Budapest 2025': 'StarLadder/2025/Major', +}; + +Future main() async { + final client = HttpClient() + ..userAgent = _userAgent + ..autoUncompress = true; + + final containersFile = File('assets/data/containers.json'); + final metadataFile = File('assets/data/tournament_metadata.json'); + + if (!containersFile.existsSync()) { + stderr.writeln('assets/data/containers.json not found.'); + exit(1); + } + + final containers = + (jsonDecode(await containersFile.readAsString()) as List) + .whereType() + .map((entry) => entry.map((k, v) => MapEntry(k.toString(), v))) + .toList(); + + final existingMetadata = metadataFile.existsSync() + ? (jsonDecode(await metadataFile.readAsString()) as List) + .whereType() + .map((entry) => entry.map((k, v) => MapEntry(k.toString(), v))) + .toList() + : >[]; + + final existingByName = { + for (final entry in existingMetadata) + _canonicalTournamentName((entry['name'] ?? '').toString()): entry, + }; + + final tournamentNames = + containers + .map((entry) => (entry['tournamentName'] ?? '').toString().trim()) + .where((name) => name.isNotEmpty) + .map(_canonicalTournamentName) + .toSet() + .toList() + ..sort(); + + final results = >[]; + + for (final tournamentName in tournamentNames) { + stdout.writeln('Importing tournament metadata: $tournamentName'); + + final existing = existingByName[tournamentName]; + try { + final pageTitle = await _resolvePageTitle(client, tournamentName); + final rawPage = await _fetchPage(client, 'raw', pageTitle); + final renderedPage = await _fetchPage(client, 'render', pageTitle); + final tournamentLogo = await _materializeTournamentLogo( + client, + tournamentName, + await _extractPreferredTournamentLogo(client, rawPage, renderedPage) ?? + (existing?['tournamentLogo'] as String?), + ); + final tournamentDates = _parseStartEndDates(renderedPage); + final stagePages = _stagePagesForTournament(pageTitle, tournamentName); + + final importedPlacements = _parsePlacementsFromRenderedHtml(renderedPage); + final placements = _hasValidPlacements(importedPlacements) + ? importedPlacements + : _readExistingPlacements(existing); + final winner = _readWinner(placements, existing); + final playoffMatches = _parsePlayoffMatchesFromRenderedHtml( + _playoffPageTitle(pageTitle, tournamentName) == pageTitle + ? renderedPage + : await _fetchPage( + client, + 'render', + _playoffPageTitle(pageTitle, tournamentName), + ), + ); + await _materializeTeamLogos(client, placements, playoffMatches); + final teamRosters = _parseTeamRostersFromRawPage(rawPage, placements); + final stageDates = >[]; + for (final stagePage in stagePages) { + try { + final stageRawPage = await _fetchPage( + client, + 'raw', + stagePage.pageTitle, + ); + final stageRange = _parseHiddenDataBoxDates(stageRawPage); + if ((stageRange.$1 ?? '').isEmpty && (stageRange.$2 ?? '').isEmpty) { + continue; + } + stageDates.add({ + 'phase': stagePage.phase, + if ((stageRange.$1 ?? '').isNotEmpty) 'startDate': stageRange.$1!, + if ((stageRange.$2 ?? '').isNotEmpty) 'endDate': stageRange.$2!, + }); + } catch (_) { + // Keep partial metadata if a stage page is missing or shaped differently. + } + } + + results.add({ + 'name': tournamentName, + 'winner': winner, + if ((tournamentLogo ?? '').isNotEmpty) 'tournamentLogo': tournamentLogo, + if ((tournamentDates.$1 ?? '').isNotEmpty) + 'startDate': tournamentDates.$1, + if ((tournamentDates.$2 ?? '').isNotEmpty) + 'endDate': tournamentDates.$2, + 'placements': placements, + 'teamRosters': teamRosters, + 'stageDates': stageDates, + 'playoffMatches': playoffMatches, + }); + } catch (error) { + stdout.writeln(' [WARN] $tournamentName: $error'); + if (existing != null) { + results.add(existing); + } else { + results.add({ + 'name': tournamentName, + 'winner': '', + 'tournamentLogo': existing?['tournamentLogo'], + 'startDate': null, + 'endDate': null, + 'placements': const >[], + 'teamRosters': const >[], + 'stageDates': const >[], + 'playoffMatches': const >[], + }); + } + } + } + + final encoder = const JsonEncoder.withIndent(' '); + await metadataFile.writeAsString('${encoder.convert(results)}\n'); + stdout.writeln('Tournament metadata written: ${results.length} tournaments'); +} + +Future _resolvePageTitle( + HttpClient client, + String tournamentName, +) async { + final override = _pageTitleOverrides[tournamentName]; + if (override != null) { + return override; + } + + final yearMatch = RegExp(r'(20\d{2})').firstMatch(tournamentName); + final year = yearMatch?.group(1); + final query = Uri.encodeQueryComponent('$tournamentName Counter-Strike'); + final uri = Uri.parse( + '$_liquipediaApiBase?action=query&list=search&srsearch=$query&format=json', + ); + final json = await _fetchJson(client, uri); + final search = ((json['query'] as Map?)?['search'] as List?) ?? const []; + + for (final result in search.whereType()) { + final title = result['title']?.toString() ?? ''; + if (title.isEmpty) { + continue; + } + if (year != null && !title.contains(year)) { + continue; + } + if (title.contains('/')) { + return title; + } + } + + throw StateError('No Liquipedia page found for $tournamentName'); +} + +Future _fetchPage( + HttpClient client, + String mode, + String pageTitle, +) async { + final base = mode == 'render' ? _liquipediaRenderBase : _liquipediaRawBase; + final accept = mode == 'render' ? 'text/html' : 'text/plain'; + final uri = Uri.parse('$base${Uri.encodeQueryComponent(pageTitle)}'); + final request = await client.getUrl(uri); + request.headers.set(HttpHeaders.acceptHeader, accept); + final response = await request.close(); + if (response.statusCode < 200 || response.statusCode >= 300) { + throw HttpException('HTTP ${response.statusCode}', uri: uri); + } + return utf8.decoder.bind(response).join(); +} + +Future> _fetchJson(HttpClient client, Uri uri) async { + final request = await client.getUrl(uri); + request.headers.set(HttpHeaders.acceptHeader, 'application/json'); + request.headers.set(HttpHeaders.acceptEncodingHeader, 'gzip'); + final response = await request.close(); + if (response.statusCode < 200 || response.statusCode >= 300) { + throw HttpException('HTTP ${response.statusCode}', uri: uri); + } + final body = await utf8.decoder.bind(response).join(); + return (jsonDecode(body) as Map).cast(); +} + +List> _parsePlacementsFromRenderedHtml( + String renderedPage, +) { + final section = _extractHtmlSection(renderedPage, 'Prize_Pool'); + if (section == null || section.isEmpty) { + return const []; + } + + final tableIndex = section.indexOf('prizepooltable-placement'); + if (tableIndex < 0) { + return const []; + } + + final placeCells = RegExp( + r'
]*>(.*?)
', + dotAll: true, + ).allMatches(section.substring(tableIndex)).toList(); + + final placements = >[]; + final tableHtml = section.substring(tableIndex); + + for (var index = 0; index < placeCells.length; index++) { + final current = placeCells[index]; + final nextStart = index + 1 < placeCells.length + ? placeCells[index + 1].start + : tableHtml.length; + final chunk = tableHtml.substring(current.start, nextStart); + + final place = _normalizePlace(_cleanHtmlText(current.group(1)!)); + if (place.isEmpty) { + continue; + } + + final teams = _parseTeamBlocks(chunk); + + for (final teamEntry in teams) { + final team = teamEntry.$1; + final logo = teamEntry.$2; + if (team.isEmpty) { + continue; + } + placements.add({ + 'place': place, + 'team': team, + if ((logo ?? '').isNotEmpty) 'teamLogo': logo!, + }); + } + } + + return placements; +} + +List> _parseTeamRostersFromRawPage( + String rawPage, + List> placements, +) { + final start = rawPage.indexOf('==Participants=='); + if (start < 0) { + return const []; + } + + final afterStart = rawPage.substring(start); + final resultsIndex = afterStart.indexOf('\n==Results=='); + final section = resultsIndex < 0 + ? afterStart + : afterStart.substring(0, resultsIndex); + + final rosterBlocks = _extractTemplateBlocks(section, '{{TeamCard'); + if (rosterBlocks.isEmpty) { + return const []; + } + + final placementLogoByTeam = { + for (final placement in placements) + placement['team']!: placement['teamLogo'], + }; + final rosters = >[]; + final seenTeams = {}; + + for (final block in rosterBlocks) { + final team = _extractTemplateField(block, 'team')?.trim() ?? ''; + if (team.isEmpty || !seenTeams.add(team)) { + continue; + } + + final players = []; + for (var i = 1; i <= 5; i++) { + final player = _extractTemplateField(block, 'p$i')?.trim() ?? ''; + if (player.isEmpty) continue; + players.add(_cleanWikiValue(player)); + } + + if (players.isEmpty) { + continue; + } + + rosters.add({ + 'team': _cleanWikiValue(team), + if ((placementLogoByTeam[_cleanWikiValue(team)] ?? '').isNotEmpty) + 'teamLogo': placementLogoByTeam[_cleanWikiValue(team)], + 'players': players, + }); + } + + return rosters; +} + +(String?, String?) _parseStartEndDates(String renderedPage) { + final startDate = _extractInfoboxValue(renderedPage, 'Start Date:'); + final endDate = _extractInfoboxValue(renderedPage, 'End Date:'); + return (startDate, endDate); +} + +(String?, String?) _parseHiddenDataBoxDates(String rawPage) { + final sdate = _extractTemplateField(rawPage, 'sdate'); + final edate = _extractTemplateField(rawPage, 'edate'); + return (sdate, edate); +} + +List<_StagePageRef> _stagePagesForTournament( + String basePageTitle, + String tournamentName, +) { + final phases = _allowedStagePhases(tournamentName); + return phases + .map( + (phase) => + _StagePageRef(pageTitle: '$basePageTitle/$phase', phase: phase), + ) + .toList(); +} + +String _playoffPageTitle(String basePageTitle, String tournamentName) { + if (_preBostonMajors.contains(tournamentName)) { + return basePageTitle; + } + + if (_classicStageMajors.contains(tournamentName)) { + return '$basePageTitle/Champions Stage'; + } + + if (tournamentName == 'PGL Copenhagen 2024' || + tournamentName == 'Perfect World Shanghai 2024') { + return '$basePageTitle/Playoff Stage'; + } + + return '$basePageTitle/Playoffs'; +} + +List> _parsePlayoffMatchesFromRenderedHtml( + String renderedPage, +) { + final bracketIndex = renderedPage.indexOf('brkts-bracket-wrapper'); + if (bracketIndex < 0) { + return const []; + } + + final section = renderedPage.substring(bracketIndex); + final headers = + RegExp( + r'
]*>([^<]+)
', + dotAll: true, + ) + .allMatches(section) + .map((match) => _cleanHtmlText(match.group(1) ?? '')) + .where((text) => text.isNotEmpty) + .toList(); + + const matchMarker = '
(.*?)
', + dotAll: true, + ).allMatches(block).take(2).map((match) { + final value = _cleanHtmlText(match.group(1) ?? ''); + return value; + }).toList(); + final dateMatch = RegExp( + r']*>(.*?)', + dotAll: true, + ).firstMatch(block); + final date = _cleanHtmlText(dateMatch?.group(1) ?? ''); + + if (teams.length < 2) { + continue; + } + + results.add({ + 'round': round, + 'team1': teams[0].$1, + 'team2': teams[1].$1, + if ((teams[0].$2 ?? '').isNotEmpty) 'team1Logo': teams[0].$2!, + if ((teams[1].$2 ?? '').isNotEmpty) 'team2Logo': teams[1].$2!, + if (scores.isNotEmpty) 'score1': scores[0], + if (scores.length > 1) 'score2': scores[1], + if (date.isNotEmpty) 'date': date, + }); + } + + return results; +} + +List _buildBracketRoundOrder(int roundCount) { + final order = []; + + void walk(int depth) { + if (depth >= roundCount) { + return; + } + if (depth == 0) { + order.add(0); + return; + } + walk(depth - 1); + walk(depth - 1); + order.add(depth); + } + + walk(roundCount - 1); + return order; +} + +String? _extractInfoboxValue(String renderedPage, String label) { + final pattern = RegExp( + RegExp.escape(label) + r'
(.*?)
', + dotAll: true, + ); + final match = pattern.firstMatch(renderedPage); + if (match == null) { + return null; + } + final cleaned = _cleanHtmlText(match.group(1) ?? ''); + return cleaned.isEmpty ? null : cleaned; +} + +Future _extractPreferredTournamentLogo( + HttpClient client, + String rawPage, + String renderedPage, +) async { + final renderedFileImages = _extractRenderedFileImageMap(renderedPage); + final preferredFileNames = [ + if ((_extractTemplateField(rawPage, 'icon') ?? '').isNotEmpty) + _extractTemplateField(rawPage, 'icon')!, + if ((_extractTemplateField(rawPage, 'icondarkmode') ?? '').isNotEmpty) + _extractTemplateField(rawPage, 'icondarkmode')!, + if ((_extractTemplateField(rawPage, 'image') ?? '').isNotEmpty) + _extractTemplateField(rawPage, 'image')!, + if ((_extractTemplateField(rawPage, 'imagedark') ?? '').isNotEmpty) + _extractTemplateField(rawPage, 'imagedark')!, + ]; + + for (final fileName in preferredFileNames) { + final normalized = fileName.trim().replaceAll('_', ' '); + final fileUrl = await _fetchLiquipediaFileUrl(client, normalized); + if ((fileUrl ?? '').isNotEmpty) { + return fileUrl; + } + final resolved = renderedFileImages[normalized]; + if ((resolved ?? '').isNotEmpty) { + return resolved; + } + } + + return null; +} + +Future _fetchLiquipediaFileUrl( + HttpClient client, + String fileName, +) async { + if (fileName.isEmpty) { + return null; + } + + final title = Uri.encodeQueryComponent('File:$fileName'); + final uri = Uri.parse( + '$_liquipediaApiBase?action=query&titles=$title&prop=imageinfo&iiprop=url&format=json', + ); + + try { + final json = await _fetchJson(client, uri); + final pages = ((json['query'] as Map?)?['pages'] as Map?) ?? const {}; + for (final page in pages.values) { + if (page is! Map) { + continue; + } + final imageInfo = page['imageinfo']; + if (imageInfo is! List || imageInfo.isEmpty || imageInfo.first is! Map) { + continue; + } + final url = (imageInfo.first as Map)['url']?.toString().trim(); + if ((url ?? '').isNotEmpty) { + return url; + } + } + } catch (_) { + return null; + } + + return null; +} + +Map _extractRenderedFileImageMap(String renderedPage) { + final result = {}; + final matches = RegExp( + r']* class="image"> _resolveLiquipediaAssetUrl(src)); + } + + return result; +} + +String? _extractTemplateField(String rawPage, String fieldName) { + final match = RegExp( + r'^\|' + RegExp.escape(fieldName) + r'\s*=\s*(.+?)\s*$', + multiLine: true, + ).firstMatch(rawPage); + if (match == null) { + return null; + } + final value = match.group(1)?.trim(); + return (value == null || value.isEmpty) ? null : value; +} + +List _extractTemplateBlocks(String source, String templateStart) { + final blocks = []; + var searchIndex = 0; + + while (true) { + final start = source.indexOf(templateStart, searchIndex); + if (start < 0) break; + + var depth = 0; + var end = start; + while (end < source.length - 1) { + final pair = source.substring(end, end + 2); + if (pair == '{{') { + depth += 1; + end += 2; + continue; + } + if (pair == '}}') { + depth -= 1; + end += 2; + if (depth <= 0) { + blocks.add(source.substring(start, end)); + break; + } + continue; + } + end += 1; + } + + searchIndex = end; + } + + return blocks; +} + +String? _extractHtmlSection(String html, String sectionId) { + final headingMatch = RegExp( + '.*?', + dotAll: true, + ).firstMatch(html); + if (headingMatch == null) { + return null; + } + + final start = headingMatch.end; + final remaining = html.substring(start); + final nextHeading = RegExp( + r'
', + ).firstMatch(remaining); + final end = nextHeading == null ? html.length : start + nextHeading.start; + return html.substring(start, end); +} + +List> _readExistingPlacements( + Map? existing, +) { + final raw = existing?['placements']; + if (raw is! List) { + return const []; + } + + return raw + .whereType() + .map( + (entry) => { + 'place': (entry['place'] ?? '').toString(), + 'team': (entry['team'] ?? '').toString(), + if ((entry['teamLogo'] ?? '').toString().isNotEmpty) + 'teamLogo': (entry['teamLogo'] ?? '').toString(), + }, + ) + .where((entry) => entry['place']!.isNotEmpty && entry['team']!.isNotEmpty) + .toList(); +} + +String _readWinner( + List> placements, + Map? existing, +) { + final fromPlacements = placements.firstWhere( + (entry) => entry['place'] == '1st', + orElse: () => const {'team': ''}, + )['team']; + + if (fromPlacements != null && fromPlacements.isNotEmpty) { + return fromPlacements; + } + + return (existing?['winner'] ?? '').toString(); +} + +bool _hasValidPlacements(List> placements) { + if (placements.isEmpty) { + return false; + } + return placements.any((entry) => entry['place'] == '1st'); +} + +String _normalizePlace(String rawPlace) { + final cleaned = rawPlace + .replaceAll('–', '-') + .replaceAll('—', '-') + .replaceAll(RegExp(r'\s+'), ' ') + .trim(); + if (cleaned.isEmpty) { + return ''; + } + + final rangeMatch = RegExp( + r'^(\d+)(?:st|nd|rd|th)?\s*-\s*(\d+)(?:st|nd|rd|th)?$', + ).firstMatch(cleaned); + if (rangeMatch != null) { + final start = int.parse(rangeMatch.group(1)!); + final end = int.parse(rangeMatch.group(2)!); + return '${_ordinal(start)}-${_ordinal(end)}'; + } + + final singleMatch = RegExp(r'^(\d+)(?:st|nd|rd|th)?$').firstMatch(cleaned); + if (singleMatch != null) { + return _ordinal(int.parse(singleMatch.group(1)!)); + } + + return ''; +} + +String _ordinal(int value) { + final remainder100 = value % 100; + if (remainder100 >= 11 && remainder100 <= 13) { + return '${value}th'; + } + + switch (value % 10) { + case 1: + return '${value}st'; + case 2: + return '${value}nd'; + case 3: + return '${value}rd'; + default: + return '${value}th'; + } +} + +String _cleanHtmlText(String input) { + var value = input; + value = value.replaceAll(RegExp(r'<[^>]+>'), ' '); + value = value + .replaceAll(' ', ' ') + .replaceAll('-', '-') + .replaceAll('&', '&') + .replaceAll(' ', ' ') + .replaceAll('–', '-') + .replaceAll('—', '-'); + value = value.replaceAll(RegExp(r'\s+'), ' '); + return value.trim(); +} + +String _cleanWikiValue(String input) { + var value = input.trim(); + value = value.replaceAll(RegExp(r'\s*\|[A-Za-z0-9_]+\s*=.*$'), ''); + value = value.replaceAll(RegExp(r']*>.*?', dotAll: true), ''); + value = value.replaceAll(RegExp(r'<[^>]+>'), ' '); + value = value.replaceAll(RegExp(r'\[\[(?:[^|\]]+\|)?([^\]]+)\]\]'), r'$1'); + value = value.replaceAll(RegExp(r'\{\{player\|([^|}]+).*?\}\}'), r'$1'); + value = value.replaceAll(RegExp(r'\{\{!.*?\}\}'), ''); + value = value.replaceAll("'''", ''); + value = value.replaceAll("''", ''); + value = value.replaceAll(' ', ' '); + value = value.replaceAll(RegExp(r'\s+'), ' '); + return value.trim(); +} + +List<(String, String?)> _parseTeamBlocks(String htmlChunk) { + final blocks = RegExp( + r'
(.*?)
\s*
', + dotAll: true, + ).allMatches(htmlChunk); + + final teams = <(String, String?)>[]; + for (final block in blocks) { + final inner = block.group(1) ?? ''; + final nameMatch = RegExp( + r']*>\s*
]*>(.*?)\s*', + dotAll: true, + ).firstMatch(inner); + final name = _cleanHtmlText(nameMatch?.group(1) ?? ''); + if (name.isEmpty) { + continue; + } + teams.add((name, _extractPreferredTeamLogo(inner))); + } + + return teams; +} + +List<(String, String?)> _parseBracketTeams(String htmlChunk) { + final entries = RegExp( + r'
]*aria-label="([^"]+)"[^>]*>(.*?)
\s*
', + dotAll: true, + ).allMatches(htmlChunk); + + final teams = <(String, String?)>[]; + for (final entry in entries.take(2)) { + final name = _cleanHtmlText(entry.group(1) ?? ''); + if (name.isEmpty) { + continue; + } + teams.add((name, _extractPreferredTeamLogo(entry.group(2) ?? ''))); + } + return teams; +} + +String? _extractPreferredTeamLogo(String htmlChunk) { + final imageMatches = RegExp( + r']+src="([^"]+)"', + dotAll: true, + ).allMatches(htmlChunk); + + String? fallback; + for (final match in imageMatches) { + final raw = match.group(1) ?? ''; + if (raw.isEmpty) { + continue; + } + final resolved = _resolveLiquipediaAssetUrl(raw); + fallback ??= resolved; + if (raw.contains('allmode') || raw.contains('lightmode')) { + return resolved; + } + } + + return fallback; +} + +String _resolveLiquipediaAssetUrl(String rawUrl) { + if (rawUrl.startsWith('http://') || rawUrl.startsWith('https://')) { + return rawUrl; + } + if (rawUrl.startsWith('//')) { + return 'https:$rawUrl'; + } + if (rawUrl.startsWith('/')) { + return 'https://liquipedia.net$rawUrl'; + } + return 'https://liquipedia.net/$rawUrl'; +} + +Future _materializeTeamLogos( + HttpClient client, + List> placements, + List> playoffMatches, +) async { + final logoDir = Directory('assets/tournament_logos'); + if (!logoDir.existsSync()) { + await logoDir.create(recursive: true); + } + + Future localize(String? value, String teamName) async { + if (value == null || value.isEmpty) { + return null; + } + if (value.startsWith('assets/')) { + return value; + } + + final uri = Uri.parse(value); + final extension = _detectImageExtension(value); + final fileName = + '${_slugify(teamName)}_${_slugify(uri.pathSegments.last)}$extension'; + final file = File('${logoDir.path}/$fileName'); + if (!file.existsSync()) { + try { + final request = await client.getUrl(uri); + final response = await request.close(); + if (response.statusCode >= 200 && response.statusCode < 300) { + final bytes = await response.fold>( + [], + (acc, data) => acc..addAll(data), + ); + await file.writeAsBytes(bytes); + } + } catch (_) { + return value; + } + } + return 'assets/tournament_logos/$fileName'; + } + + for (final placement in placements) { + if ((placement['teamLogo'] ?? '').isNotEmpty) { + placement['teamLogo'] = + await localize(placement['teamLogo'], placement['team'] ?? '') ?? + placement['teamLogo']!; + } + } + + for (final match in playoffMatches) { + if ((match['team1Logo'] ?? '').isNotEmpty) { + match['team1Logo'] = + await localize(match['team1Logo'], match['team1'] ?? '') ?? + match['team1Logo']!; + } + if ((match['team2Logo'] ?? '').isNotEmpty) { + match['team2Logo'] = + await localize(match['team2Logo'], match['team2'] ?? '') ?? + match['team2Logo']!; + } + } +} + +Future _materializeTournamentLogo( + HttpClient client, + String tournamentName, + String? logoValue, +) async { + if (logoValue == null || logoValue.isEmpty) { + return null; + } + if (logoValue.startsWith('assets/')) { + return logoValue; + } + + final logoDir = Directory('assets/tournament_logos'); + if (!logoDir.existsSync()) { + await logoDir.create(recursive: true); + } + + final uri = Uri.parse(logoValue); + final extension = _detectImageExtension(logoValue); + final slug = _slugify(tournamentName); + for (final ext in ['.png', '.svg', '.webp', '.jpg']) { + final candidate = File('${logoDir.path}/$slug$ext'); + if (candidate.existsSync()) { + await candidate.delete(); + } + } + + final fileName = '$slug$extension'; + final file = File('${logoDir.path}/$fileName'); + try { + final request = await client.getUrl(uri); + final response = await request.close(); + if (response.statusCode >= 200 && response.statusCode < 300) { + final bytes = await response.fold>( + [], + (acc, data) => acc..addAll(data), + ); + await file.writeAsBytes(bytes); + } else { + return logoValue; + } + } catch (_) { + return logoValue; + } + return 'assets/tournament_logos/$fileName'; +} + +String _detectImageExtension(String url) { + final lower = Uri.parse(url).path.toLowerCase(); + if (lower.endsWith('.svg')) return '.svg'; + if (lower.endsWith('.webp')) return '.webp'; + if (lower.endsWith('.jpg') || lower.endsWith('.jpeg')) return '.jpg'; + return '.png'; +} + +String _slugify(String value) { + return value + .toLowerCase() + .replaceAll(RegExp(r'[^a-z0-9]+'), '_') + .replaceAll(RegExp(r'_+'), '_') + .replaceAll(RegExp(r'^_|_$'), ''); +} + +String _canonicalTournamentName(String rawTournamentName) { + final repaired = rawTournamentName + .replaceAll('ELEAGUE Major Boston 2018', 'ELEAGUE Boston 2018') + .replaceAll('KrakГіw', 'Kraków') + .replaceAll('KrakГіw', 'Kraków') + .trim() + .replaceAll(RegExp(r'\s+'), ' '); + if (repaired.isEmpty) { + return repaired; + } + + final yearPrefix = RegExp(r'^(20\d{2}) (.+)$').firstMatch(repaired); + if (yearPrefix != null) { + final year = yearPrefix.group(1)!; + final rest = yearPrefix.group(2)!; + return '$rest $year'; + } + + return repaired; +} + +class _StagePageRef { + final String pageTitle; + final String phase; + + const _StagePageRef({required this.pageTitle, required this.phase}); +} + +Set _allowedStagePhases(String tournamentName) { + if (_preBostonMajors.contains(tournamentName)) { + return const {}; + } + + if (_classicStageMajors.contains(tournamentName)) { + return const {'Challengers Stage', 'Legends Stage', 'Champions Stage'}; + } + + if (tournamentName == 'PGL Copenhagen 2024' || + tournamentName == 'Perfect World Shanghai 2024') { + return const {'Opening Stage', 'Elimination Stage', 'Playoff Stage'}; + } + + return const {'Stage 1', 'Stage 2', 'Stage 3', 'Playoffs'}; +} + +const _preBostonMajors = { + 'DreamHack Winter 2013', + 'EMS One Katowice 2014', + 'ESL One Cologne 2014', + 'DreamHack Winter 2014', + 'ESL One Katowice 2015', + 'ESL One Cologne 2015', + 'DreamHack Cluj-Napoca 2015', + 'MLG Columbus 2016', + 'ESL One Cologne 2016', + 'ELEAGUE Atlanta 2017', + 'PGL Kraków 2017', +}; + +const _classicStageMajors = { + 'ELEAGUE Boston 2018', + 'ELEAGUE Major Boston 2018', + 'FACEIT London 2018', + 'IEM Katowice 2019', + 'StarLadder Berlin 2019', + 'PGL Stockholm 2021', + 'PGL Antwerp 2022', + 'IEM Rio 2022', + 'BLAST.tv Paris 2023', +}; diff --git a/tool/importer/src/dart_backend.dart b/tool/importer/src/dart_backend.dart index 85bed203..16e0c5f9 100644 --- a/tool/importer/src/dart_backend.dart +++ b/tool/importer/src/dart_backend.dart @@ -42,6 +42,12 @@ class DartImporterBackend implements ImporterBackend { final existingCharms = _io.loadJsonList( File('${dataDir.path}/charms.json'), ); + final tournamentMetadataFile = File( + '${dataDir.path}/tournament_metadata.json', + ); + final tournamentMetadata = tournamentMetadataFile.existsSync() + ? _io.loadJsonList(tournamentMetadataFile) + : >[]; final existingCases = allExistingCases.where((item) { final type = (item['type'] ?? '').toString().trim().toUpperCase(); return !{ @@ -215,7 +221,10 @@ class DartImporterBackend implements ImporterBackend { final collectionImageByName = buildCollectionImageMap(skinsData); buildCollectionMetaMap(collectionsData); - final tournamentLogoByName = buildTournamentLogoMap(stickersData); + final tournamentLogoByName = { + ...buildTournamentLogoMap(stickersData), + ...buildTournamentLogoMapFromMetadata(tournamentMetadata), + }; _io.printInfo('Tournament logo candidates: ${tournamentLogoByName.length}'); diff --git a/tool/importer/src/normalization.dart b/tool/importer/src/normalization.dart index 1bc4ec92..caf0a377 100644 --- a/tool/importer/src/normalization.dart +++ b/tool/importer/src/normalization.dart @@ -842,6 +842,26 @@ Map buildTournamentLogoMap( return result; } +Map buildTournamentLogoMapFromMetadata( + List> metadataEntries, +) { + final result = {}; + + for (final entry in metadataEntries) { + final tournamentName = (entry['name'] ?? '').toString().trim(); + final logo = (entry['tournamentLogo'] ?? '').toString().trim(); + if (tournamentName.isEmpty || logo.isEmpty) { + continue; + } + + for (final key in tournamentNameCandidates(tournamentName)) { + result.putIfAbsent(key, () => logo); + } + } + + return result; +} + String resolveContainerReleaseDate({ required String crateName, required String containerType,