/** * FireButton.js * Virtual fire button component positioned at the bottom-right of the screen. * Overlaid as a separate layer above the map when they intersect. */ const { SCREEN_WIDTH, SCREEN_HEIGHT, } = require('../base/GameGlobal'); class FireButton { constructor() { this.radius = 35; // Anchored to screen bottom-right, shifted slightly towards upper-left const padding = this.radius + 40; this.cx = SCREEN_WIDTH - padding - 15; // right edge + extra leftward offset this.cy = SCREEN_HEIGHT - padding - 30; // bottom edge + extra upward offset this._pressed = false; this._touchId = null; this._fireCallback = null; // Visual feedback this._pressScale = 1; // Touch area this._touchAreaRadius = this.radius * 1.5; } /** * Set the callback for when fire is triggered. * @param {Function} cb */ onFire(cb) { this._fireCallback = cb; } /** * Handle touch events. * @param {string} eventType * @param {Touch} touch * @returns {boolean} Whether this button consumed the touch. */ handleTouch(eventType, touch) { const tx = touch.clientX; const ty = touch.clientY; if (eventType === 'touchstart') { const dist = Math.sqrt((tx - this.cx) ** 2 + (ty - this.cy) ** 2); if (dist <= this._touchAreaRadius) { this._pressed = true; this._touchId = touch.identifier; this._pressScale = 0.85; // Fire immediately on press if (this._fireCallback) { this._fireCallback(); } return true; } return false; } if (eventType === 'touchend') { if (this._pressed && touch.identifier === this._touchId) { this._pressed = false; this._touchId = null; this._pressScale = 1; return true; } return false; } return false; } /** * Render the fire button. * @param {CanvasRenderingContext2D} ctx */ render(ctx) { ctx.save(); ctx.globalAlpha = 0.5; const r = this.radius * this._pressScale; // Outer ring ctx.strokeStyle = '#FF4444'; ctx.lineWidth = 3; ctx.beginPath(); ctx.arc(this.cx, this.cy, r, 0, Math.PI * 2); ctx.stroke(); // Inner fill ctx.fillStyle = this._pressed ? '#FF6666' : '#CC3333'; ctx.beginPath(); ctx.arc(this.cx, this.cy, r * 0.75, 0, Math.PI * 2); ctx.fill(); // Fire icon (crosshair) ctx.globalAlpha = 0.8; ctx.strokeStyle = '#FFFFFF'; ctx.lineWidth = 2; const crossSize = r * 0.35; // Horizontal line ctx.beginPath(); ctx.moveTo(this.cx - crossSize, this.cy); ctx.lineTo(this.cx + crossSize, this.cy); ctx.stroke(); // Vertical line ctx.beginPath(); ctx.moveTo(this.cx, this.cy - crossSize); ctx.lineTo(this.cx, this.cy + crossSize); ctx.stroke(); // Center dot ctx.fillStyle = '#FFFFFF'; ctx.beginPath(); ctx.arc(this.cx, this.cy, 3, 0, Math.PI * 2); ctx.fill(); ctx.restore(); } /** Whether the button is currently pressed. */ get pressed() { return this._pressed; } } module.exports = FireButton;