Add combat_system.py
parent
e07ac205b2
commit
a03a783842
|
|
@ -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))
|
||||
Loading…
Reference in New Issue