Auto-sync: 20260313_220000
This commit is contained in:
parent
da52ad9d80
commit
518cb22ec4
9 changed files with 206 additions and 186 deletions
BIN
ios/.DS_Store
vendored
BIN
ios/.DS_Store
vendored
Binary file not shown.
|
|
@ -5,7 +5,7 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
|
|
||||||
enum AppThemeType { minimal, doodle, cyberpunk, wood, arcade, grimorio }
|
enum AppThemeType { doodle, wood, cyberpunk, arcade, grimorio }
|
||||||
|
|
||||||
class ThemeColors {
|
class ThemeColors {
|
||||||
final Color background;
|
final Color background;
|
||||||
|
|
@ -24,11 +24,6 @@ class ThemeColors {
|
||||||
}
|
}
|
||||||
|
|
||||||
class AppColors {
|
class AppColors {
|
||||||
static const ThemeColors minimal = ThemeColors(
|
|
||||||
background: Color(0xFFF5F7FA), gridLine: Color(0xFFCFD8DC),
|
|
||||||
playerRed: Color(0xFFE53935), playerBlue: Color(0xFF1E88E5), text: Color(0xFF263238),
|
|
||||||
);
|
|
||||||
|
|
||||||
static const ThemeColors doodle = ThemeColors(
|
static const ThemeColors doodle = ThemeColors(
|
||||||
background: Color(0xFFFFF9E6), gridLine: Color(0xFFB0BEC5),
|
background: Color(0xFFFFF9E6), gridLine: Color(0xFFB0BEC5),
|
||||||
playerRed: Color(0xFFD32F2F), playerBlue: Color(0xFF1565C0), text: Color(0xFF37474F),
|
playerRed: Color(0xFFD32F2F), playerBlue: Color(0xFF1565C0), text: Color(0xFF37474F),
|
||||||
|
|
@ -56,10 +51,9 @@ class AppColors {
|
||||||
|
|
||||||
static ThemeColors getTheme(AppThemeType type) {
|
static ThemeColors getTheme(AppThemeType type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case AppThemeType.minimal: return minimal;
|
|
||||||
case AppThemeType.doodle: return doodle;
|
case AppThemeType.doodle: return doodle;
|
||||||
case AppThemeType.cyberpunk: return cyberpunk;
|
|
||||||
case AppThemeType.wood: return wood;
|
case AppThemeType.wood: return wood;
|
||||||
|
case AppThemeType.cyberpunk: return cyberpunk;
|
||||||
case AppThemeType.arcade: return arcade;
|
case AppThemeType.arcade: return arcade;
|
||||||
case AppThemeType.grimorio: return grimorio;
|
case AppThemeType.grimorio: return grimorio;
|
||||||
}
|
}
|
||||||
|
|
@ -69,7 +63,6 @@ class AppColors {
|
||||||
class ThemeIcons {
|
class ThemeIcons {
|
||||||
static IconData gold(AppThemeType type) {
|
static IconData gold(AppThemeType type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case AppThemeType.minimal: return Icons.star_rounded;
|
|
||||||
case AppThemeType.doodle: return FontAwesomeIcons.star;
|
case AppThemeType.doodle: return FontAwesomeIcons.star;
|
||||||
case AppThemeType.wood: return FontAwesomeIcons.gem;
|
case AppThemeType.wood: return FontAwesomeIcons.gem;
|
||||||
case AppThemeType.cyberpunk: return FontAwesomeIcons.microchip;
|
case AppThemeType.cyberpunk: return FontAwesomeIcons.microchip;
|
||||||
|
|
@ -80,7 +73,6 @@ class ThemeIcons {
|
||||||
|
|
||||||
static IconData bomb(AppThemeType type) {
|
static IconData bomb(AppThemeType type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case AppThemeType.minimal: return Icons.mood_bad_rounded;
|
|
||||||
case AppThemeType.doodle: return FontAwesomeIcons.virus;
|
case AppThemeType.doodle: return FontAwesomeIcons.virus;
|
||||||
case AppThemeType.wood: return FontAwesomeIcons.fire;
|
case AppThemeType.wood: return FontAwesomeIcons.fire;
|
||||||
case AppThemeType.cyberpunk: return FontAwesomeIcons.bug;
|
case AppThemeType.cyberpunk: return FontAwesomeIcons.bug;
|
||||||
|
|
@ -91,7 +83,6 @@ class ThemeIcons {
|
||||||
|
|
||||||
static IconData swap(AppThemeType type) {
|
static IconData swap(AppThemeType type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case AppThemeType.minimal: return Icons.sync_rounded;
|
|
||||||
case AppThemeType.doodle: return FontAwesomeIcons.arrowsRotate;
|
case AppThemeType.doodle: return FontAwesomeIcons.arrowsRotate;
|
||||||
case AppThemeType.wood: return FontAwesomeIcons.rightLeft;
|
case AppThemeType.wood: return FontAwesomeIcons.rightLeft;
|
||||||
case AppThemeType.cyberpunk: return FontAwesomeIcons.networkWired;
|
case AppThemeType.cyberpunk: return FontAwesomeIcons.networkWired;
|
||||||
|
|
@ -102,7 +93,6 @@ class ThemeIcons {
|
||||||
|
|
||||||
static IconData joker(AppThemeType type) {
|
static IconData joker(AppThemeType type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case AppThemeType.minimal: return Icons.sentiment_satisfied_alt;
|
|
||||||
case AppThemeType.doodle: return FontAwesomeIcons.faceSmileBeam;
|
case AppThemeType.doodle: return FontAwesomeIcons.faceSmileBeam;
|
||||||
case AppThemeType.wood: return FontAwesomeIcons.key;
|
case AppThemeType.wood: return FontAwesomeIcons.key;
|
||||||
case AppThemeType.cyberpunk: return FontAwesomeIcons.robot;
|
case AppThemeType.cyberpunk: return FontAwesomeIcons.robot;
|
||||||
|
|
@ -113,7 +103,6 @@ class ThemeIcons {
|
||||||
|
|
||||||
static IconData block(AppThemeType type) {
|
static IconData block(AppThemeType type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case AppThemeType.minimal: return Icons.block;
|
|
||||||
case AppThemeType.doodle: return FontAwesomeIcons.squareXmark;
|
case AppThemeType.doodle: return FontAwesomeIcons.squareXmark;
|
||||||
case AppThemeType.wood: return FontAwesomeIcons.ban;
|
case AppThemeType.wood: return FontAwesomeIcons.ban;
|
||||||
case AppThemeType.cyberpunk: return FontAwesomeIcons.shieldHalved;
|
case AppThemeType.cyberpunk: return FontAwesomeIcons.shieldHalved;
|
||||||
|
|
@ -122,7 +111,6 @@ class ThemeIcons {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- NUOVE ICONE ---
|
|
||||||
static IconData ice(AppThemeType type) {
|
static IconData ice(AppThemeType type) {
|
||||||
return FontAwesomeIcons.snowflake;
|
return FontAwesomeIcons.snowflake;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,12 @@ import '../services/storage_service.dart';
|
||||||
import '../services/multiplayer_service.dart';
|
import '../services/multiplayer_service.dart';
|
||||||
import '../core/app_colors.dart';
|
import '../core/app_colors.dart';
|
||||||
|
|
||||||
|
class CpuMatchSetup {
|
||||||
|
final int radius;
|
||||||
|
final ArenaShape shape;
|
||||||
|
CpuMatchSetup(this.radius, this.shape);
|
||||||
|
}
|
||||||
|
|
||||||
class GameController extends ChangeNotifier {
|
class GameController extends ChangeNotifier {
|
||||||
late GameBoard board;
|
late GameBoard board;
|
||||||
bool isVsCPU = false;
|
bool isVsCPU = false;
|
||||||
|
|
@ -50,11 +56,9 @@ class GameController extends ChangeNotifier {
|
||||||
bool opponentWantsRematch = false;
|
bool opponentWantsRematch = false;
|
||||||
int lastMatchXP = 0;
|
int lastMatchXP = 0;
|
||||||
|
|
||||||
// --- VARIABILI PER IL LEVEL UP ---
|
|
||||||
bool hasLeveledUp = false;
|
bool hasLeveledUp = false;
|
||||||
int newlyReachedLevel = 1;
|
int newlyReachedLevel = 1;
|
||||||
List<String> unlockedFeatures = [];
|
List<String> unlockedFeatures = [];
|
||||||
// ---------------------------------
|
|
||||||
|
|
||||||
bool isSetupPhase = true;
|
bool isSetupPhase = true;
|
||||||
bool myJokerPlaced = false;
|
bool myJokerPlaced = false;
|
||||||
|
|
@ -67,7 +71,7 @@ class GameController extends ChangeNotifier {
|
||||||
int cpuLevel = 1;
|
int cpuLevel = 1;
|
||||||
int currentMatchLevel = 1;
|
int currentMatchLevel = 1;
|
||||||
int? currentSeed;
|
int? currentSeed;
|
||||||
AppThemeType _activeTheme = AppThemeType.cyberpunk;
|
AppThemeType _activeTheme = AppThemeType.doodle;
|
||||||
|
|
||||||
String onlineHostName = "ROSSO";
|
String onlineHostName = "ROSSO";
|
||||||
String onlineGuestName = "BLU";
|
String onlineGuestName = "BLU";
|
||||||
|
|
@ -78,6 +82,25 @@ class GameController extends ChangeNotifier {
|
||||||
startNewGame(radius);
|
startNewGame(radius);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CpuMatchSetup _getSetupForCpuLevel(int level) {
|
||||||
|
final rand = Random();
|
||||||
|
if (level == 1) return CpuMatchSetup(3, ArenaShape.classic);
|
||||||
|
if (level == 2) return CpuMatchSetup(4, ArenaShape.classic);
|
||||||
|
if (level == 3) return CpuMatchSetup(4, ArenaShape.cross);
|
||||||
|
if (level == 4) return CpuMatchSetup(4, ArenaShape.donut);
|
||||||
|
if (level == 5) return CpuMatchSetup(5, ArenaShape.classic);
|
||||||
|
if (level == 6) return CpuMatchSetup(4, ArenaShape.hourglass);
|
||||||
|
if (level == 7) return CpuMatchSetup(5, ArenaShape.cross);
|
||||||
|
if (level == 8) return CpuMatchSetup(5, ArenaShape.donut);
|
||||||
|
if (level == 9) return CpuMatchSetup(5, ArenaShape.hourglass);
|
||||||
|
|
||||||
|
List<ArenaShape> hardShapes = [ArenaShape.classic, ArenaShape.cross, ArenaShape.donut, ArenaShape.hourglass, ArenaShape.chaos];
|
||||||
|
ArenaShape chosenShape = hardShapes[rand.nextInt(hardShapes.length)];
|
||||||
|
|
||||||
|
int chosenRadius = (chosenShape == ArenaShape.chaos) ? (rand.nextInt(2) + 4) : (rand.nextInt(2) + 5);
|
||||||
|
return CpuMatchSetup(chosenRadius, chosenShape);
|
||||||
|
}
|
||||||
|
|
||||||
void startNewGame(int radius, {bool vsCPU = false, bool isOnline = false, String? roomCode, bool isHost = false, ArenaShape shape = ArenaShape.classic, bool timeMode = true}) {
|
void startNewGame(int radius, {bool vsCPU = false, bool isOnline = false, String? roomCode, bool isHost = false, ArenaShape shape = ArenaShape.classic, bool timeMode = true}) {
|
||||||
_onlineSubscription?.cancel();
|
_onlineSubscription?.cancel();
|
||||||
_onlineSubscription = null;
|
_onlineSubscription = null;
|
||||||
|
|
@ -87,7 +110,6 @@ class GameController extends ChangeNotifier {
|
||||||
_hasSavedResult = false;
|
_hasSavedResult = false;
|
||||||
lastMatchXP = 0;
|
lastMatchXP = 0;
|
||||||
|
|
||||||
// Reset Level Up vars
|
|
||||||
hasLeveledUp = false;
|
hasLeveledUp = false;
|
||||||
unlockedFeatures.clear();
|
unlockedFeatures.clear();
|
||||||
|
|
||||||
|
|
@ -108,10 +130,19 @@ class GameController extends ChangeNotifier {
|
||||||
this.isHost = isHost;
|
this.isHost = isHost;
|
||||||
this.isTimeMode = timeMode;
|
this.isTimeMode = timeMode;
|
||||||
|
|
||||||
onlineShape = shape;
|
int finalRadius = radius;
|
||||||
|
ArenaShape finalShape = shape;
|
||||||
|
|
||||||
|
if (this.isVsCPU) {
|
||||||
|
CpuMatchSetup setup = _getSetupForCpuLevel(cpuLevel);
|
||||||
|
finalRadius = setup.radius;
|
||||||
|
finalShape = setup.shape;
|
||||||
|
}
|
||||||
|
|
||||||
|
onlineShape = finalShape;
|
||||||
int levelToUse = isOnline ? (currentMatchLevel == 1 ? 2 : currentMatchLevel) : cpuLevel;
|
int levelToUse = isOnline ? (currentMatchLevel == 1 ? 2 : currentMatchLevel) : cpuLevel;
|
||||||
|
|
||||||
board = GameBoard(radius: radius, level: levelToUse, seed: currentSeed, shape: onlineShape);
|
board = GameBoard(radius: finalRadius, level: levelToUse, seed: currentSeed, shape: finalShape);
|
||||||
board.currentPlayer = Player.red;
|
board.currentPlayer = Player.red;
|
||||||
|
|
||||||
isCPUThinking = false;
|
isCPUThinking = false;
|
||||||
|
|
@ -513,12 +544,10 @@ class GameController extends ChangeNotifier {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- LOGICA LEVEL UP E SBLOCCHI ---
|
|
||||||
List<String> _getUnlocks(int oldLevel, int newLevel) {
|
List<String> _getUnlocks(int oldLevel, int newLevel) {
|
||||||
List<String> unlocks = [];
|
List<String> unlocks = [];
|
||||||
for(int i = oldLevel + 1; i <= newLevel; i++) {
|
for(int i = oldLevel + 1; i <= newLevel; i++) {
|
||||||
if (i == 3) unlocks.add("Tema: Legno & Fiammiferi");
|
if (i == 3) unlocks.add("Tema: Legno & Fiammiferi");
|
||||||
if (i == 5) unlocks.add("Tema: Quaderno (Doodle)");
|
|
||||||
if (i == 7) unlocks.add("Tema: Cyberpunk");
|
if (i == 7) unlocks.add("Tema: Cyberpunk");
|
||||||
if (i == 10) {
|
if (i == 10) {
|
||||||
unlocks.add("Tema: 8-Bit Arcade");
|
unlocks.add("Tema: 8-Bit Arcade");
|
||||||
|
|
@ -538,7 +567,7 @@ class GameController extends ChangeNotifier {
|
||||||
String myRealName = StorageService.instance.playerName;
|
String myRealName = StorageService.instance.playerName;
|
||||||
if (myRealName.isEmpty) myRealName = "IO";
|
if (myRealName.isEmpty) myRealName = "IO";
|
||||||
|
|
||||||
int oldLevel = StorageService.instance.playerLevel; // Salviamo il vecchio livello
|
int oldLevel = StorageService.instance.playerLevel;
|
||||||
|
|
||||||
if (isOnline) {
|
if (isOnline) {
|
||||||
bool isWin = isHost ? board.scoreRed > board.scoreBlue : board.scoreBlue > board.scoreRed;
|
bool isWin = isHost ? board.scoreRed > board.scoreBlue : board.scoreBlue > board.scoreRed;
|
||||||
|
|
@ -574,7 +603,6 @@ class GameController extends ChangeNotifier {
|
||||||
lastMatchXP = calculatedXP;
|
lastMatchXP = calculatedXP;
|
||||||
StorageService.instance.addXP(calculatedXP);
|
StorageService.instance.addXP(calculatedXP);
|
||||||
|
|
||||||
// --- CONTROLLO LEVEL UP DOPO AVER DATO GLI XP ---
|
|
||||||
int newLevel = StorageService.instance.playerLevel;
|
int newLevel = StorageService.instance.playerLevel;
|
||||||
if (newLevel > oldLevel) {
|
if (newLevel > oldLevel) {
|
||||||
hasLeveledUp = true;
|
hasLeveledUp = true;
|
||||||
|
|
|
||||||
|
|
@ -15,46 +15,35 @@ class AudioService extends ChangeNotifier {
|
||||||
final AudioPlayer _sfxPlayer = AudioPlayer();
|
final AudioPlayer _sfxPlayer = AudioPlayer();
|
||||||
final AudioPlayer _bgmPlayer = AudioPlayer();
|
final AudioPlayer _bgmPlayer = AudioPlayer();
|
||||||
|
|
||||||
// Teniamo traccia del tema attuale per gestire bene quando togliamo il muto
|
AppThemeType _currentTheme = AppThemeType.doodle;
|
||||||
AppThemeType _currentTheme = AppThemeType.cyberpunk;
|
|
||||||
|
|
||||||
Future<void> init() async {
|
Future<void> init() async {
|
||||||
// 1. Carica la preferenza salvata
|
|
||||||
final prefs = await SharedPreferences.getInstance();
|
final prefs = await SharedPreferences.getInstance();
|
||||||
isMuted = prefs.getBool('isMuted') ?? false;
|
isMuted = prefs.getBool('isMuted') ?? false;
|
||||||
|
|
||||||
// 2. Imposta la musica in loop infinito
|
|
||||||
await _bgmPlayer.setReleaseMode(ReleaseMode.loop);
|
await _bgmPlayer.setReleaseMode(ReleaseMode.loop);
|
||||||
}
|
}
|
||||||
|
|
||||||
void toggleMute() async {
|
void toggleMute() async {
|
||||||
isMuted = !isMuted;
|
isMuted = !isMuted;
|
||||||
|
|
||||||
// Salva la scelta per il prossimo riavvio
|
|
||||||
final prefs = await SharedPreferences.getInstance();
|
final prefs = await SharedPreferences.getInstance();
|
||||||
await prefs.setBool('isMuted', isMuted);
|
await prefs.setBool('isMuted', isMuted);
|
||||||
|
|
||||||
if (isMuted) {
|
if (isMuted) {
|
||||||
await _bgmPlayer.pause();
|
await _bgmPlayer.pause();
|
||||||
} else {
|
} else {
|
||||||
// Se togliamo il muto, facciamo ripartire la canzone del tema attuale
|
|
||||||
playBgm(_currentTheme);
|
playBgm(_currentTheme);
|
||||||
}
|
}
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- BGM (Musica di sottofondo) ---
|
|
||||||
Future<void> playBgm(AppThemeType theme) async {
|
Future<void> playBgm(AppThemeType theme) async {
|
||||||
_currentTheme = theme; // Aggiorna sempre la memoria del tema
|
_currentTheme = theme;
|
||||||
|
|
||||||
// FERMA SEMPRE LA TRACCIA PRECEDENTE prima di far partire la nuova
|
|
||||||
await _bgmPlayer.stop();
|
await _bgmPlayer.stop();
|
||||||
|
|
||||||
if (isMuted) return;
|
if (isMuted) return;
|
||||||
|
|
||||||
String audioPath = '';
|
String audioPath = '';
|
||||||
|
|
||||||
// Assegna a ogni tema la sua colonna sonora
|
|
||||||
switch (theme) {
|
switch (theme) {
|
||||||
case AppThemeType.cyberpunk:
|
case AppThemeType.cyberpunk:
|
||||||
audioPath = 'audio/bgm/Cyber_Dystopia.mp3';
|
audioPath = 'audio/bgm/Cyber_Dystopia.mp3';
|
||||||
|
|
@ -71,15 +60,10 @@ class AudioService extends ChangeNotifier {
|
||||||
case AppThemeType.grimorio:
|
case AppThemeType.grimorio:
|
||||||
audioPath = 'audio/bgm/Grimorio_Astral.mp3';
|
audioPath = 'audio/bgm/Grimorio_Astral.mp3';
|
||||||
break;
|
break;
|
||||||
case AppThemeType.minimal:
|
|
||||||
// Il tema minimal non ha musica (silenzio/focus)
|
|
||||||
audioPath = '';
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (audioPath.isNotEmpty) {
|
if (audioPath.isNotEmpty) {
|
||||||
try {
|
try {
|
||||||
// IL VOLUME VA PASSATO QUI (0.15 = 15%), sennò viene ignorato!
|
|
||||||
await _bgmPlayer.play(AssetSource(audioPath), volume: 0.15);
|
await _bgmPlayer.play(AssetSource(audioPath), volume: 0.15);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
debugPrint("Errore riproduzione BGM: $e");
|
debugPrint("Errore riproduzione BGM: $e");
|
||||||
|
|
@ -91,12 +75,10 @@ class AudioService extends ChangeNotifier {
|
||||||
await _bgmPlayer.stop();
|
await _bgmPlayer.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- SFX (Effetti sonori) ---
|
|
||||||
void playLineSfx(AppThemeType theme) async {
|
void playLineSfx(AppThemeType theme) async {
|
||||||
if (isMuted) return;
|
if (isMuted) return;
|
||||||
String file = '';
|
String file = '';
|
||||||
switch (theme) {
|
switch (theme) {
|
||||||
case AppThemeType.minimal:
|
|
||||||
case AppThemeType.arcade:
|
case AppThemeType.arcade:
|
||||||
file = 'minimal_line.wav'; break;
|
file = 'minimal_line.wav'; break;
|
||||||
case AppThemeType.doodle:
|
case AppThemeType.doodle:
|
||||||
|
|
@ -109,10 +91,9 @@ class AudioService extends ChangeNotifier {
|
||||||
|
|
||||||
if (file.isNotEmpty) {
|
if (file.isNotEmpty) {
|
||||||
try {
|
try {
|
||||||
// Effetti sonori forzati al 100%
|
|
||||||
await _sfxPlayer.play(AssetSource('audio/sfx/$file'), volume: 1.0);
|
await _sfxPlayer.play(AssetSource('audio/sfx/$file'), volume: 1.0);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
debugPrint("Errore SFX Linea non trovato: $file");
|
debugPrint("Errore SFX Linea: $file");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -121,7 +102,6 @@ class AudioService extends ChangeNotifier {
|
||||||
if (isMuted) return;
|
if (isMuted) return;
|
||||||
String file = '';
|
String file = '';
|
||||||
switch (theme) {
|
switch (theme) {
|
||||||
case AppThemeType.minimal:
|
|
||||||
case AppThemeType.arcade:
|
case AppThemeType.arcade:
|
||||||
file = 'minimal_box.wav'; break;
|
file = 'minimal_box.wav'; break;
|
||||||
case AppThemeType.doodle:
|
case AppThemeType.doodle:
|
||||||
|
|
@ -136,7 +116,7 @@ class AudioService extends ChangeNotifier {
|
||||||
try {
|
try {
|
||||||
await _sfxPlayer.play(AssetSource('audio/sfx/$file'), volume: 1.0);
|
await _sfxPlayer.play(AssetSource('audio/sfx/$file'), volume: 1.0);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
debugPrint("Errore SFX Box non trovato: $file");
|
debugPrint("Errore SFX Box: $file");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,10 +17,11 @@ class StorageService {
|
||||||
|
|
||||||
Future<void> init() async {
|
Future<void> init() async {
|
||||||
_prefs = await SharedPreferences.getInstance();
|
_prefs = await SharedPreferences.getInstance();
|
||||||
_checkDailyQuests(); // All'avvio controlliamo se ci sono nuove sfide
|
_checkDailyQuests();
|
||||||
}
|
}
|
||||||
|
|
||||||
int get savedThemeIndex => _prefs.getInt('theme') ?? AppThemeType.minimal.index;
|
// Doodle è il nuovo tema di partenza (index 0)
|
||||||
|
int get savedThemeIndex => _prefs.getInt('theme') ?? AppThemeType.doodle.index;
|
||||||
Future<void> saveTheme(AppThemeType theme) async => await _prefs.setInt('theme', theme.index);
|
Future<void> saveTheme(AppThemeType theme) async => await _prefs.setInt('theme', theme.index);
|
||||||
|
|
||||||
int get savedRadius => _prefs.getInt('radius') ?? 2;
|
int get savedRadius => _prefs.getInt('radius') ?? 2;
|
||||||
|
|
@ -31,7 +32,6 @@ class StorageService {
|
||||||
|
|
||||||
int get totalXP => _prefs.getInt('totalXP') ?? 0;
|
int get totalXP => _prefs.getInt('totalXP') ?? 0;
|
||||||
|
|
||||||
// Modificato per sincronizzare automaticamente la classifica su Firebase
|
|
||||||
Future<void> addXP(int xp) async {
|
Future<void> addXP(int xp) async {
|
||||||
await _prefs.setInt('totalXP', totalXP + xp);
|
await _prefs.setInt('totalXP', totalXP + xp);
|
||||||
syncLeaderboard();
|
syncLeaderboard();
|
||||||
|
|
@ -54,20 +54,17 @@ class StorageService {
|
||||||
String get playerName => _prefs.getString('playerName') ?? '';
|
String get playerName => _prefs.getString('playerName') ?? '';
|
||||||
Future<void> savePlayerName(String name) async {
|
Future<void> savePlayerName(String name) async {
|
||||||
await _prefs.setString('playerName', name);
|
await _prefs.setString('playerName', name);
|
||||||
syncLeaderboard(); // Aggiorna il nome in classifica
|
syncLeaderboard();
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- NUOVO: SINCRONIZZAZIONE CLASSIFICA ONLINE ---
|
|
||||||
Future<void> syncLeaderboard() async {
|
Future<void> syncLeaderboard() async {
|
||||||
if (playerName.isNotEmpty) {
|
if (playerName.isNotEmpty) {
|
||||||
try {
|
try {
|
||||||
// Recuperiamo il nostro ID segreto e univoco appena creato
|
|
||||||
final user = FirebaseAuth.instance.currentUser;
|
final user = FirebaseAuth.instance.currentUser;
|
||||||
|
|
||||||
if (user != null) {
|
if (user != null) {
|
||||||
// Usiamo user.uid come nome del documento, NON più il playerName!
|
|
||||||
await FirebaseFirestore.instance.collection('leaderboard').doc(user.uid).set({
|
await FirebaseFirestore.instance.collection('leaderboard').doc(user.uid).set({
|
||||||
'name': playerName, // Il nome rimane dentro per mostrarlo nella lista
|
'name': playerName,
|
||||||
'xp': totalXP,
|
'xp': totalXP,
|
||||||
'level': playerLevel,
|
'level': playerLevel,
|
||||||
'wins': wins,
|
'wins': wins,
|
||||||
|
|
@ -75,32 +72,26 @@ class StorageService {
|
||||||
}, SetOptions(merge: true));
|
}, SetOptions(merge: true));
|
||||||
}
|
}
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
// Ignoriamo gli errori se manca la rete, si sincronizzerà dopo
|
|
||||||
debugPrint("Errore sinc. classifica: $e");
|
debugPrint("Errore sinc. classifica: $e");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- NUOVO: GESTIONE SFIDE GIORNALIERE ---
|
|
||||||
void _checkDailyQuests() {
|
void _checkDailyQuests() {
|
||||||
String today = DateTime.now().toIso8601String().substring(0, 10);
|
String today = DateTime.now().toIso8601String().substring(0, 10);
|
||||||
String lastDate = _prefs.getString('quest_date') ?? '';
|
String lastDate = _prefs.getString('quest_date') ?? '';
|
||||||
|
|
||||||
if (today != lastDate) {
|
if (today != lastDate) {
|
||||||
// Nuovo giorno, nuove sfide!
|
|
||||||
_prefs.setString('quest_date', today);
|
_prefs.setString('quest_date', today);
|
||||||
|
|
||||||
// Sfida 1: Gioca partite online
|
|
||||||
_prefs.setInt('q1_type', 0);
|
_prefs.setInt('q1_type', 0);
|
||||||
_prefs.setInt('q1_prog', 0);
|
_prefs.setInt('q1_prog', 0);
|
||||||
_prefs.setInt('q1_target', 3);
|
_prefs.setInt('q1_target', 3);
|
||||||
|
|
||||||
// Sfida 2: Vinci contro la CPU
|
|
||||||
_prefs.setInt('q2_type', 1);
|
_prefs.setInt('q2_type', 1);
|
||||||
_prefs.setInt('q2_prog', 0);
|
_prefs.setInt('q2_prog', 0);
|
||||||
_prefs.setInt('q2_target', 2);
|
_prefs.setInt('q2_target', 2);
|
||||||
|
|
||||||
// Sfida 3: Partite con forme speciali (Croce, Caos, ecc)
|
|
||||||
_prefs.setInt('q3_type', 2);
|
_prefs.setInt('q3_type', 2);
|
||||||
_prefs.setInt('q3_prog', 0);
|
_prefs.setInt('q3_prog', 0);
|
||||||
_prefs.setInt('q3_target', 2);
|
_prefs.setInt('q3_target', 2);
|
||||||
|
|
@ -119,7 +110,6 @@ class StorageService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- STORICO PARTITE ---
|
|
||||||
List<Map<String, dynamic>> get matchHistory {
|
List<Map<String, dynamic>> get matchHistory {
|
||||||
List<String> history = _prefs.getStringList('matchHistory') ?? [];
|
List<String> history = _prefs.getStringList('matchHistory') ?? [];
|
||||||
return history.map((e) => jsonDecode(e) as Map<String, dynamic>).toList();
|
return history.map((e) => jsonDecode(e) as Map<String, dynamic>).toList();
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,6 @@ class BoardPainter extends CustomPainter {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sfondo azzurrino se è di ghiaccio (anche prima di chiuderla)
|
|
||||||
if (box.type == BoxType.ice && box.owner == Player.none) {
|
if (box.type == BoxType.ice && box.owner == Player.none) {
|
||||||
canvas.drawRect(rect.deflate(2.0), Paint()..color = Colors.cyanAccent.withOpacity(0.05)..style=PaintingStyle.fill);
|
canvas.drawRect(rect.deflate(2.0), Paint()..color = Colors.cyanAccent.withOpacity(0.05)..style=PaintingStyle.fill);
|
||||||
}
|
}
|
||||||
|
|
@ -127,10 +126,9 @@ class BoardPainter extends CustomPainter {
|
||||||
Offset p1 = getScreenPos(line.p1.x, line.p1.y);
|
Offset p1 = getScreenPos(line.p1.x, line.p1.y);
|
||||||
Offset p2 = getScreenPos(line.p2.x, line.p2.y);
|
Offset p2 = getScreenPos(line.p2.x, line.p2.y);
|
||||||
|
|
||||||
// --- DISEGNO DELLA LINEA "INCRINATA" DAL GHIACCIO ---
|
|
||||||
if (line.isIceCracked) {
|
if (line.isIceCracked) {
|
||||||
_drawCrackedIceLine(canvas, p1, p2, blinkValue);
|
_drawCrackedIceLine(canvas, p1, p2, blinkValue);
|
||||||
continue; // Non ha ancora un proprietario, passiamo alla prossima!
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isLastMove = (line == board.lastMove);
|
bool isLastMove = (line == board.lastMove);
|
||||||
|
|
@ -220,7 +218,6 @@ class BoardPainter extends CustomPainter {
|
||||||
..strokeCap = StrokeCap.round
|
..strokeCap = StrokeCap.round
|
||||||
..maskFilter = const MaskFilter.blur(BlurStyle.solid, 2.0);
|
..maskFilter = const MaskFilter.blur(BlurStyle.solid, 2.0);
|
||||||
|
|
||||||
// Effetto linea frammentata
|
|
||||||
canvas.drawLine(p1, p2, Paint()..color = Colors.cyan.withOpacity(0.2)..strokeWidth=6.0);
|
canvas.drawLine(p1, p2, Paint()..color = Colors.cyan.withOpacity(0.2)..strokeWidth=6.0);
|
||||||
|
|
||||||
Vector2 dir = Vector2(p2.dx - p1.dx, p2.dy - p1.dy);
|
Vector2 dir = Vector2(p2.dx - p1.dx, p2.dy - p1.dy);
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,6 @@ class _GameScreenState extends State<GameScreen> with TickerProviderStateMixin {
|
||||||
bool _gameOverDialogShown = false;
|
bool _gameOverDialogShown = false;
|
||||||
bool _opponentLeftDialogShown = false;
|
bool _opponentLeftDialogShown = false;
|
||||||
|
|
||||||
// Variabili per coprire il posizionamento del Jolly in Locale
|
|
||||||
bool _hideJokerMessage = false;
|
bool _hideJokerMessage = false;
|
||||||
bool _wasSetupPhase = false;
|
bool _wasSetupPhase = false;
|
||||||
Player _lastJokerTurn = Player.red;
|
Player _lastJokerTurn = Player.red;
|
||||||
|
|
@ -100,7 +99,6 @@ class _GameScreenState extends State<GameScreen> with TickerProviderStateMixin {
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
|
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
|
||||||
decoration: BoxDecoration(color: theme.text.withOpacity(0.05), borderRadius: BorderRadius.circular(15)),
|
decoration: BoxDecoration(color: theme.text.withOpacity(0.05), borderRadius: BorderRadius.circular(15)),
|
||||||
// AGGIUNTO FITTEDBOX QUI
|
|
||||||
child: FittedBox(
|
child: FittedBox(
|
||||||
fit: BoxFit.scaleDown,
|
fit: BoxFit.scaleDown,
|
||||||
child: Row(
|
child: Row(
|
||||||
|
|
@ -196,8 +194,7 @@ class _GameScreenState extends State<GameScreen> with TickerProviderStateMixin {
|
||||||
titleText = "Nascondi il tuo Jolly!";
|
titleText = "Nascondi il tuo Jolly!";
|
||||||
subtitleText = "(Tocca qui per nascondere)";
|
subtitleText = "(Tocca qui per nascondere)";
|
||||||
} else {
|
} else {
|
||||||
// --- TESTI MODALITÀ LOCALE ---
|
String pName = gameController.jokerTurn == Player.red ? "ROSSO" : (themeType == AppThemeType.cyberpunk || themeType == AppThemeType.arcade || themeType == AppThemeType.aurora ? "VERDE" : "BLU");
|
||||||
String pName = gameController.jokerTurn == Player.red ? "ROSSO" : (themeType == AppThemeType.cyberpunk || themeType == AppThemeType.arcade ? "VERDE" : "BLU");
|
|
||||||
titleText = "TURNO GIOCATORE $pName";
|
titleText = "TURNO GIOCATORE $pName";
|
||||||
subtitleText = "Passa il dispositivo.\nL'avversario NON deve guardare!\n\n(Tocca qui quando sei pronto)";
|
subtitleText = "Passa il dispositivo.\nL'avversario NON deve guardare!\n\n(Tocca qui quando sei pronto)";
|
||||||
}
|
}
|
||||||
|
|
@ -207,7 +204,7 @@ class _GameScreenState extends State<GameScreen> with TickerProviderStateMixin {
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Icon(ThemeIcons.joker(themeType), color: themeType == AppThemeType.cyberpunk || themeType == AppThemeType.arcade ? Colors.yellowAccent : theme.playerBlue, size: 50),
|
Icon(ThemeIcons.joker(themeType), color: themeType == AppThemeType.cyberpunk || themeType == AppThemeType.arcade || themeType == AppThemeType.aurora ? Colors.yellowAccent : theme.playerBlue, size: 50),
|
||||||
const SizedBox(height: 15),
|
const SizedBox(height: 15),
|
||||||
Text(
|
Text(
|
||||||
titleText,
|
titleText,
|
||||||
|
|
@ -243,7 +240,7 @@ class _GameScreenState extends State<GameScreen> with TickerProviderStateMixin {
|
||||||
} else if (themeType == AppThemeType.grimorio) {
|
} else if (themeType == AppThemeType.grimorio) {
|
||||||
return Container(decoration: BoxDecoration(color: const Color(0xFF2C1E3D), borderRadius: BorderRadius.circular(30), border: Border.all(color: const Color(0xFFBCAAA4), width: 3), boxShadow: [BoxShadow(color: Colors.deepPurpleAccent.withOpacity(0.5), blurRadius: 20, spreadRadius: 5)]), child: content);
|
return Container(decoration: BoxDecoration(color: const Color(0xFF2C1E3D), borderRadius: BorderRadius.circular(30), border: Border.all(color: const Color(0xFFBCAAA4), width: 3), boxShadow: [BoxShadow(color: Colors.deepPurpleAccent.withOpacity(0.5), blurRadius: 20, spreadRadius: 5)]), child: content);
|
||||||
} else {
|
} else {
|
||||||
return Container(decoration: BoxDecoration(color: theme.background, borderRadius: BorderRadius.circular(20), border: Border.all(color: theme.gridLine, width: 2), boxShadow: [BoxShadow(color: Colors.black.withOpacity(0.15), blurRadius: 20, offset: const Offset(0, 10))]), child: content);
|
return Container(decoration: BoxDecoration(color: theme.background.withOpacity(0.95), borderRadius: BorderRadius.circular(20), border: Border.all(color: theme.gridLine.withOpacity(0.5), width: 2), boxShadow: [BoxShadow(color: Colors.black.withOpacity(0.3), blurRadius: 20, offset: const Offset(0, 10))]), child: content);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -254,13 +251,10 @@ class _GameScreenState extends State<GameScreen> with TickerProviderStateMixin {
|
||||||
final theme = themeManager.currentColors;
|
final theme = themeManager.currentColors;
|
||||||
final gameController = context.watch<GameController>();
|
final gameController = context.watch<GameController>();
|
||||||
|
|
||||||
// --- LOGICA CAMBIO TURNO E SCHERMATA JOLLY ---
|
|
||||||
if (gameController.isSetupPhase && !_wasSetupPhase) {
|
if (gameController.isSetupPhase && !_wasSetupPhase) {
|
||||||
// È appena iniziata una nuova partita
|
|
||||||
_hideJokerMessage = false;
|
_hideJokerMessage = false;
|
||||||
_lastJokerTurn = Player.red;
|
_lastJokerTurn = Player.red;
|
||||||
} else if (gameController.isSetupPhase && gameController.jokerTurn != _lastJokerTurn) {
|
} else if (gameController.isSetupPhase && gameController.jokerTurn != _lastJokerTurn) {
|
||||||
// È cambiato il turno durante il setup (in modalità locale), rifacciamo apparire la copertura
|
|
||||||
_hideJokerMessage = false;
|
_hideJokerMessage = false;
|
||||||
_lastJokerTurn = gameController.jokerTurn;
|
_lastJokerTurn = gameController.jokerTurn;
|
||||||
}
|
}
|
||||||
|
|
@ -296,7 +290,7 @@ class _GameScreenState extends State<GameScreen> with TickerProviderStateMixin {
|
||||||
if (themeType == AppThemeType.wood) bgImage = 'assets/images/wood_bg.jpg';
|
if (themeType == AppThemeType.wood) bgImage = 'assets/images/wood_bg.jpg';
|
||||||
if (themeType == AppThemeType.doodle) bgImage = 'assets/images/doodle_bg.jpg';
|
if (themeType == AppThemeType.doodle) bgImage = 'assets/images/doodle_bg.jpg';
|
||||||
|
|
||||||
Color indicatorColor = themeType == AppThemeType.cyberpunk || themeType == AppThemeType.arcade ? Colors.white : Colors.black;
|
Color indicatorColor = themeType == AppThemeType.cyberpunk || themeType == AppThemeType.arcade || themeType == AppThemeType.aurora ? Colors.white : Colors.black;
|
||||||
|
|
||||||
Widget emojiBar = const SizedBox();
|
Widget emojiBar = const SizedBox();
|
||||||
if (gameController.isOnline && !gameController.isGameOver) {
|
if (gameController.isOnline && !gameController.isGameOver) {
|
||||||
|
|
@ -304,9 +298,9 @@ class _GameScreenState extends State<GameScreen> with TickerProviderStateMixin {
|
||||||
emojiBar = Container(
|
emojiBar = Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 6),
|
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 6),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: themeType == AppThemeType.cyberpunk || themeType == AppThemeType.arcade ? Colors.black.withOpacity(0.6) : Colors.white.withOpacity(0.8),
|
color: themeType == AppThemeType.cyberpunk || themeType == AppThemeType.arcade || themeType == AppThemeType.aurora ? Colors.black.withOpacity(0.6) : Colors.white.withOpacity(0.8),
|
||||||
borderRadius: BorderRadius.circular(30),
|
borderRadius: BorderRadius.circular(30),
|
||||||
border: Border.all(color: themeType == AppThemeType.cyberpunk ? theme.playerBlue.withOpacity(0.3) : Colors.black12, width: 2),
|
border: Border.all(color: themeType == AppThemeType.cyberpunk ? theme.playerBlue.withOpacity(0.3) : Colors.white24, width: 2),
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
|
@ -388,10 +382,10 @@ class _GameScreenState extends State<GameScreen> with TickerProviderStateMixin {
|
||||||
Container(
|
Container(
|
||||||
decoration: BoxDecoration(borderRadius: BorderRadius.circular(20), boxShadow: [BoxShadow(color: Colors.black.withOpacity(0.4), offset: const Offset(0, 4), blurRadius: 5)]),
|
decoration: BoxDecoration(borderRadius: BorderRadius.circular(20), boxShadow: [BoxShadow(color: Colors.black.withOpacity(0.4), offset: const Offset(0, 4), blurRadius: 5)]),
|
||||||
child: TextButton.icon(
|
child: TextButton.icon(
|
||||||
style: TextButton.styleFrom(backgroundColor: bgImage != null || themeType == AppThemeType.arcade ? Colors.black87 : theme.background, padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20), side: BorderSide(color: Colors.white.withOpacity(0.1), width: 1))),
|
style: TextButton.styleFrom(backgroundColor: bgImage != null || themeType == AppThemeType.arcade || themeType == AppThemeType.aurora ? Colors.black87 : theme.background, padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20), side: BorderSide(color: Colors.white.withOpacity(0.1), width: 1))),
|
||||||
icon: Icon(Icons.exit_to_app, color: bgImage != null || themeType == AppThemeType.arcade ? Colors.white : theme.text, size: 20),
|
icon: Icon(Icons.exit_to_app, color: bgImage != null || themeType == AppThemeType.arcade || themeType == AppThemeType.aurora ? Colors.white : theme.text, size: 20),
|
||||||
onPressed: () { gameController.disconnectOnlineGame(); Navigator.pop(context); },
|
onPressed: () { gameController.disconnectOnlineGame(); Navigator.pop(context); },
|
||||||
label: Text("ESCI", style: _getTextStyle(themeType, TextStyle(color: bgImage != null || themeType == AppThemeType.arcade ? Colors.white : theme.text, fontWeight: FontWeight.bold, fontSize: 12))),
|
label: Text("ESCI", style: _getTextStyle(themeType, TextStyle(color: bgImage != null || themeType == AppThemeType.arcade || themeType == AppThemeType.aurora ? Colors.white : theme.text, fontWeight: FontWeight.bold, fontSize: 12))),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
@ -412,11 +406,21 @@ class _GameScreenState extends State<GameScreen> with TickerProviderStateMixin {
|
||||||
canPop: true,
|
canPop: true,
|
||||||
onPopInvoked: (didPop) { gameController.disconnectOnlineGame(); },
|
onPopInvoked: (didPop) { gameController.disconnectOnlineGame(); },
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
backgroundColor: bgImage != null ? Colors.transparent : theme.background,
|
// L'impostazione dello sfondo dipende dal tema
|
||||||
body: CustomPaint(
|
backgroundColor: themeType == AppThemeType.aurora ? Colors.transparent : (bgImage != null ? Colors.transparent : theme.background),
|
||||||
painter: themeType == AppThemeType.minimal ? FullScreenGridPainter(Colors.black.withOpacity(0.06)) : null,
|
body: Container(
|
||||||
child: Container(
|
// GRADIENTE AURORA IN BACKGROUND!
|
||||||
decoration: bgImage != null ? BoxDecoration(image: DecorationImage(image: AssetImage(bgImage), fit: BoxFit.cover, colorFilter: themeType == AppThemeType.doodle ? ColorFilter.mode(Colors.white.withOpacity(0.7), BlendMode.lighten) : null)) : null,
|
decoration: themeType == AppThemeType.aurora
|
||||||
|
? const BoxDecoration(
|
||||||
|
gradient: LinearGradient(
|
||||||
|
begin: Alignment.topLeft,
|
||||||
|
end: Alignment.bottomRight,
|
||||||
|
colors: [Color(0xFF2A0845), Color(0xFFDD2476), Color(0xFFFF512F)],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: (bgImage != null ? BoxDecoration(image: DecorationImage(image: AssetImage(bgImage), fit: BoxFit.cover, colorFilter: themeType == AppThemeType.doodle ? ColorFilter.mode(Colors.white.withOpacity(0.7), BlendMode.lighten) : null)) : null),
|
||||||
|
child: CustomPaint(
|
||||||
|
// painter rimosso per supportare il nuovo sfondo a gradiente
|
||||||
child: Stack(
|
child: Stack(
|
||||||
children: [
|
children: [
|
||||||
if (gameController.isTimeMode && !gameController.isCPUThinking && !gameController.isGameOver && gameController.timeLeft > 0 && gameController.timeLeft <= 5 && !gameController.isSetupPhase)
|
if (gameController.isTimeMode && !gameController.isCPUThinking && !gameController.isGameOver && gameController.timeLeft > 0 && gameController.timeLeft <= 5 && !gameController.isSetupPhase)
|
||||||
|
|
@ -427,13 +431,11 @@ class _GameScreenState extends State<GameScreen> with TickerProviderStateMixin {
|
||||||
|
|
||||||
Positioned.fill(child: gameContent),
|
Positioned.fill(child: gameContent),
|
||||||
|
|
||||||
// --- SCHERMATA COPRENTE PER IL PASSAGGIO DEL TELEFONO IN LOCALE ---
|
|
||||||
if (gameController.isSetupPhase && !_hideJokerMessage)
|
if (gameController.isSetupPhase && !_hideJokerMessage)
|
||||||
Positioned.fill(
|
Positioned.fill(
|
||||||
child: Container(
|
child: Container(
|
||||||
// Il colore di sfondo riempie tutto lo schermo per non far sbirciare la griglia
|
color: themeType == AppThemeType.cyberpunk || themeType == AppThemeType.arcade || themeType == AppThemeType.aurora
|
||||||
color: themeType == AppThemeType.cyberpunk || themeType == AppThemeType.arcade
|
? Colors.black.withOpacity(0.98)
|
||||||
? Colors.black
|
|
||||||
: theme.background.withOpacity(0.98),
|
: theme.background.withOpacity(0.98),
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
|
|
@ -518,7 +520,7 @@ class _WinnerVFXOverlayState extends State<WinnerVFXOverlay> with SingleTickerPr
|
||||||
}
|
}
|
||||||
|
|
||||||
void _initParticles(Size screenSize) {
|
void _initParticles(Size screenSize) {
|
||||||
int particleCount = widget.themeType == AppThemeType.cyberpunk ? 150 : 100;
|
int particleCount = widget.themeType == AppThemeType.cyberpunk || widget.themeType == AppThemeType.aurora ? 150 : 100;
|
||||||
if (widget.themeType == AppThemeType.arcade) particleCount = 80;
|
if (widget.themeType == AppThemeType.arcade) particleCount = 80;
|
||||||
if (widget.themeType == AppThemeType.grimorio) particleCount = 120;
|
if (widget.themeType == AppThemeType.grimorio) particleCount = 120;
|
||||||
|
|
||||||
|
|
@ -528,6 +530,7 @@ class _WinnerVFXOverlayState extends State<WinnerVFXOverlay> with SingleTickerPr
|
||||||
else if (widget.themeType == AppThemeType.wood) { palette = [Colors.orangeAccent, Colors.yellow, Colors.red, Colors.white]; }
|
else if (widget.themeType == AppThemeType.wood) { palette = [Colors.orangeAccent, Colors.yellow, Colors.red, Colors.white]; }
|
||||||
else if (widget.themeType == AppThemeType.arcade) { palette = [widget.winnerColor, Colors.white, Colors.greenAccent]; }
|
else if (widget.themeType == AppThemeType.arcade) { palette = [widget.winnerColor, Colors.white, Colors.greenAccent]; }
|
||||||
else if (widget.themeType == AppThemeType.grimorio) { palette = [widget.winnerColor, Colors.deepPurpleAccent, Colors.white]; }
|
else if (widget.themeType == AppThemeType.grimorio) { palette = [widget.winnerColor, Colors.deepPurpleAccent, Colors.white]; }
|
||||||
|
else if (widget.themeType == AppThemeType.aurora) { palette = [widget.winnerColor, Colors.white, Colors.orangeAccent, Colors.pinkAccent]; }
|
||||||
|
|
||||||
for (int i = 0; i < particleCount; i++) {
|
for (int i = 0; i < particleCount; i++) {
|
||||||
double speed = _rand.nextDouble() * 20 + 5;
|
double speed = _rand.nextDouble() * 20 + 5;
|
||||||
|
|
@ -540,7 +543,7 @@ class _WinnerVFXOverlayState extends State<WinnerVFXOverlay> with SingleTickerPr
|
||||||
setState(() {
|
setState(() {
|
||||||
for (var p in _particles) {
|
for (var p in _particles) {
|
||||||
p.x += p.vx; p.y += p.vy;
|
p.x += p.vx; p.y += p.vy;
|
||||||
if (widget.themeType == AppThemeType.cyberpunk) { p.vy += 0.1; p.vx *= 0.98; p.vy *= 0.98; }
|
if (widget.themeType == AppThemeType.cyberpunk || widget.themeType == AppThemeType.aurora) { p.vy += 0.1; p.vx *= 0.98; p.vy *= 0.98; }
|
||||||
else if (widget.themeType == AppThemeType.wood) { p.vy -= 0.2; p.x += math.sin(p.y * 0.05) * 2; }
|
else if (widget.themeType == AppThemeType.wood) { p.vy -= 0.2; p.x += math.sin(p.y * 0.05) * 2; }
|
||||||
else if (widget.themeType == AppThemeType.arcade) { p.vy += 0.3; p.spin = 0; p.angle = 0; }
|
else if (widget.themeType == AppThemeType.arcade) { p.vy += 0.3; p.spin = 0; p.angle = 0; }
|
||||||
else if (widget.themeType == AppThemeType.grimorio) { p.vy -= 0.1; p.x += math.sin(p.y * 0.02) * 1.5; p.size *= 0.995; }
|
else if (widget.themeType == AppThemeType.grimorio) { p.vy -= 0.1; p.x += math.sin(p.y * 0.02) * 1.5; p.size *= 0.995; }
|
||||||
|
|
@ -563,7 +566,7 @@ class _VFXPainter extends CustomPainter {
|
||||||
for (var p in particles) {
|
for (var p in particles) {
|
||||||
if (p.size < 0.5) continue;
|
if (p.size < 0.5) continue;
|
||||||
final paint = Paint()..color = p.color..style = PaintingStyle.fill;
|
final paint = Paint()..color = p.color..style = PaintingStyle.fill;
|
||||||
if (themeType == AppThemeType.cyberpunk) { paint.maskFilter = const MaskFilter.blur(BlurStyle.solid, 4.0); }
|
if (themeType == AppThemeType.cyberpunk || themeType == AppThemeType.aurora) { paint.maskFilter = const MaskFilter.blur(BlurStyle.solid, 4.0); }
|
||||||
canvas.save(); canvas.translate(p.x, p.y); canvas.rotate(p.angle);
|
canvas.save(); canvas.translate(p.x, p.y); canvas.rotate(p.angle);
|
||||||
|
|
||||||
if (themeType == AppThemeType.doodle) {
|
if (themeType == AppThemeType.doodle) {
|
||||||
|
|
@ -600,6 +603,7 @@ class _BouncingEmojiState extends State<_BouncingEmoji> with SingleTickerProvide
|
||||||
@override Widget build(BuildContext context) { return AnimatedBuilder(animation: _anim, builder: (ctx, child) => Transform.translate(offset: Offset(0, _anim.value), child: Container(padding: const EdgeInsets.all(8), decoration: const BoxDecoration(color: Colors.white, shape: BoxShape.circle, boxShadow: [BoxShadow(color: Colors.black26, blurRadius: 5)]), child: Text(widget.emoji, style: const TextStyle(fontSize: 32))))); }
|
@override Widget build(BuildContext context) { return AnimatedBuilder(animation: _anim, builder: (ctx, child) => Transform.translate(offset: Offset(0, _anim.value), child: Container(padding: const EdgeInsets.all(8), decoration: const BoxDecoration(color: Colors.white, shape: BoxShape.circle, boxShadow: [BoxShadow(color: Colors.black26, blurRadius: 5)]), child: Text(widget.emoji, style: const TextStyle(fontSize: 32))))); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mantengo la definizione per evitare buchi, ma ora Aurora usa il LinearGradient!
|
||||||
class FullScreenGridPainter extends CustomPainter {
|
class FullScreenGridPainter extends CustomPainter {
|
||||||
final Color gridColor; FullScreenGridPainter(this.gridColor);
|
final Color gridColor; FullScreenGridPainter(this.gridColor);
|
||||||
@override void paint(Canvas canvas, Size size) { final Paint paperGridPaint = Paint()..color = gridColor..strokeWidth = 1.0..style = PaintingStyle.stroke; double paperStep = 20.0; for (double i = 0; i <= size.width; i += paperStep) canvas.drawLine(Offset(i, 0), Offset(i, size.height), paperGridPaint); for (double i = 0; i <= size.height; i += paperStep) canvas.drawLine(Offset(0, i), Offset(size.width, i), paperGridPaint); }
|
@override void paint(Canvas canvas, Size size) { final Paint paperGridPaint = Paint()..color = gridColor..strokeWidth = 1.0..style = PaintingStyle.stroke; double paperStep = 20.0; for (double i = 0; i <= size.width; i += paperStep) canvas.drawLine(Offset(i, 0), Offset(i, size.height), paperGridPaint); for (double i = 0; i <= size.height; i += paperStep) canvas.drawLine(Offset(0, i), Offset(size.width, i), paperGridPaint); }
|
||||||
|
|
|
||||||
|
|
@ -405,13 +405,13 @@ class _HomeScreenState extends State<HomeScreen> with WidgetsBindingObserver {
|
||||||
StorageService.instance.syncLeaderboard();
|
StorageService.instance.syncLeaderboard();
|
||||||
});
|
});
|
||||||
_checkClipboardForInvite();
|
_checkClipboardForInvite();
|
||||||
_initDeepLinks(); // <--- AGGIUNGI QUESTO
|
_initDeepLinks();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
WidgetsBinding.instance.removeObserver(this);
|
WidgetsBinding.instance.removeObserver(this);
|
||||||
_linkSubscription?.cancel(); // <--- AGGIUNGI QUESTO
|
_linkSubscription?.cancel();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -541,13 +541,11 @@ class _HomeScreenState extends State<HomeScreen> with WidgetsBindingObserver {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- LOGICA DEEP LINKS ---
|
|
||||||
Future<void> _initDeepLinks() async {
|
Future<void> _initDeepLinks() async {
|
||||||
_appLinks = AppLinks();
|
_appLinks = AppLinks();
|
||||||
|
|
||||||
// 1. Controlla se l'app è stata aperta DA CHIUSA tramite un link
|
|
||||||
try {
|
try {
|
||||||
final initialUri = await _appLinks.getInitialLink(); // <--- ECCO LA PAROLA CORRETTA!
|
final initialUri = await _appLinks.getInitialLink();
|
||||||
if (initialUri != null) {
|
if (initialUri != null) {
|
||||||
_handleDeepLink(initialUri);
|
_handleDeepLink(initialUri);
|
||||||
}
|
}
|
||||||
|
|
@ -555,7 +553,6 @@ class _HomeScreenState extends State<HomeScreen> with WidgetsBindingObserver {
|
||||||
debugPrint("Errore lettura link iniziale: $e");
|
debugPrint("Errore lettura link iniziale: $e");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Rimane in ascolto se l'app era IN BACKGROUND
|
|
||||||
_linkSubscription = _appLinks.uriLinkStream.listen((uri) {
|
_linkSubscription = _appLinks.uriLinkStream.listen((uri) {
|
||||||
_handleDeepLink(uri);
|
_handleDeepLink(uri);
|
||||||
}, onError: (err) {
|
}, onError: (err) {
|
||||||
|
|
@ -568,7 +565,6 @@ class _HomeScreenState extends State<HomeScreen> with WidgetsBindingObserver {
|
||||||
if (uri.scheme == 'tetraq' && uri.host == 'join') {
|
if (uri.scheme == 'tetraq' && uri.host == 'join') {
|
||||||
String? code = uri.queryParameters['code'];
|
String? code = uri.queryParameters['code'];
|
||||||
if (code != null && code.length == 5) {
|
if (code != null && code.length == 5) {
|
||||||
// Usa un piccolo delay per assicurarsi che l'app sia pronta
|
|
||||||
Future.delayed(const Duration(milliseconds: 500), () {
|
Future.delayed(const Duration(milliseconds: 500), () {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
_promptJoinRoom(code.toUpperCase());
|
_promptJoinRoom(code.toUpperCase());
|
||||||
|
|
@ -577,7 +573,6 @@ class _HomeScreenState extends State<HomeScreen> with WidgetsBindingObserver {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// -------------------------
|
|
||||||
|
|
||||||
Future<void> _checkClipboardForInvite() async {
|
Future<void> _checkClipboardForInvite() async {
|
||||||
try {
|
try {
|
||||||
|
|
@ -662,9 +657,35 @@ class _HomeScreenState extends State<HomeScreen> with WidgetsBindingObserver {
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Text(isVsCPU ? loc.cpuTitle : loc.localTitle, style: _getTextStyle(themeType, TextStyle(fontSize: 26, fontWeight: FontWeight.w900, color: inkColor, letterSpacing: 2))),
|
Row(
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
width: 40,
|
||||||
|
child: IconButton(
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
icon: Icon(Icons.arrow_back_ios_new, color: inkColor, size: 26),
|
||||||
|
onPressed: () => Navigator.pop(ctx),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Text(isVsCPU ? loc.cpuTitle : loc.localTitle, textAlign: TextAlign.center, style: _getTextStyle(themeType, TextStyle(fontSize: 26, fontWeight: FontWeight.w900, color: inkColor, letterSpacing: 2))),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 40),
|
||||||
|
],
|
||||||
|
),
|
||||||
const SizedBox(height: 25),
|
const SizedBox(height: 25),
|
||||||
|
|
||||||
|
if (isVsCPU) ...[
|
||||||
|
Icon(Icons.smart_toy, size: 50, color: inkColor.withOpacity(0.6)),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
Text("MODALITÀ CAMPAGNA", style: _getTextStyle(themeType, TextStyle(fontSize: 16, fontWeight: FontWeight.w900, color: inkColor))),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
Text("Livello CPU: ${StorageService.instance.cpuLevel}\nForma e dimensioni si adatteranno alla tua bravura!", textAlign: TextAlign.center, style: _getTextStyle(themeType, TextStyle(fontSize: 13, color: inkColor.withOpacity(0.8), height: 1.4))),
|
||||||
|
const SizedBox(height: 25),
|
||||||
|
Divider(color: inkColor.withOpacity(0.3), thickness: 2.5),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
] else ...[
|
||||||
Text("FORMA ARENA", style: _getTextStyle(themeType, TextStyle(fontSize: 14, fontWeight: FontWeight.w900, color: inkColor.withOpacity(0.6), letterSpacing: 1.5))),
|
Text("FORMA ARENA", style: _getTextStyle(themeType, TextStyle(fontSize: 14, fontWeight: FontWeight.w900, color: inkColor.withOpacity(0.6), letterSpacing: 1.5))),
|
||||||
const SizedBox(height: 15),
|
const SizedBox(height: 15),
|
||||||
Wrap(
|
Wrap(
|
||||||
|
|
@ -697,6 +718,7 @@ class _HomeScreenState extends State<HomeScreen> with WidgetsBindingObserver {
|
||||||
const SizedBox(height: 25),
|
const SizedBox(height: 25),
|
||||||
Divider(color: inkColor.withOpacity(0.3), thickness: 2.5),
|
Divider(color: inkColor.withOpacity(0.3), thickness: 2.5),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
|
],
|
||||||
|
|
||||||
Text("TEMPO", style: _getTextStyle(themeType, TextStyle(fontSize: 14, fontWeight: FontWeight.w900, color: inkColor.withOpacity(0.6), letterSpacing: 1.5))),
|
Text("TEMPO", style: _getTextStyle(themeType, TextStyle(fontSize: 14, fontWeight: FontWeight.w900, color: inkColor.withOpacity(0.6), letterSpacing: 1.5))),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
|
|
@ -742,9 +764,35 @@ class _HomeScreenState extends State<HomeScreen> with WidgetsBindingObserver {
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Text(isVsCPU ? loc.cpuTitle : loc.localTitle, style: _getTextStyle(themeType, TextStyle(fontSize: 24, fontWeight: FontWeight.w900, color: theme.text, letterSpacing: 2))),
|
Row(
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
width: 40,
|
||||||
|
child: IconButton(
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
icon: Icon(Icons.arrow_back_ios_new, color: theme.text, size: 26),
|
||||||
|
onPressed: () => Navigator.pop(ctx),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Text(isVsCPU ? loc.cpuTitle : loc.localTitle, textAlign: TextAlign.center, style: _getTextStyle(themeType, TextStyle(fontSize: 24, fontWeight: FontWeight.w900, color: theme.text, letterSpacing: 2))),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 40),
|
||||||
|
],
|
||||||
|
),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
|
|
||||||
|
if (isVsCPU) ...[
|
||||||
|
Icon(Icons.smart_toy, size: 50, color: theme.playerBlue),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
Text("MODALITÀ CAMPAGNA", style: _getTextStyle(themeType, TextStyle(fontSize: 16, fontWeight: FontWeight.w900, color: theme.text, letterSpacing: 1.5))),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
Text("Livello CPU: ${StorageService.instance.cpuLevel}\nForma e dimensioni si adatteranno alla tua bravura!", textAlign: TextAlign.center, style: _getTextStyle(themeType, TextStyle(fontSize: 13, color: theme.text.withOpacity(0.7), height: 1.4))),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
Divider(color: Colors.white.withOpacity(0.05), thickness: 2),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
] else ...[
|
||||||
Text("FORMA ARENA", style: _getTextStyle(themeType, TextStyle(fontSize: 12, fontWeight: FontWeight.w900, color: theme.text.withOpacity(0.5), letterSpacing: 1.5))),
|
Text("FORMA ARENA", style: _getTextStyle(themeType, TextStyle(fontSize: 12, fontWeight: FontWeight.w900, color: theme.text.withOpacity(0.5), letterSpacing: 1.5))),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
Wrap(
|
Wrap(
|
||||||
|
|
@ -777,6 +825,7 @@ class _HomeScreenState extends State<HomeScreen> with WidgetsBindingObserver {
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
Divider(color: Colors.white.withOpacity(0.05), thickness: 2),
|
Divider(color: Colors.white.withOpacity(0.05), thickness: 2),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
|
],
|
||||||
|
|
||||||
Text("TEMPO", style: _getTextStyle(themeType, TextStyle(fontSize: 12, fontWeight: FontWeight.w900, color: theme.text.withOpacity(0.5), letterSpacing: 1.5))),
|
Text("TEMPO", style: _getTextStyle(themeType, TextStyle(fontSize: 12, fontWeight: FontWeight.w900, color: theme.text.withOpacity(0.5), letterSpacing: 1.5))),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
|
|
@ -813,7 +862,6 @@ class _HomeScreenState extends State<HomeScreen> with WidgetsBindingObserver {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- NUOVA FUNZIONE: MOSTRA LE SFIDE GIORNALIERE ---
|
|
||||||
Future<void> _showDailyQuestsDialog() async {
|
Future<void> _showDailyQuestsDialog() async {
|
||||||
final prefs = await SharedPreferences.getInstance();
|
final prefs = await SharedPreferences.getInstance();
|
||||||
|
|
||||||
|
|
@ -846,7 +894,6 @@ class _HomeScreenState extends State<HomeScreen> with WidgetsBindingObserver {
|
||||||
Text(loc.questsTitle, style: _getTextStyle(themeType, TextStyle(fontSize: 22, fontWeight: FontWeight.w900, color: theme.text, letterSpacing: 1.5))),
|
Text(loc.questsTitle, style: _getTextStyle(themeType, TextStyle(fontSize: 22, fontWeight: FontWeight.w900, color: theme.text, letterSpacing: 1.5))),
|
||||||
const SizedBox(height: 25),
|
const SizedBox(height: 25),
|
||||||
|
|
||||||
// Generiamo dinamicamente le 3 missioni salvate in memoria
|
|
||||||
...List.generate(3, (index) {
|
...List.generate(3, (index) {
|
||||||
int i = index + 1;
|
int i = index + 1;
|
||||||
int type = prefs.getInt('q${i}_type') ?? 0;
|
int type = prefs.getInt('q${i}_type') ?? 0;
|
||||||
|
|
@ -916,7 +963,6 @@ class _HomeScreenState extends State<HomeScreen> with WidgetsBindingObserver {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- NUOVA FUNZIONE: MOSTRA LA CLASSIFICA GLOBALE ---
|
|
||||||
void _showLeaderboardDialog() {
|
void _showLeaderboardDialog() {
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
|
|
@ -943,7 +989,6 @@ class _HomeScreenState extends State<HomeScreen> with WidgetsBindingObserver {
|
||||||
Text(loc.leaderboardTitle, style: _getTextStyle(themeType, TextStyle(fontSize: 20, fontWeight: FontWeight.w900, color: theme.text, letterSpacing: 1.5))),
|
Text(loc.leaderboardTitle, style: _getTextStyle(themeType, TextStyle(fontSize: 20, fontWeight: FontWeight.w900, color: theme.text, letterSpacing: 1.5))),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
|
|
||||||
// Lista giocatori pescata da Firebase!
|
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: 350,
|
height: 350,
|
||||||
child: StreamBuilder<QuerySnapshot>(
|
child: StreamBuilder<QuerySnapshot>(
|
||||||
|
|
@ -959,7 +1004,7 @@ class _HomeScreenState extends State<HomeScreen> with WidgetsBindingObserver {
|
||||||
final docs = snapshot.data!.docs;
|
final docs = snapshot.data!.docs;
|
||||||
return ListView.builder(
|
return ListView.builder(
|
||||||
physics: const BouncingScrollPhysics(),
|
physics: const BouncingScrollPhysics(),
|
||||||
itemCount: docs.length, // <--- ECCO LA RIGA MAGICA AGGIUNTA!
|
itemCount: docs.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
var data = docs[index].data() as Map<String, dynamic>;
|
var data = docs[index].data() as Map<String, dynamic>;
|
||||||
|
|
||||||
|
|
@ -1320,7 +1365,7 @@ class _HomeScreenState extends State<HomeScreen> with WidgetsBindingObserver {
|
||||||
|
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
|
|
||||||
// --- NUOVA LISTA BOTTONI CON CLASSIFICHE E SFIDE ---
|
// --- NUOVA LISTA BOTTONI ---
|
||||||
Column(
|
Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
|
|
@ -1331,7 +1376,6 @@ class _HomeScreenState extends State<HomeScreen> with WidgetsBindingObserver {
|
||||||
_buildCyberCard(_FeatureCard(title: loc.localTitle, subtitle: loc.localSub, icon: Icons.people_alt, color: Colors.red.shade200, theme: theme, themeType: themeType, onTap: () => _showMatchSetupDialog(false)), themeType),
|
_buildCyberCard(_FeatureCard(title: loc.localTitle, subtitle: loc.localSub, icon: Icons.people_alt, color: Colors.red.shade200, theme: theme, themeType: themeType, onTap: () => _showMatchSetupDialog(false)), themeType),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
|
|
||||||
// NUOVI BOTTONI PER LA VERSIONE 2.0
|
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(child: _buildCyberCard(_FeatureCard(title: loc.leaderboardTitle, subtitle: "Top 50 Globale", icon: Icons.leaderboard, color: Colors.amber.shade200, theme: theme, themeType: themeType, onTap: _showLeaderboardDialog, compact: true), themeType)),
|
Expanded(child: _buildCyberCard(_FeatureCard(title: loc.leaderboardTitle, subtitle: "Top 50 Globale", icon: Icons.leaderboard, color: Colors.amber.shade200, theme: theme, themeType: themeType, onTap: _showLeaderboardDialog, compact: true), themeType)),
|
||||||
|
|
@ -1457,7 +1501,6 @@ class _FeatureCard extends StatelessWidget {
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
// --- MAGIA QUI: FittedBox impedisce di andare a capo ---
|
|
||||||
FittedBox(
|
FittedBox(
|
||||||
fit: BoxFit.scaleDown,
|
fit: BoxFit.scaleDown,
|
||||||
alignment: Alignment.centerLeft,
|
alignment: Alignment.centerLeft,
|
||||||
|
|
@ -1522,7 +1565,6 @@ class _FeatureCard extends StatelessWidget {
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
// --- MAGIA QUI: FittedBox impedisce di andare a capo ---
|
|
||||||
FittedBox(
|
FittedBox(
|
||||||
fit: BoxFit.scaleDown,
|
fit: BoxFit.scaleDown,
|
||||||
alignment: Alignment.centerLeft,
|
alignment: Alignment.centerLeft,
|
||||||
|
|
|
||||||
|
|
@ -35,10 +35,10 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||||
padding: const EdgeInsets.all(20),
|
padding: const EdgeInsets.all(20),
|
||||||
children: [
|
children: [
|
||||||
_ThemeCard(
|
_ThemeCard(
|
||||||
title: "Minimal",
|
title: "Quaderno (Doodle)",
|
||||||
subtitle: "Linee pulite, sfondo chiaro",
|
subtitle: "Sfondo a quadretti, tratto a penna",
|
||||||
type: AppThemeType.minimal,
|
type: AppThemeType.doodle,
|
||||||
previewColors: AppColors.minimal,
|
previewColors: AppColors.doodle,
|
||||||
requiredLevel: 1,
|
requiredLevel: 1,
|
||||||
currentLevel: playerLevel,
|
currentLevel: playerLevel,
|
||||||
),
|
),
|
||||||
|
|
@ -52,15 +52,6 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||||
currentLevel: playerLevel,
|
currentLevel: playerLevel,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 15),
|
const SizedBox(height: 15),
|
||||||
_ThemeCard(
|
|
||||||
title: "Quaderno (Doodle)",
|
|
||||||
subtitle: "Sfondo a quadretti, tratto a penna",
|
|
||||||
type: AppThemeType.doodle,
|
|
||||||
previewColors: AppColors.doodle,
|
|
||||||
requiredLevel: 5,
|
|
||||||
currentLevel: playerLevel,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 15),
|
|
||||||
_ThemeCard(
|
_ThemeCard(
|
||||||
title: "Cyberpunk",
|
title: "Cyberpunk",
|
||||||
subtitle: "Nero profondo, luci al neon",
|
subtitle: "Nero profondo, luci al neon",
|
||||||
|
|
@ -164,9 +155,9 @@ class _ThemeCard extends StatelessWidget {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Container(width: 20, height: 20, decoration: BoxDecoration(color: previewColors.playerRed, shape: BoxShape.circle)),
|
Container(width: 20, height: 20, decoration: BoxDecoration(color: previewColors.playerRed, shape: BoxShape.circle, boxShadow: [BoxShadow(color: previewColors.playerRed.withOpacity(0.5), blurRadius: 4)])),
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
Container(width: 20, height: 20, decoration: BoxDecoration(color: previewColors.playerBlue, shape: BoxShape.circle)),
|
Container(width: 20, height: 20, decoration: BoxDecoration(color: previewColors.playerBlue, shape: BoxShape.circle, boxShadow: [BoxShadow(color: previewColors.playerBlue.withOpacity(0.5), blurRadius: 4)])),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue