Auto-sync: 20260324_150000
This commit is contained in:
parent
027c41a75c
commit
88f1520b5b
4 changed files with 64 additions and 21 deletions
BIN
.DS_Store
vendored
BIN
.DS_Store
vendored
Binary file not shown.
BIN
ios/.DS_Store
vendored
BIN
ios/.DS_Store
vendored
Binary file not shown.
|
|
@ -57,8 +57,11 @@ class _GameScreenState extends State<GameScreen> with TickerProviderStateMixin {
|
||||||
@override
|
@override
|
||||||
void dispose() { _blinkController.dispose(); super.dispose(); }
|
void dispose() { _blinkController.dispose(); super.dispose(); }
|
||||||
|
|
||||||
// --- NUOVO DIALOG: CONFERMA USCITA (ANTI-FUGA) ---
|
// --- DIALOG: CONFERMA USCITA (ANTI-FUGA) AGGIORNATO ---
|
||||||
void _showExitConfirmationDialog(BuildContext context, GameController gameController, ThemeColors theme, AppThemeType themeType) {
|
void _showExitConfirmationDialog(BuildContext context, GameController gameController, ThemeColors theme, AppThemeType themeType) {
|
||||||
|
// Determiniamo se è una partita locale (no CPU e no Online)
|
||||||
|
bool isLocalMatch = !gameController.isOnline && !gameController.isVsCPU;
|
||||||
|
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) => AlertDialog(
|
builder: (ctx) => AlertDialog(
|
||||||
|
|
@ -80,7 +83,9 @@ class _GameScreenState extends State<GameScreen> with TickerProviderStateMixin {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
content: Text(
|
content: Text(
|
||||||
"Se esci ora, la partita verrà registrata automaticamente come una SCONFITTA.\n\nSei sicuro di voler fuggire?",
|
isLocalMatch
|
||||||
|
? "Sei sicuro di voler interrompere la partita locale in corso?" // Testo per partite in locale
|
||||||
|
: "Se esci ora, la partita verrà registrata automaticamente come una SCONFITTA.\n\nSei sicuro di voler fuggire?", // Testo minaccioso per partite classificate
|
||||||
style: _getTextStyle(themeType, TextStyle(color: theme.text, fontSize: 15, height: 1.4)),
|
style: _getTextStyle(themeType, TextStyle(color: theme.text, fontSize: 15, height: 1.4)),
|
||||||
),
|
),
|
||||||
actions: [
|
actions: [
|
||||||
|
|
@ -95,8 +100,10 @@ class _GameScreenState extends State<GameScreen> with TickerProviderStateMixin {
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
|
||||||
),
|
),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
// 1. Assegna la sconfitta!
|
// 1. Assegna la sconfitta SOLO se non è una partita in locale!
|
||||||
StorageService.instance.addLoss();
|
if (!isLocalMatch) {
|
||||||
|
StorageService.instance.addLoss();
|
||||||
|
}
|
||||||
// 2. Disconnette e pulisce
|
// 2. Disconnette e pulisce
|
||||||
gameController.disconnectOnlineGame();
|
gameController.disconnectOnlineGame();
|
||||||
// 3. Chiude il dialog
|
// 3. Chiude il dialog
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
|
|
||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
|
import 'dart:math'; // <--- AGGIUNTO PER IL RANDOM DELL'INVITO
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||||
|
|
@ -17,8 +18,9 @@ import '../../services/multiplayer_service.dart';
|
||||||
import '../../services/storage_service.dart';
|
import '../../services/storage_service.dart';
|
||||||
import '../game/game_screen.dart';
|
import '../game/game_screen.dart';
|
||||||
import '../../widgets/cyber_border.dart';
|
import '../../widgets/cyber_border.dart';
|
||||||
import '../../widgets/painters.dart'; // <--- ECCO L'IMPORT MANCANTE!
|
import '../../widgets/painters.dart';
|
||||||
import 'lobby_widgets.dart';
|
import 'lobby_widgets.dart';
|
||||||
|
import '../home/home_modals.dart'; // <--- AGGIUNTO PER IL DIALOG DELLE IMPOSTAZIONI
|
||||||
|
|
||||||
class LobbyScreen extends StatefulWidget {
|
class LobbyScreen extends StatefulWidget {
|
||||||
final String? initialRoomCode;
|
final String? initialRoomCode;
|
||||||
|
|
@ -104,27 +106,62 @@ class _LobbyScreenState extends State<LobbyScreen> with WidgetsBindingObserver {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _createRoomAndInvite(String targetUid, String targetName) async {
|
// --- NUOVA LOGICA: FLOW PER SFIDA DIRETTA (Allineato con HomeScreen) ---
|
||||||
if (_isLoading) return;
|
void _startDirectChallengeFlow(String targetUid, String targetName) {
|
||||||
|
HomeModals.showChallengeSetupDialog(
|
||||||
|
context,
|
||||||
|
targetName,
|
||||||
|
(int radius, ArenaShape shape, String timeMode) {
|
||||||
|
_executeSendChallenge(targetUid, targetName, radius, shape, timeMode);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _executeSendChallenge(String targetUid, String targetName, int radius, ArenaShape shape, String timeMode) async {
|
||||||
setState(() => _isLoading = true);
|
setState(() => _isLoading = true);
|
||||||
|
|
||||||
|
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
||||||
|
final rnd = Random();
|
||||||
|
String roomCode = String.fromCharCodes(Iterable.generate(5, (_) => chars.codeUnitAt(rnd.nextInt(chars.length))));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
String code = await _multiplayerService.createGameRoom(
|
int gameSeed = rnd.nextInt(9999999);
|
||||||
_selectedRadius, _playerName, _selectedShape.name, _timeModeSetting, isPublic: _isPublicRoom
|
|
||||||
);
|
|
||||||
|
|
||||||
await _multiplayerService.sendInvite(targetUid, code, _playerName);
|
await FirebaseFirestore.instance.collection('games').doc(roomCode).set({
|
||||||
|
'status': 'waiting',
|
||||||
|
'hostName': StorageService.instance.playerName,
|
||||||
|
'hostUid': FirebaseAuth.instance.currentUser?.uid,
|
||||||
|
'radius': radius,
|
||||||
|
'shape': shape.name,
|
||||||
|
'timeMode': timeMode,
|
||||||
|
'isPublic': false,
|
||||||
|
'createdAt': FieldValue.serverTimestamp(),
|
||||||
|
'players': [FirebaseAuth.instance.currentUser?.uid],
|
||||||
|
'turn': 0,
|
||||||
|
'moves': [],
|
||||||
|
'seed': gameSeed,
|
||||||
|
});
|
||||||
|
|
||||||
if (!mounted) return;
|
await FirebaseFirestore.instance.collection('invites').add({
|
||||||
setState(() { _myRoomCode = code; _isLoading = false; _roomStarted = false; });
|
'toUid': targetUid,
|
||||||
|
'fromName': StorageService.instance.playerName,
|
||||||
|
'roomCode': roomCode,
|
||||||
|
'timestamp': FieldValue.serverTimestamp(),
|
||||||
|
});
|
||||||
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text("Sfida inviata a $targetName!"), backgroundColor: Colors.green));
|
setState(() => _isLoading = false);
|
||||||
_showWaitingDialog(code);
|
|
||||||
|
|
||||||
|
if (mounted) {
|
||||||
|
_showWaitingDialog(roomCode); // Usiamo il dialog interno della lobby
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (mounted) { setState(() => _isLoading = false); _showError("Errore durante la creazione della partita."); }
|
setState(() => _isLoading = false);
|
||||||
|
if (mounted) {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text("Errore: $e", style: const TextStyle(color: Colors.white)), backgroundColor: Colors.red));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
Future<void> _joinRoomByCode(String code) async {
|
Future<void> _joinRoomByCode(String code) async {
|
||||||
if (_isLoading) return;
|
if (_isLoading) return;
|
||||||
|
|
@ -193,7 +230,6 @@ class _LobbyScreenState extends State<LobbyScreen> with WidgetsBindingObserver {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
: StreamBuilder<QuerySnapshot>(
|
: StreamBuilder<QuerySnapshot>(
|
||||||
// Interroghiamo Firebase solo per gli UID dei nostri preferiti (max 10 per limiti di Firestore)
|
|
||||||
stream: FirebaseFirestore.instance.collection('leaderboard')
|
stream: FirebaseFirestore.instance.collection('leaderboard')
|
||||||
.where(FieldPath.documentId, whereIn: favs.map((f) => f['uid']).take(10).toList())
|
.where(FieldPath.documentId, whereIn: favs.map((f) => f['uid']).take(10).toList())
|
||||||
.snapshots(),
|
.snapshots(),
|
||||||
|
|
@ -202,7 +238,6 @@ class _LobbyScreenState extends State<LobbyScreen> with WidgetsBindingObserver {
|
||||||
return Center(child: CircularProgressIndicator(color: theme.playerBlue));
|
return Center(child: CircularProgressIndicator(color: theme.playerBlue));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mappiamo i risultati di Firebase per un accesso rapido
|
|
||||||
Map<String, Map<String, dynamic>> liveData = {};
|
Map<String, Map<String, dynamic>> liveData = {};
|
||||||
for (var doc in snapshot.data!.docs) {
|
for (var doc in snapshot.data!.docs) {
|
||||||
liveData[doc.id] = doc.data() as Map<String, dynamic>;
|
liveData[doc.id] = doc.data() as Map<String, dynamic>;
|
||||||
|
|
@ -219,7 +254,6 @@ class _LobbyScreenState extends State<LobbyScreen> with WidgetsBindingObserver {
|
||||||
if (liveData.containsKey(uid) && liveData[uid]!['lastActive'] != null) {
|
if (liveData.containsKey(uid) && liveData[uid]!['lastActive'] != null) {
|
||||||
Timestamp lastActive = liveData[uid]!['lastActive'];
|
Timestamp lastActive = liveData[uid]!['lastActive'];
|
||||||
int diffInSeconds = DateTime.now().difference(lastActive.toDate()).inSeconds;
|
int diffInSeconds = DateTime.now().difference(lastActive.toDate()).inSeconds;
|
||||||
// Se ha fatto un'azione negli ultimi 3 minuti, lo consideriamo online
|
|
||||||
if (diffInSeconds.abs() < 180) isOnline = true;
|
if (diffInSeconds.abs() < 180) isOnline = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -245,7 +279,8 @@ class _LobbyScreenState extends State<LobbyScreen> with WidgetsBindingObserver {
|
||||||
),
|
),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.pop(ctx);
|
Navigator.pop(ctx);
|
||||||
_createRoomAndInvite(uid, name);
|
// --- ORA USA IL FLUSSO SICURO CON LE IMPOSTAZIONI ---
|
||||||
|
_startDirectChallengeFlow(uid, name);
|
||||||
},
|
},
|
||||||
child: Text("SFIDA", style: getLobbyTextStyle(themeType, const TextStyle(color: Colors.white, fontWeight: FontWeight.bold))),
|
child: Text("SFIDA", style: getLobbyTextStyle(themeType, const TextStyle(color: Colors.white, fontWeight: FontWeight.bold))),
|
||||||
)
|
)
|
||||||
|
|
@ -273,6 +308,7 @@ class _LobbyScreenState extends State<LobbyScreen> with WidgetsBindingObserver {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _showWaitingDialog(String code) {
|
void _showWaitingDialog(String code) {
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
|
|
@ -804,4 +840,4 @@ class FullScreenGridPainter extends CustomPainter {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
|
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue