Highway_Vector/highway_vector.py
2026-10-04 15:13:14 +02:00

381 lines
13 KiB
Python

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()