import pygame import math from game.config import TOWER_DATA, COLOR_BLACK, COLOR_WHITE from game.utils import distance from game.projectile import Projectile class Tower: def __init__(self, tower_type, grid_col, grid_row, px, py): data = TOWER_DATA[tower_type] self.type = tower_type self.col = grid_col self.row = grid_row self.x = px self.y = py self.damage = data["damage"] self.range = data["range"] self.fire_rate = data["fire_rate"] self.price = data["price"] self.color = data["color"] self.name = data["name"] self.splash = data.get("splash", 0) self.slow_factor = data.get("slow_factor", 0) self.slow_duration = data.get("slow_duration", 0) self.poison_dps = data.get("poison_dps", 0) self.poison_duration = data.get("poison_duration", 0) self.chain_count = data.get("chain_count", 0) self.chain_range = data.get("chain_range", 0) self.chain_decay = data.get("chain_decay", 0) self.cooldown = 0 self.target = None def find_target(self, enemies): self.target = None best = None best_progress = -1 for e in enemies: if not e.alive: continue d = distance(self.x, self.y, e.x, e.y) if d <= self.range: wp_idx = min(e.waypoint_index, len(e.waypoints) - 1) progress = e.waypoint_index + (1 - distance(e.x, e.y, *e.waypoints[wp_idx]) / 100) if progress > best_progress: best_progress = progress best = e self.target = best def _wp(self, idx): if self.target and idx < len(self.target.waypoints): return self.target.waypoints[idx] if self.target: return self.target.waypoints[-1] from game.config import PATH_WAYPOINTS return PATH_WAYPOINTS[-1] def update(self, dt, enemies, projectiles): if self.cooldown > 0: self.cooldown -= dt self.find_target(enemies) if self.target and self.cooldown <= 0: self.cooldown = self.fire_rate proj_type = { "arrow": "arrow", "cannon": "cannon", "slow": "slow", "sniper": "sniper", "poison": "poison", "lightning": "lightning", }[self.type] projectiles.append(Projectile( self.x, self.y, self.target, self.damage, proj_type=proj_type, splash=self.splash, slow_factor=self.slow_factor, slow_duration=self.slow_duration, poison_dps=self.poison_dps, poison_duration=self.poison_duration, chain_count=self.chain_count, chain_range=self.chain_range, chain_decay=self.chain_decay, )) def draw(self, surface): ix, iy = int(self.x), int(self.y) if self.type == "arrow": pygame.draw.rect(surface, self.color, (ix - 12, iy - 12, 24, 24)) pygame.draw.rect(surface, COLOR_BLACK, (ix - 12, iy - 12, 24, 24), 2) elif self.type == "cannon": pygame.draw.circle(surface, self.color, (ix, iy), 14) pygame.draw.circle(surface, COLOR_BLACK, (ix, iy), 14, 2) pygame.draw.circle(surface, (180, 100, 30), (ix, iy), 8) elif self.type == "slow": points = [(ix, iy - 14), (ix + 14, iy), (ix, iy + 14), (ix - 14, iy)] pygame.draw.polygon(surface, self.color, points) pygame.draw.polygon(surface, COLOR_BLACK, points, 2) elif self.type == "sniper": pts = [(ix, iy - 16), (ix + 14, iy + 12), (ix - 14, iy + 12)] pygame.draw.polygon(surface, self.color, pts) pygame.draw.polygon(surface, COLOR_BLACK, pts, 2) pygame.draw.line(surface, (200, 200, 255), (ix, iy - 10), (ix, iy + 6), 2) elif self.type == "poison": pts = [] for i in range(6): angle = math.pi / 3 * i - math.pi / 6 pts.append((ix + int(14 * math.cos(angle)), iy + int(14 * math.sin(angle)))) pygame.draw.polygon(surface, self.color, pts) pygame.draw.polygon(surface, COLOR_BLACK, pts, 2) elif self.type == "lightning": pts = [ (ix - 2, iy - 14), (ix + 6, iy - 4), (ix, iy - 4), (ix + 4, iy + 14), (ix - 4, iy + 2), (ix + 1, iy + 2), ] pygame.draw.polygon(surface, self.color, pts) pygame.draw.polygon(surface, COLOR_BLACK, pts, 2) if self.target and self.target.alive: pygame.draw.line(surface, (*self.color, ), (ix, iy), (int(self.target.x), int(self.target.y)), 1) def draw_range(self, surface): pygame.draw.circle(surface, COLOR_WHITE, (int(self.x), int(self.y)), self.range, 1)