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
+119
View File
@@ -0,0 +1,119 @@
/**
* Bullet.js
* Bullet entity that travels in a straight line and interacts with terrain/tanks.
*/
const {
BULLET_SPEED,
BULLET_SIZE,
DIRECTION,
DIR_VECTORS,
MAP_OFFSET_X,
MAP_OFFSET_Y,
MAP_WIDTH,
MAP_HEIGHT,
} = require('../base/GameGlobal');
class Bullet {
constructor() {
this.x = 0;
this.y = 0;
this.direction = DIRECTION.UP;
this.speed = BULLET_SPEED;
this.size = BULLET_SIZE;
this.halfSize = BULLET_SIZE / 2;
this.alive = false;
this.canBreakSteel = false;
/** @type {'player'|'enemy'} */
this.owner = 'player';
/** @type {object|null} Reference to the tank that fired this bullet */
this.ownerTank = null;
}
/**
* Initialize/reset the bullet for reuse from object pool.
* @param {object} config
* @param {number} config.x
* @param {number} config.y
* @param {number} config.direction
* @param {string} config.owner - 'player' or 'enemy'
* @param {boolean} [config.canBreakSteel]
* @param {object} [config.ownerTank]
*/
init(config) {
this.x = config.x;
this.y = config.y;
this.direction = config.direction;
this.owner = config.owner || 'player';
this.canBreakSteel = config.canBreakSteel || false;
this.ownerTank = config.ownerTank || null;
this.alive = true;
}
/**
* Update bullet position.
* @param {number} dt - Delta time in seconds.
*/
update(dt) {
if (!this.alive) return;
const vec = DIR_VECTORS[this.direction];
const moveAmount = this.speed * dt * 60;
this.x += vec.dx * moveAmount;
this.y += vec.dy * moveAmount;
// Check map boundaries
if (
this.x < MAP_OFFSET_X ||
this.y < MAP_OFFSET_Y ||
this.x > MAP_OFFSET_X + MAP_WIDTH ||
this.y > MAP_OFFSET_Y + MAP_HEIGHT
) {
this.destroy();
}
}
/**
* Render the bullet.
* @param {CanvasRenderingContext2D} ctx
*/
render(ctx) {
if (!this.alive) return;
ctx.fillStyle = this.owner === 'player' ? '#FFFF00' : '#FF6600';
ctx.fillRect(
this.x - this.halfSize,
this.y - this.halfSize,
this.size,
this.size
);
}
/**
* Get bounding box.
* @returns {{x: number, y: number, w: number, h: number}}
*/
getBounds() {
return {
x: this.x - this.halfSize,
y: this.y - this.halfSize,
w: this.size,
h: this.size,
};
}
/**
* Destroy the bullet (mark for recycling).
*/
destroy() {
this.alive = false;
// Decrement owner's active bullet count
if (this.ownerTank && typeof this.ownerTank.activeBullets === 'number') {
this.ownerTank.activeBullets = Math.max(0, this.ownerTank.activeBullets - 1);
}
}
}
module.exports = Bullet;