Files
Wackelpeter/web/src/Boomerang.ts
T

111 lines
3.2 KiB
TypeScript

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<Enemy, number>();
constructor(scene: THREE.Scene) {
const shape = new THREE.Shape();
// Dünner Sichel-Bumerang: Bogen (~120° = 1/3 Kreis) mit schmaler Bahn
const outer = 0.75;
const inner = 0.47;
const half = (60 * 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, false
);
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 = [];
}
}