Delta for Tetra League

This commit is contained in:
dan63047 2023-06-28 19:50:40 +03:00
parent 3199bd26ec
commit fd78cf2301
6 changed files with 215 additions and 65 deletions

View File

@ -100,7 +100,7 @@ class TetrioPlayer {
country = json['country']; country = json['country'];
supporterTier = json['supporter_tier']; supporterTier = json['supporter_tier'];
verified = json['verified']; verified = json['verified'];
tlSeason1 = TetraLeagueAlpha.fromJson(json['league']); tlSeason1 = TetraLeagueAlpha.fromJson(json['league'], stateTime);
avatarRevision = json['avatar_revision']; avatarRevision = json['avatar_revision'];
bannerRevision = json['banner_revision']; bannerRevision = json['banner_revision'];
bio = json['bio']; bio = json['bio'];
@ -479,12 +479,13 @@ class EstTr {
late double esttr; late double esttr;
late double srarea; late double srarea;
late double statrank; late double statrank;
late double estglicko;
EstTr(this._apm, this._pps, this._vs, this._rd, this._app, this._dss, this._dsp, this._gbe) { 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); srarea = (_apm * 0) + (_pps * 135) + (_vs * 0) + (_app * 290) + (_dss * 0) + (_dsp * 700) + (_gbe * 0);
statrank = 11.2 * atan((srarea - 93) / 130) + 1; statrank = 11.2 * atan((srarea - 93) / 130) + 1;
if (statrank <= 0) statrank = 0.001; 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 temp = (1500 - estglicko) * pi;
double temp2 = pow((15.9056943314 * (pow(_rd, 2)) + 3527584.25978), 0.5) as double; double temp2 = pow((15.9056943314 * (pow(_rd, 2)) + 3527584.25978), 0.5) as double;
double temp3 = 1 + pow(10, (temp / temp2)) as double; double temp3 = 1 + pow(10, (temp / temp2)) as double;
@ -644,6 +645,7 @@ class EndContextMulti {
} }
class TetraLeagueAlpha { class TetraLeagueAlpha {
late DateTime timestamp;
late int gamesPlayed; late int gamesPlayed;
late int gamesWon; late int gamesWon;
late String bestRank; late String bestRank;
@ -692,7 +694,8 @@ class TetraLeagueAlpha {
double get winrate => gamesWon / gamesPlayed; double get winrate => gamesWon / gamesPlayed;
TetraLeagueAlpha.fromJson(Map<String, dynamic> json) { TetraLeagueAlpha.fromJson(Map<String, dynamic> json, ts) {
timestamp = ts;
gamesPlayed = json['gamesplayed']; gamesPlayed = json['gamesplayed'];
gamesWon = json['gameswon']; gamesWon = json['gameswon'];
rating = json['rating'].toDouble(); rating = json['rating'].toDouble();
@ -833,14 +836,16 @@ class Distinguishment {
class TetrioPlayersLeaderboard { class TetrioPlayersLeaderboard {
late String type; late String type;
late DateTime timestamp;
late List<TetrioPlayerFromLeaderboard> leaderboard; late List<TetrioPlayerFromLeaderboard> leaderboard;
TetrioPlayersLeaderboard(this.type, this.leaderboard); TetrioPlayersLeaderboard(this.type, this.leaderboard);
TetrioPlayersLeaderboard.fromJson(Map<String, dynamic> json, String type) { TetrioPlayersLeaderboard.fromJson(Map<String, dynamic> json, String type, DateTime ts) {
type = type; type = type;
timestamp = ts;
for (Map<String, dynamic> entry in json['users']) { for (Map<String, dynamic> 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(this.userId, this.username, this.role, this.xp, this.country, this.supporter, this.verified, this.league);
TetrioPlayerFromLeaderboard.fromJson(Map<String, dynamic> json) { TetrioPlayerFromLeaderboard.fromJson(Map<String, dynamic> json, DateTime ts) {
userId = json['_id']; userId = json['_id'];
username = json['username']; username = json['username'];
role = json['role']; role = json['role'];
@ -865,6 +870,6 @@ class TetrioPlayerFromLeaderboard {
country = json['country ']; country = json['country '];
supporter = json['supporter']; supporter = json['supporter'];
verified = json['verified']; verified = json['verified'];
league = TetraLeagueAlpha.fromJson(json['league']); league = TetraLeagueAlpha.fromJson(json['league'], ts);
} }
} }

View File

@ -10,7 +10,7 @@ List<DropdownMenuItem<TetrioPlayer>>? greenSideStates;
TetrioPlayer? theRedSide; TetrioPlayer? theRedSide;
List<DropdownMenuItem<TetrioPlayer>>? redSideStates; List<DropdownMenuItem<TetrioPlayer>>? redSideStates;
final TetrioService teto = TetrioService(); final TetrioService teto = TetrioService();
final DateFormat dateFormat = DateFormat.yMMMd().add_Hms(); final DateFormat dateFormat = DateFormat.yMd().add_Hm();
class CompareView extends StatefulWidget { class CompareView extends StatefulWidget {
final TetrioPlayer greenSide; final TetrioPlayer greenSide;
@ -794,6 +794,23 @@ class CompareState extends State<CompareView> {
fractionDigits: 2, fractionDigits: 2,
higherIsBetter: true, 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( child: DropdownButton(
items: states, items: states,
value: player, value: player,
//style: TextStyle(overflow: TextOverflow.clip),
onChanged: (value) => change(value!), onChanged: (value) => change(value!),
), ),
) )

View File

@ -118,28 +118,35 @@ class _MainState extends State<MainView> with SingleTickerProviderStateMixin {
List<TetraLeagueAlphaRecord> tlMatches = []; List<TetraLeagueAlphaRecord> tlMatches = [];
bool isTracking = await teto.isPlayerTracking(me.userId); bool isTracking = await teto.isPlayerTracking(me.userId);
List<TetrioPlayer> states = []; List<TetrioPlayer> states = [];
TetraLeagueAlpha? compareWith = null;
var uniqueTL = Set();
if (isTracking){ if (isTracking){
teto.storeState(me); teto.storeState(me);
teto.saveTLMatchesFromStream(await teto.getTLStream(me.userId)); teto.saveTLMatchesFromStream(await teto.getTLStream(me.userId));
states.addAll(await teto.getPlayer(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<List<FlSpot>>>[ chartsData = <DropdownMenuItem<List<FlSpot>>>[
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 tl in uniqueTL) if (tl.gamesPlayed > 9) FlSpot(tl.timestamp.millisecondsSinceEpoch.toDouble(), tl.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 tl in uniqueTL) if (tl.gamesPlayed > 9) FlSpot(tl.timestamp.millisecondsSinceEpoch.toDouble(), tl.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 tl in uniqueTL) if (tl.gamesPlayed > 9) FlSpot(tl.timestamp.millisecondsSinceEpoch.toDouble(), tl.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 tl in uniqueTL) if (tl.apm != null) FlSpot(tl.timestamp.millisecondsSinceEpoch.toDouble(), tl.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 tl in uniqueTL) if (tl.pps != null) FlSpot(tl.timestamp.millisecondsSinceEpoch.toDouble(), tl.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 tl in uniqueTL) if (tl.vs != null) FlSpot(tl.timestamp.millisecondsSinceEpoch.toDouble(), tl.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 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 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 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 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 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 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 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 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 tl in uniqueTL) if (tl.nerdStats != null) FlSpot(tl.timestamp.millisecondsSinceEpoch.toDouble(), tl.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 tl in uniqueTL) if (tl.nerdStats != null) FlSpot(tl.timestamp.millisecondsSinceEpoch.toDouble(), tl.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 tl in uniqueTL) if (tl.nerdStats != null) FlSpot(tl.timestamp.millisecondsSinceEpoch.toDouble(), tl.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 tl in uniqueTL) if (tl.nerdStats != null) FlSpot(tl.timestamp.millisecondsSinceEpoch.toDouble(), tl.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 tl in uniqueTL) if (tl.nerdStats != null) FlSpot(tl.timestamp.millisecondsSinceEpoch.toDouble(), tl.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 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 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.esttracc != null) FlSpot(tl.timestamp.millisecondsSinceEpoch.toDouble(), tl.esttracc!)], child: const Text("Accuracy of Est.")),
]; ];
tlMatches.addAll(await teto.getTLMatchesbyPlayerID(me.userId)); tlMatches.addAll(await teto.getTLMatchesbyPlayerID(me.userId));
for (var match in tlStream.records) { for (var match in tlStream.records) {
@ -155,7 +162,7 @@ class _MainState extends State<MainView> with SingleTickerProviderStateMixin {
tlMatches = tlStream.records; tlMatches = tlStream.records;
} }
Map<String, dynamic> records = await teto.fetchRecords(me.userId); Map<String, dynamic> records = await teto.fetchRecords(me.userId);
return [me, records, states, tlMatches, isTracking]; return [me, records, states, tlMatches, compareWith, isTracking];
} }
void _justUpdate() { void _justUpdate() {
@ -289,7 +296,8 @@ class _MainState extends State<MainView> with SingleTickerProviderStateMixin {
children: [ children: [
TLThingy( TLThingy(
tl: snapshot.data![0].tlSeason1, 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]), _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( _RecordThingy(
@ -588,7 +596,8 @@ class _RecordThingy extends StatelessWidget {
StatCellNum( StatCellNum(
playerStat: record!.rank!, playerStat: record!.rank!,
playerStatLabel: "Leaderboard Placement", playerStatLabel: "Leaderboard Placement",
isScreenBig: bigScreen), isScreenBig: bigScreen,
higherIsBetter: false),
Text("Obtained ${dateFormat.format(record!.timestamp!)}", Text("Obtained ${dateFormat.format(record!.timestamp!)}",
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: const TextStyle( style: const TextStyle(
@ -608,46 +617,55 @@ class _RecordThingy extends StatelessWidget {
StatCellNum( StatCellNum(
playerStat: record!.endContext!.level, playerStat: record!.endContext!.level,
playerStatLabel: "Level", playerStatLabel: "Level",
isScreenBig: bigScreen), isScreenBig: bigScreen,
higherIsBetter: true,),
if (record!.stream.contains("blitz")) if (record!.stream.contains("blitz"))
StatCellNum( StatCellNum(
playerStat: record!.endContext!.spp, playerStat: record!.endContext!.spp,
playerStatLabel: "Score\nPer Piece", playerStatLabel: "Score\nPer Piece",
fractionDigits: 2, fractionDigits: 2,
isScreenBig: bigScreen), isScreenBig: bigScreen,
higherIsBetter: true,),
StatCellNum( StatCellNum(
playerStat: record!.endContext!.piecesPlaced, playerStat: record!.endContext!.piecesPlaced,
playerStatLabel: "Pieces\nPlaced", playerStatLabel: "Pieces\nPlaced",
isScreenBig: bigScreen), isScreenBig: bigScreen,
higherIsBetter: true,),
StatCellNum( StatCellNum(
playerStat: record!.endContext!.pps, playerStat: record!.endContext!.pps,
playerStatLabel: "Pieces\nPer Second", playerStatLabel: "Pieces\nPer Second",
fractionDigits: 2, fractionDigits: 2,
isScreenBig: bigScreen), isScreenBig: bigScreen,
higherIsBetter: true,),
StatCellNum( StatCellNum(
playerStat: record!.endContext!.finesse.faults, playerStat: record!.endContext!.finesse.faults,
playerStatLabel: "Finesse\nFaults", playerStatLabel: "Finesse\nFaults",
isScreenBig: bigScreen), isScreenBig: bigScreen,
higherIsBetter: false,),
StatCellNum( StatCellNum(
playerStat: playerStat:
record!.endContext!.finessePercentage * 100, record!.endContext!.finessePercentage * 100,
playerStatLabel: "Finesse\nPercentage", playerStatLabel: "Finesse\nPercentage",
fractionDigits: 2, fractionDigits: 2,
isScreenBig: bigScreen), isScreenBig: bigScreen,
higherIsBetter: true,),
StatCellNum( StatCellNum(
playerStat: record!.endContext!.inputs, playerStat: record!.endContext!.inputs,
playerStatLabel: "Key\nPresses", playerStatLabel: "Key\nPresses",
isScreenBig: bigScreen), isScreenBig: bigScreen,
higherIsBetter: false,),
StatCellNum( StatCellNum(
playerStat: record!.endContext!.kpp, playerStat: record!.endContext!.kpp,
playerStatLabel: "KP Per\nPiece", playerStatLabel: "KP Per\nPiece",
fractionDigits: 2, fractionDigits: 2,
isScreenBig: bigScreen), isScreenBig: bigScreen,
higherIsBetter: false,),
StatCellNum( StatCellNum(
playerStat: record!.endContext!.kps, playerStat: record!.endContext!.kps,
playerStatLabel: "KP Per\nSecond", playerStatLabel: "KP Per\nSecond",
fractionDigits: 2, fractionDigits: 2,
isScreenBig: bigScreen), isScreenBig: bigScreen,
higherIsBetter: true,),
], ],
), ),
), ),

View File

@ -8,9 +8,11 @@ class StatCellNum extends StatelessWidget {
required this.playerStatLabel, required this.playerStatLabel,
required this.isScreenBig, required this.isScreenBig,
this.alertWidgets, this.alertWidgets,
this.fractionDigits}); this.fractionDigits, this.oldPlayerStat, required this.higherIsBetter});
final num playerStat; final num playerStat;
final num? oldPlayerStat;
final bool higherIsBetter;
final String playerStatLabel; final String playerStatLabel;
final bool isScreenBig; final bool isScreenBig;
final List<Widget>? alertWidgets; final List<Widget>? alertWidgets;
@ -29,6 +31,11 @@ class StatCellNum extends StatelessWidget {
fontSize: isScreenBig ? 32 : 24, 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 alertWidgets == null
? Text( ? Text(
playerStatLabel, playerStatLabel,

View File

@ -3,6 +3,7 @@ import 'package:intl/intl.dart';
import 'package:tetra_stats/data_objects/tetrio.dart'; import 'package:tetra_stats/data_objects/tetrio.dart';
import 'package:fl_chart/fl_chart.dart'; import 'package:fl_chart/fl_chart.dart';
import 'package:syncfusion_flutter_gauges/gauges.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'; import 'package:tetra_stats/widgets/stat_sell_num.dart';
var fDiff = NumberFormat("+#,###.###;-#,###.###"); var fDiff = NumberFormat("+#,###.###;-#,###.###");
@ -12,7 +13,8 @@ final NumberFormat f3 = NumberFormat.decimalPatternDigits(decimalDigits: 3);
class TLThingy extends StatelessWidget { class TLThingy extends StatelessWidget {
final TetraLeagueAlpha tl; final TetraLeagueAlpha tl;
final String userID; 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 @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -26,6 +28,7 @@ class TLThingy extends StatelessWidget {
children: (tl.gamesPlayed > 0) children: (tl.gamesPlayed > 0)
? [ ? [
Text("Tetra League", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)), 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) if (tl.gamesPlayed >= 10)
Wrap( Wrap(
direction: Axis.horizontal, direction: Axis.horizontal,
@ -39,6 +42,15 @@ class TLThingy extends StatelessWidget {
Column( Column(
children: [ children: [
Text("${f2.format(tl.rating)} TR", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)), 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( 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' : ''}", "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, textAlign: TextAlign.center,
@ -80,15 +92,15 @@ class TLThingy extends StatelessWidget {
clipBehavior: Clip.hardEdge, clipBehavior: Clip.hardEdge,
children: [ children: [
if (tl.apm != null) 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) if (tl.pps != null)
StatCellNum(playerStat: tl.pps!, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: "Pieces\nPer Second"), StatCellNum(playerStat: tl.pps!, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: "Pieces\nPer Second", higherIsBetter: true, oldPlayerStat: oldTl?.pps),
if (tl.apm != null) StatCellNum(playerStat: tl.vs!, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: "Versus\nScore"), 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"), 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"), 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"), StatCellNum(playerStat: tl.gamesPlayed, isScreenBig: bigScreen, playerStatLabel: "Games\nplayed", higherIsBetter: true, oldPlayerStat: oldTl?.gamesPlayed),
StatCellNum(playerStat: tl.gamesWon, isScreenBig: bigScreen, playerStatLabel: "Games\nwon"), 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"), 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, showLabels: false,
showTicks: false, showTicks: false,
radiusFactor: 2.1, radiusFactor: 2.1,
centerY: 0.3, centerY: 0.4,
minimum: 0, minimum: 0,
maximum: 1, maximum: 1,
ranges: [ ranges: [
@ -136,7 +148,37 @@ class TLThingy extends StatelessWidget {
knobStyle: const KnobStyle(color: Colors.transparent), knobStyle: const KnobStyle(color: Colors.transparent),
gradient: const LinearGradient(colors: [Colors.transparent, Colors.white], begin: Alignment.bottomCenter, end: Alignment.topCenter, stops: [0.5, 1]),) 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: <Widget>[
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( SizedBox(
@ -150,7 +192,7 @@ class TLThingy extends StatelessWidget {
showTicks: false, showTicks: false,
showLabels: false, showLabels: false,
radiusFactor: 2.1, radiusFactor: 2.1,
centerY: 0.3, centerY: 0.4,
minimum: 1.8, minimum: 1.8,
maximum: 2.4, maximum: 2.4,
ranges: [ ranges: [
@ -168,7 +210,36 @@ class TLThingy extends StatelessWidget {
knobStyle: const KnobStyle(color: Colors.transparent), knobStyle: const KnobStyle(color: Colors.transparent),
gradient: const LinearGradient(colors: [Colors.transparent, Colors.white], begin: Alignment.bottomCenter, end: Alignment.topCenter, stops: [0.5, 1]),) 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: <Widget>[
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, crossAxisAlignment: WrapCrossAlignment.start,
clipBehavior: Clip.hardEdge, clipBehavior: Clip.hardEdge,
children: [ 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", 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."), alertWidgets: [const Text("Downstack per Second measures how many garbage lines you clear in a second."),
const Text("Formula: (VS / 100) - (APM / 60)"), 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", 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."), alertWidgets: [const Text("Downstack per Piece measures how many garbage lines you clear per piece."),
const Text("Formula: DS/S / PPS"), 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", 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."), alertWidgets: [const Text("Just a sum of Attack per Piece and Downstack per Piece."),
const Text("Formula: APP + DS/P"), 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", 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"), 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"), 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", 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."), 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"), 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", 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."), 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))"), 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", 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"), 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"), 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,)
]) ])
], ],
), ),

View File

@ -176,13 +176,33 @@ class UserThingy extends StatelessWidget {
playerStatLabel: "XP Level", playerStatLabel: "XP Level",
isScreenBig: bigScreen, 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)} %")], 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) if (player.gameTime >= Duration.zero)
StatCellNum( StatCellNum(
playerStat: player.gameTime.inHours, playerStatLabel: "Hours\nPlayed", isScreenBig: bigScreen, alertWidgets: [Text("Exact gametime: ${player.gameTime.toString()}")]), playerStat: player.gameTime.inHours,
if (player.gamesPlayed >= 0) StatCellNum(playerStat: player.gamesPlayed, isScreenBig: bigScreen, playerStatLabel: "Online\nGames"), playerStatLabel: "Hours\nPlayed",
if (player.gamesWon >= 0) StatCellNum(playerStat: player.gamesWon, isScreenBig: bigScreen, playerStatLabel: "Games\nWon"), isScreenBig: bigScreen,
if (player.friendCount > 0) StatCellNum(playerStat: player.friendCount, isScreenBig: bigScreen, playerStatLabel: "Friends"), 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( : Text(