From a03a783842f0b7607cc9e8e27dc33e911f8d3dd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rihards=20=C5=A0ev=C4=8Duks?= Date: Wed, 25 Feb 2026 06:22:47 +0000 Subject: [PATCH] Add combat_system.py --- combat_system.py | 459 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 459 insertions(+) create mode 100644 combat_system.py diff --git a/combat_system.py b/combat_system.py new file mode 100644 index 0000000..193622d --- /dev/null +++ b/combat_system.py @@ -0,0 +1,459 @@ +import pygame +from settings import * + +from world import World +from player import Player +from camera import Camera +from inventory import Inventory +from enemy_manager import EnemyManager +from menu_system import MainMenu, PauseMenu, NewGameDialog, LoadGameDialog, SettingsMenu, DeathScreen +from save_system import SaveSystem +from weapon_system import WeaponManager +from combat_system import CombatSystem +from hotbar import Hotbar +from items import get_item + + +class Game: + def __init__(self): + pygame.init() + + # ---------------------------------- + # Display Setup + # ---------------------------------- + self.setup_display() + + self.clock = pygame.time.Clock() + self.running = True + self.paused = False + + # ---------------------------------- + # Game State Management + # ---------------------------------- + self.game_state = "menu" + self.show_inventory = False + self.world_name = "Default" + + # ---------------------------------- + # Menu Systems + # ---------------------------------- + self.main_menu = MainMenu(self) + self.new_game_dialog = None + self.load_game_dialog = None + self.pause_menu = None + self.settings_menu = None + self.death_screen = None + self.save_system = SaveSystem() + + # ---------------------------------- + # Core Systems (initialized as None) + # ---------------------------------- + self.world = None + self.player = None + self.camera = None + self.inventory = None + self.enemy_manager = None + self.weapon_manager = None + self.combat_system = None + self.hotbar = None + + # ---------------------------------- + # Debug + # ---------------------------------- + self.font = pygame.font.SysFont("consolas", 18) + + def setup_display(self): + """Setup display with current resolution and fullscreen setting""" + flags = pygame.SCALED + if FULLSCREEN: + flags |= pygame.FULLSCREEN + + self.screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT), flags) + pygame.display.set_caption(GAME_TITLE) + + def toggle_fullscreen(self): + """Toggle fullscreen mode""" + global FULLSCREEN + FULLSCREEN = not FULLSCREEN + self.setup_display() + + def init_game(self): + """Initialize game systems (called when starting new game or loading)""" + self.world = World() + self.weapon_manager = WeaponManager() + self.player = Player(100, 100, self.weapon_manager) + self.camera = Camera(self.player) + self.inventory = Inventory() + self.hotbar = Hotbar(self.inventory, self.weapon_manager) + self.enemy_manager = EnemyManager(self.world, self.player) + self.combat_system = CombatSystem(self.weapon_manager) + self.show_inventory = False + + # Add starting items and WEAPONS + self.inventory.add_item(ITEM_WOOD, 10) + self.inventory.add_item(ITEM_STONE, 5) + + # Add all weapons to inventory + self.inventory.add_item(ITEM_WOODEN_SWORD, 1) + self.inventory.add_item(ITEM_IRON_SWORD, 1) + self.inventory.add_item(ITEM_GOLD_SWORD, 1) + + # Add weapons to weapon manager + self.weapon_manager.add_weapon(ITEM_WOODEN_SWORD) + self.weapon_manager.add_weapon(ITEM_IRON_SWORD) + self.weapon_manager.add_weapon(ITEM_GOLD_SWORD) + + # ========================================================== + # MAIN LOOP + # ========================================================== + def run(self): + while self.running: + dt = self.clock.tick(FPS) / 1000 + + if self.game_state == "menu": + self.handle_menu_events() + self.update_menu() + self.draw_menu() + + elif self.game_state == "settings": + self.handle_settings_events() + self.settings_menu.update(dt) + self.settings_menu.draw(self.screen) + + elif self.game_state == "new_game_dialog": + self.handle_new_game_dialog_events() + self.new_game_dialog.update(dt) + self.new_game_dialog.draw(self.screen) + + elif self.game_state == "load_game_dialog": + self.handle_load_game_dialog_events() + self.load_game_dialog.update() + self.load_game_dialog.draw(self.screen) + + elif self.game_state == "playing": + self.handle_events() + if not self.paused: + self.update(dt) + self.draw() + + elif self.game_state == "paused": + self.handle_pause_events() + self.pause_menu.update(dt) + self.draw_paused() + + elif self.game_state == "dead": + self.handle_death_events() + self.death_screen.update(dt) + self.draw_dead() + + pygame.quit() + + # ========================================================== + # MAIN MENU STATE + # ========================================================== + def handle_menu_events(self): + action = self.main_menu.handle_events() + + if action == "new_game": + self.new_game_dialog = NewGameDialog(self) + self.game_state = "new_game_dialog" + + elif action == "load_game": + self.load_game_dialog = LoadGameDialog(self, self.save_system) + self.game_state = "load_game_dialog" + + elif action == "settings": + self.settings_menu = SettingsMenu(self) + self.game_state = "settings" + + elif action == "quit": + self.running = False + + def update_menu(self): + self.main_menu.update() + + def draw_menu(self): + self.main_menu.draw(self.screen) + + # ========================================================== + # SETTINGS STATE + # ========================================================== + def handle_settings_events(self): + action = self.settings_menu.handle_events() + + if action == "back": + self.game_state = "menu" + + # ========================================================== + # NEW GAME DIALOG STATE + # ========================================================== + def handle_new_game_dialog_events(self): + action = self.new_game_dialog.handle_events() + + if action and action.startswith("create:"): + world_name = action.replace("create:", "") + self.world_name = world_name + self.init_game() + self.game_state = "playing" + print(f"New game '{world_name}' started") + + elif action == "cancel": + self.game_state = "menu" + + # ========================================================== + # LOAD GAME DIALOG STATE + # ========================================================== + def handle_load_game_dialog_events(self): + action = self.load_game_dialog.handle_events() + + if action and action.startswith("load:"): + save_index = int(action.replace("load:", "")) + saves = self.save_system.get_save_files() + + if 0 <= save_index < len(saves): + save_file = saves[save_index]["name"] + self.init_game() + + if self.save_system.load_game(self, save_file): + self.game_state = "playing" + print(f"Game loaded: {save_file}") + else: + print("Failed to load game") + self.game_state = "load_game_dialog" + + elif action and action.startswith("delete:"): + save_index = int(action.replace("delete:", "")) + saves = self.save_system.get_save_files() + + if 0 <= save_index < len(saves): + save_file = saves[save_index]["name"] + self.save_system.delete_save(save_file) + self.load_game_dialog = LoadGameDialog(self, self.save_system) + + elif action == "cancel": + self.game_state = "menu" + + # ========================================================== + # PAUSE STATE + # ========================================================== + def handle_pause_events(self): + action = self.pause_menu.handle_events() + + if action == "resume": + self.paused = False + self.game_state = "playing" + + elif action == "save": + self.save_system.save_game(self, self.world_name) + self.pause_menu.show_message("Game saved!") + + elif action == "backup": + self.save_system.backup_game(self) + self.pause_menu.show_message("Backup created!") + + elif action == "quit_to_menu": + self.paused = False + self.game_state = "menu" + + def draw_paused(self): + self.screen.fill(BACKGROUND_COLOR) + self.world.draw(self.screen, self.camera) + self.enemy_manager.draw(self.screen, self.camera) + self.player.draw(self.screen, self.camera) + self.pause_menu.draw(self.screen) + + # ========================================================== + # DEATH STATE + # ========================================================== + def handle_death_events(self): + action = self.death_screen.handle_events() + + if action == "respawn": + # Reset player health and position + self.player.health = self.player.max_health + self.player.pos = pygame.Vector2(100, 100) + self.player.rect.x = 100 + self.player.rect.y = 100 + self.player.velocity = pygame.Vector2(0, 0) + self.game_state = "playing" + + elif action == "quit_to_menu": + self.game_state = "menu" + + def draw_dead(self): + self.screen.fill(BACKGROUND_COLOR) + self.world.draw(self.screen, self.camera) + self.enemy_manager.draw(self.screen, self.camera) + self.player.draw(self.screen, self.camera) + self.death_screen.draw(self.screen) + + # ========================================================== + # GAMEPLAY STATE + # ========================================================== + def handle_events(self): + for event in pygame.event.get(): + + if event.type == pygame.QUIT: + self.running = False + + if event.type == pygame.KEYDOWN: + if event.key == pygame.K_ESCAPE: + self.paused = True + self.pause_menu = PauseMenu(self) + self.game_state = "paused" + + # Toggle inventory + if event.key == pygame.K_i: + self.show_inventory = not self.show_inventory + + # Hotbar input + if self.hotbar: + self.hotbar.handle_input(event) + + # Mouse input for attacking and placing + if event.type == pygame.MOUSEBUTTONDOWN: + if self.show_inventory: + continue + + if event.button == 1: # Left click + # Get current held item + items_list = list(self.inventory.slots.items()) + current_item = None + if self.hotbar.selected_slot < len(items_list): + current_item_id, _ = items_list[self.hotbar.selected_slot] + current_item = get_item(current_item_id) + + # Check if holding a weapon + is_holding_weapon = (current_item is not None and + current_item.id in WEAPON_STATS) + + if is_holding_weapon: + # Attack with sword + print(f"DEBUG: Attempting sword attack with {current_item.name}") + self.combat_system.player_attack(self.player, self.enemy_manager.enemies) + else: + # Mine block if not holding weapon + print("DEBUG: Mining block") + self.world.break_block( + pygame.mouse.get_pos(), + self.camera, + self.inventory, + self.player.rect + ) + + if event.button == 3: # Right click - place block + # Get current held item + items_list = list(self.inventory.slots.items()) + if self.hotbar.selected_slot < len(items_list): + current_item_id, _ = items_list[self.hotbar.selected_slot] + current_item = get_item(current_item_id) + + # Only place if it's placeable + if current_item and current_item.placeable: + print(f"DEBUG: Placing {current_item.name}") + self.world.place_block( + pygame.mouse.get_pos(), + self.camera, + self.player.rect, + current_item.place_tile + ) + + def update(self, dt): + if self.show_inventory: + return + + self.player.update(dt, self.world) + self.camera.update() + self.enemy_manager.update(dt) + self.combat_system.update(dt) + + # Check if player is dead + if self.player.health <= 0: + self.death_screen = DeathScreen(self) + self.game_state = "dead" + return + + # Enemy AI - attack player + for enemy in self.enemy_manager.enemies: + self.combat_system.enemy_attack(enemy, self.player) + + def draw(self): + self.screen.fill(BACKGROUND_COLOR) + + # Draw world relative to camera + self.world.draw(self.screen, self.camera) + + # Draw entities + self.enemy_manager.draw(self.screen, self.camera) + self.player.draw(self.screen, self.camera) + + # UI + if self.show_inventory: + self.inventory.draw(self.screen) + + # Draw hotbar + if self.hotbar and not self.show_inventory: + self.hotbar.draw(self.screen) + + self.draw_ui() + pygame.display.flip() + + # ========================================================== + # UI / DEBUG + # ========================================================== + def draw_ui(self): + if SHOW_FPS: + fps = self.clock.get_fps() + fps_text = self.font.render(f"FPS: {int(fps)}", True, (255, 255, 255)) + self.screen.blit(fps_text, (10, 10)) + + if DEBUG_MODE: + debug_text = self.font.render( + f"Player: ({int(self.player.rect.x)}, {int(self.player.rect.y)})", + True, + (255, 255, 255) + ) + self.screen.blit(debug_text, (10, 30)) + + world_name_text = self.font.render( + f"World: {self.world_name}", + True, + (255, 255, 255) + ) + self.screen.blit(world_name_text, (10, 50)) + + weapon = self.weapon_manager.get_current_weapon() + if weapon: + weapon_text = self.font.render( + f"Weapon: {weapon.name}", + True, + (255, 255, 255) + ) + self.screen.blit(weapon_text, (10, 70)) + + # Health display + health_text = self.font.render( + f"Health: {int(self.player.health)}/{int(self.player.max_health)}", + True, + (255, 50, 50) + ) + self.screen.blit(health_text, (10, 90)) + + # Enemy count + enemy_text = self.font.render( + f"Enemies: {len(self.enemy_manager.enemies)}", + True, + (255, 100, 100) + ) + self.screen.blit(enemy_text, (10, 110)) + + # Draw currently holding item (top right) + items_list = list(self.inventory.slots.items()) + if self.hotbar and items_list: + current_item_id, current_amount = items_list[self.hotbar.selected_slot] if self.hotbar.selected_slot < len( + items_list) else (None, 0) + if current_item_id: + item = get_item(current_item_id) + if item: + holding_text = self.font.render(f"Holding: {item.name}", True, (255, 255, 255)) + self.screen.blit(holding_text, (SCREEN_WIDTH - 250, 10)) \ No newline at end of file