diff --git a/messages/messages.proto b/messages/messages.proto index 61d2067..0600a1d 100644 --- a/messages/messages.proto +++ b/messages/messages.proto @@ -209,37 +209,38 @@ message Message { string username = 1; string player_id = 2; string game_id = 3; + bool silent = 4; oneof data { - Connection connection = 4; - Licitiranje licitiranje = 5; - Card card = 6; - LicitiranjeStart licitiranje_start = 7; - GameStart game_start = 8; - LoginRequest login_request = 9; - LoginInfo login_info = 10; - LoginResponse login_response = 11; - ClearDesk clear_desk = 12; - Results results = 13; - UserList user_list = 14; - KingSelection king_selection = 15; - StartPredictions start_predictions = 16; - Predictions predictions = 17; - TalonReveal talon_reveal = 18; - PlayingReveal playing_reveal = 19; - TalonSelection talon_selection = 20; - Stash stash = 21; - GameEnd game_end = 22; - GameStartCountdown game_start_countdown = 23; - Predictions predictions_resend = 24; - Radelci radelci = 25; - Time time = 26; - ChatMessage chat_message = 27; - InvitePlayer invite_player = 28; - StashedTarock stashed_tarock = 29; - ClearHand clear_hand = 30; - ReplayLink replay_link = 31; - ReplayMove replay_move = 32; - ReplaySelectGame replay_select_game = 33; + Connection connection = 10; + Licitiranje licitiranje = 11; + Card card = 12; + LicitiranjeStart licitiranje_start = 13; + GameStart game_start = 14; + LoginRequest login_request = 15; + LoginInfo login_info = 16; + LoginResponse login_response = 17; + ClearDesk clear_desk = 18; + Results results = 19; + UserList user_list = 20; + KingSelection king_selection = 21; + StartPredictions start_predictions = 22; + Predictions predictions = 23; + TalonReveal talon_reveal = 24; + PlayingReveal playing_reveal = 25; + TalonSelection talon_selection = 26; + Stash stash = 27; + GameEnd game_end = 28; + GameStartCountdown game_start_countdown = 29; + Predictions predictions_resend = 30; + Radelci radelci = 31; + Time time = 32; + ChatMessage chat_message = 33; + InvitePlayer invite_player = 34; + StashedTarock stashed_tarock = 35; + ClearHand clear_hand = 36; + ReplayLink replay_link = 37; + ReplayMove replay_move = 38; + ReplaySelectGame replay_select_game = 39; } } diff --git a/tarok/lib/controller.dart b/tarok/lib/controller.dart index 1047b1c..d028c73 100644 --- a/tarok/lib/controller.dart +++ b/tarok/lib/controller.dart @@ -77,8 +77,14 @@ class Controller extends GetxController { var barvic = false.obs; var mondfang = false.obs; + final bool bbots = Get.parameters["bots"] == "true"; + final String gameId = Get.parameters["gameId"]!; + final int playing = int.parse(Get.parameters["playing"]!); + final bool replay = Get.parameters["replay"] == "true"; + stockskis.StockSkis? stockskisContext; var controller = TextEditingController().obs; + var gameLinkController = TextEditingController().obs; List prijatelji = [].obs; late final WebSocket socket; @@ -391,6 +397,24 @@ class Controller extends GetxController { socket.send(message); } + Future sendReplayNext() async { + if (bots) return; + if (!replay) return; + final Uint8List message = Messages.Message( + replayMove: Messages.ReplayMove(), + ).writeToBuffer(); + socket.send(message); + } + + Future sendReplayGame(int game) async { + if (bots) return; + if (!replay) return; + final Uint8List message = Messages.Message( + replaySelectGame: Messages.ReplaySelectGame(game: game), + ).writeToBuffer(); + socket.send(message); + } + Future sendMessage() async { if (bots) return; final Uint8List message = Messages.Message( @@ -562,7 +586,6 @@ class Controller extends GetxController { } cards.remove(card); - turn.value = false; bool early = await addToStih("player", "player", card.asset); if (early) return; @@ -584,7 +607,6 @@ class Controller extends GetxController { .writeToBuffer(); socket.send(message); turn.value = false; - cards.remove(card); resetPremoves(); premovedCard.value = null; } @@ -904,17 +926,29 @@ class Controller extends GetxController { startPredicting.value = false; final userId = msg.playerId; - if (userId != playerId) { - for (int i = 0; i < userWidgets.length; i++) { - if (userWidgets[i].id != userId) continue; - for (int n = 0; n < userWidgets[i].cards.length; n++) { - stockskis.Card c = userWidgets[i].cards[n]; - if (card.id != c.card.asset) continue; - userWidgets[i].cards.removeAt(n); - break; - } + for (int i = 0; i < userWidgets.length; i++) { + if (userWidgets[i].id != userId) continue; + for (int n = 0; n < userWidgets[i].cards.length; n++) { + stockskis.Card c = userWidgets[i].cards[n]; + if (card.id != c.card.asset) continue; + userWidgets[i].cards.removeAt(n); + break; + } + + if (userId != playerId) { + break; + } + + turn.value = false; + + for (int n = 0; n < cards.length; n++) { + stockskis.LocalCard c = cards[n]; + if (card.id != c.asset) continue; + cards.removeAt(n); break; } + + break; } if (firstCard.value == null) { @@ -1045,7 +1079,7 @@ class Controller extends GetxController { if (users.isEmpty) return; - debugPrint("urejam vrstni red v `users`"); + debugPrint("urejam vrstni red v `users` ${users}"); // uredimo vrstni red naših igralcev //users = []; @@ -1078,6 +1112,9 @@ class Controller extends GetxController { } debugPrint("anotacije so bile dodane"); + + users.refresh(); + userWidgets.refresh(); } else if (msg.hasGameStartCountdown()) { countdown.value = msg.gameStartCountdown.countdown; } else if (msg.hasLicitiranje()) { @@ -1114,38 +1151,51 @@ class Controller extends GetxController { validCards(); } else if (msg.hasResults()) { final r = msg.results; - results.value = r; - bool radelc = false; - for (int i = 0; i < results.value!.user.length; i++) { - if (results.value!.user[i].radelc) { - radelc = true; - break; - } - } - for (int n = 0; n < users.length; n++) { - users[n].points.add( - stockskis.ResultsPoints( - playing: false, - points: 0, - results: ResultsCompLayer.messagesToStockSkis(r), - radelc: radelc, - ), - ); + + if (!msg.silent) { + results.value = r; } - for (int i = 0; i < r.user.length; i++) { - final user = r.user[i]; - for (int k = 0; k < user.user.length; k++) { - Messages.User u = user.user[k]; - for (int n = 0; n < users.length; n++) { - if (users[n].id != u.id) continue; - debugPrint( - "found ${u.id} with points ${user.points} and total ${users[n].total}"); - users[n].points.last.points += user.points; - users[n].total += user.points; - users[n].points.last.playing = user.playing; + + if (msg.silent || !replay) { + results.value = r; + bool radelc = false; + for (int i = 0; i < results.value!.user.length; i++) { + if (results.value!.user[i].radelc) { + radelc = true; break; } } + for (int n = 0; n < users.length; n++) { + debugPrint("Dodajam točke igralcu ${users[n].id}"); + users[n].points.add( + stockskis.ResultsPoints( + playing: false, + points: 0, + results: ResultsCompLayer.messagesToStockSkis(r), + radelc: radelc, + ), + ); + } + for (int i = 0; i < r.user.length; i++) { + final user = r.user[i]; + for (int k = 0; k < user.user.length; k++) { + Messages.User u = user.user[k]; + for (int n = 0; n < users.length; n++) { + if (users[n].id != u.id) continue; + debugPrint( + "found ${u.id} with points ${user.points} and total ${users[n].total}"); + users[n].points.last.points += user.points; + users[n].total += user.points; + users[n].points.last.playing = user.playing; + break; + } + } + } + users.refresh(); + } + + if (msg.silent) { + results.value = null; } } else if (msg.hasTalonSelection()) { final talonSelection = msg.talonSelection; @@ -1268,6 +1318,8 @@ class Controller extends GetxController { } } else if (msg.hasClearHand()) { cards.value = []; + } else if (msg.hasReplayLink()) { + gameLinkController.value.text = msg.replayLink.replay; } }, onDone: () { diff --git a/tarok/lib/game.dart b/tarok/lib/game.dart index 41e722c..0d2c77a 100644 --- a/tarok/lib/game.dart +++ b/tarok/lib/game.dart @@ -2,12 +2,12 @@ import 'dart:math'; import 'package:draggable_widget/draggable_widget.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_initicon/flutter_initicon.dart'; import 'package:get/get.dart'; import 'package:tarok/constants.dart'; import 'package:tarok/controller.dart'; import 'package:stockskis/stockskis.dart' as stockskis; -import 'package:tarok/game/variables.dart'; import 'stockskis_compatibility/compatibility.dart'; @@ -72,16 +72,18 @@ class Game extends StatelessWidget { height: fullHeight / 1.6, width: fullWidth / 4, child: DefaultTabController( - length: 4, + length: controller.replay ? 5 : 4, child: Scaffold( appBar: AppBar( automaticallyImplyLeading: false, elevation: 0, - flexibleSpace: const TabBar(tabs: [ - Tab(icon: Icon(Icons.timeline)), - Tab(icon: Icon(Icons.chat)), - Tab(icon: Icon(Icons.bug_report)), - Tab(icon: Icon(Icons.info)), + flexibleSpace: TabBar(tabs: [ + const Tab(icon: Icon(Icons.timeline)), + const Tab(icon: Icon(Icons.chat)), + if (controller.replay) + const Tab(icon: Icon(Icons.fast_rewind)), + const Tab(icon: Icon(Icons.bug_report)), + const Tab(icon: Icon(Icons.info)), ]), ), body: TabBarView(children: [ @@ -137,6 +139,7 @@ class Game extends StatelessWidget { int index) => GestureDetector( onTap: () { + controller.sendReplayGame(index); controller.results.value = ResultsCompLayer .stockSkisToMessages( @@ -254,6 +257,17 @@ class Game extends StatelessWidget { }, ), ]), + if (controller.replay) + Column(children: [ + Center( + child: IconButton( + onPressed: () async { + await controller.sendReplayNext(); + }, + icon: const Icon(Icons.fast_forward), + ), + ), + ]), ListView(children: [ const Center( child: Text( @@ -442,7 +456,8 @@ class Game extends StatelessWidget { ), // ŠTIHI - if (playing == 3 && !(controller.bots && SLEPI_TAROK)) + if (controller.playing == 3 && + !(controller.bots && SLEPI_TAROK)) ...controller.stihi3( cardK, m, @@ -451,7 +466,8 @@ class Game extends StatelessWidget { cardToWidth, fullWidth, ), - if (playing == 4 && !(controller.bots && SLEPI_TAROK)) + if (controller.playing == 4 && + !(controller.bots && SLEPI_TAROK)) ...controller.stihi4( cardK, m, @@ -580,7 +596,7 @@ class Game extends StatelessWidget { ), // IMENA - if (playing == 4) + if (controller.playing == 4) ...controller.generateNames4( leftFromTop, m, @@ -589,7 +605,7 @@ class Game extends StatelessWidget { fullWidth, fullHeight, ), - if (playing == 3) + if (controller.playing == 3) ...controller.generateNames3( leftFromTop, m, @@ -1595,516 +1611,516 @@ class Game extends StatelessWidget { child: Card( child: SingleChildScrollView( child: Container( - constraints: BoxConstraints( - maxWidth: - fullWidth < 1000 ? fullWidth : fullWidth / 1.5, - ), padding: const EdgeInsets.all(10), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - ...controller.results.value!.user.map( - (e) => Column( - children: [ - ...e.user.map( - (e2) => e2.name != "" - ? Row( - mainAxisAlignment: - MainAxisAlignment.center, - children: [ - SizedBox( - height: 30, - width: 30, - child: Initicon( - text: e2.name, - elevation: 4, - size: 30, - backgroundColor: - HSLColor.fromAHSL( - 1, - hashCode(e2 - .name) % - 360, - 1, - 0.6) - .toColor(), - borderRadius: - BorderRadius.zero, + child: IntrinsicWidth( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + ...controller.results.value!.user.map( + (e) => Column( + children: [ + ...e.user.map( + (e2) => e2.name != "" + ? Row( + mainAxisAlignment: + MainAxisAlignment.center, + children: [ + SizedBox( + height: 30, + width: 30, + child: Initicon( + text: e2.name, + elevation: 4, + size: 30, + backgroundColor: + HSLColor.fromAHSL( + 1, + hashCode(e2 + .name) % + 360, + 1, + 0.6) + .toColor(), + borderRadius: + BorderRadius.zero, + ), ), - ), - const SizedBox(width: 10), - Text( - e2.name, - style: const TextStyle( - fontSize: 20), - ), - ], - ) - : const SizedBox(), - ), - DataTable( - dataRowMaxHeight: 40, - dataRowMinHeight: 40, - headingRowHeight: 40, - columns: const [ - DataColumn( - label: Expanded( - child: Text( - 'Napoved', - style: TextStyle( - fontStyle: FontStyle.italic), - ), - ), - ), - DataColumn( - label: Expanded( - child: Text( - 'Kontra', - style: TextStyle( - fontStyle: FontStyle.italic), + const SizedBox(width: 10), + Text( + e2.name, + style: const TextStyle( + fontSize: 20), + ), + ], + ) + : const SizedBox(), + ), + DataTable( + dataRowMaxHeight: 40, + dataRowMinHeight: 40, + headingRowHeight: 40, + columns: const [ + DataColumn( + label: Expanded( + child: Text( + 'Napoved', + style: TextStyle( + fontStyle: + FontStyle.italic), + ), ), ), - ), - DataColumn( - label: Expanded( - child: Text( - 'Rezultat', - style: TextStyle( - fontStyle: FontStyle.italic), + DataColumn( + label: Expanded( + child: Text( + 'Kontra', + style: TextStyle( + fontStyle: + FontStyle.italic), + ), ), ), - ), - DataColumn( - label: Expanded( - child: Text( - 'Napovedal', - style: TextStyle( - fontStyle: FontStyle.italic), + DataColumn( + label: Expanded( + child: Text( + 'Rezultat', + style: TextStyle( + fontStyle: + FontStyle.italic), + ), ), ), - ), - DataColumn( - label: Expanded( - child: Text( - 'Kontro dal', - style: TextStyle( - fontStyle: FontStyle.italic), + DataColumn( + label: Expanded( + child: Text( + 'Napovedal', + style: TextStyle( + fontStyle: + FontStyle.italic), + ), ), ), - ), - ], - rows: [ - if (e.showGamemode) - DataRow( - cells: [ - const DataCell(Text('Igra')), - DataCell(Text( - '${pow(2, e.kontraIgra)}x')), - DataCell(Text( - '${e.igra}', + DataColumn( + label: Expanded( + child: Text( + 'Kontro dal', style: TextStyle( - color: e.igra < 0 - ? Colors.red - : Colors.green, - ), - )), - DataCell(Text(controller - .currentPredictions - .value! - .igra - .name)), - DataCell(Text(controller - .currentPredictions - .value! - .igraKontraDal - .name)), - ], + fontStyle: + FontStyle.italic), + ), + ), ), - if (e.showDifference) - DataRow( - cells: [ - const DataCell(Text('Razlika')), - DataCell(Text( - '${pow(2, e.kontraIgra)}x')), - DataCell(Text( - '${e.razlika}', - style: TextStyle( - color: e.razlika < 0 - ? Colors.red - : Colors.green, - ), - )), - const DataCell(Text("")), - DataCell( - Text(controller + ], + rows: [ + if (e.showGamemode) + DataRow( + cells: [ + const DataCell(Text('Igra')), + DataCell(Text( + '${pow(2, e.kontraIgra)}x')), + DataCell(Text( + '${e.igra}', + style: TextStyle( + color: e.igra < 0 + ? Colors.red + : Colors.green, + ), + )), + DataCell(Text(controller + .currentPredictions + .value! + .igra + .name)), + DataCell(Text(controller .currentPredictions .value! .igraKontraDal - .name), - ), - ], - ), - if (e.showTrula) - DataRow( - cells: [ - const DataCell(Text('Trula')), - const DataCell(Text('1x')), - DataCell(Text( - '${e.trula}', - style: TextStyle( - color: e.trula < 0 - ? Colors.red - : Colors.green, - ), - )), - DataCell(Text(controller - .currentPredictions - .value! - .trula - .name)), - const DataCell(Text("")), - ], - ), - if (e.showKralji) - DataRow( - cells: [ - const DataCell(Text('Kralji')), - const DataCell(Text('1x')), - DataCell(Text( - '${e.kralji}', - style: TextStyle( - color: e.kralji < 0 - ? Colors.red - : Colors.green, - ), - )), - DataCell(Text(controller - .currentPredictions - .value! - .kralji - .name)), - const DataCell(Text("")), - ], - ), - if (e.showKralj) - DataRow( - cells: [ - const DataCell( - Text('Kralj ultimo')), - DataCell(Text( - '${pow(2, e.kontraKralj)}x')), - DataCell(Text( - '${e.kralj}', - style: TextStyle( - color: e.kralj < 0 - ? Colors.red - : Colors.green, - ), - )), - DataCell(Text(controller - .currentPredictions - .value! - .kraljUltimo - .name)), - DataCell(Text(controller - .currentPredictions - .value! - .kraljUltimoKontraDal - .name)), - ], - ), - if (e.showPagat) - DataRow( - cells: [ - const DataCell( - Text('Pagat ultimo')), - DataCell(Text( - '${pow(2, e.kontraPagat)}x')), - DataCell(Text( - '${e.pagat}', - style: TextStyle( - color: e.pagat < 0 - ? Colors.red - : Colors.green, + .name)), + ], + ), + if (e.showDifference) + DataRow( + cells: [ + const DataCell(Text('Razlika')), + DataCell(Text( + '${pow(2, e.kontraIgra)}x')), + DataCell(Text( + '${e.razlika}', + style: TextStyle( + color: e.razlika < 0 + ? Colors.red + : Colors.green, + ), + )), + const DataCell(Text("")), + DataCell( + Text(controller + .currentPredictions + .value! + .igraKontraDal + .name), ), - )), - DataCell(Text(controller - .currentPredictions - .value! - .pagatUltimo - .name)), - DataCell(Text(controller - .currentPredictions - .value! - .pagatUltimoKontraDal - .name)), - ], - ), - if (e.mondfang) + ], + ), + if (e.showTrula) + DataRow( + cells: [ + const DataCell(Text('Trula')), + const DataCell(Text('1x')), + DataCell(Text( + '${e.trula}', + style: TextStyle( + color: e.trula < 0 + ? Colors.red + : Colors.green, + ), + )), + DataCell(Text(controller + .currentPredictions + .value! + .trula + .name)), + const DataCell(Text("")), + ], + ), + if (e.showKralji) + DataRow( + cells: [ + const DataCell(Text('Kralji')), + const DataCell(Text('1x')), + DataCell(Text( + '${e.kralji}', + style: TextStyle( + color: e.kralji < 0 + ? Colors.red + : Colors.green, + ), + )), + DataCell(Text(controller + .currentPredictions + .value! + .kralji + .name)), + const DataCell(Text("")), + ], + ), + if (e.showKralj) + DataRow( + cells: [ + const DataCell( + Text('Kralj ultimo')), + DataCell(Text( + '${pow(2, e.kontraKralj)}x')), + DataCell(Text( + '${e.kralj}', + style: TextStyle( + color: e.kralj < 0 + ? Colors.red + : Colors.green, + ), + )), + DataCell(Text(controller + .currentPredictions + .value! + .kraljUltimo + .name)), + DataCell(Text(controller + .currentPredictions + .value! + .kraljUltimoKontraDal + .name)), + ], + ), + if (e.showPagat) + DataRow( + cells: [ + const DataCell( + Text('Pagat ultimo')), + DataCell(Text( + '${pow(2, e.kontraPagat)}x')), + DataCell(Text( + '${e.pagat}', + style: TextStyle( + color: e.pagat < 0 + ? Colors.red + : Colors.green, + ), + )), + DataCell(Text(controller + .currentPredictions + .value! + .pagatUltimo + .name)), + DataCell(Text(controller + .currentPredictions + .value! + .pagatUltimoKontraDal + .name)), + ], + ), + if (e.mondfang) + DataRow( + cells: [ + const DataCell( + Text('Izguba monda')), + DataCell(Text( + '${pow(2, e.kontraMondfang)}x')), + DataCell(Text( + "${e.points}", + style: TextStyle( + color: e.points < 0 + ? Colors.red + : Colors.green, + ), + )), + DataCell(Text(controller + .currentPredictions + .value! + .mondfang + .name)), + DataCell(Text(controller + .currentPredictions + .value! + .mondfangKontraDal + .name)), + ], + ), + if (e.skisfang) + const DataRow( + cells: [ + DataCell(Text('Škisfang')), + DataCell(Text('/')), + DataCell(Text( + '-100', + style: TextStyle( + color: Colors.red, + ), + )), + DataCell(Text("")), + DataCell(Text("")), + ], + ), DataRow( cells: [ - const DataCell( - Text('Izguba monda')), - DataCell(Text( - '${pow(2, e.kontraMondfang)}x')), - DataCell(Text( - "${e.points}", - style: TextStyle( - color: e.points < 0 - ? Colors.red - : Colors.green, - ), - )), - DataCell(Text(controller - .currentPredictions - .value! - .mondfang - .name)), - DataCell(Text(controller - .currentPredictions - .value! - .mondfangKontraDal - .name)), - ], - ), - if (e.skisfang) - const DataRow( - cells: [ - DataCell(Text('Škisfang')), - DataCell(Text('/')), - DataCell(Text( - '-100', - style: TextStyle( - color: Colors.red, - ), - )), - DataCell(Text("")), - DataCell(Text("")), - ], - ), - DataRow( - cells: [ - const DataCell(Text('Skupaj')), - const DataCell(Text('')), - DataCell( - RichText( - text: TextSpan( - children: [ - if (e.radelc) + const DataCell(Text('Skupaj')), + const DataCell(Text('')), + DataCell( + RichText( + text: TextSpan( + children: [ + if (e.radelc) + TextSpan( + text: + '${e.points ~/ 2} * 2 = ', + style: + const TextStyle( + color: Colors.grey, + ), + ), TextSpan( - text: - '${e.points ~/ 2} * 2 = ', - style: const TextStyle( - color: Colors.grey, + text: '${e.points}', + style: TextStyle( + color: e.points < 0 + ? Colors.red + : Colors.green, + fontWeight: + FontWeight.bold, ), ), - TextSpan( - text: '${e.points}', - style: TextStyle( - color: e.points < 0 - ? Colors.red - : Colors.green, - fontWeight: - FontWeight.bold, - ), - ), - ], + ], + ), ), ), - ), - const DataCell(Text("")), - const DataCell(Text("")), - ], - ), - ], - ), - ], - ), - ), - const SizedBox(height: 20), - ...controller.users.map((user) => user.endGame - ? Text("${user.name} želi končati igro.") - : const SizedBox()), - if (!controller.requestedGameEnd.value && - !controller.bots) - ElevatedButton( - onPressed: controller.gameEndSend, - child: const Text( - "Zaključi igro", + const DataCell(Text("")), + const DataCell(Text("")), + ], + ), + ], + ), + ], ), ), - const SizedBox(height: 30), + const SizedBox(height: 20), + ...controller.users.map((user) => user.endGame + ? Text("${user.name} želi končati igro.") + : const SizedBox()), + if (!controller.requestedGameEnd.value && + !controller.bots) + ElevatedButton( + onPressed: controller.gameEndSend, + child: const Text( + "Zaključi igro", + ), + ), + const SizedBox(height: 30), - ElevatedButton( - onPressed: () { - controller.razpriKarte.value = - !controller.razpriKarte.value; - }, - child: controller.razpriKarte.value - ? const Text( - "Skrij točkovanje po štihih", - ) - : const Text( - "Prikaži točkovanje po štihih", - ), - ), + ElevatedButton( + onPressed: () { + controller.razpriKarte.value = + !controller.razpriKarte.value; + }, + child: controller.razpriKarte.value + ? const Text( + "Skrij točkovanje po štihih", + ) + : const Text( + "Prikaži točkovanje po štihih", + ), + ), - // pobrane karte v štihu - if (controller.razpriKarte.value) - const Text("Pobrane karte:", - style: TextStyle(fontSize: 30)), - if (controller.razpriKarte.value) - Wrap( - children: [ - ...controller.results.value!.stih - .asMap() - .entries - .map( - (e) => e.value.card.isNotEmpty - ? Card( - elevation: 6, - child: Column( - children: [ - const SizedBox( - height: 10), - Text( - controller - .currentPredictions - .value! - .gamemode >= - 0 && - controller - .currentPredictions - .value! - .gamemode <= - 5 - ? (e.key == 0 - ? "Založeno" - : e.key + 1 == - controller - .results - .value! - .stih - .length - ? "Talon" - : "${e.key}. štih") - : (controller - .currentPredictions - .value! - .gamemode == - -1 - ? "${e.key + 1}. štih" - : (e.key + 1 == - controller - .results - .value! - .stih - .length - ? "Talon" - : "${e.key + 1}. štih")), - style: TextStyle( - fontWeight: e.value - .pickedUpByPlaying - ? FontWeight.bold - : FontWeight.w300, - fontSize: 20, + // pobrane karte v štihu + if (controller.razpriKarte.value) + const Text("Pobrane karte:", + style: TextStyle(fontSize: 30)), + if (controller.razpriKarte.value) + Wrap( + children: [ + ...controller.results.value!.stih + .asMap() + .entries + .map( + (e) => e.value.card.isNotEmpty + ? Card( + elevation: 6, + child: Column( + children: [ + const SizedBox( + height: 10), + Text( + controller.currentPredictions.value! + .gamemode >= + 0 && + controller + .currentPredictions + .value! + .gamemode <= + 5 + ? (e.key == 0 + ? "Založeno" + : e.key + 1 == + controller + .results + .value! + .stih + .length + ? "Talon" + : "${e.key}. štih") + : (controller + .currentPredictions + .value! + .gamemode == + -1 + ? "${e.key + 1}. štih" + : (e.key + 1 == + controller + .results + .value! + .stih + .length + ? "Talon" + : "${e.key + 1}. štih")), + style: TextStyle( + fontWeight: e.value + .pickedUpByPlaying + ? FontWeight + .bold + : FontWeight + .w300, + fontSize: 20, + ), ), - ), - const SizedBox( - height: 10), - SizedBox( - // neka bs konstanta, ki izvira iz nekaj vrstic bolj gor - // ali imam mentalne probleme? ja. - // ali me briga? ne. - // fuck bad code quality (e) => SizedBox( - width: fullHeight / - 8 * - 0.573 * - (1 + - 0.7 * - (e.value.card.length - - 1)) + - e.value.card - .length * - 3, - height: fullHeight / 8, - child: Stack( - children: [ - ...e.value.card - .asMap() - .entries - .map( - (entry) => - Positioned( - left: (fullHeight / - 8 * - 0.573 * - 0.7 * - entry.key) - .toDouble(), - child: - ClipRRect( - borderRadius: - BorderRadius.circular(10 * - (fullWidth / 10000)), + const SizedBox( + height: 10), + SizedBox( + // neka bs konstanta, ki izvira iz nekaj vrstic bolj gor + // ali imam mentalne probleme? ja. + // ali me briga? ne. + // fuck bad code quality (e) => SizedBox( + width: fullHeight / + 8 * + 0.573 * + (1 + + 0.7 * + (e.value.card.length - + 1)) + + e.value.card + .length * + 3, + height: + fullHeight / 8, + child: Stack( + children: [ + ...e.value.card + .asMap() + .entries + .map( + (entry) => + Positioned( + left: (fullHeight / + 8 * + 0.573 * + 0.7 * + entry.key) + .toDouble(), child: - Stack( - children: [ - Container( - color: - Colors.white, - width: fullHeight / - 8 * - 0.57, - height: - fullHeight / 8, - ), - SizedBox( - height: - fullHeight / 8, - child: - Image.asset( - "assets/tarok${entry.value.id}.webp", - filterQuality: FilterQuality.medium, + ClipRRect( + borderRadius: + BorderRadius.circular(10 * + (fullWidth / 10000)), + child: + Stack( + children: [ + Container( + color: Colors.white, + width: fullHeight / 8 * 0.57, + height: fullHeight / 8, + ), + SizedBox( + height: fullHeight / 8, + child: Image.asset( + "assets/tarok${entry.value.id}.webp", + filterQuality: FilterQuality.medium, + ), ), - ), - ], + ], + ), ), ), ), - ), - ], + ], + ), ), - ), - const SizedBox( - height: 10), - Text( - "Štih je vreden ${e.value.worth.round()} ${e.value.worth == 3 || e.value.worth == 4 ? 'točke' : e.value.worth == 2 ? 'točki' : e.value.worth == 1 ? 'točko' : 'točk'}.", - ), - if (e.value.pickedUpBy != - "") + const SizedBox( + height: 10), Text( - "Štih je pobral ${e.value.pickedUpBy}.", + "Štih je vreden ${e.value.worth.round()} ${e.value.worth == 3 || e.value.worth == 4 ? 'točke' : e.value.worth == 2 ? 'točki' : e.value.worth == 1 ? 'točko' : 'točk'}.", ), - const SizedBox( - height: 10), - ], - ), - ) - : const SizedBox(), - ), - ], - ), - const SizedBox(height: 10), - ElevatedButton( - onPressed: () { - controller.results.value = null; - }, - child: const Text( - "Zapri vpogled v rezultate", + if (e.value + .pickedUpBy != + "") + Text( + "Štih je pobral ${e.value.pickedUpBy}.", + ), + const SizedBox( + height: 10), + ], + ), + ) + : const SizedBox(), + ), + ], + ), + const SizedBox(height: 10), + ElevatedButton( + onPressed: () { + controller.results.value = null; + }, + child: const Text( + "Zapri vpogled v rezultate", + ), ), - ), - ], + ], + ), ), ), ), @@ -2186,6 +2202,33 @@ class Game extends StatelessWidget { ) .toList(), ), + const SizedBox( + height: 10, + ), + if (controller.gameLinkController.value.text != + "") + Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + IntrinsicWidth( + child: TextField( + controller: + controller.gameLinkController.value, + readOnly: true, + ), + ), + IconButton( + onPressed: () async { + Clipboard.setData(ClipboardData( + text: controller + .gameLinkController.value.text, + )); + }, + icon: const Icon(Icons.copy), + ), + ], + ), ], ), ), @@ -2198,7 +2241,8 @@ class Game extends StatelessWidget { ), ), floatingActionButton: Obx( - () => !(controller.started.value && !controller.bots) + () => (!(controller.started.value && !controller.bots) || + controller.replay) ? FloatingActionButton( onPressed: () { try { diff --git a/tarok/lib/game/variables.dart b/tarok/lib/game/variables.dart index 56f2a4e..bb9b7fa 100644 --- a/tarok/lib/game/variables.dart +++ b/tarok/lib/game/variables.dart @@ -1,5 +1,2 @@ late String playerId; String name = "Igralec"; -bool bbots = true; -String gameId = ""; -int playing = 4; diff --git a/tarok/lib/main.dart b/tarok/lib/main.dart index 0ea221b..7cf0142 100644 --- a/tarok/lib/main.dart +++ b/tarok/lib/main.dart @@ -30,6 +30,52 @@ Future preloadCards(BuildContext context) async { } } +Future replayFetch(String url) async { + final r = RegExp(r"(https:\/\/.*\/replay\/)(.*)(\?password=)(.*)"); + final match = r.firstMatch(url); + if (match == null) { + return null; + } + String? uid = match.group(2); + if (uid == null) { + return null; + } + String? password = match.group(4); + if (password == null) { + return null; + } + password = Uri.encodeFull(password); + uid = Uri.encodeFull(uid); + + debugPrint("$uid $password"); + + final token = await storage.read(key: "token"); + if (token == null) return null; + final response = await dio.get( + '$BACKEND_URL/replay/$uid?password=$password', + options: Options( + headers: {"X-Login-Token": await storage.read(key: "token")}, + ), + ); + return response.data; +} + +Future joinReplay(String url) async { + String? r = await replayFetch(url); + if (r == null) { + return; + } + Map s = jsonDecode(r); + String gameId = s["replayId"].toString(); + String players = s["playerCount"].toString(); + Get.toNamed("/game", parameters: { + "playing": players, + "gameId": gameId, + "bots": "false", + "replay": "true", + }); +} + void main() async { WidgetsFlutterBinding.ensureInitialized(); setPathUrlStrategy(); @@ -83,6 +129,16 @@ void main() async { GetPage(name: '/registration', page: () => const Register()), GetPage(name: '/friends', page: () => const Friends()), GetPage(name: '/about', page: () => const About()), + GetPage( + name: '/replay/:id', + page: () => FutureBuilder( + future: joinReplay( + "https://palcka.si/replay/${Get.parameters['id']}?password=${Get.parameters['password']}"), + builder: (BuildContext context, AsyncSnapshot snapshot) { + return const SizedBox(); + }, + ), + ), ], darkTheme: ThemeData( primaryColor: Colors.deepPurple, @@ -108,6 +164,7 @@ class _MyHomePageState extends State { List botNames = []; late TextEditingController _controller; late TextEditingController _playerNameController; + late TextEditingController _replayController; bool renderLogin = false; bool guest = false; bool mondfang = false; @@ -224,9 +281,6 @@ class _MyHomePageState extends State { } Future newGame(int players) async { - bbots = false; - playing = players; - final token = await storage.read(key: "token"); if (token == null) return; final response = await dio.post( @@ -243,13 +297,17 @@ class _MyHomePageState extends State { headers: {"X-Login-Token": await storage.read(key: "token")}, ), ); - gameId = response.data.toString(); + String gameId = response.data.toString(); debugPrint(response.statusCode.toString()); debugPrint(gameId); // ignore: use_build_context_synchronously //Navigator.pop(context); // ignore: use_build_context_synchronously - Get.toNamed("/game"); + Get.toNamed("/game", parameters: { + "playing": players.toString(), + "gameId": gameId, + "bots": "false", + }); } Future quickGameFind(int players, String tip) async { @@ -262,20 +320,20 @@ class _MyHomePageState extends State { headers: {"X-Login-Token": await storage.read(key: "token")}, ), ); - gameId = response.data.toString(); - playing = players; - // ignore: use_build_context_synchronously - //Navigator.pop(context); - // ignore: use_build_context_synchronously - Get.toNamed("/game"); + String gameId = response.data.toString(); + Get.toNamed("/game", parameters: { + "playing": players.toString(), + "gameId": gameId, + "bots": "false", + }); } void botGame(int players) { - bbots = true; - gameId = ""; - playing = players; - //Navigator.pop(context); - Get.toNamed("/game"); + Get.toNamed("/game", parameters: { + "playing": players.toString(), + "gameId": "", + "bots": "true", + }); } void rerenderLogin() { @@ -287,8 +345,6 @@ class _MyHomePageState extends State { } Future fetchGames() async { - bbots = false; - try { final response = await dio.get( "$BACKEND_URL/games", @@ -324,6 +380,7 @@ class _MyHomePageState extends State { }); _controller = TextEditingController(); _playerNameController = TextEditingController(); + _replayController = TextEditingController(); rerenderLogin(); } @@ -332,6 +389,7 @@ class _MyHomePageState extends State { super.dispose(); _controller.dispose(); _playerNameController.dispose(); + _replayController.dispose(); t.cancel(); } @@ -693,6 +751,59 @@ class _MyHomePageState extends State { child: const Text("Administratorska plošča"), ), ), + const SizedBox( + height: 10, + ), + Center( + child: ElevatedButton( + onPressed: () => showDialog( + context: context, + builder: (context) { + return StatefulBuilder(builder: (context, setState) { + return AlertDialog( + title: const Text('Posnetek igre'), + content: SingleChildScrollView( + child: Column(children: [ + const Text( + 'Tukaj lahko vpišete URL do posnetka igre', + ), + const SizedBox( + height: 10, + ), + Row(children: [ + Expanded( + child: TextField( + controller: _replayController, + decoration: const InputDecoration( + border: UnderlineInputBorder(), + labelText: 'Povezava do posnetka igre', + ), + ), + ), + ]), + ]), + ), + actions: [ + TextButton( + onPressed: () { + Navigator.pop(context); + }, + child: const Text('Prekliči'), + ), + TextButton( + onPressed: () { + joinReplay(_replayController.text); + }, + child: const Text('OK'), + ), + ], + ); + }); + }), + child: const Text("Posnetek igre"), + ), + ), + const SizedBox( height: 10, ), @@ -860,10 +971,11 @@ class _MyHomePageState extends State { ...priorityQueue.map( (e) => GestureDetector( onTap: () { - gameId = e["ID"]; - bbots = false; - playing = e["RequiredPlayers"]; - Get.toNamed("/game"); + Get.toNamed("/game", parameters: { + "playing": e["RequiredPlayers"], + "gameId": e["ID"], + "bots": "false", + }); }, child: Card( child: Column( @@ -928,10 +1040,11 @@ class _MyHomePageState extends State { ...queue.map( (e) => GestureDetector( onTap: () { - gameId = e["ID"]; - bbots = false; - playing = e["RequiredPlayers"]; - Get.toNamed("/game"); + Get.toNamed("/game", parameters: { + "playing": e["RequiredPlayers"], + "gameId": e["ID"], + "bots": "false", + }); }, child: Card( child: Column( diff --git a/tarok/lib/messages.pb.dart b/tarok/lib/messages.pb.dart index bf953b5..0890a9d 100644 --- a/tarok/lib/messages.pb.dart +++ b/tarok/lib/messages.pb.dart @@ -2946,73 +2946,74 @@ enum Message_Data { class Message extends $pb.GeneratedMessage { static const $core.Map<$core.int, Message_Data> _Message_DataByTag = { - 4 : Message_Data.connection, - 5 : Message_Data.licitiranje, - 6 : Message_Data.card, - 7 : Message_Data.licitiranjeStart, - 8 : Message_Data.gameStart, - 9 : Message_Data.loginRequest, - 10 : Message_Data.loginInfo, - 11 : Message_Data.loginResponse, - 12 : Message_Data.clearDesk, - 13 : Message_Data.results, - 14 : Message_Data.userList, - 15 : Message_Data.kingSelection, - 16 : Message_Data.startPredictions, - 17 : Message_Data.predictions, - 18 : Message_Data.talonReveal, - 19 : Message_Data.playingReveal, - 20 : Message_Data.talonSelection, - 21 : Message_Data.stash, - 22 : Message_Data.gameEnd, - 23 : Message_Data.gameStartCountdown, - 24 : Message_Data.predictionsResend, - 25 : Message_Data.radelci, - 26 : Message_Data.time, - 27 : Message_Data.chatMessage, - 28 : Message_Data.invitePlayer, - 29 : Message_Data.stashedTarock, - 30 : Message_Data.clearHand, - 31 : Message_Data.replayLink, - 32 : Message_Data.replayMove, - 33 : Message_Data.replaySelectGame, + 10 : Message_Data.connection, + 11 : Message_Data.licitiranje, + 12 : Message_Data.card, + 13 : Message_Data.licitiranjeStart, + 14 : Message_Data.gameStart, + 15 : Message_Data.loginRequest, + 16 : Message_Data.loginInfo, + 17 : Message_Data.loginResponse, + 18 : Message_Data.clearDesk, + 19 : Message_Data.results, + 20 : Message_Data.userList, + 21 : Message_Data.kingSelection, + 22 : Message_Data.startPredictions, + 23 : Message_Data.predictions, + 24 : Message_Data.talonReveal, + 25 : Message_Data.playingReveal, + 26 : Message_Data.talonSelection, + 27 : Message_Data.stash, + 28 : Message_Data.gameEnd, + 29 : Message_Data.gameStartCountdown, + 30 : Message_Data.predictionsResend, + 31 : Message_Data.radelci, + 32 : Message_Data.time, + 33 : Message_Data.chatMessage, + 34 : Message_Data.invitePlayer, + 35 : Message_Data.stashedTarock, + 36 : Message_Data.clearHand, + 37 : Message_Data.replayLink, + 38 : Message_Data.replayMove, + 39 : Message_Data.replaySelectGame, 0 : Message_Data.notSet }; static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'Message', createEmptyInstance: create) - ..oo(0, [4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33]) + ..oo(0, [10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39]) ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'username') ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'playerId') ..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'gameId') - ..aOM(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'connection', subBuilder: Connection.create) - ..aOM(5, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'licitiranje', subBuilder: Licitiranje.create) - ..aOM(6, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'card', subBuilder: Card.create) - ..aOM(7, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'licitiranjeStart', subBuilder: LicitiranjeStart.create) - ..aOM(8, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'gameStart', subBuilder: GameStart.create) - ..aOM(9, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'loginRequest', subBuilder: LoginRequest.create) - ..aOM(10, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'loginInfo', subBuilder: LoginInfo.create) - ..aOM(11, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'loginResponse', subBuilder: LoginResponse.create) - ..aOM(12, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'clearDesk', subBuilder: ClearDesk.create) - ..aOM(13, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'results', subBuilder: Results.create) - ..aOM(14, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'userList', subBuilder: UserList.create) - ..aOM(15, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'kingSelection', subBuilder: KingSelection.create) - ..aOM(16, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'startPredictions', subBuilder: StartPredictions.create) - ..aOM(17, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'predictions', subBuilder: Predictions.create) - ..aOM(18, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'talonReveal', subBuilder: TalonReveal.create) - ..aOM(19, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'playingReveal', subBuilder: PlayingReveal.create) - ..aOM(20, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'talonSelection', subBuilder: TalonSelection.create) - ..aOM(21, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'stash', subBuilder: Stash.create) - ..aOM(22, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'gameEnd', subBuilder: GameEnd.create) - ..aOM(23, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'gameStartCountdown', subBuilder: GameStartCountdown.create) - ..aOM(24, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'predictionsResend', subBuilder: Predictions.create) - ..aOM(25, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'radelci', subBuilder: Radelci.create) - ..aOM