class CoinTower(SimpleTower): """Tower that supplies coin for use. 1 tower gains 3 coins each interval (it makes use of the in game steps). 2 towers = 4, 3 towers = 5 coins, etc. """ name = "Coin Supply" colour = 'gold' cool_down_steps = 45 base_cost = 40 level_cost = 60 range = DonutRange(0,0) # Give a range of 0 to prevent targeting of enemies rotation_threshold = (1 / 3) * math.pi def __init__(self, cell_size: int, grid_size=(.9, .9), rotation=math.pi * .25, base_damage=150, level: int = 1): super().__init__(cell_size, grid_size=grid_size, rotation=rotation, base_damage=base_damage, level=level) self._earning_rate = 1 # However due to the step interval in-game, the player earns 3 coins for first tower EarntCoins._counter += 1 # Counter allows the player to stack up coin earning rates when two or more coin supply is placed def step(self, units): self.cool_down.step() if not self.cool_down.is_done(): # Stop the tower from earning coins during the cooldown process. EarntCoins._earnings = 0 return None else: EarntCoins._earnings = self._earning_rate self.cool_down.start()
class MissileTower(SimpleTower): """A tower that fires missiles that track a target""" name = 'Missile Tower' colour = 'snow' cool_down_steps = 10 base_cost = 80 level_cost = 60 range = DonutRange(1.5, 4.5) rotation_threshold = (1 / 3) * math.pi def __init__(self, cell_size: int, grid_size=(.9, .9), rotation=math.pi * .25, base_damage=150, level: int = 1): super().__init__(cell_size, grid_size=grid_size, rotation=rotation, base_damage=base_damage, level=level) self._target: AbstractEnemy = None def _get_target(self, units) -> Union[AbstractEnemy, None]: """Returns previous target, else selects new one if previous is invalid Invalid target is one of: - dead - out-of-range Return: AbstractEnemy: Returns previous target, unless it is non-existent or invalid (see above), Otherwise, selects & returns new target if a valid one can be found, Otherwise, returns None """ if self._target is None \ or self._target.is_dead() \ or not self.is_position_in_range(self._target.position): self._target = self.get_unit_in_range(units) return self._target def step(self, units): """Rotates toward 'target' and fires missile if possible""" self.cool_down.step() target = self._get_target(units.enemies) if target is None: return None # Rotate toward target angle = angle_between(self.position, target.position) partial_angle = rotate_toward(self.rotation, angle, self.rotation_threshold) self.rotation = partial_angle if angle != partial_angle or not self.cool_down.is_done(): return None self.cool_down.start() # Spawn missile on tower missile = Missile(self.position, self.cell_size, target, rotation=self.rotation, damage=self.get_damage(), grid_speed=.3) # Move missile to outer edge of tower radius = self.grid_size[0] / 2 delta = polar_to_rectangular(self.cell_size * radius, partial_angle) missile.move_by(delta) return [missile]
class TurretTower(SimpleTower): """A tower that has turrets to attack a target""" name = 'Turret Tower' colour = 'dark grey' cool_down_steps = 1 base_cost = 250 level_cost = 98 range = DonutRange(0, 1.1) rotation_threshold = (1 / 3) * math.pi def __init__(self, cell_size: int, grid_size=(.9, .9), rotation=math.pi * .25, base_damage=25, level: int = 1): super().__init__(cell_size, grid_size=grid_size, rotation=rotation, base_damage=base_damage, level=level) self._target: AbstractEnemy = None def _get_target(self, units) -> Union[AbstractEnemy, None]: if self._target is None \ or self._target.is_dead() \ or not self.is_position_in_range(self._target.position): self._target = self.get_unit_in_range(units) return self._target def step(self, units): """Rotates toward 'target' and fires if possible""" self.cool_down.step() target = self._get_target(units.enemies) if target is None: return None # Rotate toward target angle = angle_between(self.position, target.position) partial_angle = rotate_toward(self.rotation, angle, self.rotation_threshold) self.rotation = partial_angle if angle != partial_angle or not self.cool_down.is_done(): return None self.cool_down.start() # Spawn missile on tower turret = Turret(self.position, self.cell_size, target, rotation=self.rotation, damage=self.get_damage(), grid_speed=.5) # Move missile to outer edge of tower radius = self.grid_size[0] / 2 delta = polar_to_rectangular(self.cell_size * radius, partial_angle) turret.move_by(delta) return [turret]