first commit

This commit is contained in:
jakciehan
2026-04-10 22:59:39 +08:00
commit cc2e7b9bb0
89 changed files with 23631 additions and 0 deletions
+164
View File
@@ -0,0 +1,164 @@
/**
* TutorialOverlay.js
* New player tutorial overlay shown on first play.
* Displays 2-3 step instructions for controls.
*/
const {
SCREEN_WIDTH,
SCREEN_HEIGHT,
MAP_OFFSET_X,
MAP_WIDTH,
} = require('../base/GameGlobal');
class TutorialOverlay {
constructor() {
this._active = false;
this._step = 0;
this._totalSteps = 3;
this._steps = [
{
title: '移动坦克',
desc: '拖动左下角的摇杆\n控制坦克上下左右移动',
highlight: 'joystick',
},
{
title: '发射子弹',
desc: '点击右下角的按钮\n向前方发射子弹',
highlight: 'fire',
},
{
title: '保护基地',
desc: '消灭所有敌人\n不要让基地被摧毁!',
highlight: 'base',
},
];
}
/**
* Show the tutorial.
*/
show() {
this._active = true;
this._step = 0;
}
/**
* Hide the tutorial.
*/
hide() {
this._active = false;
}
/** Whether the tutorial is active. */
get active() {
return this._active;
}
/**
* Handle touch to advance steps.
* @returns {boolean} Whether the tutorial consumed the touch.
*/
handleTouch() {
if (!this._active) return false;
this._step++;
if (this._step >= this._totalSteps) {
this._active = false;
}
return true;
}
/**
* Render the tutorial overlay.
* @param {CanvasRenderingContext2D} ctx
*/
render(ctx) {
if (!this._active) return;
const step = this._steps[this._step];
// Semi-transparent overlay
ctx.fillStyle = 'rgba(0, 0, 0, 0.7)';
ctx.fillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
const cx = SCREEN_WIDTH / 2;
const cy = SCREEN_HEIGHT / 2;
// Step indicator
ctx.fillStyle = '#AAAAAA';
ctx.font = '12px Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(`${this._step + 1} / ${this._totalSteps}`, cx, cy - 80);
// Title
ctx.fillStyle = '#FFD700';
ctx.font = 'bold 24px Arial';
ctx.fillText(step.title, cx, cy - 40);
// Description (multi-line)
ctx.fillStyle = '#FFFFFF';
ctx.font = '16px Arial';
const lines = step.desc.split('\n');
for (let i = 0; i < lines.length; i++) {
ctx.fillText(lines[i], cx, cy + 10 + i * 24);
}
// Highlight area indicator
this._drawHighlight(ctx, step.highlight);
// Tap to continue
ctx.fillStyle = '#888888';
ctx.font = '13px Arial';
ctx.fillText('点击屏幕继续', cx, SCREEN_HEIGHT - 60);
}
/**
* Draw a highlight circle around the relevant UI element.
* @private
*/
_drawHighlight(ctx, type) {
ctx.save();
ctx.strokeStyle = '#FFD700';
ctx.lineWidth = 3;
ctx.setLineDash([8, 4]);
switch (type) {
case 'joystick':
ctx.beginPath();
ctx.arc(Math.floor(MAP_OFFSET_X / 2), SCREEN_HEIGHT - 100, 65, 0, Math.PI * 2);
ctx.stroke();
break;
case 'fire': {
const rightAreaStart = MAP_OFFSET_X + MAP_WIDTH;
ctx.beginPath();
ctx.arc(Math.floor(rightAreaStart + (SCREEN_WIDTH - rightAreaStart) / 2), SCREEN_HEIGHT - 100, 50, 0, Math.PI * 2);
ctx.stroke();
break;
}
case 'base': {
// Arrow pointing to base area
const baseCx = SCREEN_WIDTH / 2;
ctx.beginPath();
ctx.moveTo(baseCx, SCREEN_HEIGHT * 0.7);
ctx.lineTo(baseCx, SCREEN_HEIGHT * 0.8);
ctx.stroke();
ctx.fillStyle = '#FFD700';
ctx.beginPath();
ctx.moveTo(baseCx - 8, SCREEN_HEIGHT * 0.8);
ctx.lineTo(baseCx + 8, SCREEN_HEIGHT * 0.8);
ctx.lineTo(baseCx, SCREEN_HEIGHT * 0.8 + 10);
ctx.closePath();
ctx.fill();
break;
}
}
ctx.setLineDash([]);
ctx.restore();
}
}
module.exports = TutorialOverlay;