import * as THREE from 'three'; import type { Game } from './Game'; import type { Enemy } from './Enemy'; import { boomerangDamage, boomerangCount } from './Skills'; const ORBIT_RADIUS = 2.8; const ORBIT_HEIGHT = 0.9; const ORBIT_SPEED = 2.4; const SPIN_SPEED = 14; const HIT_CD = 0.6; const HIT_RADIUS = 0.9; export class Boomerang { private meshes: THREE.Mesh[] = []; private angle = 0; private hitTimers = new Map(); constructor(scene: THREE.Scene) { const shape = new THREE.Shape(); const outer = 1.05; const inner = 0.5; const half = (58 * Math.PI) / 180; const capR = (outer - inner) / 2; const mid = (outer + inner) / 2; shape.moveTo(outer * Math.cos(-half), outer * Math.sin(-half)); shape.absarc(0, 0, outer, -half, half, false); shape.absarc( mid * Math.cos(half), mid * Math.sin(half), capR, half, half + Math.PI, false ); shape.absarc(0, 0, inner, half, -half, true); shape.absarc( mid * Math.cos(-half), mid * Math.sin(-half), capR, -half + Math.PI, -half, true ); shape.closePath(); const geom = new THREE.ExtrudeGeometry(shape, { depth: 0.06, bevelEnabled: true, bevelThickness: 0.02, bevelSize: 0.02, bevelSegments: 2, }); geom.center(); const mat = new THREE.MeshToonMaterial({ color: 0xc98a3d }); for (let i = 0; i < 3; i++) { const mesh = new THREE.Mesh(geom, mat); mesh.rotation.x = -Math.PI / 2; mesh.castShadow = true; mesh.visible = false; scene.add(mesh); this.meshes.push(mesh); } } setLevel(level: number) { const count = level > 0 ? boomerangCount(level) : 0; for (let i = 0; i < this.meshes.length; i++) { this.meshes[i].visible = i < count; } } update(dt: number, game: Game) { const level = game.skillSystem.getLevel('boomerang'); if (level <= 0) return; const count = boomerangCount(level); const damage = boomerangDamage(level); this.angle += ORBIT_SPEED * dt; const center = game.player.body.position; for (const [enemy, t] of this.hitTimers) { this.hitTimers.set(enemy, t - dt); } for (let i = 0; i < count; i++) { const mesh = this.meshes[i]; const a = this.angle + (i / count) * Math.PI * 2; mesh.position.set( center.x + Math.cos(a) * ORBIT_RADIUS, ORBIT_HEIGHT + Math.sin(this.angle * 2 + i) * 0.15, center.z + Math.sin(a) * ORBIT_RADIUS ); mesh.rotation.z += SPIN_SPEED * dt; for (const enemy of game.enemies) { if (enemy.dead) continue; const timer = this.hitTimers.get(enemy) ?? 0; if (timer > 0) continue; const dist = enemy.body.position.distanceTo(mesh.position); if (dist < HIT_RADIUS + 0.5) { this.hitTimers.set(enemy, HIT_CD); enemy.takeDamage(damage, game, true, mesh.position); } } } } dispose(scene: THREE.Scene) { for (const mesh of this.meshes) { scene.remove(mesh); mesh.geometry.dispose(); (mesh.material as THREE.Material).dispose(); } this.meshes = []; } }