Compare commits
3 commits
3c28c1f0c2
...
c90f75cf66
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c90f75cf66 | ||
|
|
42b8180f5e | ||
| dfe392ea88 |
78 changed files with 1823 additions and 4948 deletions
BIN
.DS_Store
vendored
BIN
.DS_Store
vendored
Binary file not shown.
|
|
@ -1,2 +0,0 @@
|
||||||
index.html,1773344753424,0d5d4b835a7d632ad11d249230a15561286f2bfcd1da8305c6fb294d37e5da09
|
|
||||||
404.html,1773344753356,05cbc6f94d7a69ce2e29646eab13be2c884e61ba93e3094df5028866876d18b3
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
{
|
|
||||||
"projects": {
|
|
||||||
"default": "tetraq-32a4a"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
16
.metadata
16
.metadata
|
|
@ -4,7 +4,7 @@
|
||||||
# This file should be version controlled and should not be manually edited.
|
# This file should be version controlled and should not be manually edited.
|
||||||
|
|
||||||
version:
|
version:
|
||||||
revision: "3b62efc2a3da49882f43c372e0bc53daef7295a6"
|
revision: "ff37bef603469fb030f2b72995ab929ccfc227f0"
|
||||||
channel: "stable"
|
channel: "stable"
|
||||||
|
|
||||||
project_type: app
|
project_type: app
|
||||||
|
|
@ -13,17 +13,11 @@ project_type: app
|
||||||
migration:
|
migration:
|
||||||
platforms:
|
platforms:
|
||||||
- platform: root
|
- platform: root
|
||||||
create_revision: 3b62efc2a3da49882f43c372e0bc53daef7295a6
|
create_revision: ff37bef603469fb030f2b72995ab929ccfc227f0
|
||||||
base_revision: 3b62efc2a3da49882f43c372e0bc53daef7295a6
|
base_revision: ff37bef603469fb030f2b72995ab929ccfc227f0
|
||||||
- platform: android
|
|
||||||
create_revision: 3b62efc2a3da49882f43c372e0bc53daef7295a6
|
|
||||||
base_revision: 3b62efc2a3da49882f43c372e0bc53daef7295a6
|
|
||||||
- platform: ios
|
|
||||||
create_revision: 3b62efc2a3da49882f43c372e0bc53daef7295a6
|
|
||||||
base_revision: 3b62efc2a3da49882f43c372e0bc53daef7295a6
|
|
||||||
- platform: macos
|
- platform: macos
|
||||||
create_revision: 3b62efc2a3da49882f43c372e0bc53daef7295a6
|
create_revision: ff37bef603469fb030f2b72995ab929ccfc227f0
|
||||||
base_revision: 3b62efc2a3da49882f43c372e0bc53daef7295a6
|
base_revision: ff37bef603469fb030f2b72995ab929ccfc227f0
|
||||||
|
|
||||||
# User provided section
|
# User provided section
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,19 +8,8 @@ plugins {
|
||||||
id("dev.flutter.flutter-gradle-plugin")
|
id("dev.flutter.flutter-gradle-plugin")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Aggiungiamo esplicitamente gli import richiesti da Kotlin
|
|
||||||
import java.io.FileInputStream
|
|
||||||
import java.util.Properties
|
|
||||||
|
|
||||||
// Carichiamo il file con le password
|
|
||||||
val keystoreProperties = Properties()
|
|
||||||
val keystorePropertiesFile = rootProject.file("key.properties")
|
|
||||||
if (keystorePropertiesFile.exists()) {
|
|
||||||
keystoreProperties.load(FileInputStream(keystorePropertiesFile))
|
|
||||||
}
|
|
||||||
|
|
||||||
android {
|
android {
|
||||||
namespace = "com.amastra.tetraq"
|
namespace = "com.sanza.tetraq"
|
||||||
compileSdk = flutter.compileSdkVersion
|
compileSdk = flutter.compileSdkVersion
|
||||||
ndkVersion = flutter.ndkVersion
|
ndkVersion = flutter.ndkVersion
|
||||||
|
|
||||||
|
|
@ -29,16 +18,13 @@ android {
|
||||||
targetCompatibility = JavaVersion.VERSION_17
|
targetCompatibility = JavaVersion.VERSION_17
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sintassi aggiornata come richiesto dal compilatore Kotlin
|
kotlinOptions {
|
||||||
kotlin {
|
jvmTarget = JavaVersion.VERSION_17.toString()
|
||||||
compilerOptions {
|
|
||||||
jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||||
applicationId = "com.amastra.tetraq"
|
applicationId = "com.sanza.tetraq"
|
||||||
// You can update the following values to match your application needs.
|
// You can update the following values to match your application needs.
|
||||||
// For more information, see: https://flutter.dev/to/review-gradle-config.
|
// For more information, see: https://flutter.dev/to/review-gradle-config.
|
||||||
minSdk = flutter.minSdkVersion
|
minSdk = flutter.minSdkVersion
|
||||||
|
|
@ -47,24 +33,11 @@ android {
|
||||||
versionName = flutter.versionName
|
versionName = flutter.versionName
|
||||||
}
|
}
|
||||||
|
|
||||||
// Aggiunto il blocco per la firma in formato Kotlin DSL
|
|
||||||
signingConfigs {
|
|
||||||
create("release") {
|
|
||||||
keyAlias = keystoreProperties.getProperty("keyAlias")
|
|
||||||
keyPassword = keystoreProperties.getProperty("keyPassword")
|
|
||||||
val storeFileString = keystoreProperties.getProperty("storeFile")
|
|
||||||
if (storeFileString != null) {
|
|
||||||
storeFile = file(storeFileString)
|
|
||||||
}
|
|
||||||
storePassword = keystoreProperties.getProperty("storePassword")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
getByName("release") {
|
release {
|
||||||
// TODO: Add your own signing config for the release build.
|
// TODO: Add your own signing config for the release build.
|
||||||
// Ora usiamo la chiave di release appena creata
|
// Signing with the debug keys for now, so `flutter run --release` works.
|
||||||
signingConfig = signingConfigs.getByName("release")
|
signingConfig = signingConfigs.getByName("debug")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,25 +5,6 @@
|
||||||
"storage_bucket": "tetraq-32a4a.firebasestorage.app"
|
"storage_bucket": "tetraq-32a4a.firebasestorage.app"
|
||||||
},
|
},
|
||||||
"client": [
|
"client": [
|
||||||
{
|
|
||||||
"client_info": {
|
|
||||||
"mobilesdk_app_id": "1:705460445314:android:ceac21bb06b7a9f07b949b",
|
|
||||||
"android_client_info": {
|
|
||||||
"package_name": "com.amastra.tetraq"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"oauth_client": [],
|
|
||||||
"api_key": [
|
|
||||||
{
|
|
||||||
"current_key": "AIzaSyBsXO595xVITDPrRnXrW8HPQLOe7Rz4Gg4"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"services": {
|
|
||||||
"appinvite_service": {
|
|
||||||
"other_platform_oauth_client": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"client_info": {
|
"client_info": {
|
||||||
"mobilesdk_app_id": "1:705460445314:android:4d35fef29cfd63727b949b",
|
"mobilesdk_app_id": "1:705460445314:android:4d35fef29cfd63727b949b",
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package com.amastra.tetraq
|
package com.sanza.tetraq
|
||||||
|
|
||||||
import io.flutter.embedding.android.FlutterActivity
|
import io.flutter.embedding.android.FlutterActivity
|
||||||
|
|
||||||
BIN
assets/.DS_Store
vendored
BIN
assets/.DS_Store
vendored
Binary file not shown.
BIN
assets/audio/.DS_Store
vendored
BIN
assets/audio/.DS_Store
vendored
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 2.1 MiB |
Binary file not shown.
|
Before Width: | Height: | Size: 1.8 MiB |
|
|
@ -1,47 +1 @@
|
||||||
{
|
{"flutter":{"platforms":{"android":{"default":{"projectId":"tetraq-32a4a","appId":"1:705460445314:android:4d35fef29cfd63727b949b","fileOutput":"android/app/google-services.json"}},"ios":{"default":{"projectId":"tetraq-32a4a","appId":"1:705460445314:ios:da11cbca5d1f6bc27b949b","uploadDebugSymbols":false,"fileOutput":"ios/Runner/GoogleService-Info.plist"}},"macos":{"default":{"projectId":"tetraq-32a4a","appId":"1:705460445314:ios:da11cbca5d1f6bc27b949b","uploadDebugSymbols":false,"fileOutput":"macos/Runner/GoogleService-Info.plist"}},"dart":{"lib/firebase_options.dart":{"projectId":"tetraq-32a4a","configurations":{"android":"1:705460445314:android:4d35fef29cfd63727b949b","ios":"1:705460445314:ios:da11cbca5d1f6bc27b949b","macos":"1:705460445314:ios:da11cbca5d1f6bc27b949b"}}}}}}
|
||||||
"flutter": {
|
|
||||||
"platforms": {
|
|
||||||
"android": {
|
|
||||||
"default": {
|
|
||||||
"projectId": "tetraq-32a4a",
|
|
||||||
"appId": "1:705460445314:android:ceac21bb06b7a9f07b949b",
|
|
||||||
"fileOutput": "android/app/google-services.json"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ios": {
|
|
||||||
"default": {
|
|
||||||
"projectId": "tetraq-32a4a",
|
|
||||||
"appId": "1:705460445314:ios:54d64cb7592954327b949b",
|
|
||||||
"uploadDebugSymbols": false,
|
|
||||||
"fileOutput": "ios/Runner/GoogleService-Info.plist"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"macos": {
|
|
||||||
"default": {
|
|
||||||
"projectId": "tetraq-32a4a",
|
|
||||||
"appId": "1:705460445314:ios:da11cbca5d1f6bc27b949b",
|
|
||||||
"uploadDebugSymbols": false,
|
|
||||||
"fileOutput": "macos/Runner/GoogleService-Info.plist"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"dart": {
|
|
||||||
"lib/firebase_options.dart": {
|
|
||||||
"projectId": "tetraq-32a4a",
|
|
||||||
"configurations": {
|
|
||||||
"android": "1:705460445314:android:ceac21bb06b7a9f07b949b",
|
|
||||||
"ios": "1:705460445314:ios:54d64cb7592954327b949b",
|
|
||||||
"macos": "1:705460445314:ios:da11cbca5d1f6bc27b949b"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"hosting": {
|
|
||||||
"public": "public",
|
|
||||||
"ignore": [
|
|
||||||
"firebase.json",
|
|
||||||
"**/.*",
|
|
||||||
"**/node_modules/**"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
import 'dart:io';
|
|
||||||
import 'dart:convert';
|
|
||||||
|
|
||||||
void main() async {
|
|
||||||
final dir = Directory('lib/l10n');
|
|
||||||
if (!await dir.exists()) await dir.create(recursive: true);
|
|
||||||
|
|
||||||
final Map<String, Map<String, String>> translations = {
|
|
||||||
'it': {"appTitle": "TetraQ", "welcomeTitle": "BENVENUTO IN TETRAQ!", "nameHint": "NOME", "saveAndPlay": "SALVA E GIOCA", "onlineTitle": "ONLINE", "onlineSub": "Sfida il mondo", "cpuTitle": "VS CPU", "cpuSub": "Allenati con l'IA", "localTitle": "LOCALE", "localSub": "Stesso schermo", "leaderboardTitle": "CLASSIFICA", "questsTitle": "SFIDE", "themesTitle": "TEMI", "tutorialTitle": "TUTORIAL", "startGame": "AVVIA PARTITA", "createMatch": "CREA PARTITA", "joinMatch": "UNISCITI", "gameOver": "FINE PARTITA", "mainMenu": "TORNA AL MENU", "exit": "ESCI"},
|
|
||||||
'en': {"appTitle": "TetraQ", "welcomeTitle": "WELCOME TO TETRAQ!", "nameHint": "NAME", "saveAndPlay": "SAVE & PLAY", "onlineTitle": "ONLINE", "onlineSub": "Challenge the world", "cpuTitle": "VS CPU", "cpuSub": "Train with AI", "localTitle": "LOCAL", "localSub": "Same screen", "leaderboardTitle": "LEADERBOARD", "questsTitle": "QUESTS", "themesTitle": "THEMES", "tutorialTitle": "TUTORIAL", "startGame": "START GAME", "createMatch": "CREATE MATCH", "joinMatch": "JOIN", "gameOver": "GAME OVER", "mainMenu": "BACK TO MENU", "exit": "EXIT"},
|
|
||||||
'es': {"appTitle": "TetraQ", "welcomeTitle": "¡BIENVENIDO A TETRAQ!", "nameHint": "NOMBRE", "saveAndPlay": "GUARDAR Y JUGAR", "onlineTitle": "ONLINE", "onlineSub": "Desafía al mundo", "cpuTitle": "VS CPU", "cpuSub": "Entrena con IA", "localTitle": "LOCAL", "localSub": "Misma pantalla", "leaderboardTitle": "RANKING", "questsTitle": "MISIONES", "themesTitle": "TEMAS", "tutorialTitle": "TUTORIAL", "startGame": "INICIAR JUEGO", "createMatch": "CREAR PARTIDA", "joinMatch": "UNIRSE", "gameOver": "FIN DEL JUEGO", "mainMenu": "VOLVER AL MENÚ", "exit": "SALIR"},
|
|
||||||
'fr': {"appTitle": "TetraQ", "welcomeTitle": "BIENVENUE DANS TETRAQ !", "nameHint": "NOM", "saveAndPlay": "SAUVEGARDER ET JOUER", "onlineTitle": "EN LIGNE", "onlineSub": "Défiez le monde", "cpuTitle": "VS CPU", "cpuSub": "Entraînez avec l'IA", "localTitle": "LOCAL", "localSub": "Même écran", "leaderboardTitle": "CLASSEMENT", "questsTitle": "QUÊTES", "themesTitle": "THÈMES", "tutorialTitle": "TUTORIEL", "startGame": "JOUER", "createMatch": "CRÉER UN MATCH", "joinMatch": "REJOINDRE", "gameOver": "FIN DE PARTIE", "mainMenu": "RETOUR AU MENU", "exit": "QUITTER"},
|
|
||||||
'de': {"appTitle": "TetraQ", "welcomeTitle": "WILLKOMMEN BEI TETRAQ!", "nameHint": "NAME", "saveAndPlay": "SPEICHERN & SPIELEN", "onlineTitle": "ONLINE", "onlineSub": "Fordere die Welt heraus", "cpuTitle": "VS CPU", "cpuSub": "Trainiere mit KI", "localTitle": "LOKAL", "localSub": "Gleicher Bildschirm", "leaderboardTitle": "RANGLISTE", "questsTitle": "MISSIONEN", "themesTitle": "THEMEN", "tutorialTitle": "TUTORIAL", "startGame": "SPIEL STARTEN", "createMatch": "SPIEL ERSTELLEN", "joinMatch": "BEITRETEN", "gameOver": "SPIELENDE", "mainMenu": "ZURÜCK ZUM MENÜ", "exit": "BEENDEN"},
|
|
||||||
'pt': {"appTitle": "TetraQ", "welcomeTitle": "BEM-VINDO AO TETRAQ!", "nameHint": "NOME", "saveAndPlay": "SALVAR E JOGAR", "onlineTitle": "ONLINE", "onlineSub": "Desafie o mundo", "cpuTitle": "VS CPU", "cpuSub": "Treine com a IA", "localTitle": "LOCAL", "localSub": "Mesma tela", "leaderboardTitle": "CLASSIFICAÇÃO", "questsTitle": "DESAFIOS", "themesTitle": "TEMAS", "tutorialTitle": "TUTORIAL", "startGame": "INICIAR JOGO", "createMatch": "CRIAR PARTIDA", "joinMatch": "ENTRAR", "gameOver": "FIM DE JOGO", "mainMenu": "VOLTAR AO MENU", "exit": "SAIR"},
|
|
||||||
'ru': {"appTitle": "TetraQ", "welcomeTitle": "ДОБРО ПОЖАЛОВАТЬ В TETRAQ!", "nameHint": "ИМЯ", "saveAndPlay": "СОХРАНИТЬ И ИГРАТЬ", "onlineTitle": "ОНЛАЙН", "onlineSub": "Брось вызов миру", "cpuTitle": "VS ИИ", "cpuSub": "Тренировка с ИИ", "localTitle": "ЛОКАЛЬНО", "localSub": "Один экран", "leaderboardTitle": "РЕЙТИНГ", "questsTitle": "ЗАДАНИЯ", "themesTitle": "ТЕМЫ", "tutorialTitle": "ОБУЧЕНИЕ", "startGame": "НАЧАТЬ ИГРУ", "createMatch": "СОЗДАТЬ ИГРУ", "joinMatch": "ПРИСОЕДИНИТЬСЯ", "gameOver": "ИГРА ОКОНЧЕНА", "mainMenu": "В ГЛАВНОЕ МЕНЮ", "exit": "ВЫХОД"},
|
|
||||||
'zh': {"appTitle": "TetraQ", "welcomeTitle": "欢迎来到 TETRAQ!", "nameHint": "名字", "saveAndPlay": "保存并开始", "onlineTitle": "在线匹配", "onlineSub": "挑战世界", "cpuTitle": "人机对战", "cpuSub": "与AI训练", "localTitle": "本地游戏", "localSub": "同屏对战", "leaderboardTitle": "排行榜", "questsTitle": "任务", "themesTitle": "主题", "tutorialTitle": "教程", "startGame": "开始游戏", "createMatch": "创建比赛", "joinMatch": "加入", "gameOver": "游戏结束", "mainMenu": "返回主菜单", "exit": "退出"}
|
|
||||||
};
|
|
||||||
|
|
||||||
for (var lang in translations.keys) {
|
|
||||||
final file = File('lib/l10n/app_$lang.arb');
|
|
||||||
final Map<String, dynamic> finalContent = {"@@locale": lang, ...translations[lang]!};
|
|
||||||
await file.writeAsString(JsonEncoder.withIndent(' ').convert(finalContent));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Crea anche il file di configurazione
|
|
||||||
await File('l10n.yaml').writeAsString("arb-dir: lib/l10n\ntemplate-arb-file: app_it.arb\noutput-localization-file: app_localizations.dart\n");
|
|
||||||
}
|
|
||||||
BIN
ios/.DS_Store
vendored
BIN
ios/.DS_Store
vendored
Binary file not shown.
137
ios/Podfile.lock
137
ios/Podfile.lock
|
|
@ -1190,10 +1190,6 @@ PODS:
|
||||||
- abseil/xcprivacy (1.20240722.0)
|
- abseil/xcprivacy (1.20240722.0)
|
||||||
- app_links (7.0.0):
|
- app_links (7.0.0):
|
||||||
- Flutter
|
- Flutter
|
||||||
- AppCheckCore (11.2.0):
|
|
||||||
- GoogleUtilities/Environment (~> 8.0)
|
|
||||||
- GoogleUtilities/UserDefaults (~> 8.0)
|
|
||||||
- PromisesObjC (~> 2.4)
|
|
||||||
- audioplayers_darwin (0.0.1):
|
- audioplayers_darwin (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- BoringSSL-GRPC (0.0.37):
|
- BoringSSL-GRPC (0.0.37):
|
||||||
|
|
@ -1203,60 +1199,32 @@ PODS:
|
||||||
- BoringSSL-GRPC/Interface (= 0.0.37)
|
- BoringSSL-GRPC/Interface (= 0.0.37)
|
||||||
- BoringSSL-GRPC/Interface (0.0.37)
|
- BoringSSL-GRPC/Interface (0.0.37)
|
||||||
- cloud_firestore (6.1.2):
|
- cloud_firestore (6.1.2):
|
||||||
- Firebase/Firestore (= 12.9.0)
|
- Firebase/Firestore (= 12.8.0)
|
||||||
- firebase_core
|
- firebase_core
|
||||||
- Flutter
|
- Flutter
|
||||||
- Firebase/Auth (12.9.0):
|
- Firebase/CoreOnly (12.8.0):
|
||||||
|
- FirebaseCore (~> 12.8.0)
|
||||||
|
- Firebase/Firestore (12.8.0):
|
||||||
- Firebase/CoreOnly
|
- Firebase/CoreOnly
|
||||||
- FirebaseAuth (~> 12.9.0)
|
- FirebaseFirestore (~> 12.8.0)
|
||||||
- Firebase/CoreOnly (12.9.0):
|
- firebase_core (4.4.0):
|
||||||
- FirebaseCore (~> 12.9.0)
|
- Firebase/CoreOnly (= 12.8.0)
|
||||||
- Firebase/Firestore (12.9.0):
|
|
||||||
- Firebase/CoreOnly
|
|
||||||
- FirebaseFirestore (~> 12.9.0)
|
|
||||||
- firebase_app_check (0.4.1-5):
|
|
||||||
- Firebase/CoreOnly (~> 12.9.0)
|
|
||||||
- firebase_core
|
|
||||||
- FirebaseAppCheck (~> 12.9.0)
|
|
||||||
- Flutter
|
- Flutter
|
||||||
- firebase_auth (6.1.4):
|
- FirebaseAppCheckInterop (12.8.0)
|
||||||
- Firebase/Auth (= 12.9.0)
|
- FirebaseCore (12.8.0):
|
||||||
- firebase_core
|
- FirebaseCoreInternal (~> 12.8.0)
|
||||||
- Flutter
|
|
||||||
- firebase_core (4.5.0):
|
|
||||||
- Firebase/CoreOnly (= 12.9.0)
|
|
||||||
- Flutter
|
|
||||||
- FirebaseAppCheck (12.9.0):
|
|
||||||
- AppCheckCore (~> 11.0)
|
|
||||||
- FirebaseAppCheckInterop (~> 12.9.0)
|
|
||||||
- FirebaseCore (~> 12.9.0)
|
|
||||||
- GoogleUtilities/Environment (~> 8.1)
|
|
||||||
- GoogleUtilities/UserDefaults (~> 8.1)
|
|
||||||
- FirebaseAppCheckInterop (12.9.0)
|
|
||||||
- FirebaseAuth (12.9.0):
|
|
||||||
- FirebaseAppCheckInterop (~> 12.9.0)
|
|
||||||
- FirebaseAuthInterop (~> 12.9.0)
|
|
||||||
- FirebaseCore (~> 12.9.0)
|
|
||||||
- FirebaseCoreExtension (~> 12.9.0)
|
|
||||||
- GoogleUtilities/AppDelegateSwizzler (~> 8.1)
|
|
||||||
- GoogleUtilities/Environment (~> 8.1)
|
|
||||||
- GTMSessionFetcher/Core (< 6.0, >= 3.4)
|
|
||||||
- RecaptchaInterop (~> 101.0)
|
|
||||||
- FirebaseAuthInterop (12.9.0)
|
|
||||||
- FirebaseCore (12.9.0):
|
|
||||||
- FirebaseCoreInternal (~> 12.9.0)
|
|
||||||
- GoogleUtilities/Environment (~> 8.1)
|
- GoogleUtilities/Environment (~> 8.1)
|
||||||
- GoogleUtilities/Logger (~> 8.1)
|
- GoogleUtilities/Logger (~> 8.1)
|
||||||
- FirebaseCoreExtension (12.9.0):
|
- FirebaseCoreExtension (12.8.0):
|
||||||
- FirebaseCore (~> 12.9.0)
|
- FirebaseCore (~> 12.8.0)
|
||||||
- FirebaseCoreInternal (12.9.0):
|
- FirebaseCoreInternal (12.8.0):
|
||||||
- "GoogleUtilities/NSData+zlib (~> 8.1)"
|
- "GoogleUtilities/NSData+zlib (~> 8.1)"
|
||||||
- FirebaseFirestore (12.9.0):
|
- FirebaseFirestore (12.8.0):
|
||||||
- FirebaseCore (~> 12.9.0)
|
- FirebaseCore (~> 12.8.0)
|
||||||
- FirebaseCoreExtension (~> 12.9.0)
|
- FirebaseCoreExtension (~> 12.8.0)
|
||||||
- FirebaseFirestoreInternal (~> 12.9.0)
|
- FirebaseFirestoreInternal (~> 12.8.0)
|
||||||
- FirebaseSharedSwift (~> 12.9.0)
|
- FirebaseSharedSwift (~> 12.8.0)
|
||||||
- FirebaseFirestoreInternal (12.9.0):
|
- FirebaseFirestoreInternal (12.8.0):
|
||||||
- abseil/algorithm (~> 1.20240722.0)
|
- abseil/algorithm (~> 1.20240722.0)
|
||||||
- abseil/base (~> 1.20240722.0)
|
- abseil/base (~> 1.20240722.0)
|
||||||
- abseil/container/flat_hash_map (~> 1.20240722.0)
|
- abseil/container/flat_hash_map (~> 1.20240722.0)
|
||||||
|
|
@ -1265,38 +1233,22 @@ PODS:
|
||||||
- abseil/strings/strings (~> 1.20240722.0)
|
- abseil/strings/strings (~> 1.20240722.0)
|
||||||
- abseil/time (~> 1.20240722.0)
|
- abseil/time (~> 1.20240722.0)
|
||||||
- abseil/types (~> 1.20240722.0)
|
- abseil/types (~> 1.20240722.0)
|
||||||
- FirebaseAppCheckInterop (~> 12.9.0)
|
- FirebaseAppCheckInterop (~> 12.8.0)
|
||||||
- FirebaseCore (~> 12.9.0)
|
- FirebaseCore (~> 12.8.0)
|
||||||
- "gRPC-C++ (~> 1.69.0)"
|
- "gRPC-C++ (~> 1.69.0)"
|
||||||
- gRPC-Core (~> 1.69.0)
|
- gRPC-Core (~> 1.69.0)
|
||||||
- leveldb-library (~> 1.22)
|
- leveldb-library (~> 1.22)
|
||||||
- nanopb (~> 3.30910.0)
|
- nanopb (~> 3.30910.0)
|
||||||
- FirebaseSharedSwift (12.9.0)
|
- FirebaseSharedSwift (12.8.0)
|
||||||
- Flutter (1.0.0)
|
- Flutter (1.0.0)
|
||||||
- GoogleUtilities/AppDelegateSwizzler (8.1.0):
|
|
||||||
- GoogleUtilities/Environment
|
|
||||||
- GoogleUtilities/Logger
|
|
||||||
- GoogleUtilities/Network
|
|
||||||
- GoogleUtilities/Privacy
|
|
||||||
- GoogleUtilities/Environment (8.1.0):
|
- GoogleUtilities/Environment (8.1.0):
|
||||||
- GoogleUtilities/Privacy
|
- GoogleUtilities/Privacy
|
||||||
- GoogleUtilities/Logger (8.1.0):
|
- GoogleUtilities/Logger (8.1.0):
|
||||||
- GoogleUtilities/Environment
|
- GoogleUtilities/Environment
|
||||||
- GoogleUtilities/Privacy
|
- GoogleUtilities/Privacy
|
||||||
- GoogleUtilities/Network (8.1.0):
|
|
||||||
- GoogleUtilities/Logger
|
|
||||||
- "GoogleUtilities/NSData+zlib"
|
|
||||||
- GoogleUtilities/Privacy
|
|
||||||
- GoogleUtilities/Reachability
|
|
||||||
- "GoogleUtilities/NSData+zlib (8.1.0)":
|
- "GoogleUtilities/NSData+zlib (8.1.0)":
|
||||||
- GoogleUtilities/Privacy
|
- GoogleUtilities/Privacy
|
||||||
- GoogleUtilities/Privacy (8.1.0)
|
- GoogleUtilities/Privacy (8.1.0)
|
||||||
- GoogleUtilities/Reachability (8.1.0):
|
|
||||||
- GoogleUtilities/Logger
|
|
||||||
- GoogleUtilities/Privacy
|
|
||||||
- GoogleUtilities/UserDefaults (8.1.0):
|
|
||||||
- GoogleUtilities/Logger
|
|
||||||
- GoogleUtilities/Privacy
|
|
||||||
- "gRPC-C++ (1.69.0)":
|
- "gRPC-C++ (1.69.0)":
|
||||||
- "gRPC-C++/Implementation (= 1.69.0)"
|
- "gRPC-C++/Implementation (= 1.69.0)"
|
||||||
- "gRPC-C++/Interface (= 1.69.0)"
|
- "gRPC-C++/Interface (= 1.69.0)"
|
||||||
|
|
@ -1389,15 +1341,12 @@ PODS:
|
||||||
- gRPC-Core/Privacy (= 1.69.0)
|
- gRPC-Core/Privacy (= 1.69.0)
|
||||||
- gRPC-Core/Interface (1.69.0)
|
- gRPC-Core/Interface (1.69.0)
|
||||||
- gRPC-Core/Privacy (1.69.0)
|
- gRPC-Core/Privacy (1.69.0)
|
||||||
- GTMSessionFetcher/Core (5.1.0)
|
|
||||||
- leveldb-library (1.22.6)
|
- leveldb-library (1.22.6)
|
||||||
- nanopb (3.30910.0):
|
- nanopb (3.30910.0):
|
||||||
- nanopb/decode (= 3.30910.0)
|
- nanopb/decode (= 3.30910.0)
|
||||||
- nanopb/encode (= 3.30910.0)
|
- nanopb/encode (= 3.30910.0)
|
||||||
- nanopb/decode (3.30910.0)
|
- nanopb/decode (3.30910.0)
|
||||||
- nanopb/encode (3.30910.0)
|
- nanopb/encode (3.30910.0)
|
||||||
- PromisesObjC (2.4.0)
|
|
||||||
- RecaptchaInterop (101.0.0)
|
|
||||||
- share_plus (0.0.1):
|
- share_plus (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- shared_preferences_foundation (0.0.1):
|
- shared_preferences_foundation (0.0.1):
|
||||||
|
|
@ -1408,8 +1357,6 @@ DEPENDENCIES:
|
||||||
- app_links (from `.symlinks/plugins/app_links/ios`)
|
- app_links (from `.symlinks/plugins/app_links/ios`)
|
||||||
- audioplayers_darwin (from `.symlinks/plugins/audioplayers_darwin/ios`)
|
- audioplayers_darwin (from `.symlinks/plugins/audioplayers_darwin/ios`)
|
||||||
- cloud_firestore (from `.symlinks/plugins/cloud_firestore/ios`)
|
- cloud_firestore (from `.symlinks/plugins/cloud_firestore/ios`)
|
||||||
- firebase_app_check (from `.symlinks/plugins/firebase_app_check/ios`)
|
|
||||||
- firebase_auth (from `.symlinks/plugins/firebase_auth/ios`)
|
|
||||||
- firebase_core (from `.symlinks/plugins/firebase_core/ios`)
|
- firebase_core (from `.symlinks/plugins/firebase_core/ios`)
|
||||||
- Flutter (from `Flutter`)
|
- Flutter (from `Flutter`)
|
||||||
- share_plus (from `.symlinks/plugins/share_plus/ios`)
|
- share_plus (from `.symlinks/plugins/share_plus/ios`)
|
||||||
|
|
@ -1418,13 +1365,9 @@ DEPENDENCIES:
|
||||||
SPEC REPOS:
|
SPEC REPOS:
|
||||||
trunk:
|
trunk:
|
||||||
- abseil
|
- abseil
|
||||||
- AppCheckCore
|
|
||||||
- BoringSSL-GRPC
|
- BoringSSL-GRPC
|
||||||
- Firebase
|
- Firebase
|
||||||
- FirebaseAppCheck
|
|
||||||
- FirebaseAppCheckInterop
|
- FirebaseAppCheckInterop
|
||||||
- FirebaseAuth
|
|
||||||
- FirebaseAuthInterop
|
|
||||||
- FirebaseCore
|
- FirebaseCore
|
||||||
- FirebaseCoreExtension
|
- FirebaseCoreExtension
|
||||||
- FirebaseCoreInternal
|
- FirebaseCoreInternal
|
||||||
|
|
@ -1434,11 +1377,8 @@ SPEC REPOS:
|
||||||
- GoogleUtilities
|
- GoogleUtilities
|
||||||
- "gRPC-C++"
|
- "gRPC-C++"
|
||||||
- gRPC-Core
|
- gRPC-Core
|
||||||
- GTMSessionFetcher
|
|
||||||
- leveldb-library
|
- leveldb-library
|
||||||
- nanopb
|
- nanopb
|
||||||
- PromisesObjC
|
|
||||||
- RecaptchaInterop
|
|
||||||
|
|
||||||
EXTERNAL SOURCES:
|
EXTERNAL SOURCES:
|
||||||
app_links:
|
app_links:
|
||||||
|
|
@ -1447,10 +1387,6 @@ EXTERNAL SOURCES:
|
||||||
:path: ".symlinks/plugins/audioplayers_darwin/ios"
|
:path: ".symlinks/plugins/audioplayers_darwin/ios"
|
||||||
cloud_firestore:
|
cloud_firestore:
|
||||||
:path: ".symlinks/plugins/cloud_firestore/ios"
|
:path: ".symlinks/plugins/cloud_firestore/ios"
|
||||||
firebase_app_check:
|
|
||||||
:path: ".symlinks/plugins/firebase_app_check/ios"
|
|
||||||
firebase_auth:
|
|
||||||
:path: ".symlinks/plugins/firebase_auth/ios"
|
|
||||||
firebase_core:
|
firebase_core:
|
||||||
:path: ".symlinks/plugins/firebase_core/ios"
|
:path: ".symlinks/plugins/firebase_core/ios"
|
||||||
Flutter:
|
Flutter:
|
||||||
|
|
@ -1463,33 +1399,24 @@ EXTERNAL SOURCES:
|
||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
abseil: a05cc83bf02079535e17169a73c5be5ba47f714b
|
abseil: a05cc83bf02079535e17169a73c5be5ba47f714b
|
||||||
app_links: a754cbec3c255bd4bbb4d236ecc06f28cd9a7ce8
|
app_links: a754cbec3c255bd4bbb4d236ecc06f28cd9a7ce8
|
||||||
AppCheckCore: cc8fd0a3a230ddd401f326489c99990b013f0c4f
|
|
||||||
audioplayers_darwin: ccf9c770ee768abb07e26d90af093f7bab1c12ab
|
audioplayers_darwin: ccf9c770ee768abb07e26d90af093f7bab1c12ab
|
||||||
BoringSSL-GRPC: dded2a44897e45f28f08ae87a55ee4bcd19bc508
|
BoringSSL-GRPC: dded2a44897e45f28f08ae87a55ee4bcd19bc508
|
||||||
cloud_firestore: 81f6c428ecee874dc3808afe0e0c48a87beb5bdf
|
cloud_firestore: 4bd00c3464706d9e09dabac0bb8e9610456109f5
|
||||||
Firebase: 065f2bb395062046623036d8e6dc857bc2521d56
|
Firebase: 9a58fdbc9d8655ed7b79a19cf9690bb007d3d46d
|
||||||
firebase_app_check: 33f1df6830ec8ebadee0db0120956c44a65c7213
|
firebase_core: ee30637e6744af8e0c12a6a1e8a9718506ec2398
|
||||||
firebase_auth: fecf9fe293464b52063f5f2a7110e63ff2ab3403
|
FirebaseAppCheckInterop: ba3dc604a89815379e61ec2365101608d365cf7d
|
||||||
firebase_core: afac1aac13c931e0401c7e74ed1276112030efab
|
FirebaseCore: 0dbad74bda10b8fb9ca34ad8f375fb9dd3ebef7c
|
||||||
FirebaseAppCheck: 94dae4d9bb682bdef85a778b0c1024a4613f1e89
|
FirebaseCoreExtension: 6605938d51f765d8b18bfcafd2085276a252bee2
|
||||||
FirebaseAppCheckInterop: 4bade10286cc977e516f75d2d8312cbdfa534789
|
FirebaseCoreInternal: fe5fa466aeb314787093a7dce9f0beeaad5a2a21
|
||||||
FirebaseAuth: 3a39f6436c21ebfd7919b698228b4f89ff94c23b
|
FirebaseFirestore: 67f23000ca238ccbab79127ed59636a9a2689e74
|
||||||
FirebaseAuthInterop: f8f6ff72dc24621906497fbe5cf3c42ee815e59c
|
FirebaseFirestoreInternal: a0e7382af3d208898dcd1d4d52d8a7870632e881
|
||||||
FirebaseCore: 428912f751178b06bef0a1793effeb4a5e09a9b8
|
FirebaseSharedSwift: f57ed48f4542b2d7eb4738f4f23ba443f78b3780
|
||||||
FirebaseCoreExtension: e911052d59cd0da237a45d706fc0f81654f035c1
|
|
||||||
FirebaseCoreInternal: b321eafae5362113bc182956fafc9922cfc77b72
|
|
||||||
FirebaseFirestore: d8b76ca1feb4ca0b0f078c45f7d1bd8014a49ef1
|
|
||||||
FirebaseFirestoreInternal: 02341a9ba87f6309227b04685022a5e16307bbf7
|
|
||||||
FirebaseSharedSwift: 9d2fa84a46676302b89dbd5e6e62bce2fe376909
|
|
||||||
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
|
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
|
||||||
GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1
|
GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1
|
||||||
"gRPC-C++": cc207623316fb041a7a3e774c252cf68a058b9e8
|
"gRPC-C++": cc207623316fb041a7a3e774c252cf68a058b9e8
|
||||||
gRPC-Core: 860978b7db482de8b4f5e10677216309b5ff6330
|
gRPC-Core: 860978b7db482de8b4f5e10677216309b5ff6330
|
||||||
GTMSessionFetcher: b8ab00db932816e14b0a0664a08cb73dda6d164b
|
|
||||||
leveldb-library: cc8b8f8e013647a295ad3f8cd2ddf49a6f19be19
|
leveldb-library: cc8b8f8e013647a295ad3f8cd2ddf49a6f19be19
|
||||||
nanopb: fad817b59e0457d11a5dfbde799381cd727c1275
|
nanopb: fad817b59e0457d11a5dfbde799381cd727c1275
|
||||||
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
|
|
||||||
RecaptchaInterop: 11e0b637842dfb48308d242afc3f448062325aba
|
|
||||||
share_plus: 50da8cb520a8f0f65671c6c6a99b3617ed10a58a
|
share_plus: 50da8cb520a8f0f65671c6c6a99b3617ed10a58a
|
||||||
shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb
|
shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,6 @@
|
||||||
331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
|
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
|
||||||
4867B86862DC650EC26D5F9C /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
4867B86862DC650EC26D5F9C /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
52CB81B72F635109004C3F43 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = "<group>"; };
|
|
||||||
5C30E1EF56D9EC1CAEADBE23 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
|
5C30E1EF56D9EC1CAEADBE23 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
|
||||||
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
|
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||||
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||||
|
|
@ -147,7 +146,6 @@
|
||||||
97C146F01CF9000F007C117D /* Runner */ = {
|
97C146F01CF9000F007C117D /* Runner */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
52CB81B72F635109004C3F43 /* Runner.entitlements */,
|
|
||||||
97C146FA1CF9000F007C117D /* Main.storyboard */,
|
97C146FA1CF9000F007C117D /* Main.storyboard */,
|
||||||
97C146FD1CF9000F007C117D /* Assets.xcassets */,
|
97C146FD1CF9000F007C117D /* Assets.xcassets */,
|
||||||
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
|
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
|
||||||
|
|
@ -475,9 +473,6 @@
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
|
||||||
CODE_SIGN_STYLE = Automatic;
|
|
||||||
CURRENT_PROJECT_VERSION = 3;
|
CURRENT_PROJECT_VERSION = 3;
|
||||||
DEVELOPMENT_TEAM = 2BX6QRR7GG;
|
DEVELOPMENT_TEAM = 2BX6QRR7GG;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
|
|
@ -490,7 +485,6 @@
|
||||||
MARKETING_VERSION = 1.0.2;
|
MARKETING_VERSION = 1.0.2;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.sanza.tetraq;
|
PRODUCT_BUNDLE_IDENTIFIER = com.sanza.tetraq;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
VERSIONING_SYSTEM = "apple-generic";
|
VERSIONING_SYSTEM = "apple-generic";
|
||||||
|
|
@ -506,7 +500,7 @@
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.amastra.tetraq.RunnerTests;
|
PRODUCT_BUNDLE_IDENTIFIER = com.sanza.tetraq.RunnerTests;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
|
|
@ -524,7 +518,7 @@
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.amastra.tetraq.RunnerTests;
|
PRODUCT_BUNDLE_IDENTIFIER = com.sanza.tetraq.RunnerTests;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
|
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
|
||||||
|
|
@ -540,7 +534,7 @@
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.amastra.tetraq.RunnerTests;
|
PRODUCT_BUNDLE_IDENTIFIER = com.sanza.tetraq.RunnerTests;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
|
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
|
||||||
|
|
@ -664,9 +658,6 @@
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
|
||||||
CODE_SIGN_STYLE = Automatic;
|
|
||||||
CURRENT_PROJECT_VERSION = 3;
|
CURRENT_PROJECT_VERSION = 3;
|
||||||
DEVELOPMENT_TEAM = 2BX6QRR7GG;
|
DEVELOPMENT_TEAM = 2BX6QRR7GG;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
|
|
@ -679,7 +670,6 @@
|
||||||
MARKETING_VERSION = 1.0.2;
|
MARKETING_VERSION = 1.0.2;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.sanza.tetraq;
|
PRODUCT_BUNDLE_IDENTIFIER = com.sanza.tetraq;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
|
|
@ -693,9 +683,6 @@
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
|
||||||
CODE_SIGN_STYLE = Automatic;
|
|
||||||
CURRENT_PROJECT_VERSION = 3;
|
CURRENT_PROJECT_VERSION = 3;
|
||||||
DEVELOPMENT_TEAM = 2BX6QRR7GG;
|
DEVELOPMENT_TEAM = 2BX6QRR7GG;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
|
|
@ -708,7 +695,6 @@
|
||||||
MARKETING_VERSION = 1.0.2;
|
MARKETING_VERSION = 1.0.2;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.sanza.tetraq;
|
PRODUCT_BUNDLE_IDENTIFIER = com.sanza.tetraq;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
VERSIONING_SYSTEM = "apple-generic";
|
VERSIONING_SYSTEM = "apple-generic";
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
<key>PLIST_VERSION</key>
|
<key>PLIST_VERSION</key>
|
||||||
<string>1</string>
|
<string>1</string>
|
||||||
<key>BUNDLE_ID</key>
|
<key>BUNDLE_ID</key>
|
||||||
<string>com.amastra.tetraq</string>
|
<string>com.sanza.tetraq</string>
|
||||||
<key>PROJECT_ID</key>
|
<key>PROJECT_ID</key>
|
||||||
<string>tetraq-32a4a</string>
|
<string>tetraq-32a4a</string>
|
||||||
<key>STORAGE_BUCKET</key>
|
<key>STORAGE_BUCKET</key>
|
||||||
|
|
@ -25,6 +25,6 @@
|
||||||
<key>IS_SIGNIN_ENABLED</key>
|
<key>IS_SIGNIN_ENABLED</key>
|
||||||
<true></true>
|
<true></true>
|
||||||
<key>GOOGLE_APP_ID</key>
|
<key>GOOGLE_APP_ID</key>
|
||||||
<string>1:705460445314:ios:54d64cb7592954327b949b</string>
|
<string>1:705460445314:ios:da11cbca5d1f6bc27b949b</string>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|
@ -2,8 +2,6 @@
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
|
||||||
<true/>
|
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||||
<key>CFBundleDisplayName</key>
|
<key>CFBundleDisplayName</key>
|
||||||
|
|
@ -22,25 +20,10 @@
|
||||||
<string>$(FLUTTER_BUILD_NAME)</string>
|
<string>$(FLUTTER_BUILD_NAME)</string>
|
||||||
<key>CFBundleSignature</key>
|
<key>CFBundleSignature</key>
|
||||||
<string>????</string>
|
<string>????</string>
|
||||||
<key>CFBundleURLTypes</key>
|
|
||||||
<array>
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleTypeRole</key>
|
|
||||||
<string>Editor</string>
|
|
||||||
<key>CFBundleURLName</key>
|
|
||||||
<string>com.sanza.tetraq</string>
|
|
||||||
<key>CFBundleURLSchemes</key>
|
|
||||||
<array>
|
|
||||||
<string>tetraq</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
</array>
|
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>$(FLUTTER_BUILD_NUMBER)</string>
|
<string>$(FLUTTER_BUILD_NUMBER)</string>
|
||||||
<key>LSRequiresIPhoneOS</key>
|
<key>LSRequiresIPhoneOS</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
|
||||||
<true/>
|
|
||||||
<key>UILaunchStoryboardName</key>
|
<key>UILaunchStoryboardName</key>
|
||||||
<string>LaunchScreen</string>
|
<string>LaunchScreen</string>
|
||||||
<key>UIMainStoryboardFile</key>
|
<key>UIMainStoryboardFile</key>
|
||||||
|
|
@ -58,5 +41,9 @@
|
||||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||||
</array>
|
</array>
|
||||||
|
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||||
|
<true/>
|
||||||
|
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||||
|
<true/>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
||||||
<plist version="1.0">
|
|
||||||
<dict>
|
|
||||||
<key>com.apple.developer.associated-domains</key>
|
|
||||||
<array>
|
|
||||||
<string>applinks:tetraq-32a4a.web.app</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
arb-dir: lib/l10n
|
|
||||||
template-arb-file: app_it.arb
|
|
||||||
output-localization-file: app_localizations.dart
|
|
||||||
output-dir: lib/l10n
|
|
||||||
BIN
lib/.DS_Store
vendored
BIN
lib/.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 { doodle, wood, cyberpunk, arcade, grimorio, music } // <-- Aggiunto 'music'
|
enum AppThemeType { minimal, doodle, cyberpunk, wood, arcade, grimorio }
|
||||||
|
|
||||||
class ThemeColors {
|
class ThemeColors {
|
||||||
final Color background;
|
final Color background;
|
||||||
|
|
@ -24,6 +24,11 @@ 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),
|
||||||
|
|
@ -49,23 +54,14 @@ class AppColors {
|
||||||
playerRed: Color(0xFFE91E63), playerBlue: Color(0xFF4FC3F7), text: Color(0xFFFFF3E0),
|
playerRed: Color(0xFFE91E63), playerBlue: Color(0xFF4FC3F7), text: Color(0xFFFFF3E0),
|
||||||
);
|
);
|
||||||
|
|
||||||
// --- NUOVO TEMA MUSICA ---
|
|
||||||
static const ThemeColors music = ThemeColors(
|
|
||||||
background: Color(0xFF120B29), // Viola scuro (stile Synthwave)
|
|
||||||
gridLine: Color(0xFF6A1B9A), // Viola elettrico
|
|
||||||
playerRed: Color(0xFFFF2A6D), // Rosa acceso
|
|
||||||
playerBlue: Color(0xFF05D5FF), // Ciano
|
|
||||||
text: Color(0xFFE0E0E0),
|
|
||||||
);
|
|
||||||
|
|
||||||
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.wood: return wood;
|
|
||||||
case AppThemeType.cyberpunk: return cyberpunk;
|
case AppThemeType.cyberpunk: return cyberpunk;
|
||||||
|
case AppThemeType.wood: return wood;
|
||||||
case AppThemeType.arcade: return arcade;
|
case AppThemeType.arcade: return arcade;
|
||||||
case AppThemeType.grimorio: return grimorio;
|
case AppThemeType.grimorio: return grimorio;
|
||||||
case AppThemeType.music: return music;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -73,66 +69,65 @@ 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;
|
||||||
case AppThemeType.arcade: return FontAwesomeIcons.coins;
|
case AppThemeType.arcade: return FontAwesomeIcons.coins;
|
||||||
case AppThemeType.grimorio: return FontAwesomeIcons.crown;
|
case AppThemeType.grimorio: return FontAwesomeIcons.crown;
|
||||||
case AppThemeType.music: return FontAwesomeIcons.compactDisc; // CD/Vinile per i punti
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
case AppThemeType.arcade: return FontAwesomeIcons.ghost;
|
case AppThemeType.arcade: return FontAwesomeIcons.ghost;
|
||||||
case AppThemeType.grimorio: return FontAwesomeIcons.hatWizard;
|
case AppThemeType.grimorio: return FontAwesomeIcons.hatWizard;
|
||||||
case AppThemeType.music: return FontAwesomeIcons.volumeXmark; // Muto/Errore per la bomba
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
case AppThemeType.arcade: return FontAwesomeIcons.shuffle;
|
case AppThemeType.arcade: return FontAwesomeIcons.shuffle;
|
||||||
case AppThemeType.grimorio: return FontAwesomeIcons.hurricane;
|
case AppThemeType.grimorio: return FontAwesomeIcons.hurricane;
|
||||||
case AppThemeType.music: return FontAwesomeIcons.sliders; // Fader da DJ
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
case AppThemeType.arcade: return FontAwesomeIcons.gamepad;
|
case AppThemeType.arcade: return FontAwesomeIcons.gamepad;
|
||||||
case AppThemeType.grimorio: return FontAwesomeIcons.masksTheater;
|
case AppThemeType.grimorio: return FontAwesomeIcons.masksTheater;
|
||||||
case AppThemeType.music: return FontAwesomeIcons.headphones; // Cuffie per il Jolly
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
case AppThemeType.arcade: return FontAwesomeIcons.powerOff;
|
case AppThemeType.arcade: return FontAwesomeIcons.powerOff;
|
||||||
case AppThemeType.grimorio: return FontAwesomeIcons.meteor;
|
case AppThemeType.grimorio: return FontAwesomeIcons.meteor;
|
||||||
case AppThemeType.music: return FontAwesomeIcons.pause; // Pausa per il blocco vuoto
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- NUOVE ICONE ---
|
||||||
static IconData ice(AppThemeType type) {
|
static IconData ice(AppThemeType type) {
|
||||||
if (type == AppThemeType.music) return FontAwesomeIcons.music; // Nota musicale ghiacciata
|
|
||||||
return FontAwesomeIcons.snowflake;
|
return FontAwesomeIcons.snowflake;
|
||||||
}
|
}
|
||||||
|
|
||||||
static IconData multiplier(AppThemeType type) {
|
static IconData multiplier(AppThemeType type) {
|
||||||
if (type == AppThemeType.music) return FontAwesomeIcons.forwardFast; // Fast Forward per il x2
|
|
||||||
return FontAwesomeIcons.bolt;
|
return FontAwesomeIcons.bolt;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,11 +1,6 @@
|
||||||
// ===========================================================================
|
|
||||||
// FILE: lib/core/theme_manager.dart
|
|
||||||
// ===========================================================================
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'app_colors.dart';
|
import 'app_colors.dart';
|
||||||
import '../services/storage_service.dart';
|
import '../services/storage_service.dart';
|
||||||
import '../services/audio_service.dart'; // <-- NUOVO IMPORT PER LA MUSICA
|
|
||||||
|
|
||||||
class ThemeManager extends ChangeNotifier {
|
class ThemeManager extends ChangeNotifier {
|
||||||
late AppThemeType _currentThemeType;
|
late AppThemeType _currentThemeType;
|
||||||
|
|
@ -13,9 +8,6 @@ class ThemeManager extends ChangeNotifier {
|
||||||
ThemeManager() {
|
ThemeManager() {
|
||||||
// Quando l'app parte, legge il tema dalla memoria!
|
// Quando l'app parte, legge il tema dalla memoria!
|
||||||
_currentThemeType = AppThemeType.values[StorageService.instance.savedThemeIndex];
|
_currentThemeType = AppThemeType.values[StorageService.instance.savedThemeIndex];
|
||||||
|
|
||||||
// Fai partire subito la colonna sonora del tema salvato!
|
|
||||||
AudioService.instance.playBgm(_currentThemeType);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AppThemeType get currentThemeType => _currentThemeType;
|
AppThemeType get currentThemeType => _currentThemeType;
|
||||||
|
|
@ -24,10 +16,6 @@ class ThemeManager extends ChangeNotifier {
|
||||||
void setTheme(AppThemeType type) {
|
void setTheme(AppThemeType type) {
|
||||||
_currentThemeType = type;
|
_currentThemeType = type;
|
||||||
StorageService.instance.saveTheme(type); // Salva la scelta nel "disco fisso"
|
StorageService.instance.saveTheme(type); // Salva la scelta nel "disco fisso"
|
||||||
|
|
||||||
// Cambia magicamente la canzone in sottofondo!
|
|
||||||
AudioService.instance.playBgm(type);
|
|
||||||
|
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -48,7 +48,7 @@ class DefaultFirebaseOptions {
|
||||||
|
|
||||||
static const FirebaseOptions android = FirebaseOptions(
|
static const FirebaseOptions android = FirebaseOptions(
|
||||||
apiKey: 'AIzaSyBsXO595xVITDPrRnXrW8HPQLOe7Rz4Gg4',
|
apiKey: 'AIzaSyBsXO595xVITDPrRnXrW8HPQLOe7Rz4Gg4',
|
||||||
appId: '1:705460445314:android:ceac21bb06b7a9f07b949b',
|
appId: '1:705460445314:android:4d35fef29cfd63727b949b',
|
||||||
messagingSenderId: '705460445314',
|
messagingSenderId: '705460445314',
|
||||||
projectId: 'tetraq-32a4a',
|
projectId: 'tetraq-32a4a',
|
||||||
storageBucket: 'tetraq-32a4a.firebasestorage.app',
|
storageBucket: 'tetraq-32a4a.firebasestorage.app',
|
||||||
|
|
@ -56,11 +56,11 @@ class DefaultFirebaseOptions {
|
||||||
|
|
||||||
static const FirebaseOptions ios = FirebaseOptions(
|
static const FirebaseOptions ios = FirebaseOptions(
|
||||||
apiKey: 'AIzaSyB77j18Jgeb9gBAEwp-uyOQvr4m-RJ_rAE',
|
apiKey: 'AIzaSyB77j18Jgeb9gBAEwp-uyOQvr4m-RJ_rAE',
|
||||||
appId: '1:705460445314:ios:54d64cb7592954327b949b',
|
appId: '1:705460445314:ios:da11cbca5d1f6bc27b949b',
|
||||||
messagingSenderId: '705460445314',
|
messagingSenderId: '705460445314',
|
||||||
projectId: 'tetraq-32a4a',
|
projectId: 'tetraq-32a4a',
|
||||||
storageBucket: 'tetraq-32a4a.firebasestorage.app',
|
storageBucket: 'tetraq-32a4a.firebasestorage.app',
|
||||||
iosBundleId: 'com.amastra.tetraq',
|
iosBundleId: 'com.sanza.tetraq',
|
||||||
);
|
);
|
||||||
|
|
||||||
static const FirebaseOptions macos = FirebaseOptions(
|
static const FirebaseOptions macos = FirebaseOptions(
|
||||||
|
|
@ -71,5 +71,4 @@ class DefaultFirebaseOptions {
|
||||||
storageBucket: 'tetraq-32a4a.firebasestorage.app',
|
storageBucket: 'tetraq-32a4a.firebasestorage.app',
|
||||||
iosBundleId: 'com.sanza.tetraq',
|
iosBundleId: 'com.sanza.tetraq',
|
||||||
);
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
{
|
|
||||||
"@@locale": "de",
|
|
||||||
"appTitle": "TetraQ",
|
|
||||||
"welcomeTitle": "WILLKOMMEN BEI TETRAQ!",
|
|
||||||
"nameHint": "NAME",
|
|
||||||
"saveAndPlay": "SPEICHERN & SPIELEN",
|
|
||||||
"onlineTitle": "ONLINE",
|
|
||||||
"onlineSub": "Fordere die Welt heraus",
|
|
||||||
"cpuTitle": "VS CPU",
|
|
||||||
"cpuSub": "Trainiere mit KI",
|
|
||||||
"localTitle": "LOKAL",
|
|
||||||
"localSub": "Gleicher Bildschirm",
|
|
||||||
"leaderboardTitle": "RANGLISTE",
|
|
||||||
"questsTitle": "MISSIONEN",
|
|
||||||
"themesTitle": "THEMEN",
|
|
||||||
"tutorialTitle": "TUTORIAL",
|
|
||||||
"startGame": "SPIEL STARTEN",
|
|
||||||
"createMatch": "SPIEL ERSTELLEN",
|
|
||||||
"joinMatch": "BEITRETEN",
|
|
||||||
"gameOver": "SPIELENDE",
|
|
||||||
"mainMenu": "ZURÜCK ZUM MENÜ",
|
|
||||||
"exit": "BEENDEN"
|
|
||||||
}
|
|
||||||
|
|
@ -1,23 +1,4 @@
|
||||||
{
|
{
|
||||||
"@@locale": "en",
|
|
||||||
"appTitle": "TetraQ",
|
"appTitle": "TetraQ",
|
||||||
"welcomeTitle": "WELCOME TO TETRAQ!",
|
"playLocal": "PASS & PLAY (Local)"
|
||||||
"nameHint": "NAME",
|
|
||||||
"saveAndPlay": "SAVE & PLAY",
|
|
||||||
"onlineTitle": "ONLINE",
|
|
||||||
"onlineSub": "Challenge the world",
|
|
||||||
"cpuTitle": "VS CPU",
|
|
||||||
"cpuSub": "Train with AI",
|
|
||||||
"localTitle": "LOCAL",
|
|
||||||
"localSub": "Same screen",
|
|
||||||
"leaderboardTitle": "LEADERBOARD",
|
|
||||||
"questsTitle": "QUESTS",
|
|
||||||
"themesTitle": "THEMES",
|
|
||||||
"tutorialTitle": "TUTORIAL",
|
|
||||||
"startGame": "START GAME",
|
|
||||||
"createMatch": "CREATE MATCH",
|
|
||||||
"joinMatch": "JOIN",
|
|
||||||
"gameOver": "GAME OVER",
|
|
||||||
"mainMenu": "BACK TO MENU",
|
|
||||||
"exit": "EXIT"
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
{
|
|
||||||
"@@locale": "es",
|
|
||||||
"appTitle": "TetraQ",
|
|
||||||
"welcomeTitle": "¡BIENVENIDO A TETRAQ!",
|
|
||||||
"nameHint": "NOMBRE",
|
|
||||||
"saveAndPlay": "GUARDAR Y JUGAR",
|
|
||||||
"onlineTitle": "ONLINE",
|
|
||||||
"onlineSub": "Desafía al mundo",
|
|
||||||
"cpuTitle": "VS CPU",
|
|
||||||
"cpuSub": "Entrena con IA",
|
|
||||||
"localTitle": "LOCAL",
|
|
||||||
"localSub": "Misma pantalla",
|
|
||||||
"leaderboardTitle": "RANKING",
|
|
||||||
"questsTitle": "MISIONES",
|
|
||||||
"themesTitle": "TEMAS",
|
|
||||||
"tutorialTitle": "TUTORIAL",
|
|
||||||
"startGame": "INICIAR JUEGO",
|
|
||||||
"createMatch": "CREAR PARTIDA",
|
|
||||||
"joinMatch": "UNIRSE",
|
|
||||||
"gameOver": "FIN DEL JUEGO",
|
|
||||||
"mainMenu": "VOLVER AL MENÚ",
|
|
||||||
"exit": "SALIR"
|
|
||||||
}
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
{
|
|
||||||
"@@locale": "fr",
|
|
||||||
"appTitle": "TetraQ",
|
|
||||||
"welcomeTitle": "BIENVENUE DANS TETRAQ !",
|
|
||||||
"nameHint": "NOM",
|
|
||||||
"saveAndPlay": "SAUVEGARDER ET JOUER",
|
|
||||||
"onlineTitle": "EN LIGNE",
|
|
||||||
"onlineSub": "Défiez le monde",
|
|
||||||
"cpuTitle": "VS CPU",
|
|
||||||
"cpuSub": "Entraînez avec l'IA",
|
|
||||||
"localTitle": "LOCAL",
|
|
||||||
"localSub": "Même écran",
|
|
||||||
"leaderboardTitle": "CLASSEMENT",
|
|
||||||
"questsTitle": "QUÊTES",
|
|
||||||
"themesTitle": "THÈMES",
|
|
||||||
"tutorialTitle": "TUTORIEL",
|
|
||||||
"startGame": "JOUER",
|
|
||||||
"createMatch": "CRÉER UN MATCH",
|
|
||||||
"joinMatch": "REJOINDRE",
|
|
||||||
"gameOver": "FIN DE PARTIE",
|
|
||||||
"mainMenu": "RETOUR AU MENU",
|
|
||||||
"exit": "QUITTER"
|
|
||||||
}
|
|
||||||
|
|
@ -1,23 +1,4 @@
|
||||||
{
|
{
|
||||||
"@@locale": "it",
|
|
||||||
"appTitle": "TetraQ",
|
"appTitle": "TetraQ",
|
||||||
"welcomeTitle": "BENVENUTO IN TETRAQ!",
|
"playLocal": "PASS & PLAY (Locale)"
|
||||||
"nameHint": "NOME",
|
|
||||||
"saveAndPlay": "SALVA E GIOCA",
|
|
||||||
"onlineTitle": "ONLINE",
|
|
||||||
"onlineSub": "Sfida il mondo",
|
|
||||||
"cpuTitle": "VS CPU",
|
|
||||||
"cpuSub": "Allenati con l'IA",
|
|
||||||
"localTitle": "LOCALE",
|
|
||||||
"localSub": "Stesso schermo",
|
|
||||||
"leaderboardTitle": "CLASSIFICA",
|
|
||||||
"questsTitle": "SFIDE",
|
|
||||||
"themesTitle": "TEMI",
|
|
||||||
"tutorialTitle": "TUTORIAL",
|
|
||||||
"startGame": "AVVIA PARTITA",
|
|
||||||
"createMatch": "CREA PARTITA",
|
|
||||||
"joinMatch": "UNISCITI",
|
|
||||||
"gameOver": "FINE PARTITA",
|
|
||||||
"mainMenu": "TORNA AL MENU",
|
|
||||||
"exit": "ESCI"
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,286 +0,0 @@
|
||||||
import 'dart:async';
|
|
||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:flutter/widgets.dart';
|
|
||||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
|
||||||
import 'package:intl/intl.dart' as intl;
|
|
||||||
|
|
||||||
import 'app_localizations_de.dart';
|
|
||||||
import 'app_localizations_en.dart';
|
|
||||||
import 'app_localizations_es.dart';
|
|
||||||
import 'app_localizations_fr.dart';
|
|
||||||
import 'app_localizations_it.dart';
|
|
||||||
import 'app_localizations_pt.dart';
|
|
||||||
import 'app_localizations_ru.dart';
|
|
||||||
import 'app_localizations_zh.dart';
|
|
||||||
|
|
||||||
// ignore_for_file: type=lint
|
|
||||||
|
|
||||||
/// Callers can lookup localized strings with an instance of AppLocalizations
|
|
||||||
/// returned by `AppLocalizations.of(context)`.
|
|
||||||
///
|
|
||||||
/// Applications need to include `AppLocalizations.delegate()` in their app's
|
|
||||||
/// `localizationDelegates` list, and the locales they support in the app's
|
|
||||||
/// `supportedLocales` list. For example:
|
|
||||||
///
|
|
||||||
/// ```dart
|
|
||||||
/// import 'l10n/app_localizations.dart';
|
|
||||||
///
|
|
||||||
/// return MaterialApp(
|
|
||||||
/// localizationsDelegates: AppLocalizations.localizationsDelegates,
|
|
||||||
/// supportedLocales: AppLocalizations.supportedLocales,
|
|
||||||
/// home: MyApplicationHome(),
|
|
||||||
/// );
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// ## Update pubspec.yaml
|
|
||||||
///
|
|
||||||
/// Please make sure to update your pubspec.yaml to include the following
|
|
||||||
/// packages:
|
|
||||||
///
|
|
||||||
/// ```yaml
|
|
||||||
/// dependencies:
|
|
||||||
/// # Internationalization support.
|
|
||||||
/// flutter_localizations:
|
|
||||||
/// sdk: flutter
|
|
||||||
/// intl: any # Use the pinned version from flutter_localizations
|
|
||||||
///
|
|
||||||
/// # Rest of dependencies
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// ## iOS Applications
|
|
||||||
///
|
|
||||||
/// iOS applications define key application metadata, including supported
|
|
||||||
/// locales, in an Info.plist file that is built into the application bundle.
|
|
||||||
/// To configure the locales supported by your app, you’ll need to edit this
|
|
||||||
/// file.
|
|
||||||
///
|
|
||||||
/// First, open your project’s ios/Runner.xcworkspace Xcode workspace file.
|
|
||||||
/// Then, in the Project Navigator, open the Info.plist file under the Runner
|
|
||||||
/// project’s Runner folder.
|
|
||||||
///
|
|
||||||
/// Next, select the Information Property List item, select Add Item from the
|
|
||||||
/// Editor menu, then select Localizations from the pop-up menu.
|
|
||||||
///
|
|
||||||
/// Select and expand the newly-created Localizations item then, for each
|
|
||||||
/// locale your application supports, add a new item and select the locale
|
|
||||||
/// you wish to add from the pop-up menu in the Value field. This list should
|
|
||||||
/// be consistent with the languages listed in the AppLocalizations.supportedLocales
|
|
||||||
/// property.
|
|
||||||
abstract class AppLocalizations {
|
|
||||||
AppLocalizations(String locale)
|
|
||||||
: localeName = intl.Intl.canonicalizedLocale(locale.toString());
|
|
||||||
|
|
||||||
final String localeName;
|
|
||||||
|
|
||||||
static AppLocalizations? of(BuildContext context) {
|
|
||||||
return Localizations.of<AppLocalizations>(context, AppLocalizations);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const LocalizationsDelegate<AppLocalizations> delegate =
|
|
||||||
_AppLocalizationsDelegate();
|
|
||||||
|
|
||||||
/// A list of this localizations delegate along with the default localizations
|
|
||||||
/// delegates.
|
|
||||||
///
|
|
||||||
/// Returns a list of localizations delegates containing this delegate along with
|
|
||||||
/// GlobalMaterialLocalizations.delegate, GlobalCupertinoLocalizations.delegate,
|
|
||||||
/// and GlobalWidgetsLocalizations.delegate.
|
|
||||||
///
|
|
||||||
/// Additional delegates can be added by appending to this list in
|
|
||||||
/// MaterialApp. This list does not have to be used at all if a custom list
|
|
||||||
/// of delegates is preferred or required.
|
|
||||||
static const List<LocalizationsDelegate<dynamic>> localizationsDelegates =
|
|
||||||
<LocalizationsDelegate<dynamic>>[
|
|
||||||
delegate,
|
|
||||||
GlobalMaterialLocalizations.delegate,
|
|
||||||
GlobalCupertinoLocalizations.delegate,
|
|
||||||
GlobalWidgetsLocalizations.delegate,
|
|
||||||
];
|
|
||||||
|
|
||||||
/// A list of this localizations delegate's supported locales.
|
|
||||||
static const List<Locale> supportedLocales = <Locale>[
|
|
||||||
Locale('de'),
|
|
||||||
Locale('en'),
|
|
||||||
Locale('es'),
|
|
||||||
Locale('fr'),
|
|
||||||
Locale('it'),
|
|
||||||
Locale('pt'),
|
|
||||||
Locale('ru'),
|
|
||||||
Locale('zh'),
|
|
||||||
];
|
|
||||||
|
|
||||||
/// No description provided for @appTitle.
|
|
||||||
///
|
|
||||||
/// In it, this message translates to:
|
|
||||||
/// **'TetraQ'**
|
|
||||||
String get appTitle;
|
|
||||||
|
|
||||||
/// No description provided for @welcomeTitle.
|
|
||||||
///
|
|
||||||
/// In it, this message translates to:
|
|
||||||
/// **'BENVENUTO IN TETRAQ!'**
|
|
||||||
String get welcomeTitle;
|
|
||||||
|
|
||||||
/// No description provided for @nameHint.
|
|
||||||
///
|
|
||||||
/// In it, this message translates to:
|
|
||||||
/// **'NOME'**
|
|
||||||
String get nameHint;
|
|
||||||
|
|
||||||
/// No description provided for @saveAndPlay.
|
|
||||||
///
|
|
||||||
/// In it, this message translates to:
|
|
||||||
/// **'SALVA E GIOCA'**
|
|
||||||
String get saveAndPlay;
|
|
||||||
|
|
||||||
/// No description provided for @onlineTitle.
|
|
||||||
///
|
|
||||||
/// In it, this message translates to:
|
|
||||||
/// **'ONLINE'**
|
|
||||||
String get onlineTitle;
|
|
||||||
|
|
||||||
/// No description provided for @onlineSub.
|
|
||||||
///
|
|
||||||
/// In it, this message translates to:
|
|
||||||
/// **'Sfida il mondo'**
|
|
||||||
String get onlineSub;
|
|
||||||
|
|
||||||
/// No description provided for @cpuTitle.
|
|
||||||
///
|
|
||||||
/// In it, this message translates to:
|
|
||||||
/// **'VS CPU'**
|
|
||||||
String get cpuTitle;
|
|
||||||
|
|
||||||
/// No description provided for @cpuSub.
|
|
||||||
///
|
|
||||||
/// In it, this message translates to:
|
|
||||||
/// **'Allenati con l\'IA'**
|
|
||||||
String get cpuSub;
|
|
||||||
|
|
||||||
/// No description provided for @localTitle.
|
|
||||||
///
|
|
||||||
/// In it, this message translates to:
|
|
||||||
/// **'LOCALE'**
|
|
||||||
String get localTitle;
|
|
||||||
|
|
||||||
/// No description provided for @localSub.
|
|
||||||
///
|
|
||||||
/// In it, this message translates to:
|
|
||||||
/// **'Stesso schermo'**
|
|
||||||
String get localSub;
|
|
||||||
|
|
||||||
/// No description provided for @leaderboardTitle.
|
|
||||||
///
|
|
||||||
/// In it, this message translates to:
|
|
||||||
/// **'CLASSIFICA'**
|
|
||||||
String get leaderboardTitle;
|
|
||||||
|
|
||||||
/// No description provided for @questsTitle.
|
|
||||||
///
|
|
||||||
/// In it, this message translates to:
|
|
||||||
/// **'SFIDE'**
|
|
||||||
String get questsTitle;
|
|
||||||
|
|
||||||
/// No description provided for @themesTitle.
|
|
||||||
///
|
|
||||||
/// In it, this message translates to:
|
|
||||||
/// **'TEMI'**
|
|
||||||
String get themesTitle;
|
|
||||||
|
|
||||||
/// No description provided for @tutorialTitle.
|
|
||||||
///
|
|
||||||
/// In it, this message translates to:
|
|
||||||
/// **'TUTORIAL'**
|
|
||||||
String get tutorialTitle;
|
|
||||||
|
|
||||||
/// No description provided for @startGame.
|
|
||||||
///
|
|
||||||
/// In it, this message translates to:
|
|
||||||
/// **'AVVIA PARTITA'**
|
|
||||||
String get startGame;
|
|
||||||
|
|
||||||
/// No description provided for @createMatch.
|
|
||||||
///
|
|
||||||
/// In it, this message translates to:
|
|
||||||
/// **'CREA PARTITA'**
|
|
||||||
String get createMatch;
|
|
||||||
|
|
||||||
/// No description provided for @joinMatch.
|
|
||||||
///
|
|
||||||
/// In it, this message translates to:
|
|
||||||
/// **'UNISCITI'**
|
|
||||||
String get joinMatch;
|
|
||||||
|
|
||||||
/// No description provided for @gameOver.
|
|
||||||
///
|
|
||||||
/// In it, this message translates to:
|
|
||||||
/// **'FINE PARTITA'**
|
|
||||||
String get gameOver;
|
|
||||||
|
|
||||||
/// No description provided for @mainMenu.
|
|
||||||
///
|
|
||||||
/// In it, this message translates to:
|
|
||||||
/// **'TORNA AL MENU'**
|
|
||||||
String get mainMenu;
|
|
||||||
|
|
||||||
/// No description provided for @exit.
|
|
||||||
///
|
|
||||||
/// In it, this message translates to:
|
|
||||||
/// **'ESCI'**
|
|
||||||
String get exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
class _AppLocalizationsDelegate
|
|
||||||
extends LocalizationsDelegate<AppLocalizations> {
|
|
||||||
const _AppLocalizationsDelegate();
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<AppLocalizations> load(Locale locale) {
|
|
||||||
return SynchronousFuture<AppLocalizations>(lookupAppLocalizations(locale));
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool isSupported(Locale locale) => <String>[
|
|
||||||
'de',
|
|
||||||
'en',
|
|
||||||
'es',
|
|
||||||
'fr',
|
|
||||||
'it',
|
|
||||||
'pt',
|
|
||||||
'ru',
|
|
||||||
'zh',
|
|
||||||
].contains(locale.languageCode);
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool shouldReload(_AppLocalizationsDelegate old) => false;
|
|
||||||
}
|
|
||||||
|
|
||||||
AppLocalizations lookupAppLocalizations(Locale locale) {
|
|
||||||
// Lookup logic when only language code is specified.
|
|
||||||
switch (locale.languageCode) {
|
|
||||||
case 'de':
|
|
||||||
return AppLocalizationsDe();
|
|
||||||
case 'en':
|
|
||||||
return AppLocalizationsEn();
|
|
||||||
case 'es':
|
|
||||||
return AppLocalizationsEs();
|
|
||||||
case 'fr':
|
|
||||||
return AppLocalizationsFr();
|
|
||||||
case 'it':
|
|
||||||
return AppLocalizationsIt();
|
|
||||||
case 'pt':
|
|
||||||
return AppLocalizationsPt();
|
|
||||||
case 'ru':
|
|
||||||
return AppLocalizationsRu();
|
|
||||||
case 'zh':
|
|
||||||
return AppLocalizationsZh();
|
|
||||||
}
|
|
||||||
|
|
||||||
throw FlutterError(
|
|
||||||
'AppLocalizations.delegate failed to load unsupported locale "$locale". This is likely '
|
|
||||||
'an issue with the localizations generation tool. Please file an issue '
|
|
||||||
'on GitHub with a reproducible sample app and the gen-l10n configuration '
|
|
||||||
'that was used.',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,70 +0,0 @@
|
||||||
// ignore: unused_import
|
|
||||||
import 'package:intl/intl.dart' as intl;
|
|
||||||
import 'app_localizations.dart';
|
|
||||||
|
|
||||||
// ignore_for_file: type=lint
|
|
||||||
|
|
||||||
/// The translations for German (`de`).
|
|
||||||
class AppLocalizationsDe extends AppLocalizations {
|
|
||||||
AppLocalizationsDe([String locale = 'de']) : super(locale);
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get appTitle => 'TetraQ';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get welcomeTitle => 'WILLKOMMEN BEI TETRAQ!';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get nameHint => 'NAME';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get saveAndPlay => 'SPEICHERN & SPIELEN';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get onlineTitle => 'ONLINE';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get onlineSub => 'Fordere die Welt heraus';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get cpuTitle => 'VS CPU';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get cpuSub => 'Trainiere mit KI';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get localTitle => 'LOKAL';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get localSub => 'Gleicher Bildschirm';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get leaderboardTitle => 'RANGLISTE';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get questsTitle => 'MISSIONEN';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get themesTitle => 'THEMEN';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get tutorialTitle => 'TUTORIAL';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get startGame => 'SPIEL STARTEN';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get createMatch => 'SPIEL ERSTELLEN';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get joinMatch => 'BEITRETEN';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get gameOver => 'SPIELENDE';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get mainMenu => 'ZURÜCK ZUM MENÜ';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get exit => 'BEENDEN';
|
|
||||||
}
|
|
||||||
|
|
@ -1,70 +0,0 @@
|
||||||
// ignore: unused_import
|
|
||||||
import 'package:intl/intl.dart' as intl;
|
|
||||||
import 'app_localizations.dart';
|
|
||||||
|
|
||||||
// ignore_for_file: type=lint
|
|
||||||
|
|
||||||
/// The translations for English (`en`).
|
|
||||||
class AppLocalizationsEn extends AppLocalizations {
|
|
||||||
AppLocalizationsEn([String locale = 'en']) : super(locale);
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get appTitle => 'TetraQ';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get welcomeTitle => 'WELCOME TO TETRAQ!';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get nameHint => 'NAME';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get saveAndPlay => 'SAVE & PLAY';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get onlineTitle => 'ONLINE';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get onlineSub => 'Challenge the world';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get cpuTitle => 'VS CPU';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get cpuSub => 'Train with AI';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get localTitle => 'LOCAL';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get localSub => 'Same screen';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get leaderboardTitle => 'LEADERBOARD';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get questsTitle => 'QUESTS';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get themesTitle => 'THEMES';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get tutorialTitle => 'TUTORIAL';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get startGame => 'START GAME';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get createMatch => 'CREATE MATCH';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get joinMatch => 'JOIN';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get gameOver => 'GAME OVER';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get mainMenu => 'BACK TO MENU';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get exit => 'EXIT';
|
|
||||||
}
|
|
||||||
|
|
@ -1,70 +0,0 @@
|
||||||
// ignore: unused_import
|
|
||||||
import 'package:intl/intl.dart' as intl;
|
|
||||||
import 'app_localizations.dart';
|
|
||||||
|
|
||||||
// ignore_for_file: type=lint
|
|
||||||
|
|
||||||
/// The translations for Spanish Castilian (`es`).
|
|
||||||
class AppLocalizationsEs extends AppLocalizations {
|
|
||||||
AppLocalizationsEs([String locale = 'es']) : super(locale);
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get appTitle => 'TetraQ';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get welcomeTitle => '¡BIENVENIDO A TETRAQ!';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get nameHint => 'NOMBRE';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get saveAndPlay => 'GUARDAR Y JUGAR';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get onlineTitle => 'ONLINE';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get onlineSub => 'Desafía al mundo';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get cpuTitle => 'VS CPU';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get cpuSub => 'Entrena con IA';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get localTitle => 'LOCAL';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get localSub => 'Misma pantalla';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get leaderboardTitle => 'RANKING';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get questsTitle => 'MISIONES';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get themesTitle => 'TEMAS';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get tutorialTitle => 'TUTORIAL';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get startGame => 'INICIAR JUEGO';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get createMatch => 'CREAR PARTIDA';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get joinMatch => 'UNIRSE';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get gameOver => 'FIN DEL JUEGO';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get mainMenu => 'VOLVER AL MENÚ';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get exit => 'SALIR';
|
|
||||||
}
|
|
||||||
|
|
@ -1,70 +0,0 @@
|
||||||
// ignore: unused_import
|
|
||||||
import 'package:intl/intl.dart' as intl;
|
|
||||||
import 'app_localizations.dart';
|
|
||||||
|
|
||||||
// ignore_for_file: type=lint
|
|
||||||
|
|
||||||
/// The translations for French (`fr`).
|
|
||||||
class AppLocalizationsFr extends AppLocalizations {
|
|
||||||
AppLocalizationsFr([String locale = 'fr']) : super(locale);
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get appTitle => 'TetraQ';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get welcomeTitle => 'BIENVENUE DANS TETRAQ !';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get nameHint => 'NOM';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get saveAndPlay => 'SAUVEGARDER ET JOUER';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get onlineTitle => 'EN LIGNE';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get onlineSub => 'Défiez le monde';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get cpuTitle => 'VS CPU';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get cpuSub => 'Entraînez avec l\'IA';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get localTitle => 'LOCAL';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get localSub => 'Même écran';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get leaderboardTitle => 'CLASSEMENT';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get questsTitle => 'QUÊTES';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get themesTitle => 'THÈMES';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get tutorialTitle => 'TUTORIEL';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get startGame => 'JOUER';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get createMatch => 'CRÉER UN MATCH';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get joinMatch => 'REJOINDRE';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get gameOver => 'FIN DE PARTIE';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get mainMenu => 'RETOUR AU MENU';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get exit => 'QUITTER';
|
|
||||||
}
|
|
||||||
|
|
@ -1,70 +0,0 @@
|
||||||
// ignore: unused_import
|
|
||||||
import 'package:intl/intl.dart' as intl;
|
|
||||||
import 'app_localizations.dart';
|
|
||||||
|
|
||||||
// ignore_for_file: type=lint
|
|
||||||
|
|
||||||
/// The translations for Italian (`it`).
|
|
||||||
class AppLocalizationsIt extends AppLocalizations {
|
|
||||||
AppLocalizationsIt([String locale = 'it']) : super(locale);
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get appTitle => 'TetraQ';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get welcomeTitle => 'BENVENUTO IN TETRAQ!';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get nameHint => 'NOME';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get saveAndPlay => 'SALVA E GIOCA';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get onlineTitle => 'ONLINE';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get onlineSub => 'Sfida il mondo';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get cpuTitle => 'VS CPU';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get cpuSub => 'Allenati con l\'IA';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get localTitle => 'LOCALE';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get localSub => 'Stesso schermo';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get leaderboardTitle => 'CLASSIFICA';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get questsTitle => 'SFIDE';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get themesTitle => 'TEMI';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get tutorialTitle => 'TUTORIAL';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get startGame => 'AVVIA PARTITA';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get createMatch => 'CREA PARTITA';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get joinMatch => 'UNISCITI';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get gameOver => 'FINE PARTITA';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get mainMenu => 'TORNA AL MENU';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get exit => 'ESCI';
|
|
||||||
}
|
|
||||||
|
|
@ -1,70 +0,0 @@
|
||||||
// ignore: unused_import
|
|
||||||
import 'package:intl/intl.dart' as intl;
|
|
||||||
import 'app_localizations.dart';
|
|
||||||
|
|
||||||
// ignore_for_file: type=lint
|
|
||||||
|
|
||||||
/// The translations for Portuguese (`pt`).
|
|
||||||
class AppLocalizationsPt extends AppLocalizations {
|
|
||||||
AppLocalizationsPt([String locale = 'pt']) : super(locale);
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get appTitle => 'TetraQ';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get welcomeTitle => 'BEM-VINDO AO TETRAQ!';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get nameHint => 'NOME';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get saveAndPlay => 'SALVAR E JOGAR';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get onlineTitle => 'ONLINE';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get onlineSub => 'Desafie o mundo';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get cpuTitle => 'VS CPU';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get cpuSub => 'Treine com a IA';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get localTitle => 'LOCAL';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get localSub => 'Mesma tela';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get leaderboardTitle => 'CLASSIFICAÇÃO';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get questsTitle => 'DESAFIOS';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get themesTitle => 'TEMAS';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get tutorialTitle => 'TUTORIAL';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get startGame => 'INICIAR JOGO';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get createMatch => 'CRIAR PARTIDA';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get joinMatch => 'ENTRAR';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get gameOver => 'FIM DE JOGO';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get mainMenu => 'VOLTAR AO MENU';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get exit => 'SAIR';
|
|
||||||
}
|
|
||||||
|
|
@ -1,70 +0,0 @@
|
||||||
// ignore: unused_import
|
|
||||||
import 'package:intl/intl.dart' as intl;
|
|
||||||
import 'app_localizations.dart';
|
|
||||||
|
|
||||||
// ignore_for_file: type=lint
|
|
||||||
|
|
||||||
/// The translations for Russian (`ru`).
|
|
||||||
class AppLocalizationsRu extends AppLocalizations {
|
|
||||||
AppLocalizationsRu([String locale = 'ru']) : super(locale);
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get appTitle => 'TetraQ';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get welcomeTitle => 'ДОБРО ПОЖАЛОВАТЬ В TETRAQ!';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get nameHint => 'ИМЯ';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get saveAndPlay => 'СОХРАНИТЬ И ИГРАТЬ';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get onlineTitle => 'ОНЛАЙН';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get onlineSub => 'Брось вызов миру';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get cpuTitle => 'VS ИИ';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get cpuSub => 'Тренировка с ИИ';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get localTitle => 'ЛОКАЛЬНО';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get localSub => 'Один экран';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get leaderboardTitle => 'РЕЙТИНГ';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get questsTitle => 'ЗАДАНИЯ';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get themesTitle => 'ТЕМЫ';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get tutorialTitle => 'ОБУЧЕНИЕ';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get startGame => 'НАЧАТЬ ИГРУ';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get createMatch => 'СОЗДАТЬ ИГРУ';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get joinMatch => 'ПРИСОЕДИНИТЬСЯ';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get gameOver => 'ИГРА ОКОНЧЕНА';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get mainMenu => 'В ГЛАВНОЕ МЕНЮ';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get exit => 'ВЫХОД';
|
|
||||||
}
|
|
||||||
|
|
@ -1,70 +0,0 @@
|
||||||
// ignore: unused_import
|
|
||||||
import 'package:intl/intl.dart' as intl;
|
|
||||||
import 'app_localizations.dart';
|
|
||||||
|
|
||||||
// ignore_for_file: type=lint
|
|
||||||
|
|
||||||
/// The translations for Chinese (`zh`).
|
|
||||||
class AppLocalizationsZh extends AppLocalizations {
|
|
||||||
AppLocalizationsZh([String locale = 'zh']) : super(locale);
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get appTitle => 'TetraQ';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get welcomeTitle => '欢迎来到 TETRAQ!';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get nameHint => '名字';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get saveAndPlay => '保存并开始';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get onlineTitle => '在线匹配';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get onlineSub => '挑战世界';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get cpuTitle => '人机对战';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get cpuSub => '与AI训练';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get localTitle => '本地游戏';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get localSub => '同屏对战';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get leaderboardTitle => '排行榜';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get questsTitle => '任务';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get themesTitle => '主题';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get tutorialTitle => '教程';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get startGame => '开始游戏';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get createMatch => '创建比赛';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get joinMatch => '加入';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get gameOver => '游戏结束';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get mainMenu => '返回主菜单';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get exit => '退出';
|
|
||||||
}
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
{
|
|
||||||
"@@locale": "pt",
|
|
||||||
"appTitle": "TetraQ",
|
|
||||||
"welcomeTitle": "BEM-VINDO AO TETRAQ!",
|
|
||||||
"nameHint": "NOME",
|
|
||||||
"saveAndPlay": "SALVAR E JOGAR",
|
|
||||||
"onlineTitle": "ONLINE",
|
|
||||||
"onlineSub": "Desafie o mundo",
|
|
||||||
"cpuTitle": "VS CPU",
|
|
||||||
"cpuSub": "Treine com a IA",
|
|
||||||
"localTitle": "LOCAL",
|
|
||||||
"localSub": "Mesma tela",
|
|
||||||
"leaderboardTitle": "CLASSIFICAÇÃO",
|
|
||||||
"questsTitle": "DESAFIOS",
|
|
||||||
"themesTitle": "TEMAS",
|
|
||||||
"tutorialTitle": "TUTORIAL",
|
|
||||||
"startGame": "INICIAR JOGO",
|
|
||||||
"createMatch": "CRIAR PARTIDA",
|
|
||||||
"joinMatch": "ENTRAR",
|
|
||||||
"gameOver": "FIM DE JOGO",
|
|
||||||
"mainMenu": "VOLTAR AO MENU",
|
|
||||||
"exit": "SAIR"
|
|
||||||
}
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
{
|
|
||||||
"@@locale": "ru",
|
|
||||||
"appTitle": "TetraQ",
|
|
||||||
"welcomeTitle": "ДОБРО ПОЖАЛОВАТЬ В TETRAQ!",
|
|
||||||
"nameHint": "ИМЯ",
|
|
||||||
"saveAndPlay": "СОХРАНИТЬ И ИГРАТЬ",
|
|
||||||
"onlineTitle": "ОНЛАЙН",
|
|
||||||
"onlineSub": "Брось вызов миру",
|
|
||||||
"cpuTitle": "VS ИИ",
|
|
||||||
"cpuSub": "Тренировка с ИИ",
|
|
||||||
"localTitle": "ЛОКАЛЬНО",
|
|
||||||
"localSub": "Один экран",
|
|
||||||
"leaderboardTitle": "РЕЙТИНГ",
|
|
||||||
"questsTitle": "ЗАДАНИЯ",
|
|
||||||
"themesTitle": "ТЕМЫ",
|
|
||||||
"tutorialTitle": "ОБУЧЕНИЕ",
|
|
||||||
"startGame": "НАЧАТЬ ИГРУ",
|
|
||||||
"createMatch": "СОЗДАТЬ ИГРУ",
|
|
||||||
"joinMatch": "ПРИСОЕДИНИТЬСЯ",
|
|
||||||
"gameOver": "ИГРА ОКОНЧЕНА",
|
|
||||||
"mainMenu": "В ГЛАВНОЕ МЕНЮ",
|
|
||||||
"exit": "ВЫХОД"
|
|
||||||
}
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
{
|
|
||||||
"@@locale": "zh",
|
|
||||||
"appTitle": "TetraQ",
|
|
||||||
"welcomeTitle": "欢迎来到 TETRAQ!",
|
|
||||||
"nameHint": "名字",
|
|
||||||
"saveAndPlay": "保存并开始",
|
|
||||||
"onlineTitle": "在线匹配",
|
|
||||||
"onlineSub": "挑战世界",
|
|
||||||
"cpuTitle": "人机对战",
|
|
||||||
"cpuSub": "与AI训练",
|
|
||||||
"localTitle": "本地游戏",
|
|
||||||
"localSub": "同屏对战",
|
|
||||||
"leaderboardTitle": "排行榜",
|
|
||||||
"questsTitle": "任务",
|
|
||||||
"themesTitle": "主题",
|
|
||||||
"tutorialTitle": "教程",
|
|
||||||
"startGame": "开始游戏",
|
|
||||||
"createMatch": "创建比赛",
|
|
||||||
"joinMatch": "加入",
|
|
||||||
"gameOver": "游戏结束",
|
|
||||||
"mainMenu": "返回主菜单",
|
|
||||||
"exit": "退出"
|
|
||||||
}
|
|
||||||
|
|
@ -17,12 +17,6 @@ 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;
|
||||||
|
|
@ -56,10 +50,6 @@ class GameController extends ChangeNotifier {
|
||||||
bool opponentWantsRematch = false;
|
bool opponentWantsRematch = false;
|
||||||
int lastMatchXP = 0;
|
int lastMatchXP = 0;
|
||||||
|
|
||||||
bool hasLeveledUp = false;
|
|
||||||
int newlyReachedLevel = 1;
|
|
||||||
List<String> unlockedFeatures = [];
|
|
||||||
|
|
||||||
bool isSetupPhase = true;
|
bool isSetupPhase = true;
|
||||||
bool myJokerPlaced = false;
|
bool myJokerPlaced = false;
|
||||||
bool oppJokerPlaced = false;
|
bool oppJokerPlaced = false;
|
||||||
|
|
@ -71,7 +61,7 @@ class GameController extends ChangeNotifier {
|
||||||
int cpuLevel = 1;
|
int cpuLevel = 1;
|
||||||
int currentMatchLevel = 1;
|
int currentMatchLevel = 1;
|
||||||
int? currentSeed;
|
int? currentSeed;
|
||||||
AppThemeType _activeTheme = AppThemeType.doodle;
|
AppThemeType _activeTheme = AppThemeType.cyberpunk;
|
||||||
|
|
||||||
String onlineHostName = "ROSSO";
|
String onlineHostName = "ROSSO";
|
||||||
String onlineGuestName = "BLU";
|
String onlineGuestName = "BLU";
|
||||||
|
|
@ -82,25 +72,6 @@ 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;
|
||||||
|
|
@ -110,9 +81,6 @@ class GameController extends ChangeNotifier {
|
||||||
_hasSavedResult = false;
|
_hasSavedResult = false;
|
||||||
lastMatchXP = 0;
|
lastMatchXP = 0;
|
||||||
|
|
||||||
hasLeveledUp = false;
|
|
||||||
unlockedFeatures.clear();
|
|
||||||
|
|
||||||
myReaction = null;
|
myReaction = null;
|
||||||
opponentReaction = null;
|
opponentReaction = null;
|
||||||
_lastOpponentReactionTime = null;
|
_lastOpponentReactionTime = null;
|
||||||
|
|
@ -130,19 +98,10 @@ class GameController extends ChangeNotifier {
|
||||||
this.isHost = isHost;
|
this.isHost = isHost;
|
||||||
this.isTimeMode = timeMode;
|
this.isTimeMode = timeMode;
|
||||||
|
|
||||||
int finalRadius = radius;
|
onlineShape = shape;
|
||||||
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: finalRadius, level: levelToUse, seed: currentSeed, shape: finalShape);
|
board = GameBoard(radius: radius, level: levelToUse, seed: currentSeed, shape: onlineShape);
|
||||||
board.currentPlayer = Player.red;
|
board.currentPlayer = Player.red;
|
||||||
|
|
||||||
isCPUThinking = false;
|
isCPUThinking = false;
|
||||||
|
|
@ -155,11 +114,12 @@ class GameController extends ChangeNotifier {
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
void placeJoker(int bx, int by) {
|
// --- AGGIUNTA LA COORDINATA Z PER IL 3D ---
|
||||||
|
void placeJoker(int bx, int by, {int bz = 0}) {
|
||||||
if (!isSetupPhase) return;
|
if (!isSetupPhase) return;
|
||||||
|
|
||||||
Box? target;
|
Box? target;
|
||||||
try { target = board.boxes.firstWhere((b) => b.x == bx && b.y == by); } catch(e) {}
|
try { target = board.boxes.firstWhere((b) => b.x == bx && b.y == by && b.z == bz); } catch(e) {}
|
||||||
|
|
||||||
if (target == null || target.type == BoxType.invisible || target.hiddenJokerOwner != null) return;
|
if (target == null || target.type == BoxType.invisible || target.hiddenJokerOwner != null) return;
|
||||||
|
|
||||||
|
|
@ -172,7 +132,7 @@ class GameController extends ChangeNotifier {
|
||||||
|
|
||||||
String prefix = isHost ? 'p1' : 'p2';
|
String prefix = isHost ? 'p1' : 'p2';
|
||||||
FirebaseFirestore.instance.collection('games').doc(roomCode).update({
|
FirebaseFirestore.instance.collection('games').doc(roomCode).update({
|
||||||
'${prefix}_joker': {'x': bx, 'y': by}
|
'${prefix}_joker': {'x': bx, 'y': by, 'z': bz}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
target.hiddenJokerOwner = jokerTurn;
|
target.hiddenJokerOwner = jokerTurn;
|
||||||
|
|
@ -326,21 +286,16 @@ class GameController extends ChangeNotifier {
|
||||||
void _handleTimeOut() {
|
void _handleTimeOut() {
|
||||||
if (!isTimeMode || isSetupPhase) return;
|
if (!isTimeMode || isSetupPhase) return;
|
||||||
|
|
||||||
// Solo chi deve giocare può subire il timeout (se è online)
|
if (isOnline) {
|
||||||
if (isOnline && board.currentPlayer != myPlayer) return;
|
Line randomMove = AIEngine.getBestMove(board, 5);
|
||||||
|
handleLineTap(randomMove, _activeTheme, forced: true);
|
||||||
// 1. Raccogliamo TUTTE le linee ancora libere e giocabili
|
} else if (isVsCPU && board.currentPlayer == Player.red) {
|
||||||
List<Line> availableLines = board.lines.where((l) => l.owner == Player.none && l.isPlayable).toList();
|
Line randomMove = AIEngine.getBestMove(board, cpuLevel);
|
||||||
|
handleLineTap(randomMove, _activeTheme, forced: true);
|
||||||
// Sicurezza: se non ci sono mosse, non facciamo nulla
|
} else if (!isVsCPU) {
|
||||||
if (availableLines.isEmpty) return;
|
Line randomMove = AIEngine.getBestMove(board, 5);
|
||||||
|
handleLineTap(randomMove, _activeTheme, forced: true);
|
||||||
// 2. Scegliamo una linea in modo PURAMENTE CASUALE (nessuna intelligenza artificiale)
|
}
|
||||||
final random = Random();
|
|
||||||
Line randomMove = availableLines[random.nextInt(availableLines.length)];
|
|
||||||
|
|
||||||
// 3. Eseguiamo la mossa forzata
|
|
||||||
handleLineTap(randomMove, _activeTheme, forced: true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void disconnectOnlineGame() {
|
void disconnectOnlineGame() {
|
||||||
|
|
@ -406,14 +361,15 @@ class GameController extends ChangeNotifier {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isSetupPhase) {
|
if (isSetupPhase) {
|
||||||
|
// --- SINCRONIZZAZIONE JOLLY IN 3D ---
|
||||||
if (!isHost && data['p1_joker'] != null && !oppJokerPlaced) {
|
if (!isHost && data['p1_joker'] != null && !oppJokerPlaced) {
|
||||||
int jx = data['p1_joker']['x']; int jy = data['p1_joker']['y'];
|
int jx = data['p1_joker']['x']; int jy = data['p1_joker']['y']; int jz = data['p1_joker']['z'] ?? 0;
|
||||||
board.boxes.firstWhere((b) => b.x == jx && b.y == jy).hiddenJokerOwner = Player.red;
|
board.boxes.firstWhere((b) => b.x == jx && b.y == jy && b.z == jz).hiddenJokerOwner = Player.red;
|
||||||
oppJokerPlaced = true; _checkSetupComplete();
|
oppJokerPlaced = true; _checkSetupComplete();
|
||||||
}
|
}
|
||||||
if (isHost && data['p2_joker'] != null && !oppJokerPlaced) {
|
if (isHost && data['p2_joker'] != null && !oppJokerPlaced) {
|
||||||
int jx = data['p2_joker']['x']; int jy = data['p2_joker']['y'];
|
int jx = data['p2_joker']['x']; int jy = data['p2_joker']['y']; int jz = data['p2_joker']['z'] ?? 0;
|
||||||
board.boxes.firstWhere((b) => b.x == jx && b.y == jy).hiddenJokerOwner = Player.blue;
|
board.boxes.firstWhere((b) => b.x == jx && b.y == jy && b.z == jz).hiddenJokerOwner = Player.blue;
|
||||||
oppJokerPlaced = true; _checkSetupComplete();
|
oppJokerPlaced = true; _checkSetupComplete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -549,20 +505,6 @@ class GameController extends ChangeNotifier {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
List<String> _getUnlocks(int oldLevel, int newLevel) {
|
|
||||||
List<String> unlocks = [];
|
|
||||||
for(int i = oldLevel + 1; i <= newLevel; i++) {
|
|
||||||
if (i == 3) unlocks.add("Tema: Legno & Fiammiferi");
|
|
||||||
if (i == 7) unlocks.add("Tema: Cyberpunk");
|
|
||||||
if (i == 10) {
|
|
||||||
unlocks.add("Tema: 8-Bit Arcade");
|
|
||||||
unlocks.add("Forma Arena: Caos");
|
|
||||||
}
|
|
||||||
if (i == 15) unlocks.add("Tema: Grimorio");
|
|
||||||
}
|
|
||||||
return unlocks;
|
|
||||||
}
|
|
||||||
|
|
||||||
void _saveMatchResult() {
|
void _saveMatchResult() {
|
||||||
if (_hasSavedResult) return;
|
if (_hasSavedResult) return;
|
||||||
_hasSavedResult = true;
|
_hasSavedResult = true;
|
||||||
|
|
@ -572,8 +514,6 @@ 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;
|
|
||||||
|
|
||||||
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;
|
||||||
calculatedXP = isWin ? 20 : (isDraw ? 5 : 2);
|
calculatedXP = isWin ? 20 : (isDraw ? 5 : 2);
|
||||||
|
|
@ -581,9 +521,7 @@ class GameController extends ChangeNotifier {
|
||||||
int myScore = isHost ? board.scoreRed : board.scoreBlue;
|
int myScore = isHost ? board.scoreRed : board.scoreBlue;
|
||||||
int oppScore = isHost ? board.scoreBlue : board.scoreRed;
|
int oppScore = isHost ? board.scoreBlue : board.scoreRed;
|
||||||
StorageService.instance.saveMatchToHistory(myName: myRealName, opponent: oppName, myScore: myScore, oppScore: oppScore, isOnline: true);
|
StorageService.instance.saveMatchToHistory(myName: myRealName, opponent: oppName, myScore: myScore, oppScore: oppScore, isOnline: true);
|
||||||
|
|
||||||
if (isWin) StorageService.instance.updateQuestProgress(0, 1);
|
if (isWin) StorageService.instance.updateQuestProgress(0, 1);
|
||||||
|
|
||||||
} else if (isVsCPU) {
|
} else if (isVsCPU) {
|
||||||
int myScore = board.scoreRed; int cpuScore = board.scoreBlue;
|
int myScore = board.scoreRed; int cpuScore = board.scoreBlue;
|
||||||
bool isWin = myScore > cpuScore;
|
bool isWin = myScore > cpuScore;
|
||||||
|
|
@ -605,17 +543,7 @@ class GameController extends ChangeNotifier {
|
||||||
StorageService.instance.updateQuestProgress(2, 1);
|
StorageService.instance.updateQuestProgress(2, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
lastMatchXP = calculatedXP;
|
lastMatchXP = calculatedXP; StorageService.instance.addXP(calculatedXP); notifyListeners();
|
||||||
StorageService.instance.addXP(calculatedXP);
|
|
||||||
|
|
||||||
int newLevel = StorageService.instance.playerLevel;
|
|
||||||
if (newLevel > oldLevel) {
|
|
||||||
hasLeveledUp = true;
|
|
||||||
newlyReachedLevel = newLevel;
|
|
||||||
unlockedFeatures = _getUnlocks(oldLevel, newLevel);
|
|
||||||
}
|
|
||||||
|
|
||||||
notifyListeners();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void increaseLevelAndRestart() {
|
void increaseLevelAndRestart() {
|
||||||
|
|
|
||||||
|
|
@ -1,44 +1,23 @@
|
||||||
// ===========================================================================
|
|
||||||
// FILE: lib/main.dart
|
|
||||||
// ===========================================================================
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'core/theme_manager.dart';
|
import 'core/theme_manager.dart';
|
||||||
import 'logic/game_controller.dart';
|
import 'logic/game_controller.dart';
|
||||||
import 'ui/home/home_screen.dart';
|
import 'ui/home/home_screen.dart';
|
||||||
import 'services/storage_service.dart';
|
import 'services/storage_service.dart'; // <-- Importiamo il servizio
|
||||||
import 'services/audio_service.dart';
|
|
||||||
import 'package:firebase_core/firebase_core.dart';
|
import 'package:firebase_core/firebase_core.dart';
|
||||||
import 'package:firebase_auth/firebase_auth.dart';
|
|
||||||
import 'firebase_options.dart';
|
import 'firebase_options.dart';
|
||||||
import 'package:firebase_app_check/firebase_app_check.dart';
|
|
||||||
|
|
||||||
// --- IMPORT PER IL SUPPORTO MULTILINGUA ---
|
|
||||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
|
||||||
import 'package:tetraq/l10n/app_localizations.dart';
|
|
||||||
|
|
||||||
void main() async {
|
void main() async {
|
||||||
|
// Assicuriamoci che i motori di Flutter siano pronti
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
||||||
|
// 1. Accendiamo Firebase! (Questo ti era sfuggito)
|
||||||
await Firebase.initializeApp(
|
await Firebase.initializeApp(
|
||||||
options: DefaultFirebaseOptions.currentPlatform,
|
options: DefaultFirebaseOptions.currentPlatform,
|
||||||
);
|
);
|
||||||
|
|
||||||
await FirebaseAppCheck.instance.activate(
|
// 2. Accendiamo la Memoria Locale!
|
||||||
androidProvider: kDebugMode ? AndroidProvider.debug : AndroidProvider.playIntegrity,
|
|
||||||
appleProvider: kDebugMode ? AppleProvider.debug : AppleProvider.deviceCheck,
|
|
||||||
);
|
|
||||||
|
|
||||||
try {
|
|
||||||
await FirebaseAuth.instance.signInAnonymously();
|
|
||||||
} catch (e) {
|
|
||||||
debugPrint("Errore Auth: $e");
|
|
||||||
}
|
|
||||||
|
|
||||||
await StorageService.instance.init();
|
await StorageService.instance.init();
|
||||||
await AudioService.instance.init();
|
|
||||||
|
|
||||||
runApp(
|
runApp(
|
||||||
MultiProvider(
|
MultiProvider(
|
||||||
|
|
@ -63,14 +42,6 @@ class TetraQApp extends StatelessWidget {
|
||||||
fontFamily: 'Roboto',
|
fontFamily: 'Roboto',
|
||||||
useMaterial3: true,
|
useMaterial3: true,
|
||||||
),
|
),
|
||||||
|
|
||||||
// --- BIVIO DELLE LINGUE ATTIVATO! ---
|
|
||||||
// Flutter si occuperà di caricare automaticamente tutte le lingue
|
|
||||||
// che hai generato tramite lo script.
|
|
||||||
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
|
||||||
supportedLocales: AppLocalizations.supportedLocales,
|
|
||||||
// ------------------------------------
|
|
||||||
|
|
||||||
home: const HomeScreen(),
|
home: const HomeScreen(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,18 +5,20 @@
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
enum Player { red, blue, none }
|
enum Player { red, blue, none }
|
||||||
enum BoxType { normal, gold, bomb, invisible, swap, ice, multiplier } // Aggiunti ice e multiplier
|
enum BoxType { normal, gold, bomb, invisible, swap, ice, multiplier }
|
||||||
enum ArenaShape { classic, cross, donut, hourglass, chaos }
|
// --- AGGIUNTA LA FORMA 3D ---
|
||||||
|
enum ArenaShape { classic, cross, donut, hourglass, chaos, test, pyramid3D }
|
||||||
|
|
||||||
class Dot {
|
class Dot {
|
||||||
final int x;
|
final int x;
|
||||||
final int y;
|
final int y;
|
||||||
Dot(this.x, this.y);
|
final int z; // --- NUOVO: ALTEZZA 3D ---
|
||||||
|
Dot(this.x, this.y, {this.z = 0});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) => identical(this, other) || other is Dot && runtimeType == other.runtimeType && x == other.x && y == other.y;
|
bool operator ==(Object other) => identical(this, other) || other is Dot && runtimeType == other.runtimeType && x == other.x && y == other.y && z == other.z;
|
||||||
@override
|
@override
|
||||||
int get hashCode => x.hashCode ^ y.hashCode;
|
int get hashCode => x.hashCode ^ y.hashCode ^ z.hashCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
class Line {
|
class Line {
|
||||||
|
|
@ -24,7 +26,7 @@ class Line {
|
||||||
final Dot p2;
|
final Dot p2;
|
||||||
Player owner = Player.none;
|
Player owner = Player.none;
|
||||||
bool isPlayable = false;
|
bool isPlayable = false;
|
||||||
bool isIceCracked = false; // NUOVO: Stato per il blocco di ghiaccio
|
bool isIceCracked = false;
|
||||||
|
|
||||||
Line(this.p1, this.p2);
|
Line(this.p1, this.p2);
|
||||||
|
|
||||||
|
|
@ -34,6 +36,7 @@ class Line {
|
||||||
class Box {
|
class Box {
|
||||||
final int x;
|
final int x;
|
||||||
final int y;
|
final int y;
|
||||||
|
final int z; // --- NUOVO: PIANO DELLA SCATOLA ---
|
||||||
Player owner = Player.none;
|
Player owner = Player.none;
|
||||||
late Line top, bottom, left, right;
|
late Line top, bottom, left, right;
|
||||||
BoxType type = BoxType.normal;
|
BoxType type = BoxType.normal;
|
||||||
|
|
@ -42,7 +45,7 @@ class Box {
|
||||||
Player? hiddenJokerOwner;
|
Player? hiddenJokerOwner;
|
||||||
bool isJokerRevealed = false;
|
bool isJokerRevealed = false;
|
||||||
|
|
||||||
Box(this.x, this.y);
|
Box(this.x, this.y, {this.z = 0});
|
||||||
|
|
||||||
bool isClosed() {
|
bool isClosed() {
|
||||||
if (type == BoxType.invisible) return false;
|
if (type == BoxType.invisible) return false;
|
||||||
|
|
@ -50,13 +53,13 @@ class Box {
|
||||||
}
|
}
|
||||||
|
|
||||||
int getCalculatedValue(Player closer) {
|
int getCalculatedValue(Player closer) {
|
||||||
if (hiddenJokerOwner != null) {
|
if (hiddenJokerOwner != null) return (closer == hiddenJokerOwner) ? 2 : -1;
|
||||||
return (closer == hiddenJokerOwner) ? 2 : -1;
|
|
||||||
}
|
|
||||||
if (type == BoxType.gold) return 2;
|
if (type == BoxType.gold) return 2;
|
||||||
if (type == BoxType.bomb) return -1;
|
if (type == BoxType.bomb) return -1;
|
||||||
if (type == BoxType.swap || type == BoxType.ice || type == BoxType.multiplier) return 0; // Il moltiplicatore e il ghiaccio non danno punti base
|
if (type == BoxType.swap || type == BoxType.ice || type == BoxType.multiplier) return 0;
|
||||||
return 1;
|
|
||||||
|
// --- NUOVO: NELLA PIRAMIDE I PIANI ALTI VALGONO DI PIÙ! ---
|
||||||
|
return 1 + z;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -77,10 +80,7 @@ class GameBoard {
|
||||||
int scoreRed = 0;
|
int scoreRed = 0;
|
||||||
int scoreBlue = 0;
|
int scoreBlue = 0;
|
||||||
bool isGameOver = false;
|
bool isGameOver = false;
|
||||||
|
|
||||||
Line? lastMove;
|
Line? lastMove;
|
||||||
|
|
||||||
// Variabili per il Moltiplicatore
|
|
||||||
bool redHasMultiplier = false;
|
bool redHasMultiplier = false;
|
||||||
bool blueHasMultiplier = false;
|
bool blueHasMultiplier = false;
|
||||||
|
|
||||||
|
|
@ -89,205 +89,111 @@ class GameBoard {
|
||||||
}
|
}
|
||||||
|
|
||||||
void _generateBoard() {
|
void _generateBoard() {
|
||||||
final random = seed != null ? Random(seed) : Random();
|
dots.clear(); lines.clear(); boxes.clear(); lastMove = null;
|
||||||
int chaosAlgorithm = random.nextInt(5);
|
|
||||||
|
|
||||||
if (shape == ArenaShape.chaos) {
|
if (shape == ArenaShape.pyramid3D) {
|
||||||
columns = radius * 2 + 1;
|
// --- LOGICA GENERAZIONE PIRAMIDE 3D ---
|
||||||
rows = (radius * 3) + 2;
|
columns = 4; rows = 4; // Base 4x4
|
||||||
} else {
|
int maxZ = 4; // 4 Piani (0, 1, 2, 3)
|
||||||
columns = radius * 2 + 1;
|
|
||||||
rows = radius * 2 + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
dots.clear();
|
for (int z = 0; z < maxZ; z++) {
|
||||||
lines.clear();
|
int currentLayerSize = columns - z; // Il piano si restringe man mano che sale
|
||||||
boxes.clear();
|
for (int y = 0; y < currentLayerSize; y++) {
|
||||||
lastMove = null;
|
for (int x = 0; x < currentLayerSize; x++) {
|
||||||
|
var box = Box(x, y, z: z);
|
||||||
|
// Più sali, più possibilità ci sono di trovare Oro o Bombe
|
||||||
|
if (z > 0 && Random().nextDouble() > 0.8) box.type = BoxType.gold;
|
||||||
|
if (z > 1 && Random().nextDouble() > 0.85) box.type = BoxType.bomb;
|
||||||
|
boxes.add(box);
|
||||||
|
|
||||||
for (int y = 0; y < rows; y++) {
|
Dot tl = _getOrAddDot(x, y, z);
|
||||||
for (int x = 0; x < columns; x++) {
|
Dot tr = _getOrAddDot(x + 1, y, z);
|
||||||
var box = Box(x, y);
|
Dot bl = _getOrAddDot(x, y + 1, z);
|
||||||
bool isVisible = true;
|
Dot br = _getOrAddDot(x + 1, y + 1, z);
|
||||||
|
|
||||||
if (shape != ArenaShape.chaos) {
|
box.top = _getOrAddLine(tl, tr); box.bottom = _getOrAddLine(bl, br);
|
||||||
int dx = (x - radius).abs();
|
box.left = _getOrAddLine(tl, bl); box.right = _getOrAddLine(tr, br);
|
||||||
int dy = (y - radius).abs();
|
|
||||||
isVisible = (dx + dy) <= radius;
|
|
||||||
|
|
||||||
if (isVisible) {
|
|
||||||
switch (shape) {
|
|
||||||
case ArenaShape.cross:
|
|
||||||
int spessoreBraccio = radius > 3 ? 1 : 0;
|
|
||||||
if (dx > spessoreBraccio && dy > spessoreBraccio) isVisible = false; break;
|
|
||||||
case ArenaShape.donut:
|
|
||||||
int dimensioneBuco = radius > 3 ? 2 : 1;
|
|
||||||
if ((dx + dy) <= dimensioneBuco) isVisible = false; break;
|
|
||||||
case ArenaShape.hourglass:
|
|
||||||
if (dx > dy) isVisible = false;
|
|
||||||
if (x == radius && y == radius) isVisible = true; break;
|
|
||||||
default: break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
double percentY = y / rows;
|
|
||||||
if (chaosAlgorithm == 0) {
|
|
||||||
isVisible = (x % 2 == 0) && (random.nextDouble() > 0.15);
|
|
||||||
} else if (chaosAlgorithm == 1) {
|
|
||||||
double chance = 0.2 + (percentY * 0.7);
|
|
||||||
isVisible = random.nextDouble() < chance;
|
|
||||||
} else if (chaosAlgorithm == 2) {
|
|
||||||
int midY = rows ~/ 2;
|
|
||||||
int distFromCenterY = (y - midY).abs();
|
|
||||||
int allowedWidth = (distFromCenterY / midY * radius).ceil() + 1;
|
|
||||||
int dx = (x - radius).abs();
|
|
||||||
isVisible = dx <= allowedWidth && random.nextDouble() > 0.1;
|
|
||||||
} else if (chaosAlgorithm == 3) {
|
|
||||||
isVisible = (y % 2 == 0) ? (x < columns - 1) : (x > 0);
|
|
||||||
if (random.nextDouble() > 0.8) isVisible = false;
|
|
||||||
} else if (chaosAlgorithm == 4) {
|
|
||||||
isVisible = random.nextDouble() > 0.45;
|
|
||||||
}
|
|
||||||
if (x == radius && y == rows ~/ 2) isVisible = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isVisible) {
|
|
||||||
box.type = BoxType.invisible;
|
|
||||||
} else if (level > 1) {
|
|
||||||
double chance = random.nextDouble();
|
|
||||||
if (chance < 0.08) box.type = BoxType.gold;
|
|
||||||
else if (chance > 0.92) box.type = BoxType.bomb;
|
|
||||||
else if (level >= 5 && chance > 0.88 && chance <= 0.92) box.type = BoxType.swap;
|
|
||||||
else if (level >= 10 && chance > 0.83 && chance <= 0.88) box.type = BoxType.ice; // Nuova Scatola Ghiaccio
|
|
||||||
else if (level >= 15 && chance > 0.78 && chance <= 0.83) box.type = BoxType.multiplier; // Nuova Scatola x2
|
|
||||||
}
|
|
||||||
boxes.add(box);
|
|
||||||
}
|
}
|
||||||
}
|
_updatePyramidPlayability(); // Blocchiamo i piani superiori!
|
||||||
|
} else {
|
||||||
|
// (Qui c'è il codice standard 2D che abbiamo lasciato invariato per non rompere nulla)
|
||||||
|
columns = shape == ArenaShape.chaos ? radius * 2 + 1 : (shape == ArenaShape.test ? 3 : radius * 2 + 1);
|
||||||
|
rows = shape == ArenaShape.chaos ? (radius * 3) + 2 : (shape == ArenaShape.test ? 3 : radius * 2 + 1);
|
||||||
|
|
||||||
for (var box in boxes) {
|
for (int y = 0; y < rows; y++) {
|
||||||
Dot tl = _getOrAddDot(box.x, box.y);
|
for (int x = 0; x < columns; x++) {
|
||||||
Dot tr = _getOrAddDot(box.x + 1, box.y);
|
var box = Box(x, y);
|
||||||
Dot bl = _getOrAddDot(box.x, box.y + 1);
|
boxes.add(box);
|
||||||
Dot br = _getOrAddDot(box.x + 1, box.y + 1);
|
Dot tl = _getOrAddDot(x, y, 0); Dot tr = _getOrAddDot(x + 1, y, 0);
|
||||||
|
Dot bl = _getOrAddDot(x, y + 1, 0); Dot br = _getOrAddDot(x + 1, y + 1, 0);
|
||||||
box.top = _getOrAddLine(tl, tr);
|
box.top = _getOrAddLine(tl, tr); box.bottom = _getOrAddLine(bl, br);
|
||||||
box.bottom = _getOrAddLine(bl, br);
|
box.left = _getOrAddLine(tl, bl); box.right = _getOrAddLine(tr, br);
|
||||||
box.left = _getOrAddLine(tl, bl);
|
box.top.isPlayable = true; box.bottom.isPlayable = true; box.left.isPlayable = true; box.right.isPlayable = true;
|
||||||
box.right = _getOrAddLine(tr, br);
|
}
|
||||||
|
|
||||||
if (box.type != BoxType.invisible) {
|
|
||||||
box.top.isPlayable = true; box.bottom.isPlayable = true;
|
|
||||||
box.left.isPlayable = true; box.right.isPlayable = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Dot _getOrAddDot(int x, int y) {
|
// Sblocca i piani superiori solo se il "pavimento" è solido
|
||||||
for (var dot in dots) { if (dot.x == x && dot.y == y) return dot; }
|
void _updatePyramidPlayability() {
|
||||||
var newDot = Dot(x, y);
|
if (shape != ArenaShape.pyramid3D) return;
|
||||||
dots.add(newDot); return newDot;
|
for (var box in boxes) {
|
||||||
|
if (box.z == 0) {
|
||||||
|
if (box.owner == Player.none) {
|
||||||
|
box.top.isPlayable = true; box.bottom.isPlayable = true; box.left.isPlayable = true; box.right.isPlayable = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Cerca i 4 quadrati del piano di sotto che lo sorreggono
|
||||||
|
bool isSupported = true;
|
||||||
|
for (int dy = 0; dy <= 1; dy++) {
|
||||||
|
for (int dx = 0; dx <= 1; dx++) {
|
||||||
|
var supportBox = boxes.where((b) => b.z == box.z - 1 && b.x == box.x + dx && b.y == box.y + dy).firstOrNull;
|
||||||
|
if (supportBox == null || !supportBox.isClosed()) isSupported = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (box.owner == Player.none) {
|
||||||
|
box.top.isPlayable = isSupported; box.bottom.isPlayable = isSupported;
|
||||||
|
box.left.isPlayable = isSupported; box.right.isPlayable = isSupported;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Dot _getOrAddDot(int x, int y, int z) {
|
||||||
|
for (var dot in dots) { if (dot.x == x && dot.y == y && dot.z == z) return dot; }
|
||||||
|
var newDot = Dot(x, y, z: z); dots.add(newDot); return newDot;
|
||||||
}
|
}
|
||||||
|
|
||||||
Line _getOrAddLine(Dot a, Dot b) {
|
Line _getOrAddLine(Dot a, Dot b) {
|
||||||
for (var line in lines) { if (line.connects(a, b)) return line; }
|
for (var line in lines) { if (line.connects(a, b)) return line; }
|
||||||
var newLine = Line(a, b);
|
var newLine = Line(a, b); lines.add(newLine); return newLine;
|
||||||
lines.add(newLine); return newLine;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool playMove(Line lineToPlay, {Player? forcedPlayer}) {
|
bool playMove(Line lineToPlay, {Player? forcedPlayer}) {
|
||||||
if (isGameOver) return false;
|
if (isGameOver) return false;
|
||||||
|
|
||||||
Player playerMakingMove = forcedPlayer ?? currentPlayer;
|
Player playerMakingMove = forcedPlayer ?? currentPlayer;
|
||||||
Line? actualLine;
|
Line? actualLine;
|
||||||
for (var l in lines) {
|
for (var l in lines) { if (l.connects(lineToPlay.p1, lineToPlay.p2)) { actualLine = l; break; } }
|
||||||
if (l.connects(lineToPlay.p1, lineToPlay.p2)) { actualLine = l; break; }
|
|
||||||
}
|
|
||||||
|
|
||||||
if (actualLine == null || actualLine.owner != Player.none || !actualLine.isPlayable) return false;
|
if (actualLine == null || actualLine.owner != Player.none || !actualLine.isPlayable) return false;
|
||||||
|
|
||||||
// --- LOGICA BLOCCO DI GHIACCIO ---
|
|
||||||
bool closesIce = false;
|
|
||||||
for (var box in boxes) {
|
|
||||||
if (box.type == BoxType.ice && box.owner == Player.none) {
|
|
||||||
int linesCount = 0;
|
|
||||||
if (box.top.owner != Player.none || box.top == actualLine) linesCount++;
|
|
||||||
if (box.bottom.owner != Player.none || box.bottom == actualLine) linesCount++;
|
|
||||||
if (box.left.owner != Player.none || box.left == actualLine) linesCount++;
|
|
||||||
if (box.right.owner != Player.none || box.right == actualLine) linesCount++;
|
|
||||||
if (linesCount == 4) closesIce = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (closesIce && !actualLine.isIceCracked) {
|
|
||||||
actualLine.isIceCracked = true; // Si incrina ma non si chiude!
|
|
||||||
lastMove = actualLine;
|
|
||||||
if (forcedPlayer == null) currentPlayer = (currentPlayer == Player.red) ? Player.blue : Player.red;
|
|
||||||
else currentPlayer = (forcedPlayer == Player.red) ? Player.blue : Player.red;
|
|
||||||
return true; // Mossa valida, ma turno finito.
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mossa normale o secondo colpo al ghiaccio
|
|
||||||
actualLine.isIceCracked = false;
|
|
||||||
actualLine.owner = playerMakingMove;
|
actualLine.owner = playerMakingMove;
|
||||||
lastMove = actualLine;
|
lastMove = actualLine;
|
||||||
|
|
||||||
bool scoredPoint = false;
|
bool scoredPoint = false;
|
||||||
bool triggeredSwap = false;
|
|
||||||
|
|
||||||
for (var box in boxes) {
|
for (var box in boxes) {
|
||||||
if (box.owner == Player.none && box.isClosed()) {
|
if (box.owner == Player.none && box.isClosed()) {
|
||||||
box.owner = playerMakingMove;
|
box.owner = playerMakingMove; scoredPoint = true;
|
||||||
scoredPoint = true;
|
|
||||||
|
|
||||||
if (box.hiddenJokerOwner != null) box.isJokerRevealed = true;
|
|
||||||
|
|
||||||
int points = box.getCalculatedValue(playerMakingMove);
|
int points = box.getCalculatedValue(playerMakingMove);
|
||||||
|
if (playerMakingMove == Player.red) scoreRed += points; else scoreBlue += points;
|
||||||
// --- LOGICA MOLTIPLICATORE x2 ---
|
|
||||||
if (box.type == BoxType.multiplier) {
|
|
||||||
if (playerMakingMove == Player.red) redHasMultiplier = true;
|
|
||||||
else blueHasMultiplier = true;
|
|
||||||
} else if (points != 0) {
|
|
||||||
// Se la scatola chiusa dà punti e il giocatore ha un x2 attivo...
|
|
||||||
if (playerMakingMove == Player.red && redHasMultiplier) {
|
|
||||||
points *= 2;
|
|
||||||
redHasMultiplier = false; // Si consuma
|
|
||||||
} else if (playerMakingMove == Player.blue && blueHasMultiplier) {
|
|
||||||
points *= 2;
|
|
||||||
blueHasMultiplier = false; // Si consuma
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (playerMakingMove == Player.red) { scoreRed += points; }
|
|
||||||
else { scoreBlue += points; }
|
|
||||||
|
|
||||||
if (box.type == BoxType.swap && box.hiddenJokerOwner == null) {
|
|
||||||
triggeredSwap = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (box.type == BoxType.invisible && !box.isRevealed) {
|
|
||||||
if (box.top.owner != Player.none && box.bottom.owner != Player.none &&
|
|
||||||
box.left.owner != Player.none && box.right.owner != Player.none) {
|
|
||||||
box.isRevealed = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (triggeredSwap) {
|
if (shape == ArenaShape.pyramid3D) _updatePyramidPlayability(); // Ricalcola chi può giocare sopra!
|
||||||
int temp = scoreRed; scoreRed = scoreBlue; scoreBlue = temp;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lines.where((l) => l.isPlayable).every((l) => l.owner != Player.none)) { isGameOver = true; }
|
if (lines.where((l) => l.isPlayable).every((l) => l.owner != Player.none)) isGameOver = true;
|
||||||
|
if (!scoredPoint && !isGameOver) currentPlayer = (playerMakingMove == Player.red) ? Player.blue : Player.red;
|
||||||
if (forcedPlayer == null) {
|
|
||||||
if (!scoredPoint && !isGameOver) { currentPlayer = (currentPlayer == Player.red) ? Player.blue : Player.red; }
|
|
||||||
else if (scoredPoint && !isGameOver) { currentPlayer = playerMakingMove; }
|
|
||||||
} else {
|
|
||||||
if (!scoredPoint && !isGameOver) { currentPlayer = (forcedPlayer == Player.red) ? Player.blue : Player.red; }
|
|
||||||
else { currentPlayer = forcedPlayer; }
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:audioplayers/audioplayers.dart';
|
import 'package:audioplayers/audioplayers.dart';
|
||||||
import '../core/app_colors.dart';
|
import '../core/app_colors.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
|
||||||
|
|
||||||
class AudioService extends ChangeNotifier {
|
class AudioService extends ChangeNotifier {
|
||||||
static final AudioService instance = AudioService._internal();
|
static final AudioService instance = AudioService._internal();
|
||||||
|
|
@ -13,104 +12,35 @@ class AudioService extends ChangeNotifier {
|
||||||
|
|
||||||
bool isMuted = false;
|
bool isMuted = false;
|
||||||
final AudioPlayer _sfxPlayer = AudioPlayer();
|
final AudioPlayer _sfxPlayer = AudioPlayer();
|
||||||
final AudioPlayer _bgmPlayer = AudioPlayer();
|
|
||||||
|
|
||||||
AppThemeType _currentTheme = AppThemeType.doodle;
|
void toggleMute() {
|
||||||
|
|
||||||
Future<void> init() async {
|
|
||||||
final prefs = await SharedPreferences.getInstance();
|
|
||||||
isMuted = prefs.getBool('isMuted') ?? false;
|
|
||||||
await _bgmPlayer.setReleaseMode(ReleaseMode.loop);
|
|
||||||
}
|
|
||||||
|
|
||||||
void toggleMute() async {
|
|
||||||
isMuted = !isMuted;
|
isMuted = !isMuted;
|
||||||
final prefs = await SharedPreferences.getInstance();
|
|
||||||
await prefs.setBool('isMuted', isMuted);
|
|
||||||
|
|
||||||
if (isMuted) {
|
|
||||||
// Se abbiamo appena silenziato, FERMA TUTTO immediatamente.
|
|
||||||
await _bgmPlayer.pause();
|
|
||||||
await _sfxPlayer.stop();
|
|
||||||
} else {
|
|
||||||
// Se riaccendiamo, fai ripartire la canzone
|
|
||||||
playBgm(_currentTheme);
|
|
||||||
}
|
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> playBgm(AppThemeType theme) async {
|
|
||||||
_currentTheme = theme;
|
|
||||||
await _bgmPlayer.stop();
|
|
||||||
|
|
||||||
if (isMuted) return;
|
|
||||||
|
|
||||||
String audioPath = '';
|
|
||||||
|
|
||||||
switch (theme) {
|
|
||||||
case AppThemeType.cyberpunk:
|
|
||||||
audioPath = 'audio/bgm/Cyber_Dystopia.mp3';
|
|
||||||
break;
|
|
||||||
case AppThemeType.doodle:
|
|
||||||
audioPath = 'audio/bgm/Quad_Dreams.mp3';
|
|
||||||
break;
|
|
||||||
case AppThemeType.wood:
|
|
||||||
audioPath = 'audio/bgm/Legno_Canopy.mp3';
|
|
||||||
break;
|
|
||||||
case AppThemeType.arcade:
|
|
||||||
audioPath = 'audio/bgm/8-bit_Prowler.mp3';
|
|
||||||
break;
|
|
||||||
case AppThemeType.grimorio:
|
|
||||||
audioPath = 'audio/bgm/Grimorio_Astral.mp3';
|
|
||||||
break;
|
|
||||||
case AppThemeType.music:
|
|
||||||
audioPath = 'audio/bgm/Music_Loop.mp3'; // <-- DEVI INSERIRE QUESTO FILE IN ASSETS
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (audioPath.isNotEmpty) {
|
|
||||||
try {
|
|
||||||
await _bgmPlayer.play(AssetSource(audioPath), volume: 0.15);
|
|
||||||
} catch (e) {
|
|
||||||
debugPrint("Errore riproduzione BGM: $e");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> stopBgm() async {
|
|
||||||
await _bgmPlayer.stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
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.arcade:
|
case AppThemeType.minimal:
|
||||||
case AppThemeType.music: // Usiamo l'effetto arcade o cyber per la musica
|
case AppThemeType.arcade: // Suono secco per l'arcade
|
||||||
file = 'minimal_line.wav'; break;
|
file = 'minimal_line.wav'; break;
|
||||||
case AppThemeType.doodle:
|
case AppThemeType.doodle:
|
||||||
case AppThemeType.wood:
|
case AppThemeType.wood:
|
||||||
file = 'doodle_line.wav'; break;
|
file = 'doodle_line.wav'; break;
|
||||||
case AppThemeType.cyberpunk:
|
case AppThemeType.cyberpunk:
|
||||||
case AppThemeType.grimorio:
|
case AppThemeType.grimorio: // Suono etereo per la magia
|
||||||
file = 'cyber_line.wav'; break;
|
file = 'cyber_line.wav'; break;
|
||||||
}
|
}
|
||||||
|
await _sfxPlayer.play(AssetSource('audio/sfx/$file'));
|
||||||
if (file.isNotEmpty) {
|
|
||||||
try {
|
|
||||||
await _sfxPlayer.play(AssetSource('audio/sfx/$file'), volume: 1.0);
|
|
||||||
} catch (e) {
|
|
||||||
debugPrint("Errore SFX Linea: $file");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void playBoxSfx(AppThemeType theme) async {
|
void playBoxSfx(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:
|
||||||
case AppThemeType.music:
|
|
||||||
file = 'minimal_box.wav'; break;
|
file = 'minimal_box.wav'; break;
|
||||||
case AppThemeType.doodle:
|
case AppThemeType.doodle:
|
||||||
case AppThemeType.wood:
|
case AppThemeType.wood:
|
||||||
|
|
@ -119,27 +49,16 @@ class AudioService extends ChangeNotifier {
|
||||||
case AppThemeType.grimorio:
|
case AppThemeType.grimorio:
|
||||||
file = 'cyber_box.wav'; break;
|
file = 'cyber_box.wav'; break;
|
||||||
}
|
}
|
||||||
|
await _sfxPlayer.play(AssetSource('audio/sfx/$file'));
|
||||||
if (file.isNotEmpty) {
|
|
||||||
try {
|
|
||||||
await _sfxPlayer.play(AssetSource('audio/sfx/$file'), volume: 1.0);
|
|
||||||
} catch (e) {
|
|
||||||
debugPrint("Errore SFX Box: $file");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void playBonusSfx() async {
|
void playBonusSfx() async {
|
||||||
if (isMuted) return;
|
if (isMuted) return;
|
||||||
try {
|
await _sfxPlayer.play(AssetSource('audio/sfx/bonus.wav'));
|
||||||
await _sfxPlayer.play(AssetSource('audio/sfx/bonus.wav'), volume: 1.0);
|
|
||||||
} catch(e) {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void playBombSfx() async {
|
void playBombSfx() async {
|
||||||
if (isMuted) return;
|
if (isMuted) return;
|
||||||
try {
|
await _sfxPlayer.play(AssetSource('audio/sfx/bomb.wav'));
|
||||||
await _sfxPlayer.play(AssetSource('audio/sfx/bomb.wav'), volume: 1.0);
|
|
||||||
} catch(e) {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -4,17 +4,15 @@
|
||||||
|
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||||
import 'package:firebase_auth/firebase_auth.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:share_plus/share_plus.dart';
|
import 'package:share_plus/share_plus.dart';
|
||||||
|
|
||||||
class MultiplayerService {
|
class MultiplayerService {
|
||||||
final FirebaseFirestore _firestore = FirebaseFirestore.instance;
|
final FirebaseFirestore _firestore = FirebaseFirestore.instance;
|
||||||
final FirebaseAuth _auth = FirebaseAuth.instance;
|
|
||||||
|
|
||||||
CollectionReference get _gamesCollection => _firestore.collection('games');
|
CollectionReference get _gamesCollection => _firestore.collection('games');
|
||||||
|
|
||||||
Future<String> createGameRoom(int boardRadius, String hostName, String shapeName, bool isTimeMode, {bool isPublic = true}) async {
|
Future<String> createGameRoom(int boardRadius, String hostName, String shapeName, bool isTimeMode) async {
|
||||||
String roomCode = _generateRoomCode();
|
String roomCode = _generateRoomCode();
|
||||||
int randomSeed = Random().nextInt(1000000);
|
int randomSeed = Random().nextInt(1000000);
|
||||||
|
|
||||||
|
|
@ -27,11 +25,10 @@ class MultiplayerService {
|
||||||
'moves': [],
|
'moves': [],
|
||||||
'seed': randomSeed,
|
'seed': randomSeed,
|
||||||
'hostName': hostName,
|
'hostName': hostName,
|
||||||
'hostUid': _auth.currentUser?.uid, // NUOVO: Salviamo l'ID univoco del creatore
|
|
||||||
'guestName': '',
|
'guestName': '',
|
||||||
'shape': shapeName,
|
'shape': shapeName,
|
||||||
'timeMode': isTimeMode,
|
'timeMode': isTimeMode,
|
||||||
'isPublic': isPublic,
|
// Nuovi campi per Emojis e Rivincita
|
||||||
'p1_reaction': null,
|
'p1_reaction': null,
|
||||||
'p2_reaction': null,
|
'p2_reaction': null,
|
||||||
'p1_rematch': false,
|
'p1_rematch': false,
|
||||||
|
|
@ -55,18 +52,8 @@ class MultiplayerService {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Stream<QuerySnapshot> getPublicRooms() {
|
|
||||||
return _gamesCollection
|
|
||||||
.where('status', isEqualTo: 'waiting')
|
|
||||||
.where('isPublic', isEqualTo: true)
|
|
||||||
.snapshots();
|
|
||||||
}
|
|
||||||
|
|
||||||
void shareInviteLink(String roomCode) {
|
void shareInviteLink(String roomCode) {
|
||||||
String message = "Ehi! Giochiamo a TetraQ? 🎮\n\n"
|
String message = "Ehi! Giochiamo a TetraQ? 🎮\nCopia questo intero messaggio e apri l'app per entrare direttamente, oppure inserisci manualmente il codice: $roomCode";
|
||||||
"Clicca su questo link per entrare direttamente in stanza:\n"
|
|
||||||
"tetraq://join?code=$roomCode\n\n"
|
|
||||||
"Oppure apri l'app e inserisci manualmente il codice: $roomCode";
|
|
||||||
Share.share(message);
|
Share.share(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -82,6 +69,7 @@ class MultiplayerService {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- NUOVI METODI PER REAZIONI E RIVINCITA ---
|
||||||
Future<void> sendReaction(String roomCode, bool isHost, String reaction) async {
|
Future<void> sendReaction(String roomCode, bool isHost, String reaction) async {
|
||||||
try {
|
try {
|
||||||
String prefix = isHost ? 'p1' : 'p2';
|
String prefix = isHost ? 'p1' : 'p2';
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,6 @@ import 'dart:convert';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||||
import '../core/app_colors.dart';
|
import '../core/app_colors.dart';
|
||||||
import 'package:firebase_auth/firebase_auth.dart';
|
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
|
|
||||||
class StorageService {
|
class StorageService {
|
||||||
static final StorageService instance = StorageService._internal();
|
static final StorageService instance = StorageService._internal();
|
||||||
|
|
@ -17,11 +15,10 @@ class StorageService {
|
||||||
|
|
||||||
Future<void> init() async {
|
Future<void> init() async {
|
||||||
_prefs = await SharedPreferences.getInstance();
|
_prefs = await SharedPreferences.getInstance();
|
||||||
_checkDailyQuests();
|
_checkDailyQuests(); // All'avvio controlliamo se ci sono nuove sfide
|
||||||
}
|
}
|
||||||
|
|
||||||
// Doodle è il nuovo tema di partenza (index 0)
|
int get savedThemeIndex => _prefs.getInt('theme') ?? AppThemeType.minimal.index;
|
||||||
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;
|
||||||
|
|
@ -32,6 +29,7 @@ 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,44 +52,46 @@ 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();
|
syncLeaderboard(); // Aggiorna il nome in classifica
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- NUOVO: SINCRONIZZAZIONE CLASSIFICA ONLINE ---
|
||||||
Future<void> syncLeaderboard() async {
|
Future<void> syncLeaderboard() async {
|
||||||
if (playerName.isNotEmpty) {
|
if (playerName.isNotEmpty) {
|
||||||
try {
|
try {
|
||||||
final user = FirebaseAuth.instance.currentUser;
|
await FirebaseFirestore.instance.collection('leaderboard').doc(playerName).set({
|
||||||
|
'name': playerName,
|
||||||
if (user != null) {
|
'xp': totalXP,
|
||||||
await FirebaseFirestore.instance.collection('leaderboard').doc(user.uid).set({
|
'level': playerLevel,
|
||||||
'name': playerName,
|
'wins': wins,
|
||||||
'xp': totalXP,
|
'lastActive': FieldValue.serverTimestamp(),
|
||||||
'level': playerLevel,
|
}, SetOptions(merge: true));
|
||||||
'wins': wins,
|
|
||||||
'lastActive': FieldValue.serverTimestamp(),
|
|
||||||
}, SetOptions(merge: true));
|
|
||||||
}
|
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
debugPrint("Errore sinc. classifica: $e");
|
// Ignoriamo gli errori se manca la rete, si sincronizzerà dopo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- 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);
|
||||||
|
|
@ -110,6 +110,7 @@ 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();
|
||||||
|
|
|
||||||
BIN
lib/ui/.DS_Store
vendored
BIN
lib/ui/.DS_Store
vendored
Binary file not shown.
|
|
@ -1,128 +0,0 @@
|
||||||
// ===========================================================================
|
|
||||||
// FILE: lib/ui/admin/admin_screen.dart
|
|
||||||
// ===========================================================================
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
|
||||||
import 'package:intl/intl.dart';
|
|
||||||
import 'package:provider/provider.dart';
|
|
||||||
import '../../core/theme_manager.dart';
|
|
||||||
|
|
||||||
class AdminScreen extends StatelessWidget {
|
|
||||||
const AdminScreen({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final theme = context.watch<ThemeManager>().currentColors;
|
|
||||||
|
|
||||||
return Scaffold(
|
|
||||||
backgroundColor: theme.background,
|
|
||||||
appBar: AppBar(
|
|
||||||
title: Text("DASHBOARD ADMIN 🕵️♂️", style: TextStyle(color: theme.text, fontWeight: FontWeight.w900, letterSpacing: 2)),
|
|
||||||
backgroundColor: theme.background,
|
|
||||||
iconTheme: IconThemeData(color: theme.text),
|
|
||||||
elevation: 0,
|
|
||||||
),
|
|
||||||
body: StreamBuilder<QuerySnapshot>(
|
|
||||||
// Ordiniamo per Ultimo Accesso, così i giocatori attivi di recente sono in cima!
|
|
||||||
stream: FirebaseFirestore.instance.collection('leaderboard').orderBy('lastActive', descending: true).snapshots(),
|
|
||||||
builder: (context, snapshot) {
|
|
||||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
|
||||||
return Center(child: CircularProgressIndicator(color: theme.playerBlue));
|
|
||||||
}
|
|
||||||
if (!snapshot.hasData || snapshot.data!.docs.isEmpty) {
|
|
||||||
return Center(child: Text("Nessun giocatore trovato nel database.", style: TextStyle(color: theme.text)));
|
|
||||||
}
|
|
||||||
|
|
||||||
var docs = snapshot.data!.docs;
|
|
||||||
|
|
||||||
return ListView.builder(
|
|
||||||
padding: const EdgeInsets.all(16),
|
|
||||||
itemCount: docs.length,
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
var data = docs[index].data() as Map<String, dynamic>;
|
|
||||||
|
|
||||||
String name = data['name'] ?? 'Fantasma';
|
|
||||||
int level = data['level'] ?? 1;
|
|
||||||
int xp = data['xp'] ?? 0;
|
|
||||||
int wins = data['wins'] ?? 0;
|
|
||||||
String platform = data['platform'] ?? 'Sconosciuta';
|
|
||||||
|
|
||||||
// Formattazione Date (Se esistono)
|
|
||||||
DateTime? created;
|
|
||||||
if (data['accountCreated'] != null) created = (data['accountCreated'] as Timestamp).toDate();
|
|
||||||
|
|
||||||
DateTime? lastActive;
|
|
||||||
if (data['lastActive'] != null) lastActive = (data['lastActive'] as Timestamp).toDate();
|
|
||||||
|
|
||||||
String createdStr = created != null ? DateFormat('dd MMM yyyy').format(created) : 'N/D';
|
|
||||||
String lastActiveStr = lastActive != null ? DateFormat('dd MMM yyyy - HH:mm').format(lastActive) : 'N/D';
|
|
||||||
|
|
||||||
IconData platformIcon = Icons.device_unknown;
|
|
||||||
if (platform == 'iOS') platformIcon = Icons.apple;
|
|
||||||
if (platform == 'Android') platformIcon = Icons.android;
|
|
||||||
|
|
||||||
return Card(
|
|
||||||
color: theme.text.withOpacity(0.05),
|
|
||||||
elevation: 0,
|
|
||||||
margin: const EdgeInsets.only(bottom: 12),
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(15),
|
|
||||||
side: BorderSide(color: theme.gridLine.withOpacity(0.3))
|
|
||||||
),
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(16),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Text(name, style: TextStyle(color: theme.playerBlue, fontSize: 22, fontWeight: FontWeight.w900)),
|
|
||||||
Icon(platformIcon, color: theme.text.withOpacity(0.7)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Text("Liv. $level", style: TextStyle(color: theme.playerRed, fontWeight: FontWeight.bold, fontSize: 16)),
|
|
||||||
const SizedBox(width: 15),
|
|
||||||
Text("$xp XP", style: TextStyle(color: theme.text.withOpacity(0.7))),
|
|
||||||
const SizedBox(width: 15),
|
|
||||||
Text("Vittorie: $wins", style: TextStyle(color: Colors.amber.shade700, fontWeight: FontWeight.bold)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const Padding(
|
|
||||||
padding: EdgeInsets.symmetric(vertical: 8.0),
|
|
||||||
child: Divider(),
|
|
||||||
),
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text("Registrato il:", style: TextStyle(color: theme.text.withOpacity(0.5), fontSize: 10)),
|
|
||||||
Text(createdStr, style: TextStyle(color: theme.text, fontSize: 12, fontWeight: FontWeight.bold)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.end,
|
|
||||||
children: [
|
|
||||||
Text("Ultimo Accesso:", style: TextStyle(color: theme.text.withOpacity(0.5), fontSize: 10)),
|
|
||||||
Text(lastActiveStr, style: TextStyle(color: Colors.green, fontSize: 12, fontWeight: FontWeight.bold)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
|
|
||||||
import '../../models/game_board.dart';
|
import '../../models/game_board.dart';
|
||||||
import '../../core/app_colors.dart';
|
import '../../core/app_colors.dart';
|
||||||
|
|
@ -20,6 +21,8 @@ class BoardPainter extends CustomPainter {
|
||||||
final Player myPlayer;
|
final Player myPlayer;
|
||||||
final Player jokerTurn;
|
final Player jokerTurn;
|
||||||
|
|
||||||
|
final double cameraAngle; // Angolazione della telecamera a 360 gradi!
|
||||||
|
|
||||||
BoardPainter({
|
BoardPainter({
|
||||||
required this.board,
|
required this.board,
|
||||||
required this.theme,
|
required this.theme,
|
||||||
|
|
@ -29,348 +32,231 @@ class BoardPainter extends CustomPainter {
|
||||||
required this.isSetupPhase,
|
required this.isSetupPhase,
|
||||||
required this.myPlayer,
|
required this.myPlayer,
|
||||||
required this.jokerTurn,
|
required this.jokerTurn,
|
||||||
this.blinkValue = 0.0
|
this.blinkValue = 0.0,
|
||||||
|
this.cameraAngle = 0.0,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Color _darken(Color c, [double amount = .1]) {
|
||||||
|
assert(amount >= 0 && amount <= 1);
|
||||||
|
final hsl = HSLColor.fromColor(c);
|
||||||
|
final hslDark = hsl.withLightness((hsl.lightness - amount).clamp(0.0, 1.0));
|
||||||
|
return hslDark.toColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
// LA MAGIA: Proiezione Isometrica Ruotabile
|
||||||
|
Offset projectLogical(double x, double y, double z, Size size) {
|
||||||
|
if (board.shape != ArenaShape.pyramid3D) {
|
||||||
|
int gridPoints = board.columns + 1;
|
||||||
|
double spacing = size.width / gridPoints;
|
||||||
|
return Offset(x * spacing + (spacing / 2), y * spacing + (spacing / 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
double tileW = size.width / 3.8;
|
||||||
|
double tileH = tileW * 0.55;
|
||||||
|
double zHeight = tileW * 0.45; // L'altezza fisica del blocco 3D
|
||||||
|
|
||||||
|
// Calcoliamo la posizione tenendo conto del restringimento della piramide (+ z * 0.5)
|
||||||
|
double actualX = x + z * 0.5 - board.columns / 2.0;
|
||||||
|
double actualY = y + z * 0.5 - board.rows / 2.0;
|
||||||
|
|
||||||
|
// Matrice di rotazione della telecamera (Z-Axis)
|
||||||
|
double rx = actualX * cos(cameraAngle) - actualY * sin(cameraAngle);
|
||||||
|
double ry = actualX * sin(cameraAngle) + actualY * cos(cameraAngle);
|
||||||
|
|
||||||
|
// Proiezione Isometrica 2D
|
||||||
|
double sx = (rx - ry) * tileW;
|
||||||
|
// Il SEGRETO: -z sposta fisicamente in ALTO la faccia del cubo rispetto allo schermo
|
||||||
|
double sy = (rx + ry) * tileH - (z * zHeight);
|
||||||
|
|
||||||
|
return Offset(size.width / 2 + sx, size.height * 0.70 + sy);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calcola la distanza dalla telecamera per lo Z-Buffering
|
||||||
|
double getDepth(Box b) {
|
||||||
|
double actualX = b.x + b.z * 0.5 - board.columns / 2.0;
|
||||||
|
double actualY = b.y + b.z * 0.5 - board.rows / 2.0;
|
||||||
|
double rx = actualX * cos(cameraAngle) - actualY * sin(cameraAngle);
|
||||||
|
double ry = actualX * sin(cameraAngle) + actualY * cos(cameraAngle);
|
||||||
|
return rx + ry; // Ordina dal più lontano al più vicino
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void paint(Canvas canvas, Size size) {
|
void paint(Canvas canvas, Size size) {
|
||||||
if (themeType == AppThemeType.doodle) {
|
if (board.shape == ArenaShape.pyramid3D) _paint3D(canvas, size);
|
||||||
final Paint paperGridPaint = Paint()
|
else _paint2D(canvas, size);
|
||||||
..color = Colors.grey.withOpacity(0.3)
|
}
|
||||||
..strokeWidth = 1.0
|
|
||||||
..style = PaintingStyle.stroke;
|
|
||||||
|
|
||||||
double paperStep = 20.0;
|
void _paint3D(Canvas canvas, Size size) {
|
||||||
for (double i = 0; i <= size.width; i += paperStep) {
|
double visualZHeight = (size.width / 3.8) * 0.45;
|
||||||
canvas.drawLine(Offset(i, 0), Offset(i, size.height), paperGridPaint);
|
int maxZ = 4;
|
||||||
}
|
Set<Line> drawnLines = {};
|
||||||
for (double i = 0; i <= size.height; i += paperStep) {
|
|
||||||
canvas.drawLine(Offset(0, i), Offset(size.width, i), paperGridPaint);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int gridPoints = board.columns + 1;
|
// Costruiamo la piramide piano per piano, partendo dal basso
|
||||||
double spacing = size.width / gridPoints;
|
for (int currentZ = 0; currentZ < maxZ; currentZ++) {
|
||||||
double offset = spacing / 2;
|
var currentLevelBoxes = board.boxes.where((b) => b.z == currentZ && b.type != BoxType.invisible).toList();
|
||||||
Offset getScreenPos(int x, int y) => Offset(x * spacing + offset, y * spacing + offset);
|
|
||||||
|
|
||||||
// --- NUOVO SFONDO LUMINOSO SAGOMATO (Solo per tema Musica) ---
|
// Ordiniamo dal fondo allo schermo
|
||||||
if (themeType == AppThemeType.music) {
|
currentLevelBoxes.sort((a, b) => getDepth(a).compareTo(getDepth(b)));
|
||||||
Path arenaShape = Path();
|
|
||||||
// Uniamo la forma di ogni box giocabile per creare il tappeto
|
for (var box in currentLevelBoxes) {
|
||||||
for (var box in board.boxes) {
|
bool isPlayable = box.top.isPlayable;
|
||||||
if (box.type != BoxType.invisible) { // Ignora i buchi
|
bool isOwned = box.owner != Player.none;
|
||||||
Offset p1 = getScreenPos(box.x, box.y);
|
if (!isOwned && !isPlayable) continue;
|
||||||
Offset p2 = getScreenPos(box.x + 1, box.y + 1);
|
|
||||||
arenaShape.addRect(Rect.fromPoints(p1, p2));
|
// Disegniamo prima le LINEE DELLA GRIGLIA relative a questo cubo
|
||||||
|
void drawLine(Line l, double lx1, double ly1, double lx2, double ly2) {
|
||||||
|
if (drawnLines.contains(l)) return;
|
||||||
|
drawnLines.add(l);
|
||||||
|
if (!l.isPlayable && l.owner == Player.none) return;
|
||||||
|
|
||||||
|
Offset pt1 = projectLogical(lx1, ly1, currentZ.toDouble(), size);
|
||||||
|
Offset pt2 = projectLogical(lx2, ly2, currentZ.toDouble(), size);
|
||||||
|
|
||||||
|
if (l.isIceCracked) { _drawCrackedIceLine(canvas, pt1, pt2, blinkValue); return; }
|
||||||
|
Color lineColor = l.owner == Player.none ? theme.gridLine.withOpacity(0.5) : (l.owner == Player.red ? theme.playerRed : theme.playerBlue);
|
||||||
|
if (l == board.lastMove && l.owner != Player.none) canvas.drawLine(pt1, pt2, Paint()..color = Colors.white.withOpacity(blinkValue * 0.8)..strokeWidth = 10.0..strokeCap = StrokeCap.round..maskFilter = const MaskFilter.blur(BlurStyle.normal, 4.0));
|
||||||
|
canvas.drawLine(pt1, pt2, Paint()..color = lineColor..strokeWidth = 4.0..strokeCap = StrokeCap.round);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Disegniamo lo sfondo unito, chiaro e con un po' di blur
|
drawLine(box.top, box.x.toDouble(), box.y.toDouble(), box.x + 1.0, box.y.toDouble());
|
||||||
canvas.drawPath(
|
drawLine(box.right, box.x + 1.0, box.y.toDouble(), box.x + 1.0, box.y + 1.0);
|
||||||
arenaShape,
|
drawLine(box.bottom, box.x.toDouble(), box.y + 1.0, box.x + 1.0, box.y + 1.0);
|
||||||
Paint()
|
drawLine(box.left, box.x.toDouble(), box.y.toDouble(), box.x.toDouble(), box.y + 1.0);
|
||||||
..color = Colors.white.withOpacity(0.08) // Colore chiarissimo
|
|
||||||
..style = PaintingStyle.fill
|
|
||||||
..maskFilter = const MaskFilter.blur(BlurStyle.normal, 10.0), // Effetto stacco/glow
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// -----------------------------------------------------------
|
|
||||||
|
|
||||||
for (var box in board.boxes) {
|
// Disegniamo i PALLINI
|
||||||
Offset p1 = getScreenPos(box.x, box.y);
|
void drawDot(Dot d) {
|
||||||
Offset p2 = getScreenPos(box.x + 1, box.y + 1);
|
if (board.lines.any((l) => (l.p1 == d || l.p2 == d) && l.isPlayable)) {
|
||||||
Rect rect = Rect.fromPoints(p1, p2);
|
canvas.drawCircle(projectLogical(d.x.toDouble(), d.y.toDouble(), currentZ.toDouble(), size), 5.0, Paint()..color = theme.text.withOpacity(0.8));
|
||||||
|
|
||||||
if (box.type == BoxType.invisible) {
|
|
||||||
if (box.isRevealed) {
|
|
||||||
_drawIconInBox(canvas, rect, ThemeIcons.block(themeType), Colors.grey.shade500);
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sfondo azzurrino se è di ghiaccio (anche prima di chiuderla)
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (box.owner != Player.none) {
|
|
||||||
final boxPaint = Paint()
|
|
||||||
..style = PaintingStyle.fill
|
|
||||||
..color = box.owner == Player.red ? theme.playerRed.withOpacity(0.6) : theme.playerBlue.withOpacity(0.6);
|
|
||||||
|
|
||||||
if (themeType == AppThemeType.wood) {
|
|
||||||
_drawFlameBox(canvas, rect, box.owner == Player.red);
|
|
||||||
} else if (themeType == AppThemeType.doodle) {
|
|
||||||
Color penColor = box.owner == Player.red ? Colors.redAccent.shade700 : Colors.blueAccent.shade700;
|
|
||||||
_drawScribbleBox(canvas, rect, penColor);
|
|
||||||
} else if (themeType == AppThemeType.arcade) {
|
|
||||||
_drawArcadeBox(canvas, rect, box.owner == Player.red ? theme.playerRed : theme.playerBlue);
|
|
||||||
} else if (themeType == AppThemeType.grimorio) {
|
|
||||||
_drawGrimorioBox(canvas, rect, box.owner == Player.red ? theme.playerRed : theme.playerBlue);
|
|
||||||
} else {
|
|
||||||
canvas.drawRect(rect, boxPaint);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (box.hiddenJokerOwner != null) {
|
|
||||||
Color jokerColor = box.hiddenJokerOwner == Player.red ? theme.playerRed : theme.playerBlue;
|
|
||||||
|
|
||||||
if (box.isJokerRevealed) {
|
|
||||||
_drawIconInBox(canvas, rect, ThemeIcons.joker(themeType), jokerColor);
|
|
||||||
} else {
|
|
||||||
bool canSee = false;
|
|
||||||
if (isOnline || isVsCPU) {
|
|
||||||
canSee = box.hiddenJokerOwner == myPlayer;
|
|
||||||
} else {
|
|
||||||
canSee = false;
|
|
||||||
}
|
|
||||||
if (canSee) {
|
|
||||||
_drawIconInBox(canvas, rect, ThemeIcons.joker(themeType), jokerColor.withOpacity(0.3));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
drawDot(box.top.p1); drawDot(box.top.p2); drawDot(box.bottom.p2); drawDot(box.bottom.p1);
|
||||||
|
|
||||||
if (box.type == BoxType.gold) {
|
// --- IL CUBO SOLIDO ---
|
||||||
_drawIconInBox(canvas, rect, ThemeIcons.gold(themeType), Colors.amber);
|
if (isOwned) {
|
||||||
} else if (box.type == BoxType.bomb) {
|
// Calcoliamo i 4 vertici del TETTO (Z + 1)
|
||||||
_drawIconInBox(canvas, rect, ThemeIcons.bomb(themeType), themeType == AppThemeType.cyberpunk || themeType == AppThemeType.arcade || themeType == AppThemeType.music ? Colors.greenAccent : Colors.deepPurple);
|
List<Offset> topCorners = [
|
||||||
} else if (box.type == BoxType.swap) {
|
projectLogical(box.x.toDouble(), box.y.toDouble(), currentZ + 1.0, size),
|
||||||
_drawIconInBox(canvas, rect, ThemeIcons.swap(themeType), Colors.purpleAccent);
|
projectLogical(box.x + 1.0, box.y.toDouble(), currentZ + 1.0, size),
|
||||||
} else if (box.type == BoxType.ice) {
|
projectLogical(box.x + 1.0, box.y + 1.0, currentZ + 1.0, size),
|
||||||
_drawIconInBox(canvas, rect, ThemeIcons.ice(themeType), Colors.cyanAccent);
|
projectLogical(box.x.toDouble(), box.y + 1.0, currentZ + 1.0, size),
|
||||||
} else if (box.type == BoxType.multiplier) {
|
];
|
||||||
_drawIconInBox(canvas, rect, ThemeIcons.multiplier(themeType), Colors.yellowAccent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var line in board.lines) {
|
// Algoritmo Geometrico Infallibile: troviamo la silhouette
|
||||||
if (!line.isPlayable) continue;
|
topCorners.sort((a, b) => a.dy.compareTo(b.dy));
|
||||||
|
Offset screenTop = topCorners[0]; // Il punto più alto sullo schermo
|
||||||
|
Offset screenBottom = topCorners[3]; // Il punto più basso sullo schermo
|
||||||
|
Offset screenLeft = topCorners[1].dx < topCorners[2].dx ? topCorners[1] : topCorners[2];
|
||||||
|
Offset screenRight = topCorners[1].dx > topCorners[2].dx ? topCorners[1] : topCorners[2];
|
||||||
|
|
||||||
Offset p1 = getScreenPos(line.p1.x, line.p1.y);
|
Color baseColor = box.owner == Player.red ? theme.playerRed : theme.playerBlue;
|
||||||
Offset p2 = getScreenPos(line.p2.x, line.p2.y);
|
|
||||||
|
|
||||||
// --- DISEGNO DELLA LINEA "INCRINATA" DAL GHIACCIO ---
|
// PARETE SINISTRA: Dal tetto scende verso il basso
|
||||||
if (line.isIceCracked) {
|
Path leftWall = Path()
|
||||||
_drawCrackedIceLine(canvas, p1, p2, blinkValue);
|
..moveTo(screenLeft.dx, screenLeft.dy)
|
||||||
continue; // Non ha ancora un proprietario, passiamo alla prossima!
|
..lineTo(screenBottom.dx, screenBottom.dy)
|
||||||
}
|
..lineTo(screenBottom.dx, screenBottom.dy + visualZHeight)
|
||||||
|
..lineTo(screenLeft.dx, screenLeft.dy + visualZHeight)
|
||||||
|
..close();
|
||||||
|
canvas.drawPath(leftWall, Paint()..color = _darken(baseColor, 0.15)..style = PaintingStyle.fill);
|
||||||
|
canvas.drawPath(leftWall, Paint()..color = Colors.black.withOpacity(0.3)..style = PaintingStyle.stroke..strokeWidth = 1.0);
|
||||||
|
|
||||||
bool isLastMove = (line == board.lastMove);
|
// PARETE DESTRA: Dal tetto scende verso il basso
|
||||||
Color lineColor = line.owner == Player.none
|
Path rightWall = Path()
|
||||||
? theme.gridLine.withOpacity(0.4)
|
..moveTo(screenBottom.dx, screenBottom.dy)
|
||||||
: (line.owner == Player.red ? theme.playerRed : theme.playerBlue);
|
..lineTo(screenRight.dx, screenRight.dy)
|
||||||
|
..lineTo(screenRight.dx, screenRight.dy + visualZHeight)
|
||||||
|
..lineTo(screenBottom.dx, screenBottom.dy + visualZHeight)
|
||||||
|
..close();
|
||||||
|
canvas.drawPath(rightWall, Paint()..color = _darken(baseColor, 0.35)..style = PaintingStyle.fill);
|
||||||
|
canvas.drawPath(rightWall, Paint()..color = Colors.black.withOpacity(0.3)..style = PaintingStyle.stroke..strokeWidth = 1.0);
|
||||||
|
|
||||||
if (isLastMove && line.owner != Player.none && themeType != AppThemeType.wood && themeType != AppThemeType.cyberpunk && themeType != AppThemeType.arcade && themeType != AppThemeType.grimorio) {
|
// IL TETTO: Disegnato per ultimo, copre i muri ed è solido
|
||||||
canvas.drawLine(p1, p2, Paint()..color = Colors.white.withOpacity(blinkValue * 0.5)..strokeWidth = 16.0..strokeCap = StrokeCap.round..maskFilter = const MaskFilter.blur(BlurStyle.normal, 6.0));
|
Path roof = Path()..moveTo(screenTop.dx, screenTop.dy)..lineTo(screenRight.dx, screenRight.dy)..lineTo(screenBottom.dx, screenBottom.dy)..lineTo(screenLeft.dx, screenLeft.dy)..close();
|
||||||
}
|
canvas.drawPath(roof, Paint()..color = baseColor..style = PaintingStyle.fill);
|
||||||
|
canvas.drawPath(roof, Paint()..color = Colors.white.withOpacity(0.5)..style = PaintingStyle.stroke..strokeWidth = 2.0);
|
||||||
|
|
||||||
if (themeType == AppThemeType.wood) {
|
_drawBoxIcon(canvas, roof, box);
|
||||||
if (line.owner == Player.none) {
|
|
||||||
canvas.drawLine(p1, p2, Paint()..color = const Color(0xFF3E2723).withOpacity(0.3)..strokeWidth = 4.5..strokeCap = StrokeCap.round);
|
} else if (isPlayable) {
|
||||||
} else {
|
// PAVIMENTO VUOTO
|
||||||
Color headColor = lineColor;
|
Offset f0 = projectLogical(box.x.toDouble(), box.y.toDouble(), currentZ.toDouble(), size);
|
||||||
if (isLastMove) headColor = Color.lerp(headColor, Colors.yellow, blinkValue * 0.8) ?? headColor;
|
Offset f1 = projectLogical(box.x + 1.0, box.y.toDouble(), currentZ.toDouble(), size);
|
||||||
_drawRealisticMatch(canvas, p1, p2, headColor, isLastMove: isLastMove, blinkValue: blinkValue);
|
Offset f2 = projectLogical(box.x + 1.0, box.y + 1.0, currentZ.toDouble(), size);
|
||||||
|
Offset f3 = projectLogical(box.x.toDouble(), box.y + 1.0, currentZ.toDouble(), size);
|
||||||
|
Path floor = Path()..moveTo(f0.dx, f0.dy)..lineTo(f1.dx, f1.dy)..lineTo(f2.dx, f2.dy)..lineTo(f3.dx, f3.dy)..close();
|
||||||
|
|
||||||
|
canvas.drawPath(floor, Paint()..color = Colors.white.withOpacity(0.08)..style = PaintingStyle.fill);
|
||||||
|
_drawBoxIcon(canvas, floor, box);
|
||||||
}
|
}
|
||||||
} else if (themeType == AppThemeType.cyberpunk) {
|
|
||||||
_drawNeonLine(canvas, p1, p2, lineColor, line.owner != Player.none, isLastMove: isLastMove, blinkValue: blinkValue);
|
|
||||||
} else if (themeType == AppThemeType.doodle) {
|
|
||||||
Color doodleColor = line.owner == Player.none ? Colors.black.withOpacity(0.05) : lineColor;
|
|
||||||
if (isLastMove && line.owner != Player.none) doodleColor = Color.lerp(doodleColor, Colors.black, blinkValue * 0.4) ?? doodleColor;
|
|
||||||
_drawWobblyLine(canvas, p1, p2, doodleColor, line.owner != Player.none, isLastMove: isLastMove, blinkValue: blinkValue);
|
|
||||||
} else if (themeType == AppThemeType.arcade) {
|
|
||||||
_drawArcadeLine(canvas, p1, p2, lineColor, line.owner != Player.none, isLastMove: isLastMove, blinkValue: blinkValue);
|
|
||||||
} else if (themeType == AppThemeType.grimorio) {
|
|
||||||
_drawGrimorioLine(canvas, p1, p2, lineColor, line.owner != Player.none, isLastMove: isLastMove, blinkValue: blinkValue);
|
|
||||||
} else if (themeType == AppThemeType.music) {
|
|
||||||
// Linee nere per la base nel tema musica
|
|
||||||
if (line.owner == Player.none) lineColor = Colors.black.withOpacity(0.4);
|
|
||||||
canvas.drawLine(p1, p2, Paint()..color = lineColor..strokeWidth = isLastMove ? 6.0 + (2.0 * blinkValue) : 6.0..strokeCap = StrokeCap.round);
|
|
||||||
} else {
|
|
||||||
if (isLastMove && line.owner != Player.none) lineColor = Color.lerp(lineColor, Colors.white, blinkValue * 0.5) ?? lineColor;
|
|
||||||
canvas.drawLine(p1, p2, Paint()..color = lineColor..strokeWidth = isLastMove ? 6.0 + (2.0 * blinkValue) : 6.0..strokeCap = StrokeCap.round);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final dotPaint = Paint()..style = PaintingStyle.fill;
|
|
||||||
Set<Dot> activeDots = {};
|
|
||||||
for (var line in board.lines) {
|
|
||||||
if (line.isPlayable) {
|
|
||||||
activeDots.add(line.p1); activeDots.add(line.p2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var dot in activeDots) {
|
|
||||||
Offset pos = getScreenPos(dot.x, dot.y);
|
|
||||||
if (themeType == AppThemeType.wood) {
|
|
||||||
canvas.drawCircle(pos, 3.5, dotPaint..color = const Color(0xFF3E2723).withOpacity(0.2));
|
|
||||||
} else if (themeType == AppThemeType.cyberpunk) {
|
|
||||||
canvas.drawCircle(pos, 6.0, Paint()..color = theme.gridLine.withOpacity(0.3));
|
|
||||||
canvas.drawCircle(pos, 3.0, Paint()..color = Colors.white.withOpacity(0.5));
|
|
||||||
} else if (themeType == AppThemeType.doodle) {
|
|
||||||
canvas.drawRect(Rect.fromCenter(center: pos, width: 4, height: 4), dotPaint..color = Colors.black.withOpacity(0.25));
|
|
||||||
} else if (themeType == AppThemeType.arcade) {
|
|
||||||
canvas.drawRect(Rect.fromCenter(center: pos, width: 8, height: 8), dotPaint..color = theme.gridLine.withOpacity(0.9));
|
|
||||||
canvas.drawRect(Rect.fromCenter(center: pos, width: 4, height: 4), dotPaint..color = theme.background);
|
|
||||||
} else if (themeType == AppThemeType.grimorio) {
|
|
||||||
canvas.drawCircle(pos, 6.0, Paint()..color = theme.gridLine.withOpacity(0.3)..maskFilter = const MaskFilter.blur(BlurStyle.normal, 3.0));
|
|
||||||
Path crystal = Path()..moveTo(pos.dx, pos.dy - 5)..lineTo(pos.dx + 3, pos.dy)..lineTo(pos.dx, pos.dy + 5)..lineTo(pos.dx - 3, pos.dy)..close();
|
|
||||||
canvas.drawPath(crystal, dotPaint..color = theme.gridLine.withOpacity(0.8));
|
|
||||||
} else if (themeType == AppThemeType.music) {
|
|
||||||
// Pallini (dots) neri per staccare dal fondo chiaro
|
|
||||||
canvas.drawCircle(pos, 4.5, dotPaint..color = Colors.black87);
|
|
||||||
} else {
|
|
||||||
canvas.drawCircle(pos, 5.0, dotPaint..color = theme.text.withOpacity(0.6));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _drawIconInBox(Canvas canvas, Rect rect, IconData icon, Color color) {
|
void _paint2D(Canvas canvas, Size size) {
|
||||||
TextPainter textPainter = TextPainter(textDirection: TextDirection.ltr);
|
for (var box in board.boxes) {
|
||||||
textPainter.text = TextSpan(
|
if (box.type == BoxType.invisible) continue;
|
||||||
text: String.fromCharCode(icon.codePoint),
|
Offset p1 = projectLogical(box.top.p1.x.toDouble(), box.top.p1.y.toDouble(), 0, size);
|
||||||
style: TextStyle(
|
Offset p2 = projectLogical(box.top.p2.x.toDouble(), box.top.p2.y.toDouble(), 0, size);
|
||||||
color: themeType == AppThemeType.arcade ? color : color.withOpacity(0.7),
|
Offset p3 = projectLogical(box.bottom.p2.x.toDouble(), box.bottom.p2.y.toDouble(), 0, size);
|
||||||
fontSize: rect.width * 0.45,
|
Offset p4 = projectLogical(box.bottom.p1.x.toDouble(), box.bottom.p1.y.toDouble(), 0, size);
|
||||||
fontFamily: icon.fontFamily,
|
Path poly = Path()..moveTo(p1.dx, p1.dy)..lineTo(p2.dx, p2.dy)..lineTo(p3.dx, p3.dy)..lineTo(p4.dx, p4.dy)..close();
|
||||||
package: icon.fontPackage,
|
|
||||||
shadows: themeType == AppThemeType.arcade ? [] : [Shadow(color: color.withOpacity(0.6), blurRadius: 10, offset: const Offset(0, 0))]
|
if (box.owner != Player.none) {
|
||||||
),
|
Color c = box.owner == Player.red ? theme.playerRed : theme.playerBlue;
|
||||||
);
|
canvas.drawPath(poly, Paint()..color = c.withOpacity(0.85)..style = PaintingStyle.fill);
|
||||||
textPainter.layout();
|
}
|
||||||
textPainter.paint(canvas, Offset(rect.center.dx - textPainter.width / 2, rect.center.dy - textPainter.height / 2));
|
_drawBoxIcon(canvas, poly, box);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var line in board.lines) {
|
||||||
|
if (!line.isPlayable && line.owner == Player.none) continue;
|
||||||
|
Offset p1 = projectLogical(line.p1.x.toDouble(), line.p1.y.toDouble(), 0, size);
|
||||||
|
Offset p2 = projectLogical(line.p2.x.toDouble(), line.p2.y.toDouble(), 0, size);
|
||||||
|
|
||||||
|
if (line.isIceCracked) { _drawCrackedIceLine(canvas, p1, p2, blinkValue); continue; }
|
||||||
|
Color lineColor = line.owner == Player.none ? theme.gridLine.withOpacity(0.4) : (line.owner == Player.red ? theme.playerRed : theme.playerBlue);
|
||||||
|
if (line == board.lastMove && line.owner != Player.none) canvas.drawLine(p1, p2, Paint()..color = Colors.white.withOpacity(blinkValue * 0.8)..strokeWidth = 12.0..strokeCap = StrokeCap.round..maskFilter = const MaskFilter.blur(BlurStyle.normal, 4.0));
|
||||||
|
canvas.drawLine(p1, p2, Paint()..color = lineColor..strokeWidth = (line.owner == Player.none ? 3.0 : 6.0)..strokeCap = StrokeCap.round);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var dot in board.dots) {
|
||||||
|
bool isVisible = board.lines.any((l) => (l.p1 == dot || l.p2 == dot) && l.isPlayable);
|
||||||
|
if (isVisible) canvas.drawCircle(projectLogical(dot.x.toDouble(), dot.y.toDouble(), 0, size), 4.0, Paint()..color = theme.text.withOpacity(0.8));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _drawBoxIcon(Canvas canvas, Path face, Box box) {
|
||||||
|
if (box.type == BoxType.gold) _drawIconOnPath(canvas, face, FontAwesomeIcons.crown, Colors.amber);
|
||||||
|
else if (box.type == BoxType.bomb) _drawIconOnPath(canvas, face, FontAwesomeIcons.skull, Colors.redAccent);
|
||||||
|
else if (box.type == BoxType.swap) _drawIconOnPath(canvas, face, FontAwesomeIcons.arrowsRotate, Colors.purpleAccent);
|
||||||
|
else if (box.type == BoxType.multiplier) _drawIconOnPath(canvas, face, FontAwesomeIcons.bolt, Colors.yellowAccent);
|
||||||
|
else if (box.type == BoxType.ice && box.owner == Player.none) {
|
||||||
|
canvas.drawPath(face, Paint()..color = Colors.cyanAccent.withOpacity(0.15)..style = PaintingStyle.fill);
|
||||||
|
_drawIconOnPath(canvas, face, FontAwesomeIcons.snowflake, Colors.cyanAccent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _drawIconOnPath(Canvas canvas, Path path, IconData icon, Color color) {
|
||||||
|
Rect bounds = path.getBounds();
|
||||||
|
TextPainter tp = TextPainter(text: TextSpan(text: String.fromCharCode(icon.codePoint), style: TextStyle(color: color, fontSize: 16, fontFamily: icon.fontFamily, package: icon.fontPackage)), textDirection: TextDirection.ltr)..layout();
|
||||||
|
tp.paint(canvas, Offset(bounds.center.dx - tp.width / 2, bounds.center.dy - tp.height / 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
void _drawCrackedIceLine(Canvas canvas, Offset p1, Offset p2, double blink) {
|
void _drawCrackedIceLine(Canvas canvas, Offset p1, Offset p2, double blink) {
|
||||||
Paint crackPaint = Paint()
|
Paint crackPaint = Paint()..color = Colors.cyanAccent.withOpacity(0.6 + (0.4 * blink))..strokeWidth = 3.0..style = PaintingStyle.stroke..strokeCap = StrokeCap.round..maskFilter = const MaskFilter.blur(BlurStyle.solid, 2.0);
|
||||||
..color = Colors.cyanAccent.withOpacity(0.6 + (0.4 * blink))
|
|
||||||
..strokeWidth = 3.0
|
|
||||||
..style = PaintingStyle.stroke
|
|
||||||
..strokeCap = StrokeCap.round
|
|
||||||
..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);
|
||||||
|
double dx = p2.dx - p1.dx; double dy = p2.dy - p1.dy;
|
||||||
Vector2 dir = Vector2(p2.dx - p1.dx, p2.dy - p1.dy);
|
double len = sqrt(dx * dx + dy * dy);
|
||||||
double len = dir.length; Vector2 ndir = dir.normalized(); Vector2 perp = Vector2(-ndir.y, ndir.x);
|
if (len == 0) return;
|
||||||
|
double ndx = dx / len; double ndy = dy / len;
|
||||||
Path crack = Path()..moveTo(p1.dx, p1.dy);
|
Path crack = Path()..moveTo(p1.dx, p1.dy);
|
||||||
int zigzags = 6;
|
for (int i = 1; i < 6; i++) {
|
||||||
for (int i=1; i<zigzags; i++) {
|
|
||||||
double d = len * (i / zigzags);
|
|
||||||
Offset basePt = Offset(p1.dx + ndir.x * d, p1.dy + ndir.y * d);
|
|
||||||
double offset = (i % 2 == 0 ? 3.0 : -3.0);
|
double offset = (i % 2 == 0 ? 3.0 : -3.0);
|
||||||
crack.lineTo(basePt.dx + perp.x * offset, basePt.dy + perp.y * offset);
|
crack.lineTo(p1.dx + ndx * (len * (i / 6)) + (-ndy) * offset, p1.dy + ndy * (len * (i / 6)) + ndx * offset);
|
||||||
}
|
}
|
||||||
crack.lineTo(p2.dx, p2.dy);
|
crack.lineTo(p2.dx, p2.dy);
|
||||||
canvas.drawPath(crack, crackPaint);
|
canvas.drawPath(crack, crackPaint);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _drawArcadeBox(Canvas canvas, Rect rect, Color color) {
|
|
||||||
double pixelSize = 4.0; Paint paint = Paint()..color = color.withOpacity(0.9)..style = PaintingStyle.fill;
|
|
||||||
for (double y = rect.top; y < rect.bottom; y += pixelSize) {
|
|
||||||
for (double x = rect.left; x < rect.right; x += pixelSize) {
|
|
||||||
int xi = ((x - rect.left) / pixelSize).floor(); int yi = ((y - rect.top) / pixelSize).floor();
|
|
||||||
if ((xi + yi) % 2 == 0) canvas.drawRect(Rect.fromLTWH(x, y, pixelSize, pixelSize), paint);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
canvas.drawRect(rect.deflate(2.0), Paint()..color = Colors.white.withOpacity(0.4)..style = PaintingStyle.stroke..strokeWidth = 2.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _drawGrimorioBox(Canvas canvas, Rect rect, Color color) {
|
|
||||||
canvas.drawRect(rect, Paint()..color = color.withOpacity(0.15)..style=PaintingStyle.fill);
|
|
||||||
Offset c = rect.center; double r = rect.width * 0.35;
|
|
||||||
Paint linePaint = Paint()..color = color.withOpacity(0.8)..style = PaintingStyle.stroke..strokeWidth = 1.5..maskFilter = const MaskFilter.blur(BlurStyle.solid, 1.0);
|
|
||||||
canvas.drawCircle(c, r, linePaint); canvas.drawCircle(c, r * 0.8, linePaint..strokeWidth = 0.5);
|
|
||||||
Path p = Path();
|
|
||||||
for(int i=0; i<3; i++) {
|
|
||||||
double a = -pi/2 + i * 2*pi/3; Offset pt = Offset(c.dx + r*cos(a), c.dy + r*sin(a));
|
|
||||||
if(i==0) p.moveTo(pt.dx, pt.dy); else p.lineTo(pt.dx, pt.dy);
|
|
||||||
}
|
|
||||||
p.close(); canvas.drawPath(p, linePaint..strokeWidth = 1.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _drawArcadeLine(Canvas canvas, Offset p1, Offset p2, Color color, bool isConquered, {bool isLastMove = false, double blinkValue = 0.0}) {
|
|
||||||
double pixelSize = 6.0; Vector2 dir = Vector2(p2.dx - p1.dx, p2.dy - p1.dy); double len = dir.length; Vector2 ndir = dir.normalized();
|
|
||||||
Paint paint = Paint()..color = isConquered ? color : color.withOpacity(0.15)..style = PaintingStyle.fill;
|
|
||||||
Paint highlight = Paint()..color = Colors.white.withOpacity(0.6)..style = PaintingStyle.fill;
|
|
||||||
for(double d = 0; d <= len; d += pixelSize + 1.0) {
|
|
||||||
Offset pt = Offset(p1.dx + ndir.x * d, p1.dy + ndir.y * d);
|
|
||||||
canvas.drawRect(Rect.fromCenter(center: pt, width: pixelSize, height: pixelSize), paint);
|
|
||||||
if (isConquered && (d / (pixelSize+1.0)).floor() % 3 == 0) canvas.drawRect(Rect.fromCenter(center: pt - const Offset(1,1), width: pixelSize*0.4, height: pixelSize*0.4), highlight);
|
|
||||||
}
|
|
||||||
if (isLastMove && isConquered) canvas.drawRect(Rect.fromPoints(p1, p2).inflate(4.0), Paint()..color = Colors.white.withOpacity(blinkValue*0.4)..style=PaintingStyle.stroke..strokeWidth=2.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _drawGrimorioLine(Canvas canvas, Offset p1, Offset p2, Color color, bool isConquered, {bool isLastMove = false, double blinkValue = 0.0}) {
|
|
||||||
if (!isConquered) { canvas.drawLine(p1, p2, Paint()..color = color.withOpacity(0.15)..strokeWidth = 2.0..strokeCap = StrokeCap.round); return; }
|
|
||||||
canvas.drawLine(p1, p2, Paint()..color = color.withOpacity(0.6)..strokeWidth = 5.0..strokeCap = StrokeCap.round..maskFilter = const MaskFilter.blur(BlurStyle.normal, 4.0));
|
|
||||||
canvas.drawLine(p1, p2, Paint()..color = Colors.white.withOpacity(0.7)..strokeWidth = 1.5..strokeCap = StrokeCap.round);
|
|
||||||
int seed = (p1.dx * 1000 + p1.dy).toInt(); Random rand = Random(seed);
|
|
||||||
Vector2 dir = Vector2(p2.dx - p1.dx, p2.dy - p1.dy); double len = dir.length; Vector2 ndir = dir.normalized(); Vector2 perp = Vector2(-ndir.y, ndir.x);
|
|
||||||
Path thread1 = Path(); Path thread2 = Path(); int segments = 15; double step = len / segments;
|
|
||||||
double phaseOffset = (isLastMove ? blinkValue * pi * 4 : 0) + rand.nextDouble()*pi;
|
|
||||||
for(int i = 0; i <= segments; i++) {
|
|
||||||
double d = i * step; Offset basePt = Offset(p1.dx + ndir.x * d, p1.dy + ndir.y * d);
|
|
||||||
double amplitude = 3.5; double wave1 = sin(d * 0.15 + phaseOffset) * amplitude; double wave2 = cos(d * 0.15 + phaseOffset) * amplitude;
|
|
||||||
Offset pt1 = basePt + Offset(perp.x * wave1, perp.y * wave1); Offset pt2 = basePt + Offset(perp.x * wave2, perp.y * wave2);
|
|
||||||
if (i == 0) { thread1.moveTo(pt1.dx, pt1.dy); thread2.moveTo(pt2.dx, pt2.dy); } else { thread1.lineTo(pt1.dx, pt1.dy); thread2.lineTo(pt2.dx, pt2.dy); }
|
|
||||||
}
|
|
||||||
Paint threadPaint = Paint()..color = color.withOpacity(0.9)..style = PaintingStyle.stroke..strokeWidth = 1.5..maskFilter = const MaskFilter.blur(BlurStyle.solid, 1.0);
|
|
||||||
canvas.drawPath(thread1, threadPaint); canvas.drawPath(thread2, threadPaint..color = Colors.white.withOpacity(0.5));
|
|
||||||
}
|
|
||||||
|
|
||||||
void _drawFlameBox(Canvas canvas, Rect baseRect, bool isRed) {
|
|
||||||
final rand = Random((baseRect.left + baseRect.top).toInt());
|
|
||||||
Offset center = baseRect.center; double w = baseRect.width * 0.35; double h = baseRect.height * 0.55; Offset bottomCenter = Offset(center.dx, center.dy + h * 0.5);
|
|
||||||
Color outerColor = isRed ? Colors.red.shade600.withOpacity(0.85) : Colors.blue.shade700.withOpacity(0.85); Color midColor = isRed ? Colors.orangeAccent : Colors.lightBlueAccent; Color coreColor = isRed ? Colors.yellowAccent : Colors.white;
|
|
||||||
canvas.drawOval(Rect.fromCenter(center: bottomCenter, width: w * 1.5, height: w * 0.5), Paint()..color = Colors.black.withOpacity(0.4)..maskFilter = const MaskFilter.blur(BlurStyle.normal, 4.0));
|
|
||||||
void drawFlameLayer(double scale, Color color, double tipOffsetX) {
|
|
||||||
Path path = Path(); double fw = w * scale; double fh = h * scale;
|
|
||||||
path.moveTo(bottomCenter.dx, bottomCenter.dy); path.cubicTo(bottomCenter.dx + fw, bottomCenter.dy, bottomCenter.dx + fw * 0.8, bottomCenter.dy - fh * 0.6, bottomCenter.dx + tipOffsetX, bottomCenter.dy - fh); path.cubicTo(bottomCenter.dx - fw * 0.8, bottomCenter.dy - fh * 0.6, bottomCenter.dx - fw, bottomCenter.dy, bottomCenter.dx, bottomCenter.dy);
|
|
||||||
canvas.drawPath(path, Paint()..color = color..style = PaintingStyle.fill..maskFilter = const MaskFilter.blur(BlurStyle.normal, 1.5));
|
|
||||||
}
|
|
||||||
double randomTipX = (rand.nextDouble() - 0.5) * w * 0.8; drawFlameLayer(1.0, outerColor, randomTipX); drawFlameLayer(0.65, midColor.withOpacity(0.9), randomTipX * 0.6); drawFlameLayer(0.35, coreColor.withOpacity(0.9), randomTipX * 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _drawScribbleBox(Canvas canvas, Rect baseRect, Color color) {
|
|
||||||
final rand = Random((baseRect.left + baseRect.top).toInt());
|
|
||||||
final paint = Paint()..color = color.withOpacity(0.85)..style = PaintingStyle.stroke..strokeWidth = 3.5..strokeCap = StrokeCap.round..strokeJoin = StrokeJoin.round;
|
|
||||||
final path = Path(); Rect rect = baseRect.deflate(4.0); int numZigs = 15 + rand.nextInt(6); double stepY = rect.height / numZigs;
|
|
||||||
path.moveTo(rect.left + rand.nextDouble() * 5, rect.top + rand.nextDouble() * 5);
|
|
||||||
for (int i = 1; i <= numZigs; i++) { double targetX = (i % 2 != 0) ? rect.right + (rand.nextDouble() * 4 - 2) : rect.left + (rand.nextDouble() * 4 - 2); double targetY = rect.top + stepY * i + (rand.nextDouble() - 0.5) * 3; double ctrlX = rect.center.dx + (rand.nextDouble() - 0.5) * 20; double ctrlY = targetY - stepY / 2; path.quadraticBezierTo(ctrlX, ctrlY, targetX, targetY); }
|
|
||||||
canvas.drawPath(path, paint);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _drawRealisticMatch(Canvas canvas, Offset p1, Offset p2, Color headColor, {bool isLastMove = false, double blinkValue = 0.0}) {
|
|
||||||
int seed = (p1.dx * 1000 + p1.dy).toInt(); Random rand = Random(seed); Vector2 dir = Vector2(p2.dx - p1.dx, p2.dy - p1.dy).normalized(); double shrink = 8.0; Offset start = Offset(p1.dx + dir.x * shrink, p1.dy + dir.y * shrink); Offset end = Offset(p2.dx - dir.x * shrink, p2.dy - dir.y * shrink); start += Offset(rand.nextDouble() * 4 - 2, rand.nextDouble() * 4 - 2); end += Offset(rand.nextDouble() * 4 - 2, rand.nextDouble() * 4 - 2); bool headAtEnd = rand.nextBool(); Offset headPos = headAtEnd ? end : start; Offset tailPos = headAtEnd ? start : end; Vector2 matchDir = Vector2(headPos.dx - tailPos.dx, headPos.dy - tailPos.dy).normalized();
|
|
||||||
canvas.drawLine(tailPos + const Offset(4, 4), headPos + const Offset(4, 4), Paint()..color = Colors.black.withOpacity(0.6)..strokeWidth = 7.0..strokeCap = StrokeCap.round);
|
|
||||||
if (isLastMove) { canvas.drawCircle(headPos, 8.0 + (blinkValue * 6.0), Paint()..color = Colors.orangeAccent.withOpacity(0.6 * blinkValue)..maskFilter = const MaskFilter.blur(BlurStyle.normal, 6.0)); }
|
|
||||||
canvas.drawLine(tailPos, headPos, Paint()..color = const Color(0xFF6D4C41)..strokeWidth = 7.0..strokeCap = StrokeCap.round); canvas.drawLine(tailPos, headPos, Paint()..color = const Color(0xFFEDC498)..strokeWidth = 4.0..strokeCap = StrokeCap.round); Offset burnPos = Offset(headPos.dx - matchDir.x * 8, headPos.dy - matchDir.y * 8); canvas.drawLine(burnPos, headPos, Paint()..color = const Color(0xFF2E1A14)..strokeWidth = 6.0..strokeCap = StrokeCap.round);
|
|
||||||
canvas.save(); canvas.translate(headPos.dx, headPos.dy); double angle = atan2(matchDir.y, matchDir.x); canvas.rotate(angle); Rect headOval = Rect.fromCenter(center: Offset.zero, width: 18.0, height: 13.0); canvas.drawOval(headOval.shift(const Offset(1, 2)), Paint()..color = Colors.black.withOpacity(0.6)); canvas.drawOval(headOval, Paint()..color = headColor); canvas.restore();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _drawNeonLine(Canvas canvas, Offset p1, Offset p2, Color color, bool isConquered, {bool isLastMove = false, double blinkValue = 0.0}) {
|
|
||||||
double mainWidth = isConquered ? (isLastMove ? 6.0 + (blinkValue * 3.0) : 6.0) : 3.0; Color coreColor = isConquered ? (isLastMove ? Color.lerp(Colors.white, color, 1.0 - blinkValue)! : Colors.white.withOpacity(0.9)) : color.withOpacity(0.6);
|
|
||||||
canvas.drawLine(p1, p2, Paint()..color = color.withOpacity(isConquered ? (isLastMove ? 0.4 + (0.4 * blinkValue) : 0.4) : 0.2)..strokeWidth = mainWidth * 4..strokeCap = StrokeCap.round..maskFilter = MaskFilter.blur(BlurStyle.normal, isConquered ? 12.0 : 6.0));
|
|
||||||
if (isConquered) { canvas.drawLine(p1, p2, Paint()..color = color.withOpacity(isLastMove ? 0.7 + (0.3 * blinkValue) : 0.7)..strokeWidth = mainWidth * 2..strokeCap = StrokeCap.round..maskFilter = const MaskFilter.blur(BlurStyle.normal, 6.0)); }
|
|
||||||
canvas.drawLine(p1, p2, Paint()..color = coreColor..strokeWidth = mainWidth..strokeCap = StrokeCap.round);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _drawWobblyLine(Canvas canvas, Offset p1, Offset p2, Color color, bool isConquered, {bool isLastMove = false, double blinkValue = 0.0}) {
|
|
||||||
final random = Random((p1.dx + p1.dy + p2.dx + p2.dy).toInt()); final dx = p2.dx - p1.dx; final dy = p2.dy - p1.dy;
|
|
||||||
double strokeW = isConquered ? (isLastMove ? 4.5 + (2.0 * blinkValue) : 4.5) : 2.0;
|
|
||||||
final basePaint = Paint()..color = color..strokeWidth = strokeW..style = PaintingStyle.stroke..strokeCap = StrokeCap.round;
|
|
||||||
final mid1 = Offset(p1.dx + dx / 2 + (random.nextDouble() - 0.5) * 8, p1.dy + dy / 2 + (random.nextDouble() - 0.5) * 8); canvas.drawPath(Path()..moveTo(p1.dx, p1.dy)..quadraticBezierTo(mid1.dx, mid1.dy, p2.dx, p2.dy), basePaint);
|
|
||||||
final mid2 = Offset(p1.dx + dx / 2 + (random.nextDouble() - 0.5) * 6, p1.dy + dy / 2 + (random.nextDouble() - 0.5) * 6); canvas.drawPath(Path()..moveTo(p1.dx, p1.dy)..quadraticBezierTo(mid2.dx, mid2.dy, p2.dx, p2.dy), basePaint..strokeWidth = strokeW * 0.5..color = color.withOpacity(0.8));
|
|
||||||
}
|
|
||||||
|
|
||||||
@override bool shouldRepaint(covariant BoardPainter oldDelegate) => true;
|
@override bool shouldRepaint(covariant BoardPainter oldDelegate) => true;
|
||||||
}
|
}
|
||||||
|
|
||||||
class Vector2 {
|
|
||||||
final double x, y; Vector2(this.x, this.y); double get length => sqrt(x * x + y * y);
|
|
||||||
Vector2 normalized() { double l = length; return l == 0 ? Vector2(0, 0) : Vector2(x / l, y / l); }
|
|
||||||
}
|
|
||||||
|
|
@ -5,16 +5,15 @@
|
||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
import 'dart:math' as math;
|
import 'dart:math' as math;
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
import '../../logic/game_controller.dart';
|
import '../../logic/game_controller.dart';
|
||||||
import '../../core/theme_manager.dart';
|
import '../../core/theme_manager.dart';
|
||||||
import '../../core/app_colors.dart';
|
import '../../core/app_colors.dart';
|
||||||
import '../../models/game_board.dart';
|
|
||||||
import 'board_painter.dart';
|
import 'board_painter.dart';
|
||||||
import 'score_board.dart';
|
import 'score_board.dart';
|
||||||
import 'package:google_fonts/google_fonts.dart';
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
import '../../services/storage_service.dart';
|
|
||||||
|
|
||||||
TextStyle _getTextStyle(AppThemeType themeType, TextStyle baseStyle) {
|
TextStyle _getTextStyle(AppThemeType themeType, TextStyle baseStyle) {
|
||||||
if (themeType == AppThemeType.doodle) {
|
if (themeType == AppThemeType.doodle) {
|
||||||
|
|
@ -26,8 +25,6 @@ TextStyle _getTextStyle(AppThemeType themeType, TextStyle baseStyle) {
|
||||||
));
|
));
|
||||||
} else if (themeType == AppThemeType.grimorio) {
|
} else if (themeType == AppThemeType.grimorio) {
|
||||||
return GoogleFonts.cinzelDecorative(textStyle: baseStyle.copyWith(fontWeight: FontWeight.bold));
|
return GoogleFonts.cinzelDecorative(textStyle: baseStyle.copyWith(fontWeight: FontWeight.bold));
|
||||||
} else if (themeType == AppThemeType.music) {
|
|
||||||
return GoogleFonts.audiowide(textStyle: baseStyle.copyWith(letterSpacing: 1.5));
|
|
||||||
}
|
}
|
||||||
return baseStyle;
|
return baseStyle;
|
||||||
}
|
}
|
||||||
|
|
@ -48,6 +45,8 @@ class _GameScreenState extends State<GameScreen> with TickerProviderStateMixin {
|
||||||
bool _wasSetupPhase = false;
|
bool _wasSetupPhase = false;
|
||||||
Player _lastJokerTurn = Player.red;
|
Player _lastJokerTurn = Player.red;
|
||||||
|
|
||||||
|
double _cameraAngle = 0.0; // La nostra telecamera fluida a 360 gradi
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
|
@ -59,30 +58,18 @@ class _GameScreenState extends State<GameScreen> with TickerProviderStateMixin {
|
||||||
|
|
||||||
void _showGameOverDialog(BuildContext context, GameController game, ThemeColors theme, AppThemeType themeType) {
|
void _showGameOverDialog(BuildContext context, GameController game, ThemeColors theme, AppThemeType themeType) {
|
||||||
_gameOverDialogShown = true;
|
_gameOverDialogShown = true;
|
||||||
|
|
||||||
showDialog(
|
showDialog(
|
||||||
barrierDismissible: false,
|
barrierDismissible: false, context: context,
|
||||||
context: context,
|
|
||||||
builder: (dialogContext) => Consumer<GameController>(
|
builder: (dialogContext) => Consumer<GameController>(
|
||||||
builder: (context, controller, child) {
|
builder: (context, controller, child) {
|
||||||
if (!controller.isGameOver) {
|
if (!controller.isGameOver) {
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) { if (_gameOverDialogShown) { _gameOverDialogShown = false; if (Navigator.canPop(dialogContext)) Navigator.pop(dialogContext); } });
|
||||||
if (_gameOverDialogShown) {
|
|
||||||
_gameOverDialogShown = false;
|
|
||||||
if (Navigator.canPop(dialogContext)) Navigator.pop(dialogContext);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return const SizedBox.shrink();
|
return const SizedBox.shrink();
|
||||||
}
|
}
|
||||||
|
|
||||||
int red = controller.board.scoreRed; int blue = controller.board.scoreBlue;
|
int red = controller.board.scoreRed; int blue = controller.board.scoreBlue;
|
||||||
bool playerBeatCPU = controller.isVsCPU && red > blue;
|
bool playerBeatCPU = controller.isVsCPU && red > blue;
|
||||||
|
String nameRed = controller.isOnline ? controller.onlineHostName.toUpperCase() : "TU";
|
||||||
String myName = StorageService.instance.playerName.toUpperCase();
|
String nameBlue = controller.isOnline ? controller.onlineGuestName.toUpperCase() : (themeType == AppThemeType.cyberpunk || themeType == AppThemeType.arcade ? "VERDE" : "BLU");
|
||||||
if (myName.isEmpty) myName = "TU";
|
|
||||||
|
|
||||||
String nameRed = controller.isOnline ? controller.onlineHostName.toUpperCase() : myName;
|
|
||||||
String nameBlue = controller.isOnline ? controller.onlineGuestName.toUpperCase() : (themeType == AppThemeType.cyberpunk || themeType == AppThemeType.arcade || themeType == AppThemeType.music ? "VERDE" : "BLU");
|
|
||||||
if (controller.isVsCPU) nameBlue = "CPU";
|
if (controller.isVsCPU) nameBlue = "CPU";
|
||||||
|
|
||||||
String winnerText = ""; Color winnerColor = theme.text;
|
String winnerText = ""; Color winnerColor = theme.text;
|
||||||
|
|
@ -91,8 +78,7 @@ class _GameScreenState extends State<GameScreen> with TickerProviderStateMixin {
|
||||||
else { winnerText = "PAREGGIO!"; winnerColor = theme.text; }
|
else { winnerText = "PAREGGIO!"; winnerColor = theme.text; }
|
||||||
|
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
backgroundColor: theme.background,
|
backgroundColor: theme.background, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20), side: BorderSide(color: winnerColor.withOpacity(0.5), width: 2)),
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20), side: BorderSide(color: winnerColor.withOpacity(0.5), width: 2)),
|
|
||||||
title: Text("FINE PARTITA", textAlign: TextAlign.center, style: _getTextStyle(themeType, TextStyle(color: theme.text, fontWeight: FontWeight.bold, fontSize: 22))),
|
title: Text("FINE PARTITA", textAlign: TextAlign.center, style: _getTextStyle(themeType, TextStyle(color: theme.text, fontWeight: FontWeight.bold, fontSize: 22))),
|
||||||
content: Column(
|
content: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
|
@ -100,83 +86,49 @@ class _GameScreenState extends State<GameScreen> with TickerProviderStateMixin {
|
||||||
Text(winnerText, textAlign: TextAlign.center, style: _getTextStyle(themeType, TextStyle(fontSize: 26, fontWeight: FontWeight.w900, color: winnerColor))),
|
Text(winnerText, textAlign: TextAlign.center, style: _getTextStyle(themeType, TextStyle(fontSize: 26, fontWeight: FontWeight.w900, color: winnerColor))),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
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)),
|
child: Row(
|
||||||
child: FittedBox(
|
mainAxisSize: MainAxisSize.min,
|
||||||
fit: BoxFit.scaleDown,
|
children: [
|
||||||
child: Row(
|
Text("$nameRed: $red", style: _getTextStyle(themeType, TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: theme.playerRed))),
|
||||||
mainAxisSize: MainAxisSize.min,
|
Text(" - ", style: _getTextStyle(themeType, TextStyle(fontSize: 18, color: theme.text))),
|
||||||
children: [
|
Text("$nameBlue: $blue", style: _getTextStyle(themeType, TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: theme.playerBlue))),
|
||||||
Text("$nameRed: $red", style: _getTextStyle(themeType, TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: theme.playerRed))),
|
],
|
||||||
Text(" - ", style: _getTextStyle(themeType, TextStyle(fontSize: 18, color: theme.text))),
|
|
||||||
Text("$nameBlue: $blue", style: _getTextStyle(themeType, TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: theme.playerBlue))),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (controller.lastMatchXP > 0) ...[
|
if (controller.lastMatchXP > 0) ...[
|
||||||
const SizedBox(height: 15),
|
const SizedBox(height: 15),
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 6),
|
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 6),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(color: Colors.green.withOpacity(0.15), borderRadius: BorderRadius.circular(20), border: Border.all(color: Colors.greenAccent, width: 1.5), boxShadow: themeType == AppThemeType.cyberpunk ? [const BoxShadow(color: Colors.greenAccent, blurRadius: 10, spreadRadius: -5)] : []),
|
||||||
color: Colors.green.withOpacity(0.15),
|
|
||||||
borderRadius: BorderRadius.circular(20),
|
|
||||||
border: Border.all(color: Colors.greenAccent, width: 1.5),
|
|
||||||
boxShadow: (themeType == AppThemeType.cyberpunk || themeType == AppThemeType.music) ? [const BoxShadow(color: Colors.greenAccent, blurRadius: 10, spreadRadius: -5)] : [],
|
|
||||||
),
|
|
||||||
child: Text("+ ${controller.lastMatchXP} XP", style: _getTextStyle(themeType, const TextStyle(color: Colors.greenAccent, fontWeight: FontWeight.w900, fontSize: 16, letterSpacing: 1.5))),
|
child: Text("+ ${controller.lastMatchXP} XP", style: _getTextStyle(themeType, const TextStyle(color: Colors.greenAccent, fontWeight: FontWeight.w900, fontSize: 16, letterSpacing: 1.5))),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
||||||
if (controller.isVsCPU) ...[
|
if (controller.isVsCPU) ...[
|
||||||
const SizedBox(height: 15),
|
const SizedBox(height: 15),
|
||||||
Text("Difficoltà CPU: Livello ${controller.cpuLevel}", style: _getTextStyle(themeType, TextStyle(fontSize: 14, fontWeight: FontWeight.w500, color: theme.text.withOpacity(0.7)))),
|
Text("Difficoltà CPU: Livello ${controller.cpuLevel}", style: _getTextStyle(themeType, TextStyle(fontSize: 14, fontWeight: FontWeight.w500, color: theme.text.withOpacity(0.7)))),
|
||||||
],
|
],
|
||||||
if (controller.isOnline) ...[
|
if (controller.isOnline) ...[
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
if (controller.rematchRequested && !controller.opponentWantsRematch)
|
if (controller.rematchRequested && !controller.opponentWantsRematch) Text("In attesa di $nameBlue...", style: _getTextStyle(themeType, const TextStyle(color: Colors.amber, fontWeight: FontWeight.bold, fontStyle: FontStyle.italic))),
|
||||||
Text("In attesa di $nameBlue...", style: _getTextStyle(themeType, const TextStyle(color: Colors.amber, fontWeight: FontWeight.bold, fontStyle: FontStyle.italic))),
|
if (controller.opponentWantsRematch && !controller.rematchRequested) Text("$nameBlue vuole la rivincita!", style: _getTextStyle(themeType, const TextStyle(color: Colors.greenAccent, fontWeight: FontWeight.bold))),
|
||||||
if (controller.opponentWantsRematch && !controller.rematchRequested)
|
if (controller.rematchRequested && controller.opponentWantsRematch) Text("Avvio nuova partita...", style: _getTextStyle(themeType, const TextStyle(color: Colors.green, fontWeight: FontWeight.bold))),
|
||||||
Text("$nameBlue vuole la rivincita!", style: _getTextStyle(themeType, const TextStyle(color: Colors.greenAccent, fontWeight: FontWeight.bold))),
|
|
||||||
if (controller.rematchRequested && controller.opponentWantsRematch)
|
|
||||||
Text("Avvio nuova partita...", style: _getTextStyle(themeType, const TextStyle(color: Colors.green, fontWeight: FontWeight.bold))),
|
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
actionsPadding: const EdgeInsets.only(left: 20, right: 20, bottom: 20, top: 10),
|
actionsPadding: const EdgeInsets.only(left: 20, right: 20, bottom: 20, top: 10), actionsAlignment: MainAxisAlignment.center,
|
||||||
actionsAlignment: MainAxisAlignment.center,
|
|
||||||
actions: [
|
actions: [
|
||||||
Column(
|
Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
if (playerBeatCPU)
|
if (playerBeatCPU)
|
||||||
ElevatedButton(
|
ElevatedButton(style: ElevatedButton.styleFrom(backgroundColor: winnerColor, foregroundColor: Colors.white, padding: const EdgeInsets.symmetric(vertical: 15), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)), elevation: 5), onPressed: () { controller.increaseLevelAndRestart(); }, child: Text("PROSSIMO LIVELLO ➔", style: _getTextStyle(themeType, const TextStyle(fontWeight: FontWeight.bold, fontSize: 16))))
|
||||||
style: ElevatedButton.styleFrom(backgroundColor: winnerColor, foregroundColor: Colors.white, padding: const EdgeInsets.symmetric(vertical: 15), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)), elevation: 5),
|
|
||||||
onPressed: () { controller.increaseLevelAndRestart(); },
|
|
||||||
child: Text("PROSSIMO LIVELLO ➔", style: _getTextStyle(themeType, const TextStyle(fontWeight: FontWeight.bold, fontSize: 16))),
|
|
||||||
)
|
|
||||||
else if (controller.isOnline)
|
else if (controller.isOnline)
|
||||||
ElevatedButton(
|
ElevatedButton(style: ElevatedButton.styleFrom(backgroundColor: controller.rematchRequested ? Colors.grey : (winnerColor == theme.text ? theme.playerBlue : winnerColor), foregroundColor: Colors.white, padding: const EdgeInsets.symmetric(vertical: 15), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)), elevation: 5), onPressed: controller.rematchRequested ? null : () { controller.requestRematch(); }, child: Text(controller.opponentWantsRematch ? "ACCETTA RIVINCITA" : "CHIEDI RIVINCITA", style: _getTextStyle(themeType, const TextStyle(fontWeight: FontWeight.bold, fontSize: 16, letterSpacing: 1.0))))
|
||||||
style: ElevatedButton.styleFrom(backgroundColor: controller.rematchRequested ? Colors.grey : (winnerColor == theme.text ? theme.playerBlue : winnerColor), foregroundColor: Colors.white, padding: const EdgeInsets.symmetric(vertical: 15), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)), elevation: 5),
|
|
||||||
onPressed: controller.rematchRequested ? null : () { controller.requestRematch(); },
|
|
||||||
child: Text(controller.opponentWantsRematch ? "ACCETTA RIVINCITA" : "CHIEDI RIVINCITA", style: _getTextStyle(themeType, const TextStyle(fontWeight: FontWeight.bold, fontSize: 16, letterSpacing: 1.0))),
|
|
||||||
)
|
|
||||||
else
|
else
|
||||||
ElevatedButton(
|
ElevatedButton(style: ElevatedButton.styleFrom(backgroundColor: winnerColor == theme.text ? theme.playerBlue : winnerColor, foregroundColor: Colors.white, padding: const EdgeInsets.symmetric(vertical: 15), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)), elevation: 5), onPressed: () { controller.startNewGame(controller.board.radius, vsCPU: controller.isVsCPU, shape: controller.board.shape, timeMode: controller.isTimeMode); }, child: Text("RIGIOCA", style: _getTextStyle(themeType, const TextStyle(fontWeight: FontWeight.bold, fontSize: 16, letterSpacing: 2)))),
|
||||||
style: ElevatedButton.styleFrom(backgroundColor: winnerColor == theme.text ? theme.playerBlue : winnerColor, foregroundColor: Colors.white, padding: const EdgeInsets.symmetric(vertical: 15), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)), elevation: 5),
|
|
||||||
onPressed: () { controller.startNewGame(controller.board.radius, vsCPU: controller.isVsCPU, shape: controller.board.shape, timeMode: controller.isTimeMode); },
|
|
||||||
child: Text("RIGIOCA", style: _getTextStyle(themeType, const TextStyle(fontWeight: FontWeight.bold, fontSize: 16, letterSpacing: 2))),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
OutlinedButton(
|
OutlinedButton(style: OutlinedButton.styleFrom(foregroundColor: theme.text, side: BorderSide(color: theme.text.withOpacity(0.3), width: 2), padding: const EdgeInsets.symmetric(vertical: 15), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15))), onPressed: () { if (controller.isOnline) controller.disconnectOnlineGame(); _gameOverDialogShown = false; Navigator.pop(dialogContext); Navigator.pop(context); }, child: Text("TORNA AL MENU", style: _getTextStyle(themeType, TextStyle(fontWeight: FontWeight.bold, color: theme.text, fontSize: 14, letterSpacing: 1.5)))),
|
||||||
style: OutlinedButton.styleFrom(foregroundColor: theme.text, side: BorderSide(color: theme.text.withOpacity(0.3), width: 2), padding: const EdgeInsets.symmetric(vertical: 15), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15))),
|
|
||||||
onPressed: () {
|
|
||||||
if (controller.isOnline) controller.disconnectOnlineGame();
|
|
||||||
_gameOverDialogShown = false;
|
|
||||||
Navigator.pop(dialogContext); Navigator.pop(context);
|
|
||||||
},
|
|
||||||
child: Text("TORNA AL MENU", style: _getTextStyle(themeType, TextStyle(fontWeight: FontWeight.bold, color: theme.text, fontSize: 14, letterSpacing: 1.5))),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
|
|
@ -187,64 +139,19 @@ class _GameScreenState extends State<GameScreen> with TickerProviderStateMixin {
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildThemedJokerMessage(ThemeColors theme, AppThemeType themeType, GameController gameController) {
|
Widget _buildThemedJokerMessage(ThemeColors theme, AppThemeType themeType, GameController gameController) {
|
||||||
String titleText = "";
|
String titleText = ""; String subtitleText = "";
|
||||||
String subtitleText = "";
|
if (gameController.isOnline) { titleText = gameController.myJokerPlaced ? "In attesa dell'avversario..." : "Nascondi il tuo Jolly!"; subtitleText = gameController.myJokerPlaced ? "" : "(Tocca qui per nascondere)"; }
|
||||||
|
else if (gameController.isVsCPU) { titleText = "Nascondi il tuo Jolly!"; subtitleText = "(Tocca qui per nascondere)"; }
|
||||||
|
else { String pName = gameController.jokerTurn == Player.red ? "ROSSO" : (themeType == AppThemeType.cyberpunk || themeType == AppThemeType.arcade ? "VERDE" : "BLU"); titleText = "TURNO GIOCATORE $pName"; subtitleText = "Passa il dispositivo.\nL'avversario NON deve guardare!\n\n(Tocca qui quando sei pronto)"; }
|
||||||
|
|
||||||
if (gameController.isOnline) {
|
Widget content = Padding(padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 25), child: Column(mainAxisSize: MainAxisSize.min, children: [Icon(ThemeIcons.joker(themeType), color: themeType == AppThemeType.cyberpunk || themeType == AppThemeType.arcade ? Colors.yellowAccent : theme.playerBlue, size: 50), const SizedBox(height: 15), Text(titleText, textAlign: TextAlign.center, style: _getTextStyle(themeType, TextStyle(color: themeType == AppThemeType.doodle ? Colors.black87 : theme.text, fontSize: 20, fontWeight: FontWeight.bold))), const SizedBox(height: 25), Text(subtitleText, textAlign: TextAlign.center, style: _getTextStyle(themeType, TextStyle(color: themeType == AppThemeType.doodle ? Colors.black54 : theme.text.withOpacity(0.6), fontSize: 12, height: 1.5)))]));
|
||||||
titleText = gameController.myJokerPlaced ? "In attesa dell'avversario..." : "Nascondi il tuo Jolly!";
|
|
||||||
subtitleText = gameController.myJokerPlaced ? "" : "(Tocca qui per nascondere)";
|
|
||||||
} else if (gameController.isVsCPU) {
|
|
||||||
titleText = "Nascondi il tuo Jolly!";
|
|
||||||
subtitleText = "(Tocca qui per nascondere)";
|
|
||||||
} else {
|
|
||||||
String pName = gameController.jokerTurn == Player.red ? "ROSSO" : (themeType == AppThemeType.cyberpunk || themeType == AppThemeType.arcade || themeType == AppThemeType.music ? "VERDE" : "BLU");
|
|
||||||
titleText = "TURNO GIOCATORE $pName";
|
|
||||||
subtitleText = "Passa il dispositivo.\nL'avversario NON deve guardare!\n\n(Tocca qui quando sei pronto)";
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget content = Padding(
|
if (themeType == AppThemeType.cyberpunk) return Container(decoration: BoxDecoration(color: Colors.black.withOpacity(0.9), borderRadius: BorderRadius.circular(20), border: Border.all(color: Colors.yellowAccent, width: 2), boxShadow: [const BoxShadow(color: Colors.yellowAccent, blurRadius: 15, spreadRadius: 0)]), child: content);
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 25),
|
else if (themeType == AppThemeType.doodle) return Container(decoration: BoxDecoration(color: const Color(0xFFF9F9F9), borderRadius: BorderRadius.circular(8), border: Border.all(color: Colors.black87, width: 3), boxShadow: const [BoxShadow(color: Colors.black26, offset: Offset(6, 6))]), child: content);
|
||||||
child: Column(
|
else if (themeType == AppThemeType.wood) return Container(decoration: BoxDecoration(color: const Color(0xFF5D4037), borderRadius: BorderRadius.circular(15), border: Border.all(color: const Color(0xFF3E2723), width: 4), boxShadow: [BoxShadow(color: Colors.black.withOpacity(0.6), blurRadius: 15, offset: const Offset(0, 8))]), child: content);
|
||||||
mainAxisSize: MainAxisSize.min,
|
else if (themeType == AppThemeType.arcade) return Container(decoration: BoxDecoration(color: Colors.black, borderRadius: BorderRadius.zero, border: Border.all(color: Colors.greenAccent, width: 4)), child: content);
|
||||||
children: [
|
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);
|
||||||
Icon(ThemeIcons.joker(themeType), color: themeType == AppThemeType.cyberpunk || themeType == AppThemeType.arcade || themeType == AppThemeType.music ? Colors.yellowAccent : theme.playerBlue, size: 50),
|
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);
|
||||||
const SizedBox(height: 15),
|
|
||||||
Text(
|
|
||||||
titleText,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: _getTextStyle(themeType, TextStyle(
|
|
||||||
color: themeType == AppThemeType.doodle ? Colors.black87 : theme.text,
|
|
||||||
fontSize: 20,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
)),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 25),
|
|
||||||
Text(
|
|
||||||
subtitleText,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: _getTextStyle(themeType, TextStyle(
|
|
||||||
color: themeType == AppThemeType.doodle ? Colors.black54 : theme.text.withOpacity(0.6),
|
|
||||||
fontSize: 12,
|
|
||||||
height: 1.5
|
|
||||||
)),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (themeType == AppThemeType.cyberpunk || themeType == AppThemeType.music) {
|
|
||||||
return Container(decoration: BoxDecoration(color: Colors.black.withOpacity(0.9), borderRadius: BorderRadius.circular(20), border: Border.all(color: Colors.purpleAccent, width: 2), boxShadow: [BoxShadow(color: Colors.purpleAccent.withOpacity(0.6), blurRadius: 15, spreadRadius: 0)]), child: content);
|
|
||||||
} else if (themeType == AppThemeType.doodle) {
|
|
||||||
return Container(decoration: BoxDecoration(color: const Color(0xFFF9F9F9), borderRadius: BorderRadius.circular(8), border: Border.all(color: Colors.black87, width: 3), boxShadow: const [BoxShadow(color: Colors.black26, offset: Offset(6, 6))]), child: content);
|
|
||||||
} else if (themeType == AppThemeType.wood) {
|
|
||||||
return Container(decoration: BoxDecoration(color: const Color(0xFF5D4037), borderRadius: BorderRadius.circular(15), border: Border.all(color: const Color(0xFF3E2723), width: 4), boxShadow: [BoxShadow(color: Colors.black.withOpacity(0.6), blurRadius: 15, offset: const Offset(0, 8))]), child: content);
|
|
||||||
} else if (themeType == AppThemeType.arcade) {
|
|
||||||
return Container(decoration: BoxDecoration(color: Colors.black, borderRadius: BorderRadius.zero, border: Border.all(color: Colors.greenAccent, width: 4)), child: content);
|
|
||||||
} 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);
|
|
||||||
} else {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
@ -254,36 +161,14 @@ 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>();
|
||||||
|
|
||||||
if (gameController.isSetupPhase && !_wasSetupPhase) {
|
if (gameController.isSetupPhase && !_wasSetupPhase) { _hideJokerMessage = false; _lastJokerTurn = Player.red; }
|
||||||
_hideJokerMessage = false;
|
else if (gameController.isSetupPhase && gameController.jokerTurn != _lastJokerTurn) { _hideJokerMessage = false; _lastJokerTurn = gameController.jokerTurn; }
|
||||||
_lastJokerTurn = Player.red;
|
|
||||||
} else if (gameController.isSetupPhase && gameController.jokerTurn != _lastJokerTurn) {
|
|
||||||
_hideJokerMessage = false;
|
|
||||||
_lastJokerTurn = gameController.jokerTurn;
|
|
||||||
}
|
|
||||||
_wasSetupPhase = gameController.isSetupPhase;
|
_wasSetupPhase = gameController.isSetupPhase;
|
||||||
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
if (gameController.opponentLeft && !_opponentLeftDialogShown) {
|
if (gameController.opponentLeft && !_opponentLeftDialogShown) {
|
||||||
_opponentLeftDialogShown = true;
|
_opponentLeftDialogShown = true;
|
||||||
showDialog(
|
showDialog(barrierDismissible: false, context: context, builder: (dialogContext) => AlertDialog(backgroundColor: theme.background, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)), title: Text("VITTORIA A TAVOLINO!", textAlign: TextAlign.center, style: _getTextStyle(themeType, TextStyle(color: theme.playerRed, fontWeight: FontWeight.bold))), content: Text("L'avversario ha abbandonato la stanza.\nSei il vincitore incontestato!", textAlign: TextAlign.center, style: _getTextStyle(themeType, TextStyle(color: theme.text, fontSize: 16))), actionsAlignment: MainAxisAlignment.center, actions: [ElevatedButton(style: ElevatedButton.styleFrom(backgroundColor: theme.playerBlue, foregroundColor: Colors.white, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15))), onPressed: () { gameController.disconnectOnlineGame(); Navigator.pop(dialogContext); Navigator.pop(context); }, child: Text("MENU PRINCIPALE", style: _getTextStyle(themeType, const TextStyle(fontWeight: FontWeight.bold))))]));
|
||||||
barrierDismissible: false,
|
|
||||||
context: context,
|
|
||||||
builder: (dialogContext) => AlertDialog(
|
|
||||||
backgroundColor: theme.background,
|
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
|
|
||||||
title: Text("VITTORIA A TAVOLINO!", textAlign: TextAlign.center, style: _getTextStyle(themeType, TextStyle(color: theme.playerRed, fontWeight: FontWeight.bold))),
|
|
||||||
content: Text("L'avversario ha abbandonato la stanza.\nSei il vincitore incontestato!", textAlign: TextAlign.center, style: _getTextStyle(themeType, TextStyle(color: theme.text, fontSize: 16))),
|
|
||||||
actionsAlignment: MainAxisAlignment.center,
|
|
||||||
actions: [
|
|
||||||
ElevatedButton(
|
|
||||||
style: ElevatedButton.styleFrom(backgroundColor: theme.playerBlue, foregroundColor: Colors.white, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15))),
|
|
||||||
onPressed: () { gameController.disconnectOnlineGame(); Navigator.pop(dialogContext); Navigator.pop(context); },
|
|
||||||
child: Text("MENU PRINCIPALE", style: _getTextStyle(themeType, const TextStyle(fontWeight: FontWeight.bold))),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
)
|
|
||||||
);
|
|
||||||
} else if (gameController.board.isGameOver && !_gameOverDialogShown) {
|
} else if (gameController.board.isGameOver && !_gameOverDialogShown) {
|
||||||
_showGameOverDialog(context, gameController, theme, themeType);
|
_showGameOverDialog(context, gameController, theme, themeType);
|
||||||
}
|
}
|
||||||
|
|
@ -291,30 +176,14 @@ class _GameScreenState extends State<GameScreen> with TickerProviderStateMixin {
|
||||||
|
|
||||||
String? bgImage;
|
String? bgImage;
|
||||||
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'; // Questo è lo sfondo carta
|
if (themeType == AppThemeType.doodle) bgImage = 'assets/images/doodle_bg.jpg';
|
||||||
if (themeType == AppThemeType.cyberpunk) bgImage = 'assets/images/cyber_bg.jpg';
|
|
||||||
if (themeType == AppThemeType.music) bgImage = 'assets/images/music_bg.jpg';
|
|
||||||
|
|
||||||
Color indicatorColor = themeType == AppThemeType.cyberpunk || themeType == AppThemeType.arcade || themeType == AppThemeType.music ? Colors.white : Colors.black;
|
Color indicatorColor = themeType == AppThemeType.cyberpunk || themeType == AppThemeType.arcade ? Colors.white : Colors.black;
|
||||||
|
|
||||||
Widget emojiBar = const SizedBox();
|
Widget emojiBar = const SizedBox();
|
||||||
if (gameController.isOnline && !gameController.isGameOver) {
|
if (gameController.isOnline && !gameController.isGameOver) {
|
||||||
final List<String> emojis = ['😂', '😡', '😱', '🥳', '👀'];
|
final List<String> emojis = ['😂', '😡', '😱', '🥳', '👀'];
|
||||||
emojiBar = Container(
|
emojiBar = Container(padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 6), decoration: BoxDecoration(color: themeType == AppThemeType.cyberpunk || themeType == AppThemeType.arcade ? Colors.black.withOpacity(0.6) : Colors.white.withOpacity(0.8), borderRadius: BorderRadius.circular(30), border: Border.all(color: themeType == AppThemeType.cyberpunk ? theme.playerBlue.withOpacity(0.3) : Colors.black12, width: 2)), child: Row(mainAxisSize: MainAxisSize.min, children: emojis.map((e) => GestureDetector(onTap: () => gameController.sendReaction(e), child: Padding(padding: const EdgeInsets.symmetric(horizontal: 6), child: Text(e, style: const TextStyle(fontSize: 22))))).toList()));
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 6),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: themeType == AppThemeType.cyberpunk || themeType == AppThemeType.arcade || themeType == AppThemeType.music ? Colors.black.withOpacity(0.6) : Colors.white.withOpacity(0.8),
|
|
||||||
borderRadius: BorderRadius.circular(30),
|
|
||||||
border: Border.all(color: themeType == AppThemeType.cyberpunk || themeType == AppThemeType.music ? theme.playerBlue.withOpacity(0.3) : Colors.white24, width: 2),
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: emojis.map((e) => GestureDetector(
|
|
||||||
onTap: () => gameController.sendReaction(e),
|
|
||||||
child: Padding(padding: const EdgeInsets.symmetric(horizontal: 6), child: Text(e, style: const TextStyle(fontSize: 22))),
|
|
||||||
)).toList(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget gameContent = SafeArea(
|
Widget gameContent = SafeArea(
|
||||||
|
|
@ -339,41 +208,31 @@ class _GameScreenState extends State<GameScreen> with TickerProviderStateMixin {
|
||||||
|
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
width: actualWidth, height: actualHeight,
|
width: actualWidth, height: actualHeight,
|
||||||
child: Stack(
|
child: GestureDetector(
|
||||||
children: [
|
behavior: HitTestBehavior.opaque,
|
||||||
// --- IL VERO SFONDO SFOCATO SAGOMATO ---
|
onTapUp: (details) => _handleTap(details.localPosition, actualWidth, actualHeight, gameController, themeType),
|
||||||
if (themeType == AppThemeType.music)
|
onPanUpdate: (details) {
|
||||||
Positioned.fill(
|
if (gameController.board.shape == ArenaShape.pyramid3D) {
|
||||||
child: ClipPath(
|
setState(() {
|
||||||
clipper: _ArenaClipper(gameController.board),
|
_cameraAngle -= details.delta.dx * 0.015;
|
||||||
child: BackdropFilter(
|
});
|
||||||
filter: ImageFilter.blur(sigmaX: 8.0, sigmaY: 8.0),
|
}
|
||||||
child: Container(
|
},
|
||||||
color: Colors.white.withOpacity(0.08),
|
child: AnimatedBuilder(
|
||||||
),
|
animation: _blinkController,
|
||||||
|
builder: (context, child) {
|
||||||
|
return CustomPaint(
|
||||||
|
size: Size(actualWidth, actualHeight),
|
||||||
|
painter: BoardPainter(
|
||||||
|
board: gameController.board, theme: theme, themeType: themeType,
|
||||||
|
blinkValue: _blinkController.value, isOnline: gameController.isOnline,
|
||||||
|
isVsCPU: gameController.isVsCPU, isSetupPhase: gameController.isSetupPhase,
|
||||||
|
myPlayer: gameController.myPlayer, jokerTurn: gameController.jokerTurn,
|
||||||
|
cameraAngle: _cameraAngle,
|
||||||
),
|
),
|
||||||
),
|
);
|
||||||
),
|
}
|
||||||
|
),
|
||||||
GestureDetector(
|
|
||||||
behavior: HitTestBehavior.opaque,
|
|
||||||
onTapDown: (details) => _handleTap(details.localPosition, actualWidth, actualHeight, gameController, themeType),
|
|
||||||
child: AnimatedBuilder(
|
|
||||||
animation: _blinkController,
|
|
||||||
builder: (context, child) {
|
|
||||||
return CustomPaint(
|
|
||||||
size: Size(actualWidth, actualHeight),
|
|
||||||
painter: BoardPainter(
|
|
||||||
board: gameController.board, theme: theme, themeType: themeType,
|
|
||||||
blinkValue: _blinkController.value, isOnline: gameController.isOnline,
|
|
||||||
isVsCPU: gameController.isVsCPU, isSetupPhase: gameController.isSetupPhase,
|
|
||||||
myPlayer: gameController.myPlayer, jokerTurn: gameController.jokerTurn,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -388,118 +247,44 @@ class _GameScreenState extends State<GameScreen> with TickerProviderStateMixin {
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
if (gameController.isVsCPU)
|
if (gameController.isVsCPU)
|
||||||
Container(
|
Container(padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), decoration: BoxDecoration(color: indicatorColor.withOpacity(0.1), borderRadius: BorderRadius.circular(20), border: Border.all(color: indicatorColor.withOpacity(0.3))), child: Row(mainAxisSize: MainAxisSize.min, children: [Icon(Icons.smart_toy_rounded, size: 16, color: indicatorColor), const SizedBox(width: 8), Text("LIVELLO CPU: ${gameController.cpuLevel}", style: _getTextStyle(themeType, TextStyle(color: indicatorColor, fontWeight: FontWeight.bold, fontSize: 11, letterSpacing: 1.0)))]))
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
|
||||||
decoration: BoxDecoration(color: indicatorColor.withOpacity(0.1), borderRadius: BorderRadius.circular(20), border: Border.all(color: indicatorColor.withOpacity(0.3))),
|
|
||||||
child: Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Icon(Icons.smart_toy_rounded, size: 16, color: indicatorColor), const SizedBox(width: 8),
|
|
||||||
Text("LIVELLO CPU: ${gameController.cpuLevel}", style: _getTextStyle(themeType, TextStyle(color: indicatorColor, fontWeight: FontWeight.bold, fontSize: 11, letterSpacing: 1.0))),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
)
|
|
||||||
else
|
else
|
||||||
emojiBar,
|
emojiBar,
|
||||||
|
|
||||||
Container(
|
Container(decoration: BoxDecoration(borderRadius: BorderRadius.circular(20), boxShadow: [BoxShadow(color: Colors.black.withOpacity(0.4), offset: const Offset(0, 4), blurRadius: 5)]), 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))), icon: Icon(Icons.exit_to_app, color: bgImage != null || themeType == AppThemeType.arcade ? Colors.white : theme.text, size: 20), 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))))),
|
||||||
decoration: BoxDecoration(borderRadius: BorderRadius.circular(20), boxShadow: [BoxShadow(color: Colors.black.withOpacity(0.4), offset: const Offset(0, 4), blurRadius: 5)]),
|
|
||||||
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))),
|
|
||||||
icon: Icon(Icons.exit_to_app, color: bgImage != null || themeType == AppThemeType.arcade ? Colors.white : theme.text, size: 20),
|
|
||||||
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))),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
||||||
if (gameController.myReaction != null)
|
if (gameController.board.shape == ArenaShape.pyramid3D)
|
||||||
Positioned(top: 80, left: gameController.isHost ? 30 : null, right: gameController.isHost ? null : 30, child: _BouncingEmoji(emoji: gameController.myReaction!)),
|
Positioned(top: 80, left: 0, right: 0, child: Center(child: Text("Scorri in orizzontale per ruotare", style: TextStyle(color: Colors.white.withOpacity(0.5), fontStyle: FontStyle.italic, fontWeight: FontWeight.bold, letterSpacing: 1.5)))),
|
||||||
if (gameController.opponentReaction != null)
|
|
||||||
Positioned(top: 80, left: !gameController.isHost ? 30 : null, right: !gameController.isHost ? null : 30, child: _BouncingEmoji(emoji: gameController.opponentReaction!)),
|
if (gameController.myReaction != null) Positioned(top: 80, left: gameController.isHost ? 30 : null, right: gameController.isHost ? null : 30, child: _BouncingEmoji(emoji: gameController.myReaction!)),
|
||||||
|
if (gameController.opponentReaction != null) Positioned(top: 80, left: !gameController.isHost ? 30 : null, right: !gameController.isHost ? null : 30, child: _BouncingEmoji(emoji: gameController.opponentReaction!)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
return PopScope(
|
return PopScope(
|
||||||
canPop: true,
|
canPop: true, onPopInvoked: (didPop) { gameController.disconnectOnlineGame(); },
|
||||||
onPopInvoked: (didPop) { gameController.disconnectOnlineGame(); },
|
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
backgroundColor: Colors.transparent, // Assicuriamo che la scaffold base sia trasparente
|
backgroundColor: bgImage != null ? Colors.transparent : theme.background,
|
||||||
body: Stack(
|
body: CustomPaint(
|
||||||
children: [
|
painter: themeType == AppThemeType.minimal ? FullScreenGridPainter(Colors.black.withOpacity(0.06)) : null,
|
||||||
// 1. Sfondo base a tinta unita (In caso non ci sia l'immagine)
|
child: Container(
|
||||||
Container(color: themeType == AppThemeType.doodle ? Colors.white : theme.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,
|
||||||
|
child: Stack(
|
||||||
// 2. Immagine di Sfondo per tutti i temi che la supportano
|
children: [
|
||||||
if (bgImage != null)
|
if (gameController.isTimeMode && !gameController.isCPUThinking && !gameController.isGameOver && gameController.timeLeft > 0 && gameController.timeLeft <= 5 && !gameController.isSetupPhase) Positioned.fill(child: BlitzBackgroundEffect(timeLeft: gameController.timeLeft, color: theme.playerRed, themeType: themeType)),
|
||||||
Positioned.fill(
|
if (gameController.effectText.isNotEmpty) Positioned.fill(child: SpecialEventBackgroundEffect(text: gameController.effectText, color: gameController.effectColor, themeType: themeType)),
|
||||||
child: Image.asset(
|
Positioned.fill(child: gameContent),
|
||||||
bgImage,
|
if (gameController.isSetupPhase && !_hideJokerMessage) Positioned.fill(child: Container(color: themeType == AppThemeType.cyberpunk || themeType == AppThemeType.arcade ? Colors.black : theme.background.withOpacity(0.98), child: Center(child: Padding(padding: const EdgeInsets.symmetric(horizontal: 30.0), child: GestureDetector(onTap: () { setState(() { _hideJokerMessage = true; }); }, child: Material(color: Colors.transparent, child: _buildThemedJokerMessage(theme, themeType, gameController))))))),
|
||||||
fit: BoxFit.cover,
|
if (gameController.isGameOver && gameController.board.scoreRed != gameController.board.scoreBlue) Positioned.fill(child: IgnorePointer(child: WinnerVFXOverlay(winnerColor: gameController.board.scoreRed > gameController.board.scoreBlue ? theme.playerRed : theme.playerBlue, themeType: themeType))),
|
||||||
alignment: Alignment.center,
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
// 3. Griglia a righe incrociate per il doodle (Sopra l'immagine di carta)
|
|
||||||
if (themeType == AppThemeType.doodle)
|
|
||||||
Positioned.fill(
|
|
||||||
child: CustomPaint(
|
|
||||||
painter: FullScreenGridPainter(Colors.blue.withOpacity(0.15)),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// 4. Patina scura (Cyberpunk e Music) per far risaltare il neon
|
|
||||||
if (bgImage != null && (themeType == AppThemeType.cyberpunk || themeType == AppThemeType.music))
|
|
||||||
Positioned.fill(
|
|
||||||
child: Container(
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
gradient: LinearGradient(
|
|
||||||
begin: Alignment.topCenter, end: Alignment.bottomCenter,
|
|
||||||
colors: [Colors.black.withOpacity(0.4), Colors.black.withOpacity(0.8)]
|
|
||||||
)
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// 5. Effetto "Furia" o Timeout
|
|
||||||
if (gameController.isTimeMode && !gameController.isCPUThinking && !gameController.isGameOver && gameController.timeLeft > 0 && gameController.timeLeft <= 5 && !gameController.isSetupPhase)
|
|
||||||
Positioned.fill(child: BlitzBackgroundEffect(timeLeft: gameController.timeLeft, color: theme.playerRed, themeType: themeType)),
|
|
||||||
|
|
||||||
// 6. Testo degli Eventi
|
|
||||||
if (gameController.effectText.isNotEmpty)
|
|
||||||
Positioned.fill(child: SpecialEventBackgroundEffect(text: gameController.effectText, color: gameController.effectColor, themeType: themeType)),
|
|
||||||
|
|
||||||
// 7. Il Gioco Vero e Proprio
|
|
||||||
Positioned.fill(child: gameContent),
|
|
||||||
|
|
||||||
// 8. Schermata Passaggio Dispositivo
|
|
||||||
if (gameController.isSetupPhase && !_hideJokerMessage)
|
|
||||||
Positioned.fill(
|
|
||||||
child: Container(
|
|
||||||
color: themeType == AppThemeType.cyberpunk || themeType == AppThemeType.arcade || themeType == AppThemeType.music
|
|
||||||
? Colors.black.withOpacity(0.98)
|
|
||||||
: theme.background.withOpacity(0.98),
|
|
||||||
child: Center(
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 30.0),
|
|
||||||
child: GestureDetector(
|
|
||||||
onTap: () { setState(() { _hideJokerMessage = true; }); },
|
|
||||||
child: Material(color: Colors.transparent, child: _buildThemedJokerMessage(theme, themeType, gameController)),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// 9. Effetti di Vittoria
|
|
||||||
if (gameController.isGameOver && gameController.board.scoreRed != gameController.board.scoreBlue)
|
|
||||||
Positioned.fill(child: IgnorePointer(child: WinnerVFXOverlay(winnerColor: gameController.board.scoreRed > gameController.board.scoreBlue ? theme.playerRed : theme.playerBlue, themeType: themeType))),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
@ -508,21 +293,48 @@ class _GameScreenState extends State<GameScreen> with TickerProviderStateMixin {
|
||||||
void _handleTap(Offset tapPos, double width, double height, GameController controller, AppThemeType themeType) {
|
void _handleTap(Offset tapPos, double width, double height, GameController controller, AppThemeType themeType) {
|
||||||
final board = controller.board;
|
final board = controller.board;
|
||||||
if (board.isGameOver) return;
|
if (board.isGameOver) return;
|
||||||
int cols = board.columns + 1; double spacing = width / cols; double offset = spacing / 2;
|
|
||||||
|
BoardPainter dummyPainter = BoardPainter(
|
||||||
|
board: board, theme: AppColors.minimal, themeType: AppThemeType.minimal,
|
||||||
|
isOnline: false, isVsCPU: false, isSetupPhase: false,
|
||||||
|
myPlayer: Player.red, jokerTurn: Player.red,
|
||||||
|
cameraAngle: _cameraAngle,
|
||||||
|
);
|
||||||
|
|
||||||
if (controller.isSetupPhase) {
|
if (controller.isSetupPhase) {
|
||||||
int bx = ((tapPos.dx - offset) / spacing).floor(); int by = ((tapPos.dy - offset) / spacing).floor();
|
var sortedBoxes = List<Box>.from(board.boxes);
|
||||||
controller.placeJoker(bx, by); return;
|
sortedBoxes.sort((a,b) => dummyPainter.getDepth(b).compareTo(dummyPainter.getDepth(a)));
|
||||||
|
|
||||||
|
for (var box in sortedBoxes) {
|
||||||
|
if (box.type == BoxType.invisible) continue;
|
||||||
|
Offset p0 = dummyPainter.projectLogical(box.x.toDouble(), box.y.toDouble(), box.z.toDouble(), Size(width, height));
|
||||||
|
Offset p1 = dummyPainter.projectLogical(box.x + 1.0, box.y.toDouble(), box.z.toDouble(), Size(width, height));
|
||||||
|
Offset p2 = dummyPainter.projectLogical(box.x + 1.0, box.y + 1.0, box.z.toDouble(), Size(width, height));
|
||||||
|
Offset p3 = dummyPainter.projectLogical(box.x.toDouble(), box.y + 1.0, box.z.toDouble(), Size(width, height));
|
||||||
|
|
||||||
|
Path poly = Path()..moveTo(p0.dx, p0.dy)..lineTo(p1.dx, p1.dy)..lineTo(p2.dx, p2.dy)..lineTo(p3.dx, p3.dy)..close();
|
||||||
|
if (poly.contains(tapPos)) {
|
||||||
|
controller.placeJoker(box.x, box.y, bz: box.z);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Line? closestLine; double minDistance = double.infinity; double maxTouchDistance = spacing * 0.4;
|
Line? closestLine;
|
||||||
|
double minDistance = double.infinity;
|
||||||
|
double maxTouchDistance = 40.0;
|
||||||
|
|
||||||
for (var line in board.lines) {
|
for (var line in board.lines) {
|
||||||
if (line.owner != Player.none || !line.isPlayable) continue;
|
if (line.owner != Player.none || !line.isPlayable) continue;
|
||||||
Offset screenP1 = Offset(line.p1.x * spacing + offset, line.p1.y * spacing + offset);
|
|
||||||
Offset screenP2 = Offset(line.p2.x * spacing + offset, line.p2.y * spacing + offset);
|
Offset screenP1 = dummyPainter.projectLogical(line.p1.x.toDouble(), line.p1.y.toDouble(), line.p1.z.toDouble(), Size(width, height));
|
||||||
|
Offset screenP2 = dummyPainter.projectLogical(line.p2.x.toDouble(), line.p2.y.toDouble(), line.p2.z.toDouble(), Size(width, height));
|
||||||
|
|
||||||
double dist = _distanceToSegment(tapPos, screenP1, screenP2);
|
double dist = _distanceToSegment(tapPos, screenP1, screenP2);
|
||||||
if (dist < minDistance && dist < maxTouchDistance) { minDistance = dist; closestLine = line; }
|
if (dist < minDistance && dist < maxTouchDistance) { minDistance = dist; closestLine = line; }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (closestLine != null) { controller.handleLineTap(closestLine, themeType); }
|
if (closestLine != null) { controller.handleLineTap(closestLine, themeType); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -535,37 +347,6 @@ class _GameScreenState extends State<GameScreen> with TickerProviderStateMixin {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===========================================================================
|
|
||||||
// CLIPPER MAGICO: Ritaglia l'effetto sfocatura sull'esatta forma dell'arena
|
|
||||||
// ===========================================================================
|
|
||||||
class _ArenaClipper extends CustomClipper<Path> {
|
|
||||||
final GameBoard board;
|
|
||||||
_ArenaClipper(this.board);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Path getClip(Size size) {
|
|
||||||
int cols = board.columns + 1;
|
|
||||||
double spacing = size.width / cols;
|
|
||||||
double offset = spacing / 2;
|
|
||||||
Path path = Path();
|
|
||||||
|
|
||||||
for (var box in board.boxes) {
|
|
||||||
if (box.type != BoxType.invisible) {
|
|
||||||
path.addRect(Rect.fromLTWH(
|
|
||||||
box.x * spacing + offset,
|
|
||||||
box.y * spacing + offset,
|
|
||||||
spacing,
|
|
||||||
spacing
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool shouldReclip(covariant _ArenaClipper oldClipper) => true;
|
|
||||||
}
|
|
||||||
|
|
||||||
class _Particle {
|
class _Particle {
|
||||||
double x, y, vx, vy, size, angle, spin;
|
double x, y, vx, vy, size, angle, spin;
|
||||||
Color color; int type;
|
Color color; int type;
|
||||||
|
|
@ -597,7 +378,7 @@ class _WinnerVFXOverlayState extends State<WinnerVFXOverlay> with SingleTickerPr
|
||||||
}
|
}
|
||||||
|
|
||||||
void _initParticles(Size screenSize) {
|
void _initParticles(Size screenSize) {
|
||||||
int particleCount = widget.themeType == AppThemeType.cyberpunk || widget.themeType == AppThemeType.music ? 150 : 100;
|
int particleCount = widget.themeType == AppThemeType.cyberpunk ? 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;
|
||||||
|
|
||||||
|
|
@ -607,7 +388,6 @@ 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.music) { palette.add(Colors.pinkAccent); palette.add(Colors.cyanAccent); }
|
|
||||||
|
|
||||||
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;
|
||||||
|
|
@ -620,7 +400,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 || widget.themeType == AppThemeType.music) { p.vy += 0.1; p.vx *= 0.98; p.vy *= 0.98; }
|
if (widget.themeType == AppThemeType.cyberpunk) { 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; }
|
||||||
|
|
@ -643,7 +423,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 || themeType == AppThemeType.music) { paint.maskFilter = const MaskFilter.blur(BlurStyle.solid, 4.0); }
|
if (themeType == AppThemeType.cyberpunk) { 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) {
|
||||||
|
|
|
||||||
|
|
@ -4,28 +4,12 @@
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:google_fonts/google_fonts.dart';
|
// Import separati e puliti
|
||||||
import '../../logic/game_controller.dart';
|
import '../../logic/game_controller.dart';
|
||||||
import '../../models/game_board.dart';
|
import '../../models/game_board.dart';
|
||||||
import '../../core/theme_manager.dart';
|
import '../../core/theme_manager.dart';
|
||||||
import '../../services/audio_service.dart';
|
import '../../services/audio_service.dart';
|
||||||
import '../../core/app_colors.dart';
|
import '../../core/app_colors.dart';
|
||||||
import '../../services/storage_service.dart';
|
|
||||||
import '../home/dialog.dart'; // <--- IMPORTANTE: Importa il TutorialDialog
|
|
||||||
|
|
||||||
TextStyle _getTextStyle(AppThemeType themeType, TextStyle baseStyle) {
|
|
||||||
if (themeType == AppThemeType.doodle) {
|
|
||||||
return GoogleFonts.permanentMarker(textStyle: baseStyle);
|
|
||||||
} else if (themeType == AppThemeType.arcade) {
|
|
||||||
return GoogleFonts.pressStart2p(textStyle: baseStyle.copyWith(
|
|
||||||
fontSize: baseStyle.fontSize != null ? baseStyle.fontSize! * 0.75 : null,
|
|
||||||
letterSpacing: 0.5,
|
|
||||||
));
|
|
||||||
} else if (themeType == AppThemeType.grimorio) {
|
|
||||||
return GoogleFonts.cinzelDecorative(textStyle: baseStyle.copyWith(fontWeight: FontWeight.bold));
|
|
||||||
}
|
|
||||||
return baseStyle;
|
|
||||||
}
|
|
||||||
|
|
||||||
class ScoreBoard extends StatefulWidget {
|
class ScoreBoard extends StatefulWidget {
|
||||||
const ScoreBoard({super.key});
|
const ScoreBoard({super.key});
|
||||||
|
|
@ -48,24 +32,21 @@ class _ScoreBoardState extends State<ScoreBoard> {
|
||||||
bool isRedTurn = controller.board.currentPlayer == Player.red;
|
bool isRedTurn = controller.board.currentPlayer == Player.red;
|
||||||
bool isMuted = AudioService.instance.isMuted;
|
bool isMuted = AudioService.instance.isMuted;
|
||||||
|
|
||||||
String myName = StorageService.instance.playerName.toUpperCase();
|
String nameRed = "ROSSO";
|
||||||
if (myName.isEmpty) myName = "TU";
|
String nameBlue = themeType == AppThemeType.cyberpunk ? "VERDE" : "BLU";
|
||||||
|
|
||||||
String nameRed = myName;
|
|
||||||
String nameBlue = themeType == AppThemeType.cyberpunk || themeType == AppThemeType.arcade ? "VERDE" : "BLU";
|
|
||||||
|
|
||||||
if (controller.isOnline) {
|
if (controller.isOnline) {
|
||||||
nameRed = controller.onlineHostName.toUpperCase();
|
nameRed = controller.onlineHostName.toUpperCase();
|
||||||
nameBlue = controller.onlineGuestName.toUpperCase();
|
nameBlue = controller.onlineGuestName.toUpperCase();
|
||||||
} else if (controller.isVsCPU) {
|
} else if (controller.isVsCPU) {
|
||||||
nameRed = myName;
|
nameRed = "TU";
|
||||||
nameBlue = "CPU";
|
nameBlue = "CPU";
|
||||||
}
|
}
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
padding: const EdgeInsets.only(top: 10, bottom: 20, left: 20, right: 20),
|
padding: const EdgeInsets.only(top: 10, bottom: 20, left: 20, right: 20),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: themeType == AppThemeType.doodle ? theme.background : theme.background.withOpacity(0.95),
|
color: theme.background.withOpacity(0.95),
|
||||||
boxShadow: [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: Colors.black.withOpacity(0.3),
|
color: Colors.black.withOpacity(0.3),
|
||||||
|
|
@ -81,84 +62,33 @@ class _ScoreBoardState extends State<ScoreBoard> {
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
_PlayerScore(color: theme.playerRed, score: redScore, isTurn: isRedTurn, textColor: theme.text, title: nameRed, themeType: themeType),
|
_PlayerScore(color: theme.playerRed, score: redScore, isTurn: isRedTurn, textColor: theme.text, title: nameRed),
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
"TETRAQ",
|
"TETRAQ",
|
||||||
style: _getTextStyle(themeType, TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 24,
|
fontSize: 24,
|
||||||
fontWeight: FontWeight.w900,
|
fontWeight: FontWeight.w900,
|
||||||
color: theme.text,
|
color: theme.text,
|
||||||
letterSpacing: 4,
|
letterSpacing: 4,
|
||||||
shadows: themeType == AppThemeType.doodle
|
shadows: [Shadow(color: Colors.black.withOpacity(0.3), offset: const Offset(1, 2), blurRadius: 2)]
|
||||||
? [
|
)
|
||||||
// EFFETTO RILIEVO (Luce in alto a sx, ombra in basso a dx)
|
|
||||||
const Shadow(color: Colors.white, offset: Offset(-1.5, -1.5), blurRadius: 1),
|
|
||||||
Shadow(color: Colors.black.withOpacity(0.25), offset: const Offset(1.5, 1.5), blurRadius: 2),
|
|
||||||
]
|
|
||||||
: [Shadow(color: Colors.black.withOpacity(0.3), offset: const Offset(1, 2), blurRadius: 2)]
|
|
||||||
))
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
IconButton(
|
||||||
|
icon: Icon(isMuted ? Icons.volume_off : Icons.volume_up, color: theme.text.withOpacity(0.7)),
|
||||||
// --- ROW DEI PULSANTI AGGIORNATA ---
|
onPressed: () {
|
||||||
Row(
|
setState(() {
|
||||||
mainAxisSize: MainAxisSize.min,
|
AudioService.instance.toggleMute();
|
||||||
children: [
|
});
|
||||||
// TASTO AUDIO CON CONTORNO
|
},
|
||||||
GestureDetector(
|
|
||||||
behavior: HitTestBehavior.opaque,
|
|
||||||
onTap: () {
|
|
||||||
setState(() {
|
|
||||||
AudioService.instance.toggleMute();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
child: Container(
|
|
||||||
padding: const EdgeInsets.all(6),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: themeType == AppThemeType.doodle ? Colors.transparent : theme.text.withOpacity(0.05),
|
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
border: Border.all(color: themeType == AppThemeType.doodle ? const Color(0xFF111122) : theme.text.withOpacity(0.3), width: 1.5),
|
|
||||||
),
|
|
||||||
child: Icon(
|
|
||||||
isMuted ? Icons.volume_off : Icons.volume_up,
|
|
||||||
color: themeType == AppThemeType.doodle ? const Color(0xFF111122) : theme.text.withOpacity(0.8),
|
|
||||||
size: 16
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
const SizedBox(width: 10),
|
|
||||||
|
|
||||||
// TASTO INFORMAZIONI (TUTORIAL) CON CONTORNO
|
|
||||||
GestureDetector(
|
|
||||||
behavior: HitTestBehavior.opaque,
|
|
||||||
onTap: () {
|
|
||||||
showDialog(context: context, builder: (ctx) => const TutorialDialog());
|
|
||||||
},
|
|
||||||
child: Container(
|
|
||||||
padding: const EdgeInsets.all(6),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: themeType == AppThemeType.doodle ? Colors.transparent : theme.text.withOpacity(0.05),
|
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
border: Border.all(color: themeType == AppThemeType.doodle ? const Color(0xFF111122) : theme.text.withOpacity(0.3), width: 1.5),
|
|
||||||
),
|
|
||||||
child: Icon(
|
|
||||||
Icons.info_outline,
|
|
||||||
color: themeType == AppThemeType.doodle ? const Color(0xFF111122) : theme.text.withOpacity(0.8),
|
|
||||||
size: 16
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
||||||
_PlayerScore(color: theme.playerBlue, score: blueScore, isTurn: !isRedTurn, textColor: theme.text, title: nameBlue, themeType: themeType),
|
_PlayerScore(color: theme.playerBlue, score: blueScore, isTurn: !isRedTurn, textColor: theme.text, title: nameBlue),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
@ -171,16 +101,15 @@ class _PlayerScore extends StatelessWidget {
|
||||||
final bool isTurn;
|
final bool isTurn;
|
||||||
final Color textColor;
|
final Color textColor;
|
||||||
final String title;
|
final String title;
|
||||||
final AppThemeType themeType;
|
|
||||||
|
|
||||||
const _PlayerScore({required this.color, required this.score, required this.isTurn, required this.textColor, required this.title, required this.themeType});
|
const _PlayerScore({required this.color, required this.score, required this.isTurn, required this.textColor, required this.title});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Column(
|
return Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Text(title, style: _getTextStyle(themeType, TextStyle(fontWeight: FontWeight.bold, color: isTurn ? color : textColor.withOpacity(0.5), fontSize: 12))),
|
Text(title, style: TextStyle(fontWeight: FontWeight.bold, color: isTurn ? color : textColor.withOpacity(0.5), fontSize: 12)),
|
||||||
const SizedBox(height: 5),
|
const SizedBox(height: 5),
|
||||||
AnimatedContainer(
|
AnimatedContainer(
|
||||||
duration: const Duration(milliseconds: 300),
|
duration: const Duration(milliseconds: 300),
|
||||||
|
|
@ -193,7 +122,7 @@ class _PlayerScore extends StatelessWidget {
|
||||||
BoxShadow(color: color.withOpacity(0.5), offset: const Offset(0, 4), blurRadius: 6)
|
BoxShadow(color: color.withOpacity(0.5), offset: const Offset(0, 4), blurRadius: 6)
|
||||||
] : [],
|
] : [],
|
||||||
),
|
),
|
||||||
child: Text('$score', style: _getTextStyle(themeType, TextStyle(fontSize: 22, fontWeight: FontWeight.bold, color: isTurn ? Colors.white : textColor.withOpacity(0.5)))),
|
child: Text('$score', style: TextStyle(fontSize: 22, fontWeight: FontWeight.bold, color: isTurn ? Colors.white : textColor.withOpacity(0.5))),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,375 +0,0 @@
|
||||||
// ===========================================================================
|
|
||||||
// FILE: lib/ui/home/dialogs/dialog.dart
|
|
||||||
// ===========================================================================
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:provider/provider.dart';
|
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
|
||||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
|
||||||
import 'package:firebase_auth/firebase_auth.dart';
|
|
||||||
|
|
||||||
import '../../../core/theme_manager.dart';
|
|
||||||
import '../../../core/app_colors.dart';
|
|
||||||
import '../../../l10n/app_localizations.dart';
|
|
||||||
import '../../../widgets/painters.dart';
|
|
||||||
import '../../../widgets/cyber_border.dart';
|
|
||||||
|
|
||||||
// ===========================================================================
|
|
||||||
// 1. DIALOGO MISSIONI (QUESTS)
|
|
||||||
// ===========================================================================
|
|
||||||
class QuestsDialog extends StatelessWidget {
|
|
||||||
const QuestsDialog({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final themeManager = context.watch<ThemeManager>();
|
|
||||||
final theme = themeManager.currentColors;
|
|
||||||
final themeType = themeManager.currentThemeType;
|
|
||||||
final loc = AppLocalizations.of(context)!;
|
|
||||||
|
|
||||||
return FutureBuilder<SharedPreferences>(
|
|
||||||
future: SharedPreferences.getInstance(),
|
|
||||||
builder: (context, snapshot) {
|
|
||||||
if (!snapshot.hasData) return const SizedBox();
|
|
||||||
final prefs = snapshot.data!;
|
|
||||||
|
|
||||||
return Dialog(
|
|
||||||
backgroundColor: Colors.transparent,
|
|
||||||
insetPadding: const EdgeInsets.all(20),
|
|
||||||
child: Container(
|
|
||||||
padding: const EdgeInsets.all(25.0),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
gradient: LinearGradient(begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [theme.background.withOpacity(0.95), theme.background.withOpacity(0.8)]),
|
|
||||||
borderRadius: BorderRadius.circular(25),
|
|
||||||
border: Border.all(color: theme.playerBlue.withOpacity(0.5), width: 2),
|
|
||||||
boxShadow: [BoxShadow(color: theme.playerBlue.withOpacity(0.2), blurRadius: 20, spreadRadius: 5)]
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Icon(Icons.assignment_turned_in, size: 50, color: theme.playerBlue),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
Text(loc.questsTitle, style: getSharedTextStyle(themeType, TextStyle(fontSize: 22, fontWeight: FontWeight.w900, color: theme.text, letterSpacing: 1.5))),
|
|
||||||
const SizedBox(height: 25),
|
|
||||||
|
|
||||||
...List.generate(3, (index) {
|
|
||||||
int i = index + 1;
|
|
||||||
int type = prefs.getInt('q${i}_type') ?? 0;
|
|
||||||
int prog = prefs.getInt('q${i}_prog') ?? 0;
|
|
||||||
int target = prefs.getInt('q${i}_target') ?? 1;
|
|
||||||
|
|
||||||
String title = "";
|
|
||||||
IconData icon = Icons.star;
|
|
||||||
if (type == 0) { title = "Vinci partite Online"; icon = Icons.public; }
|
|
||||||
else if (type == 1) { title = "Vinci contro la CPU"; icon = Icons.smart_toy; }
|
|
||||||
else { title = "Gioca in Arene Speciali"; icon = Icons.extension; }
|
|
||||||
|
|
||||||
bool completed = prog >= target;
|
|
||||||
double percent = (prog / target).clamp(0.0, 1.0);
|
|
||||||
|
|
||||||
return Container(
|
|
||||||
margin: const EdgeInsets.only(bottom: 15),
|
|
||||||
padding: const EdgeInsets.all(12),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: completed ? Colors.green.withOpacity(0.1) : theme.text.withOpacity(0.05),
|
|
||||||
borderRadius: BorderRadius.circular(15),
|
|
||||||
border: Border.all(color: completed ? Colors.green : theme.gridLine.withOpacity(0.3)),
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Icon(icon, color: completed ? Colors.green : theme.text.withOpacity(0.6), size: 30),
|
|
||||||
const SizedBox(width: 15),
|
|
||||||
Expanded(
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(title, style: getSharedTextStyle(themeType, TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: completed ? Colors.green : theme.text))),
|
|
||||||
const SizedBox(height: 6),
|
|
||||||
ClipRRect(
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
child: LinearProgressIndicator(value: percent, backgroundColor: theme.gridLine.withOpacity(0.2), color: completed ? Colors.green : theme.playerBlue, minHeight: 8),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 10),
|
|
||||||
Text("$prog / $target", style: getSharedTextStyle(themeType, TextStyle(fontWeight: FontWeight.bold, color: theme.text.withOpacity(0.6)))),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
|
|
||||||
const SizedBox(height: 15),
|
|
||||||
SizedBox(
|
|
||||||
width: double.infinity, height: 50,
|
|
||||||
child: ElevatedButton(
|
|
||||||
style: ElevatedButton.styleFrom(backgroundColor: theme.playerBlue, foregroundColor: Colors.white, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15))),
|
|
||||||
onPressed: () => Navigator.pop(context),
|
|
||||||
child: const Text("CHIUDI", style: TextStyle(fontSize: 16, fontWeight: FontWeight.w900, letterSpacing: 2)),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ===========================================================================
|
|
||||||
// 2. DIALOGO CLASSIFICA (LEADERBOARD)
|
|
||||||
// ===========================================================================
|
|
||||||
class LeaderboardDialog extends StatelessWidget {
|
|
||||||
const LeaderboardDialog({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final themeManager = context.watch<ThemeManager>();
|
|
||||||
final theme = themeManager.currentColors;
|
|
||||||
final themeType = themeManager.currentThemeType;
|
|
||||||
final loc = AppLocalizations.of(context)!;
|
|
||||||
|
|
||||||
Widget content = Container(
|
|
||||||
padding: const EdgeInsets.all(20.0),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
gradient: LinearGradient(begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [theme.background.withOpacity(0.95), theme.background.withOpacity(0.8)]),
|
|
||||||
borderRadius: BorderRadius.circular(25),
|
|
||||||
border: Border.all(color: Colors.amber.withOpacity(0.8), width: 2),
|
|
||||||
boxShadow: [BoxShadow(color: Colors.amber.withOpacity(0.2), blurRadius: 20, spreadRadius: 5)]
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
const Icon(Icons.emoji_events, size: 50, color: Colors.amber),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
Text(loc.leaderboardTitle, style: getSharedTextStyle(themeType, TextStyle(fontSize: 20, fontWeight: FontWeight.w900, color: theme.text, letterSpacing: 1.5))),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
|
|
||||||
SizedBox(
|
|
||||||
height: 350,
|
|
||||||
child: StreamBuilder<QuerySnapshot>(
|
|
||||||
stream: FirebaseFirestore.instance.collection('leaderboard').orderBy('xp', descending: true).limit(50).snapshots(),
|
|
||||||
builder: (context, snapshot) {
|
|
||||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
|
||||||
return Center(child: CircularProgressIndicator(color: theme.playerBlue));
|
|
||||||
}
|
|
||||||
if (!snapshot.hasData || snapshot.data!.docs.isEmpty) {
|
|
||||||
return Center(child: Text("Ancora nessun campione...", style: TextStyle(color: theme.text.withOpacity(0.5))));
|
|
||||||
}
|
|
||||||
|
|
||||||
final docs = snapshot.data!.docs;
|
|
||||||
return ListView.builder(
|
|
||||||
physics: const BouncingScrollPhysics(),
|
|
||||||
itemCount: docs.length,
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
var data = docs[index].data() as Map<String, dynamic>;
|
|
||||||
String? myUid = FirebaseAuth.instance.currentUser?.uid;
|
|
||||||
bool isMe = docs[index].id == myUid;
|
|
||||||
|
|
||||||
return Container(
|
|
||||||
margin: const EdgeInsets.only(bottom: 8),
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: isMe ? theme.playerBlue.withOpacity(0.2) : theme.text.withOpacity(0.05),
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
border: isMe ? Border.all(color: theme.playerBlue, width: 1.5) : null
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Text("#${index + 1}", style: getSharedTextStyle(themeType, TextStyle(fontWeight: FontWeight.w900, color: index == 0 ? Colors.amber : (index == 1 ? Colors.grey.shade400 : (index == 2 ? Colors.brown.shade300 : theme.text.withOpacity(0.5)))))),
|
|
||||||
const SizedBox(width: 15),
|
|
||||||
Expanded(child: Text(data['name'] ?? 'Unknown', style: getSharedTextStyle(themeType, TextStyle(fontSize: 16, fontWeight: isMe ? FontWeight.w900 : FontWeight.bold, color: theme.text)))),
|
|
||||||
Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.end,
|
|
||||||
children: [
|
|
||||||
Text("Lv. ${data['level'] ?? 1}", style: TextStyle(color: theme.playerRed, fontWeight: FontWeight.bold, fontSize: 12)),
|
|
||||||
Text("${data['xp'] ?? 0} XP", style: TextStyle(color: theme.text.withOpacity(0.6), fontSize: 10)),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
const SizedBox(height: 15),
|
|
||||||
SizedBox(
|
|
||||||
width: double.infinity, height: 50,
|
|
||||||
child: ElevatedButton(
|
|
||||||
style: ElevatedButton.styleFrom(backgroundColor: Colors.amber.shade700, foregroundColor: Colors.black, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15))),
|
|
||||||
onPressed: () => Navigator.pop(context),
|
|
||||||
child: const Text("CHIUDI", style: TextStyle(fontSize: 16, fontWeight: FontWeight.w900, letterSpacing: 2)),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (themeType == AppThemeType.cyberpunk || themeType == AppThemeType.music) {
|
|
||||||
content = AnimatedCyberBorder(child: content);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Dialog(backgroundColor: Colors.transparent, insetPadding: const EdgeInsets.all(20), child: content);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ===========================================================================
|
|
||||||
// 3. DIALOGO TUTORIAL
|
|
||||||
// ===========================================================================
|
|
||||||
class TutorialDialog extends StatelessWidget {
|
|
||||||
const TutorialDialog({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final themeManager = context.watch<ThemeManager>();
|
|
||||||
final theme = themeManager.currentColors;
|
|
||||||
final themeType = themeManager.currentThemeType;
|
|
||||||
Color inkColor = const Color(0xFF111122);
|
|
||||||
|
|
||||||
String goldLabel = themeType == AppThemeType.grimorio ? "CORONA:" : "ORO:";
|
|
||||||
String bombLabel = themeType == AppThemeType.grimorio ? "STREGA:" : "BOMBA:";
|
|
||||||
String jokerLabel = themeType == AppThemeType.grimorio ? "GIULLARE:" : "JOLLY:";
|
|
||||||
|
|
||||||
Widget dialogContent = themeType == AppThemeType.doodle
|
|
||||||
? Transform.rotate(
|
|
||||||
angle: -0.01,
|
|
||||||
child: CustomPaint(
|
|
||||||
painter: DoodleBackgroundPainter(fillColor: Colors.yellow.shade50, strokeColor: inkColor, seed: 400),
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(25.0),
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Center(child: Text("COME GIOCARE", style: getSharedTextStyle(themeType, TextStyle(fontSize: 28, fontWeight: FontWeight.w900, color: inkColor, letterSpacing: 2)))),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
TutorialStep(icon: Icons.line_axis, text: "Chiudi i 4 lati di un quadrato per conquistare 1 punto e avere una mossa extra!", themeType: themeType, inkColor: inkColor, theme: theme),
|
|
||||||
const SizedBox(height: 15),
|
|
||||||
TutorialStep(icon: Icons.lens_blur, text: "Ma presta attenzione! Ogni quadrato nasconde un'insidia o un regalo!", themeType: themeType, inkColor: inkColor, theme: theme),
|
|
||||||
const SizedBox(height: 15),
|
|
||||||
const Divider(color: Colors.black26, thickness: 2),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
Center(child: Text("GLOSSARIO ARENA", style: getSharedTextStyle(themeType, TextStyle(fontSize: 18, fontWeight: FontWeight.w900, color: inkColor)))),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
|
|
||||||
TutorialStep(icon: ThemeIcons.gold(themeType), iconColor: Colors.amber.shade700, text: "$goldLabel Chiudilo per ottenere +2 Punti.", themeType: themeType, inkColor: inkColor, theme: theme),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
TutorialStep(icon: ThemeIcons.bomb(themeType), iconColor: Colors.deepPurple, text: "$bombLabel Non chiuderlo! Perderai -1 Punto.", themeType: themeType, inkColor: inkColor, theme: theme),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
TutorialStep(icon: ThemeIcons.swap(themeType), iconColor: Colors.purpleAccent, text: "SCAMBIO: Inverte istantaneamente i punteggi dei giocatori.", themeType: themeType, inkColor: inkColor, theme: theme),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
TutorialStep(icon: ThemeIcons.joker(themeType), iconColor: Colors.green.shade600, text: "$jokerLabel Scegli dove nasconderlo a inizio partita. Se lo chiudi tu +2, se lo chiude l'avversario -1!", themeType: themeType, inkColor: inkColor, theme: theme),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
TutorialStep(icon: ThemeIcons.ice(themeType), iconColor: Colors.cyanAccent, text: "GHIACCIO: Devi cliccarlo due volte per poterlo rompere e chiudere.", themeType: themeType, inkColor: inkColor, theme: theme),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
TutorialStep(icon: ThemeIcons.multiplier(themeType), iconColor: Colors.yellowAccent, text: "x2: Non dà punti, ma raddoppia il punteggio della prossima casella che chiudi!", themeType: themeType, inkColor: inkColor, theme: theme),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
TutorialStep(icon: ThemeIcons.block(themeType), iconColor: Colors.grey, text: "BUCO NERO: Questa casella non esiste. Se la chiudi perdi il turno.", themeType: themeType, inkColor: inkColor, theme: theme),
|
|
||||||
|
|
||||||
const SizedBox(height: 25),
|
|
||||||
Center(
|
|
||||||
child: GestureDetector(
|
|
||||||
onTap: () => Navigator.pop(context),
|
|
||||||
child: CustomPaint(
|
|
||||||
painter: DoodleBackgroundPainter(fillColor: Colors.red.shade200, strokeColor: inkColor, seed: 401),
|
|
||||||
child: Container(
|
|
||||||
height: 50, width: 150, alignment: Alignment.center,
|
|
||||||
child: Text("HO CAPITO!", style: getSharedTextStyle(themeType, TextStyle(fontSize: 18, fontWeight: FontWeight.w900, color: inkColor))),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: Container(
|
|
||||||
padding: const EdgeInsets.all(25.0),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
gradient: LinearGradient(begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [theme.background.withOpacity(0.95), theme.background.withOpacity(0.8)]),
|
|
||||||
borderRadius: BorderRadius.circular(25),
|
|
||||||
border: themeType == AppThemeType.cyberpunk || themeType == AppThemeType.arcade || themeType == AppThemeType.music ? null : Border.all(color: Colors.white.withOpacity(0.15), width: 1.5),
|
|
||||||
boxShadow: themeType == AppThemeType.cyberpunk || themeType == AppThemeType.arcade || themeType == AppThemeType.music ? [] : [BoxShadow(color: Colors.black.withOpacity(0.5), blurRadius: 20, offset: const Offset(4, 10))],
|
|
||||||
),
|
|
||||||
child: SingleChildScrollView(
|
|
||||||
physics: const BouncingScrollPhysics(),
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Center(child: Text("COME GIOCARE", style: getSharedTextStyle(themeType, TextStyle(fontSize: 24, fontWeight: FontWeight.w900, color: theme.text, letterSpacing: 2)))),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
TutorialStep(icon: Icons.grid_4x4, text: "Chiudi i 4 lati di un quadrato per conquistare 1 punto e avere una mossa extra!", themeType: themeType, inkColor: inkColor, theme: theme),
|
|
||||||
const SizedBox(height: 15),
|
|
||||||
TutorialStep(icon: Icons.lens_blur, text: "Ma presta attenzione! Ogni quadrato nasconde un'insidia o un regalo!", themeType: themeType, inkColor: inkColor, theme: theme),
|
|
||||||
const SizedBox(height: 15),
|
|
||||||
const Divider(color: Colors.white24, thickness: 1.5),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
Center(child: Text("GLOSSARIO ARENA", style: getSharedTextStyle(themeType, TextStyle(fontSize: 16, fontWeight: FontWeight.w900, color: theme.text.withOpacity(0.7), letterSpacing: 1.5)))),
|
|
||||||
const SizedBox(height: 15),
|
|
||||||
|
|
||||||
TutorialStep(icon: ThemeIcons.gold(themeType), iconColor: Colors.amber, text: "$goldLabel Chiudilo per ottenere +2 Punti.", themeType: themeType, inkColor: inkColor, theme: theme),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
TutorialStep(icon: ThemeIcons.bomb(themeType), iconColor: themeType == AppThemeType.cyberpunk || themeType == AppThemeType.arcade ? Colors.greenAccent : Colors.deepPurple, text: "$bombLabel Non chiuderlo! Perderai -1 Punto.", themeType: themeType, inkColor: inkColor, theme: theme),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
TutorialStep(icon: ThemeIcons.swap(themeType), iconColor: Colors.purpleAccent, text: "SCAMBIO: Inverte istantaneamente i punteggi dei giocatori.", themeType: themeType, inkColor: inkColor, theme: theme),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
TutorialStep(icon: ThemeIcons.joker(themeType), iconColor: theme.playerBlue, text: "$jokerLabel Scegli dove nasconderlo a inizio partita. Se lo chiudi tu +2, se lo chiude l'avversario -1!", themeType: themeType, inkColor: inkColor, theme: theme),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
TutorialStep(icon: ThemeIcons.ice(themeType), iconColor: Colors.cyanAccent, text: "GHIACCIO: Devi cliccarlo due volte per poterlo rompere e chiudere.", themeType: themeType, inkColor: inkColor, theme: theme),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
TutorialStep(icon: ThemeIcons.multiplier(themeType), iconColor: Colors.yellowAccent, text: "x2: Non dà punti, ma raddoppia il punteggio della prossima casella che chiudi!", themeType: themeType, inkColor: inkColor, theme: theme),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
TutorialStep(icon: ThemeIcons.block(themeType), iconColor: Colors.grey, text: "BUCO NERO: Questa casella non esiste. Se la chiudi perdi il turno.", themeType: themeType, inkColor: inkColor, theme: theme),
|
|
||||||
|
|
||||||
const SizedBox(height: 30),
|
|
||||||
SizedBox(
|
|
||||||
width: double.infinity, height: 50,
|
|
||||||
child: ElevatedButton(
|
|
||||||
style: ElevatedButton.styleFrom(backgroundColor: theme.playerBlue, foregroundColor: Colors.white, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15))),
|
|
||||||
onPressed: () => Navigator.pop(context),
|
|
||||||
child: const Text("CHIUDI", style: TextStyle(fontSize: 16, fontWeight: FontWeight.w900, letterSpacing: 2)),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (themeType == AppThemeType.cyberpunk || themeType == AppThemeType.music) {
|
|
||||||
dialogContent = AnimatedCyberBorder(child: dialogContent);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Dialog(backgroundColor: Colors.transparent, insetPadding: const EdgeInsets.symmetric(horizontal: 20, vertical: 20), child: dialogContent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class TutorialStep extends StatelessWidget {
|
|
||||||
final IconData icon;
|
|
||||||
final Color? iconColor;
|
|
||||||
final String text;
|
|
||||||
final AppThemeType themeType;
|
|
||||||
final Color inkColor;
|
|
||||||
final ThemeColors theme;
|
|
||||||
|
|
||||||
const TutorialStep({super.key, required this.icon, this.iconColor, required this.text, required this.themeType, required this.inkColor, required this.theme});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Row(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Icon(icon, color: iconColor ?? (themeType == AppThemeType.doodle ? inkColor : theme.playerBlue), size: 28),
|
|
||||||
const SizedBox(width: 15),
|
|
||||||
Expanded(
|
|
||||||
child: Text(text, style: getSharedTextStyle(themeType, TextStyle(fontSize: 14, color: themeType == AppThemeType.doodle ? inkColor : theme.text.withOpacity(0.8), height: 1.3))),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,12 +1,7 @@
|
||||||
// ===========================================================================
|
|
||||||
// FILE: lib/ui/multiplayer/lobby_screen.dart
|
|
||||||
// ===========================================================================
|
|
||||||
|
|
||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
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';
|
||||||
import 'package:firebase_auth/firebase_auth.dart';
|
|
||||||
import 'dart:math' as math;
|
import 'dart:math' as math;
|
||||||
import '../../logic/game_controller.dart';
|
import '../../logic/game_controller.dart';
|
||||||
import '../../models/game_board.dart';
|
import '../../models/game_board.dart';
|
||||||
|
|
@ -245,14 +240,14 @@ class _NeonTimeSwitch extends StatelessWidget {
|
||||||
mainAxisSize: MainAxisSize.max,
|
mainAxisSize: MainAxisSize.max,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Icon(isTimeMode ? Icons.timer : Icons.timer_off, color: isTimeMode ? Colors.white : doodleColor, size: 20),
|
Icon(isTimeMode ? Icons.timer : Icons.timer_off, color: isTimeMode ? Colors.white : doodleColor, size: 24),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 12),
|
||||||
Column(
|
Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Text(isTimeMode ? 'A TEMPO' : 'RELAX', style: _getTextStyle(themeType, TextStyle(color: isTimeMode ? Colors.white : doodleColor, fontWeight: FontWeight.w900, fontSize: 12, letterSpacing: 1.0))),
|
Text(isTimeMode ? 'A TEMPO' : 'RELAX', style: _getTextStyle(themeType, TextStyle(color: isTimeMode ? Colors.white : doodleColor, fontWeight: FontWeight.w900, fontSize: 14, letterSpacing: 2.0))),
|
||||||
Text(isTimeMode ? '15s a mossa' : 'Senza limiti', style: _getTextStyle(themeType, TextStyle(color: isTimeMode ? Colors.white : doodleColor.withOpacity(0.8), fontSize: 9, fontWeight: FontWeight.bold))),
|
Text(isTimeMode ? '15 sec a mossa' : 'Nessun limite', style: _getTextStyle(themeType, TextStyle(color: isTimeMode ? Colors.white : doodleColor.withOpacity(0.8), fontSize: 11, fontWeight: FontWeight.bold))),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
@ -289,107 +284,14 @@ class _NeonTimeSwitch extends StatelessWidget {
|
||||||
mainAxisSize: MainAxisSize.max,
|
mainAxisSize: MainAxisSize.max,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Icon(isTimeMode ? Icons.timer : Icons.timer_off, color: isTimeMode ? Colors.amber : theme.text.withOpacity(0.5), size: 20),
|
Icon(isTimeMode ? Icons.timer : Icons.timer_off, color: isTimeMode ? Colors.amber : theme.text.withOpacity(0.5), size: 24),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 12),
|
||||||
Column(
|
Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Text(isTimeMode ? 'A TEMPO' : 'RELAX', style: _getTextStyle(themeType, TextStyle(color: isTimeMode ? Colors.white : theme.text.withOpacity(0.5), fontWeight: FontWeight.w900, fontSize: 11, letterSpacing: 1.5))),
|
Text(isTimeMode ? 'A TEMPO' : 'RELAX', style: _getTextStyle(themeType, TextStyle(color: isTimeMode ? Colors.white : theme.text.withOpacity(0.5), fontWeight: FontWeight.w900, fontSize: 13, letterSpacing: 1.5))),
|
||||||
Text(isTimeMode ? '15s a mossa' : 'Senza limiti', style: _getTextStyle(themeType, TextStyle(color: isTimeMode ? Colors.amber.shade200 : theme.text.withOpacity(0.4), fontSize: 9, fontWeight: FontWeight.bold))),
|
Text(isTimeMode ? '15 sec a mossa' : 'Nessun limite', style: _getTextStyle(themeType, TextStyle(color: isTimeMode ? Colors.amber.shade200 : theme.text.withOpacity(0.4), fontSize: 10, fontWeight: FontWeight.bold))),
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _NeonPrivacySwitch extends StatelessWidget {
|
|
||||||
final bool isPublic;
|
|
||||||
final ThemeColors theme;
|
|
||||||
final AppThemeType themeType;
|
|
||||||
final VoidCallback onTap;
|
|
||||||
|
|
||||||
const _NeonPrivacySwitch({required this.isPublic, required this.theme, required this.themeType, required this.onTap});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
if (themeType == AppThemeType.doodle) {
|
|
||||||
Color doodleColor = isPublic ? Colors.green.shade600 : Colors.red.shade600;
|
|
||||||
return Transform.rotate(
|
|
||||||
angle: 0.015,
|
|
||||||
child: GestureDetector(
|
|
||||||
onTap: onTap,
|
|
||||||
child: AnimatedContainer(
|
|
||||||
duration: const Duration(milliseconds: 200),
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
|
||||||
transform: Matrix4.translationValues(0, isPublic ? 3 : 0, 0),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: isPublic ? doodleColor : Colors.white,
|
|
||||||
borderRadius: const BorderRadius.only(
|
|
||||||
topLeft: Radius.circular(15), topRight: Radius.circular(8),
|
|
||||||
bottomLeft: Radius.circular(6), bottomRight: Radius.circular(15),
|
|
||||||
),
|
|
||||||
border: Border.all(color: isPublic ? theme.text : doodleColor.withOpacity(0.5), width: 2.5),
|
|
||||||
boxShadow: [BoxShadow(color: isPublic ? theme.text.withOpacity(0.8) : doodleColor.withOpacity(0.2), offset: const Offset(4, 5), blurRadius: 0)],
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
mainAxisSize: MainAxisSize.max,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Icon(isPublic ? Icons.public : Icons.lock, color: isPublic ? Colors.white : doodleColor, size: 20),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Text(isPublic ? 'PUBBLICA' : 'PRIVATA', style: _getTextStyle(themeType, TextStyle(color: isPublic ? Colors.white : doodleColor, fontWeight: FontWeight.w900, fontSize: 12, letterSpacing: 1.0))),
|
|
||||||
Text(isPublic ? 'In Bacheca' : 'Solo Codice', style: _getTextStyle(themeType, TextStyle(color: isPublic ? Colors.white : doodleColor.withOpacity(0.8), fontSize: 9, fontWeight: FontWeight.bold))),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return GestureDetector(
|
|
||||||
onTap: onTap,
|
|
||||||
child: AnimatedContainer(
|
|
||||||
duration: const Duration(milliseconds: 300),
|
|
||||||
curve: Curves.easeInOut,
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
borderRadius: BorderRadius.circular(15),
|
|
||||||
gradient: LinearGradient(
|
|
||||||
begin: Alignment.topLeft,
|
|
||||||
end: Alignment.bottomRight,
|
|
||||||
colors: isPublic
|
|
||||||
? [Colors.greenAccent.withOpacity(0.25), Colors.greenAccent.withOpacity(0.05)]
|
|
||||||
: [theme.playerRed.withOpacity(0.25), theme.playerRed.withOpacity(0.05)],
|
|
||||||
),
|
|
||||||
border: Border.all(color: isPublic ? Colors.greenAccent : theme.playerRed, width: isPublic ? 2 : 1),
|
|
||||||
boxShadow: isPublic
|
|
||||||
? [BoxShadow(color: Colors.greenAccent.withOpacity(0.3), blurRadius: 15, spreadRadius: 2)]
|
|
||||||
: [
|
|
||||||
BoxShadow(color: Colors.black.withOpacity(0.4), blurRadius: 6, offset: const Offset(2, 4)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
mainAxisSize: MainAxisSize.max,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Icon(isPublic ? Icons.public : Icons.lock, color: isPublic ? Colors.greenAccent : theme.playerRed, size: 20),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Text(isPublic ? 'PUBBLICA' : 'PRIVATA', style: _getTextStyle(themeType, TextStyle(color: isPublic ? Colors.white : theme.text.withOpacity(0.8), fontWeight: FontWeight.w900, fontSize: 11, letterSpacing: 1.5))),
|
|
||||||
Text(isPublic ? 'Tutti ti vedono' : 'Solo con Codice', style: _getTextStyle(themeType, TextStyle(color: isPublic ? Colors.greenAccent.shade200 : theme.playerRed.withOpacity(0.7), fontSize: 9, fontWeight: FontWeight.bold))),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
@ -411,7 +313,7 @@ class _NeonActionButton extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
if (themeType == AppThemeType.doodle) {
|
if (themeType == AppThemeType.doodle) {
|
||||||
double tilt = (label == "UNISCITI" || label == "ANNULLA") ? -0.015 : 0.02;
|
double tilt = (label == "UNISCITI") ? -0.015 : 0.02;
|
||||||
return Transform.rotate(
|
return Transform.rotate(
|
||||||
angle: tilt,
|
angle: tilt,
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
|
|
@ -428,13 +330,7 @@ class _NeonActionButton extends StatelessWidget {
|
||||||
boxShadow: [BoxShadow(color: theme.text.withOpacity(0.9), offset: const Offset(4, 4), blurRadius: 0)],
|
boxShadow: [BoxShadow(color: theme.text.withOpacity(0.9), offset: const Offset(4, 4), blurRadius: 0)],
|
||||||
),
|
),
|
||||||
child: Center(
|
child: Center(
|
||||||
child: FittedBox(
|
child: Text(label, style: _getTextStyle(themeType, const TextStyle(fontSize: 20, fontWeight: FontWeight.w900, letterSpacing: 3.0, color: Colors.white))),
|
||||||
fit: BoxFit.scaleDown,
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 10.0),
|
|
||||||
child: Text(label, style: _getTextStyle(themeType, const TextStyle(fontSize: 20, fontWeight: FontWeight.w900, letterSpacing: 3.0, color: Colors.white))),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -455,13 +351,7 @@ class _NeonActionButton extends StatelessWidget {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
child: Center(
|
child: Center(
|
||||||
child: FittedBox(
|
child: Text(label, style: _getTextStyle(themeType, const TextStyle(fontSize: 16, fontWeight: FontWeight.w900, letterSpacing: 2.0, color: Colors.white, shadows: [Shadow(color: Colors.black, blurRadius: 2, offset: Offset(1, 1))]))),
|
||||||
fit: BoxFit.scaleDown,
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 10.0),
|
|
||||||
child: Text(label, style: _getTextStyle(themeType, const TextStyle(fontSize: 16, fontWeight: FontWeight.w900, letterSpacing: 2.0, color: Colors.white, shadows: [Shadow(color: Colors.black, blurRadius: 2, offset: Offset(1, 1))]))),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
@ -533,7 +423,7 @@ class LobbyScreen extends StatefulWidget {
|
||||||
State<LobbyScreen> createState() => _LobbyScreenState();
|
State<LobbyScreen> createState() => _LobbyScreenState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _LobbyScreenState extends State<LobbyScreen> with WidgetsBindingObserver {
|
class _LobbyScreenState extends State<LobbyScreen> {
|
||||||
final MultiplayerService _multiplayerService = MultiplayerService();
|
final MultiplayerService _multiplayerService = MultiplayerService();
|
||||||
late TextEditingController _codeController;
|
late TextEditingController _codeController;
|
||||||
|
|
||||||
|
|
@ -541,20 +431,13 @@ class _LobbyScreenState extends State<LobbyScreen> with WidgetsBindingObserver {
|
||||||
String? _myRoomCode;
|
String? _myRoomCode;
|
||||||
String _playerName = '';
|
String _playerName = '';
|
||||||
|
|
||||||
// Variabile per gestire l'effetto "sipario"
|
|
||||||
bool _isCreatingRoom = false;
|
|
||||||
|
|
||||||
int _selectedRadius = 4;
|
int _selectedRadius = 4;
|
||||||
ArenaShape _selectedShape = ArenaShape.classic;
|
ArenaShape _selectedShape = ArenaShape.classic;
|
||||||
bool _isTimeMode = true;
|
bool _isTimeMode = true;
|
||||||
bool _isPublicRoom = true;
|
|
||||||
|
|
||||||
bool _roomStarted = false;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
WidgetsBinding.instance.addObserver(this);
|
|
||||||
_codeController = TextEditingController();
|
_codeController = TextEditingController();
|
||||||
_playerName = StorageService.instance.playerName;
|
_playerName = StorageService.instance.playerName;
|
||||||
|
|
||||||
|
|
@ -566,53 +449,30 @@ class _LobbyScreenState extends State<LobbyScreen> with WidgetsBindingObserver {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() { _codeController.dispose(); super.dispose(); }
|
||||||
WidgetsBinding.instance.removeObserver(this);
|
|
||||||
_cleanupGhostRoom();
|
|
||||||
_codeController.dispose();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void didChangeAppLifecycleState(AppLifecycleState state) {
|
|
||||||
if (state == AppLifecycleState.paused || state == AppLifecycleState.detached) {
|
|
||||||
_cleanupGhostRoom();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _cleanupGhostRoom() {
|
|
||||||
if (_myRoomCode != null && !_roomStarted) {
|
|
||||||
FirebaseFirestore.instance.collection('games').doc(_myRoomCode).delete();
|
|
||||||
_myRoomCode = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _createRoom() async {
|
Future<void> _createRoom() async {
|
||||||
if (_isLoading) return;
|
if (_isLoading) return;
|
||||||
setState(() => _isLoading = true);
|
setState(() => _isLoading = true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
String code = await _multiplayerService.createGameRoom(
|
String code = await _multiplayerService.createGameRoom(_selectedRadius, _playerName, _selectedShape.name, _isTimeMode);
|
||||||
_selectedRadius, _playerName, _selectedShape.name, _isTimeMode, isPublic: _isPublicRoom
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
setState(() { _myRoomCode = code; _isLoading = false; _roomStarted = false; });
|
setState(() { _myRoomCode = code; _isLoading = false; });
|
||||||
|
|
||||||
if (!_isPublicRoom) {
|
_multiplayerService.shareInviteLink(code);
|
||||||
_multiplayerService.shareInviteLink(code);
|
|
||||||
}
|
|
||||||
_showWaitingDialog(code);
|
_showWaitingDialog(code);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (mounted) { setState(() => _isLoading = false); _showError("Errore durante la creazione della partita."); }
|
if (mounted) { setState(() => _isLoading = false); _showError("Errore durante la creazione della partita."); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _joinRoomByCode(String code) async {
|
Future<void> _joinRoom() async {
|
||||||
if (_isLoading) return;
|
if (_isLoading) return;
|
||||||
FocusScope.of(context).unfocus();
|
FocusScope.of(context).unfocus();
|
||||||
|
|
||||||
code = code.trim().toUpperCase();
|
String code = _codeController.text.trim().toUpperCase();
|
||||||
if (code.isEmpty || code.length != 5) { _showError("Inserisci un codice valido di 5 caratteri."); return; }
|
if (code.isEmpty || code.length != 5) { _showError("Inserisci un codice valido di 5 caratteri."); return; }
|
||||||
|
|
||||||
setState(() => _isLoading = true);
|
setState(() => _isLoading = true);
|
||||||
|
|
@ -672,10 +532,10 @@ class _LobbyScreenState extends State<LobbyScreen> with WidgetsBindingObserver {
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Icon(_isPublicRoom ? Icons.podcasts : Icons.share, color: theme.playerBlue, size: 32), const SizedBox(height: 12),
|
Icon(Icons.share, color: theme.playerBlue, size: 32), const SizedBox(height: 12),
|
||||||
Text(_isPublicRoom ? "Sei in Bacheca!" : "Invita un amico", textAlign: TextAlign.center, style: _getTextStyle(themeType, TextStyle(color: theme.text, fontWeight: FontWeight.w900, fontSize: 18))),
|
Text("Invita un amico", textAlign: TextAlign.center, style: _getTextStyle(themeType, TextStyle(color: theme.text, fontWeight: FontWeight.w900, fontSize: 18))),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
Text(_isPublicRoom ? "Aspettiamo che uno sfidante si unisca dalla lobby pubblica." : "Condividi il codice. La partita inizierà appena si unirà.", textAlign: TextAlign.center, style: _getTextStyle(themeType, TextStyle(color: themeType == AppThemeType.doodle ? theme.text : theme.text.withOpacity(0.8), fontSize: 14, height: 1.5))),
|
Text("Condividi il codice. La partita inizierà appena si unirà.", textAlign: TextAlign.center, style: _getTextStyle(themeType, TextStyle(color: themeType == AppThemeType.doodle ? theme.text : theme.text.withOpacity(0.8), fontSize: 14, height: 1.5))),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -704,7 +564,6 @@ class _LobbyScreenState extends State<LobbyScreen> with WidgetsBindingObserver {
|
||||||
if (snapshot.hasData && snapshot.data!.exists) {
|
if (snapshot.hasData && snapshot.data!.exists) {
|
||||||
var data = snapshot.data!.data() as Map<String, dynamic>;
|
var data = snapshot.data!.data() as Map<String, dynamic>;
|
||||||
if (data['status'] == 'playing') {
|
if (data['status'] == 'playing') {
|
||||||
_roomStarted = true;
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
context.read<GameController>().startNewGame(_selectedRadius, isOnline: true, roomCode: code, isHost: true, shape: _selectedShape, timeMode: _isTimeMode);
|
context.read<GameController>().startNewGame(_selectedRadius, isOnline: true, roomCode: code, isHost: true, shape: _selectedShape, timeMode: _isTimeMode);
|
||||||
|
|
@ -713,30 +572,19 @@ class _LobbyScreenState extends State<LobbyScreen> with WidgetsBindingObserver {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return PopScope(
|
return Dialog(
|
||||||
canPop: false,
|
backgroundColor: Colors.transparent,
|
||||||
onPopInvoked: (didPop) {
|
insetPadding: const EdgeInsets.all(20),
|
||||||
if (didPop) return;
|
child: Column(
|
||||||
_cleanupGhostRoom();
|
mainAxisSize: MainAxisSize.min,
|
||||||
Navigator.pop(context);
|
children: [
|
||||||
},
|
dialogContent,
|
||||||
child: Dialog(
|
const SizedBox(height: 20),
|
||||||
backgroundColor: Colors.transparent,
|
TextButton(
|
||||||
insetPadding: const EdgeInsets.all(20),
|
onPressed: () { FirebaseFirestore.instance.collection('games').doc(code).delete(); Navigator.pop(context); },
|
||||||
child: Column(
|
child: Text("ANNULLA", style: _getTextStyle(themeType, TextStyle(color: Colors.red, fontWeight: FontWeight.w900, fontSize: 20, letterSpacing: 2.0, shadows: themeType == AppThemeType.doodle ? [] : [const Shadow(color: Colors.black, blurRadius: 2)]))),
|
||||||
mainAxisSize: MainAxisSize.min,
|
),
|
||||||
children: [
|
],
|
||||||
dialogContent,
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
TextButton(
|
|
||||||
onPressed: () {
|
|
||||||
_cleanupGhostRoom();
|
|
||||||
Navigator.pop(context);
|
|
||||||
},
|
|
||||||
child: Text("ANNULLA", style: _getTextStyle(themeType, TextStyle(color: Colors.red, fontWeight: FontWeight.w900, fontSize: 20, letterSpacing: 2.0, shadows: themeType == AppThemeType.doodle ? [] : [const Shadow(color: Colors.black, blurRadius: 2)]))),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
@ -757,9 +605,9 @@ class _LobbyScreenState extends State<LobbyScreen> with WidgetsBindingObserver {
|
||||||
if (themeType == AppThemeType.cyberpunk) bgImage = 'assets/images/cyber_bg.jpg';
|
if (themeType == AppThemeType.cyberpunk) bgImage = 'assets/images/cyber_bg.jpg';
|
||||||
|
|
||||||
bool isChaosUnlocked = true;
|
bool isChaosUnlocked = true;
|
||||||
|
|
||||||
Color doodlePenColor = const Color(0xFF00008B);
|
Color doodlePenColor = const Color(0xFF00008B);
|
||||||
|
|
||||||
// --- PANNELLO IMPOSTAZIONI STANZA ---
|
|
||||||
Widget hostPanel = Transform.rotate(
|
Widget hostPanel = Transform.rotate(
|
||||||
angle: themeType == AppThemeType.doodle ? 0.01 : 0,
|
angle: themeType == AppThemeType.doodle ? 0.01 : 0,
|
||||||
child: Container(
|
child: Container(
|
||||||
|
|
@ -777,7 +625,7 @@ class _LobbyScreenState extends State<LobbyScreen> with WidgetsBindingObserver {
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Center(child: Text("IMPOSTAZIONI STANZA", textAlign: TextAlign.center, style: _getTextStyle(themeType, TextStyle(fontSize: 12, fontWeight: FontWeight.w900, color: themeType == AppThemeType.doodle ? theme.text : theme.text.withOpacity(0.6), letterSpacing: 2.0)))),
|
Center(child: Text("IMPOSTAZIONI GRIGLIA", textAlign: TextAlign.center, style: _getTextStyle(themeType, TextStyle(fontSize: 12, fontWeight: FontWeight.w900, color: themeType == AppThemeType.doodle ? theme.text : theme.text.withOpacity(0.6), letterSpacing: 2.0)))),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
|
|
||||||
Text("FORMA ARENA", style: _getTextStyle(themeType, TextStyle(fontSize: 10, fontWeight: FontWeight.w900, color: themeType == AppThemeType.doodle ? theme.text : theme.text.withOpacity(0.5), letterSpacing: 1.5))),
|
Text("FORMA ARENA", style: _getTextStyle(themeType, TextStyle(fontSize: 10, fontWeight: FontWeight.w900, color: themeType == AppThemeType.doodle ? theme.text : theme.text.withOpacity(0.5), letterSpacing: 1.5))),
|
||||||
|
|
@ -786,15 +634,11 @@ class _LobbyScreenState extends State<LobbyScreen> with WidgetsBindingObserver {
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
Expanded(child: _NeonShapeButton(icon: Icons.diamond_outlined, label: 'Rombo', isSelected: _selectedShape == ArenaShape.classic, theme: theme, themeType: themeType, onTap: () => setState(() => _selectedShape = ArenaShape.classic))),
|
_NeonShapeButton(icon: Icons.diamond_outlined, label: 'Rombo', isSelected: _selectedShape == ArenaShape.classic, theme: theme, themeType: themeType, onTap: () => setState(() => _selectedShape = ArenaShape.classic)),
|
||||||
const SizedBox(width: 4),
|
_NeonShapeButton(icon: Icons.add, label: 'Croce', isSelected: _selectedShape == ArenaShape.cross, theme: theme, themeType: themeType, onTap: () => setState(() => _selectedShape = ArenaShape.cross)),
|
||||||
Expanded(child: _NeonShapeButton(icon: Icons.add, label: 'Croce', isSelected: _selectedShape == ArenaShape.cross, theme: theme, themeType: themeType, onTap: () => setState(() => _selectedShape = ArenaShape.cross))),
|
_NeonShapeButton(icon: Icons.donut_large, label: 'Buco', isSelected: _selectedShape == ArenaShape.donut, theme: theme, themeType: themeType, onTap: () => setState(() => _selectedShape = ArenaShape.donut)),
|
||||||
const SizedBox(width: 4),
|
_NeonShapeButton(icon: Icons.hourglass_bottom, label: 'Clessidra', isSelected: _selectedShape == ArenaShape.hourglass, theme: theme, themeType: themeType, onTap: () => setState(() => _selectedShape = ArenaShape.hourglass)),
|
||||||
Expanded(child: _NeonShapeButton(icon: Icons.donut_large, label: 'Buco', isSelected: _selectedShape == ArenaShape.donut, theme: theme, themeType: themeType, onTap: () => setState(() => _selectedShape = ArenaShape.donut))),
|
_NeonShapeButton(icon: Icons.all_inclusive, label: 'Caos', isSelected: _selectedShape == ArenaShape.chaos, theme: theme, themeType: themeType, isSpecial: true, isLocked: !isChaosUnlocked, onTap: () => setState(() => _selectedShape = ArenaShape.chaos)),
|
||||||
const SizedBox(width: 4),
|
|
||||||
Expanded(child: _NeonShapeButton(icon: Icons.hourglass_bottom, label: 'Clessidra', isSelected: _selectedShape == ArenaShape.hourglass, theme: theme, themeType: themeType, onTap: () => setState(() => _selectedShape = ArenaShape.hourglass))),
|
|
||||||
const SizedBox(width: 4),
|
|
||||||
Expanded(child: _NeonShapeButton(icon: Icons.all_inclusive, label: 'Caos', isSelected: _selectedShape == ArenaShape.chaos, theme: theme, themeType: themeType, isSpecial: true, isLocked: !isChaosUnlocked, onTap: () => setState(() => _selectedShape = ArenaShape.chaos))),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
||||||
|
|
@ -818,15 +662,9 @@ class _LobbyScreenState extends State<LobbyScreen> with WidgetsBindingObserver {
|
||||||
Divider(color: themeType == AppThemeType.doodle ? theme.text.withOpacity(0.5) : Colors.white.withOpacity(0.05), thickness: themeType == AppThemeType.doodle ? 2.5 : 1.5),
|
Divider(color: themeType == AppThemeType.doodle ? theme.text.withOpacity(0.5) : Colors.white.withOpacity(0.05), thickness: themeType == AppThemeType.doodle ? 2.5 : 1.5),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
|
|
||||||
Text("REGOLE E VISIBILITÀ", style: _getTextStyle(themeType, TextStyle(fontSize: 10, fontWeight: FontWeight.w900, color: themeType == AppThemeType.doodle ? theme.text : theme.text.withOpacity(0.5), letterSpacing: 1.5))),
|
Text("TEMPO", style: _getTextStyle(themeType, TextStyle(fontSize: 10, fontWeight: FontWeight.w900, color: themeType == AppThemeType.doodle ? theme.text : theme.text.withOpacity(0.5), letterSpacing: 1.5))),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
Row(
|
_NeonTimeSwitch(isTimeMode: _isTimeMode, theme: theme, themeType: themeType, onTap: () => setState(() => _isTimeMode = !_isTimeMode)),
|
||||||
children: [
|
|
||||||
Expanded(child: _NeonTimeSwitch(isTimeMode: _isTimeMode, theme: theme, themeType: themeType, onTap: () => setState(() => _isTimeMode = !_isTimeMode))),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
Expanded(child: _NeonPrivacySwitch(isPublic: _isPublicRoom, theme: theme, themeType: themeType, onTap: () => setState(() => _isPublicRoom = !_isPublicRoom))),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -838,9 +676,7 @@ class _LobbyScreenState extends State<LobbyScreen> with WidgetsBindingObserver {
|
||||||
|
|
||||||
Widget uiContent = SafeArea(
|
Widget uiContent = SafeArea(
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
physics: const BouncingScrollPhysics(),
|
padding: const EdgeInsets.symmetric(horizontal: 20.0, vertical: 10.0),
|
||||||
// Padding inferiore aumentato a 60 per evitare il taglio dei pulsanti
|
|
||||||
padding: EdgeInsets.only(left: 20.0, right: 20.0, top: 10.0, bottom: MediaQuery.of(context).padding.bottom + 60.0),
|
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
|
|
@ -867,198 +703,51 @@ class _LobbyScreenState extends State<LobbyScreen> with WidgetsBindingObserver {
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
|
hostPanel,
|
||||||
|
const SizedBox(height: 15),
|
||||||
|
_NeonActionButton(label: "CREA PARTITA", color: theme.playerRed, onTap: _createRoom, theme: theme, themeType: themeType),
|
||||||
|
|
||||||
// --- L'EFFETTO SIPARIO CON ANIMATED SIZE ---
|
const SizedBox(height: 20),
|
||||||
AnimatedSize(
|
|
||||||
duration: const Duration(milliseconds: 300),
|
|
||||||
curve: Curves.easeInOut,
|
|
||||||
alignment: Alignment.topCenter,
|
|
||||||
child: _isCreatingRoom
|
|
||||||
? Column( // MENU CREAZIONE (Aperto)
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
||||||
children: [
|
|
||||||
hostPanel,
|
|
||||||
const SizedBox(height: 15),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Expanded( // Entrambi in un Expanded "liscio" si dividono il 50% di spazio
|
|
||||||
child: _NeonActionButton(label: "AVVIA", color: theme.playerRed, onTap: _createRoom, theme: theme, themeType: themeType),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 10),
|
|
||||||
Expanded( // Entrambi in un Expanded "liscio" si dividono il 50% di spazio
|
|
||||||
child: _NeonActionButton(label: "ANNULLA", color: Colors.grey.shade600, onTap: () => setState(() => _isCreatingRoom = false), theme: theme, themeType: themeType),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
: Column( // MENU BASE (Chiuso)
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
||||||
children: [
|
|
||||||
_NeonActionButton(label: "CREA PARTITA", color: theme.playerRed, onTap: () { FocusScope.of(context).unfocus(); setState(() => _isCreatingRoom = true); }, theme: theme, themeType: themeType),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Expanded(child: Divider(color: theme.text.withOpacity(0.4), thickness: themeType == AppThemeType.doodle ? 2 : 1.0)),
|
|
||||||
Padding(padding: const EdgeInsets.symmetric(horizontal: 10), child: Text("OPPURE", style: _getTextStyle(themeType, TextStyle(color: themeType == AppThemeType.doodle ? theme.text : theme.text.withOpacity(0.5), fontWeight: FontWeight.bold, letterSpacing: 2.0, fontSize: 13)))),
|
|
||||||
Expanded(child: Divider(color: theme.text.withOpacity(0.4), thickness: themeType == AppThemeType.doodle ? 2 : 1.0)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
|
|
||||||
Transform.rotate(
|
|
||||||
angle: themeType == AppThemeType.doodle ? 0.02 : 0,
|
|
||||||
child: Container(
|
|
||||||
decoration: themeType == AppThemeType.doodle ? BoxDecoration(
|
|
||||||
color: Colors.white,
|
|
||||||
borderRadius: const BorderRadius.only(topLeft: Radius.circular(20), bottomRight: Radius.circular(20), topRight: Radius.circular(5), bottomLeft: Radius.circular(5)),
|
|
||||||
border: Border.all(color: theme.text, width: 2.5),
|
|
||||||
boxShadow: [BoxShadow(color: theme.text.withOpacity(0.8), offset: const Offset(5, 5), blurRadius: 0)],
|
|
||||||
) : BoxDecoration(
|
|
||||||
boxShadow: [BoxShadow(color: theme.playerBlue.withOpacity(0.15), blurRadius: 15, spreadRadius: 1)]
|
|
||||||
),
|
|
||||||
child: TextField(
|
|
||||||
controller: _codeController, textCapitalization: TextCapitalization.characters, textAlign: TextAlign.center, maxLength: 5,
|
|
||||||
style: _getTextStyle(themeType, TextStyle(fontSize: 28, fontWeight: FontWeight.w900, color: theme.text, letterSpacing: 12, shadows: themeType == AppThemeType.doodle ? [] : [Shadow(color: theme.playerBlue.withOpacity(0.5), blurRadius: 8)])),
|
|
||||||
decoration: InputDecoration(
|
|
||||||
contentPadding: const EdgeInsets.symmetric(vertical: 12),
|
|
||||||
hintText: "CODICE", hintStyle: _getTextStyle(themeType, TextStyle(color: theme.text.withOpacity(0.3), letterSpacing: 10, fontSize: 20)), counterText: "",
|
|
||||||
filled: themeType != AppThemeType.doodle,
|
|
||||||
fillColor: themeType == AppThemeType.cyberpunk ? Colors.black.withOpacity(0.85) : theme.text.withOpacity(0.05),
|
|
||||||
enabledBorder: themeType == AppThemeType.doodle ? InputBorder.none : OutlineInputBorder(borderSide: BorderSide(color: theme.gridLine.withOpacity(0.5), width: 2.0), borderRadius: BorderRadius.circular(15)),
|
|
||||||
focusedBorder: themeType == AppThemeType.doodle ? InputBorder.none : OutlineInputBorder(borderSide: BorderSide(color: theme.playerBlue, width: 3.0), borderRadius: BorderRadius.circular(15)),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 15),
|
|
||||||
_NeonActionButton(label: "UNISCITI", color: theme.playerBlue, onTap: () => _joinRoomByCode(_codeController.text), theme: theme, themeType: themeType),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
const SizedBox(height: 25),
|
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(child: Divider(color: theme.text.withOpacity(0.4), thickness: themeType == AppThemeType.doodle ? 2 : 1.0)),
|
Expanded(child: Divider(color: theme.text.withOpacity(0.4), thickness: themeType == AppThemeType.doodle ? 2 : 1.0)),
|
||||||
Padding(padding: const EdgeInsets.symmetric(horizontal: 10), child: Text("LOBBY PUBBLICA", style: _getTextStyle(themeType, TextStyle(color: themeType == AppThemeType.doodle ? theme.text : theme.text.withOpacity(0.5), fontWeight: FontWeight.bold, letterSpacing: 2.0, fontSize: 13)))),
|
Padding(padding: const EdgeInsets.symmetric(horizontal: 10), child: Text("OPPURE", style: _getTextStyle(themeType, TextStyle(color: themeType == AppThemeType.doodle ? theme.text : theme.text.withOpacity(0.5), fontWeight: FontWeight.bold, letterSpacing: 2.0, fontSize: 13)))),
|
||||||
Expanded(child: Divider(color: theme.text.withOpacity(0.4), thickness: themeType == AppThemeType.doodle ? 2 : 1.0)),
|
Expanded(child: Divider(color: theme.text.withOpacity(0.4), thickness: themeType == AppThemeType.doodle ? 2 : 1.0)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 15),
|
|
||||||
|
|
||||||
// --- LA VERA E PROPRIA BACHECA PUBBLICA ---
|
|
||||||
StreamBuilder<QuerySnapshot>(
|
|
||||||
stream: _multiplayerService.getPublicRooms(),
|
|
||||||
builder: (context, snapshot) {
|
|
||||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
|
||||||
return Padding(padding: const EdgeInsets.all(20), child: Center(child: CircularProgressIndicator(color: theme.playerBlue)));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!snapshot.hasData || snapshot.data!.docs.isEmpty) {
|
|
||||||
return Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 20.0),
|
|
||||||
child: Center(child: Text("Nessuna stanza pubblica al momento.\nCreane una tu!", textAlign: TextAlign.center, style: _getTextStyle(themeType, TextStyle(color: theme.text.withOpacity(0.6), height: 1.5)))),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
DateTime now = DateTime.now();
|
|
||||||
String? myUid = FirebaseAuth.instance.currentUser?.uid;
|
|
||||||
|
|
||||||
var docs = snapshot.data!.docs.where((doc) {
|
|
||||||
var data = doc.data() as Map<String, dynamic>;
|
|
||||||
if (data['isPublic'] != true) return false;
|
|
||||||
if (data['hostUid'] != null && data['hostUid'] == myUid) return false;
|
|
||||||
|
|
||||||
Timestamp? createdAt = data['createdAt'] as Timestamp?;
|
|
||||||
if (createdAt != null) {
|
|
||||||
int ageInMinutes = now.difference(createdAt.toDate()).inMinutes;
|
|
||||||
if (ageInMinutes > 15) {
|
|
||||||
FirebaseFirestore.instance.collection('games').doc(doc.id).delete();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}).toList();
|
|
||||||
|
|
||||||
if (docs.isEmpty) {
|
|
||||||
return Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 20.0),
|
|
||||||
child: Center(child: Text("Nessuna stanza pubblica al momento.\nCreane una tu!", textAlign: TextAlign.center, style: _getTextStyle(themeType, TextStyle(color: theme.text.withOpacity(0.6), height: 1.5)))),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
docs.sort((a, b) {
|
|
||||||
Timestamp? tA = (a.data() as Map<String, dynamic>)['createdAt'] as Timestamp?;
|
|
||||||
Timestamp? tB = (b.data() as Map<String, dynamic>)['createdAt'] as Timestamp?;
|
|
||||||
if (tA == null || tB == null) return 0;
|
|
||||||
return tB.compareTo(tA);
|
|
||||||
});
|
|
||||||
|
|
||||||
return ListView.builder(
|
|
||||||
shrinkWrap: true,
|
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
itemCount: docs.length,
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
var doc = docs[index];
|
|
||||||
var data = doc.data() as Map<String, dynamic>;
|
|
||||||
String host = data['hostName'] ?? 'Sconosciuto';
|
|
||||||
int r = data['radius'] ?? 4;
|
|
||||||
String shapeStr = data['shape'] ?? 'classic';
|
|
||||||
bool time = data['timeMode'] ?? true;
|
|
||||||
|
|
||||||
String prettyShape = "Rombo";
|
|
||||||
if (shapeStr == 'cross') prettyShape = "Croce";
|
|
||||||
else if (shapeStr == 'donut') prettyShape = "Buco";
|
|
||||||
else if (shapeStr == 'hourglass') prettyShape = "Clessidra";
|
|
||||||
else if (shapeStr == 'chaos') prettyShape = "Caos";
|
|
||||||
|
|
||||||
return Transform.rotate(
|
|
||||||
angle: themeType == AppThemeType.doodle ? (index % 2 == 0 ? 0.01 : -0.01) : 0,
|
|
||||||
child: Container(
|
|
||||||
margin: const EdgeInsets.only(bottom: 12),
|
|
||||||
padding: const EdgeInsets.all(12),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: themeType == AppThemeType.doodle ? Colors.white : theme.text.withOpacity(0.05),
|
|
||||||
borderRadius: BorderRadius.circular(15),
|
|
||||||
border: Border.all(color: themeType == AppThemeType.doodle ? theme.text : theme.playerBlue.withOpacity(0.3), width: themeType == AppThemeType.doodle ? 2 : 1),
|
|
||||||
boxShadow: themeType == AppThemeType.doodle ? [BoxShadow(color: theme.text.withOpacity(0.6), offset: const Offset(3, 4))] : [],
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
CircleAvatar(backgroundColor: theme.playerRed.withOpacity(0.2), child: Icon(Icons.person, color: theme.playerRed)),
|
|
||||||
const SizedBox(width: 12),
|
|
||||||
Expanded(
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text("Stanza di $host", style: _getTextStyle(themeType, TextStyle(color: theme.text, fontWeight: FontWeight.bold, fontSize: 16))),
|
|
||||||
const SizedBox(height: 4),
|
|
||||||
Text("Raggio: $r • $prettyShape • ${time ? 'A Tempo' : 'Relax'}", style: _getTextStyle(themeType, TextStyle(color: theme.text.withOpacity(0.6), fontSize: 11))),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
ElevatedButton(
|
|
||||||
style: ElevatedButton.styleFrom(
|
|
||||||
backgroundColor: theme.playerBlue, foregroundColor: Colors.white,
|
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
|
|
||||||
elevation: themeType == AppThemeType.doodle ? 0 : 2,
|
|
||||||
side: themeType == AppThemeType.doodle ? BorderSide(color: theme.text, width: 2) : BorderSide.none,
|
|
||||||
),
|
|
||||||
onPressed: () => _joinRoomByCode(doc.id),
|
|
||||||
child: Text("ENTRA", style: _getTextStyle(themeType, const TextStyle(fontWeight: FontWeight.w900, letterSpacing: 1.0))),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
),
|
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
|
|
||||||
|
Transform.rotate(
|
||||||
|
angle: themeType == AppThemeType.doodle ? 0.02 : 0,
|
||||||
|
child: Container(
|
||||||
|
decoration: themeType == AppThemeType.doodle ? BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
borderRadius: const BorderRadius.only(topLeft: Radius.circular(20), bottomRight: Radius.circular(20), topRight: Radius.circular(5), bottomLeft: Radius.circular(5)),
|
||||||
|
border: Border.all(color: theme.text, width: 2.5),
|
||||||
|
boxShadow: [BoxShadow(color: theme.text.withOpacity(0.8), offset: const Offset(5, 5), blurRadius: 0)],
|
||||||
|
) : BoxDecoration(
|
||||||
|
boxShadow: [BoxShadow(color: theme.playerBlue.withOpacity(0.15), blurRadius: 15, spreadRadius: 1)]
|
||||||
|
),
|
||||||
|
child: TextField(
|
||||||
|
controller: _codeController, textCapitalization: TextCapitalization.characters, textAlign: TextAlign.center, maxLength: 5,
|
||||||
|
style: _getTextStyle(themeType, TextStyle(fontSize: 28, fontWeight: FontWeight.w900, color: theme.text, letterSpacing: 12, shadows: themeType == AppThemeType.doodle ? [] : [Shadow(color: theme.playerBlue.withOpacity(0.5), blurRadius: 8)])),
|
||||||
|
decoration: InputDecoration(
|
||||||
|
contentPadding: const EdgeInsets.symmetric(vertical: 12),
|
||||||
|
hintText: "CODICE", hintStyle: _getTextStyle(themeType, TextStyle(color: theme.text.withOpacity(0.3), letterSpacing: 10, fontSize: 20)), counterText: "",
|
||||||
|
filled: themeType != AppThemeType.doodle,
|
||||||
|
fillColor: themeType == AppThemeType.cyberpunk ? Colors.black.withOpacity(0.85) : theme.text.withOpacity(0.05),
|
||||||
|
enabledBorder: themeType == AppThemeType.doodle ? InputBorder.none : OutlineInputBorder(borderSide: BorderSide(color: theme.gridLine.withOpacity(0.5), width: 2.0), borderRadius: BorderRadius.circular(15)),
|
||||||
|
focusedBorder: themeType == AppThemeType.doodle ? InputBorder.none : OutlineInputBorder(borderSide: BorderSide(color: theme.playerBlue, width: 3.0), borderRadius: BorderRadius.circular(15)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 15),
|
||||||
|
_NeonActionButton(label: "UNISCITI", color: theme.playerBlue, onTap: _joinRoom, theme: theme, themeType: themeType),
|
||||||
|
|
||||||
|
const SizedBox(height: 10),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -35,10 +35,10 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||||
padding: const EdgeInsets.all(20),
|
padding: const EdgeInsets.all(20),
|
||||||
children: [
|
children: [
|
||||||
_ThemeCard(
|
_ThemeCard(
|
||||||
title: "Quaderno (Doodle)",
|
title: "Minimal",
|
||||||
subtitle: "Sfondo a quadretti, tratto a penna",
|
subtitle: "Linee pulite, sfondo chiaro",
|
||||||
type: AppThemeType.doodle,
|
type: AppThemeType.minimal,
|
||||||
previewColors: AppColors.doodle,
|
previewColors: AppColors.minimal,
|
||||||
requiredLevel: 1,
|
requiredLevel: 1,
|
||||||
currentLevel: playerLevel,
|
currentLevel: playerLevel,
|
||||||
),
|
),
|
||||||
|
|
@ -52,6 +52,15 @@ 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",
|
||||||
|
|
@ -78,15 +87,6 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||||
requiredLevel: 15,
|
requiredLevel: 15,
|
||||||
currentLevel: playerLevel,
|
currentLevel: playerLevel,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 15),
|
|
||||||
_ThemeCard(
|
|
||||||
title: "Musica",
|
|
||||||
subtitle: "Vinili, cassette e vibrazioni sonore",
|
|
||||||
type: AppThemeType.music,
|
|
||||||
previewColors: AppColors.music,
|
|
||||||
requiredLevel: 20, // Tema Esclusivo di Livello 20!
|
|
||||||
currentLevel: playerLevel,
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
@ -164,9 +164,9 @@ class _ThemeCard extends StatelessWidget {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Container(width: 20, height: 20, decoration: BoxDecoration(color: previewColors.playerRed, shape: BoxShape.circle, boxShadow: [BoxShadow(color: previewColors.playerRed.withOpacity(0.5), blurRadius: 4)])),
|
Container(width: 20, height: 20, decoration: BoxDecoration(color: previewColors.playerRed, shape: BoxShape.circle)),
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
Container(width: 20, height: 20, decoration: BoxDecoration(color: previewColors.playerBlue, shape: BoxShape.circle, boxShadow: [BoxShadow(color: previewColors.playerBlue.withOpacity(0.5), blurRadius: 4)])),
|
Container(width: 20, height: 20, decoration: BoxDecoration(color: previewColors.playerBlue, shape: BoxShape.circle)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -1,57 +0,0 @@
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:provider/provider.dart';
|
|
||||||
import '../core/theme_manager.dart'; // Import aggiornato
|
|
||||||
import 'dart:math' as math;
|
|
||||||
|
|
||||||
class AnimatedCyberBorder extends StatefulWidget {
|
|
||||||
final Widget child;
|
|
||||||
const AnimatedCyberBorder({super.key, required this.child});
|
|
||||||
@override
|
|
||||||
State<AnimatedCyberBorder> createState() => _AnimatedCyberBorderState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _AnimatedCyberBorderState extends State<AnimatedCyberBorder> with SingleTickerProviderStateMixin {
|
|
||||||
late AnimationController _controller;
|
|
||||||
@override
|
|
||||||
void initState() { super.initState(); _controller = AnimationController(vsync: this, duration: const Duration(seconds: 3))..repeat(); }
|
|
||||||
@override
|
|
||||||
void dispose() { _controller.dispose(); super.dispose(); }
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final theme = context.watch<ThemeManager>().currentColors;
|
|
||||||
return AnimatedBuilder(
|
|
||||||
animation: _controller,
|
|
||||||
builder: (context, child) {
|
|
||||||
return CustomPaint(
|
|
||||||
painter: CyberBorderPainter(animationValue: _controller.value, color1: theme.playerBlue, color2: theme.playerRed),
|
|
||||||
child: Container(
|
|
||||||
decoration: BoxDecoration(color: theme.background.withOpacity(0.9), borderRadius: BorderRadius.circular(15), boxShadow: [BoxShadow(color: theme.playerBlue.withOpacity(0.3), blurRadius: 25, spreadRadius: 2)]),
|
|
||||||
padding: const EdgeInsets.all(3),
|
|
||||||
child: widget.child,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: widget.child,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class CyberBorderPainter extends CustomPainter {
|
|
||||||
final double animationValue;
|
|
||||||
final Color color1;
|
|
||||||
final Color color2;
|
|
||||||
CyberBorderPainter({required this.animationValue, required this.color1, required this.color2});
|
|
||||||
|
|
||||||
@override
|
|
||||||
void paint(Canvas canvas, Size size) {
|
|
||||||
final rect = Offset.zero & size;
|
|
||||||
final RRect rrect = RRect.fromRectAndRadius(rect, const Radius.circular(15));
|
|
||||||
final Paint paint = Paint()
|
|
||||||
..shader = SweepGradient(colors: [color1, color2, color1, color2, color1], stops: const [0.0, 0.25, 0.5, 0.75, 1.0], transform: GradientRotation(animationValue * 2 * math.pi)).createShader(rect)
|
|
||||||
..style = PaintingStyle.stroke
|
|
||||||
..strokeWidth = 4.0
|
|
||||||
..maskFilter = const MaskFilter.blur(BlurStyle.solid, 4);
|
|
||||||
canvas.drawRRect(rrect, paint);
|
|
||||||
}
|
|
||||||
@override bool shouldRepaint(covariant CyberBorderPainter oldDelegate) => oldDelegate.animationValue != animationValue;
|
|
||||||
}
|
|
||||||
|
|
@ -1,13 +1,8 @@
|
||||||
// ===========================================================================
|
|
||||||
// FILE: lib/widgets/game_over_dialog.dart
|
|
||||||
// ===========================================================================
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import '../logic/game_controller.dart';
|
import '../logic/game_controller.dart';
|
||||||
import '../core/theme_manager.dart';
|
import '../core/theme_manager.dart';
|
||||||
import '../core/app_colors.dart';
|
import '../core/app_colors.dart';
|
||||||
import '../services/storage_service.dart';
|
|
||||||
|
|
||||||
class GameOverDialog extends StatelessWidget {
|
class GameOverDialog extends StatelessWidget {
|
||||||
const GameOverDialog({super.key});
|
const GameOverDialog({super.key});
|
||||||
|
|
@ -24,18 +19,15 @@ class GameOverDialog extends StatelessWidget {
|
||||||
|
|
||||||
bool playerBeatCPU = game.isVsCPU && red > blue;
|
bool playerBeatCPU = game.isVsCPU && red > blue;
|
||||||
|
|
||||||
String myName = StorageService.instance.playerName.toUpperCase();
|
|
||||||
if (myName.isEmpty) myName = "TU";
|
|
||||||
|
|
||||||
// --- LOGICA NOMI ---
|
// --- LOGICA NOMI ---
|
||||||
String nameRed = myName;
|
String nameRed = "ROSSO";
|
||||||
String nameBlue = themeType == AppThemeType.cyberpunk || themeType == AppThemeType.arcade ? "VERDE" : "BLU";
|
String nameBlue = themeType == AppThemeType.cyberpunk ? "VERDE" : "BLU";
|
||||||
|
|
||||||
if (game.isOnline) {
|
if (game.isOnline) {
|
||||||
nameRed = game.onlineHostName.toUpperCase();
|
nameRed = game.onlineHostName.toUpperCase();
|
||||||
nameBlue = game.onlineGuestName.toUpperCase();
|
nameBlue = game.onlineGuestName.toUpperCase();
|
||||||
} else if (game.isVsCPU) {
|
} else if (game.isVsCPU) {
|
||||||
nameRed = myName;
|
nameRed = "TU";
|
||||||
nameBlue = "CPU";
|
nameBlue = "CPU";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,90 +0,0 @@
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import '../core/app_colors.dart'; // Import aggiornato
|
|
||||||
import 'painters.dart';
|
|
||||||
|
|
||||||
class FeatureCard extends StatelessWidget {
|
|
||||||
final String title;
|
|
||||||
final String subtitle;
|
|
||||||
final IconData icon;
|
|
||||||
final Color color;
|
|
||||||
final ThemeColors theme;
|
|
||||||
final AppThemeType themeType;
|
|
||||||
final VoidCallback onTap;
|
|
||||||
final bool isFeatured;
|
|
||||||
final bool compact;
|
|
||||||
|
|
||||||
const FeatureCard({super.key, required this.title, required this.subtitle, required this.icon, required this.color, required this.theme, required this.themeType, required this.onTap, this.isFeatured = false, this.compact = false});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
if (themeType == AppThemeType.doodle) {
|
|
||||||
double tilt = (title.length % 2 == 0) ? -0.015 : 0.02;
|
|
||||||
Color inkColor = const Color(0xFF111122);
|
|
||||||
|
|
||||||
return Transform.rotate(
|
|
||||||
angle: tilt,
|
|
||||||
child: GestureDetector(
|
|
||||||
onTap: onTap,
|
|
||||||
child: CustomPaint(
|
|
||||||
painter: DoodleBackgroundPainter(fillColor: color, strokeColor: inkColor, seed: title.length * 5),
|
|
||||||
child: Padding(
|
|
||||||
padding: EdgeInsets.symmetric(horizontal: compact ? 12.0 : 22.0, vertical: compact ? 12.0 : 16.0),
|
|
||||||
child: Row(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Icon(icon, color: inkColor, size: compact ? 24 : 32),
|
|
||||||
const SizedBox(width: 12),
|
|
||||||
Expanded(
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
FittedBox(fit: BoxFit.scaleDown, alignment: Alignment.centerLeft, child: Text(title, style: getSharedTextStyle(themeType, TextStyle(color: inkColor, fontSize: compact ? 16 : 24, fontWeight: FontWeight.w900)))),
|
|
||||||
if (!compact) ...[ const SizedBox(height: 2), Text(subtitle, style: getSharedTextStyle(themeType, TextStyle(color: inkColor.withOpacity(0.8), fontSize: 14, fontWeight: FontWeight.bold))) ]
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (!compact) Icon(Icons.chevron_right_rounded, color: inkColor.withOpacity(0.6), size: 32),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return GestureDetector(
|
|
||||||
onTap: onTap,
|
|
||||||
child: Container(
|
|
||||||
padding: EdgeInsets.symmetric(horizontal: compact ? 12.0 : 20.0, vertical: compact ? 10.0 : 14.0),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
gradient: LinearGradient(begin: Alignment.topLeft, end: Alignment.bottomRight, colors: isFeatured ? [color.withOpacity(0.9), color.withOpacity(0.6)] : [color.withOpacity(0.25), color.withOpacity(0.05)]),
|
|
||||||
borderRadius: BorderRadius.circular(15), border: Border.all(color: color.withOpacity(isFeatured ? 0.5 : 0.2), width: 1.5),
|
|
||||||
boxShadow: [BoxShadow(color: Colors.black.withOpacity(0.6), offset: const Offset(0, 8), blurRadius: 15), BoxShadow(color: color.withOpacity(isFeatured ? 0.3 : 0.05), offset: const Offset(-1, -1), blurRadius: 5)]
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Container(
|
|
||||||
padding: EdgeInsets.all(compact ? 6 : 10),
|
|
||||||
decoration: BoxDecoration(gradient: LinearGradient(begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [Colors.white.withOpacity(0.3), Colors.white.withOpacity(0.05)]), shape: BoxShape.circle, border: Border.all(color: Colors.white.withOpacity(0.2)), boxShadow: [BoxShadow(color: Colors.black.withOpacity(0.2), blurRadius: 5, offset: const Offset(2, 4))]),
|
|
||||||
child: Icon(icon, color: isFeatured ? Colors.white : color, size: compact ? 20 : 26),
|
|
||||||
),
|
|
||||||
SizedBox(width: compact ? 10 : 20),
|
|
||||||
Expanded(
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
FittedBox(fit: BoxFit.scaleDown, alignment: Alignment.centerLeft, child: Text(title, style: getSharedTextStyle(themeType, TextStyle(color: isFeatured ? Colors.white : theme.text, fontSize: compact ? 14 : 18, fontWeight: FontWeight.w900, shadows: [Shadow(color: Colors.black.withOpacity(0.5), offset: const Offset(1, 2), blurRadius: 2)])))),
|
|
||||||
if (!compact) ...[ const SizedBox(height: 2), Text(subtitle, style: getSharedTextStyle(themeType, TextStyle(color: isFeatured ? Colors.white.withOpacity(0.8) : theme.text.withOpacity(0.6), fontSize: 12, fontWeight: FontWeight.bold))) ]
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (!compact) Icon(Icons.chevron_right_rounded, color: isFeatured ? Colors.white.withOpacity(0.7) : color.withOpacity(0.5), size: 30),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,129 +0,0 @@
|
||||||
// ===========================================================================
|
|
||||||
// FILE: lib/widgets/music_theme_widgets.dart
|
|
||||||
// ===========================================================================
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import '../core/app_colors.dart';
|
|
||||||
import 'painters.dart';
|
|
||||||
|
|
||||||
class MusicCassetteCard extends StatelessWidget {
|
|
||||||
final String title;
|
|
||||||
final String subtitle;
|
|
||||||
final Color neonColor;
|
|
||||||
final double angle;
|
|
||||||
final IconData leftIcon;
|
|
||||||
final IconData rightIcon;
|
|
||||||
final VoidCallback onTap;
|
|
||||||
final AppThemeType themeType;
|
|
||||||
|
|
||||||
const MusicCassetteCard({super.key, required this.title, required this.subtitle, required this.neonColor, required this.angle, required this.leftIcon, required this.rightIcon, required this.onTap, required this.themeType});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Transform.rotate(
|
|
||||||
angle: angle,
|
|
||||||
child: GestureDetector(
|
|
||||||
onTap: onTap,
|
|
||||||
child: Container(
|
|
||||||
// Aumentato leggermente l'altezza a 125 per evitare l'overflow
|
|
||||||
height: 125, margin: const EdgeInsets.symmetric(vertical: 8, horizontal: 10), padding: const EdgeInsets.all(12),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: const Color(0xFF22222A), borderRadius: BorderRadius.circular(8), border: Border.all(color: Colors.black87, width: 2),
|
|
||||||
boxShadow: [ BoxShadow(color: neonColor.withOpacity(0.5), blurRadius: 25, spreadRadius: 2), const BoxShadow(color: Colors.black54, offset: Offset(5, 10), blurRadius: 15) ]
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: Container(
|
|
||||||
decoration: BoxDecoration(color: neonColor.withOpacity(0.15), borderRadius: BorderRadius.circular(4), border: Border.all(color: neonColor.withOpacity(0.5), width: 1.5)),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Padding(padding: const EdgeInsets.symmetric(horizontal: 12), child: Icon(leftIcon, color: neonColor, size: 28)),
|
|
||||||
Expanded(
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
// Aggiunto minAxisSize per far stare il contenuto stretto
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Flexible(child: FittedBox(fit: BoxFit.scaleDown, child: Text(title, style: getSharedTextStyle(themeType, TextStyle(color: Colors.white, fontSize: 20, fontWeight: FontWeight.w900, shadows: [Shadow(color: neonColor, blurRadius: 10)]))))),
|
|
||||||
Flexible(child: FittedBox(fit: BoxFit.scaleDown, child: Text(subtitle, style: getSharedTextStyle(themeType, const TextStyle(color: Colors.white70, fontSize: 11, fontWeight: FontWeight.bold))))),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Padding(padding: const EdgeInsets.symmetric(horizontal: 12), child: Icon(rightIcon, color: neonColor, size: 28)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
Container(
|
|
||||||
height: 35, width: 180, decoration: BoxDecoration(color: const Color(0xFF0D0D12), borderRadius: BorderRadius.circular(20), border: Border.all(color: Colors.white24, width: 1)),
|
|
||||||
child: Stack(
|
|
||||||
alignment: Alignment.center,
|
|
||||||
children: [
|
|
||||||
Container(height: 2, width: 120, color: const Color(0xFF333333)),
|
|
||||||
Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ _buildSpool(), _buildSpool() ]),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildSpool() {
|
|
||||||
return Container(
|
|
||||||
width: 26, height: 26, decoration: BoxDecoration(shape: BoxShape.circle, color: Colors.white70, border: Border.all(color: Colors.black87, width: 5)),
|
|
||||||
child: Center(child: Container(width: 6, height: 6, decoration: const BoxDecoration(shape: BoxShape.circle, color: Colors.black))),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class MusicKnobCard extends StatelessWidget {
|
|
||||||
final String title;
|
|
||||||
final IconData icon;
|
|
||||||
final VoidCallback onTap;
|
|
||||||
final AppThemeType themeType;
|
|
||||||
final Color? iconColor;
|
|
||||||
|
|
||||||
const MusicKnobCard({super.key, required this.title, required this.icon, required this.onTap, required this.themeType, this.iconColor});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return GestureDetector(
|
|
||||||
onTap: onTap,
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Container(
|
|
||||||
width: 65, height: 65,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
shape: BoxShape.circle, color: const Color(0xFF222222), border: Border.all(color: const Color(0xFF111111), width: 2),
|
|
||||||
boxShadow: const [BoxShadow(color: Colors.black87, blurRadius: 10, offset: Offset(2, 6)), BoxShadow(color: Colors.white12, blurRadius: 2, offset: Offset(-1, -1))],
|
|
||||||
),
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(6.0),
|
|
||||||
child: Container(
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
shape: BoxShape.circle, border: Border.all(color: Colors.black54, width: 1),
|
|
||||||
gradient: const SweepGradient(colors: [Color(0xFF555555), Color(0xFFAAAAAA), Color(0xFF555555), Color(0xFF222222), Color(0xFF555555)]),
|
|
||||||
),
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(4.0),
|
|
||||||
child: Container(
|
|
||||||
decoration: const BoxDecoration(shape: BoxShape.circle, color: Color(0xFF1A1A1A)),
|
|
||||||
child: Center(child: Icon(icon, color: iconColor ?? Colors.white70, size: 20)),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
FittedBox(fit: BoxFit.scaleDown, child: Text(title, style: getSharedTextStyle(themeType, const TextStyle(color: Colors.white70, fontSize: 11, fontWeight: FontWeight.bold, letterSpacing: 1.0)))),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,73 +0,0 @@
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:google_fonts/google_fonts.dart';
|
|
||||||
import 'dart:math' as math;
|
|
||||||
import '../core/app_colors.dart'; // Import aggiornato
|
|
||||||
|
|
||||||
TextStyle getSharedTextStyle(AppThemeType themeType, TextStyle baseStyle) {
|
|
||||||
if (themeType == AppThemeType.doodle) {
|
|
||||||
return GoogleFonts.permanentMarker(textStyle: baseStyle);
|
|
||||||
} else if (themeType == AppThemeType.arcade) {
|
|
||||||
return GoogleFonts.pressStart2p(textStyle: baseStyle.copyWith(fontSize: baseStyle.fontSize != null ? baseStyle.fontSize! * 0.75 : null, letterSpacing: 0.5));
|
|
||||||
} else if (themeType == AppThemeType.grimorio) {
|
|
||||||
return GoogleFonts.cinzelDecorative(textStyle: baseStyle.copyWith(fontWeight: FontWeight.bold));
|
|
||||||
} else if (themeType == AppThemeType.music) {
|
|
||||||
return GoogleFonts.audiowide(textStyle: baseStyle.copyWith(letterSpacing: 1.5));
|
|
||||||
}
|
|
||||||
return baseStyle;
|
|
||||||
}
|
|
||||||
|
|
||||||
class DoodleBackgroundPainter extends CustomPainter {
|
|
||||||
final Color fillColor; final Color strokeColor; final int seed; final bool isCircle;
|
|
||||||
DoodleBackgroundPainter({required this.fillColor, required this.strokeColor, required this.seed, this.isCircle = false});
|
|
||||||
|
|
||||||
@override
|
|
||||||
void paint(Canvas canvas, Size size) {
|
|
||||||
final math.Random random = math.Random(seed);
|
|
||||||
double wobble() => random.nextDouble() * 6 - 3;
|
|
||||||
final Paint fillPaint = Paint()..color = fillColor..style = PaintingStyle.fill;
|
|
||||||
final Paint strokePaint = Paint()..color = strokeColor..strokeWidth = 2.5..style = PaintingStyle.stroke..strokeCap = StrokeCap.round..strokeJoin = StrokeJoin.round;
|
|
||||||
|
|
||||||
if (isCircle) {
|
|
||||||
final Rect rect = Rect.fromLTWH(wobble(), wobble(), size.width + wobble(), size.height + wobble());
|
|
||||||
canvas.save(); canvas.translate(wobble(), wobble()); canvas.drawOval(rect, fillPaint); canvas.restore();
|
|
||||||
canvas.drawOval(rect, strokePaint);
|
|
||||||
canvas.save(); canvas.translate(random.nextDouble() * 4 - 2, random.nextDouble() * 4 - 2); canvas.drawOval(rect, strokePaint..strokeWidth = 1.0..color = strokeColor.withOpacity(0.6)); canvas.restore();
|
|
||||||
} else {
|
|
||||||
final Path path = Path()..moveTo(wobble(), wobble())..lineTo(size.width + wobble(), wobble())..lineTo(size.width + wobble(), size.height + wobble())..lineTo(wobble(), size.height + wobble())..close();
|
|
||||||
final Path fillPath = Path()..moveTo(wobble() * 1.5, wobble() * 1.5)..lineTo(size.width + wobble() * 1.5, wobble() * 1.5)..lineTo(size.width + wobble() * 1.5, size.height + wobble() * 1.5)..lineTo(wobble() * 1.5, size.height + wobble() * 1.5)..close();
|
|
||||||
canvas.drawPath(fillPath, fillPaint);
|
|
||||||
canvas.drawPath(path, strokePaint);
|
|
||||||
canvas.save(); canvas.translate(random.nextDouble() * 3 - 1.5, random.nextDouble() * 3 - 1.5); canvas.drawPath(path, strokePaint..strokeWidth = 1.0..color = strokeColor.withOpacity(0.6)); canvas.restore();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@override bool shouldRepaint(covariant DoodleBackgroundPainter oldDelegate) => oldDelegate.fillColor != fillColor || oldDelegate.strokeColor != strokeColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
class AudioCablesPainter extends CustomPainter {
|
|
||||||
@override
|
|
||||||
void paint(Canvas canvas, Size size) {
|
|
||||||
final paint = Paint()..color = const Color(0xFF151515)..style = PaintingStyle.stroke..strokeWidth = 8.0..strokeCap = StrokeCap.round;
|
|
||||||
final highlight = Paint()..color = const Color(0xFF3A3A3A)..style = PaintingStyle.stroke..strokeWidth = 2.0..strokeCap = StrokeCap.round;
|
|
||||||
void drawCable(Path path) { canvas.drawPath(path, paint); canvas.drawPath(path, highlight); }
|
|
||||||
|
|
||||||
Path c1 = Path()..moveTo(-20, size.height * 0.2)..quadraticBezierTo(100, size.height * 0.25, 50, size.height * 0.4)..quadraticBezierTo(0, size.height * 0.5, -20, size.height * 0.55); drawCable(c1);
|
|
||||||
Path c2 = Path()..moveTo(size.width + 20, size.height * 0.4)..quadraticBezierTo(size.width - 100, size.height * 0.5, size.width - 50, size.height * 0.7)..quadraticBezierTo(size.width, size.height * 0.8, size.width + 20, size.height * 0.85); drawCable(c2);
|
|
||||||
Path c3 = Path()..moveTo(size.width * 0.2, size.height + 20)..quadraticBezierTo(size.width * 0.3, size.height - 80, size.width * 0.5, size.height - 60)..quadraticBezierTo(size.width * 0.7, size.height - 40, size.width * 0.8, size.height + 20); drawCable(c3);
|
|
||||||
|
|
||||||
_drawJack(canvas, Offset(80, size.height * 0.38), -0.5);
|
|
||||||
_drawJack(canvas, Offset(size.width - 60, size.height * 0.68), 0.8);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _drawJack(Canvas canvas, Offset pos, double angle) {
|
|
||||||
canvas.save(); canvas.translate(pos.dx, pos.dy); canvas.rotate(angle);
|
|
||||||
canvas.drawRect(const Rect.fromLTWH(-15, -4, 15, 8), Paint()..color = const Color(0xFF151515));
|
|
||||||
canvas.drawRRect(RRect.fromRectAndRadius(const Rect.fromLTWH(0, -6, 25, 12), const Radius.circular(2)), Paint()..color = const Color(0xFF222222));
|
|
||||||
canvas.drawRRect(RRect.fromRectAndRadius(const Rect.fromLTWH(2, -4, 21, 8), const Radius.circular(2)), Paint()..color = const Color(0xFF444444));
|
|
||||||
canvas.drawRect(const Rect.fromLTWH(25, -2, 15, 4), Paint()..color = const Color(0xFFCCCCCC));
|
|
||||||
canvas.drawRect(const Rect.fromLTWH(40, -1.5, 5, 3), Paint()..color = const Color(0xFFAAAAAA));
|
|
||||||
canvas.drawLine(const Offset(30, -2), const Offset(30, 2), Paint()..color = Colors.black..strokeWidth = 1.5);
|
|
||||||
canvas.drawLine(const Offset(35, -2), const Offset(35, 2), Paint()..color = Colors.black..strokeWidth = 1.5);
|
|
||||||
canvas.restore();
|
|
||||||
}
|
|
||||||
@override bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
|
|
||||||
}
|
|
||||||
BIN
macos/.DS_Store
vendored
BIN
macos/.DS_Store
vendored
Binary file not shown.
|
|
@ -8,8 +8,6 @@ import Foundation
|
||||||
import app_links
|
import app_links
|
||||||
import audioplayers_darwin
|
import audioplayers_darwin
|
||||||
import cloud_firestore
|
import cloud_firestore
|
||||||
import firebase_app_check
|
|
||||||
import firebase_auth
|
|
||||||
import firebase_core
|
import firebase_core
|
||||||
import share_plus
|
import share_plus
|
||||||
import shared_preferences_foundation
|
import shared_preferences_foundation
|
||||||
|
|
@ -18,8 +16,6 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||||
AppLinksMacosPlugin.register(with: registry.registrar(forPlugin: "AppLinksMacosPlugin"))
|
AppLinksMacosPlugin.register(with: registry.registrar(forPlugin: "AppLinksMacosPlugin"))
|
||||||
AudioplayersDarwinPlugin.register(with: registry.registrar(forPlugin: "AudioplayersDarwinPlugin"))
|
AudioplayersDarwinPlugin.register(with: registry.registrar(forPlugin: "AudioplayersDarwinPlugin"))
|
||||||
FLTFirebaseFirestorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseFirestorePlugin"))
|
FLTFirebaseFirestorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseFirestorePlugin"))
|
||||||
FLTFirebaseAppCheckPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseAppCheckPlugin"))
|
|
||||||
FLTFirebaseAuthPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseAuthPlugin"))
|
|
||||||
FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin"))
|
FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin"))
|
||||||
SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin"))
|
SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin"))
|
||||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||||
|
|
|
||||||
|
|
@ -1190,10 +1190,6 @@ PODS:
|
||||||
- abseil/xcprivacy (1.20240722.0)
|
- abseil/xcprivacy (1.20240722.0)
|
||||||
- app_links (6.4.1):
|
- app_links (6.4.1):
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- AppCheckCore (11.2.0):
|
|
||||||
- GoogleUtilities/Environment (~> 8.0)
|
|
||||||
- GoogleUtilities/UserDefaults (~> 8.0)
|
|
||||||
- PromisesObjC (~> 2.4)
|
|
||||||
- audioplayers_darwin (0.0.1):
|
- audioplayers_darwin (0.0.1):
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- BoringSSL-GRPC (0.0.37):
|
- BoringSSL-GRPC (0.0.37):
|
||||||
|
|
@ -1203,65 +1199,33 @@ PODS:
|
||||||
- BoringSSL-GRPC/Interface (= 0.0.37)
|
- BoringSSL-GRPC/Interface (= 0.0.37)
|
||||||
- BoringSSL-GRPC/Interface (0.0.37)
|
- BoringSSL-GRPC/Interface (0.0.37)
|
||||||
- cloud_firestore (6.1.2):
|
- cloud_firestore (6.1.2):
|
||||||
- Firebase/CoreOnly (~> 12.9.0)
|
- Firebase/CoreOnly (~> 12.8.0)
|
||||||
- Firebase/Firestore (~> 12.9.0)
|
- Firebase/Firestore (~> 12.8.0)
|
||||||
- firebase_core
|
- firebase_core
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- Firebase/AppCheck (12.9.0):
|
- Firebase/CoreOnly (12.8.0):
|
||||||
|
- FirebaseCore (~> 12.8.0)
|
||||||
|
- Firebase/Firestore (12.8.0):
|
||||||
- Firebase/CoreOnly
|
- Firebase/CoreOnly
|
||||||
- FirebaseAppCheck (~> 12.9.0)
|
- FirebaseFirestore (~> 12.8.0)
|
||||||
- Firebase/Auth (12.9.0):
|
- firebase_core (4.4.0):
|
||||||
- Firebase/CoreOnly
|
- Firebase/CoreOnly (~> 12.8.0)
|
||||||
- FirebaseAuth (~> 12.9.0)
|
|
||||||
- Firebase/CoreOnly (12.9.0):
|
|
||||||
- FirebaseCore (~> 12.9.0)
|
|
||||||
- Firebase/Firestore (12.9.0):
|
|
||||||
- Firebase/CoreOnly
|
|
||||||
- FirebaseFirestore (~> 12.9.0)
|
|
||||||
- firebase_app_check (0.4.1-5):
|
|
||||||
- Firebase/AppCheck (~> 12.9.0)
|
|
||||||
- Firebase/CoreOnly (~> 12.9.0)
|
|
||||||
- firebase_core
|
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- firebase_auth (6.1.4):
|
- FirebaseAppCheckInterop (12.8.0)
|
||||||
- Firebase/Auth (~> 12.9.0)
|
- FirebaseCore (12.8.0):
|
||||||
- Firebase/CoreOnly (~> 12.9.0)
|
- FirebaseCoreInternal (~> 12.8.0)
|
||||||
- firebase_core
|
|
||||||
- FlutterMacOS
|
|
||||||
- firebase_core (4.5.0):
|
|
||||||
- Firebase/CoreOnly (~> 12.9.0)
|
|
||||||
- FlutterMacOS
|
|
||||||
- FirebaseAppCheck (12.9.0):
|
|
||||||
- AppCheckCore (~> 11.0)
|
|
||||||
- FirebaseAppCheckInterop (~> 12.9.0)
|
|
||||||
- FirebaseCore (~> 12.9.0)
|
|
||||||
- GoogleUtilities/Environment (~> 8.1)
|
|
||||||
- GoogleUtilities/UserDefaults (~> 8.1)
|
|
||||||
- FirebaseAppCheckInterop (12.9.0)
|
|
||||||
- FirebaseAuth (12.9.0):
|
|
||||||
- FirebaseAppCheckInterop (~> 12.9.0)
|
|
||||||
- FirebaseAuthInterop (~> 12.9.0)
|
|
||||||
- FirebaseCore (~> 12.9.0)
|
|
||||||
- FirebaseCoreExtension (~> 12.9.0)
|
|
||||||
- GoogleUtilities/AppDelegateSwizzler (~> 8.1)
|
|
||||||
- GoogleUtilities/Environment (~> 8.1)
|
|
||||||
- GTMSessionFetcher/Core (< 6.0, >= 3.4)
|
|
||||||
- RecaptchaInterop (~> 101.0)
|
|
||||||
- FirebaseAuthInterop (12.9.0)
|
|
||||||
- FirebaseCore (12.9.0):
|
|
||||||
- FirebaseCoreInternal (~> 12.9.0)
|
|
||||||
- GoogleUtilities/Environment (~> 8.1)
|
- GoogleUtilities/Environment (~> 8.1)
|
||||||
- GoogleUtilities/Logger (~> 8.1)
|
- GoogleUtilities/Logger (~> 8.1)
|
||||||
- FirebaseCoreExtension (12.9.0):
|
- FirebaseCoreExtension (12.8.0):
|
||||||
- FirebaseCore (~> 12.9.0)
|
- FirebaseCore (~> 12.8.0)
|
||||||
- FirebaseCoreInternal (12.9.0):
|
- FirebaseCoreInternal (12.8.0):
|
||||||
- "GoogleUtilities/NSData+zlib (~> 8.1)"
|
- "GoogleUtilities/NSData+zlib (~> 8.1)"
|
||||||
- FirebaseFirestore (12.9.0):
|
- FirebaseFirestore (12.8.0):
|
||||||
- FirebaseCore (~> 12.9.0)
|
- FirebaseCore (~> 12.8.0)
|
||||||
- FirebaseCoreExtension (~> 12.9.0)
|
- FirebaseCoreExtension (~> 12.8.0)
|
||||||
- FirebaseFirestoreInternal (~> 12.9.0)
|
- FirebaseFirestoreInternal (~> 12.8.0)
|
||||||
- FirebaseSharedSwift (~> 12.9.0)
|
- FirebaseSharedSwift (~> 12.8.0)
|
||||||
- FirebaseFirestoreInternal (12.9.0):
|
- FirebaseFirestoreInternal (12.8.0):
|
||||||
- abseil/algorithm (~> 1.20240722.0)
|
- abseil/algorithm (~> 1.20240722.0)
|
||||||
- abseil/base (~> 1.20240722.0)
|
- abseil/base (~> 1.20240722.0)
|
||||||
- abseil/container/flat_hash_map (~> 1.20240722.0)
|
- abseil/container/flat_hash_map (~> 1.20240722.0)
|
||||||
|
|
@ -1270,38 +1234,22 @@ PODS:
|
||||||
- abseil/strings/strings (~> 1.20240722.0)
|
- abseil/strings/strings (~> 1.20240722.0)
|
||||||
- abseil/time (~> 1.20240722.0)
|
- abseil/time (~> 1.20240722.0)
|
||||||
- abseil/types (~> 1.20240722.0)
|
- abseil/types (~> 1.20240722.0)
|
||||||
- FirebaseAppCheckInterop (~> 12.9.0)
|
- FirebaseAppCheckInterop (~> 12.8.0)
|
||||||
- FirebaseCore (~> 12.9.0)
|
- FirebaseCore (~> 12.8.0)
|
||||||
- "gRPC-C++ (~> 1.69.0)"
|
- "gRPC-C++ (~> 1.69.0)"
|
||||||
- gRPC-Core (~> 1.69.0)
|
- gRPC-Core (~> 1.69.0)
|
||||||
- leveldb-library (~> 1.22)
|
- leveldb-library (~> 1.22)
|
||||||
- nanopb (~> 3.30910.0)
|
- nanopb (~> 3.30910.0)
|
||||||
- FirebaseSharedSwift (12.9.0)
|
- FirebaseSharedSwift (12.8.0)
|
||||||
- FlutterMacOS (1.0.0)
|
- FlutterMacOS (1.0.0)
|
||||||
- GoogleUtilities/AppDelegateSwizzler (8.1.0):
|
|
||||||
- GoogleUtilities/Environment
|
|
||||||
- GoogleUtilities/Logger
|
|
||||||
- GoogleUtilities/Network
|
|
||||||
- GoogleUtilities/Privacy
|
|
||||||
- GoogleUtilities/Environment (8.1.0):
|
- GoogleUtilities/Environment (8.1.0):
|
||||||
- GoogleUtilities/Privacy
|
- GoogleUtilities/Privacy
|
||||||
- GoogleUtilities/Logger (8.1.0):
|
- GoogleUtilities/Logger (8.1.0):
|
||||||
- GoogleUtilities/Environment
|
- GoogleUtilities/Environment
|
||||||
- GoogleUtilities/Privacy
|
- GoogleUtilities/Privacy
|
||||||
- GoogleUtilities/Network (8.1.0):
|
|
||||||
- GoogleUtilities/Logger
|
|
||||||
- "GoogleUtilities/NSData+zlib"
|
|
||||||
- GoogleUtilities/Privacy
|
|
||||||
- GoogleUtilities/Reachability
|
|
||||||
- "GoogleUtilities/NSData+zlib (8.1.0)":
|
- "GoogleUtilities/NSData+zlib (8.1.0)":
|
||||||
- GoogleUtilities/Privacy
|
- GoogleUtilities/Privacy
|
||||||
- GoogleUtilities/Privacy (8.1.0)
|
- GoogleUtilities/Privacy (8.1.0)
|
||||||
- GoogleUtilities/Reachability (8.1.0):
|
|
||||||
- GoogleUtilities/Logger
|
|
||||||
- GoogleUtilities/Privacy
|
|
||||||
- GoogleUtilities/UserDefaults (8.1.0):
|
|
||||||
- GoogleUtilities/Logger
|
|
||||||
- GoogleUtilities/Privacy
|
|
||||||
- "gRPC-C++ (1.69.0)":
|
- "gRPC-C++ (1.69.0)":
|
||||||
- "gRPC-C++/Implementation (= 1.69.0)"
|
- "gRPC-C++/Implementation (= 1.69.0)"
|
||||||
- "gRPC-C++/Interface (= 1.69.0)"
|
- "gRPC-C++/Interface (= 1.69.0)"
|
||||||
|
|
@ -1394,14 +1342,12 @@ PODS:
|
||||||
- gRPC-Core/Privacy (= 1.69.0)
|
- gRPC-Core/Privacy (= 1.69.0)
|
||||||
- gRPC-Core/Interface (1.69.0)
|
- gRPC-Core/Interface (1.69.0)
|
||||||
- gRPC-Core/Privacy (1.69.0)
|
- gRPC-Core/Privacy (1.69.0)
|
||||||
- GTMSessionFetcher/Core (5.1.0)
|
|
||||||
- leveldb-library (1.22.6)
|
- leveldb-library (1.22.6)
|
||||||
- nanopb (3.30910.0):
|
- nanopb (3.30910.0):
|
||||||
- nanopb/decode (= 3.30910.0)
|
- nanopb/decode (= 3.30910.0)
|
||||||
- nanopb/encode (= 3.30910.0)
|
- nanopb/encode (= 3.30910.0)
|
||||||
- nanopb/decode (3.30910.0)
|
- nanopb/decode (3.30910.0)
|
||||||
- nanopb/encode (3.30910.0)
|
- nanopb/encode (3.30910.0)
|
||||||
- PromisesObjC (2.4.0)
|
|
||||||
- share_plus (0.0.1):
|
- share_plus (0.0.1):
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- shared_preferences_foundation (0.0.1):
|
- shared_preferences_foundation (0.0.1):
|
||||||
|
|
@ -1412,8 +1358,6 @@ DEPENDENCIES:
|
||||||
- app_links (from `Flutter/ephemeral/.symlinks/plugins/app_links/macos`)
|
- app_links (from `Flutter/ephemeral/.symlinks/plugins/app_links/macos`)
|
||||||
- audioplayers_darwin (from `Flutter/ephemeral/.symlinks/plugins/audioplayers_darwin/macos`)
|
- audioplayers_darwin (from `Flutter/ephemeral/.symlinks/plugins/audioplayers_darwin/macos`)
|
||||||
- cloud_firestore (from `Flutter/ephemeral/.symlinks/plugins/cloud_firestore/macos`)
|
- cloud_firestore (from `Flutter/ephemeral/.symlinks/plugins/cloud_firestore/macos`)
|
||||||
- firebase_app_check (from `Flutter/ephemeral/.symlinks/plugins/firebase_app_check/macos`)
|
|
||||||
- firebase_auth (from `Flutter/ephemeral/.symlinks/plugins/firebase_auth/macos`)
|
|
||||||
- firebase_core (from `Flutter/ephemeral/.symlinks/plugins/firebase_core/macos`)
|
- firebase_core (from `Flutter/ephemeral/.symlinks/plugins/firebase_core/macos`)
|
||||||
- FlutterMacOS (from `Flutter/ephemeral`)
|
- FlutterMacOS (from `Flutter/ephemeral`)
|
||||||
- share_plus (from `Flutter/ephemeral/.symlinks/plugins/share_plus/macos`)
|
- share_plus (from `Flutter/ephemeral/.symlinks/plugins/share_plus/macos`)
|
||||||
|
|
@ -1422,13 +1366,9 @@ DEPENDENCIES:
|
||||||
SPEC REPOS:
|
SPEC REPOS:
|
||||||
trunk:
|
trunk:
|
||||||
- abseil
|
- abseil
|
||||||
- AppCheckCore
|
|
||||||
- BoringSSL-GRPC
|
- BoringSSL-GRPC
|
||||||
- Firebase
|
- Firebase
|
||||||
- FirebaseAppCheck
|
|
||||||
- FirebaseAppCheckInterop
|
- FirebaseAppCheckInterop
|
||||||
- FirebaseAuth
|
|
||||||
- FirebaseAuthInterop
|
|
||||||
- FirebaseCore
|
- FirebaseCore
|
||||||
- FirebaseCoreExtension
|
- FirebaseCoreExtension
|
||||||
- FirebaseCoreInternal
|
- FirebaseCoreInternal
|
||||||
|
|
@ -1438,10 +1378,8 @@ SPEC REPOS:
|
||||||
- GoogleUtilities
|
- GoogleUtilities
|
||||||
- "gRPC-C++"
|
- "gRPC-C++"
|
||||||
- gRPC-Core
|
- gRPC-Core
|
||||||
- GTMSessionFetcher
|
|
||||||
- leveldb-library
|
- leveldb-library
|
||||||
- nanopb
|
- nanopb
|
||||||
- PromisesObjC
|
|
||||||
|
|
||||||
EXTERNAL SOURCES:
|
EXTERNAL SOURCES:
|
||||||
app_links:
|
app_links:
|
||||||
|
|
@ -1450,10 +1388,6 @@ EXTERNAL SOURCES:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/audioplayers_darwin/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/audioplayers_darwin/macos
|
||||||
cloud_firestore:
|
cloud_firestore:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/cloud_firestore/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/cloud_firestore/macos
|
||||||
firebase_app_check:
|
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/firebase_app_check/macos
|
|
||||||
firebase_auth:
|
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/firebase_auth/macos
|
|
||||||
firebase_core:
|
firebase_core:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/firebase_core/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/firebase_core/macos
|
||||||
FlutterMacOS:
|
FlutterMacOS:
|
||||||
|
|
@ -1466,32 +1400,24 @@ EXTERNAL SOURCES:
|
||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
abseil: a05cc83bf02079535e17169a73c5be5ba47f714b
|
abseil: a05cc83bf02079535e17169a73c5be5ba47f714b
|
||||||
app_links: 05a6ec2341985eb05e9f97dc63f5837c39895c3f
|
app_links: 05a6ec2341985eb05e9f97dc63f5837c39895c3f
|
||||||
AppCheckCore: cc8fd0a3a230ddd401f326489c99990b013f0c4f
|
|
||||||
audioplayers_darwin: 761f2948df701d05b5db603220c384fb55720012
|
audioplayers_darwin: 761f2948df701d05b5db603220c384fb55720012
|
||||||
BoringSSL-GRPC: dded2a44897e45f28f08ae87a55ee4bcd19bc508
|
BoringSSL-GRPC: dded2a44897e45f28f08ae87a55ee4bcd19bc508
|
||||||
cloud_firestore: a2a9382e6cc4dd07345748b904b3b194ea46be44
|
cloud_firestore: 71947b640bd24f6f849d9d185e5d0a619fa6b93b
|
||||||
Firebase: 065f2bb395062046623036d8e6dc857bc2521d56
|
Firebase: 9a58fdbc9d8655ed7b79a19cf9690bb007d3d46d
|
||||||
firebase_app_check: 1ea404b52b0910bf632b1ea2e00ceb8d1730cb44
|
firebase_core: b1697fb64ff2b9ca16baaa821205f8b0c058e5d2
|
||||||
firebase_auth: 8db6796451d9aa44d4cc49b3e757865c65ce170f
|
FirebaseAppCheckInterop: ba3dc604a89815379e61ec2365101608d365cf7d
|
||||||
firebase_core: c74b220e9288decea6bed17399c249734a7e76d2
|
FirebaseCore: 0dbad74bda10b8fb9ca34ad8f375fb9dd3ebef7c
|
||||||
FirebaseAppCheck: 94dae4d9bb682bdef85a778b0c1024a4613f1e89
|
FirebaseCoreExtension: 6605938d51f765d8b18bfcafd2085276a252bee2
|
||||||
FirebaseAppCheckInterop: 4bade10286cc977e516f75d2d8312cbdfa534789
|
FirebaseCoreInternal: fe5fa466aeb314787093a7dce9f0beeaad5a2a21
|
||||||
FirebaseAuth: 3a39f6436c21ebfd7919b698228b4f89ff94c23b
|
FirebaseFirestore: 67f23000ca238ccbab79127ed59636a9a2689e74
|
||||||
FirebaseAuthInterop: f8f6ff72dc24621906497fbe5cf3c42ee815e59c
|
FirebaseFirestoreInternal: a0e7382af3d208898dcd1d4d52d8a7870632e881
|
||||||
FirebaseCore: 428912f751178b06bef0a1793effeb4a5e09a9b8
|
FirebaseSharedSwift: f57ed48f4542b2d7eb4738f4f23ba443f78b3780
|
||||||
FirebaseCoreExtension: e911052d59cd0da237a45d706fc0f81654f035c1
|
|
||||||
FirebaseCoreInternal: b321eafae5362113bc182956fafc9922cfc77b72
|
|
||||||
FirebaseFirestore: d8b76ca1feb4ca0b0f078c45f7d1bd8014a49ef1
|
|
||||||
FirebaseFirestoreInternal: 02341a9ba87f6309227b04685022a5e16307bbf7
|
|
||||||
FirebaseSharedSwift: 9d2fa84a46676302b89dbd5e6e62bce2fe376909
|
|
||||||
FlutterMacOS: d0db08ddef1a9af05a5ec4b724367152bb0500b1
|
FlutterMacOS: d0db08ddef1a9af05a5ec4b724367152bb0500b1
|
||||||
GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1
|
GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1
|
||||||
"gRPC-C++": cc207623316fb041a7a3e774c252cf68a058b9e8
|
"gRPC-C++": cc207623316fb041a7a3e774c252cf68a058b9e8
|
||||||
gRPC-Core: 860978b7db482de8b4f5e10677216309b5ff6330
|
gRPC-Core: 860978b7db482de8b4f5e10677216309b5ff6330
|
||||||
GTMSessionFetcher: b8ab00db932816e14b0a0664a08cb73dda6d164b
|
|
||||||
leveldb-library: cc8b8f8e013647a295ad3f8cd2ddf49a6f19be19
|
leveldb-library: cc8b8f8e013647a295ad3f8cd2ddf49a6f19be19
|
||||||
nanopb: fad817b59e0457d11a5dfbde799381cd727c1275
|
nanopb: fad817b59e0457d11a5dfbde799381cd727c1275
|
||||||
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
|
|
||||||
share_plus: 510bf0af1a42cd602274b4629920c9649c52f4cc
|
share_plus: 510bf0af1a42cd602274b4629920c9649c52f4cc
|
||||||
shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb
|
shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,15 +2,13 @@
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>com.apple.security.app-sandbox</key>
|
<key>com.apple.security.app-sandbox</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>com.apple.security.cs.allow-jit</key>
|
<key>com.apple.security.cs.allow-jit</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>com.apple.security.network.client</key>
|
<key>com.apple.security.network.client</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>com.apple.security.network.server</key>
|
<key>com.apple.security.network.server</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>keychain-access-groups</key>
|
|
||||||
<array/>
|
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|
@ -8,7 +8,5 @@
|
||||||
<true/>
|
<true/>
|
||||||
<key>com.apple.security.network.client</key>
|
<key>com.apple.security.network.client</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>keychain-access-groups</key>
|
|
||||||
<array/>
|
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|
|
||||||
|
|
@ -1,33 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
<title>Page Not Found</title>
|
|
||||||
|
|
||||||
<style media="screen">
|
|
||||||
body { background: #ECEFF1; color: rgba(0,0,0,0.87); font-family: Roboto, Helvetica, Arial, sans-serif; margin: 0; padding: 0; }
|
|
||||||
#message { background: white; max-width: 360px; margin: 100px auto 16px; padding: 32px 24px 16px; border-radius: 3px; }
|
|
||||||
#message h3 { color: #888; font-weight: normal; font-size: 16px; margin: 16px 0 12px; }
|
|
||||||
#message h2 { color: #ffa100; font-weight: bold; font-size: 16px; margin: 0 0 8px; }
|
|
||||||
#message h1 { font-size: 22px; font-weight: 300; color: rgba(0,0,0,0.6); margin: 0 0 16px;}
|
|
||||||
#message p { line-height: 140%; margin: 16px 0 24px; font-size: 14px; }
|
|
||||||
#message a { display: block; text-align: center; background: #039be5; text-transform: uppercase; text-decoration: none; color: white; padding: 16px; border-radius: 4px; }
|
|
||||||
#message, #message a { box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24); }
|
|
||||||
#load { color: rgba(0,0,0,0.4); text-align: center; font-size: 13px; }
|
|
||||||
@media (max-width: 600px) {
|
|
||||||
body, #message { margin-top: 0; background: white; box-shadow: none; }
|
|
||||||
body { border-top: 16px solid #ffa100; }
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="message">
|
|
||||||
<h2>404</h2>
|
|
||||||
<h1>Page Not Found</h1>
|
|
||||||
<p>The specified file was not found on this website. Please check the URL for mistakes and try again.</p>
|
|
||||||
<h3>Why am I seeing this?</h3>
|
|
||||||
<p>This page was generated by the Firebase Command-Line Interface. To modify it, edit the <code>404.html</code> file in your project's configured <code>public</code> directory.</p>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
@ -1,89 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
<title>Welcome to Firebase Hosting</title>
|
|
||||||
|
|
||||||
<!-- update the version number as needed -->
|
|
||||||
<script defer src="/__/firebase/12.10.0/firebase-app-compat.js"></script>
|
|
||||||
<!-- include only the Firebase features as you need -->
|
|
||||||
<script defer src="/__/firebase/12.10.0/firebase-auth-compat.js"></script>
|
|
||||||
<script defer src="/__/firebase/12.10.0/firebase-database-compat.js"></script>
|
|
||||||
<script defer src="/__/firebase/12.10.0/firebase-firestore-compat.js"></script>
|
|
||||||
<script defer src="/__/firebase/12.10.0/firebase-functions-compat.js"></script>
|
|
||||||
<script defer src="/__/firebase/12.10.0/firebase-messaging-compat.js"></script>
|
|
||||||
<script defer src="/__/firebase/12.10.0/firebase-storage-compat.js"></script>
|
|
||||||
<script defer src="/__/firebase/12.10.0/firebase-analytics-compat.js"></script>
|
|
||||||
<script defer src="/__/firebase/12.10.0/firebase-remote-config-compat.js"></script>
|
|
||||||
<script defer src="/__/firebase/12.10.0/firebase-performance-compat.js"></script>
|
|
||||||
<!--
|
|
||||||
initialize the SDK after all desired features are loaded, set useEmulator to false
|
|
||||||
to avoid connecting the SDK to running emulators.
|
|
||||||
-->
|
|
||||||
<script defer src="/__/firebase/init.js?useEmulator=true"></script>
|
|
||||||
|
|
||||||
<style media="screen">
|
|
||||||
body { background: #ECEFF1; color: rgba(0,0,0,0.87); font-family: Roboto, Helvetica, Arial, sans-serif; margin: 0; padding: 0; }
|
|
||||||
#message { background: white; max-width: 360px; margin: 100px auto 16px; padding: 32px 24px; border-radius: 3px; }
|
|
||||||
#message h2 { color: #ffa100; font-weight: bold; font-size: 16px; margin: 0 0 8px; }
|
|
||||||
#message h1 { font-size: 22px; font-weight: 300; color: rgba(0,0,0,0.6); margin: 0 0 16px;}
|
|
||||||
#message p { line-height: 140%; margin: 16px 0 24px; font-size: 14px; }
|
|
||||||
#message a { display: block; text-align: center; background: #039be5; text-transform: uppercase; text-decoration: none; color: white; padding: 16px; border-radius: 4px; }
|
|
||||||
#message, #message a { box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24); }
|
|
||||||
#load { color: rgba(0,0,0,0.4); text-align: center; font-size: 13px; }
|
|
||||||
@media (max-width: 600px) {
|
|
||||||
body, #message { margin-top: 0; background: white; box-shadow: none; }
|
|
||||||
body { border-top: 16px solid #ffa100; }
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="message">
|
|
||||||
<h2>Welcome</h2>
|
|
||||||
<h1>Firebase Hosting Setup Complete</h1>
|
|
||||||
<p>You're seeing this because you've successfully setup Firebase Hosting. Now it's time to go build something extraordinary!</p>
|
|
||||||
<a target="_blank" href="https://firebase.google.com/docs/hosting/">Open Hosting Documentation</a>
|
|
||||||
</div>
|
|
||||||
<p id="load">Firebase SDK Loading…</p>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
|
||||||
const loadEl = document.querySelector('#load');
|
|
||||||
// // 🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥
|
|
||||||
// // The Firebase SDK is initialized and available here!
|
|
||||||
//
|
|
||||||
// firebase.auth().onAuthStateChanged(user => { });
|
|
||||||
// firebase.database().ref('/path/to/ref').on('value', snapshot => { });
|
|
||||||
// firebase.firestore().doc('/foo/bar').get().then(() => { });
|
|
||||||
// firebase.functions().httpsCallable('yourFunction')().then(() => { });
|
|
||||||
// firebase.messaging().requestPermission().then(() => { });
|
|
||||||
// firebase.storage().ref('/path/to/ref').getDownloadURL().then(() => { });
|
|
||||||
// firebase.analytics(); // call to activate
|
|
||||||
// firebase.analytics().logEvent('tutorial_completed');
|
|
||||||
// firebase.performance(); // call to activate
|
|
||||||
//
|
|
||||||
// // 🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥
|
|
||||||
|
|
||||||
try {
|
|
||||||
let app = firebase.app();
|
|
||||||
let features = [
|
|
||||||
'auth',
|
|
||||||
'database',
|
|
||||||
'firestore',
|
|
||||||
'functions',
|
|
||||||
'messaging',
|
|
||||||
'storage',
|
|
||||||
'analytics',
|
|
||||||
'remoteConfig',
|
|
||||||
'performance',
|
|
||||||
].filter(feature => typeof app[feature] === 'function');
|
|
||||||
loadEl.textContent = `Firebase SDK loaded with ${features.join(', ')}`;
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
loadEl.textContent = 'Error loading the Firebase SDK, check the console.';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
84
pubspec.lock
84
pubspec.lock
|
|
@ -5,10 +5,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: _flutterfire_internals
|
name: _flutterfire_internals
|
||||||
sha256: afe15ce18a287d2f89da95566e62892df339b1936bbe9b83587df45b944ee72a
|
sha256: cd83f7d6bd4e4c0b0b4fef802e8796784032e1cc23d7b0e982cf5d05d9bbe182
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.67"
|
version: "1.3.66"
|
||||||
app_links:
|
app_links:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|
@ -129,22 +129,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.2"
|
version: "2.1.2"
|
||||||
change_app_package_name:
|
|
||||||
dependency: "direct dev"
|
|
||||||
description:
|
|
||||||
name: change_app_package_name
|
|
||||||
sha256: "8e43b754fe960426904d77ed4c62fa8c9834deaf6e293ae40963fa447482c4c5"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.5.0"
|
|
||||||
characters:
|
characters:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: characters
|
name: characters
|
||||||
sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803
|
sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.4.0"
|
version: "1.4.1"
|
||||||
checked_yaml:
|
checked_yaml:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -257,62 +249,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "7.0.1"
|
version: "7.0.1"
|
||||||
firebase_app_check:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: firebase_app_check
|
|
||||||
sha256: cc0bfeb003c5747e963f4f378e72c8c5c5f91a243f2e85b7da29ff42f4709996
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.4.1+5"
|
|
||||||
firebase_app_check_platform_interface:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: firebase_app_check_platform_interface
|
|
||||||
sha256: "54166319f9121bd94b2374864e84dd54507438095fc2d42ca49f23957b16b0d1"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.2.1+5"
|
|
||||||
firebase_app_check_web:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: firebase_app_check_web
|
|
||||||
sha256: f8dddba09bd7786e017b58e74455886106ac7ca9e798769e2cac3e988fc2d146
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.2.2+3"
|
|
||||||
firebase_auth:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: firebase_auth
|
|
||||||
sha256: b20d1540460814c5984474c1e9dd833bdbcff6ecd8d6ad86cc9da8cfd581c172
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "6.1.4"
|
|
||||||
firebase_auth_platform_interface:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: firebase_auth_platform_interface
|
|
||||||
sha256: fd0225320b6bbc92460c86352d16b60aea15f9ef88292774cca97b0522ea9f72
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "8.1.6"
|
|
||||||
firebase_auth_web:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: firebase_auth_web
|
|
||||||
sha256: be7dccb263b89fbda2a564de9d8193118196e8481ffb937222a025cdfdf82c40
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "6.1.2"
|
|
||||||
firebase_core:
|
firebase_core:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: firebase_core
|
name: firebase_core
|
||||||
sha256: f0997fee80fbb6d2c658c5b88ae87ba1f9506b5b37126db64fc2e75d8e977fbb
|
sha256: "923085c881663ef685269b013e241b428e1fb03cdd0ebde265d9b40ff18abf80"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.5.0"
|
version: "4.4.0"
|
||||||
firebase_core_platform_interface:
|
firebase_core_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -325,10 +269,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: firebase_core_web
|
name: firebase_core_web
|
||||||
sha256: "856ca92bf2d75a63761286ab8e791bda3a85184c2b641764433b619647acfca6"
|
sha256: "83e7356c704131ca4d8d8dd57e360d8acecbca38b1a3705c7ae46cc34c708084"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.5.0"
|
version: "3.4.0"
|
||||||
fixnum:
|
fixnum:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -505,18 +449,18 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: matcher
|
name: matcher
|
||||||
sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2
|
sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.12.17"
|
version: "0.12.19"
|
||||||
material_color_utilities:
|
material_color_utilities:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: material_color_utilities
|
name: material_color_utilities
|
||||||
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
|
sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.11.1"
|
version: "0.13.0"
|
||||||
meta:
|
meta:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -790,10 +734,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: test_api
|
name: test_api
|
||||||
sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55
|
sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.7.7"
|
version: "0.7.10"
|
||||||
typed_data:
|
typed_data:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
name: tetraq
|
name: tetraq
|
||||||
description: A new Flutter project.
|
description: A new Flutter project.
|
||||||
publish_to: 'none'
|
publish_to: 'none'
|
||||||
version: 1.1.4+6
|
version: 1.0.2+4
|
||||||
environment:
|
environment:
|
||||||
sdk: ^3.10.7
|
sdk: ^3.10.7
|
||||||
|
|
||||||
|
|
@ -18,20 +18,17 @@ dependencies:
|
||||||
flutter_localizations: # Il sistema multilingua ufficiale
|
flutter_localizations: # Il sistema multilingua ufficiale
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
firebase_core: ^4.4.0
|
firebase_core: ^4.4.0
|
||||||
firebase_auth: ^6.1.4 # <--- NUOVO: LA CORAZZA DI SICUREZZA!
|
|
||||||
cloud_firestore: ^6.1.2
|
cloud_firestore: ^6.1.2
|
||||||
share_plus: ^12.0.1
|
share_plus: ^12.0.1
|
||||||
app_links: ^7.0.0
|
app_links: ^7.0.0
|
||||||
google_fonts: ^8.0.2
|
google_fonts: ^8.0.2
|
||||||
font_awesome_flutter: ^10.12.0
|
font_awesome_flutter: ^10.12.0
|
||||||
firebase_app_check: ^0.4.1+5
|
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
flutter_lints: ^6.0.0
|
flutter_lints: ^6.0.0
|
||||||
flutter_launcher_icons: ^0.13.1
|
flutter_launcher_icons: ^0.13.1
|
||||||
change_app_package_name: ^1.5.0
|
|
||||||
|
|
||||||
flutter:
|
flutter:
|
||||||
uses-material-design: true
|
uses-material-design: true
|
||||||
|
|
@ -44,7 +41,6 @@ flutter:
|
||||||
- assets/images/
|
- assets/images/
|
||||||
- assets/audio/bgm/
|
- assets/audio/bgm/
|
||||||
- assets/audio/sfx/
|
- assets/audio/sfx/
|
||||||
- assets/audio/
|
|
||||||
|
|
||||||
|
|
||||||
flutter_icons:
|
flutter_icons:
|
||||||
|
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
{
|
|
||||||
"applinks": {
|
|
||||||
"apps": [],
|
|
||||||
"details": [
|
|
||||||
{
|
|
||||||
"appID": "2BX6QRR7GG.com.sanza.tetraq",
|
|
||||||
"paths": [ "/join" ]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
[{
|
|
||||||
"relation": ["delegate_permission/common.handle_all_urls"],
|
|
||||||
"target": {
|
|
||||||
"namespace": "android_app",
|
|
||||||
"package_name": "com.amastra.tetraq",
|
|
||||||
"sha256_cert_fingerprints": ["DA_INSERIRE_IN_FUTURO"]
|
|
||||||
}
|
|
||||||
}]
|
|
||||||
Loading…
Reference in a new issue