Experimental new feature

This commit is contained in:
dan63047 2024-03-07 01:34:15 +03:00
parent 0648ca9a5d
commit f95ffb59aa
13 changed files with 439 additions and 255 deletions

View File

@ -1139,6 +1139,73 @@ class News {
} }
} }
class PlayerLeaderboardPosition{
late LeaderboardPosition apm;
late LeaderboardPosition pps;
late LeaderboardPosition vs;
late LeaderboardPosition gamesPlayed;
late LeaderboardPosition gamesWon;
late LeaderboardPosition winrate;
late LeaderboardPosition app;
late LeaderboardPosition vsapm;
late LeaderboardPosition dss;
late LeaderboardPosition dsp;
late LeaderboardPosition appdsp;
late LeaderboardPosition cheese;
late LeaderboardPosition gbe;
late LeaderboardPosition nyaapp;
late LeaderboardPosition area;
late LeaderboardPosition estTr;
late LeaderboardPosition accOfEst;
PlayerLeaderboardPosition({
required this.apm,
required this.pps,
required this.vs,
required this.gamesPlayed,
required this.gamesWon,
required this.winrate,
required this.app,
required this.vsapm,
required this.dss,
required this.dsp,
required this.appdsp,
required this.cheese,
required this.gbe,
required this.nyaapp,
required this.area,
required this.estTr,
required this.accOfEst
});
PlayerLeaderboardPosition.fromSearchResults(List<LeaderboardPosition> results){
apm = results[0];
pps = results[1];
vs = results[2];
gamesPlayed = results[3];
gamesWon = results[4];
winrate = results[5];
app = results[6];
vsapm = results[7];
dss = results[8];
dsp = results[9];
appdsp = results[10];
cheese = results[11];
gbe = results[12];
nyaapp = results[13];
area = results[14];
estTr = results[15];
accOfEst = results[16];
}
}
class LeaderboardPosition{
int position;
double percentage;
LeaderboardPosition(this.position, this.percentage);
}
class TetrioPlayersLeaderboard { class TetrioPlayersLeaderboard {
late String type; late String type;
late DateTime timestamp; late DateTime timestamp;
@ -1163,6 +1230,20 @@ class TetrioPlayersLeaderboard {
return lb; return lb;
} }
List<TetrioPlayerFromLeaderboard> getStatRankingSequel(Stats stat){
List<TetrioPlayerFromLeaderboard> lb = List.from(leaderboard);
lb.sort(((a, b) {
if (a.getStatByEnum(stat) > b.getStatByEnum(stat)){
return -1;
}else if (a.getStatByEnum(stat) == b.getStatByEnum(stat)){
return 0;
}else{
return 1;
}
}));
return lb;
}
List<dynamic> getAverageOfRank(String rank){ // i tried to refactor it and that's was terrible List<dynamic> getAverageOfRank(String rank){ // i tried to refactor it and that's was terrible
if (rank.isNotEmpty && !rankCutoffs.keys.contains(rank)) throw Exception("Invalid rank"); if (rank.isNotEmpty && !rankCutoffs.keys.contains(rank)) throw Exception("Invalid rank");
List<TetrioPlayerFromLeaderboard> filtredLeaderboard = List.from(leaderboard); List<TetrioPlayerFromLeaderboard> filtredLeaderboard = List.from(leaderboard);
@ -1753,6 +1834,19 @@ class TetrioPlayersLeaderboard {
} }
} }
PlayerLeaderboardPosition? getLeaderboardPosition(String userID) {
if (leaderboard.indexWhere((element) => element.userId == userID) == -1) return null;
List<Stats> stats = [Stats.apm, Stats.pps, Stats.vs, Stats.gp, Stats.gw, Stats.wr,
Stats.app, Stats.vsapm, Stats.dss, Stats.dsp, Stats.appdsp, Stats.cheese, Stats.gbe, Stats.nyaapp, Stats.area, Stats.eTR, Stats.acceTR];
List<LeaderboardPosition> results = [];
for (Stats stat in stats) {
List<TetrioPlayerFromLeaderboard> sortedLeaderboard = getStatRanking(leaderboard, stat, reversed: false);
int position = sortedLeaderboard.indexWhere((element) => element.userId == userID) + 1;
results.add(LeaderboardPosition(position, position / sortedLeaderboard.length));
}
return PlayerLeaderboardPosition.fromSearchResults(results);
}
Map<String, List<dynamic>> get averages => { Map<String, List<dynamic>> get averages => {
'x': getAverageOfRank("x"), 'x': getAverageOfRank("x"),
'u': getAverageOfRank("u"), 'u': getAverageOfRank("u"),

View File

@ -74,6 +74,7 @@ class TetrioService extends DB {
final Map<String, Map<String, dynamic>> _recordsCache = {}; final Map<String, Map<String, dynamic>> _recordsCache = {};
final Map<String, dynamic> _replaysCache = {}; // the only one is different: {"replayID": [replayString, replayBytes]} final Map<String, dynamic> _replaysCache = {}; // the only one is different: {"replayID": [replayString, replayBytes]}
final Map<String, TetrioPlayersLeaderboard> _leaderboardsCache = {}; final Map<String, TetrioPlayersLeaderboard> _leaderboardsCache = {};
final Map<String, PlayerLeaderboardPosition> _lbPositions = {};
final Map<String, List<News>> _newsCache = {}; final Map<String, List<News>> _newsCache = {};
final Map<String, Map<String, double?>> _topTRcache = {}; final Map<String, Map<String, double?>> _topTRcache = {};
final Map<String, TetraLeagueAlphaStream> _tlStreamsCache = {}; final Map<String, TetraLeagueAlphaStream> _tlStreamsCache = {};
@ -142,6 +143,14 @@ class TetrioService extends DB {
db.insert(tetrioTLReplayStatsTable, {idCol: replay.id, "data": jsonEncode(replay.toJson())}); db.insert(tetrioTLReplayStatsTable, {idCol: replay.id, "data": jsonEncode(replay.toJson())});
} }
void cacheLeaderboardPositions(String userID, PlayerLeaderboardPosition positions){
_lbPositions[userID] = positions;
}
PlayerLeaderboardPosition? getCachedLeaderboardPositions(String userID){
return _lbPositions[userID];
}
/// Downloads replay from inoue (szy API). Requiers [replayID]. If request have /// Downloads replay from inoue (szy API). Requiers [replayID]. If request have
/// different from 200 statusCode, it will throw an excepction. Returns list, that contains same replay /// different from 200 statusCode, it will throw an excepction. Returns list, that contains same replay
/// as string and as binary. /// as string and as binary.
@ -504,6 +513,7 @@ class TetrioService extends DB {
switch (response.statusCode) { switch (response.statusCode) {
case 200: case 200:
_lbPositions.clear();
var rawJson = jsonDecode(response.body); var rawJson = jsonDecode(response.body);
if (rawJson['success']) { // if api confirmed that everything ok if (rawJson['success']) { // if api confirmed that everything ok
TetrioPlayersLeaderboard leaderboard = TetrioPlayersLeaderboard.fromJson(rawJson['data']['users'], "league", DateTime.fromMillisecondsSinceEpoch(rawJson['cache']['cached_at'])); TetrioPlayersLeaderboard leaderboard = TetrioPlayersLeaderboard.fromJson(rawJson['data']['users'], "league", DateTime.fromMillisecondsSinceEpoch(rawJson['cache']['cached_at']));

View File

@ -0,0 +1,7 @@
import 'package:intl/intl.dart';
import 'package:tetra_stats/gen/strings.g.dart';
final NumberFormat comparef = NumberFormat("+#,###.###;-#,###.###")..maximumFractionDigits = 3;
final NumberFormat intf = NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 0);
final NumberFormat f3 = NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 3);
final NumberFormat f2 = NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 2);

View File

@ -13,7 +13,6 @@ double? vs;
NerdStats? nerdStats; NerdStats? nerdStats;
EstTr? estTr; EstTr? estTr;
Playstyle? playstyle; Playstyle? playstyle;
final NumberFormat f2 = NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 2);
late String oldWindowTitle; late String oldWindowTitle;
class CalcView extends StatefulWidget { class CalcView extends StatefulWidget {

View File

@ -31,6 +31,7 @@ import 'package:go_router/go_router.dart';
Future<List> me = Future.delayed(const Duration(seconds: 60), () => [null, null, null, null, null, null]); // I love lists shut up Future<List> me = Future.delayed(const Duration(seconds: 60), () => [null, null, null, null, null, null]); // I love lists shut up
TetrioPlayersLeaderboard? everyone; TetrioPlayersLeaderboard? everyone;
PlayerLeaderboardPosition? meAmongEveryone;
String _searchFor = "6098518e3d5155e6ec429cdc"; // who we looking for String _searchFor = "6098518e3d5155e6ec429cdc"; // who we looking for
String _titleNickname = "dan63047"; String _titleNickname = "dan63047";
final TetrioService teto = TetrioService(); // thing, that manadge our local DB final TetrioService teto = TetrioService(); // thing, that manadge our local DB
@ -168,8 +169,14 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
news = requests[2] as List<News>; news = requests[2] as List<News>;
topTR = requests.elementAtOrNull(3) as double?; // No TR - no Top TR topTR = requests.elementAtOrNull(3) as double?; // No TR - no Top TR
// Get tetra League leaderboard if needed meAmongEveryone = teto.getCachedLeaderboardPositions(me.userId);
// if(prefs.getBool("loadLeaderboard") == true) everyone = await teto.fetchTLLeaderboard(); if (meAmongEveryone == null && prefs.getBool("showPositions") == true){
// Get tetra League leaderboard
everyone = teto.getCachedLeaderboard();
everyone ??= await teto.fetchTLLeaderboard();
meAmongEveryone = await compute(everyone!.getLeaderboardPosition, me.userId);
if (meAmongEveryone != null) teto.cacheLeaderboardPositions(me.userId, meAmongEveryone!);
}
// Making list of Tetra League matches // Making list of Tetra League matches
List<TetraLeagueAlphaRecord> tlMatches = []; List<TetraLeagueAlphaRecord> tlMatches = [];
@ -394,7 +401,15 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
body: TabBarView( body: TabBarView(
controller: _tabController, controller: _tabController,
children: [ children: [
TLThingy(tl: snapshot.data![0].tlSeason1, userID: snapshot.data![0].userId, states: snapshot.data![2], topTR: snapshot.data![7], bot: snapshot.data![0].role == "bot", guest: snapshot.data![0].role == "anon"), TLThingy(
tl: snapshot.data![0].tlSeason1,
userID: snapshot.data![0].userId,
states: snapshot.data![2],
topTR: snapshot.data![7],
bot: snapshot.data![0].role == "bot",
guest: snapshot.data![0].role == "anon",
lbPositions: meAmongEveryone
),
_TLRecords(userID: snapshot.data![0].userId, data: snapshot.data![3]), _TLRecords(userID: snapshot.data![0].userId, data: snapshot.data![3]),
_History(states: snapshot.data![2], update: _justUpdate), _History(states: snapshot.data![2], update: _justUpdate),
_RecordThingy(record: snapshot.data![1]['sprint'], rank: snapshot.data![0].tlSeason1.percentileRank), _RecordThingy(record: snapshot.data![1]['sprint'], rank: snapshot.data![0].tlSeason1.percentileRank),
@ -958,71 +973,58 @@ class _RecordThingy extends StatelessWidget {
else if (record!.stream.contains("blitz")) Text(t.blitz, style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)), else if (record!.stream.contains("blitz")) Text(t.blitz, style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)),
// show main metric // show main metric
Wrap( Row(
direction: Axis.horizontal, mainAxisAlignment: MainAxisAlignment.center,
alignment: WrapAlignment.spaceAround, crossAxisAlignment: CrossAxisAlignment.end,
crossAxisAlignment: WrapCrossAlignment.center, textBaseline: TextBaseline.alphabetic,
clipBehavior: Clip.hardEdge,
children: [ children: [
// Show grade based on closest rank average // Show grade based on closest rank average
if (record!.stream.contains("40l")) Image.asset("res/tetrio_tl_alpha_ranks/${closestAverageSprint.key}.png", height: 96) if (record!.stream.contains("40l")) Image.asset("res/tetrio_tl_alpha_ranks/${closestAverageSprint.key}.png", height: 48)
else if (record!.stream.contains("blitz")) Image.asset("res/tetrio_tl_alpha_ranks/${closestAverageBlitz.key}.png", height: 96), else if (record!.stream.contains("blitz")) Image.asset("res/tetrio_tl_alpha_ranks/${closestAverageBlitz.key}.png", height: 48),
// TODO: I'm not sure abour that element. Maybe, it could be done differenly // Show result
Column( if (record!.stream.contains("40l")) Text(get40lTime(record!.endContext!.finalTime.inMicroseconds), style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28))
children: [ else if (record!.stream.contains("blitz")) Text(NumberFormat.decimalPattern().format(record!.endContext!.score), style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)),
// Show result
if (record!.stream.contains("40l")) Text(get40lTime(record!.endContext!.finalTime.inMicroseconds), style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28))
else if (record!.stream.contains("blitz")) Text(NumberFormat.decimalPattern().format(record!.endContext!.score), style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)),
// Show difference between rank average
if (record!.stream.contains("40l") && (rank != null && rank != "z")) Text(
"${readableTimeDifference(record!.endContext!.finalTime, sprintAverages[rank]!)} ${sprintBetterThanRankAverage??false ? "better" : "worse"} than ${rank!.toUpperCase()} rank average",
textAlign: TextAlign.center,
style: TextStyle(
color: sprintBetterThanRankAverage??false ?
Colors.greenAccent :
Colors.redAccent
)
)
else if (record!.stream.contains("40l") && (rank == null || rank == "z")) Text(
"${readableTimeDifference(record!.endContext!.finalTime, closestAverageSprint.value)} ${sprintBetterThanClosestAverage ? "better" : "worse"} than ${closestAverageSprint.key!.toUpperCase()} rank average",
textAlign: TextAlign.center,
style: TextStyle(
color: sprintBetterThanClosestAverage ?
Colors.greenAccent :
Colors.redAccent
)
)
else if (record!.stream.contains("blitz") && (rank != null && rank != "z")) Text(
"${readableIntDifference(record!.endContext!.score, blitzAverages[rank]!)} ${blitzBetterThanRankAverage??false ? "better" : "worse"} than ${rank!.toUpperCase()} rank average",
textAlign: TextAlign.center,
style: TextStyle(
color: blitzBetterThanRankAverage??false ?
Colors.greenAccent :
Colors.redAccent
)
)
else if (record!.stream.contains("blitz") && (rank == null || rank == "z")) Text(
"${readableIntDifference(record!.endContext!.score, closestAverageBlitz.value)} ${blitzBetterThanClosestAverage ? "better" : "worse"} than ${closestAverageBlitz.key!.toUpperCase()} rank average",
textAlign: TextAlign.center,
style: TextStyle(
color: blitzBetterThanClosestAverage ?
Colors.greenAccent :
Colors.redAccent
)
),
],
),
], ],
), ),
// if (record!.stream.contains("40l")) Text(get40lTime(record!.endContext!.finalTime.inMicroseconds), style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28))
// else if (record!.stream.contains("blitz")) Text(NumberFormat.decimalPattern().format(record!.endContext!.score), style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)),
// // Compare with averages // Show difference between rank average
// if (record!.stream.contains("40l") && rank != null) RichText(text: TextSpan(text: "${readableTimeDifference(record!.endContext!.finalTime, sprintAverages[rank]!)} ${sprintBetterThanRankAverage??false ? "better" : "worse"} than ${rank!.toUpperCase()} rank average", style: TextStyle(fontFamily: "Eurostile Round", color: sprintBetterThanRankAverage??false ? Colors.green : Colors.red))) if (record!.stream.contains("40l") && (rank != null && rank != "z")) Text(
// //Text("${record!.endContext!.finalTime - sprintAverages[rank]!}; ${sprintAverages[rank]}; ${get40lTime((record!.endContext!.finalTime - sprintAverages[rank]!).inMicroseconds)}") "${readableTimeDifference(record!.endContext!.finalTime, sprintAverages[rank]!)} ${sprintBetterThanRankAverage??false ? "better" : "worse"} than ${rank!.toUpperCase()} rank average",
// else if (record!.stream.contains("blitz")) Text("${closestAverageBlitz}; ${blitzAverages[rank]}"), textAlign: TextAlign.center,
style: TextStyle(
color: sprintBetterThanRankAverage??false ?
Colors.greenAccent :
Colors.redAccent
)
)
else if (record!.stream.contains("40l") && (rank == null || rank == "z")) Text(
"${readableTimeDifference(record!.endContext!.finalTime, closestAverageSprint.value)} ${sprintBetterThanClosestAverage ? "better" : "worse"} than ${closestAverageSprint.key!.toUpperCase()} rank average",
textAlign: TextAlign.center,
style: TextStyle(
color: sprintBetterThanClosestAverage ?
Colors.greenAccent :
Colors.redAccent
)
)
else if (record!.stream.contains("blitz") && (rank != null && rank != "z")) Text(
"${readableIntDifference(record!.endContext!.score, blitzAverages[rank]!)} ${blitzBetterThanRankAverage??false ? "better" : "worse"} than ${rank!.toUpperCase()} rank average",
textAlign: TextAlign.center,
style: TextStyle(
color: blitzBetterThanRankAverage??false ?
Colors.greenAccent :
Colors.redAccent
)
)
else if (record!.stream.contains("blitz") && (rank == null || rank == "z")) Text(
"${readableIntDifference(record!.endContext!.score, closestAverageBlitz.value)} ${blitzBetterThanClosestAverage ? "better" : "worse"} than ${closestAverageBlitz.key!.toUpperCase()} rank average",
textAlign: TextAlign.center,
style: TextStyle(
color: blitzBetterThanClosestAverage ?
Colors.greenAccent :
Colors.redAccent
)
),
// Show rank if presented // Show rank if presented
if (record!.rank != null) StatCellNum(playerStat: record!.rank!, playerStatLabel: "Leaderboard Placement", isScreenBig: bigScreen, higherIsBetter: false), if (record!.rank != null) StatCellNum(playerStat: record!.rank!, playerStatLabel: "Leaderboard Placement", isScreenBig: bigScreen, higherIsBetter: false),
@ -1054,7 +1056,8 @@ class _RecordThingy extends StatelessWidget {
// List of actions // List of actions
Padding(padding: const EdgeInsets.fromLTRB(0, 16, 0, 48), Padding(padding: const EdgeInsets.fromLTRB(0, 16, 0, 48),
child: SizedBox(width: bigScreen ? MediaQuery.of(context).size.width * 0.4 : MediaQuery.of(context).size.width * 0.85, child: Container(width: bigScreen ? MediaQuery.of(context).size.width * 0.4 : MediaQuery.of(context).size.width * 0.85,
constraints: BoxConstraints(maxWidth: 452),
child: Column(crossAxisAlignment: CrossAxisAlignment.start, child: Column(crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Row( Row(

View File

@ -8,7 +8,6 @@ import 'package:tetra_stats/views/tl_match_view.dart';
import 'package:window_manager/window_manager.dart'; import 'package:window_manager/window_manager.dart';
final TetrioService teto = TetrioService(); final TetrioService teto = TetrioService();
final NumberFormat f2 = NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 2);
late String oldWindowTitle; late String oldWindowTitle;
class MatchesView extends StatefulWidget { class MatchesView extends StatefulWidget {

View File

@ -1,8 +1,8 @@
import 'dart:io'; import 'dart:io';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:tetra_stats/gen/strings.g.dart'; import 'package:tetra_stats/gen/strings.g.dart';
import 'package:tetra_stats/utils/numers_formats.dart';
import 'package:tetra_stats/views/rank_averages_view.dart'; import 'package:tetra_stats/views/rank_averages_view.dart';
import 'package:window_manager/window_manager.dart'; import 'package:window_manager/window_manager.dart';
import 'main_view.dart'; // lol import 'main_view.dart'; // lol
@ -40,7 +40,6 @@ class RanksAverages extends State<RankAveragesView> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final NumberFormat f2 = NumberFormat.decimalPattern(LocaleSettings.currentLocale.languageCode)..maximumFractionDigits = 2;
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(t.rankAveragesViewTitle), title: Text(t.rankAveragesViewTitle),

View File

@ -26,7 +26,7 @@ class SettingsState extends State<SettingsView> {
late SharedPreferences prefs; late SharedPreferences prefs;
final TetrioService teto = TetrioService(); final TetrioService teto = TetrioService();
String defaultNickname = "Checking..."; String defaultNickname = "Checking...";
late bool loadLeaderboard; late bool showPositions;
final TextEditingController _playertext = TextEditingController(); final TextEditingController _playertext = TextEditingController();
@override @override
@ -47,10 +47,10 @@ class SettingsState extends State<SettingsView> {
Future<void> _getPreferences() async { Future<void> _getPreferences() async {
prefs = await SharedPreferences.getInstance(); prefs = await SharedPreferences.getInstance();
if (prefs.getBool("loadLeaderboard") != null) { if (prefs.getBool("showPositions") != null) {
loadLeaderboard = prefs.getBool("loadLeaderboard")!; showPositions = prefs.getBool("showPositions")!;
} else { } else {
loadLeaderboard = false; showPositions = false;
} }
_setDefaultNickname(prefs.getString("player")); _setDefaultNickname(prefs.getString("player"));
} }
@ -266,12 +266,12 @@ class SettingsState extends State<SettingsView> {
onTap: () { onTap: () {
Navigator.pushNamed(context, "/customization"); Navigator.pushNamed(context, "/customization");
},), },),
ListTile(title: Text("Load leaderboard on startup"), ListTile(title: Text("Show LB position for each stat"),
subtitle: Text("That will allow app to show additional stats, like..."), subtitle: Text("That will impact on app performance..."),
trailing: Switch(value: loadLeaderboard, onChanged: (bool value){ trailing: Switch(value: showPositions, onChanged: (bool value){
prefs.setBool("loadLeaderboard", value); prefs.setBool("showPositions", value);
setState(() { setState(() {
loadLeaderboard = value; showPositions = value;
}); });
}),), }),),
const Divider(), const Divider(),

View File

@ -71,7 +71,7 @@ class TLLeaderboardState extends State<TLLeaderboardView> {
case ConnectionState.none: case ConnectionState.none:
case ConnectionState.waiting: case ConnectionState.waiting:
case ConnectionState.active: case ConnectionState.active:
return const Center(child: Text('Fetching...')); return const Center(child: CircularProgressIndicator());
case ConnectionState.done: case ConnectionState.done:
final allPlayers = snapshot.data?.getStatRanking(snapshot.data!.leaderboard, _sortBy, reversed: reversed, country: _country); final allPlayers = snapshot.data?.getStatRanking(snapshot.data!.leaderboard, _sortBy, reversed: reversed, country: _country);
if (!kIsWeb && !Platform.isAndroid && !Platform.isIOS) windowManager.setTitle("Tetra Stats: ${t.tlLeaderboard} - ${t.players(n: allPlayers!.length)}"); if (!kIsWeb && !Platform.isAndroid && !Platform.isIOS) windowManager.setTitle("Tetra Stats: ${t.tlLeaderboard} - ${t.players(n: allPlayers!.length)}");

View File

@ -0,0 +1,98 @@
import 'package:flutter/material.dart';
import 'package:syncfusion_flutter_gauges/gauges.dart';
import 'package:tetra_stats/data_objects/tetrio.dart';
import 'package:tetra_stats/gen/strings.g.dart';
import 'package:tetra_stats/utils/numers_formats.dart';
import 'package:tetra_stats/widgets/tl_thingy.dart';
class GaugetNum extends StatelessWidget {
final num playerStat;
final num? oldPlayerStat;
final bool higherIsBetter;
final List<GaugeRange> ranges;
final double minimum;
final double maximum;
final String playerStatLabel;
final String? okText;
final String? alertTitle;
final List<Widget>? alertWidgets;
final LeaderboardPosition? pos;
const GaugetNum(
{super.key,
required this.playerStat,
required this.playerStatLabel,
this.alertWidgets,
this.oldPlayerStat,
required this.higherIsBetter,
required this.minimum,
required this.maximum,
required this.ranges,
this.okText, this.alertTitle, this.pos});
@override
Widget build(BuildContext context) {
return SizedBox(
width: 200,
height: 120,
child: SfRadialGauge(
title: GaugeTitle(text: playerStatLabel),
axes: [RadialAxis(
startAngle: 180,
endAngle: 360,
showLabels: false,
showTicks: false,
radiusFactor: 2.1,
centerY: 0.5,
minimum: minimum,
maximum: maximum,
ranges: ranges,
pointers: [
NeedlePointer(
value: playerStat as double,
enableAnimation: true,
needleLength: 0.9,
needleStartWidth: 2,
needleEndWidth: 15,
knobStyle: const KnobStyle(color: Colors.transparent),
gradient: const LinearGradient(colors: [Colors.transparent, Colors.white], begin: Alignment.bottomCenter, end: Alignment.topCenter, stops: [0.5, 1]),)
],
annotations: [GaugeAnnotation(
widget: TextButton(child: Text(f3.format(playerStat),
style: const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 36, color: Colors.white)),
onPressed: (){
showDialog(
context: context,
builder: (BuildContext context) => AlertDialog(
title: Text(alertTitle??playerStatLabel, style: const TextStyle(fontFamily: "Eurostile Round Extended")),
content: SingleChildScrollView(child: ListBody(children: alertWidgets!)),
actions: <Widget>[
TextButton(
child: Text(okText??t.popupActions.ok),
onPressed: () {
Navigator.of(context).pop();
},
)
],
));
},), verticalAlignment: GaugeAlignment.far, positionFactor: 0.05),
if (oldPlayerStat != null || pos != null) GaugeAnnotation(
widget: RichText(text: TextSpan(
text: "",
style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 14, color: Colors.grey),
children: [
if (oldPlayerStat != null) TextSpan(text: comparef.format(playerStat - oldPlayerStat!), style: TextStyle(
color: higherIsBetter ?
oldPlayerStat! > playerStat ? Colors.redAccent : Colors.greenAccent :
oldPlayerStat! < playerStat ? Colors.redAccent : Colors.greenAccent
),),
if ((oldTl != null && oldTl!.gamesPlayed > 0) && pos != null) const TextSpan(text: ""),
if (pos != null) TextSpan(text: pos!.position >= 1000 ? "Top ${f2.format(pos!.percentage*100)}%" : "${pos!.position}")
]
),
),
positionFactor: 0.05)],
)],),
);
}
}

View File

@ -1,6 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:intl/intl.dart'; import 'package:tetra_stats/utils/numers_formats.dart';
import 'package:tetra_stats/gen/strings.g.dart';
class TrailingStats extends StatelessWidget{ class TrailingStats extends StatelessWidget{
final double yourAPM; final double yourAPM;
@ -14,7 +13,6 @@ class TrailingStats extends StatelessWidget{
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final NumberFormat f2 = NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 2);
const TextStyle style = TextStyle(height: 1.1, fontWeight: FontWeight.w100); const TextStyle style = TextStyle(height: 1.1, fontWeight: FontWeight.w100);
return Table( return Table(
defaultColumnWidth: const IntrinsicColumnWidth(), defaultColumnWidth: const IntrinsicColumnWidth(),

View File

@ -1,6 +1,8 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:tetra_stats/data_objects/tetrio.dart';
import 'package:tetra_stats/gen/strings.g.dart'; import 'package:tetra_stats/gen/strings.g.dart';
import 'package:tetra_stats/utils/numers_formats.dart';
class StatCellNum extends StatelessWidget { class StatCellNum extends StatelessWidget {
const StatCellNum( const StatCellNum(
@ -12,7 +14,7 @@ class StatCellNum extends StatelessWidget {
this.fractionDigits, this.fractionDigits,
this.oldPlayerStat, this.oldPlayerStat,
required this.higherIsBetter, required this.higherIsBetter,
this.okText, this.alertTitle}); this.okText, this.alertTitle, this.pos});
final num playerStat; final num playerStat;
final num? oldPlayerStat; final num? oldPlayerStat;
@ -23,11 +25,11 @@ class StatCellNum extends StatelessWidget {
final String? alertTitle; final String? alertTitle;
final List<Widget>? alertWidgets; final List<Widget>? alertWidgets;
final int? fractionDigits; final int? fractionDigits;
final LeaderboardPosition? pos;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
NumberFormat comparef = NumberFormat("+#,###.###;-#,###.###")..maximumFractionDigits = fractionDigits ?? 0; NumberFormat comparef = NumberFormat("+#,###.###;-#,###.###")..maximumFractionDigits = fractionDigits ?? 0;
NumberFormat intf = NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 0);
NumberFormat fractionf = NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: fractionDigits ?? 0)..maximumIntegerDigits = 0; NumberFormat fractionf = NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: fractionDigits ?? 0)..maximumIntegerDigits = 0;
num fraction = playerStat.isNegative ? 1 - (playerStat - playerStat.floor()) : playerStat - playerStat.floor(); num fraction = playerStat.isNegative ? 1 - (playerStat - playerStat.floor()) : playerStat - playerStat.floor();
int integer = playerStat.isNegative ? (playerStat + fraction).toInt() : (playerStat - fraction).toInt(); int integer = playerStat.isNegative ? (playerStat + fraction).toInt() : (playerStat - fraction).toInt();
@ -48,11 +50,20 @@ class StatCellNum extends StatelessWidget {
) )
) )
), ),
if (oldPlayerStat != null) Text(comparef.format(playerStat - oldPlayerStat!), style: TextStyle( if (oldPlayerStat != null || pos != null) RichText(text: TextSpan(
color: higherIsBetter ? text: "",
oldPlayerStat! > playerStat ? Colors.redAccent : Colors.greenAccent : style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 14, color: Colors.grey),
oldPlayerStat! < playerStat ? Colors.redAccent : Colors.greenAccent children: [
),), if (oldPlayerStat != null) TextSpan(text: comparef.format(playerStat - oldPlayerStat!), style: TextStyle(
color: higherIsBetter ?
oldPlayerStat! > playerStat ? Colors.redAccent : Colors.greenAccent :
oldPlayerStat! < playerStat ? Colors.redAccent : Colors.greenAccent
),),
if (oldPlayerStat != null && pos != null) const TextSpan(text: ""),
if (pos != null) TextSpan(text: pos!.position >= 1000 ? "Top ${f2.format(pos!.percentage*100)}%" : "${pos!.position}")
]
),
),
alertWidgets == null alertWidgets == null
? Text( ? Text(
playerStatLabel, playerStatLabel,

View File

@ -3,13 +3,14 @@ import 'package:intl/intl.dart';
import 'package:tetra_stats/data_objects/tetrio.dart'; import 'package:tetra_stats/data_objects/tetrio.dart';
import 'package:syncfusion_flutter_gauges/gauges.dart'; import 'package:syncfusion_flutter_gauges/gauges.dart';
import 'package:tetra_stats/gen/strings.g.dart'; import 'package:tetra_stats/gen/strings.g.dart';
import 'package:tetra_stats/utils/numers_formats.dart';
import 'package:tetra_stats/widgets/gauget_num.dart';
import 'package:tetra_stats/widgets/graphs.dart'; import 'package:tetra_stats/widgets/graphs.dart';
import 'package:tetra_stats/widgets/stat_sell_num.dart'; import 'package:tetra_stats/widgets/stat_sell_num.dart';
var fDiff = NumberFormat("+#,###.###;-#,###.###"); var fDiff = NumberFormat("+#,###.###;-#,###.###");
var intFDiff = NumberFormat("+#,###;-#,###");
final DateFormat dateFormat = DateFormat.yMMMd(LocaleSettings.currentLocale.languageCode).add_Hms(); final DateFormat dateFormat = DateFormat.yMMMd(LocaleSettings.currentLocale.languageCode).add_Hms();
final NumberFormat f2 = NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 2);
final NumberFormat f3 = NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 3);
late RangeValues _currentRangeValues; late RangeValues _currentRangeValues;
TetraLeagueAlpha? oldTl; TetraLeagueAlpha? oldTl;
late TetraLeagueAlpha currentTl; late TetraLeagueAlpha currentTl;
@ -23,7 +24,8 @@ class TLThingy extends StatefulWidget {
final bool bot; final bool bot;
final bool guest; final bool guest;
final double? topTR; final double? topTR;
const TLThingy({super.key, required this.tl, required this.userID, required this.states, this.showTitle = true, this.bot=false, this.guest=false, this.topTR}); final PlayerLeaderboardPosition? lbPositions;
const TLThingy({super.key, required this.tl, required this.userID, required this.states, this.showTitle = true, this.bot=false, this.guest=false, this.topTR, this.lbPositions});
@override @override
State<TLThingy> createState() => _TLThingyState(); State<TLThingy> createState() => _TLThingyState();
@ -47,6 +49,8 @@ class _TLThingyState extends State<TLThingy> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final t = Translations.of(context); final t = Translations.of(context);
NumberFormat fractionfEstTR = NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 2)..maximumIntegerDigits = 0;
NumberFormat fractionfEstTRAcc = NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 3)..maximumIntegerDigits = 0;
if (currentTl.gamesPlayed == 0) return Center(child: Text(widget.guest ? t.anonTL : widget.bot ? t.botTL : t.neverPlayedTL, style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 28), textAlign: TextAlign.center,)); if (currentTl.gamesPlayed == 0) return Center(child: Text(widget.guest ? t.anonTL : widget.bot ? t.botTL : t.neverPlayedTL, style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 28), textAlign: TextAlign.center,));
return LayoutBuilder(builder: (context, constraints) { return LayoutBuilder(builder: (context, constraints) {
bool bigScreen = constraints.maxWidth > 768; bool bigScreen = constraints.maxWidth > 768;
@ -159,13 +163,13 @@ class _TLThingyState extends State<TLThingy> {
crossAxisAlignment: WrapCrossAlignment.start, crossAxisAlignment: WrapCrossAlignment.start,
clipBehavior: Clip.hardEdge, clipBehavior: Clip.hardEdge,
children: [ children: [
if (currentTl.apm != null) StatCellNum(playerStat: currentTl.apm!, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: t.statCellNum.apm, higherIsBetter: true, oldPlayerStat: oldTl?.apm), if (currentTl.apm != null) StatCellNum(playerStat: currentTl.apm!, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: t.statCellNum.apm, higherIsBetter: true, oldPlayerStat: oldTl?.apm, pos: widget.lbPositions?.apm),
if (currentTl.pps != null) StatCellNum(playerStat: currentTl.pps!, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: t.statCellNum.pps, higherIsBetter: true, oldPlayerStat: oldTl?.pps), if (currentTl.pps != null) StatCellNum(playerStat: currentTl.pps!, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: t.statCellNum.pps, higherIsBetter: true, oldPlayerStat: oldTl?.pps, pos: widget.lbPositions?.pps),
if (currentTl.vs != null) StatCellNum(playerStat: currentTl.vs!, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: t.statCellNum.vs, higherIsBetter: true, oldPlayerStat: oldTl?.vs), if (currentTl.vs != null) StatCellNum(playerStat: currentTl.vs!, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: t.statCellNum.vs, higherIsBetter: true, oldPlayerStat: oldTl?.vs, pos: widget.lbPositions?.vs),
if (currentTl.standingLocal > 0) StatCellNum(playerStat: currentTl.standingLocal, isScreenBig: bigScreen, playerStatLabel: t.statCellNum.lbpc, higherIsBetter: false, oldPlayerStat: oldTl?.standingLocal), if (currentTl.standingLocal > 0) StatCellNum(playerStat: currentTl.standingLocal, isScreenBig: bigScreen, playerStatLabel: t.statCellNum.lbpc, higherIsBetter: false, oldPlayerStat: oldTl?.standingLocal),
StatCellNum(playerStat: currentTl.gamesPlayed, isScreenBig: bigScreen, playerStatLabel: t.statCellNum.gamesPlayed, higherIsBetter: true, oldPlayerStat: oldTl?.gamesPlayed), StatCellNum(playerStat: currentTl.gamesPlayed, isScreenBig: bigScreen, playerStatLabel: t.statCellNum.gamesPlayed, higherIsBetter: true, oldPlayerStat: oldTl?.gamesPlayed, pos: widget.lbPositions?.gamesPlayed),
StatCellNum(playerStat: currentTl.gamesWon, isScreenBig: bigScreen, playerStatLabel: t.statCellNum.gamesWonTL, higherIsBetter: true, oldPlayerStat: oldTl?.gamesWon), StatCellNum(playerStat: currentTl.gamesWon, isScreenBig: bigScreen, playerStatLabel: t.statCellNum.gamesWonTL, higherIsBetter: true, oldPlayerStat: oldTl?.gamesWon, pos: widget.lbPositions?.gamesWon),
StatCellNum(playerStat: currentTl.winrate * 100, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: t.statCellNum.winrate, higherIsBetter: true, oldPlayerStat: oldTl != null ? oldTl!.winrate*100 : null), StatCellNum(playerStat: currentTl.winrate * 100, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: t.statCellNum.winrate, higherIsBetter: true, oldPlayerStat: oldTl != null ? oldTl!.winrate*100 : null, pos: widget.lbPositions?.winrate),
], ],
), ),
), ),
@ -176,138 +180,31 @@ class _TLThingyState extends State<TLThingy> {
Padding( Padding(
padding: const EdgeInsets.fromLTRB(0, 40, 0, 0), padding: const EdgeInsets.fromLTRB(0, 40, 0, 0),
child: Wrap( child: Wrap(
direction: Axis.horizontal, direction: Axis.horizontal,
alignment: WrapAlignment.center, alignment: WrapAlignment.center,
spacing: 35, spacing: 35,
crossAxisAlignment: WrapCrossAlignment.start, crossAxisAlignment: WrapCrossAlignment.start,
clipBehavior: Clip.hardEdge, clipBehavior: Clip.hardEdge,
children: [ children: [
SizedBox( GaugetNum(playerStat: currentTl.nerdStats!.app, playerStatLabel: t.statCellNum.app, higherIsBetter: true, minimum: 0, maximum: 1, ranges: [
width: 200, GaugeRange(startValue: 0, endValue: 0.2, color: Colors.red),
height: 120, GaugeRange(startValue: 0.2, endValue: 0.4, color: Colors.yellow),
child: SfRadialGauge( GaugeRange(startValue: 0.4, endValue: 0.6, color: Colors.green),
title: GaugeTitle(text: t.statCellNum.app), GaugeRange(startValue: 0.6, endValue: 0.8, color: Colors.blue),
axes: [RadialAxis( GaugeRange(startValue: 0.8, endValue: 1, color: Colors.purple),
startAngle: 180, ], alertWidgets: [
endAngle: 360, Text(t.statCellNum.appDescription),
showLabels: false, Text("${t.exactValue}: ${currentTl.nerdStats!.app}")
showTicks: false, ], oldPlayerStat: oldTl?.nerdStats?.app, pos: widget.lbPositions?.app),
radiusFactor: 2.1, GaugetNum(playerStat: currentTl.nerdStats!.vsapm, playerStatLabel: "VS / APM", higherIsBetter: true, minimum: 1.8, maximum: 2.4, ranges: [
centerY: 0.5, GaugeRange(startValue: 1.8, endValue: 2.0, color: Colors.green),
minimum: 0, GaugeRange(startValue: 2.0, endValue: 2.2, color: Colors.blue),
maximum: 1, GaugeRange(startValue: 2.2, endValue: 2.4, color: Colors.purple),
ranges: [ ], alertWidgets: [
GaugeRange(startValue: 0, endValue: 0.2, color: Colors.red), Text(t.statCellNum.vsapmDescription),
GaugeRange(startValue: 0.2, endValue: 0.4, color: Colors.yellow), Text("${t.exactValue}: ${currentTl.nerdStats!.vsapm}")
GaugeRange(startValue: 0.4, endValue: 0.6, color: Colors.green), ], oldPlayerStat: oldTl?.nerdStats?.vsapm, pos: widget.lbPositions?.vsapm)
GaugeRange(startValue: 0.6, endValue: 0.8, color: Colors.blue), ]),
GaugeRange(startValue: 0.8, endValue: 1, color: Colors.purple),
],
pointers: [
NeedlePointer(
value: currentTl.nerdStats!.app,
enableAnimation: true,
needleLength: 0.9,
needleStartWidth: 2,
needleEndWidth: 15,
knobStyle: const KnobStyle(color: Colors.transparent),
gradient: const LinearGradient(colors: [Colors.transparent, Colors.white], begin: Alignment.bottomCenter, end: Alignment.topCenter, stops: [0.5, 1]),)
],
annotations: [GaugeAnnotation(
widget: TextButton(child: Text(f3.format(currentTl.nerdStats!.app),
style: const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 36, color: Colors.white)),
onPressed: (){
showDialog(
context: context,
builder: (BuildContext context) => AlertDialog(
title: Text(t.statCellNum.app,
style: const TextStyle(
fontFamily: "Eurostile Round Extended")),
content: SingleChildScrollView(
child: ListBody(children: [
Text(t.statCellNum.appDescription),
Text("${t.exactValue}: ${currentTl.nerdStats!.app}")
]),
),
actions: <Widget>[
TextButton(
child: Text(t.popupActions.ok),
onPressed: () {
Navigator.of(context).pop();
},
)
],
));
},), verticalAlignment: GaugeAlignment.far, positionFactor: 0.05,),
if (oldTl != null && oldTl!.gamesPlayed > 0) GaugeAnnotation(widget: Text(fDiff.format(currentTl.nerdStats!.app - oldTl!.nerdStats!.app), style: TextStyle(
color: currentTl.nerdStats!.app - oldTl!.nerdStats!.app < 0 ?
Colors.redAccent :
Colors.greenAccent
),), positionFactor: 0.05,)],
)],),
),
SizedBox(
width: 200,
height: 120,
child: SfRadialGauge(
title: const GaugeTitle(text: "VS / APM"),
axes: [RadialAxis(
startAngle: 180,
endAngle: 360,
showTicks: false,
showLabels: false,
radiusFactor: 2.1,
centerY: 0.5,
minimum: 1.8,
maximum: 2.4,
ranges: [
GaugeRange(startValue: 1.8, endValue: 2.0, color: Colors.green),
GaugeRange(startValue: 2.0, endValue: 2.2, color: Colors.blue),
GaugeRange(startValue: 2.2, endValue: 2.4, color: Colors.purple),
],
pointers: [
NeedlePointer(
value: currentTl.nerdStats!.vsapm,
enableAnimation: true,
needleLength: 0.9,
needleStartWidth: 2,
needleEndWidth: 15,
knobStyle: const KnobStyle(color: Colors.transparent),
gradient: const LinearGradient(colors: [Colors.transparent, Colors.white], begin: Alignment.bottomCenter, end: Alignment.topCenter, stops: [0.5, 1]),)
],
annotations: [GaugeAnnotation(
widget: TextButton(child: Text(f3.format(currentTl.nerdStats!.vsapm),
style: const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 36, color: Colors.white)),
onPressed: (){
showDialog(
context: context,
builder: (BuildContext context) => AlertDialog(
title: const Text("VS / APM",
style: TextStyle(
fontFamily: "Eurostile Round Extended")),
content: SingleChildScrollView(
child: ListBody(children: [
Text(t.statCellNum.vsapmDescription),
Text("${t.exactValue}: ${currentTl.nerdStats!.vsapm}")
]),
),
actions: <Widget>[
TextButton(
child: Text(t.popupActions.ok),
onPressed: () {
Navigator.of(context).pop();
},
)
],
));
},), verticalAlignment: GaugeAlignment.far, positionFactor: 0.05),
if (oldTl != null && oldTl!.gamesPlayed > 0) GaugeAnnotation(widget: Text(fDiff.format(currentTl.nerdStats!.vsapm - oldTl!.nerdStats!.vsapm), style: TextStyle(
color: currentTl.nerdStats!.vsapm - oldTl!.nerdStats!.vsapm < 0 ?
Colors.redAccent :
Colors.greenAccent
),), positionFactor: 0.05,)],
)],),
),]),
), ),
Wrap( Wrap(
direction: Axis.horizontal, direction: Axis.horizontal,
@ -317,6 +214,7 @@ class _TLThingyState extends State<TLThingy> {
clipBehavior: Clip.hardEdge, clipBehavior: Clip.hardEdge,
children: [ children: [
StatCellNum(playerStat: currentTl.nerdStats!.dss, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: t.statCellNum.dss, StatCellNum(playerStat: currentTl.nerdStats!.dss, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: t.statCellNum.dss,
pos: widget.lbPositions?.dss,
alertWidgets: [Text(t.statCellNum.dssDescription), alertWidgets: [Text(t.statCellNum.dssDescription),
Text("${t.formula}: (VS / 100) - (APM / 60)"), Text("${t.formula}: (VS / 100) - (APM / 60)"),
Text("${t.exactValue}: ${currentTl.nerdStats!.dss}"),], Text("${t.exactValue}: ${currentTl.nerdStats!.dss}"),],
@ -324,6 +222,7 @@ class _TLThingyState extends State<TLThingy> {
higherIsBetter: true, higherIsBetter: true,
oldPlayerStat: oldTl?.nerdStats?.dss,), oldPlayerStat: oldTl?.nerdStats?.dss,),
StatCellNum(playerStat: currentTl.nerdStats!.dsp, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: t.statCellNum.dsp, StatCellNum(playerStat: currentTl.nerdStats!.dsp, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: t.statCellNum.dsp,
pos: widget.lbPositions?.dsp,
alertWidgets: [Text(t.statCellNum.dspDescription), alertWidgets: [Text(t.statCellNum.dspDescription),
Text("${t.formula}: DS/S / PPS"), Text("${t.formula}: DS/S / PPS"),
Text("${t.exactValue}: ${currentTl.nerdStats!.dsp}"),], Text("${t.exactValue}: ${currentTl.nerdStats!.dsp}"),],
@ -331,6 +230,7 @@ class _TLThingyState extends State<TLThingy> {
higherIsBetter: true, higherIsBetter: true,
oldPlayerStat: oldTl?.nerdStats?.dsp,), oldPlayerStat: oldTl?.nerdStats?.dsp,),
StatCellNum(playerStat: currentTl.nerdStats!.appdsp, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: t.statCellNum.appdsp, StatCellNum(playerStat: currentTl.nerdStats!.appdsp, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: t.statCellNum.appdsp,
pos: widget.lbPositions?.appdsp,
alertWidgets: [Text(t.statCellNum.appdspDescription), alertWidgets: [Text(t.statCellNum.appdspDescription),
Text("${t.formula}: APP + DS/P"), Text("${t.formula}: APP + DS/P"),
Text("${t.exactValue}: ${currentTl.nerdStats!.appdsp}"),], Text("${t.exactValue}: ${currentTl.nerdStats!.appdsp}"),],
@ -338,6 +238,7 @@ class _TLThingyState extends State<TLThingy> {
higherIsBetter: true, higherIsBetter: true,
oldPlayerStat: oldTl?.nerdStats?.appdsp,), oldPlayerStat: oldTl?.nerdStats?.appdsp,),
StatCellNum(playerStat: currentTl.nerdStats!.cheese, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: t.statCellNum.cheese, StatCellNum(playerStat: currentTl.nerdStats!.cheese, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: t.statCellNum.cheese,
pos: widget.lbPositions?.cheese,
alertWidgets: [Text(t.statCellNum.cheeseDescription), alertWidgets: [Text(t.statCellNum.cheeseDescription),
Text("${t.formula}: (DS/P * 150) + ((VS/APM - 2) * 50) + (0.6 - APP) * 125"), Text("${t.formula}: (DS/P * 150) + ((VS/APM - 2) * 50) + (0.6 - APP) * 125"),
Text("${t.exactValue}: ${currentTl.nerdStats!.cheese}"),], Text("${t.exactValue}: ${currentTl.nerdStats!.cheese}"),],
@ -345,6 +246,7 @@ class _TLThingyState extends State<TLThingy> {
higherIsBetter: true, higherIsBetter: true,
oldPlayerStat: oldTl?.nerdStats?.cheese,), oldPlayerStat: oldTl?.nerdStats?.cheese,),
StatCellNum(playerStat: currentTl.nerdStats!.gbe, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: t.statCellNum.gbe, StatCellNum(playerStat: currentTl.nerdStats!.gbe, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: t.statCellNum.gbe,
pos: widget.lbPositions?.gbe,
alertWidgets: [Text(t.statCellNum.gbeDescription), alertWidgets: [Text(t.statCellNum.gbeDescription),
Text("${t.formula}: APP * DS/P * 2"), Text("${t.formula}: APP * DS/P * 2"),
Text("${t.exactValue}: ${currentTl.nerdStats!.gbe}"),], Text("${t.exactValue}: ${currentTl.nerdStats!.gbe}"),],
@ -352,6 +254,7 @@ class _TLThingyState extends State<TLThingy> {
higherIsBetter: true, higherIsBetter: true,
oldPlayerStat: oldTl?.nerdStats?.gbe,), oldPlayerStat: oldTl?.nerdStats?.gbe,),
StatCellNum(playerStat: currentTl.nerdStats!.nyaapp, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: t.statCellNum.nyaapp, StatCellNum(playerStat: currentTl.nerdStats!.nyaapp, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: t.statCellNum.nyaapp,
pos: widget.lbPositions?.nyaapp,
alertWidgets: [Text(t.statCellNum.nyaappDescription), alertWidgets: [Text(t.statCellNum.nyaappDescription),
Text("${t.formula}: APP - 5 * tan(radians((Cheese Index / -30) + 1))"), Text("${t.formula}: APP - 5 * tan(radians((Cheese Index / -30) + 1))"),
Text("${t.exactValue}: ${currentTl.nerdStats!.nyaapp}"),], Text("${t.exactValue}: ${currentTl.nerdStats!.nyaapp}"),],
@ -359,6 +262,7 @@ class _TLThingyState extends State<TLThingy> {
higherIsBetter: true, higherIsBetter: true,
oldPlayerStat: oldTl?.nerdStats?.nyaapp,), oldPlayerStat: oldTl?.nerdStats?.nyaapp,),
StatCellNum(playerStat: currentTl.nerdStats!.area, isScreenBig: bigScreen, fractionDigits: 1, playerStatLabel: t.statCellNum.area, StatCellNum(playerStat: currentTl.nerdStats!.area, isScreenBig: bigScreen, fractionDigits: 1, playerStatLabel: t.statCellNum.area,
pos: widget.lbPositions?.area,
alertWidgets: [Text(t.statCellNum.areaDescription), alertWidgets: [Text(t.statCellNum.areaDescription),
Text("${t.formula}: APM * 1 + PPS * 45 + VS * 0.444 + APP * 185 + DS/S * 175 + DS/P * 450 + Garbage Effi * 315"), Text("${t.formula}: APM * 1 + PPS * 45 + VS * 0.444 + APP * 185 + DS/S * 175 + DS/P * 450 + Garbage Effi * 315"),
Text("${t.exactValue}: ${currentTl.nerdStats!.area}"),], Text("${t.exactValue}: ${currentTl.nerdStats!.area}"),],
@ -370,42 +274,104 @@ class _TLThingyState extends State<TLThingy> {
), ),
if (currentTl.estTr != null) if (currentTl.estTr != null)
Padding( Padding(
padding: const EdgeInsets.fromLTRB(0, 16, 0, 48), padding: const EdgeInsets.fromLTRB(0, 48, 0, 48),
child: SizedBox( child: Container(
//alignment: Alignment.center,
width: bigScreen ? MediaQuery.of(context).size.width * 0.4 : MediaQuery.of(context).size.width * 0.85, width: bigScreen ? MediaQuery.of(context).size.width * 0.4 : MediaQuery.of(context).size.width * 0.85,
child: Column( constraints: BoxConstraints(maxWidth: 768),
crossAxisAlignment: CrossAxisAlignment.start, child: Wrap(
alignment: WrapAlignment.spaceBetween,
spacing: 20,
children: [ children: [
Row( Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(t.statCellNum.estOfTR, style: TextStyle(height: 0.1),),
"${bigScreen ? t.statCellNum.estOfTR : t.statCellNum.estOfTRShort}:", RichText(
style: const TextStyle(fontSize: 24), text: TextSpan(
text: intf.format(currentTl.estTr!.esttr.truncate()),
style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 36, fontWeight: FontWeight.w500),
children: [TextSpan(text: fractionfEstTR.format(currentTl.estTr!.esttr - currentTl.estTr!.esttr.truncate()).substring(1), style: TextStyle(fontFamily: "Eurostile Round", fontSize: 14, fontWeight: FontWeight.w100))]
),
), ),
Text( if (oldTl?.estTr?.esttr != null || widget.lbPositions != null) RichText(text: TextSpan(
f2.format(currentTl.estTr!.esttr), text: "",
style: const TextStyle(fontSize: 24), style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 14, color: Colors.grey, height: 0.5),
children: [
if (oldTl?.estTr?.esttr != null) TextSpan(text: comparef.format(currentTl.estTr!.esttr - oldTl!.estTr!.esttr), style: TextStyle(
color: oldTl!.estTr!.esttr > currentTl.estTr!.esttr ? Colors.redAccent : Colors.greenAccent
),),
if (oldTl?.estTr?.esttr != null && widget.lbPositions?.estTr != null) const TextSpan(text: ""),
if (widget.lbPositions?.estTr != null) TextSpan(text: widget.lbPositions!.estTr.position >= 1000 ? "Top ${f2.format(widget.lbPositions!.estTr.percentage*100)}%" : "${widget.lbPositions!.estTr.position}")
]
),
), ),
], ],),
), Column(
if (currentTl.rating >= 0) crossAxisAlignment: CrossAxisAlignment.end,
Row( children: [
mainAxisAlignment: MainAxisAlignment.spaceBetween, Text(t.statCellNum.accOfEst, style: const TextStyle(height: 0.1),),
RichText(
text: TextSpan(
text: (currentTl.esttracc != null) ? intFDiff.format(currentTl.esttracc!.truncate()) : "-",
style: const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 36, fontWeight: FontWeight.w500),
children: [
TextSpan(text: (currentTl.esttracc != null) ? fractionfEstTRAcc.format(currentTl.esttracc!.isNegative ? 1 - (currentTl.esttracc! - currentTl.esttracc!.truncate()) : (currentTl.esttracc! - currentTl.esttracc!.truncate())).substring(1) : ".---", style: TextStyle(fontFamily: "Eurostile Round", fontSize: 14, fontWeight: FontWeight.w100))
]
),
),
if (oldTl?.esttracc != null || widget.lbPositions != null) RichText(text: TextSpan(
text: "",
style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 14, color: Colors.grey, height: 0.5),
children: [ children: [
Text( if (oldTl?.esttracc != null) TextSpan(text: comparef.format(currentTl.esttracc! - oldTl!.esttracc!), style: TextStyle(
"${bigScreen ? t.statCellNum.accOfEst : t.statCellNum.accOfEstShort}:", color: oldTl!.esttracc! > currentTl.esttracc! ? Colors.redAccent : Colors.greenAccent
style: const TextStyle(fontSize: 24), ),),
), if (oldTl?.esttracc != null && widget.lbPositions?.accOfEst != null) const TextSpan(text: ""),
Text( if (widget.lbPositions?.accOfEst != null) TextSpan(text: widget.lbPositions!.accOfEst.position >= 1000 ? "Top ${f2.format(widget.lbPositions!.accOfEst.percentage*100)}%" : "${widget.lbPositions!.accOfEst.position}")
fDiff.format(currentTl.esttracc!), ]
style: const TextStyle(fontSize: 24), ),
),
],
), ),
],)
], ],
), ),
), )
// child: Container(
// width: bigScreen ? MediaQuery.of(context).size.width * 0.4 : MediaQuery.of(context).size.width * 0.85,
// constraints: BoxConstraints(maxWidth: 452),
// child: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: [
// Text(
// "${bigScreen ? t.statCellNum.estOfTR : t.statCellNum.estOfTRShort}:",
// style: const TextStyle(fontSize: 24),
// ),
// Text(
// f2.format(currentTl.estTr!.esttr),
// style: const TextStyle(fontSize: 24),
// ),
// ],
// ),
// if (currentTl.rating >= 0)
// Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: [
// Text(
// "${bigScreen ? t.statCellNum.accOfEst : t.statCellNum.accOfEstShort}:",
// style: const TextStyle(fontSize: 24),
// ),
// Text(
// fDiff.format(currentTl.esttracc!),
// style: const TextStyle(fontSize: 24),
// ),
// ],
// ),
// ],
// ),
// ),
), ),
if (currentTl.nerdStats != null) Graphs(currentTl.apm!, currentTl.pps!, currentTl.vs!, currentTl.nerdStats!, currentTl.playstyle!) if (currentTl.nerdStats != null) Graphs(currentTl.apm!, currentTl.pps!, currentTl.vs!, currentTl.nerdStats!, currentTl.playstyle!)
] ]