diff --git a/.gitignore b/.gitignore
index c79b2e4..c2658d7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1 @@
node_modules/
-.DS_Store
-*.log
-.env
-.env.local
diff --git a/README.md b/README.md
index a45bf89..5fd225e 100644
--- a/README.md
+++ b/README.md
@@ -1,146 +1,114 @@
-# 🎮 Platanus Hack 25: Arcade Challenge
+# PLAT-MAN 🍌🦍
-At [Platanus Hack 25](https://hack.platan.us) we will have an arcade machine. While we could put some cool retro games on it, it is way better if it can be turned into a challenge.
+Un juego arcade para Platanus Hack 25 donde un gorila escapa de agentes corporativos estresados mientras recolecta plátanos.
-**Your mission:** Build the best arcade game using Phaser 3 (JS Game Lib) that will run on our physical arcade machine!
+## 🎮 Descripción
----
+- **3 niveles** con dificultad creciente
+- **Agentes corporativos** como enemigos (con trajes y maletines)
+- **Bananas realistas** que dan poder temporal
+- **Sistema de ranking** con Top 5 (almacenado en localStorage)
+- **Música de fondo** generada con Web Audio API
+- **Sprites procedurales** dibujados en runtime (sin imágenes externas)
-## 🏆 Prizes
+## 🎯 Controles
-**🥇 First Place:**
-- 💵 **$250 USD in cash**
-- 🎟️ **A slot to participate in Platanus Hack**
-- 🎮 **Your game featured on the arcade machine**
+- **Flechas** o **WASD**: Mover al gorila
+- **ESPACIO/ENTER**: Iniciar juego / Confirmar
+- **↑↓**: Cambiar letra en ranking
+- **←→**: Mover entre letras en ranking
+- **R**: Reiniciar (durante el juego)
-**🥈 Second Place:**
-- 💵 **$100 USD in cash**
-- 🎮 **Your game featured on the arcade machine**
+## 📁 Estructura del Proyecto
----
-
-## 📋 Restrictions
-
-Your game must comply with these technical restrictions:
-
-### Size Limit
-- ✅ **Maximum 50KB after minification** (before gzip)
-- The game code is automatically minified - focus on writing good code
-
-### Code Restrictions
-- ✅ **Pure vanilla JavaScript only** - No `import` or `require` statements
-- ✅ **No external URLs** - No `http://`, `https://`, or `//` (except `data:` URIs for base64)
-- ✅ **No network calls** - No `fetch`, `XMLHttpRequest`, or similar APIs
-- ✅ **Sandboxed environment** - Game runs in an iframe with no internet access
-
-### What You CAN Use
-- ✅ **Phaser 3** (v3.87.0) - Loaded externally via CDN (not counted in size limit)
-- ✅ **Base64-encoded images** - Using `data:` URIs
-- ✅ **Procedurally generated graphics** - Using Phaser's Graphics API
-- ✅ **Generated audio tones** - Using Phaser's Web Audio API
-- ✅ **Canvas-based rendering and effects**
-
-# 🕹️ Controls
-
-Your game will run on a real arcade cabinet with physical joysticks and buttons!
-
-
-
-## Arcade Button Mapping
-
-The arcade cabinet sends specific key codes when buttons are pressed:
-
-**Player 1:**
-- **Joystick**: `P1U`, `P1D`, `P1L`, `P1R` (Up, Down, Left, Right)
-- **Joystick Diagonals**: `P1DL`, `P1DR` (Down-Left, Down-Right)
-- **Action Buttons**: `P1A`, `P1B`, `P1C` (top row) / `P1X`, `P1Y`, `P1Z` (bottom row)
-- **Start**: `START1`
-
-**Player 2:**
-- **Joystick**: `P2U`, `P2D`, `P2L`, `P2R`
-- **Joystick Diagonals**: `P2DL`, `P2DR`
-- **Action Buttons**: `P2A`, `P2B`, `P2C` / `P2X`, `P2Y`, `P2Z`
-- **Start**: `START2`
-
-## Testing Locally
+```
+platanus-phaser-game/
+├── game.js # ✅ Código principal del juego (sin imports)
+├── metadata.json # ✅ Nombre y descripción del juego
+├── index.html # HTML con Phaser desde CDN
+├── README.md # Este archivo
+└── cover.png # (pendiente) Imagen 800x600px
+```
-For local testing, you can map these arcade buttons to keyboard keys. The mapping supports **multiple keyboard keys per arcade button** (useful for alternatives like WASD + Arrow Keys). See `game.js` for the complete `ARCADE_CONTROLS` mapping template.
+## ⚙️ Características Técnicas
-By default:
-- Player 1 uses **WASD** (joystick) and **U/I/O/J/K/L** (action buttons)
-- Player 2 uses **Arrow Keys** (joystick) and **R/T/Y/F/G/H** (action buttons)
+### Cumple con restricciones:
+- ✅ **Sin imports**: JavaScript vanilla puro
+- ✅ **Sin URLs externas** en game.js (Phaser desde CDN no cuenta)
+- ✅ **Sin fetch/XMLHttpRequest**
+- ✅ **Sprites procedurales**: Dibujados con Canvas API
+- ✅ **Audio generado**: Usando Web Audio API de Phaser
+- ✅ **Tamaño optimizado**: Código minificable
-💡 **Tip**: Keep controls simple - design for joystick + 1-2 action buttons for the best arcade experience!
+### Phaser 3 Features utilizados:
+- `Phaser.Game` y configuración
+- Physics (Arcade)
+- Sprites y texturas procedurales
+- Tweens para animaciones
+- Keyboard input
+- Groups y colisiones
+- LocalStorage para persistencia
----
+## 🚀 Desarrollo
-## ⏰ Deadline & Submission
+### Instalar dependencias:
+```bash
+pnpm install
+```
-**Deadline:** Sunday, November 10, 2025 at 23:59 (Santiago time)
+### Ejecutar en desarrollo:
+```bash
+pnpm dev
+```
-### How to Submit
+### Verificar restricciones:
+```bash
+pnpm check-restrictions
+```
-Submitting your project is easy:
+## 🎨 Sprites
-1. **Save your changes** - Make sure `game.js`, `metadata.json`, and `cover.png` are ready
- - **Important:** Your game must include a custom `cover.png` file (800x600 pixels) showcasing your game
-2. **Git push** - Push your code to your repository:
- ```bash
- git add .
- git commit -m "Final submission"
- git push
- ```
-3. **Hit Submit** - Click the submit button in the development UI and follow the steps
+Todos los sprites son generados proceduralmente en el código:
-That's it! 🎉
+- **Gorila**: Cuerpo completo con brazos, piernas, expresión facial
+- **Agentes**: Oficinistas con traje, corbata, maletín, expresiones (serio/asustado)
+- **Bananas**: Realistas con gradientes, brillos y manchas maduras
+- **Paredes**: Tiles con efectos visuales por nivel
----
+## 🎵 Audio
-## 🚀 Quick Start
+Música de fondo simple generada con osciladores (notas: C, E, G, E).
-### 1. Install Dependencies
-```bash
-pnpm install
-```
+## 🏆 Sistema de Ranking
-### 2. Start Development Server
-```bash
-pnpm dev
-```
-This starts a server at `http://localhost:3000` with live restriction checking.
+- Top 5 mejores puntuaciones
+- Iniciales de 3 letras
+- Colores especiales (oro, plata, bronce)
+- Guardado en localStorage
-### 3. Build Your Game
-- **Edit `game.js`** - Write your arcade game code
-- **Update `metadata.json`** - Set your game name and description
-- **Create `cover.png`** - Design an 800x600 pixel cover image for your game
-- **Watch the dev server** - It shows live updates on file size and restrictions
+## 📊 Niveles
----
+1. **Etapa 1** (Azul): Velocidad de agentes: 120
+2. **Etapa 2** (Púrpura): Velocidad de agentes: 150
+3. **Etapa 3** (Naranja): Velocidad de agentes: 180
-## 🤖 Vibecoding Your Game
+## 🎯 Objetivo del Juego
-This challenge is designed for **vibecoding** - building your game with AI assistance!
+Recolecta los 6 plátanos en cada nivel mientras evitas a los agentes corporativos. Cuando recolectas un plátano, los agentes se estresan y puedes comerlos por 4 segundos. Completa las 3 etapas para ganar.
-### What We've Set Up For You
+## 📝 Puntuación
-- **`AGENTS.md`** - Pre-configured instructions so your IDE (Cursor, Windsurf, etc.) understands the challenge
-- **`docs/phaser-quick-start.md`** - Quick reference guide for Phaser 3
-- **`docs/phaser-api.md`** - Comprehensive Phaser 3 API documentation
+- **10 puntos** por plátano
+- **50 puntos** por agente comido
+- **1 punto** por segundo sobrevivido
-Your AI agent already knows:
-- ✅ All the challenge restrictions
-- ✅ How to use Phaser 3 effectively
-- ✅ Best practices for staying under 50KB
-- ✅ What files to edit (`game.js` and `metadata.json` only)
+## 🔧 Próximos Pasos
-### How to Vibecode
+- [ ] Crear `cover.png` (800x600px)
+- [ ] Ejecutar `pnpm check-restrictions` para verificar tamaño
+- [ ] Optimizar código si excede 50KB
-Simply tell your AI assistant what game you want to build! For example:
+## 👥 Créditos
-> "Create a Space Invaders clone with colorful enemies"
->
-> "Build a flappy bird style game with procedural graphics"
->
-> "Make a breakout game with power-ups"
+Juego creado para **Platanus Hack 25: Arcade Challenge**
-Your AI will handle the implementation, keeping everything within the restrictions automatically!
diff --git a/cover.png b/cover.png
index 717f038..6cc654a 100644
Binary files a/cover.png and b/cover.png differ
diff --git a/create-cover-v2.html b/create-cover-v2.html
new file mode 100644
index 0000000..8f81356
--- /dev/null
+++ b/create-cover-v2.html
@@ -0,0 +1,410 @@
+
+
+
+
+ PLAT-MAN Cover - Professional
+
+
+
+
+ ⬇️ Descargar Cover Profesional
+
+
+
+
+
diff --git a/create-cover.html b/create-cover.html
new file mode 100644
index 0000000..f8dd396
--- /dev/null
+++ b/create-cover.html
@@ -0,0 +1,303 @@
+
+
+
+
+ Generate Cover
+
+
+
+
+ Download cover.png
+
+
+
+
+
diff --git a/game.js b/game.js
index 5987201..542817a 100644
--- a/game.js
+++ b/game.js
@@ -1,85 +1,107 @@
-// Platanus Hack 25: Snake Game
-// Navigate the snake around the "PLATANUS HACK ARCADE" title made of blocks!
-
-// =============================================================================
-// ARCADE BUTTON MAPPING - COMPLETE TEMPLATE
-// =============================================================================
-// Reference: See button-layout.webp at hack.platan.us/assets/images/arcade/
-//
-// Maps arcade button codes to keyboard keys for local testing.
-// Each arcade code can map to multiple keyboard keys (array values).
-// The arcade cabinet sends codes like 'P1U', 'P1A', etc. when buttons are pressed.
-//
-// To use in your game:
-// if (key === 'P1U') { ... } // Works on both arcade and local (via keyboard)
-//
-// CURRENT GAME USAGE (Snake):
-// - P1U/P1D/P1L/P1R (Joystick) → Snake Direction
-// - P1A (Button A) or START1 (Start Button) → Restart Game
-// =============================================================================
-
-const ARCADE_CONTROLS = {
- // ===== PLAYER 1 CONTROLS =====
- // Joystick - Left hand on WASD
- 'P1U': ['w'],
- 'P1D': ['s'],
- 'P1L': ['a'],
- 'P1R': ['d'],
- 'P1DL': null, // Diagonal down-left (no keyboard default)
- 'P1DR': null, // Diagonal down-right (no keyboard default)
-
- // Action Buttons - Right hand on home row area (ergonomic!)
- // Top row (ABC): U, I, O | Bottom row (XYZ): J, K, L
- 'P1A': ['u'],
- 'P1B': ['i'],
- 'P1C': ['o'],
- 'P1X': ['j'],
- 'P1Y': ['k'],
- 'P1Z': ['l'],
-
- // Start Button
- 'START1': ['1', 'Enter'],
-
- // ===== PLAYER 2 CONTROLS =====
- // Joystick - Right hand on Arrow Keys
- 'P2U': ['ArrowUp'],
- 'P2D': ['ArrowDown'],
- 'P2L': ['ArrowLeft'],
- 'P2R': ['ArrowRight'],
- 'P2DL': null, // Diagonal down-left (no keyboard default)
- 'P2DR': null, // Diagonal down-right (no keyboard default)
-
- // Action Buttons - Left hand (avoiding P1's WASD keys)
- // Top row (ABC): R, T, Y | Bottom row (XYZ): F, G, H
- 'P2A': ['r'],
- 'P2B': ['t'],
- 'P2C': ['y'],
- 'P2X': ['f'],
- 'P2Y': ['g'],
- 'P2Z': ['h'],
-
- // Start Button
- 'START2': ['2']
-};
+/**
+ * PLAT-MAN - Arcade Game
+ * Un gorila escapa de agentes corporativos estresados mientras recolecta plátanos
+ */
-// Build reverse lookup: keyboard key → arcade button code
-const KEYBOARD_TO_ARCADE = {};
-for (const [arcadeCode, keyboardKeys] of Object.entries(ARCADE_CONTROLS)) {
- if (keyboardKeys) {
- // Handle both array and single value
- const keys = Array.isArray(keyboardKeys) ? keyboardKeys : [keyboardKeys];
- keys.forEach(key => {
- KEYBOARD_TO_ARCADE[key] = arcadeCode;
- });
+// ================ CONFIGURACIÓN GLOBAL ================
+const W = 800;
+const H = 600;
+const POWER_DURATION_MS = 4000;
+const BANANAS_TOTAL = 6;
+const GHOST_COUNT = 4;
+const RANKING_KEY = 'platman_ranking';
+
+// Configuración de niveles
+const LEVEL_CONFIG = {
+ 1: {
+ bgColor: 0x0f0f14,
+ wallColor: "#1e2aff",
+ wallStroke: "#5360ff",
+ ghostSpeed: 120,
+ name: "Etapa 1",
+ walls: [
+ [W/2, 40, W-40, 18], [W/2, H-40, W-40, 18],
+ [40, H/2, 18, H-80], [W-40, H/2, 18, H-80],
+ [W/2, 200, 320, 18], [W/2, 300, 18, 240],
+ [250, 430, 270, 18], [550, 430, 270, 18],
+ [150, 260, 18, 160], [650, 260, 18, 160]
+ ]
+ },
+ 2: {
+ bgColor: 0x1a0a1a,
+ wallColor: "#8e2aff",
+ wallStroke: "#b360ff",
+ ghostSpeed: 150,
+ name: "Etapa 2",
+ walls: [
+ [W/2, 40, W-40, 18], [W/2, H-40, W-40, 18],
+ [40, H/2, 18, H-80], [W-40, H/2, 18, H-80],
+ [200, 150, 18, 200], [600, 150, 18, 200],
+ [200, 450, 18, 200], [600, 450, 18, 200],
+ [W/2, 300, 200, 18], [300, 200, 200, 18],
+ [500, 400, 200, 18]
+ ]
+ },
+ 3: {
+ bgColor: 0x1a140a,
+ wallColor: "#ff6e2a",
+ wallStroke: "#ff9060",
+ ghostSpeed: 180,
+ name: "Etapa 3",
+ walls: [
+ [W/2, 40, W-40, 18], [W/2, H-40, W-40, 18],
+ [40, H/2, 18, H-80], [W-40, H/2, 18, H-80],
+ [250, 200, 300, 18], [550, 400, 300, 18],
+ [W/2, H/2, 100, 100], [200, 350, 18, 180],
+ [600, 250, 18, 180], [350, 150, 18, 120],
+ [450, 450, 18, 120]
+ ]
}
-}
+};
+
+// ================ VARIABLES GLOBALES ================
+let gameState = 'start'; // 'start', 'playing', 'gameOver', 'ranking'
+let currentLevel = 1;
+let score = 0;
+let startTime = 0;
+let powerMode = false;
+let powerExpiresAt = 0;
+let isGameOver = false;
+
+// Objetos del juego
+let player;
+let cursors;
+let wasdKeys;
+let bananas;
+let ghosts;
+let wallBodies;
+let wallVisuals;
+let bgRect;
+let scoreText;
+let levelText;
+let bgMusicTimer;
+
+// UI de ranking
+let rankingInputs = [];
+let currentInitials = ['A', 'A', 'A'];
+let selectedIndex = 0;
+let finalScore = 0;
+let wonGame = false;
+// ================ CONFIGURACIÓN DE PHASER ================
const config = {
type: Phaser.AUTO,
- width: 800,
- height: 600,
- backgroundColor: '#000000',
+ width: W,
+ height: H,
+ parent: 'game-container',
+ backgroundColor: "#000000",
+ render: { pixelArt: true, roundPixels: true },
+ physics: {
+ default: "arcade",
+ arcade: { debug: false, gravity: { y: 0 } }
+ },
scene: {
+ preload: preload,
create: create,
update: update
}
@@ -87,350 +109,1233 @@ const config = {
const game = new Phaser.Game(config);
-// Game variables
-let snake = [];
-let snakeSize = 15;
-let direction = { x: 1, y: 0 };
-let nextDirection = { x: 1, y: 0 };
-let food;
-let score = 0;
-let scoreText;
-let titleBlocks = [];
-let gameOver = false;
-let moveTimer = 0;
-let moveDelay = 100; // Faster initial speed (was 150ms)
-let graphics;
-
-// Pixel font patterns (5x5 grid for each letter)
-const letters = {
- P: [[1,1,1,1],[1,0,0,1],[1,1,1,1],[1,0,0,0],[1,0,0,0]],
- L: [[1,0,0,0],[1,0,0,0],[1,0,0,0],[1,0,0,0],[1,1,1,1]],
- A: [[0,1,1,0],[1,0,0,1],[1,1,1,1],[1,0,0,1],[1,0,0,1]],
- T: [[1,1,1,1],[0,1,0,0],[0,1,0,0],[0,1,0,0],[0,1,0,0]],
- N: [[1,0,0,1],[1,1,0,1],[1,0,1,1],[1,0,0,1],[1,0,0,1]],
- U: [[1,0,0,1],[1,0,0,1],[1,0,0,1],[1,0,0,1],[1,1,1,1]],
- S: [[0,1,1,1],[1,0,0,0],[0,1,1,0],[0,0,0,1],[1,1,1,0]],
- H: [[1,0,0,1],[1,0,0,1],[1,1,1,1],[1,0,0,1],[1,0,0,1]],
- C: [[0,1,1,1],[1,0,0,0],[1,0,0,0],[1,0,0,0],[0,1,1,1]],
- K: [[1,0,0,1],[1,0,1,0],[1,1,0,0],[1,0,1,0],[1,0,0,1]],
- '2': [[1,1,1,0],[0,0,0,1],[0,1,1,0],[1,0,0,0],[1,1,1,1]],
- '5': [[1,1,1,1],[1,0,0,0],[1,1,1,0],[0,0,0,1],[1,1,1,0]],
- ':': [[0,0,0,0],[0,1,0,0],[0,0,0,0],[0,1,0,0],[0,0,0,0]],
- R: [[1,1,1,0],[1,0,0,1],[1,1,1,0],[1,0,1,0],[1,0,0,1]],
- D: [[1,1,1,0],[1,0,0,1],[1,0,0,1],[1,0,0,1],[1,1,1,0]],
- E: [[1,1,1,1],[1,0,0,0],[1,1,1,0],[1,0,0,0],[1,1,1,1]]
-};
-
-// Bold font for ARCADE (filled/solid style)
-const boldLetters = {
- A: [[1,1,1,1,1],[1,1,0,1,1],[1,1,1,1,1],[1,1,0,1,1],[1,1,0,1,1]],
- R: [[1,1,1,1,0],[1,1,0,1,1],[1,1,1,1,0],[1,1,0,1,1],[1,1,0,1,1]],
- C: [[1,1,1,1,1],[1,1,0,0,0],[1,1,0,0,0],[1,1,0,0,0],[1,1,1,1,1]],
- D: [[1,1,1,1,0],[1,1,0,1,1],[1,1,0,1,1],[1,1,0,1,1],[1,1,1,1,0]],
- E: [[1,1,1,1,1],[1,1,0,0,0],[1,1,1,1,0],[1,1,0,0,0],[1,1,1,1,1]]
-};
+// ================ PRELOAD ================
+function preload() {
+ createAllSprites(this);
+}
+// ================ CREATE ================
function create() {
- const scene = this;
- graphics = this.add.graphics();
-
- // Build "PLATANUS HACK ARCADE" in cyan - centered and grid-aligned
- // PLATANUS: 8 letters × (4 cols + 1 spacing) = 40 blocks, but last letter no spacing = 39 blocks × 15px = 585px
- let x = Math.floor((800 - 585) / 2 / snakeSize) * snakeSize;
- let y = Math.floor(180 / snakeSize) * snakeSize;
- 'PLATANUS'.split('').forEach(char => {
- x = drawLetter(char, x, y, 0x00ffff);
- });
+ this.scene = this;
+ showStartScreen(this);
+}
- // HACK: 4 letters × (4 cols + 1 spacing) = 20 blocks, but last letter no spacing = 19 blocks × 15px = 285px
- x = Math.floor((800 - 285) / 2 / snakeSize) * snakeSize;
- y = Math.floor(280 / snakeSize) * snakeSize;
- 'HACK'.split('').forEach(char => {
- x = drawLetter(char, x, y, 0x00ffff);
- });
+// ================ UPDATE ================
+function update() {
+ if (gameState === 'playing') {
+ updateGame(this);
+ }
+}
+
+// ================ CREACIÓN DE SPRITES ================
+function createAllSprites(scene) {
+ const drawTex = (key, w, h, draw) => {
+ const tex = scene.textures.createCanvas(key, w, h);
+ const ctx = tex.getContext();
+ ctx.clearRect(0, 0, w, h);
+ draw(ctx, w, h);
+ tex.refresh();
+ };
- // ARCADE: 6 letters × (5 cols + 1 spacing) = 36 blocks, but last letter no spacing = 35 blocks × 15px = 525px
- x = Math.floor((800 - 525) / 2 / snakeSize) * snakeSize;
- y = Math.floor(380 / snakeSize) * snakeSize;
- 'ARCADE'.split('').forEach(char => {
- x = drawLetter(char, x, y, 0xff00ff, true);
+ // Gorila del logo (pantalla inicio)
+ drawTex("gorilla_logo", 96, 96, (ctx, w, h) => {
+ ctx.save();
+ const cx = 48, cy = 48;
+ // Sombra
+ ctx.fillStyle = "rgba(0,0,0,0.4)";
+ ctx.beginPath();
+ ctx.ellipse(cx, 86, 24, 8, 0, 0, Math.PI*2);
+ ctx.fill();
+ // Piernas
+ const legGrad = ctx.createLinearGradient(0, 70, 0, 82);
+ legGrad.addColorStop(0, "#5a3a22");
+ legGrad.addColorStop(1, "#3e2816");
+ ctx.fillStyle = legGrad;
+ ctx.beginPath();
+ ctx.ellipse(38, 74, 8, 12, 0.15, 0, Math.PI*2);
+ ctx.fill();
+ ctx.beginPath();
+ ctx.ellipse(58, 74, 8, 12, -0.15, 0, Math.PI*2);
+ ctx.fill();
+ // Cuerpo
+ const bodyGrad = ctx.createRadialGradient(cx, 50, 10, cx, 55, 24);
+ bodyGrad.addColorStop(0, "#6b4423");
+ bodyGrad.addColorStop(0.6, "#4a2f1a");
+ bodyGrad.addColorStop(1, "#2e1c0f");
+ ctx.fillStyle = bodyGrad;
+ ctx.beginPath();
+ ctx.ellipse(cx, 55, 22, 20, 0, 0, Math.PI*2);
+ ctx.fill();
+ // Pecho
+ const chestGrad = ctx.createRadialGradient(cx, 55, 5, cx, 58, 14);
+ chestGrad.addColorStop(0, "#8d6a4c");
+ chestGrad.addColorStop(1, "#6b4423");
+ ctx.fillStyle = chestGrad;
+ ctx.beginPath();
+ ctx.ellipse(cx, 58, 14, 11, 0, 0, Math.PI*2);
+ ctx.fill();
+ // Brazos
+ const armGrad = ctx.createLinearGradient(0, 48, 0, 72);
+ armGrad.addColorStop(0, "#5a3a22");
+ armGrad.addColorStop(1, "#3e2816");
+ ctx.fillStyle = armGrad;
+ ctx.beginPath();
+ ctx.ellipse(26, 58, 8, 22, 0.25, 0, Math.PI*2);
+ ctx.fill();
+ ctx.beginPath();
+ ctx.ellipse(23, 74, 6, 8, 0, 0, Math.PI*2);
+ ctx.fill();
+ ctx.beginPath();
+ ctx.ellipse(70, 58, 8, 22, -0.25, 0, Math.PI*2);
+ ctx.fill();
+ ctx.beginPath();
+ ctx.ellipse(73, 74, 6, 8, 0, 0, Math.PI*2);
+ ctx.fill();
+ // Hombros
+ ctx.fillStyle = bodyGrad;
+ ctx.beginPath();
+ ctx.ellipse(34, 46, 10, 9, -0.2, 0, Math.PI*2);
+ ctx.fill();
+ ctx.beginPath();
+ ctx.ellipse(62, 46, 10, 9, 0.2, 0, Math.PI*2);
+ ctx.fill();
+ // Cabeza
+ const headGrad = ctx.createRadialGradient(cx, 32, 8, cx, 34, 20);
+ headGrad.addColorStop(0, "#8f6241");
+ headGrad.addColorStop(1, "#543720");
+ ctx.fillStyle = headGrad;
+ ctx.beginPath();
+ ctx.ellipse(cx, 34, 20, 18, 0, 0, Math.PI*2);
+ ctx.fill();
+ // Orejas
+ ctx.fillStyle = "#6b4423";
+ ctx.beginPath();
+ ctx.ellipse(30, 32, 6, 7, 0, 0, Math.PI*2);
+ ctx.fill();
+ ctx.beginPath();
+ ctx.ellipse(66, 32, 6, 7, 0, 0, Math.PI*2);
+ ctx.fill();
+ // Hocico
+ const muzzleGrad = ctx.createRadialGradient(cx, 42, 4, cx, 44, 12);
+ muzzleGrad.addColorStop(0, "#caa17d");
+ muzzleGrad.addColorStop(1, "#8d6a4c");
+ ctx.fillStyle = muzzleGrad;
+ ctx.beginPath();
+ ctx.ellipse(cx, 44, 12, 10, 0, 0, Math.PI*2);
+ ctx.fill();
+ // Nariz
+ ctx.fillStyle = "#2e1c0f";
+ ctx.beginPath();
+ ctx.ellipse(cx, 42, 4, 3, 0, 0, Math.PI*2);
+ ctx.fill();
+ // Ojos
+ ctx.fillStyle = "#ffffff";
+ ctx.beginPath();
+ ctx.ellipse(40, 34, 4, 5, 0, 0, Math.PI*2);
+ ctx.fill();
+ ctx.beginPath();
+ ctx.ellipse(56, 34, 4, 5, 0, 0, Math.PI*2);
+ ctx.fill();
+ // Pupilas
+ ctx.fillStyle = "#1a1a1a";
+ ctx.beginPath();
+ ctx.arc(40, 35, 2, 0, Math.PI*2);
+ ctx.fill();
+ ctx.beginPath();
+ ctx.arc(56, 35, 2, 0, Math.PI*2);
+ ctx.fill();
+ // Cejas
+ ctx.strokeStyle = "#2e1c0f";
+ ctx.lineWidth = 3;
+ ctx.lineCap = "round";
+ ctx.beginPath();
+ ctx.moveTo(34, 30);
+ ctx.quadraticCurveTo(38, 28, 44, 30);
+ ctx.stroke();
+ ctx.beginPath();
+ ctx.moveTo(52, 30);
+ ctx.quadraticCurveTo(56, 28, 62, 30);
+ ctx.stroke();
+ // Boca
+ ctx.strokeStyle = "#4a2f1a";
+ ctx.lineWidth = 2;
+ ctx.beginPath();
+ ctx.arc(cx, 48, 6, 0.2, Math.PI - 0.2);
+ ctx.stroke();
+ ctx.restore();
});
- // Score display
- scoreText = this.add.text(16, 16, 'Score: 0', {
- fontSize: '24px',
- fontFamily: 'Arial, sans-serif',
- color: '#00ff00'
+ // Gorila del juego (más pequeño)
+ drawTex("gorilla32b", 48, 48, (ctx, w, h) => {
+ ctx.save();
+ const cx = 24, cy = 24;
+ // Sombra
+ ctx.fillStyle = "rgba(0,0,0,0.4)";
+ ctx.beginPath();
+ ctx.ellipse(cx, 43, 12, 4, 0, 0, Math.PI*2);
+ ctx.fill();
+ // Piernas
+ const legGrad = ctx.createLinearGradient(0, 35, 0, 41);
+ legGrad.addColorStop(0, "#5a3a22");
+ legGrad.addColorStop(1, "#3e2816");
+ ctx.fillStyle = legGrad;
+ ctx.beginPath();
+ ctx.ellipse(19, 37, 4, 6, 0.15, 0, Math.PI*2);
+ ctx.fill();
+ ctx.beginPath();
+ ctx.ellipse(29, 37, 4, 6, -0.15, 0, Math.PI*2);
+ ctx.fill();
+ // Cuerpo
+ const bodyGrad = ctx.createRadialGradient(cx, 25, 5, cx, 28, 12);
+ bodyGrad.addColorStop(0, "#6b4423");
+ bodyGrad.addColorStop(0.6, "#4a2f1a");
+ bodyGrad.addColorStop(1, "#2e1c0f");
+ ctx.fillStyle = bodyGrad;
+ ctx.beginPath();
+ ctx.ellipse(cx, 28, 11, 10, 0, 0, Math.PI*2);
+ ctx.fill();
+ // Pecho
+ const chestGrad = ctx.createRadialGradient(cx, 28, 2, cx, 29, 7);
+ chestGrad.addColorStop(0, "#8d6a4c");
+ chestGrad.addColorStop(1, "#6b4423");
+ ctx.fillStyle = chestGrad;
+ ctx.beginPath();
+ ctx.ellipse(cx, 29, 7, 5, 0, 0, Math.PI*2);
+ ctx.fill();
+ // Brazos
+ const armGrad = ctx.createLinearGradient(0, 24, 0, 36);
+ armGrad.addColorStop(0, "#5a3a22");
+ armGrad.addColorStop(1, "#3e2816");
+ ctx.fillStyle = armGrad;
+ ctx.beginPath();
+ ctx.ellipse(13, 29, 4, 11, 0.25, 0, Math.PI*2);
+ ctx.fill();
+ ctx.beginPath();
+ ctx.ellipse(11, 37, 3, 4, 0, 0, Math.PI*2);
+ ctx.fill();
+ ctx.beginPath();
+ ctx.ellipse(35, 29, 4, 11, -0.25, 0, Math.PI*2);
+ ctx.fill();
+ ctx.beginPath();
+ ctx.ellipse(37, 37, 3, 4, 0, 0, Math.PI*2);
+ ctx.fill();
+ // Hombros
+ ctx.fillStyle = bodyGrad;
+ ctx.beginPath();
+ ctx.ellipse(17, 23, 5, 4, -0.2, 0, Math.PI*2);
+ ctx.fill();
+ ctx.beginPath();
+ ctx.ellipse(31, 23, 5, 4, 0.2, 0, Math.PI*2);
+ ctx.fill();
+ // Cabeza
+ const headGrad = ctx.createRadialGradient(cx, 16, 4, cx, 17, 9);
+ headGrad.addColorStop(0, "#8f6241");
+ headGrad.addColorStop(1, "#543720");
+ ctx.fillStyle = headGrad;
+ ctx.beginPath();
+ ctx.ellipse(cx, 17, 10, 8, 0, 0, Math.PI*2);
+ ctx.fill();
+ // Orejas
+ ctx.fillStyle = "#6b4423";
+ ctx.beginPath();
+ ctx.ellipse(18, 16, 3, 4, 0, 0, Math.PI*2);
+ ctx.fill();
+ ctx.beginPath();
+ ctx.ellipse(30, 16, 3, 4, 0, 0, Math.PI*2);
+ ctx.fill();
+ // Hocico
+ const muzzleGrad = ctx.createRadialGradient(cx, 20, 2, cx, 22, 6);
+ muzzleGrad.addColorStop(0, "#caa17d");
+ muzzleGrad.addColorStop(1, "#8d6a4c");
+ ctx.fillStyle = muzzleGrad;
+ ctx.beginPath();
+ ctx.ellipse(cx, 22, 6, 5, 0, 0, Math.PI*2);
+ ctx.fill();
+ // Nariz
+ ctx.fillStyle = "#2e1c0f";
+ ctx.beginPath();
+ ctx.ellipse(cx, 20, 2, 1.5, 0, 0, Math.PI*2);
+ ctx.fill();
+ // Ojos
+ ctx.fillStyle = "#ffffff";
+ ctx.beginPath();
+ ctx.ellipse(20, 17, 2, 2.5, 0, 0, Math.PI*2);
+ ctx.fill();
+ ctx.beginPath();
+ ctx.ellipse(28, 17, 2, 2.5, 0, 0, Math.PI*2);
+ ctx.fill();
+ // Pupilas
+ ctx.fillStyle = "#1a1a1a";
+ ctx.beginPath();
+ ctx.arc(20, 17.5, 1, 0, Math.PI*2);
+ ctx.fill();
+ ctx.beginPath();
+ ctx.arc(28, 17.5, 1, 0, Math.PI*2);
+ ctx.fill();
+ // Cejas
+ ctx.strokeStyle = "#2e1c0f";
+ ctx.lineWidth = 1.5;
+ ctx.lineCap = "round";
+ ctx.beginPath();
+ ctx.moveTo(18, 14);
+ ctx.quadraticCurveTo(20, 13.5, 23, 14);
+ ctx.stroke();
+ ctx.beginPath();
+ ctx.moveTo(25, 14);
+ ctx.quadraticCurveTo(27, 13.5, 30, 14);
+ ctx.stroke();
+ // Boca
+ ctx.strokeStyle = "#4a2f1a";
+ ctx.lineWidth = 1.5;
+ ctx.beginPath();
+ ctx.arc(cx, 24, 3, 0.2, Math.PI - 0.2);
+ ctx.stroke();
+ ctx.restore();
});
- // Instructions
- this.add.text(400, 560, 'Use Joystick to Move | Avoid Walls, Yourself & The Title!', {
- fontSize: '16px',
- fontFamily: 'Arial, sans-serif',
- color: '#888888',
- align: 'center'
- }).setOrigin(0.5);
+ // Banana realista - forma alargada en C
+ drawTex("banana32", 52, 20, (ctx, w, h) => {
+ ctx.save();
+ // Gradiente amarillo más natural
+ const mainGrad = ctx.createLinearGradient(6, 4, 46, 16);
+ mainGrad.addColorStop(0, "#F4E04D");
+ mainGrad.addColorStop(0.15, "#FFF176");
+ mainGrad.addColorStop(0.4, "#FFD54F");
+ mainGrad.addColorStop(0.7, "#FFC107");
+ mainGrad.addColorStop(0.9, "#FFB300");
+ mainGrad.addColorStop(1, "#D4A017");
+ ctx.fillStyle = mainGrad;
+
+ // Forma alargada de plátano en C
+ ctx.beginPath();
+ // Empezar desde el tallo (izquierda superior)
+ ctx.moveTo(6, 8);
+ // Curva superior (parte convexa del plátano)
+ ctx.bezierCurveTo(10, 3, 24, 2, 38, 4);
+ ctx.bezierCurveTo(44, 5, 48, 7, 46, 10);
+ // Punta derecha
+ ctx.lineTo(45, 11);
+ // Curva inferior (parte cóncava del plátano)
+ ctx.bezierCurveTo(43, 13, 28, 17, 14, 16);
+ ctx.bezierCurveTo(8, 15, 5, 12, 6, 8);
+ ctx.closePath();
+ ctx.fill();
+
+ // Sombra en el lado cóncavo
+ const shadowGrad = ctx.createLinearGradient(12, 8, 12, 16);
+ shadowGrad.addColorStop(0, "rgba(255, 160, 0, 0)");
+ shadowGrad.addColorStop(0.5, "rgba(184, 134, 11, 0.25)");
+ shadowGrad.addColorStop(1, "rgba(139, 101, 8, 0.4)");
+ ctx.fillStyle = shadowGrad;
+ ctx.beginPath();
+ ctx.moveTo(10, 10);
+ ctx.quadraticCurveTo(20, 14, 32, 13);
+ ctx.quadraticCurveTo(38, 12, 42, 11);
+ ctx.quadraticCurveTo(30, 12, 18, 12);
+ ctx.quadraticCurveTo(13, 11, 10, 10);
+ ctx.fill();
+
+ // Brillo en la parte convexa
+ const highlightGrad = ctx.createRadialGradient(28, 6, 1, 28, 6, 12);
+ highlightGrad.addColorStop(0, "rgba(255, 255, 255, 0.7)");
+ highlightGrad.addColorStop(0.5, "rgba(255, 255, 220, 0.3)");
+ highlightGrad.addColorStop(1, "rgba(255, 255, 255, 0)");
+ ctx.fillStyle = highlightGrad;
+ ctx.beginPath();
+ ctx.ellipse(28, 7, 10, 3, -0.2, 0, Math.PI * 2);
+ ctx.fill();
+
+ // Manchas marrones características (más realistas)
+ ctx.fillStyle = "rgba(101, 67, 33, 0.35)";
+ // Mancha 1
+ ctx.beginPath();
+ ctx.ellipse(18, 10, 2.5, 1.2, 0.3, 0, Math.PI * 2);
+ ctx.fill();
+ // Mancha 2
+ ctx.beginPath();
+ ctx.ellipse(28, 12, 1.8, 1, -0.2, 0, Math.PI * 2);
+ ctx.fill();
+ // Mancha 3
+ ctx.beginPath();
+ ctx.ellipse(36, 9, 2, 1, 0.4, 0, Math.PI * 2);
+ ctx.fill();
+ // Mancha 4 (pequeña)
+ ctx.beginPath();
+ ctx.ellipse(14, 13, 1.2, 0.7, 0.6, 0, Math.PI * 2);
+ ctx.fill();
+ // Mancha 5 (pequeña)
+ ctx.beginPath();
+ ctx.ellipse(40, 11, 1.5, 0.8, -0.5, 0, Math.PI * 2);
+ ctx.fill();
+
+ // Tallo en el extremo izquierdo
+ ctx.fillStyle = "#8B7355";
+ ctx.beginPath();
+ ctx.moveTo(5, 8);
+ ctx.lineTo(3, 7);
+ ctx.lineTo(3, 9);
+ ctx.lineTo(5, 9);
+ ctx.fill();
+
+ // Contorno sutil
+ ctx.strokeStyle = "#B8860B";
+ ctx.lineWidth = 0.8;
+ ctx.beginPath();
+ ctx.moveTo(6, 8);
+ ctx.bezierCurveTo(10, 3, 24, 2, 38, 4);
+ ctx.bezierCurveTo(44, 5, 48, 7, 46, 10);
+ ctx.lineTo(45, 11);
+ ctx.bezierCurveTo(43, 13, 28, 17, 14, 16);
+ ctx.bezierCurveTo(8, 15, 5, 12, 6, 8);
+ ctx.stroke();
+
+ // Línea central característica del plátano
+ ctx.strokeStyle = "rgba(184, 134, 11, 0.3)";
+ ctx.lineWidth = 0.5;
+ ctx.beginPath();
+ ctx.moveTo(8, 10);
+ ctx.quadraticCurveTo(24, 9, 42, 10);
+ ctx.stroke();
+
+ ctx.restore();
+ });
- // Initialize snake (start top left)
- snake = [
- { x: 75, y: 60 },
- { x: 60, y: 60 },
- { x: 45, y: 60 }
- ];
+ // Oficinistas/Agentes
+ const agentTex = (key, suitColor, stressed) => {
+ drawTex(key, 48, 48, (ctx, w, h) => {
+ ctx.save();
+ const cx = 24;
+ // Piernas
+ ctx.fillStyle = stressed ? "#1a1a1a" : "#2c2c2c";
+ ctx.fillRect(16, 34, 6, 12);
+ ctx.fillRect(26, 34, 6, 12);
+ // Zapatos
+ ctx.fillStyle = "#000000";
+ ctx.fillRect(14, 44, 8, 3);
+ ctx.fillRect(26, 44, 8, 3);
+ // Traje
+ const suitGrad = ctx.createLinearGradient(cx, 20, cx, 36);
+ suitGrad.addColorStop(0, suitColor);
+ suitGrad.addColorStop(1, stressed ? "#1a0000" : "#000033");
+ ctx.fillStyle = suitGrad;
+ ctx.fillRect(14, 20, 20, 16);
+ // Camisa
+ ctx.fillStyle = "#f5f5f5";
+ ctx.beginPath();
+ ctx.moveTo(24, 22);
+ ctx.lineTo(20, 26);
+ ctx.lineTo(20, 34);
+ ctx.lineTo(28, 34);
+ ctx.lineTo(28, 26);
+ ctx.closePath();
+ ctx.fill();
+ // Corbata
+ ctx.fillStyle = stressed ? "#8B0000" : "#003366";
+ ctx.beginPath();
+ ctx.moveTo(24, 24);
+ ctx.lineTo(22, 26);
+ ctx.lineTo(22, 33);
+ ctx.lineTo(24, 34);
+ ctx.lineTo(26, 33);
+ ctx.lineTo(26, 26);
+ ctx.closePath();
+ ctx.fill();
+ // Brazos
+ ctx.fillStyle = suitColor;
+ ctx.fillRect(10, 24, 4, 10);
+ ctx.fillRect(34, 24, 4, 10);
+ // Manos
+ ctx.fillStyle = "#ffd4a3";
+ ctx.beginPath();
+ ctx.arc(12, 34, 3, 0, Math.PI * 2);
+ ctx.fill();
+ ctx.beginPath();
+ ctx.arc(36, 34, 3, 0, Math.PI * 2);
+ ctx.fill();
+ // Cabeza
+ const skinGrad = ctx.createRadialGradient(cx, 14, 3, cx, 14, 8);
+ skinGrad.addColorStop(0, "#ffe0bd");
+ skinGrad.addColorStop(1, "#ffd4a3");
+ ctx.fillStyle = skinGrad;
+ ctx.beginPath();
+ ctx.arc(cx, 14, 8, 0, Math.PI * 2);
+ ctx.fill();
+ // Pelo
+ ctx.fillStyle = stressed ? "#2c2c2c" : "#3d2817";
+ ctx.beginPath();
+ ctx.ellipse(cx, 10, 8, 4, 0, Math.PI, Math.PI * 2);
+ ctx.fill();
+ // Ojos y expresión
+ if (stressed) {
+ ctx.fillStyle = "#ffffff";
+ ctx.beginPath();
+ ctx.arc(20, 14, 3, 0, Math.PI * 2);
+ ctx.fill();
+ ctx.beginPath();
+ ctx.arc(28, 14, 3, 0, Math.PI * 2);
+ ctx.fill();
+ ctx.fillStyle = "#000000";
+ ctx.beginPath();
+ ctx.arc(20, 14, 1.5, 0, Math.PI * 2);
+ ctx.fill();
+ ctx.beginPath();
+ ctx.arc(28, 14, 1.5, 0, Math.PI * 2);
+ ctx.fill();
+ ctx.strokeStyle = "#000000";
+ ctx.lineWidth = 1.5;
+ ctx.beginPath();
+ ctx.arc(cx, 17, 3, 0, Math.PI);
+ ctx.stroke();
+ } else {
+ ctx.fillStyle = "#ffffff";
+ ctx.beginPath();
+ ctx.ellipse(20, 14, 2, 3, 0, 0, Math.PI * 2);
+ ctx.fill();
+ ctx.beginPath();
+ ctx.ellipse(28, 14, 2, 3, 0, 0, Math.PI * 2);
+ ctx.fill();
+ ctx.fillStyle = "#1a1a1a";
+ ctx.beginPath();
+ ctx.arc(20, 14, 1, 0, Math.PI * 2);
+ ctx.fill();
+ ctx.beginPath();
+ ctx.arc(28, 14, 1, 0, Math.PI * 2);
+ ctx.fill();
+ ctx.strokeStyle = "#2c2c2c";
+ ctx.lineWidth = 1.5;
+ ctx.beginPath();
+ ctx.moveTo(18, 12);
+ ctx.lineTo(20, 13);
+ ctx.stroke();
+ ctx.beginPath();
+ ctx.moveTo(30, 12);
+ ctx.lineTo(28, 13);
+ ctx.stroke();
+ ctx.strokeStyle = "#000000";
+ ctx.lineWidth = 1;
+ ctx.beginPath();
+ ctx.moveTo(20, 19);
+ ctx.lineTo(28, 19);
+ ctx.stroke();
+ // Maletín
+ ctx.fillStyle = "#4a3428";
+ ctx.fillRect(8, 30, 4, 3);
+ ctx.strokeStyle = "#2c1810";
+ ctx.lineWidth = 0.5;
+ ctx.strokeRect(8, 30, 4, 3);
+ }
+ ctx.restore();
+ });
+ };
- // Spawn initial food
- spawnFood();
+ agentTex("ghost_red32", "#8B0000", false);
+ agentTex("ghost_blue32", "#4169E1", true);
- // Keyboard and Arcade Button input
- this.input.keyboard.on('keydown', (event) => {
- // Normalize keyboard input to arcade codes for easier testing
- const key = KEYBOARD_TO_ARCADE[event.key] || event.key;
+ // Tiles de pared por nivel
+ for (let level = 1; level <= 3; level++) {
+ drawTex(`wall_tile_${level}`, 16, 16, (ctx, w, h) => {
+ ctx.fillStyle = LEVEL_CONFIG[level].wallColor;
+ ctx.fillRect(0, 0, w, h);
+ ctx.strokeStyle = LEVEL_CONFIG[level].wallStroke;
+ ctx.lineWidth = 2;
+ ctx.strokeRect(1, 1, w-2, h-2);
+ ctx.strokeStyle = "rgba(255,255,255,0.15)";
+ ctx.beginPath();
+ ctx.moveTo(2, 2);
+ ctx.lineTo(w-3, 2);
+ ctx.lineTo(w-3, h-3);
+ ctx.stroke();
+ });
+ }
+}
- // Restart game (arcade buttons only)
- if (gameOver && (key === 'P1A' || key === 'START1')) {
- restartGame(scene);
- return;
+// ================ PANTALLA DE INICIO ================
+function showStartScreen(scene) {
+ gameState = 'start';
+
+ // IMPORTANTE: Limpiar completamente el estado del juego
+ cleanupGame(scene);
+
+ // Limpiar escena visual
+ scene.children.removeAll();
+
+ // Fondo
+ scene.add.rectangle(W/2, H/2, W, H, 0x000000);
+
+ // Logo gorila
+ const logo = scene.add.image(W/2, 180, "gorilla_logo");
+ logo.setScale(1.5);
+
+ // Título
+ scene.add.text(W/2, 330, "PLAT-MAN", {
+ fontSize: "64px",
+ color: "#FFD700",
+ fontStyle: "bold",
+ fontFamily: "Arial"
+ }).setOrigin(0.5);
+
+ // Descripción
+ scene.add.text(W/2, 400, "¡Escapa de los agentes corporativos!", {
+ fontSize: "20px",
+ color: "#ffffff",
+ fontFamily: "Arial"
+ }).setOrigin(0.5);
+
+ // Instrucciones
+ scene.add.text(W/2, 460, "Controles: Flechas o WASD", {
+ fontSize: "18px",
+ color: "#cccccc",
+ fontFamily: "Arial"
+ }).setOrigin(0.5);
+
+ // Botón start
+ const startText = scene.add.text(W/2, 520, "PRESIONA ESPACIO PARA COMENZAR", {
+ fontSize: "18px",
+ color: "#cccccc",
+ fontFamily: "Arial"
+ }).setOrigin(0.5);
+
+ // Parpadeo
+ scene.tweens.add({
+ targets: startText,
+ alpha: 0.3,
+ duration: 800,
+ yoyo: true,
+ repeat: -1
+ });
+
+ // Detectar ESPACIO o ENTER
+ scene.input.keyboard.once('keydown-SPACE', () => {
+ if (scene.sound.context && scene.sound.context.state === 'suspended') {
+ scene.sound.context.resume();
}
-
- // Direction controls (keyboard keys get mapped to arcade codes)
- if (key === 'P1U' && direction.y === 0) {
- nextDirection = { x: 0, y: -1 };
- } else if (key === 'P1D' && direction.y === 0) {
- nextDirection = { x: 0, y: 1 };
- } else if (key === 'P1L' && direction.x === 0) {
- nextDirection = { x: -1, y: 0 };
- } else if (key === 'P1R' && direction.x === 0) {
- nextDirection = { x: 1, y: 0 };
+ startGame(scene);
+ });
+
+ scene.input.keyboard.once('keydown-ENTER', () => {
+ if (scene.sound.context && scene.sound.context.state === 'suspended') {
+ scene.sound.context.resume();
}
+ startGame(scene);
});
-
- playTone(this, 440, 0.1);
}
-function drawLetter(char, startX, startY, color, useBold = false) {
- const pattern = useBold ? boldLetters[char] : letters[char];
- if (!pattern) return startX + 30;
-
- for (let row = 0; row < pattern.length; row++) {
- for (let col = 0; col < pattern[row].length; col++) {
- if (pattern[row][col]) {
- const blockX = startX + col * snakeSize;
- const blockY = startY + row * snakeSize;
- titleBlocks.push({ x: blockX, y: blockY, color: color });
- }
- }
+// ================ LIMPIAR JUEGO COMPLETO ================
+function cleanupGame(scene) {
+ // Detener música
+ stopBackgroundMusic();
+
+ // Reanudar física si estaba pausada
+ if (scene.physics && scene.physics.world) {
+ scene.physics.resume();
+ }
+
+ // Limpiar todos los event listeners del teclado
+ if (scene.input && scene.input.keyboard) {
+ scene.input.keyboard.removeAllListeners();
+ }
+
+ // Limpiar grupos de física
+ if (bananas) {
+ bananas.clear(true, true);
+ bananas = null;
}
- return startX + (pattern[0].length + 1) * snakeSize;
+ if (ghosts) {
+ ghosts.clear(true, true);
+ ghosts = null;
+ }
+ if (wallBodies) {
+ wallBodies.clear(true, true);
+ wallBodies = null;
+ }
+ if (wallVisuals) {
+ wallVisuals.clear(true, true);
+ wallVisuals = null;
+ }
+
+ // Limpiar colisores
+ if (scene.playerWallCollider) {
+ scene.physics.world.removeCollider(scene.playerWallCollider);
+ scene.playerWallCollider = null;
+ }
+ if (scene.ghostWallCollider) {
+ scene.physics.world.removeCollider(scene.ghostWallCollider);
+ scene.ghostWallCollider = null;
+ }
+ if (scene.ghostGhostCollider) {
+ scene.physics.world.removeCollider(scene.ghostGhostCollider);
+ scene.ghostGhostCollider = null;
+ }
+
+ // Resetear variables globales
+ player = null;
+ cursors = null;
+ wasdKeys = null;
+ bgRect = null;
+ scoreText = null;
+ levelText = null;
+ currentLevel = 1;
+ score = 0;
+ startTime = 0;
+ powerMode = false;
+ powerExpiresAt = 0;
+ isGameOver = false;
+ rankingInputs = [];
+ currentInitials = ['A', 'A', 'A'];
+ selectedIndex = 0;
+ finalScore = 0;
+ wonGame = false;
}
-function update(_time, delta) {
- if (gameOver) return;
+// ================ INICIAR JUEGO ================
+function startGame(scene) {
+ // Asegurar que todo esté limpio
+ cleanupGame(scene);
+
+ gameState = 'playing';
+ currentLevel = 1;
+ score = 0;
+ isGameOver = false;
+ powerMode = false;
+ powerExpiresAt = 0;
+
+ // Limpiar escena visual
+ scene.children.removeAll();
+
+ // Reanudar física
+ scene.physics.resume();
+
+ // Música de fondo
+ createBackgroundMusic(scene);
+
+ // Fondo
+ bgRect = scene.add.rectangle(W/2, H/2, W-20, H-20, LEVEL_CONFIG[1].bgColor)
+ .setStrokeStyle(2, 0x3333ff);
+
+ // Paredes
+ createWalls(scene);
+
+ // Jugador
+ player = scene.add.image(100, 100, "gorilla32b");
+ player.setScale(2.0);
+ scene.physics.add.existing(player);
+ player.body.setCollideWorldBounds(true);
+ player.body.setCircle(20, player.width/2 - 20, player.height/2 - 20);
+ player.body.setVelocity(0, 0); // Asegurar velocidad 0
+ player.baseY = player.y;
+ player.idleOffset = 0;
+
+ // Bananas
+ createBananas(scene);
+
+ // Agentes
+ createGhosts(scene);
+
+ // Colisiones
+ setupCollisions(scene);
+
+ // HUD
+ scoreText = scene.add.text(16, 12, "Puntaje: 0", {
+ fontSize: "20px",
+ fill: "#ffffff"
+ });
+ levelText = scene.add.text(W - 16, 12, "Etapa 1", {
+ fontSize: "20px",
+ fill: "#ffffff"
+ }).setOrigin(1, 0);
+ scene.add.text(W/2, H-24, "Flechas/WASD: mover • R: reiniciar", {
+ fontSize: "14px",
+ fill: "#cccccc"
+ }).setOrigin(0.5, 1);
+
+ // Controles
+ cursors = scene.input.keyboard.createCursorKeys();
+ wasdKeys = scene.input.keyboard.addKeys({
+ up: Phaser.Input.Keyboard.KeyCodes.W,
+ down: Phaser.Input.Keyboard.KeyCodes.S,
+ left: Phaser.Input.Keyboard.KeyCodes.A,
+ right: Phaser.Input.Keyboard.KeyCodes.D
+ });
+
+ // Reinicio con R
+ scene.input.keyboard.on("keydown-R", () => {
+ showStartScreen(scene);
+ });
+
+ startTime = scene.time.now;
+}
- moveTimer += delta;
- if (moveTimer >= moveDelay) {
- moveTimer = 0;
- direction = nextDirection;
- moveSnake(this);
+// ================ MÚSICA DE FONDO ================
+function createBackgroundMusic(scene) {
+ try {
+ bgMusicTimer = scene.time.addEvent({
+ delay: 500,
+ callback: () => {
+ if (!isGameOver && scene.sound && scene.sound.context) {
+ try {
+ const audioContext = scene.sound.context;
+ if (audioContext && audioContext.state === 'running') {
+ const notes = [262, 330, 392, 330];
+ const noteIndex = (bgMusicTimer.repeatCount % notes.length);
+ const oscillator = audioContext.createOscillator();
+ const gainNode = audioContext.createGain();
+ oscillator.frequency.value = notes[noteIndex];
+ gainNode.gain.value = 0.05;
+ oscillator.connect(gainNode);
+ gainNode.connect(audioContext.destination);
+ oscillator.start();
+ oscillator.stop(audioContext.currentTime + 0.1);
+ }
+ } catch(e) {}
+ }
+ },
+ loop: true
+ });
+ } catch(e) {
+ console.log("Audio no disponible");
}
-
- drawGame();
}
-function moveSnake(scene) {
- const head = snake[0];
- const newHead = {
- x: head.x + direction.x * snakeSize,
- y: head.y + direction.y * snakeSize
- };
-
- // Check wall collision
- if (newHead.x < 0 || newHead.x >= 800 || newHead.y < 0 || newHead.y >= 600) {
- endGame(scene);
- return;
+function stopBackgroundMusic() {
+ if (bgMusicTimer) {
+ bgMusicTimer.remove();
+ bgMusicTimer = null;
}
+}
- // Check self collision
- for (let segment of snake) {
- if (segment.x === newHead.x && segment.y === newHead.y) {
- endGame(scene);
- return;
+// ================ CREAR PAREDES ================
+function createWalls(scene) {
+ if (wallBodies) wallBodies.clear(true, true);
+ if (wallVisuals) wallVisuals.clear(true, true);
+
+ wallBodies = scene.physics.add.staticGroup();
+ wallVisuals = scene.add.group();
+
+ const walls = LEVEL_CONFIG[currentLevel].walls;
+ walls.forEach(([x, y, w, h]) => {
+ // Cuerpo físico invisible
+ const body = scene.add.rectangle(x, y, w, h);
+ scene.physics.add.existing(body, true);
+ wallBodies.add(body);
+
+ // Visual con tiles
+ const tile = scene.add.image(0, 0, `wall_tile_${currentLevel}`);
+ const tw = tile.width;
+ const th = tile.height;
+ tile.destroy();
+
+ const cols = Math.max(1, Math.floor(w / tw));
+ const rows = Math.max(1, Math.floor(h / th));
+ const startX = x - (cols * tw) / 2 + tw / 2;
+ const startY = y - (rows * th) / 2 + th / 2;
+
+ for (let r = 0; r < rows; r++) {
+ for (let c = 0; c < cols; c++) {
+ const tileSprite = scene.add.image(
+ startX + c * tw,
+ startY + r * th,
+ `wall_tile_${currentLevel}`
+ );
+ wallVisuals.add(tileSprite);
+ }
}
- }
+ });
+}
- // Check title block collision
- for (let block of titleBlocks) {
- if (newHead.x === block.x && newHead.y === block.y) {
- endGame(scene);
- return;
- }
+// ================ CREAR BANANAS ================
+function createBananas(scene) {
+ if (bananas) bananas.clear(true, true);
+ bananas = scene.physics.add.group();
+
+ for (let i = 0; i < BANANAS_TOTAL; i++) {
+ const p = randomPoint();
+ const b = scene.add.image(p.x, p.y, "banana32");
+ b.setScale(1.4); // Ajustado para el nuevo diseño alargado
+ scene.physics.add.existing(b);
+ bananas.add(b);
}
+}
- snake.unshift(newHead);
-
- // Check food collision
- if (newHead.x === food.x && newHead.y === food.y) {
- score += 10;
- scoreText.setText('Score: ' + score);
- spawnFood();
- playTone(scene, 880, 0.1);
+// ================ CREAR AGENTES ================
+function createGhosts(scene) {
+ if (ghosts) ghosts.clear(true, true);
+ ghosts = scene.physics.add.group();
+
+ for (let i = 0; i < GHOST_COUNT; i++) {
+ const p = randomPoint();
+ const g = scene.add.image(p.x, p.y, "ghost_red32");
+ g.setScale(1.6);
+ scene.physics.add.existing(g);
+ g.body.setCollideWorldBounds(true);
+ g.body.setBounce(1, 1);
+ g.eaten = false;
+ g.respawnAt = 0;
+ g.nextDirChange = scene.time.now;
+ setGhostVelocity(g, scene);
+ ghosts.add(g);
+ }
+}
- if (moveDelay > 50) { // Faster max speed (was 80ms)
- moveDelay -= 2;
- }
- } else {
- snake.pop();
+// ================ COLISIONES ================
+function setupCollisions(scene) {
+ if (scene.playerWallCollider) {
+ scene.physics.world.removeCollider(scene.playerWallCollider);
+ }
+ if (scene.ghostWallCollider) {
+ scene.physics.world.removeCollider(scene.ghostWallCollider);
}
+ if (scene.ghostGhostCollider) {
+ scene.physics.world.removeCollider(scene.ghostGhostCollider);
+ }
+
+ scene.playerWallCollider = scene.physics.add.collider(player, wallBodies);
+ scene.ghostWallCollider = scene.physics.add.collider(ghosts, wallBodies);
+ scene.ghostGhostCollider = scene.physics.add.collider(ghosts, ghosts);
}
-function spawnFood() {
- let valid = false;
- let attempts = 0;
-
- while (!valid && attempts < 100) {
- attempts++;
- const gridX = Math.floor(Math.random() * 53) * snakeSize;
- const gridY = Math.floor(Math.random() * 40) * snakeSize;
-
- // Check not on snake
- let onSnake = false;
- for (let segment of snake) {
- if (segment.x === gridX && segment.y === gridY) {
- onSnake = true;
- break;
+// ================ UPDATE DEL JUEGO ================
+function updateGame(scene) {
+ if (isGameOver) return;
+
+ // Movimiento del jugador
+ const speed = powerMode ? 240 : 200;
+ let velX = 0, velY = 0;
+
+ if (cursors.left.isDown || wasdKeys.left.isDown) velX = -speed;
+ if (cursors.right.isDown || wasdKeys.right.isDown) velX = speed;
+ if (cursors.up.isDown || wasdKeys.up.isDown) velY = -speed;
+ if (cursors.down.isDown || wasdKeys.down.isDown) velY = speed;
+
+ player.body.setVelocity(velX, velY);
+
+ // Animación idle
+ if (velX === 0 && velY === 0) {
+ player.idleOffset += 0.1;
+ const bobAmount = Math.sin(player.idleOffset) * 2;
+ player.y = player.baseY + bobAmount;
+ player.setRotation(0);
+ } else {
+ player.baseY = player.y;
+ player.idleOffset = 0;
+ const tilt = velX !== 0 ? (velX > 0 ? 0.15 : -0.15) : (velY > 0 ? 0.1 : -0.1);
+ player.setRotation(tilt);
+ }
+
+ // Colisión con bananas
+ scene.physics.overlap(player, bananas, (p, banana) => {
+ banana.destroy();
+ score += 10;
+ activatePowerMode(scene);
+ });
+
+ // Modo poder
+ if (powerMode && scene.time.now >= powerExpiresAt) {
+ setPowerMode(scene, false);
+ }
+
+ // Actualizar agentes
+ ghosts.getChildren().forEach(g => {
+ if (g.eaten) {
+ if (scene.time.now >= g.respawnAt) {
+ g.eaten = false;
+ const p = randomPoint();
+ g.setPosition(p.x, p.y);
+ g.setVisible(true);
+ g.setTexture(powerMode ? "ghost_blue32" : "ghost_red32");
+ setGhostVelocity(g, scene);
}
- }
-
- // Check not on title blocks
- let onTitle = false;
- for (let block of titleBlocks) {
- if (gridX === block.x && gridY === block.y) {
- onTitle = true;
- break;
+ } else {
+ if (scene.time.now >= g.nextDirChange) {
+ setGhostVelocity(g, scene);
+ g.nextDirChange = scene.time.now + Phaser.Math.Between(700, 1500);
+ }
+
+ // Colisión con agentes
+ const dist = Phaser.Math.Distance.Between(player.x, player.y, g.x, g.y);
+ if (dist < 30) {
+ if (powerMode) {
+ score += 50;
+ g.eaten = true;
+ g.setVisible(false);
+ g.body.setVelocity(0);
+ g.respawnAt = scene.time.now + 2500;
+ } else {
+ gameOver(scene, false);
+ }
}
}
-
- if (!onSnake && !onTitle) {
- food = { x: gridX, y: gridY };
- valid = true;
+ });
+
+ // Actualizar HUD
+ const survived = Math.floor((scene.time.now - startTime) / 1000);
+ const total = score + survived;
+ scoreText.setText(`Puntaje: ${total}${powerMode ? " (PODER!)" : ""}`);
+
+ // Victoria si no quedan bananas
+ if (bananas.countActive(true) === 0) {
+ if (currentLevel < 3) {
+ advanceLevel(scene);
+ } else {
+ gameOver(scene, true);
}
}
}
-function drawGame() {
- graphics.clear();
-
- // Draw title blocks
- titleBlocks.forEach(block => {
- graphics.fillStyle(block.color, 1);
- graphics.fillRect(block.x, block.y, snakeSize - 2, snakeSize - 2);
- });
-
- // Draw snake
- snake.forEach((segment, index) => {
- if (index === 0) {
- graphics.fillStyle(0x00ff00, 1);
- } else {
- graphics.fillStyle(0x00aa00, 1);
- }
- graphics.fillRect(segment.x, segment.y, snakeSize - 2, snakeSize - 2);
+// ================ MODO PODER ================
+function activatePowerMode(scene) {
+ powerMode = true;
+ powerExpiresAt = scene.time.now + POWER_DURATION_MS;
+ ghosts.getChildren().forEach(g => {
+ if (!g.eaten) g.setTexture("ghost_blue32");
});
+}
- // Draw food
- graphics.fillStyle(0xff0000, 1);
- graphics.fillRect(food.x, food.y, snakeSize - 2, snakeSize - 2);
+function setPowerMode(scene, active) {
+ powerMode = active;
+ if (!active) {
+ ghosts.getChildren().forEach(g => {
+ if (!g.eaten) g.setTexture("ghost_red32");
+ });
+ }
}
-function endGame(scene) {
- gameOver = true;
- playTone(scene, 220, 0.5);
-
- // Semi-transparent overlay
- const overlay = scene.add.graphics();
- overlay.fillStyle(0x000000, 0.7);
- overlay.fillRect(0, 0, 800, 600);
-
- // Game Over title with glow effect
- const gameOverText = scene.add.text(400, 300, 'GAME OVER', {
- fontSize: '64px',
- fontFamily: 'Arial, sans-serif',
- color: '#ff0000',
- align: 'center',
- stroke: '#ff6666',
- strokeThickness: 8
+// ================ AVANZAR NIVEL ================
+function advanceLevel(scene) {
+ currentLevel++;
+ powerMode = false;
+ powerExpiresAt = 0;
+
+ bgRect.setFillStyle(LEVEL_CONFIG[currentLevel].bgColor);
+
+ player.x = 100;
+ player.y = 100;
+ player.baseY = player.y;
+ player.setRotation(0);
+
+ createWalls(scene);
+ createBananas(scene);
+ createGhosts(scene);
+ setupCollisions(scene);
+
+ levelText.setText(LEVEL_CONFIG[currentLevel].name);
+
+ const msg = scene.add.text(W/2, H/2, `¡${LEVEL_CONFIG[currentLevel].name}!`, {
+ fontSize: "48px",
+ color: "#ffffff",
+ fontStyle: "bold"
}).setOrigin(0.5);
-
- // Pulsing animation for game over text
+
scene.tweens.add({
- targets: gameOverText,
- scale: { from: 1, to: 1.1 },
- alpha: { from: 1, to: 0.8 },
- duration: 800,
- yoyo: true,
- repeat: -1,
- ease: 'Sine.easeInOut'
+ targets: msg,
+ alpha: 0,
+ duration: 2000,
+ onComplete: () => msg.destroy()
});
+}
- // Score display
- scene.add.text(400, 400, 'SCORE: ' + score, {
- fontSize: '36px',
- fontFamily: 'Arial, sans-serif',
- color: '#00ffff',
- align: 'center',
- stroke: '#000000',
- strokeThickness: 4
- }).setOrigin(0.5);
+// ================ GAME OVER ================
+function gameOver(scene, win) {
+ isGameOver = true;
+ scene.physics.pause();
+ stopBackgroundMusic();
+
+ const survived = Math.floor((scene.time.now - startTime) / 1000);
+ finalScore = score + survived;
+ wonGame = win;
+
+ showRankingScreen(scene);
+}
- // Restart instruction with subtle animation
- const restartText = scene.add.text(400, 480, 'Press Button A or START to Restart', {
- fontSize: '24px',
- fontFamily: 'Arial, sans-serif',
- color: '#ffff00',
- align: 'center',
- stroke: '#000000',
- strokeThickness: 3
+// ================ PANTALLA DE RANKING ================
+function showRankingScreen(scene) {
+ gameState = 'ranking';
+
+ // Limpiar event listeners previos
+ scene.input.keyboard.removeAllListeners();
+
+ scene.children.removeAll();
+
+ scene.add.rectangle(W/2, H/2, W, H, 0x000000);
+
+ if (wonGame) {
+ scene.add.text(W/2, 80, "¡VICTORIA!", {
+ fontSize: "48px",
+ color: "#FFD700",
+ fontStyle: "bold",
+ fontFamily: "Arial"
+ }).setOrigin(0.5);
+
+ scene.add.text(W/2, 130, "¡Completaste las 3 etapas!", {
+ fontSize: "20px",
+ color: "#00ff00",
+ fontFamily: "Arial"
+ }).setOrigin(0.5);
+ } else {
+ scene.add.text(W/2, 80, "GAME OVER", {
+ fontSize: "48px",
+ color: "#ff0000",
+ fontStyle: "bold",
+ fontFamily: "Arial"
+ }).setOrigin(0.5);
+ }
+
+ const scoreY = wonGame ? 180 : 150;
+ scene.add.text(W/2, scoreY, `Tu puntuación: ${finalScore}`, {
+ fontSize: "32px",
+ color: "#ffffff",
+ fontFamily: "Arial"
+ }).setOrigin(0.5);
+
+ scene.add.text(W/2, scoreY + 40, "Ingresa tus iniciales (3 letras):", {
+ fontSize: "20px",
+ color: "#FFD700",
+ fontFamily: "Arial"
}).setOrigin(0.5);
+
+ // Crear inputs para iniciales
+ rankingInputs = [];
+ currentInitials = ['A', 'A', 'A'];
+ selectedIndex = 0;
+
+ for (let i = 0; i < 3; i++) {
+ const x = W/2 - 60 + i * 60;
+ const y = scoreY + 100;
+
+ const bg = scene.add.rectangle(x, y, 50, 60, 0x333333);
+ const txt = scene.add.text(x, y, currentInitials[i], {
+ fontSize: "40px",
+ color: "#ffffff",
+ fontFamily: "Arial"
+ }).setOrigin(0.5);
+
+ rankingInputs.push({ bg, txt, index: i });
+ }
+
+ updateRankingSelection(scene);
+
+ scene.add.text(W/2, scoreY + 180, "↑↓ cambiar letra ←→ mover ENTER confirmar", {
+ fontSize: "16px",
+ color: "#cccccc",
+ fontFamily: "Arial"
+ }).setOrigin(0.5);
+
+ // Controles - usar once para evitar múltiples registros
+ const handleUp = () => changeInitial(scene, 1);
+ const handleDown = () => changeInitial(scene, -1);
+ const handleLeft = () => moveSelection(scene, -1);
+ const handleRight = () => moveSelection(scene, 1);
+ const handleEnter = () => saveRanking(scene);
+
+ scene.input.keyboard.on('keydown-UP', handleUp);
+ scene.input.keyboard.on('keydown-DOWN', handleDown);
+ scene.input.keyboard.on('keydown-LEFT', handleLeft);
+ scene.input.keyboard.on('keydown-RIGHT', handleRight);
+ scene.input.keyboard.on('keydown-ENTER', handleEnter);
+}
- // Blinking animation for restart text
- scene.tweens.add({
- targets: restartText,
- alpha: { from: 1, to: 0.3 },
- duration: 600,
- yoyo: true,
- repeat: -1,
- ease: 'Sine.easeInOut'
+function updateRankingSelection(scene) {
+ rankingInputs.forEach((item, i) => {
+ if (i === selectedIndex) {
+ item.bg.setFillStyle(0xFFD700);
+ item.txt.setColor("#000000");
+ } else {
+ item.bg.setFillStyle(0x333333);
+ item.txt.setColor("#ffffff");
+ }
});
}
-function restartGame(scene) {
- snake = [
- { x: 75, y: 60 },
- { x: 60, y: 60 },
- { x: 45, y: 60 }
- ];
- direction = { x: 1, y: 0 };
- nextDirection = { x: 1, y: 0 };
- score = 0;
- gameOver = false;
- moveDelay = 100; // Match new faster initial speed
- scoreText.setText('Score: 0');
- spawnFood();
- scene.scene.restart();
+function changeInitial(scene, dir) {
+ if (gameState !== 'ranking') return;
+ const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
+ let idx = letters.indexOf(currentInitials[selectedIndex]);
+ idx = (idx + dir + letters.length) % letters.length;
+ currentInitials[selectedIndex] = letters[idx];
+ rankingInputs[selectedIndex].txt.setText(currentInitials[selectedIndex]);
}
-function playTone(scene, frequency, duration) {
- const audioContext = scene.sound.context;
- const oscillator = audioContext.createOscillator();
- const gainNode = audioContext.createGain();
+function moveSelection(scene, dir) {
+ if (gameState !== 'ranking') return;
+ selectedIndex = (selectedIndex + dir + 3) % 3;
+ updateRankingSelection(scene);
+}
- oscillator.connect(gainNode);
- gainNode.connect(audioContext.destination);
+function saveRanking(scene) {
+ if (gameState !== 'ranking') return;
+
+ const initials = currentInitials.join('');
+ let ranking = [];
+
+ try {
+ const saved = localStorage.getItem(RANKING_KEY);
+ if (saved) ranking = JSON.parse(saved);
+ } catch(e) {}
+
+ ranking.push({ initials, score: finalScore });
+ ranking.sort((a, b) => b.score - a.score);
+ ranking = ranking.slice(0, 5);
+
+ try {
+ localStorage.setItem(RANKING_KEY, JSON.stringify(ranking));
+ } catch(e) {}
+
+ showFinalRanking(scene, ranking);
+}
- oscillator.frequency.value = frequency;
- oscillator.type = 'square';
+function showFinalRanking(scene, ranking) {
+ // Limpiar todo antes de mostrar ranking final
+ scene.input.keyboard.removeAllListeners();
+ scene.children.removeAll();
+
+ scene.add.rectangle(W/2, H/2, W, H, 0x000000);
+
+ scene.add.text(W/2, 80, "TOP 5 RANKING", {
+ fontSize: "48px",
+ color: "#FFD700",
+ fontStyle: "bold",
+ fontFamily: "Arial"
+ }).setOrigin(0.5);
+
+ ranking.forEach((entry, i) => {
+ const y = 160 + i * 50;
+ let color = "#ffffff";
+ if (i === 0) color = "#FFD700";
+ else if (i === 1) color = "#C0C0C0";
+ else if (i === 2) color = "#CD7F32";
+
+ scene.add.text(W/2, y, `${i + 1}. ${entry.initials} - ${entry.score}`, {
+ fontSize: "32px",
+ color: color,
+ fontFamily: "Arial"
+ }).setOrigin(0.5);
+ });
+
+ scene.add.text(W/2, 450, "Presiona ESPACIO para volver al inicio", {
+ fontSize: "18px",
+ color: "#cccccc",
+ fontFamily: "Arial"
+ }).setOrigin(0.5);
+
+ scene.input.keyboard.once('keydown-SPACE', () => {
+ showStartScreen(scene);
+ });
+}
- gainNode.gain.setValueAtTime(0.1, audioContext.currentTime);
- gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + duration);
+// ================ UTILIDADES ================
+function randomPoint() {
+ return {
+ x: Phaser.Math.Between(80, W - 80),
+ y: Phaser.Math.Between(80, H - 80)
+ };
+}
- oscillator.start(audioContext.currentTime);
- oscillator.stop(audioContext.currentTime + duration);
+function setGhostVelocity(ghost, scene) {
+ const speed = LEVEL_CONFIG[currentLevel].ghostSpeed;
+ const dirs = [
+ { x: 1, y: 0 }, { x: -1, y: 0 },
+ { x: 0, y: 1 }, { x: 0, y: -1 }
+ ];
+ const d = Phaser.Utils.Array.GetRandom(dirs);
+ ghost.body.setVelocity(d.x * speed, d.y * speed);
}
+
diff --git a/index.html b/index.html
index c1dfbd3..e7ecd09 100644
--- a/index.html
+++ b/index.html
@@ -1,413 +1,36 @@
-
+
-
-
- Platanus Hack 25: Arcade Challenge
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
diff --git a/metadata.json b/metadata.json
index 45028c2..44e3202 100644
--- a/metadata.json
+++ b/metadata.json
@@ -1,4 +1,5 @@
{
- "game_name": "",
- "description": ""
+ "game_name": "PLAT-MAN",
+ "description": "Un gorila recolecta plátanos mientras escapa de agentes corporativos estresados en un laberinto de oficina. 3 niveles, ranking de puntuaciones."
}
+
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..1d428d8
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,1078 @@
+{
+ "name": "platanus-phaser-game",
+ "version": "1.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "platanus-phaser-game",
+ "version": "1.0.0",
+ "license": "ISC",
+ "dependencies": {
+ "phaser": "^3.90.0"
+ },
+ "devDependencies": {
+ "vite": "^7.2.1"
+ }
+ },
+ "node_modules/@esbuild/aix-ppc64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz",
+ "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz",
+ "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz",
+ "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz",
+ "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz",
+ "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz",
+ "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz",
+ "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz",
+ "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz",
+ "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz",
+ "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz",
+ "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz",
+ "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz",
+ "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz",
+ "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz",
+ "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz",
+ "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz",
+ "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz",
+ "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz",
+ "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz",
+ "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz",
+ "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openharmony-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz",
+ "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz",
+ "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz",
+ "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz",
+ "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz",
+ "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@rollup/rollup-android-arm-eabi": {
+ "version": "4.52.5",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.5.tgz",
+ "integrity": "sha512-8c1vW4ocv3UOMp9K+gToY5zL2XiiVw3k7f1ksf4yO1FlDFQ1C2u72iACFnSOceJFsWskc2WZNqeRhFRPzv+wtQ==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-android-arm64": {
+ "version": "4.52.5",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.5.tgz",
+ "integrity": "sha512-mQGfsIEFcu21mvqkEKKu2dYmtuSZOBMmAl5CFlPGLY94Vlcm+zWApK7F/eocsNzp8tKmbeBP8yXyAbx0XHsFNA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-arm64": {
+ "version": "4.52.5",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.5.tgz",
+ "integrity": "sha512-takF3CR71mCAGA+v794QUZ0b6ZSrgJkArC+gUiG6LB6TQty9T0Mqh3m2ImRBOxS2IeYBo4lKWIieSvnEk2OQWA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-x64": {
+ "version": "4.52.5",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.5.tgz",
+ "integrity": "sha512-W901Pla8Ya95WpxDn//VF9K9u2JbocwV/v75TE0YIHNTbhqUTv9w4VuQ9MaWlNOkkEfFwkdNhXgcLqPSmHy0fA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-arm64": {
+ "version": "4.52.5",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.5.tgz",
+ "integrity": "sha512-QofO7i7JycsYOWxe0GFqhLmF6l1TqBswJMvICnRUjqCx8b47MTo46W8AoeQwiokAx3zVryVnxtBMcGcnX12LvA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-x64": {
+ "version": "4.52.5",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.5.tgz",
+ "integrity": "sha512-jr21b/99ew8ujZubPo9skbrItHEIE50WdV86cdSoRkKtmWa+DDr6fu2c/xyRT0F/WazZpam6kk7IHBerSL7LDQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
+ "version": "4.52.5",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.5.tgz",
+ "integrity": "sha512-PsNAbcyv9CcecAUagQefwX8fQn9LQ4nZkpDboBOttmyffnInRy8R8dSg6hxxl2Re5QhHBf6FYIDhIj5v982ATQ==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-musleabihf": {
+ "version": "4.52.5",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.5.tgz",
+ "integrity": "sha512-Fw4tysRutyQc/wwkmcyoqFtJhh0u31K+Q6jYjeicsGJJ7bbEq8LwPWV/w0cnzOqR2m694/Af6hpFayLJZkG2VQ==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-gnu": {
+ "version": "4.52.5",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.5.tgz",
+ "integrity": "sha512-a+3wVnAYdQClOTlyapKmyI6BLPAFYs0JM8HRpgYZQO02rMR09ZcV9LbQB+NL6sljzG38869YqThrRnfPMCDtZg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-musl": {
+ "version": "4.52.5",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.5.tgz",
+ "integrity": "sha512-AvttBOMwO9Pcuuf7m9PkC1PUIKsfaAJ4AYhy944qeTJgQOqJYJ9oVl2nYgY7Rk0mkbsuOpCAYSs6wLYB2Xiw0Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-loong64-gnu": {
+ "version": "4.52.5",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.5.tgz",
+ "integrity": "sha512-DkDk8pmXQV2wVrF6oq5tONK6UHLz/XcEVow4JTTerdeV1uqPeHxwcg7aFsfnSm9L+OO8WJsWotKM2JJPMWrQtA==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-ppc64-gnu": {
+ "version": "4.52.5",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.5.tgz",
+ "integrity": "sha512-W/b9ZN/U9+hPQVvlGwjzi+Wy4xdoH2I8EjaCkMvzpI7wJUs8sWJ03Rq96jRnHkSrcHTpQe8h5Tg3ZzUPGauvAw==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-gnu": {
+ "version": "4.52.5",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.5.tgz",
+ "integrity": "sha512-sjQLr9BW7R/ZiXnQiWPkErNfLMkkWIoCz7YMn27HldKsADEKa5WYdobaa1hmN6slu9oWQbB6/jFpJ+P2IkVrmw==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-musl": {
+ "version": "4.52.5",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.5.tgz",
+ "integrity": "sha512-hq3jU/kGyjXWTvAh2awn8oHroCbrPm8JqM7RUpKjalIRWWXE01CQOf/tUNWNHjmbMHg/hmNCwc/Pz3k1T/j/Lg==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-s390x-gnu": {
+ "version": "4.52.5",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.5.tgz",
+ "integrity": "sha512-gn8kHOrku8D4NGHMK1Y7NA7INQTRdVOntt1OCYypZPRt6skGbddska44K8iocdpxHTMMNui5oH4elPH4QOLrFQ==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-gnu": {
+ "version": "4.52.5",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.5.tgz",
+ "integrity": "sha512-hXGLYpdhiNElzN770+H2nlx+jRog8TyynpTVzdlc6bndktjKWyZyiCsuDAlpd+j+W+WNqfcyAWz9HxxIGfZm1Q==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-musl": {
+ "version": "4.52.5",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.5.tgz",
+ "integrity": "sha512-arCGIcuNKjBoKAXD+y7XomR9gY6Mw7HnFBv5Rw7wQRvwYLR7gBAgV7Mb2QTyjXfTveBNFAtPt46/36vV9STLNg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-openharmony-arm64": {
+ "version": "4.52.5",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.5.tgz",
+ "integrity": "sha512-QoFqB6+/9Rly/RiPjaomPLmR/13cgkIGfA40LHly9zcH1S0bN2HVFYk3a1eAyHQyjs3ZJYlXvIGtcCs5tko9Cw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-arm64-msvc": {
+ "version": "4.52.5",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.5.tgz",
+ "integrity": "sha512-w0cDWVR6MlTstla1cIfOGyl8+qb93FlAVutcor14Gf5Md5ap5ySfQ7R9S/NjNaMLSFdUnKGEasmVnu3lCMqB7w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-ia32-msvc": {
+ "version": "4.52.5",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.5.tgz",
+ "integrity": "sha512-Aufdpzp7DpOTULJCuvzqcItSGDH73pF3ko/f+ckJhxQyHtp67rHw3HMNxoIdDMUITJESNE6a8uh4Lo4SLouOUg==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-gnu": {
+ "version": "4.52.5",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.52.5.tgz",
+ "integrity": "sha512-UGBUGPFp1vkj6p8wCRraqNhqwX/4kNQPS57BCFc8wYh0g94iVIW33wJtQAx3G7vrjjNtRaxiMUylM0ktp/TRSQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-msvc": {
+ "version": "4.52.5",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.5.tgz",
+ "integrity": "sha512-TAcgQh2sSkykPRWLrdyy2AiceMckNf5loITqXxFI5VuQjS5tSuw3WlwdN8qv8vzjLAUTvYaH/mVjSFpbkFbpTg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
+ "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/esbuild": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz",
+ "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.25.12",
+ "@esbuild/android-arm": "0.25.12",
+ "@esbuild/android-arm64": "0.25.12",
+ "@esbuild/android-x64": "0.25.12",
+ "@esbuild/darwin-arm64": "0.25.12",
+ "@esbuild/darwin-x64": "0.25.12",
+ "@esbuild/freebsd-arm64": "0.25.12",
+ "@esbuild/freebsd-x64": "0.25.12",
+ "@esbuild/linux-arm": "0.25.12",
+ "@esbuild/linux-arm64": "0.25.12",
+ "@esbuild/linux-ia32": "0.25.12",
+ "@esbuild/linux-loong64": "0.25.12",
+ "@esbuild/linux-mips64el": "0.25.12",
+ "@esbuild/linux-ppc64": "0.25.12",
+ "@esbuild/linux-riscv64": "0.25.12",
+ "@esbuild/linux-s390x": "0.25.12",
+ "@esbuild/linux-x64": "0.25.12",
+ "@esbuild/netbsd-arm64": "0.25.12",
+ "@esbuild/netbsd-x64": "0.25.12",
+ "@esbuild/openbsd-arm64": "0.25.12",
+ "@esbuild/openbsd-x64": "0.25.12",
+ "@esbuild/openharmony-arm64": "0.25.12",
+ "@esbuild/sunos-x64": "0.25.12",
+ "@esbuild/win32-arm64": "0.25.12",
+ "@esbuild/win32-ia32": "0.25.12",
+ "@esbuild/win32-x64": "0.25.12"
+ }
+ },
+ "node_modules/eventemitter3": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
+ "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==",
+ "license": "MIT"
+ },
+ "node_modules/fdir": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
+ "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.11",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
+ "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/phaser": {
+ "version": "3.90.0",
+ "resolved": "https://registry.npmjs.org/phaser/-/phaser-3.90.0.tgz",
+ "integrity": "sha512-/cziz/5ZIn02uDkC9RzN8VF9x3Gs3XdFFf9nkiMEQT3p7hQlWuyjy4QWosU802qqno2YSLn2BfqwOKLv/sSVfQ==",
+ "license": "MIT",
+ "dependencies": {
+ "eventemitter3": "^5.0.1"
+ }
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/picomatch": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
+ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.5.6",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
+ "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.11",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/rollup": {
+ "version": "4.52.5",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.5.tgz",
+ "integrity": "sha512-3GuObel8h7Kqdjt0gxkEzaifHTqLVW56Y/bjN7PSQtkKr0w3V/QYSdt6QWYtd7A1xUtYQigtdUfgj1RvWVtorw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "1.0.8"
+ },
+ "bin": {
+ "rollup": "dist/bin/rollup"
+ },
+ "engines": {
+ "node": ">=18.0.0",
+ "npm": ">=8.0.0"
+ },
+ "optionalDependencies": {
+ "@rollup/rollup-android-arm-eabi": "4.52.5",
+ "@rollup/rollup-android-arm64": "4.52.5",
+ "@rollup/rollup-darwin-arm64": "4.52.5",
+ "@rollup/rollup-darwin-x64": "4.52.5",
+ "@rollup/rollup-freebsd-arm64": "4.52.5",
+ "@rollup/rollup-freebsd-x64": "4.52.5",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.52.5",
+ "@rollup/rollup-linux-arm-musleabihf": "4.52.5",
+ "@rollup/rollup-linux-arm64-gnu": "4.52.5",
+ "@rollup/rollup-linux-arm64-musl": "4.52.5",
+ "@rollup/rollup-linux-loong64-gnu": "4.52.5",
+ "@rollup/rollup-linux-ppc64-gnu": "4.52.5",
+ "@rollup/rollup-linux-riscv64-gnu": "4.52.5",
+ "@rollup/rollup-linux-riscv64-musl": "4.52.5",
+ "@rollup/rollup-linux-s390x-gnu": "4.52.5",
+ "@rollup/rollup-linux-x64-gnu": "4.52.5",
+ "@rollup/rollup-linux-x64-musl": "4.52.5",
+ "@rollup/rollup-openharmony-arm64": "4.52.5",
+ "@rollup/rollup-win32-arm64-msvc": "4.52.5",
+ "@rollup/rollup-win32-ia32-msvc": "4.52.5",
+ "@rollup/rollup-win32-x64-gnu": "4.52.5",
+ "@rollup/rollup-win32-x64-msvc": "4.52.5",
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/tinyglobby": {
+ "version": "0.2.15",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
+ "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/SuperchupuDev"
+ }
+ },
+ "node_modules/vite": {
+ "version": "7.2.1",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-7.2.1.tgz",
+ "integrity": "sha512-qTl3VF7BvOupTR85Zc561sPEgxyUSNSvTQ9fit7DEMP7yPgvvIGm5Zfa1dOM+kOwWGNviK9uFM9ra77+OjK7lQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "esbuild": "^0.25.0",
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.3",
+ "postcss": "^8.5.6",
+ "rollup": "^4.43.0",
+ "tinyglobby": "^0.2.15"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "funding": {
+ "url": "https://github.com/vitejs/vite?sponsor=1"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ },
+ "peerDependencies": {
+ "@types/node": "^20.19.0 || >=22.12.0",
+ "jiti": ">=1.21.0",
+ "less": "^4.0.0",
+ "lightningcss": "^1.21.0",
+ "sass": "^1.70.0",
+ "sass-embedded": "^1.70.0",
+ "stylus": ">=0.54.8",
+ "sugarss": "^5.0.0",
+ "terser": "^5.16.0",
+ "tsx": "^4.8.1",
+ "yaml": "^2.4.2"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "jiti": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "lightningcss": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "sass-embedded": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ },
+ "tsx": {
+ "optional": true
+ },
+ "yaml": {
+ "optional": true
+ }
+ }
+ }
+ }
+}
diff --git a/package.json b/package.json
index 0415c3d..e4c4ffb 100644
--- a/package.json
+++ b/package.json
@@ -1,18 +1,20 @@
{
- "name": "platanus-hack-25-arcade",
+ "name": "platanus-phaser-game",
"version": "1.0.0",
- "description": "Phaser arcade game challenge starter kit",
+ "description": "Plat-Man - Juego de gorila con Phaser 3",
"type": "module",
"scripts": {
"dev": "vite",
- "dev:old": "tsx --watch dev-server.ts",
- "check-restrictions": "tsx check-restrictions.ts"
+ "build": "vite build",
+ "preview": "vite preview"
+ },
+ "keywords": [],
+ "author": "",
+ "license": "ISC",
+ "dependencies": {
+ "phaser": "^3.90.0"
},
"devDependencies": {
- "@swc/core": "^1.13.5",
- "@types/node": "^22.10.5",
- "tsx": "^4.19.2",
- "typescript": "^5.7.2",
- "vite": "^7.1.12"
+ "vite": "^7.2.1"
}
}
diff --git a/vite.config.js b/vite.config.js
new file mode 100644
index 0000000..7b6198d
--- /dev/null
+++ b/vite.config.js
@@ -0,0 +1,13 @@
+import { defineConfig } from 'vite';
+
+export default defineConfig({
+ server: {
+ port: 3000,
+ open: true
+ },
+ build: {
+ outDir: 'dist',
+ assetsDir: 'assets'
+ }
+});
+