Auto-sync: 20260323_000000

This commit is contained in:
Paolo 2026-03-23 00:00:04 +01:00
parent 95a0fddaba
commit c3390609c3

View file

@ -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<dynamic> 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<dynamic> 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;