Add aura, boomerang, trap and gas cloud skills with hotbar and ESC pause
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
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();
|
||||
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 = [];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user