import sys import math import pygame # Inicialización de Pygame pygame.init() # Configuración de pantalla SCREEN_WIDTH = 800 SCREEN_HEIGHT = 600 FPS = 60 # Isometría y Cuadrícula TILE_WIDTH = 64 TILE_HEIGHT = 32 GRID_SIZE_X = 8 GRID_SIZE_Y = 120 # Colores de apoyo COLOR_BG = (15, 15, 25) COLOR_ROAD = (50, 50, 65) COLOR_GRID = (30, 30, 45) def grid_to_iso(gx, gy, gz=0): """Convierte coordenadas 3D de rejilla en coordenadas 2D en pantalla.""" iso_x = (gx - gy) * (TILE_WIDTH // 2) iso_y = (gx + gy) * (TILE_HEIGHT // 2) - (gz * 16) return iso_x, iso_y # ============================================================================== # GENERADOR DE SPRITES AUTOMÁTICO (O Carga desde PNG si existen) # ============================================================================== def load_or_create_sprite(filename, create_func): """Intenta cargar una imagen PNG. Si no existe, la crea dinámicamente.""" try: image = pygame.image.load(filename).convert_alpha() return image except (pygame.error, FileNotFoundError): return create_func() def create_player_sprite(): surf = pygame.Surface((64, 64), pygame.SRCALPHA) # Cuerpo principal Lasertron pygame.draw.ellipse(surf, (255, 200, 0), (16, 20, 32, 28)) pygame.draw.ellipse(surf, (255, 255, 200), (20, 12, 24, 20)) pygame.draw.circle(surf, (0, 200, 255), (32, 20), 6) # Visor # Cañón de disparo pygame.draw.rect(surf, (150, 150, 150), (30, 6, 4, 10)) return surf def create_vortex_sprite(): surf = pygame.Surface((64, 64), pygame.SRCALPHA) # Base pesada VORTEX pygame.draw.ellipse(surf, (180, 40, 40), (8, 24, 48, 30)) pygame.draw.ellipse(surf, (220, 60, 60), (14, 16, 36, 22)) pygame.draw.circle(surf, (255, 255, 255), (32, 24), 8) pygame.draw.circle(surf, (255, 0, 0), (32, 24), 4) return surf def create_block_sprite(): surf = pygame.Surface((64, 64), pygame.SRCALPHA) # Cara superior pts_top = [(32, 8), (56, 20), (32, 32), (8, 20)] pygame.draw.polygon(surf, (210, 90, 50), pts_top) # Cara izquierda pts_left = [(8, 20), (32, 32), (32, 52), (8, 40)] pygame.draw.polygon(surf, (150, 60, 30), pts_left) # Cara derecha pts_right = [(32, 32), (56, 20), (56, 40), (32, 52)] pygame.draw.polygon(surf, (180, 75, 40), pts_right) # Bordes pygame.draw.polygon(surf, (255, 255, 255), pts_top, 1) return surf def create_enemy_sprite(): surf = pygame.Surface((64, 64), pygame.SRCALPHA) # Dron Alienígena pygame.draw.ellipse(surf, (50, 220, 100), (12, 16, 40, 32)) pygame.draw.circle(surf, (255, 0, 100), (32, 30), 8) pygame.draw.line(surf, (50, 220, 100), (20, 48), (12, 58), 3) pygame.draw.line(surf, (50, 220, 100), (44, 48), (52, 58), 3) return surf def create_bullet_sprite(): surf = pygame.Surface((32, 32), pygame.SRCALPHA) pygame.draw.circle(surf, (0, 255, 255), (16, 16), 6) pygame.draw.circle(surf, (255, 255, 255), (16, 16), 3) return surf # ============================================================================== # CLASES DEL JUEGO # ============================================================================== class Entity: def __init__(self, gx, gy, sprite, is_solid=True): self.gx = float(gx) self.gy = float(gy) self.gz = 0 self.sprite = sprite self.is_solid = is_solid def draw(self, surface, camera_offset_x, camera_offset_y): screen_x, screen_y = grid_to_iso(self.gx, self.gy, self.gz) draw_x = screen_x + camera_offset_x - (self.sprite.get_width() // 2) draw_y = screen_y + camera_offset_y - (self.sprite.get_height() // 2) surface.blit(self.sprite, (draw_x, draw_y)) class Bullet(Entity): def __init__(self, gx, gy, dir_x, dir_y, sprite): super().__init__(gx, gy, sprite, is_solid=False) self.dir_x = dir_x self.dir_y = dir_y self.speed = 0.25 self.alive = True def update(self, entities): self.gx += self.dir_x * self.speed self.gy += self.dir_y * self.speed if not (0 <= self.gx < GRID_SIZE_X and 0 <= self.gy < GRID_SIZE_Y): self.alive = False return for entity in entities: if entity != self and entity.is_solid: if abs(entity.gx - self.gx) < 0.5 and abs(entity.gy - self.gy) < 0.5: self.alive = False if isinstance(entity, (PushBlock, Enemy)): entity.health -= 1 break class PushBlock(Entity): def __init__(self, gx, gy, sprite): super().__init__(gx, gy, sprite, is_solid=True) self.health = 2 def push(self, dx, dy, entities): new_gx = self.gx + dx new_gy = self.gy + dy if not (0 <= new_gx < GRID_SIZE_X and 0 <= new_gy < GRID_SIZE_Y): return False for entity in entities: if entity != self and entity.is_solid: if abs(entity.gx - new_gx) < 0.5 and abs(entity.gy - new_gy) < 0.5: return False self.gx = new_gx self.gy = new_gy return True class Enemy(Entity): def __init__(self, gx, gy, sprite): super().__init__(gx, gy, sprite, is_solid=True) self.speed = 0.03 self.direction = 1 self.health = 1 def update(self, entities): target_gx = self.gx + (self.speed * self.direction) if target_gx <= 0.5 or target_gx >= GRID_SIZE_X - 0.5: self.direction *= -1 target_gx = self.gx + (self.speed * self.direction) self.gx = target_gx class Player(Entity): def __init__(self, gx, gy, sprite, bullet_sprite): super().__init__(gx, gy, sprite, is_solid=True) self.bullet_sprite = bullet_sprite self.speed = 0.09 self.facing_x = 0.0 self.facing_y = -1.0 self.shoot_cooldown = 0 def update(self, keys, entities, bullets): dx, dy = 0, 0 if keys[pygame.K_LEFT] or keys[pygame.K_a]: dx -= self.speed if keys[pygame.K_RIGHT] or keys[pygame.K_d]: dx += self.speed if keys[pygame.K_UP] or keys[pygame.K_w]: dy -= self.speed if keys[pygame.K_DOWN] or keys[pygame.K_s]: dy += self.speed if dx != 0 or dy != 0: norm = math.sqrt(dx * dx + dy * dy) self.facing_x = dx / norm self.facing_y = dy / norm target_gx = self.gx + dx target_gy = self.gy + dy if 0 <= target_gx < GRID_SIZE_X and 0 <= target_gy < GRID_SIZE_Y: blocked = False for entity in entities: if entity != self and entity.is_solid: if abs(entity.gx - target_gx) < 0.5 and abs(entity.gy - target_gy) < 0.5: if isinstance(entity, PushBlock): push_dx = 0.15 if dx > 0 else (-0.15 if dx < 0 else 0) push_dy = 0.15 if dy > 0 else (-0.15 if dy < 0 else 0) if not entity.push(push_dx, push_dy, entities): blocked = True else: blocked = True if not blocked: self.gx = target_gx self.gy = target_gy if self.shoot_cooldown > 0: self.shoot_cooldown -= 1 if keys[pygame.K_SPACE] and self.shoot_cooldown == 0: spawn_x = self.gx + (self.facing_x * 0.7) spawn_y = self.gy + (self.facing_y * 0.7) bullets.append(Bullet(spawn_x, spawn_y, self.facing_x, self.facing_y, self.bullet_sprite)) self.shoot_cooldown = 15 class Vortex(Entity): def __init__(self, gx, gy, sprite): super().__init__(gx, gy, sprite, is_solid=True) self.speed = 0.012 def update(self, entities): target_gy = self.gy - self.speed blocked = False for entity in entities: if entity != self and entity.is_solid: if abs(entity.gx - self.gx) < 0.6 and abs(entity.gy - target_gy) < 0.6: blocked = True break if not blocked and target_gy >= 0: self.gy = target_gy class Game: def __init__(self): self.screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT)) pygame.display.set_caption("Highway Encounter - PNG Sprites Version") self.clock = pygame.time.Clock() self.font = pygame.font.SysFont("monospace", 18, bold=True) # Carga o Generación de Sprites self.spr_player = load_or_create_sprite("player.png", create_player_sprite) self.spr_vortex = load_or_create_sprite("vortex.png", create_vortex_sprite) self.spr_block = load_or_create_sprite("block.png", create_block_sprite) self.spr_enemy = load_or_create_sprite("enemy.png", create_enemy_sprite) self.spr_bullet = load_or_create_sprite("bullet.png", create_bullet_sprite) self.lives = 4 self.score = 0 self.reset_level() def reset_level(self): self.vortex = Vortex(3.5, GRID_SIZE_Y - 5, self.spr_vortex) self.player = Player(3.5, GRID_SIZE_Y - 8, self.spr_player, self.spr_bullet) self.entities = [self.vortex, self.player] self.bullets = [] self.spares = [] for i in range(self.lives - 1): spare = Entity(3.5, GRID_SIZE_Y - 3 + (i * 1.2), self.spr_player, is_solid=False) self.spares.append(spare) for y in range(10, GRID_SIZE_Y - 15, 6): self.entities.append(PushBlock(y % GRID_SIZE_X, y, self.spr_block)) if y % 12 == 0: self.entities.append(Enemy(3.5, y - 2, self.spr_enemy)) def handle_collisions(self): for entity in self.entities: if isinstance(entity, Enemy): if abs(entity.gx - self.player.gx) < 0.5 and abs(entity.gy - self.player.gy) < 0.5: self.lives -= 1 if self.lives > 0: self.reset_level() return for entity in self.entities[:]: if hasattr(entity, "health") and entity.health <= 0: self.entities.remove(entity) self.score += 100 def draw_hud(self): zone = int((GRID_SIZE_Y - self.vortex.gy) / 4) hud_text = f"ZONE: {zone:02d} | LIVES: {self.lives} | SCORE: {self.score:06d}" text_surface = self.font.render(hud_text, True, (255, 255, 255)) pygame.draw.rect(self.screen, (0, 0, 0), (0, 0, SCREEN_WIDTH, 40)) self.screen.blit(text_surface, (20, 10)) def run(self): running = True while running: self.clock.tick(FPS) for event in pygame.event.get(): if event.type == pygame.QUIT: running = False if self.lives <= 0: self.screen.fill((0, 0, 0)) msg = self.font.render("GAME OVER - Press R to Restart", True, (255, 50, 50)) self.screen.blit(msg, (SCREEN_WIDTH // 2 - 150, SCREEN_HEIGHT // 2)) pygame.display.flip() keys = pygame.key.get_pressed() if keys[pygame.K_r]: self.lives = 4 self.score = 0 self.reset_level() continue keys = pygame.key.get_pressed() self.player.update(keys, self.entities, self.bullets) self.vortex.update(self.entities) for entity in self.entities: if isinstance(entity, Enemy): entity.update(self.entities) for bullet in self.bullets[:]: bullet.update(self.entities) if not bullet.alive: self.bullets.remove(bullet) self.handle_collisions() iso_x, iso_y = grid_to_iso(self.player.gx, self.player.gy) camera_offset_x = SCREEN_WIDTH // 2 - iso_x camera_offset_y = SCREEN_HEIGHT // 2 - iso_y + 100 self.screen.fill(COLOR_BG) for gy in range(0, GRID_SIZE_Y): for gx in range(0, GRID_SIZE_X): sx, sy = grid_to_iso(gx, gy) draw_x = sx + camera_offset_x draw_y = sy + camera_offset_y if -100 < draw_x < SCREEN_WIDTH + 100 and -100 < draw_y < SCREEN_HEIGHT + 100: pts = [ (draw_x, draw_y), (draw_x + TILE_WIDTH // 2, draw_y + TILE_HEIGHT // 2), (draw_x, draw_y + TILE_HEIGHT), (draw_x - TILE_WIDTH // 2, draw_y + TILE_HEIGHT // 2), ] pygame.draw.polygon(self.screen, COLOR_ROAD, pts) pygame.draw.polygon(self.screen, COLOR_GRID, pts, 1) all_drawables = self.entities + self.bullets + self.spares sorted_entities = sorted(all_drawables, key=lambda e: e.gy, reverse=True) for entity in sorted_entities: entity.draw(self.screen, camera_offset_x, camera_offset_y) self.draw_hud() pygame.display.flip() pygame.quit() sys.exit() if __name__ == "__main__": Game().run()