import * as THREE from "three"; export class Projectile { constructor(pos, target, speed, scene, projectileEffect = null) { this.position = pos.clone(); this.target = target; this.speed = speed; this.scene = scene; this.damage = 0; // Will be set by tower this.projectileEffect = projectileEffect; const geo = new THREE.SphereGeometry(0.15, 8, 8); const mat = new THREE.MeshStandardMaterial({ color: projectileEffect && projectileEffect.color ? projectileEffect.color : 0xffe082, emissive: projectileEffect && projectileEffect.emissive ? projectileEffect.emissive : 0x553300, }); const mesh = new THREE.Mesh(geo, mat); mesh.castShadow = true; mesh.position.copy(this.position); this.mesh = mesh; scene.add(mesh); this.alive = true; } update(dt, spawnHitEffect) { if (!this.alive) return "dead"; if (!this.target || this.target.isDead()) { this.alive = false; return "dead"; } const toTarget = new THREE.Vector3().subVectors( this.target.mesh.position, this.position ); const dist = toTarget.length(); if (dist < 0.4) { this.target.takeDamage(this.damage); // Apply on-hit effect if any if ( this.projectileEffect && this.projectileEffect.type === "slow" && this.target.applySlow ) { const mult = this.projectileEffect.mult ?? 1.0; const duration = this.projectileEffect.duration ?? 0; this.target.applySlow(mult, duration); } spawnHitEffect(this.position); this.alive = false; return "hit"; } toTarget.normalize(); this.position.addScaledVector(toTarget, this.speed * dt); this.mesh.position.copy(this.position); return "ok"; } destroy() { this.scene.remove(this.mesh); } }