Add Spider Queen, Frost Giant, Necromancer and Dragon bosses
This commit is contained in:
@@ -0,0 +1,527 @@
|
||||
import * as THREE from 'three';
|
||||
import type { Game } from './Game';
|
||||
import { Enemy } from './Enemy';
|
||||
import { Ghost } from './Ghost';
|
||||
import { Slime } from './Slime';
|
||||
import { Spider } from './Spider';
|
||||
import { playSound } from './SoundManager';
|
||||
|
||||
const MAX_HEALTH = 13000;
|
||||
const HIT_FLASH_TIME = 0.14;
|
||||
const MODEL_SCALE = 1.8;
|
||||
const FLOAT_HEIGHT = 1.2;
|
||||
const BOB_AMPLITUDE = 0.25;
|
||||
const DRIFT_SPEED = 2.5;
|
||||
|
||||
const TELEPORT_COOLDOWN = 6;
|
||||
const TELEPORT_COOLDOWN_2 = 4;
|
||||
const TELEPORT_CHARGE = 1.0;
|
||||
const TELEPORT_CHARGE_2 = 0.6;
|
||||
const TELEPORT_RADIUS = 3.2;
|
||||
const TELEPORT_DAMAGE = 15;
|
||||
const TELEPORT_RING_PULSE = 6;
|
||||
|
||||
const VOLLEY_COOLDOWN = 4;
|
||||
const VOLLEY_COOLDOWN_2 = 3;
|
||||
const VOLLEY_COUNT = 5;
|
||||
const VOLLEY_COUNT_2 = 7;
|
||||
const VOLLEY_SPREAD = 0.4;
|
||||
const BOLT_DAMAGE = 18;
|
||||
|
||||
const SUMMON_COOLDOWN = 8;
|
||||
const SUMMON_COOLDOWN_2 = 6;
|
||||
const SUMMON_COUNT = 3;
|
||||
const SUMMON_COUNT_2 = 5;
|
||||
const SUMMON_TELEGRAPH = 0.8;
|
||||
|
||||
const ENRAGE_RATIO = 0.4;
|
||||
|
||||
export class Necromancer extends Enemy {
|
||||
private model: THREE.Group;
|
||||
private game: Game | null = null;
|
||||
private waveTime = 0;
|
||||
private enraged = false;
|
||||
private hitFlash = 0;
|
||||
|
||||
private robeMat!: THREE.MeshToonMaterial;
|
||||
private darkMat!: THREE.MeshToonMaterial;
|
||||
private glowMat!: THREE.MeshBasicMaterial;
|
||||
private eyeMat!: THREE.MeshBasicMaterial;
|
||||
private shadow!: THREE.Mesh;
|
||||
private shadowMat!: THREE.MeshBasicMaterial;
|
||||
|
||||
private teleportTimer = 3;
|
||||
private volleyTimer = 1.5;
|
||||
private summonTimer = 5;
|
||||
private teleportState: 'idle' | 'charge' = 'idle';
|
||||
private teleportCharge = 0;
|
||||
private teleportTarget = new THREE.Vector3();
|
||||
private teleportRing!: THREE.Group;
|
||||
private teleportRingMesh!: THREE.Mesh;
|
||||
private teleportRingDisc!: THREE.Mesh;
|
||||
private teleportRingMat!: THREE.MeshBasicMaterial;
|
||||
private teleportDiscMat!: THREE.MeshBasicMaterial;
|
||||
private teleportRingInScene = false;
|
||||
|
||||
private spawnRings: { group: THREE.Group; timer: number; spawn: () => void }[] = [];
|
||||
private arrivalFlashes: { mesh: THREE.Mesh; mat: THREE.MeshBasicMaterial; life: number }[] = [];
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.health = MAX_HEALTH;
|
||||
this.maxHealth = MAX_HEALTH;
|
||||
this.isBoss = true;
|
||||
this.displayName = 'Erz-Nekromant';
|
||||
this.speed = 0;
|
||||
this.xp = 320;
|
||||
this.contactDps = 15;
|
||||
this.contactRadius = 1.8;
|
||||
this.guaranteedDrop = true;
|
||||
|
||||
this.model = new THREE.Group();
|
||||
this.model.scale.setScalar(MODEL_SCALE);
|
||||
this.fadeOutModel = this.model;
|
||||
this.body.add(this.model);
|
||||
|
||||
this.robeMat = new THREE.MeshToonMaterial({ color: 0x6a3a9a, emissive: 0x1a0a2a });
|
||||
this.darkMat = new THREE.MeshToonMaterial({ color: 0x2a1445 });
|
||||
this.glowMat = new THREE.MeshBasicMaterial({ color: 0xbb66ff });
|
||||
this.eyeMat = new THREE.MeshBasicMaterial({ color: 0xdd88ff });
|
||||
|
||||
// Robe (umgedrehter Kegel, schwingt beim Schweben)
|
||||
const robe = new THREE.Mesh(new THREE.ConeGeometry(0.9, 1.8, 12, 1, true), this.robeMat);
|
||||
robe.position.y = -0.2;
|
||||
robe.rotation.x = Math.PI;
|
||||
robe.castShadow = true;
|
||||
this.registerFadeMesh(robe);
|
||||
this.model.add(robe);
|
||||
|
||||
// Rocksaum
|
||||
const hem = new THREE.Mesh(new THREE.CircleGeometry(0.95, 12), this.darkMat);
|
||||
hem.rotation.x = -Math.PI / 2;
|
||||
hem.position.y = -1.05;
|
||||
this.registerFadeMesh(hem);
|
||||
this.model.add(hem);
|
||||
|
||||
// Oberkörper
|
||||
const body = new THREE.Mesh(new THREE.SphereGeometry(0.55, 16, 12), this.robeMat);
|
||||
body.position.y = 0.6;
|
||||
body.scale.set(1, 1.2, 1);
|
||||
body.castShadow = true;
|
||||
this.registerFadeMesh(body);
|
||||
this.model.add(body);
|
||||
|
||||
// Kapuze
|
||||
const hood = new THREE.Mesh(new THREE.ConeGeometry(0.42, 0.8, 12, 1, true), this.darkMat);
|
||||
hood.position.set(0, 1.1, 0.1);
|
||||
hood.rotation.x = 0.15;
|
||||
this.registerFadeMesh(hood);
|
||||
this.model.add(hood);
|
||||
|
||||
// Kapuzen-Öffnung (+Z, dunkel)
|
||||
const hoodFace = new THREE.Mesh(new THREE.CircleGeometry(0.36, 12), this.darkMat);
|
||||
hoodFace.position.set(0, 1.02, 0.42);
|
||||
hoodFace.rotation.y = Math.PI / 2 - 0.1;
|
||||
hoodFace.rotation.x = -0.35;
|
||||
this.registerFadeMesh(hoodFace);
|
||||
this.model.add(hoodFace);
|
||||
|
||||
// Leuchtende Augen in der Kapuze (+Z)
|
||||
for (const side of [-1, 1]) {
|
||||
const eye = new THREE.Mesh(new THREE.SphereGeometry(0.07, 8, 6), this.eyeMat);
|
||||
eye.position.set(side * 0.13, 1.05, 0.44);
|
||||
this.model.add(eye);
|
||||
}
|
||||
|
||||
// Glühender Amulett-Kern
|
||||
const amulet = new THREE.Mesh(new THREE.SphereGeometry(0.08, 8, 6), this.glowMat);
|
||||
amulet.position.set(0, 0.62, 0.52);
|
||||
this.model.add(amulet);
|
||||
|
||||
// Arm mit Stab
|
||||
const arm = new THREE.Mesh(new THREE.CylinderGeometry(0.09, 0.12, 0.6, 8), this.darkMat);
|
||||
arm.position.set(0.55, 0.7, 0.15);
|
||||
arm.rotation.z = -0.6;
|
||||
this.registerFadeMesh(arm);
|
||||
this.model.add(arm);
|
||||
|
||||
const staff = new THREE.Mesh(new THREE.CylinderGeometry(0.05, 0.05, 2.0, 8), this.darkMat);
|
||||
staff.position.set(0.85, 1.3, 0.25);
|
||||
staff.rotation.z = 0.35;
|
||||
staff.rotation.x = 0.2;
|
||||
this.registerFadeMesh(staff);
|
||||
this.model.add(staff);
|
||||
|
||||
const orbMat = new THREE.MeshBasicMaterial({
|
||||
color: 0xbb66ff,
|
||||
transparent: true,
|
||||
opacity: 0.85,
|
||||
blending: THREE.AdditiveBlending,
|
||||
depthWrite: false,
|
||||
});
|
||||
const orb = new THREE.Mesh(new THREE.SphereGeometry(0.16, 10, 8), orbMat);
|
||||
orb.position.set(1.2, 1.95, 0.35);
|
||||
this.model.add(orb);
|
||||
this.orbMat = orbMat;
|
||||
this.orb = orb;
|
||||
|
||||
// Boden-Schatten
|
||||
this.shadowMat = new THREE.MeshBasicMaterial({
|
||||
color: 0x000000,
|
||||
transparent: true,
|
||||
opacity: 0.3,
|
||||
});
|
||||
this.shadow = new THREE.Mesh(new THREE.CircleGeometry(0.9, 20), this.shadowMat);
|
||||
this.shadow.rotation.x = -Math.PI / 2;
|
||||
this.shadow.renderOrder = 998;
|
||||
this.body.add(this.shadow);
|
||||
|
||||
// Teleport-Telegraf: Ring + Scheibe am Zielpunkt
|
||||
this.teleportRingMat = new THREE.MeshBasicMaterial({
|
||||
color: 0xbb66ff,
|
||||
transparent: true,
|
||||
opacity: 0,
|
||||
side: THREE.DoubleSide,
|
||||
depthWrite: false,
|
||||
});
|
||||
this.teleportDiscMat = new THREE.MeshBasicMaterial({
|
||||
color: 0x8833cc,
|
||||
transparent: true,
|
||||
opacity: 0,
|
||||
depthWrite: false,
|
||||
});
|
||||
const ringMesh = new THREE.Mesh(
|
||||
new THREE.RingGeometry(1.6, 2.0, 32),
|
||||
this.teleportRingMat
|
||||
);
|
||||
ringMesh.rotation.x = -Math.PI / 2;
|
||||
ringMesh.position.y = 0.05;
|
||||
const disc = new THREE.Mesh(new THREE.CircleGeometry(1.8, 32), this.teleportDiscMat);
|
||||
disc.rotation.x = -Math.PI / 2;
|
||||
disc.position.y = 0.03;
|
||||
this.teleportRingMesh = ringMesh;
|
||||
this.teleportRingDisc = disc;
|
||||
this.teleportRing = new THREE.Group();
|
||||
this.teleportRing.add(ringMesh, disc);
|
||||
this.teleportRing.visible = false;
|
||||
}
|
||||
|
||||
private orb!: THREE.Mesh;
|
||||
private orbMat!: THREE.MeshBasicMaterial;
|
||||
|
||||
playAnim() {
|
||||
// Prozedurale Optik
|
||||
}
|
||||
|
||||
updateAnimation(dt: number) {
|
||||
this.updateFadeDeath(dt, { duration: 1.2, sink: 0.5 });
|
||||
}
|
||||
|
||||
update(dt: number, game: Game) {
|
||||
this.game = game;
|
||||
this.waveTime += dt;
|
||||
|
||||
// Schweben
|
||||
this.body.position.y = FLOAT_HEIGHT + Math.sin(this.waveTime * 2.2) * BOB_AMPLITUDE;
|
||||
this.shadow.position.y = 0.06 - this.body.position.y;
|
||||
const sway = Math.sin(this.waveTime * 1.5) * 0.06;
|
||||
this.model.rotation.z = sway;
|
||||
this.model.rotation.x = Math.sin(this.waveTime * 2.2) * 0.04;
|
||||
|
||||
// Hit-Feedback
|
||||
if (this.hitFlash > 0) {
|
||||
this.hitFlash -= dt;
|
||||
this.robeMat.color.set(0xffffff);
|
||||
this.darkMat.color.set(0xffffff);
|
||||
this.model.scale.setScalar(MODEL_SCALE * (1 + (this.hitFlash / HIT_FLASH_TIME) * 0.05));
|
||||
} else {
|
||||
this.robeMat.color.set(this.enraged ? 0x8a2233 : 0x6a3a9a);
|
||||
this.darkMat.color.set(this.enraged ? 0x551520 : 0x2a1445);
|
||||
this.model.scale.setScalar(MODEL_SCALE);
|
||||
}
|
||||
|
||||
// Orb pulsiert
|
||||
const orbPulse = 1 + Math.abs(Math.sin(this.waveTime * 5)) * 0.25;
|
||||
this.orb.scale.setScalar(orbPulse);
|
||||
this.orbMat.opacity = 0.6 + Math.abs(Math.sin(this.waveTime * 5)) * 0.3;
|
||||
|
||||
// Enrage unter 40% HP
|
||||
if (!this.enraged && this.health < MAX_HEALTH * ENRAGE_RATIO) {
|
||||
this.enraged = true;
|
||||
this.eyeMat.color.set(0xff4422);
|
||||
this.glowMat.color.set(0xff6644);
|
||||
playSound('damage', 0.9);
|
||||
game.triggerCameraShake(0.35, 0.5);
|
||||
}
|
||||
|
||||
// Zum Spieler driften (langsam)
|
||||
const playerPos = game.player.body.position;
|
||||
const toPlayer = new THREE.Vector3().subVectors(playerPos, 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(DRIFT_SPEED);
|
||||
if (this.knockback > 0) {
|
||||
moveDir = dir.multiplyScalar(-DRIFT_SPEED);
|
||||
this.knockback -= dt;
|
||||
}
|
||||
this.body.position.x += moveDir.x * dt;
|
||||
this.body.position.z += moveDir.z * dt;
|
||||
game.clampToArena(this.body.position);
|
||||
}
|
||||
|
||||
// Teleport
|
||||
if (this.teleportState === 'idle') {
|
||||
this.teleportTimer -= dt;
|
||||
if (this.teleportTimer <= 0) {
|
||||
this.teleportTimer = this.enraged ? TELEPORT_COOLDOWN_2 : TELEPORT_COOLDOWN;
|
||||
this.startTeleport(game);
|
||||
}
|
||||
} else {
|
||||
this.updateTeleportCharge(dt, game);
|
||||
}
|
||||
|
||||
// Salven
|
||||
this.volleyTimer -= dt;
|
||||
if (this.volleyTimer <= 0) {
|
||||
this.volleyTimer = this.enraged ? VOLLEY_COOLDOWN_2 : VOLLEY_COOLDOWN;
|
||||
this.fireVolley(game);
|
||||
}
|
||||
|
||||
// Beschwörung
|
||||
this.summonTimer -= dt;
|
||||
if (this.summonTimer <= 0) {
|
||||
this.summonTimer = this.enraged ? SUMMON_COOLDOWN_2 : SUMMON_COOLDOWN;
|
||||
this.startSummon(game);
|
||||
}
|
||||
|
||||
// Beschwörungs-Telegraphen
|
||||
for (let i = this.spawnRings.length - 1; i >= 0; i--) {
|
||||
const r = this.spawnRings[i];
|
||||
r.timer -= dt;
|
||||
if (r.timer <= 0) {
|
||||
r.spawn();
|
||||
game.scene.remove(r.group);
|
||||
r.group.traverse((obj) => {
|
||||
if (obj instanceof THREE.Mesh) {
|
||||
obj.geometry.dispose();
|
||||
(obj.material as THREE.Material).dispose();
|
||||
}
|
||||
});
|
||||
this.spawnRings.splice(i, 1);
|
||||
} else {
|
||||
const t = 1 - r.timer / SUMMON_TELEGRAPH;
|
||||
r.group.scale.setScalar(0.5 + t * 1.2);
|
||||
}
|
||||
}
|
||||
|
||||
// Ankunfts-Blitz-Flächen
|
||||
for (let i = this.arrivalFlashes.length - 1; i >= 0; i--) {
|
||||
const f = this.arrivalFlashes[i];
|
||||
f.life -= dt;
|
||||
const t = 1 - Math.max(0, f.life) / 0.3;
|
||||
f.mesh.scale.setScalar(0.5 + t * 5);
|
||||
f.mat.opacity = 0.7 * (1 - t);
|
||||
if (f.life <= 0) {
|
||||
game.scene.remove(f.mesh);
|
||||
f.mesh.geometry.dispose();
|
||||
f.mat.dispose();
|
||||
this.arrivalFlashes.splice(i, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- Teleport ---
|
||||
|
||||
private startTeleport(game: Game) {
|
||||
this.teleportState = 'charge';
|
||||
this.teleportCharge = this.enraged ? TELEPORT_CHARGE_2 : TELEPORT_CHARGE;
|
||||
const playerPos = game.player.body.position;
|
||||
// Ziel = aktuelle Spielerposition (leicht zufällig versetzt)
|
||||
this.teleportTarget.set(
|
||||
playerPos.x + (Math.random() - 0.5) * 2,
|
||||
0,
|
||||
playerPos.z + (Math.random() - 0.5) * 2
|
||||
);
|
||||
game.clampToArena(this.teleportTarget);
|
||||
if (!this.teleportRingInScene) {
|
||||
game.scene.add(this.teleportRing);
|
||||
this.teleportRingInScene = true;
|
||||
}
|
||||
this.teleportRing.position.set(this.teleportTarget.x, 0, this.teleportTarget.z);
|
||||
this.teleportRing.visible = true;
|
||||
playSound('spawn', 0.7);
|
||||
}
|
||||
|
||||
private updateTeleportCharge(dt: number, game: Game) {
|
||||
this.teleportCharge -= dt;
|
||||
const pulse = Math.abs(Math.sin(this.waveTime * TELEPORT_RING_PULSE));
|
||||
this.teleportRingMat.opacity = 0.3 + 0.5 * pulse;
|
||||
this.teleportDiscMat.opacity = 0.1 + 0.08 * pulse;
|
||||
|
||||
if (this.teleportCharge <= 0) {
|
||||
this.teleportState = 'idle';
|
||||
this.teleportRing.visible = false;
|
||||
this.teleportRingMat.opacity = 0;
|
||||
this.teleportDiscMat.opacity = 0;
|
||||
|
||||
// Blitz
|
||||
this.body.position.x = this.teleportTarget.x;
|
||||
this.body.position.z = this.teleportTarget.z;
|
||||
playSound('teleport', 0.8);
|
||||
|
||||
// Ankunftsexplosion + Schaden, wenn der Spieler unter dem Zielpunkt steht
|
||||
const flashMat = new THREE.MeshBasicMaterial({
|
||||
color: 0xbb66ff,
|
||||
transparent: true,
|
||||
opacity: 0.7,
|
||||
side: THREE.DoubleSide,
|
||||
depthWrite: false,
|
||||
});
|
||||
const flash = new THREE.Mesh(new THREE.RingGeometry(0.9, 1.1, 32), flashMat);
|
||||
flash.rotation.x = -Math.PI / 2;
|
||||
flash.position.set(this.teleportTarget.x, 0.07, this.teleportTarget.z);
|
||||
game.scene.add(flash);
|
||||
this.arrivalFlashes.push({ mesh: flash, mat: flashMat, life: 0.3 });
|
||||
|
||||
if (game.player.health > 0 && game.player.body.position.y <= 0.8) {
|
||||
const dx = game.player.body.position.x - this.teleportTarget.x;
|
||||
const dz = game.player.body.position.z - this.teleportTarget.z;
|
||||
if (Math.hypot(dx, dz) < TELEPORT_RADIUS) {
|
||||
game.player.damage(TELEPORT_DAMAGE, this.teleportTarget);
|
||||
}
|
||||
}
|
||||
|
||||
// Salve vom neuen Standort
|
||||
this.fireVolley(game);
|
||||
}
|
||||
}
|
||||
|
||||
// --- Projektilsalve ---
|
||||
|
||||
private fireVolley(game: Game) {
|
||||
const from = new THREE.Vector3(this.body.position.x, FLOAT_HEIGHT + 0.6, this.body.position.z);
|
||||
const target = game.player.body.position.clone();
|
||||
const count = this.enraged ? VOLLEY_COUNT_2 : VOLLEY_COUNT;
|
||||
const baseDir = new THREE.Vector3().subVectors(target, from);
|
||||
baseDir.y = 0;
|
||||
baseDir.normalize();
|
||||
const perp = new THREE.Vector3(-baseDir.z, 0, baseDir.x);
|
||||
for (let i = 0; i < count; i++) {
|
||||
const off = (i / (count - 1) - 0.5) * 2 * VOLLEY_SPREAD;
|
||||
const dir = new THREE.Vector3()
|
||||
.addScaledVector(baseDir, 1)
|
||||
.addScaledVector(perp, off)
|
||||
.normalize();
|
||||
const t = from.clone().add(dir.multiplyScalar(25));
|
||||
t.y = 0.5;
|
||||
game.spawnEnemyProjectile(from, t, BOLT_DAMAGE);
|
||||
}
|
||||
playSound('spawn', 0.5);
|
||||
}
|
||||
|
||||
// --- Beschwörung ---
|
||||
|
||||
private startSummon(game: Game) {
|
||||
const count = this.enraged ? SUMMON_COUNT_2 : SUMMON_COUNT;
|
||||
const types = [() => new Ghost(), () => new Slime(), () => new Spider()];
|
||||
for (let i = 0; i < count; i++) {
|
||||
const a = Math.random() * Math.PI * 2;
|
||||
const r = 5 + Math.random() * 8;
|
||||
const [x, z] = this.clampToArena(game,
|
||||
this.body.position.x + Math.cos(a) * r,
|
||||
this.body.position.z + Math.sin(a) * r
|
||||
);
|
||||
|
||||
// Wachsender Boden-Glow als Telegraf
|
||||
const mat = new THREE.MeshBasicMaterial({
|
||||
color: 0xbb66ff,
|
||||
transparent: true,
|
||||
opacity: 0.35,
|
||||
depthWrite: false,
|
||||
});
|
||||
const ring = new THREE.Mesh(new THREE.CircleGeometry(1.6, 24), mat);
|
||||
ring.rotation.x = -Math.PI / 2;
|
||||
ring.position.set(x, 0.05, z);
|
||||
ring.scale.setScalar(0.5);
|
||||
const group = new THREE.Group();
|
||||
group.add(ring);
|
||||
game.scene.add(group);
|
||||
|
||||
const type = types[Math.floor(Math.random() * types.length)];
|
||||
this.spawnRings.push({
|
||||
group,
|
||||
timer: SUMMON_TELEGRAPH,
|
||||
spawn: () => {
|
||||
game.spawnEnemyInstance(type(), x, z, 0.1);
|
||||
playSound('spawn', 0.5);
|
||||
},
|
||||
});
|
||||
}
|
||||
playSound('spawn', 0.6);
|
||||
}
|
||||
|
||||
private clampToArena(game: Game, x: number, z: number): [number, number] {
|
||||
const v = new THREE.Vector3(x, 0, z);
|
||||
game.clampToArena(v);
|
||||
return [v.x, v.z];
|
||||
}
|
||||
|
||||
override takeDamage(
|
||||
damage: number,
|
||||
game: Game,
|
||||
withKnockback = true,
|
||||
sourcePos?: THREE.Vector3
|
||||
) {
|
||||
super.takeDamage(damage, game, withKnockback, sourcePos);
|
||||
this.hitFlash = HIT_FLASH_TIME;
|
||||
}
|
||||
|
||||
protected onDeath(_game: Game) {
|
||||
this.cleanupEffects();
|
||||
this.beginFadeDeath();
|
||||
}
|
||||
|
||||
override dispose() {
|
||||
super.dispose();
|
||||
this.cleanupEffects();
|
||||
}
|
||||
|
||||
private cleanupEffects() {
|
||||
if (this.game) {
|
||||
for (const r of this.spawnRings) {
|
||||
this.game.scene.remove(r.group);
|
||||
r.group.traverse((obj) => {
|
||||
if (obj instanceof THREE.Mesh) {
|
||||
obj.geometry.dispose();
|
||||
(obj.material as THREE.Material).dispose();
|
||||
}
|
||||
});
|
||||
}
|
||||
for (const f of this.arrivalFlashes) {
|
||||
this.game.scene.remove(f.mesh);
|
||||
f.mesh.geometry.dispose();
|
||||
f.mat.dispose();
|
||||
}
|
||||
}
|
||||
this.spawnRings = [];
|
||||
this.arrivalFlashes = [];
|
||||
this.teleportState = 'idle';
|
||||
this.teleportRing.visible = false;
|
||||
if (this.teleportRingInScene && this.game) {
|
||||
this.game.scene.remove(this.teleportRing);
|
||||
}
|
||||
this.teleportRingInScene = false;
|
||||
this.robeMat.color.set(0x6a3a9a);
|
||||
this.darkMat.color.set(0x2a1445);
|
||||
this.eyeMat.color.set(0xdd88ff);
|
||||
this.glowMat.color.set(0xbb66ff);
|
||||
this.model.scale.setScalar(MODEL_SCALE);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user