Загрузить файлы в «/»
This commit is contained in:
3673
level_1.json
Normal file
3673
level_1.json
Normal file
File diff suppressed because it is too large
Load Diff
264
main.py
Normal file
264
main.py
Normal file
@@ -0,0 +1,264 @@
|
|||||||
|
|
||||||
|
|
||||||
|
import pygame
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from typing import List, Dict
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class GameMain:
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
|
||||||
|
pygame.init()
|
||||||
|
|
||||||
|
self.screen_width = 1200
|
||||||
|
|
||||||
|
self.screen_height = 800
|
||||||
|
|
||||||
|
self.screen = pygame.display.set_mode((self.screen_width, self.screen_height))
|
||||||
|
|
||||||
|
pygame.display.set_caption("Strategy Game")
|
||||||
|
|
||||||
|
self.clock = pygame.time.Clock()
|
||||||
|
|
||||||
|
self.running = True
|
||||||
|
|
||||||
|
self.dt = 0.0
|
||||||
|
|
||||||
|
self.fixed_dt = 1.0 / 60.0
|
||||||
|
|
||||||
|
self.accumulator = 0.0
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
self.modules = {}
|
||||||
|
|
||||||
|
self.game_state = None
|
||||||
|
|
||||||
|
self.current_scene = "game"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def init_game(self) -> None:
|
||||||
|
|
||||||
|
from input_events import InputEvents
|
||||||
|
|
||||||
|
from input_commands import InputCommands
|
||||||
|
|
||||||
|
from state_loader import StateLoader
|
||||||
|
|
||||||
|
from state_updater import StateUpdater
|
||||||
|
|
||||||
|
from entity_creation import EntityCreation
|
||||||
|
|
||||||
|
from entity_behavior import EntityBehavior
|
||||||
|
|
||||||
|
from ai_decision import AIDecision
|
||||||
|
|
||||||
|
from render_tiles import RenderTiles
|
||||||
|
|
||||||
|
from render_entities import RenderEntities
|
||||||
|
|
||||||
|
from ui_panels import UIPanels
|
||||||
|
|
||||||
|
from sound_manager import SoundManager
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
self.modules['input_events'] = InputEvents()
|
||||||
|
|
||||||
|
self.modules['input_commands'] = InputCommands()
|
||||||
|
|
||||||
|
self.modules['state_loader'] = StateLoader()
|
||||||
|
|
||||||
|
self.modules['state_updater'] = StateUpdater()
|
||||||
|
|
||||||
|
self.modules['entity_creation'] = EntityCreation()
|
||||||
|
|
||||||
|
self.modules['entity_behavior'] = EntityBehavior()
|
||||||
|
|
||||||
|
self.modules['ai_decision'] = AIDecision()
|
||||||
|
|
||||||
|
self.modules['render_tiles'] = RenderTiles()
|
||||||
|
|
||||||
|
self.modules['render_entities'] = RenderEntities()
|
||||||
|
|
||||||
|
self.modules['ui_panels'] = UIPanels(self.screen_width, self.screen_height)
|
||||||
|
|
||||||
|
self.modules['sound_manager'] = SoundManager()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
self.game_state = self.modules['state_loader'].load_level('level_1.json')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def game_loop(self) -> None:
|
||||||
|
|
||||||
|
while self.running:
|
||||||
|
|
||||||
|
self.dt = self.clock.tick(60) / 1000.0
|
||||||
|
|
||||||
|
self.accumulator += self.dt
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
while self.accumulator >= self.fixed_dt:
|
||||||
|
|
||||||
|
self._fixed_update()
|
||||||
|
|
||||||
|
self.accumulator -= self.fixed_dt
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
self._render()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
self.shutdown()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def _fixed_update(self) -> None:
|
||||||
|
|
||||||
|
events = self.modules['input_events'].poll_events()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if self.modules['input_events'].quit_requested:
|
||||||
|
|
||||||
|
self.running = False
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
player_commands = self.modules['input_commands'].generate_commands(events, self.game_state)
|
||||||
|
|
||||||
|
ai_commands = self.modules['ai_decision'].decide(self.game_state, self.fixed_dt)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
all_commands = player_commands + ai_commands
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
actions = self.modules['state_updater'].apply_commands(self.game_state, all_commands)
|
||||||
|
|
||||||
|
self.modules['state_updater'].tick(self.game_state, self.fixed_dt)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
behavior_actions = self.modules['entity_behavior'].process_behaviors(self.game_state, self.fixed_dt)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
sound_events = self._extract_sound_events(actions + behavior_actions)
|
||||||
|
|
||||||
|
visual_effects = self.modules['sound_manager'].handle_events(sound_events)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
self._apply_visual_effects(visual_effects)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def _render(self) -> None:
|
||||||
|
|
||||||
|
self.screen.fill((0, 0, 0))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
camera = self.game_state.camera if hasattr(self.game_state, 'camera') else {'x': 0, 'y': 0, 'zoom': 1.0}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
self.modules['render_tiles'].draw_map(self.screen, self.game_state.map_tiles, camera)
|
||||||
|
|
||||||
|
self.modules['render_entities'].draw_entities(self.screen, self.game_state.entities, camera)
|
||||||
|
|
||||||
|
self.modules['ui_panels'].draw(self.screen)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
pygame.display.flip()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def _extract_sound_events(self, actions: List[Dict]) -> List[Dict]:
|
||||||
|
|
||||||
|
sound_events = []
|
||||||
|
|
||||||
|
for action in actions:
|
||||||
|
|
||||||
|
if action.get('type') in ['deal_damage', 'building_completed', 'gather_resources']:
|
||||||
|
|
||||||
|
sound_events.append({'type': self._map_action_to_sound(action['type'])})
|
||||||
|
|
||||||
|
return sound_events
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def _map_action_to_sound(self, action_type: str) -> str:
|
||||||
|
|
||||||
|
sound_map = {
|
||||||
|
|
||||||
|
'deal_damage': 'unit_attack',
|
||||||
|
|
||||||
|
'building_completed': 'building_complete',
|
||||||
|
|
||||||
|
'gather_resources': 'resource_gathered'
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return sound_map.get(action_type, '')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def _apply_visual_effects(self, visual_effects: List[Dict]) -> None:
|
||||||
|
|
||||||
|
for effect in visual_effects:
|
||||||
|
|
||||||
|
if hasattr(self.game_state, 'visual_effects'):
|
||||||
|
|
||||||
|
self.game_state.visual_effects.append(effect)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
self.game_state.visual_effects = [effect]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def handle_state_transition(self, scene: str) -> None:
|
||||||
|
|
||||||
|
self.current_scene = scene
|
||||||
|
|
||||||
|
if scene == "menu":
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
elif scene == "game":
|
||||||
|
|
||||||
|
self.game_state = self.modules['state_loader'].load_level('level_1.json')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def shutdown(self) -> None:
|
||||||
|
|
||||||
|
pygame.quit()
|
||||||
|
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
|
||||||
|
game = GameMain()
|
||||||
|
|
||||||
|
game.init_game()
|
||||||
|
|
||||||
|
game.game_loop()
|
||||||
|
|
||||||
220
render_entities.py
Normal file
220
render_entities.py
Normal file
@@ -0,0 +1,220 @@
|
|||||||
|
|
||||||
|
|
||||||
|
import pygame
|
||||||
|
|
||||||
|
from typing import List, Dict
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class RenderEntities:
|
||||||
|
|
||||||
|
def __init__(self, tile_size: int = 64):
|
||||||
|
|
||||||
|
self.tile_size = tile_size
|
||||||
|
|
||||||
|
self.entity_sprites = {}
|
||||||
|
|
||||||
|
self.animations = {}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def draw_entities(self, surface: pygame.Surface, entities: List[Dict], camera: Dict) -> None:
|
||||||
|
|
||||||
|
if not entities:
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
camera_x = camera.get('x', 0)
|
||||||
|
|
||||||
|
camera_y = camera.get('y', 0)
|
||||||
|
|
||||||
|
zoom = camera.get('zoom', 1.0)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
for entity in entities:
|
||||||
|
|
||||||
|
if 'x' in entity and 'y' in entity:
|
||||||
|
|
||||||
|
screen_x = (entity['x'] - camera_x) * zoom
|
||||||
|
|
||||||
|
screen_y = (entity['y'] - camera_y) * zoom
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
sprite = self._get_entity_sprite(entity)
|
||||||
|
|
||||||
|
if sprite:
|
||||||
|
|
||||||
|
scaled_width = int(sprite.get_width() * zoom)
|
||||||
|
|
||||||
|
scaled_height = int(sprite.get_height() * zoom)
|
||||||
|
|
||||||
|
scaled_sprite = pygame.transform.scale(sprite, (scaled_width, scaled_height))
|
||||||
|
|
||||||
|
surface.blit(scaled_sprite, (screen_x - scaled_width // 2, screen_y - scaled_height // 2))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
self._draw_health_bar(surface, entity, screen_x, screen_y, scaled_width if 'sprite' in locals() else self.tile_size, zoom)
|
||||||
|
|
||||||
|
self._draw_selection(surface, entity, screen_x, screen_y, scaled_width if 'sprite' in locals() else self.tile_size, zoom)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def animate_entity(self, entity: Dict, dt: float) -> None:
|
||||||
|
|
||||||
|
entity_id = entity.get('id')
|
||||||
|
|
||||||
|
if entity_id not in self.animations:
|
||||||
|
|
||||||
|
self.animations[entity_id] = {'frame': 0, 'time': 0}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
animation = self.animations[entity_id]
|
||||||
|
|
||||||
|
animation['time'] += dt
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if animation['time'] >= 0.1:
|
||||||
|
|
||||||
|
animation['frame'] = (animation['frame'] + 1) % 4
|
||||||
|
|
||||||
|
animation['time'] = 0
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def _get_entity_sprite(self, entity: Dict) -> pygame.Surface:
|
||||||
|
|
||||||
|
entity_type = entity.get('type')
|
||||||
|
|
||||||
|
entity_name = entity.get('name', '').lower()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if entity_id := entity.get('id'):
|
||||||
|
|
||||||
|
self.animate_entity(entity, 0.016)
|
||||||
|
|
||||||
|
frame = self.animations[entity_id]['frame']
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
frame = 0
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
sprite_key = f"{entity_type}_{entity_name}_{frame}"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if sprite_key not in self.entity_sprites:
|
||||||
|
|
||||||
|
self.entity_sprites[sprite_key] = self._create_entity_sprite(entity, frame)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return self.entity_sprites[sprite_key]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def _create_entity_sprite(self, entity: Dict, frame: int) -> pygame.Surface:
|
||||||
|
|
||||||
|
entity_type = entity.get('type')
|
||||||
|
|
||||||
|
size = self.tile_size
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if entity_type == 'unit':
|
||||||
|
|
||||||
|
color = (0, 0, 255) if entity.get('owner') == 'player' else (255, 0, 0)
|
||||||
|
|
||||||
|
surface = pygame.Surface((size // 2, size // 2), pygame.SRCALPHA)
|
||||||
|
|
||||||
|
pygame.draw.circle(surface, color, (size // 4, size // 4), size // 4)
|
||||||
|
|
||||||
|
if frame % 2 == 0:
|
||||||
|
|
||||||
|
pygame.draw.circle(surface, (255, 255, 255), (size // 4, size // 4), size // 8)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
elif entity_type == 'building':
|
||||||
|
|
||||||
|
color = (0, 100, 200) if entity.get('owner') == 'player' else (200, 0, 0)
|
||||||
|
|
||||||
|
surface = pygame.Surface((size, size), pygame.SRCALPHA)
|
||||||
|
|
||||||
|
pygame.draw.rect(surface, color, (0, 0, size, size))
|
||||||
|
|
||||||
|
if not entity.get('completed', True):
|
||||||
|
|
||||||
|
progress = entity.get('build_progress', 0) / entity.get('build_time', 1)
|
||||||
|
|
||||||
|
pygame.draw.rect(surface, (100, 100, 100), (0, size * (1 - progress), size, size * progress))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
elif entity_type == 'resource':
|
||||||
|
|
||||||
|
surface = pygame.Surface((size, size), pygame.SRCALPHA)
|
||||||
|
|
||||||
|
pygame.draw.circle(surface, (255, 255, 0), (size // 2, size // 2), size // 3)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
surface = pygame.Surface((size, size), pygame.SRCALPHA)
|
||||||
|
|
||||||
|
pygame.draw.rect(surface, (200, 200, 200), (0, 0, size, size))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return surface
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def _draw_health_bar(self, surface: pygame.Surface, entity: Dict, x: float, y: float, width: int, zoom: float) -> None:
|
||||||
|
|
||||||
|
if 'health' in entity and 'max_health' in entity and entity['max_health'] > 0:
|
||||||
|
|
||||||
|
health_ratio = entity['health'] / entity['max_health']
|
||||||
|
|
||||||
|
bar_width = width
|
||||||
|
|
||||||
|
bar_height = max(4, int(6 * zoom))
|
||||||
|
|
||||||
|
bar_x = x - bar_width // 2
|
||||||
|
|
||||||
|
bar_y = y - width // 2 - bar_height - 2
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
background_rect = pygame.Rect(bar_x, bar_y, bar_width, bar_height)
|
||||||
|
|
||||||
|
health_rect = pygame.Rect(bar_x, bar_y, int(bar_width * health_ratio), bar_height)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
pygame.draw.rect(surface, (255, 0, 0), background_rect)
|
||||||
|
|
||||||
|
pygame.draw.rect(surface, (0, 255, 0), health_rect)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def _draw_selection(self, surface: pygame.Surface, entity: Dict, x: float, y: float, width: int, zoom: float) -> None:
|
||||||
|
|
||||||
|
if entity.get('selected', False):
|
||||||
|
|
||||||
|
selection_size = width + int(10 * zoom)
|
||||||
|
|
||||||
|
selection_rect = pygame.Rect(x - selection_size // 2, y - selection_size // 2, selection_size, selection_size)
|
||||||
|
|
||||||
|
pygame.draw.rect(surface, (255, 255, 0), selection_rect, int(2 * zoom))
|
||||||
|
|
||||||
124
render_tiles.py
Normal file
124
render_tiles.py
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
|
||||||
|
|
||||||
|
import pygame
|
||||||
|
|
||||||
|
from typing import List, Dict
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class RenderTiles:
|
||||||
|
|
||||||
|
def __init__(self, tile_size: int = 64):
|
||||||
|
|
||||||
|
self.tile_size = tile_size
|
||||||
|
|
||||||
|
self.tile_sprites = {}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def load_tile_sprites(self, asset_manifest: Dict) -> Dict:
|
||||||
|
|
||||||
|
self.tile_sprites = {}
|
||||||
|
|
||||||
|
for tile_type, sprite_path in asset_manifest.get('tiles', {}).items():
|
||||||
|
|
||||||
|
try:
|
||||||
|
|
||||||
|
sprite = pygame.image.load(sprite_path).convert_alpha()
|
||||||
|
|
||||||
|
self.tile_sprites[tile_type] = pygame.transform.scale(sprite, (self.tile_size, self.tile_size))
|
||||||
|
|
||||||
|
except pygame.error:
|
||||||
|
|
||||||
|
print(f"Failed to load tile sprite: {sprite_path}")
|
||||||
|
|
||||||
|
self.tile_sprites[tile_type] = self._create_placeholder_tile(tile_type)
|
||||||
|
|
||||||
|
return self.tile_sprites
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def draw_map(self, surface: pygame.Surface, map_tiles: List[List[Dict]], camera: Dict) -> None:
|
||||||
|
|
||||||
|
if not map_tiles:
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
camera_x = camera.get('x', 0)
|
||||||
|
|
||||||
|
camera_y = camera.get('y', 0)
|
||||||
|
|
||||||
|
zoom = camera.get('zoom', 1.0)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
screen_width = surface.get_width()
|
||||||
|
|
||||||
|
screen_height = surface.get_height()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
start_col = max(0, int(camera_x // self.tile_size))
|
||||||
|
|
||||||
|
start_row = max(0, int(camera_y // self.tile_size))
|
||||||
|
|
||||||
|
end_col = min(len(map_tiles[0]), int((camera_x + screen_width / zoom) // self.tile_size) + 1)
|
||||||
|
|
||||||
|
end_row = min(len(map_tiles), int((camera_y + screen_height / zoom) // self.tile_size) + 1)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
for row in range(start_row, end_row):
|
||||||
|
|
||||||
|
for col in range(start_col, end_col):
|
||||||
|
|
||||||
|
if row < len(map_tiles) and col < len(map_tiles[row]):
|
||||||
|
|
||||||
|
tile_data = map_tiles[row][col]
|
||||||
|
|
||||||
|
tile_type = tile_data.get('type', 'grass')
|
||||||
|
|
||||||
|
sprite = self.tile_sprites.get(tile_type, self._create_placeholder_tile(tile_type))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
screen_x = (col * self.tile_size - camera_x) * zoom
|
||||||
|
|
||||||
|
screen_y = (row * self.tile_size - camera_y) * zoom
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
scaled_sprite = pygame.transform.scale(sprite, (int(self.tile_size * zoom), int(self.tile_size * zoom)))
|
||||||
|
|
||||||
|
surface.blit(scaled_sprite, (screen_x, screen_y))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def _create_placeholder_tile(self, tile_type: str) -> pygame.Surface:
|
||||||
|
|
||||||
|
surface = pygame.Surface((self.tile_size, self.tile_size), pygame.SRCALPHA)
|
||||||
|
|
||||||
|
color_map = {
|
||||||
|
|
||||||
|
'grass': (100, 200, 100),
|
||||||
|
|
||||||
|
'water': (100, 100, 255),
|
||||||
|
|
||||||
|
'mountain': (150, 150, 150),
|
||||||
|
|
||||||
|
'forest': (50, 150, 50),
|
||||||
|
|
||||||
|
'sand': (240, 230, 140)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
color = color_map.get(tile_type, (200, 200, 200))
|
||||||
|
|
||||||
|
surface.fill(color)
|
||||||
|
|
||||||
|
pygame.draw.rect(surface, (50, 50, 50), surface.get_rect(), 1)
|
||||||
|
|
||||||
|
return surface
|
||||||
|
|
||||||
122
sound_manager.py
Normal file
122
sound_manager.py
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
|
||||||
|
|
||||||
|
import pygame
|
||||||
|
|
||||||
|
from typing import List, Dict
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class SoundManager:
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
|
||||||
|
pygame.mixer.init()
|
||||||
|
|
||||||
|
self.sounds = {}
|
||||||
|
|
||||||
|
self.music_volume = 0.5
|
||||||
|
|
||||||
|
self.sound_volume = 0.7
|
||||||
|
|
||||||
|
self.visual_effects = []
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def handle_events(self, sound_events: List[Dict]) -> List[Dict]:
|
||||||
|
|
||||||
|
self.visual_effects = []
|
||||||
|
|
||||||
|
for event in sound_events:
|
||||||
|
|
||||||
|
self.play_sound(event.get('type'))
|
||||||
|
|
||||||
|
return self.visual_effects
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def play_sound(self, event_name: str) -> None:
|
||||||
|
|
||||||
|
if event_name not in self.sounds:
|
||||||
|
|
||||||
|
self._load_sound(event_name)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
sound = self.sounds.get(event_name)
|
||||||
|
|
||||||
|
if sound:
|
||||||
|
|
||||||
|
sound.set_volume(self.sound_volume)
|
||||||
|
|
||||||
|
sound.play()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
self._create_visual_effect(event_name)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def set_volume(self, levels: Dict[str, float]) -> None:
|
||||||
|
|
||||||
|
self.music_volume = levels.get('music', self.music_volume)
|
||||||
|
|
||||||
|
self.sound_volume = levels.get('sound', self.sound_volume)
|
||||||
|
|
||||||
|
pygame.mixer.music.set_volume(self.music_volume)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def _load_sound(self, event_name: str) -> None:
|
||||||
|
|
||||||
|
sound_paths = {
|
||||||
|
|
||||||
|
'unit_attack': 'sounds/attack.wav',
|
||||||
|
|
||||||
|
'building_complete': 'sounds/building_complete.wav',
|
||||||
|
|
||||||
|
'unit_selected': 'sounds/select.wav',
|
||||||
|
|
||||||
|
'resource_gathered': 'sounds/gather.wav',
|
||||||
|
|
||||||
|
'error': 'sounds/error.wav'
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
path = sound_paths.get(event_name)
|
||||||
|
|
||||||
|
if path:
|
||||||
|
|
||||||
|
try:
|
||||||
|
|
||||||
|
self.sounds[event_name] = pygame.mixer.Sound(path)
|
||||||
|
|
||||||
|
except pygame.error:
|
||||||
|
|
||||||
|
print(f"Failed to load sound: {path}")
|
||||||
|
|
||||||
|
self.sounds[event_name] = None
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def _create_visual_effect(self, event_name: str) -> None:
|
||||||
|
|
||||||
|
effect_map = {
|
||||||
|
|
||||||
|
'unit_attack': {'type': 'flash', 'color': (255, 0, 0), 'duration': 0.2},
|
||||||
|
|
||||||
|
'building_complete': {'type': 'sparkle', 'color': (0, 255, 0), 'duration': 1.0},
|
||||||
|
|
||||||
|
'resource_gathered': {'type': 'glow', 'color': (255, 255, 0), 'duration': 0.5}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
effect = effect_map.get(event_name)
|
||||||
|
|
||||||
|
if effect:
|
||||||
|
|
||||||
|
self.visual_effects.append(effect)
|
||||||
|
|
||||||
Reference in New Issue
Block a user