Add skill system, level-ups, and new enemy types

This commit is contained in:
2026-08-17 16:38:58 +02:00
parent 4751d428a7
commit 6f207ba1a0
23 changed files with 2192 additions and 83 deletions
+110
View File
@@ -0,0 +1,110 @@
import * as THREE from 'three';
import type { Game } from './Game';
import { Enemy } from './Enemy';
import { playSound } from './SoundManager';
export class Slime extends Enemy {
private model: THREE.Group;
private bodyMesh: THREE.Mesh;
private time = Math.random() * Math.PI * 2;
private size: number;
constructor(size = 0) {
super();
this.size = size;
this.model = new THREE.Group();
this.fadeOutModel = this.model;
this.body.add(this.model);
const color = size === 0 ? 0x33cc88 : 0x66eebb;
const mat = new THREE.MeshToonMaterial({ color });
this.bodyMesh = new THREE.Mesh(new THREE.SphereGeometry(0.6, 20, 16), mat);
this.bodyMesh.position.y = 0.55;
this.bodyMesh.castShadow = true;
this.registerFadeMesh(this.bodyMesh);
this.model.add(this.bodyMesh);
// Cute eyes (face toward the player: -Z)
const eyeMat = new THREE.MeshToonMaterial({ color: 0x111122 });
for (const side of [-1, 1]) {
const eye = new THREE.Mesh(new THREE.SphereGeometry(0.09, 10, 8), eyeMat);
eye.position.set(side * 0.18, 0.72, -0.42);
this.registerFadeMesh(eye);
this.model.add(eye);
}
if (size === 0) {
this.health = 40;
this.speed = 3.2;
this.xp = 12;
this.contactDps = 25;
} else {
this.health = 10;
this.speed = 4;
this.xp = 3;
this.contactDps = 12;
this.contactRadius = 1.5;
this.model.scale.setScalar(0.55);
this.grace = 0.3;
}
}
playAnim() {
// Procedural visuals only
}
updateAnimation(dt: number) {
if (this.grace > 0) this.grace -= dt;
this.updateFadeDeath(dt, { duration: 0.7, shrink: 0.9 });
}
update(dt: number, game: Game) {
this.time += dt;
// Squishy pulse
const pulse = 1 + Math.sin(this.time * 5) * 0.08;
this.model.scale.y = (this.size === 0 ? 1 : 0.55) * pulse;
const squash = 1 + (1 - pulse) * 0.5;
this.model.scale.x = (this.size === 0 ? 1 : 0.55) * squash;
this.model.scale.z = this.model.scale.x;
// Hop-wiggle toward the player
const toPlayer = new THREE.Vector3()
.subVectors(game.player.body.position, this.body.position);
toPlayer.y = 0;
const dist = toPlayer.length();
if (dist > 0.1) {
const dir = toPlayer.normalize();
this.body.lookAt(
this.body.position.x + dir.x,
this.body.position.y,
this.body.position.z + dir.z
);
let moveDir = dir.multiplyScalar(this.speed);
if (this.knockback > 0) {
moveDir = dir.multiplyScalar(-this.speed);
this.knockback -= dt;
}
this.body.position.x += moveDir.x * dt;
this.body.position.z += moveDir.z * dt;
game.clampToArena(this.body.position);
}
}
protected onDeath(game: Game) {
this.beginFadeDeath();
if (this.size === 0) {
playSound('spawn', 0.5);
for (let i = 0; i < 2; i++) {
const child = new Slime(1);
game.spawnEnemyInstance(
child,
this.body.position.x + (Math.random() - 0.5) * 1.2,
this.body.position.z + (Math.random() - 0.5) * 1.2,
0.1
);
}
}
}
}