/** * BuffSelectScene.js * Pre-game buff selection screen. * Allows players to purchase Shield (100g) and/or Double Fire (150g) * before entering a game level. */ const { SCREEN_WIDTH, SCREEN_HEIGHT, COLORS, SCENE, } = require('../base/GameGlobal'); const { t } = require('../i18n/I18n'); const BuffManager = require('../managers/BuffManager'); const BUFF_TYPE = BuffManager.BUFF_TYPE; const BUFF_COST = BuffManager.BUFF_COST; // Layout constants const CARD_W = Math.min(SCREEN_WIDTH * 0.38, 160); const CARD_H = Math.min(SCREEN_HEIGHT * 0.3, 140); const CARD_GAP = 20; const CARD_Y = SCREEN_HEIGHT * 0.3; const BuffSelectScene = { _gameParams: null, // params to pass to GameScene _buttons: {}, _buffManager: null, enter(params) { this._gameParams = params || {}; this._buffManager = GameGlobal.buffManager; this._buttons = {}; // Clear any previous buffs if (this._buffManager) { this._buffManager.clearBuffs(); } this._calculateLayout(); }, exit() {}, _calculateLayout() { const cx = SCREEN_WIDTH / 2; // Two buff cards side by side const card1X = cx - CARD_W - CARD_GAP / 2; const card2X = cx + CARD_GAP / 2; this._buttons = { shield: { x: card1X, y: CARD_Y, w: CARD_W, h: CARD_H, type: BUFF_TYPE.SHIELD }, doubleFire: { x: card2X, y: CARD_Y, w: CARD_W, h: CARD_H, type: BUFF_TYPE.DOUBLE_FIRE }, }; // Start/Skip button const btnW = Math.min(SCREEN_WIDTH * 0.5, 200); const btnH = 42; const btnY = CARD_Y + CARD_H + 30; this._buttons.start = { x: cx - btnW / 2, y: btnY, w: btnW, h: btnH }; this._buttons.skip = { x: cx - btnW / 2, y: btnY + btnH + 12, w: btnW, h: btnH }; }, update(dt) {}, render(ctx) { // Background ctx.fillStyle = COLORS.MENU_BG; ctx.fillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); // Title ctx.fillStyle = COLORS.MENU_TITLE; ctx.font = 'bold 24px Arial'; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; ctx.fillText(t('buff.title') || 'Pre-Game Buffs', SCREEN_WIDTH / 2, SCREEN_HEIGHT * 0.12); // Gold balance const gold = GameGlobal.currencyManager ? GameGlobal.currencyManager.getGold() : 0; ctx.fillStyle = '#FFD700'; ctx.font = 'bold 16px Arial'; ctx.fillText(`🪙 ${gold}`, SCREEN_WIDTH / 2, SCREEN_HEIGHT * 0.2); // Render buff cards this._renderBuffCard(ctx, this._buttons.shield, t('buff.shield') || '🛡️ Shield', t('buff.shieldDesc') || 'Start with a shield layer', BUFF_COST[BUFF_TYPE.SHIELD], BUFF_TYPE.SHIELD ); this._renderBuffCard(ctx, this._buttons.doubleFire, t('buff.doubleFire') || '🔥 Double Fire', t('buff.doubleFireDesc') || '2x bullet power for 10s', BUFF_COST[BUFF_TYPE.DOUBLE_FIRE], BUFF_TYPE.DOUBLE_FIRE ); // Start button (if any buff purchased) const hasBuffs = this._buffManager && this._buffManager.getActiveBuffs().length > 0; if (hasBuffs) { this._renderButton(ctx, this._buttons.start, t('buff.start') || 'Start Game', '#4CAF50'); } // Skip button this._renderButton(ctx, this._buttons.skip, t('buff.skip') || 'Skip →', '#666666'); }, _renderBuffCard(ctx, rect, title, desc, cost, buffType) { if (!rect) return; const purchased = this._buffManager && this._buffManager.hasBuff(buffType); const canAfford = GameGlobal.currencyManager && GameGlobal.currencyManager.hasGold(cost); // Card background ctx.fillStyle = purchased ? 'rgba(76, 175, 80, 0.3)' : 'rgba(255,255,255,0.05)'; ctx.strokeStyle = purchased ? '#4CAF50' : '#444444'; ctx.lineWidth = 2; // Rounded rect const r = 10; ctx.beginPath(); ctx.moveTo(rect.x + r, rect.y); ctx.lineTo(rect.x + rect.w - r, rect.y); ctx.arcTo(rect.x + rect.w, rect.y, rect.x + rect.w, rect.y + r, r); ctx.lineTo(rect.x + rect.w, rect.y + rect.h - r); ctx.arcTo(rect.x + rect.w, rect.y + rect.h, rect.x + rect.w - r, rect.y + rect.h, r); ctx.lineTo(rect.x + r, rect.y + rect.h); ctx.arcTo(rect.x, rect.y + rect.h, rect.x, rect.y + rect.h - r, r); ctx.lineTo(rect.x, rect.y + r); ctx.arcTo(rect.x, rect.y, rect.x + r, rect.y, r); ctx.closePath(); ctx.fill(); ctx.stroke(); const cx = rect.x + rect.w / 2; // Title ctx.fillStyle = '#FFFFFF'; ctx.font = 'bold 16px Arial'; ctx.textAlign = 'center'; ctx.fillText(title, cx, rect.y + 30); // Description ctx.fillStyle = '#AAAAAA'; ctx.font = '11px Arial'; ctx.fillText(desc, cx, rect.y + 55); // Cost or status if (purchased) { ctx.fillStyle = '#4CAF50'; ctx.font = 'bold 14px Arial'; ctx.fillText(t('buff.purchased') || '✓ Purchased', cx, rect.y + rect.h - 25); } else { ctx.fillStyle = canAfford ? '#FFD700' : '#FF4444'; ctx.font = 'bold 14px Arial'; ctx.fillText(`🪙 ${cost}`, cx, rect.y + rect.h - 25); } }, _renderButton(ctx, rect, label, color) { if (!rect) return; ctx.fillStyle = color; ctx.strokeStyle = '#333333'; ctx.lineWidth = 1; const r = 6; ctx.beginPath(); ctx.moveTo(rect.x + r, rect.y); ctx.lineTo(rect.x + rect.w - r, rect.y); ctx.arcTo(rect.x + rect.w, rect.y, rect.x + rect.w, rect.y + r, r); ctx.lineTo(rect.x + rect.w, rect.y + rect.h - r); ctx.arcTo(rect.x + rect.w, rect.y + rect.h, rect.x + rect.w - r, rect.y + rect.h, r); ctx.lineTo(rect.x + r, rect.y + rect.h); ctx.arcTo(rect.x, rect.y + rect.h, rect.x, rect.y + rect.h - r, r); ctx.lineTo(rect.x, rect.y + r); ctx.arcTo(rect.x, rect.y, rect.x + r, rect.y, r); ctx.closePath(); ctx.fill(); ctx.stroke(); ctx.fillStyle = '#FFFFFF'; ctx.font = 'bold 16px Arial'; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; ctx.fillText(label, rect.x + rect.w / 2, rect.y + rect.h / 2); }, _hitTest(tx, ty, rect) { if (!rect) return false; return tx >= rect.x && tx <= rect.x + rect.w && ty >= rect.y && ty <= rect.y + rect.h; }, _startGame() { const sm = GameGlobal.sceneManager; if (!sm._scenes.has(SCENE.GAME)) { const GameScene = require('./GameScene'); sm.register(SCENE.GAME, GameScene); } // ★ DEBUG: Force level 20 (Boss Battle) for quick verification of Boss tank gap-fix // const params = Object.assign({}, this._gameParams, { level: 20 }); const params = Object.assign({}, this._gameParams); sm.switchTo(SCENE.GAME, params); }, handleTouch(eventType, e) { if (eventType !== 'touchstart') return; const touch = e.touches[0]; const tx = touch.clientX; const ty = touch.clientY; // Shield card if (this._hitTest(tx, ty, this._buttons.shield)) { if (this._buffManager && !this._buffManager.hasBuff(BUFF_TYPE.SHIELD)) { const result = this._buffManager.purchaseBuff(BUFF_TYPE.SHIELD); if (!result.success) { console.log(`[BuffSelectScene] Shield purchase failed: ${result.error}`); } } return; } // Double Fire card if (this._hitTest(tx, ty, this._buttons.doubleFire)) { if (this._buffManager && !this._buffManager.hasBuff(BUFF_TYPE.DOUBLE_FIRE)) { const result = this._buffManager.purchaseBuff(BUFF_TYPE.DOUBLE_FIRE); if (!result.success) { console.log(`[BuffSelectScene] Double Fire purchase failed: ${result.error}`); } } return; } // Start button if (this._hitTest(tx, ty, this._buttons.start)) { this._startGame(); return; } // Skip button if (this._hitTest(tx, ty, this._buttons.skip)) { this._startGame(); return; } }, }; module.exports = BuffSelectScene;