Add game source, converter pipeline, and web port
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
import * as THREE from 'three';
|
||||
import { Weapon } from './Weapon';
|
||||
import { Player } from './Player';
|
||||
import { loadGLB } from './MeshLoader';
|
||||
import { playSound } from './SoundManager';
|
||||
|
||||
export class Sword extends Weapon {
|
||||
private mixer: THREE.AnimationMixer | null = null;
|
||||
private clips: Map<string, THREE.AnimationAction> = new Map();
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.COOLDOWN1_TIME = 0.5;
|
||||
|
||||
this.mesh.position.set(-0.35, 0.3, 0.35);
|
||||
this.mesh.scale.set(0.2, 0.2, 0.2);
|
||||
this.mesh.rotation.set(0.3, 0, 0.3);
|
||||
|
||||
// Fallback geometry
|
||||
const bladeGeom = new THREE.BoxGeometry(0.08, 1.0, 0.15);
|
||||
const handleGeom = new THREE.CylinderGeometry(0.05, 0.05, 0.2, 8);
|
||||
const guardGeom = new THREE.BoxGeometry(0.25, 0.06, 0.06);
|
||||
const metalMat = new THREE.MeshStandardMaterial({ color: 0xcccccc, metalness: 0.9, roughness: 0.3 });
|
||||
const darkMat = new THREE.MeshStandardMaterial({ color: 0x444444, roughness: 0.6 });
|
||||
|
||||
const blade = new THREE.Mesh(bladeGeom, metalMat);
|
||||
blade.position.y = 0.4;
|
||||
blade.castShadow = true;
|
||||
const guard = new THREE.Mesh(guardGeom, metalMat);
|
||||
guard.position.y = -0.1;
|
||||
const handle = new THREE.Mesh(handleGeom, darkMat);
|
||||
handle.position.y = -0.25;
|
||||
this.mesh.add(blade);
|
||||
this.mesh.add(guard);
|
||||
this.mesh.add(handle);
|
||||
this._fallback = true;
|
||||
}
|
||||
|
||||
async init() {
|
||||
try {
|
||||
const gltf = await loadGLB('sword');
|
||||
const model = gltf.scene;
|
||||
model.scale.set(1, 1, 1);
|
||||
const swordTexture = new THREE.TextureLoader().load('./textures/sword.png');
|
||||
swordTexture.flipY = false;
|
||||
swordTexture.colorSpace = THREE.SRGBColorSpace;
|
||||
model.traverse((child) => {
|
||||
if (child instanceof THREE.Mesh) {
|
||||
child.castShadow = true;
|
||||
child.material = new THREE.MeshToonMaterial({ map: swordTexture });
|
||||
}
|
||||
});
|
||||
|
||||
// Remove fallback geometry
|
||||
while (this.mesh.children.length > 0) {
|
||||
this.mesh.remove(this.mesh.children[0]);
|
||||
}
|
||||
this._fallback = false;
|
||||
this.mesh.add(model);
|
||||
|
||||
if (gltf.animations.length > 0) {
|
||||
this.mixer = new THREE.AnimationMixer(model);
|
||||
for (const clip of gltf.animations) {
|
||||
const action = this.mixer.clipAction(clip);
|
||||
this.clips.set(clip.name, action);
|
||||
}
|
||||
const normal = this.clips.get('normal');
|
||||
if (normal) {
|
||||
normal.play();
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('Failed to load sword model', e);
|
||||
}
|
||||
}
|
||||
|
||||
private _fallback = false;
|
||||
|
||||
updateAnimation(dt: number) {
|
||||
if (this.mixer) this.mixer.update(dt);
|
||||
}
|
||||
|
||||
skill1(_groundPoint: THREE.Vector3, player: Player): void {
|
||||
if (this.cooldown1 > 0) return;
|
||||
this.cooldown1 = this.COOLDOWN1_TIME;
|
||||
|
||||
playSound('sword_hit1', 0.5);
|
||||
|
||||
// Play skill1 animation
|
||||
const action = this.clips.get('skill1');
|
||||
if (action) {
|
||||
action.reset();
|
||||
action.setLoop(THREE.LoopOnce, 1);
|
||||
action.setEffectiveTimeScale(2);
|
||||
action.play();
|
||||
}
|
||||
|
||||
const { game } = player;
|
||||
|
||||
for (const enemy of game.enemies) {
|
||||
if (enemy.dead) continue;
|
||||
const dist = enemy.body.position.distanceTo(player.body.position);
|
||||
if (dist < 3) {
|
||||
const toEnemy = new THREE.Vector3()
|
||||
.subVectors(enemy.body.position, player.body.position).normalize();
|
||||
const playerFwd = new THREE.Vector3(0, 0, 1);
|
||||
playerFwd.applyQuaternion(player.body.quaternion);
|
||||
const dot = playerFwd.dot(toEnemy);
|
||||
if (dot > 0.3) {
|
||||
enemy.takeDamage(50, game);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
skill2(): void {
|
||||
// Not implemented
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user