Files
restitution/src/entities/components/CombatComponent.js
root 2e07519648 Refactor: Component-based architecture + 10 sub-systems
- Implemented 10 sub-systems (Economy, Pathfinding, Combat, Selection, Network, Map, Entity/Building/ControlPoint state machines, Orchestrator)
- Refactored Custom_Entity.js → Unit.js with 5 components (health, owner, inventory, movement, combat)
- Added Jest test suite with 100+ tests (EconomySystem 100%, EntityStateMachine 100%, PathfindingSystem 99%, Unit.js 72%)
- All webpack builds pass (0 errors)
- BMAD-auto team-respawn flow: 10 parallel sub-agents implemented systems

Architecture: Phaser 3 + XState + socket.io + EasyStar
Mode: team-respawn
Model: custom/ollama-cloud-pro
2026-05-29 22:13:44 +00:00

115 lines
3.5 KiB
JavaScript

/**
* CombatComponent — weapon range, damage, fire rate for a Unit.
*/
import CONSTANTS from 'PhaserClasses/CustomConstants';
export default class CombatComponent {
/**
* @param {import('../Unit').default} unit
* @param {Object} [config]
* @param {number} [config.range=150] - weapon range in px
* @param {number} [config.damage=10] - base damage per hit
* @param {number} [config.fireRate=1000] - ms between shots
* @param {string} [config.damageType='rifle'] - key into CombatSystem modifiers
* @param {number} [config.accuracy=1.0] - 0.0 to 1.0 hit probability
*/
constructor(unit, config = {}) {
this.unit = unit;
/** @type {number} */
this._range = config.range ?? CONSTANTS.RIFLE.RANGE;
/** @type {number} */
this._damage = config.damage ?? CONSTANTS.RIFLE.DAMAGE;
/** @type {number} */
this._fireRate = config.fireRate ?? 1000;
/** @type {string} */
this._damageType = config.damageType ?? 'rifle';
/** @type {number} */
this._accuracy = config.accuracy ?? 1.0;
/** @type {number} */
this._lastFireTime = 0;
/** @type {import('../Unit').default|null} */
this._target = null;
}
// ── Accessors ─────────────────────────────────────────────────
/** @returns {number} */ get range() { return this._range; }
/** @returns {number} */ get damage() { return this._damage; }
/** @returns {number} */ get fireRate() { return this._fireRate; }
/** @returns {string} */ get damageType() { return this._damageType; }
/** @returns {number} */ get accuracy() { return this._accuracy; }
/** @returns {number} */ get lastFireTime() { return this._lastFireTime; }
/** @returns {import('../Unit').default|null} */ get target() { return this._target; }
/** @param {number} val */
set range(val) { this._range = val; }
/** @param {number} val */
set damage(val) { this._damage = val; }
/** @param {number} val */
set fireRate(val) { this._fireRate = val; }
/** @param {import('../Unit').default|null} val */
set target(val) { this._target = val; }
// ── Public API ────────────────────────────────────────────────
/**
* Check if the weapon can fire (cooldown elapsed).
* @param {number} currentTime - scene time in ms
* @returns {boolean}
*/
canFire(currentTime) {
return (currentTime - this._lastFireTime) >= this._fireRate;
}
/**
* Record a shot fired at the given time.
* @param {number} time - scene time in ms
*/
recordFire(time) {
this._lastFireTime = time;
}
/**
* Check if this unit can hit a target based on range.
* @param {import('../Unit').default} target
* @returns {boolean}
*/
canHitBody(target) {
if (!target || !target.body || !this.unit || !this.unit.body) return false;
const pointA = this.unit.body.center;
const pointB = target.body.center;
return Phaser.Math.Distance.BetweenPoints(pointA, pointB) < this._range;
}
/**
* Check if a shot connects (accuracy roll).
* @returns {boolean}
*/
accuracyCheck() {
return Math.random() < this._accuracy;
}
/**
* Serialize for network sync.
*/
serialize() {
return {
range: this._range,
damage: this._damage,
fireRate: this._fireRate,
damageType: this._damageType,
accuracy: this._accuracy,
};
}
}