diff --git a/README.md b/README.md index f6c729e..bf07c5c 100644 --- a/README.md +++ b/README.md @@ -2,21 +2,19 @@ Track your and other players stats in TETR.IO +Tetra Stats works with TETR.IO Tetra Channel API, providing data from it and calculating some addtitional metrics, based on this data. + You can [download an app](https://github.com/dan63047/TetraStats/releases), or [use web version](https://ts.dan63.by). ![Screenshot of the app 1](https://imgur.com/e8CYvj3.png) # Available functionality - Advanced stats for players +- Charts for analyzing players Tetra League standing and Tetra League itself - Ranks cutoffs -- Minimums, averages, and maximums for every stat of every rank, as well, as whole leaderboard -- Chart for analyzing tetra league state +- Full and sortable Tetra League leagerboard +- Stats and Damage Calculator - Local database, that can store players data -- Comparison to players, rank averages, and player stats from the past -- Stats Calculator -- Player history in charts -- Tetra League matches history -- Time-weighted stats in Tetra League matches # Special thanks - **kerrmunism** — formulas @@ -24,5 +22,7 @@ You can [download an app](https://github.com/dan63047/TetraStats/releases), or [ - **neko_ab4093** — Simplified Chinese localization - **osk** and his team — TETR.IO -## Legal note -I do NOT own any assets located in `/res/*`, excluding app icon (`/res/icons/app.png`) and localization (`/res/i18n/*`), which is distributed under GNU license (as well, as this software) \ No newline at end of file +## Legal notes +Tetra Stats is not associated with TETR.IO or osk in any capacity. + +I do NOT own any assets located in `/res/*`, excluding app icon (`/res/icons/app.png`), localization (`/res/i18n/*`) and images (`/res/images/*`), which is distributed under GNU license (as well, as this software) \ No newline at end of file diff --git a/lib/data_objects/summaries.dart b/lib/data_objects/summaries.dart index 22e22ab..78a726a 100644 --- a/lib/data_objects/summaries.dart +++ b/lib/data_objects/summaries.dart @@ -28,7 +28,7 @@ class Summaries { json['40l']['rank_local']); if (json['blitz']['record'] != null) blitz = RecordSingle.fromJson(json['blitz']['record'], - json['blitz']['rank'], json['40l']['rank_local']); + json['blitz']['rank'], json['blitz']['rank_local']); if (json['zenith']['record'] != null) zenith = RecordSingle.fromJson(json['zenith']['record'], json['zenith']['rank'], json['zenith']['rank_local']); diff --git a/lib/data_objects/tetrio_multiplayer_replay.dart b/lib/data_objects/tetrio_multiplayer_replay.dart index 3b14781..e2c14bc 100644 --- a/lib/data_objects/tetrio_multiplayer_replay.dart +++ b/lib/data_objects/tetrio_multiplayer_replay.dart @@ -38,7 +38,7 @@ class Garbage{ // charsys where??? late int sent; late int recived; int? attack; - late int cleared; + int? cleared; int? sent_normal; int? maxspike; int? maxspike_nomult; @@ -65,7 +65,7 @@ class Garbage{ // charsys where??? } Garbage operator + (Garbage other){ - return Garbage(sent: sent + other.sent, recived: recived + other.recived, attack: attack??0 + (other.attack??0), cleared: cleared + other.cleared); + return Garbage(sent: sent + other.sent, recived: recived + other.recived, attack: attack??0 + (other.attack??0), cleared: (cleared??0) + (other.cleared??0)); } } diff --git a/lib/gen/strings.g.dart b/lib/gen/strings.g.dart index fbca647..1565380 100644 --- a/lib/gen/strings.g.dart +++ b/lib/gen/strings.g.dart @@ -3,10 +3,10 @@ /// Original: res/i18n /// To regenerate, run: `dart run slang` /// -/// Locales: 2 -/// Strings: 1530 (765 per locale) +/// Locales: 3 +/// Strings: 2295 (765 per locale) /// -/// Built on 2024-12-21 at 17:08 UTC +/// Built on 2024-12-31 at 17:29 UTC // coverage:ignore-file // ignore_for_file: type=lint @@ -26,7 +26,8 @@ const AppLocale _baseLocale = AppLocale.en; /// - if (LocaleSettings.currentLocale == AppLocale.en) // locale check enum AppLocale with BaseAppLocale { en(languageCode: 'en', build: Translations.build), - ruRu(languageCode: 'ru', countryCode: 'RU', build: _StringsRuRu.build); + ruRu(languageCode: 'ru', countryCode: 'RU', build: _StringsRuRu.build), + zhCn(languageCode: 'zh', countryCode: 'CN', build: _StringsZhCn.build); const AppLocale({required this.languageCode, this.scriptCode, this.countryCode, required this.build}); // ignore: unused_element @@ -1018,7 +1019,7 @@ class _StringsAboutViewEn { String get providedFormulas => 'Provided formulas'; String get providedS1history => 'Provided S1 history'; String get inoue => 'Inoue (replay grabber)'; - String get zhCNlocale => 'Simplfied Chinise locale'; + String get zhCNlocale => 'Simplfied Chinese locale'; String get supportHim => 'Support him!'; } @@ -2276,7 +2277,7 @@ class _StringsCutoffsDestinationRuRu implements _StringsCutoffsDestinationEn { @override String relevance({required Object timestamp}) => 'на момент ${timestamp}'; @override String get actual => 'Требование'; @override String get target => 'Цель'; - @override String get cutoffTR => 'Требуемый TR'; + @override String get cutoffTR => 'Треб. TR'; @override String get targetTR => 'Целевой TR'; @override String get state => 'Состояние'; @override String get advanced => 'Продвинутая'; @@ -3204,6 +3205,1545 @@ class _StringsStatsLineClearsRuRu implements _StringsStatsLineClearsEn { @override String get penta => 'Pentas'; } +// Path: +class _StringsZhCn implements Translations { + /// You can call this constructor and build your own translation instance of this locale. + /// Constructing via the enum [AppLocale.build] is preferred. + _StringsZhCn.build({Map? overrides, PluralResolver? cardinalResolver, PluralResolver? ordinalResolver}) + : assert(overrides == null, 'Set "translation_overrides: true" in order to enable this feature.'), + $meta = TranslationMetadata( + locale: AppLocale.zhCn, + overrides: overrides ?? {}, + cardinalResolver: cardinalResolver, + ordinalResolver: ordinalResolver, + ) { + $meta.setFlatMapFunction(_flatMapFunction); + } + + /// Metadata for the translations of . + @override final TranslationMetadata $meta; + + /// Access flat map + @override dynamic operator[](String key) => $meta.getTranslation(key); + + @override late final _StringsZhCn _root = this; // ignore: unused_field + + // Translations + @override Map get locales => { + 'en': '英语 (English)', + 'ru-RU': '俄语 (Русский)', + 'zh-CN': '简体中文', + }; + @override Map get gamemodes => { + 'league': 'Tetra 联赛', + 'zenith': '快速游戏', + 'zenithex': '快速游戏 · 专家模式', + '40l': '40行竞速', + 'blitz': '闪电战', + '5mblast': '5,000,000 Blast', + 'zen': '禅意模式', + }; + @override late final _StringsDestinationsZhCn destinations = _StringsDestinationsZhCn._(_root); + @override Map get playerRole => { + 'user': '用户', + 'banned': '已封禁', + 'bot': '机器人', + 'sysop': '系统操作员', + 'admin': '管理员', + 'mod': '管理员', + 'halfmod': '社区管理员', + 'anon': '匿名用户', + }; + @override String get goBackButton => '返回'; + @override String get nanow => '目前不可用...'; + @override String seasonEnds({required Object countdown}) => '当前赛季还有${countdown}结束'; + @override String get seasonEnded => '赛季已结束'; + @override String overallPB({required Object pb}) => '生涯最佳:${pb} m'; + @override String gamesUntilRanked({required Object left}) => '还有${left}局才可获得段位'; + @override String numOfVictories({required Object wins}) => '~${wins}次胜局'; + @override String get promotionOnNextWin => '下一场胜局即可升段'; + @override String numOfdefeats({required Object losses}) => '~${losses}次负局'; + @override String get demotionOnNextLoss => '下一场负局即可掉段'; + @override String get records => '记录'; + @override String get nerdStats => '详细信息'; + @override String get playstyles => '游戏方式'; + @override String get horoscopes => '散点图'; + @override String get relatedAchievements => '相关成就'; + @override String get season => '赛季'; + @override String get smooth => '平滑'; + @override String get dateAndTime => '日期和时间:'; + @override String get TLfullLBnote => '很大,但允许你通过玩家的数据对玩家进行排序,还可以按段位筛选玩家'; + @override String get rank => '段位'; + @override String verdictGeneral({required Object rank, required Object n, required Object verdict}) => '比 ${rank} 段平均数据${n} ${verdict}'; + @override String get verdictBetter => '好'; + @override String get verdictWorse => '差'; + @override String get localStanding => '本地'; + @override late final _StringsXpZhCn xp = _StringsXpZhCn._(_root); + @override late final _StringsGametimeZhCn gametime = _StringsGametimeZhCn._(_root); + @override String get track => '跟踪'; + @override String get stopTracking => '停止跟踪'; + @override String supporter({required Object tier}) => '${tier}级会员'; + @override String comparingWith({required Object newDate, required Object oldDate}) => '${newDate}的数据与${oldDate}相比'; + @override String get compare => '比较'; + @override String get comparison => '比较'; + @override String get enterUsername => '输入用户名或者\$avgX (X是一个段位)'; + @override String get general => '常规'; + @override String get badges => '勋章'; + @override String obtainDate({required Object date}) => '于${date}获得'; + @override String get assignedManualy => '此徽章由TETR.IO管理员手动颁发'; + @override String get distinguishment => '区别'; + @override String get banned => '已封禁'; + @override String get bannedSubtext => '由于 TETR.IO 规则或服务条款被违反 而被封禁'; + @override String get badStanding => '信誉不佳'; + @override String get badStandingSubtext => '近期有一次或多次违禁行为'; + @override String get botAccount => '机器人账号'; + @override String botAccountSubtext({required Object botMaintainers}) => '由${botMaintainers}管理'; + @override String get copiedToClipboard => '已复制到剪贴板!'; + @override String get bio => '个性签名'; + @override String get news => '新闻'; + @override late final _StringsMatchResultZhCn matchResult = _StringsMatchResultZhCn._(_root); + @override late final _StringsDistinguishmentsZhCn distinguishments = _StringsDistinguishmentsZhCn._(_root); + @override late final _StringsNewsEntriesZhCn newsEntries = _StringsNewsEntriesZhCn._(_root); + @override String rankupMiddle({required Object r}) => '${r} 段'; + @override String get copyUserID => '点击以复制用户 ID'; + @override String get searchHint => '用户名或 ID'; + @override String get navMenu => '导航菜单'; + @override String get navMenuTooltip => '打开导航菜单'; + @override String get refresh => '刷新数据'; + @override String get searchButton => '搜索'; + @override String get trackedPlayers => '跟踪的玩家'; + @override String get standing => '名次'; + @override String get previousSeasons => '上赛季'; + @override String get recent => '最近'; + @override String get top => '前'; + @override String get noRecord => '暂无记录'; + @override String sprintAndBlitsRelevance({required Object date}) => '${date}'; + @override late final _StringsSnackBarMessagesZhCn snackBarMessages = _StringsSnackBarMessagesZhCn._(_root); + @override late final _StringsErrorsZhCn errors = _StringsErrorsZhCn._(_root); + @override late final _StringsActionsZhCn actions = _StringsActionsZhCn._(_root); + @override late final _StringsGraphsDestinationZhCn graphsDestination = _StringsGraphsDestinationZhCn._(_root); + @override late final _StringsFilterModaleZhCn filterModale = _StringsFilterModaleZhCn._(_root); + @override late final _StringsCutoffsDestinationZhCn cutoffsDestination = _StringsCutoffsDestinationZhCn._(_root); + @override late final _StringsRankViewZhCn rankView = _StringsRankViewZhCn._(_root); + @override late final _StringsStateViewZhCn stateView = _StringsStateViewZhCn._(_root); + @override late final _StringsTlMatchViewZhCn tlMatchView = _StringsTlMatchViewZhCn._(_root); + @override late final _StringsCalcDestinationZhCn calcDestination = _StringsCalcDestinationZhCn._(_root); + @override late final _StringsInfoDestinationZhCn infoDestination = _StringsInfoDestinationZhCn._(_root); + @override late final _StringsLeaderboardsDestinationZhCn leaderboardsDestination = _StringsLeaderboardsDestinationZhCn._(_root); + @override late final _StringsSavedDataDestinationZhCn savedDataDestination = _StringsSavedDataDestinationZhCn._(_root); + @override late final _StringsSettingsDestinationZhCn settingsDestination = _StringsSettingsDestinationZhCn._(_root); + @override late final _StringsHomeNavigationZhCn homeNavigation = _StringsHomeNavigationZhCn._(_root); + @override late final _StringsGraphsNavigationZhCn graphsNavigation = _StringsGraphsNavigationZhCn._(_root); + @override late final _StringsCalcNavigationZhCn calcNavigation = _StringsCalcNavigationZhCn._(_root); + @override late final _StringsFirstTimeViewZhCn firstTimeView = _StringsFirstTimeViewZhCn._(_root); + @override late final _StringsAboutViewZhCn aboutView = _StringsAboutViewZhCn._(_root); + @override late final _StringsStatsZhCn stats = _StringsStatsZhCn._(_root); + @override Map get countries => { + '': '全球', + 'AF': '阿富汗', + 'AX': '奥兰群岛', + 'AL': '阿尔巴尼亚', + 'DZ': '阿尔及利亚', + 'AS': '美属萨摩亚', + 'AD': '安道尔', + 'AO': '安哥拉', + 'AI': '安圭拉', + 'AQ': '南极洲', + 'AG': '安提瓜和巴布达', + 'AR': '阿根廷', + 'AM': '亚美尼亚', + 'AW': '阿鲁巴', + 'AU': '澳大利亚', + 'AT': '奥地利', + 'AZ': '阿塞拜疆', + 'BS': '巴哈马', + 'BH': '巴林', + 'BD': '孟加拉国', + 'BB': '巴巴多斯', + 'BY': '白俄罗斯', + 'BE': '比利时', + 'BZ': '伯利兹', + 'BJ': '贝宁', + 'BM': '百慕大', + 'BT': '不丹', + 'BO': '玻利维亚', + 'BA': '波黑', + 'BW': '博茨瓦纳', + 'BV': '布韦岛', + 'BR': '巴西', + 'IO': '英属印度洋领地', + 'BN': '文莱', + 'BG': '保加利亚', + 'BF': '布基纳法索', + 'BI': '布隆迪', + 'KH': '柬埔寨', + 'CM': '喀麦隆', + 'CA': '加拿大', + 'CV': '佛得角', + 'BQ': '荷兰加勒比区', + 'KY': '开曼群岛', + 'CF': '中非', + 'TD': '乍得', + 'CL': '智利', + 'CN': '中国', + 'CX': '圣诞岛', + 'CC': '科科斯群岛', + 'CO': '哥伦比亚', + 'KM': '科摩罗', + 'CG': '刚果(布)', + 'CD': '刚果(金)', + 'CK': '库克群岛', + 'CR': '哥斯达黎加', + 'CI': '科特迪瓦', + 'HR': '克罗地亚', + 'CU': '古巴', + 'CW': '库拉索', + 'CY': '塞浦路斯', + 'CZ': '捷克', + 'DK': '丹麦', + 'DJ': '吉布提', + 'DM': '多米尼克', + 'DO': '多米尼加共和国', + 'EC': '厄瓜多尔', + 'EG': '埃及', + 'SV': '萨尔瓦多', + 'GB-ENG': '英格兰', + 'GQ': '赤道几内亚', + 'ER': '厄立特里亚', + 'EE': '爱沙尼亚', + 'ET': '埃塞俄比亚', + 'EU': '欧洲', + 'FK': '福克兰群岛 (马尔维纳斯)', + 'FO': '法罗群岛', + 'FJ': '斐济', + 'FI': '芬兰', + 'FR': '法国', + 'GF': '法属圭亚那', + 'PF': '法属波利尼西亚', + 'TF': '法属南部领地', + 'GA': '加蓬', + 'GM': '冈比亚', + 'GE': '格鲁吉亚', + 'DE': '德国', + 'GH': '加纳', + 'GI': '直布罗陀', + 'GR': '希腊', + 'GL': '格陵兰', + 'GD': '格林纳达', + 'GP': '瓜德罗普', + 'GU': '关岛', + 'GT': '危地马拉', + 'GG': '根西', + 'GN': '几内亚', + 'GW': '几内亚比绍', + 'GY': '圭亚那', + 'HT': '海地', + 'HM': '赫德岛和麦克唐纳群岛', + 'VA': '梵蒂冈', + 'HN': '洪都拉斯', + 'HK': '中国香港', + 'HU': '匈牙利', + 'IS': '冰岛', + 'IN': '印度', + 'ID': '印尼', + 'IR': '伊朗', + 'IQ': '伊拉克', + 'IE': '爱尔兰', + 'IM': '马恩岛', + 'IL': '以色列', + 'IT': '意大利', + 'JM': '牙买加', + 'JP': '日本', + 'JE': '泽西', + 'JO': '约旦', + 'KZ': '哈萨克斯坦', + 'KE': '肯尼亚', + 'KI': '基里巴斯', + 'KP': '朝鲜', + 'KR': '韩国', + 'XK': '科索沃', + 'KW': '科威特', + 'KG': '吉尔吉斯斯坦', + 'LA': '老挝', + 'LV': '拉托维亚', + 'LB': '黎巴嫩', + 'LS': '莱索托', + 'LR': '利比里亚', + 'LY': '利比亚', + 'LI': '列支敦士登', + 'LT': '立陶宛', + 'LU': '卢森堡', + 'MO': '中国澳门', + 'MK': '马其顿', + 'MG': '马达加斯加', + 'MW': '马拉维', + 'MY': '马来西亚', + 'MV': '马尔代夫', + 'ML': '马里', + 'MT': '马耳他', + 'MH': '马绍尔群岛', + 'MQ': '马提尼克', + 'MR': '毛里塔尼亚', + 'MU': '毛里求斯', + 'YT': '马约特岛', + 'MX': '墨西哥', + 'FM': '密克罗尼西亚', + 'MD': '摩尔多瓦', + 'MC': '摩纳哥', + 'ME': '黑山', + 'MA': '摩洛哥', + 'MN': '蒙古', + 'MS': '蒙特塞拉特', + 'MZ': '莫桑比克', + 'MM': '缅甸', + 'NA': '纳米比亚', + 'NR': '瑙鲁', + 'NP': '尼泊尔', + 'NL': '荷兰', + 'AN': '荷属安地列斯', + 'NC': '新喀里多尼亚', + 'NZ': '新西兰', + 'NI': '尼加拉瓜', + 'NE': '尼日尔', + 'NG': '尼日利亚', + 'NU': '纽埃', + 'NF': '诺福克岛', + 'GB-NIR': '北爱尔兰', + 'MP': '北马里亚纳群岛', + 'NO': '挪威', + 'OM': '阿曼', + 'PK': '巴基斯坦', + 'PW': '帕劳', + 'PS': '巴勒斯坦', + 'PA': '巴拿马', + 'PG': '巴布亚新几内亚', + 'PY': '巴拉圭', + 'PE': '秘鲁', + 'PH': '菲律宾', + 'PN': '皮特凯恩', + 'PL': '波兰', + 'PT': '葡萄牙', + 'PR': '波多黎哥', + 'QA': '卡塔尔', + 'RE': '留尼汪', + 'RO': '罗马尼亚', + 'RU': '俄罗斯', + 'RW': '卢旺达', + 'BL': '圣巴泰勒米', + 'SH': '圣赫勒拿-阿森松-特里斯坦达库尼亚', + 'KN': '圣基茨和尼维斯', + 'LC': '圣卢西亚', + 'MF': '圣马丁', + 'PM': '圣皮埃尔和密克隆', + 'VC': '圣文森特和格林纳丁斯', + 'WS': '萨摩亚', + 'SM': '圣马利诺', + 'ST': '圣多美和普林西比', + 'SA': '沙特阿拉伯', + 'GB-SCT': '苏格兰', + 'SN': '塞内加尔', + 'RS': '塞尔维亚', + 'SC': '塞舌尔', + 'SL': '塞拉利昂', + 'SG': '新加坡', + 'SX': '荷属圣马丁', + 'SK': '斯洛伐克', + 'SI': '斯洛文尼亚', + 'SB': '所罗门', + 'SO': '索马里', + 'ZA': '南非', + 'GS': '南乔治亚和南桑威奇', + 'SS': '南苏丹', + 'ES': '西班牙', + 'LK': '斯里兰卡', + 'SD': '苏丹', + 'SR': '苏里南', + 'SJ': '斯瓦尔巴和扬马延', + 'SZ': '斯威士兰', + 'SE': '瑞典', + 'CH': '瑞士', + 'SY': '叙利亚', + 'TW': '中国台湾', + 'TJ': '塔吉克斯坦', + 'TZ': '坦桑尼亚', + 'TH': '泰国', + 'TL': '东帝汶', + 'TG': '多哥', + 'TK': '托克劳', + 'TO': '汤加', + 'TT': '特立尼达和多巴哥', + 'TN': '突尼斯', + 'TR': '土耳其', + 'TM': '土库曼斯坦', + 'TC': '特克斯河凯科斯群岛', + 'TV': '图瓦卢', + 'UG': '乌干达', + 'UA': '乌克兰', + 'AE': '阿联酋', + 'GB': '英国', + 'US': '美国', + 'UY': '乌拉圭', + 'UM': '美国本土外小岛屿', + 'UZ': '乌兹别克斯坦', + 'VU': '瓦努阿图', + 'VE': '委内瑞拉', + 'VN': '越南', + 'VG': '英属维京群岛', + 'VI': '美属维京群岛', + 'GB-WLS': '威尔士', + 'WF': '瓦利斯群岛和富图纳群岛', + 'EH': '西撒哈拉', + 'YE': '也门', + 'ZM': '赞比亚', + 'ZW': '津巴布韦', + 'XX': '未知', + 'XM': '月球', + }; +} + +// Path: destinations +class _StringsDestinationsZhCn implements _StringsDestinationsEn { + _StringsDestinationsZhCn._(this._root); + + @override final _StringsZhCn _root; // ignore: unused_field + + // Translations + @override String get home => '主页'; + @override String get graphs => '图表'; + @override String get leaderboards => '排行榜'; + @override String get cutoffs => '段位分界线'; + @override String get calc => '计算器'; + @override String get info => '信息中心'; + @override String get data => '已保存的数据'; + @override String get settings => '设置'; +} + +// Path: xp +class _StringsXpZhCn implements _StringsXpEn { + _StringsXpZhCn._(this._root); + + @override final _StringsZhCn _root; // ignore: unused_field + + // Translations + @override String get title => '经验等级'; + @override String progressToNextLevel({required Object percentage}) => '到下一等级的进度:${percentage}'; + @override String progressTowardsGoal({required Object goal, required Object percentage, required Object left}) => '从0级到${goal}级的进度:${percentage} (还差 ${left} 点经验值)'; +} + +// Path: gametime +class _StringsGametimeZhCn implements _StringsGametimeEn { + _StringsGametimeZhCn._(this._root); + + @override final _StringsZhCn _root; // ignore: unused_field + + // Translations + @override String get title => '精确游戏时长'; + @override String gametimeAday({required Object gametime}) => '平均每天${gametime}'; + @override String breakdown({required Object years, required Object months, required Object days, required Object minutes, required Object seconds}) => '相当于 ${years} 年,\n${months} 月,\n${days} 天,\n${minutes} 分钟,\n${seconds} 秒'; +} + +// Path: matchResult +class _StringsMatchResultZhCn implements _StringsMatchResultEn { + _StringsMatchResultZhCn._(this._root); + + @override final _StringsZhCn _root; // ignore: unused_field + + // Translations + @override String get victory => '胜利'; + @override String get defeat => '失败'; + @override String get tie => '平局'; + @override String get dqvictory => '对手被取消资格'; + @override String get dqdefeat => '被取消资格'; + @override String get nocontest => '无竞赛记录'; + @override String get nullified => '竞赛记录已取消'; +} + +// Path: distinguishments +class _StringsDistinguishmentsZhCn implements _StringsDistinguishmentsEn { + _StringsDistinguishmentsZhCn._(this._root); + + @override final _StringsZhCn _root; // ignore: unused_field + + // Translations + @override String get noHeader => '缺少标题'; + @override String get noFooter => '缺少标题'; + @override String get twc => 'TETR.IO 世界冠军'; + @override String twcYear({required Object year}) => '${year} TETR.IO 世界杯'; +} + +// Path: newsEntries +class _StringsNewsEntriesZhCn implements _StringsNewsEntriesEn { + _StringsNewsEntriesZhCn._(this._root); + + @override final _StringsZhCn _root; // ignore: unused_field + + // Translations + @override TextSpan leaderboard({required InlineSpan gametype, required InlineSpan rank}) => TextSpan(children: [ + const TextSpan(text: '在'), + gametype, + const TextSpan(text: '中荣获第'), + rank, + const TextSpan(text: '名'), + ]); + @override TextSpan personalbest({required InlineSpan gametype, required InlineSpan pb}) => TextSpan(children: [ + const TextSpan(text: '在'), + gametype, + const TextSpan(text: '中取得新纪录:'), + pb, + ]); + @override TextSpan badge({required InlineSpan badge}) => TextSpan(children: [ + const TextSpan(text: '获得勋章 '), + badge, + ]); + @override TextSpan rankup({required InlineSpan rank}) => TextSpan(children: [ + const TextSpan(text: '升 '), + rank, + ]); + @override TextSpan supporter({required InlineSpanBuilder s}) => TextSpan(children: [ + const TextSpan(text: '成为'), + s('TETR.IO supporter'), + ]); + @override TextSpan supporter_gift({required InlineSpanBuilder s}) => TextSpan(children: [ + const TextSpan(text: '被赠送'), + s('TETR.IO supporter'), + ]); + @override TextSpan unknown({required InlineSpan type}) => TextSpan(children: [ + const TextSpan(text: '未知新闻类型 '), + type, + ]); +} + +// Path: snackBarMessages +class _StringsSnackBarMessagesZhCn implements _StringsSnackBarMessagesEn { + _StringsSnackBarMessagesZhCn._(this._root); + + @override final _StringsZhCn _root; // ignore: unused_field + + // Translations + @override String stateRemoved({required Object date}) => '成功移除${date}时的状态!'; + @override String matchRemoved({required Object date}) => '成功移除${date}时的一局!'; + @override String get notForWeb => '此功能在网络版本中不可用'; + @override String get importSuccess => '导入成功'; + @override String get importCancelled => '导入已取消'; +} + +// Path: errors +class _StringsErrorsZhCn implements _StringsErrorsEn { + _StringsErrorsZhCn._(this._root); + + @override final _StringsZhCn _root; // ignore: unused_field + + // Translations + @override String get noRecords => '暂无记录'; + @override String get notEnoughData => '数据不足'; + @override String get noHistorySaved => '没有保存历史记录'; + @override String connection({required Object code, required Object message}) => '连接错误:${code} ${message}'; + @override String get noSuchUser => '用户不存在'; + @override String get noSuchUserSub => '您输入的内容有误,或者用户不存在'; + @override String get discordNotAssigned => '没有指定Discord ID的用户'; + @override String get discordNotAssignedSub => '请确保您提供了有效的 ID'; + @override String get history => '缺少该玩家的历史'; + @override String get actionSuggestion => '也许,您想要'; + @override String get p1nkl0bst3rTLmatches => '没有找到Tetra联赛比赛'; + @override String get clientException => '你尚未连接'; + @override String get forbidden => '您的 IP 地址已被封禁'; + @override String forbiddenSub({required Object nickname}) => '如果你在使用VPN,请关闭。如果仍然不可以,请联系${nickname}'; + @override String get tooManyRequests => '您的评分已经被限制'; + @override String get tooManyRequestsSub => '请稍后重试'; + @override String get internal => 'TETR.IO 出现了问题!'; + @override String get internalSub => 'osk,或许,已经知道了'; + @override String get internalWebVersion => 'TETR.IO (也许是oskware_bridge,我不知道到底是哪儿) 出现了问题!'; + @override String get internalWebVersionSub => '如果 osk status 页面显示一切都很正常,请联系dan63047'; + @override String get oskwareBridge => 'oskware_bridge 出现了问题!'; + @override String get oskwareBridgeSub => '请联系dan63047'; + @override String get p1nkl0bst3rForbidden => '第三方API屏蔽了您的 IP 地址'; + @override String get p1nkl0bst3rTooManyRequests => '第三方API请求过多,请稍后再试'; + @override String get p1nkl0bst3rinternal => 'p1nkl0bst3r 那边出现了问题!'; + @override String get p1nkl0bst3rinternalWebVersion => 'p1nkl0bst3r 那边(也许是oskware_bridge,我不知道到底是哪儿)出现了问题!'; + @override String get replayAlreadySaved => '回放已保存'; + @override String get replayExpired => '回放已过期或不再可用'; + @override String get replayRejected => '第三方API屏蔽了您的 IP 地址'; +} + +// Path: actions +class _StringsActionsZhCn implements _StringsActionsEn { + _StringsActionsZhCn._(this._root); + + @override final _StringsZhCn _root; // ignore: unused_field + + // Translations + @override String get cancel => '取消'; + @override String get submit => '确定'; + @override String get ok => '确定'; + @override String get apply => '应用'; + @override String get refresh => '刷新'; +} + +// Path: graphsDestination +class _StringsGraphsDestinationZhCn implements _StringsGraphsDestinationEn { + _StringsGraphsDestinationZhCn._(this._root); + + @override final _StringsZhCn _root; // ignore: unused_field + + // Translations + @override String get fetchAndsaveTLHistory => '获取玩家历史'; + @override String get fetchAndSaveOldTLmatches => '获取 Tetra 联赛历史记录'; + @override String fetchAndsaveTLHistoryResult({required Object number}) => '找到 ${number} 个状态'; + @override String fetchAndSaveOldTLmatchesResult({required Object number}) => '找到 ${number} 场比赛'; + @override String gamesPlayed({required Object games}) => '游玩次数:${games}'; + @override String get dateAndTime => '日期和时间'; + @override String get filterModaleTitle => '在图表上筛选等级'; +} + +// Path: filterModale +class _StringsFilterModaleZhCn implements _StringsFilterModaleEn { + _StringsFilterModaleZhCn._(this._root); + + @override final _StringsZhCn _root; // ignore: unused_field + + // Translations + @override String get all => '全部'; +} + +// Path: cutoffsDestination +class _StringsCutoffsDestinationZhCn implements _StringsCutoffsDestinationEn { + _StringsCutoffsDestinationZhCn._(this._root); + + @override final _StringsZhCn _root; // ignore: unused_field + + // Translations + @override String get title => 'Tetra 联赛 状态'; + @override String relevance({required Object timestamp}) => '${timestamp}'; + @override String get actual => '实际'; + @override String get target => '目标'; + @override String get cutoffTR => '分段 TR'; + @override String get targetTR => '目标 TR'; + @override String get state => '状态'; + @override String get advanced => '高级选项'; + @override String players({required Object n}) => '玩家(${n})'; + @override String get moreInfo => '更多信息'; + @override String NumberOne({required Object tr}) => '№ 1 is ${tr} TR'; + @override String inflated({required Object tr}) => '高于目标 ${tr}'; + @override String get notInflated => '不偏高'; + @override String deflated({required Object tr}) => '低于目标 ${tr}'; + @override String get notDeflated => '不偏低'; + @override String get wellDotDotDot => '嗯…'; + @override String fromPlace({required Object n}) => '自 № ${n}'; + @override String get viewButton => '查看'; +} + +// Path: rankView +class _StringsRankViewZhCn implements _StringsRankViewEn { + _StringsRankViewZhCn._(this._root); + + @override final _StringsZhCn _root; // ignore: unused_field + + // Translations + @override String rankTitle({required Object rank}) => '${rank} 段数据'; + @override String get everyoneTitle => '全部排行榜'; + @override String get trRange => 'TR 范围'; + @override String get supposedToBe => '应为'; + @override String gap({required Object value}) => '相差 ${value}'; + @override String trGap({required Object value}) => '相差 ${value} TR'; + @override String get deflationGap => '偏低量'; + @override String get inflationGap => '偏高量'; + @override String get LBposRange => '排行榜位置范围'; + @override String overpopulated({required Object players}) => '比期望的多 ${players}'; + @override String underpopulated({required Object players}) => '比期望的少 ${players}'; + @override String get PlayersEqualSupposedToBe => '符合'; + @override String get avgStats => '平均数据'; + @override String avgForRank({required Object rank}) => '${rank} 段平均数据'; + @override String get avgNerdStats => '平均详细信息'; + @override String get minimums => '最小值'; + @override String get maximums => '最大值'; +} + +// Path: stateView +class _StringsStateViewZhCn implements _StringsStateViewEn { + _StringsStateViewZhCn._(this._root); + + @override final _StringsZhCn _root; // ignore: unused_field + + // Translations + @override String title({required Object date}) => '${date}的状态'; +} + +// Path: tlMatchView +class _StringsTlMatchViewZhCn implements _StringsTlMatchViewEn { + _StringsTlMatchViewZhCn._(this._root); + + @override final _StringsZhCn _root; // ignore: unused_field + + // Translations + @override String get match => '匹配'; + @override String get vs => 'vs'; + @override String get winner => '获胜者'; + @override String roundNumber({required Object n}) => '第${n}回合'; + @override String get statsFor => '状态'; + @override String get numberOfRounds => '回合数'; + @override String get matchLength => '比赛时长'; + @override String get roundLength => '回合时长'; + @override String get matchStats => '比赛数据'; + @override String get downloadReplay => '下载 .ttrm 回放'; + @override String get openReplay => '在 TETR.IO 中打开回放'; +} + +// Path: calcDestination +class _StringsCalcDestinationZhCn implements _StringsCalcDestinationEn { + _StringsCalcDestinationZhCn._(this._root); + + @override final _StringsZhCn _root; // ignore: unused_field + + // Translations + @override String placeholders({required Object stat}) => '输入你的${stat}'; + @override String get tip => '输入值并按 "计算" 来查看TA的详细信息'; + @override String get statsCalcButton => '计算'; + @override String get damageCalcTip => '点击左侧的操作在此添加'; + @override String get actions => '操作'; + @override String get results => '结果'; + @override String get rules => '规则'; + @override String get noSpinClears => '非 Spin 清除'; + @override String get spins => 'Spin'; + @override String get miniSpins => 'Mini spin'; + @override String get noLineclear => '无清除(连消结束)'; + @override String get custom => '自定义'; + @override String get multiplier => '倍增'; + @override String get pcDamage => '全消伤害'; + @override String get comboTable => '连击'; + @override String get b2bChaining => 'B2B增伤'; + @override String get surgeStartAtB2B => '开始于B2B'; + @override String get surgeStartAmount => '初始值'; + @override String get totalDamage => '累计伤害'; + @override String get lineclears => '清除行数'; + @override String get combo => '连击'; + @override String get surge => 'B2B充能'; + @override String get pcs => '全消'; +} + +// Path: infoDestination +class _StringsInfoDestinationZhCn implements _StringsInfoDestinationEn { + _StringsInfoDestinationZhCn._(this._root); + + @override final _StringsZhCn _root; // ignore: unused_field + + // Translations + @override String get title => '信息中心'; + @override String get sprintAndBlitzAverages => '40 行 & 闪电战平均数据'; + @override String get sprintAndBlitzAveragesDescription => '计算40 行 & 闪电战平均数据是个很繁琐的过程,所以很久才会更新一次。 点击标题查看完整的 40 行 & 闪电战平均数据表'; + @override String get tetraStatsWiki => 'Tetra Stats Wiki'; + @override String get tetraStatsWikiDescription => '查看更多关于Tetra Stats提供的功能和数据'; + @override String get about => '关于 Tetra Stats'; + @override String get aboutDescription => '由 dan63 开发'; +} + +// Path: leaderboardsDestination +class _StringsLeaderboardsDestinationZhCn implements _StringsLeaderboardsDestinationEn { + _StringsLeaderboardsDestinationZhCn._(this._root); + + @override final _StringsZhCn _root; // ignore: unused_field + + // Translations + @override String get title => '排行榜'; + @override String get tl => 'Tetra 联赛(当前赛季)'; + @override String get fullTL => 'Tetra 联赛(当前赛季,完整)'; + @override String get ar => '成就点'; +} + +// Path: savedDataDestination +class _StringsSavedDataDestinationZhCn implements _StringsSavedDataDestinationEn { + _StringsSavedDataDestinationZhCn._(this._root); + + @override final _StringsZhCn _root; // ignore: unused_field + + // Translations + @override String get title => '已保存的数据'; + @override String get tip => '选择左边的昵称以查看与之相关的数据'; + @override String seasonTLstates({required Object s}) => '第${s}赛季状态'; + @override String get TLrecords => '联赛记录'; +} + +// Path: settingsDestination +class _StringsSettingsDestinationZhCn implements _StringsSettingsDestinationEn { + _StringsSettingsDestinationZhCn._(this._root); + + @override final _StringsZhCn _root; // ignore: unused_field + + // Translations + @override String get title => '设置'; + @override String get general => '常规'; + @override String get customization => '自定义设置'; + @override String get database => '本地数据库'; + @override String get checking => '正在检查...'; + @override String get enterToSubmit => '按回车键提交'; + @override String get account => '您的 TETR.IO 账号'; + @override String get accountDescription => '该玩家的状态将在启动此应用后立即加载。 默认情况下,它会加载我的数据。如要更改,请在此输入您的昵称。'; + @override String get done => '完成!'; + @override String get noSuchAccount => '账号不存在'; + @override String get language => '语言'; + @override String languageDescription({required Object languages}) => 'Tetra Stats 有${languages}。默认情况下,应用程序将选择您的系统语言,如果您的系统区域设置不可用,则选择英语。'; + @override String languages({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('zh'))(n, + zero: '0种语言', + one: '${n}种语言', + two: '${n}种语言', + few: '${n}种语言', + many: '${n}种语言', + other: '${n}种语言', + ); + @override String get updateInTheBackground => '后台更新数据'; + @override String get updateInTheBackgroundDescription => '如果开启,Tetra Stats将尝试在缓存过期后查询新信息。通常一次/5分钟。'; + @override String get compareStats => '将TL数据与段位平均水平作比较'; + @override String get compareStatsDescription => '如果开启,Tetra Stats将提供额外的量度,使您能够将自己与普通玩家的等级相比较。 你看到它的方式——统计信息将以相应的颜色高亮,用光标悬停在它们上面以获取更多信息。'; + @override String get showPosition => '显示排行榜中的位置'; + @override String get showPositionDescription => '这可能需要一些时间(和流量),但您可以看到您在排行榜上的位置,按数据排序'; + @override String get accentColor => '主题色'; + @override String get accentColorDescription => '这种颜色会在这个应用上可见,而且通常会高亮显示交互界面元素。'; + @override String get accentColorModale => '选取主题色'; + @override String get timestamps => '时间戳格式'; + @override String timestampsDescriptionPart1({required Object d}) => '您可以选择时间戳显示时间的方式。默认情况下,它们以 GMT 时区显示时间,并根据所选区域设置进行格式设置,例如:${d}。'; + @override String timestampsDescriptionPart2({required Object y, required Object r}) => '这里还有:\n• 以您的时区设置的区域设置:${y}\n• 相对时间戳:${r}'; + @override String get timestampsAbsoluteGMT => 'GMT'; + @override String get timestampsAbsoluteLocalTime => '您的时区'; + @override String get timestampsRelative => '相对'; + @override String get sheetbotLikeGraphs => 'Sheetbot 型雷达图'; + @override String get sheetbotLikeGraphsDescription => '尽管我认为,图表在 SheetBot 中的工作方式不是很正确,有些人感到困惑,那 -0.5 Stride 看起来不像它在 SheetBot 图表上那样。因此,我们这里有:如果开启,则如果数值为负,则图形上的点可以出现在图形的另一半。'; + @override String get oskKagariGimmick => 'Osk-Kagari'; + @override String get oskKagariGimmickDescription => '如果开启,osk的段位会显示为:kagari:'; + @override String get bytesOfDataStored => '存储数据'; + @override String get TLrecordsSaved => '已保存 Tetra 联赛记录'; + @override String get TLplayerstatesSaved => '已保存 Tetra 联赛玩家状态'; + @override String get fixButton => '修复'; + @override String get compressButton => '压缩'; + @override String get exportDB => '导出本地数据库'; + @override String get desktopExportAlertTitle => '桌面导出'; + @override String get desktopExportText => '看起来您在桌面上使用了这个应用程序。请检查您的文档文件夹,您应该找到"TetraStats.db"。请将其复制到某处'; + @override String get androidExportAlertTitle => 'Android 导出'; + @override String androidExportText({required Object exportedDB}) => '已导出。\n${exportedDB}'; + @override String get importDB => '导入本地数据库'; + @override String get importDBDescription => '还原您的备份。请注意已存储的数据库将被覆盖。'; + @override String get importWrongFileType => '文件类型错误!'; +} + +// Path: homeNavigation +class _StringsHomeNavigationZhCn implements _StringsHomeNavigationEn { + _StringsHomeNavigationZhCn._(this._root); + + @override final _StringsZhCn _root; // ignore: unused_field + + // Translations + @override String get overview => '概览'; + @override String get standing => '名次'; + @override String get seasons => '赛季'; + @override String get mathces => '比赛场次'; + @override String get pb => '个人最佳'; + @override String get normal => '普通模式'; + @override String get expert => '专家模式'; + @override String get expertRecords => '专家模式记录'; +} + +// Path: graphsNavigation +class _StringsGraphsNavigationZhCn implements _StringsGraphsNavigationEn { + _StringsGraphsNavigationZhCn._(this._root); + + @override final _StringsZhCn _root; // ignore: unused_field + + // Translations + @override String get history => '玩家历史记录'; + @override String get league => '联赛状态'; + @override String get cutoffs => '分段线历史'; +} + +// Path: calcNavigation +class _StringsCalcNavigationZhCn implements _StringsCalcNavigationEn { + _StringsCalcNavigationZhCn._(this._root); + + @override final _StringsZhCn _root; // ignore: unused_field + + // Translations + @override String get stats => '数据计算器'; + @override String get damage => '伤害计算器'; +} + +// Path: firstTimeView +class _StringsFirstTimeViewZhCn implements _StringsFirstTimeViewEn { + _StringsFirstTimeViewZhCn._(this._root); + + @override final _StringsZhCn _root; // ignore: unused_field + + // Translations + @override String get welcome => '欢迎使用 Tetra Stats'; + @override String get description => '服务,允许您跟踪TETR.IO的各种数据'; + @override String get nicknameQuestion => '您的昵称是?'; + @override String get inpuntHint => '在此处输入... (3-16个符号)'; + @override String get emptyInputError => '不能提交空字符串'; + @override String niceToSeeYou({required Object n}) => '很高兴见到你,${n}'; + @override String get letsTakeALook => '让我们看看您的统计资料...'; + @override String get skip => '跳过'; +} + +// Path: aboutView +class _StringsAboutViewZhCn implements _StringsAboutViewEn { + _StringsAboutViewZhCn._(this._root); + + @override final _StringsZhCn _root; // ignore: unused_field + + // Translations + @override String get title => '关于 Tetra Stats'; + @override String get about => 'Tetra Stats是一种服务,与TETR.IO Tetra Channel API共用,提供数据并根据这种数据计算一些附加度量。 服务允许用户用"Track"功能跟踪他们在Tetra League中的进度,这个功能记录每个Tetra Leage更改到本地数据库(非自动) 您必须不时地访问服务。这样,这些更改可以通过图表来查看。\n\nBeanserver blaster 是Tetra Stats的一部分,它被拆解成服务器侧脚本。 它提供完整的Tetra League排行榜,允许Tetra Stats通过任何公式对排行榜进行排序并生成散点图,这允许用户分析Tetra联赛趋势。 它还提供了Tetra League 的评分历史,用户也可以通过图表看到。\n\n我们有一个添加回放分析和锦标赛历史记录的计划,所以随时关注!\n\n服务没有与TETR.IO与osk以任何身份关联。'; + @override String get appVersion => '版本'; + @override String build({required Object build}) => '${build}'; + @override String get GHrepo => 'GitHub Repository'; + @override String get submitAnIssue => '提交问题'; + @override String get credits => '鸣谢'; + @override String get authorAndDeveloper => '作者 & 开发者'; + @override String get providedFormulas => '提供的公式'; + @override String get providedS1history => '提供的 S1 历史'; + @override String get inoue => 'Inoue (回放抓取器)'; + @override String get zhCNlocale => '简中翻译员'; + @override String get supportHim => '为他提供支持!'; +} + +// Path: stats +class _StringsStatsZhCn implements _StringsStatsEn { + _StringsStatsZhCn._(this._root); + + @override final _StringsZhCn _root; // ignore: unused_field + + // Translations + @override String get registrationDate => '注册时间'; + @override String get gametime => '游玩时长'; + @override String get ogp => '在线游戏次数'; + @override String get ogw => '在线游戏胜利次数'; + @override String get followers => '粉丝'; + @override late final _StringsStatsXpZhCn xp = _StringsStatsXpZhCn._(_root); + @override late final _StringsStatsTrZhCn tr = _StringsStatsTrZhCn._(_root); + @override late final _StringsStatsGlickoZhCn glicko = _StringsStatsGlickoZhCn._(_root); + @override late final _StringsStatsRdZhCn rd = _StringsStatsRdZhCn._(_root); + @override late final _StringsStatsGlixareZhCn glixare = _StringsStatsGlixareZhCn._(_root); + @override late final _StringsStatsS1trZhCn s1tr = _StringsStatsS1trZhCn._(_root); + @override late final _StringsStatsGpZhCn gp = _StringsStatsGpZhCn._(_root); + @override late final _StringsStatsGwZhCn gw = _StringsStatsGwZhCn._(_root); + @override late final _StringsStatsWinrateZhCn winrate = _StringsStatsWinrateZhCn._(_root); + @override late final _StringsStatsApmZhCn apm = _StringsStatsApmZhCn._(_root); + @override late final _StringsStatsPpsZhCn pps = _StringsStatsPpsZhCn._(_root); + @override late final _StringsStatsVsZhCn vs = _StringsStatsVsZhCn._(_root); + @override late final _StringsStatsAppZhCn app = _StringsStatsAppZhCn._(_root); + @override late final _StringsStatsVsapmZhCn vsapm = _StringsStatsVsapmZhCn._(_root); + @override late final _StringsStatsDssZhCn dss = _StringsStatsDssZhCn._(_root); + @override late final _StringsStatsDspZhCn dsp = _StringsStatsDspZhCn._(_root); + @override late final _StringsStatsAppdspZhCn appdsp = _StringsStatsAppdspZhCn._(_root); + @override late final _StringsStatsCheeseZhCn cheese = _StringsStatsCheeseZhCn._(_root); + @override late final _StringsStatsGbeZhCn gbe = _StringsStatsGbeZhCn._(_root); + @override late final _StringsStatsNyaappZhCn nyaapp = _StringsStatsNyaappZhCn._(_root); + @override late final _StringsStatsAreaZhCn area = _StringsStatsAreaZhCn._(_root); + @override late final _StringsStatsEtrZhCn etr = _StringsStatsEtrZhCn._(_root); + @override late final _StringsStatsEtraccZhCn etracc = _StringsStatsEtraccZhCn._(_root); + @override late final _StringsStatsOpenerZhCn opener = _StringsStatsOpenerZhCn._(_root); + @override late final _StringsStatsPlonkZhCn plonk = _StringsStatsPlonkZhCn._(_root); + @override late final _StringsStatsStrideZhCn stride = _StringsStatsStrideZhCn._(_root); + @override late final _StringsStatsInfdsZhCn infds = _StringsStatsInfdsZhCn._(_root); + @override late final _StringsStatsAltitudeZhCn altitude = _StringsStatsAltitudeZhCn._(_root); + @override late final _StringsStatsClimbSpeedZhCn climbSpeed = _StringsStatsClimbSpeedZhCn._(_root); + @override late final _StringsStatsPeakClimbSpeedZhCn peakClimbSpeed = _StringsStatsPeakClimbSpeedZhCn._(_root); + @override late final _StringsStatsKosZhCn kos = _StringsStatsKosZhCn._(_root); + @override late final _StringsStatsB2bZhCn b2b = _StringsStatsB2bZhCn._(_root); + @override late final _StringsStatsFinesseZhCn finesse = _StringsStatsFinesseZhCn._(_root); + @override late final _StringsStatsFinesseFaultsZhCn finesseFaults = _StringsStatsFinesseFaultsZhCn._(_root); + @override late final _StringsStatsTotalTimeZhCn totalTime = _StringsStatsTotalTimeZhCn._(_root); + @override late final _StringsStatsLevelZhCn level = _StringsStatsLevelZhCn._(_root); + @override late final _StringsStatsPiecesZhCn pieces = _StringsStatsPiecesZhCn._(_root); + @override late final _StringsStatsSppZhCn spp = _StringsStatsSppZhCn._(_root); + @override late final _StringsStatsKpZhCn kp = _StringsStatsKpZhCn._(_root); + @override late final _StringsStatsKppZhCn kpp = _StringsStatsKppZhCn._(_root); + @override late final _StringsStatsKpsZhCn kps = _StringsStatsKpsZhCn._(_root); + @override String blitzScore({required Object p}) => '${p} 分'; + @override String levelUpRequirement({required Object p}) => '还需 ${p} 升到下一级'; + @override String get piecesTotal => '放块总数'; + @override String get piecesWithPerfectFinesse => '极简块数'; + @override String get score => '分数'; + @override String get lines => '行数'; + @override String get linesShort => '行'; + @override String get pcs => '全消数'; + @override String get holds => '暂存数'; + @override String get spike => '最高暴击'; + @override String top({required Object percentage}) => '前 ${percentage}'; + @override String topRank({required Object rank}) => '最高段位:${rank}'; + @override String get floor => '层'; + @override String get split => '拆分'; + @override String get total => '总计'; + @override String get sent => '已发送'; + @override String get received => '已接收'; + @override String get placement => '排名'; + @override String get peak => '最高'; + @override String qpWithMods({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('zh'))(n, + one: '使用 1 个模组', + two: '使用 ${n} 个模组', + few: '使用 ${n} 个模组', + many: '使用 ${n} 个模组', + other: '使用 ${n} 个模组', + ); + @override String inputs({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('zh'))(n, + zero: '${n} 按键', + one: '${n} 按键', + two: '${n} 按键', + few: '${n} 按键', + many: '${n} 按键', + other: '${n} 按键', + ); + @override String tspinsTotal({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('zh'))(n, + zero: '总共 ${n} 次T旋', + one: '总共 ${n} 次T旋', + two: '总共 ${n} 次T旋', + few: '总共 ${n} 次T旋', + many: '总共 ${n} 次T旋', + other: '总共 ${n} 次T旋', + ); + @override String linesCleared({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('zh'))(n, + zero: '总共消除 ${n} 行', + one: '总共消除 ${n} 行', + two: '总共消除 ${n} 行', + few: '总共消除 ${n} 行', + many: '总共消除 ${n} 行', + other: '总共消除 ${n} 行', + ); + @override late final _StringsStatsGraphsZhCn graphs = _StringsStatsGraphsZhCn._(_root); + @override String players({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('zh'))(n, + zero: '${n} 名玩家', + one: '${n} 名玩家', + two: '${n} 名玩家', + few: '${n} 名玩家', + many: '${n} 名玩家', + other: '${n} 名玩家', + ); + @override String games({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('zh'))(n, + zero: '${n} 次游戏', + one: '${n} 次游戏', + two: '${n} 次游戏', + few: '${n} 次游戏', + many: '${n} 次游戏', + other: '${n} 次游戏', + ); + @override late final _StringsStatsLineClearZhCn lineClear = _StringsStatsLineClearZhCn._(_root); + @override late final _StringsStatsLineClearsZhCn lineClears = _StringsStatsLineClearsZhCn._(_root); + @override String get mini => 'Mini'; + @override String get tSpin => 'T-spin'; + @override String get tSpins => 'T-spins'; + @override String get spin => 'Spin'; + @override String get spins => 'Spins'; +} + +// Path: stats.xp +class _StringsStatsXpZhCn implements _StringsStatsXpEn { + _StringsStatsXpZhCn._(this._root); + + @override final _StringsZhCn _root; // ignore: unused_field + + // Translations + @override String get short => '经验值'; + @override String get full => '经验点'; +} + +// Path: stats.tr +class _StringsStatsTrZhCn implements _StringsStatsTrEn { + _StringsStatsTrZhCn._(this._root); + + @override final _StringsZhCn _root; // ignore: unused_field + + // Translations + @override String get short => 'TR'; + @override String get full => 'Tetra 评分'; +} + +// Path: stats.glicko +class _StringsStatsGlickoZhCn implements _StringsStatsGlickoEn { + _StringsStatsGlickoZhCn._(this._root); + + @override final _StringsZhCn _root; // ignore: unused_field + + // Translations + @override String get short => 'Glicko'; + @override String get full => 'Glicko'; +} + +// Path: stats.rd +class _StringsStatsRdZhCn implements _StringsStatsRdEn { + _StringsStatsRdZhCn._(this._root); + + @override final _StringsZhCn _root; // ignore: unused_field + + // Translations + @override String get short => 'RD'; + @override String get full => '评分偏差'; +} + +// Path: stats.glixare +class _StringsStatsGlixareZhCn implements _StringsStatsGlixareEn { + _StringsStatsGlixareZhCn._(this._root); + + @override final _StringsZhCn _root; // ignore: unused_field + + // Translations + @override String get short => 'GXE'; + @override String get full => 'GLIXARE'; +} + +// Path: stats.s1tr +class _StringsStatsS1trZhCn implements _StringsStatsS1trEn { + _StringsStatsS1trZhCn._(this._root); + + @override final _StringsZhCn _root; // ignore: unused_field + + // Translations + @override String get short => 'S1 TR'; + @override String get full => '第 1 赛季式 TR'; +} + +// Path: stats.gp +class _StringsStatsGpZhCn implements _StringsStatsGpEn { + _StringsStatsGpZhCn._(this._root); + + @override final _StringsZhCn _root; // ignore: unused_field + + // Translations + @override String get short => 'GP'; + @override String get full => '总场数'; +} + +// Path: stats.gw +class _StringsStatsGwZhCn implements _StringsStatsGwEn { + _StringsStatsGwZhCn._(this._root); + + @override final _StringsZhCn _root; // ignore: unused_field + + // Translations + @override String get short => 'GW'; + @override String get full => '胜场数'; +} + +// Path: stats.winrate +class _StringsStatsWinrateZhCn implements _StringsStatsWinrateEn { + _StringsStatsWinrateZhCn._(this._root); + + @override final _StringsZhCn _root; // ignore: unused_field + + // Translations + @override String get short => 'WR%'; + @override String get full => '胜率'; +} + +// Path: stats.apm +class _StringsStatsApmZhCn implements _StringsStatsApmEn { + _StringsStatsApmZhCn._(this._root); + + @override final _StringsZhCn _root; // ignore: unused_field + + // Translations + @override String get short => 'APM'; + @override String get full => '每分钟攻击数'; +} + +// Path: stats.pps +class _StringsStatsPpsZhCn implements _StringsStatsPpsEn { + _StringsStatsPpsZhCn._(this._root); + + @override final _StringsZhCn _root; // ignore: unused_field + + // Translations + @override String get short => 'PPS'; + @override String get full => '每秒块数'; +} + +// Path: stats.vs +class _StringsStatsVsZhCn implements _StringsStatsVsEn { + _StringsStatsVsZhCn._(this._root); + + @override final _StringsZhCn _root; // ignore: unused_field + + // Translations + @override String get short => 'VS'; + @override String get full => 'VS 分数'; +} + +// Path: stats.app +class _StringsStatsAppZhCn implements _StringsStatsAppEn { + _StringsStatsAppZhCn._(this._root); + + @override final _StringsZhCn _root; // ignore: unused_field + + // Translations + @override String get short => 'APP'; + @override String get full => '每块攻击数'; +} + +// Path: stats.vsapm +class _StringsStatsVsapmZhCn implements _StringsStatsVsapmEn { + _StringsStatsVsapmZhCn._(this._root); + + @override final _StringsZhCn _root; // ignore: unused_field + + // Translations + @override String get short => 'VS/APM'; + @override String get full => 'VS / APM'; +} + +// Path: stats.dss +class _StringsStatsDssZhCn implements _StringsStatsDssEn { + _StringsStatsDssZhCn._(this._root); + + @override final _StringsZhCn _root; // ignore: unused_field + + // Translations + @override String get short => 'DS/S'; + @override String get full => '每秒挖掘数'; +} + +// Path: stats.dsp +class _StringsStatsDspZhCn implements _StringsStatsDspEn { + _StringsStatsDspZhCn._(this._root); + + @override final _StringsZhCn _root; // ignore: unused_field + + // Translations + @override String get short => 'DS/P'; + @override String get full => '每块挖掘数'; +} + +// Path: stats.appdsp +class _StringsStatsAppdspZhCn implements _StringsStatsAppdspEn { + _StringsStatsAppdspZhCn._(this._root); + + @override final _StringsZhCn _root; // ignore: unused_field + + // Translations + @override String get short => 'APP+DSP'; + @override String get full => 'APP + DSP'; +} + +// Path: stats.cheese +class _StringsStatsCheeseZhCn implements _StringsStatsCheeseEn { + _StringsStatsCheeseZhCn._(this._root); + + @override final _StringsZhCn _root; // ignore: unused_field + + // Translations + @override String get short => 'CI'; + @override String get full => '垃圾行混乱指数'; +} + +// Path: stats.gbe +class _StringsStatsGbeZhCn implements _StringsStatsGbeEn { + _StringsStatsGbeZhCn._(this._root); + + @override final _StringsZhCn _root; // ignore: unused_field + + // Translations + @override String get short => 'GbE'; + @override String get full => '垃圾行效率'; +} + +// Path: stats.nyaapp +class _StringsStatsNyaappZhCn implements _StringsStatsNyaappEn { + _StringsStatsNyaappZhCn._(this._root); + + @override final _StringsZhCn _root; // ignore: unused_field + + // Translations + @override String get short => 'wAPP'; + @override String get full => '加权APP'; +} + +// Path: stats.area +class _StringsStatsAreaZhCn implements _StringsStatsAreaEn { + _StringsStatsAreaZhCn._(this._root); + + @override final _StringsZhCn _root; // ignore: unused_field + + // Translations + @override String get short => '面积'; + @override String get full => '面积'; +} + +// Path: stats.etr +class _StringsStatsEtrZhCn implements _StringsStatsEtrEn { + _StringsStatsEtrZhCn._(this._root); + + @override final _StringsZhCn _root; // ignore: unused_field + + // Translations + @override String get short => 'eTR'; + @override String get full => '预测 TR'; +} + +// Path: stats.etracc +class _StringsStatsEtraccZhCn implements _StringsStatsEtraccEn { + _StringsStatsEtraccZhCn._(this._root); + + @override final _StringsZhCn _root; // ignore: unused_field + + // Translations + @override String get short => '±eTR'; + @override String get full => '预测实际差量'; +} + +// Path: stats.opener +class _StringsStatsOpenerZhCn implements _StringsStatsOpenerEn { + _StringsStatsOpenerZhCn._(this._root); + + @override final _StringsZhCn _root; // ignore: unused_field + + // Translations + @override String get short => '定式'; + @override String get full => '定式'; +} + +// Path: stats.plonk +class _StringsStatsPlonkZhCn implements _StringsStatsPlonkEn { + _StringsStatsPlonkZhCn._(this._root); + + @override final _StringsZhCn _root; // ignore: unused_field + + // Translations + @override String get short => '太极'; + @override String get full => '太极'; +} + +// Path: stats.stride +class _StringsStatsStrideZhCn implements _StringsStatsStrideEn { + _StringsStatsStrideZhCn._(this._root); + + @override final _StringsZhCn _root; // ignore: unused_field + + // Translations + @override String get short => '速度'; + @override String get full => '速度'; +} + +// Path: stats.infds +class _StringsStatsInfdsZhCn implements _StringsStatsInfdsEn { + _StringsStatsInfdsZhCn._(this._root); + + @override final _StringsZhCn _root; // ignore: unused_field + + // Translations + @override String get short => '挖掘'; + @override String get full => '挖掘'; +} + +// Path: stats.altitude +class _StringsStatsAltitudeZhCn implements _StringsStatsAltitudeEn { + _StringsStatsAltitudeZhCn._(this._root); + + @override final _StringsZhCn _root; // ignore: unused_field + + // Translations + @override String get short => 'm'; + @override String get full => '高度'; +} + +// Path: stats.climbSpeed +class _StringsStatsClimbSpeedZhCn implements _StringsStatsClimbSpeedEn { + _StringsStatsClimbSpeedZhCn._(this._root); + + @override final _StringsZhCn _root; // ignore: unused_field + + // Translations + @override String get short => 'CSP'; + @override String get full => '爬行速度'; + @override String get gaugetTitle => '爬行速度'; +} + +// Path: stats.peakClimbSpeed +class _StringsStatsPeakClimbSpeedZhCn implements _StringsStatsPeakClimbSpeedEn { + _StringsStatsPeakClimbSpeedZhCn._(this._root); + + @override final _StringsZhCn _root; // ignore: unused_field + + // Translations + @override String get short => '最高CSP'; + @override String get full => '最高爬行速度'; + @override String get gaugetTitle => '最高'; +} + +// Path: stats.kos +class _StringsStatsKosZhCn implements _StringsStatsKosEn { + _StringsStatsKosZhCn._(this._root); + + @override final _StringsZhCn _root; // ignore: unused_field + + // Translations + @override String get short => 'KO\'s'; + @override String get full => '击杀'; +} + +// Path: stats.b2b +class _StringsStatsB2bZhCn implements _StringsStatsB2bEn { + _StringsStatsB2bZhCn._(this._root); + + @override final _StringsZhCn _root; // ignore: unused_field + + // Translations + @override String get short => 'B2B'; + @override String get full => '背靠背/满贯'; +} + +// Path: stats.finesse +class _StringsStatsFinesseZhCn implements _StringsStatsFinesseEn { + _StringsStatsFinesseZhCn._(this._root); + + @override final _StringsZhCn _root; // ignore: unused_field + + // Translations + @override String get short => '极'; + @override String get full => '极简率'; + @override String get widgetTitle => '简率'; +} + +// Path: stats.finesseFaults +class _StringsStatsFinesseFaultsZhCn implements _StringsStatsFinesseFaultsEn { + _StringsStatsFinesseFaultsZhCn._(this._root); + + @override final _StringsZhCn _root; // ignore: unused_field + + // Translations + @override String get short => '非极简'; + @override String get full => '非极简操作数'; +} + +// Path: stats.totalTime +class _StringsStatsTotalTimeZhCn implements _StringsStatsTotalTimeEn { + _StringsStatsTotalTimeZhCn._(this._root); + + @override final _StringsZhCn _root; // ignore: unused_field + + // Translations + @override String get short => '时长'; + @override String get full => '总时长'; + @override String get widgetTitle => '总时长'; +} + +// Path: stats.level +class _StringsStatsLevelZhCn implements _StringsStatsLevelEn { + _StringsStatsLevelZhCn._(this._root); + + @override final _StringsZhCn _root; // ignore: unused_field + + // Translations + @override String get short => 'Lvl'; + @override String get full => '等级'; +} + +// Path: stats.pieces +class _StringsStatsPiecesZhCn implements _StringsStatsPiecesEn { + _StringsStatsPiecesZhCn._(this._root); + + @override final _StringsZhCn _root; // ignore: unused_field + + // Translations + @override String get short => 'P'; + @override String get full => '块'; +} + +// Path: stats.spp +class _StringsStatsSppZhCn implements _StringsStatsSppEn { + _StringsStatsSppZhCn._(this._root); + + @override final _StringsZhCn _root; // ignore: unused_field + + // Translations + @override String get short => 'SPP'; + @override String get full => '每块得分'; +} + +// Path: stats.kp +class _StringsStatsKpZhCn implements _StringsStatsKpEn { + _StringsStatsKpZhCn._(this._root); + + @override final _StringsZhCn _root; // ignore: unused_field + + // Translations + @override String get short => 'KP'; + @override String get full => '按键'; +} + +// Path: stats.kpp +class _StringsStatsKppZhCn implements _StringsStatsKppEn { + _StringsStatsKppZhCn._(this._root); + + @override final _StringsZhCn _root; // ignore: unused_field + + // Translations + @override String get short => 'KPP'; + @override String get full => '每块按键数'; +} + +// Path: stats.kps +class _StringsStatsKpsZhCn implements _StringsStatsKpsEn { + _StringsStatsKpsZhCn._(this._root); + + @override final _StringsZhCn _root; // ignore: unused_field + + // Translations + @override String get short => 'KPS'; + @override String get full => '每秒按键数'; +} + +// Path: stats.graphs +class _StringsStatsGraphsZhCn implements _StringsStatsGraphsEn { + _StringsStatsGraphsZhCn._(this._root); + + @override final _StringsZhCn _root; // ignore: unused_field + + // Translations + @override String get attack => '攻击'; + @override String get speed => '速度'; + @override String get defense => '防御'; + @override String get cheese => '奶酪层'; +} + +// Path: stats.lineClear +class _StringsStatsLineClearZhCn implements _StringsStatsLineClearEn { + _StringsStatsLineClearZhCn._(this._root); + + @override final _StringsZhCn _root; // ignore: unused_field + + // Translations + @override String get single => 'Single'; + @override String get double => 'Double'; + @override String get triple => 'Triple'; + @override String get quad => 'Quad'; + @override String get penta => 'Penta'; + @override String get hexa => 'Hexa'; + @override String get hepta => 'Hepta'; + @override String get octa => 'Octa'; + @override String get ennea => 'Ennea'; + @override String get deca => 'Deca'; + @override String get hendeca => 'Hendeca'; + @override String get dodeca => 'Dodeca'; + @override String get triadeca => 'Triadeca'; + @override String get tessaradeca => 'Tessaradeca'; + @override String get pentedeca => 'Pentedeca'; + @override String get hexadeca => 'Hexadeca'; + @override String get heptadeca => 'Heptadeca'; + @override String get octadeca => 'Octadeca'; + @override String get enneadeca => 'Enneadeca'; + @override String get eicosa => 'Eicosa'; + @override String get kagaris => 'Kagaris'; +} + +// Path: stats.lineClears +class _StringsStatsLineClearsZhCn implements _StringsStatsLineClearsEn { + _StringsStatsLineClearsZhCn._(this._root); + + @override final _StringsZhCn _root; // ignore: unused_field + + // Translations + @override String get zero => 'Zeros'; + @override String get single => 'Singles'; + @override String get double => 'Doubles'; + @override String get triple => 'Triples'; + @override String get quad => 'Quads'; + @override String get penta => 'Pentas'; +} + /// Flat map(s) containing all translations. /// Only for edge cases! For simple maps, use the map function of this library. @@ -3562,7 +5102,7 @@ extension on Translations { case 'aboutView.providedFormulas': return 'Provided formulas'; case 'aboutView.providedS1history': return 'Provided S1 history'; case 'aboutView.inoue': return 'Inoue (replay grabber)'; - case 'aboutView.zhCNlocale': return 'Simplfied Chinise locale'; + case 'aboutView.zhCNlocale': return 'Simplfied Chinese locale'; case 'aboutView.supportHim': return 'Support him!'; case 'stats.registrationDate': return 'Registration Date'; case 'stats.gametime': return 'Time Played'; @@ -4211,7 +5751,7 @@ extension on _StringsRuRu { case 'cutoffsDestination.relevance': return ({required Object timestamp}) => 'на момент ${timestamp}'; case 'cutoffsDestination.actual': return 'Требование'; case 'cutoffsDestination.target': return 'Цель'; - case 'cutoffsDestination.cutoffTR': return 'Требуемый TR'; + case 'cutoffsDestination.cutoffTR': return 'Треб. TR'; case 'cutoffsDestination.targetTR': return 'Целевой TR'; case 'cutoffsDestination.state': return 'Состояние'; case 'cutoffsDestination.advanced': return 'Продвинутая'; @@ -4834,3 +6374,816 @@ extension on _StringsRuRu { } } } + +extension on _StringsZhCn { + dynamic _flatMapFunction(String path) { + switch (path) { + case 'locales.en': return '英语 (English)'; + case 'locales.ru-RU': return '俄语 (Русский)'; + case 'locales.zh-CN': return '简体中文'; + case 'gamemodes.league': return 'Tetra 联赛'; + case 'gamemodes.zenith': return '快速游戏'; + case 'gamemodes.zenithex': return '快速游戏 · 专家模式'; + case 'gamemodes.40l': return '40行竞速'; + case 'gamemodes.blitz': return '闪电战'; + case 'gamemodes.5mblast': return '5,000,000 Blast'; + case 'gamemodes.zen': return '禅意模式'; + case 'destinations.home': return '主页'; + case 'destinations.graphs': return '图表'; + case 'destinations.leaderboards': return '排行榜'; + case 'destinations.cutoffs': return '段位分界线'; + case 'destinations.calc': return '计算器'; + case 'destinations.info': return '信息中心'; + case 'destinations.data': return '已保存的数据'; + case 'destinations.settings': return '设置'; + case 'playerRole.user': return '用户'; + case 'playerRole.banned': return '已封禁'; + case 'playerRole.bot': return '机器人'; + case 'playerRole.sysop': return '系统操作员'; + case 'playerRole.admin': return '管理员'; + case 'playerRole.mod': return '管理员'; + case 'playerRole.halfmod': return '社区管理员'; + case 'playerRole.anon': return '匿名用户'; + case 'goBackButton': return '返回'; + case 'nanow': return '目前不可用...'; + case 'seasonEnds': return ({required Object countdown}) => '当前赛季还有${countdown}结束'; + case 'seasonEnded': return '赛季已结束'; + case 'overallPB': return ({required Object pb}) => '生涯最佳:${pb} m'; + case 'gamesUntilRanked': return ({required Object left}) => '还有${left}局才可获得段位'; + case 'numOfVictories': return ({required Object wins}) => '~${wins}次胜局'; + case 'promotionOnNextWin': return '下一场胜局即可升段'; + case 'numOfdefeats': return ({required Object losses}) => '~${losses}次负局'; + case 'demotionOnNextLoss': return '下一场负局即可掉段'; + case 'records': return '记录'; + case 'nerdStats': return '详细信息'; + case 'playstyles': return '游戏方式'; + case 'horoscopes': return '散点图'; + case 'relatedAchievements': return '相关成就'; + case 'season': return '赛季'; + case 'smooth': return '平滑'; + case 'dateAndTime': return '日期和时间:'; + case 'TLfullLBnote': return '很大,但允许你通过玩家的数据对玩家进行排序,还可以按段位筛选玩家'; + case 'rank': return '段位'; + case 'verdictGeneral': return ({required Object rank, required Object n, required Object verdict}) => '比 ${rank} 段平均数据${n} ${verdict}'; + case 'verdictBetter': return '好'; + case 'verdictWorse': return '差'; + case 'localStanding': return '本地'; + case 'xp.title': return '经验等级'; + case 'xp.progressToNextLevel': return ({required Object percentage}) => '到下一等级的进度:${percentage}'; + case 'xp.progressTowardsGoal': return ({required Object goal, required Object percentage, required Object left}) => '从0级到${goal}级的进度:${percentage} (还差 ${left} 点经验值)'; + case 'gametime.title': return '精确游戏时长'; + case 'gametime.gametimeAday': return ({required Object gametime}) => '平均每天${gametime}'; + case 'gametime.breakdown': return ({required Object years, required Object months, required Object days, required Object minutes, required Object seconds}) => '相当于 ${years} 年,\n${months} 月,\n${days} 天,\n${minutes} 分钟,\n${seconds} 秒'; + case 'track': return '跟踪'; + case 'stopTracking': return '停止跟踪'; + case 'supporter': return ({required Object tier}) => '${tier}级会员'; + case 'comparingWith': return ({required Object newDate, required Object oldDate}) => '${newDate}的数据与${oldDate}相比'; + case 'compare': return '比较'; + case 'comparison': return '比较'; + case 'enterUsername': return '输入用户名或者\$avgX (X是一个段位)'; + case 'general': return '常规'; + case 'badges': return '勋章'; + case 'obtainDate': return ({required Object date}) => '于${date}获得'; + case 'assignedManualy': return '此徽章由TETR.IO管理员手动颁发'; + case 'distinguishment': return '区别'; + case 'banned': return '已封禁'; + case 'bannedSubtext': return '由于 TETR.IO 规则或服务条款被违反 而被封禁'; + case 'badStanding': return '信誉不佳'; + case 'badStandingSubtext': return '近期有一次或多次违禁行为'; + case 'botAccount': return '机器人账号'; + case 'botAccountSubtext': return ({required Object botMaintainers}) => '由${botMaintainers}管理'; + case 'copiedToClipboard': return '已复制到剪贴板!'; + case 'bio': return '个性签名'; + case 'news': return '新闻'; + case 'matchResult.victory': return '胜利'; + case 'matchResult.defeat': return '失败'; + case 'matchResult.tie': return '平局'; + case 'matchResult.dqvictory': return '对手被取消资格'; + case 'matchResult.dqdefeat': return '被取消资格'; + case 'matchResult.nocontest': return '无竞赛记录'; + case 'matchResult.nullified': return '竞赛记录已取消'; + case 'distinguishments.noHeader': return '缺少标题'; + case 'distinguishments.noFooter': return '缺少标题'; + case 'distinguishments.twc': return 'TETR.IO 世界冠军'; + case 'distinguishments.twcYear': return ({required Object year}) => '${year} TETR.IO 世界杯'; + case 'newsEntries.leaderboard': return ({required InlineSpan gametype, required InlineSpan rank}) => TextSpan(children: [ + const TextSpan(text: '在'), + gametype, + const TextSpan(text: '中荣获第'), + rank, + const TextSpan(text: '名'), + ]); + case 'newsEntries.personalbest': return ({required InlineSpan gametype, required InlineSpan pb}) => TextSpan(children: [ + const TextSpan(text: '在'), + gametype, + const TextSpan(text: '中取得新纪录:'), + pb, + ]); + case 'newsEntries.badge': return ({required InlineSpan badge}) => TextSpan(children: [ + const TextSpan(text: '获得勋章 '), + badge, + ]); + case 'newsEntries.rankup': return ({required InlineSpan rank}) => TextSpan(children: [ + const TextSpan(text: '升 '), + rank, + ]); + case 'newsEntries.supporter': return ({required InlineSpanBuilder s}) => TextSpan(children: [ + const TextSpan(text: '成为'), + s('TETR.IO supporter'), + ]); + case 'newsEntries.supporter_gift': return ({required InlineSpanBuilder s}) => TextSpan(children: [ + const TextSpan(text: '被赠送'), + s('TETR.IO supporter'), + ]); + case 'newsEntries.unknown': return ({required InlineSpan type}) => TextSpan(children: [ + const TextSpan(text: '未知新闻类型 '), + type, + ]); + case 'rankupMiddle': return ({required Object r}) => '${r} 段'; + case 'copyUserID': return '点击以复制用户 ID'; + case 'searchHint': return '用户名或 ID'; + case 'navMenu': return '导航菜单'; + case 'navMenuTooltip': return '打开导航菜单'; + case 'refresh': return '刷新数据'; + case 'searchButton': return '搜索'; + case 'trackedPlayers': return '跟踪的玩家'; + case 'standing': return '名次'; + case 'previousSeasons': return '上赛季'; + case 'recent': return '最近'; + case 'top': return '前'; + case 'noRecord': return '暂无记录'; + case 'sprintAndBlitsRelevance': return ({required Object date}) => '${date}'; + case 'snackBarMessages.stateRemoved': return ({required Object date}) => '成功移除${date}时的状态!'; + case 'snackBarMessages.matchRemoved': return ({required Object date}) => '成功移除${date}时的一局!'; + case 'snackBarMessages.notForWeb': return '此功能在网络版本中不可用'; + case 'snackBarMessages.importSuccess': return '导入成功'; + case 'snackBarMessages.importCancelled': return '导入已取消'; + case 'errors.noRecords': return '暂无记录'; + case 'errors.notEnoughData': return '数据不足'; + case 'errors.noHistorySaved': return '没有保存历史记录'; + case 'errors.connection': return ({required Object code, required Object message}) => '连接错误:${code} ${message}'; + case 'errors.noSuchUser': return '用户不存在'; + case 'errors.noSuchUserSub': return '您输入的内容有误,或者用户不存在'; + case 'errors.discordNotAssigned': return '没有指定Discord ID的用户'; + case 'errors.discordNotAssignedSub': return '请确保您提供了有效的 ID'; + case 'errors.history': return '缺少该玩家的历史'; + case 'errors.actionSuggestion': return '也许,您想要'; + case 'errors.p1nkl0bst3rTLmatches': return '没有找到Tetra联赛比赛'; + case 'errors.clientException': return '你尚未连接'; + case 'errors.forbidden': return '您的 IP 地址已被封禁'; + case 'errors.forbiddenSub': return ({required Object nickname}) => '如果你在使用VPN,请关闭。如果仍然不可以,请联系${nickname}'; + case 'errors.tooManyRequests': return '您的评分已经被限制'; + case 'errors.tooManyRequestsSub': return '请稍后重试'; + case 'errors.internal': return 'TETR.IO 出现了问题!'; + case 'errors.internalSub': return 'osk,或许,已经知道了'; + case 'errors.internalWebVersion': return 'TETR.IO (也许是oskware_bridge,我不知道到底是哪儿) 出现了问题!'; + case 'errors.internalWebVersionSub': return '如果 osk status 页面显示一切都很正常,请联系dan63047'; + case 'errors.oskwareBridge': return 'oskware_bridge 出现了问题!'; + case 'errors.oskwareBridgeSub': return '请联系dan63047'; + case 'errors.p1nkl0bst3rForbidden': return '第三方API屏蔽了您的 IP 地址'; + case 'errors.p1nkl0bst3rTooManyRequests': return '第三方API请求过多,请稍后再试'; + case 'errors.p1nkl0bst3rinternal': return 'p1nkl0bst3r 那边出现了问题!'; + case 'errors.p1nkl0bst3rinternalWebVersion': return 'p1nkl0bst3r 那边(也许是oskware_bridge,我不知道到底是哪儿)出现了问题!'; + case 'errors.replayAlreadySaved': return '回放已保存'; + case 'errors.replayExpired': return '回放已过期或不再可用'; + case 'errors.replayRejected': return '第三方API屏蔽了您的 IP 地址'; + case 'actions.cancel': return '取消'; + case 'actions.submit': return '确定'; + case 'actions.ok': return '确定'; + case 'actions.apply': return '应用'; + case 'actions.refresh': return '刷新'; + case 'graphsDestination.fetchAndsaveTLHistory': return '获取玩家历史'; + case 'graphsDestination.fetchAndSaveOldTLmatches': return '获取 Tetra 联赛历史记录'; + case 'graphsDestination.fetchAndsaveTLHistoryResult': return ({required Object number}) => '找到 ${number} 个状态'; + case 'graphsDestination.fetchAndSaveOldTLmatchesResult': return ({required Object number}) => '找到 ${number} 场比赛'; + case 'graphsDestination.gamesPlayed': return ({required Object games}) => '游玩次数:${games}'; + case 'graphsDestination.dateAndTime': return '日期和时间'; + case 'graphsDestination.filterModaleTitle': return '在图表上筛选等级'; + case 'filterModale.all': return '全部'; + case 'cutoffsDestination.title': return 'Tetra 联赛 状态'; + case 'cutoffsDestination.relevance': return ({required Object timestamp}) => '${timestamp}'; + case 'cutoffsDestination.actual': return '实际'; + case 'cutoffsDestination.target': return '目标'; + case 'cutoffsDestination.cutoffTR': return '分段 TR'; + case 'cutoffsDestination.targetTR': return '目标 TR'; + case 'cutoffsDestination.state': return '状态'; + case 'cutoffsDestination.advanced': return '高级选项'; + case 'cutoffsDestination.players': return ({required Object n}) => '玩家(${n})'; + case 'cutoffsDestination.moreInfo': return '更多信息'; + case 'cutoffsDestination.NumberOne': return ({required Object tr}) => '№ 1 is ${tr} TR'; + case 'cutoffsDestination.inflated': return ({required Object tr}) => '高于目标 ${tr}'; + case 'cutoffsDestination.notInflated': return '不偏高'; + case 'cutoffsDestination.deflated': return ({required Object tr}) => '低于目标 ${tr}'; + case 'cutoffsDestination.notDeflated': return '不偏低'; + case 'cutoffsDestination.wellDotDotDot': return '嗯…'; + case 'cutoffsDestination.fromPlace': return ({required Object n}) => '自 № ${n}'; + case 'cutoffsDestination.viewButton': return '查看'; + case 'rankView.rankTitle': return ({required Object rank}) => '${rank} 段数据'; + case 'rankView.everyoneTitle': return '全部排行榜'; + case 'rankView.trRange': return 'TR 范围'; + case 'rankView.supposedToBe': return '应为'; + case 'rankView.gap': return ({required Object value}) => '相差 ${value}'; + case 'rankView.trGap': return ({required Object value}) => '相差 ${value} TR'; + case 'rankView.deflationGap': return '偏低量'; + case 'rankView.inflationGap': return '偏高量'; + case 'rankView.LBposRange': return '排行榜位置范围'; + case 'rankView.overpopulated': return ({required Object players}) => '比期望的多 ${players}'; + case 'rankView.underpopulated': return ({required Object players}) => '比期望的少 ${players}'; + case 'rankView.PlayersEqualSupposedToBe': return '符合'; + case 'rankView.avgStats': return '平均数据'; + case 'rankView.avgForRank': return ({required Object rank}) => '${rank} 段平均数据'; + case 'rankView.avgNerdStats': return '平均详细信息'; + case 'rankView.minimums': return '最小值'; + case 'rankView.maximums': return '最大值'; + case 'stateView.title': return ({required Object date}) => '${date}的状态'; + case 'tlMatchView.match': return '匹配'; + case 'tlMatchView.vs': return 'vs'; + case 'tlMatchView.winner': return '获胜者'; + case 'tlMatchView.roundNumber': return ({required Object n}) => '第${n}回合'; + case 'tlMatchView.statsFor': return '状态'; + case 'tlMatchView.numberOfRounds': return '回合数'; + case 'tlMatchView.matchLength': return '比赛时长'; + case 'tlMatchView.roundLength': return '回合时长'; + case 'tlMatchView.matchStats': return '比赛数据'; + case 'tlMatchView.downloadReplay': return '下载 .ttrm 回放'; + case 'tlMatchView.openReplay': return '在 TETR.IO 中打开回放'; + case 'calcDestination.placeholders': return ({required Object stat}) => '输入你的${stat}'; + case 'calcDestination.tip': return '输入值并按 "计算" 来查看TA的详细信息'; + case 'calcDestination.statsCalcButton': return '计算'; + case 'calcDestination.damageCalcTip': return '点击左侧的操作在此添加'; + case 'calcDestination.actions': return '操作'; + case 'calcDestination.results': return '结果'; + case 'calcDestination.rules': return '规则'; + case 'calcDestination.noSpinClears': return '非 Spin 清除'; + case 'calcDestination.spins': return 'Spin'; + case 'calcDestination.miniSpins': return 'Mini spin'; + case 'calcDestination.noLineclear': return '无清除(连消结束)'; + case 'calcDestination.custom': return '自定义'; + case 'calcDestination.multiplier': return '倍增'; + case 'calcDestination.pcDamage': return '全消伤害'; + case 'calcDestination.comboTable': return '连击'; + case 'calcDestination.b2bChaining': return 'B2B增伤'; + case 'calcDestination.surgeStartAtB2B': return '开始于B2B'; + case 'calcDestination.surgeStartAmount': return '初始值'; + case 'calcDestination.totalDamage': return '累计伤害'; + case 'calcDestination.lineclears': return '清除行数'; + case 'calcDestination.combo': return '连击'; + case 'calcDestination.surge': return 'B2B充能'; + case 'calcDestination.pcs': return '全消'; + case 'infoDestination.title': return '信息中心'; + case 'infoDestination.sprintAndBlitzAverages': return '40 行 & 闪电战平均数据'; + case 'infoDestination.sprintAndBlitzAveragesDescription': return '计算40 行 & 闪电战平均数据是个很繁琐的过程,所以很久才会更新一次。 点击标题查看完整的 40 行 & 闪电战平均数据表'; + case 'infoDestination.tetraStatsWiki': return 'Tetra Stats Wiki'; + case 'infoDestination.tetraStatsWikiDescription': return '查看更多关于Tetra Stats提供的功能和数据'; + case 'infoDestination.about': return '关于 Tetra Stats'; + case 'infoDestination.aboutDescription': return '由 dan63 开发'; + case 'leaderboardsDestination.title': return '排行榜'; + case 'leaderboardsDestination.tl': return 'Tetra 联赛(当前赛季)'; + case 'leaderboardsDestination.fullTL': return 'Tetra 联赛(当前赛季,完整)'; + case 'leaderboardsDestination.ar': return '成就点'; + case 'savedDataDestination.title': return '已保存的数据'; + case 'savedDataDestination.tip': return '选择左边的昵称以查看与之相关的数据'; + case 'savedDataDestination.seasonTLstates': return ({required Object s}) => '第${s}赛季状态'; + case 'savedDataDestination.TLrecords': return '联赛记录'; + case 'settingsDestination.title': return '设置'; + case 'settingsDestination.general': return '常规'; + case 'settingsDestination.customization': return '自定义设置'; + case 'settingsDestination.database': return '本地数据库'; + case 'settingsDestination.checking': return '正在检查...'; + case 'settingsDestination.enterToSubmit': return '按回车键提交'; + case 'settingsDestination.account': return '您的 TETR.IO 账号'; + case 'settingsDestination.accountDescription': return '该玩家的状态将在启动此应用后立即加载。 默认情况下,它会加载我的数据。如要更改,请在此输入您的昵称。'; + case 'settingsDestination.done': return '完成!'; + case 'settingsDestination.noSuchAccount': return '账号不存在'; + case 'settingsDestination.language': return '语言'; + case 'settingsDestination.languageDescription': return ({required Object languages}) => 'Tetra Stats 有${languages}。默认情况下,应用程序将选择您的系统语言,如果您的系统区域设置不可用,则选择英语。'; + case 'settingsDestination.languages': return ({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('zh'))(n, + zero: '0种语言', + one: '${n}种语言', + two: '${n}种语言', + few: '${n}种语言', + many: '${n}种语言', + other: '${n}种语言', + ); + case 'settingsDestination.updateInTheBackground': return '后台更新数据'; + case 'settingsDestination.updateInTheBackgroundDescription': return '如果开启,Tetra Stats将尝试在缓存过期后查询新信息。通常一次/5分钟。'; + case 'settingsDestination.compareStats': return '将TL数据与段位平均水平作比较'; + case 'settingsDestination.compareStatsDescription': return '如果开启,Tetra Stats将提供额外的量度,使您能够将自己与普通玩家的等级相比较。 你看到它的方式——统计信息将以相应的颜色高亮,用光标悬停在它们上面以获取更多信息。'; + case 'settingsDestination.showPosition': return '显示排行榜中的位置'; + case 'settingsDestination.showPositionDescription': return '这可能需要一些时间(和流量),但您可以看到您在排行榜上的位置,按数据排序'; + case 'settingsDestination.accentColor': return '主题色'; + case 'settingsDestination.accentColorDescription': return '这种颜色会在这个应用上可见,而且通常会高亮显示交互界面元素。'; + case 'settingsDestination.accentColorModale': return '选取主题色'; + case 'settingsDestination.timestamps': return '时间戳格式'; + case 'settingsDestination.timestampsDescriptionPart1': return ({required Object d}) => '您可以选择时间戳显示时间的方式。默认情况下,它们以 GMT 时区显示时间,并根据所选区域设置进行格式设置,例如:${d}。'; + case 'settingsDestination.timestampsDescriptionPart2': return ({required Object y, required Object r}) => '这里还有:\n• 以您的时区设置的区域设置:${y}\n• 相对时间戳:${r}'; + case 'settingsDestination.timestampsAbsoluteGMT': return 'GMT'; + case 'settingsDestination.timestampsAbsoluteLocalTime': return '您的时区'; + case 'settingsDestination.timestampsRelative': return '相对'; + case 'settingsDestination.sheetbotLikeGraphs': return 'Sheetbot 型雷达图'; + case 'settingsDestination.sheetbotLikeGraphsDescription': return '尽管我认为,图表在 SheetBot 中的工作方式不是很正确,有些人感到困惑,那 -0.5 Stride 看起来不像它在 SheetBot 图表上那样。因此,我们这里有:如果开启,则如果数值为负,则图形上的点可以出现在图形的另一半。'; + case 'settingsDestination.oskKagariGimmick': return 'Osk-Kagari'; + case 'settingsDestination.oskKagariGimmickDescription': return '如果开启,osk的段位会显示为:kagari:'; + case 'settingsDestination.bytesOfDataStored': return '存储数据'; + case 'settingsDestination.TLrecordsSaved': return '已保存 Tetra 联赛记录'; + case 'settingsDestination.TLplayerstatesSaved': return '已保存 Tetra 联赛玩家状态'; + case 'settingsDestination.fixButton': return '修复'; + case 'settingsDestination.compressButton': return '压缩'; + case 'settingsDestination.exportDB': return '导出本地数据库'; + case 'settingsDestination.desktopExportAlertTitle': return '桌面导出'; + case 'settingsDestination.desktopExportText': return '看起来您在桌面上使用了这个应用程序。请检查您的文档文件夹,您应该找到"TetraStats.db"。请将其复制到某处'; + case 'settingsDestination.androidExportAlertTitle': return 'Android 导出'; + case 'settingsDestination.androidExportText': return ({required Object exportedDB}) => '已导出。\n${exportedDB}'; + case 'settingsDestination.importDB': return '导入本地数据库'; + case 'settingsDestination.importDBDescription': return '还原您的备份。请注意已存储的数据库将被覆盖。'; + case 'settingsDestination.importWrongFileType': return '文件类型错误!'; + case 'homeNavigation.overview': return '概览'; + case 'homeNavigation.standing': return '名次'; + case 'homeNavigation.seasons': return '赛季'; + case 'homeNavigation.mathces': return '比赛场次'; + case 'homeNavigation.pb': return '个人最佳'; + case 'homeNavigation.normal': return '普通模式'; + case 'homeNavigation.expert': return '专家模式'; + case 'homeNavigation.expertRecords': return '专家模式记录'; + case 'graphsNavigation.history': return '玩家历史记录'; + case 'graphsNavigation.league': return '联赛状态'; + case 'graphsNavigation.cutoffs': return '分段线历史'; + case 'calcNavigation.stats': return '数据计算器'; + case 'calcNavigation.damage': return '伤害计算器'; + case 'firstTimeView.welcome': return '欢迎使用 Tetra Stats'; + case 'firstTimeView.description': return '服务,允许您跟踪TETR.IO的各种数据'; + case 'firstTimeView.nicknameQuestion': return '您的昵称是?'; + case 'firstTimeView.inpuntHint': return '在此处输入... (3-16个符号)'; + case 'firstTimeView.emptyInputError': return '不能提交空字符串'; + case 'firstTimeView.niceToSeeYou': return ({required Object n}) => '很高兴见到你,${n}'; + case 'firstTimeView.letsTakeALook': return '让我们看看您的统计资料...'; + case 'firstTimeView.skip': return '跳过'; + case 'aboutView.title': return '关于 Tetra Stats'; + case 'aboutView.about': return 'Tetra Stats是一种服务,与TETR.IO Tetra Channel API共用,提供数据并根据这种数据计算一些附加度量。 服务允许用户用"Track"功能跟踪他们在Tetra League中的进度,这个功能记录每个Tetra Leage更改到本地数据库(非自动) 您必须不时地访问服务。这样,这些更改可以通过图表来查看。\n\nBeanserver blaster 是Tetra Stats的一部分,它被拆解成服务器侧脚本。 它提供完整的Tetra League排行榜,允许Tetra Stats通过任何公式对排行榜进行排序并生成散点图,这允许用户分析Tetra联赛趋势。 它还提供了Tetra League 的评分历史,用户也可以通过图表看到。\n\n我们有一个添加回放分析和锦标赛历史记录的计划,所以随时关注!\n\n服务没有与TETR.IO与osk以任何身份关联。'; + case 'aboutView.appVersion': return '版本'; + case 'aboutView.build': return ({required Object build}) => '${build}'; + case 'aboutView.GHrepo': return 'GitHub Repository'; + case 'aboutView.submitAnIssue': return '提交问题'; + case 'aboutView.credits': return '鸣谢'; + case 'aboutView.authorAndDeveloper': return '作者 & 开发者'; + case 'aboutView.providedFormulas': return '提供的公式'; + case 'aboutView.providedS1history': return '提供的 S1 历史'; + case 'aboutView.inoue': return 'Inoue (回放抓取器)'; + case 'aboutView.zhCNlocale': return '简中翻译员'; + case 'aboutView.supportHim': return '为他提供支持!'; + case 'stats.registrationDate': return '注册时间'; + case 'stats.gametime': return '游玩时长'; + case 'stats.ogp': return '在线游戏次数'; + case 'stats.ogw': return '在线游戏胜利次数'; + case 'stats.followers': return '粉丝'; + case 'stats.xp.short': return '经验值'; + case 'stats.xp.full': return '经验点'; + case 'stats.tr.short': return 'TR'; + case 'stats.tr.full': return 'Tetra 评分'; + case 'stats.glicko.short': return 'Glicko'; + case 'stats.glicko.full': return 'Glicko'; + case 'stats.rd.short': return 'RD'; + case 'stats.rd.full': return '评分偏差'; + case 'stats.glixare.short': return 'GXE'; + case 'stats.glixare.full': return 'GLIXARE'; + case 'stats.s1tr.short': return 'S1 TR'; + case 'stats.s1tr.full': return '第 1 赛季式 TR'; + case 'stats.gp.short': return 'GP'; + case 'stats.gp.full': return '总场数'; + case 'stats.gw.short': return 'GW'; + case 'stats.gw.full': return '胜场数'; + case 'stats.winrate.short': return 'WR%'; + case 'stats.winrate.full': return '胜率'; + case 'stats.apm.short': return 'APM'; + case 'stats.apm.full': return '每分钟攻击数'; + case 'stats.pps.short': return 'PPS'; + case 'stats.pps.full': return '每秒块数'; + case 'stats.vs.short': return 'VS'; + case 'stats.vs.full': return 'VS 分数'; + case 'stats.app.short': return 'APP'; + case 'stats.app.full': return '每块攻击数'; + case 'stats.vsapm.short': return 'VS/APM'; + case 'stats.vsapm.full': return 'VS / APM'; + case 'stats.dss.short': return 'DS/S'; + case 'stats.dss.full': return '每秒挖掘数'; + case 'stats.dsp.short': return 'DS/P'; + case 'stats.dsp.full': return '每块挖掘数'; + case 'stats.appdsp.short': return 'APP+DSP'; + case 'stats.appdsp.full': return 'APP + DSP'; + case 'stats.cheese.short': return 'CI'; + case 'stats.cheese.full': return '垃圾行混乱指数'; + case 'stats.gbe.short': return 'GbE'; + case 'stats.gbe.full': return '垃圾行效率'; + case 'stats.nyaapp.short': return 'wAPP'; + case 'stats.nyaapp.full': return '加权APP'; + case 'stats.area.short': return '面积'; + case 'stats.area.full': return '面积'; + case 'stats.etr.short': return 'eTR'; + case 'stats.etr.full': return '预测 TR'; + case 'stats.etracc.short': return '±eTR'; + case 'stats.etracc.full': return '预测实际差量'; + case 'stats.opener.short': return '定式'; + case 'stats.opener.full': return '定式'; + case 'stats.plonk.short': return '太极'; + case 'stats.plonk.full': return '太极'; + case 'stats.stride.short': return '速度'; + case 'stats.stride.full': return '速度'; + case 'stats.infds.short': return '挖掘'; + case 'stats.infds.full': return '挖掘'; + case 'stats.altitude.short': return 'm'; + case 'stats.altitude.full': return '高度'; + case 'stats.climbSpeed.short': return 'CSP'; + case 'stats.climbSpeed.full': return '爬行速度'; + case 'stats.climbSpeed.gaugetTitle': return '爬行速度'; + case 'stats.peakClimbSpeed.short': return '最高CSP'; + case 'stats.peakClimbSpeed.full': return '最高爬行速度'; + case 'stats.peakClimbSpeed.gaugetTitle': return '最高'; + case 'stats.kos.short': return 'KO\'s'; + case 'stats.kos.full': return '击杀'; + case 'stats.b2b.short': return 'B2B'; + case 'stats.b2b.full': return '背靠背/满贯'; + case 'stats.finesse.short': return '极'; + case 'stats.finesse.full': return '极简率'; + case 'stats.finesse.widgetTitle': return '简率'; + case 'stats.finesseFaults.short': return '非极简'; + case 'stats.finesseFaults.full': return '非极简操作数'; + case 'stats.totalTime.short': return '时长'; + case 'stats.totalTime.full': return '总时长'; + case 'stats.totalTime.widgetTitle': return '总时长'; + case 'stats.level.short': return 'Lvl'; + case 'stats.level.full': return '等级'; + case 'stats.pieces.short': return 'P'; + case 'stats.pieces.full': return '块'; + case 'stats.spp.short': return 'SPP'; + case 'stats.spp.full': return '每块得分'; + case 'stats.kp.short': return 'KP'; + case 'stats.kp.full': return '按键'; + case 'stats.kpp.short': return 'KPP'; + case 'stats.kpp.full': return '每块按键数'; + case 'stats.kps.short': return 'KPS'; + case 'stats.kps.full': return '每秒按键数'; + case 'stats.blitzScore': return ({required Object p}) => '${p} 分'; + case 'stats.levelUpRequirement': return ({required Object p}) => '还需 ${p} 升到下一级'; + case 'stats.piecesTotal': return '放块总数'; + case 'stats.piecesWithPerfectFinesse': return '极简块数'; + case 'stats.score': return '分数'; + case 'stats.lines': return '行数'; + case 'stats.linesShort': return '行'; + case 'stats.pcs': return '全消数'; + case 'stats.holds': return '暂存数'; + case 'stats.spike': return '最高暴击'; + case 'stats.top': return ({required Object percentage}) => '前 ${percentage}'; + case 'stats.topRank': return ({required Object rank}) => '最高段位:${rank}'; + case 'stats.floor': return '层'; + case 'stats.split': return '拆分'; + case 'stats.total': return '总计'; + case 'stats.sent': return '已发送'; + case 'stats.received': return '已接收'; + case 'stats.placement': return '排名'; + case 'stats.peak': return '最高'; + case 'stats.qpWithMods': return ({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('zh'))(n, + one: '使用 1 个模组', + two: '使用 ${n} 个模组', + few: '使用 ${n} 个模组', + many: '使用 ${n} 个模组', + other: '使用 ${n} 个模组', + ); + case 'stats.inputs': return ({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('zh'))(n, + zero: '${n} 按键', + one: '${n} 按键', + two: '${n} 按键', + few: '${n} 按键', + many: '${n} 按键', + other: '${n} 按键', + ); + case 'stats.tspinsTotal': return ({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('zh'))(n, + zero: '总共 ${n} 次T旋', + one: '总共 ${n} 次T旋', + two: '总共 ${n} 次T旋', + few: '总共 ${n} 次T旋', + many: '总共 ${n} 次T旋', + other: '总共 ${n} 次T旋', + ); + case 'stats.linesCleared': return ({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('zh'))(n, + zero: '总共消除 ${n} 行', + one: '总共消除 ${n} 行', + two: '总共消除 ${n} 行', + few: '总共消除 ${n} 行', + many: '总共消除 ${n} 行', + other: '总共消除 ${n} 行', + ); + case 'stats.graphs.attack': return '攻击'; + case 'stats.graphs.speed': return '速度'; + case 'stats.graphs.defense': return '防御'; + case 'stats.graphs.cheese': return '奶酪层'; + case 'stats.players': return ({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('zh'))(n, + zero: '${n} 名玩家', + one: '${n} 名玩家', + two: '${n} 名玩家', + few: '${n} 名玩家', + many: '${n} 名玩家', + other: '${n} 名玩家', + ); + case 'stats.games': return ({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('zh'))(n, + zero: '${n} 次游戏', + one: '${n} 次游戏', + two: '${n} 次游戏', + few: '${n} 次游戏', + many: '${n} 次游戏', + other: '${n} 次游戏', + ); + case 'stats.lineClear.single': return 'Single'; + case 'stats.lineClear.double': return 'Double'; + case 'stats.lineClear.triple': return 'Triple'; + case 'stats.lineClear.quad': return 'Quad'; + case 'stats.lineClear.penta': return 'Penta'; + case 'stats.lineClear.hexa': return 'Hexa'; + case 'stats.lineClear.hepta': return 'Hepta'; + case 'stats.lineClear.octa': return 'Octa'; + case 'stats.lineClear.ennea': return 'Ennea'; + case 'stats.lineClear.deca': return 'Deca'; + case 'stats.lineClear.hendeca': return 'Hendeca'; + case 'stats.lineClear.dodeca': return 'Dodeca'; + case 'stats.lineClear.triadeca': return 'Triadeca'; + case 'stats.lineClear.tessaradeca': return 'Tessaradeca'; + case 'stats.lineClear.pentedeca': return 'Pentedeca'; + case 'stats.lineClear.hexadeca': return 'Hexadeca'; + case 'stats.lineClear.heptadeca': return 'Heptadeca'; + case 'stats.lineClear.octadeca': return 'Octadeca'; + case 'stats.lineClear.enneadeca': return 'Enneadeca'; + case 'stats.lineClear.eicosa': return 'Eicosa'; + case 'stats.lineClear.kagaris': return 'Kagaris'; + case 'stats.lineClears.zero': return 'Zeros'; + case 'stats.lineClears.single': return 'Singles'; + case 'stats.lineClears.double': return 'Doubles'; + case 'stats.lineClears.triple': return 'Triples'; + case 'stats.lineClears.quad': return 'Quads'; + case 'stats.lineClears.penta': return 'Pentas'; + case 'stats.mini': return 'Mini'; + case 'stats.tSpin': return 'T-spin'; + case 'stats.tSpins': return 'T-spins'; + case 'stats.spin': return 'Spin'; + case 'stats.spins': return 'Spins'; + case 'countries.': return '全球'; + case 'countries.AF': return '阿富汗'; + case 'countries.AX': return '奥兰群岛'; + case 'countries.AL': return '阿尔巴尼亚'; + case 'countries.DZ': return '阿尔及利亚'; + case 'countries.AS': return '美属萨摩亚'; + case 'countries.AD': return '安道尔'; + case 'countries.AO': return '安哥拉'; + case 'countries.AI': return '安圭拉'; + case 'countries.AQ': return '南极洲'; + case 'countries.AG': return '安提瓜和巴布达'; + case 'countries.AR': return '阿根廷'; + case 'countries.AM': return '亚美尼亚'; + case 'countries.AW': return '阿鲁巴'; + case 'countries.AU': return '澳大利亚'; + case 'countries.AT': return '奥地利'; + case 'countries.AZ': return '阿塞拜疆'; + case 'countries.BS': return '巴哈马'; + case 'countries.BH': return '巴林'; + case 'countries.BD': return '孟加拉国'; + case 'countries.BB': return '巴巴多斯'; + case 'countries.BY': return '白俄罗斯'; + case 'countries.BE': return '比利时'; + case 'countries.BZ': return '伯利兹'; + case 'countries.BJ': return '贝宁'; + case 'countries.BM': return '百慕大'; + case 'countries.BT': return '不丹'; + case 'countries.BO': return '玻利维亚'; + case 'countries.BA': return '波黑'; + case 'countries.BW': return '博茨瓦纳'; + case 'countries.BV': return '布韦岛'; + case 'countries.BR': return '巴西'; + case 'countries.IO': return '英属印度洋领地'; + case 'countries.BN': return '文莱'; + case 'countries.BG': return '保加利亚'; + case 'countries.BF': return '布基纳法索'; + case 'countries.BI': return '布隆迪'; + case 'countries.KH': return '柬埔寨'; + case 'countries.CM': return '喀麦隆'; + case 'countries.CA': return '加拿大'; + case 'countries.CV': return '佛得角'; + case 'countries.BQ': return '荷兰加勒比区'; + case 'countries.KY': return '开曼群岛'; + case 'countries.CF': return '中非'; + case 'countries.TD': return '乍得'; + case 'countries.CL': return '智利'; + case 'countries.CN': return '中国'; + case 'countries.CX': return '圣诞岛'; + case 'countries.CC': return '科科斯群岛'; + case 'countries.CO': return '哥伦比亚'; + case 'countries.KM': return '科摩罗'; + case 'countries.CG': return '刚果(布)'; + case 'countries.CD': return '刚果(金)'; + case 'countries.CK': return '库克群岛'; + case 'countries.CR': return '哥斯达黎加'; + case 'countries.CI': return '科特迪瓦'; + case 'countries.HR': return '克罗地亚'; + case 'countries.CU': return '古巴'; + case 'countries.CW': return '库拉索'; + case 'countries.CY': return '塞浦路斯'; + case 'countries.CZ': return '捷克'; + case 'countries.DK': return '丹麦'; + case 'countries.DJ': return '吉布提'; + case 'countries.DM': return '多米尼克'; + case 'countries.DO': return '多米尼加共和国'; + case 'countries.EC': return '厄瓜多尔'; + case 'countries.EG': return '埃及'; + case 'countries.SV': return '萨尔瓦多'; + case 'countries.GB-ENG': return '英格兰'; + case 'countries.GQ': return '赤道几内亚'; + case 'countries.ER': return '厄立特里亚'; + case 'countries.EE': return '爱沙尼亚'; + case 'countries.ET': return '埃塞俄比亚'; + case 'countries.EU': return '欧洲'; + case 'countries.FK': return '福克兰群岛 (马尔维纳斯)'; + case 'countries.FO': return '法罗群岛'; + case 'countries.FJ': return '斐济'; + case 'countries.FI': return '芬兰'; + case 'countries.FR': return '法国'; + case 'countries.GF': return '法属圭亚那'; + case 'countries.PF': return '法属波利尼西亚'; + case 'countries.TF': return '法属南部领地'; + case 'countries.GA': return '加蓬'; + case 'countries.GM': return '冈比亚'; + case 'countries.GE': return '格鲁吉亚'; + case 'countries.DE': return '德国'; + case 'countries.GH': return '加纳'; + case 'countries.GI': return '直布罗陀'; + case 'countries.GR': return '希腊'; + case 'countries.GL': return '格陵兰'; + case 'countries.GD': return '格林纳达'; + case 'countries.GP': return '瓜德罗普'; + case 'countries.GU': return '关岛'; + case 'countries.GT': return '危地马拉'; + case 'countries.GG': return '根西'; + case 'countries.GN': return '几内亚'; + case 'countries.GW': return '几内亚比绍'; + case 'countries.GY': return '圭亚那'; + case 'countries.HT': return '海地'; + case 'countries.HM': return '赫德岛和麦克唐纳群岛'; + case 'countries.VA': return '梵蒂冈'; + case 'countries.HN': return '洪都拉斯'; + case 'countries.HK': return '中国香港'; + case 'countries.HU': return '匈牙利'; + case 'countries.IS': return '冰岛'; + case 'countries.IN': return '印度'; + case 'countries.ID': return '印尼'; + case 'countries.IR': return '伊朗'; + case 'countries.IQ': return '伊拉克'; + case 'countries.IE': return '爱尔兰'; + case 'countries.IM': return '马恩岛'; + case 'countries.IL': return '以色列'; + case 'countries.IT': return '意大利'; + case 'countries.JM': return '牙买加'; + case 'countries.JP': return '日本'; + case 'countries.JE': return '泽西'; + case 'countries.JO': return '约旦'; + case 'countries.KZ': return '哈萨克斯坦'; + case 'countries.KE': return '肯尼亚'; + case 'countries.KI': return '基里巴斯'; + case 'countries.KP': return '朝鲜'; + case 'countries.KR': return '韩国'; + case 'countries.XK': return '科索沃'; + case 'countries.KW': return '科威特'; + case 'countries.KG': return '吉尔吉斯斯坦'; + case 'countries.LA': return '老挝'; + case 'countries.LV': return '拉托维亚'; + case 'countries.LB': return '黎巴嫩'; + case 'countries.LS': return '莱索托'; + case 'countries.LR': return '利比里亚'; + case 'countries.LY': return '利比亚'; + case 'countries.LI': return '列支敦士登'; + case 'countries.LT': return '立陶宛'; + case 'countries.LU': return '卢森堡'; + case 'countries.MO': return '中国澳门'; + case 'countries.MK': return '马其顿'; + case 'countries.MG': return '马达加斯加'; + case 'countries.MW': return '马拉维'; + case 'countries.MY': return '马来西亚'; + case 'countries.MV': return '马尔代夫'; + case 'countries.ML': return '马里'; + case 'countries.MT': return '马耳他'; + case 'countries.MH': return '马绍尔群岛'; + case 'countries.MQ': return '马提尼克'; + case 'countries.MR': return '毛里塔尼亚'; + case 'countries.MU': return '毛里求斯'; + case 'countries.YT': return '马约特岛'; + case 'countries.MX': return '墨西哥'; + case 'countries.FM': return '密克罗尼西亚'; + case 'countries.MD': return '摩尔多瓦'; + case 'countries.MC': return '摩纳哥'; + case 'countries.ME': return '黑山'; + case 'countries.MA': return '摩洛哥'; + case 'countries.MN': return '蒙古'; + case 'countries.MS': return '蒙特塞拉特'; + case 'countries.MZ': return '莫桑比克'; + case 'countries.MM': return '缅甸'; + case 'countries.NA': return '纳米比亚'; + case 'countries.NR': return '瑙鲁'; + case 'countries.NP': return '尼泊尔'; + case 'countries.NL': return '荷兰'; + case 'countries.AN': return '荷属安地列斯'; + case 'countries.NC': return '新喀里多尼亚'; + case 'countries.NZ': return '新西兰'; + case 'countries.NI': return '尼加拉瓜'; + case 'countries.NE': return '尼日尔'; + case 'countries.NG': return '尼日利亚'; + case 'countries.NU': return '纽埃'; + case 'countries.NF': return '诺福克岛'; + case 'countries.GB-NIR': return '北爱尔兰'; + case 'countries.MP': return '北马里亚纳群岛'; + case 'countries.NO': return '挪威'; + case 'countries.OM': return '阿曼'; + case 'countries.PK': return '巴基斯坦'; + case 'countries.PW': return '帕劳'; + case 'countries.PS': return '巴勒斯坦'; + case 'countries.PA': return '巴拿马'; + case 'countries.PG': return '巴布亚新几内亚'; + case 'countries.PY': return '巴拉圭'; + case 'countries.PE': return '秘鲁'; + case 'countries.PH': return '菲律宾'; + case 'countries.PN': return '皮特凯恩'; + case 'countries.PL': return '波兰'; + case 'countries.PT': return '葡萄牙'; + case 'countries.PR': return '波多黎哥'; + case 'countries.QA': return '卡塔尔'; + case 'countries.RE': return '留尼汪'; + case 'countries.RO': return '罗马尼亚'; + case 'countries.RU': return '俄罗斯'; + case 'countries.RW': return '卢旺达'; + case 'countries.BL': return '圣巴泰勒米'; + case 'countries.SH': return '圣赫勒拿-阿森松-特里斯坦达库尼亚'; + case 'countries.KN': return '圣基茨和尼维斯'; + case 'countries.LC': return '圣卢西亚'; + case 'countries.MF': return '圣马丁'; + case 'countries.PM': return '圣皮埃尔和密克隆'; + case 'countries.VC': return '圣文森特和格林纳丁斯'; + case 'countries.WS': return '萨摩亚'; + case 'countries.SM': return '圣马利诺'; + case 'countries.ST': return '圣多美和普林西比'; + case 'countries.SA': return '沙特阿拉伯'; + case 'countries.GB-SCT': return '苏格兰'; + case 'countries.SN': return '塞内加尔'; + case 'countries.RS': return '塞尔维亚'; + case 'countries.SC': return '塞舌尔'; + case 'countries.SL': return '塞拉利昂'; + case 'countries.SG': return '新加坡'; + case 'countries.SX': return '荷属圣马丁'; + case 'countries.SK': return '斯洛伐克'; + case 'countries.SI': return '斯洛文尼亚'; + case 'countries.SB': return '所罗门'; + case 'countries.SO': return '索马里'; + case 'countries.ZA': return '南非'; + case 'countries.GS': return '南乔治亚和南桑威奇'; + case 'countries.SS': return '南苏丹'; + case 'countries.ES': return '西班牙'; + case 'countries.LK': return '斯里兰卡'; + case 'countries.SD': return '苏丹'; + case 'countries.SR': return '苏里南'; + case 'countries.SJ': return '斯瓦尔巴和扬马延'; + case 'countries.SZ': return '斯威士兰'; + case 'countries.SE': return '瑞典'; + case 'countries.CH': return '瑞士'; + case 'countries.SY': return '叙利亚'; + case 'countries.TW': return '中国台湾'; + case 'countries.TJ': return '塔吉克斯坦'; + case 'countries.TZ': return '坦桑尼亚'; + case 'countries.TH': return '泰国'; + case 'countries.TL': return '东帝汶'; + case 'countries.TG': return '多哥'; + case 'countries.TK': return '托克劳'; + case 'countries.TO': return '汤加'; + case 'countries.TT': return '特立尼达和多巴哥'; + case 'countries.TN': return '突尼斯'; + case 'countries.TR': return '土耳其'; + case 'countries.TM': return '土库曼斯坦'; + case 'countries.TC': return '特克斯河凯科斯群岛'; + case 'countries.TV': return '图瓦卢'; + case 'countries.UG': return '乌干达'; + case 'countries.UA': return '乌克兰'; + case 'countries.AE': return '阿联酋'; + case 'countries.GB': return '英国'; + case 'countries.US': return '美国'; + case 'countries.UY': return '乌拉圭'; + case 'countries.UM': return '美国本土外小岛屿'; + case 'countries.UZ': return '乌兹别克斯坦'; + case 'countries.VU': return '瓦努阿图'; + case 'countries.VE': return '委内瑞拉'; + case 'countries.VN': return '越南'; + case 'countries.VG': return '英属维京群岛'; + case 'countries.VI': return '美属维京群岛'; + case 'countries.GB-WLS': return '威尔士'; + case 'countries.WF': return '瓦利斯群岛和富图纳群岛'; + case 'countries.EH': return '西撒哈拉'; + case 'countries.YE': return '也门'; + case 'countries.ZM': return '赞比亚'; + case 'countries.ZW': return '津巴布韦'; + case 'countries.XX': return '未知'; + case 'countries.XM': return '月球'; + default: return null; + } + } +} diff --git a/lib/services/tetrio_crud.dart b/lib/services/tetrio_crud.dart index b7f107e..592dfaf 100644 --- a/lib/services/tetrio_crud.dart +++ b/lib/services/tetrio_crud.dart @@ -35,7 +35,7 @@ import 'package:tetra_stats/services/sqlite_db_controller.dart'; import 'package:csv/csv.dart'; const String dbName = "TetraStats.db"; -const String webVersionDomain = "ts.dan63.by"; +const String webVersionDomain = "tsbeta.dan63.by"; const String tetrioUsersTable = "tetrioUsers"; const String tetrioUsersToTrackTable = "tetrioUsersToTrack"; const String tetraLeagueMatchesTable = "tetrioAlphaLeagueMathces"; @@ -888,13 +888,17 @@ class TetrioService extends DB { Future> fetchTetrioRecordsLeaderboard({String? prisecter, String? lb, String? country}) async{ Uri url; if (kIsWeb) { - url = Uri.https(webVersionDomain, 'oskware_bridge.php', {"endpoint": "TLLeaderboard"}); - } else { - url = Uri.https('ch.tetr.io', 'api/records/${lb??"40l_global"}', { - "limit": "100", + url = Uri.https(webVersionDomain, 'oskware_bridge.php', { + "endpoint": "RecordsLeaderboard", + "lb": lb??"40l", if (prisecter != null) "after": prisecter, if (country != null) "country": country }); + } else { + url = Uri.https('ch.tetr.io', 'api/records/${lb??"40l"}_${country != null ? "country_${country}":"global"}', { + "limit": "100", + if (prisecter != null) "after": prisecter + }); } try{ final response = await client.get(url); @@ -908,12 +912,12 @@ class TetrioService extends DB { for (Map entry in rawJson['data']['entries']) { leaderboard.add(RecordSingle.fromJson(entry, -1, -1)); } - developer.log("fetchTLLeaderboard: Leaderboard retrieved and cached", name: "services/tetrio_crud"); + developer.log("fetchTetrioRecordsLeaderboard: Leaderboard retrieved and cached", name: "services/tetrio_crud"); //_leaderboardsCache[rawJson['cache']['cached_until'].toString()] = leaderboard; //_cache.store(leaderboard, rawJson['cache']['cached_until']); return leaderboard; } else { // idk how to hit that one - developer.log("fetchTLLeaderboard: Bruh", name: "services/tetrio_crud", error: rawJson); + developer.log("fetchTetrioRecordsLeaderboard: Bruh", name: "services/tetrio_crud", error: rawJson); throw Exception("Failed to get leaderboard (problems on the tetr.io side)"); // will it be on tetr.io side? } case 403: @@ -928,7 +932,7 @@ class TetrioService extends DB { case 504: throw TetrioInternalProblem(); default: - developer.log("fetchTLLeaderboard: Failed to fetch leaderboard", name: "services/tetrio_crud", error: response.statusCode); + developer.log("fetchTetrioRecordsLeaderboard: Failed to fetch leaderboard", name: "services/tetrio_crud", error: response.statusCode); throw ConnectionIssue(response.statusCode, response.reasonPhrase??"No reason"); } } on http.ClientException catch (e, s) { @@ -990,15 +994,22 @@ class TetrioService extends DB { /// Retrieves avaliable Tetra League matches from Tetra Channel api. Returns stream object (fake stream). /// Throws an exception if fails to retrieve. - Future fetchTLStream(String userID) async { - TetraLeagueBetaStream? cached = _cache.get(userID, TetraLeagueBetaStream); - if (cached != null) return cached; + Future fetchTLStream(String userID, {String? prisecter}) async { + // TetraLeagueBetaStream? cached = _cache.get(userID, TetraLeagueBetaStream); + // if (cached != null) return cached; Uri url; if (kIsWeb) { - url = Uri.https(webVersionDomain, 'oskware_bridge.php', {"endpoint": "tetrioUserTL", "user": userID.toLowerCase().trim()}); + url = Uri.https(webVersionDomain, 'oskware_bridge.php', { + "endpoint": "tetrioUserTL", + "user": userID.toLowerCase().trim(), + if (prisecter != null) "after": prisecter + }); } else { - url = Uri.https('ch.tetr.io', 'api/users/${userID.toLowerCase().trim()}/records/league/recent'); + url = Uri.https('ch.tetr.io', 'api/users/${userID.toLowerCase().trim()}/records/league/recent', { + "limit": "100", + if (prisecter != null) "after": prisecter + }); } try { final response = await client.get(url); diff --git a/lib/views/about_view.dart b/lib/views/about_view.dart index b605b89..aa45181 100644 --- a/lib/views/about_view.dart +++ b/lib/views/about_view.dart @@ -30,9 +30,9 @@ class AboutCard extends StatelessWidget{ Widget build(BuildContext context) { return Card(child: Column( children: [ - Text(title, style: Theme.of(context).textTheme.titleSmall, textAlign: TextAlign.center), + Text(title, style: Theme.of(context).textTheme.titleMedium, textAlign: TextAlign.center), Divider(), - Text(value, textAlign: TextAlign.center, style: const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 36, fontWeight: FontWeight.w500, color: Colors.white)), + Text(value, textAlign: TextAlign.center, style: Theme.of(context).textTheme.headlineMedium), if (undervalue != null) Text(undervalue!, textAlign: TextAlign.center), Divider(), Padding( @@ -115,7 +115,7 @@ class AboutState extends State { ]), Card(child: Center(child: Padding( padding: const EdgeInsets.fromLTRB(0.0, 6.0, 0.0, 18.0), - child: Text(t.aboutView.credits, style: Theme.of(context).textTheme.titleLarge, textAlign: TextAlign.center), + child: Text(t.aboutView.credits, style: Theme.of(context).textTheme.titleSmall, textAlign: TextAlign.center), ))), Wrap( direction: Axis.horizontal, diff --git a/lib/views/compare_view_tiles.dart b/lib/views/compare_view_tiles.dart index 576fd72..465c581 100644 --- a/lib/views/compare_view_tiles.dart +++ b/lib/views/compare_view_tiles.dart @@ -6,6 +6,7 @@ import 'package:fl_chart/fl_chart.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; +import 'dart:developer' as developer; import 'package:tetra_stats/data_objects/aggregate_stats.dart'; import 'package:tetra_stats/data_objects/record_single.dart'; import 'package:tetra_stats/data_objects/summaries.dart'; @@ -14,6 +15,7 @@ import 'package:tetra_stats/data_objects/tetrio_player.dart'; import 'package:tetra_stats/data_objects/tetrio_zen.dart'; import 'package:tetra_stats/gen/strings.g.dart'; import 'package:tetra_stats/main.dart' show teto; +import 'package:tetra_stats/services/crud_exceptions.dart'; import 'package:tetra_stats/utils/numers_formats.dart'; import 'package:tetra_stats/utils/relative_timestamps.dart'; import 'package:tetra_stats/utils/text_shadow.dart'; @@ -406,7 +408,7 @@ class CompareState extends State { Text(s.league.playstyle != null ? f4.format(s.league.playstyle!.infds) : "---"), ]); formattedValues[2].add([ - RichText(text: TextSpan(text: zenithRun != null ? "${f2.format(zenithRun.stats.zenith!.altitude)} m" : "---", style: TextStyle(fontFamily: "Eurostile Round"), children: [if (zenithRun != null && oldZenithRun) TextSpan(text: " (${zenithRun.revolution})", style: TextStyle(color: Colors.grey))])), + RichText(text: TextSpan(text: zenithRun != null ? "${f2.format(zenithRun.stats.zenith!.altitude)} m" : "---", style: TextStyle(fontFamily: "Eurostile Round", color: Colors.white), children: [if (zenithRun != null && oldZenithRun) TextSpan(text: " (${zenithRun.revolution})", style: TextStyle(color: Colors.grey))])), Text(zenithRun != null ? "№ "+intf.format(zenithRun.rank) : "---"), Text(zenithRun != null ? f2.format(zenithRun.aggregateStats.apm) : "---"), Text(zenithRun != null ? f2.format(zenithRun.aggregateStats.pps) : "---"), @@ -434,7 +436,7 @@ class CompareState extends State { Text(zenithRun?.aggregateStats.playstyle != null ? f4.format(zenithRun!.aggregateStats.playstyle.infds) : "---"), ]); formattedValues[3].add([ - RichText(text: TextSpan(text: zenithExRun != null ? "${f2.format(zenithExRun.stats.zenith!.altitude)} m" : "---", style: TextStyle(fontFamily: "Eurostile Round"), children: [if (zenithExRun != null && oldZenithExRun) TextSpan(text: " (${zenithExRun.revolution})", style: TextStyle(color: Colors.grey))])), + RichText(text: TextSpan(text: zenithExRun != null ? "${f2.format(zenithExRun.stats.zenith!.altitude)} m" : "---", style: TextStyle(fontFamily: "Eurostile Round", color: Colors.white), children: [if (zenithExRun != null && oldZenithExRun) TextSpan(text: " (${zenithExRun.revolution})", style: TextStyle(color: Colors.grey))])), Text(zenithExRun != null ? "№ "+intf.format(zenithExRun.rank) : "---"), Text(zenithExRun != null ? f2.format(zenithExRun.aggregateStats.apm) : "---"), Text(zenithExRun != null ? f2.format(zenithExRun.aggregateStats.pps) : "---"), @@ -738,19 +740,31 @@ class CompareState extends State { }); } - void addPlayer(String nickname) async { - if (nickname.startsWith("\$avg")){ - await addRankAverages(nickname.substring(4).toLowerCase()); - }else{ - players.add(await teto.fetchPlayer(nickname)); - summaries.add(await teto.fetchSummaries(players.last.userId)); - addvaluesEntrys(players.last, summaries.last); - nicknames.add(players.last.username); + Future addPlayer(String nickname) async { + try { + if (nickname.startsWith("\$avg")){ + await addRankAverages(nickname.substring(4).toLowerCase()); + return null; + }else{ + late TetrioPlayer player; + late Summaries summary; + List requests = await Future.wait([teto.fetchPlayer(nickname), teto.fetchSummaries(players.last.userId)]); + player = requests[0]; + summary = requests[1]; + players.add(player); + summaries.add(summary); + addvaluesEntrys(players.last, summaries.last); + nicknames.add(players.last.username); + } + } on Exception catch (e) { + developer.log("Failed to add player:", error: e); + return e; } best = recalculateBestEntries(); setState(() { }); + return null; } Future addRankAverages(String rank) async { @@ -832,103 +846,105 @@ class CompareState extends State { child: const Icon(Icons.arrow_back), ), ), - body: SingleChildScrollView( + body: SafeArea( child: SingleChildScrollView( - scrollDirection: Axis.horizontal, - controller: _scrollController, - physics: const AlwaysScrollableScrollPhysics(), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + controller: _scrollController, + physics: const AlwaysScrollableScrollPhysics(), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - SizedBox( - height: 175.0, - width: 300.0, - child: Card( - child: Padding( - padding: const EdgeInsets.fromLTRB(90.0, 18.0, 5.0, 0), - child: Text(t.comparison, style: TextStyle(fontSize: 28)), + Row( + children: [ + SizedBox( + height: 175.0, + width: 300.0, + child: Card( + child: Padding( + padding: const EdgeInsets.fromLTRB(90.0, 18.0, 5.0, 0), + child: Text(t.comparison, style: TextStyle(fontSize: 28)), + ), ), ), - ), - for (var p in players) SizedBox( - width: 300.0, - child: HeaderCard(p, removePlayer), - ), - SizedBox(width: 300, child: AddNewColumnCard(addPlayer)) - ] - ), - if (tlOnly) SizedBox( - width: 300+300*summaries.length.toDouble(), - child: Column( - children: [ - Row( - children: [ - SizedBox( - width: 300.0, - child: Card( - child: Column(children: [ - for (String title in TitesForStats[TitesForStats.keys.elementAt(1)]!) Text(title), - ]), - ), - ), - for (int k = 0; k < formattedValues[1].length; k++) SizedBox( - width: 300.0, - child: Card( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - for (int l = 0; l < formattedValues[1][k].length; l++) Container(decoration: (rawValues[1].length > 1 && rawValues[1][k][l] != null && best[1][l] == rawValues[1][k][l]) ? BoxDecoration(boxShadow: [BoxShadow(color: Colors.cyanAccent.withAlpha(96), spreadRadius: 0, blurRadius: 4)]) : null, child: formattedValues[1][k][l]), - ], + for (var p in players) SizedBox( + width: 300.0, + child: HeaderCard(p, removePlayer), + ), + SizedBox(width: 300, child: AddNewColumnCard(addPlayer)) + ] + ), + if (tlOnly) SizedBox( + width: 300+300*summaries.length.toDouble(), + child: Column( + children: [ + Row( + children: [ + SizedBox( + width: 300.0, + child: Card( + child: Column(children: [ + for (String title in TitesForStats[TitesForStats.keys.elementAt(1)]!) Text(title), + ]), ), ), - ), - ] - ), - VsGraphs(stats: [for (var s in summaries) if (s.league.nerdStats != null) AggregateStats.precalculated(s.league.apm!, s.league.pps!, s.league.vs!, s.league.nerdStats!, s.league.playstyle!)], nicknames: [for (int i = 0; i < summaries.length; i++) if (summaries[i].league.nerdStats != null) nicknames[i]]), - ], - ), - ) - else for (int i = 0; i < formattedValues.length; i++) SizedBox( - width: 300+300*summaries.length.toDouble(), - child: ExpansionTile( - title: Text(TitesForStats.keys.elementAt(i), style: _expansionTileTitleTextStyle), - children: [ - Row( - children: [ - SizedBox( - width: 300.0, - child: Card( - child: Column(children: [ - for (String title in TitesForStats[TitesForStats.keys.elementAt(i)]!) Text(title), - ]), - ), - ), - for (int k = 0; k < formattedValues[i].length; k++) SizedBox( - width: 300.0, - child: Card( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - for (int l = 0; l < formattedValues[i][k].length; l++) Container(decoration: (rawValues[0].length > 1 && rawValues[i][k][l] != null && best[i][l] == rawValues[i][k][l]) ? BoxDecoration(boxShadow: [BoxShadow(color: Colors.cyanAccent.withAlpha(96), spreadRadius: 0, blurRadius: 4)]) : null, child: formattedValues[i][k][l]), - ], + for (int k = 0; k < formattedValues[1].length; k++) SizedBox( + width: 300.0, + child: Card( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + for (int l = 0; l < formattedValues[1][k].length; l++) Container(decoration: (rawValues[1].length > 1 && rawValues[1][k][l] != null && best[1][l] == rawValues[1][k][l]) ? BoxDecoration(boxShadow: [BoxShadow(color: Colors.cyanAccent.withAlpha(96), spreadRadius: 0, blurRadius: 4)]) : null, child: formattedValues[1][k][l]), + ], + ), ), ), - ), - ] - ), - if (i == 1) VsGraphs(stats: [for (var s in summaries) if (s.league.nerdStats != null) AggregateStats.precalculated(s.league.apm!, s.league.pps!, s.league.vs!, s.league.nerdStats!, s.league.playstyle!)], nicknames: [for (int i = 0; i < summaries.length; i++) if (summaries[i].league.nerdStats != null) nicknames[i]]), - if (i == 2) VsGraphs(stats: [for (var s in summaries) if ((s.zenith != null || s.zenithCareerBest != null) && (s.zenith?.aggregateStats??s.zenithCareerBest!.aggregateStats).apm > 0.00) s.zenith?.aggregateStats??s.zenithCareerBest!.aggregateStats], nicknames: [for (int i = 0; i < summaries.length; i++) if ((summaries[i].zenith != null || summaries[i].zenithCareerBest != null) && (summaries[i].zenith?.aggregateStats??summaries[i].zenithCareerBest!.aggregateStats).apm > 0.00) nicknames[i]]), - if (i == 3) VsGraphs(stats: [for (var s in summaries) if ((s.zenithEx != null || s.zenithExCareerBest != null) && (s.zenithEx?.aggregateStats??s.zenithExCareerBest!.aggregateStats).apm > 0.00) s.zenithEx?.aggregateStats??s.zenithExCareerBest!.aggregateStats], nicknames: [for (int i = 0; i < summaries.length; i++) if ((summaries[i].zenithEx != null || summaries[i].zenithExCareerBest != null) && (summaries[i].zenithEx?.aggregateStats??summaries[i].zenithExCareerBest!.aggregateStats).apm > 0.00) nicknames[i]]), - ], + ] + ), + VsGraphs(stats: [for (var s in summaries) if (s.league.nerdStats != null) AggregateStats.precalculated(s.league.apm!, s.league.pps!, s.league.vs!, s.league.nerdStats!, s.league.playstyle!)], nicknames: [for (int i = 0; i < summaries.length; i++) if (summaries[i].league.nerdStats != null) nicknames[i]]), + ], + ), + ) + else for (int i = 0; i < formattedValues.length; i++) SizedBox( + width: 300+300*summaries.length.toDouble(), + child: ExpansionTile( + title: Text(TitesForStats.keys.elementAt(i), style: _expansionTileTitleTextStyle), + children: [ + Row( + children: [ + SizedBox( + width: 300.0, + child: Card( + child: Column(children: [ + for (String title in TitesForStats[TitesForStats.keys.elementAt(i)]!) Text(title), + ]), + ), + ), + for (int k = 0; k < formattedValues[i].length; k++) SizedBox( + width: 300.0, + child: Card( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + for (int l = 0; l < formattedValues[i][k].length; l++) Container(decoration: (rawValues[0].length > 1 && rawValues[i][k][l] != null && best[i][l] == rawValues[i][k][l]) ? BoxDecoration(boxShadow: [BoxShadow(color: Colors.cyanAccent.withAlpha(96), spreadRadius: 0, blurRadius: 4)]) : null, child: formattedValues[i][k][l]), + ], + ), + ), + ), + ] + ), + if (i == 1) VsGraphs(stats: [for (var s in summaries) if (s.league.nerdStats != null) AggregateStats.precalculated(s.league.apm!, s.league.pps!, s.league.vs!, s.league.nerdStats!, s.league.playstyle!)], nicknames: [for (int i = 0; i < summaries.length; i++) if (summaries[i].league.nerdStats != null) nicknames[i]]), + if (i == 2) VsGraphs(stats: [for (var s in summaries) if ((s.zenith != null || s.zenithCareerBest != null) && (s.zenith?.aggregateStats??s.zenithCareerBest!.aggregateStats).apm > 0.00) s.zenith?.aggregateStats??s.zenithCareerBest!.aggregateStats], nicknames: [for (int i = 0; i < summaries.length; i++) if ((summaries[i].zenith != null || summaries[i].zenithCareerBest != null) && (summaries[i].zenith?.aggregateStats??summaries[i].zenithCareerBest!.aggregateStats).apm > 0.00) nicknames[i]]), + if (i == 3) VsGraphs(stats: [for (var s in summaries) if ((s.zenithEx != null || s.zenithExCareerBest != null) && (s.zenithEx?.aggregateStats??s.zenithExCareerBest!.aggregateStats).apm > 0.00) s.zenithEx?.aggregateStats??s.zenithExCareerBest!.aggregateStats], nicknames: [for (int i = 0; i < summaries.length; i++) if ((summaries[i].zenithEx != null || summaries[i].zenithExCareerBest != null) && (summaries[i].zenithEx?.aggregateStats??summaries[i].zenithExCareerBest!.aggregateStats).apm > 0.00) nicknames[i]]), + ], + ), ), - ), - ]), - ], + ]), + ], + ), ), ), ), @@ -1001,7 +1017,7 @@ class HeaderCard extends StatelessWidget{ }} class AddNewColumnCard extends StatefulWidget{ - final Function addPlayer; + final Future Function(String) addPlayer; const AddNewColumnCard(this.addPlayer); @@ -1010,10 +1026,9 @@ class AddNewColumnCard extends StatefulWidget{ } class _AddNewColumnCardState extends State with SingleTickerProviderStateMixin { - // TODO: make spinner while awaiting for data - // TODO: show error if failed to retrieve data late AnimationController _animController; late Animation _anim; + bool showSpinner = false; @override void initState(){ @@ -1037,6 +1052,14 @@ class _AddNewColumnCardState extends State with SingleTickerPr super.dispose(); } + String getExcepitonText(Exception e){ + return switch(e.runtimeType){ + TetrioPlayerNotExist => t.errors.noSuchUser, + ConnectionIssue => t.errors.connection(code: (e as ConnectionIssue).code, message: e.message), + _ => e.toString() + }; + } + @override Widget build(BuildContext context) { return SizedBox( @@ -1055,11 +1078,28 @@ class _AddNewColumnCardState extends State with SingleTickerPr TextField( autofocus: true, onSubmitted: (value){ - widget.addPlayer(value); + widget.addPlayer(value).then((onValue){ + showSpinner = false; + setState(() { + if (onValue != null) ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(getExcepitonText(onValue)))); + }); + }); + setState(() { + showSpinner = true; + }); }, onTapOutside: (event) { setState((){_animController.animateBack(0);}); }, + ), + Padding( + padding: const EdgeInsets.only(top: 8.0), + child: Opacity( + opacity: showSpinner ? 1.0 : 0.0, + child: CircularProgressIndicator( + value: showSpinner ? null : 0.0, + ) + ), ) ], ), diff --git a/lib/views/destination_graphs.dart b/lib/views/destination_graphs.dart index 6c8825d..1a932e2 100644 --- a/lib/views/destination_graphs.dart +++ b/lib/views/destination_graphs.dart @@ -53,7 +53,8 @@ class _DestinationGraphsState extends State { List excludeRanks = []; late Future> futureLeague = getTetraLeagueData(_Xchart, Ychart); String searchLeague = ""; - //Duration postSeasonLeft = seasonStart.difference(DateTime.now()); + int? TLstatePlayers; + DateTime? TLrelevance; @override void initState(){ @@ -177,6 +178,8 @@ class _DestinationGraphsState extends State { Future> getTetraLeagueData(Stats x, Stats y) async { TetrioPlayersLeaderboard leaderboard = await teto.fetchTLLeaderboard(); + TLrelevance = leaderboard.timestamp; + TLstatePlayers = leaderboard.leaderboard.length; List<_MyScatterSpot> _spots = [ for (TetrioPlayerFromLeaderboard entry in leaderboard.leaderboard) if (excludeRanks.indexOf(entry.rank) == -1) _MyScatterSpot( @@ -359,6 +362,25 @@ class _DestinationGraphsState extends State { crossAxisAlignment: WrapCrossAlignment.center, alignment: WrapAlignment.center, children: [ + if (graph == Graph.leagueState && TLstatePlayers != null && TLrelevance != null) Row( + mainAxisSize: MainAxisSize.min, + children: [ + Padding( + padding: const EdgeInsets.only(left: 8.0), + child: RichText( + textAlign: TextAlign.right, + text: TextSpan( + style: TextStyle(color: Colors.white, fontFamily: "Eurostile Round"), + children: [ + TextSpan(text: t.stats.players(n: TLstatePlayers!)), + TextSpan(text: "\n"), + TextSpan(text: timestamp(TLrelevance!)) + ] + ) + ), + ) + ], + ), if (graph == Graph.history) Row( mainAxisSize: MainAxisSize.min, children: [ @@ -421,7 +443,10 @@ class _DestinationGraphsState extends State { setState(() { if (graph == Graph.history) _gamesPlayedInsteadOfDateAndTime = value! as bool; - else _Xchart = value! as Stats; + else{ + _Xchart = value! as Stats; + setState((){futureLeague = getTetraLeagueData(_Xchart, Ychart);}); + } }); } ), @@ -437,6 +462,7 @@ class _DestinationGraphsState extends State { onChanged: (value) { setState(() { Ychart = value!; + futureLeague = getTetraLeagueData(_Xchart, Ychart); }); } ), diff --git a/lib/views/destination_home.dart b/lib/views/destination_home.dart index fd04b1e..f2ab957 100644 --- a/lib/views/destination_home.dart +++ b/lib/views/destination_home.dart @@ -1,5 +1,5 @@ +import 'dart:async'; import 'dart:math'; - import 'package:flutter/material.dart'; import 'package:flutter_layout_grid/flutter_layout_grid.dart'; import 'package:flutter_markdown/flutter_markdown.dart'; @@ -238,28 +238,10 @@ class ZenithCard extends StatelessWidget { splitsCard(), ], ), - if (record != null) Card( - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - const Spacer(), - Text(t.nerdStats, style: Theme.of(context).textTheme.titleLarge), - const Spacer() - ], - ), - ), + if (record != null) Card(child: Center(child: Text(t.nerdStats, style: Theme.of(context).textTheme.titleLarge, textAlign: TextAlign.center))), if (record != null) NerdStatsThingy(nerdStats: record!.aggregateStats.nerdStats, width: width), if (record != null) Graphs(record!.aggregateStats.apm, record!.aggregateStats.pps, record!.aggregateStats.vs, record!.aggregateStats.nerdStats, record!.aggregateStats.playstyle), - if (achievements.isNotEmpty) Card( - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - const Spacer(), - Text(t.relatedAchievements, style: Theme.of(context).textTheme.titleLarge), - const Spacer() - ], - ), - ), + if (achievements.isNotEmpty) Card(child: Center(child: Text(t.relatedAchievements, style: Theme.of(context).textTheme.titleLarge, textAlign: TextAlign.center))), if (achievements.isNotEmpty) Wrap( direction: Axis.horizontal, children: [ @@ -290,7 +272,13 @@ class RecordCard extends StatelessWidget { mainAxisSize: MainAxisSize.min, children: [ if (closestAverage != null) Padding(padding: const EdgeInsets.only(right: 8.0), - child: Image.asset("res/tetrio_tl_alpha_ranks/${closestAverage!.key}.png", height: 96) + child: Tooltip(message: "${t.rankView.avgForRank(rank: closestAverage!.key.toUpperCase())}: ${ + switch(record!.gamemode){ + "40l" => get40lTime(closestAverage!.value.inMicroseconds), + "blitz" => NumberFormat.decimalPattern().format(closestAverage!.value), + _ => closestAverage!.value.toString() + } + }", child: Image.asset("res/tetrio_tl_alpha_ranks/${closestAverage!.key}.png", height: 96)) ), Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -327,7 +315,7 @@ class RecordCard extends StatelessWidget { if (record!.rank != -1) TextSpan(text: "№ ${intf.format(record!.rank)}", style: TextStyle(color: getColorOfRank(record!.rank))), if (record!.rank != -1) const TextSpan(text: " • "), if (record!.countryRank != -1) TextSpan(text: "№ ${intf.format(record!.countryRank)} ${t.localStanding}", style: TextStyle(color: getColorOfRank(record!.countryRank))), - if (record!.countryRank != -1) const TextSpan(text: " • "), + if (record!.countryRank != -1) TextSpan(text: width > 600.0 ? " • " : "\n"), TextSpan(text: timestamp(record!.timestamp)), ] ), @@ -505,7 +493,13 @@ class RecordSummary extends StatelessWidget{ mainAxisSize: MainAxisSize.min, children: [ if (closestAverage != null && record != null) Padding(padding: const EdgeInsets.only(right: 8.0), - child: Image.asset("res/tetrio_tl_alpha_ranks/${closestAverage!.key}.png", height: 96)) + child: Tooltip(message: "${t.rankView.avgForRank(rank: closestAverage!.key.toUpperCase())}: ${ + switch(record!.gamemode){ + "40l" => get40lTime(closestAverage!.value.inMicroseconds), + "blitz" => NumberFormat.decimalPattern().format(closestAverage!.value), + _ => closestAverage!.value.toString() + } + }", child: Image.asset("res/tetrio_tl_alpha_ranks/${closestAverage!.key}.png", height: 96))) else !hideRank ? Image.asset("res/tetrio_tl_alpha_ranks/z.png", height: 96) : Container(), if (record != null) Column( crossAxisAlignment: hideRank ? CrossAxisAlignment.center : CrossAxisAlignment.start, @@ -944,30 +938,10 @@ class _DestinationHomeState extends State with SingleTickerProv // ) // ), // ), - if (data.nerdStats != null) Card( - //surfaceTintColor: rankColors[data.rank], - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - const Spacer(), - Text(t.nerdStats, style: Theme.of(context).textTheme.titleLarge), - const Spacer() - ], - ), - ), + if (data.nerdStats != null) Card(child: Center(child: Text(t.nerdStats, style: Theme.of(context).textTheme.titleLarge, textAlign: TextAlign.center))), if (data.nerdStats != null) NerdStatsThingy(nerdStats: toSee.nerdStats!, oldNerdStats: toCompare?.nerdStats, averages: averages, lbPos: lbPos, width: width), if (data.nerdStats != null) Graphs(toSee.apm!, toSee.pps!, toSee.vs!, toSee.nerdStats!, toSee.playstyle!), - Card( - //surfaceTintColor: rankColors[data.rank], - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - const Spacer(), - Text(t.relatedAchievements, style: Theme.of(context).textTheme.titleLarge), - const Spacer() - ], - ), - ), + Card(child: Center(child: Text(t.relatedAchievements, style: Theme.of(context).textTheme.titleLarge, textAlign: TextAlign.center))), Wrap( direction: Axis.horizontal, children: [ @@ -1162,7 +1136,6 @@ class _DestinationHomeState extends State with SingleTickerProv @override initState(){ _transition = AnimationController(vsync: this, duration: Durations.long4); - _offsetAnimation = Tween( begin: Offset.zero, end: const Offset(1.5, 0.0), @@ -1170,7 +1143,6 @@ class _DestinationHomeState extends State with SingleTickerProv parent: _transition, curve: Curves.elasticIn, )); - super.initState(); } @@ -1404,7 +1376,7 @@ class _DestinationHomeState extends State with SingleTickerProv if (snapshot.data!.player!.role == "banned") FakeDistinguishmentThingy(banned: true) else if (snapshot.data!.player!.badstanding == true) FakeDistinguishmentThingy(badStanding: true), rigthCard(snapshot, sprintAchievements, blitzAchievements, tlAchievements, qpAchievements, qpExAchievements, width), - if (rightCard == Cards.overview) Card( + if (rightCard == Cards.overview && snapshot.data?.player?.bio != null) Card( child: Column( children: [ Row( diff --git a/lib/views/destination_info.dart b/lib/views/destination_info.dart index cf0015c..f1e3ae5 100644 --- a/lib/views/destination_info.dart +++ b/lib/views/destination_info.dart @@ -30,38 +30,19 @@ class InfoCard extends StatelessWidget { Widget build(BuildContext context) { return Card( clipBehavior: Clip.hardEdge, - child: viewportWidth > 768.0 ? SizedBox( - width: 450, - height: height, + child: SizedBox( + width: viewportWidth > 768.0 ? 450 : viewportWidth, + height: viewportWidth > 768.0 ? height : null, child: Column( + mainAxisSize: MainAxisSize.min, children: [ - Image.asset(assetLink, fit: BoxFit.cover, height: 300.0), - TextButton(child: Text(title, style: Theme.of(context).textTheme.titleLarge!.copyWith(decoration: TextDecoration.underline, decorationColor: Colors.white70, decorationStyle: TextDecorationStyle.dotted), textAlign: TextAlign.center), onPressed: onPressed), + Image.asset(assetLink, fit: BoxFit.cover, height: viewportWidth > 768.0 ? 300.0 : 150.0, width: viewportWidth > 768.0 ? null : viewportWidth), + TextButton(child: Text(title, style: (viewportWidth > 768.0 ? Theme.of(context).textTheme.titleLarge : Theme.of(context).textTheme.titleSmall)!.copyWith(decoration: TextDecoration.underline, decorationColor: Colors.white70, decorationStyle: TextDecorationStyle.dotted), textAlign: TextAlign.center), onPressed: onPressed), Padding( padding: const EdgeInsets.all(12.0), child: Text(description), ), - Spacer() - ], - ), - ) : SizedBox( - width: viewportWidth, - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Image.asset(assetLink, fit: BoxFit.cover, width: 200.0), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - TextButton(child: Text(title, style: Theme.of(context).textTheme.titleLarge!.copyWith(decoration: TextDecoration.underline, decorationColor: Colors.white70, decorationStyle: TextDecorationStyle.dotted, fontSize: 28), textAlign: TextAlign.center), onPressed: onPressed), - Padding( - padding: const EdgeInsets.all(12.0), - child: Text(description), - ), - ], - ), - ), + if (viewportWidth > 768.0) Spacer() ], ), ), @@ -75,7 +56,7 @@ class _DestinationInfo extends State { Widget build(BuildContext context) { List cards = [ InfoCard( - height: widget.constraints.maxHeight - 77, + height: widget.constraints.maxHeight, viewportWidth: widget.constraints.maxWidth, assetLink: "res/images/info card 1.png", title: t.infoDestination.sprintAndBlitzAverages, @@ -87,7 +68,7 @@ class _DestinationInfo extends State { } ), InfoCard( - height: widget.constraints.maxHeight - 77, + height: widget.constraints.maxHeight, viewportWidth: widget.constraints.maxWidth, assetLink: "res/images/info card 2.png", title: t.infoDestination.tetraStatsWiki, @@ -97,7 +78,7 @@ class _DestinationInfo extends State { } ), InfoCard( - height: widget.constraints.maxHeight - 77, + height: widget.constraints.maxHeight, viewportWidth: widget.constraints.maxWidth, assetLink: "res/images/info card 3.png", title: t.infoDestination.about, @@ -114,11 +95,14 @@ class _DestinationInfo extends State { mainAxisSize: MainAxisSize.min, children: [ Card( - child: Center(child: Text(t.infoDestination.title, style: Theme.of(context).textTheme.titleLarge)), + child: Center(child: Text(t.infoDestination.title, style: widget.constraints.maxWidth > 768.0 ? Theme.of(context).textTheme.titleLarge : Theme.of(context).textTheme.titleSmall!.copyWith(height: 1.1))), ), - SingleChildScrollView( - scrollDirection: Axis.horizontal, - child: widget.constraints.maxWidth > 768.0 ? Row(children: cards) : Column(children: cards), + SizedBox( + height: widget.constraints.maxWidth > 768.0 ? widget.constraints.maxHeight - 61 : widget.constraints.maxHeight - 170, + child: SingleChildScrollView( + scrollDirection: widget.constraints.maxWidth > 768.0 ? Axis.horizontal : Axis.vertical, + child: widget.constraints.maxWidth > 768.0 ? Row(children: cards) : Column(children: cards), + ), ) ], ); diff --git a/lib/views/destination_leaderboards.dart b/lib/views/destination_leaderboards.dart index 0196993..ff70180 100644 --- a/lib/views/destination_leaderboards.dart +++ b/lib/views/destination_leaderboards.dart @@ -3,12 +3,14 @@ import 'dart:math'; import 'package:flutter/material.dart'; import 'package:tetra_stats/data_objects/tetrio_constants.dart'; import 'package:tetra_stats/data_objects/tetrio_player_from_leaderboard.dart'; +import 'package:tetra_stats/data_objects/tetrio_players_leaderboard.dart'; import 'package:tetra_stats/gen/strings.g.dart'; import 'package:tetra_stats/main.dart'; import 'package:tetra_stats/utils/numers_formats.dart'; import 'package:tetra_stats/utils/relative_timestamps.dart'; import 'package:tetra_stats/views/user_view.dart'; import 'package:tetra_stats/widgets/future_error.dart'; +import 'package:tetra_stats/widgets/text_timestamp.dart'; class DestinationLeaderboards extends StatefulWidget{ final BoxConstraints constraints; @@ -59,6 +61,8 @@ class _DestinationLeaderboardsState extends State { List _stats = [for (MapEntry e in chartsShortTitles.entries) DropdownMenuEntry(value: e.key, label: e.value)]; String? _country; Stats stat = Stats.tr; + int? fullTLlbPlayers; + DateTime? fullTLlbTimestamp; bool? getTotalFilterValue(){ if (_excludeRanks.isEmpty) return true; @@ -74,24 +78,36 @@ class _DestinationLeaderboardsState extends State { try { _isFetchingData = true; setState(() {}); + TetrioPlayersLeaderboard? fullLB; + + if (_currentLb == Leaderboards.fullTL){ + fullLB = await teto.fetchTLLeaderboard(); + fullTLlbPlayers = fullLB.leaderboard.length; + fullTLlbTimestamp = fullLB.timestamp; + _reachedTheEnd = true; + } final items = switch(_currentLb){ Leaderboards.tl => await teto.fetchTetrioLeaderboard(prisecter: prisecter, country: _country), - Leaderboards.fullTL => (await teto.fetchTLLeaderboard()).getStatRankingFromLB(stat, country: _country??""), + Leaderboards.fullTL => fullLB!.getStatRankingFromLB(stat, country: _country??""), Leaderboards.xp => await teto.fetchTetrioLeaderboard(prisecter: prisecter, lb: "xp", country: _country), Leaderboards.ar => await teto.fetchTetrioLeaderboard(prisecter: prisecter, lb: "ar", country: _country), Leaderboards.sprint => await teto.fetchTetrioRecordsLeaderboard(prisecter: prisecter, country: _country), - Leaderboards.blitz => await teto.fetchTetrioRecordsLeaderboard(prisecter: prisecter, lb: "blitz_global", country: _country), - Leaderboards.zenith => await teto.fetchTetrioRecordsLeaderboard(prisecter: prisecter, lb: "zenith_global", country: _country), - Leaderboards.zenithex => await teto.fetchTetrioRecordsLeaderboard(prisecter: prisecter, lb: "zenithex_global", country: _country), + Leaderboards.blitz => await teto.fetchTetrioRecordsLeaderboard(prisecter: prisecter, lb: "blitz", country: _country), + Leaderboards.zenith => await teto.fetchTetrioRecordsLeaderboard(prisecter: prisecter, lb: "zenith", country: _country), + Leaderboards.zenithex => await teto.fetchTetrioRecordsLeaderboard(prisecter: prisecter, lb: "zenithex", country: _country), }; if (_currentLb == Leaderboards.fullTL && _excludeRanks.isNotEmpty) items.removeWhere((e) => _excludeRanks.indexOf((e as TetrioPlayerFromLeaderboard).rank) != -1); - if (_currentLb == Leaderboards.fullTL || items.isEmpty) _reachedTheEnd = true; + if (items.isEmpty) _reachedTheEnd = true; list.addAll((_reverse && _currentLb == Leaderboards.fullTL) ? items.reversed : items); - - _dataStreamController.add(list); - prisecter = list.last.prisecter.toString(); + if (items.isNotEmpty){ + _dataStreamController.add(list); + prisecter = list.last.prisecter.toString(); + } else{ + _dataStreamController.add([]); + } + } catch (e) { _dataStreamController.addError(e); } finally { @@ -179,8 +195,8 @@ class _DestinationLeaderboardsState extends State { ), if (_currentLb == Leaderboards.fullTL) IconButton( color: _excludeRanks.isNotEmpty ? Theme.of(context).colorScheme.primary : null, - onPressed: (){ - showDialog(context: context, builder: (BuildContext context) { + onPressed: () async { + await showDialog(context: context, builder: (BuildContext context) { return StatefulBuilder( builder: (context, StateSetter setAlertState) { return AlertDialog( @@ -216,11 +232,8 @@ class _DestinationLeaderboardsState extends State { actions: [ TextButton( child: const Text("Apply"), - onPressed: () {Navigator.of(context).pop(); setState((){ - _currentLb = Leaderboards.fullTL; - list.clear(); - prisecter = null; - _fetchData();}); + onPressed: () { + Navigator.of(context).pop(); } ) ] @@ -228,22 +241,30 @@ class _DestinationLeaderboardsState extends State { } ); }); + setState(() { + _currentLb = Leaderboards.fullTL; + list.clear(); + prisecter = null; + _isFetchingData = false; + _reachedTheEnd = false; + _fetchData(); + }); }, icon: Icon(Icons.filter_alt)), if (_currentLb == Leaderboards.fullTL) IconButton( color: _reverse ? Theme.of(context).colorScheme.primary : null, icon: Transform.rotate(angle: _reverse ? pi : 0.0, child: Icon(Icons.filter_list)), onPressed: (){ - setState((){ - _reverse = !_reverse; - _currentLb = Leaderboards.fullTL; - list.clear(); - prisecter = null; - _fetchData(); - }); + _reverse = !_reverse; + list.clear(); + prisecter = null; + _isFetchingData = false; + _reachedTheEnd = false; + _fetchData(); }, ) ], ), + if (_currentLb == Leaderboards.fullTL && fullTLlbPlayers != null && fullTLlbTimestamp != null) Text("${t.stats.players(n: fullTLlbPlayers!)} • ${t.sprintAndBlitsRelevance(date: timestamp(fullTLlbTimestamp!))}"), const Divider(), Expanded( child: ListView.builder( @@ -284,7 +305,7 @@ class _DestinationLeaderboardsState extends State { }, Leaderboards.xp => Text("LVL ${f2.format(snapshot.data![index].level)}", style: trailingStyle), Leaderboards.ar => Text("${intf.format(snapshot.data![index].ar)} AR", style: trailingStyle), - Leaderboards.sprint => Text(get40lTime(snapshot.data![index].stats.finalTime.inMicroseconds), style: trailingStyle), + Leaderboards.sprint => Text(getALittleBitMoreNormalTime(snapshot.data![index].stats.finalTime), style: trailingStyle), Leaderboards.blitz => Text(intf.format(snapshot.data![index].stats.score), style: trailingStyle), Leaderboards.zenith => Text("${f2.format(snapshot.data![index].stats.zenith!.altitude)} m", style: trailingStyle), Leaderboards.zenithex => Text("${f2.format(snapshot.data![index].stats.zenith!.altitude)} m", style: trailingStyle) @@ -297,7 +318,7 @@ class _DestinationLeaderboardsState extends State { }, Leaderboards.xp => "${f2.format(snapshot.data![index].xp)} XP${snapshot.data![index].playtime.isNegative ? "" : ", ${playtime(snapshot.data![index].playtime)} of gametime"}", Leaderboards.ar => "${snapshot.data![index].ar_counts}", - Leaderboards.sprint => "${intf.format(snapshot.data![index].stats.finesse.faults)} FF, ${f2.format(snapshot.data![index].stats.kpp)} KPP, ${f2.format(snapshot.data![index].stats.kps)} KPS, ${f2.format(snapshot.data![index].stats.pps)} PPS, ${intf.format(snapshot.data![index].stats.piecesPlaced)} P", + Leaderboards.sprint => "${snapshot.data?[index]?.stats?.finesse?.faults != null ? intf.format(snapshot.data![index].stats.finesse.faults) : "?"} FF, ${f2.format(snapshot.data![index].stats.kpp)} KPP, ${f2.format(snapshot.data![index].stats.kps)} KPS, ${f2.format(snapshot.data![index].stats.pps)} PPS, ${intf.format(snapshot.data![index].stats.piecesPlaced)} P", Leaderboards.blitz => "lvl ${snapshot.data![index].stats.level}, ${f2.format(snapshot.data![index].stats.pps)} PPS, ${f2.format(snapshot.data![index].stats.spp)} SPP", Leaderboards.zenith => "${f2.format(snapshot.data![index].aggregateStats.apm)} APM, ${f2.format(snapshot.data![index].aggregateStats.pps)} PPS, ${intf.format(snapshot.data![index].stats.kills)} KO's, ${f2.format(snapshot.data![index].stats.cps)} climb speed (${f2.format(snapshot.data![index].stats.zenith!.peakrank)} peak), ${intf.format(snapshot.data![index].stats.topBtB)} B2B", Leaderboards.zenithex => "${f2.format(snapshot.data![index].aggregateStats.apm)} APM, ${f2.format(snapshot.data![index].aggregateStats.pps)} PPS, ${intf.format(snapshot.data![index].stats.kills)} KO's, ${f2.format(snapshot.data![index].stats.cps)} climb speed (${f2.format(snapshot.data![index].stats.zenith!.peakrank)} peak), ${intf.format(snapshot.data![index].stats.topBtB)} B2B" diff --git a/lib/views/first_time_view.dart b/lib/views/first_time_view.dart index 67173d7..1d23a84 100644 --- a/lib/views/first_time_view.dart +++ b/lib/views/first_time_view.dart @@ -144,8 +144,8 @@ class _FirstTimeState extends State with SingleTickerProviderStat _animController.animateTo(0.9); setState((){ userSet = true; - title = "Nice to see you, ${nickname}"; - subtitle = "Let's take a look at your stats..."; + title = t.firstTimeView.niceToSeeYou(n: nickname); + subtitle = t.firstTimeView.letsTakeALook; }); Timer(Duration(seconds: 2), () => _animController.animateTo(1.0, duration: Duration(seconds: 1))); Timer(Duration(seconds: 3), () => context.replace("/")); diff --git a/lib/views/main_view.dart b/lib/views/main_view.dart index 42adc3b..3a7f629 100644 --- a/lib/views/main_view.dart +++ b/lib/views/main_view.dart @@ -34,57 +34,57 @@ int destination = 0; // - different design for radar graphs // - i should put tooltips everywhere Future getData(String searchFor, {bool withHistory = false}) async { - TetrioPlayer player; - try{ - if (searchFor.startsWith("ds:")){ - player = await teto.fetchPlayer(searchFor.substring(3), isItDiscordID: true); // we trying to get him with that - }else{ - player = await teto.fetchPlayer(searchFor); // Otherwise it's probably a user id or username - } - - }on TetrioPlayerNotExist{ - return FetchResults(false, null, [], null, null, null, null, null, false, TetrioPlayerNotExist()); + TetrioPlayer player; + try{ + if (searchFor.startsWith("ds:")){ + player = await teto.fetchPlayer(searchFor.substring(3), isItDiscordID: true); // we trying to get him with that + }else{ + player = await teto.fetchPlayer(searchFor); // Otherwise it's probably a user id or username } - late Summaries summaries; - late News? news; - late Cutoffs? cutoffs; - late CutoffsTetrio? averages; - try { - List requests = await Future.wait([ - teto.fetchSummaries(player.userId), - teto.fetchNews(player.userId), - teto.fetchCutoffsBeanserver(), - if (prefs.getBool("showAverages") ?? true) teto.fetchCutoffsTetrio() - ]); - - summaries = requests[0]; - news = requests[1]; - cutoffs = requests.elementAtOrNull(2); - averages = requests.elementAtOrNull(3); - - if(withHistory) await teto.fetchAndsaveTLHistory(player.userId, 1); // Retrieve if needed - } on Exception catch (e) { - return FetchResults(false, null, [], null, null, null, null, null, false, e); - } - PlayerLeaderboardPosition? _meAmongEveryone; - if (prefs.getBool("showPositions") == true){ - // Get tetra League leaderboard - _everyone = teto.getCachedLeaderboard(); - _everyone ??= await teto.fetchTLLeaderboard(); - if (_everyone!.leaderboard.isNotEmpty){ - _meAmongEveryone = await compute(_everyone!.getLeaderboardPosition, {player.userId: summaries.league}); - if (_meAmongEveryone != null) teto.cacheLeaderboardPositions(player.userId, _meAmongEveryone); - } - } - List states = await teto.getStates(player.userId, season: currentSeason); - - bool isTracking = await teto.isPlayerTracking(player.userId); - if (isTracking){ // if tracked - save data to local DB - await teto.storeState(summaries.league); - } - - return FetchResults(true, player, states.reversed.toList(), summaries, news, cutoffs, averages, _meAmongEveryone, isTracking, null); + + }on TetrioPlayerNotExist{ + return FetchResults(false, null, [], null, null, null, null, null, false, TetrioPlayerNotExist()); } + late Summaries summaries; + late News? news; + late Cutoffs? cutoffs; + late CutoffsTetrio? averages; + try { + List requests = await Future.wait([ + teto.fetchSummaries(player.userId), + teto.fetchNews(player.userId), + teto.fetchCutoffsBeanserver(), + if (prefs.getBool("showAverages") ?? true) teto.fetchCutoffsTetrio() + ]); + + summaries = requests[0]; + news = requests[1]; + cutoffs = requests.elementAtOrNull(2); + averages = requests.elementAtOrNull(3); + + if(withHistory) await teto.fetchAndsaveTLHistory(player.userId, 1); // Retrieve if needed + } on Exception catch (e) { + return FetchResults(false, null, [], null, null, null, null, null, false, e); + } + PlayerLeaderboardPosition? _meAmongEveryone; + if (prefs.getBool("showPositions") == true){ + // Get tetra League leaderboard + _everyone = teto.getCachedLeaderboard(); + _everyone ??= await teto.fetchTLLeaderboard(); + if (_everyone!.leaderboard.isNotEmpty){ + _meAmongEveryone = await compute(_everyone!.getLeaderboardPosition, {player.userId: summaries.league}); + if (_meAmongEveryone != null) teto.cacheLeaderboardPositions(player.userId, _meAmongEveryone); + } + } + List states = await teto.getStates(player.userId, season: currentSeason); + + bool isTracking = await teto.isPlayerTracking(player.userId); + if (isTracking){ // if tracked - save data to local DB + await teto.storeState(summaries.league); + } + + return FetchResults(true, player, states.reversed.toList(), summaries, news, cutoffs, averages, _meAmongEveryone, isTracking, null); +} class MainView extends StatefulWidget { final String? player; diff --git a/lib/views/rank_view.dart b/lib/views/rank_view.dart index 2664e2b..f544b65 100644 --- a/lib/views/rank_view.dart +++ b/lib/views/rank_view.dart @@ -34,74 +34,86 @@ class _RankState extends State { } Widget partOfTheWidget(List? data){ + double? avgAPM = data != null ? data[0].apm : widget.cutoffTetrio.apm; + double? avgPPS = data != null ? data[0].pps : widget.cutoffTetrio.pps; + double? avgVS = data != null ? data[0].vs : widget.cutoffTetrio.vs; + double? avgAPP = data != null ? data[1]["avgAPP"] : widget.cutoffTetrio.nerdStats?.app; + double? avgVSAPM = data != null ? data[1]["avgVSAPM"] : widget.cutoffTetrio.nerdStats?.vsapm; + double? avgDSS = data != null ? data[1]["avgDSS"] : widget.cutoffTetrio.nerdStats?.dss; + double? avgDSP = data != null ? data[1]["avgDSP"] : widget.cutoffTetrio.nerdStats?.dsp; + double? avgAPPDSP = data != null ? data[1]["avgAPPDSP"] : widget.cutoffTetrio.nerdStats?.appdsp; + double? avgCheese = data != null ? data[1]["avgCheese"] : widget.cutoffTetrio.nerdStats?.cheese; + double? avgGbE = data != null ? data[1]["avgGBE"] : widget.cutoffTetrio.nerdStats?.gbe; + double? avgNyaAPP = data != null ? data[1]["avgNyaAPP"] : widget.cutoffTetrio.nerdStats?.nyaapp; + double? avgArea = data != null ? data[1]["avgArea"] : widget.cutoffTetrio.nerdStats?.area; return Column( children: [ Divider(), Text(t.rankView.avgStats, style: Theme.of(context).textTheme.displayLarge), - Text("${f2.format(data != null ? data[0].apm : widget.cutoffTetrio.apm)} ${t.stats.apm.short} • ${f2.format(data != null ? data[0].pps : widget.cutoffTetrio.pps)} ${t.stats.pps.short} • ${f2.format(data != null ? data[0].vs : widget.cutoffTetrio.vs)} ${t.stats.vs.short}", style: Theme.of(context).textTheme.displayLarge), + Text("${avgAPM != null ? f2.format(avgAPM) : "-.--"} ${t.stats.apm.short} • ${avgPPS != null ? f2.format(avgPPS) : "-.--"} ${t.stats.pps.short} • ${avgVS != null ? f2.format(avgVS) : "-.--"} ${t.stats.vs.short}", style: Theme.of(context).textTheme.displayLarge), Divider(), Center(child: Text(t.rankView.avgNerdStats, style: Theme.of(context).textTheme.displayLarge)), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text(t.stats.app.full, style: Theme.of(context).textTheme.displayLarge), - Text(f3.format(data != null ? data[1]["avgAPP"] : widget.cutoffTetrio.nerdStats?.app), style: Theme.of(context).textTheme.displayLarge) + Text(avgAPP != null ? f3.format(avgAPP) : "-.---", style: Theme.of(context).textTheme.displayLarge) ], ), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text(t.stats.vsapm.full, style: Theme.of(context).textTheme.displayLarge), - Text(f3.format(data != null ? data[1]["avgVSAPM"] : widget.cutoffTetrio.nerdStats?.vsapm), style: Theme.of(context).textTheme.displayLarge) + Text(avgVSAPM != null ? f3.format(avgVSAPM) : "-.---", style: Theme.of(context).textTheme.displayLarge) ], ), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text(t.stats.dss.full, style: Theme.of(context).textTheme.displayLarge), - Text(f3.format(data != null ? data[1]["avgDSS"] : widget.cutoffTetrio.nerdStats?.dss), style: Theme.of(context).textTheme.displayLarge) + Text(avgDSS != null ? f3.format(avgDSS) : "-.---", style: Theme.of(context).textTheme.displayLarge) ], ), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text(t.stats.dsp.full, style: Theme.of(context).textTheme.displayLarge), - Text(f3.format(data != null ? data[1]["avgDSP"] : widget.cutoffTetrio.nerdStats?.dsp), style: Theme.of(context).textTheme.displayLarge) + Text(avgDSP != null ? f3.format(avgDSP) : "-.---", style: Theme.of(context).textTheme.displayLarge) ], ), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text(t.stats.appdsp.full, style: Theme.of(context).textTheme.displayLarge), - Text(f3.format(data != null ? data[1]["avgAPPDSP"] : widget.cutoffTetrio.nerdStats?.appdsp), style: Theme.of(context).textTheme.displayLarge) + Text(avgAPPDSP != null ? f3.format(avgAPPDSP) : "-.---", style: Theme.of(context).textTheme.displayLarge) ], ), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text(t.stats.cheese.full, style: Theme.of(context).textTheme.displayLarge), - Text(f2.format(data != null ? data[1]["avgCheese"] : widget.cutoffTetrio.nerdStats?.cheese), style: Theme.of(context).textTheme.displayLarge) + Text(avgCheese != null ? f3.format(avgCheese) : "--.--", style: Theme.of(context).textTheme.displayLarge) ], ), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text(t.stats.gbe.full, style: Theme.of(context).textTheme.displayLarge), - Text(f3.format(data != null ? data[1]["avgGBE"] : widget.cutoffTetrio.nerdStats?.gbe), style: Theme.of(context).textTheme.displayLarge) + Text(avgGbE != null ? f3.format(avgGbE) : "-.---", style: Theme.of(context).textTheme.displayLarge) ], ), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text(t.stats.nyaapp.full, style: Theme.of(context).textTheme.displayLarge), - Text(f3.format(data != null ? data[1]["avgNyaAPP"] : widget.cutoffTetrio.nerdStats?.nyaapp), style: Theme.of(context).textTheme.displayLarge) + Text(avgNyaAPP != null ? f3.format(avgNyaAPP) : "-.---", style: Theme.of(context).textTheme.displayLarge) ], ), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text(t.stats.area.full, style: Theme.of(context).textTheme.displayLarge), - Text(f1.format(data != null ? data[1]["avgArea"] : widget.cutoffTetrio.nerdStats?.area), style: Theme.of(context).textTheme.displayLarge) + Text(avgArea != null ? f3.format(avgArea) : "---.-", style: Theme.of(context).textTheme.displayLarge) ], ), ], diff --git a/lib/widgets/badges_thingy.dart b/lib/widgets/badges_thingy.dart index 32b8486..34b2eb5 100644 --- a/lib/widgets/badges_thingy.dart +++ b/lib/widgets/badges_thingy.dart @@ -2,6 +2,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart' hide Badge; import 'package:tetra_stats/data_objects/badge.dart'; import 'package:tetra_stats/gen/strings.g.dart'; +import 'package:tetra_stats/services/tetrio_crud.dart' show webVersionDomain; import 'package:tetra_stats/utils/numers_formats.dart'; import 'package:tetra_stats/widgets/text_timestamp.dart'; @@ -45,7 +46,12 @@ class BadgesThingy extends StatelessWidget{ crossAxisAlignment: WrapCrossAlignment.center, spacing: 25, children: [ - Image.asset("res/tetrio_badges/${badge.badgeId}.png"), + Image.network( + kIsWeb ? "https://${webVersionDomain}/oskware_bridge.php?endpoint=TetrioBadge&badge=${badge.badgeId}" : "https://tetr.io/res/badges/${badge.badgeId}.png", + errorBuilder:(context, error, stackTrace) { + return ErrorWidget(error); + } + ), Text(badge.ts != null ? t.obtainDate(date: timestamp(badge.ts!)) : t.assignedManualy), @@ -65,20 +71,14 @@ class BadgesThingy extends StatelessWidget{ ); }, ), - tooltip: badge.label, - icon: Image.asset( - "res/tetrio_badges/${badge.badgeId}.png", - height: 32, - errorBuilder: (context, error, stackTrace) { - return Image.network( - kIsWeb ? "https://ts.dan63.by/oskware_bridge.php?endpoint=TetrioBadge&badge=${badge.badgeId}" : "https://tetr.io/res/badges/${badge.badgeId}.png", - height: 32, - errorBuilder:(context, error, stackTrace) { - return Image.asset("res/icons/kagari.png", height: 32, width: 32); - } - ); - }, - ) + tooltip: badge.label, + icon: Image.network( + kIsWeb ? "https://${webVersionDomain}/oskware_bridge.php?endpoint=TetrioBadge&badge=${badge.badgeId}" : "https://tetr.io/res/badges/${badge.badgeId}.png", + height: 32, + errorBuilder:(context, error, stackTrace) { + return Image.asset("res/icons/kagari.png", height: 32, width: 32); + } + ) ) ], ), diff --git a/lib/widgets/news_thingy.dart b/lib/widgets/news_thingy.dart index ee32ebf..bc7db58 100644 --- a/lib/widgets/news_thingy.dart +++ b/lib/widgets/news_thingy.dart @@ -1,8 +1,10 @@ +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'package:tetra_stats/data_objects/news.dart'; import 'package:tetra_stats/data_objects/news_entry.dart'; import 'package:tetra_stats/gen/strings.g.dart'; +import 'package:tetra_stats/services/tetrio_crud.dart' show webVersionDomain; import 'package:tetra_stats/utils/numers_formats.dart'; import 'package:tetra_stats/utils/relative_timestamps.dart'; import 'package:tetra_stats/widgets/text_timestamp.dart'; @@ -73,14 +75,14 @@ class NewsThingy extends StatelessWidget{ ) ), subtitle: Text(timestamp(news.timestamp)), - leading: Image.asset( - "res/tetrio_badges/${news.data["type"]}.png", + leading: Image.network( + kIsWeb ? "https://${webVersionDomain}/oskware_bridge.php?endpoint=TetrioBadge&badge=${news.data["type"]}" : "https://tetr.io/res/badges/${news.data["type"]}.png", height: 48, width: 48, - errorBuilder: (context, error, stackTrace) { - return Image.asset("res/icons/kagari.png", height: 64, width: 64); - }, - ), + errorBuilder:(context, error, stackTrace) { + return Image.asset("res/icons/kagari.png", height: 32, width: 32); + } + ) ); case "rankup": return ListTile( diff --git a/lib/widgets/tl_records_thingy.dart b/lib/widgets/tl_records_thingy.dart index a0f738e..26e33ab 100644 --- a/lib/widgets/tl_records_thingy.dart +++ b/lib/widgets/tl_records_thingy.dart @@ -1,37 +1,116 @@ +import 'dart:async'; + import 'package:flutter/material.dart'; import 'package:tetra_stats/data_objects/beta_record.dart'; import 'package:tetra_stats/main.dart'; import 'package:tetra_stats/widgets/beta_league_entry_thingy.dart'; import 'package:tetra_stats/widgets/future_error.dart'; -class TLRecords extends StatelessWidget { +class TLRecords extends StatefulWidget { final String userID; /// Widget, that displays Tetra League records. /// Accepts list of TL records ([data]) and [userID] of player from the view const TLRecords(this.userID); + @override + State createState() => _TLRecordsState(); +} + +class _TLRecordsState extends State { + List records = []; + bool isFetchingRecords = false; + bool reachedEndOfRecords = false; + final StreamController> _recordsStreamController = StreamController>.broadcast(); + Stream> get recordsStream => _recordsStreamController.stream; + String? recordsPrisecter; + late final ScrollController _scrollController; + + @override + void initState(){ + _scrollController = ScrollController(); + _scrollController.addListener(() { + _scrollController.addListener(() { + final maxScroll = _scrollController.position.maxScrollExtent; + final currentScroll = _scrollController.position.pixels; + + if (currentScroll == maxScroll) { + _fetchRecord(widget.userID); + } + }); + }); + _fetchRecord(widget.userID); + super.initState(); + } + + Future _fetchRecord(String userID) async { + if (isFetchingRecords || reachedEndOfRecords) { + // Avoid fetching new data while already fetching + return; + } + try { + isFetchingRecords = true; + + final items = (await teto.fetchTLStream(userID, prisecter: recordsPrisecter)).records; + + if (items.isEmpty) reachedEndOfRecords = true; + records.addAll(items); + if (items.isNotEmpty){ + _recordsStreamController.add(records); + recordsPrisecter = records.last.prisecter.toString(); + } else{ + _recordsStreamController.add([]); + } + } catch (e) { + _recordsStreamController.addError(e); + } finally { + // Set to false when data fetching is complete + isFetchingRecords = false; + } +} + + Future resetRecords(String userID) async { + records.clear(); + recordsPrisecter = null; + reachedEndOfRecords = false; + _fetchRecord(userID); + } + @override Widget build(BuildContext context) { - return FutureBuilder( - future: teto.fetchTLStream(userID), + return StreamBuilder( + stream: recordsStream, builder: (context, snapshot) { switch (snapshot.connectionState){ case ConnectionState.none: case ConnectionState.waiting: - case ConnectionState.active: return const Center(child: CircularProgressIndicator()); + case ConnectionState.active: case ConnectionState.done: if (snapshot.hasData){ - return Column( - children: [ - for (BetaRecord record in snapshot.data!.records) BetaLeagueEntryThingy(record, userID) - ], + return SizedBox( + height: MediaQuery.of(context).size.height - 130, + child: ListView.builder( + controller: _scrollController, + itemCount: records.length, + prototypeItem: Padding( + padding: const EdgeInsets.only(top: 4.0), + child: ListTile( + leading: Text("0"), + title: Text("ehhh...", style: TextStyle(fontSize: 22)), + trailing: SizedBox(height: 36, width: 1), + subtitle: const Text("eh\n...", style: TextStyle(color: Colors.grey, fontSize: 12)), + ), + ), + itemBuilder: (BuildContext context, int index){ + return BetaLeagueEntryThingy(records[index], widget.userID); + } + ), ); } if (snapshot.hasError){ return SizedBox(height: 500, child: Center(child: FutureError(snapshot))); } + return const Center(child: Text("whar?")); } - return const Text("what?"); }, ); } diff --git a/lib/widgets/user_thingy.dart b/lib/widgets/user_thingy.dart index f1ccbba..9362cef 100644 --- a/lib/widgets/user_thingy.dart +++ b/lib/widgets/user_thingy.dart @@ -1,9 +1,9 @@ +import 'dart:io'; import 'dart:math'; import 'dart:ui' as ui; import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart' show Size; import 'package:intl/intl.dart'; import 'package:syncfusion_flutter_gauges/gauges.dart'; import 'package:tetra_stats/data_objects/tetrio_constants.dart'; @@ -201,7 +201,7 @@ class _UserThingyState extends State with SingleTickerProviderStateM ), ), Positioned( - top: widget.player.bannerRevision != null ? 160.0 : 80.0, + top: (kIsWeb || !Platform.isAndroid) ? widget.player.bannerRevision != null ? 160.0 : 80.0 : widget.player.bannerRevision != null ? 152.0 : 72.0, left: 160.0, child: Row( children: [ diff --git a/lib/widgets/zenith_thingy.dart b/lib/widgets/zenith_thingy.dart index ba9b267..55231bb 100644 --- a/lib/widgets/zenith_thingy.dart +++ b/lib/widgets/zenith_thingy.dart @@ -83,15 +83,21 @@ class ZenithThingy extends StatelessWidget{ if (zenith!.rank != -1) TextSpan(text: "№ ${intf.format(zenith!.rank)}", style: TextStyle(color: getColorOfRank(zenith!.rank))), if (zenith!.rank != -1) const TextSpan(text: " • "), if (zenith!.countryRank != -1) TextSpan(text: "№ ${intf.format(zenith!.countryRank)} local", style: TextStyle(color: getColorOfRank(zenith!.countryRank))), - if (zenith!.countryRank != -1) const TextSpan(text: " • "), + if (zenith!.countryRank != -1) TextSpan(text: width > 400.0 ? " • " : "\n"), TextSpan(text: timestamp(zenith!.timestamp)), ] ), ), ], ), - if (zenith != null && (zenith!.extras as ZenithExtras).mods.isNotEmpty) Container(width: 16.0), - if (zenith != null && (zenith!.extras as ZenithExtras).mods.isNotEmpty) for (String mod in (zenith!.extras as ZenithExtras).mods) Image.asset("res/icons/${mod}.png", height: 64.0) + if (zenith != null && (zenith!.extras as ZenithExtras).mods.isNotEmpty && width > 600.0) Container(width: 16.0), + if (zenith != null && (zenith!.extras as ZenithExtras).mods.isNotEmpty && width > 600.0) for (String mod in (zenith!.extras as ZenithExtras).mods) Image.asset("res/icons/${mod}.png", height: 64.0) + ], + ), + if (zenith != null && (zenith!.extras as ZenithExtras).mods.isNotEmpty && width <= 600.0) Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + for (String mod in (zenith!.extras as ZenithExtras).mods) Image.asset("res/icons/${mod}.png", height: 32.0) ], ), if (zenith != null) Row( diff --git a/pubspec.yaml b/pubspec.yaml index 7f7bc40..483f070 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: tetra_stats description: Track your and other player stats in TETR.IO publish_to: 'none' -version: 2.0.0-beta2+40 +version: 2.0.0+41 environment: sdk: '>=3.0.0' @@ -88,7 +88,6 @@ flutter: - res/avatars/ - res/icons/ - res/tetrio_tl_alpha_ranks/ - - res/tetrio_badges/ - res/images/ # An image asset can refer to one or more resolution-specific "variants", see diff --git a/res/i18n/strings_ru-RU.i18n.json b/res/i18n/strings_ru-RU.i18n.json index 7ac7536..1acb9aa 100644 --- a/res/i18n/strings_ru-RU.i18n.json +++ b/res/i18n/strings_ru-RU.i18n.json @@ -188,7 +188,7 @@ "relevance": "на момент $timestamp", "actual": "Требование", "target": "Цель", - "cutoffTR": "Требуемый TR", + "cutoffTR": "Треб. TR", "targetTR": "Целевой TR", "state": "Состояние", "advanced": "Продвинутая", diff --git a/res/i18n/strings_zh-CN.i18n.json b/res/i18n/strings_zh-CN.i18n.json new file mode 100644 index 0000000..2728f4f --- /dev/null +++ b/res/i18n/strings_zh-CN.i18n.json @@ -0,0 +1,929 @@ +{ + "locales(map)": { + "en": "英语 (English)", + "ru-RU": "俄语 (Русский)", + "zh-CN": "简体中文" + }, + "gamemodes(map)": { + "league": "Tetra 联赛", + "zenith": "快速游戏", + "zenithex": "快速游戏 · 专家模式", + "40l": "40行竞速", + "blitz": "闪电战", + "5mblast": "5,000,000 Blast", + "zen": "禅意模式" + }, + "destinations": { + "home": "主页", + "graphs": "图表", + "leaderboards": "排行榜", + "cutoffs": "段位分界线", + "calc": "计算器", + "info": "信息中心", + "data": "已保存的数据", + "settings": "设置" + }, + "playerRole(map)": { + "user": "用户", + "banned": "已封禁", + "bot": "机器人", + "sysop": "系统操作员", + "admin": "管理员", + "mod": "管理员", + "halfmod": "社区管理员", + "anon": "匿名用户" + }, + "goBackButton": "返回", + "nanow": "目前不可用...", + "seasonEnds": "当前赛季还有${countdown}结束", + "seasonEnded": "赛季已结束", + "overallPB": "生涯最佳:$pb m", + "gamesUntilRanked": "还有${left}局才可获得段位", + "numOfVictories": "~${wins}次胜局", + "promotionOnNextWin": "下一场胜局即可升段", + "numOfdefeats": "~${losses}次负局", + "demotionOnNextLoss": "下一场负局即可掉段", + "records": "记录", + "nerdStats": "详细信息", + "playstyles": "游戏方式", + "horoscopes": "散点图", + "relatedAchievements": "相关成就", + "season": "赛季", + "smooth": "平滑", + "dateAndTime": "日期和时间:", + "TLfullLBnote": "很大,但允许你通过玩家的数据对玩家进行排序,还可以按段位筛选玩家", + "rank": "段位", + "verdictGeneral": "比 $rank 段平均数据$n $verdict", + "verdictBetter": "好", + "verdictWorse": "差", + "localStanding": "本地", + "xp": { + "title": "经验等级", + "progressToNextLevel": "到下一等级的进度:$percentage", + "progressTowardsGoal": "从0级到$goal级的进度:$percentage (还差 $left 点经验值)" + }, + "gametime": { + "title": "精确游戏时长", + "gametimeAday": "平均每天$gametime", + "breakdown": "相当于 $years 年,\n$months 月,\n$days 天,\n$minutes 分钟,\n$seconds 秒" + }, + "track": "跟踪", + "stopTracking": "停止跟踪", + "supporter": "${tier}级会员", + "comparingWith": "${newDate}的数据与${oldDate}相比", + "compare": "比较", + "comparison": "比较", + "enterUsername": "输入用户名或者\\$avgX (X是一个段位)", + "general": "常规", + "badges": "勋章", + "obtainDate": "于${date}获得", + "assignedManualy": "此徽章由TETR.IO管理员手动颁发", + "distinguishment": "区别", + "banned": "已封禁", + "bannedSubtext": "由于 TETR.IO 规则或服务条款被违反 而被封禁", + "badStanding": "信誉不佳", + "badStandingSubtext": "近期有一次或多次违禁行为", + "botAccount": "机器人账号", + "botAccountSubtext": "由$botMaintainers管理", + "copiedToClipboard": "已复制到剪贴板!", + "bio": "个性签名", + "news": "新闻", + "matchResult": { + "victory": "胜利", + "defeat": "失败", + "tie": "平局", + "dqvictory": "对手被取消资格", + "dqdefeat": "被取消资格", + "nocontest": "无竞赛记录", + "nullified": "竞赛记录已取消" + }, + "distinguishments": { + "noHeader": "缺少标题", + "noFooter": "缺少标题", + "twc": "TETR.IO 世界冠军", + "twcYear": "$year TETR.IO 世界杯" + }, + "newsEntries": { + "leaderboard(rich)": "在$gametype中荣获第$rank名", + "personalbest(rich)": "在$gametype中取得新纪录:$pb", + "badge(rich)": "获得勋章 $badge", + "rankup(rich)": "升 $rank", + "supporter(rich)": "成为${s(TETR.IO supporter)}", + "supporter_gift(rich)": "被赠送${s(TETR.IO supporter)}", + "unknown(rich)": "未知新闻类型 $type" + }, + "rankupMiddle": "${r} 段", + "copyUserID": "点击以复制用户 ID", + "searchHint": "用户名或 ID", + "navMenu": "导航菜单", + "navMenuTooltip": "打开导航菜单", + "refresh": "刷新数据", + "searchButton": "搜索", + "trackedPlayers": "跟踪的玩家", + "standing": "名次", + "previousSeasons": "上赛季", + "recent": "最近", + "top": "前", + "noRecord": "暂无记录", + "sprintAndBlitsRelevance": "${date}", + "snackBarMessages": { + "stateRemoved": "成功移除${date}时的状态!", + "matchRemoved": "成功移除${date}时的一局!", + "notForWeb": "此功能在网络版本中不可用", + "importSuccess": "导入成功", + "importCancelled": "导入已取消" + }, + "errors": { + "noRecords": "暂无记录", + "notEnoughData": "数据不足", + "noHistorySaved": "没有保存历史记录", + "connection": "连接错误:${code} ${message}", + "noSuchUser": "用户不存在", + "noSuchUserSub": "您输入的内容有误,或者用户不存在", + "discordNotAssigned": "没有指定Discord ID的用户", + "discordNotAssignedSub": "请确保您提供了有效的 ID", + "history": "缺少该玩家的历史", + "actionSuggestion": "也许,您想要", + "p1nkl0bst3rTLmatches": "没有找到Tetra联赛比赛", + "clientException": "你尚未连接", + "forbidden": "您的 IP 地址已被封禁", + "forbiddenSub": "如果你在使用VPN,请关闭。如果仍然不可以,请联系$nickname", + "tooManyRequests": "您的评分已经被限制", + "tooManyRequestsSub": "请稍后重试", + "internal": "TETR.IO 出现了问题!", + "internalSub": "osk,或许,已经知道了", + "internalWebVersion": "TETR.IO (也许是oskware_bridge,我不知道到底是哪儿) 出现了问题!", + "internalWebVersionSub": "如果 osk status 页面显示一切都很正常,请联系dan63047", + "oskwareBridge": "oskware_bridge 出现了问题!", + "oskwareBridgeSub": "请联系dan63047", + "p1nkl0bst3rForbidden": "第三方API屏蔽了您的 IP 地址", + "p1nkl0bst3rTooManyRequests": "第三方API请求过多,请稍后再试", + "p1nkl0bst3rinternal": "p1nkl0bst3r 那边出现了问题!", + "p1nkl0bst3rinternalWebVersion": "p1nkl0bst3r 那边(也许是oskware_bridge,我不知道到底是哪儿)出现了问题!", + "replayAlreadySaved": "回放已保存", + "replayExpired": "回放已过期或不再可用", + "replayRejected": "第三方API屏蔽了您的 IP 地址" + }, + "actions": { + "cancel": "取消", + "submit": "确定", + "ok": "确定", + "apply": "应用", + "refresh": "刷新" + }, + "graphsDestination": { + "fetchAndsaveTLHistory": "获取玩家历史", + "fetchAndSaveOldTLmatches": "获取 Tetra 联赛历史记录", + "fetchAndsaveTLHistoryResult": "找到 ${number} 个状态", + "fetchAndSaveOldTLmatchesResult": "找到 ${number} 场比赛", + "gamesPlayed": "游玩次数:$games", + "dateAndTime": "日期和时间", + "filterModaleTitle": "在图表上筛选等级" + }, + "filterModale": { + "all": "全部" + }, + "cutoffsDestination": { + "title": "Tetra 联赛 状态", + "relevance": "$timestamp", + "actual": "实际", + "target": "目标", + "cutoffTR": "分段 TR", + "targetTR": "目标 TR", + "state": "状态", + "advanced": "高级选项", + "players": "玩家($n)", + "moreInfo": "更多信息", + "NumberOne": "№ 1 is $tr TR", + "inflated": "高于目标 $tr", + "notInflated": "不偏高", + "deflated": "低于目标 $tr", + "notDeflated": "不偏低", + "wellDotDotDot": "嗯…", + "fromPlace": "自 № $n", + "viewButton": "查看" + }, + "rankView": { + "rankTitle": "$rank 段数据", + "everyoneTitle": "全部排行榜", + "trRange": "TR 范围", + "supposedToBe": "应为", + "gap": "相差 $value", + "trGap": "相差 $value TR", + "deflationGap": "偏低量", + "inflationGap": "偏高量", + "LBposRange": "排行榜位置范围", + "overpopulated": "比期望的多 $players", + "underpopulated": "比期望的少 $players", + "PlayersEqualSupposedToBe": "符合", + "avgStats": "平均数据", + "avgForRank": "$rank 段平均数据", + "avgNerdStats": "平均详细信息", + "minimums": "最小值", + "maximums": "最大值" + }, + "stateView": { + "title": "$date的状态" + }, + "tlMatchView": { + "match": "匹配", + "vs": "vs", + "winner": "获胜者", + "roundNumber": "第$n回合", + "statsFor": "状态", + "numberOfRounds": "回合数", + "matchLength": "比赛时长", + "roundLength": "回合时长", + "matchStats": "比赛数据", + "downloadReplay": "下载 .ttrm 回放", + "openReplay": "在 TETR.IO 中打开回放" + }, + "calcDestination": { + "placeholders": "输入你的$stat", + "tip": "输入值并按 \"计算\" 来查看TA的详细信息", + "statsCalcButton": "计算", + "damageCalcTip": "点击左侧的操作在此添加", + "actions": "操作", + "results": "结果", + "rules": "规则", + "noSpinClears": "非 Spin 清除", + "spins": "Spin", + "miniSpins": "Mini spin", + "noLineclear": "无清除(连消结束)", + "custom": "自定义", + "multiplier": "倍增", + "pcDamage": "全消伤害", + "comboTable": "连击", + "b2bChaining": "B2B增伤", + "surgeStartAtB2B": "开始于B2B", + "surgeStartAmount": "初始值", + "totalDamage": "累计伤害", + "lineclears": "清除行数", + "combo": "连击", + "surge": "B2B充能", + "pcs": "全消" + }, + "infoDestination": { + "title": "信息中心", + "sprintAndBlitzAverages": "40 行 & 闪电战平均数据", + "sprintAndBlitzAveragesDescription": "计算40 行 & 闪电战平均数据是个很繁琐的过程,所以很久才会更新一次。 点击标题查看完整的 40 行 & 闪电战平均数据表", + "tetraStatsWiki": "Tetra Stats Wiki", + "tetraStatsWikiDescription": "查看更多关于Tetra Stats提供的功能和数据", + "about": "关于 Tetra Stats", + "aboutDescription": "由 dan63 开发" + }, + "leaderboardsDestination": { + "title": "排行榜", + "tl": "Tetra 联赛(当前赛季)", + "fullTL": "Tetra 联赛(当前赛季,完整)", + "ar": "成就点" + }, + "savedDataDestination": { + "title": "已保存的数据", + "tip": "选择左边的昵称以查看与之相关的数据", + "seasonTLstates": "第$s赛季状态", + "TLrecords": "联赛记录" + }, + "settingsDestination": { + "title": "设置", + "general": "常规", + "customization": "自定义设置", + "database": "本地数据库", + "checking": "正在检查...", + "enterToSubmit": "按回车键提交", + "account": "您的 TETR.IO 账号", + "accountDescription": "该玩家的状态将在启动此应用后立即加载。 默认情况下,它会加载我的数据。如要更改,请在此输入您的昵称。", + "done": "完成!", + "noSuchAccount": "账号不存在", + "language": "语言", + "languageDescription": "Tetra Stats 有$languages。默认情况下,应用程序将选择您的系统语言,如果您的系统区域设置不可用,则选择英语。", + "languages(plural)": { + "zero": "0种语言", + "one": "$n种语言", + "two": "$n种语言", + "few": "$n种语言", + "many": "$n种语言", + "other": "$n种语言" + }, + "updateInTheBackground": "后台更新数据", + "updateInTheBackgroundDescription": "如果开启,Tetra Stats将尝试在缓存过期后查询新信息。通常一次/5分钟。", + "compareStats": "将TL数据与段位平均水平作比较", + "compareStatsDescription": "如果开启,Tetra Stats将提供额外的量度,使您能够将自己与普通玩家的等级相比较。 你看到它的方式——统计信息将以相应的颜色高亮,用光标悬停在它们上面以获取更多信息。", + "showPosition": "显示排行榜中的位置", + "showPositionDescription": "这可能需要一些时间(和流量),但您可以看到您在排行榜上的位置,按数据排序", + "accentColor": "主题色", + "accentColorDescription": "这种颜色会在这个应用上可见,而且通常会高亮显示交互界面元素。", + "accentColorModale": "选取主题色", + "timestamps": "时间戳格式", + "timestampsDescriptionPart1": "您可以选择时间戳显示时间的方式。默认情况下,它们以 GMT 时区显示时间,并根据所选区域设置进行格式设置,例如:$d。", + "timestampsDescriptionPart2": "这里还有:\n• 以您的时区设置的区域设置:$y\n• 相对时间戳:$r", + "timestampsAbsoluteGMT": "GMT", + "timestampsAbsoluteLocalTime": "您的时区", + "timestampsRelative": "相对", + "sheetbotLikeGraphs": "Sheetbot 型雷达图", + "sheetbotLikeGraphsDescription": "尽管我认为,图表在 SheetBot 中的工作方式不是很正确,有些人感到困惑,那 -0.5 Stride 看起来不像它在 SheetBot 图表上那样。因此,我们这里有:如果开启,则如果数值为负,则图形上的点可以出现在图形的另一半。", + "oskKagariGimmick": "Osk-Kagari", + "oskKagariGimmickDescription": "如果开启,osk的段位会显示为:kagari:", + "bytesOfDataStored": "存储数据", + "TLrecordsSaved": "已保存 Tetra 联赛记录", + "TLplayerstatesSaved": "已保存 Tetra 联赛玩家状态", + "fixButton": "修复", + "compressButton": "压缩", + "exportDB": "导出本地数据库", + "desktopExportAlertTitle": "桌面导出", + "desktopExportText": "看起来您在桌面上使用了这个应用程序。请检查您的文档文件夹,您应该找到\"TetraStats.db\"。请将其复制到某处", + "androidExportAlertTitle": "Android 导出", + "androidExportText": "已导出。\n${exportedDB}", + "importDB": "导入本地数据库", + "importDBDescription": "还原您的备份。请注意已存储的数据库将被覆盖。", + "importWrongFileType": "文件类型错误!" + }, + "homeNavigation": { + "overview": "概览", + "standing": "名次", + "seasons": "赛季", + "mathces": "比赛场次", + "pb": "个人最佳", + "normal": "普通模式", + "expert": "专家模式", + "expertRecords": "专家模式记录" + }, + "graphsNavigation": { + "history": "玩家历史记录", + "league": "联赛状态", + "cutoffs": "分段线历史" + }, + "calcNavigation": { + "stats": "数据计算器", + "damage": "伤害计算器" + }, + "firstTimeView": { + "welcome": "欢迎使用 Tetra Stats", + "description": "服务,允许您跟踪TETR.IO的各种数据", + "nicknameQuestion": "您的昵称是?", + "inpuntHint": "在此处输入... (3-16个符号)", + "emptyInputError": "不能提交空字符串", + "niceToSeeYou": "很高兴见到你,$n", + "letsTakeALook": "让我们看看您的统计资料...", + "skip": "跳过" + }, + "aboutView": { + "title": "关于 Tetra Stats", + "about": "Tetra Stats是一种服务,与TETR.IO Tetra Channel API共用,提供数据并根据这种数据计算一些附加度量。 服务允许用户用\"Track\"功能跟踪他们在Tetra League中的进度,这个功能记录每个Tetra Leage更改到本地数据库(非自动) 您必须不时地访问服务。这样,这些更改可以通过图表来查看。\n\nBeanserver blaster 是Tetra Stats的一部分,它被拆解成服务器侧脚本。 它提供完整的Tetra League排行榜,允许Tetra Stats通过任何公式对排行榜进行排序并生成散点图,这允许用户分析Tetra联赛趋势。 它还提供了Tetra League 的评分历史,用户也可以通过图表看到。\n\n我们有一个添加回放分析和锦标赛历史记录的计划,所以随时关注!\n\n服务没有与TETR.IO与osk以任何身份关联。", + "appVersion": "版本", + "build": "$build", + "GHrepo": "GitHub Repository", + "submitAnIssue": "提交问题", + "credits": "鸣谢", + "authorAndDeveloper": "作者 & 开发者", + "providedFormulas": "提供的公式", + "providedS1history": "提供的 S1 历史", + "inoue": "Inoue (回放抓取器)", + "zhCNlocale": "简中翻译员", + "supportHim": "为他提供支持!" + }, + "stats": { + "registrationDate": "注册时间", + "gametime": "游玩时长", + "ogp": "在线游戏次数", + "ogw": "在线游戏胜利次数", + "followers": "粉丝", + "xp": { + "short": "经验值", + "full": "经验点" + }, + "tr": { + "short": "TR", + "full": "Tetra 评分" + }, + "glicko": { + "short": "Glicko", + "full": "Glicko" + }, + "rd": { + "short": "RD", + "full": "评分偏差" + }, + "glixare": { + "short": "GXE", + "full": "GLIXARE" + }, + "s1tr": { + "short": "S1 TR", + "full": "第 1 赛季式 TR" + }, + "gp": { + "short": "GP", + "full": "总场数" + }, + "gw": { + "short": "GW", + "full": "胜场数" + }, + "winrate": { + "short": "WR%", + "full": "胜率" + }, + "apm": { + "short": "APM", + "full": "每分钟攻击数" + }, + "pps": { + "short": "PPS", + "full": "每秒块数" + }, + "vs": { + "short": "VS", + "full": "VS 分数" + }, + "app": { + "short": "APP", + "full": "每块攻击数" + }, + "vsapm": { + "short": "VS/APM", + "full": "VS / APM" + }, + "dss": { + "short": "DS/S", + "full": "每秒挖掘数" + }, + "dsp": { + "short": "DS/P", + "full": "每块挖掘数" + }, + "appdsp": { + "short": "APP+DSP", + "full": "APP + DSP" + }, + "cheese": { + "short": "CI", + "full": "垃圾行混乱指数" + }, + "gbe": { + "short": "GbE", + "full": "垃圾行效率" + }, + "nyaapp": { + "short": "wAPP", + "full": "加权APP" + }, + "area": { + "short": "面积", + "full": "面积" + }, + "etr": { + "short": "eTR", + "full": "预测 TR" + }, + "etracc": { + "short": "±eTR", + "full": "预测实际差量" + }, + "opener": { + "short": "定式", + "full": "定式" + }, + "plonk": { + "short": "太极", + "full": "太极" + }, + "stride": { + "short": "速度", + "full": "速度" + }, + "infds": { + "short": "挖掘", + "full": "挖掘" + }, + "altitude": { + "short": "m", + "full": "高度" + }, + "climbSpeed": { + "short": "CSP", + "full": "爬行速度", + "gaugetTitle": "爬行速度" + }, + "peakClimbSpeed": { + "short": "最高CSP", + "full": "最高爬行速度", + "gaugetTitle": "最高" + }, + "kos": { + "short": "KO's", + "full": "击杀" + }, + "b2b": { + "short": "B2B", + "full": "背靠背/满贯" + }, + "finesse": { + "short": "极", + "full": "极简率", + "widgetTitle": "简率" + }, + "finesseFaults": { + "short": "非极简", + "full": "非极简操作数" + }, + "totalTime": { + "short": "时长", + "full": "总时长", + "widgetTitle": "总时长" + }, + "level": { + "short": "Lvl", + "full": "等级" + }, + "pieces": { + "short": "P", + "full": "块" + }, + "spp": { + "short": "SPP", + "full": "每块得分" + }, + "kp": { + "short": "KP", + "full": "按键" + }, + "kpp": { + "short": "KPP", + "full": "每块按键数" + }, + "kps": { + "short": "KPS", + "full": "每秒按键数" + }, + "blitzScore": "$p 分", + "levelUpRequirement": "还需 $p 升到下一级", + "piecesTotal": "放块总数", + "piecesWithPerfectFinesse": "极简块数", + "score": "分数", + "lines": "行数", + "linesShort": "行", + "pcs": "全消数", + "holds": "暂存数", + "spike": "最高暴击", + "top": "前 $percentage", + "topRank": "最高段位:$rank", + "floor": "层", + "split": "拆分", + "total": "总计", + "sent": "已发送", + "received": "已接收", + "placement": "排名", + "peak": "最高", + "qpWithMods(plural)": { + "one": "使用 1 个模组", + "two": "使用 $n 个模组", + "few": "使用 $n 个模组", + "many": "使用 $n 个模组", + "other": "使用 $n 个模组" + }, + "inputs(plural)": { + "zero": "$n 按键", + "one": "$n 按键", + "two": "$n 按键", + "few": "$n 按键", + "many": "$n 按键", + "other": "$n 按键" + }, + "tspinsTotal(plural)": { + "zero": "总共 $n 次T旋", + "one": "总共 $n 次T旋", + "two": "总共 $n 次T旋", + "few": "总共 $n 次T旋", + "many": "总共 $n 次T旋", + "other": "总共 $n 次T旋" + }, + "linesCleared(plural)": { + "zero": "总共消除 $n 行", + "one": "总共消除 $n 行", + "two": "总共消除 $n 行", + "few": "总共消除 $n 行", + "many": "总共消除 $n 行", + "other": "总共消除 $n 行" + }, + "graphs": { + "attack": "攻击", + "speed": "速度", + "defense": "防御", + "cheese": "奶酪层" + }, + "players(plural)": { + "zero": "$n 名玩家", + "one": "$n 名玩家", + "two": "$n 名玩家", + "few": "$n 名玩家", + "many": "$n 名玩家", + "other": "$n 名玩家" + }, + "games(plural)": { + "zero": "$n 次游戏", + "one": "$n 次游戏", + "two": "$n 次游戏", + "few": "$n 次游戏", + "many": "$n 次游戏", + "other": "$n 次游戏" + }, + "lineClear": { + "single": "Single", + "double": "Double", + "triple": "Triple", + "quad": "Quad", + "penta": "Penta", + "hexa": "Hexa", + "hepta": "Hepta", + "octa": "Octa", + "ennea": "Ennea", + "deca": "Deca", + "hendeca": "Hendeca", + "dodeca": "Dodeca", + "triadeca": "Triadeca", + "tessaradeca": "Tessaradeca", + "pentedeca": "Pentedeca", + "hexadeca": "Hexadeca", + "heptadeca": "Heptadeca", + "octadeca": "Octadeca", + "enneadeca": "Enneadeca", + "eicosa": "Eicosa", + "kagaris": "Kagaris" + }, + "lineClears": { + "zero": "Zeros", + "single": "Singles", + "double": "Doubles", + "triple": "Triples", + "quad": "Quads", + "penta": "Pentas" + }, + "mini": "Mini", + "tSpin": "T-spin", + "tSpins": "T-spins", + "spin": "Spin", + "spins": "Spins" + }, + "countries(map)": { + "": "全球", + "AF": "阿富汗", + "AX": "奥兰群岛", + "AL": "阿尔巴尼亚", + "DZ": "阿尔及利亚", + "AS": "美属萨摩亚", + "AD": "安道尔", + "AO": "安哥拉", + "AI": "安圭拉", + "AQ": "南极洲", + "AG": "安提瓜和巴布达", + "AR": "阿根廷", + "AM": "亚美尼亚", + "AW": "阿鲁巴", + "AU": "澳大利亚", + "AT": "奥地利", + "AZ": "阿塞拜疆", + "BS": "巴哈马", + "BH": "巴林", + "BD": "孟加拉国", + "BB": "巴巴多斯", + "BY": "白俄罗斯", + "BE": "比利时", + "BZ": "伯利兹", + "BJ": "贝宁", + "BM": "百慕大", + "BT": "不丹", + "BO": "玻利维亚", + "BA": "波黑", + "BW": "博茨瓦纳", + "BV": "布韦岛", + "BR": "巴西", + "IO": "英属印度洋领地", + "BN": "文莱", + "BG": "保加利亚", + "BF": "布基纳法索", + "BI": "布隆迪", + "KH": "柬埔寨", + "CM": "喀麦隆", + "CA": "加拿大", + "CV": "佛得角", + "BQ": "荷兰加勒比区", + "KY": "开曼群岛", + "CF": "中非", + "TD": "乍得", + "CL": "智利", + "CN": "中国", + "CX": "圣诞岛", + "CC": "科科斯群岛", + "CO": "哥伦比亚", + "KM": "科摩罗", + "CG": "刚果(布)", + "CD": "刚果(金)", + "CK": "库克群岛", + "CR": "哥斯达黎加", + "CI": "科特迪瓦", + "HR": "克罗地亚", + "CU": "古巴", + "CW": "库拉索", + "CY": "塞浦路斯", + "CZ": "捷克", + "DK": "丹麦", + "DJ": "吉布提", + "DM": "多米尼克", + "DO": "多米尼加共和国", + "EC": "厄瓜多尔", + "EG": "埃及", + "SV": "萨尔瓦多", + "GB-ENG": "英格兰", + "GQ": "赤道几内亚", + "ER": "厄立特里亚", + "EE": "爱沙尼亚", + "ET": "埃塞俄比亚", + "EU": "欧洲", + "FK": "福克兰群岛 (马尔维纳斯)", + "FO": "法罗群岛", + "FJ": "斐济", + "FI": "芬兰", + "FR": "法国", + "GF": "法属圭亚那", + "PF": "法属波利尼西亚", + "TF": "法属南部领地", + "GA": "加蓬", + "GM": "冈比亚", + "GE": "格鲁吉亚", + "DE": "德国", + "GH": "加纳", + "GI": "直布罗陀", + "GR": "希腊", + "GL": "格陵兰", + "GD": "格林纳达", + "GP": "瓜德罗普", + "GU": "关岛", + "GT": "危地马拉", + "GG": "根西", + "GN": "几内亚", + "GW": "几内亚比绍", + "GY": "圭亚那", + "HT": "海地", + "HM": "赫德岛和麦克唐纳群岛", + "VA": "梵蒂冈", + "HN": "洪都拉斯", + "HK": "中国香港", + "HU": "匈牙利", + "IS": "冰岛", + "IN": "印度", + "ID": "印尼", + "IR": "伊朗", + "IQ": "伊拉克", + "IE": "爱尔兰", + "IM": "马恩岛", + "IL": "以色列", + "IT": "意大利", + "JM": "牙买加", + "JP": "日本", + "JE": "泽西", + "JO": "约旦", + "KZ": "哈萨克斯坦", + "KE": "肯尼亚", + "KI": "基里巴斯", + "KP": "朝鲜", + "KR": "韩国", + "XK": "科索沃", + "KW": "科威特", + "KG": "吉尔吉斯斯坦", + "LA": "老挝", + "LV": "拉托维亚", + "LB": "黎巴嫩", + "LS": "莱索托", + "LR": "利比里亚", + "LY": "利比亚", + "LI": "列支敦士登", + "LT": "立陶宛", + "LU": "卢森堡", + "MO": "中国澳门", + "MK": "马其顿", + "MG": "马达加斯加", + "MW": "马拉维", + "MY": "马来西亚", + "MV": "马尔代夫", + "ML": "马里", + "MT": "马耳他", + "MH": "马绍尔群岛", + "MQ": "马提尼克", + "MR": "毛里塔尼亚", + "MU": "毛里求斯", + "YT": "马约特岛", + "MX": "墨西哥", + "FM": "密克罗尼西亚", + "MD": "摩尔多瓦", + "MC": "摩纳哥", + "ME": "黑山", + "MA": "摩洛哥", + "MN": "蒙古", + "MS": "蒙特塞拉特", + "MZ": "莫桑比克", + "MM": "缅甸", + "NA": "纳米比亚", + "NR": "瑙鲁", + "NP": "尼泊尔", + "NL": "荷兰", + "AN": "荷属安地列斯", + "NC": "新喀里多尼亚", + "NZ": "新西兰", + "NI": "尼加拉瓜", + "NE": "尼日尔", + "NG": "尼日利亚", + "NU": "纽埃", + "NF": "诺福克岛", + "GB-NIR": "北爱尔兰", + "MP": "北马里亚纳群岛", + "NO": "挪威", + "OM": "阿曼", + "PK": "巴基斯坦", + "PW": "帕劳", + "PS": "巴勒斯坦", + "PA": "巴拿马", + "PG": "巴布亚新几内亚", + "PY": "巴拉圭", + "PE": "秘鲁", + "PH": "菲律宾", + "PN": "皮特凯恩", + "PL": "波兰", + "PT": "葡萄牙", + "PR": "波多黎哥", + "QA": "卡塔尔", + "RE": "留尼汪", + "RO": "罗马尼亚", + "RU": "俄罗斯", + "RW": "卢旺达", + "BL": "圣巴泰勒米", + "SH": "圣赫勒拿-阿森松-特里斯坦达库尼亚", + "KN": "圣基茨和尼维斯", + "LC": "圣卢西亚", + "MF": "圣马丁", + "PM": "圣皮埃尔和密克隆", + "VC": "圣文森特和格林纳丁斯", + "WS": "萨摩亚", + "SM": "圣马利诺", + "ST": "圣多美和普林西比", + "SA": "沙特阿拉伯", + "GB-SCT": "苏格兰", + "SN": "塞内加尔", + "RS": "塞尔维亚", + "SC": "塞舌尔", + "SL": "塞拉利昂", + "SG": "新加坡", + "SX": "荷属圣马丁", + "SK": "斯洛伐克", + "SI": "斯洛文尼亚", + "SB": "所罗门", + "SO": "索马里", + "ZA": "南非", + "GS": "南乔治亚和南桑威奇", + "SS": "南苏丹", + "ES": "西班牙", + "LK": "斯里兰卡", + "SD": "苏丹", + "SR": "苏里南", + "SJ": "斯瓦尔巴和扬马延", + "SZ": "斯威士兰", + "SE": "瑞典", + "CH": "瑞士", + "SY": "叙利亚", + "TW": "中国台湾", + "TJ": "塔吉克斯坦", + "TZ": "坦桑尼亚", + "TH": "泰国", + "TL": "东帝汶", + "TG": "多哥", + "TK": "托克劳", + "TO": "汤加", + "TT": "特立尼达和多巴哥", + "TN": "突尼斯", + "TR": "土耳其", + "TM": "土库曼斯坦", + "TC": "特克斯河凯科斯群岛", + "TV": "图瓦卢", + "UG": "乌干达", + "UA": "乌克兰", + "AE": "阿联酋", + "GB": "英国", + "US": "美国", + "UY": "乌拉圭", + "UM": "美国本土外小岛屿", + "UZ": "乌兹别克斯坦", + "VU": "瓦努阿图", + "VE": "委内瑞拉", + "VN": "越南", + "VG": "英属维京群岛", + "VI": "美属维京群岛", + "GB-WLS": "威尔士", + "WF": "瓦利斯群岛和富图纳群岛", + "EH": "西撒哈拉", + "YE": "也门", + "ZM": "赞比亚", + "ZW": "津巴布韦", + "XX": "未知", + "XM": "月球" + } +} diff --git a/res/tetrio_badges/100player.png b/res/tetrio_badges/100player.png deleted file mode 100644 index f219375..0000000 Binary files a/res/tetrio_badges/100player.png and /dev/null differ diff --git a/res/tetrio_badges/20tsd.png b/res/tetrio_badges/20tsd.png deleted file mode 100644 index dcbdf82..0000000 Binary files a/res/tetrio_badges/20tsd.png and /dev/null differ diff --git a/res/tetrio_badges/5mblast_1.png b/res/tetrio_badges/5mblast_1.png deleted file mode 100644 index 6abd717..0000000 Binary files a/res/tetrio_badges/5mblast_1.png and /dev/null differ diff --git a/res/tetrio_badges/5mblast_10.png b/res/tetrio_badges/5mblast_10.png deleted file mode 100644 index 6e454d9..0000000 Binary files a/res/tetrio_badges/5mblast_10.png and /dev/null differ diff --git a/res/tetrio_badges/5mblast_100.png b/res/tetrio_badges/5mblast_100.png deleted file mode 100644 index 8911ee1..0000000 Binary files a/res/tetrio_badges/5mblast_100.png and /dev/null differ diff --git a/res/tetrio_badges/5mblast_1000.png b/res/tetrio_badges/5mblast_1000.png deleted file mode 100644 index 557e296..0000000 Binary files a/res/tetrio_badges/5mblast_1000.png and /dev/null differ diff --git a/res/tetrio_badges/allclear.png b/res/tetrio_badges/allclear.png deleted file mode 100644 index 36965fd..0000000 Binary files a/res/tetrio_badges/allclear.png and /dev/null differ diff --git a/res/tetrio_badges/bugbounty.png b/res/tetrio_badges/bugbounty.png deleted file mode 100644 index 5412404..0000000 Binary files a/res/tetrio_badges/bugbounty.png and /dev/null differ diff --git a/res/tetrio_badges/cometopen_1.png b/res/tetrio_badges/cometopen_1.png deleted file mode 100644 index c127ec0..0000000 Binary files a/res/tetrio_badges/cometopen_1.png and /dev/null differ diff --git a/res/tetrio_badges/cometopen_2.png b/res/tetrio_badges/cometopen_2.png deleted file mode 100644 index ea8d3a1..0000000 Binary files a/res/tetrio_badges/cometopen_2.png and /dev/null differ diff --git a/res/tetrio_badges/cometopen_3.png b/res/tetrio_badges/cometopen_3.png deleted file mode 100644 index 523db1b..0000000 Binary files a/res/tetrio_badges/cometopen_3.png and /dev/null differ diff --git a/res/tetrio_badges/early-supporter.png b/res/tetrio_badges/early-supporter.png deleted file mode 100644 index 0eee60c..0000000 Binary files a/res/tetrio_badges/early-supporter.png and /dev/null differ diff --git a/res/tetrio_badges/founder.png b/res/tetrio_badges/founder.png deleted file mode 100644 index 33fce01..0000000 Binary files a/res/tetrio_badges/founder.png and /dev/null differ diff --git a/res/tetrio_badges/galactic2x2_1.png b/res/tetrio_badges/galactic2x2_1.png deleted file mode 100644 index c0d27d6..0000000 Binary files a/res/tetrio_badges/galactic2x2_1.png and /dev/null differ diff --git a/res/tetrio_badges/galactic2x2_2.png b/res/tetrio_badges/galactic2x2_2.png deleted file mode 100644 index 167cace..0000000 Binary files a/res/tetrio_badges/galactic2x2_2.png and /dev/null differ diff --git a/res/tetrio_badges/galactic2x2_3.png b/res/tetrio_badges/galactic2x2_3.png deleted file mode 100644 index 77df947..0000000 Binary files a/res/tetrio_badges/galactic2x2_3.png and /dev/null differ diff --git a/res/tetrio_badges/ggc_1.png b/res/tetrio_badges/ggc_1.png deleted file mode 100644 index 26f25a5..0000000 Binary files a/res/tetrio_badges/ggc_1.png and /dev/null differ diff --git a/res/tetrio_badges/ggc_2.png b/res/tetrio_badges/ggc_2.png deleted file mode 100644 index 2759375..0000000 Binary files a/res/tetrio_badges/ggc_2.png and /dev/null differ diff --git a/res/tetrio_badges/ggc_3.png b/res/tetrio_badges/ggc_3.png deleted file mode 100644 index 3b29b97..0000000 Binary files a/res/tetrio_badges/ggc_3.png and /dev/null differ diff --git a/res/tetrio_badges/hdoxii_1.png b/res/tetrio_badges/hdoxii_1.png deleted file mode 100644 index 0379f17..0000000 Binary files a/res/tetrio_badges/hdoxii_1.png and /dev/null differ diff --git a/res/tetrio_badges/hdoxii_2.png b/res/tetrio_badges/hdoxii_2.png deleted file mode 100644 index 7511e5a..0000000 Binary files a/res/tetrio_badges/hdoxii_2.png and /dev/null differ diff --git a/res/tetrio_badges/hdoxii_3.png b/res/tetrio_badges/hdoxii_3.png deleted file mode 100644 index 5b692ab..0000000 Binary files a/res/tetrio_badges/hdoxii_3.png and /dev/null differ diff --git a/res/tetrio_badges/heart.png b/res/tetrio_badges/heart.png deleted file mode 100644 index acc47ea..0000000 Binary files a/res/tetrio_badges/heart.png and /dev/null differ diff --git a/res/tetrio_badges/hnprism_1.png b/res/tetrio_badges/hnprism_1.png deleted file mode 100644 index 4e03c19..0000000 Binary files a/res/tetrio_badges/hnprism_1.png and /dev/null differ diff --git a/res/tetrio_badges/hnprism_2.png b/res/tetrio_badges/hnprism_2.png deleted file mode 100644 index b3fda4e..0000000 Binary files a/res/tetrio_badges/hnprism_2.png and /dev/null differ diff --git a/res/tetrio_badges/hnprism_3.png b/res/tetrio_badges/hnprism_3.png deleted file mode 100644 index 5caa0e2..0000000 Binary files a/res/tetrio_badges/hnprism_3.png and /dev/null differ diff --git a/res/tetrio_badges/hnstratosphere50_1.png b/res/tetrio_badges/hnstratosphere50_1.png deleted file mode 100644 index 21b0ea7..0000000 Binary files a/res/tetrio_badges/hnstratosphere50_1.png and /dev/null differ diff --git a/res/tetrio_badges/hnstratosphere50_2.png b/res/tetrio_badges/hnstratosphere50_2.png deleted file mode 100644 index 8928550..0000000 Binary files a/res/tetrio_badges/hnstratosphere50_2.png and /dev/null differ diff --git a/res/tetrio_badges/hnstratosphere50_3.png b/res/tetrio_badges/hnstratosphere50_3.png deleted file mode 100644 index d251ebf..0000000 Binary files a/res/tetrio_badges/hnstratosphere50_3.png and /dev/null differ diff --git a/res/tetrio_badges/ift_1.png b/res/tetrio_badges/ift_1.png deleted file mode 100644 index 6a27df8..0000000 Binary files a/res/tetrio_badges/ift_1.png and /dev/null differ diff --git a/res/tetrio_badges/ift_2.png b/res/tetrio_badges/ift_2.png deleted file mode 100644 index 97d85ab..0000000 Binary files a/res/tetrio_badges/ift_2.png and /dev/null differ diff --git a/res/tetrio_badges/ift_3.png b/res/tetrio_badges/ift_3.png deleted file mode 100644 index 51213b9..0000000 Binary files a/res/tetrio_badges/ift_3.png and /dev/null differ diff --git a/res/tetrio_badges/infdev.png b/res/tetrio_badges/infdev.png deleted file mode 100644 index c94e78d..0000000 Binary files a/res/tetrio_badges/infdev.png and /dev/null differ diff --git a/res/tetrio_badges/kod_by_founder.png b/res/tetrio_badges/kod_by_founder.png deleted file mode 100644 index a4aad3b..0000000 Binary files a/res/tetrio_badges/kod_by_founder.png and /dev/null differ diff --git a/res/tetrio_badges/kod_founder.png b/res/tetrio_badges/kod_founder.png deleted file mode 100644 index 06aa918..0000000 Binary files a/res/tetrio_badges/kod_founder.png and /dev/null differ diff --git a/res/tetrio_badges/leaderboard1.png b/res/tetrio_badges/leaderboard1.png deleted file mode 100644 index 6264876..0000000 Binary files a/res/tetrio_badges/leaderboard1.png and /dev/null differ diff --git a/res/tetrio_badges/mmc_tabi_1.png b/res/tetrio_badges/mmc_tabi_1.png deleted file mode 100644 index 45e343f..0000000 Binary files a/res/tetrio_badges/mmc_tabi_1.png and /dev/null differ diff --git a/res/tetrio_badges/mmc_tabi_2.png b/res/tetrio_badges/mmc_tabi_2.png deleted file mode 100644 index d0e8e50..0000000 Binary files a/res/tetrio_badges/mmc_tabi_2.png and /dev/null differ diff --git a/res/tetrio_badges/mmc_tabi_3.png b/res/tetrio_badges/mmc_tabi_3.png deleted file mode 100644 index 5f64b43..0000000 Binary files a/res/tetrio_badges/mmc_tabi_3.png and /dev/null differ diff --git a/res/tetrio_badges/mmc_tabi_superlobby.png b/res/tetrio_badges/mmc_tabi_superlobby.png deleted file mode 100644 index 1b4fef2..0000000 Binary files a/res/tetrio_badges/mmc_tabi_superlobby.png and /dev/null differ diff --git a/res/tetrio_badges/mmc_tabi_superlobby2.png b/res/tetrio_badges/mmc_tabi_superlobby2.png deleted file mode 100644 index 72a5810..0000000 Binary files a/res/tetrio_badges/mmc_tabi_superlobby2.png and /dev/null differ diff --git a/res/tetrio_badges/mmc_tabi_superlobby3.png b/res/tetrio_badges/mmc_tabi_superlobby3.png deleted file mode 100644 index 3cd35a0..0000000 Binary files a/res/tetrio_badges/mmc_tabi_superlobby3.png and /dev/null differ diff --git a/res/tetrio_badges/mmc_tabi_superlobby4.png b/res/tetrio_badges/mmc_tabi_superlobby4.png deleted file mode 100644 index 08d6401..0000000 Binary files a/res/tetrio_badges/mmc_tabi_superlobby4.png and /dev/null differ diff --git a/res/tetrio_badges/mmc_tabi_superlobby5.png b/res/tetrio_badges/mmc_tabi_superlobby5.png deleted file mode 100644 index edf07dd..0000000 Binary files a/res/tetrio_badges/mmc_tabi_superlobby5.png and /dev/null differ diff --git a/res/tetrio_badges/mts_1.png b/res/tetrio_badges/mts_1.png deleted file mode 100644 index 0cf0b28..0000000 Binary files a/res/tetrio_badges/mts_1.png and /dev/null differ diff --git a/res/tetrio_badges/mts_2.png b/res/tetrio_badges/mts_2.png deleted file mode 100644 index eb704f8..0000000 Binary files a/res/tetrio_badges/mts_2.png and /dev/null differ diff --git a/res/tetrio_badges/mts_3.png b/res/tetrio_badges/mts_3.png deleted file mode 100644 index 8f706f3..0000000 Binary files a/res/tetrio_badges/mts_3.png and /dev/null differ diff --git a/res/tetrio_badges/mts_participation.png b/res/tetrio_badges/mts_participation.png deleted file mode 100644 index b48f401..0000000 Binary files a/res/tetrio_badges/mts_participation.png and /dev/null differ diff --git a/res/tetrio_badges/pkrescueroyale_1.png b/res/tetrio_badges/pkrescueroyale_1.png deleted file mode 100644 index 0e06c80..0000000 Binary files a/res/tetrio_badges/pkrescueroyale_1.png and /dev/null differ diff --git a/res/tetrio_badges/pkrescueroyale_2.png b/res/tetrio_badges/pkrescueroyale_2.png deleted file mode 100644 index 432fe8c..0000000 Binary files a/res/tetrio_badges/pkrescueroyale_2.png and /dev/null differ diff --git a/res/tetrio_badges/pkrescueroyale_3.png b/res/tetrio_badges/pkrescueroyale_3.png deleted file mode 100644 index 8290a96..0000000 Binary files a/res/tetrio_badges/pkrescueroyale_3.png and /dev/null differ diff --git a/res/tetrio_badges/pkstarcup_1.png b/res/tetrio_badges/pkstarcup_1.png deleted file mode 100644 index f91cb97..0000000 Binary files a/res/tetrio_badges/pkstarcup_1.png and /dev/null differ diff --git a/res/tetrio_badges/pkstarcup_2.png b/res/tetrio_badges/pkstarcup_2.png deleted file mode 100644 index 0e932d7..0000000 Binary files a/res/tetrio_badges/pkstarcup_2.png and /dev/null differ diff --git a/res/tetrio_badges/pkstarcup_3.png b/res/tetrio_badges/pkstarcup_3.png deleted file mode 100644 index 4004fea..0000000 Binary files a/res/tetrio_badges/pkstarcup_3.png and /dev/null differ diff --git a/res/tetrio_badges/redgevo_1.png b/res/tetrio_badges/redgevo_1.png deleted file mode 100644 index fd03b88..0000000 Binary files a/res/tetrio_badges/redgevo_1.png and /dev/null differ diff --git a/res/tetrio_badges/redgevo_2.png b/res/tetrio_badges/redgevo_2.png deleted file mode 100644 index 28dcf72..0000000 Binary files a/res/tetrio_badges/redgevo_2.png and /dev/null differ diff --git a/res/tetrio_badges/redgevo_3.png b/res/tetrio_badges/redgevo_3.png deleted file mode 100644 index 16ddf2e..0000000 Binary files a/res/tetrio_badges/redgevo_3.png and /dev/null differ diff --git a/res/tetrio_badges/rengervl_1.png b/res/tetrio_badges/rengervl_1.png deleted file mode 100644 index f294f62..0000000 Binary files a/res/tetrio_badges/rengervl_1.png and /dev/null differ diff --git a/res/tetrio_badges/rengervl_2.png b/res/tetrio_badges/rengervl_2.png deleted file mode 100644 index dd50c6b..0000000 Binary files a/res/tetrio_badges/rengervl_2.png and /dev/null differ diff --git a/res/tetrio_badges/rengervl_3.png b/res/tetrio_badges/rengervl_3.png deleted file mode 100644 index 3e6b560..0000000 Binary files a/res/tetrio_badges/rengervl_3.png and /dev/null differ diff --git a/res/tetrio_badges/sakurablend_1.png b/res/tetrio_badges/sakurablend_1.png deleted file mode 100644 index 2501257..0000000 Binary files a/res/tetrio_badges/sakurablend_1.png and /dev/null differ diff --git a/res/tetrio_badges/sakurablend_2.png b/res/tetrio_badges/sakurablend_2.png deleted file mode 100644 index b5ce4ca..0000000 Binary files a/res/tetrio_badges/sakurablend_2.png and /dev/null differ diff --git a/res/tetrio_badges/sakurablend_3.png b/res/tetrio_badges/sakurablend_3.png deleted file mode 100644 index b15fcc0..0000000 Binary files a/res/tetrio_badges/sakurablend_3.png and /dev/null differ diff --git a/res/tetrio_badges/scuncapped_1.png b/res/tetrio_badges/scuncapped_1.png deleted file mode 100644 index 28b81b6..0000000 Binary files a/res/tetrio_badges/scuncapped_1.png and /dev/null differ diff --git a/res/tetrio_badges/scuncapped_2.png b/res/tetrio_badges/scuncapped_2.png deleted file mode 100644 index 3ac3d21..0000000 Binary files a/res/tetrio_badges/scuncapped_2.png and /dev/null differ diff --git a/res/tetrio_badges/scuncapped_3.png b/res/tetrio_badges/scuncapped_3.png deleted file mode 100644 index 4925bbd..0000000 Binary files a/res/tetrio_badges/scuncapped_3.png and /dev/null differ diff --git a/res/tetrio_badges/secretgrade.png b/res/tetrio_badges/secretgrade.png deleted file mode 100644 index 86894e4..0000000 Binary files a/res/tetrio_badges/secretgrade.png and /dev/null differ diff --git a/res/tetrio_badges/sfu_raccoon_1.png b/res/tetrio_badges/sfu_raccoon_1.png deleted file mode 100644 index 356d48b..0000000 Binary files a/res/tetrio_badges/sfu_raccoon_1.png and /dev/null differ diff --git a/res/tetrio_badges/sfu_raccoon_2.png b/res/tetrio_badges/sfu_raccoon_2.png deleted file mode 100644 index 9f98af0..0000000 Binary files a/res/tetrio_badges/sfu_raccoon_2.png and /dev/null differ diff --git a/res/tetrio_badges/sfu_raccoon_3.png b/res/tetrio_badges/sfu_raccoon_3.png deleted file mode 100644 index e4ebc90..0000000 Binary files a/res/tetrio_badges/sfu_raccoon_3.png and /dev/null differ diff --git a/res/tetrio_badges/sii_1.png b/res/tetrio_badges/sii_1.png deleted file mode 100644 index 420d137..0000000 Binary files a/res/tetrio_badges/sii_1.png and /dev/null differ diff --git a/res/tetrio_badges/sii_2.png b/res/tetrio_badges/sii_2.png deleted file mode 100644 index fb9d66a..0000000 Binary files a/res/tetrio_badges/sii_2.png and /dev/null differ diff --git a/res/tetrio_badges/sii_3.png b/res/tetrio_badges/sii_3.png deleted file mode 100644 index 491a0c8..0000000 Binary files a/res/tetrio_badges/sii_3.png and /dev/null differ diff --git a/res/tetrio_badges/sii_participation.png b/res/tetrio_badges/sii_participation.png deleted file mode 100644 index d7059e9..0000000 Binary files a/res/tetrio_badges/sii_participation.png and /dev/null differ diff --git a/res/tetrio_badges/streamersuperlobby.png b/res/tetrio_badges/streamersuperlobby.png deleted file mode 100644 index 21af4e3..0000000 Binary files a/res/tetrio_badges/streamersuperlobby.png and /dev/null differ diff --git a/res/tetrio_badges/stride_1.png b/res/tetrio_badges/stride_1.png deleted file mode 100644 index 027ed12..0000000 Binary files a/res/tetrio_badges/stride_1.png and /dev/null differ diff --git a/res/tetrio_badges/stride_2.png b/res/tetrio_badges/stride_2.png deleted file mode 100644 index 04e692a..0000000 Binary files a/res/tetrio_badges/stride_2.png and /dev/null differ diff --git a/res/tetrio_badges/stride_3.png b/res/tetrio_badges/stride_3.png deleted file mode 100644 index a4a85f8..0000000 Binary files a/res/tetrio_badges/stride_3.png and /dev/null differ diff --git a/res/tetrio_badges/stride_participation.png b/res/tetrio_badges/stride_participation.png deleted file mode 100644 index 19c1160..0000000 Binary files a/res/tetrio_badges/stride_participation.png and /dev/null differ diff --git a/res/tetrio_badges/stride_superlobby.png b/res/tetrio_badges/stride_superlobby.png deleted file mode 100644 index cd43105..0000000 Binary files a/res/tetrio_badges/stride_superlobby.png and /dev/null differ diff --git a/res/tetrio_badges/superlobby.png b/res/tetrio_badges/superlobby.png deleted file mode 100644 index a4e887c..0000000 Binary files a/res/tetrio_badges/superlobby.png and /dev/null differ diff --git a/res/tetrio_badges/superlobby2.png b/res/tetrio_badges/superlobby2.png deleted file mode 100644 index 0a69b93..0000000 Binary files a/res/tetrio_badges/superlobby2.png and /dev/null differ diff --git a/res/tetrio_badges/taws2_1.png b/res/tetrio_badges/taws2_1.png deleted file mode 100644 index 61ac03c..0000000 Binary files a/res/tetrio_badges/taws2_1.png and /dev/null differ diff --git a/res/tetrio_badges/taws2_2.png b/res/tetrio_badges/taws2_2.png deleted file mode 100644 index 33939d0..0000000 Binary files a/res/tetrio_badges/taws2_2.png and /dev/null differ diff --git a/res/tetrio_badges/taws2_3.png b/res/tetrio_badges/taws2_3.png deleted file mode 100644 index f80aa0f..0000000 Binary files a/res/tetrio_badges/taws2_3.png and /dev/null differ diff --git a/res/tetrio_badges/taws_u50_1.png b/res/tetrio_badges/taws_u50_1.png deleted file mode 100644 index ae1137f..0000000 Binary files a/res/tetrio_badges/taws_u50_1.png and /dev/null differ diff --git a/res/tetrio_badges/taws_u50_2.png b/res/tetrio_badges/taws_u50_2.png deleted file mode 100644 index 0731799..0000000 Binary files a/res/tetrio_badges/taws_u50_2.png and /dev/null differ diff --git a/res/tetrio_badges/taws_u50_3.png b/res/tetrio_badges/taws_u50_3.png deleted file mode 100644 index 0158753..0000000 Binary files a/res/tetrio_badges/taws_u50_3.png and /dev/null differ diff --git a/res/tetrio_badges/tawshdsl_capped.png b/res/tetrio_badges/tawshdsl_capped.png deleted file mode 100644 index 3a252dc..0000000 Binary files a/res/tetrio_badges/tawshdsl_capped.png and /dev/null differ diff --git a/res/tetrio_badges/tawshdsl_uncapped.png b/res/tetrio_badges/tawshdsl_uncapped.png deleted file mode 100644 index 4235c64..0000000 Binary files a/res/tetrio_badges/tawshdsl_uncapped.png and /dev/null differ diff --git a/res/tetrio_badges/tawshdslsanta.png b/res/tetrio_badges/tawshdslsanta.png deleted file mode 100644 index c30a8da..0000000 Binary files a/res/tetrio_badges/tawshdslsanta.png and /dev/null differ diff --git a/res/tetrio_badges/tawsignite_expert.png b/res/tetrio_badges/tawsignite_expert.png deleted file mode 100644 index 8394154..0000000 Binary files a/res/tetrio_badges/tawsignite_expert.png and /dev/null differ diff --git a/res/tetrio_badges/tawslg.png b/res/tetrio_badges/tawslg.png deleted file mode 100644 index b28d64c..0000000 Binary files a/res/tetrio_badges/tawslg.png and /dev/null differ diff --git a/res/tetrio_badges/tawslo.png b/res/tetrio_badges/tawslo.png deleted file mode 100644 index 3064fb3..0000000 Binary files a/res/tetrio_badges/tawslo.png and /dev/null differ diff --git a/res/tetrio_badges/tetralympic_bronze.png b/res/tetrio_badges/tetralympic_bronze.png deleted file mode 100644 index 2282959..0000000 Binary files a/res/tetrio_badges/tetralympic_bronze.png and /dev/null differ diff --git a/res/tetrio_badges/tetralympic_gold.png b/res/tetrio_badges/tetralympic_gold.png deleted file mode 100644 index 94b2267..0000000 Binary files a/res/tetrio_badges/tetralympic_gold.png and /dev/null differ diff --git a/res/tetrio_badges/tetralympic_masters.png b/res/tetrio_badges/tetralympic_masters.png deleted file mode 100644 index 089a6b3..0000000 Binary files a/res/tetrio_badges/tetralympic_masters.png and /dev/null differ diff --git a/res/tetrio_badges/tetralympic_silver.png b/res/tetrio_badges/tetralympic_silver.png deleted file mode 100644 index 5a4b0c4..0000000 Binary files a/res/tetrio_badges/tetralympic_silver.png and /dev/null differ diff --git a/res/tetrio_badges/thaitour_1.png b/res/tetrio_badges/thaitour_1.png deleted file mode 100644 index 903568d..0000000 Binary files a/res/tetrio_badges/thaitour_1.png and /dev/null differ diff --git a/res/tetrio_badges/thaitour_2.png b/res/tetrio_badges/thaitour_2.png deleted file mode 100644 index f3ffccd..0000000 Binary files a/res/tetrio_badges/thaitour_2.png and /dev/null differ diff --git a/res/tetrio_badges/thaitour_3.png b/res/tetrio_badges/thaitour_3.png deleted file mode 100644 index cec402b..0000000 Binary files a/res/tetrio_badges/thaitour_3.png and /dev/null differ diff --git a/res/tetrio_badges/tiolatam_1.png b/res/tetrio_badges/tiolatam_1.png deleted file mode 100644 index d27611f..0000000 Binary files a/res/tetrio_badges/tiolatam_1.png and /dev/null differ diff --git a/res/tetrio_badges/tiolatam_2.png b/res/tetrio_badges/tiolatam_2.png deleted file mode 100644 index 958b1df..0000000 Binary files a/res/tetrio_badges/tiolatam_2.png and /dev/null differ diff --git a/res/tetrio_badges/tiolatam_3.png b/res/tetrio_badges/tiolatam_3.png deleted file mode 100644 index 6ef1eb6..0000000 Binary files a/res/tetrio_badges/tiolatam_3.png and /dev/null differ diff --git a/res/tetrio_badges/ttsd_ou_1.png b/res/tetrio_badges/ttsd_ou_1.png deleted file mode 100644 index c937225..0000000 Binary files a/res/tetrio_badges/ttsd_ou_1.png and /dev/null differ diff --git a/res/tetrio_badges/ttsd_ou_2.png b/res/tetrio_badges/ttsd_ou_2.png deleted file mode 100644 index 1891b91..0000000 Binary files a/res/tetrio_badges/ttsd_ou_2.png and /dev/null differ diff --git a/res/tetrio_badges/ttsd_ou_3.png b/res/tetrio_badges/ttsd_ou_3.png deleted file mode 100644 index 28ea18a..0000000 Binary files a/res/tetrio_badges/ttsd_ou_3.png and /dev/null differ diff --git a/res/tetrio_badges/ttsdpf_1.png b/res/tetrio_badges/ttsdpf_1.png deleted file mode 100644 index 4749f2e..0000000 Binary files a/res/tetrio_badges/ttsdpf_1.png and /dev/null differ diff --git a/res/tetrio_badges/ttsdpf_2.png b/res/tetrio_badges/ttsdpf_2.png deleted file mode 100644 index 136d014..0000000 Binary files a/res/tetrio_badges/ttsdpf_2.png and /dev/null differ diff --git a/res/tetrio_badges/ttsdpf_3.png b/res/tetrio_badges/ttsdpf_3.png deleted file mode 100644 index ba2c977..0000000 Binary files a/res/tetrio_badges/ttsdpf_3.png and /dev/null differ diff --git a/res/tetrio_badges/ttsdtc_1.png b/res/tetrio_badges/ttsdtc_1.png deleted file mode 100644 index 3565860..0000000 Binary files a/res/tetrio_badges/ttsdtc_1.png and /dev/null differ diff --git a/res/tetrio_badges/ttsdtc_2.png b/res/tetrio_badges/ttsdtc_2.png deleted file mode 100644 index 2986832..0000000 Binary files a/res/tetrio_badges/ttsdtc_2.png and /dev/null differ diff --git a/res/tetrio_badges/ttsdtc_3.png b/res/tetrio_badges/ttsdtc_3.png deleted file mode 100644 index ce507ce..0000000 Binary files a/res/tetrio_badges/ttsdtc_3.png and /dev/null differ diff --git a/res/tetrio_badges/twc23_1.png b/res/tetrio_badges/twc23_1.png deleted file mode 100644 index 3411c01..0000000 Binary files a/res/tetrio_badges/twc23_1.png and /dev/null differ diff --git a/res/tetrio_badges/twc23_2.png b/res/tetrio_badges/twc23_2.png deleted file mode 100644 index ecab68c..0000000 Binary files a/res/tetrio_badges/twc23_2.png and /dev/null differ diff --git a/res/tetrio_badges/twc23_3.png b/res/tetrio_badges/twc23_3.png deleted file mode 100644 index 4824d57..0000000 Binary files a/res/tetrio_badges/twc23_3.png and /dev/null differ diff --git a/res/tetrio_badges/twc23_4.png b/res/tetrio_badges/twc23_4.png deleted file mode 100644 index d033d1e..0000000 Binary files a/res/tetrio_badges/twc23_4.png and /dev/null differ diff --git a/res/tetrio_badges/twc23_honorary.png b/res/tetrio_badges/twc23_honorary.png deleted file mode 100644 index 469ce26..0000000 Binary files a/res/tetrio_badges/twc23_honorary.png and /dev/null differ diff --git a/res/tetrio_badges/twc23_t16.png b/res/tetrio_badges/twc23_t16.png deleted file mode 100644 index 54f9bdd..0000000 Binary files a/res/tetrio_badges/twc23_t16.png and /dev/null differ diff --git a/res/tetrio_badges/twc23_t64.png b/res/tetrio_badges/twc23_t64.png deleted file mode 100644 index 35e8198..0000000 Binary files a/res/tetrio_badges/twc23_t64.png and /dev/null differ diff --git a/res/tetrio_badges/twc23_t8.png b/res/tetrio_badges/twc23_t8.png deleted file mode 100644 index d9055ce..0000000 Binary files a/res/tetrio_badges/twc23_t8.png and /dev/null differ diff --git a/res/tetrio_badges/ubcea_1.png b/res/tetrio_badges/ubcea_1.png deleted file mode 100644 index 8076904..0000000 Binary files a/res/tetrio_badges/ubcea_1.png and /dev/null differ diff --git a/res/tetrio_badges/ubcea_2.png b/res/tetrio_badges/ubcea_2.png deleted file mode 100644 index 3f4d46e..0000000 Binary files a/res/tetrio_badges/ubcea_2.png and /dev/null differ diff --git a/res/tetrio_badges/ubcea_3.png b/res/tetrio_badges/ubcea_3.png deleted file mode 100644 index f9ff961..0000000 Binary files a/res/tetrio_badges/ubcea_3.png and /dev/null differ diff --git a/res/tetrio_badges/underdog_1.png b/res/tetrio_badges/underdog_1.png deleted file mode 100644 index 9307ecc..0000000 Binary files a/res/tetrio_badges/underdog_1.png and /dev/null differ diff --git a/res/tetrio_badges/underdog_2.png b/res/tetrio_badges/underdog_2.png deleted file mode 100644 index a2b0a74..0000000 Binary files a/res/tetrio_badges/underdog_2.png and /dev/null differ diff --git a/res/tetrio_badges/underdog_3.png b/res/tetrio_badges/underdog_3.png deleted file mode 100644 index 272fcc2..0000000 Binary files a/res/tetrio_badges/underdog_3.png and /dev/null differ diff --git a/res/tetrio_badges/underdog_predict.png b/res/tetrio_badges/underdog_predict.png deleted file mode 100644 index e66aca7..0000000 Binary files a/res/tetrio_badges/underdog_predict.png and /dev/null differ diff --git a/res/tetrio_badges/uoftflag_1.png b/res/tetrio_badges/uoftflag_1.png deleted file mode 100644 index b6a51db..0000000 Binary files a/res/tetrio_badges/uoftflag_1.png and /dev/null differ diff --git a/res/tetrio_badges/uoftflag_2.png b/res/tetrio_badges/uoftflag_2.png deleted file mode 100644 index 4113f0b..0000000 Binary files a/res/tetrio_badges/uoftflag_2.png and /dev/null differ diff --git a/res/tetrio_badges/uoftflag_3.png b/res/tetrio_badges/uoftflag_3.png deleted file mode 100644 index d26430c..0000000 Binary files a/res/tetrio_badges/uoftflag_3.png and /dev/null differ diff --git a/res/tetrio_badges/wpl_1.png b/res/tetrio_badges/wpl_1.png deleted file mode 100644 index 6be04d9..0000000 Binary files a/res/tetrio_badges/wpl_1.png and /dev/null differ diff --git a/res/tetrio_badges/wpl_2.png b/res/tetrio_badges/wpl_2.png deleted file mode 100644 index 6dc8536..0000000 Binary files a/res/tetrio_badges/wpl_2.png and /dev/null differ diff --git a/res/tetrio_badges/wpl_3.png b/res/tetrio_badges/wpl_3.png deleted file mode 100644 index 1922237..0000000 Binary files a/res/tetrio_badges/wpl_3.png and /dev/null differ diff --git a/res/tetrio_badges/wplc_1.png b/res/tetrio_badges/wplc_1.png deleted file mode 100644 index f2f3542..0000000 Binary files a/res/tetrio_badges/wplc_1.png and /dev/null differ diff --git a/res/tetrio_badges/wplc_2.png b/res/tetrio_badges/wplc_2.png deleted file mode 100644 index cdd6461..0000000 Binary files a/res/tetrio_badges/wplc_2.png and /dev/null differ diff --git a/res/tetrio_badges/wplc_3.png b/res/tetrio_badges/wplc_3.png deleted file mode 100644 index 2025666..0000000 Binary files a/res/tetrio_badges/wplc_3.png and /dev/null differ diff --git a/res/tetrio_badges/wplc_participation.png b/res/tetrio_badges/wplc_participation.png deleted file mode 100644 index 2c51e80..0000000 Binary files a/res/tetrio_badges/wplc_participation.png and /dev/null differ diff --git a/web/index.html b/web/index.html index ba6ded4..d3f6a1b 100644 --- a/web/index.html +++ b/web/index.html @@ -131,7 +131,7 @@ } - +