diff --git a/highway_vector.py b/highway_vector.py index 8f0ad6b..171e888 100644 --- a/highway_vector.py +++ b/highway_vector.py @@ -1,9 +1,13 @@ import math import os import sys +import array +import random import pygame +# Inicialización de Pygame y el mezclador de audio pygame.init() +pygame.mixer.init(frequency=22050, size=-16, channels=1) # Configuración de Pantalla SCREEN_WIDTH = 800 @@ -31,6 +35,56 @@ COLOR_ORANGE = (255, 140, 0) COLOR_CYAN = (0, 200, 255) +def generate_sound(sound_type): + """Genera efectos de sonido retro de forma sintética sin archivos externos.""" + sample_rate = 22050 + buffer = array.array("h") + + if sound_type == "shoot": + # Disparo: Tono descendente agudo rápido + duration = 0.1 + total_samples = int(sample_rate * duration) + for i in range(total_samples): + t = i / total_samples + freq = 800 * (1 - t * 0.7) + val = math.sin(2 * math.pi * freq * (i / sample_rate)) + env = 1 - t + sample = int(val * env * 12000) + buffer.append(sample) + + elif sound_type == "hit": + # Choque: Golpe sordo de baja frecuencia + duration = 0.15 + total_samples = int(sample_rate * duration) + for i in range(total_samples): + t = i / total_samples + freq = 150 * (1 - t * 0.5) + noise = (random.random() * 2 - 1) * 0.3 + val = math.sin(2 * math.pi * freq * (i / sample_rate)) + noise + env = (1 - t) ** 2 + sample = int(val * env * 15000) + buffer.append(max(-32768, min(32767, sample))) + + elif sound_type == "destroy": + # Destrucción: Ruido de explosión corta + duration = 0.2 + total_samples = int(sample_rate * duration) + for i in range(total_samples): + t = i / total_samples + noise = random.random() * 2 - 1 + env = (1 - t) ** 1.5 + sample = int(noise * env * 14000) + buffer.append(sample) + + return pygame.mixer.Sound(buffer=buffer) + + +# Cargar sonidos sintéticos +SOUND_SHOOT = generate_sound("shoot") +SOUND_HIT = generate_sound("hit") +SOUND_DESTROY = generate_sound("destroy") + + def grid_to_iso(gx, gy, gz=0): """Convierte coordenadas 3D de rejilla a coordenadas 2D de pantalla.""" iso_x = (gx - gy) * (TILE_WIDTH // 2) @@ -204,10 +258,10 @@ class Bullet(Entity): and abs(entity.gy - self.gy) < 0.5 ): self.alive = False + SOUND_HIT.play() if isinstance(entity, WhiteBlock): entity.health -= 1 elif isinstance(entity, PurpleBlock): - # El bloque morado es empujado por el disparo entity.push_by_bullet(self.dir_x, self.dir_y, entities) elif isinstance(entity, Enemy) and entity.destructible: entity.take_damage() @@ -250,7 +304,6 @@ class PurpleBlock(Entity): ) def push_by_bullet(self, dir_x, dir_y, entities): - """Se desplaza 1 casilla entera en la dirección del impacto del disparo.""" step_x = 1 if dir_x > 0.3 else (-1 if dir_x < -0.3 else 0) step_y = 1 if dir_y > 0.3 else (-1 if dir_y < -0.3 else 0) @@ -386,7 +439,6 @@ class Player(Entity): abs(entity.gx - target_gx) < 0.5 and abs(entity.gy - target_gy) < 0.5 ): - # Bloque Blanco: Se empuja caminando if isinstance(entity, WhiteBlock): push_dx = ( 0.15 if dx > 0 else (-0.15 if dx < 0 else 0) @@ -396,7 +448,6 @@ class Player(Entity): ) if not entity.push(push_dx, push_dy, entities): blocked = True - # Bloque Morado y otros elementos: Bloquean el movimiento del jugador else: blocked = True if not blocked: @@ -412,6 +463,7 @@ class Player(Entity): bullets.append( Bullet(spawn_x, spawn_y, self.facing_x, self.facing_y) ) + SOUND_SHOOT.play() self.shoot_cooldown = 15 @@ -420,6 +472,7 @@ class Vortex(Entity): def __init__(self, gx, gy): super().__init__(gx, gy, "vortex", "vortex.png", is_solid=True) self.speed = 0.012 + self.was_blocked = False def update(self, entities): target_gy = self.gy - self.speed @@ -434,6 +487,11 @@ class Vortex(Entity): blocked = True break + if blocked and not self.was_blocked: + SOUND_HIT.play() + + self.was_blocked = blocked + if not blocked and target_gy >= 0: self.gy = target_gy @@ -472,9 +530,7 @@ class Game: ) self.spares.append(spare) - # Generar mapa balanceado con bloques blancos y morados for y in range(10, GRID_SIZE_Y - 12): - # Alternar entre bloques Blancos (empujables) y Morados (desplazados por disparos) if y % 6 == 0: lane = (y * 3) % GRID_SIZE_X if (y // 6) % 2 == 0: @@ -504,12 +560,13 @@ class Game: ) if player_hit or vortex_hit: + SOUND_DESTROY.play() return True - # Eliminar bloques blancos destruidos y enemigos abatidos for entity in self.entities[:]: if hasattr(entity, "health") and entity.health <= 0: self.entities.remove(entity) + SOUND_DESTROY.play() self.score += 200 return False diff --git a/img-versiones/version_4.1.png b/img-versiones/version_4.1.png new file mode 100644 index 0000000..83bd81c Binary files /dev/null and b/img-versiones/version_4.1.png differ