feat: add timer-based tower spawning mechanic triggered by player desk interaction
This commit is contained in:
+36
-8
@@ -28,6 +28,7 @@ from src.settings import (
|
|||||||
DOOR_HP,
|
DOOR_HP,
|
||||||
FPS,
|
FPS,
|
||||||
PLAYER_SPEED,
|
PLAYER_SPEED,
|
||||||
|
TOWER_TABLE_ACTIVATION_TIME,
|
||||||
)
|
)
|
||||||
from src.shop import Shop
|
from src.shop import Shop
|
||||||
from src.startscreen import Startscreen
|
from src.startscreen import Startscreen
|
||||||
@@ -65,6 +66,7 @@ class Game:
|
|||||||
self.game_start_time = 0
|
self.game_start_time = 0
|
||||||
self.endscreen: Optional[Endscreen] = None
|
self.endscreen: Optional[Endscreen] = None
|
||||||
self.current_room_key: Optional[tuple[int, int, int, int]] = None
|
self.current_room_key: Optional[tuple[int, int, int, int]] = None
|
||||||
|
self.table_touch_start_time: Optional[int] = None
|
||||||
|
|
||||||
self.map = Map()
|
self.map = Map()
|
||||||
self.active_door: Optional[Door] = None
|
self.active_door: Optional[Door] = None
|
||||||
@@ -147,10 +149,11 @@ class Game:
|
|||||||
self.player.hit_wall()
|
self.player.hit_wall()
|
||||||
|
|
||||||
self.player.update_animation(dt)
|
self.player.update_animation(dt)
|
||||||
self.ects.update(self.is_player_at_desk())
|
|
||||||
self.handle_room_table_collision()
|
|
||||||
player_rect = self.get_actor_rect(self.player)
|
player_rect = self.get_actor_rect(self.player)
|
||||||
room_rect = self.map.get_current_room(player_rect)
|
room_rect = self.map.get_current_room(player_rect)
|
||||||
|
is_at_desk = self.is_player_at_desk(player_rect, room_rect)
|
||||||
|
self.ects.update(is_at_desk)
|
||||||
|
self.handle_room_table_collision()
|
||||||
self.handle_room_change(room_rect)
|
self.handle_room_change(room_rect)
|
||||||
self.update_items(player_rect, room_rect)
|
self.update_items(player_rect, room_rect)
|
||||||
self.monsters.update(
|
self.monsters.update(
|
||||||
@@ -159,7 +162,7 @@ class Game:
|
|||||||
self.is_position_walkable,
|
self.is_position_walkable,
|
||||||
self.get_monster_spawn_blockers(),
|
self.get_monster_spawn_blockers(),
|
||||||
)
|
)
|
||||||
self.update_tower()
|
self.update_tower(room_rect, is_at_desk)
|
||||||
self.check_end_conditions()
|
self.check_end_conditions()
|
||||||
|
|
||||||
def get_player_movement(self, dt: float) -> pygame.Vector2:
|
def get_player_movement(self, dt: float) -> pygame.Vector2:
|
||||||
@@ -186,35 +189,58 @@ class Game:
|
|||||||
row = int(position.y // tile_size)
|
row = int(position.y // tile_size)
|
||||||
return self.map.is_accessible(column, row)
|
return self.map.is_accessible(column, row)
|
||||||
|
|
||||||
def is_player_at_desk(self) -> bool:
|
def is_player_at_desk(
|
||||||
|
self,
|
||||||
|
player_rect: Optional[pygame.Rect] = None,
|
||||||
|
room_rect: Optional[pygame.Rect] = None,
|
||||||
|
) -> bool:
|
||||||
"""Return True while the player's hitbox touches a desk in the current room."""
|
"""Return True while the player's hitbox touches a desk in the current room."""
|
||||||
|
|
||||||
|
if player_rect is None:
|
||||||
player_rect = self.get_actor_rect(self.player)
|
player_rect = self.get_actor_rect(self.player)
|
||||||
|
if room_rect is None:
|
||||||
room_rect = self.map.get_current_room(player_rect)
|
room_rect = self.map.get_current_room(player_rect)
|
||||||
if room_rect is None:
|
if room_rect is None:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return any(desk.check_collision(player_rect) for desk in self.map.get_desks_for_room(room_rect))
|
return any(desk.check_collision(player_rect) for desk in self.map.get_desks_for_room(room_rect))
|
||||||
|
|
||||||
def update_tower(self) -> None:
|
def update_tower(
|
||||||
"""Spawn a tower when enough ECTS are available and let it attack the zombie."""
|
self,
|
||||||
|
room_rect: Optional[pygame.Rect],
|
||||||
|
is_at_desk: bool,
|
||||||
|
) -> None:
|
||||||
|
"""Spawn a tower after the player studies at a table, then let it attack."""
|
||||||
|
|
||||||
player_rect = self.get_actor_rect(self.player)
|
|
||||||
room_rect = self.map.get_current_room(player_rect)
|
|
||||||
monster = self.monsters.monsters[0] if self.monsters.monsters else None
|
monster = self.monsters.monsters[0] if self.monsters.monsters else None
|
||||||
if monster is None or not monster.is_alive:
|
if monster is None or not monster.is_alive:
|
||||||
self.tower.expire_if_needed()
|
self.tower.expire_if_needed()
|
||||||
|
self.update_table_touch_timer(is_at_desk)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
can_spawn_tower = self.update_table_touch_timer(is_at_desk)
|
||||||
damage = self.tower.update(
|
damage = self.tower.update(
|
||||||
room_rect,
|
room_rect,
|
||||||
self.get_tower_spawn_blockers(),
|
self.get_tower_spawn_blockers(),
|
||||||
self.ects,
|
self.ects,
|
||||||
monster.rect,
|
monster.rect,
|
||||||
|
can_spawn=can_spawn_tower,
|
||||||
)
|
)
|
||||||
if monster is not None and damage > 0:
|
if monster is not None and damage > 0:
|
||||||
monster.take_damage(damage)
|
monster.take_damage(damage)
|
||||||
|
|
||||||
|
def update_table_touch_timer(self, is_at_desk: bool) -> bool:
|
||||||
|
now = pygame.time.get_ticks()
|
||||||
|
if not is_at_desk:
|
||||||
|
self.table_touch_start_time = None
|
||||||
|
return False
|
||||||
|
|
||||||
|
if self.table_touch_start_time is None:
|
||||||
|
self.table_touch_start_time = now
|
||||||
|
return False
|
||||||
|
|
||||||
|
return now - self.table_touch_start_time >= TOWER_TABLE_ACTIVATION_TIME
|
||||||
|
|
||||||
def handle_room_change(self, room_rect: Optional[pygame.Rect]) -> None:
|
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
|
room_key = self.get_room_key(room_rect) if room_rect is not None else None
|
||||||
if room_key == self.current_room_key:
|
if room_key == self.current_room_key:
|
||||||
@@ -225,6 +251,7 @@ class Game:
|
|||||||
|
|
||||||
self.item_manager.clear()
|
self.item_manager.clear()
|
||||||
self.tower.clear(reset_spawned=True)
|
self.tower.clear(reset_spawned=True)
|
||||||
|
self.table_touch_start_time = None
|
||||||
self.current_room_key = room_key
|
self.current_room_key = room_key
|
||||||
|
|
||||||
def reset_active_encounter(self) -> None:
|
def reset_active_encounter(self) -> None:
|
||||||
@@ -281,6 +308,7 @@ class Game:
|
|||||||
self.shop = Shop(self.ects, self.item_manager.items, self.sound_manager)
|
self.shop = Shop(self.ects, self.item_manager.items, self.sound_manager)
|
||||||
self.item_manager.clear()
|
self.item_manager.clear()
|
||||||
self.current_room_key = None
|
self.current_room_key = None
|
||||||
|
self.table_touch_start_time = None
|
||||||
self.tower.clear(reset_spawned=True)
|
self.tower.clear(reset_spawned=True)
|
||||||
self.active_door = None
|
self.active_door = None
|
||||||
self.doors_by_room = {}
|
self.doors_by_room = {}
|
||||||
|
|||||||
+2
-1
@@ -67,6 +67,7 @@ TOWER_COST = 10
|
|||||||
PROJECTILE_DMG = 2
|
PROJECTILE_DMG = 2
|
||||||
PROJECTILE_SPEED = 7
|
PROJECTILE_SPEED = 7
|
||||||
COLOR_PROJECTILE = (200, 200, 0)
|
COLOR_PROJECTILE = (200, 200, 0)
|
||||||
|
TOWER_TABLE_ACTIVATION_TIME = 5000 # ms the player must stay at a table before tower spawn
|
||||||
TOWER_TIMER = 30000 # how long the tower stays and attack before it disappears
|
TOWER_TIMER = 30000 # how long the tower stays and attack before it disappears
|
||||||
|
|
||||||
# --- ITEM SPAWN TIMER ---
|
# --- ITEM SPAWN TIMER ---
|
||||||
@@ -149,7 +150,7 @@ SFX_BUY_FAIL = f"{SOUND_DIR}/buy_fail.wav"
|
|||||||
SFX_MONSTER_HEALED = f"{SOUND_DIR}/monster_healed.wav"
|
SFX_MONSTER_HEALED = f"{SOUND_DIR}/monster_healed.wav"
|
||||||
SFX_MONSTER_TRANSFORM = f"{SOUND_DIR}/monster_transform.wav"
|
SFX_MONSTER_TRANSFORM = f"{SOUND_DIR}/monster_transform.wav"
|
||||||
|
|
||||||
BGM_NORMAL = f"{SOUND_DIR}/bgm_normal.wav"
|
BGM_NORMAL = f"{SOUND_DIR}/bgm_creepy.wav"
|
||||||
BGM_CREEPY = f"{SOUND_DIR}/bgm_creepy.wav"
|
BGM_CREEPY = f"{SOUND_DIR}/bgm_creepy.wav"
|
||||||
|
|
||||||
VOLUME_SFX = 0.6
|
VOLUME_SFX = 0.6
|
||||||
|
|||||||
@@ -71,12 +71,14 @@ class Tower:
|
|||||||
blocked_rects: list[pygame.Rect],
|
blocked_rects: list[pygame.Rect],
|
||||||
ects,
|
ects,
|
||||||
monster_rect: Optional[pygame.Rect],
|
monster_rect: Optional[pygame.Rect],
|
||||||
|
can_spawn: bool = True,
|
||||||
) -> int:
|
) -> int:
|
||||||
"""Update tower spawning/shooting and return damage dealt to the monster."""
|
"""Update tower spawning/shooting and return damage dealt to the monster."""
|
||||||
|
|
||||||
now = pygame.time.get_ticks()
|
now = pygame.time.get_ticks()
|
||||||
total_damage = 0
|
total_damage = 0
|
||||||
|
|
||||||
|
if can_spawn:
|
||||||
self.try_spawn(room_rect, blocked_rects, ects)
|
self.try_spawn(room_rect, blocked_rects, ects)
|
||||||
|
|
||||||
if self.tower_rect is not None and monster_rect is not None:
|
if self.tower_rect is not None and monster_rect is not None:
|
||||||
|
|||||||
Reference in New Issue
Block a user