From 5b75abdd888297098228e108295fe5790bf032a6 Mon Sep 17 00:00:00 2001 From: tiennm99 Date: Sun, 27 Apr 2025 18:44:27 +0700 Subject: [PATCH] fix: bug when get into level --- src/game/levels/Levels.js | 15 +-- src/game/objects/LightingSystem.js | 8 +- src/game/objects/Player.js | 22 ++-- src/game/scenes/Game.js | 191 +++++++++++++++++++---------- 4 files changed, 147 insertions(+), 89 deletions(-) diff --git a/src/game/levels/Levels.js b/src/game/levels/Levels.js index 79a6129..98f1342 100644 --- a/src/game/levels/Levels.js +++ b/src/game/levels/Levels.js @@ -150,9 +150,8 @@ export const LEVELS = [ type: "static", position: { row: 1, col: 6 }, litCells: [ - // Removed ALL lit cells to make level 5 completable - // { row: 1, col: 5 }, - // { row: 1, col: 7 } + // Adding back one lit cell with a path still possible + { row: 1, col: 7 } ] }, { @@ -230,9 +229,8 @@ export const LEVELS = [ type: "static", position: { row: 1, col: 5 }, litCells: [ - // Removed ALL lit cells to make level 7 completable - // { row: 1, col: 4 }, - // { row: 1, col: 6 } + // Adding back one lit cell with a path still possible + { row: 1, col: 6 } ] }, { @@ -244,9 +242,8 @@ export const LEVELS = [ type: "static", position: { row: 5, col: 2 }, litCells: [ - // Removed ALL lit cells to make level 7 completable - // { row: 5, col: 1 }, - // { row: 5, col: 3 } + // Adding back one lit cell with a path still possible + { row: 5, col: 1 } ] } ] diff --git a/src/game/objects/LightingSystem.js b/src/game/objects/LightingSystem.js index 06e9ac4..afd1a57 100644 --- a/src/game/objects/LightingSystem.js +++ b/src/game/objects/LightingSystem.js @@ -4,8 +4,10 @@ export class LightingSystem { constructor(scene, grid) { this.scene = scene; this.grid = grid; + // Create graphics object for rendering light effects + this.graphics = this.scene.add.graphics(); } - + // Xóa tất cả ánh sáng trên lưới clearAllLight() { for (let row = 0; row < this.grid.rows; row++) { @@ -14,11 +16,11 @@ export class LightingSystem { } } } - + // Cập nhật ánh sáng từ tất cả các trạm gác updateLightFromGuards(guards) { this.clearAllLight(); - + if (guards && Array.isArray(guards)) { guards.forEach(guard => { guard.updateLight(); diff --git a/src/game/objects/Player.js b/src/game/objects/Player.js index 9ec4a86..c88fbb4 100644 --- a/src/game/objects/Player.js +++ b/src/game/objects/Player.js @@ -13,12 +13,12 @@ export class Player { createSprite() { // Lấy vị trí pixel từ tọa độ lưới const { x, y } = this.scene.gridToScreen(this.row, this.col); - + // Tạo sprite người chơi (hình tròn đen) this.sprite = this.scene.add.circle( - x, - y, - this.grid.cellSize / 3, + x, + y, + this.grid.cellSize / 3, 0x000000 ); } @@ -31,10 +31,10 @@ export class Player { this.row = row; this.col = col; - + // Lấy vị trí pixel mới const { x, y } = this.scene.gridToScreen(this.row, this.col); - + // Tạo hiệu ứng di chuyển this.scene.tweens.add({ targets: this.sprite, @@ -43,7 +43,7 @@ export class Player { duration: 100, ease: 'Linear' }); - + return true; } @@ -51,7 +51,7 @@ export class Player { move(direction) { let newRow = this.row; let newCol = this.col; - + switch (direction) { case 'up': newRow--; @@ -66,18 +66,18 @@ export class Player { newCol++; break; } - + return this.moveTo(newRow, newCol); } // Kiểm tra xem người chơi có đang ở ô sáng không isInLitCell() { - return this.grid.getCell(this.row, this.col).isLit; + return this.grid.isLight(this.row, this.col); } // Kiểm tra xem người chơi có đến đích không isAtGoal() { - return this.grid.getCell(this.row, this.col).isGoal; + return this.grid.isGoal(this.row, this.col); } // Cập nhật vị trí sprite diff --git a/src/game/scenes/Game.js b/src/game/scenes/Game.js index d218cbf..7ce40c9 100644 --- a/src/game/scenes/Game.js +++ b/src/game/scenes/Game.js @@ -37,40 +37,40 @@ export class Game extends Phaser.Scene { // Calculate grid position to center it const width = this.cameras.main.width; const height = this.cameras.main.height; - + this.gridOffsetX = (width - (this.gridSize * this.cellSize)) / 2; this.gridOffsetY = (height - (this.gridSize * this.cellSize)) / 2; - + // Create grid system this.createGridSystem(); - + // Create lighting system this.lightSystem = new LightingSystem(this, this.grid); - + // Make guard classes available to LevelManager this.StaticGuard = StaticGuard; this.RotatingGuard = RotatingGuard; this.BlinkingGuard = BlinkingGuard; this.PatrollingGuard = PatrollingGuard; - + // Create level manager this.levelManager = new LevelManager(this); - + // Create turn manager this.turnManager = new TurnManager(this); - + // Create player this.createPlayer(0, 0); // Default position, will be updated by level data - + // Setup input this.setupInput(); - + // Create UI this.createUI(); - + // Create mobile controls if needed this.createMobileControls(); - + // Load level this.levelManager.loadLevel(this.currentLevel); } @@ -80,16 +80,21 @@ export class Game extends Phaser.Scene { if (!this.isPaused) { // Check for keyboard input this.handleKeyboardInput(); + + // Check for final level special condition + if (this.isFinalLevel) { + this.checkFinalLevelCondition(); + } } } createGridSystem() { this.grid = new GridSystem(this, this.gridSize, this.gridSize, this.cellSize); - + // Position the grid in the center of the screen this.grid.graphics.x = this.gridOffsetX; this.grid.graphics.y = this.gridOffsetY; - + // Render the grid this.grid.render(); } @@ -101,7 +106,7 @@ export class Game extends Phaser.Scene { setupInput() { // Setup keyboard input this.cursors = this.input.keyboard.createCursorKeys(); - + // WASD keys this.wasd = { up: this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.W), @@ -109,18 +114,18 @@ export class Game extends Phaser.Scene { down: this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.S), right: this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.D) }; - + // Add click/tap input for grid cells this.input.on('pointerdown', (pointer) => { if (this.isPaused || !this.inputEnabled || !this.turnManager.isPlayerTurn) return; - + // Convert screen coordinates to grid coordinates const gridPos = this.screenToGrid(pointer.x, pointer.y); - + // Check if the clicked position is adjacent to the player const rowDiff = Math.abs(gridPos.row - this.player.row); const colDiff = Math.abs(gridPos.col - this.player.col); - + // Only allow moving to adjacent cells (not diagonally) if ((rowDiff === 1 && colDiff === 0) || (rowDiff === 0 && colDiff === 1)) { if (this.player.moveTo(gridPos.row, gridPos.col)) { @@ -133,15 +138,15 @@ export class Game extends Phaser.Scene { handleKeyboardInput() { // Only process input during player's turn and when input is enabled if (!this.inputEnabled || !this.turnManager.isPlayerTurn) return; - + // Use a cooldown to prevent multiple inputs in a single frame if (this.inputCooldown > 0) { this.inputCooldown--; return; } - + let direction = null; - + // Check arrow keys if (this.cursors.up.isDown || this.wasd.up.isDown) { direction = 'up'; @@ -152,7 +157,7 @@ export class Game extends Phaser.Scene { } else if (this.cursors.right.isDown || this.wasd.right.isDown) { direction = 'right'; } - + if (direction) { if (this.player.move(direction)) { this.turnManager.nextTurn(); @@ -164,56 +169,56 @@ export class Game extends Phaser.Scene { createUI() { // Create UI elements const width = this.cameras.main.width; - + // Lives display this.livesText = this.add.text(20, 20, `Lives: ${this.livesRemaining}`, { font: '18px Arial', fill: '#ffffff' }); - + // Level display this.levelText = this.add.text(width - 20, 20, `Level: ${this.currentLevel}`, { font: '18px Arial', fill: '#ffffff' }); this.levelText.setOrigin(1, 0); - + // Pause button this.pauseButton = this.add.rectangle(width - 20, 60, 100, 30, 0x444444); this.pauseButton.setOrigin(1, 0); - + this.pauseText = this.add.text(width - 70, 75, 'PAUSE', { font: '16px Arial', fill: '#ffffff' }); this.pauseText.setOrigin(0.5, 0.5); - + this.pauseButton.setInteractive({ useHandCursor: true }) .on('pointerdown', () => { this.togglePause(); }); - + // Create pause menu (initially hidden) this.createPauseMenu(); } - + createPauseMenu() { const width = this.cameras.main.width; const height = this.cameras.main.height; - + // Create container for pause menu this.pauseMenu = this.add.container(width / 2, height / 2); - + // Background const bg = this.add.rectangle(0, 0, 300, 250, 0x000000, 0.8); - + // Title const title = this.add.text(0, -100, 'PAUSED', { font: 'bold 32px Arial', fill: '#ffffff' }); title.setOrigin(0.5, 0.5); - + // Resume button const resumeButton = this.add.rectangle(0, -40, 200, 40, 0x444444); const resumeText = this.add.text(0, -40, 'RESUME', { @@ -221,7 +226,7 @@ export class Game extends Phaser.Scene { fill: '#ffffff' }); resumeText.setOrigin(0.5, 0.5); - + // Restart button const restartButton = this.add.rectangle(0, 20, 200, 40, 0x444444); const restartText = this.add.text(0, 20, 'RESTART LEVEL', { @@ -229,7 +234,7 @@ export class Game extends Phaser.Scene { fill: '#ffffff' }); restartText.setOrigin(0.5, 0.5); - + // Main menu button const menuButton = this.add.rectangle(0, 80, 200, 40, 0x444444); const menuText = this.add.text(0, 80, 'MAIN MENU', { @@ -237,10 +242,10 @@ export class Game extends Phaser.Scene { fill: '#ffffff' }); menuText.setOrigin(0.5, 0.5); - + // Add all elements to container this.pauseMenu.add([bg, title, resumeButton, resumeText, restartButton, restartText, menuButton, menuText]); - + // Make buttons interactive resumeButton.setInteractive({ useHandCursor: true }) .on('pointerover', () => resumeButton.fillColor = 0x666666) @@ -248,7 +253,7 @@ export class Game extends Phaser.Scene { .on('pointerdown', () => { this.togglePause(); }); - + restartButton.setInteractive({ useHandCursor: true }) .on('pointerover', () => restartButton.fillColor = 0x666666) .on('pointerout', () => restartButton.fillColor = 0x444444) @@ -256,59 +261,59 @@ export class Game extends Phaser.Scene { this.togglePause(); this.levelManager.loadLevel(this.currentLevel); }); - + menuButton.setInteractive({ useHandCursor: true }) .on('pointerover', () => menuButton.fillColor = 0x666666) .on('pointerout', () => menuButton.fillColor = 0x444444) .on('pointerdown', () => { this.scene.start('MainMenu'); }); - + // Hide pause menu initially this.pauseMenu.setVisible(false); } - + togglePause() { this.isPaused = !this.isPaused; this.pauseMenu.setVisible(this.isPaused); this.inputEnabled = !this.isPaused; } - + createMobileControls() { // Create mobile control buttons const width = this.cameras.main.width; const height = this.cameras.main.height; - + // Container for mobile controls this.mobileControls = this.add.container(width / 2, height - 100); - + // Create directional buttons const buttonSize = 60; const buttonSpacing = 70; - + // Up button const upButton = this.add.circle(0, -buttonSpacing, buttonSize / 2, 0x444444); const upText = this.add.text(0, -buttonSpacing, '↑', { font: '32px Arial', fill: '#ffffff' }); upText.setOrigin(0.5, 0.5); - + // Left button const leftButton = this.add.circle(-buttonSpacing, 0, buttonSize / 2, 0x444444); const leftText = this.add.text(-buttonSpacing, 0, '←', { font: '32px Arial', fill: '#ffffff' }); leftText.setOrigin(0.5, 0.5); - + // Right button const rightButton = this.add.circle(buttonSpacing, 0, buttonSize / 2, 0x444444); const rightText = this.add.text(buttonSpacing, 0, '→', { font: '32px Arial', fill: '#ffffff' }); rightText.setOrigin(0.5, 0.5); - + // Down button const downButton = this.add.circle(0, buttonSpacing, buttonSize / 2, 0x444444); const downText = this.add.text(0, buttonSpacing, '↓', { font: '32px Arial', fill: '#ffffff' }); downText.setOrigin(0.5, 0.5); - + // Add buttons to container this.mobileControls.add([upButton, upText, leftButton, leftText, rightButton, rightText, downButton, downText]); - + // Make buttons interactive upButton.setInteractive({ useHandCursor: true }) .on('pointerdown', () => { @@ -318,7 +323,7 @@ export class Game extends Phaser.Scene { } } }); - + leftButton.setInteractive({ useHandCursor: true }) .on('pointerdown', () => { if (!this.isPaused && this.inputEnabled && this.turnManager.isPlayerTurn) { @@ -327,7 +332,7 @@ export class Game extends Phaser.Scene { } } }); - + rightButton.setInteractive({ useHandCursor: true }) .on('pointerdown', () => { if (!this.isPaused && this.inputEnabled && this.turnManager.isPlayerTurn) { @@ -336,7 +341,7 @@ export class Game extends Phaser.Scene { } } }); - + downButton.setInteractive({ useHandCursor: true }) .on('pointerdown', () => { if (!this.isPaused && this.inputEnabled && this.turnManager.isPlayerTurn) { @@ -345,16 +350,16 @@ export class Game extends Phaser.Scene { } } }); - + // Only show mobile controls on touch devices this.mobileControls.setVisible(false); - + // Check if device has touch capability if (this.sys.game.device.input.touch) { this.mobileControls.setVisible(true); } } - + // Helper method to convert grid coordinates to screen coordinates gridToScreen(row, col) { const gridPos = this.grid.gridToPixel(row, col); @@ -363,24 +368,24 @@ export class Game extends Phaser.Scene { y: gridPos.y + this.gridOffsetY }; } - + // Helper method to convert screen coordinates to grid coordinates screenToGrid(x, y) { return this.grid.pixelToGrid(x - this.gridOffsetX, y - this.gridOffsetY); } - + // Xử lý khi người chơi bị bắt handlePlayerCaught() { this.livesRemaining--; - + // Cập nhật hiển thị số mạng if (this.livesText) { this.livesText.setText(`Lives: ${this.livesRemaining}`); } - + if (this.livesRemaining <= 0) { // Hết mạng, game over - this.scene.start('GameOver', { + this.scene.start('GameOver', { level: this.currentLevel, isLastLevel: false }); @@ -389,30 +394,84 @@ export class Game extends Phaser.Scene { this.levelManager.loadLevel(this.currentLevel); } } - + // Xử lý khi người chơi hoàn thành màn chơi handleLevelComplete() { const nextLevel = this.currentLevel + 1; - + // Kiểm tra xem có phải màn cuối không if (this.isFinalLevel) { // Đây là màn cuối với cái kết đặc biệt - this.scene.start('GameOver', { + this.scene.start('GameOver', { level: this.currentLevel, isLastLevel: true }); } else if (nextLevel > this.levelManager.getTotalLevels()) { // Đã hoàn thành tất cả các màn - this.scene.start('GameOver', { + this.scene.start('GameOver', { level: this.currentLevel, isLastLevel: false }); } else { // Chuyển đến màn tiếp theo - this.scene.start('Game', { + this.scene.start('Game', { level: nextLevel, lives: this.livesRemaining }); } } + + // Kiểm tra điều kiện đặc biệt cho màn cuối + checkFinalLevelCondition() { + if (!this.isFinalLevel) return false; + + // Kiểm tra xem người chơi có đang ở gần công chúa không + const playerRow = this.player.row; + const playerCol = this.player.col; + const goalRow = 9; // Vị trí công chúa ở màn cuối (row 9, col 9) + const goalCol = 9; + + // Tính khoảng cách Manhattan từ người chơi đến công chúa + const distance = Math.abs(playerRow - goalRow) + Math.abs(playerCol - goalCol); + + // Nếu người chơi đến gần công chúa (khoảng cách <= 2), kích hoạt cơ chế đặc biệt + if (distance <= 2) { + // Bật sáng toàn bộ bản đồ + this.lightUpEntireMap(); + return true; + } + + return false; + } + + // Bật sáng toàn bộ bản đồ cho màn cuối + lightUpEntireMap() { + // Hiển thị thông báo + const width = this.cameras.main.width; + const height = this.cameras.main.height; + + if (!this.finalLevelMessage) { + this.finalLevelMessage = this.add.text(width / 2, height / 4, + 'Công chúa phát hiện ra ninja! Toàn bộ cung điện sáng đèn!', { + font: 'bold 20px Arial', + fill: '#FF0000', + backgroundColor: '#000000', + padding: { x: 10, y: 5 } + }); + this.finalLevelMessage.setOrigin(0.5, 0.5); + this.finalLevelMessage.setDepth(100); + } + + // Bật sáng toàn bộ các ô + for (let row = 0; row < this.grid.rows; row++) { + for (let col = 0; col < this.grid.cols; col++) { + if (!this.grid.isWall(row, col)) { + this.grid.setLight(row, col, true); + } + } + } + + // Vẽ lại lưới + this.grid.render(); + } }