import sys import pygame # Inicialización de Pygame pygame.init() # Constantes de Pantalla SCREEN_WIDTH = 800 SCREEN_HEIGHT = 600 FPS = 60 # Configuración de Cuadrícula e Isometría TILE_WIDTH = 64 TILE_HEIGHT = 32 GRID_SIZE_X = 8 GRID_SIZE_Y = 100 # Largo de la autopista # Colores (Paleta Retro Spectrum/Amstrad) COLOR_BG = (10, 10, 15) COLOR_GRID = (40, 40, 60) COLOR_ROAD = (60, 60, 80) COLOR_VORTEX = (0, 255, 200) COLOR_PLAYER = (255, 200, 0) COLOR_BLOCK = (200, 50, 50) COLOR_WALL = (100, 100, 120) def grid_to_iso(gx, gy, gz=0): """Convierte coordenadas de cuadrícula 3D (x, y, z) a coordenadas de pantalla 2D.""" iso_x = (gx - gy) * (TILE_WIDTH // 2) iso_y = (gx + gy) * (TILE_HEIGHT // 2) - (gz * 16) return iso_x, iso_y class Entity: def __init__(self, gx, gy, color, is_solid=True): self.gx = float(gx) self.gy = float(gy) self.gz = 0 self.color = color self.is_solid = is_solid self.radius = 0.4 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 draw_y = screen_y + camera_offset_y # Dibujar un bloque o personaje estilo isométrico points = [ (draw_x, draw_y - 20), (draw_x + TILE_WIDTH // 2, draw_y - 20 + TILE_HEIGHT // 2), (draw_x, draw_y - 20 + TILE_HEIGHT), (draw_x - TILE_WIDTH // 2, draw_y - 20 + TILE_HEIGHT // 2), ] pygame.draw.polygon(surface, self.color, points) pygame.draw.polygon( surface, (255, 255, 255), points, 2 ) # Borde brillante class PushBlock(Entity): def __init__(self, gx, gy): super().__init__(gx, gy, COLOR_BLOCK, is_solid=True) def push(self, dx, dy, entities): new_gx = self.gx + dx new_gy = self.gy + dy # Comprobar límites de la carretera if not (0 <= new_gx < GRID_SIZE_X and 0 <= new_gy < GRID_SIZE_Y): return False # Comprobar colisión con otras entidades for entity in entities: if entity != self and entity.is_solid: if ( abs(entity.gx - new_gx) < 0.6 and abs(entity.gy - new_gy) < 0.6 ): return False self.gx = new_gx self.gy = new_gy return True class Player(Entity): def __init__(self, gx, gy): super().__init__(gx, gy, COLOR_PLAYER, is_solid=True) self.speed = 0.08 def update(self, keys, entities): 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: target_gx = self.gx + dx target_gy = self.gy + dy # Limites horizontales if not (0 <= target_gx < GRID_SIZE_X): dx = 0 if not (0 <= target_gy < GRID_SIZE_Y): dy = 0 # Intento de movimiento y colisión con bloques for entity in entities: if entity != self and entity.is_solid: if ( abs(entity.gx - (self.gx + dx)) < 0.6 and abs(entity.gy - (self.gy + dy)) < 0.6 ): # Intentar empujar si es un PushBlock if isinstance(entity, PushBlock): push_dx = 0.1 if dx > 0 else (-0.1 if dx < 0 else 0) push_dy = 0.1 if dy > 0 else (-0.1 if dy < 0 else 0) if not entity.push(push_dx, push_dy, entities): return else: return # Bloqueado self.gx += dx self.gy += dy class Vortex(Entity): def __init__(self, gx, gy): super().__init__(gx, gy, COLOR_VORTEX, is_solid=True) self.speed = 0.015 # Avance constante automático def update(self, entities): target_gy = self.gy - self.speed # Verificar si hay algún obstáculo al avanzar blocked = False for entity in entities: if entity != self and entity.is_solid: if ( abs(entity.gx - self.gx) < 0.7 and abs(entity.gy - target_gy) < 0.7 ): 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 Python Remake") self.clock = pygame.time.Clock() self.vortex = Vortex(3.5, 95) self.player = Player(3.5, 92) self.entities = [self.vortex, self.player] # Generar bloques de prueba a lo largo de la autopista for y in range(10, 90, 5): self.entities.append(PushBlock(y % GRID_SIZE_X, y)) self.entities.append(PushBlock((y + 3) % GRID_SIZE_X, y + 2)) def run(self): running = True while running: self.clock.tick(FPS) # 1. Procesar Eventos for event in pygame.event.get(): if event.type == pygame.QUIT: running = False # 2. Actualizar Estado keys = pygame.key.get_pressed() self.player.update(keys, self.entities) self.vortex.update(self.entities) # 3. Calcular desplazamiento de cámara (centrada en el VORTEX o el jugador) 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 # 4. Renderizado self.screen.fill(COLOR_BG) # Dibujar suelo (Autopista) 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 # Solo dibujar si está visible en pantalla 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) # Ordenar entidades por coordenada 'gy' descendente para renderizar correctamente la profundidad sorted_entities = sorted( self.entities, key=lambda e: e.gy, reverse=True ) for entity in sorted_entities: entity.draw(self.screen, camera_offset_x, camera_offset_y) pygame.display.flip() pygame.quit() sys.exit() if __name__ == "__main__": Game().run()