From d6bdf22a106a8a0b7855107333b504246d71f68b Mon Sep 17 00:00:00 2001 From: Niklas Haiden Date: Fri, 8 May 2026 19:30:18 +0200 Subject: [PATCH] refactor: improve room handling and tower expiration logic --- src/game.py | 32 +++++++++++++++++--------------- src/tower.py | 20 +++++++++++++++----- 2 files changed, 32 insertions(+), 20 deletions(-) diff --git a/src/game.py b/src/game.py index 0c1d52b..62b0ae2 100644 --- a/src/game.py +++ b/src/game.py @@ -66,7 +66,7 @@ class Game: self.player_name: str = "" self.game_start_time = 0 self.endscreen: Optional[Endscreen] = None - self.current_item_room_key: Optional[tuple[int, int, int, int]] = None + self.current_room_key: Optional[tuple[int, int, int, int]] = None self.map = Map() self.active_door: Optional[Door] = None @@ -151,7 +151,10 @@ class Game: self.player.update_animation(dt) self.ects.update(self.is_player_at_desk()) self.handle_room_table_collision() - self.update_items() + player_rect = self.get_actor_rect(self.player) + room_rect = self.map.get_current_room(player_rect) + self.handle_room_change(room_rect) + self.update_items(player_rect, room_rect) self.monsters.update( dt, self.player.position, @@ -202,7 +205,7 @@ class Game: room_rect = self.map.get_current_room(player_rect) monster = self.monsters.monsters[0] if self.monsters.monsters else None if monster is None or not monster.is_alive: - self.tower.clear() + self.tower.expire_if_needed() return damage = self.tower.update( @@ -213,19 +216,18 @@ class Game: ) if monster is not None and damage > 0: monster.take_damage(damage) - if not monster.is_alive: - self.tower.clear() - def update_items(self) -> None: - """Spawn room items and process one player purchase request.""" - - player_rect = self.get_actor_rect(self.player) - room_rect = self.map.get_current_room(player_rect) + def handle_room_change(self, room_rect: Optional[pygame.Rect]) -> None: room_key = self.get_room_key(room_rect) if room_rect is not None else None + if room_key == self.current_room_key: + return - if room_key != self.current_item_room_key: - self.item_manager.clear() - self.current_item_room_key = room_key + self.item_manager.clear() + self.tower.clear(reset_spawned=True) + self.current_room_key = room_key + + def update_items(self, player_rect: pygame.Rect, room_rect: Optional[pygame.Rect]) -> None: + """Spawn room items and process one player purchase request.""" self.item_manager.update(room_rect, self.get_item_spawn_blockers()) @@ -269,8 +271,8 @@ class Game: self.ects = ECTS() self.shop = Shop(self.ects, self.item_manager.items, self.sound_manager) self.item_manager.clear() - self.current_item_room_key = None - self.tower.clear() + self.current_room_key = None + self.tower.clear(reset_spawned=True) self.active_door = None self.doors_by_room = {} self.player.set_position(self.find_start_position()) diff --git a/src/tower.py b/src/tower.py index 1e2f9b3..3a72d8e 100644 --- a/src/tower.py +++ b/src/tower.py @@ -25,6 +25,7 @@ class Tower: self.projectiles: list[dict[str, float]] = [] self.tower_start: Optional[int] = None self.last_shot: int = 0 + self.has_spawned = False try: self.tower_image = pygame.image.load("assets/items/tower.png").convert_alpha() @@ -46,7 +47,7 @@ class Tower: def try_spawn(self, room_rect: Optional[pygame.Rect], blocked_rects: list[pygame.Rect], ects) -> bool: """Spawn and pay for one tower when the player has enough ECTS.""" - if room_rect is None or self.tower_rect is not None: + if room_rect is None or self.tower_rect is not None or self.has_spawned: return False if ects.ects_balance < TOWER_COST: return False @@ -59,6 +60,7 @@ class Tower: ects.decrease_balance(TOWER_COST) self.tower_rect = rect self.tower_start = pygame.time.get_ticks() + self.has_spawned = True self.projectiles = [] self._play_sfx("buy_success") return True @@ -97,12 +99,18 @@ class Tower: self.projectiles.remove(projectile) self._play_sfx("monster_hit") - if self.tower_rect is not None and self.tower_start is not None: - if now - self.tower_start >= TOWER_TIMER: - self.clear() + self.expire_if_needed(now) return total_damage + def expire_if_needed(self, now: Optional[int] = None) -> None: + if self.tower_rect is None or self.tower_start is None: + return + + current_time = pygame.time.get_ticks() if now is None else now + if current_time - self.tower_start >= TOWER_TIMER: + self.clear() + def draw(self, surface: pygame.Surface, font: pygame.font.Font, cam_x: int = 0, cam_y: int = 0) -> None: if self.tower_rect is not None and self.tower_start is not None: draw_rect = pygame.Rect( @@ -134,11 +142,13 @@ class Tower: else: pygame.draw.rect(surface, COLOR_PROJECTILE, projectile_rect) - def clear(self) -> None: + def clear(self, reset_spawned: bool = False) -> None: self.tower_rect = None self.projectiles = [] self.tower_start = None self.last_shot = 0 + if reset_spawned: + self.has_spawned = False def _shoot_at(self, monster_rect: pygame.Rect) -> None: if self.tower_rect is None: