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 ); } } } }