added docstrings to every file and function. (with claude, checked it)
tower and game_play dont meet pylint check yet.
This commit is contained in:
+72
-9
@@ -1,8 +1,25 @@
|
||||
import pygame as pg
|
||||
from game.util import *
|
||||
"""Tower module - tower placement, targeting, shooting, and rendering."""
|
||||
import math
|
||||
import pygame as pg
|
||||
from game.util import tile_to_pixel
|
||||
|
||||
class Tower(pg.sprite.Sprite):
|
||||
# pylint: disable=too-many-instance-attributes
|
||||
"""A player-placed tower that targets and damages nearby enemies.
|
||||
|
||||
Attributes:
|
||||
type (str): Asset key identifying the tower variant.
|
||||
range (float): Attack radius in world-space pixels.
|
||||
level (int): Current upgrade level.
|
||||
selected (bool): Whether the tower is selected (draws range circle).
|
||||
angle (float): Current facing angle in degrees.
|
||||
target (Enemy | None): The enemy currently being targeted, or ``None``.
|
||||
fire_rate (int): Frames between shots.
|
||||
fire_cooldown (int): Frames remaining until the next shot.
|
||||
damage (float): Damage dealt per shot.
|
||||
pos (tuple): World-space pixel centre position.
|
||||
tile_pos (tuple): Grid position the tower occupies.
|
||||
"""
|
||||
def __init__(self, game, pos, tile_pos, type, range, level=1):
|
||||
super().__init__()
|
||||
self.game = game
|
||||
@@ -27,6 +44,11 @@ class Tower(pg.sprite.Sprite):
|
||||
self.set_action('shoot')
|
||||
|
||||
def update(self, enemy_group):
|
||||
"""Advance animation, pick a target, rotate toward it, and shoot if ready.
|
||||
|
||||
Args:
|
||||
enemy_group (EnemyGroup -> SpriteGroup): All active enemies to consider as targets.
|
||||
"""
|
||||
self.animation.update()
|
||||
self.pick_target(enemy_group)
|
||||
self.rotate()
|
||||
@@ -39,11 +61,24 @@ class Tower(pg.sprite.Sprite):
|
||||
self.shoot()
|
||||
|
||||
def set_action(self, action):
|
||||
"""Switch the active animation if it differs from the current one.
|
||||
|
||||
Args:
|
||||
action (str): Animation key (e.g. ``"shoot"``).
|
||||
"""
|
||||
if action != self.action:
|
||||
self.action = action
|
||||
self.animation = self.game.assets[self.type + '/' + self.action].copy()
|
||||
|
||||
def render(self, surf, camera):
|
||||
def render(self, surf, camera):
|
||||
"""Draw the tower animation frame, rotated and scaled to the camera zoom.
|
||||
|
||||
Also draws a semi-transparent range circle when the tower is selected.
|
||||
|
||||
Args:
|
||||
surf (pg.Surface): Target surface to draw onto.
|
||||
camera (Camera): Active camera for zoom and coordinate conversion.
|
||||
"""
|
||||
screen_pos = camera.world_to_screen(pg.Vector2(
|
||||
tile_to_pixel(self.tile_pos, self.game.tilemap.tile_size, True)
|
||||
))
|
||||
@@ -61,15 +96,22 @@ class Tower(pg.sprite.Sprite):
|
||||
self.rect = scaled_image.get_rect(center=screen_pos)
|
||||
|
||||
if self.selected:
|
||||
#draw circle around tower if selected
|
||||
scaled_range = int(self.range * camera.zoom)
|
||||
range_surf = pg.Surface((scaled_range * 2, scaled_range * 2), pg.SRCALPHA)
|
||||
pg.draw.circle(range_surf, (200, 200, 200, 100), (scaled_range, scaled_range), scaled_range)
|
||||
pg.draw.circle(
|
||||
range_surf,
|
||||
(200, 200, 200, 100),
|
||||
(scaled_range, scaled_range),
|
||||
scaled_range
|
||||
)
|
||||
range_rect = range_surf.get_rect(center=screen_pos)
|
||||
surf.blit(range_surf, range_rect)
|
||||
|
||||
surf.blit(scaled_image, self.rect)
|
||||
|
||||
|
||||
def rotate(self):
|
||||
"""Face the tower toward its current target, or hold the last angle if no target."""
|
||||
if self.target is None:
|
||||
# print("no target")
|
||||
return # keep last angle, or reset to a default — your call
|
||||
@@ -80,8 +122,15 @@ class Tower(pg.sprite.Sprite):
|
||||
direction = self.target.pos - tower_world_pos
|
||||
self.angle = math.degrees(math.atan2(-direction[1], direction[0])) -90
|
||||
# print(f"angle {self.angle}")
|
||||
|
||||
|
||||
def pick_target(self, enemy_group):
|
||||
"""Select the closest enemy within range as the current target.
|
||||
|
||||
Sets ``self.target`` to ``None`` if no enemy is in range.
|
||||
|
||||
Args:
|
||||
enemy_group (EnemyGroup): All active enemies to search through.
|
||||
"""
|
||||
closest = None
|
||||
closest_dist = float('inf')
|
||||
|
||||
@@ -96,17 +145,31 @@ class Tower(pg.sprite.Sprite):
|
||||
closest_dist = distance
|
||||
|
||||
self.target = closest # explicitly None if nothing in range
|
||||
|
||||
|
||||
def shoot(self):
|
||||
"""Deal damage to the current target, play the shoot animation, and reset cooldown."""
|
||||
self.target.take_damage(self.damage)
|
||||
self.animation.play_animation()
|
||||
self.fire_cooldown = self.fire_rate
|
||||
|
||||
class TowerGroup(pg.sprite.Group):
|
||||
"""Sprite group that delegates rendering and updates to each tower."""
|
||||
|
||||
def render(self, surf, camera):
|
||||
"""Render all towers in the group.
|
||||
|
||||
Args:
|
||||
surf (pg.Surface): Target surface to draw onto.
|
||||
camera (Camera): Active camera for zoom and coordinate conversion.
|
||||
"""
|
||||
for tower in self.sprites():
|
||||
tower.render(surf, camera)
|
||||
|
||||
|
||||
def update(self, enemies):
|
||||
"""Update all towers in the group.
|
||||
|
||||
Args:
|
||||
enemies (EnemyGroup): All active enemies, passed to each tower's update.
|
||||
"""
|
||||
for tower in self.sprites():
|
||||
tower.update(enemies)
|
||||
tower.update(enemies)
|
||||
|
||||
Reference in New Issue
Block a user