From c3390609c3cac87bf8fe7b049892b06352b1c097 Mon Sep 17 00:00:00 2001 From: Paolo Date: Mon, 23 Mar 2026 00:00:04 +0100 Subject: [PATCH] Auto-sync: 20260323_000000 --- lib/logic/game_controller.dart | 55 ++++++++++++++++++---------------- 1 file changed, 30 insertions(+), 25 deletions(-) diff --git a/lib/logic/game_controller.dart b/lib/logic/game_controller.dart index a671845..8b7ae3b 100644 --- a/lib/logic/game_controller.dart +++ b/lib/logic/game_controller.dart @@ -157,23 +157,19 @@ class GameController extends ChangeNotifier { // --- LOGICA TIMER --- if (this.isVsCPU) { - // La CPU usa sempre la sua formula basata sul Livello Profilo int pLevel = StorageService.instance.playerLevel; int calculatedTime = 15 - ((pLevel - 1) * 12 / 14).round(); maxTime = calculatedTime.clamp(3, 15); } else { - // Multiplayer e Locale if (timeModeSetting == 'dynamic') { - // Parte da 10s e toglie 2s per ogni rivincita (Minimo 2s) maxTime = max(2, 10 - (consecutiveRematches * 2)); } else if (timeModeSetting == 'relax') { - maxTime = 0; // Il timer non scatterà + maxTime = 0; } else { - maxTime = 10; // Fisso 10s + maxTime = 10; } } timeLeft = maxTime; - // ------------------- int finalRadius = radius; ArenaShape finalShape = shape; @@ -407,10 +403,12 @@ class GameController extends ChangeNotifier { onlineHostName = data['hostName'] ?? "ROSSO"; onlineGuestName = (data['guestName'] != null && data['guestName'] != '') ? data['guestName'] : "BLU"; + // 1. GESTIONE ABBANDONO if (data['status'] == 'abandoned' && !board.isGameOver && !opponentLeft) { opponentLeft = true; notifyListeners(); return; } + // 2. GESTIONE REAZIONI String? p1React = data['p1_reaction']; Timestamp? p1Time = data['p1_reaction_time'] as Timestamp?; String? p2React = data['p2_reaction']; @@ -424,30 +422,39 @@ class GameController extends ChangeNotifier { _showReaction(false, p1React); } + // 3. LOGICA RIVINCITA MIGLIORATA bool p1Rematch = data['p1_rematch'] ?? false; bool p2Rematch = data['p2_rematch'] ?? false; opponentWantsRematch = isHost ? p2Rematch : p1Rematch; - // === LA RIVINCITA INCREMENTA IL CONTATORE DELLA MODALITA' DINAMICA === - if (data['status'] == 'playing' && (data['moves'] as List).isEmpty && rematchRequested) { - currentSeed = data['seed']; - consecutiveRematches++; - - String tMode = data['timeMode'] is String ? data['timeMode'] : (data['timeMode'] == true ? 'fixed' : 'relax'); - - startNewGame(data['radius'], isOnline: true, roomCode: roomCode, isHost: isHost, shape: ArenaShape.values.firstWhere((e) => e.name == data['shape']), timeMode: tMode, isRematch: true); - return; - } - - if (p1Rematch && p2Rematch && isHost && data['status'] != 'playing') { + // SOLO L'HOST si occupa di chiamare resetMatch sul server + if (isHost && p1Rematch && p2Rematch && data['status'] != 'playing') { currentMatchLevel++; int newSeed = DateTime.now().millisecondsSinceEpoch % 1000000; final rand = Random(); int newRadius = rand.nextInt(4) + 3; ArenaShape newShape = ArenaShape.values[rand.nextInt(ArenaShape.values.length)]; + + // Questo cambierà lo status in 'playing' e svuoterà l'array moves. MultiplayerService().resetMatch(roomCode!, newRadius, newShape.name, newSeed); + return; // L'host aspetterà il prossimo trigger dal server con il nuovo seed. } + int? hostSeed = data['seed']; + int hostRadius = data['radius'] ?? board.radius; + String shapeStr = data['shape'] ?? 'classic'; + ArenaShape hostShape = ArenaShape.values.firstWhere((e) => e.name == shapeStr, orElse: () => ArenaShape.classic); + String hostTimeMode = data['timeMode'] is String ? data['timeMode'] : (data['timeMode'] == true ? 'fixed' : 'relax'); + + // TUTTI (Host e Guest) ripartono SOLO quando vedono il reset effettivo (nuovo seed e status 'playing') + if (rematchRequested && data['status'] == 'playing' && hostSeed != null && hostSeed != currentSeed) { + currentSeed = hostSeed; + consecutiveRematches++; + startNewGame(hostRadius, isOnline: true, roomCode: roomCode, isHost: isHost, shape: hostShape, timeMode: hostTimeMode, isRematch: true); + return; + } + + // 4. GESTIONE FASE INIZIALE (JOLLY) if (isSetupPhase) { if (!isHost && data['p1_joker'] != null && !oppJokerPlaced) { int jx = data['p1_joker']['x']; int jy = data['p1_joker']['y']; @@ -461,15 +468,9 @@ class GameController extends ChangeNotifier { } } - List moves = data['moves'] ?? []; + // 5. AGGIORNAMENTO LIVELLO / SEED (se non in rivincita) int hostLevel = data['matchLevel'] ?? 1; - int? hostSeed = data['seed']; - int hostRadius = data['radius'] ?? board.radius; - String shapeStr = data['shape'] ?? 'classic'; - ArenaShape hostShape = ArenaShape.values.firstWhere((e) => e.name == shapeStr, orElse: () => ArenaShape.classic); onlineShape = hostShape; - - String hostTimeMode = data['timeMode'] is String ? data['timeMode'] : (data['timeMode'] == true ? 'fixed' : 'relax'); timeModeSetting = hostTimeMode; if (!rematchRequested && (hostLevel > currentMatchLevel || (isOnline && currentSeed == null && hostSeed != null) || (hostSeed != null && hostSeed != currentSeed))) { @@ -480,9 +481,12 @@ class GameController extends ChangeNotifier { isCPUThinking = false; notifyListeners(); return; } + // 6. GESTIONE MOSSE + List moves = data['moves'] ?? []; int firebaseMovesCount = moves.length; int localMovesCount = board.lines.where((l) => l.owner != Player.none).length; + // Resilienza: se il locale ha mosse e il server no (e non stiamo aspettando una rivincita), pulisci. if (firebaseMovesCount == 0 && localMovesCount > 0 && !rematchRequested) { int levelToUse = (currentMatchLevel == 1) ? 2 : currentMatchLevel; board = GameBoard(radius: hostRadius, level: levelToUse, seed: currentSeed, shape: onlineShape); @@ -490,6 +494,7 @@ class GameController extends ChangeNotifier { notifyListeners(); return; } + // Applica mosse remote if (firebaseMovesCount > localMovesCount) { bool newMovesApplied = false;