97 lines
3.1 KiB
TypeScript
97 lines
3.1 KiB
TypeScript
import * as THREE from 'three';
|
|
import { Weapon } from './Weapon';
|
|
import { Player } from './Player';
|
|
import { Fireball } from './Fireball';
|
|
import { playSound } from './SoundManager';
|
|
import { loadGLB } from './MeshLoader';
|
|
import { fireballDamage, fireballCooldown, fireballRadius, teleportCooldown } from './Skills';
|
|
|
|
export class Staff extends Weapon {
|
|
constructor() {
|
|
super();
|
|
this.COOLDOWN1_TIME = 5;
|
|
this.COOLDOWN2_TIME = 50;
|
|
|
|
// Fallback geometry
|
|
const shaftGeom = new THREE.CylinderGeometry(0.04, 0.05, 1.0, 8);
|
|
const crystalGeom = new THREE.OctahedronGeometry(0.08);
|
|
const woodMat = new THREE.MeshToonMaterial({ color: 0x8B4513 });
|
|
const crystalMat = new THREE.MeshToonMaterial({ color: 0x4488ff });
|
|
const shaft = new THREE.Mesh(shaftGeom, woodMat);
|
|
shaft.position.y = 0.3;
|
|
shaft.castShadow = true;
|
|
const crystal = new THREE.Mesh(crystalGeom, crystalMat);
|
|
crystal.position.y = 0.9;
|
|
this.mesh.add(shaft);
|
|
this.mesh.add(crystal);
|
|
|
|
this.mesh.position.set(0.35, 0.3, 0.35);
|
|
this.mesh.scale.set(0.35, 0.35, 0.35);
|
|
this.mesh.rotation.set(-0.2, 0, -0.2);
|
|
}
|
|
|
|
async init() {
|
|
try {
|
|
const gltf = await loadGLB('staff');
|
|
const model = gltf.scene;
|
|
const staffTexture = new THREE.TextureLoader().load('./textures/staff1.png');
|
|
staffTexture.flipY = false;
|
|
staffTexture.colorSpace = THREE.SRGBColorSpace;
|
|
model.traverse((child) => {
|
|
if (child instanceof THREE.Mesh) {
|
|
child.castShadow = true;
|
|
child.material = new THREE.MeshToonMaterial({ map: staffTexture });
|
|
}
|
|
});
|
|
while (this.mesh.children.length > 0) {
|
|
this.mesh.remove(this.mesh.children[0]);
|
|
}
|
|
this.mesh.add(model);
|
|
} catch {
|
|
// Keep fallback
|
|
}
|
|
}
|
|
|
|
skill1(groundPoint: THREE.Vector3, player: Player): void {
|
|
const level = player.game.skillSystem.getLevel('fireball');
|
|
if (level <= 0) return;
|
|
if (this.cooldown1 > 0) return;
|
|
|
|
this.COOLDOWN1_TIME = fireballCooldown(level);
|
|
this.cooldown1 = this.COOLDOWN1_TIME;
|
|
|
|
// Spawn at staff height, fly toward the cursor point
|
|
const spawnPos = player.body.position.clone();
|
|
spawnPos.y += 0.8;
|
|
|
|
const dir = new THREE.Vector3()
|
|
.subVectors(groundPoint, spawnPos)
|
|
.normalize()
|
|
.multiplyScalar(25);
|
|
dir.setY(0);
|
|
|
|
const fb = new Fireball(spawnPos, dir);
|
|
fb.damage = fireballDamage(level);
|
|
fb.explosionRadius = fireballRadius(level);
|
|
player.game.scene.add(fb.body);
|
|
player.game.fireballs.push(fb);
|
|
playSound('fireball', 0.5);
|
|
}
|
|
|
|
skill2(groundPoint: THREE.Vector3, player: Player): void {
|
|
const level = player.game.skillSystem.getLevel('teleport');
|
|
if (level <= 0) return;
|
|
if (this.cooldown2 > 0) return;
|
|
|
|
this.COOLDOWN2_TIME = teleportCooldown(level);
|
|
this.cooldown2 = this.COOLDOWN2_TIME;
|
|
|
|
const newPos = groundPoint.clone();
|
|
newPos.y = 0.5;
|
|
newPos.x = Math.max(-47, Math.min(47, newPos.x));
|
|
newPos.z = Math.max(-47, Math.min(47, newPos.z));
|
|
player.body.position.copy(newPos);
|
|
playSound('teleport', 0.5);
|
|
}
|
|
}
|