victor's picture
victor HF Staff
ok
0ee2f3f
import * as THREE from "three";
import { Projectile } from "../../Projectile.js";
import { findNearestWithinRange } from "../common/targeting.js";
const VISUAL_TOP_INCREMENT = 0.08;
const VISUAL_TOP_CAP = 0.4;
export default {
key: "slow",
buildHead(tower) {
const headGeo = new THREE.SphereGeometry(
0.55,
24,
16,
0,
Math.PI * 2,
Math.PI / 2,
Math.PI / 2
);
const headMat = new THREE.MeshStandardMaterial({
color: 0xffb6c1,
metalness: 0.15,
roughness: 0.35,
emissive: 0x4a0a2a,
emissiveIntensity: 0.4,
side: THREE.DoubleSide,
});
const head = new THREE.Mesh(headGeo, headMat);
head.castShadow = true;
head.position.set(0, 0.8, 0);
tower.baseMesh.add(head);
tower.headMesh = head;
tower.head = head;
tower.headTopY = tower.mesh.position.y + head.position.y + 0.4;
},
tryFire(tower, dt, enemies, projectiles, projectileSpeed) {
tower.fireCooldown -= dt;
if (tower.fireCooldown > 0) return;
const target = findNearestWithinRange(tower, enemies);
if (!target) return;
const dir = new THREE.Vector3().subVectors(
target.mesh.position,
tower.position
);
const yaw = Math.atan2(dir.x, dir.z);
tower.mesh.rotation.y = yaw;
tower.fireCooldown = 1 / tower.rate;
const spawnY =
typeof tower.headTopY === "number" ? tower.headTopY - 0.1 : 0.9;
const proj = new Projectile(
tower.position.clone().add(new THREE.Vector3(0, spawnY, 0)),
target,
projectileSpeed,
tower.scene,
tower.projectileEffect || null
);
proj.damage = tower.damage;
projectiles.push(proj);
tower.playShootSound();
},
applyVisualLevel(tower) {
const lvl = tower.level;
const head = tower.headMesh;
if (!head) return;
const baseMat = tower.baseMesh?.material;
const headMat = head.material;
if (tower.levelRing) {
tower.scene.remove(tower.levelRing);
tower.levelRing.geometry.dispose();
if (tower.levelRing.material?.dispose) tower.levelRing.material.dispose();
tower.levelRing = null;
}
// No height increment on level gain — only rings should reflect level
if (lvl <= 1) {
if (baseMat) {
baseMat.color?.set?.(0xff69b4);
baseMat.emissive?.set?.(0x000000);
baseMat.emissiveIntensity = 0.0;
}
// Keep original head height/position; recompute top using current head position
tower.headTopY = (tower.mesh?.position.y ?? 0.25) + head.position.y + 0.4;
headMat.color?.set?.(0xffb6c1);
headMat.emissive?.set?.(0x4a0a2a);
headMat.emissiveIntensity = 0.2;
} else {
if (baseMat) {
baseMat.color?.set?.(0xff5ea8);
baseMat.emissive?.set?.(0x2a0a1a);
baseMat.emissiveIntensity = 0.08;
}
// Keep original head height/position; recompute top using current head position
tower.headTopY = (tower.mesh?.position.y ?? 0.25) + head.position.y + 0.4;
headMat.color?.set?.(0xffc6d9);
headMat.emissive?.set?.(0x9a135a);
headMat.emissiveIntensity = 0.35;
const ringGeom = new THREE.TorusGeometry(0.45, 0.035, 8, 24);
const ringMat = new THREE.MeshStandardMaterial({
color: 0xff8fc2,
emissive: 0xe01a6b,
emissiveIntensity: 0.55,
metalness: 0.3,
roughness: 0.45,
});
const ring = new THREE.Mesh(ringGeom, ringMat);
ring.castShadow = false;
ring.receiveShadow = false;
const topY = tower.headTopY ?? head.position.y + 0.8;
ring.position.set(
tower.mesh.position.x,
topY + 0.02,
tower.mesh.position.z
);
ring.rotation.x = Math.PI / 2;
ring.name = "tower_level_ring";
tower.levelRing = ring;
tower.scene.add(ring);
}
},
};