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)) (255, 255, 255) ) self.screen.blit(debug_text, (10, 30)) if self.paused: pause_text = self.font.render("PAUSED", True, (255, 0, 0)) rect = pause_text.get_rect(center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2)) self.screen.blit(pause_text, rect)