File size: 3,770 Bytes
9e6ef9c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0ee2f3f
9e6ef9c
 
 
 
 
 
 
 
0ee2f3f
9e6ef9c
 
 
 
 
 
 
 
 
 
 
 
0ee2f3f
 
9e6ef9c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0ee2f3f
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
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: "basic",

  buildHead(tower) {
    const headGeo = new THREE.BoxGeometry(0.8, 0.4, 0.8);
    const headMat = new THREE.MeshStandardMaterial({
      color: 0x90caf9,
      metalness: 0.18,
      roughness: 0.45,
      emissive: 0x000000,
      emissiveIntensity: 0.6,
      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?.(0x5c6bc0);
        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?.(0x90caf9);
      headMat.emissive?.set?.(0x4a0a2a);
      headMat.emissiveIntensity = 0.2;
    } else {
      if (baseMat) {
        baseMat.color?.set?.(0x6f7bd6);
        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?.(0xa5d6ff);
      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: 0x3aa6ff,
        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);
    }
  },
};