import { Vector2 } from 'three' import SAT from 'sat' import SATX from './satx.js' export default class Projectile { id = crypto.randomUUID() after = null height = 50 memory = {} onCollide = null owner = null position = new Vector2() radius = 5 speed = 1000 visualRadius = null #dest = null #homingTarget = null #game = null get game() { return this.#game } set game(value) { this.#game = value } set destination(value) { this.#dest = value } set homingTarget(value) { this.#homingTarget = value } get destination() { return this.#dest ?? this.#homingTarget?.position } constructor(options = {}) { Object.entries(options).forEach(([key, value]) => this[key] = value) if (this.visualRadius == null) { this.visualRadius = this.radius } } checkCollisions(collider) { (this.game?.entities ?? []).filter((e) => e.id != this.id).forEach((e) => { if (e.id == this.owner?.id) { return } if (SATX.collideObject(collider, e.collider())) { if (this.onCollide != null) { this.onCollide(this, e) } } }) } collider() { return new SAT.Circle(new SAT.Vector(this.position.x, this.position.y), this.radius) } despawn() { this.game?.despawn(this) } update() { this.#move() if (this.onCollide != null) { this.checkCollisions(this.collider()) } this.#checkIfArrived() } #checkIfArrived() { if (this.destination == null) { return } if (!this.position.equals(this.destination)) { return } if (this.after != null) { this.after(this, this.#homingTarget) } this.despawn() } #move() { if (this.destination == null) { return } const speed = (this.speed / (this.game?.tickBudget ?? 1000)) const prevPos = this.position.clone() if (this.position.distanceTo(this.destination) < speed) { this.position.copy(this.destination) } else { const step = this.destination.clone().sub(this.position).normalize().multiplyScalar(speed) this.position.add(step) } const tunnel = SATX.entityTunnel(prevPos.x, prevPos.y, this.position.x, this.position.y, this.radius) if (this.onCollide != null) { this.checkCollisions(tunnel) } } }