Files
tankwar_proj/js/scenes/BuffSelectScene.js
2026-04-10 22:59:39 +08:00

250 lines
7.4 KiB
JavaScript

/**
* 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);
}
sm.switchTo(SCENE.GAME, this._gameParams);
},
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;