diff --git a/lib/data_objects/freyhoe_test.dart b/lib/data_objects/freyhoe_test.dart index 5275e86..6b74a6b 100644 --- a/lib/data_objects/freyhoe_test.dart +++ b/lib/data_objects/freyhoe_test.dart @@ -1,25 +1,551 @@ -//import 'dart:convert'; -//import 'dart:io'; +// import 'dart:convert'; +// import 'dart:developer' as developer; +// import 'dart:ffi'; +// import 'dart:math'; +// import 'package:logging/logging.dart' as logging; +// import 'package:vector_math/vector_math_64.dart'; +// import 'dart:io'; +// import 'tetrio_multiplayer_replay.dart'; -//import 'package:path_provider/path_provider.dart'; +// class HandlingHandler{ +// double das; +// double arr; +// late double dasLeft; // frames +// late double arrLeft; // frames +// bool sdfActive = false; +// bool activeLeft = false; +// bool activeRight = false; +// int direction = 0; // -1 - left, 1 - right, 0 - none -//import 'tetrio_multiplayer_replay.dart'; +// HandlingHandler(this.das, this.arr){ +// dasLeft = das; +// arrLeft = arr; +// } -/// That thing allows me to test my new staff i'm trying to implement -//void main() async { - // List queue = List.from(tetrominoes); - // TetrioRNG rng = TetrioRNG(0); - // queue = rng.shuffleList(queue); - // print(queue); - // queue = List.from(tetrominoes); - // queue = rng.shuffleList(queue); - // print(queue); +// @override +// String toString(){ +// return "das: ${das}f; arr: ${arr}f"; +// } - // var downloadPath = await getDownloadsDirectory(); - // ReplayData replay = ReplayData.fromJson(jsonDecode(File("${downloadPath!.path}/65b504a9ade6d287b8427af0").readAsStringSync())); - // List> board = [for (var i = 0 ; i < 40; i++) [for (var i = 0 ; i < 10; i++) Tetromino.empty]]; - // print(replay.rawJson); +// int movementKeyPressed(bool left, bool right, double subframe){ +// if (left) { +// activeLeft = left; +// direction = -1; +// } +// if (right) { +// activeRight = right; +// direction = 1; +// } +// dasLeft = das - (1 - subframe); +// return direction; +// } - //print(""); +// void movementKeyReleased(bool left, bool right, double subframe){ +// if (left) { +// activeLeft = !left; +// } +// if (right) { +// activeRight = !right; +// } +// if (activeLeft) { +// direction = -1; +// } +// if (activeRight) { +// direction = 1; +// } +// if (activeLeft && activeRight){ +// arrLeft = arr; +// dasLeft = das; +// direction = 0; +// } +// } + +// int processMovenent(double delta){ +// if (!activeLeft && !activeRight) return 0; +// if (dasLeft > 0.0) { +// dasLeft -= delta; +// if (dasLeft < 0.0) { +// arrLeft += dasLeft; +// dasLeft = 0.0; +// return direction; +// }else{ +// return 0; +// } +// }else{ +// arrLeft -= delta; +// if (arr == 0.0) return direction*10; +// if (arrLeft < 0.0) { +// arrLeft += arr; +// return direction; +// }else { +// return 0; +// } +// } +// } +// } + +// class Board{ +// int width; +// int height; +// int bufferHeight; +// late List> board; +// late int totalHeight; + +// Board(this.width, this.height, this.bufferHeight){ +// totalHeight = height+bufferHeight; +// board = [for (var i = 0 ; i < totalHeight; i++) [for (var i = 0 ; i < width; i++) Tetromino.empty]]; +// } + +// @override +// String toString() { +// String result = ""; +// for (var row in board.reversed){ +// for (var cell in row) result += cell.name[0]; +// result += "\n"; +// } +// return result; +// } + +// bool isOccupied(Coords coords) +// { +// if (coords.x < 0 || coords.x >= width || +// coords.y < 0 || coords.y >= totalHeight || +// board[coords.y][coords.x] != Tetromino.empty) return true; +// return false; +// } + +// bool positionIsValid(Tetromino type, Coords coords, int r){ +// List shape = shapes[type.index][r]; +// for (Coords mino in shape){ +// if (isOccupied(coords+mino)) return false; +// } +// return true; +// } + +// bool wasATSpin(Tetromino type, Coords coords, int rot){ +// if (!(positionIsValid(type, Coords(coords.x+1, coords.y), rot) || +// positionIsValid(type, Coords(coords.x-1, coords.y), rot) || +// positionIsValid(type, Coords(coords.x, coords.y+1), rot) || +// positionIsValid(type, Coords(coords.x, coords.y-1), rot)) +// ){ +// return true; +// } +// return false; +// } + +// void writeToBoard(Tetromino type, Coords coords, int rot) { +// if (!positionIsValid(type, coords, rot)) throw Exception("Attempted to write $type to $coords in $rot rot"); +// List shape = shapes[type.index][rot]; +// for (Coords mino in shape){ +// var finalCoords = coords+mino; +// board[finalCoords.y][finalCoords.x] = type; +// } +// } + +// void writeGarbage(GarbageData data, [int? amt]){ +// List> garbage = [for (int i = 0; i < (amt??data.amt!); i++) [for (int k = 0; k < width; k++) k == data.column! ? Tetromino.empty : Tetromino.garbage]]; +// board.insertAll(0, garbage); +// board.removeRange(height-garbage.length, height); +// } + +// List clearFullLines(){ +// int linesCleared = 0; +// int garbageLines = 0; +// int difficultLineClear = 0; +// for (int i = 0; i < board.length; i++){ +// if (board[i-linesCleared].every((element) => element != Tetromino.empty)){ +// if (board[i-linesCleared].any((element) => element == Tetromino.garbage)) garbageLines++; +// board.removeAt(i-linesCleared); +// List emptyRow = [for (int t = 0; t < width; t++) Tetromino.empty]; +// board.add(emptyRow); +// linesCleared += 1; +// } +// } +// if (linesCleared >= 4) difficultLineClear = 1; +// return [linesCleared, garbageLines, difficultLineClear]; +// } +// } + +// class IncomingGarbage{ +// int? frameOfThreat; // will enter board after this frame, null if unconfirmed +// GarbageData data; + +// IncomingGarbage(this.data); + +// @override +// String toString(){ +// return "f$frameOfThreat: col${data.column} amt${data.amt}"; +// } + +// void confirm(int confirmationFrame, int garbageSpeed){ +// frameOfThreat = confirmationFrame+garbageSpeed; +// } +// } + +// class LineClearResult{ +// int linesCleared; +// Tetromino piece; +// bool spin; +// int garbageCleared; +// int column; +// int attackProduced; + +// LineClearResult(this.linesCleared, this.piece, this.spin, this.garbageCleared, this.column, this.attackProduced); +// } + +// class Stats{ +// int combo = -1; +// int btb = -1; +// int attackRecived = 0; +// int attackTanked = 0; + +// LineClearResult processLineClear(List clearFullLinesResult, Tetromino current, Coords pos, bool spinWasLastMove, bool tspin){ +// if (clearFullLinesResult[0] > 0) combo++; +// else combo = -1; +// if (clearFullLinesResult[2] > 0) btb++; +// else btb = -1; +// int attack = 0; +// switch (clearFullLinesResult[0]){ +// case 0: +// if (spinWasLastMove && tspin) { +// attack = garbage['t-spin']!; +// } +// break; +// case 1: +// if (spinWasLastMove && tspin) { +// attack = garbage['t-spin single']!; +// }else{ +// attack = garbage['single']!; +// } +// break; +// case 2: +// if (spinWasLastMove && tspin) { +// attack = garbage['t-spin double']!; +// }else{ +// attack = garbage['double']!; +// } +// break; +// case 3: +// if (spinWasLastMove && tspin) { +// attack = garbage['t-spin triple']!; +// }else{ +// attack = garbage['triple']!; +// } +// break; +// case 4: +// if (spinWasLastMove && tspin) { +// attack = garbage['t-spin quad']!; +// }else{ +// attack = garbage['quad']!; +// } +// break; +// case 5: +// if (spinWasLastMove && tspin) { +// attack = garbage['t-spin penta']!; +// }else{ +// attack = garbage['penta']!; +// } +// break; +// case _: +// developer.log("${clearFullLinesResult[0]} lines cleared"); +// break; +// } +// return LineClearResult(clearFullLinesResult[0], Tetromino.empty, false, clearFullLinesResult[1], 0, attack); +// } +// } + +// class Simulation{ + +// } + +// // That thing allows me to test my new staff i'm trying to implement +// void main() async { +// var replayJson = jsonDecode(File("/home/dan63047/Документы/replays/6550eecf2ffc5604e6224fc5.ttrm").readAsStringSync()); +// // frame 994: garbage lost +// // frame 1550: T-spin failed +// ReplayData replay = ReplayData.fromJson(replayJson); +// TetrioRNG rng = TetrioRNG(replay.stats[0][0].seed); +// List queue = rng.shuffleList(tetrominoes.toList()); +// List events = readEventList(replay.rawJson); +// DataFullOptions? settings; +// HandlingHandler? handling; +// Map activeKeypresses = {}; +// int currentFrame = 0; +// double subframesWent = 0; +// events.removeAt(0); // get rig of Event.start +// Event nextEvent = events.removeAt(0); +// Stats stats = Stats(); +// Board board = Board(10, 20, 20); +// KicksetBase kickset = SRSPlus(); +// List garbageQueue = []; +// Tetromino? hold; +// int rot = 0; +// bool spinWasLastMove = false; +// Coords coords = Coords(3, 21); +// double lockDelay = 30; // frames +// int lockResets = 15; +// bool floored = false; +// double gravityBucket = 1.0; + +// developer.log("Seed is ${replay.stats[0][0].seed}, first bag is $queue"); +// Tetromino current = queue.removeAt(0); +// //developer.log("Second bag is ${rng.shuffleList(tetrominoes)}"); + +// int sonicDrop(){ +// int height = coords.y; +// while (board.positionIsValid(current, Coords(coords.x, height), rot)){ +// height -= 1; +// } +// height += 1; +// return height; +// } + +// Tetromino getNewOne(){ + +// if (queue.length <= 1) { +// List nextPieces = rng.shuffleList(tetrominoes.toList()); +// queue.addAll(nextPieces); +// } +// //developer.log("Next queue is $queue"); +// rot = 0; +// lockResets = 15; +// lockDelay = 30; +// floored = false; +// gravityBucket = 1.0; +// return queue.removeAt(0); +// } + +// bool handleRotation(int r){ +// if (r == 0) return true; +// int futureRotation = (rot + r) % 4; +// List tests = (current == Tetromino.I ? kickset.kickTableI : kickset.kickTable)[rot][r == -1 ? 0 : r]; +// for (Coords test in tests){ +// if (board.positionIsValid(current, coords+test, futureRotation)){ +// coords += test; +// rot = futureRotation; +// return true; +// } +// } +// return false; +// } + +// void handleHardDrop(){ +// board.writeToBoard(current, coords, rot); +// bool tspin = board.wasATSpin(current, coords, rot); +// LineClearResult lineClear = stats.processLineClear(board.clearFullLines(), current, coords, spinWasLastMove, tspin); +// print("${lineClear.linesCleared} lines, ${lineClear.garbageCleared} garbage"); +// if (garbageQueue.isNotEmpty) { +// if (lineClear.linesCleared > 0){ +// int garbageToDelete = lineClear.attackProduced; +// for (IncomingGarbage garbage in garbageQueue){ +// if (garbage.data.amt! >= garbageToDelete) { +// garbageToDelete -= garbage.data.amt!; +// lineClear.attackProduced = garbageToDelete; +// garbage.data.amt = 0; +// }else{ +// garbage.data.amt = garbage.data.amt! - garbageToDelete; +// lineClear.attackProduced = 0; +// break; +// } +// } +// }else{ +// int needToTake = settings!.garbageCap!; +// for (IncomingGarbage garbage in garbageQueue){ +// if ((garbage.frameOfThreat??99999999) > currentFrame) break; +// if (garbage.data.amt! > needToTake) { +// board.writeGarbage(garbage.data, needToTake); +// garbage.data.amt = garbage.data.amt! - needToTake; +// break; +// } else { +// board.writeGarbage(garbage.data); +// needToTake -= garbage.data.amt!; +// garbage.data.amt = 0; +// } +// } +// } +// garbageQueue.removeWhere((element) => element.data.amt == 0); +// } +// current = getNewOne(); +// coords = Coords(3, 21) + spawnPositionFixes[current.index]; +// } + +// void handleGravity(double frames){ +// if (frames == 0) return; +// gravityBucket += settings != null ? (handling!.sdfActive ? max(settings.g! * settings.handling!.sdf, 0.05 * settings.handling!.sdf) : settings.g!) * frames : 0; +// int gravityImpact = 0; +// if (gravityBucket >= 1.0){ +// gravityImpact = gravityBucket.truncate(); +// gravityBucket -= gravityBucket.truncate(); +// } +// while (gravityImpact > 0){ +// if (board.positionIsValid(current, Coords(coords.x, coords.y-1), rot)) {coords.y -= 1; floored = false;} +// else floored = true; +// gravityImpact--; +// } +// if (floored) lockDelay -= frames; +// if (lockDelay <= 0 && floored){ +// handleHardDrop(); +// } +// } + +// void handleMovement(double frames){ +// if (frames == 0 || handling == null) return; +// int movement = handling.processMovenent(frames); +// while (movement.abs() > 0){ +// if (board.positionIsValid(current, Coords(movement.isNegative ? coords.x-1 : coords.x+1, coords.y), rot)) movement.isNegative ? coords.x-- : coords.x++; +// movement.isNegative ? movement++ : movement--; +// } +// } + +// coords += spawnPositionFixes[current.index]; +// for (currentFrame; currentFrame <= replay.roundLengths[0]; currentFrame++){ +// handleMovement(1-subframesWent); +// handleGravity(1-subframesWent); +// subframesWent = 0; + +// if (settings?.handling?.sdf == 41) coords.y = sonicDrop(); +// print("$currentFrame: $current at $coords\n$board"); +// //print(stats.combo); +// if (currentFrame == nextEvent.frame){ +// while (currentFrame == nextEvent.frame){ +// print("Processing $nextEvent"); +// switch (nextEvent.type){ +// case EventType.start: +// developer.log("go"); +// break; +// case EventType.full: +// settings = (nextEvent as EventFull).data.options; +// handling = HandlingHandler(settings!.handling!.das.toDouble(), settings.handling!.arr.toDouble()); +// lockDelay = settings.locktime!.toDouble(); +// lockResets = settings.lockresets!; +// break; +// case EventType.keydown: +// nextEvent as EventKeyPress; +// double subframesDiff = nextEvent.data.subframe - subframesWent; +// subframesWent += subframesDiff; +// handleMovement(subframesDiff); +// handleGravity(subframesDiff); +// //activeKeypresses[nextEvent.data.key] = nextEvent; +// switch (nextEvent.data.key){ +// case KeyType.rotateCCW: +// handleRotation(-1); +// break; +// case KeyType.rotateCW: +// handleRotation(1); +// break; +// case KeyType.rotate180: +// handleRotation(2); +// break; +// case KeyType.moveLeft: +// case KeyType.moveRight: +// int pontencialMovement = handling!.movementKeyPressed(nextEvent.data.key == KeyType.moveLeft, nextEvent.data.key == KeyType.moveRight, nextEvent.data.subframe); +// if (board.positionIsValid(current, Coords(coords.x+pontencialMovement, coords.y), rot)) coords.x += pontencialMovement; +// break; +// case KeyType.softDrop: +// handling!.sdfActive = true; +// break; +// case KeyType.hardDrop: +// coords.y = sonicDrop(); +// handleHardDrop(); +// case KeyType.hold: +// switch (hold){ +// case null: +// hold = current; +// current = getNewOne(); +// break; +// case _: +// Tetromino temp; +// temp = hold; +// hold = current; +// current = temp; +// rot = 0; +// lockResets = 15; +// lockDelay = 30; +// gravityBucket = 1.0; +// floored = false; +// coords = Coords(3, 21) + spawnPositionFixes[current.index]; +// break; +// } +// break; +// case KeyType.chat: +// // TODO: Handle this case. +// case KeyType.exit: +// // TODO: Handle this case. +// case KeyType.retry: +// // TODO: Handle this case. +// default: +// developer.log(nextEvent.data.key.name); +// } +// if (nextEvent.data.key == KeyType.rotateCW && nextEvent.data.key == KeyType.rotateCW && nextEvent.data.key == KeyType.rotateCW){ +// spinWasLastMove = true; +// }else{ +// spinWasLastMove = false; +// } +// break; +// case EventType.keyup: +// nextEvent as EventKeyPress; +// double subframesDiff = nextEvent.data.subframe - subframesWent; +// subframesWent += subframesDiff; +// handleMovement(subframesDiff); +// handleGravity(subframesDiff); +// switch (nextEvent.data.key){ +// case KeyType.moveLeft: +// case KeyType.moveRight: +// handling!.movementKeyReleased(nextEvent.data.key == KeyType.moveLeft, nextEvent.data.key == KeyType.moveRight, nextEvent.data.subframe); +// break; +// case KeyType.softDrop: +// handling?.sdfActive = false; +// break; +// case KeyType.rotateCCW: +// // TODO: Handle this case. +// case KeyType.rotateCW: +// // TODO: Handle this case. +// case KeyType.rotate180: +// // TODO: Handle this case. +// case KeyType.hardDrop: +// // TODO: Handle this case. +// case KeyType.hold: +// // TODO: Handle this case. +// case KeyType.chat: +// // TODO: Handle this case. +// case KeyType.exit: +// // TODO: Handle this case. +// case KeyType.retry: +// // TODO: Handle this case. +// } +// activeKeypresses.remove(nextEvent.data.key); +// break; +// case EventType.end: +// currentFrame = replay.roundLengths[0]+1; +// break; +// case EventType.ige: +// nextEvent as EventIGE; +// switch (nextEvent.data.data.type){ +// case "interaction": +// garbageQueue.add(IncomingGarbage((nextEvent.data.data as IGEdataInteraction).data)); +// case "interaction_confirm": +// GarbageData data = (nextEvent.data.data as IGEdataInteraction).data; +// if(data.type != "targeted") garbageQueue.firstWhere((element) => element.data.iid == data.iid).confirm(currentFrame, settings!.garbageSpeed!); +// case "allow_targeting": +// case "target": +// default: +// developer.log("Unknown IGE type: ${nextEvent.data.type}", level: 900); +// break; +// } +// // garbage speed counts from interaction comfirm +// break; +// default: +// developer.log("Event wasn't processed: ${nextEvent}", level: 900); +// } +// try{ +// nextEvent = events.removeAt(0); +// } +// catch (e){ +// developer.log(e.toString()); +// } +// } +// //developer.log("Next is: $nextEvent"); +// } +// } // exit(0); -//} \ No newline at end of file +// } \ No newline at end of file diff --git a/lib/data_objects/tetrio.dart b/lib/data_objects/tetrio.dart index e89b2cf..8ff586c 100644 --- a/lib/data_objects/tetrio.dart +++ b/lib/data_objects/tetrio.dart @@ -897,11 +897,11 @@ class TetraLeagueAlphaRecord{ TetraLeagueAlphaRecord({required this.replayId, required this.ownId, required this.timestamp, required this.endContext, required this.replayAvalable}); TetraLeagueAlphaRecord.fromJson(Map json) { - ownId = json['_id']; endContext = [EndContextMulti.fromJson(json['endcontext'][0]), EndContextMulti.fromJson(json['endcontext'][1])]; replayId = json['replayid']; + ownId = json['_id']??replayId; timestamp = DateTime.parse(json['ts']); - replayAvalable = true; + replayAvalable = ownId != replayId; } Map toJson() { @@ -973,19 +973,19 @@ class EndContextMulti { EndContextMulti.fromJson(Map json) { userId = json['id'] ?? json['user']['_id']; username = json['username'] ?? json['user']['username']; - handling = Handling.fromJson(json['handling']); + handling = json['handling'] != null ? Handling.fromJson(json['handling']) : Handling(arr: -1, das: -1, sdf: -1, dcd: 0, cancel: true, safeLock: true); success = json['success']; - inputs = json['inputs']; - piecesPlaced = json['piecesplaced']; + inputs = json['inputs'] ?? -1; + piecesPlaced = json['piecesplaced'] ?? -1; naturalOrder = json['naturalorder']; wins = json['wins']; points = json['points']['primary']; secondary = json['points']['secondary'].toDouble(); tertiary = json['points']['tertiary'].toDouble(); - secondaryTracking = json['points']['secondaryAvgTracking'].map((e) => e.toDouble()).toList(); - tertiaryTracking = json['points']['tertiaryAvgTracking'].map((e) => e.toDouble()).toList(); + secondaryTracking = json['points']['secondaryAvgTracking'] != null ? json['points']['secondaryAvgTracking'].map((e) => e.toDouble()).toList() : []; + tertiaryTracking = json['points']['tertiaryAvgTracking'] != null ? json['points']['tertiaryAvgTracking'].map((e) => e.toDouble()).toList() : []; extra = json['points']['extra']['vs'].toDouble(); - extraTracking = json['points']['extraAvgTracking']['aggregatestats___vsscore'].map((e) => e.toDouble()).toList(); + extraTracking = json['points']['extraAvgTracking'] != null ? json['points']['extraAvgTracking']['aggregatestats___vsscore'].map((e) => e.toDouble()).toList() : []; nerdStats = NerdStats(secondary, tertiary, extra); nerdStatsTracking = [for (int i = 0; i < secondaryTracking.length; i++) NerdStats(secondaryTracking[i], tertiaryTracking[i], extraTracking[i])]; estTr = EstTr(secondary, tertiary, extra, nerdStats.app, nerdStats.dss, nerdStats.dsp, nerdStats.gbe); diff --git a/lib/data_objects/tetrio_multiplayer_replay.dart b/lib/data_objects/tetrio_multiplayer_replay.dart index 9e5ecbd..9b521ad 100644 --- a/lib/data_objects/tetrio_multiplayer_replay.dart +++ b/lib/data_objects/tetrio_multiplayer_replay.dart @@ -16,14 +16,14 @@ int biggestSpikeFromReplay(events){ spikeCounter = event['data']['data']['data']['amt']; biggestSpike = spikeCounter; }else{ - if (event['data']['data']['frame'] - previousIGEeventFrame < 60){ + if ((event['data']['data']['frame']??event['data']['frame']) - previousIGEeventFrame < 60){ spikeCounter = spikeCounter + event['data']['data']['data']['amt'] as int; }else{ spikeCounter = event['data']['data']['data']['amt']; } biggestSpike = max(biggestSpike, spikeCounter); } - previousIGEeventFrame = event['data']['data']['frame']; + previousIGEeventFrame = event['data']['data']['frame']??event['data']['frame']; } } return biggestSpike; @@ -216,8 +216,8 @@ class ReplayData{ List KPSmultipliedByWeights = [0, 0]; totalStats = [ReplayStats.createEmpty(), ReplayStats.createEmpty()]; for(var round in json['data']) { - int firstInEndContext = round['replays'][0]["events"].last['data']['export']['options']['gameid'].startsWith(endcontext[0].userId) ? 0 : 1; - int secondInEndContext = round['replays'][1]["events"].last['data']['export']['options']['gameid'].startsWith(endcontext[1].userId) ? 1 : 0; + int firstInEndContext = round['replays'][0]["events"].last['data']['export']['options']['username'].startsWith(endcontext[0].username) ? 0 : 1; + int secondInEndContext = round['replays'][1]["events"].last['data']['export']['options']['username'].startsWith(endcontext[1].username) ? 1 : 0; int roundLength = max(round['replays'][0]['frames'], round['replays'][1]['frames']); roundLengths.add(roundLength); totalLength = totalLength + max(round['replays'][0]['frames'], round['replays'][1]['frames']); @@ -228,7 +228,7 @@ class ReplayData{ VSmultipliedByWeights[0] += endcontext[0].extraTracking[roundID]*roundLength; VSmultipliedByWeights[1] += endcontext[1].extraTracking[roundID]*roundLength; int winner = round['board'].indexWhere((element) => element['success'] == true); - roundWinners.add([round['board'][winner]['id'], round['board'][winner]['username']]); + roundWinners.add([round['board'][winner]['id']??round['board'][winner]['user']['_id'], round['board'][winner]['username']??round['board'][winner]['user']['username']]); ReplayStats playerOne = ReplayStats.fromJson(round['replays'][firstInEndContext]['events'].last['data']['export']['stats'], biggestSpikeFromReplay(round['replays'][secondInEndContext]['events']), round['replays'][firstInEndContext]['frames']); // (events contain recived attacks) ReplayStats playerTwo = ReplayStats.fromJson(round['replays'][secondInEndContext]['events'].last['data']['export']['stats'], biggestSpikeFromReplay(round['replays'][firstInEndContext]['events']), round['replays'][secondInEndContext]['frames']); SPPmultipliedByWeights[0] += playerOne.spp*roundLength; @@ -283,7 +283,7 @@ class ReplayData{ // events.add(EventFull(id, frame, type, DataFull.fromJson(event["data"]))); // break; // case EventType.targets: -// // TODO +// events.add(EventTargets(id, frame, type, Targets.fromJson(event["data"]))); // break; // case EventType.keydown: // events.add(EventKeyPress(id, frame, type, @@ -302,15 +302,24 @@ class ReplayData{ // )); // break; // case EventType.end: -// // TODO: Handle this case. +// events.add(EventEnd(id, frame, type, EndData(event['data']['reason'], DataFull.fromJson(event['data']['export'])))); +// break; // case EventType.ige: -// // TODO: Handle this case. +// events.add(EventIGE(id, frame, type, IGE( +// event['data']['id'], +// event['data']['frame'], +// event['data']['type'], +// event['data']['data'] +// )) +// ); +// break; // case EventType.exit: -// // TODO: Handle this case. +// events.add(Event(id, frame, type)); +// break; // } // id++; // } -// return []; +// return events; // } // enum EventType @@ -356,10 +365,12 @@ class ReplayData{ // class Keypress{ // KeyType key; -// double subframe; +// late double subframe; // bool released; -// Keypress(this.key, this.subframe, this.released); +// Keypress(this.key, num sframe, this.released){ +// subframe = sframe.toDouble(); +// } // } // class EventKeyPress extends Event{ @@ -368,13 +379,144 @@ class ReplayData{ // EventKeyPress(super.id, super.frame, super.type, this.data); // } +// class Targets{ +// String? id; +// int? frame; +// String? type; +// List? data; + +// Targets(this.id, this.frame, this.type, this.data); + +// Targets.fromJson(Map json){ +// id = json["id"]; +// frame = json["frame"]; +// type = json["type"]; +// data = json["data"]; +// } +// } + +// class EventTargets extends Event{ +// Targets data; + +// EventTargets(super.id, super.frame, super.type, this.data); +// } + +// class IGEdata{ +// late String type; +// late String? gameid; +// late int? frame; + +// IGEdata.fromJson(Map d){ +// type = d['type']; +// gameid = d['gameid']; +// frame = d['frame']; +// } +// } + +// enum GarbageStatus +// { +// sleeping, +// caution, +// spawn, +// danger +// } + +// class GarbageData{ +// int? id; +// int? iid; +// int? ackiid; +// String? username; +// late String type; +// bool? active; +// GarbageStatus? status; +// int? delay; +// bool? queued; +// int? amt; +// int? x; +// int? y; +// int? size; +// int? column; +// int? cid; +// bool? firstcycle; +// int? gid; +// bool? value; + +// GarbageData.fromJson(Map data){ +// id = data['id']; +// iid = data['iid']; +// ackiid = data['ackiid']; +// username = data['username']; +// type = data['type']; +// active = data['active']; +// status = data['status'] != null ? GarbageStatus.values[data['status']] : null; +// delay = data['delay']; +// queued = data['queued']; +// amt = data['amt']; +// x = data['x']; +// y = data['y']; +// size = data['size']; +// column = data['column']; +// cid = data['cid']; +// firstcycle = data['firstcycle']; +// gid = data['gid']; +// value = data['value']; +// } +// } + +// class IGEdataTarget extends IGEdata{ +// late List targets; + +// GarbageData? data; +// //compatibility for v15 targets event +// String? sender_id; + +// IGEdataTarget.fromJson(Map d) : super.fromJson(d){ +// targets = d['targets']; +// data = d['data'] != null ? GarbageData.fromJson(d['data']) : null; +// } +// } + +// class IGEdataAllowTargeting extends IGEdata{ +// late bool value; + +// IGEdataAllowTargeting.fromJson(Map d) : super.fromJson(d){ +// value = d['value']; +// frame = d['frame']; +// } +// } + +// class IGEdataInteraction extends IGEdata{ +// late GarbageData data; +// String? sender; +// String? senderID; +// int? sentFrame; +// bool confirm = false; + + +// IGEdataInteraction.fromJson(Map d) : super.fromJson(d){ +// //data = Targeted(d['data']['targeted'], d['data']['value']); +// data = GarbageData.fromJson(d['data']); +// sender = d['sender']; +// senderID = d['sender_id']; +// confirm = type == "interaction_confirm"; +// } +// } + // class IGE{ // int id; // int frame; // String type; -// int amount; +// late IGEdata data; -// IGE(this.id, this.frame, this.type, this.amount); +// IGE(this.id, this.frame, this.type, Map d){ +// data = switch (d["type"] as String) { +// "interaction" => IGEdataInteraction.fromJson(d), +// "interaction_confirm" => IGEdataInteraction.fromJson(d), +// "target" => IGEdataTarget.fromJson(d), +// "allow_targeting" => IGEdataAllowTargeting.fromJson(d), +// _ => IGEdata.fromJson(d), +// }; +// } // } // class EventIGE extends Event{ @@ -383,6 +525,19 @@ class ReplayData{ // EventIGE(super.id, super.frame, super.type, this.data); // } +// class EndData { +// String reason; +// DataFull export; + +// EndData(this.reason, this.export); +// } + +// class EventEnd extends Event{ +// EndData data; + +// EventEnd(super.id, super.frame, super.type, this.data); +// } + // class Hold // { // String? piece; @@ -434,8 +589,9 @@ class ReplayData{ // stock = json["stock"]; // gMargin = json["gmargin"]; // gIncrease = json["gincrease"]; -// garbageMultiplier = json["garbagemultiplier"]; -// garbageCapIncrease = json["garbagecapincrease"]; +// garbageMultiplier = json["garbagemultiplier"].toDouble(); +// garbageCap = json["garbagecap"]; +// garbageCapIncrease = json["garbagecapincrease"].toDouble(); // garbageCapMax = json["garbagecapmax"]; // garbageHoleSize = json["garbageholesize"]; // garbageBlocking = json["garbageblocking"]; @@ -462,7 +618,7 @@ class ReplayData{ // class DataFullStats // { -// double? seed; +// int? seed; // int? lines; // int? levelLines; // int? levelLinesNeeded; @@ -511,8 +667,8 @@ class ReplayData{ // class DataFullGame // { -// List>? board; -// List? bag; +// List? board; +// List? bag; // double? g; // bool? playing; // Hold? hold; @@ -595,54 +751,74 @@ class ReplayData{ // empty // } +// class Coords{ +// int x; +// int y; + +// Coords(this.x, this.y); + +// @override +// String toString() { +// return "($x; $y)"; +// } + +// Coords operator+(Coords other){ +// return Coords(x+other.x, y+other.y); +// } + +// Coords operator-(Coords other){ +// return Coords(x-other.x, y-other.y); +// } +// } + // List tetrominoes = [Tetromino.Z, Tetromino.L, Tetromino.O, Tetromino.S, Tetromino.I, Tetromino.J, Tetromino.T]; -// List>> shapes = [ +// List>> shapes = [ // [ // Z -// [Vector2(0, 0), Vector2(1, 0), Vector2(1, 1), Vector2(2, 1)], -// [Vector2(2, 0), Vector2(1, 1), Vector2(2, 1), Vector2(1, 2)], -// [Vector2(0, 1), Vector2(1, 1), Vector2(1, 2), Vector2(2, 2)], -// [Vector2(1, 0), Vector2(0, 1), Vector2(1, 1), Vector2(0, 2)] +// [Coords(0, 2), Coords(1, 2), Coords(1, 1), Coords(2, 1)], +// [Coords(2, 2), Coords(2, 1), Coords(1, 1), Coords(1, 0)], +// [Coords(2, 0), Coords(1, 0), Coords(1, 1), Coords(0, 1)], +// [Coords(0, 0), Coords(0, 1), Coords(1, 1), Coords(1, 2)] // ], // [ // L -// [Vector2(2, 0), Vector2(0, 1), Vector2(1, 1), Vector2(2, 1)], -// [Vector2(1, 0), Vector2(1, 1), Vector2(1, 2), Vector2(2, 2)], -// [Vector2(0, 1), Vector2(1, 1), Vector2(2, 1), Vector2(0, 2)], -// [Vector2(0, 0), Vector2(1, 0), Vector2(1, 1), Vector2(1, 2)] +// [Coords(2, 2), Coords(2, 1), Coords(1, 1), Coords(0, 1)], +// [Coords(2, 0), Coords(1, 0), Coords(1, 1), Coords(1, 2)], +// [Coords(0, 0), Coords(0, 1), Coords(1, 1), Coords(2, 1)], +// [Coords(0, 2), Coords(1, 2), Coords(1, 1), Coords(1, 0)] // ], // [ // O -// [Vector2(0, 0), Vector2(1, 0), Vector2(0, 1), Vector2(1, 1)], -// [Vector2(0, 0), Vector2(1, 0), Vector2(0, 1), Vector2(1, 1)], -// [Vector2(0, 0), Vector2(1, 0), Vector2(0, 1), Vector2(1, 1)], -// [Vector2(0, 0), Vector2(1, 0), Vector2(0, 1), Vector2(1, 1)] +// [Coords(0, 0), Coords(1, 0), Coords(0, 1), Coords(1, 1)], +// [Coords(0, 0), Coords(1, 0), Coords(0, 1), Coords(1, 1)], +// [Coords(0, 0), Coords(1, 0), Coords(0, 1), Coords(1, 1)], +// [Coords(0, 0), Coords(1, 0), Coords(0, 1), Coords(1, 1)] // ], // [ // S -// [Vector2(1, 0), Vector2(2, 0), Vector2(0, 1), Vector2(1, 1)], -// [Vector2(1, 0), Vector2(1, 1), Vector2(2, 1), Vector2(2, 2)], -// [Vector2(1, 1), Vector2(2, 1), Vector2(0, 2), Vector2(1, 2)], -// [Vector2(0, 0), Vector2(0, 1), Vector2(1, 1), Vector2(1, 2)] +// [Coords(2, 2), Coords(1, 2), Coords(1, 1), Coords(0, 1)], +// [Coords(2, 0), Coords(2, 1), Coords(1, 1), Coords(1, 2)], +// [Coords(0, 0), Coords(1, 0), Coords(1, 1), Coords(2, 1)], +// [Coords(0, 2), Coords(0, 1), Coords(1, 1), Coords(1, 0)] // ], // [ // I -// [Vector2(0, 1), Vector2(1, 1), Vector2(2, 1), Vector2(3, 1)], -// [Vector2(2, 0), Vector2(2, 1), Vector2(2, 2), Vector2(2, 3)], -// [Vector2(0, 2), Vector2(1, 2), Vector2(2, 2), Vector2(3, 2)], -// [Vector2(1, 0), Vector2(1, 1), Vector2(1, 2), Vector2(1, 3)] +// [Coords(0, 2), Coords(1, 2), Coords(2, 2), Coords(3, 2)], +// [Coords(2, 3), Coords(2, 2), Coords(2, 1), Coords(2, 0)], +// [Coords(3, 1), Coords(2, 1), Coords(1, 1), Coords(0, 1)], +// [Coords(1, 0), Coords(1, 1), Coords(1, 2), Coords(1, 3)] // ], // [ // J -// [Vector2(0, 0), Vector2(0, 1), Vector2(1, 1), Vector2(2, 1)], -// [Vector2(1, 0), Vector2(2, 0), Vector2(1, 1), Vector2(1, 2)], -// [Vector2(0, 1), Vector2(1, 1), Vector2(2, 1), Vector2(2, 2)], -// [Vector2(1, 0), Vector2(1, 1), Vector2(0, 2), Vector2(1, 2)] +// [Coords(0, 2), Coords(0, 1), Coords(1, 1), Coords(2, 1)], +// [Coords(2, 2), Coords(1, 2), Coords(1, 1), Coords(1, 0)], +// [Coords(2, 0), Coords(2, 1), Coords(1, 1), Coords(0, 1)], +// [Coords(0, 0), Coords(1, 0), Coords(1, 1), Coords(1, 2)] // ], // [ // T -// [Vector2(1, 0), Vector2(0, 1), Vector2(1, 1), Vector2(2, 1)], -// [Vector2(1, 0), Vector2(1, 1), Vector2(2, 1), Vector2(1, 2)], -// [Vector2(0, 1), Vector2(1, 1), Vector2(2, 1), Vector2(1, 2)], -// [Vector2(1, 0), Vector2(0, 1), Vector2(1, 1), Vector2(1, 2)] +// [Coords(1, 2), Coords(0, 1), Coords(1, 1), Coords(2, 1)], +// [Coords(2, 1), Coords(1, 2), Coords(1, 1), Coords(1, 0)], +// [Coords(1, 0), Coords(2, 1), Coords(1, 1), Coords(0, 1)], +// [Coords(0, 1), Coords(1, 0), Coords(1, 1), Coords(1, 2)] // ] // ]; -// List spawnPositionFixes = [Vector2(1, 1), Vector2(1, 1), Vector2(0, 1), Vector2(1, 1), Vector2(1, 1), Vector2(1, 1), Vector2(1, 1)]; +// List spawnPositionFixes = [Coords(0, 0), Coords(0, 0), Coords(1, 1), Coords(0, 0), Coords(0, -1), Coords(0, 0), Coords(0, 0)]; -// const Map garbage = { +// const Map garbage = { // "single": 0, // "double": 1, // "triple": 2, @@ -665,3 +841,61 @@ class ReplayData{ // int comboMinifier = 1; // double comboMinifierLog = 1.25; // List comboTable = [0, 1, 1, 2, 2, 3, 3, 4, 4, 4, 5]; + +// class KicksetBase { +// List? additionalOffsets; +// late List additionalOffsetEmpty; +// List? spawnRotation; +// late List>> kickTable; //kickTable[initRot][rotDirection-1][kick] +// late List>> kickTableI; +// } + +// class SRSPlus extends KicksetBase{ +// SRSPlus(){ +// kickTable = [ +// [ +// [Coords( 0, 0), Coords( 1, 0), Coords( 1, 1), Coords( 0,-2), Coords( 1,-2)], // 0 -> 270 +// [Coords( 0, 0), Coords(-1, 0), Coords(-1, 1), Coords( 0,-2), Coords(-1,-2)], // 0 -> 90 +// [Coords( 0, 0), Coords( 0, 1), Coords( 1, 1), Coords(-1, 1), Coords( 1, 0), Coords(-1, 0)], // 0 -> 180 +// ], +// [ +// [Coords( 0, 0), Coords( 1, 0), Coords( 1,-1), Coords( 0, 2), Coords( 1, 2)], // 90 -> 0 +// [Coords( 0, 0), Coords( 1, 0), Coords( 1,-1), Coords( 0, 2), Coords( 1, 2)], // 90 -> 180 +// [Coords( 0, 0), Coords( 1, 0), Coords( 1, 2), Coords( 1, 1), Coords( 0, 2), Coords( 0, 1)], // 90 -> 270 +// ], +// [ +// [Coords( 0, 0), Coords(-1, 0), Coords(-1, 1), Coords( 0,-2), Coords(-1,-2)], // 180 -> 90 +// [Coords( 0, 0), Coords( 1, 0), Coords( 1, 1), Coords( 0,-2), Coords( 1,-2)], // 180 -> 270 +// [Coords( 0, 0), Coords( 0,-1), Coords(-1,-1), Coords( 1,-1), Coords(-1, 0), Coords( 1, 0)], // 180 -> 0 +// ], +// [ +// [Coords( 0, 0), Coords(-1, 0), Coords(-1,-1), Coords( 0, 2), Coords(-1, 2)], // 270 -> 180 +// [Coords( 0, 0), Coords(-1, 0), Coords(-1,-1), Coords( 0, 2), Coords(-1, 2)], // 270 -> 0 +// [Coords( 0, 0), Coords(-1, 0), Coords(-1, 2), Coords(-1, 1), Coords( 0, 2), Coords( 0, 1)], // 270 -> 90 +// ] +// ]; +// kickTableI = [ +// [ +// [Coords( 0, 0), Coords(-1, 0), Coords( 2, 0), Coords( 2,-1), Coords(-1, 2)], // 0 -> 270 +// [Coords( 0, 0), Coords( 1, 0), Coords( 2, 0), Coords(-1,-1), Coords( 1, 2)], // 0 -> 90 +// [Coords( 0, 0), Coords( 0, 1)], // 0 -> 180 +// ], +// [ +// [Coords( 0, 0), Coords(-1, 0), Coords( 2, 0), Coords(-1,-2), Coords( 2,-1)], // 90 -> 0 +// [Coords( 0, 0), Coords(-1, 0), Coords( 2, 0), Coords(-1, 2), Coords( 2, 1)], // 90 -> 180 +// [Coords( 0, 0), Coords( 1, 0)], // 90 -> 270 +// ], +// [ +// [Coords( 0, 0), Coords(-2, 0), Coords( 1, 0), Coords(-2, 1), Coords( 1,-2)], // 180 -> 90 +// [Coords( 0, 0), Coords(-2, 0), Coords(-1, 0), Coords( 2, 1), Coords(-1,-2)], // 180 -> 270 +// [Coords( 0, 0), Coords( 0,-1)], // 180 -> 0 +// ], +// [ +// [Coords( 0, 0), Coords( 1, 0), Coords(-2, 0), Coords( 1, 2), Coords(-2,-1)], // 270 -> 180 +// [Coords( 0, 0), Coords( 1, 0), Coords(-2, 0), Coords( 1,-2), Coords(-2, 1)], // 270 -> 0 +// [Coords( 0, 0), Coords(-1, 0)], // 270 -> 90 +// ] +// ]; +// additionalOffsetEmpty = [Coords( 0, 0),Coords( 0, 0),Coords( 0, 0),Coords( 0, 0)]; +// } +// } diff --git a/lib/gen/strings.g.dart b/lib/gen/strings.g.dart index 6b05e47..42e4e73 100644 --- a/lib/gen/strings.g.dart +++ b/lib/gen/strings.g.dart @@ -6,7 +6,7 @@ /// Locales: 2 /// Strings: 1182 (591 per locale) /// -/// Built on 2024-06-16 at 21:03 UTC +/// Built on 2024-07-10 at 15:23 UTC // coverage:ignore-file // ignore_for_file: type=lint @@ -321,7 +321,7 @@ class Translations implements BaseTranslations { String get fromBeginning => 'From beginning'; String get calc => 'Calc'; String get calcViewNoValues => 'Enter values to calculate the stats'; - String get rankAveragesViewTitle => 'Ranks cutoff and average stats'; + String get rankAveragesViewTitle => 'Ranks cutoffs'; String get sprintAndBlitsViewTitle => '40 lines and Blitz averages'; String sprintAndBlitsRelevance({required Object date}) => 'Relevance: ${date}'; String get rank => 'Rank'; @@ -1016,7 +1016,7 @@ class _StringsRu implements Translations { @override String get fromBeginning => 'С начала'; @override String get calc => 'Считать'; @override String get calcViewNoValues => 'Введите значения, чтобы посчитать статистику'; - @override String get rankAveragesViewTitle => 'Требования рангов и средние значения'; + @override String get rankAveragesViewTitle => 'Требования рангов'; @override String get sprintAndBlitsViewTitle => 'Средние результаты 40 линий и блица'; @override String sprintAndBlitsRelevance({required Object date}) => 'Актуальность: ${date}'; @override String get rank => 'Ранг'; @@ -1703,7 +1703,7 @@ extension on Translations { case 'fromBeginning': return 'From beginning'; case 'calc': return 'Calc'; case 'calcViewNoValues': return 'Enter values to calculate the stats'; - case 'rankAveragesViewTitle': return 'Ranks cutoff and average stats'; + case 'rankAveragesViewTitle': return 'Ranks cutoffs'; case 'sprintAndBlitsViewTitle': return '40 lines and Blitz averages'; case 'sprintAndBlitsRelevance': return ({required Object date}) => 'Relevance: ${date}'; case 'rank': return 'Rank'; @@ -2314,7 +2314,7 @@ extension on _StringsRu { case 'fromBeginning': return 'С начала'; case 'calc': return 'Считать'; case 'calcViewNoValues': return 'Введите значения, чтобы посчитать статистику'; - case 'rankAveragesViewTitle': return 'Требования рангов и средние значения'; + case 'rankAveragesViewTitle': return 'Требования рангов'; case 'sprintAndBlitsViewTitle': return 'Средние результаты 40 линий и блица'; case 'sprintAndBlitsRelevance': return ({required Object date}) => 'Актуальность: ${date}'; case 'rank': return 'Ранг'; diff --git a/lib/services/tetrio_crud.dart b/lib/services/tetrio_crud.dart index 3911cd5..f821c73 100644 --- a/lib/services/tetrio_crud.dart +++ b/lib/services/tetrio_crud.dart @@ -379,7 +379,6 @@ class TetrioService extends DB { TopTr result = TopTr(id, null); developer.log("fetchTopTR: Probably, player doesn't have top TR", name: "services/tetrio_crud", error: response.statusCode); _cache.store(result, DateTime.now().millisecondsSinceEpoch + 300000); - //_topTRcache[(DateTime.now().millisecondsSinceEpoch + 300000).toString()] = {id: null}; return result; // if not 200 or 404 - throw a unique for each code exception case 403: @@ -392,7 +391,10 @@ class TetrioService extends DB { case 502: case 503: case 504: - throw P1nkl0bst3rInternalProblem(); + TopTr result = TopTr(id, null); + developer.log("fetchTopTR: API returned ${response.statusCode}", name: "services/tetrio_crud", error: response.statusCode); + //_cache.store(result, DateTime.now().millisecondsSinceEpoch + 300000); + return result; default: developer.log("fetchTopTR: Failed to fetch top TR", name: "services/tetrio_crud", error: response.statusCode); throw ConnectionIssue(response.statusCode, response.reasonPhrase??"No reason"); @@ -445,7 +447,8 @@ class TetrioService extends DB { case 502: case 503: case 504: - throw P1nkl0bst3rInternalProblem(); + developer.log("fetchCutoffs: Cutoffs are unavalable (${response.statusCode})", name: "services/tetrio_crud", error: response.statusCode); + return null; default: developer.log("fetchCutoffs: Failed to fetch top Cutoffs", name: "services/tetrio_crud", error: response.statusCode); throw ConnectionIssue(response.statusCode, response.reasonPhrase??"No reason"); @@ -603,7 +606,7 @@ class TetrioService extends DB { if (kIsWeb) { url = Uri.https('ts.dan63.by', 'oskware_bridge.php', {"endpoint": "TLMatches", "user": userID}); } else { - url = Uri.https('api.p1nkl0bst3r.xyz', 'tlmatches/$userID'); + url = Uri.https('api.p1nkl0bst3r.xyz', 'tlmatches/$userID', {"before": "0", "count": "9000"}); } try{ @@ -611,62 +614,9 @@ class TetrioService extends DB { switch (response.statusCode) { case 200: - // that one api returns csv instead of json - List> csv = const CsvToListConverter().convert(response.body)..removeAt(0); - List matches = []; - - // parsing data into TetraLeagueAlphaRecord objects - for (var entry in csv){ - TetraLeagueAlphaRecord match = TetraLeagueAlphaRecord( - replayId: entry[0].toString(), - ownId: entry[0].toString(), // i gonna disting p1nkl0bst3r entries with it - timestamp: DateTime.parse(entry[1]), - endContext: [ - EndContextMulti( - userId: entry[2].toString(), - username: entry[3].toString(), - naturalOrder: 0, - inputs: -1, - piecesPlaced: -1, - handling: Handling(arr: -1, das: -1, sdf: -1, dcd: 0, cancel: true, safeLock: true), - points: entry[4], - wins: entry[4], - secondary: entry[6], - secondaryTracking: [], - tertiary: entry[5], - tertiaryTracking: [], - extra: entry[7], - extraTracking: [], - success: true - ), - EndContextMulti( - userId: entry[8].toString(), - username: entry[9].toString(), - naturalOrder: 1, - inputs: -1, - piecesPlaced: -1, - handling: Handling(arr: -1, das: -1, sdf: -1, dcd: 0, cancel: true, safeLock: true), - points: entry[10], - wins: entry[10], - secondary: entry[12], - secondaryTracking: [], - tertiary: entry[11], - tertiaryTracking: [], - extra: entry[13], - extraTracking: [], - success: false - ) - ], - replayAvalable: false - ); - matches.add(match); - } - - // trying to dump it to local DB - TetraLeagueAlphaStream fakeStream = TetraLeagueAlphaStream(userId: userID, records: matches); - saveTLMatchesFromStream(fakeStream); - - return matches; + TetraLeagueAlphaStream stream = TetraLeagueAlphaStream.fromJson(jsonDecode(response.body)['data']['records'], userID); + saveTLMatchesFromStream(stream); + return stream.records; case 404: developer.log("fetchAndSaveOldTLmatches: Probably, history doesn't exist", name: "services/tetrio_crud", error: response.statusCode); throw TetrioHistoryNotExist(); diff --git a/lib/views/main_view.dart b/lib/views/main_view.dart index 8680e59..14ab269 100644 --- a/lib/views/main_view.dart +++ b/lib/views/main_view.dart @@ -306,7 +306,7 @@ class _MainState extends State with TickerProviderStateMixin { }); } - return [me, records, states, tlMatches, compareWith, isTracking, news, topTR, recent, sprint, blitz]; + return [me, records, states, tlMatches, compareWith, isTracking, news, topTR, recent, sprint, blitz, tlMatches.elementAtOrNull(0)?.timestamp]; } /// Triggers widgets rebuild @@ -459,6 +459,7 @@ class _MainState extends State with TickerProviderStateMixin { userID: snapshot.data![0].userId, states: snapshot.data![2], topTR: snapshot.data![7]?.tr, + lastMatchPlayed: snapshot.data![11], bot: snapshot.data![0].role == "bot", guest: snapshot.data![0].role == "anon", thatRankCutoff: thatRankCutoff, @@ -1084,7 +1085,7 @@ class _TwoRecordsThingy extends StatelessWidget { if (sprint != null) FinesseThingy(sprint?.endContext.finesse, sprint?.endContext.finessePercentage), if (sprint != null) LineclearsThingy(sprint!.endContext.clears, sprint!.endContext.lines, sprint!.endContext.holds, sprint!.endContext.tSpins), if (sprint != null) Text("${sprint!.endContext.inputs} KP • ${f2.format(sprint!.endContext.kps)} KPS"), - Wrap( + if (sprint != null) Wrap( alignment: WrapAlignment.spaceBetween, crossAxisAlignment: WrapCrossAlignment.start, spacing: 20, @@ -1170,7 +1171,7 @@ class _TwoRecordsThingy extends StatelessWidget { if (blitz != null) FinesseThingy(blitz?.endContext.finesse, blitz?.endContext.finessePercentage), if (blitz != null) LineclearsThingy(blitz!.endContext.clears, blitz!.endContext.lines, blitz!.endContext.holds, blitz!.endContext.tSpins), if (blitz != null) Text("${blitz!.endContext.piecesPlaced} P • ${blitz!.endContext.inputs} KP • ${f2.format(blitz!.endContext.kpp)} KPP • ${f2.format(blitz!.endContext.kps)} KPS"), - Wrap( + if (blitz != null) Wrap( alignment: WrapAlignment.spaceBetween, crossAxisAlignment: WrapCrossAlignment.start, spacing: 20, @@ -1184,7 +1185,7 @@ class _TwoRecordsThingy extends StatelessWidget { child: Column( mainAxisSize: MainAxisSize.min, children: [ - for (int i = 1; i < sprintStream.records.length; i++) ListTile( + for (int i = 1; i < blitzStream.records.length; i++) ListTile( onTap: () => Navigator.push(context, MaterialPageRoute(builder: (context) => SingleplayerRecordView(record: blitzStream.records[i]))), leading: Text("#${i+1}", style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 28, shadows: textShadow, height: 0.9) ), title: Text("${NumberFormat.decimalPattern().format(blitzStream.records[i].endContext.score)} points", diff --git a/lib/views/ranks_averages_view.dart b/lib/views/ranks_averages_view.dart index 5b481b3..dbfa0e9 100644 --- a/lib/views/ranks_averages_view.dart +++ b/lib/views/ranks_averages_view.dart @@ -49,14 +49,13 @@ class RanksAverages extends State { child: averages.isEmpty ? const Center(child: Text('Fetching...')) : ListView.builder( itemCount: averages.length, itemBuilder: (context, index){ - bool bigScreen = MediaQuery.of(context).size.width > 768; List keys = averages.keys.toList(); return ListTile( leading: Image.asset("res/tetrio_tl_alpha_ranks/${keys[index]}.png", height: 48), title: Text(t.players(n: averages[keys[index]]?[1]["players"]), style: const TextStyle(fontFamily: "Eurostile Round Extended")), subtitle: Text("${f2.format(averages[keys[index]]?[0].apm)} APM, ${f2.format(averages[keys[index]]?[0].pps)} PPS, ${f2.format(averages[keys[index]]?[0].vs)} VS, ${f2.format(averages[keys[index]]?[0].nerdStats.app)} APP, ${f2.format(averages[keys[index]]?[0].nerdStats.vsapm)} VS/APM", - style: const TextStyle(fontFamily: "Eurostile Round Condensed", color: Colors.grey)), - trailing: Text("${f2.format(averages[keys[index]]?[1]["toEnterTR"])} TR", style: bigScreen ? const TextStyle(fontSize: 28) : null), + style: const TextStyle(fontFamily: "Eurostile Round Condensed", color: Colors.grey, fontSize: 13)), + trailing: Text("${f2.format(averages[keys[index]]?[1]["toEnterTR"])} TR", style: const TextStyle(fontSize: 28, fontFamily: "Eurostile Round")), onTap: (){ if (averages[keys[index]]?[1]["players"] > 0) { Navigator.push( diff --git a/lib/views/settings_view.dart b/lib/views/settings_view.dart index 07a15d6..1eb96c5 100644 --- a/lib/views/settings_view.dart +++ b/lib/views/settings_view.dart @@ -288,6 +288,13 @@ class SettingsState extends State { subtitle: Text(t.aboutAppText(appName: packageInfo.appName, packageName: packageInfo.packageName, version: packageInfo.version, buildNumber: packageInfo.buildNumber)), trailing: const Icon(Icons.arrow_right) ), + // Wrap( + // alignment: WrapAlignment.center, + // spacing: 8, + // children: [ + // TextButton(child: Text("Donate to me"), onPressed: (){},),TextButton(child: Text("Donate to NOT me"), onPressed: (){},),TextButton(child: Text("Donate to someone else"), onPressed: (){},), + // ], + // ), ], )), ); diff --git a/lib/views/tl_leaderboard_view.dart b/lib/views/tl_leaderboard_view.dart index d69bff8..8b16a28 100644 --- a/lib/views/tl_leaderboard_view.dart +++ b/lib/views/tl_leaderboard_view.dart @@ -58,7 +58,7 @@ class TLLeaderboardState extends State { ); }, icon: const Icon(Icons.compress), - tooltip: t.averages, + tooltip: t.rankAveragesViewTitle, ), ], ), @@ -102,11 +102,11 @@ class TLLeaderboardState extends State { ), TextButton(onPressed: (){ Navigator.push( - context, - MaterialPageRoute( - builder: (context) => RankView(rank: snapshot.data!.getAverageOfRank("")), - ), - ); + context, + MaterialPageRoute( + builder: (context) => RankView(rank: snapshot.data!.getAverageOfRank("")), + ), + ); }, child: Text(t.everyoneAverages, style: const TextStyle(fontSize: 25))) ],) @@ -184,12 +184,12 @@ class TLLeaderboardState extends State { style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 28 : 24, height: 0.9) ), title: Text(allPlayers[index].username, style: TextStyle(fontFamily: bigScreen ? "Eurostile Round Extended" : "Eurostile Round", height: 0.9)), - subtitle: Text(_sortBy == Stats.tr ? "${f2.format(allPlayers[index].apm)} APM, ${f2.format(allPlayers[index].pps)} PPS, ${f2.format(allPlayers[index].vs)} VS, ${f2.format(allPlayers[index].nerdStats.app)} APP, ${f2.format(allPlayers[index].nerdStats.vsapm)} VS/APM" : "${_f4.format(allPlayers[index].getStatByEnum(_sortBy))} ${chartsShortTitles[_sortBy]}", - style: TextStyle(fontFamily: "Eurostile Round Condensed", fontSize: bigScreen ? null : 12, color: _sortBy == Stats.tr ? Colors.grey : null)), + subtitle: (bigScreen || _sortBy != Stats.tr) ? Text(_sortBy == Stats.tr ? "${f2.format(allPlayers[index].apm)} APM, ${f2.format(allPlayers[index].pps)} PPS, ${f2.format(allPlayers[index].vs)} VS, ${f2.format(allPlayers[index].nerdStats.app)} APP, ${f2.format(allPlayers[index].nerdStats.vsapm)} VS/APM" : "${_f4.format(allPlayers[index].getStatByEnum(_sortBy))} ${chartsShortTitles[_sortBy]}", + style: TextStyle(fontFamily: "Eurostile Round Condensed", fontSize: bigScreen ? null : 13, color: _sortBy == Stats.tr ? Colors.grey : null)) : null, trailing: Row( mainAxisSize: MainAxisSize.min, children: [ - Text("${f2.format(allPlayers[index].rating)} TR", style: TextStyle(fontSize: bigScreen ? 28 : 22)), + Text("${f2.format(allPlayers[index].rating)} TR", style: const TextStyle(fontSize: 28)), Image.asset("res/tetrio_tl_alpha_ranks/${allPlayers[index].rank}.png", height: bigScreen ? 48 : 36), ], ), diff --git a/lib/views/tl_match_view.dart b/lib/views/tl_match_view.dart index 96d23ae..7d001ae 100644 --- a/lib/views/tl_match_view.dart +++ b/lib/views/tl_match_view.dart @@ -269,7 +269,7 @@ class TlMatchResultState extends State { label: "Sent", higherIsBetter: true), CompareThingy(greenSide: roundSelector.isNegative ? snapshot.data!.totalStats[greenSidePlayer].garbage.recived : snapshot.data!.stats[roundSelector][greenSidePlayer].garbage.recived, redSide: roundSelector.isNegative ? snapshot.data!.totalStats[redSidePlayer].garbage.recived : snapshot.data!.stats[roundSelector][redSidePlayer].garbage.recived, - label: "Recived", higherIsBetter: true), + label: "Received", higherIsBetter: true), CompareThingy(greenSide: roundSelector.isNegative ? snapshot.data!.totalStats[greenSidePlayer].garbage.attack : snapshot.data!.stats[roundSelector][greenSidePlayer].garbage.attack, redSide: roundSelector.isNegative ? snapshot.data!.totalStats[redSidePlayer].garbage.attack : snapshot.data!.stats[roundSelector][redSidePlayer].garbage.attack, label: "Attack", higherIsBetter: true), diff --git a/lib/widgets/singleplayer_record.dart b/lib/widgets/singleplayer_record.dart index 3dd9209..5d19c94 100644 --- a/lib/widgets/singleplayer_record.dart +++ b/lib/widgets/singleplayer_record.dart @@ -126,7 +126,7 @@ class SingleplayerRecord extends StatelessWidget { LineclearsThingy(record!.endContext.clears, record!.endContext.lines, record!.endContext.holds, record!.endContext.tSpins), if (record!.endContext.gameType == "40l") Text("${record!.endContext.inputs} KP • ${f2.format(record!.endContext.kps)} KPS"), if (record!.endContext.gameType == "blitz") Text("${record!.endContext.piecesPlaced} P • ${record!.endContext.inputs} KP • ${f2.format(record!.endContext.kpp)} KPP • ${f2.format(record!.endContext.kps)} KPS"), - Wrap( + if (record != null) Wrap( alignment: WrapAlignment.spaceBetween, crossAxisAlignment: WrapCrossAlignment.start, spacing: 20, diff --git a/lib/widgets/tl_rating_thingy.dart b/lib/widgets/tl_rating_thingy.dart index 735b06c..f5c3e7e 100644 --- a/lib/widgets/tl_rating_thingy.dart +++ b/lib/widgets/tl_rating_thingy.dart @@ -1,3 +1,5 @@ +import 'dart:math'; + import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'package:tetra_stats/data_objects/tetrio.dart'; @@ -6,14 +8,16 @@ import 'package:tetra_stats/main.dart' show prefs; import 'package:tetra_stats/utils/numers_formats.dart'; var fDiff = NumberFormat("+#,###.####;-#,###.####"); +DateTime seasonEnd = DateTime.utc(2024, 07, 26); class TLRatingThingy extends StatelessWidget{ final String userID; final TetraLeagueAlpha tlData; final TetraLeagueAlpha? oldTl; final double? topTR; + final DateTime? lastMatchPlayed; - const TLRatingThingy({super.key, required this.userID, required this.tlData, this.oldTl, this.topTR}); + const TLRatingThingy({super.key, required this.userID, required this.tlData, this.oldTl, this.topTR, this.lastMatchPlayed}); @override Widget build(BuildContext context) { @@ -23,6 +27,11 @@ class TLRatingThingy extends StatelessWidget{ List formatedTR = f4.format(tlData.rating).split(decimalSeparator); List formatedGlicko = f4.format(tlData.glicko).split(decimalSeparator); List formatedPercentile = f4.format(tlData.percentile * 100).split(decimalSeparator); + DateTime now = DateTime.now(); + bool beforeS1end = now.isBefore(seasonEnd); + int daysLeft = seasonEnd.difference(now).inDays; + print(max(0, 7 - (lastMatchPlayed != null ? now.difference(lastMatchPlayed!).inDays : 7))); + int safeRD = min(100, (100 + ((tlData.rd! >= 100 && tlData.decaying) ? 7 : max(0, 7 - (lastMatchPlayed != null ? now.difference(lastMatchPlayed!).inDays : 7))) - daysLeft).toInt()); return Wrap( direction: Axis.horizontal, alignment: WrapAlignment.spaceAround, @@ -83,7 +92,8 @@ class TLRatingThingy extends StatelessWidget{ if (topTR != null) TextSpan(text: " (${f2.format(topTR)} TR)"), TextSpan(text: " • ${prefs.getInt("ratingMode") == 1 ? "${f2.format(tlData.rating)} TR • RD: " : "Glicko: ${f2.format(tlData.glicko!)}±"}"), TextSpan(text: f2.format(tlData.rd!), style: tlData.decaying ? TextStyle(color: tlData.rd! > 98 ? Colors.red : Colors.yellow) : null), - if (tlData.decaying) WidgetSpan(child: Icon(Icons.trending_up, color: tlData.rd! > 98 ? Colors.red : Colors.yellow,), alignment: PlaceholderAlignment.middle, baseline: TextBaseline.alphabetic) + if (tlData.decaying) WidgetSpan(child: Icon(Icons.trending_up, color: tlData.rd! > 98 ? Colors.red : Colors.yellow,), alignment: PlaceholderAlignment.middle, baseline: TextBaseline.alphabetic), + if (beforeS1end) tlData.rd! <= safeRD ? TextSpan(text: " (Safe)", style: TextStyle(color: Colors.greenAccent)) : TextSpan(text: " (> ${safeRD} RD !!!)", style: TextStyle(color: Colors.redAccent)) ], ), ), diff --git a/lib/widgets/tl_thingy.dart b/lib/widgets/tl_thingy.dart index d22333e..95ffe97 100644 --- a/lib/widgets/tl_thingy.dart +++ b/lib/widgets/tl_thingy.dart @@ -31,7 +31,8 @@ class TLThingy extends StatefulWidget { final double? nextRankCutoff; final double? nextRankCutoffGlicko; final double? nextRankTarget; - 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, this.averages, this.nextRankCutoff, this.thatRankCutoff, this.thatRankCutoffGlicko, this.nextRankCutoffGlicko, this.nextRankTarget, this.thatRankTarget}); + final DateTime? lastMatchPlayed; + 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, this.averages, this.nextRankCutoff, this.thatRankCutoff, this.thatRankCutoffGlicko, this.nextRankCutoffGlicko, this.nextRankTarget, this.thatRankTarget, this.lastMatchPlayed}); @override State createState() => _TLThingyState(); @@ -57,8 +58,8 @@ class _TLThingyState extends State { Widget build(BuildContext context) { final t = Translations.of(context); String decimalSeparator = f2.symbols.DECIMAL_SEP; - List estTRformated = f2.format(currentTl.estTr!.esttr).split(decimalSeparator); - List estTRaccFormated = intFDiff.format(currentTl.esttracc!).split("."); + List estTRformated = currentTl.estTr != null ? f2.format(currentTl.estTr!.esttr).split(decimalSeparator) : []; + List estTRaccFormated = currentTl.esttracc != null ? intFDiff.format(currentTl.esttracc!).split(".") : []; 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) { bool bigScreen = constraints.maxWidth >= 768; @@ -92,7 +93,7 @@ class _TLThingyState extends State { }); }, ), - if (currentTl.gamesPlayed >= 10) TLRatingThingy(userID: widget.userID, tlData: currentTl, oldTl: oldTl, topTR: widget.topTR), + if (currentTl.gamesPlayed > 9) TLRatingThingy(userID: widget.userID, tlData: currentTl, oldTl: oldTl, topTR: widget.topTR, lastMatchPlayed: widget.lastMatchPlayed), if (currentTl.gamesPlayed > 9) TLProgress( tlData: currentTl, previousRankTRcutoff: widget.thatRankCutoff, diff --git a/pubspec.yaml b/pubspec.yaml index 24111e6..2007d63 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: tetra_stats description: Track your and other player stats in TETR.IO publish_to: 'none' -version: 1.6.0+20 +version: 1.6.1+21 environment: sdk: '>=3.0.0' diff --git a/res/i18n/strings.i18n.json b/res/i18n/strings.i18n.json index 3136a87..8dd3631 100644 --- a/res/i18n/strings.i18n.json +++ b/res/i18n/strings.i18n.json @@ -186,7 +186,7 @@ "fromBeginning": "From beginning", "calc": "Calc", "calcViewNoValues": "Enter values to calculate the stats", - "rankAveragesViewTitle": "Ranks cutoff and average stats", + "rankAveragesViewTitle": "Ranks cutoffs", "sprintAndBlitsViewTitle": "40 lines and Blitz averages", "sprintAndBlitsRelevance": "Relevance: ${date}", "rank": "Rank", diff --git a/res/i18n/strings_ru.i18n.json b/res/i18n/strings_ru.i18n.json index 00dbc00..63c49d5 100644 --- a/res/i18n/strings_ru.i18n.json +++ b/res/i18n/strings_ru.i18n.json @@ -186,7 +186,7 @@ "fromBeginning": "С начала", "calc": "Считать", "calcViewNoValues": "Введите значения, чтобы посчитать статистику", - "rankAveragesViewTitle": "Требования рангов и средние значения", + "rankAveragesViewTitle": "Требования рангов", "sprintAndBlitsViewTitle": "Средние результаты 40 линий и блица", "sprintAndBlitsRelevance": "Актуальность: ${date}", "rank": "Ранг", diff --git a/web/index.html b/web/index.html index 745bea0..5626d5d 100644 --- a/web/index.html +++ b/web/index.html @@ -80,6 +80,9 @@ font-weight: 500; font-style: normal; } + a{ + color: aqua; + } #preloader{ display: flex; width: fit-content; @@ -111,6 +114,15 @@ padding-top: 8px; animation: 1s cubic-bezier(.46,.03,.52,.96) infinite alternate breathing; } + .error{ + color: red; + } + #tip{ + position: absolute; + bottom: 5%; + color: gray; + text-align: center; + } @media (max-width: 502px){ #preloader{ flex-direction: column; @@ -130,24 +142,45 @@

Loading...

-