Compare commits
24 Commits
fcd3d1a7e5
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 6afbd2529f | |||
| d9fc5e8798 | |||
| 508534f905 | |||
| bd6f2b2907 | |||
| 357c9e2dbd | |||
| 408baee726 | |||
| b54a44dbd4 | |||
| 1bcf3332d8 | |||
| 688d1921aa | |||
| 25bc5a1374 | |||
| ae9837ae42 | |||
| ba1717cbb6 | |||
| 00e81f9da4 | |||
| 56f17b5ded | |||
| 064d7c0bc2 | |||
| 4a4e65127f | |||
| af32d40452 | |||
| 305950f571 | |||
| 1586ea9209 | |||
| 5ee1f35178 | |||
| d3a2de1353 | |||
| f63b995018 | |||
| 4d9dd16398 | |||
| 638b50efb2 |
@@ -1,3 +1,60 @@
|
||||
# green-gaming
|
||||
# 🌍 Save the Island - Progetto per la Giornata della Terra
|
||||
|
||||
Gioco per la Giornata della Terra
|
||||
**Save the Island** è un videogioco web in 3D nato per sensibilizzare gli utenti sulla gestione dei rifiuti e l'importanza del riciclo. Il progetto adotta un'architettura Fullstack basata su **XAMPP** per la gestione di una classifica globale e il salvataggio dei record.
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ Architettura Tecnica
|
||||
* **Entrypoint:** `index.php` (Pagina di atterraggio con trailer e controlli).
|
||||
* **Backend:** PHP + MySQL (XAMPP) per la gestione del database e dei punteggi.
|
||||
* **Frontend:** **Three.js** (motore 3D), JavaScript (ES6+), CSS3.
|
||||
* **Trasferimento Dati:** Utilizzo di Cookie temporanei o `localStorage` per mantenere i dati tra le diverse fasi di gioco.
|
||||
|
||||
---
|
||||
|
||||
## 🗺️ Roadmap di Sviluppo
|
||||
|
||||
### 🚩 Milestone Generali (Nucleo del Progetto)
|
||||
- [✅] **Design UI & Mockup:** Finalizzazione degli asset grafici (basati sullo schema Draw.io).
|
||||
- [✅] **Configurazione Database:** Creazione delle tabelle `classifica` e `records` su MySQL.
|
||||
- [✅] **Logica Entrypoint:** Sviluppo di `index.php` con video di sfondo e overlay dei comandi.
|
||||
- [✅] **Sistema di Trasferimento:** Implementazione logica per il passaggio dei dati dalla Fase 1 alla Fase 2.
|
||||
- [❌] **Gestione Impostazioni:** Pannello per regolare il volume e inserire il nome della squadra (servira' nella classifica).
|
||||
- [✅] **Pagina Dinamica:** Creazione della classifica in PHP con recupero dati in tempo reale.
|
||||
|
||||
### 🏝️ Fase 1: La Raccolta (Timer: 1m)
|
||||
- [✅] **Ambiente di gioco:** Modellazione dell'isola 3D con Three.js e gestione dei confini della mappa.
|
||||
- [✅] **Sistema di Spawn:** Posizionamento casuale dei rifiuti su 20 coordinate casuali.
|
||||
- [✅] **Sistema Ostacoli:** Inserimento di modelli 3D di alberi e oggetti ambientali decorativi.
|
||||
- [✅] **Meccaniche di Raccolta:** Gestione delle collisioni e incremento del punteggio ecologico.
|
||||
- [✅] **Interfaccia (HUD):** Overlay con Timer (60s), contatore rifiuti e disattivazione della pausa.
|
||||
- [✅] **Schermata di caricamento:** Schermata di caricamento iniziale
|
||||
- [✅] **Schermata iniziale:** Schermata iniziale
|
||||
- [❌] **Rifinitura (Polish):** Animazioni di raccolta e ottimizzazione delle mesh 3D.
|
||||
- [✅] **Timer:** Meccanica del timer per il tempo di raccolta dei rifiuti
|
||||
- [❌] **Personaggio:** Cilindro che rappresenta il personaggio (fatto per collisioni piu' precise)
|
||||
|
||||
### ♻️ Fase 2: Lo Smistamento (Timer: 5s * Punteggio)
|
||||
- [✅] **Timer Dinamico:** Calcolo del tempo a disposizione basato sul successo della Fase 1.
|
||||
- [✅] **Motore di Smistamento:** Logica di convalida (Rifiuto ↔️ Bidone corretto).
|
||||
- [✅] **Interfaccia Utente:** Layout con i 6 bidoni (Plastica, Umido, Indifferenziata, Vetro, Carta, Alluminio).
|
||||
- [✅] **Gestione Input:** Meccanica di interazione tramite Drag & Drop o selezione rapida.
|
||||
- [✅] **Feedback Visivo:** Effetti sonori e visivi per risposte corrette o errate.
|
||||
- [❌] **Rifinitura (Polish):** Abbellimento grafico.
|
||||
|
||||
### 🏆 Fase Finale: Classifica & Record
|
||||
- [✅] **Calcolo Punteggio:** Elaborazione dei risultati finali e calcolo dei bonus velocità.
|
||||
- [✅] **Verifica Record:** Confronto del punteggio con il record personale salvato.
|
||||
- [✅] **Classifica Globale:** Script PHP per l'invio e la visualizzazione della Top 10 dal database.
|
||||
- [✅] **Classifica completa:** Pagina PHP per visualizzare la classifica di tutte le partite dal database.
|
||||
- [✅] **Schermata Game Over:** Riepilogo statistiche e opzione per riavviare la partita.
|
||||
|
||||
---
|
||||
|
||||
## 📂 Struttura delle Cartelle
|
||||
* `/assets` - Modelli 3D, video loop, texture dei rifiuti e icone dei bidoni.
|
||||
* `/css` - Fogli di stile per i menu e l'interfaccia di gioco (HUD).
|
||||
* `/js` - Logica delle due fasi del gioco (fase1.js, fase2.js).
|
||||
* `/php` - Script per il backend (`db_connect.php`, `save_score.php`).
|
||||
* `/pages` - Pagine HTML dedicate alle varie pagine del gioco (`fase1.html`, `fase2.html`).
|
||||
* `index.php` - Homepage e controller principale del progetto.
|
||||
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 87 KiB |
|
After Width: | Height: | Size: 164 KiB |
|
After Width: | Height: | Size: 5.8 KiB |
|
After Width: | Height: | Size: 457 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 253 KiB |
|
After Width: | Height: | Size: 9.2 KiB |
|
After Width: | Height: | Size: 164 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 1.6 MiB |
|
After Width: | Height: | Size: 1.7 MiB |
|
After Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 16 MiB After Width: | Height: | Size: 16 MiB |
|
Before Width: | Height: | Size: 16 MiB After Width: | Height: | Size: 16 MiB |
|
Before Width: | Height: | Size: 4.7 MiB After Width: | Height: | Size: 4.7 MiB |
|
Before Width: | Height: | Size: 14 MiB After Width: | Height: | Size: 14 MiB |
|
Before Width: | Height: | Size: 13 MiB After Width: | Height: | Size: 13 MiB |
@@ -0,0 +1,148 @@
|
||||
/* --- VARIABILI COLORI --- */
|
||||
:root {
|
||||
--bg-light: #f8fafc;
|
||||
--text-dark: #1e293b;
|
||||
--green-eco: #10b981;
|
||||
--green-dark: #065f46;
|
||||
--gold: #f1c40f;
|
||||
--silver: #95a5a6;
|
||||
--bronze: #cd7f32;
|
||||
--white: #ffffff;
|
||||
}
|
||||
|
||||
/* --- RESET E BASE --- */
|
||||
body {
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
||||
background-color: var(--bg-light);
|
||||
margin: 0;
|
||||
padding: 40px 20px;
|
||||
color: var(--text-dark);
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
color: var(--text-dark);
|
||||
font-size: 2.5rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
/* --- BOTTONI ORDINAMENTO --- */
|
||||
.sorting-buttons {
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.sort-button {
|
||||
display: inline-block;
|
||||
padding: 12px 25px;
|
||||
margin: 0 10px;
|
||||
background-color: var(--white);
|
||||
color: var(--green-eco);
|
||||
text-decoration: none;
|
||||
border-radius: 12px;
|
||||
border: 2px solid var(--green-eco);
|
||||
font-weight: 600;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.sort-button:hover {
|
||||
background-color: var(--green-eco);
|
||||
color: var(--white);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.sort-button.active {
|
||||
background-color: var(--green-eco);
|
||||
color: var(--white);
|
||||
box-shadow: 0 4px 12px rgba(16, 185, 129, 0.3);
|
||||
}
|
||||
|
||||
/* --- TABELLA --- */
|
||||
table {
|
||||
width: 100%;
|
||||
max-width: 1100px;
|
||||
margin: 0 auto;
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
background-color: var(--white);
|
||||
border-radius: 20px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 10px 30px rgba(0,0,0,0.05);
|
||||
}
|
||||
|
||||
th, td {
|
||||
padding: 18px;
|
||||
text-align: center;
|
||||
border-bottom: 1px solid #edf2f7;
|
||||
}
|
||||
|
||||
th {
|
||||
background-color: var(--green-eco);
|
||||
color: var(--white);
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
font-size: 0.85rem;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
/* --- COLORAZIONE PODIO DINAMICA (Basata su Classi) --- */
|
||||
|
||||
/* 1° Posto - ORO (Funziona ovunque sia la riga) */
|
||||
tr.rank-gold {
|
||||
background-color: rgba(241, 196, 15, 0.08) !important;
|
||||
}
|
||||
tr.rank-gold td:first-child strong {
|
||||
color: var(--gold);
|
||||
font-size: 1.4rem;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/* 2° Posto - ARGENTO */
|
||||
tr.rank-silver {
|
||||
background-color: rgba(189, 195, 199, 0.08) !important;
|
||||
}
|
||||
tr.rank-silver td:first-child strong {
|
||||
color: var(--silver);
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
/* 3° Posto - BRONZO */
|
||||
tr.rank-bronze {
|
||||
background-color: rgba(205, 127, 50, 0.05) !important;
|
||||
}
|
||||
tr.rank-bronze td:first-child strong {
|
||||
color: var(--bronze);
|
||||
font-size: 1.15rem;
|
||||
}
|
||||
|
||||
/* Stile base per la colonna posizione */
|
||||
td strong {
|
||||
font-weight: 800;
|
||||
color: var(--text-dark);
|
||||
}
|
||||
|
||||
/* Effetto al passaggio del mouse sulle righe */
|
||||
tr:hover {
|
||||
background-color: #f1f5f9;
|
||||
}
|
||||
|
||||
/* Arrotondamento angoli inferiori della tabella */
|
||||
table tr:last-child td:first-child { border-bottom-left-radius: 20px; }
|
||||
table tr:last-child td:last-child { border-bottom-right-radius: 20px; }
|
||||
|
||||
/* Rimuove l'ultima linea per pulizia estetica */
|
||||
table tr:last-child td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
/* --- RESPONSIVE --- */
|
||||
@media (max-width: 768px) {
|
||||
body { padding: 20px 10px; }
|
||||
table { font-size: 0.8rem; }
|
||||
th, td { padding: 12px 8px; }
|
||||
h1 { font-size: 1.8rem; }
|
||||
.sort-button { padding: 10px 15px; margin: 5px; }
|
||||
}
|
||||
@@ -0,0 +1,391 @@
|
||||
/* --- VARIABILI GLOBALI --- */
|
||||
:root {
|
||||
--green-bright: #2ecc71;
|
||||
--green-glow: rgba(46, 204, 113, 0.6);
|
||||
--glass-bg: rgba(0, 0, 0, 0.7);
|
||||
--glass-border: rgba(46, 204, 113, 0.3);
|
||||
--top: 10px;
|
||||
--border: 20px;
|
||||
--text: 1.5rem;
|
||||
}
|
||||
|
||||
/* --- RESET E BODY --- */
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
background-color: #000;
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* --- HUD DI GIOCO (Punti e Tempo) --- */
|
||||
#punti, #tempo {
|
||||
position: absolute;
|
||||
top: var(--top);
|
||||
color: white;
|
||||
font-size: var(--text);
|
||||
pointer-events: none;
|
||||
display: none;
|
||||
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.8), 0 0 10px var(--green-glow);
|
||||
font-weight: bold;
|
||||
z-index: 50;
|
||||
}
|
||||
|
||||
#punti { left: var(--border); }
|
||||
#tempo { right: var(--border); }
|
||||
#fps-counter {
|
||||
position: absolute;
|
||||
top: var(--top); /* Usa la stessa variabile degli altri */
|
||||
left: 50%;
|
||||
transform: translateX(-50%); /* Lo centra perfettamente */
|
||||
color: var(--green-bright);
|
||||
font-size: 1.2rem; /* Leggermente più piccolo dei punti per non distrarre */
|
||||
font-family: monospace;
|
||||
pointer-events: none;
|
||||
display: none; /* Verrà mostrato insieme agli altri al lock() */
|
||||
text-shadow: 0 0 10px var(--green-glow);
|
||||
z-index: 50;
|
||||
}
|
||||
|
||||
/* --- MIRINO (Crosshair) --- */
|
||||
#crosshair {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
color: var(--green-bright);
|
||||
font-size: 24px;
|
||||
pointer-events: none;
|
||||
display: none;
|
||||
z-index: 10;
|
||||
text-shadow: 0 0 5px #000;
|
||||
}
|
||||
|
||||
/* --- SCHERMATA DI CARICAMENTO (MATRIX) --- */
|
||||
#loading-screen {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: #000;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 9999;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#matrix-canvas {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
.loader-content {
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
width: 80%;
|
||||
max-width: 500px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.eco-title {
|
||||
font-family: 'Courier New', monospace;
|
||||
color: var(--green-bright);
|
||||
font-size: 1.5rem;
|
||||
font-weight: bold;
|
||||
letter-spacing: 5px;
|
||||
margin-bottom: 30px;
|
||||
text-shadow: 0 0 10px var(--green-glow);
|
||||
}
|
||||
|
||||
.bar-container {
|
||||
width: 100%;
|
||||
background: #222 !important;
|
||||
height: 10px;
|
||||
border-radius: 5px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
z-index: 20;
|
||||
border: 1px solid rgba(255,255,255,0.1);
|
||||
}
|
||||
|
||||
#loading-bar {
|
||||
width: 0%;
|
||||
height: 100%;
|
||||
background-color: var(--green-bright) !important;
|
||||
box-shadow: 0 0 20px var(--green-bright);
|
||||
transition: width 0.3s ease-out;
|
||||
}
|
||||
|
||||
#loading-text {
|
||||
color: var(--green-bright);
|
||||
font-family: monospace;
|
||||
font-size: 0.9rem;
|
||||
letter-spacing: 2px;
|
||||
opacity: 0.8;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
/* --- POPUP INSERIMENTO USERNAME --- */
|
||||
/* Overlay con sfocatura profonda */
|
||||
#name-popup-overlay {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 9999;
|
||||
background: radial-gradient(circle, rgba(0, 20, 10, 0.7) 0%, rgba(0, 0, 0, 0.95) 100%);
|
||||
backdrop-filter: blur(12px);
|
||||
display: none;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
}
|
||||
|
||||
/* Contenitore Popup */
|
||||
.name-popup {
|
||||
background: rgba(10, 15, 12, 0.85);
|
||||
border: 2px solid var(--green-bright);
|
||||
padding: 3rem;
|
||||
border-radius: 30px;
|
||||
text-align: center;
|
||||
box-shadow: 0 0 40px rgba(46, 204, 113, 0.2), inset 0 0 20px rgba(46, 204, 113, 0.1);
|
||||
max-width: 450px;
|
||||
width: 90%;
|
||||
transform: translateY(20px);
|
||||
animation: popupFadeIn 0.6s ease forwards;
|
||||
border-top: 5px solid var(--green-bright); /* Accento superiore */
|
||||
}
|
||||
|
||||
@keyframes popupFadeIn {
|
||||
to { transform: translateY(0); opacity: 1; }
|
||||
}
|
||||
|
||||
.name-popup h3 {
|
||||
color: var(--green-bright);
|
||||
margin-bottom: 10px;
|
||||
letter-spacing: 3px;
|
||||
text-transform: uppercase;
|
||||
font-weight: 900;
|
||||
text-shadow: 0 0 10px var(--green-glow);
|
||||
}
|
||||
|
||||
.name-popup p {
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: 2rem;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Input stilizzato */
|
||||
#username-input {
|
||||
width: 100%;
|
||||
padding: 15px;
|
||||
background: rgba(0, 0, 0, 0.4);
|
||||
border: 1px solid rgba(46, 204, 113, 0.4);
|
||||
border-radius: 12px;
|
||||
color: #fff;
|
||||
font-size: 1.2rem;
|
||||
margin-bottom: 25px;
|
||||
outline: none;
|
||||
text-align: center;
|
||||
transition: all 0.3s ease;
|
||||
box-sizing: border-box;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
#username-input:focus {
|
||||
border-color: var(--green-bright);
|
||||
box-shadow: 0 0 15px var(--green-glow);
|
||||
background: rgba(46, 204, 113, 0.05);
|
||||
}
|
||||
|
||||
/* Pulsante di conferma */
|
||||
#confirm-name-btn {
|
||||
width: 100%;
|
||||
background: var(--green-bright);
|
||||
color: #002b12;
|
||||
border: none;
|
||||
padding: 15px;
|
||||
font-weight: 800;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
cursor: pointer;
|
||||
border-radius: 12px;
|
||||
transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
}
|
||||
|
||||
#confirm-name-btn:hover {
|
||||
background: #fff;
|
||||
color: #000;
|
||||
box-shadow: 0 0 25px #fff;
|
||||
transform: scale(1.03);
|
||||
}
|
||||
|
||||
/* Effetto errore (scuotimento) */
|
||||
.shake {
|
||||
animation: shakeAnim 0.4s ease;
|
||||
}
|
||||
|
||||
@keyframes shakeAnim {
|
||||
0%, 100% { transform: translateX(0); }
|
||||
25% { transform: translateX(-10px); }
|
||||
75% { transform: translateX(10px); }
|
||||
}
|
||||
|
||||
/* --- SCHERMATA INIZIALE (START) --- */
|
||||
/* L'overlay serve come ulteriore strato di colore per far leggere bene i testi */
|
||||
.overlay {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: radial-gradient(circle, rgba(0,0,0,0) 0%, rgba(0,0,0,0.6) 100%);
|
||||
z-index: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Assicurati che il contenitore start non mostri lo scale dell'immagine fuori dai bordi */
|
||||
#start {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
overflow: hidden; /* Fondamentale per il trucco dello scale */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
#bg-island {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
z-index: -1;
|
||||
/* Effetto sfocatura e luminosità ridotta */
|
||||
filter: blur(6px) brightness(0.5);
|
||||
/* Lo scale(1.1) serve a "ingrandire" l'immagine quel tanto che basta
|
||||
per nascondere i bordi sfocati che altrimenti diventerebbero bianchi */
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
.overlay {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
z-index: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.content {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* Titolo Missione */
|
||||
.top-section {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding-top: 40px;
|
||||
}
|
||||
|
||||
.main-title {
|
||||
width: 25%;
|
||||
height: auto;
|
||||
filter: drop-shadow(0 0 15px var(--green-bright));
|
||||
}
|
||||
|
||||
/* Layout a Tre Colonne */
|
||||
.bottom-section {
|
||||
flex: 1.5;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
align-items: center;
|
||||
padding: 0 5%;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.item {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* Box con effetto Vetro (Glassmorphism) */
|
||||
.controls-box, .instructions-box {
|
||||
background: var(--glass-bg);
|
||||
backdrop-filter: blur(10px);
|
||||
border: 1px solid var(--glass-border);
|
||||
padding: 25px;
|
||||
border-radius: 15px;
|
||||
color: white;
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
|
||||
width: 100%;
|
||||
max-width: 350px;
|
||||
}
|
||||
|
||||
.controls-box h3, .instructions-box h3 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 15px;
|
||||
font-size: 1.3rem;
|
||||
text-transform: uppercase;
|
||||
color: var(--green-bright);
|
||||
text-align: center;
|
||||
border-bottom: 1px solid var(--glass-border);
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.controls-box p, .instructions-box p {
|
||||
margin: 8px 0;
|
||||
font-size: 1rem;
|
||||
text-align: center;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
/* Tasti Tastiera */
|
||||
kbd {
|
||||
background-color: #333;
|
||||
border: 1px solid var(--green-bright);
|
||||
color: var(--green-bright);
|
||||
padding: 3px 6px;
|
||||
border-radius: 4px;
|
||||
font-family: monospace;
|
||||
font-weight: bold;
|
||||
box-shadow: 0 0 5px var(--green-glow);
|
||||
}
|
||||
|
||||
/* Testo Pulsante Centrale */
|
||||
.pulse-text {
|
||||
font-size: 2rem;
|
||||
font-weight: 800;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 4px;
|
||||
color: #fff;
|
||||
text-shadow: 0 0 15px var(--green-bright);
|
||||
cursor: pointer;
|
||||
animation: pulse 2s infinite;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0% { opacity: 1; transform: scale(1); }
|
||||
50% { opacity: 0.5; transform: scale(0.95); }
|
||||
100% { opacity: 1; transform: scale(1); }
|
||||
}
|
||||
|
||||
/* Responsive per schermi piccoli */
|
||||
@media (max-width: 900px) {
|
||||
.bottom-section {
|
||||
grid-template-columns: 1fr;
|
||||
overflow-y: auto;
|
||||
padding-bottom: 40px;
|
||||
}
|
||||
.main-title { width: 50%; }
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
/* --- VARIABILI GLOBALI --- */
|
||||
:root {
|
||||
--bg-light: #f8fafc;
|
||||
--text-dark: #1e293b;
|
||||
--green-eco: #10b981;
|
||||
--red-alert: #ef4444;
|
||||
--counter-top: #334155;
|
||||
}
|
||||
|
||||
/* --- RESET BASE --- */
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: 'Inter', -apple-system, sans-serif;
|
||||
background-color: var(--bg-light);
|
||||
color: var(--text-dark);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
/* --- UI SUPERIORE (Punti e Tempo) --- */
|
||||
#ui {
|
||||
margin-top: 20px;
|
||||
padding: 12px 40px;
|
||||
background: white;
|
||||
border-radius: 16px;
|
||||
font-size: 1.1rem;
|
||||
font-weight: 700;
|
||||
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
|
||||
border: 1px solid #e2e8f0;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
#ui span {
|
||||
color: var(--green-eco);
|
||||
}
|
||||
|
||||
/* --- CONTAINER GIOCO --- */
|
||||
#game-container {
|
||||
width: 92vw;
|
||||
height: 82vh;
|
||||
background-color: #000; /* Fondo nero per il mix-blend-mode */
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
border-radius: 24px;
|
||||
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
|
||||
margin: 20px auto;
|
||||
border: 8px solid white; /* Cornice stile tablet */
|
||||
}
|
||||
|
||||
/* --- AREA DEI BIDONI (Ingranditi) --- */
|
||||
#bins-container {
|
||||
display: flex;
|
||||
justify-content: center; /* Centra i bidoni */
|
||||
gap: 12px; /* Spazio tra i bidoni */
|
||||
align-items: flex-end;
|
||||
padding: 30px 20px;
|
||||
width: 100%;
|
||||
height: 72%; /* Più spazio verticale per cestini grandi */
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.bin {
|
||||
position: relative;
|
||||
/* Ingrandimento: ora i bidoni occupano fino al 20% della larghezza */
|
||||
width: 20%;
|
||||
max-width: 200px;
|
||||
min-width: 140px;
|
||||
transition: transform 0.2s cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||
}
|
||||
|
||||
.bin:hover {
|
||||
transform: scale(1.08) translateY(-10px);
|
||||
}
|
||||
|
||||
/* Gestione immagini con sfondo nero e scritta interna */
|
||||
.bin img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
mix-blend-mode: screen; /* Rende il nero dell'immagine trasparente */
|
||||
pointer-events: none;
|
||||
z-index: 2;
|
||||
position: relative;
|
||||
|
||||
/* Aumentiamo luminosità e contrasto per far leggere meglio le scritte nere */
|
||||
filter: brightness(1.25) contrast(1.1);
|
||||
}
|
||||
|
||||
/* --- EFFETTI FEEDBACK (Bagliore sotto) --- */
|
||||
.bin::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
bottom: 5%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 100%;
|
||||
height: 100px;
|
||||
border-radius: 50%;
|
||||
opacity: 0;
|
||||
filter: blur(25px);
|
||||
transition: opacity 0.3s ease;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.glow-success::after {
|
||||
opacity: 0.8;
|
||||
background: var(--green-eco);
|
||||
box-shadow: 0 0 60px 30px var(--green-eco);
|
||||
}
|
||||
|
||||
.glow-error::after {
|
||||
opacity: 0.8;
|
||||
background: var(--red-alert);
|
||||
box-shadow: 0 0 60px 30px var(--red-alert);
|
||||
}
|
||||
|
||||
/* --- I RIFIUTI (Trash) --- */
|
||||
.trash {
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
object-fit: contain;
|
||||
position: absolute;
|
||||
z-index: 500;
|
||||
cursor: grab;
|
||||
/* Ombra per farli sembrare sollevati */
|
||||
filter: drop-shadow(0 12px 10px rgba(0,0,0,0.4));
|
||||
}
|
||||
|
||||
.trash:active {
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
/* --- IL BANCONE (Counter) --- */
|
||||
#counter {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 120px;
|
||||
background: var(--counter-top);
|
||||
border-top: 3px solid rgba(255,255,255,0.1);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* Effetto profondità sul bordo del bancone */
|
||||
#counter::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 8px;
|
||||
background: linear-gradient(to bottom, rgba(0,0,0,0.3), transparent);
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: Arial, sans-serif;
|
||||
background-color: #f0f0f0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#game-container {
|
||||
/*width: 1000px;
|
||||
height: 600px;*/
|
||||
width: 90vw;
|
||||
height: 90vh;
|
||||
background-color: #000;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
border: 5px solid #333;
|
||||
margin: 0 auto;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
/* Area dei bidoni */
|
||||
#bins-container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 20px 10px;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* Effetto bagliore per risposta corretta (Verde) */
|
||||
.glow-success {
|
||||
box-shadow: 0 0 30px 15px rgba(0, 255, 0, 0.6);
|
||||
border-radius: 50%; /* Rende l'effetto ovale/circolare */
|
||||
transition: box-shadow 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
/* Effetto bagliore per risposta errata (Rosso) */
|
||||
.glow-error {
|
||||
box-shadow: 0 0 30px 15px rgba(255, 0, 0, 0.6);
|
||||
border-radius: 50%;
|
||||
transition: box-shadow 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.bin img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
pointer-events: none;
|
||||
|
||||
/* Questa riga è la magia: */
|
||||
mix-blend-mode: screen;
|
||||
|
||||
/* 'screen' mantiene i colori chiari e rende invisibile il nero.
|
||||
In questo modo il bagliore dietro passerà attraverso le zone nere. */
|
||||
}
|
||||
|
||||
|
||||
/* Modifica leggera al bagliore per renderlo più visibile sotto il bidone */
|
||||
.bin::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 60%; /* Abbassato un po' per centrarsi meglio sulla base del bidone */
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 150px;
|
||||
height: 100px; /* Più largo che alto per un effetto ovale perfetto */
|
||||
border-radius: 50%;
|
||||
opacity: 0;
|
||||
filter: blur(10px); /* Sfuma i bordi della luce */
|
||||
transition: opacity 0.3s;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
/* Stato Successo (Verde) */
|
||||
.glow-success::after {
|
||||
opacity: 1;
|
||||
box-shadow: 0 0 40px 20px rgba(0, 255, 0, 0.7);
|
||||
background-color: rgba(0, 255, 0, 0.2); /* Opzionale: un leggero centro colorato */
|
||||
}
|
||||
|
||||
/* Stato Errore (Rosso) */
|
||||
.glow-error::after {
|
||||
opacity: 1;
|
||||
box-shadow: 0 0 40px 20px rgba(255, 0, 0, 0.7);
|
||||
background-color: rgba(255, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.bin img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
pointer-events: none; /* Impedisce che il mouse "prenda" l'immagine del bidone */
|
||||
}
|
||||
|
||||
/* I rifiuti */
|
||||
.trash {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
object-fit: contain;
|
||||
position: absolute;
|
||||
z-index: 100;
|
||||
}
|
||||
.trash:active {
|
||||
cursor: grabbing;
|
||||
}
|
||||
/* Il bancone marrone in basso */
|
||||
#counter {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 100px;
|
||||
background-color: #5d4037;
|
||||
/* Colore marrone bancone */
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* Area UI (Punti e Tempo) */
|
||||
#ui {
|
||||
margin-top: 10px;
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,194 @@
|
||||
/* --- VARIABILI GLOBALI --- */
|
||||
:root {
|
||||
--green-bright: #2ecc71;
|
||||
--green-glow: rgba(46, 204, 113, 0.4);
|
||||
--glass-bg: rgba(255, 255, 255, 0.05);
|
||||
--glass-border: rgba(46, 204, 113, 0.2);
|
||||
--top: 10px;
|
||||
--text-base: 1.1rem;
|
||||
}
|
||||
|
||||
/* --- RESET E BASE --- */
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
background-color: #000; /* Sfondo di sicurezza */
|
||||
}
|
||||
|
||||
/* --- SCHERMATA FASE 2 --- */
|
||||
#start-fase2 {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 500;
|
||||
color: white;
|
||||
font-family: 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
|
||||
|
||||
/* Sfondo Sfumato Base */
|
||||
background: radial-gradient(circle at center, #0a2f1a 0%, #000000 100%);
|
||||
transition: opacity 0.5s ease;
|
||||
}
|
||||
|
||||
/* Overlay per dare texture allo sfondo */
|
||||
.overlay-gradient {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: linear-gradient(180deg, transparent 0%, rgba(46, 204, 113, 0.05) 100%);
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
/* --- CONTENUTO --- */
|
||||
.content {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* Sezione Titolo (50% sopra) */
|
||||
.top-section {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.top-section h1 {
|
||||
font-size: 3.5rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 5px;
|
||||
color: var(--green-bright);
|
||||
text-shadow: 0 0 20px var(--green-glow);
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
/* Sezione Box (50% sotto) */
|
||||
.bottom-section {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
width: 100%;
|
||||
align-items: flex-start;
|
||||
padding-top: 20px;
|
||||
}
|
||||
|
||||
.item {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: flex-start;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
/* --- BOX STILIZZATI (Glassmorphism) --- */
|
||||
.controls-box, .instructions-box {
|
||||
background: var(--glass-bg);
|
||||
backdrop-filter: blur(12px);
|
||||
-webkit-backdrop-filter: blur(12px);
|
||||
border: 1px solid var(--glass-border);
|
||||
border-top: 4px solid var(--green-bright);
|
||||
padding: 30px;
|
||||
border-radius: 20px;
|
||||
box-shadow: 0 15px 35px rgba(0,0,0,0.6);
|
||||
width: 100%;
|
||||
max-width: 350px;
|
||||
min-height: 200px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.controls-box h3, .instructions-box h3 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 20px;
|
||||
font-size: 1.4rem;
|
||||
text-transform: uppercase;
|
||||
color: var(--green-bright);
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.controls-box p, .instructions-box p {
|
||||
font-size: var(--text-base);
|
||||
line-height: 1.6;
|
||||
margin: 10px 0;
|
||||
text-shadow: 1px 1px 2px rgba(0,0,0,0.5);
|
||||
}
|
||||
|
||||
/* --- ELEMENTI SPECIALI --- */
|
||||
kbd {
|
||||
background: #222;
|
||||
color: var(--green-bright);
|
||||
border: 1px solid var(--green-bright);
|
||||
padding: 3px 10px;
|
||||
border-radius: 5px;
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
font-weight: bold;
|
||||
box-shadow: 0 0 5px var(--green-glow);
|
||||
}
|
||||
|
||||
b, i {
|
||||
color: var(--green-bright);
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
/* --- PULSANTE CENTRALE --- */
|
||||
.item.center {
|
||||
align-items: center; /* Il pulsante centrale lo vogliamo a metà altezza */
|
||||
height: 50%;
|
||||
}
|
||||
|
||||
.pulse-text {
|
||||
display: inline-block;
|
||||
padding: 20px 40px;
|
||||
font-size: 1.5rem;
|
||||
font-weight: 800;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
color: white;
|
||||
border: 2px solid var(--green-bright);
|
||||
border-radius: 50px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
animation: pulse 2s infinite;
|
||||
background: rgba(46, 204, 113, 0.1);
|
||||
}
|
||||
|
||||
.pulse-text:hover {
|
||||
background: var(--green-bright);
|
||||
color: black;
|
||||
box-shadow: 0 0 30px var(--green-glow);
|
||||
transform: scale(1.1);
|
||||
animation-play-state: paused; /* Si ferma quando ci passi sopra */
|
||||
}
|
||||
|
||||
/* --- ANIMAZIONI --- */
|
||||
@keyframes pulse {
|
||||
0% { transform: scale(1); opacity: 1; box-shadow: 0 0 0 0 rgba(46, 204, 113, 0.7); }
|
||||
50% { transform: scale(1.05); opacity: 0.7; box-shadow: 0 0 20px 10px rgba(46, 204, 113, 0); }
|
||||
100% { transform: scale(1); opacity: 1; box-shadow: 0 0 0 0 rgba(46, 204, 113, 0); }
|
||||
}
|
||||
|
||||
/* --- RESPONSIVE VELOCE --- */
|
||||
@media (max-width: 900px) {
|
||||
.bottom-section {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
.item {
|
||||
margin-bottom: 20px;
|
||||
width: 80%;
|
||||
}
|
||||
.top-section h1 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
:root {
|
||||
--green-bright: #2ecc71;
|
||||
--green-glow: #27ae60;
|
||||
--bg-overlay: rgba(0, 0, 0, 0.6);
|
||||
--panel-bg: rgba(0, 0, 0, 0.3);
|
||||
--text-main: 1.2rem;
|
||||
}
|
||||
|
||||
body, html {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
color: white;
|
||||
background: url('../assets/media/img/sfondo_finale.png') no-repeat center center fixed;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
/* Overlay per scurire leggermente lo sfondo e far risaltare il testo */
|
||||
.overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: var(--bg-overlay);
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
/* Layout Principale (Diviso in Sinistra e Destra) */
|
||||
#start {
|
||||
position: relative;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* --- SEZIONE SINISTRA (60% del totale) --- */
|
||||
.left1 {
|
||||
flex: 1.5;
|
||||
display: flex;
|
||||
flex-direction: column; /* Divide in Alto e Basso */
|
||||
border-right: 1px solid rgba(46, 204, 113, 0.2);
|
||||
}
|
||||
|
||||
/* Parte in ALTO a sinistra (Titolo) */
|
||||
.left1 .top {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.left1 .top h1 {
|
||||
font-size: 4.5rem;
|
||||
margin: 0;
|
||||
color: #fff;
|
||||
text-shadow: 0 0 20px var(--green-bright), 0 0 30px var(--green-glow);
|
||||
letter-spacing: 5px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* Parte in BASSO a sinistra (Divisa in Bottoni e Punteggi) */
|
||||
.left1 .bottom {
|
||||
flex: 1;
|
||||
display: flex; /* Divide in Sinistra (left2) e Destra (right2) */
|
||||
padding: 40px;
|
||||
gap: 30px;
|
||||
}
|
||||
|
||||
/* Bottoni (Basso-Sinistra) */
|
||||
.left2 {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
button {
|
||||
width: 280px;
|
||||
padding: 18px;
|
||||
font-size: 1.1rem;
|
||||
font-weight: bold;
|
||||
color: white;
|
||||
background: transparent;
|
||||
border: 2px solid var(--green-bright);
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
text-transform: uppercase;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background: var(--green-bright);
|
||||
color: black;
|
||||
box-shadow: 0 0 25px var(--green-bright);
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
/* Riepilogo Punteggi (Basso-Destra) */
|
||||
.right2 {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: var(--panel-bg);
|
||||
border: 1px solid rgba(46, 204, 113, 0.3);
|
||||
border-radius: 20px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.right2 p {
|
||||
font-size: 1.4rem;
|
||||
line-height: 2;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.right2 b {
|
||||
color: var(--green-bright);
|
||||
font-size: 1.6rem;
|
||||
display: block;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.right2 i {
|
||||
color: #ccc;
|
||||
font-style: normal;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* --- SEZIONE DESTRA (40% del totale - Classifica) --- */
|
||||
.right1 {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 40px 20px;
|
||||
background: rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
.right1 h2 {
|
||||
font-size: 2.5rem;
|
||||
color: var(--green-bright);
|
||||
margin-bottom: 30px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
/* Tabella Classifica */
|
||||
table.container {
|
||||
width: 90%;
|
||||
border-collapse: collapse;
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
table.container th {
|
||||
background: rgba(46, 204, 113, 0.5);
|
||||
padding: 15px;
|
||||
text-align: center;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
table.container td {
|
||||
padding: 12px;
|
||||
text-align: center;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
table.container tr:last-child td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
table.container tr:hover {
|
||||
background: rgba(46, 204, 113, 0.1);
|
||||
}
|
||||
|
||||
/* Supporto per Mobile/Schermi piccoli */
|
||||
@media (max-width: 1000px) {
|
||||
.content {
|
||||
flex-direction: column;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.left1 .bottom {
|
||||
flex-direction: column;
|
||||
}
|
||||
.left1 .top h1 {
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
CREATE TABLE punteggi (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
data_partita DATE NOT NULL,
|
||||
score1 INT NOT NULL, -- rifiuti raccolti (fase 1)
|
||||
score2 INT NOT NULL, -- rifiuti separati correttamente (fase 2)
|
||||
scoreT INT NOT NULL, -- punteggio finale normalizzato 0-10000
|
||||
nome VARCHAR(100) NOT NULL
|
||||
);
|
||||
CREATE TABLE punteggi_test (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
data_partita DATE NOT NULL,
|
||||
score1 INT NOT NULL, -- rifiuti raccolti (fase 1)
|
||||
score2 INT NOT NULL, -- rifiuti separati correttamente (fase 2)
|
||||
scoreT INT NOT NULL, -- punteggio finale normalizzato 0-10000
|
||||
nome VARCHAR(100) NOT NULL
|
||||
);
|
||||
|
||||
-- score1: 0-180 (rifiuti raccolti in 1 minuto)
|
||||
-- score2: 0-score1 (rifiuti separati correttamente)
|
||||
-- scoreT: 0-10000 (punteggio finale normalizzato)
|
||||
|
||||
-- velocita = score1 / 60
|
||||
-- precisione = score2 / score1
|
||||
-- punteggio finale = 10000 * ( (score1 / MAX_SCORE) + (score2 / MAX_SCORE) ) / 2
|
||||
-- MAX_SCORE = 180 (numero massimo possibile di rifiuti raccolti in 1 minuto)
|
||||
|
||||
|
||||
-- DATI DI TEST
|
||||
INSERT INTO punteggi_test (data_partita, score1, score2, scoreT, nome) VALUES
|
||||
('2024-06-01', 150, 120, 7500, 'Simone'),
|
||||
('2024-06-02', 180, 160, 9444, 'Luca'),
|
||||
('2024-06-03', 120, 100, 6111, 'Giulia'),
|
||||
('2024-06-04', 90, 70, 4444, 'Marco'),
|
||||
('2024-06-05', 60, 50, 3055, 'Sara');
|
||||
@@ -1,29 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="it">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Semplice Island FPS</title>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="start">
|
||||
<img src="./img/titolo.png" alt="titolo" />
|
||||
<div class="container">
|
||||
<div class="left">ISTRUZIONI</div>
|
||||
<div class="center">Clicca per giocare</div>
|
||||
<div class="right"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="punti">Rifiuti: <span id="score">0</span></div>
|
||||
<div id="tempo">Tempo: <span id="time">XX:XX</span></div>
|
||||
<script type="importmap">
|
||||
{
|
||||
"imports": {
|
||||
"three": "https://unpkg.com/three@0.160.0/build/three.module.js",
|
||||
"three/addons/": "https://unpkg.com/three@0.160.0/examples/jsm/"
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<script type="module" src="script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,25 @@
|
||||
<head>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans:ital,wght@0,100..900;1,100..900&display=swap" rel="stylesheet">
|
||||
<script>
|
||||
const MAX_SCORE = 180;
|
||||
</script>
|
||||
<?php include 'php/config.php'; ?>
|
||||
</head>
|
||||
<?php
|
||||
$route = [
|
||||
'start' => 'fase1.html',
|
||||
'mid' => 'istruzioniFase2.html',
|
||||
'sep' => 'fase2.html',
|
||||
'end' => 'risultatiFinali.php',
|
||||
'leaderboard' => 'classifica.php',
|
||||
'error' => 'error.html'
|
||||
];
|
||||
$p = $_GET['pagina'] ?? 'start';
|
||||
if (!array_key_exists($p, $route)) {
|
||||
http_response_code(404);
|
||||
$p = 'error';
|
||||
}
|
||||
include __DIR__ . '/pages/' . $route[$p];
|
||||
?>
|
||||
@@ -0,0 +1,407 @@
|
||||
import * as THREE from 'three';
|
||||
import { PointerLockControls } from 'three/addons/controls/PointerLockControls.js';
|
||||
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
|
||||
|
||||
// 0. IMPOSTAZIONE FPS
|
||||
let lastTime = performance.now();
|
||||
let frameCount = 0;
|
||||
let fps = 0;
|
||||
const fpsDisplay = document.getElementById('fps-counter');
|
||||
|
||||
// 1a. INIZIALIZZAZIONE CARICAMENTO ASSETS
|
||||
const manager = new THREE.LoadingManager();
|
||||
manager.onStart = function (url, itemsLoaded, itemsTotal) {
|
||||
// Testo iniziale
|
||||
document.getElementById("loading-text").innerHTML = "IDENTIFICAZIONE RISORSE...";
|
||||
};
|
||||
manager.onLoad = function () {
|
||||
const loadingText = document.getElementById("loading-text");
|
||||
// Testo in italiano, tecnico e pulito
|
||||
loadingText.innerHTML = "CARICAMENTO COMPLETATO CON SUCCESSO";
|
||||
// Estetica finale: testo fisso e brillante
|
||||
loadingText.style.animation = "none";
|
||||
loadingText.style.color = "#ffffff";
|
||||
loadingText.style.textShadow = "0 0 15px var(--green-bright)";
|
||||
loadingText.style.opacity = "1";
|
||||
// Timeout per lasciare all'utente il tempo di leggere il successo
|
||||
setTimeout(() => {
|
||||
const loadingScreen = document.getElementById('loading-screen');
|
||||
loadingScreen.style.opacity = '0';
|
||||
loadingScreen.style.transition = 'opacity 0.8s ease';
|
||||
setTimeout(() => {
|
||||
loadingScreen.style.display = 'none';
|
||||
// MOSTRA IL POPUP DEL NOME
|
||||
document.getElementById('name-popup-overlay').style.display = 'flex';
|
||||
}, 800);
|
||||
}, 500);
|
||||
animate(); // Parte il loop di Three.js
|
||||
};
|
||||
manager.onProgress = function (url, itemsLoaded, itemsTotal) {
|
||||
const percent = Math.round((itemsLoaded / itemsTotal) * 100);
|
||||
// Estraiamo solo il nome del file dall'URL per pulizia
|
||||
// Esempio: "assets/models/bottiglia.glb" diventa "bottiglia.glb"
|
||||
const fileName = url.split('/').pop().toUpperCase();
|
||||
// Aggiorniamo la barra
|
||||
const bar = document.getElementById('loading-bar');
|
||||
if (bar) bar.style.width = percent + '%';
|
||||
// Nuovo formato del testo: NOME_FILE [XX%]
|
||||
document.getElementById("loading-text").innerHTML = `${fileName} [${percent}%]`;
|
||||
};
|
||||
manager.onError = function (url) {
|
||||
document.getElementById("loading-text").innerHTML = `ERRORE CARICAMENTO: ${url.split('/').pop()}`;
|
||||
document.getElementById("loading-text").style.color = "#ff4d4d";
|
||||
};
|
||||
// GESTIONE CONFERMA NOME
|
||||
document.getElementById('confirm-name-btn').addEventListener('click', function() {
|
||||
const input = document.getElementById('username-input');
|
||||
const name = input.value.trim();
|
||||
localStorage.setItem("username", name);
|
||||
// 1. Nascondi il Popup
|
||||
const popup = document.getElementById('name-popup-overlay');
|
||||
popup.style.opacity = '0';
|
||||
popup.style.transition = 'opacity 0.5s ease';
|
||||
|
||||
setTimeout(() => {
|
||||
popup.style.display = 'none';
|
||||
|
||||
// 2. MOSTRA LA SCHERMATA INIZIALE (Controlli e Titolo)
|
||||
const mainContent = document.getElementById('main-start-content');
|
||||
mainContent.style.display = 'flex';
|
||||
// Piccolo trucco per l'animazione di entrata (fade in)
|
||||
setTimeout(() => {
|
||||
mainContent.style.opacity = '1';
|
||||
mainContent.style.transition = 'opacity 1s ease';
|
||||
}, 50);
|
||||
|
||||
}, 500);
|
||||
});
|
||||
|
||||
// 1b. SCENA E CAMERA
|
||||
const scene = new THREE.Scene();
|
||||
scene.background = new THREE.Color(0x33ccff); // Cielo più azzurro
|
||||
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
|
||||
const renderer = new THREE.WebGLRenderer();
|
||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||
document.body.appendChild(renderer.domElement);
|
||||
window.addEventListener('resize', function () {
|
||||
renderer.setSize(window.innerWidth, window.innerHeight)
|
||||
})
|
||||
// AGGIUNTA MARE
|
||||
const oceanGeometry = new THREE.PlaneGeometry(2000, 2000);
|
||||
const oceanMaterial = new THREE.MeshStandardMaterial({
|
||||
color: 0x0077be,
|
||||
transparent: true,
|
||||
opacity: 0.8
|
||||
});
|
||||
const ocean = new THREE.Mesh(oceanGeometry, oceanMaterial);
|
||||
ocean.rotation.x = -Math.PI / 2;
|
||||
ocean.position.y = -0.2; // Leggermente sotto l'isola
|
||||
scene.add(ocean);
|
||||
|
||||
// 2. LUCI E AMBIENTE
|
||||
scene.add(new THREE.AmbientLight(0xffffff, 0.8));
|
||||
const sunLight = new THREE.DirectionalLight(0xffffff, 1);
|
||||
sunLight.position.set(10, 20, 10);
|
||||
scene.add(sunLight);
|
||||
|
||||
const islandGroup = new THREE.Group();
|
||||
|
||||
const altezzaIsola = 0.5; // Quanto sta il prato sopra il livello del mare (0.0)
|
||||
const altezzaOcchi = 1.6; // L'altezza standard della telecamera
|
||||
|
||||
// --- IL FONDALE (Sabbia scura sul fondo) ---
|
||||
const floorGeo = new THREE.PlaneGeometry(1000, 1000);
|
||||
const floorMat = new THREE.MeshStandardMaterial({ color: 0x1a1a1a }); // Grigio scuro/Fondale
|
||||
const floor = new THREE.Mesh(floorGeo, floorMat);
|
||||
floor.rotation.x = -Math.PI / 2;
|
||||
floor.position.y = -3; // 5 metri sotto il livello del mare
|
||||
scene.add(floor);
|
||||
|
||||
// --- IL PRATO ---
|
||||
const raggioPrato = 19;
|
||||
const grassGeo = new THREE.CircleGeometry(raggioPrato, 64);
|
||||
const grassMat = new THREE.MeshStandardMaterial({ color: 0x4df556 });
|
||||
const grass = new THREE.Mesh(grassGeo, grassMat);
|
||||
grass.rotation.x = -Math.PI / 2;
|
||||
|
||||
// Posizioniamo il prato all'altezza desiderata
|
||||
grass.position.y = altezzaIsola;
|
||||
islandGroup.add(grass);
|
||||
|
||||
// --- LA SABBIA (Cilindro Svasato) ---
|
||||
const altezzaSabbia = 1.5; // Altezza totale del blocco sabbia
|
||||
const sandGeo = new THREE.CylinderGeometry(raggioPrato, 22 + 10, altezzaSabbia + 3, 64, 1, true);
|
||||
const sandMat = new THREE.MeshStandardMaterial({ color: 0xedc9af, side: THREE.DoubleSide });
|
||||
const sand = new THREE.Mesh(sandGeo, sandMat);
|
||||
|
||||
// Per far combaciare la cima del cilindro con il prato:
|
||||
// L'altezza è 1.5, il centro del cilindro deve stare a (AltezzaIsola - metà altezza cilindro)
|
||||
sand.position.y = altezzaIsola - (altezzaSabbia / 2) - 1.5;
|
||||
islandGroup.add(sand);
|
||||
|
||||
scene.add(islandGroup);
|
||||
scene.fog = new THREE.Fog(0x33ccff, 20, 100);
|
||||
|
||||
// 3. CONTROLLI
|
||||
window.totalTime;
|
||||
class PausableTimer {
|
||||
constructor(callback, delay) {
|
||||
this.callback = callback;
|
||||
this.delay = delay; // salvi il delay originale
|
||||
this.remaining = delay;
|
||||
this.timerId = null;
|
||||
this.start = null;
|
||||
this.loop = null;
|
||||
this.cont = 0;
|
||||
window.totalTime = delay / 1000;
|
||||
}
|
||||
pause() {
|
||||
if (this.timerId) {
|
||||
clearTimeout(this.timerId);
|
||||
this.timerId = null;
|
||||
// Calcola il tempo passato
|
||||
this.remaining -= Date.now() - this.start;
|
||||
}
|
||||
if (this.loop) {
|
||||
clearInterval(this.loop);
|
||||
this.loop = null;
|
||||
}
|
||||
console.log("pausa");
|
||||
}
|
||||
resume() {
|
||||
console.log("resume");
|
||||
if (this.timerId) return; // già in esecuzione
|
||||
if (this.remaining > 0) {
|
||||
this.start = Date.now();
|
||||
this.timerId = setTimeout(() => {
|
||||
this.pause();
|
||||
this.callback();
|
||||
}, this.remaining);
|
||||
|
||||
// Se il ciclo di aggiornamento non è già attivo, avvialo
|
||||
if (!this.loop) {
|
||||
this.loop = setInterval(() => {
|
||||
this.cont++;
|
||||
// Aggiorna il timer visualizzato
|
||||
document.getElementById("minuti").innerHTML = String(Math.floor((window.totalTime - this.cont) / 60)).padStart(2, '0');
|
||||
document.getElementById("secondi").innerHTML = String((window.totalTime - this.cont) % 60).padStart(2, '0');
|
||||
console.log(`${document.getElementById("minuti").innerHTML}:${document.getElementById("secondi").innerHTML}`);
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const timer = new PausableTimer(() => {
|
||||
localStorage.setItem("punteggioFase1", document.getElementById("score").innerHTML.trim());
|
||||
window.location = "?pagina=mid";
|
||||
}, 60000)
|
||||
function lock() {
|
||||
document.getElementById('start').style.display = 'none';
|
||||
document.getElementById('punti').style.display = 'block';
|
||||
document.getElementById('tempo').style.display = 'block';
|
||||
document.getElementById('crosshair').style.display = 'block';
|
||||
document.getElementById('fps-counter').style.display = 'block';
|
||||
}
|
||||
function unlock() {
|
||||
timer.pause();
|
||||
document.getElementById('start').style.display = 'flex';
|
||||
document.getElementById('punti').style.display = 'none';
|
||||
document.getElementById('tempo').style.display = 'none';
|
||||
document.getElementById('crosshair').style.display = 'none';
|
||||
document.getElementById('fps-counter').style.display = 'none';
|
||||
}
|
||||
const controls = new PointerLockControls(camera, document.body);
|
||||
document.getElementById('start').addEventListener('click', () => {
|
||||
controls.lock();
|
||||
lock();
|
||||
});
|
||||
controls.addEventListener('lock', () => {
|
||||
lock();
|
||||
timer.resume();
|
||||
});
|
||||
controls.addEventListener('unlock', () => { unlock(); });
|
||||
|
||||
// 4. GESTIONE OGGETTI DI SCENA (20 rifiuti + 6 alberi)
|
||||
const trashArray = [];
|
||||
let score = 0;
|
||||
const loader = new GLTFLoader(manager);
|
||||
|
||||
/*loader.load('assets/models/rifiuto.glb', (gltf) => {
|
||||
const mesh = gltf.scene.getObjectByName("Trash_Pile_03_GEO");
|
||||
mesh.geometry.center(); // Centra l'oggetto per collisioni precise
|
||||
for (let i = 0; i < 20; i++) {
|
||||
const clone = mesh.clone();
|
||||
scene.add(clone);
|
||||
trashArray.push(clone);
|
||||
spawn(clone, 0.2);
|
||||
}
|
||||
});*/
|
||||
loader.load('assets/models/trashpile-v1.glb', (gltf) => {
|
||||
const mesh = gltf.scene;
|
||||
for (let i = 0; i < 20; i++) {
|
||||
const clone = mesh.clone();
|
||||
scene.add(clone);
|
||||
trashArray.push(clone);
|
||||
spawn(clone, 0.2);
|
||||
}
|
||||
});
|
||||
|
||||
loader.load('assets/models/tree.glb', (gltf) => {
|
||||
const mesh = gltf.scene;
|
||||
mesh.scale.set(4, 4, 4);
|
||||
console.log(mesh);
|
||||
|
||||
function spawn(obj, radius, minDistance) {
|
||||
const points = [];
|
||||
const attempts = 1000; // Numero massimo di tentativi per trovare un punto valido
|
||||
for (let i = 0; i < attempts; i++) {
|
||||
let angle = Math.random() * 2 * Math.PI;
|
||||
let dist = Math.random() * radius;
|
||||
let x = dist * Math.cos(angle);
|
||||
let z = dist * Math.sin(angle);
|
||||
let valid = true;
|
||||
for (let j = 0; j < points.length; j++) {
|
||||
const dx = x - points[j].x;
|
||||
const dz = z - points[j].z;
|
||||
const distance = Math.sqrt(dx * dx + dz * dz);
|
||||
if (distance < minDistance) {
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (valid) {
|
||||
points.push({ x: x, z: z });
|
||||
}
|
||||
}
|
||||
|
||||
// Spawna gli alberi nei punti generati
|
||||
for (let i = 0; i < points.length; i++) {
|
||||
const clone = obj.clone();
|
||||
const a = Math.random() * Math.PI * 2;
|
||||
const r = Math.random() * 18;
|
||||
const x = Math.cos(a) * r;
|
||||
const z = Math.sin(a) * r;
|
||||
// Y deve essere altezzaIsola + un piccolo offset
|
||||
clone.position.set(points[i].x, altezzaIsola - 0.1, points[i].z);
|
||||
clone.rotation.y = Math.random() * Math.PI * 2; // Rotazione tra 0 e 360 gradi
|
||||
scene.add(clone);
|
||||
}
|
||||
}
|
||||
spawn(mesh, 18, 8); // Raggio 18, distanza minima 5
|
||||
});
|
||||
console.log(scene)
|
||||
|
||||
function spawn(obj) {
|
||||
const a = Math.random() * Math.PI * 2;
|
||||
const r = Math.random() * 18;
|
||||
const x = Math.cos(a) * r;
|
||||
const z = Math.sin(a) * r;
|
||||
// Y deve essere altezzaIsola + un piccolo offset
|
||||
obj.position.set(x, altezzaIsola + 0.1, z);
|
||||
}
|
||||
|
||||
// 5. MOVIMENTO E COLLISIONI
|
||||
const arrow = false;
|
||||
const keys = {};
|
||||
document.onkeydown = (e) => keys[e.code] = true;
|
||||
document.onkeyup = (e) => keys[e.code] = false;
|
||||
|
||||
// Setup Raycaster (fuori dal loop animate)
|
||||
const raycaster = new THREE.Raycaster();
|
||||
const downVector = new THREE.Vector3(0, -1, 0);
|
||||
|
||||
// --- CONFIGURAZIONE MOVIMENTO ---
|
||||
let canLeaveIsland = false; // Se false, l'acqua blocca il movimento.
|
||||
let lastSafePosition = camera.position.clone();
|
||||
|
||||
function animate() {
|
||||
requestAnimationFrame(animate);
|
||||
// --- Calcolo FPS ---
|
||||
frameCount++;
|
||||
const currentTime = performance.now();
|
||||
// Ogni secondo (1000ms) aggiorniamo il contatore visivo
|
||||
if (currentTime >= lastTime + 1000) {
|
||||
fps = frameCount;
|
||||
fpsDisplay.innerText = `FPS: ${fps}`;
|
||||
// Colore dinamico: verde se fluido, giallo/rosso se lagga
|
||||
if (fps < 30) fpsDisplay.style.color = "#ff4d4d";
|
||||
else if (fps < 55) fpsDisplay.style.color = "#f1c40f";
|
||||
else fpsDisplay.style.color = "var(--green-bright)";
|
||||
|
||||
frameCount = 0;
|
||||
lastTime = currentTime;
|
||||
}
|
||||
|
||||
if (controls.isLocked) {
|
||||
// 1. Salva la posizione attuale prima del movimento
|
||||
const oldPos = camera.position.clone();
|
||||
|
||||
// 2. Esegui il movimento WASD
|
||||
if (arrow) {
|
||||
if (keys['ArrowUp']) controls.moveForward(0.15);
|
||||
if (keys['ArrowDown']) controls.moveForward(-0.15);
|
||||
if (keys['ArrowLeft']) controls.moveRight(-0.15);
|
||||
if (keys['ArrowRight']) controls.moveRight(0.15);
|
||||
} else {
|
||||
if (keys['KeyW']) controls.moveForward(0.15);
|
||||
if (keys['KeyS']) controls.moveForward(-0.15);
|
||||
if (keys['KeyA']) controls.moveRight(-0.15);
|
||||
if (keys['KeyD']) controls.moveRight(0.15);
|
||||
}
|
||||
|
||||
// 3. --- GESTIONE ALTEZZA DINAMICA ---
|
||||
const rayOrigin = camera.position.clone();
|
||||
rayOrigin.y = 10; // Spara dall'alto verso il basso
|
||||
raycaster.set(rayOrigin, downVector);
|
||||
|
||||
// Controlliamo Prato e Sabbia
|
||||
const intersects = raycaster.intersectObjects([grass, sand]);
|
||||
|
||||
if (intersects.length > 0) {
|
||||
// Logica del "Punto più alto":
|
||||
// Se il raggio colpisce sia prato che sabbia, prendiamo il prato.
|
||||
let highestPoint = -Infinity;
|
||||
for (let i = 0; i < intersects.length; i++) {
|
||||
if (intersects[i].point.y > highestPoint) {
|
||||
highestPoint = intersects[i].point.y;
|
||||
}
|
||||
}
|
||||
|
||||
// Applichiamo l'altezza alla camera
|
||||
camera.position.y = highestPoint + altezzaOcchi;
|
||||
|
||||
// --- CONTROLLO BARRIERA ACQUA ---
|
||||
// Se il punto più alto è sotto il livello del mare (0)
|
||||
// e non possiamo uscire, blocchiamo il movimento
|
||||
if (!canLeaveIsland && highestPoint < 0) {
|
||||
camera.position.x = oldPos.x;
|
||||
camera.position.z = oldPos.z;
|
||||
camera.position.y = oldPos.y;
|
||||
} else {
|
||||
// Posizione valida, la salviamo
|
||||
lastSafePosition.copy(camera.position);
|
||||
camera.position.y = camera.position.y > (-2.25 + altezzaOcchi) ? camera.position.y : (-2.25 + altezzaOcchi);
|
||||
}
|
||||
} else {
|
||||
// --- FUORI DALL'ISOLA (VUOTO) ---
|
||||
if (!canLeaveIsland) {
|
||||
camera.position.copy(oldPos);
|
||||
} else {
|
||||
camera.position.y = -2.25 + altezzaOcchi; // Volo sull'acqua
|
||||
}
|
||||
}
|
||||
|
||||
// --- COLLISIONI RIFIUTI ---
|
||||
trashArray.forEach(t => {
|
||||
if (camera.position.distanceTo(t.position) < 1.8) {
|
||||
score++;
|
||||
document.getElementById('score').innerText = score;
|
||||
spawn(t);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
renderer.render(scene, camera);
|
||||
}
|
||||
document.getElementById("minuti").innerHTML = String(Math.floor(window.totalTime / 60)).padStart(2, '0');
|
||||
document.getElementById("secondi").innerHTML = String(window.totalTime % 60).padStart(2, '0');
|
||||
@@ -0,0 +1,274 @@
|
||||
// PASSO 1: Lista di tutti i rifiuti con la loro categoria
|
||||
const listaRifiuti = [
|
||||
{ img: 'assets/media/img/fase2/rifiuti/bicchiereEbottiglia.webp', tipo: 'vetro' },
|
||||
{ img: 'assets/media/img/fase2/rifiuti/bottiglie.webp', tipo: 'plastica' },
|
||||
{ img: 'assets/media/img/fase2/rifiuti/bucciaBanana.png', tipo: 'umido' },
|
||||
{ img: 'assets/media/img/fase2/rifiuti/torsoloMela.webp', tipo: 'umido' },
|
||||
{ img: 'assets/media/img/fase2/rifiuti/giornale.png', tipo: 'carta' },
|
||||
{ img: 'assets/media/img/fase2/rifiuti/scatolaCartone.png', tipo: 'carta' },
|
||||
{ img: 'assets/media/img/fase2/rifiuti/lattina.png', tipo: 'alluminio' },
|
||||
{ img: 'assets/media/img/fase2/rifiuti/scatolaTonno.webp', tipo: 'alluminio' },
|
||||
{ img: 'assets/media/img/fase2/rifiuti/sacchettoPlastica.png', tipo: 'plastica' },
|
||||
{ img: 'assets/media/img/fase2/rifiuti/marmellata.png', tipo: 'vetro' },
|
||||
{ img: 'assets/media/img/fase2/rifiuti/mozziconiSigaretta.png', tipo: 'indifferenziata' },
|
||||
{ img: 'assets/media/img/fase2/rifiuti/pannolino.png', tipo: 'indifferenziata' }
|
||||
];
|
||||
|
||||
let rifiutiTotali = 0;
|
||||
const counter = document.getElementById('counter');
|
||||
const scoreDisplay = document.getElementById('score');
|
||||
|
||||
// PASSO 2:Funzione per creare un rifiuto casuale sul bancone
|
||||
function generaRifiuto() {
|
||||
const indiceRandom = Math.floor(Math.random() * listaRifiuti.length);
|
||||
const rifiutoScelto = listaRifiuti[indiceRandom];
|
||||
|
||||
const imgElement = document.createElement('img');
|
||||
imgElement.src = rifiutoScelto.img;
|
||||
imgElement.classList.add('trash');
|
||||
imgElement.dataset.tipo = rifiutoScelto.tipo;
|
||||
|
||||
counter.appendChild(imgElement);
|
||||
attivaDragAndDrop(imgElement);
|
||||
}
|
||||
// Avviamo la prima generazione
|
||||
window.onload = () => {
|
||||
generaRifiuto();
|
||||
// Qui faremo partire anche il timer in futuro
|
||||
};
|
||||
|
||||
|
||||
//PASSO 3: Funzione per rendere un elemento trascinabile
|
||||
function attivaDragAndDrop(elemento) {
|
||||
let staTrascinando = false;
|
||||
|
||||
elemento.addEventListener('mousedown', (e) => {
|
||||
staTrascinando = true;
|
||||
|
||||
elemento.style.mixBlendMode = "normal";
|
||||
|
||||
// 1. IMPORTANTE: Spostiamo l'elemento dal "counter" al "game-container"
|
||||
// Questo evita che l'oggetto scompaia per conflitti di posizionamento
|
||||
const container = document.getElementById('game-container');
|
||||
if (elemento.parentElement.id === 'counter') {
|
||||
container.appendChild(elemento);
|
||||
}
|
||||
|
||||
elemento.style.position = 'absolute';
|
||||
elemento.style.zIndex = 1000;
|
||||
elemento.style.cursor = 'grabbing';
|
||||
|
||||
// Chiamiamo subito la funzione di movimento per posizionarlo sotto il mouse
|
||||
muoviOggetto(e);
|
||||
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
function muoviOggetto(e) {
|
||||
if (!staTrascinando) return;
|
||||
|
||||
const container = document.getElementById('game-container');
|
||||
const rect = container.getBoundingClientRect();
|
||||
|
||||
// Calcolo posizione
|
||||
let x = e.clientX - rect.left - (elemento.offsetWidth / 2);
|
||||
let y = e.clientY - rect.top - (elemento.offsetHeight / 2);
|
||||
|
||||
// Applichiamo le coordinate e resettiamo gli stili del bancone
|
||||
elemento.style.left = x + 'px';
|
||||
elemento.style.top = y + 'px';
|
||||
elemento.style.bottom = 'auto';
|
||||
elemento.style.transform = 'none';
|
||||
}
|
||||
|
||||
document.addEventListener('mousemove', (e) => {
|
||||
if (staTrascinando) {
|
||||
muoviOggetto(e);
|
||||
evidenziaBidoneSotto(elemento);
|
||||
}
|
||||
});
|
||||
|
||||
function evidenziaBidoneSotto(rifiuto) {
|
||||
const bidoni = document.querySelectorAll('.bin');
|
||||
|
||||
bidoni.forEach(bidone => {
|
||||
const rectB = bidone.getBoundingClientRect();
|
||||
const rectR = rifiuto.getBoundingClientRect();
|
||||
|
||||
const sovrapposti = !(rectR.right < rectB.left ||
|
||||
rectR.left > rectB.right ||
|
||||
rectR.bottom < rectB.top ||
|
||||
rectR.top > rectB.bottom);
|
||||
|
||||
if (sovrapposti) {
|
||||
bidone.classList.add('highlight');
|
||||
} else {
|
||||
bidone.classList.remove('highlight');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
document.addEventListener('mouseup', () => {
|
||||
if (staTrascinando) {
|
||||
staTrascinando = false;
|
||||
elemento.style.cursor = 'grab';
|
||||
|
||||
// Ora puoi chiamare la funzione del Punto 4
|
||||
controllaCollisione(elemento);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
//PUNTO 4: Funzione per controllare se il rifiuto è stato depositato correttamente
|
||||
function controllaCollisione(rifiuto) {
|
||||
const bidoni = document.querySelectorAll('.bin');
|
||||
let depositato = false;
|
||||
|
||||
bidoni.forEach(bidone => {
|
||||
const rectB = bidone.getBoundingClientRect();
|
||||
const rectR = rifiuto.getBoundingClientRect();
|
||||
|
||||
const sovrapposti = !(rectR.right < rectB.left ||
|
||||
rectR.left > rectB.right ||
|
||||
rectR.bottom < rectB.top ||
|
||||
rectR.top > rectB.bottom);
|
||||
|
||||
if (sovrapposti) {
|
||||
depositato = true;
|
||||
|
||||
// PORTIAMO IL BIDONE IN PRIMO PIANO
|
||||
bidone.style.zIndex = "2000";
|
||||
|
||||
rifiutiTotali++;
|
||||
if (rifiuto.dataset.tipo === bidone.dataset.type) {
|
||||
// CORRETTO
|
||||
punteggio += 1;
|
||||
bidone.classList.add('glow-success');
|
||||
bidone.style.transform = "scale(1.1)";
|
||||
|
||||
setTimeout(() => {
|
||||
bidone.classList.remove('glow-success');
|
||||
bidone.style.transform = "scale(1)";
|
||||
bidone.style.zIndex = "1"; // Ritorna al suo posto dopo l'effetto
|
||||
}, 400);
|
||||
} else {
|
||||
// SBAGLIATO
|
||||
punteggio = punteggio;
|
||||
bidone.classList.add('glow-error');
|
||||
|
||||
setTimeout(() => {
|
||||
bidone.classList.remove('glow-error');
|
||||
bidone.style.zIndex = "1"; // Ritorna al suo posto dopo l'effetto
|
||||
}, 400);
|
||||
}
|
||||
if (rifiutiTotali >= localStorage.getItem('punteggioFase1')) {
|
||||
mostraFineGioco();
|
||||
}
|
||||
document.getElementById('score').innerText = punteggio;
|
||||
rifiuto.remove();
|
||||
setTimeout(generaRifiuto, 500);
|
||||
}
|
||||
});
|
||||
|
||||
if (!depositato) {
|
||||
riposizionaSulBancone(rifiuto);
|
||||
}
|
||||
}
|
||||
|
||||
// Usiamo nomi diversi per non confondere il browser
|
||||
let timerIntervallo;
|
||||
let tempoRimanente;
|
||||
|
||||
function avviaTimer(secondiIniziali) {
|
||||
const minuti = document.getElementById('minuti');
|
||||
const secondi = document.getElementById('secondi');
|
||||
|
||||
// Inizializziamo la variabile globale con il valore ricevuto
|
||||
tempoRimanente = secondiIniziali;
|
||||
minuti.innerText = String(Math.floor(tempoRimanente / 60)).padStart(2, '0');
|
||||
secondi.innerText = String(tempoRimanente % 60).padStart(2, '0');
|
||||
|
||||
// Puliamo timer precedenti
|
||||
clearInterval(timerIntervallo);
|
||||
|
||||
timerIntervallo = setInterval(() => {
|
||||
tempoRimanente--; // Sottraiamo un secondo alla variabile globale
|
||||
minuti.innerText = String(Math.floor(tempoRimanente / 60)).padStart(2, '0');
|
||||
secondi.innerText = String(tempoRimanente % 60).padStart(2, '0');
|
||||
|
||||
if (tempoRimanente <= 0) {
|
||||
clearInterval(timerIntervallo);
|
||||
tempoRimanente = 0;
|
||||
minuti.innerText = "00";
|
||||
secondi.innerText = "00";
|
||||
mostraFineGioco();//LISA E' COMPITO TUO!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!1
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
window.onload = () => {
|
||||
punteggio = 0;
|
||||
scoreDisplay.innerText = punteggio;
|
||||
|
||||
|
||||
// Recuperiamo il valore dal localStorage (se vuoto mettiamo 10 come base)
|
||||
let puntiSalvati = localStorage.getItem('punteggioFase1');
|
||||
let tempoDaImpostare = puntiSalvati * 5;
|
||||
|
||||
generaRifiuto();
|
||||
|
||||
// Facciamo partire il timer con il valore calcolato
|
||||
avviaTimer(tempoDaImpostare);
|
||||
};
|
||||
|
||||
function riposizionaSulBancone(rifiuto) {
|
||||
const counter = document.getElementById('counter');
|
||||
counter.appendChild(rifiuto);
|
||||
|
||||
// Ripristiniamo l'effetto per ignorare lo sfondo nero sul bancone marrone
|
||||
rifiuto.style.mixBlendMode = "screen";
|
||||
|
||||
// Resettiamo le posizioni per rimetterlo al centro del bancone
|
||||
rifiuto.style.position = 'relative';
|
||||
rifiuto.style.left = "auto";
|
||||
rifiuto.style.top = "auto";
|
||||
rifiuto.style.bottom = "auto";
|
||||
rifiuto.style.transform = "none";
|
||||
}
|
||||
|
||||
function mostraFineGioco() {
|
||||
// Blocca la generazione di nuovi rifiuti
|
||||
clearInterval(timerIntervallo);
|
||||
// Salva il punteggio nel localStorage
|
||||
localStorage.setItem('punteggioFase2', punteggio);
|
||||
const username = localStorage.getItem('username') || 'Anonimo';
|
||||
const score1 = parseInt(localStorage.getItem('punteggioFase1')) || 0;
|
||||
const score2 = parseInt(localStorage.getItem('punteggioFase2')) || 0;
|
||||
const scoreT = 10_000 * ((score1 / MAX_SCORE) + (score2 / MAX_SCORE)) / 2;
|
||||
const data_partita = new Date().toISOString().split('T')[0]; // Formato YYYY-MM-DD
|
||||
fetch(`${SERVER_URL}/php/save_score.php`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
},
|
||||
body: new URLSearchParams({
|
||||
nome: username,
|
||||
score1: score1,
|
||||
score2: score2,
|
||||
scoreT: scoreT,
|
||||
data_partita: data_partita
|
||||
})
|
||||
})
|
||||
.then(response => response.text())
|
||||
.then(result => {
|
||||
console.log("Punteggio salvato con successo:", result);
|
||||
window.location = "?pagina=end";
|
||||
})
|
||||
.catch(error => {
|
||||
console.error("Errore nella richiesta:", error);
|
||||
window.location = "?pagina=end";
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
const canvas = document.getElementById('matrix-canvas');
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
canvas.width = window.innerWidth;
|
||||
canvas.height = window.innerHeight;
|
||||
|
||||
const symbols = '♻☘01RECYCLE-EARTH-SAVE-FUTURE';
|
||||
const fontSize = 16;
|
||||
const columns = canvas.width / fontSize;
|
||||
const drops = Array(Math.floor(columns)).fill(1);
|
||||
|
||||
function drawMatrix() {
|
||||
// Sfondo semi-trasparente per creare l'effetto scia
|
||||
ctx.fillStyle = 'rgba(0, 0, 0, 0.05)';
|
||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
ctx.fillStyle = '#2ecc71'; // Il tuo verde brillante
|
||||
ctx.font = fontSize + 'px monospace';
|
||||
|
||||
for (let i = 0; i < drops.length; i++) {
|
||||
const text = symbols.charAt(Math.floor(Math.random() * symbols.length));
|
||||
ctx.fillText(text, i * fontSize, drops[i] * fontSize);
|
||||
|
||||
if (drops[i] * fontSize > canvas.height && Math.random() > 0.975) {
|
||||
drops[i] = 0;
|
||||
}
|
||||
drops[i]++;
|
||||
}
|
||||
}
|
||||
|
||||
// Avvia l'animazione
|
||||
setInterval(drawMatrix, 35);
|
||||
|
||||
// Adatta se l'utente ridimensiona la finestra
|
||||
window.addEventListener('resize', () => {
|
||||
canvas.width = window.innerWidth;
|
||||
canvas.height = window.innerHeight;
|
||||
});
|
||||
@@ -0,0 +1,103 @@
|
||||
Struttura della cartella del progetto: (temporanea e mmodificabile)
|
||||
```text
|
||||
.
|
||||
├── assets/
|
||||
│ ├── media/
|
||||
│ │ ├── img/
|
||||
│ │ │ ├── titolo.png
|
||||
│ │ │ └── fase2/
|
||||
│ │ │ ├── contenitori/
|
||||
│ │ │ │ ├── alluminio.jpeg
|
||||
│ │ │ │ ├── carta.jpeg
|
||||
│ │ │ │ ├── indifferenziata.jpeg
|
||||
│ │ │ │ ├── plastica.jpeg
|
||||
│ │ │ │ ├── umido.jpeg
|
||||
│ │ │ │ └── vetro.jpeg
|
||||
│ │ │ └── rifiuti/
|
||||
│ │ │ ├── bicchiereEbottiglia.webp
|
||||
│ │ │ ├── bottiglie.webp
|
||||
│ │ │ ├── bucciaBanana.png
|
||||
│ │ │ ├── giornale.png
|
||||
│ │ │ ├── lattina.png
|
||||
│ │ │ ├── marmellata.png
|
||||
│ │ │ ├── mozziconiSigaretta.png
|
||||
│ │ │ ├── pannolino.png
|
||||
│ │ │ ├── sacchettoPlastica.png
|
||||
│ │ │ ├── scatolaCartone.png
|
||||
│ │ │ ├── scatolaTonno.webp
|
||||
│ │ │ └── torsoloMela.webp
|
||||
│ │ └── video/
|
||||
│ │ └── background.mp4
|
||||
│ ├── models/
|
||||
│ │ ├── rifiuto.glb
|
||||
│ │ └── tree.glb
|
||||
│ └── texture/
|
||||
│ └── rifiuto/
|
||||
│ ├── Trash_AlbedoTransparency.png
|
||||
│ ├── Trash_Ambient_Occlusion.png
|
||||
│ ├── Trash_MetallicSmoothness.png
|
||||
│ ├── Trash_Normal.png
|
||||
│ └── Trash_Roughness.png
|
||||
├── css/
|
||||
│ ├── classifica.css
|
||||
│ ├── fase1.css
|
||||
│ ├── fase2.css
|
||||
│ ├── istruzioniFase2.css
|
||||
│ └── risultatiFinali.css
|
||||
├── js/
|
||||
│ ├── fase1.js
|
||||
│ └── fase2.js
|
||||
├── materiale/
|
||||
│ ├── idea gioco.drawio
|
||||
│ ├── idea gioco.png
|
||||
│ ├── ideaRisultato.jpeg
|
||||
│ ├── progetto.txt
|
||||
│ ├── schermata finale.png
|
||||
│ ├── schermata iniziale.png
|
||||
│ ├── STRUTTURA.md
|
||||
│ └── TODO.txt
|
||||
├── pages/
|
||||
│ ├── classifica.php
|
||||
│ ├── error.html
|
||||
│ ├── fase1.html
|
||||
│ ├── fase2.html
|
||||
│ ├── istruzioniFase2.html
|
||||
│ └── risultatiFinali.html
|
||||
├── php/
|
||||
│ ├── db.php
|
||||
│ ├── leaderboard_utils.php
|
||||
│ └── save_score.php
|
||||
├── .gitignore
|
||||
├── db_iniziale.sql
|
||||
├── index.php
|
||||
└── README.md
|
||||
```
|
||||
|
||||
Workflow del gioco:
|
||||
```
|
||||
Inizio -> 60s per raccogliere i rifiuti -> Salvataggio del punteggio in localStorage -> Schermata intermedia -> <punteggio>*5s per separare i rifiuti -> Invio dati della partita al database (nome squadra, rifiuti raccolti nella fase 1, rifiuti separati correttamente nella fase 2, punteggio finale, data della partita) -> caricamento schermata finale con classifica ridotta (posizione, squadra, punteggio) e pulsanti per giocare ancora, impostazioni e la classifica completa (classifica.php) con tutti i dati e tutte le partite giocate (classifica ridotta: solo le prime 5/10 partite)
|
||||
```
|
||||
```
|
||||
start -> mid -> sep -> end
|
||||
```
|
||||
|
||||
Indirizzamento pagine in index.php
|
||||
```
|
||||
index.php?p=xxx
|
||||
index.php?p=start -> include 'fase1.html' (schermata iniziale + prima fase)
|
||||
index.php?p=mid -> include 'istruzioniFase2.html' (istruzioni per la seconda fase con pulsante per iniziare)
|
||||
index.php?p=sep -> include 'fase2.html' (separazione dei rifiuti + reindirizzamento a schermata finale)
|
||||
index.php?p=end -> include 'risultatiFinali.html' (schermata finale + record assoluto (nome squadra, punteggio finale, data) + classifica ridotta + pulsante 'gioca ancora' (-> index.php?p=start) + pulsante per classifica completa (-> _blank::index.php?p=leaderboard))
|
||||
index.php?p=leaderboard -> classifica globale dei risultati con visualizzazione completa dei dati
|
||||
```
|
||||
|
||||
Array associativo con indirizzamenti da parametri get
|
||||
```php
|
||||
$route = [
|
||||
'start' => 'fase1.html',
|
||||
'mid' => 'istruzioniFase2.html',
|
||||
'sep' => 'fase2.html',
|
||||
'end' => 'risultatiFinali.html',
|
||||
'leaderboard' => 'classifica.php'
|
||||
]
|
||||
```
|
||||
|
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 59 KiB |
|
After Width: | Height: | Size: 98 KiB |
@@ -0,0 +1,5 @@
|
||||
Indirizzi:
|
||||
DB: localhost
|
||||
web:
|
||||
docker-xampp: http://[url]:[porta]/www/
|
||||
xampp locale: http://localhost:[porta]/
|
||||
@@ -1,3 +1,5 @@
|
||||
Nome gioco: Pulisci il mondo
|
||||
|
||||
posizioni: 20 posizioni predefinite scelte random
|
||||
tempo 1° fase: 1m
|
||||
tempo 2° fase: 5s * punteggio
|
||||
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 9.8 KiB |
@@ -0,0 +1,77 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="it">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Classifica completa</title>
|
||||
<link rel="stylesheet" href="css/classifica.css">
|
||||
</head>
|
||||
<body>
|
||||
<h1>Classifica</h1>
|
||||
|
||||
<?php
|
||||
require 'php/leaderboard_utils.php';
|
||||
$sort = $_GET['sort'] ?? 'score';
|
||||
|
||||
// Carico la mappa delle posizioni (logica ID -> Posizione)
|
||||
$rankMap = getRankMap();
|
||||
|
||||
// Carico i dati in base all'ordinamento scelto
|
||||
if ($sort === 'data') {
|
||||
$leaderboard = getSortedLeaderboardByDate();
|
||||
} else {
|
||||
$leaderboard = getSortedLeaderboardByScore();
|
||||
}
|
||||
?>
|
||||
|
||||
<div class="sorting-buttons">
|
||||
<a href="?pagina=leaderboard&sort=data" class="sort-button <?php echo $sort === 'data' ? 'active' : ''; ?>">Ordina per data</a>
|
||||
<a href="?pagina=leaderboard&sort=score" class="sort-button <?php echo $sort === 'score' ? 'active' : ''; ?>">Ordina per punteggio</a>
|
||||
</div>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Posizione</th>
|
||||
<th>Nome</th>
|
||||
<th>1° Fase (RPS)</th>
|
||||
<th>2° Fase (%)</th>
|
||||
<th>Punteggio Totale (% isola pulita)</th>
|
||||
<th>Data</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (empty($leaderboard)): ?>
|
||||
<tr><td colspan="6" style="text-align:center;">Nessun punteggio registrato.</td></tr>
|
||||
<?php else: ?>
|
||||
<?php foreach ($leaderboard as $entry): ?>
|
||||
<?php
|
||||
// Recupero la posizione reale (1, 2, 3...)
|
||||
$posizioneReale = $rankMap[$entry['id']] + 1;
|
||||
|
||||
// Determiniamo la classe del podio
|
||||
$classePodio = '';
|
||||
if ($posizioneReale === 1) $classePodio = 'rank-gold';
|
||||
elseif ($posizioneReale === 2) $classePodio = 'rank-silver';
|
||||
elseif ($posizioneReale === 3) $classePodio = 'rank-bronze';
|
||||
|
||||
// Formattazione dati (tua logica esistente)
|
||||
$rps = number_format(intval($entry['score1']) / 60, 2);
|
||||
$fase2_perc = (intval($entry['score1']) > 0) ? intval(intval($entry['score2']) / intval($entry['score1']) * 100) : 0;
|
||||
$punteggio_finale = number_format(intval($entry['scoreT']) / 100, 2);
|
||||
$data_formattata = date("d/m/Y", strtotime($entry['data_partita']));
|
||||
?>
|
||||
<tr class="<?php echo $classePodio; ?>">
|
||||
<td><strong><?php echo $posizioneReale; ?>°</strong></td>
|
||||
<td><?php echo htmlspecialchars($entry['nome']); ?></td>
|
||||
<td><?php echo $rps; ?> rps</td>
|
||||
<td><?php echo $fase2_perc; ?>%</td>
|
||||
<td><?php echo $punteggio_finale; ?>%</td>
|
||||
<td><?php echo $data_formattata; ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,12 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="it">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Errore</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Errore</h1>
|
||||
<p>La pagina richiesta non è stata trovata.</p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,83 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="it">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Semplice Island FPS</title>
|
||||
<link rel="stylesheet" href="css/fase1.css">
|
||||
</head>
|
||||
<body>
|
||||
<!-- SCHERMATA DI CARICAMENTO -->
|
||||
<div id="loading-screen">
|
||||
<canvas id="matrix-canvas"></canvas>
|
||||
<div class="loader-content">
|
||||
<div class="eco-title">SAVE THE ISLAND</div>
|
||||
<div class="bar-container">
|
||||
<div id="loading-bar"></div>
|
||||
</div>
|
||||
<div id="loading-text">Caricamento delle risorse...</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- POPUP INSERIMENTO USERNAME -->
|
||||
<div id="name-popup-overlay">
|
||||
<div class="name-popup">
|
||||
<h3>IDENTIFICAZIONE GIOCATORE</h3>
|
||||
<p>Inserisci il tuo nome per iniziare a giocare:</p>
|
||||
<input type="text" id="username-input" placeholder="Nome Giocatore..." maxlength="15">
|
||||
<button id="confirm-name-btn">ACCEDI AL GIOCO</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- SCHERMATA INIZIALE -->
|
||||
<div id="start">
|
||||
<img src="assets/media/img/sfondo_iniziale.png" alt="Sfondo Isola" id="bg-island">
|
||||
<div class="overlay"></div>
|
||||
<div class="content">
|
||||
<header class="top-section">
|
||||
<img src="assets/media/img/titolo.png" alt="Eco-Mission" class="main-title" />
|
||||
</header>
|
||||
<main class="bottom-section">
|
||||
<section class="item side-panel">
|
||||
<div class="glass-box">
|
||||
<h3>Controlli</h3>
|
||||
<div class="control-list">
|
||||
<p><kbd>Freccia su / W</kbd> Avanti</p>
|
||||
<p><kbd>Freccia giù / S</kbd> Indietro</p>
|
||||
<p><kbd>Freccia destra / D</kbd> Destra</p>
|
||||
<p><kbd>Freccia sinistra / A</kbd> Sinistra</p>
|
||||
<p><kbd>Mouse</kbd> Punto di vista</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="item center-panel">
|
||||
<div class="pulse-text" id="btn-start">Clicca per giocare</div>
|
||||
</section>
|
||||
<section class="item side-panel">
|
||||
<div class="glass-box">
|
||||
<h3>Istruzioni</h3>
|
||||
<p><strong>Obiettivo:</strong> raccogliere tutti i rifiuti sull’isola</p>
|
||||
<p>Attenzione allo scadere del tempo!</p>
|
||||
<p>Usa i comandi per esplorare e ripulire</p>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- INTERFACCIA GRAFICA DEL GIOCO -->
|
||||
<div id="punti">Rifiuti: <span id="score">0</span></div>
|
||||
<div id="fps-counter">FPS: 0</div>
|
||||
<div id="tempo">Tempo: <span id="minuti">XX</span>:<span id="secondi">XX</span></div>
|
||||
<div id="crosshair">+</div>
|
||||
<script type="importmap">
|
||||
{
|
||||
"imports": {
|
||||
"three": "https://unpkg.com/three@0.160.0/build/three.module.js",
|
||||
"three/addons/": "https://unpkg.com/three@0.160.0/examples/jsm/"
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<script type="module" src="js/fase1.js"></script>
|
||||
<script src="js/loadingScreen.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,27 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="it">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Eco-Game: Differenziata</title>
|
||||
<link rel="stylesheet" href="css/fase2.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="ui">
|
||||
Punti: <span id="score">0</span> | Tempo: <span id="minuti"></span>:<span id="secondi"></span>
|
||||
</div>
|
||||
|
||||
<div id="game-container">
|
||||
<div id="bins-container">
|
||||
<div class="bin" data-type="alluminio"><img src="assets/media/img/fase2/contenitori/alluminio.jpeg" alt="Alluminio"></div>
|
||||
<div class="bin" data-type="umido"><img src="assets/media/img/fase2/contenitori/umido.jpeg" alt="Umido"></div>
|
||||
<div class="bin" data-type="plastica"><img src="assets/media/img/fase2/contenitori/plastica.jpeg" alt="Plastica"></div>
|
||||
<div class="bin" data-type="vetro"><img src="assets/media/img/fase2/contenitori/vetro.jpeg" alt="Vetro"></div>
|
||||
<div class="bin" data-type="carta"><img src="assets/media/img/fase2/contenitori/carta.jpeg" alt="Carta"></div>
|
||||
<div class="bin" data-type="indifferenziata"><img src="assets/media/img/fase2/contenitori/indifferenziata.jpeg" alt="Indifferenziata"></div>
|
||||
</div>
|
||||
<div id="counter"></div>
|
||||
</div>
|
||||
<script src="js/fase2.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,60 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="it">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Istruzioni fase 2</title>
|
||||
<link rel="stylesheet" href="css/istruzioniFase2.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="start-fase2">
|
||||
<div class="overlay-gradient"></div>
|
||||
<div class="content">
|
||||
<div class="top-section">
|
||||
<h1>Istruzioni fase 2</h1>
|
||||
</div>
|
||||
<div class="container bottom-section">
|
||||
<div class="item left">
|
||||
<div class="controls-box">
|
||||
<h3>Controlli</h3>
|
||||
<p><kbd>Mouse</kbd> Per trascinare i rifiuti nei cestini</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item center">
|
||||
<span class="pulse-text" id="btn-start-f2">Clicca per giocare</span>
|
||||
</div>
|
||||
<div class="item right">
|
||||
<div class="instructions-box">
|
||||
<h3>Istruzioni</h3>
|
||||
<p>
|
||||
Hai <b><i id="tempo-f2-val">--</i></b> secondi per completare il livello
|
||||
<br>
|
||||
Hai raccolto <b><i id="punti-f2-val">--</i></b> rifiuti nella fase precedente
|
||||
<br>
|
||||
<b>Obiettivo:</b> separare correttamente i rifiuti nei cestini corrispondenti
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script defer>
|
||||
const punti = localStorage.getItem('punteggioFase1') || 0;
|
||||
document.getElementById('punti-f2-val').textContent = punti;
|
||||
document.getElementById('tempo-f2-val').textContent = punti * 5; // 5 secondi per ogni rifiuto raccolto nella fase 1
|
||||
document.getElementById('btn-start-f2').style.cursor = 'pointer';
|
||||
const schermata = document.getElementById('start-fase2');
|
||||
schermata.style.display = 'flex';
|
||||
|
||||
// Listener per il pulsante centrale
|
||||
document.getElementById('btn-start-f2').onclick = function() {
|
||||
schermata.style.opacity = '0';
|
||||
schermata.style.transition = 'opacity 0.5s ease';
|
||||
setTimeout(() => {
|
||||
schermata.style.display = 'none';
|
||||
window.location = '?pagina=sep';
|
||||
}, 500);
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
require 'php/leaderboard_utils.php';
|
||||
$sort = $_GET['sort'] ?? 'data';
|
||||
|
||||
// Carico la mappa delle posizioni (logica ID -> Posizione)
|
||||
$rankMap = getRankMap();
|
||||
// Carico i dati in base all'ordinamento scelto
|
||||
$leaderboard = getTop10byScore();
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="it">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Schermata Finale</title>
|
||||
<link rel="stylesheet" href="css/risultatiFinali.css">
|
||||
</head>
|
||||
<body>
|
||||
<!-- Fase 2: separazione dei rifiuti tramite drag&drop -->
|
||||
<!-- Layout simile a div#start in pages/fase1.html -->
|
||||
<div id="start">
|
||||
<div class="overlay"></div>
|
||||
<div class="content">
|
||||
<div class="left1">
|
||||
<div class="top">
|
||||
<h1>MISSIONE COMPIUTA</h1>
|
||||
</div>
|
||||
<div class="bottom">
|
||||
<div class="left2">
|
||||
<button type="button" onclick="window.open('?pagina=leaderboard', '_blank')">Classifica Completa</button>
|
||||
<button type="button" onclick="localStorage.clear(); window.location = '?pagina=start'">Gioca Ancora</button>
|
||||
</div>
|
||||
<div class="right2">
|
||||
<p>
|
||||
<b>Punteggi:</b>
|
||||
<br>
|
||||
Prima fase: <i><span id="score1a"></span>/180</i>
|
||||
<br>
|
||||
Seconda fase: <i><span id="score2"></span>/<span id="score1b"></span></i>
|
||||
<br>
|
||||
Totale: <i><span id="scoreTotale"></span>/100</i>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="right1">
|
||||
<h2>Top 10</h2>
|
||||
<table class="container">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>N°</th>
|
||||
<th>Nome</th>
|
||||
<th>Punteggio</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (empty($leaderboard)): ?>
|
||||
<tr><td colspan="3" style="text-align:center;">Nessun punteggio registrato.</td></tr>
|
||||
<?php else: ?>
|
||||
<?php foreach ($leaderboard as $entry): ?>
|
||||
<?php
|
||||
// Recupero la posizione reale dalla mappa tramite l'ID
|
||||
$posizioneReale = $rankMap[$entry['id']] + 1;
|
||||
$punteggio_finale = number_format(intval($entry['scoreT']) / 100, 2);
|
||||
?>
|
||||
<tr>
|
||||
<td><strong><?php echo $posizioneReale; ?>°</strong></td>
|
||||
<td><?php echo htmlspecialchars($entry['nome']); ?></td>
|
||||
<td><?php echo $punteggio_finale; ?>%</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script defer>
|
||||
const score1 = localStorage.getItem('punteggioFase1') || 0;
|
||||
const score2 = localStorage.getItem('punteggioFase2') || 0;
|
||||
const scoreT = 10_000 * ((score1 / MAX_SCORE) + (score2 / MAX_SCORE)) / 2;
|
||||
document.getElementById('score1a').textContent = score1;
|
||||
document.getElementById('score1b').textContent = score1;
|
||||
document.getElementById('score2').textContent = score2;
|
||||
document.getElementById('scoreTotale').textContent = scoreT.toFixed(0) / 100;
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,3 @@
|
||||
<script>
|
||||
const SERVER_URL = 'http://simonez-cloud.ddns.net:30200/www/GDT'
|
||||
</script>
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
$servername = "localhost";
|
||||
$username = "root";
|
||||
$password = "";
|
||||
$dbname = "save-the-island";
|
||||
$conn = new mysqli($servername, $username, $password, $dbname);
|
||||
if ($conn->connect_error) {
|
||||
die("Connection failed: " . $conn->connect_error);
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
/*
|
||||
database schema:
|
||||
CREATE TABLE punteggi (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
data_partita DATE NOT NULL,
|
||||
score1 INT NOT NULL,
|
||||
score2 INT NOT NULL,
|
||||
scoreT INT NOT NULL,
|
||||
nome VARCHAR(100) NOT NULL
|
||||
);
|
||||
*/
|
||||
|
||||
define('TABLE_NAME', 'punteggi_test'); // Cambia in 'punteggi_test' se necessario
|
||||
require 'db.php';
|
||||
|
||||
/**
|
||||
* Crea una mappa [ID => Posizione] basata sullo scoreT
|
||||
* Utile per sapere la posizione di una partita a prescindere dall'ordinamento visivo
|
||||
*/
|
||||
function getRankMap() {
|
||||
global $conn;
|
||||
$result = $conn->query("SELECT id FROM " . TABLE_NAME . " ORDER BY scoreT DESC");
|
||||
$orderedIds = array_column($result->fetch_all(MYSQLI_ASSOC), 'id');
|
||||
// array_flip trasforma [0 => id1, 1 => id2] in [id1 => 0, id2 => 1]
|
||||
$rankMap = array_flip($orderedIds);
|
||||
return $rankMap;
|
||||
}
|
||||
|
||||
function getSortedLeaderboardByDate() {
|
||||
global $conn;
|
||||
$result = $conn->query("SELECT * FROM " . TABLE_NAME . " ORDER BY data_partita DESC, id DESC");
|
||||
return $result->fetch_all(MYSQLI_ASSOC);
|
||||
}
|
||||
|
||||
function getSortedLeaderboardByScore() {
|
||||
global $conn;
|
||||
$result = $conn->query("SELECT * FROM " . TABLE_NAME . " ORDER BY scoreT DESC");
|
||||
return $result->fetch_all(MYSQLI_ASSOC);
|
||||
}
|
||||
|
||||
// Altre funzioni di utility se necessarie...
|
||||
function getTop10byScore() {
|
||||
global $conn;
|
||||
$result = $conn->query("SELECT * FROM " . TABLE_NAME . " ORDER BY scoreT DESC LIMIT 10");
|
||||
return $result->fetch_all(MYSQLI_ASSOC);
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
require 'db.php';
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$data_partita = $_POST['data_partita'];
|
||||
$score1 = $_POST['score1'];
|
||||
$score2 = $_POST['score2'];
|
||||
$scoreT = $_POST['scoreT'];
|
||||
$nome = $_POST['nome'];
|
||||
|
||||
$stmt = $conn->prepare("INSERT INTO punteggi_test (data_partita, score1, score2, scoreT, nome) VALUES (?, ?, ?, ?, ?)");
|
||||
$stmt->bind_param("siiis", $data_partita, $score1, $score2, $scoreT, $nome);
|
||||
$stmt->execute();
|
||||
$stmt->close();
|
||||
}
|
||||
?>
|
||||
|
Before Width: | Height: | Size: 11 KiB |
@@ -1,87 +0,0 @@
|
||||
import * as THREE from 'three';
|
||||
import { PointerLockControls } from 'three/addons/controls/PointerLockControls.js';
|
||||
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
|
||||
|
||||
// 1. SCENA E CAMERA
|
||||
const scene = new THREE.Scene();
|
||||
scene.background = new THREE.Color(0x87ceeb);
|
||||
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
|
||||
const renderer = new THREE.WebGLRenderer();
|
||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||
document.body.appendChild(renderer.domElement);
|
||||
window.addEventListener('resize', function () {
|
||||
renderer.setSize(window.innerWidth, window.innerHeight)
|
||||
})
|
||||
|
||||
// 2. LUCI E AMBIENTE
|
||||
scene.add(new THREE.AmbientLight(0xffffff, 1));
|
||||
const island = new THREE.Mesh(new THREE.CircleGeometry(20, 32), new THREE.MeshStandardMaterial({ color: 0x4df556 })); // #4df555
|
||||
island.rotation.x = -Math.PI / 2;
|
||||
scene.add(island);
|
||||
|
||||
// 3. CONTROLLI
|
||||
function lock() {
|
||||
document.getElementById('start').style.display = 'none';
|
||||
document.getElementById('punti').style.display = 'block';
|
||||
document.getElementById('tempo').style.display = 'block';
|
||||
}
|
||||
function unlock() {
|
||||
document.getElementById('start').style.display = 'flex';
|
||||
document.getElementById('punti').style.display = 'none';
|
||||
document.getElementById('tempo').style.display = 'none';
|
||||
}
|
||||
const controls = new PointerLockControls(camera, document.body);
|
||||
document.getElementById('start').addEventListener('click', () => controls.lock());
|
||||
controls.addEventListener('lock', lock);
|
||||
controls.addEventListener('unlock', unlock);
|
||||
|
||||
// 4. GESTIONE RIFIUTI (20 pezzi)
|
||||
const trashArray = [];
|
||||
let score = 0;
|
||||
const loader = new GLTFLoader();
|
||||
|
||||
loader.load('models/rifiuto.glb', (gltf) => {
|
||||
const mesh = gltf.scene.getObjectByName("Trash_Pile_03_GEO");
|
||||
mesh.geometry.center(); // Centra l'oggetto per collisioni precise
|
||||
for (let i = 0; i < 20; i++) {
|
||||
const clone = mesh.clone();
|
||||
scene.add(clone);
|
||||
trashArray.push(clone);
|
||||
spawn(clone);
|
||||
}
|
||||
});
|
||||
|
||||
function spawn(obj) {
|
||||
const a = Math.random() * Math.PI * 2;
|
||||
const r = Math.random() * 18;
|
||||
obj.position.set(Math.cos(a) * r, 0.3, Math.sin(a) * r);
|
||||
}
|
||||
|
||||
// 5. MOVIMENTO E COLLISIONI
|
||||
const keys = {};
|
||||
document.onkeydown = (e) => keys[e.code] = true;
|
||||
document.onkeyup = (e) => keys[e.code] = false;
|
||||
|
||||
function animate() {
|
||||
requestAnimationFrame(animate);
|
||||
if (controls.isLocked) {
|
||||
if (keys['KeyW']) controls.moveForward(0.15);
|
||||
if (keys['KeyS']) controls.moveForward(-0.15);
|
||||
if (keys['KeyA']) controls.moveRight(-0.15);
|
||||
if (keys['KeyD']) controls.moveRight(0.15);
|
||||
camera.position.y = 1.6;
|
||||
|
||||
// Controllo collisioni
|
||||
const pPos = camera.position.clone();
|
||||
pPos.y -= 1.0;
|
||||
trashArray.forEach(t => {
|
||||
if (new THREE.Box3().setFromObject(t).expandByScalar(0.3).containsPoint(pPos)) {
|
||||
score++;
|
||||
document.getElementById('score').innerText = score;
|
||||
spawn(t);
|
||||
}
|
||||
});
|
||||
}
|
||||
renderer.render(scene, camera);
|
||||
}
|
||||
animate();
|
||||
@@ -1,41 +0,0 @@
|
||||
:root {
|
||||
--top: 10px;
|
||||
--border: 20px;
|
||||
--text: 1.5rem;
|
||||
}
|
||||
body {
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
#punti {
|
||||
position: absolute;
|
||||
top: var(--top);
|
||||
left: var(--border);
|
||||
color: white;
|
||||
font-size: var(--text);
|
||||
pointer-events: none;
|
||||
display: none;
|
||||
}
|
||||
#tempo {
|
||||
position: absolute;
|
||||
top: var(--top);
|
||||
right: var(--border);
|
||||
color: white;
|
||||
font-size: var(--text);
|
||||
pointer-events: none;
|
||||
display: none;
|
||||
}
|
||||
#start {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0,0,0,0.5);
|
||||
color: white;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
// file per testare save_score.php
|
||||
// richiesta post a save_score.php con i seguenti dati:
|
||||
// nome: "Simone"
|
||||
// score1: 120
|
||||
// score2: 118
|
||||
// scoreT: 80
|
||||
$data = [
|
||||
'nome' => 'TEST',
|
||||
'score1' => 120,
|
||||
'score2' => 118,
|
||||
'scoreT' => 6611,
|
||||
'data_partita' => date("Y-m-d")
|
||||
];
|
||||
// richiesta post a save_score.php con i dati richiesti
|
||||
$options = [
|
||||
'http' => [
|
||||
'header' => "Content-type: application/x-www-form-urlencoded\r\n",
|
||||
'method' => 'POST',
|
||||
'content' => http_build_query($data),
|
||||
],
|
||||
];
|
||||
$context = stream_context_create($options);
|
||||
$result = file_get_contents('http://simonez-cloud.ddns.net:30200/www/GDT/php/save_score.php', false, $context);
|
||||
if ($result === FALSE) {
|
||||
echo "Errore nella richiesta";
|
||||
} else {
|
||||
echo "Punteggio salvato con successo";
|
||||
}
|
||||
?>
|
||||