From fd78cf2301d043729a29456322db3cefbeb11581 Mon Sep 17 00:00:00 2001 From: dan63047 Date: Wed, 28 Jun 2023 19:50:40 +0300 Subject: [PATCH] Delta for Tetra League --- lib/data_objects/tetrio.dart | 19 +++-- lib/views/compare_view.dart | 20 +++++- lib/views/main_view.dart | 76 ++++++++++++-------- lib/widgets/stat_sell_num.dart | 9 ++- lib/widgets/tl_thingy.dart | 128 +++++++++++++++++++++++++++------ lib/widgets/user_thingy.dart | 28 ++++++-- 6 files changed, 215 insertions(+), 65 deletions(-) diff --git a/lib/data_objects/tetrio.dart b/lib/data_objects/tetrio.dart index 9ba703d..dfbd642 100644 --- a/lib/data_objects/tetrio.dart +++ b/lib/data_objects/tetrio.dart @@ -100,7 +100,7 @@ class TetrioPlayer { country = json['country']; supporterTier = json['supporter_tier']; verified = json['verified']; - tlSeason1 = TetraLeagueAlpha.fromJson(json['league']); + tlSeason1 = TetraLeagueAlpha.fromJson(json['league'], stateTime); avatarRevision = json['avatar_revision']; bannerRevision = json['banner_revision']; bio = json['bio']; @@ -479,12 +479,13 @@ class EstTr { late double esttr; late double srarea; late double statrank; + late double estglicko; EstTr(this._apm, this._pps, this._vs, this._rd, this._app, this._dss, this._dsp, this._gbe) { srarea = (_apm * 0) + (_pps * 135) + (_vs * 0) + (_app * 290) + (_dss * 0) + (_dsp * 700) + (_gbe * 0); statrank = 11.2 * atan((srarea - 93) / 130) + 1; if (statrank <= 0) statrank = 0.001; - double estglicko = (4.0867 * srarea + 186.68); + estglicko = (4.0867 * srarea + 186.68); double temp = (1500 - estglicko) * pi; double temp2 = pow((15.9056943314 * (pow(_rd, 2)) + 3527584.25978), 0.5) as double; double temp3 = 1 + pow(10, (temp / temp2)) as double; @@ -644,6 +645,7 @@ class EndContextMulti { } class TetraLeagueAlpha { + late DateTime timestamp; late int gamesPlayed; late int gamesWon; late String bestRank; @@ -692,7 +694,8 @@ class TetraLeagueAlpha { double get winrate => gamesWon / gamesPlayed; - TetraLeagueAlpha.fromJson(Map json) { + TetraLeagueAlpha.fromJson(Map json, ts) { + timestamp = ts; gamesPlayed = json['gamesplayed']; gamesWon = json['gameswon']; rating = json['rating'].toDouble(); @@ -833,14 +836,16 @@ class Distinguishment { class TetrioPlayersLeaderboard { late String type; + late DateTime timestamp; late List leaderboard; TetrioPlayersLeaderboard(this.type, this.leaderboard); - TetrioPlayersLeaderboard.fromJson(Map json, String type) { + TetrioPlayersLeaderboard.fromJson(Map json, String type, DateTime ts) { type = type; + timestamp = ts; for (Map entry in json['users']) { - leaderboard.add(TetrioPlayerFromLeaderboard.fromJson(entry)); + leaderboard.add(TetrioPlayerFromLeaderboard.fromJson(entry, ts)); } } } @@ -857,7 +862,7 @@ class TetrioPlayerFromLeaderboard { TetrioPlayerFromLeaderboard(this.userId, this.username, this.role, this.xp, this.country, this.supporter, this.verified, this.league); - TetrioPlayerFromLeaderboard.fromJson(Map json) { + TetrioPlayerFromLeaderboard.fromJson(Map json, DateTime ts) { userId = json['_id']; username = json['username']; role = json['role']; @@ -865,6 +870,6 @@ class TetrioPlayerFromLeaderboard { country = json['country ']; supporter = json['supporter']; verified = json['verified']; - league = TetraLeagueAlpha.fromJson(json['league']); + league = TetraLeagueAlpha.fromJson(json['league'], ts); } } diff --git a/lib/views/compare_view.dart b/lib/views/compare_view.dart index 9fc6cb9..ff1ec84 100644 --- a/lib/views/compare_view.dart +++ b/lib/views/compare_view.dart @@ -10,7 +10,7 @@ List>? greenSideStates; TetrioPlayer? theRedSide; List>? redSideStates; final TetrioService teto = TetrioService(); -final DateFormat dateFormat = DateFormat.yMMMd().add_Hms(); +final DateFormat dateFormat = DateFormat.yMd().add_Hm(); class CompareView extends StatefulWidget { final TetrioPlayer greenSide; @@ -794,6 +794,23 @@ class CompareState extends State { fractionDigits: 2, higherIsBetter: true, ), + CompareThingy( + label: "By Est. TR", + greenSide: getWinrateByTR( + theGreenSide!.tlSeason1.estTr!.estglicko, + theGreenSide!.tlSeason1.rd!, + theRedSide!.tlSeason1.estTr!.estglicko, + theRedSide!.tlSeason1.rd!) * + 100, + redSide: getWinrateByTR( + theRedSide!.tlSeason1.estTr!.estglicko, + theRedSide!.tlSeason1.rd!, + theGreenSide!.tlSeason1.estTr!.estglicko, + theGreenSide!.tlSeason1.rd!) * + 100, + fractionDigits: 2, + higherIsBetter: true, + ), ], ) ], @@ -858,6 +875,7 @@ class PlayerSelector extends StatelessWidget { child: DropdownButton( items: states, value: player, + //style: TextStyle(overflow: TextOverflow.clip), onChanged: (value) => change(value!), ), ) diff --git a/lib/views/main_view.dart b/lib/views/main_view.dart index 0290353..d79dd96 100644 --- a/lib/views/main_view.dart +++ b/lib/views/main_view.dart @@ -118,28 +118,35 @@ class _MainState extends State with SingleTickerProviderStateMixin { List tlMatches = []; bool isTracking = await teto.isPlayerTracking(me.userId); List states = []; + TetraLeagueAlpha? compareWith = null; + var uniqueTL = Set(); if (isTracking){ teto.storeState(me); teto.saveTLMatchesFromStream(await teto.getTLStream(me.userId)); states.addAll(await teto.getPlayer(me.userId)); + states.forEach((element) { + if (uniqueTL.isNotEmpty && uniqueTL.last != element.tlSeason1) uniqueTL.add(element.tlSeason1); + if (uniqueTL.isEmpty) uniqueTL.add(element.tlSeason1); + }); + compareWith = uniqueTL.toList()[uniqueTL.length - 2]; chartsData = >>[ - DropdownMenuItem(value: [for (var state in states) if (state.tlSeason1.gamesPlayed > 9) FlSpot(state.state.millisecondsSinceEpoch.toDouble(), state.tlSeason1.rating)], child: const Text("Tetra Rating")), - DropdownMenuItem(value: [for (var state in states) if (state.tlSeason1.gamesPlayed > 9) FlSpot(state.state.millisecondsSinceEpoch.toDouble(), state.tlSeason1.glicko!)], child: const Text("Glicko")), - DropdownMenuItem(value: [for (var state in states) if (state.tlSeason1.gamesPlayed > 9) FlSpot(state.state.millisecondsSinceEpoch.toDouble(), state.tlSeason1.rd!)], child: const Text("Rating Deviation")), - DropdownMenuItem(value: [for (var state in states) if (state.tlSeason1.apm != null) FlSpot(state.state.millisecondsSinceEpoch.toDouble(), state.tlSeason1.apm!)], child: const Text("Attack Per Minute")), - DropdownMenuItem(value: [for (var state in states) if (state.tlSeason1.pps != null) FlSpot(state.state.millisecondsSinceEpoch.toDouble(), state.tlSeason1.pps!)], child: const Text("Pieces Per Second")), - DropdownMenuItem(value: [for (var state in states) if (state.tlSeason1.vs != null) FlSpot(state.state.millisecondsSinceEpoch.toDouble(), state.tlSeason1.vs!)], child: const Text("Versus Score")), - DropdownMenuItem(value: [for (var state in states) if (state.tlSeason1.nerdStats != null) FlSpot(state.state.millisecondsSinceEpoch.toDouble(), state.tlSeason1.nerdStats!.app)], child: const Text("Attack Per Piece")), - DropdownMenuItem(value: [for (var state in states) if (state.tlSeason1.nerdStats != null) FlSpot(state.state.millisecondsSinceEpoch.toDouble(), state.tlSeason1.nerdStats!.dss)], child: const Text("Downstack Per Second")), - DropdownMenuItem(value: [for (var state in states) if (state.tlSeason1.nerdStats != null) FlSpot(state.state.millisecondsSinceEpoch.toDouble(), state.tlSeason1.nerdStats!.dsp)], child: const Text("Downstack Per Piece")), - DropdownMenuItem(value: [for (var state in states) if (state.tlSeason1.nerdStats != null) FlSpot(state.state.millisecondsSinceEpoch.toDouble(), state.tlSeason1.nerdStats!.appdsp)], child: const Text("APP + DS/P")), - DropdownMenuItem(value: [for (var state in states) if (state.tlSeason1.nerdStats != null) FlSpot(state.state.millisecondsSinceEpoch.toDouble(), state.tlSeason1.nerdStats!.vsapm)], child: const Text("VS/APM")), - DropdownMenuItem(value: [for (var state in states) if (state.tlSeason1.nerdStats != null) FlSpot(state.state.millisecondsSinceEpoch.toDouble(), state.tlSeason1.nerdStats!.cheese)], child: const Text("Cheese Index")), - DropdownMenuItem(value: [for (var state in states) if (state.tlSeason1.nerdStats != null) FlSpot(state.state.millisecondsSinceEpoch.toDouble(), state.tlSeason1.nerdStats!.gbe)], child: const Text("Garbage Efficiency")), - DropdownMenuItem(value: [for (var state in states) if (state.tlSeason1.nerdStats != null) FlSpot(state.state.millisecondsSinceEpoch.toDouble(), state.tlSeason1.nerdStats!.nyaapp)], child: const Text("Weighted APP")), - DropdownMenuItem(value: [for (var state in states) if (state.tlSeason1.nerdStats != null) FlSpot(state.state.millisecondsSinceEpoch.toDouble(), state.tlSeason1.nerdStats!.area)], child: const Text("Area")), - DropdownMenuItem(value: [for (var state in states) if (state.tlSeason1.estTr != null) FlSpot(state.state.millisecondsSinceEpoch.toDouble(), state.tlSeason1.estTr!.esttr)], child: const Text("Est. of TR")), - DropdownMenuItem(value: [for (var state in states) if (state.tlSeason1.esttracc != null) FlSpot(state.state.millisecondsSinceEpoch.toDouble(), state.tlSeason1.esttracc!)], child: const Text("Accuracy of Est.")), + DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.gamesPlayed > 9) FlSpot(tl.timestamp.millisecondsSinceEpoch.toDouble(), tl.rating)], child: const Text("Tetra Rating")), + DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.gamesPlayed > 9) FlSpot(tl.timestamp.millisecondsSinceEpoch.toDouble(), tl.glicko!)], child: const Text("Glicko")), + DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.gamesPlayed > 9) FlSpot(tl.timestamp.millisecondsSinceEpoch.toDouble(), tl.rd!)], child: const Text("Rating Deviation")), + DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.apm != null) FlSpot(tl.timestamp.millisecondsSinceEpoch.toDouble(), tl.apm!)], child: const Text("Attack Per Minute")), + DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.pps != null) FlSpot(tl.timestamp.millisecondsSinceEpoch.toDouble(), tl.pps!)], child: const Text("Pieces Per Second")), + DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.vs != null) FlSpot(tl.timestamp.millisecondsSinceEpoch.toDouble(), tl.vs!)], child: const Text("Versus Score")), + DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.nerdStats != null) FlSpot(tl.timestamp.millisecondsSinceEpoch.toDouble(), tl.nerdStats!.app)], child: const Text("Attack Per Piece")), + DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.nerdStats != null) FlSpot(tl.timestamp.millisecondsSinceEpoch.toDouble(), tl.nerdStats!.dss)], child: const Text("Downstack Per Second")), + DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.nerdStats != null) FlSpot(tl.timestamp.millisecondsSinceEpoch.toDouble(), tl.nerdStats!.dsp)], child: const Text("Downstack Per Piece")), + DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.nerdStats != null) FlSpot(tl.timestamp.millisecondsSinceEpoch.toDouble(), tl.nerdStats!.appdsp)], child: const Text("APP + DS/P")), + DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.nerdStats != null) FlSpot(tl.timestamp.millisecondsSinceEpoch.toDouble(), tl.nerdStats!.vsapm)], child: const Text("VS/APM")), + DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.nerdStats != null) FlSpot(tl.timestamp.millisecondsSinceEpoch.toDouble(), tl.nerdStats!.cheese)], child: const Text("Cheese Index")), + DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.nerdStats != null) FlSpot(tl.timestamp.millisecondsSinceEpoch.toDouble(), tl.nerdStats!.gbe)], child: const Text("Garbage Efficiency")), + DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.nerdStats != null) FlSpot(tl.timestamp.millisecondsSinceEpoch.toDouble(), tl.nerdStats!.nyaapp)], child: const Text("Weighted APP")), + DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.nerdStats != null) FlSpot(tl.timestamp.millisecondsSinceEpoch.toDouble(), tl.nerdStats!.area)], child: const Text("Area")), + DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.estTr != null) FlSpot(tl.timestamp.millisecondsSinceEpoch.toDouble(), tl.estTr!.esttr)], child: const Text("Est. of TR")), + DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.esttracc != null) FlSpot(tl.timestamp.millisecondsSinceEpoch.toDouble(), tl.esttracc!)], child: const Text("Accuracy of Est.")), ]; tlMatches.addAll(await teto.getTLMatchesbyPlayerID(me.userId)); for (var match in tlStream.records) { @@ -155,7 +162,7 @@ class _MainState extends State with SingleTickerProviderStateMixin { tlMatches = tlStream.records; } Map records = await teto.fetchRecords(me.userId); - return [me, records, states, tlMatches, isTracking]; + return [me, records, states, tlMatches, compareWith, isTracking]; } void _justUpdate() { @@ -289,7 +296,8 @@ class _MainState extends State with SingleTickerProviderStateMixin { children: [ TLThingy( tl: snapshot.data![0].tlSeason1, - userID: snapshot.data![0].userId), + userID: snapshot.data![0].userId, + oldTl: snapshot.data![4],), _TLRecords(userID: snapshot.data![0].userId, data: snapshot.data![3]), _History(states: snapshot.data![2], update: _justUpdate), _RecordThingy( @@ -588,7 +596,8 @@ class _RecordThingy extends StatelessWidget { StatCellNum( playerStat: record!.rank!, playerStatLabel: "Leaderboard Placement", - isScreenBig: bigScreen), + isScreenBig: bigScreen, + higherIsBetter: false), Text("Obtained ${dateFormat.format(record!.timestamp!)}", textAlign: TextAlign.center, style: const TextStyle( @@ -608,46 +617,55 @@ class _RecordThingy extends StatelessWidget { StatCellNum( playerStat: record!.endContext!.level, playerStatLabel: "Level", - isScreenBig: bigScreen), + isScreenBig: bigScreen, + higherIsBetter: true,), if (record!.stream.contains("blitz")) StatCellNum( playerStat: record!.endContext!.spp, playerStatLabel: "Score\nPer Piece", fractionDigits: 2, - isScreenBig: bigScreen), + isScreenBig: bigScreen, + higherIsBetter: true,), StatCellNum( playerStat: record!.endContext!.piecesPlaced, playerStatLabel: "Pieces\nPlaced", - isScreenBig: bigScreen), + isScreenBig: bigScreen, + higherIsBetter: true,), StatCellNum( playerStat: record!.endContext!.pps, playerStatLabel: "Pieces\nPer Second", fractionDigits: 2, - isScreenBig: bigScreen), + isScreenBig: bigScreen, + higherIsBetter: true,), StatCellNum( playerStat: record!.endContext!.finesse.faults, playerStatLabel: "Finesse\nFaults", - isScreenBig: bigScreen), + isScreenBig: bigScreen, + higherIsBetter: false,), StatCellNum( playerStat: record!.endContext!.finessePercentage * 100, playerStatLabel: "Finesse\nPercentage", fractionDigits: 2, - isScreenBig: bigScreen), + isScreenBig: bigScreen, + higherIsBetter: true,), StatCellNum( playerStat: record!.endContext!.inputs, playerStatLabel: "Key\nPresses", - isScreenBig: bigScreen), + isScreenBig: bigScreen, + higherIsBetter: false,), StatCellNum( playerStat: record!.endContext!.kpp, playerStatLabel: "KP Per\nPiece", fractionDigits: 2, - isScreenBig: bigScreen), + isScreenBig: bigScreen, + higherIsBetter: false,), StatCellNum( playerStat: record!.endContext!.kps, playerStatLabel: "KP Per\nSecond", fractionDigits: 2, - isScreenBig: bigScreen), + isScreenBig: bigScreen, + higherIsBetter: true,), ], ), ), diff --git a/lib/widgets/stat_sell_num.dart b/lib/widgets/stat_sell_num.dart index fa5ec29..afa33b7 100644 --- a/lib/widgets/stat_sell_num.dart +++ b/lib/widgets/stat_sell_num.dart @@ -8,9 +8,11 @@ class StatCellNum extends StatelessWidget { required this.playerStatLabel, required this.isScreenBig, this.alertWidgets, - this.fractionDigits}); + this.fractionDigits, this.oldPlayerStat, required this.higherIsBetter}); final num playerStat; + final num? oldPlayerStat; + final bool higherIsBetter; final String playerStatLabel; final bool isScreenBig; final List? alertWidgets; @@ -29,6 +31,11 @@ class StatCellNum extends StatelessWidget { fontSize: isScreenBig ? 32 : 24, ), ), + if (oldPlayerStat != null) Text(NumberFormat("+#,###.###;-#,###.###").format(playerStat - oldPlayerStat!), style: TextStyle( + color: higherIsBetter ? + oldPlayerStat! > playerStat ? Colors.red : Colors.green : + oldPlayerStat! < playerStat ? Colors.red : Colors.green + ),), alertWidgets == null ? Text( playerStatLabel, diff --git a/lib/widgets/tl_thingy.dart b/lib/widgets/tl_thingy.dart index 5670567..56d987e 100644 --- a/lib/widgets/tl_thingy.dart +++ b/lib/widgets/tl_thingy.dart @@ -3,6 +3,7 @@ import 'package:intl/intl.dart'; import 'package:tetra_stats/data_objects/tetrio.dart'; import 'package:fl_chart/fl_chart.dart'; import 'package:syncfusion_flutter_gauges/gauges.dart'; +import 'package:tetra_stats/services/tetrio_crud.dart'; import 'package:tetra_stats/widgets/stat_sell_num.dart'; var fDiff = NumberFormat("+#,###.###;-#,###.###"); @@ -12,7 +13,8 @@ final NumberFormat f3 = NumberFormat.decimalPatternDigits(decimalDigits: 3); class TLThingy extends StatelessWidget { final TetraLeagueAlpha tl; final String userID; - const TLThingy({Key? key, required this.tl, required this.userID}) : super(key: key); + final TetraLeagueAlpha? oldTl; + const TLThingy({Key? key, required this.tl, required this.userID, this.oldTl}) : super(key: key); @override Widget build(BuildContext context) { @@ -26,6 +28,7 @@ class TLThingy extends StatelessWidget { children: (tl.gamesPlayed > 0) ? [ Text("Tetra League", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)), + if (oldTl != null) Text("Comparing with data from ${DateFormat.yMMMd().add_Hms().format(oldTl!.timestamp)}"), if (tl.gamesPlayed >= 10) Wrap( direction: Axis.horizontal, @@ -39,6 +42,15 @@ class TLThingy extends StatelessWidget { Column( children: [ Text("${f2.format(tl.rating)} TR", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)), + if (oldTl != null) Text( + "${fDiff.format(tl.rating - oldTl!.rating)} TR", + textAlign: TextAlign.center, + style: TextStyle( + color: tl.rating - oldTl!.rating < 0 ? + Colors.red : + Colors.green + ), + ), Text( "Top ${f2.format(tl.percentile * 100)}% (${tl.percentileRank.toUpperCase()}) • Top Rank: ${tl.bestRank.toUpperCase()} • Glicko: ${f2.format(tl.glicko!)}±${f2.format(tl.rd!)}${tl.decaying ? ' • Decaying' : ''}", textAlign: TextAlign.center, @@ -80,15 +92,15 @@ class TLThingy extends StatelessWidget { clipBehavior: Clip.hardEdge, children: [ if (tl.apm != null) - StatCellNum(playerStat: tl.apm!, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: "Attack\nPer Minute"), + StatCellNum(playerStat: tl.apm!, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: "Attack\nPer Minute", higherIsBetter: true, oldPlayerStat: oldTl?.apm), if (tl.pps != null) - StatCellNum(playerStat: tl.pps!, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: "Pieces\nPer Second"), - if (tl.apm != null) StatCellNum(playerStat: tl.vs!, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: "Versus\nScore"), - if (tl.standing > 0) StatCellNum(playerStat: tl.standing, isScreenBig: bigScreen, playerStatLabel: "Leaderboard\nplacement"), - if (tl.standingLocal > 0) StatCellNum(playerStat: tl.standingLocal, isScreenBig: bigScreen, playerStatLabel: "Country LB\nplacement"), - StatCellNum(playerStat: tl.gamesPlayed, isScreenBig: bigScreen, playerStatLabel: "Games\nplayed"), - StatCellNum(playerStat: tl.gamesWon, isScreenBig: bigScreen, playerStatLabel: "Games\nwon"), - StatCellNum(playerStat: tl.winrate * 100, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: "Winrate\nprecentage"), + StatCellNum(playerStat: tl.pps!, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: "Pieces\nPer Second", higherIsBetter: true, oldPlayerStat: oldTl?.pps), + if (tl.vs != null) StatCellNum(playerStat: tl.vs!, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: "Versus\nScore", higherIsBetter: true, oldPlayerStat: oldTl?.vs), + if (tl.standing > 0) StatCellNum(playerStat: tl.standing, isScreenBig: bigScreen, playerStatLabel: "Leaderboard\nplacement", higherIsBetter: false, oldPlayerStat: oldTl?.standing), + if (tl.standingLocal > 0) StatCellNum(playerStat: tl.standingLocal, isScreenBig: bigScreen, playerStatLabel: "Country LB\nplacement", higherIsBetter: false, oldPlayerStat: oldTl?.standingLocal), + StatCellNum(playerStat: tl.gamesPlayed, isScreenBig: bigScreen, playerStatLabel: "Games\nplayed", higherIsBetter: true, oldPlayerStat: oldTl?.gamesPlayed), + StatCellNum(playerStat: tl.gamesWon, isScreenBig: bigScreen, playerStatLabel: "Games\nwon", higherIsBetter: true, oldPlayerStat: oldTl?.gamesWon), + StatCellNum(playerStat: tl.winrate * 100, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: "Winrate\nprecentage", higherIsBetter: true, oldPlayerStat: oldTl != null ? oldTl!.winrate*100 : null), ], ), ), @@ -116,7 +128,7 @@ class TLThingy extends StatelessWidget { showLabels: false, showTicks: false, radiusFactor: 2.1, - centerY: 0.3, + centerY: 0.4, minimum: 0, maximum: 1, ranges: [ @@ -136,7 +148,37 @@ class TLThingy extends StatelessWidget { 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: Text(f3.format(tl.nerdStats!.app), style: const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 36)))], + annotations: [GaugeAnnotation( + widget: TextButton(child: Text(f3.format(tl.nerdStats!.app), + style: const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 36, color: Colors.white)), + onPressed: (){ + showDialog( + context: context, + builder: (BuildContext context) => AlertDialog( + title: const Text("Attack Per Piece", + style: TextStyle( + fontFamily: "Eurostile Round Extended")), + content: SingleChildScrollView( + child: ListBody(children: [ + const Text("Main efficiency metric. Tells how many attack you producing per piece"), + Text("Raw value: ${tl.nerdStats!.app}") + ]), + ), + actions: [ + TextButton( + child: const Text('OK'), + onPressed: () { + Navigator.of(context).pop(); + }, + ) + ], + )); + },), verticalAlignment: GaugeAlignment.far, positionFactor: 0.05,), + if (oldTl != null) GaugeAnnotation(widget: Text(fDiff.format(tl.nerdStats!.app - oldTl!.nerdStats!.app), style: TextStyle( + color: tl.nerdStats!.app - oldTl!.nerdStats!.app < 0 ? + Colors.red : + Colors.green + ),), positionFactor: 0.05,)], )],), ), SizedBox( @@ -150,7 +192,7 @@ class TLThingy extends StatelessWidget { showTicks: false, showLabels: false, radiusFactor: 2.1, - centerY: 0.3, + centerY: 0.4, minimum: 1.8, maximum: 2.4, ranges: [ @@ -168,7 +210,36 @@ class TLThingy extends StatelessWidget { 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: Text(f3.format(tl.nerdStats!.vsapm), style: const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 36)))], + annotations: [GaugeAnnotation( + widget: TextButton(child: Text(f3.format(tl.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: const SingleChildScrollView( + child: ListBody(children: [ + Text("Basically, tells how much and how efficient you using garbage in your attacks") + ]), + ), + actions: [ + TextButton( + child: const Text('OK'), + onPressed: () { + Navigator.of(context).pop(); + }, + ) + ], + )); + },), verticalAlignment: GaugeAlignment.far, positionFactor: 0.05,), + if (oldTl != null) GaugeAnnotation(widget: Text(fDiff.format(tl.nerdStats!.vsapm - oldTl!.nerdStats!.vsapm), style: TextStyle( + color: tl.nerdStats!.vsapm - oldTl!.nerdStats!.vsapm < 0 ? + Colors.red : + Colors.green + ),), positionFactor: 0.05,)], )],), ),]), ), @@ -179,37 +250,48 @@ class TLThingy extends StatelessWidget { crossAxisAlignment: WrapCrossAlignment.start, clipBehavior: Clip.hardEdge, children: [ - //StatCellNum(playerStat: tl.nerdStats!.app, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: "Attack\nPer Piece"), - //StatCellNum(playerStat: tl.nerdStats!.vsapm, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: "VS/APM"), StatCellNum(playerStat: tl.nerdStats!.dss, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: "Downstack\nPer Second", alertWidgets: [const Text("Downstack per Second measures how many garbage lines you clear in a second."), const Text("Formula: (VS / 100) - (APM / 60)"), - Text("(${tl.vs} / 100) - (${tl.apm} / 60) = ${tl.nerdStats!.dss}"),], - ), + Text("Raw value: ${tl.nerdStats!.dss}"),], + higherIsBetter: true, + oldPlayerStat: oldTl?.nerdStats?.dss,), StatCellNum(playerStat: tl.nerdStats!.dsp, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: "Downstack\nPer Piece", alertWidgets: [const Text("Downstack per Piece measures how many garbage lines you clear per piece."), const Text("Formula: DS/S / PPS"), - Text("${tl.nerdStats!.dss} / ${tl.pps} = ${tl.nerdStats!.dsp}"),],), + Text("Raw value: ${tl.nerdStats!.dsp}"),], + higherIsBetter: true, + oldPlayerStat: oldTl?.nerdStats?.dsp,), StatCellNum(playerStat: tl.nerdStats!.appdsp, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: "APP + DS/P", alertWidgets: [const Text("Just a sum of Attack per Piece and Downstack per Piece."), const Text("Formula: APP + DS/P"), - Text("${tl.nerdStats!.app} + ${tl.nerdStats!.dsp} = ${tl.nerdStats!.appdsp}"),]), + Text("Raw value: ${tl.nerdStats!.appdsp}"),], + higherIsBetter: true, + oldPlayerStat: oldTl?.nerdStats?.appdsp,), StatCellNum(playerStat: tl.nerdStats!.cheese, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: "Cheese\nIndex", alertWidgets: [const Text("Cheese Index is an approximation how much clean / cheese garbage player sends. Lower = more clean. Higher = more cheese.\nInvented by kerrmunism"), const Text("Formula: (DS/P * 150) + ((VS/APM - 2) * 50) + (0.6 - APP) * 125"), - Text("(${tl.nerdStats!.dsp} * 150) + ((${tl.nerdStats!.vsapm} - 2) * 50) + (0.6 - ${tl.nerdStats!.app}) * 125 = ${tl.nerdStats!.cheese}"),]), + Text("Raw value: ${tl.nerdStats!.cheese}"),], + higherIsBetter: true, + oldPlayerStat: oldTl?.nerdStats?.cheese,), StatCellNum(playerStat: tl.nerdStats!.gbe, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: "Garbage\nEfficiency", alertWidgets: [const Text("Garbage Efficiency measures how well player uses their garbage. Higher = better or they use their garbage more. Lower = they mostly send their garbage back at cheese or rarely clear garbage.\nInvented by Zepheniah and Dragonboy."), const Text("Formula: ((APP * DS/S) / PPS) * 2"), - Text("((${tl.nerdStats!.app} * ${tl.nerdStats!.dss}) / ${tl.pps}) * 2 = ${tl.nerdStats!.gbe}"),]), + Text("Raw value: ${tl.nerdStats!.gbe}"),], + higherIsBetter: true, + oldPlayerStat: oldTl?.nerdStats?.gbe,), StatCellNum(playerStat: tl.nerdStats!.nyaapp, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: "Weighted\nAPP", alertWidgets: [const Text("Essentially, a measure of your ability to send cheese while still maintaining a high APP.\nInvented by Wertj."), const Text("Formula: APP - 5 * tan(radians((Cheese Index / -30) + 1))"), - Text("${tl.nerdStats!.app} - 5 * tan(radians((${tl.nerdStats!.cheese} / -30) + 1)) = ${tl.nerdStats!.nyaapp}"),]), + Text("Raw value: ${tl.nerdStats!.nyaapp}"),], + higherIsBetter: true, + oldPlayerStat: oldTl?.nerdStats?.nyaapp,), StatCellNum(playerStat: tl.nerdStats!.area, isScreenBig: bigScreen, fractionDigits: 1, playerStatLabel: "Area", alertWidgets: [const Text("How much space your shape takes up on the graph, if you exclude the cheese and vs/apm sections"), const Text("Formula: APM * 1 + PPS * 45 + VS * 0.444 + APP * 185 + DS/S * 175 + DS/P * 450 + Garbage Effi * 315"), - Text("${tl.apm} * 1 + ${tl.pps} * 45 + ${tl.vs} * 0.444 + ${tl.nerdStats!.app} * 185 + ${tl.nerdStats!.dss} * 175 + ${tl.nerdStats!.dsp} * 450 + ${tl.nerdStats!.gbe} * 315 = ${tl.nerdStats!.area}"),]) + Text("Raw value: ${tl.nerdStats!.area}"),], + higherIsBetter: true, + oldPlayerStat: oldTl?.nerdStats?.area,) ]) ], ), diff --git a/lib/widgets/user_thingy.dart b/lib/widgets/user_thingy.dart index cf3469a..9eff288 100644 --- a/lib/widgets/user_thingy.dart +++ b/lib/widgets/user_thingy.dart @@ -176,13 +176,33 @@ class UserThingy extends StatelessWidget { playerStatLabel: "XP Level", isScreenBig: bigScreen, alertWidgets: [Text("${NumberFormat.decimalPatternDigits(decimalDigits: 2).format(player.xp)} XP", style: const TextStyle(fontFamily: "Eurostile Round Extended"),), Text("Progress to next level: ${((player.level - player.level.floor()) * 100).toStringAsFixed(2)} %"), Text("Progress from 0 XP to level 5000: ${((player.xp / 67009017.7589378) * 100).toStringAsFixed(2)} %")], + higherIsBetter: true, ), if (player.gameTime >= Duration.zero) StatCellNum( - playerStat: player.gameTime.inHours, playerStatLabel: "Hours\nPlayed", isScreenBig: bigScreen, alertWidgets: [Text("Exact gametime: ${player.gameTime.toString()}")]), - if (player.gamesPlayed >= 0) StatCellNum(playerStat: player.gamesPlayed, isScreenBig: bigScreen, playerStatLabel: "Online\nGames"), - if (player.gamesWon >= 0) StatCellNum(playerStat: player.gamesWon, isScreenBig: bigScreen, playerStatLabel: "Games\nWon"), - if (player.friendCount > 0) StatCellNum(playerStat: player.friendCount, isScreenBig: bigScreen, playerStatLabel: "Friends"), + playerStat: player.gameTime.inHours, + playerStatLabel: "Hours\nPlayed", + isScreenBig: bigScreen, + alertWidgets: [Text("Exact gametime: ${player.gameTime.toString()}")], + higherIsBetter: true,), + if (player.gamesPlayed >= 0) + StatCellNum( + playerStat: player.gamesPlayed, + isScreenBig: bigScreen, + playerStatLabel: "Online\nGames", + higherIsBetter: true,), + if (player.gamesWon >= 0) + StatCellNum( + playerStat: player.gamesWon, + isScreenBig: bigScreen, + playerStatLabel: "Games\nWon", + higherIsBetter: true,), + if (player.friendCount > 0) + StatCellNum( + playerStat: player.friendCount, + isScreenBig: bigScreen, + playerStatLabel: "Friends", + higherIsBetter: true,), ], ) : Text(