diff --git a/src/item.py b/src/item.py new file mode 100644 index 0000000..e0cec9e --- /dev/null +++ b/src/item.py @@ -0,0 +1,133 @@ +import pygame +import random +from src.settings import ( + ITEM_SPAWN_INTERVAL, ITEM_SHOPPING_TIMER, ITEM_SIZE, + COLOR_HP_BAR_YELLOW, COLOR_HP_BAR_BG +) + + +class Item: + def __init__(self, x, y, item_type): + self.x = x + self.y = y + self.item_type = item_type + + # collision rect for player (2 times item size for width and height) + self.rect = pygame.Rect(self.x, self.y, ITEM_SIZE, ITEM_SIZE) + + # timer to store time when item appeared + self.spawn_time = pygame.time.get_ticks() + + # try to load sprite with item type + try: + path = f"assets/items/{item_type}.png" + self.image = pygame.image.load(path).convert_alpha() + self.image = pygame.transform.scale(self.image, (ITEM_SIZE, ITEM_SIZE)) + except Exception as e: + print(f"Error loading item sprite {item_type}: {e}") + self.image = None + + # to check if the time ran out to buy the item + def is_expired(self): + return pygame.time.get_ticks() - self.spawn_time >= ITEM_SHOPPING_TIMER + + # cam offset to convert world coordinates to screen coordinates + def draw(self, screen, cam_x, cam_y): + draw_x = self.x - cam_x + draw_y = self.y - cam_y + + if self.image is not None: + screen.blit(self.image, (draw_x, draw_y)) + else: + # fallback using settings colors + # coffee brown, beer yellow + if self.item_type == "coffee": + color = (120, 70, 30) + else: + color = COLOR_HP_BAR_YELLOW + + draw_rect = (draw_x, draw_y, ITEM_SIZE, ITEM_SIZE) + pygame.draw.rect(screen, color, draw_rect) + pygame.draw.rect(screen, COLOR_HP_BAR_BG, draw_rect, 2) + + +# try 50 times to find a free position to spawn item +def _random_position(room_rect, blocked_rects): + attempts = 0 + max_attempts = 50 + + while attempts < max_attempts: + # generate random x/y coordinates with room boundaries + # * 2 to have a margin to walls + x = random.randint(room_rect.left + ITEM_SIZE, room_rect.right - ITEM_SIZE * 2) + y = random.randint(room_rect.top + ITEM_SIZE, room_rect.bottom - ITEM_SIZE * 2) + + # create a rect at this position for testing collision + test_rect = pygame.Rect(x, y, ITEM_SIZE, ITEM_SIZE) + blocked = False + for b in blocked_rects: + if test_rect.colliderect(b): + blocked = True + break + + # if position is not blocked, return the position + if not blocked: + return x, y + + attempts += 1 + + # no spot after 50 attempts -> return + return None + + +class ItemManager: + def __init__(self): + self.items = [] + self.last_spawn_time = pygame.time.get_ticks() + + # remove expired items and spawn new ones + def update(self, room_rect, blocked_rects): + now = pygame.time.get_ticks() + + remaining_items = [] + + for item in self.items: + if item.is_expired() == False: + remaining_items.append(item) + + # replace the old list with the new temp_list + self.items = remaining_items + + # spawn logic for new items + if room_rect is not None: + if now - self.last_spawn_time >= ITEM_SPAWN_INTERVAL: + pos = _random_position(room_rect, blocked_rects) + if pos is not None: + x, y = pos + item_type = random.choice(["coffee", "beer"]) + self.items.append(Item(x, y, item_type)) + + self.last_spawn_time = now + + # check if player press "e" to buy + def check_collect(self, player_rect, keys): + if keys[pygame.K_e]: + for item in self.items: + if player_rect.colliderect(item.rect): + return item + return None + + # delete item from list after buying it + def remove_item(self, item): + if item in self.items: + self.items.remove(item) + + # draw all active items + def draw(self, screen, cam_x, cam_y): + for item in self.items: + item.draw(screen, cam_x, cam_y) + + # clear items when changing room + def clear(self): + self.items = [] + self.last_spawn_time = pygame.time.get_ticks() \ No newline at end of file diff --git a/src/settings.py b/src/settings.py index 36b11aa..32b0f0e 100644 --- a/src/settings.py +++ b/src/settings.py @@ -6,6 +6,7 @@ from src.map import tile_size DESK_WIDTH = 70 DESK_HEIGHT = 40 + # --- DOOR --- DOOR_WIDTH = 20 DOOR_HEIGHT = 100 @@ -20,6 +21,7 @@ COLOR_HP_BAR_BG = (50, 50, 50) # Valid movement/animation directions. Direction = Literal["down", "left", "right", "up"] + # Sprite files are grouped by direction. # Example: "down" uses sprite_01.png, sprite_02.png, sprite_03.png. DIRECTION_FRAMES: Mapping[Direction, Tuple[int, int, int]] = { @@ -29,21 +31,24 @@ DIRECTION_FRAMES: Mapping[Direction, Tuple[int, int, int]] = { "up": (10, 11, 12), } + # Default drawing size and animation speed. DEFAULT_SPRITE_HEIGHT: int = 138 DEFAULT_FRAME_TIME: float = 0.16 # --- ECTS --- START_ECTS_VALUE = 1 # how many ECTS are generated at the beginning -ECTS_UPDATE_INTERVAL = 1000 # time in ms = 1 sec +ECTS_UPDATE_INTERVAL = 1000 # time in ms (1000 = 1 sec) BEER_BOTTLE_ECTS_VALUE = 2 BEER_COST = 50 + # --- BUTTONS --- COLOR_BUTTON = (0, 255, 0) COLOR_BUTTON_HOVER = (200, 255, 0) + # --- STARTSCREEN --- COLOR_STARTSCREEN_BACKGROUND = ((0, 0, 0)) COLOR_STARTSCREEN_FONT_TITLE = (0, 255, 0) @@ -60,6 +65,7 @@ FONT_ENDSCREEN = "Arial" COFFEE_COST = 30 COFFEE_HEAL_VALUE = 30 + # --- TOWER --- TOWER_COST = 150 PROJECTILE_DMG = 15 @@ -68,11 +74,13 @@ COLOR_PROJECTILE = (200, 200, 0) TOWER_TIMER = 30000 # how long the tower stays and attack before it disappears -# --- SHOPPING TIMER --- -ITEM_SHOPPING_TIMER = 10000 # for items like coffee, beer, tower - how long u have time to buy it +# --- ITEM SPAWN TIMER --- +ITEM_SPAWN_INTERVAL = 5000 # ms until a new item spawns +ITEM_SHOPPING_TIMER = 10000 # how long you have time to buy items before they despawn +ITEM_SIZE = 30 -# -- ZOMBIE --- +# --- ZOMBIE --- ZOMBIE_EASY_HP = 30 ZOMBIE_EASY_DMG = 3