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