File size: 5,749 Bytes
b29710c
 
 
 
 
 
9e6ef9c
 
 
 
 
 
 
 
b29710c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e51a3e6
72b06d8
e51a3e6
 
ba75198
 
d28100d
ba75198
 
 
 
 
 
 
 
 
 
b29710c
ba75198
 
 
 
e4d85e6
 
ba75198
 
b29710c
 
 
 
 
 
 
 
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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
import * as THREE from "three";

// Game settings
export const INITIAL_MONEY = 200;
export const INITIAL_LIVES = 10;

// Audio settings
export const AUDIO_CONFIG = {
  musicVolume: 0.4, // Background music volume (0-1)
  effectsVolume: 0.3, // Sound effects volume (0-1)
  musicEnabled: true, // Enable/disable background music
  effectsEnabled: true, // Enable/disable sound effects
};

// Tower settings
// Define multiple tower types, keeping the original as "basic"
export const TOWER_TYPES = {
  basic: {
    key: "basic",
    name: "Basic Tower",
    type: "basic",
    cost: 50,
    range: 9,
    fireRate: 1.0, // shots per second
    damage: 6,
  },
  slow: {
    key: "slow",
    name: "Slow Tower",
    type: "slow",
    cost: 60,
    range: 8.5,
    fireRate: 0.9,
    damage: 4,
    // On-hit slow: 40% slow (mult 0.6) for 2.5s, refresh on re-hit
    projectileEffect: {
      type: "slow",
      mult: 0.6,
      duration: 2.5,
      color: 0x80d8ff,
      emissive: 0x104a70,
    },
  },
  sniper: {
    key: "sniper",
    name: "Sniper Tower",
    type: "sniper",
    cost: 130,
    // 2x the range of basic
    range: 18,
    // slow fire rate
    fireRate: 0.7, // shots per second (reload ≈ 3.33s)
    damage: 32, // high damage per shot
    // Sniper specific parameters
    aimTime: 0.8, // seconds to aim before firing
    projectileSpeed: 40, // high-speed dart
    // Targeting priority identifier
    targetPriority: "closestToExit",
    // If target moves out of this threshold (>= range) during aiming, cancel
    cancelThreshold: 18,
    // Optional chance to pierce; behavior can be extended later
    pierceChance: 0.0,
  },
  electric: {
    key: "electric",
    name: "Electric Tower",
    type: "electric",
    cost: 200,
    // Set to the smallest tower range (slow tower = 8.5)
    range: 8.5, // world units (scene uses world units; 1 cell = 2 units, so ~4.25 cells)
    fireRate: 1.0, // shots per second
    damage: 25, // per target
    maxTargets: 3,
    // Persist electric arc visuals for 2 seconds
    arcDurationMs: 2000,
    // Visual tuning for arcs (can be read by Tower)
    arc: {
      color: 0x9ad6ff, // light electric blue
      coreColor: 0xe6fbff,
      thickness: 2,
      jitter: 0.25,
      segments: 10,
    },
    // Targeting priority
    targetPriority: "closestToExit",
  },
};
// Back-compat alias for existing code that expects TOWER_CONFIG
export const TOWER_CONFIG = TOWER_TYPES.basic;

// Projectile settings
export const PROJECTILE_SPEED = 18;

// Upgrade settings
export const UPGRADE_MAX_LEVEL = 5;
export const UPGRADE_START_COST = 40;
export const UPGRADE_COST_SCALE = 1.6;
export const UPGRADE_RANGE_SCALE = 1.1; // +10% per level
export const UPGRADE_RATE_SCALE = 1.15; // +15% per level
export const UPGRADE_DAMAGE_SCALE = 1.12; // +12% per level
export const SELL_REFUND_RATE = 0.7;

// Infinite wave scaling configuration (replaces static WAVES array)
export const WAVE_SCALING = {
  // base values (wave 1)
  baseCount: 8,
  baseHP: 12,
  baseSpeed: 3.0,
  baseReward: 5,
  baseSpawnInterval: 0.8,

  // per-wave growth (applied from wave 2 onward)
  countPerWave: 3, // +3 enemies each wave
  hpMultiplierPerWave: 1.15, // HP *= 1.18 each wave
  speedIncrementPerWave: 0.025, // +0.05 speed per wave
  rewardMultiplierPerWave: 1.03, // reward *= 1.03 each wave
  spawnIntervalDecayPerWave: 0.01, // -0.02s per wave

  // safeguards/limits
  minSpawnInterval: 0.3,
  maxSpeed: 5.5,
  roundCountToInt: true,
};

// Compute wave parameters for a given wave number (1-based)
export function getWaveParams(n) {
  const w = WAVE_SCALING;
  const waveNum = Math.max(1, Math.floor(n));

  const countBase = w.baseCount + (waveNum - 1) * w.countPerWave;
  const count = w.roundCountToInt
    ? Math.max(1, Math.round(countBase))
    : Math.max(1, countBase);

  const hp = Math.max(
    1,
    Math.round(w.baseHP * Math.pow(w.hpMultiplierPerWave, waveNum - 1))
  );

  const speed = Math.min(
    w.maxSpeed,
    w.baseSpeed + (waveNum - 1) * w.speedIncrementPerWave
  );

  const reward = Math.max(
    1,
    Math.round(w.baseReward * Math.pow(w.rewardMultiplierPerWave, waveNum - 1))
  );

  const spawnInterval = Math.max(
    w.minSpawnInterval,
    w.baseSpawnInterval - (waveNum - 1) * w.spawnIntervalDecayPerWave
  );

  return { count, hp, speed, reward, spawnInterval };
}

// Grid settings
export const GROUND_SIZE = 72; // slightly larger to allow wider spacing
export const GRID_CELL_SIZE = 2;

// Single-path Z-shaped waypoints from top to bottom.
// Snakes left↔right across the board with vertical steps.
const HALF = GROUND_SIZE / 2;
const PATH_MARGIN = 4 * GRID_CELL_SIZE; // 4 squares (8 units) from edges
const LEFT_X = -HALF + PATH_MARGIN;
const RIGHT_X = HALF - PATH_MARGIN;
const MID_Z = 0; // middle of the board
// Inset start/end vertical lines a bit further from side edges
const EDGE_INSET = 5 * GRID_CELL_SIZE; // 5 cells inward (10 units)
const LEFT_X_IN = LEFT_X + EDGE_INSET;
const RIGHT_X_IN = RIGHT_X - EDGE_INSET;

// Exactly 3 turns: right at top, down at top-right, left at mid, down at mid-left
export const PATH_POINTS = [
  new THREE.Vector3(RIGHT_X_IN, 0, HALF),   // start top-right, straight down (inset)
  new THREE.Vector3(RIGHT_X_IN, 0, MID_Z),  // vertical start segment
  new THREE.Vector3(LEFT_X_IN, 0, MID_Z),   // horizontal across the middle
  new THREE.Vector3(LEFT_X_IN, 0, -HALF),   // vertical to bottom exit (inset)
];

// Disable multi-path branching; keep empty to fallback everywhere to PATH_POINTS
export const PATHS = [];

// Visual settings
export const SCENE_BACKGROUND = 0x202432;

// Road settings
export const ROAD_HALF_WIDTH = 1.5;
export const ROAD_BEVEL_SIZE = 1.2;
export const ROAD_ARC_SEGMENTS = 16;